diff --git a/assets/components/header/header.vue b/assets/components/header/header.vue index 7733dee4..d5663179 100644 --- a/assets/components/header/header.vue +++ b/assets/components/header/header.vue @@ -85,17 +85,18 @@
- + + + {{ notifications.length }}
@@ -138,19 +139,14 @@ /> - - Alert @@ -315,7 +314,26 @@ export default { } .header-notifications { + position: relative; padding: 1rem .75rem 1rem 1rem; + + &.unseen .icon { + fill: var(--primary); + } +} + +.notifications-count { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + position: absolute; + bottom: .05rem; + left: .1rem; + color: var(--text-light); + font-size: .6rem; + font-weight: bold; } .account { @@ -360,10 +378,6 @@ export default { } } -.notifications { - padding: 1rem; -} - @media(max-width: $breakpoint-kilo) { .search-full { display: none; diff --git a/assets/components/header/notifications.vue b/assets/components/header/notifications.vue new file mode 100644 index 00000000..69a4b60a --- /dev/null +++ b/assets/components/header/notifications.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/assets/js/curate.js b/assets/js/curate.js index 393ac5f7..c99ad999 100644 --- a/assets/js/curate.js +++ b/assets/js/curate.js @@ -196,10 +196,20 @@ function curateUser(user) { return curatedUser; } +function curateNotification(notification) { + const curatedNotification = notification; + + curatedNotification.scene = curateRelease(notification.scene); + curatedNotification.alert = curateAlert(notification.alert.alert || notification.alert); + + return curatedNotification; +} + export { curateActor, curateEntity, curateRelease, + curateNotification, curateTag, curateStash, curateUser, diff --git a/assets/js/ui/actions.js b/assets/js/ui/actions.js index 9545bed4..b61810d0 100644 --- a/assets/js/ui/actions.js +++ b/assets/js/ui/actions.js @@ -1,6 +1,6 @@ import { graphql } from '../api'; import { releaseFields, actorStashesFields } from '../fragments'; -import { curateRelease, curateActor } from '../curate'; +import { curateRelease, curateActor, curateNotification } from '../curate'; function initUiActions(store, _router) { function setTagFilter({ commit }, filter) { @@ -29,6 +29,53 @@ function initUiActions(store, _router) { localStorage.setItem('sfw', sfw); } + async function fetchNotifications({ commit }) { + if (!store.state.auth.user) { + return []; + } + + const { notifications } = await graphql(` + query Notifications( + $hasAuth: Boolean! + $userId: Int + ) { + notifications { + id + sceneId + userId + createdAt + scene { + ${releaseFields} + } + alert { + tags: alertsTags { + tag { + id + name + slug + } + } + actors: alertsActors { + actor { + id + name + slug + } + } + } + } + } + `, { + hasAuth: !!store.state.auth.user, + userId: store.state.auth.user?.id, + }); + + const curatedNotifications = notifications.map(notification => curateNotification(notification)); + + commit('setNotifications', curatedNotifications); + return curatedNotifications; + } + async function search({ _commit }, { query, limit = 20 }) { const res = await graphql(` query SearchReleases( @@ -176,6 +223,7 @@ function initUiActions(store, _router) { setBatch, setSfw, setTheme, + fetchNotifications, fetchStats, }; } diff --git a/assets/js/ui/mutations.js b/assets/js/ui/mutations.js index 76184f2f..db8f4969 100644 --- a/assets/js/ui/mutations.js +++ b/assets/js/ui/mutations.js @@ -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, diff --git a/assets/js/ui/observers.js b/assets/js/ui/observers.js index 311d45cb..d0780de8 100644 --- a/assets/js/ui/observers.js +++ b/assets/js/ui/observers.js @@ -1,4 +1,4 @@ -function initUiObservers(store, _router) { +async function initUiObservers(store, _router) { const body = document.querySelector('body'); body.classList.add(store.state.ui.theme); @@ -29,6 +29,8 @@ function initUiObservers(store, _router) { store.dispatch('setTheme', 'light'); } }); + + await store.dispatch('fetchNotifications'); } export default initUiObservers; diff --git a/assets/js/ui/state.js b/assets/js/ui/state.js index e6818dfe..157bd378 100644 --- a/assets/js/ui/state.js +++ b/assets/js/ui/state.js @@ -11,4 +11,5 @@ export default { batch: storedBatch || 'all', sfw: storedSfw === 'true' || false, theme: storedTheme || deviceTheme, + notifications: [], }; diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js index 8d845b71..00588b46 100644 --- a/migrations/20190325001339_releases.js +++ b/migrations/20190325001339_releases.js @@ -1567,6 +1567,13 @@ exports.up = knex => Promise.resolve() WHERE alerts.id = alerts_stashes.alert_id AND alerts.user_id = current_user_id() )); + + ALTER TABLE notifications ENABLE ROW LEVEL SECURITY; + + CREATE POLICY notifications_policy_select ON notifications FOR SELECT USING (notifications.user_id = current_user_id()); + CREATE POLICY notifications_policy_update ON notifications FOR UPDATE USING (notifications.user_id = current_user_id()); + CREATE POLICY notifications_policy_delete ON notifications FOR DELETE USING (notifications.user_id = current_user_id()); + CREATE POLICY notifications_policy_insert ON notifications FOR INSERT WITH CHECK (true); `, { visitor: knex.raw(config.database.query.user), }); diff --git a/src/store-releases.js b/src/store-releases.js index cb562a0b..fc8a3af0 100644 --- a/src/store-releases.js +++ b/src/store-releases.js @@ -264,8 +264,6 @@ async function notifyAlerts(scenes) { scene_id: notification.scene_id, }))); - console.log(releases.rows); - return releases.rows; }