diff --git a/src/components/interactions/interactions.js b/src/components/interactions/interactions.js index cc6a15e1ac..cc51b470d5 100644 --- a/src/components/interactions/interactions.js +++ b/src/components/interactions/interactions.js @@ -5,6 +5,8 @@ const tabModeDict = { mentions: ['mention'], 'likes+repeats': ['repeat', 'like'], follows: ['follow'], + reactions: ['pleroma:emoji_reaction'], + reports: ['pleroma:report'], moves: ['move'] } @@ -12,7 +14,8 @@ const Interactions = { data () { return { allowFollowingMove: this.$store.state.users.currentUser.allow_following_move, - filterMode: tabModeDict.mentions + filterMode: tabModeDict.mentions, + canSeeReports: ['moderator', 'admin'].includes(this.$store.state.users.currentUser.role) } }, methods: { diff --git a/src/components/interactions/interactions.vue b/src/components/interactions/interactions.vue index 57d5d87c3d..b7291c02ca 100644 --- a/src/components/interactions/interactions.vue +++ b/src/components/interactions/interactions.vue @@ -21,6 +21,15 @@ key="follows" :label="$t('interactions.follows')" /> + + + + {{ $t('notifications.submitted_report') }} + + + + + diff --git a/src/components/user_reporting_modal/user_reporting_modal.js b/src/components/user_reporting_modal/user_reporting_modal.js index 8d171b2d58..85ffc6616a 100644 --- a/src/components/user_reporting_modal/user_reporting_modal.js +++ b/src/components/user_reporting_modal/user_reporting_modal.js @@ -1,4 +1,3 @@ - import Status from '../status/status.vue' import List from '../list/list.vue' import Checkbox from '../checkbox/checkbox.vue' @@ -21,14 +20,17 @@ const UserReportingModal = { } }, computed: { + reportModal () { + return this.$store.state.reports.reportModal + }, isLoggedIn () { return !!this.$store.state.users.currentUser }, isOpen () { - return this.isLoggedIn && this.$store.state.reports.modalActivated + return this.isLoggedIn && this.reportModal.activated }, userId () { - return this.$store.state.reports.userId + return this.reportModal.userId }, user () { return this.$store.getters.findUser(this.userId) @@ -37,10 +39,10 @@ const UserReportingModal = { return !this.user.is_local && this.user.screen_name.substr(this.user.screen_name.indexOf('@') + 1) }, statuses () { - return this.$store.state.reports.statuses + return this.reportModal.statuses }, preTickedIds () { - return this.$store.state.reports.preTickedIds + return this.reportModal.preTickedIds } }, watch: { diff --git a/src/i18n/en.json b/src/i18n/en.json index 7215b884fe..2e84595973 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -66,6 +66,7 @@ "more": "More", "loading": "Loading…", "generic_error": "An error occured", + "generic_error_message": "An error occured: {0}", "error_retry": "Please try again", "retry": "Try again", "optional": "optional", @@ -163,6 +164,7 @@ "no_more_notifications": "No more notifications", "migrated_to": "migrated to", "reacted_with": "reacted with {0}", + "submitted_report": "submitted a report", "poll_ended": "poll has ended" }, "polls": { @@ -198,6 +200,8 @@ "interactions": { "favs_repeats": "Repeats and favorites", "follows": "New follows", + "emoji_reactions": "Emoji Reactions", + "reports": "Reports", "moves": "User migrates", "load_older": "Load older interactions" }, @@ -266,6 +270,16 @@ "searching_for": "Searching for", "error": "Not found." }, + "report": { + "reporter": "Reporter:", + "reported_user": "Reported user:", + "reported_statuses": "Reported statuses:", + "notes": "Notes:", + "state": "State:", + "state_open": "Open", + "state_closed": "Closed", + "state_resolved": "Resolved" + }, "selectable_list": { "select_all": "Select all" }, diff --git a/src/i18n/nl.json b/src/i18n/nl.json index 5c00efc401..64c92b6851 100644 --- a/src/i18n/nl.json +++ b/src/i18n/nl.json @@ -744,6 +744,8 @@ "favs_repeats": "Herhalingen en favorieten", "follows": "Nieuwe gevolgden", "moves": "Gebruikermigraties", + "emoji_reactions": "Emoji Reacties", + "reports": "Rapportages", "load_older": "Oudere interacties laden" }, "remote_user_resolver": { @@ -751,6 +753,17 @@ "error": "Niet gevonden.", "remote_user_resolver": "Externe gebruikers-zoeker" }, + "report": { + "reporter": "Reporteerder:", + "reported_user": "Gerapporteerde gebruiker:", + "reported_statuses": "Gerapporteerde statussen:", + "notes": "Notas:", + "state": "Status:", + "state_open": "Open", + "state_closed": "Gesloten", + "state_resolved": "Opgelost" + }, + "selectable_list": { "select_all": "Alles selecteren" }, diff --git a/src/modules/config.js b/src/modules/config.js index 910fb1722e..c34b2c8c4b 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -59,6 +59,7 @@ export const defaultState = { moves: true, emojiReactions: true, followRequest: true, + reports: true, chatMention: true, polls: true }, diff --git a/src/modules/reports.js b/src/modules/reports.js index fea83e5f07..925792c031 100644 --- a/src/modules/reports.js +++ b/src/modules/reports.js @@ -2,20 +2,29 @@ import filter from 'lodash/filter' const reports = { state: { - userId: null, - statuses: [], - preTickedIds: [], - modalActivated: false + reportModal: { + userId: null, + statuses: [], + preTickedIds: [], + activated: false + }, + reports: {} }, mutations: { openUserReportingModal (state, { userId, statuses, preTickedIds }) { - state.userId = userId - state.statuses = statuses - state.preTickedIds = preTickedIds - state.modalActivated = true + state.reportModal.userId = userId + state.reportModal.statuses = statuses + state.reportModal.preTickedIds = preTickedIds + state.reportModal.activated = true }, closeUserReportingModal (state) { - state.modalActivated = false + state.reportModal.activated = false + }, + setReportState (reportsState, { id, state }) { + reportsState.reports[id].state = state + }, + addReport (state, report) { + state.reports[report.id] = report } }, actions: { @@ -31,6 +40,23 @@ const reports = { }, closeUserReportingModal ({ commit }) { commit('closeUserReportingModal') + }, + setReportState ({ commit, dispatch, rootState }, { id, state }) { + const oldState = rootState.reports.reports[id].state + commit('setReportState', { id, state }) + rootState.api.backendInteractor.setReportState({ id, state }).catch(e => { + console.error('Failed to set report state', e) + dispatch('pushGlobalNotice', { + level: 'error', + messageKey: 'general.generic_error_message', + messageArgs: [e.message], + timeout: 5000 + }) + commit('setReportState', { id, state: oldState }) + }) + }, + addReport ({ commit }, report) { + commit('addReport', report) } } } diff --git a/src/modules/statuses.js b/src/modules/statuses.js index c4475005fc..4aa0468309 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -337,6 +337,10 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item } + if (notification.type === 'pleroma:report') { + dispatch('addReport', notification.report) + } + if (notification.type === 'pleroma:emoji_reaction') { dispatch('fetchEmojiReactionsBy', notification.status.id) } diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 86d9eaf4b1..7b6f7d6763 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -93,6 +93,7 @@ const PLEROMA_CHAT_URL = id => `/api/v1/pleroma/chats/by-account-id/${id}` const PLEROMA_CHAT_MESSAGES_URL = id => `/api/v1/pleroma/chats/${id}/messages` const PLEROMA_CHAT_READ_URL = id => `/api/v1/pleroma/chats/${id}/read` const PLEROMA_DELETE_CHAT_MESSAGE_URL = (chatId, messageId) => `/api/v1/pleroma/chats/${chatId}/messages/${messageId}` +const PLEROMA_ADMIN_REPORTS = '/api/pleroma/admin/reports' const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups' const oldfetch = window.fetch @@ -588,7 +589,8 @@ const fetchTimeline = ({ listId = false, tag = false, withMuted = false, - replyVisibility = 'all' + replyVisibility = 'all', + includeTypes = [] }) => { const timelineUrls = { public: MASTODON_PUBLIC_TIMELINE, @@ -640,6 +642,11 @@ const fetchTimeline = ({ if (replyVisibility !== 'all') { params.push(['reply_visibility', replyVisibility]) } + if (includeTypes.length > 0) { + includeTypes.forEach(type => { + params.push(['include_types[]', type]) + }) + } params.push(['limit', 20]) @@ -1424,6 +1431,38 @@ const deleteChatMessage = ({ chatId, messageId, credentials }) => { }) } +const setReportState = ({ id, state, credentials }) => { + // TODO: Can't use promisedRequest because on OK this does not return json + // See https://git.pleroma.social/pleroma/pleroma-fe/-/merge_requests/1322 + return fetch(PLEROMA_ADMIN_REPORTS, { + headers: { + ...authHeaders(credentials), + Accept: 'application/json', + 'Content-Type': 'application/json' + }, + method: 'PATCH', + body: JSON.stringify({ + reports: [{ + id, + state + }] + }) + }) + .then(data => { + if (data.status >= 500) { + throw Error(data.statusText) + } else if (data.status >= 400) { + return data.json() + } + return data + }) + .then(data => { + if (data.errors) { + throw Error(data.errors[0].message) + } + }) +} + const apiService = { verifyCredentials, fetchTimeline, @@ -1523,7 +1562,8 @@ const apiService = { chatMessages, sendChatMessage, readChat, - deleteChatMessage + deleteChatMessage, + setReportState } export default apiService diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index e6a87b63f1..e9cbcfe6a7 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -390,6 +390,13 @@ export const parseNotification = (data) => { : parseUser(data.target) output.from_profile = parseUser(data.account) output.emoji = data.emoji + if (data.report) { + output.report = data.report + output.report.content = data.report.content + output.report.acct = parseUser(data.report.account) + output.report.actor = parseUser(data.report.actor) + output.report.statuses = data.report.statuses.map(parseStatus) + } } else { const parsedNotice = parseStatus(data.notice) output.type = data.ntype diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index a221b022d2..0f8b9b029f 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -15,6 +15,7 @@ export const visibleTypes = store => { rootState.config.notificationVisibility.followRequest && 'follow_request', rootState.config.notificationVisibility.moves && 'move', rootState.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction', + rootState.config.notificationVisibility.reports && 'pleroma:report', rootState.config.notificationVisibility.polls && 'poll' ].filter(_ => _)) } @@ -99,6 +100,9 @@ export const prepareNotificationObject = (notification, i18n) => { case 'follow_request': i18nString = 'follow_request' break + case 'pleroma:report': + i18nString = 'submitted_report' + break case 'poll': i18nString = 'poll_ended' break diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js index c0e299e406..6c2472107d 100644 --- a/src/services/notifications_fetcher/notifications_fetcher.service.js +++ b/src/services/notifications_fetcher/notifications_fetcher.service.js @@ -1,6 +1,18 @@ import apiService from '../api/api.service.js' import { promiseInterval } from '../promise_interval/promise_interval.js' +// For using include_types when fetching notifications. +// Note: chat_mention excluded as pleroma-fe polls them separately +const mastoApiNotificationTypes = [ + 'mention', + 'favourite', + 'reblog', + 'follow', + 'move', + 'pleroma:emoji_reaction', + 'pleroma:report' +] + const update = ({ store, notifications, older }) => { store.dispatch('addNewNotifications', { notifications, older }) } @@ -12,6 +24,7 @@ const fetchAndUpdate = ({ store, credentials, older = false, since }) => { const timelineData = rootState.statuses.notifications const hideMutedPosts = getters.mergedConfig.hideMutedPosts + args.includeTypes = mastoApiNotificationTypes args.withMuted = !hideMutedPosts args.timeline = 'notifications' @@ -63,6 +76,7 @@ const fetchNotifications = ({ store, args, older }) => { messageArgs: [error.message], timeout: 5000 }) + console.error(error) }) }