forked from DebaucheryLibrarian/traxxx
Merged filters into new settings dialog, added experimental summary field.
This commit is contained in:
parent
637669e3d1
commit
b5e308562e
|
@ -8,17 +8,10 @@
|
|||
/>
|
||||
|
||||
<transition name="slide">
|
||||
<Sidebar
|
||||
v-if="showSidebar"
|
||||
@toggle-sidebar="(state) => toggleSidebar(state)"
|
||||
@show-filters="(state) => toggleFilters(state)"
|
||||
/>
|
||||
<Sidebar v-if="showSidebar" />
|
||||
</transition>
|
||||
|
||||
<Header
|
||||
@toggle-sidebar="(state) => toggleSidebar(state)"
|
||||
@show-filters="(state) => toggleFilters(state)"
|
||||
/>
|
||||
<Header />
|
||||
|
||||
<p
|
||||
v-if="config.showDisclaimer"
|
||||
|
@ -30,13 +23,18 @@
|
|||
class="content"
|
||||
@scroll="scroll"
|
||||
>
|
||||
<router-view @scroll="scrollToTop" />
|
||||
<RouterView @scroll="scrollToTop" />
|
||||
</div>
|
||||
|
||||
<Filters
|
||||
v-if="showFilters"
|
||||
@close="toggleFilters(false)"
|
||||
/>
|
||||
|
||||
<Settings
|
||||
v-if="showSettings"
|
||||
@close="toggleSettings(false)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -45,6 +43,7 @@ import Warning from './warning.vue';
|
|||
import Header from '../header/header.vue';
|
||||
import Sidebar from '../sidebar/sidebar.vue';
|
||||
import Filters from '../filters/filters.vue';
|
||||
import Settings from '../settings/settings.vue';
|
||||
|
||||
function toggleSidebar(state) {
|
||||
this.showSidebar = typeof state === 'boolean' ? state : !this.showSidebar;
|
||||
|
@ -55,6 +54,11 @@ function toggleFilters(state) {
|
|||
this.showSidebar = false;
|
||||
}
|
||||
|
||||
function toggleSettings(state) {
|
||||
this.showSettings = state;
|
||||
this.showSidebar = false;
|
||||
}
|
||||
|
||||
async function setConsent(consent, includeQueer) {
|
||||
if (consent) {
|
||||
this.showWarning = false;
|
||||
|
@ -88,6 +92,9 @@ function scrollToTop() {
|
|||
function mounted() {
|
||||
document.addEventListener('click', this.blur);
|
||||
window.addEventListener('resize', this.resize);
|
||||
|
||||
this.events.on('toggleSettings', this.toggleSettings);
|
||||
this.events.on('toggleSidebar', this.toggleSidebar);
|
||||
}
|
||||
|
||||
function beforeUnmount() {
|
||||
|
@ -101,12 +108,14 @@ export default {
|
|||
Sidebar,
|
||||
Warning,
|
||||
Filters,
|
||||
Settings,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showSidebar: false,
|
||||
showWarning: localStorage.getItem('consent') !== window.env.sessionId,
|
||||
showFilters: false,
|
||||
showSettings: false,
|
||||
selected: null,
|
||||
};
|
||||
},
|
||||
|
@ -115,6 +124,7 @@ export default {
|
|||
methods: {
|
||||
toggleSidebar,
|
||||
toggleFilters,
|
||||
toggleSettings,
|
||||
setConsent,
|
||||
blur,
|
||||
resize,
|
||||
|
|
|
@ -114,13 +114,16 @@ export default {
|
|||
}
|
||||
|
||||
::v-deep(.dialog-body) {
|
||||
padding: 1rem;
|
||||
flex-grow: 1;
|
||||
box-sizing: border-box;
|
||||
padding: 1rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
::v-deep(.dialog-section:not(:last-child)) {
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: solid 1px var(--shadow-hint);
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<header class="header">
|
||||
<div class="header-nav">
|
||||
<router-link
|
||||
<RouterLink
|
||||
to="/"
|
||||
class="logo-link"
|
||||
><h1 class="header-logo">
|
||||
|
@ -9,12 +9,12 @@
|
|||
class="logo"
|
||||
v-html="logo"
|
||||
/>
|
||||
</h1></router-link>
|
||||
</h1></RouterLink>
|
||||
|
||||
<nav class="nav">
|
||||
<ul class="nav-list nolist">
|
||||
<li class="nav-item">
|
||||
<router-link
|
||||
<RouterLink
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
:to="{ name: 'actors', params: { pageNumber: 1 } }"
|
||||
custom
|
||||
|
@ -25,11 +25,11 @@
|
|||
:class="{ active: isActive }"
|
||||
@click="navigate"
|
||||
>Actors</a>
|
||||
</router-link>
|
||||
</RouterLink>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<router-link
|
||||
<RouterLink
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
:to="{ name: 'channels' }"
|
||||
custom
|
||||
|
@ -40,11 +40,11 @@
|
|||
:class="{ active: isActive }"
|
||||
@click="navigate"
|
||||
>Channels</a>
|
||||
</router-link>
|
||||
</RouterLink>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<router-link
|
||||
<RouterLink
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
:to="{ name: 'tags' }"
|
||||
custom
|
||||
|
@ -55,11 +55,11 @@
|
|||
:class="{ active: isActive }"
|
||||
@click="navigate"
|
||||
>Tags</a>
|
||||
</router-link>
|
||||
</RouterLink>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<router-link
|
||||
<RouterLink
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
:to="{ name: 'movies', params: { range: 'latest', pageNumber: 1 } }"
|
||||
custom
|
||||
|
@ -70,7 +70,7 @@
|
|||
:class="{ active: isActive }"
|
||||
@click="navigate"
|
||||
>Movies</a>
|
||||
</router-link>
|
||||
</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
@ -79,7 +79,7 @@
|
|||
<div class="header-section">
|
||||
<div
|
||||
class="sidebar-toggle noselect"
|
||||
@click.stop="$emit('toggleSidebar')"
|
||||
@click.stop="events.emit('toggleSidebar')"
|
||||
><Icon icon="menu" /></div>
|
||||
|
||||
<Tooltip v-if="me">
|
||||
|
@ -95,7 +95,7 @@
|
|||
>{{ unseenNotificationsCount }}</span>
|
||||
</div>
|
||||
|
||||
<template v-slot:tooltip>
|
||||
<template #tooltip>
|
||||
<Notifications
|
||||
:notifications="notifications"
|
||||
:unseen-count="unseenNotificationsCount"
|
||||
|
@ -115,8 +115,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<template v-slot:tooltip>
|
||||
<Menu @show-filters="state => $emit('showFilters', state)" />
|
||||
<template #tooltip>
|
||||
<Menu />
|
||||
</template>
|
||||
</Tooltip>
|
||||
|
||||
|
@ -133,7 +133,7 @@
|
|||
icon="search"
|
||||
/></button>
|
||||
|
||||
<template v-slot:tooltip>
|
||||
<template #tooltip>
|
||||
<Search
|
||||
:searching="searching"
|
||||
class="compact"
|
||||
|
|
|
@ -68,9 +68,9 @@
|
|||
|
||||
<li
|
||||
class="menu-item"
|
||||
@click="$emit('showFilters', true)"
|
||||
@click="events.emit('toggleSettings', true)"
|
||||
>
|
||||
<Icon icon="filter" />Filters
|
||||
<Icon icon="cog" />Settings
|
||||
</li>
|
||||
|
||||
<li
|
||||
|
@ -104,7 +104,7 @@ function signup(state) {
|
|||
}
|
||||
|
||||
function favorites() {
|
||||
return this.me?.stashes.find(stash => stash.primary);
|
||||
return this.me?.stashes.find((stash) => stash.primary);
|
||||
}
|
||||
|
||||
function me(state) {
|
||||
|
@ -130,7 +130,6 @@ export default {
|
|||
}),
|
||||
favorites,
|
||||
},
|
||||
emits: ['showFilters'],
|
||||
methods: {
|
||||
setSfw,
|
||||
setTheme,
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
<span class="row-label">Part of</span>
|
||||
|
||||
<div class="movies">
|
||||
<router-link
|
||||
<RouterLink
|
||||
v-for="movie in [...release.movies, ...release.series]"
|
||||
:key="`movie-${movie.id}`"
|
||||
:to="{ name: movie.type || 'movie', params: { releaseId: movie.id, releaseSlug: movie.slug } }"
|
||||
|
@ -98,7 +98,7 @@
|
|||
:src="getPath(movie.covers[0] || movie.poster, 'thumbnail')"
|
||||
class="movie-cover"
|
||||
>
|
||||
</router-link>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -113,12 +113,12 @@
|
|||
>
|
||||
<span class="row-label">Director</span>
|
||||
|
||||
<router-link
|
||||
<RouterLink
|
||||
v-for="director in release.directors"
|
||||
:key="`director-${director.id}`"
|
||||
class="link director"
|
||||
:to="`/director/${director.id}/${director.slug}`"
|
||||
>{{ director.name }}</router-link>
|
||||
>{{ director.name }}</RouterLink>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
@ -162,10 +162,10 @@
|
|||
>
|
||||
<span class="row-label">Studio</span>
|
||||
|
||||
<router-link
|
||||
<RouterLink
|
||||
:to="`/studio/${release.studio.slug}`"
|
||||
class="link studio"
|
||||
>{{ release.studio.name }}</router-link>
|
||||
>{{ release.studio.name }}</RouterLink>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
@ -227,17 +227,40 @@
|
|||
<div class="row">
|
||||
<span class="row-label">Added</span>
|
||||
|
||||
<router-link
|
||||
<RouterLink
|
||||
:to="`/added/${formatDate(release.createdAt, 'YYYY/MM/DD')}`"
|
||||
:title="`Added on ${formatDate(release.createdAt, 'MMMM D, YYYY HH:mm')}`"
|
||||
class="link added"
|
||||
>{{ release.createdBatchId }}: {{ formatDate(release.createdAt, 'MMMM D, YYYY HH:mm') }}</router-link>
|
||||
>{{ release.createdBatchId }}: {{ formatDate(release.createdAt, 'MMMM D, YYYY HH:mm') }}</RouterLink>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span class="row-label">Summary</span>
|
||||
|
||||
<div class="summary">
|
||||
<input
|
||||
ref="summary"
|
||||
v-model="summary"
|
||||
class="input"
|
||||
@focus="selectSummary"
|
||||
>
|
||||
|
||||
<button
|
||||
v-if="hasClipboard"
|
||||
type="button"
|
||||
class="button button-secondary"
|
||||
:disabled="summaryCopied"
|
||||
@focus="copySummary"
|
||||
>{{ summaryCopied ? 'Copied!' : 'Copy' }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import formatSummary from '../../js/utils/format-summary';
|
||||
|
||||
import Details from './details.vue';
|
||||
import Banner from './banner.vue';
|
||||
import StashButton from '../stashes/button.vue';
|
||||
|
@ -266,6 +289,7 @@ async function fetchRelease(scroll = true) {
|
|||
}
|
||||
|
||||
this.stashedBy = this.release.stashes;
|
||||
this.setSummary();
|
||||
}
|
||||
|
||||
async function stashScene(stashId) {
|
||||
|
@ -288,6 +312,37 @@ function me() {
|
|||
return this.$store.state.auth.user;
|
||||
}
|
||||
|
||||
function setSummary() {
|
||||
// this.summary = `${this.release.entity.name} - ${this.release.title} (${this.release.actors.map((actor) => actor.name).join(', ')}, ${this.formatDate(this.release.date, 'DD-MM-YYYY')})`;
|
||||
const simpleRelease = {
|
||||
channel: this.release.entity.name,
|
||||
network: this.release.entity.parent?.name || this.release.entity.name,
|
||||
title: this.release.title,
|
||||
movie: this.release.movies?.[0]?.title,
|
||||
actors: this.release.actors.map((actor) => actor.name),
|
||||
tags: this.release.tags.map((tag) => tag.name),
|
||||
date: this.release.date,
|
||||
};
|
||||
|
||||
this.summary = formatSummary(simpleRelease, this.$store.state.ui.summaryFormat);
|
||||
}
|
||||
|
||||
async function selectSummary() {
|
||||
this.$refs.summary.select();
|
||||
}
|
||||
|
||||
async function copySummary() {
|
||||
const { state } = await navigator.permissions.query({ name: 'clipboard-write' });
|
||||
|
||||
if (state === 'granted' || state === 'prompt') {
|
||||
await navigator.clipboard.writeText(this.summary);
|
||||
|
||||
this.summaryCopied = true;
|
||||
|
||||
setTimeout(() => { this.summaryCopied = false; }, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function bannerBackground() {
|
||||
return (this.release.poster && this.getBgPath(this.release.poster, 'thumbnail'))
|
||||
|| (this.release.covers.length > 0 && this.getBgPath(this.release.covers[0], 'thumbnail'));
|
||||
|
@ -318,7 +373,10 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
release: null,
|
||||
summary: null,
|
||||
summaryCopied: false,
|
||||
stashedBy: [],
|
||||
hasClipboard: !!navigator?.clipboard?.writeText,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -332,6 +390,9 @@ export default {
|
|||
},
|
||||
mounted: fetchRelease,
|
||||
methods: {
|
||||
copySummary,
|
||||
selectSummary,
|
||||
setSummary,
|
||||
fetchRelease,
|
||||
stashScene,
|
||||
unstashScene,
|
||||
|
@ -502,6 +563,18 @@ export default {
|
|||
margin: 0 0 -.15rem .1rem;
|
||||
}
|
||||
|
||||
.summary {
|
||||
display: flex;
|
||||
|
||||
.input {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
display: inline-flex;
|
||||
color: var(--link);
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<div class="dialog-section">
|
||||
<h3 class="form-heading">Show me</h3>
|
||||
|
||||
<ul class="tags nolist">
|
||||
<li
|
||||
v-for="tag in tags"
|
||||
:key="tag"
|
||||
class="tags-item"
|
||||
>
|
||||
<Checkbox
|
||||
:checked="!tagFilter.includes(tag)"
|
||||
:label="tag"
|
||||
class="tag"
|
||||
@change="(state) => filterTag(tag, state)"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p class="disclaimer">You may still incidentally see filtered out content</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Checkbox from '../form/checkbox.vue';
|
||||
|
||||
function tagFilter() {
|
||||
return this.$store.state.ui.tagFilter;
|
||||
}
|
||||
|
||||
function filterTag(tag, isChecked) {
|
||||
if (isChecked) {
|
||||
this.$store.dispatch('setTagFilter', this.tagFilter.filter((filteredTag) => filteredTag !== tag));
|
||||
} else {
|
||||
this.$store.dispatch('setTagFilter', this.tagFilter.concat(tag));
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Checkbox,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tags: ['anal', 'gay', 'transsexual', 'bisexual', 'pissing', 'anal prolapse'],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
tagFilter,
|
||||
},
|
||||
methods: {
|
||||
filterTag,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dialog-body {
|
||||
width: 40rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.filters {
|
||||
width: 20rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.tags-item {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: .5rem 0;
|
||||
}
|
||||
|
||||
.disclaimer {
|
||||
margin: 1rem 0 0 0;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
font-size: .9rem;
|
||||
color: var(--shadow);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,81 @@
|
|||
<template>
|
||||
<Dialog
|
||||
title="Settings"
|
||||
@close="$emit('close')"
|
||||
>
|
||||
<nav class="tabs">
|
||||
<button
|
||||
class="tab"
|
||||
:class="{ selected: section === 'filters' }"
|
||||
@click="section = 'filters'"
|
||||
>Filters</button>
|
||||
|
||||
<button
|
||||
class="tab"
|
||||
:class="{ selected: section === 'summary' }"
|
||||
@click="section = 'summary'"
|
||||
>Summary</button>
|
||||
</nav>
|
||||
|
||||
<div class="dialog-body">
|
||||
<component :is="sections[section]" />
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { shallowRef } from 'vue';
|
||||
|
||||
import Filters from './filters.vue';
|
||||
import Summary from './summary.vue';
|
||||
|
||||
export default {
|
||||
emits: ['close'],
|
||||
data() {
|
||||
return {
|
||||
sections: {
|
||||
filters: shallowRef(Filters),
|
||||
summary: shallowRef(Summary),
|
||||
},
|
||||
section: 'filters',
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dialog-body {
|
||||
width: 40rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tab {
|
||||
flex-grow: 1;
|
||||
padding: .75rem 1rem;
|
||||
background: var(--shadow-touch);
|
||||
border: none;
|
||||
border-bottom: solid 1px var(--shadow-hint);
|
||||
color: var(--shadow);
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
|
||||
&:not(:first-child) {
|
||||
border-left: solid 1px var(--shadow-hint);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: none;
|
||||
color: var(--primary);
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--shadow-strong);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,327 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="dialog-section">
|
||||
<h3 class="form-heading">Summary format</h3>
|
||||
|
||||
<ul class="summary nolist">
|
||||
<li
|
||||
v-for="(group, groupIndex) in summaryFormat"
|
||||
:key="groupIndex"
|
||||
class="summary-group"
|
||||
>
|
||||
<div class="summary-options">
|
||||
<label class="summary-option">Delimiter
|
||||
<input
|
||||
class="input summary-delimiter"
|
||||
:value="group.delimiter"
|
||||
@change="setSummaryGroupValue(groupIndex, 'delimiter', $event.target.value)"
|
||||
>
|
||||
</label>
|
||||
|
||||
<label class="summary-option">Brackets
|
||||
<select
|
||||
class="select summary-delimiter"
|
||||
:value="group.bracket"
|
||||
@change="setSummaryGroupValue(groupIndex, 'bracket', $event.target.value)"
|
||||
>
|
||||
<option
|
||||
v-for="bracket in brackets"
|
||||
:key="bracket"
|
||||
>{{ bracket }}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<ul class="summary-segments nolist">
|
||||
<li
|
||||
v-for="(segment, segmentIndex) in group.segments"
|
||||
:key="segmentIndex"
|
||||
class="summary-segment"
|
||||
>
|
||||
<select
|
||||
class="select summary-prop"
|
||||
:value="segment.prop"
|
||||
@change="setSummarySegmentValue(groupIndex, segmentIndex, 'prop', $event.target.value)"
|
||||
>
|
||||
<option>channel</option>
|
||||
<option>network</option>
|
||||
<option>title</option>
|
||||
<option>movie</option>
|
||||
<option>tags</option>
|
||||
<option>actors</option>
|
||||
<option>date</option>
|
||||
</select>
|
||||
|
||||
<input
|
||||
v-if="delimitedProps.includes(segment.prop)"
|
||||
class="input summary-delimiter"
|
||||
:value="segment.delimiter"
|
||||
@change="setSummarySegmentValue(groupIndex, segmentIndex, 'delimiter', $event.target.value)"
|
||||
>
|
||||
|
||||
<select
|
||||
v-if="segment.prop === 'date'"
|
||||
class="select summary-format"
|
||||
:value="segment.format"
|
||||
@change="setSummarySegmentValue(groupIndex, segmentIndex, 'format', $event.target.value)"
|
||||
>
|
||||
<option>YYYY-MM-DD</option>
|
||||
<option>DD-MM-YYYY</option>
|
||||
<option>MM/DD/YYYY</option>
|
||||
</select>
|
||||
|
||||
<Icon
|
||||
icon="bin"
|
||||
class="active"
|
||||
@click="removeSummarySegment(groupIndex, segmentIndex)"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<li class="summary-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="button button-secondary"
|
||||
@click="addSummarySegment(groupIndex)"
|
||||
>Add segment</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="button button-secondary"
|
||||
@click="removeSummaryGroup(groupIndex)"
|
||||
>Remove group</button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="summary summary-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="button button-secondary"
|
||||
@click="addSummaryGroup"
|
||||
>Add group</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="button button-secondary"
|
||||
@click="resetSummaryFormat"
|
||||
>Reset to default</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="dialog-section">
|
||||
<h3 class="form-heading">Preview</h3>
|
||||
|
||||
<input
|
||||
class="input summary-preview"
|
||||
:value="summary"
|
||||
:title="summary"
|
||||
disabled
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import formatSummary from '../../js/utils/format-summary';
|
||||
|
||||
function summary() {
|
||||
return formatSummary(this.scene, this.summaryFormat);
|
||||
}
|
||||
|
||||
function summaryFormat() {
|
||||
return this.$store.state.ui.summaryFormat;
|
||||
}
|
||||
|
||||
function setSummaryGroupValue(targetGroupIndex, target, value) {
|
||||
const newFormat = this.summaryFormat.map((group, groupIndex) => {
|
||||
if (groupIndex === targetGroupIndex) {
|
||||
return {
|
||||
...group,
|
||||
[target]: value,
|
||||
};
|
||||
}
|
||||
|
||||
return group;
|
||||
});
|
||||
|
||||
this.$store.dispatch('setSummaryFormat', newFormat);
|
||||
}
|
||||
|
||||
function setSummarySegmentValue(targetGroupIndex, targetSegmentIndex, target, value) {
|
||||
const newFormat = this.summaryFormat.map((group, groupIndex) => {
|
||||
if (groupIndex === targetGroupIndex) {
|
||||
return {
|
||||
...group,
|
||||
segments: group.segments.map((segment, segmentIndex) => {
|
||||
if (segmentIndex === targetSegmentIndex) {
|
||||
return {
|
||||
...segment,
|
||||
[target]: value,
|
||||
};
|
||||
}
|
||||
|
||||
return segment;
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return group;
|
||||
});
|
||||
|
||||
this.$store.dispatch('setSummaryFormat', newFormat);
|
||||
}
|
||||
|
||||
function addSummarySegment(targetGroupIndex) {
|
||||
const newFormat = this.summaryFormat.map((group, groupIndex) => {
|
||||
if (groupIndex === targetGroupIndex) {
|
||||
return {
|
||||
...group,
|
||||
segments: group.segments.concat({
|
||||
prop: 'title',
|
||||
delimiter: ',', // default delimiter for when prop is changed to iterabte like actors or tags
|
||||
format: 'YYYY-MM-DD', // default format for when prop is changed to date
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return group;
|
||||
});
|
||||
|
||||
this.$store.dispatch('setSummaryFormat', newFormat);
|
||||
}
|
||||
|
||||
function addSummaryGroup() {
|
||||
const newFormat = this.summaryFormat.concat({
|
||||
delimiter: ' - ',
|
||||
segments: [{ prop: 'title' }],
|
||||
});
|
||||
|
||||
this.$store.dispatch('setSummaryFormat', newFormat);
|
||||
}
|
||||
|
||||
function removeSummaryGroup(groupIndex) {
|
||||
const newFormat = this.summaryFormat.filter((group, index) => index !== groupIndex);
|
||||
|
||||
this.$store.dispatch('setSummaryFormat', newFormat);
|
||||
}
|
||||
|
||||
function removeSummarySegment(targetGroupIndex, targetSegmentIndex) {
|
||||
const newFormat = this.summaryFormat.map((group, groupIndex) => ({
|
||||
...group,
|
||||
segments: groupIndex === targetGroupIndex
|
||||
? group.segments.filter((segment, index) => index !== targetSegmentIndex)
|
||||
: group.segments,
|
||||
}));
|
||||
|
||||
this.$store.dispatch('setSummaryFormat', newFormat);
|
||||
}
|
||||
|
||||
function resetSummaryFormat() {
|
||||
this.$store.dispatch('setSummaryFormat', this.$store.state.ui.defaultSummaryFormat);
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
delimiters: [null, '-', '_', ',', '.'],
|
||||
brackets: [null, '()', '[]', '{}', '<>'],
|
||||
delimitedProps: ['actors', 'tags'],
|
||||
scene: {
|
||||
channel: 'Channel',
|
||||
network: 'Network',
|
||||
title: 'Title',
|
||||
movie: 'Movie',
|
||||
scene: 1,
|
||||
actors: ['Jane Doe', 'John Doe'],
|
||||
date: new Date(),
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
summary,
|
||||
summaryFormat,
|
||||
},
|
||||
methods: {
|
||||
addSummaryGroup,
|
||||
setSummaryGroupValue,
|
||||
addSummarySegment,
|
||||
setSummarySegmentValue,
|
||||
removeSummaryGroup,
|
||||
removeSummarySegment,
|
||||
resetSummaryFormat,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.summary {
|
||||
.icon {
|
||||
padding: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.summary-group,
|
||||
.summary-segment {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.summary-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: .5rem;
|
||||
|
||||
.button {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
padding-bottom: .5rem;
|
||||
border-bottom: solid 1px var(--shadow-hint);
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.summary-segments {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.summary-segment {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: .25rem;
|
||||
}
|
||||
|
||||
.summary-delimiter {
|
||||
width: 5rem;
|
||||
}
|
||||
|
||||
.summary-format {
|
||||
width: 9rem;
|
||||
}
|
||||
|
||||
.summary-prop {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.summary-options {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
.summary-option:not(:last-child) {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.summary-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.summary-preview {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
</style>
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div
|
||||
class="sidebar-container"
|
||||
@click="$emit('toggleSidebar', false)"
|
||||
@click="events.emit('toggleSidebar', false)"
|
||||
>
|
||||
<div
|
||||
class="sidebar"
|
||||
|
@ -9,10 +9,10 @@
|
|||
>
|
||||
<div class="sidebar-section">
|
||||
<div class="sidebar-header">
|
||||
<router-link
|
||||
<RouterLink
|
||||
to="/updates"
|
||||
class="logo-link"
|
||||
@click="$emit('toggleSidebar', false)"
|
||||
@click="events.emit('toggleSidebar', false)"
|
||||
>
|
||||
<h1 class="sidebar-logo">
|
||||
<div
|
||||
|
@ -20,27 +20,27 @@
|
|||
v-html="logo"
|
||||
/>
|
||||
</h1>
|
||||
</router-link>
|
||||
</RouterLink>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="sidebar-close noselect"
|
||||
@click.native="$emit('toggleSidebar', false)"
|
||||
@click.native="events.emit('toggleSidebar', false)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Search
|
||||
class="search"
|
||||
@search="$emit('toggleSidebar', false)"
|
||||
@search="events.emit('toggleSidebar', false)"
|
||||
/>
|
||||
|
||||
<nav class="nav">
|
||||
<ul class="nolist">
|
||||
<li
|
||||
class="nav-item"
|
||||
@click="$emit('toggleSidebar', false)"
|
||||
@click="events.emit('toggleSidebar', false)"
|
||||
>
|
||||
<router-link
|
||||
<RouterLink
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
to="/updates"
|
||||
custom
|
||||
|
@ -51,14 +51,14 @@
|
|||
:class="{ active: isActive }"
|
||||
@click="navigate"
|
||||
>Home</a>
|
||||
</router-link>
|
||||
</RouterLink>
|
||||
</li>
|
||||
|
||||
<li
|
||||
class="nav-item"
|
||||
@click="$emit('toggleSidebar', false)"
|
||||
@click="events.emit('toggleSidebar', false)"
|
||||
>
|
||||
<router-link
|
||||
<RouterLink
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
to="/actors"
|
||||
custom
|
||||
|
@ -69,14 +69,14 @@
|
|||
:class="{ active: isActive }"
|
||||
@click="navigate"
|
||||
>Actors</a>
|
||||
</router-link>
|
||||
</RouterLink>
|
||||
</li>
|
||||
|
||||
<li
|
||||
class="nav-item"
|
||||
@click="$emit('toggleSidebar', false)"
|
||||
@click="events.emit('toggleSidebar', false)"
|
||||
>
|
||||
<router-link
|
||||
<RouterLink
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
to="/channels"
|
||||
custom
|
||||
|
@ -87,14 +87,14 @@
|
|||
:class="{ active: isActive }"
|
||||
@click="navigate"
|
||||
>Channels</a>
|
||||
</router-link>
|
||||
</RouterLink>
|
||||
</li>
|
||||
|
||||
<li
|
||||
class="nav-item"
|
||||
@click="$emit('toggleSidebar', false)"
|
||||
@click="events.emit('toggleSidebar', false)"
|
||||
>
|
||||
<router-link
|
||||
<RouterLink
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
to="/movies"
|
||||
custom
|
||||
|
@ -105,14 +105,14 @@
|
|||
:class="{ active: isActive }"
|
||||
@click="navigate"
|
||||
>Movies</a>
|
||||
</router-link>
|
||||
</RouterLink>
|
||||
</li>
|
||||
|
||||
<li
|
||||
class="nav-item"
|
||||
@click="$emit('toggleSidebar', false)"
|
||||
@click="events.emit('toggleSidebar', false)"
|
||||
>
|
||||
<router-link
|
||||
<RouterLink
|
||||
v-slot="{ href, isActive, navigate }"
|
||||
to="/tags"
|
||||
custom
|
||||
|
@ -123,7 +123,7 @@
|
|||
:class="{ active: isActive }"
|
||||
@click="navigate"
|
||||
>Tags</a>
|
||||
</router-link>
|
||||
</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
@ -132,23 +132,23 @@
|
|||
<div class="sidebar-section controls noselect">
|
||||
<label
|
||||
v-if="login && me"
|
||||
@click="$emit('toggleSidebar', false)"
|
||||
@click="events.emit('toggleSidebar', false)"
|
||||
>
|
||||
<router-link
|
||||
<RouterLink
|
||||
:to="{ name: 'user', params: { username: me.username } }"
|
||||
class="toggle username nolink"
|
||||
>{{ me.username }}</router-link>
|
||||
>{{ me.username }}</RouterLink>
|
||||
</label>
|
||||
|
||||
<div class="toggles noselect">
|
||||
<label
|
||||
v-if="login && !me"
|
||||
@click="$emit('toggleSidebar', false)"
|
||||
@click="events.emit('toggleSidebar', false)"
|
||||
>
|
||||
<router-link
|
||||
<RouterLink
|
||||
:to="{ name: 'login', query: { ref: $route.path } }"
|
||||
class="toggle nolink"
|
||||
><Icon icon="enter2" />Log in</router-link>
|
||||
><Icon icon="enter2" />Log in</RouterLink>
|
||||
</label>
|
||||
|
||||
<label
|
||||
|
@ -183,8 +183,8 @@
|
|||
|
||||
<label
|
||||
class="toggle"
|
||||
@click="$emit('showFilters', true)"
|
||||
><Icon icon="filter" />Filters</label>
|
||||
@click="events.emit('toggleSettings', true)"
|
||||
><Icon icon="cog" />Settings</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -230,7 +230,6 @@ export default {
|
|||
components: {
|
||||
Search,
|
||||
},
|
||||
emits: ['toggleSidebar', 'showFilters'],
|
||||
data() {
|
||||
return {
|
||||
logo,
|
||||
|
|
|
@ -54,10 +54,15 @@
|
|||
.button-secondary {
|
||||
color: var(--primary);
|
||||
|
||||
&:hover {
|
||||
color: var(--highlight-strong);
|
||||
&:hover:not(:disabled) {
|
||||
color: var(--text-light);
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: var(--shadow-strong);
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.album-toggle {
|
||||
|
|
|
@ -10,6 +10,11 @@ function initUiActions(store, _router) {
|
|||
localStorage.setItem('tagFilter', tagFilter);
|
||||
}
|
||||
|
||||
function setSummaryFormat({ commit }, summaryFormat) {
|
||||
commit('setSummaryFormat', summaryFormat);
|
||||
localStorage.setItem('summaryFormat', JSON.stringify(summaryFormat));
|
||||
}
|
||||
|
||||
function setRange({ commit }, range) {
|
||||
commit('setRange', range);
|
||||
}
|
||||
|
@ -309,6 +314,7 @@ function initUiActions(store, _router) {
|
|||
checkNotifications,
|
||||
search,
|
||||
setTagFilter,
|
||||
setSummaryFormat,
|
||||
setRange,
|
||||
setBatch,
|
||||
setSfw,
|
||||
|
|
|
@ -2,6 +2,10 @@ function setTagFilter(state, tagFilter) {
|
|||
state.tagFilter = tagFilter;
|
||||
}
|
||||
|
||||
function setSummaryFormat(state, summaryFormat) {
|
||||
state.summaryFormat = summaryFormat;
|
||||
}
|
||||
|
||||
function setRange(state, range) {
|
||||
state.range = range;
|
||||
}
|
||||
|
@ -20,6 +24,7 @@ function setTheme(state, theme) {
|
|||
|
||||
export default {
|
||||
setTagFilter,
|
||||
setSummaryFormat,
|
||||
setRange,
|
||||
setBatch,
|
||||
setSfw,
|
||||
|
|
|
@ -1,12 +1,43 @@
|
|||
const storedTagFilter = localStorage.getItem('tagFilter');
|
||||
const storedSummaryFormat = localStorage.getItem('summaryFormat');
|
||||
const storedBatch = localStorage.getItem('batch');
|
||||
const storedSfw = localStorage.getItem('sfw');
|
||||
const storedTheme = localStorage.getItem('theme');
|
||||
|
||||
const deviceTheme = window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
|
||||
const defaultSummaryFormat = [
|
||||
{
|
||||
delimiter: ' - ',
|
||||
segments: [
|
||||
{
|
||||
prop: 'channel',
|
||||
},
|
||||
{
|
||||
prop: 'title',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
delimiter: ', ',
|
||||
bracket: '()',
|
||||
segments: [
|
||||
{
|
||||
prop: 'actors',
|
||||
delimiter: ', ',
|
||||
},
|
||||
{
|
||||
prop: 'date',
|
||||
format: 'DD-MM-YYYY',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
tagFilter: storedTagFilter ? storedTagFilter.split(',') : [],
|
||||
summaryFormat: storedSummaryFormat ? JSON.parse(storedSummaryFormat) : defaultSummaryFormat,
|
||||
defaultSummaryFormat,
|
||||
range: 'latest',
|
||||
batch: storedBatch || 'all',
|
||||
sfw: storedSfw === 'true' || false,
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import { formatDate } from '../format';
|
||||
|
||||
function formatSummary(release, summaryFormat) {
|
||||
return summaryFormat
|
||||
.map((group) => ({
|
||||
...group,
|
||||
segments: group.segments
|
||||
.filter((segment) => {
|
||||
if (!release[segment.prop]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(release[segment.prop]) && release[segment.prop].length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((segment) => {
|
||||
if (Array.isArray(release[segment.prop])) {
|
||||
return release[segment.prop].join(segment.delimiter || ', ');
|
||||
}
|
||||
|
||||
if (segment.prop === 'date') {
|
||||
return formatDate(release[segment.prop], segment.format || 'YYYY-MM-DD');
|
||||
}
|
||||
|
||||
return release[segment.prop];
|
||||
}),
|
||||
}))
|
||||
.filter((group) => group.segments.length > 0)
|
||||
.map((group) => `${group.bracket?.[0] || ''}${group.segments.join(group.delimiter || ' - ')}${group.bracket?.[1] || ''}`)
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
export default formatSummary;
|
|
@ -419,7 +419,7 @@ async function scrapeScene({ query }, url, channel, baseRelease, mobileItem, opt
|
|||
return release;
|
||||
}
|
||||
|
||||
async function scrapeReleaseApi(data, site, options) {
|
||||
async function scrapeReleaseApi(data, site, options, movieScenes) {
|
||||
const release = {};
|
||||
|
||||
release.entryId = data.clip_id || data.movie_id;
|
||||
|
@ -470,6 +470,10 @@ async function scrapeReleaseApi(data, site, options) {
|
|||
};
|
||||
}
|
||||
|
||||
if (movieScenes?.length > 0) {
|
||||
release.scenes = await Promise.all(movieScenes.map((movieScene) => scrapeReleaseApi(movieScene, site, options)));
|
||||
}
|
||||
|
||||
release.channel = data.sitename;
|
||||
release.qualities = data.download_sizes;
|
||||
|
||||
|
@ -706,10 +710,16 @@ async function fetchMovieApi(url, site, baseRelease, options) {
|
|||
indexName: 'all_movies',
|
||||
params: `query=&page=0&facets=[]&tagFilters=&facetFilters=[["movie_id:${entryId}"]]`,
|
||||
},
|
||||
{
|
||||
indexName: 'all_scenes_latest_desc',
|
||||
params: `query=&page=0&facets=[]&tagFilters=&facetFilters=[["movie_id:${entryId}"]]`,
|
||||
},
|
||||
/*
|
||||
{
|
||||
indexName: 'all_movies',
|
||||
params: 'query=&page=0&hitsPerPage=1&attributesToRetrieve=[]&attributesToHighlight=[]&attributesToSnippet=[]&tagFilters=&analytics=false&clickAnalytics=false&facets=clip_id',
|
||||
},
|
||||
*/
|
||||
],
|
||||
}, {
|
||||
headers: {
|
||||
|
@ -720,7 +730,7 @@ async function fetchMovieApi(url, site, baseRelease, options) {
|
|||
});
|
||||
|
||||
if (res.status === 200 && res.body.results?.[0]?.hits.length > 0) {
|
||||
return scrapeReleaseApi(res.body.results[0].hits[0], site, options);
|
||||
return scrapeReleaseApi(res.body.results[0].hits[0], site, options, res.body.results[1]?.hits);
|
||||
}
|
||||
|
||||
if (res.status === 200) {
|
||||
|
|
Loading…
Reference in New Issue