Generalized filters bar, added to network page.

This commit is contained in:
ThePendulum 2019-11-15 02:37:17 +01:00
parent 23492bb5d0
commit 0575dbc7e4
17 changed files with 193 additions and 113 deletions

View File

@ -7,7 +7,7 @@
:checked="range === 'all'" :checked="range === 'all'"
type="radio" type="radio"
class="range-input" class="range-input"
@click="$emit('set-range', 'all')" @click="setRange('all')"
> >
<label <label
:for="`${_uid}-all`" :for="`${_uid}-all`"
@ -21,7 +21,7 @@
:checked="range === 'new'" :checked="range === 'new'"
type="radio" type="radio"
class="range-input" class="range-input"
@click="$emit('set-range', 'new')" @click="setRange('new')"
> >
<label <label
:for="`${_uid}-new`" :for="`${_uid}-new`"
@ -35,7 +35,7 @@
:checked="range === 'upcoming'" :checked="range === 'upcoming'"
type="radio" type="radio"
class="range-input" class="range-input"
@click="$emit('set-range', 'upcoming')" @click="setRange('upcoming')"
> >
<label <label
:for="`${_uid}-upcoming`" :for="`${_uid}-upcoming`"
@ -50,7 +50,7 @@
<Filters <Filters
class="filters-container" class="filters-container"
:filter="filter" :filter="filter"
@set-filter="filter => $emit('set-filter', filter)" @set-filter="setFilter"
/> />
<v-popover class="filters-compact"> <v-popover class="filters-compact">
@ -60,7 +60,7 @@
<Filters <Filters
:compact="true" :compact="true"
:filter="filter" :filter="filter"
@set-filter="filter => $emit('set-filter', filter)" @set-filter="setFilter"
/> />
</div> </div>
</v-popover> </v-popover>
@ -69,22 +69,49 @@
</template> </template>
<script> <script>
import { mapState } from 'vuex';
import Filters from './filters.vue'; import Filters from './filters.vue';
function filter(state) {
return state.ui.filter;
}
function range(state) {
return state.ui.range;
}
async function setFilter(newFilter) {
this.$store.dispatch('setFilter', newFilter);
await this.fetchReleases();
}
async function setRange(newRange) {
this.$store.dispatch('setRange', newRange);
await this.fetchReleases();
}
export default { export default {
components: { components: {
Filters, Filters,
}, },
props: { props: {
filter: { fetchReleases: {
type: Array, type: Function,
default: () => [],
},
range: {
type: String,
default: null, default: null,
}, },
}, },
computed: {
...mapState({
filter,
range,
}),
},
methods: {
setFilter,
setRange,
},
}; };
</script> </script>

View File

@ -1,11 +1,6 @@
<template> <template>
<div class="content"> <div class="content">
<FilterBar <FilterBar :fetch-releases="fetchReleases" />
:filter="filter"
:range="range"
@set-filter="setFilter"
@set-range="setRange"
/>
<div class="content-inner"> <div class="content-inner">
<ul class="scenes nolist"> <ul class="scenes nolist">
@ -22,30 +17,11 @@
</template> </template>
<script> <script>
import FilterBar from './filter-bar.vue'; import FilterBar from '../header/filter-bar.vue';
import ReleaseTile from '../tile/release.vue'; import ReleaseTile from '../tile/release.vue';
import rangeDates from '../../js/range-dates';
async function fetchReleases() { async function fetchReleases() {
this.releases = await this.$store.dispatch('fetchReleases', { this.releases = await this.$store.dispatch('fetchReleases');
filter: this.filter,
...rangeDates(this.range),
});
}
async function setFilter(filter) {
this.filter = filter;
localStorage.setItem('filter', this.filter);
await this.fetchReleases();
}
async function setRange(range) {
this.range = range;
localStorage.setItem('range', this.range);
await this.fetchReleases();
} }
async function mounted() { async function mounted() {
@ -60,12 +36,7 @@ export default {
ReleaseTile, ReleaseTile,
}, },
data() { data() {
const storedFilter = localStorage.getItem('filter');
const storedRange = localStorage.getItem('range');
return { return {
filter: storedFilter ? storedFilter.split(',') : ['gay', 'transsexual'],
range: storedRange || 'new',
releases: [], releases: [],
networks: [], networks: [],
pageTitle: null, pageTitle: null,
@ -74,8 +45,6 @@ export default {
mounted, mounted,
methods: { methods: {
fetchReleases, fetchReleases,
setFilter,
setRange,
}, },
}; };
</script> </script>

View File

@ -3,6 +3,8 @@
v-if="network" v-if="network"
class="content network" class="content network"
> >
<FilterBar :fetch-releases="fetchReleases" />
<div class="content-inner"> <div class="content-inner">
<div class="header"> <div class="header">
<a <a
@ -54,12 +56,19 @@
</template> </template>
<script> <script>
import FilterBar from '../header/filter-bar.vue';
import ReleaseTile from '../tile/release.vue'; import ReleaseTile from '../tile/release.vue';
import SiteTile from '../tile/site.vue'; import SiteTile from '../tile/site.vue';
async function mounted() { async function fetchReleases() {
[this.network] = await this.$store.dispatch('fetchNetworks', this.$route.params.networkSlug);
this.releases = await this.$store.dispatch('fetchNetworkReleases', this.$route.params.networkSlug); this.releases = await this.$store.dispatch('fetchNetworkReleases', this.$route.params.networkSlug);
}
async function mounted() {
[[this.network]] = await Promise.all([
this.$store.dispatch('fetchNetworks', this.$route.params.networkSlug),
this.fetchReleases(),
]);
this.sites = this.network.sites this.sites = this.network.sites
.filter(site => !site.independent) .filter(site => !site.independent)
@ -70,6 +79,7 @@ async function mounted() {
export default { export default {
components: { components: {
FilterBar,
ReleaseTile, ReleaseTile,
SiteTile, SiteTile,
}, },
@ -82,6 +92,9 @@ export default {
}; };
}, },
mounted, mounted,
methods: {
fetchReleases,
},
}; };
</script> </script>

View File

@ -207,7 +207,7 @@ function pageTitle() {
} }
async function mounted() { async function mounted() {
this.release = await this.$store.dispatch('fetchReleases', { id: this.$route.params.releaseId }); this.release = await this.$store.dispatch('fetchReleaseById', this.$route.params.releaseId);
} }
export default { export default {

View File

@ -1,14 +1,20 @@
import { get } from '../api'; import { get } from '../api';
function initNetworksActions(_store, _router) { function initNetworksActions(store, _router) {
async function fetchNetworks({ _commit }, networkId) { async function fetchNetworks({ _commit }, networkId) {
const networks = await get(`/networks/${networkId || ''}`); const networks = await get(`/networks/${networkId || ''}`, {
});
return networks; return networks;
} }
async function fetchNetworkReleases({ _commit }, networkId) { async function fetchNetworkReleases({ _commit }, networkId) {
const releases = await get(`/networks/${networkId}/releases`); const releases = await get(`/networks/${networkId}/releases`, {
filter: store.state.ui.filter,
after: store.getters.after,
before: store.getters.before,
});
return releases; return releases;
} }

View File

@ -1,18 +0,0 @@
function rangeDates(range) {
return ({
new: () => ({
after: new Date(0),
before: new Date(),
}),
upcoming: () => ({
after: new Date(),
before: new Date(2 ** 42),
}),
all: () => ({
after: new Date(0),
before: new Date(2 ** 42),
}),
})[range]();
}
export default rangeDates;

View File

@ -1,28 +1,25 @@
import dayjs from 'dayjs';
import { get } from '../api'; import { get } from '../api';
function initReleasesActions(_store, _router) { function initReleasesActions(store, _router) {
async function fetchReleases({ _commit }, { async function fetchReleases({ _commit }) {
id, const releases = await get('/releases', {
filter, filter: store.state.ui.filter,
after, after: store.getters.after,
before, before: store.getters.before,
}) {
const afterString = dayjs(after).format('YYYY-MM-DD');
const beforeString = dayjs(before).format('YYYY-MM-DD');
const releases = await get(`/releases/${id || ''}`, {
filter,
after: afterString,
before: beforeString,
}); });
return releases; return releases;
} }
async function fetchReleaseById({ _commit }, releaseId) {
const release = await get(`/releases/${releaseId}`);
return release;
}
return { return {
fetchReleases, fetchReleases,
fetchReleaseById,
}; };
} }

View File

@ -1,6 +1,7 @@
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import initUiStore from './ui/ui';
import initAuthStore from './auth/auth'; import initAuthStore from './auth/auth';
import initReleasesStore from './releases/releases'; import initReleasesStore from './releases/releases';
import initSitesStore from './sites/sites'; import initSitesStore from './sites/sites';
@ -13,6 +14,7 @@ function initStore(router) {
const store = new Vuex.Store(); const store = new Vuex.Store();
store.registerModule('ui', initUiStore(store, router));
store.registerModule('auth', initAuthStore(store, router)); store.registerModule('auth', initAuthStore(store, router));
store.registerModule('releases', initReleasesStore(store, router)); store.registerModule('releases', initReleasesStore(store, router));
store.registerModule('actors', initActorsStore(store, router)); store.registerModule('actors', initActorsStore(store, router));

20
assets/js/ui/actions.js Normal file
View File

@ -0,0 +1,20 @@
// import { get } from '../api';
function initUiActions(_store, _router) {
function setFilter({ commit }, filter) {
commit('setFilter', filter);
localStorage.setItem('filter', filter);
}
function setRange({ commit }, range) {
commit('setRange', range);
localStorage.setItem('range', range);
}
return {
setFilter,
setRange,
};
}
export default initUiActions;

34
assets/js/ui/getters.js Normal file
View File

@ -0,0 +1,34 @@
import dayjs from 'dayjs';
const dateRanges = {
new: () => ({
after: dayjs(new Date(0)).format('YYYY-MM-DD'),
before: dayjs(new Date()).format('YYYY-MM-DD'),
}),
upcoming: () => ({
after: dayjs(new Date()).format('YYYY-MM-DD'),
before: dayjs(new Date(2 ** 42)).format('YYYY-MM-DD'),
}),
all: () => ({
after: dayjs(new Date(0)).format('YYYY-MM-DD'),
before: dayjs(new Date(2 ** 42)).format('YYYY-MM-DD'),
}),
};
function rangeDates(state) {
return dateRanges[state.range]();
}
function before(state) {
return dateRanges[state.range]().before;
}
function after(state) {
return dateRanges[state.range]().after;
}
export default {
rangeDates,
before,
after,
};

12
assets/js/ui/mutations.js Normal file
View File

@ -0,0 +1,12 @@
function setFilter(state, filter) {
state.filter = filter;
}
function setRange(state, range) {
state.range = range;
}
export default {
setFilter,
setRange,
};

7
assets/js/ui/state.js Normal file
View File

@ -0,0 +1,7 @@
const storedFilter = localStorage.getItem('filter');
const storedRange = localStorage.getItem('range');
export default {
filter: storedFilter ? storedFilter.split(',') : ['gay', 'transsexual'],
range: storedRange || 'new',
};

15
assets/js/ui/ui.js Normal file
View File

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

View File

@ -1,24 +1,24 @@
/* $primary: #ff886c; */ /* $primary: #ff886c; */
.filters[data-v-643133a6] { .filters[data-v-e35db0d8] {
display: inline-block; display: inline-block;
list-style: none; list-style: none;
padding: .5rem; padding: .5rem;
margin: 0; margin: 0;
} }
.filters[data-v-643133a6]:not(:last-child) { .filters[data-v-e35db0d8]:not(:last-child) {
border-right: solid 1px rgba(0, 0, 0, 0.1); border-right: solid 1px rgba(0, 0, 0, 0.1);
} }
.filter[data-v-643133a6] { .filter[data-v-e35db0d8] {
display: inline-block; display: inline-block;
} }
.compact .filters[data-v-643133a6] { .compact .filters[data-v-e35db0d8] {
padding: 0; padding: 0;
border: none; border: none;
} }
.compact .filter[data-v-643133a6] { .compact .filter[data-v-e35db0d8] {
margin: 0 0 1.5rem 0; margin: 0 0 1.5rem 0;
} }
.toggle[data-v-643133a6] { .toggle[data-v-e35db0d8] {
color: rgba(0, 0, 0, 0.2); color: rgba(0, 0, 0, 0.2);
box-sizing: border-box; box-sizing: border-box;
padding: .5rem; padding: .5rem;
@ -28,19 +28,19 @@
font-weight: bold; font-weight: bold;
cursor: pointer; cursor: pointer;
} }
.toggle .check[data-v-643133a6] { .toggle .check[data-v-e35db0d8] {
display: none; display: none;
} }
.toggle[data-v-643133a6]:hover { .toggle[data-v-e35db0d8]:hover {
color: rgba(0, 0, 0, 0.5); color: rgba(0, 0, 0, 0.5);
} }
.toggle.active[data-v-643133a6] { .toggle.active[data-v-e35db0d8] {
color: #ff6c88; color: #ff6c88;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2); box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
} }
/* $primary: #ff886c; */ /* $primary: #ff886c; */
.filter-bar[data-v-a678373a] { .filter-bar[data-v-6db17c96] {
background: #fff; background: #fff;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -48,19 +48,19 @@
font-size: 0; font-size: 0;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5); box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
} }
.filter-bar .icon[data-v-a678373a] { .filter-bar .icon[data-v-6db17c96] {
fill: rgba(0, 0, 0, 0.5); fill: rgba(0, 0, 0, 0.5);
} }
.filters-container[data-v-a678373a] { .filters-container[data-v-6db17c96] {
display: inline-block; display: inline-block;
} }
.filters-compact[data-v-a678373a] { .filters-compact[data-v-6db17c96] {
font-size: 1rem; font-size: 1rem;
font-weight: bold; font-weight: bold;
display: none; display: none;
margin: 0 0 0 .5rem; margin: 0 0 0 .5rem;
} }
.range-button[data-v-a678373a] { .range-button[data-v-6db17c96] {
color: rgba(0, 0, 0, 0.5); color: rgba(0, 0, 0, 0.5);
background: #fff; background: #fff;
display: inline-block; display: inline-block;
@ -70,21 +70,21 @@
font-size: .8rem; font-size: .8rem;
font-weight: bold; font-weight: bold;
} }
.range-button[data-v-a678373a]:hover { .range-button[data-v-6db17c96]:hover {
color: #222; color: #222;
cursor: pointer; cursor: pointer;
} }
.range-input[data-v-a678373a] { .range-input[data-v-6db17c96] {
display: none; display: none;
} }
.range-input:checked + .range-button[data-v-a678373a] { .range-input:checked + .range-button[data-v-6db17c96] {
color: #ff6c88; color: #ff6c88;
} }
@media (max-width: 720px) { @media (max-width: 720px) {
.filters-container[data-v-a678373a] { .filters-container[data-v-6db17c96] {
display: none; display: none;
} }
.filters-compact[data-v-a678373a] { .filters-compact[data-v-6db17c96] {
display: inline-block; display: inline-block;
} }
} }

View File

@ -77,6 +77,8 @@ function commonQuery(queryBuilder, {
before = new Date(2 ** 44), // May 2109 before = new Date(2 ** 44), // May 2109
limit = 100, limit = 100,
}) { }) {
const finalFilter = [].concat(filter); // ensure filter is array
queryBuilder queryBuilder
.leftJoin('sites', 'releases.site_id', 'sites.id') .leftJoin('sites', 'releases.site_id', 'sites.id')
.leftJoin('studios', 'releases.studio_id', 'studios.id') .leftJoin('studios', 'releases.studio_id', 'studios.id')
@ -93,7 +95,7 @@ function commonQuery(queryBuilder, {
.select('*') .select('*')
.from('tags_associated') .from('tags_associated')
.leftJoin('tags', 'tags_associated.tag_id', 'tags.id') .leftJoin('tags', 'tags_associated.tag_id', 'tags.id')
.whereIn('tags.slug', filter) .whereIn('tags.slug', finalFilter)
.andWhereRaw('tags_associated.release_id = releases.id'); .andWhereRaw('tags_associated.release_id = releases.id');
}) })
.andWhere('date', '>', after) .andWhere('date', '>', after)

View File

@ -9,13 +9,7 @@ const {
} = require('../releases'); } = require('../releases');
async function fetchReleasesApi(req, res) { async function fetchReleasesApi(req, res) {
const filter = req.query.filter ? [].concat(req.query.filter) : []; // don't filter for 'undefined' const releases = await fetchReleases({}, req.query);
const releases = await fetchReleases({}, {
filter,
after: req.query.after,
before: req.query.before,
});
res.send(releases); res.send(releases);
} }
@ -47,7 +41,7 @@ async function fetchNetworkReleasesApi(req, res) {
const releases = await fetchNetworkReleases({ const releases = await fetchNetworkReleases({
id: networkId, id: networkId,
slug: networkSlug, slug: networkSlug,
}); }, req.query);
res.send(releases); res.send(releases);
} }