Added stashes with experimental row security policies. Added tag photos.

This commit is contained in:
DebaucheryLibrarian
2021-03-14 04:54:43 +01:00
parent 816529b0ca
commit e371e9725a
58 changed files with 610 additions and 172 deletions

View File

@@ -49,7 +49,7 @@ async function login() {
this.success = false;
try {
await this.$store.dispatch('login', {
const user = await this.$store.dispatch('login', {
username: this.username,
password: this.password,
});
@@ -57,7 +57,7 @@ async function login() {
this.success = true;
setTimeout(() => {
this.$router.replace(this.$route.query.ref || { name: 'home' });
this.$router.replace(this.$route.query.ref || { name: 'user', params: { username: user.username } });
}, 1000);
} catch (error) {
this.error = error.message;

View File

@@ -93,82 +93,7 @@
</div>
<template v-slot:tooltip>
<div class="menu">
<ul class="menu-items noselect">
<router-link
v-if="!me"
to="/login"
class="menu-item"
@click.stop
>
<Icon icon="enter2" />Log in
</router-link>
<li
v-if="me"
class="menu-username"
>{{ me.username }}</li>
<li
v-if="me"
class="menu-item"
@click.stop="$store.dispatch('logout')"
>
<Icon icon="enter2" />Log out
</li>
<li
v-show="!sfw"
class="menu-item"
@click.stop="setSfw(true)"
>
<Icon
icon="flower"
class="toggle noselect"
/>Safe mode
</li>
<li
v-show="sfw"
class="menu-item"
@click.stop="setSfw(false)"
>
<Icon
icon="fire"
class="toggle noselect"
/>Filth mode
</li>
<li
v-show="theme === 'light'"
class="menu-item"
@click.stop="setTheme('dark')"
>
<Icon
icon="moon"
class="toggle noselect"
/>Dark theme
</li>
<li
v-show="theme === 'dark'"
class="menu-item"
@click.stop="setTheme('light')"
>
<Icon
icon="sun"
class="toggle noselect"
/>Light theme
</li>
<li
class="menu-item"
@click="$emit('showFilters', true)"
>
<Icon icon="filter" />Filters
</li>
</ul>
</div>
<Menu />
</template>
</Tooltip>
@@ -201,34 +126,14 @@
</template>
<script>
import { mapState } from 'vuex';
import Menu from './menu.vue';
import Search from './search.vue';
import logo from '../../img/logo.svg';
function sfw(state) {
return state.ui.sfw;
}
function theme(state) {
return state.ui.theme;
}
function me(state) {
return state.auth.user;
}
function setTheme(newTheme) {
this.$store.dispatch('setTheme', newTheme);
}
function setSfw(enabled) {
this.$store.dispatch('setSfw', enabled);
}
export default {
components: {
Menu,
Search,
},
emits: ['toggleSidebar', 'showFilters'],
@@ -239,17 +144,6 @@ export default {
showFilters: false,
};
},
computed: {
...mapState({
sfw,
theme,
me,
}),
},
methods: {
setSfw,
setTheme,
},
};
</script>
@@ -414,51 +308,6 @@ export default {
}
}
.menu-items {
list-style: none;
padding: 0;
margin: 0;
}
.menu-item {
display: flex;
padding: .75rem 1rem .75rem .75rem;
color: inherit;
text-decoration: none;
.icon {
fill: var(--darken);
margin: 0 1rem 0 0;
}
&.disabled {
color: var(--darken-weak);
cursor: default;
.icon {
fill: var(--darken-weak);
}
}
&:hover:not(.disabled) {
cursor: pointer;
color: var(--primary);
.icon {
fill: var(--primary);
}
}
}
.menu-username {
font-weight: bold;
color: var(--shadow-strong);
font-size: .9rem;
padding: .75rem 1rem;
border-bottom: solid 1px var(--shadow-hint);
text-align: center;
}
.search-compact {
display: none;
height: 100%;

View File

@@ -0,0 +1,168 @@
<template>
<div class="menu">
<ul class="menu-items noselect">
<router-link
v-if="me"
:to="{ name: 'user', params: { username: me.username } }"
class="menu-username"
>{{ me.username }}</router-link>
<router-link
v-else
to="/login"
class="menu-item"
@click.stop
>
<Icon icon="enter2" />Log in
</router-link>
<li
v-if="me"
class="menu-item"
@click.stop="$store.dispatch('logout')"
>
<Icon icon="enter2" />Log out
</li>
<li
v-show="!sfw"
class="menu-item"
@click.stop="setSfw(true)"
>
<Icon
icon="flower"
class="toggle noselect"
/>Safe mode
</li>
<li
v-show="sfw"
class="menu-item"
@click.stop="setSfw(false)"
>
<Icon
icon="fire"
class="toggle noselect"
/>Filth mode
</li>
<li
v-show="theme === 'light'"
class="menu-item"
@click.stop="setTheme('dark')"
>
<Icon
icon="moon"
class="toggle noselect"
/>Dark theme
</li>
<li
v-show="theme === 'dark'"
class="menu-item"
@click.stop="setTheme('light')"
>
<Icon
icon="sun"
class="toggle noselect"
/>Light theme
</li>
<li
class="menu-item"
@click="$emit('showFilters', true)"
>
<Icon icon="filter" />Filters
</li>
</ul>
</div>
</template>
<script>
import { mapState } from 'vuex';
function sfw(state) {
return state.ui.sfw;
}
function theme(state) {
return state.ui.theme;
}
function me(state) {
return state.auth.user;
}
function setTheme(newTheme) {
this.$store.dispatch('setTheme', newTheme);
}
function setSfw(enabled) {
this.$store.dispatch('setSfw', enabled);
}
export default {
computed: {
...mapState({
sfw,
theme,
me,
}),
},
methods: {
setSfw,
setTheme,
},
};
</script>
<style lang="scss" scoped>
@import 'breakpoints';
.menu-items {
list-style: none;
padding: 0;
margin: 0;
}
.menu-item {
display: flex;
padding: .75rem 1rem .75rem .75rem;
color: inherit;
text-decoration: none;
.icon {
fill: var(--darken);
margin: 0 1rem 0 0;
}
&.disabled {
color: var(--darken-weak);
cursor: default;
.icon {
fill: var(--darken-weak);
}
}
&:hover:not(.disabled) {
cursor: pointer;
color: var(--primary);
.icon {
fill: var(--primary);
}
}
}
.menu-username {
display: block;
font-weight: bold;
color: var(--shadow-strong);
font-size: .9rem;
padding: .75rem 1rem;
border-bottom: solid 1px var(--shadow-hint);
text-align: center;
text-decoration: none;
}
</style>

View File

@@ -73,7 +73,10 @@
>{{ release.entity.name }}</h3>
</a>
<span class="row">
<span
v-if="release.actors?.length > 0"
class="row"
>
<ul
class="actors nolist"
:title="release.actors.map(actor => actor.name).join(', ')"
@@ -106,7 +109,7 @@
>{{ release.shootId }}</span>
<ul
v-if="release.tags.length > 0"
v-if="release.tags?.length > 0"
:title="release.tags.map(tag => tag.name).join(', ')"
class="tags nolist"
>

View File

@@ -4,7 +4,7 @@
:class="{ new: release.isNew }"
>
<span
v-if="release.entity.type !== 'network' && !release.entity.independent && release.entity.parent"
v-if="release.entity && release.entity.type !== 'network' && !release.entity.independent && release.entity.parent"
class="site"
>
<router-link
@@ -33,7 +33,7 @@
</span>
<router-link
v-else
v-else-if="release.entity"
:to="`/${release.entity.type}/${release.entity.slug}`"
class="site site-link"
>

View File

@@ -0,0 +1,113 @@
<template>
<div
v-if="user"
class="user"
>
<div class="header">
<h2 class="username">{{ user.username }}</h2>
</div>
<section
v-if="stashes.length > 0"
class="section"
>
<h3 class="heading">Stashes</h3>
<ul class="stashes nolist">
<li
v-for="stash in stashes"
:key="stash.id"
>
<h4 class="stash-name">{{ stash.name }}</h4>
<ul class="stash nolist actors">
<li
v-for="item in stash.actors"
:key="item.id"
><Actor :actor="item.actor" /></li>
</ul>
<ul class="stash nolist scenes">
<li
v-for="item in stash.scenes"
:key="item.id"
><Scene :release="item.scene" /></li>
</ul>
</li>
</ul>
</section>
</div>
</template>
<script>
import Actor from '../actors/tile.vue';
import Scene from '../releases/scene-tile.vue';
async function mounted() {
this.user = await this.$store.dispatch('fetchMe');
this.stashes = await this.$store.dispatch('fetchUserStashes', this.user.id);
}
export default {
components: {
Actor,
Scene,
},
data() {
return {
user: this.$route.params.username === this.$store.state.auth.user?.username
? this.$store.state.auth.user
: null,
stashes: [],
};
},
mounted,
};
</script>
<style lang="scss" scoped>
.header {
padding: 1rem;
background: var(--profile);
}
.username {
margin: 0;
font-size: 1.5rem;
color: var(--text-light);
}
.section {
padding: 1rem;
margin: 0 0 1rem 0;
}
.heading {
color: var(--primary);
}
.stash {
margin: 0 0 1rem 0;
}
.stash-name {
color: var(--shadow-strong);
margin: 0 0 1rem 0;
}
.actors {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
grid-gap: .5rem;
flex-grow: 1;
flex-wrap: wrap;
}
.scenes {
width: 100%;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
grid-gap: .5rem;
box-sizing: border-box;
}
</style>

View File

@@ -5,10 +5,14 @@ $breakpoint3: 1200px;
$breakpoint4: 1500px;
:root {
/* --primary: #ff886c; */
/*
--primary: #ff6c88;
--primary-strong: #ff4166;
--primary-faded: #ffdfee;
*/
--primary: #f28;
--primary-strong: #f90071;
--primary-faded: #ff4e9f;
--text-dark: #222;
--text-light: #fff;

View File

@@ -125,9 +125,32 @@ function curateTag(tag) {
return curatedTag;
}
function curateStash(stash) {
const curatedStash = {
...stash,
};
if (stash.scenes) {
curatedStash.scenes = stash.scenes.map(item => ({
...item,
scene: curateRelease(item.scene),
}));
}
if (stash.actors) {
curatedStash.actors = stash.actors.map(item => ({
...item,
actor: curateActor(item.actor),
}));
}
return curatedStash;
}
export {
curateActor,
curateEntity,
curateRelease,
curateTag,
curateStash,
};

View File

@@ -3,6 +3,7 @@ import { createRouter, createWebHistory } from 'vue-router';
import Home from '../components/home/home.vue';
import Login from '../components/auth/login.vue';
import Signup from '../components/auth/signup.vue';
import User from '../components/users/user.vue';
import Release from '../components/releases/release.vue';
import Entity from '../components/entities/entity.vue';
import Networks from '../components/networks/networks.vue';
@@ -38,6 +39,11 @@ const routes = [
name: 'singup',
component: Signup,
},
{
path: '/user/:username',
name: 'user',
component: User,
},
{
path: '/updates',
redirect: {

View File

@@ -0,0 +1,102 @@
import { graphql } from '../api';
import { curateStash } from '../curate';
function initStashesActions(_store, _router) {
async function fetchUserStashes(context, userId) {
const { stashes } = await graphql(`
query Stashes(
$userId: Int!
) {
stashes(
filter: {
userId: {
equalTo: $userId
}
}
) {
id
name
actors: stashesActors {
comment
actor {
id
name
slug
gender
age
ageFromBirth
dateOfBirth
birthCity
birthState
birthCountry: countryByBirthCountryAlpha2 {
alpha2
name
alias
}
avatar: avatarMedia {
id
path
thumbnail
lazy
}
}
}
scenes: stashesScenes {
comment
scene {
id
title
slug
url
date
actors: releasesActors {
actor {
id
name
slug
}
}
tags: releasesTags {
tag {
id
name
slug
}
}
entity {
id
name
slug
independent
parent {
id
name
slug
independent
}
}
poster: releasesPosterByReleaseId {
media {
path
thumbnail
lazy
isS3
}
}
}
}
}
}
`, {
userId,
});
return stashes.map(stash => curateStash(stash));
}
return {
fetchUserStashes,
};
}
export default initStashesActions;

View File

@@ -0,0 +1 @@
export default {};

View File

@@ -0,0 +1,13 @@
import state from './state';
import mutations from './mutations';
import actions from './actions';
function initStashesStore(store, router) {
return {
state,
mutations,
actions: actions(store, router),
};
}
export default initStashesStore;

View File

@@ -0,0 +1 @@
export default {};

View File

@@ -2,20 +2,24 @@ import Vuex from 'vuex';
import initUiStore from './ui/ui';
import initAuthStore from './auth/auth';
import initUsersStore from './users/users';
import initReleasesStore from './releases/releases';
import initEntitiesStore from './entities/entities';
import initActorsStore from './actors/actors';
import initTagsStore from './tags/tags';
import initStashesStore from './stashes/stashes';
function initStore(router) {
const store = new Vuex.Store();
store.registerModule('ui', initUiStore(store, router));
store.registerModule('auth', initAuthStore(store, router));
store.registerModule('users', initUsersStore(store, router));
store.registerModule('releases', initReleasesStore(store, router));
store.registerModule('entities', initEntitiesStore(store, router));
store.registerModule('actors', initActorsStore(store, router));
store.registerModule('tags', initTagsStore(store, router));
store.registerModule('stashes', initStashesStore(store, router));
return store;
}

View File

@@ -0,0 +1,15 @@
import { get } from '../api';
function initUsersActions(_store, _router) {
async function fetchUser(context, username) {
const user = await get(`/users/${username}`);
return user;
}
return {
fetchUser,
};
}
export default initUsersActions;

View File

@@ -0,0 +1 @@
export default {};

1
assets/js/users/state.js Normal file
View File

@@ -0,0 +1 @@
export default {};

13
assets/js/users/users.js Normal file
View File

@@ -0,0 +1,13 @@
import state from './state';
import mutations from './mutations';
import actions from './actions';
function initUsersStore(store, router) {
return {
state,
mutations,
actions: actions(store, router),
};
}
export default initUsersStore;