Compare commits

..

No commits in common. "83ed793e398e5809adcfd930e26a7dc68426c89f" and "4806b0aa418078e77ece1492e07d10562d39018b" have entirely different histories.

20 changed files with 108 additions and 282 deletions

View File

@ -137,7 +137,7 @@ async function fetchEntity(scroll = true) {
this.pageTitle = entity.name;
if (scroll && this.$refs.filter?.$el) {
if (scroll) {
this.$refs.filter.$el.scrollIntoView();
}
}
@ -146,11 +146,9 @@ async function mounted() {
await this.fetchEntity();
}
async function route(to) {
if (to.name === 'channel' || to.name === 'network' || to.name === 'studio') {
await this.fetchEntity();
this.expanded = false;
}
async function route() {
await this.fetchEntity();
this.expanded = false;
}
export default {

View File

@ -85,22 +85,18 @@
<Tooltip v-if="me">
<div
class="header-button header-notifications"
:class="{ unseen: unseenNotificationsCount > 0 }"
:class="{ unseen: unseenNotifications.length > 0 }"
>
<Icon icon="bell2" />
<span
v-if="unseenNotificationsCount > 0"
v-if="unseenNotifications.length > 0"
class="notifications-count"
>{{ unseenNotificationsCount }}</span>
>{{ unseenNotifications.length }}</span>
</div>
<template v-slot:tooltip>
<Notifications
:notifications="notifications"
:unseen-count="unseenNotificationsCount"
@check="fetchNotifications"
/>
<Notifications />
</template>
</Tooltip>
@ -119,6 +115,8 @@
</template>
</Tooltip>
<Search class="search-full" />
<Tooltip
class="search-compact"
:open="searching"
@ -141,8 +139,6 @@
/>
</template>
</Tooltip>
<Search class="search-full" />
</div>
</header>
</template>
@ -158,11 +154,12 @@ function me() {
return this.$store.state.auth.user;
}
async function fetchNotifications() {
const { notifications, unseenCount } = await this.$store.dispatch('fetchNotifications');
function notifications() {
return this.$store.state.ui.notifications;
}
this.notifications = notifications;
this.unseenNotificationsCount = unseenCount;
function unseenNotifications() {
return this.$store.state.ui.notifications.filter(notification => !notification.seen);
}
export default {
@ -177,19 +174,12 @@ export default {
logo,
searching: false,
showFilters: false,
notifications: [],
unseenNotificationsCount: 0,
};
},
computed: {
me,
},
watch: {
me: fetchNotifications,
},
mounted: fetchNotifications,
methods: {
fetchNotifications,
notifications,
unseenNotifications,
},
};
</script>
@ -325,12 +315,12 @@ export default {
}
.header-account {
padding: 1rem 1.25rem 1rem .75rem;
padding: 1rem 1rem 1rem .75rem;
}
.header-notifications {
position: relative;
padding: 1rem .75rem;
padding: 1rem .75rem 1rem 1rem;
&.unseen .icon {
fill: var(--primary);
@ -344,7 +334,7 @@ export default {
justify-content: center;
position: absolute;
bottom: .3rem;
left: 0;
left: .1rem;
color: var(--primary);
font-size: .6rem;
font-weight: bold;

View File

@ -1,18 +1,19 @@
<template>
<div class="notifications">
<div
class="notifications"
>
<div class="notifications-header">
<h4 class="notifications-title">Notifications</h4>
<div class="notifications-actions">
<Icon
v-if="unseenCount > 0"
v-if="unseenNotifications.length > 0"
v-tooltip="'Mark all as seen'"
icon="checkmark"
@click="checkNotifications"
/>
<Icon
v-tooltip="'Add alert'"
icon="plus3"
@click="showAddAlert = true"
/>
@ -39,7 +40,7 @@
:key="`notification-${notification.id}`"
:class="{ unseen: !notification.seen }"
class="notification"
@click="checkNotification(notification.id, true)"
@click="checkNotification(notification.id)"
>
<router-link
:to="`/scene/${notification.scene.id}/${notification.scene.slug}`"
@ -99,67 +100,56 @@
v-tooltip="'Mark as seen'"
icon="checkmark"
class="notification-check"
@click.prevent.stop="checkNotification(notification.id)"
@click.prevent="checkNotification(notification.id)"
/>
<Icon
v-if="notification.alert"
v-tooltip="`You set an alert for <strong>${notification.alert.tags.map(tag => tag.name).join(', ') || 'all'}</strong> scenes with <strong>${notification.alert.actors.map(actor => actor.name).join(', ') || 'any actor'}</strong> for <strong>${notification.alert.entity?.name || 'any channel'}</strong>`"
icon="question5"
@click.prevent.stop
@click.prevent
/>
</router-link>
</li>
</ul>
</div>
<div @click="$emit('blur')">
<router-link
to="/notifications"
class="notification-link notification-more"
>See all</router-link>
</div>
</div>
</template>
<script>
import AddAlert from '../alerts/add.vue';
async function checkNotifications() {
await this.$store.dispatch('checkNotifications');
this.$emit('check');
function notifications() {
return this.$store.state.ui.notifications;
}
async function checkNotification(notificationId, blur) {
function unseenNotifications() {
return this.notifications.filter(notification => !notification.seen);
}
async function checkNotifications() {
await this.$store.dispatch('checkNotifications');
await this.$store.dispatch('fetchNotifications');
}
async function checkNotification(notificationId) {
await this.$store.dispatch('checkNotification', notificationId);
this.$emit('check');
if (blur) {
this.events.emit('blur');
}
await this.$store.dispatch('fetchNotifications');
}
export default {
components: {
AddAlert,
},
props: {
notifications: {
type: Array,
default: () => [],
},
unseenCount: {
type: Number,
default: 0,
},
},
data() {
return {
showAddAlert: false,
};
},
computed: {
notifications,
unseenNotifications,
},
methods: {
checkNotifications,
checkNotification,
@ -170,9 +160,6 @@ export default {
<style lang="scss" scoped>
.notifications {
width: 30rem;
max-height: calc(100vh - 5rem);
display: flex;
flex-direction: column;
}
.notifications-header {
@ -183,15 +170,11 @@ export default {
padding: .5rem;
fill: var(--shadow);
&:first-child {
padding: .5rem .5rem .5rem 1.5rem;
}
&:last-child {
padding: .5rem 1.5rem .5rem .5rem;
padding: .5rem 1rem .5rem .5rem;
}
&:hover {
:hover {
fill: var(--primary);
cursor: pointer;
}
@ -208,8 +191,6 @@ export default {
}
.notifications-body {
flex-grow: 1;
overflow-y: auto;
box-shadow: 0 0 3px var(--shadow-weak);
}
@ -221,6 +202,7 @@ export default {
.notification {
display: block;
border-right: solid .5rem var(--shadow-touch);
margin: 0 0 -1px 0;
color: var(--text);
&.unseen {
@ -231,10 +213,6 @@ export default {
padding: 1.3rem .5rem;
fill: var(--shadow-weak);
&.notification-check {
padding: 1.3rem .5rem 1.3rem 1rem;
}
&:last-child {
padding: 1.3rem 1rem 1.3rem .5rem;
}
@ -246,7 +224,6 @@ export default {
&:not(:last-child) {
border-bottom: solid 1px var(--shadow-hint);
margin: 0 0 -1px 0;
}
&:hover {
@ -328,22 +305,8 @@ export default {
font-weight: bold;
}
.notification-more {
display: block;
padding: .5rem 1rem;
color: var(--shadow);
text-align: center;
font-size: .9rem;
font-weight: bold;
&:hover {
color: var(--primary);
}
}
.poster {
width: 6rem;
height: 3.6rem;
object-fit: cover;
object-position: center;
}

View File

@ -51,7 +51,6 @@ export default {
query: this.$route.query ? this.$route.query.q : null,
};
},
emits: ['search'],
watch: {
$route: route,
searching,
@ -72,8 +71,8 @@ export default {
flex-grow: 1;
align-items: center;
justify-content: flex-end;
padding: 0 1rem 0 0;
border-left: solid 1px var(--shadow-hint);
margin: 0 .25rem 0 0;
&.compact {
padding: 0;
@ -93,7 +92,7 @@ export default {
.search-input {
height: 100%;
width: 100%;
padding: .5rem 0 .5rem .75rem;
padding: .5rem 0 .5rem .5rem;
border: none;
color: var(--text);
background: var(--background);
@ -120,20 +119,14 @@ export default {
}
}
&:focus {
&::placeholder {
color: var(--shadow-weak);
}
& + .search-button:not(:hover) .icon {
fill: var(--shadow);
}
&:focus::placeholder {
color: var(--shadow-weak);
}
}
.search-button {
height: 100%;
padding: 0 1.25rem 0 1rem;
padding: 0 1rem;
background: none;
border: none;
margin: .3rem 0 0 0;
@ -147,7 +140,7 @@ export default {
cursor: pointer;
.icon {
fill: var(--primary);
fill: var(--shadow);
}
}
}

View File

@ -1,76 +0,0 @@
<template>
<div
ref="page"
class="notifications-container"
>
<h1 class="heading">Notifications</h1>
<ul class="notifications nolist">
<li
v-for="notification in notifications"
:key="notification.id"
>
<SceneTile :release="notification.scene" />
</li>
</ul>
<Pagination
:items-total="totalCount"
:items-per-page="10"
/>
</div>
</template>
<script>
import Pagination from '../pagination/pagination.vue';
import SceneTile from '../releases/scene-tile.vue';
async function fetchNotifications() {
const { notifications, totalCount, unseenCount } = await this.$store.dispatch('fetchNotifications', {
page: this.$route.params.pageNumber,
limit: this.limit,
});
this.notifications = notifications;
this.unseenNotificationsCount = unseenCount;
this.totalCount = totalCount;
this.$emit('scroll');
}
export default {
components: {
Pagination,
SceneTile,
},
data() {
return {
notifications: [],
limit: 10,
totalCount: 0,
unseenNotificationsCount: 0,
};
},
emits: ['scroll'],
watch: {
$route: fetchNotifications,
},
mounted: fetchNotifications,
};
</script>
<style lang="scss" scoped>
.notifications-container {
padding: 1rem;
}
.notifications {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
grid-gap: .5rem;
}
.pagination {
margin: 1rem 0 0 0;
}
</style>

View File

@ -22,8 +22,6 @@
:src="getPath(release.poster, 'thumbnail')"
:style="{ 'background-image': getBgPath(release.poster, 'lazy') }"
:alt="release.title"
:width="release.poster.thumbnailWidth"
:height="release.poster.thumbnailHeight"
class="thumbnail"
loading="lazy"
>
@ -33,8 +31,6 @@
:src="getPath(release.photos[0], 'thumbnail')"
:style="{ 'background-image': getBgPath(release.photos[0], 'lazy') } "
:alt="release.title"
:width="release.photos[0].thumbnailWidth"
:height="release.photos[0].thumbnailHeight"
class="thumbnail"
loading="lazy"
>

View File

@ -1,6 +1,6 @@
import { get, post, del } from '../api';
function initAuthActions(store, _router) {
function initAuthActions(_store, _router) {
async function fetchMe({ commit }) {
try {
const user = await get('/session');
@ -18,7 +18,6 @@ function initAuthActions(store, _router) {
const user = await post('/session', credentials);
commit('setUser', user);
await store.dispatch('fetchNotifications');
return user;
}

View File

@ -10,7 +10,6 @@ import Networks from '../components/networks/networks.vue';
import Actor from '../components/actors/actor.vue';
import Actors from '../components/actors/actors.vue';
import Movies from '../components/releases/movies.vue';
import Notifications from '../components/notifications/notifications.vue';
import Tag from '../components/tags/tag.vue';
import Tags from '../components/tags/tags.vue';
import Stash from '../components/stashes/stash.vue';
@ -203,20 +202,6 @@ const routes = [
component: Tags,
name: 'tags',
},
{
path: '/notifications',
redirect: {
name: 'notifications',
params: {
pageNumber: 1,
},
},
},
{
path: '/notifications/:pageNumber',
component: Notifications,
name: 'notifications',
},
{
path: '/stash/:stashId/:stashSlug?',
component: Stash,

View File

@ -29,79 +29,63 @@ function initUiActions(store, _router) {
localStorage.setItem('sfw', sfw);
}
async function fetchNotifications(_context, { page = 1, limit = 10 } = {}) {
async function fetchNotifications({ commit }) {
if (!store.state.auth.user) {
return [];
}
const { notifications, unseenNotifications } = await graphql(`
const { notifications } = await graphql(`
query Notifications(
$hasAuth: Boolean!
$userId: Int
$limit: Int = 10
$offset: Int = 0
) {
notifications: notificationsConnection(
first: $limit
offset: $offset
notifications(
first: 10
orderBy: CREATED_AT_DESC
) {
nodes {
id
sceneId
userId
seen
createdAt
scene {
${releaseFields}
id
sceneId
userId
seen
createdAt
scene {
${releaseFields}
}
alert {
tags: alertsTags {
tag {
id
name
slug
}
}
alert {
tags: alertsTags {
tag {
id
name
slug
}
actors: alertsActors {
actor {
id
name
slug
}
actors: alertsActors {
actor {
id
name
slug
}
}
entity: alertsEntityByAlertId {
entity {
id
name
slug
independent
}
}
entity: alertsEntityByAlertId {
entity {
id
name
slug
independent
}
}
}
totalCount
}
unseenNotifications: notificationsConnection(
filter: { seen: { equalTo: false } }
) {
totalCount
}
}
}
`, {
hasAuth: !!store.state.auth.user,
userId: store.state.auth.user?.id,
limit,
offset: (page - 1) * limit,
});
const curatedNotifications = notifications.nodes.map(notification => curateNotification(notification));
const curatedNotifications = notifications.map(notification => curateNotification(notification));
return {
notifications: curatedNotifications,
totalCount: notifications.totalCount,
unseenCount: unseenNotifications.totalCount,
};
commit('setNotifications', curatedNotifications);
return curatedNotifications;
}
async function checkNotification(context, notificationId) {
@ -213,11 +197,9 @@ function initUiActions(store, _router) {
userId: store.state.auth.user?.id,
});
console.log(res.results);
return {
releases: res?.results.map(result => curateRelease(result.release)) || [],
actors: res?.actors.map(actor => curateActor(actor)) || [],
releases: res.results.map(result => curateRelease(result.release)),
actors: res.actors.map(actor => curateActor(actor)),
};
}

View File

@ -1,3 +1,7 @@
function setNotifications(state, notifications) {
state.notifications = notifications;
}
function setTagFilter(state, tagFilter) {
state.tagFilter = tagFilter;
}
@ -19,6 +23,7 @@ function setTheme(state, theme) {
}
export default {
setNotifications,
setTagFilter,
setRange,
setBatch,

View File

@ -11,4 +11,5 @@ export default {
batch: storedBatch || 'all',
sfw: storedSfw === 'true' || false,
theme: storedTheme || deviceTheme,
notifications: [],
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@ -2989,7 +2989,7 @@ const sites = [
{
name: 'Black Ambush',
slug: 'blackambush',
tags: ['bbc'],
alias: ['interracial', 'bbc'],
url: 'https://blackambush.com',
parent: 'exploitedx',
},

View File

@ -763,9 +763,8 @@ const tagMedia = [
['dv-tp', 0, 'Luna Rival in SZ1490'],
['facefucking', 5, 'Mia Moore B', 'throated'],
['facefucking', 6, 'Halle Hayes in "Towering Temptress"', '5kporn'],
['facefucking', 'adria_rae_throated', 'Adria Rae in "Adria Rae Sucks Cock All Day"', 'throated'],
['facefucking', 1, 'Paige Owens in "Dark Meat 12"', 'evilangel'],
['facefucking', 7, 'Anya Olsen and Audrey Snow in "Babysitter Busted Giving A BJ"', 'mommyblowsbest'],
['facefucking', 1, 'Paige Owens in "Dark Meat 12"', 'evilangel'],
['facefucking', 0, 'Ashly Anderson in "Rough Love"', 'hookuphotshot'],
['facefucking', 2, 'Jynx Maze', 'throated'],
['facefucking', 4, 'Brooklyn Gray in "Throats Fucks 6"', 'evilangel'],
@ -794,7 +793,7 @@ const tagMedia = [
['enhanced-boobs', '23d', 'Lulu Sex Bomb in "Tropical Touch"'],
['enhanced-boobs', 22, 'Sakura Sena'],
['enhanced-boobs', 'mareeva_trudy_photodromm_1', 'Mareeva and Trudy', 'photodromm'],
['enhanced-boobs', 'lara_frost_handsonhardcore', 'Lara Frost in "Handyman & Hubby Try To Satisfy Horny Little Ukrainian Nympho"', 'handsonhardcore'],
['enhanced-boobs', 'lara_frost_legalporno', 'Lara Frost in NRX059', 'legalporno'],
['enhanced-boobs', 'shawna_lenee_inthecrack_3', 'Shawna Lenee', 'inthecrack'],
['enhanced-boobs', 16, 'Marsha May in "Once You Go Black 7"', 'julesjordan'],
['enhanced-boobs', 'azul_hermosa_pornstarslikeitbig', 'Azul Hermosa in "She Likes Rough Quickies"', 'pornstarslikeitbig'],

View File

@ -51,11 +51,11 @@ async function removeAlert(alertId) {
async function notify(scenes) {
const releases = await knex.raw(`
SELECT alerts.id as alert_id, alerts.notify, alerts.email, releases.id as scene_id, users.id as user_id, COALESCE(json_agg(alerts_stashes.stash_id) FILTER (WHERE alerts_stashes.stash_id IS NOT NULL), '[]') as stashes
SELECT alerts.id as alert_id, alerts.notify, alerts.email, releases.id as scene_id, users.id as user_id
FROM releases
CROSS JOIN alerts
LEFT JOIN users ON users.id = alerts.user_id
LEFT JOIN alerts_stashes ON alerts_stashes.alert_id = alerts.id
LEFT JOIN releases_tags ON releases_tags.release_id = releases.id
/* match updated IDs from input */
WHERE (releases.id = ANY(:sceneIds))
/* match tags */
@ -111,26 +111,15 @@ async function notify(scenes) {
sceneIds: scenes.map(scene => scene.id),
});
const notifications = releases.rows
.filter(alert => alert.notify)
.map(notification => ({
const notifications = releases.rows.filter(alert => alert.notify);
await knex('notifications')
.insert(notifications.map(notification => ({
user_id: notification.user_id,
alert_id: notification.alert_id,
scene_id: notification.scene_id,
}));
const stashes = releases.rows
.filter(release => release.stashes.length > 0)
.flatMap(release => release.stashes.map(stash => ({
scene_id: release.scene_id,
stash_id: stash,
})));
await Promise.all([
knex('notifications').insert(notifications),
knex('stashes_scenes').insert(stashes),
]);
return releases.rows;
}
@ -144,6 +133,8 @@ async function updateNotification(notificationId, notification, sessionUser) {
}
async function updateNotifications(notification, sessionUser) {
console.log(notification, sessionUser.id);
await knex('notifications')
.where('user_id', sessionUser.id)
.update({