From 5b7acca0bab12e88ad5b2c80b120859e440133de Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sat, 22 Feb 2020 10:57:08 +0200 Subject: make theme loading work with source-only presets --- src/modules/instance.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/instance.js b/src/modules/instance.js index 8781646d..f96337e4 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -1,5 +1,6 @@ import { set } from 'vue' import { getPreset, applyTheme } from '../services/style_setter/style_setter.js' +import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js' import { instanceDefaultProperties } from './config.js' const defaultState = { @@ -159,7 +160,14 @@ const instance = { // No need to apply theme if there's user theme already const { customTheme } = rootState.config if (customTheme) return - applyTheme(themeData.theme) + + // New theme presets don't have 'theme' property, they use 'source' + const themeSource = themeData.source + if (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION) { + applyTheme(themeSource) + } else { + applyTheme(themeData.theme) + } }) }, fetchEmoji ({ dispatch, state }) { -- cgit v1.2.3-70-g09d2 From ee582855d2f6ea02d8ee3cc4536b0b71ccece920 Mon Sep 17 00:00:00 2001 From: Shpuld Shpludson Date: Fri, 28 Feb 2020 17:20:40 +0000 Subject: Apply suggestion to src/modules/instance.js --- src/modules/instance.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/instance.js b/src/modules/instance.js index f96337e4..ffece311 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -163,7 +163,7 @@ const instance = { // New theme presets don't have 'theme' property, they use 'source' const themeSource = themeData.source - if (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION) { + if (!themeData.theme || (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION)) { applyTheme(themeSource) } else { applyTheme(themeData.theme) -- cgit v1.2.3-70-g09d2 From ab4005add57f36cc78b774971f9942c5894362dc Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Mon, 2 Mar 2020 08:35:57 +0200 Subject: add status unavailable message when status can't be loaded in status preview --- src/components/status_popover/status_popover.js | 7 +++++++ src/components/status_popover/status_popover.vue | 10 ++++++++-- src/i18n/en.json | 3 ++- src/i18n/fi.json | 3 ++- src/modules/statuses.js | 2 +- 5 files changed, 20 insertions(+), 5 deletions(-) (limited to 'src/modules') diff --git a/src/components/status_popover/status_popover.js b/src/components/status_popover/status_popover.js index cb55f67e..159132a9 100644 --- a/src/components/status_popover/status_popover.js +++ b/src/components/status_popover/status_popover.js @@ -5,6 +5,11 @@ const StatusPopover = { props: [ 'statusId' ], + data () { + return { + error: false + } + }, computed: { status () { return find(this.$store.state.statuses.allStatuses, { id: this.statusId }) @@ -18,6 +23,8 @@ const StatusPopover = { enter () { if (!this.status) { this.$store.dispatch('fetchStatus', this.statusId) + .then(data => (this.error = false)) + .catch(e => (this.error = true)) } } } diff --git a/src/components/status_popover/status_popover.vue b/src/components/status_popover/status_popover.vue index 11f6cb5a..f5948207 100644 --- a/src/components/status_popover/status_popover.vue +++ b/src/components/status_popover/status_popover.vue @@ -17,9 +17,15 @@ :statusoid="status" :compact="true" /> +
+ {{ $t('status.status_unavailable') }} +
@@ -50,7 +56,7 @@ border: none; } - .status-preview-loading { + .status-preview-no-content { padding: 1em; text-align: center; diff --git a/src/i18n/en.json b/src/i18n/en.json index 82acc1ab..54d0608e 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -615,7 +615,8 @@ "reply_to": "Reply to", "replies_list": "Replies:", "mute_conversation": "Mute conversation", - "unmute_conversation": "Unmute conversation" + "unmute_conversation": "Unmute conversation", + "status_unavailable": "Status unavailable" }, "user_card": { "approve": "Approve", diff --git a/src/i18n/fi.json b/src/i18n/fi.json index ac8b2ac9..926e6087 100644 --- a/src/i18n/fi.json +++ b/src/i18n/fi.json @@ -289,7 +289,8 @@ "reply_to": "Vastaus", "replies_list": "Vastaukset:", "mute_conversation": "Hiljennä keskustelu", - "unmute_conversation": "Poista hiljennys" + "unmute_conversation": "Poista hiljennys", + "status_unavailable": "Viesti ei saatavissa" }, "user_card": { "approve": "Hyväksy", diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 25b62ac7..f1b7dcbd 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -616,7 +616,7 @@ const statuses = { commit('setNotificationsSilence', { value }) }, fetchStatus ({ rootState, dispatch }, id) { - rootState.api.backendInteractor.fetchStatus({ id }) + return rootState.api.backendInteractor.fetchStatus({ id }) .then((status) => dispatch('addNewStatuses', { statuses: [status] })) }, deleteStatus ({ rootState, commit }, status) { -- cgit v1.2.3-70-g09d2 From 0702934f4f05595194c73cc463b2e72a27e3b1e2 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 4 Mar 2020 00:23:14 +0200 Subject: fix trasparency problems in some cases (purple headers) --- src/modules/config.js | 1 + src/services/theme_data/theme_data.service.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/modules') diff --git a/src/modules/config.js b/src/modules/config.js index e6b373b4..7997521d 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -102,6 +102,7 @@ const config = { setPreset(value) break case 'customTheme': + case 'customThemeSource': applyTheme(value) } } diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js index e6ff82e6..de6561cd 100644 --- a/src/services/theme_data/theme_data.service.js +++ b/src/services/theme_data/theme_data.service.js @@ -352,7 +352,8 @@ export const getColors = (sourceColors, sourceOpacity) => SLOT_ORDERED.reduce(({ } const opacitySlot = getOpacitySlot(key) const ownOpacitySlot = value.opacity - if (opacitySlot && (outputColor.a === undefined || ownOpacitySlot)) { + const opacityOverriden = ownOpacitySlot && sourceOpacity[opacitySlot] !== undefined + if (opacitySlot && (outputColor.a === undefined || opacityOverriden)) { const dependencySlot = deps[0] if (dependencySlot && colors[dependencySlot] === 'transparent') { outputColor.a = 0 -- cgit v1.2.3-70-g09d2 From 01b07f01e9340935faf51e5a3c8034cc90423989 Mon Sep 17 00:00:00 2001 From: eugenijm Date: Sat, 25 Apr 2020 07:04:39 +0300 Subject: Add support for follow request notifications --- CHANGELOG.md | 3 ++ src/components/notification/notification.js | 19 ++++++++++ src/components/notification/notification.vue | 44 ++++++++++++++++------ src/components/notifications/notifications.scss | 15 ++++++++ src/i18n/en.json | 5 ++- src/modules/config.js | 3 +- src/modules/statuses.js | 22 ++++++++++- src/services/api/api.service.js | 11 ++++++ .../entity_normalizer/entity_normalizer.service.js | 5 +-- .../notification_utils/notification_utils.js | 7 +++- static/fontello.json | 14 ++++++- 11 files changed, 128 insertions(+), 20 deletions(-) (limited to 'src/modules') diff --git a/CHANGELOG.md b/CHANGELOG.md index f45561d0..685fe629 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - Emoji autocomplete will match any part of the word and not just start, for example :drool will now helpfully suggest :blobcatdrool: and :blobcatdroolreach: +### Add +- Follow request notification support + ## [2.0.2] - 2020-04-08 ### Fixed - Favorite/Repeat avatars not showing up on private instances/non-public posts diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index e7bd769e..6deee7d5 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -2,6 +2,7 @@ import Status from '../status/status.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import UserCard from '../user_card/user_card.vue' import Timeago from '../timeago/timeago.vue' +import { isStatusNotification } from '../../services/notification_utils/notification_utils.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' @@ -32,6 +33,21 @@ const Notification = { }, toggleMute () { this.unmuted = !this.unmuted + }, + approveUser () { + this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) + this.$store.dispatch('removeFollowRequest', this.user) + this.$store.dispatch('updateNotification', { + id: this.notification.id, + updater: notification => { + notification.type = 'follow' + } + }) + }, + denyUser () { + this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) + this.$store.dispatch('removeFollowRequest', this.user) + this.$store.dispatch('dismissNotification', { id: this.notification.id }) } }, computed: { @@ -57,6 +73,9 @@ const Notification = { }, needMute () { return this.user.muted + }, + isStatusNotification () { + return isStatusNotification(this.notification.type) } } } diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 51875747..02802776 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -74,6 +74,10 @@ {{ $t('notifications.followed_you') }} + + + {{ $t('notifications.follow_request') }} + {{ $t('notifications.migrated_to') }} @@ -87,30 +91,30 @@
- + - +
- + - +
{ each(notifications, (notification) => { - if (notification.type !== 'follow' && notification.type !== 'move') { + if (isStatusNotification(notification.type)) { notification.action = addStatusToGlobalStorage(state, notification.action).item notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item } @@ -361,13 +362,16 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot case 'move': i18nString = 'migrated_to' break + case 'follow_request': + i18nString = 'follow_request' + break } if (notification.type === 'pleroma:emoji_reaction') { notifObj.body = rootGetters.i18n.t('notifications.reacted_with', [notification.emoji]) } else if (i18nString) { notifObj.body = rootGetters.i18n.t('notifications.' + i18nString) - } else { + } else if (isStatusNotification(notification.type)) { notifObj.body = notification.status.text } @@ -521,6 +525,13 @@ export const mutations = { notification.seen = true }) }, + dismissNotification (state, { id }) { + state.notifications.data = state.notifications.data.filter(n => n.id !== id) + }, + updateNotification (state, { id, updater }) { + const notification = find(state.notifications.data, n => n.id === id) + notification && updater(notification) + }, queueFlush (state, { timeline, id }) { state.timelines[timeline].flushMarker = id }, @@ -680,6 +691,13 @@ const statuses = { credentials: rootState.users.currentUser.credentials }) }, + dismissNotification ({ rootState, commit }, { id }) { + rootState.api.backendInteractor.dismissNotification({ id }) + .then(() => commit('dismissNotification', { id })) + }, + updateNotification ({ rootState, commit }, { id, updater }) { + commit('updateNotification', { id, updater }) + }, fetchFavsAndRepeats ({ rootState, commit }, id) { Promise.all([ rootState.api.backendInteractor.fetchFavoritedByUsers({ id }), diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index ad2b2ad5..cda61ee2 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -29,6 +29,7 @@ const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials' const MASTODON_REGISTRATION_URL = '/api/v1/accounts' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' const MASTODON_USER_NOTIFICATIONS_URL = '/api/v1/notifications' +const MASTODON_DISMISS_NOTIFICATION_URL = id => `/api/v1/notifications/${id}/dismiss` const MASTODON_FAVORITE_URL = id => `/api/v1/statuses/${id}/favourite` const MASTODON_UNFAVORITE_URL = id => `/api/v1/statuses/${id}/unfavourite` const MASTODON_RETWEET_URL = id => `/api/v1/statuses/${id}/reblog` @@ -1010,6 +1011,15 @@ const unmuteDomain = ({ domain, credentials }) => { }) } +const dismissNotification = ({ credentials, id }) => { + return promisedRequest({ + url: MASTODON_DISMISS_NOTIFICATION_URL(id), + method: 'POST', + payload: { id }, + credentials + }) +} + export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => { return Object.entries({ ...(credentials @@ -1165,6 +1175,7 @@ const apiService = { denyUser, suggestions, markNotificationsAsSeen, + dismissNotification, vote, fetchPoll, fetchFavoritedByUsers, diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index 84169a7b..6cacd0b8 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -1,4 +1,5 @@ import escape from 'escape-html' +import { isStatusNotification } from '../notification_utils/notification_utils.js' const qvitterStatusType = (status) => { if (status.is_post_verb) { @@ -346,9 +347,7 @@ export const parseNotification = (data) => { if (masto) { output.type = mastoDict[data.type] || data.type output.seen = data.pleroma.is_seen - output.status = output.type === 'follow' || output.type === 'move' - ? null - : parseStatus(data.status) + output.status = isStatusNotification(output.type) ? parseStatus(data.status) : null output.action = output.status // TODO: Refactor, this is unneeded output.target = output.type !== 'move' ? null diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index b17bd7bf..eb479227 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -1,4 +1,4 @@ -import { filter, sortBy } from 'lodash' +import { filter, sortBy, includes } from 'lodash' export const notificationsFromStore = store => store.state.statuses.notifications.data @@ -7,10 +7,15 @@ export const visibleTypes = store => ([ store.state.config.notificationVisibility.mentions && 'mention', store.state.config.notificationVisibility.repeats && 'repeat', store.state.config.notificationVisibility.follows && 'follow', + store.state.config.notificationVisibility.followRequest && 'follow_request', store.state.config.notificationVisibility.moves && 'move', store.state.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction' ].filter(_ => _)) +const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reaction'] + +export const isStatusNotification = (type) => includes(statusNotifications, type) + const sortById = (a, b) => { const seqA = Number(a.id) const seqB = Number(b.id) diff --git a/static/fontello.json b/static/fontello.json index 5a7086a2..5963b68b 100755 --- a/static/fontello.json +++ b/static/fontello.json @@ -345,6 +345,18 @@ "css": "link", "code": 59427, "src": "fontawesome" + }, + { + "uid": "8b80d36d4ef43889db10bc1f0dc9a862", + "css": "user", + "code": 59428, + "src": "fontawesome" + }, + { + "uid": "12f4ece88e46abd864e40b35e05b11cd", + "css": "ok", + "code": 59431, + "src": "fontawesome" } ] -} +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 75519223f9a715aacb99d3780ee681089a479292 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sat, 2 May 2020 10:52:57 +0300 Subject: mark single notifs as seen properly on server --- src/components/follow_request_card/follow_request_card.js | 2 +- src/components/notification/notification.js | 2 +- src/modules/statuses.js | 12 ++++++++++++ src/services/api/api.service.js | 12 ++++++++---- 4 files changed, 22 insertions(+), 6 deletions(-) (limited to 'src/modules') diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index 2a9d3db5..33e2699e 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -18,11 +18,11 @@ const FollowRequestCard = { this.$store.dispatch('removeFollowRequest', this.user) const notifId = this.findFollowRequestNotificationId() + this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId }) this.$store.dispatch('updateNotification', { id: notifId, updater: notification => { notification.type = 'follow' - notification.seen = true } }) }, diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 8c20ff09..abe3bebe 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -37,11 +37,11 @@ const Notification = { approveUser () { this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) + this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id }) this.$store.dispatch('updateNotification', { id: this.notification.id, updater: notification => { notification.type = 'follow' - notification.seen = true } }) }, diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 239f41eb..2a8b9581 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -525,6 +525,10 @@ export const mutations = { notification.seen = true }) }, + markSingleNotificationAsSeen (state, { id }) { + const notification = find(state.notifications.data, n => n.id === id) + if (notification) notification.seen = true + }, dismissNotification (state, { id }) { state.notifications.data = state.notifications.data.filter(n => n.id !== id) }, @@ -691,6 +695,14 @@ const statuses = { credentials: rootState.users.currentUser.credentials }) }, + markSingleNotificationAsSeen ({ rootState, commit }, { id }) { + commit('markSingleNotificationAsSeen', { id }) + apiService.markNotificationsAsSeen({ + single: true, + id, + credentials: rootState.users.currentUser.credentials + }) + }, dismissNotification ({ rootState, commit }, { id }) { rootState.api.backendInteractor.dismissNotification({ id }) .then(() => commit('dismissNotification', { id })) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 3a58c38d..72c8874f 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -4,7 +4,6 @@ import 'whatwg-fetch' import { RegistrationError, StatusCodeError } from '../errors/errors' /* eslint-env browser */ -const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json' const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import' const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import' const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account' @@ -17,6 +16,7 @@ const DEACTIVATE_USER_URL = '/api/pleroma/admin/users/deactivate' const ADMIN_USERS_URL = '/api/pleroma/admin/users' const SUGGESTIONS_URL = '/api/v1/suggestions' const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings' +const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read' const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa' const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes' @@ -841,12 +841,16 @@ const suggestions = ({ credentials }) => { }).then((data) => data.json()) } -const markNotificationsAsSeen = ({ id, credentials }) => { +const markNotificationsAsSeen = ({ id, credentials, single = false }) => { const body = new FormData() - body.append('latest_id', id) + if (single) { + body.append('id', id) + } else { + body.append('max_id', id) + } - return fetch(QVITTER_USER_NOTIFICATIONS_READ_URL, { + return fetch(NOTIFICATION_READ_URL, { body, headers: authHeaders(credentials), method: 'POST' -- cgit v1.2.3-70-g09d2 From 92ccaa97bb0a2c15b96e2fbcf03823ba902dc516 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sat, 2 May 2020 11:51:39 +0300 Subject: don't dismiss a rejected follow request on server --- src/components/follow_request_card/follow_request_card.js | 9 +++++---- src/components/notification/notification.js | 6 ++++-- src/modules/statuses.js | 5 ++++- 3 files changed, 13 insertions(+), 7 deletions(-) (limited to 'src/modules') diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index 33e2699e..cbd75311 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -27,11 +27,12 @@ const FollowRequestCard = { }) }, denyUser () { - this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) - this.$store.dispatch('removeFollowRequest', this.user) - const notifId = this.findFollowRequestNotificationId() - this.$store.dispatch('dismissNotification', { id: notifId }) + this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) + .then(() => { + this.$store.dispatch('dismissNotificationLocal', { id: notifId }) + this.$store.dispatch('removeFollowRequest', this.user) + }) } } } diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index abe3bebe..1ae81ce4 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -47,8 +47,10 @@ const Notification = { }, denyUser () { this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) - this.$store.dispatch('removeFollowRequest', this.user) - this.$store.dispatch('dismissNotification', { id: this.notification.id }) + .then(() => { + this.$store.dispatch('dismissNotificationLocal', { id: this.notification.id }) + this.$store.dispatch('removeFollowRequest', this.user) + }) } }, computed: { diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 2a8b9581..cd8c1dba 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -703,9 +703,12 @@ const statuses = { credentials: rootState.users.currentUser.credentials }) }, + dismissNotificationLocal ({ rootState, commit }, { id }) { + commit('dismissNotification', { id }) + }, dismissNotification ({ rootState, commit }, { id }) { + commit('dismissNotification', { id }) rootState.api.backendInteractor.dismissNotification({ id }) - .then(() => commit('dismissNotification', { id })) }, updateNotification ({ rootState, commit }, { id, updater }) { commit('updateNotification', { id, updater }) -- cgit v1.2.3-70-g09d2