From e067783a30be41644abe1aecca631ef31cf476a8 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 17 Mar 2022 12:59:10 -0400 Subject: [PATCH 01/31] Add announcement display with placeholder messages --- src/boot/routes.js | 2 + src/components/announcement/announcement.js | 13 ++++++ src/components/announcement/announcement.vue | 19 +++++++++ .../announcements_page/announcements_page.js | 42 +++++++++++++++++++ .../announcements_page/announcements_page.vue | 21 ++++++++++ src/components/nav_panel/nav_panel.js | 6 ++- src/components/navigation/navigation.js | 5 +++ src/components/side_drawer/side_drawer.vue | 13 ++++++ 8 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/components/announcement/announcement.js create mode 100644 src/components/announcement/announcement.vue create mode 100644 src/components/announcements_page/announcements_page.js create mode 100644 src/components/announcements_page/announcements_page.vue diff --git a/src/boot/routes.js b/src/boot/routes.js index 63dd1297d9..2dc900e7ef 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -24,6 +24,7 @@ import Lists from 'components/lists/lists.vue' import ListsTimeline from 'components/lists_timeline/lists_timeline.vue' import ListsEdit from 'components/lists_edit/lists_edit.vue' import NavPanel from 'src/components/nav_panel/nav_panel.vue' +import AnnouncementsPage from 'components/announcements_page/announcements_page.vue' export default (store) => { const validateAuthenticatedRoute = (to, from, next) => { @@ -76,6 +77,7 @@ export default (store) => { { name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) }, { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute }, { name: 'about', path: '/about', component: About }, + { name: 'announcements', path: '/announcements', component: AnnouncementsPage }, { name: 'user-profile', path: '/users/:name', component: UserProfile }, { name: 'legacy-user-profile', path: '/:name', component: UserProfile }, { name: 'lists', path: '/lists', component: Lists }, diff --git a/src/components/announcement/announcement.js b/src/components/announcement/announcement.js new file mode 100644 index 0000000000..595f3b4e0d --- /dev/null +++ b/src/components/announcement/announcement.js @@ -0,0 +1,13 @@ + +const Announcement = { + props: { + announcement: Object + }, + computed: { + content () { + return this.announcement.content + } + } +} + +export default Announcement diff --git a/src/components/announcement/announcement.vue b/src/components/announcement/announcement.vue new file mode 100644 index 0000000000..c31328ee64 --- /dev/null +++ b/src/components/announcement/announcement.vue @@ -0,0 +1,19 @@ + + + + + diff --git a/src/components/announcements_page/announcements_page.js b/src/components/announcements_page/announcements_page.js new file mode 100644 index 0000000000..716468e6aa --- /dev/null +++ b/src/components/announcements_page/announcements_page.js @@ -0,0 +1,42 @@ +import Announcement from '../announcement/announcement.vue' + +const AnnouncementsPage = { + components: { + Announcement + }, + computed: { + announcements () { + return [{ + "id": "8", + "content": "

Looks like there was an issue processing audio attachments without embedded art since yesterday due to an experimental new feature. That issue has now been fixed, so you may see older posts with audio from other servers pop up in your feeds now as they are being finally properly processed. Sorry!

", + "starts_at": null, + "ends_at": null, + "all_day": false, + "published_at": "2020-07-03T01:27:38.726Z", + "updated_at": "2020-07-03T01:27:38.752Z", + "read": true, + "mentions": [], + "statuses": [], + "tags": [], + "emojis": [], + "reactions": [] + }, { + "id": "8", + "content": "

Looks like there was an issue processing audio attachments without embedded art since yesterday due to an experimental new feature. That issue has now been fixed, so you may see older posts with audio from other servers pop up in your feeds now as they are being finally properly processed. Sorry!

", + "starts_at": null, + "ends_at": null, + "all_day": false, + "published_at": "2020-07-03T01:27:38.726Z", + "updated_at": "2020-07-03T01:27:38.752Z", + "read": true, + "mentions": [], + "statuses": [], + "tags": [], + "emojis": [], + "reactions": [] + }] + } + } +} + +export default AnnouncementsPage diff --git a/src/components/announcements_page/announcements_page.vue b/src/components/announcements_page/announcements_page.vue new file mode 100644 index 0000000000..16b2f955f6 --- /dev/null +++ b/src/components/announcements_page/announcements_page.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index b54f2fa287..66c8a4cde1 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -18,7 +18,8 @@ import { faBell, faInfoCircle, faStream, - faList + faList, + faBullhorn } from '@fortawesome/free-solid-svg-icons' library.add( @@ -32,7 +33,8 @@ library.add( faBell, faInfoCircle, faStream, - faList + faList, + faBullhorn ) const NavPanel = { props: ['forceExpand', 'forceEditMode'], diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js index f66dd98159..4541e60b24 100644 --- a/src/components/navigation/navigation.js +++ b/src/components/navigation/navigation.js @@ -71,5 +71,10 @@ export const ROOT_ITEMS = { anon: true, icon: 'info-circle', label: 'nav.about' + }, + announcements: { + route: 'announcements', + icon: 'bullhorn', + label: 'nav.announcements' } } diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index cbeafdd2af..dccb8ab702 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -191,6 +191,19 @@ /> {{ $t("nav.administration") }} +
  • + + {{ $t("nav.announcements") }} + +
  • Date: Thu, 17 Mar 2022 14:01:45 -0400 Subject: [PATCH 02/31] Fetch real data from backend --- src/components/announcement/announcement.js | 10 ++++ src/components/announcement/announcement.vue | 24 +++++++- .../announcements_page/announcements_page.js | 33 ++-------- src/main.js | 4 +- src/modules/announcements.js | 60 +++++++++++++++++++ src/services/api/api.service.js | 18 +++++- 6 files changed, 117 insertions(+), 32 deletions(-) create mode 100644 src/modules/announcements.js diff --git a/src/components/announcement/announcement.js b/src/components/announcement/announcement.js index 595f3b4e0d..9e043e4842 100644 --- a/src/components/announcement/announcement.js +++ b/src/components/announcement/announcement.js @@ -6,6 +6,16 @@ const Announcement = { computed: { content () { return this.announcement.content + }, + isRead () { + return this.announcement.read + } + }, + methods: { + markAsRead () { + if (!this.isRead) { + return this.$store.dispatch('markAnnouncementAsRead', this.announcement.id) + } } } } diff --git a/src/components/announcement/announcement.vue b/src/components/announcement/announcement.vue index c31328ee64..d859146810 100644 --- a/src/components/announcement/announcement.vue +++ b/src/components/announcement/announcement.vue @@ -1,6 +1,24 @@ @@ -15,5 +33,9 @@ border-bottom-color: var(--border, $fallback--border); border-radius: 0; padding: var(--status-margin, $status-margin); + + .heading, .body { + margin-bottom: var(--status-margin, $status-margin); + } } diff --git a/src/components/announcements_page/announcements_page.js b/src/components/announcements_page/announcements_page.js index 716468e6aa..a2a218fd50 100644 --- a/src/components/announcements_page/announcements_page.js +++ b/src/components/announcements_page/announcements_page.js @@ -4,37 +4,12 @@ const AnnouncementsPage = { components: { Announcement }, + mounted () { + this.$store.dispatch('fetchAnnouncements') + }, computed: { announcements () { - return [{ - "id": "8", - "content": "

    Looks like there was an issue processing audio attachments without embedded art since yesterday due to an experimental new feature. That issue has now been fixed, so you may see older posts with audio from other servers pop up in your feeds now as they are being finally properly processed. Sorry!

    ", - "starts_at": null, - "ends_at": null, - "all_day": false, - "published_at": "2020-07-03T01:27:38.726Z", - "updated_at": "2020-07-03T01:27:38.752Z", - "read": true, - "mentions": [], - "statuses": [], - "tags": [], - "emojis": [], - "reactions": [] - }, { - "id": "8", - "content": "

    Looks like there was an issue processing audio attachments without embedded art since yesterday due to an experimental new feature. That issue has now been fixed, so you may see older posts with audio from other servers pop up in your feeds now as they are being finally properly processed. Sorry!

    ", - "starts_at": null, - "ends_at": null, - "all_day": false, - "published_at": "2020-07-03T01:27:38.726Z", - "updated_at": "2020-07-03T01:27:38.752Z", - "read": true, - "mentions": [], - "statuses": [], - "tags": [], - "emojis": [], - "reactions": [] - }] + return this.$store.state.announcements.announcements } } } diff --git a/src/main.js b/src/main.js index 6aa9cbb7b2..d3e60a0fa1 100644 --- a/src/main.js +++ b/src/main.js @@ -24,6 +24,7 @@ import editStatusModule from './modules/editStatus.js' import statusHistoryModule from './modules/statusHistory.js' import chatsModule from './modules/chats.js' +import announcementsModule from './modules/announcements.js' import { createI18n } from 'vue-i18n' @@ -91,7 +92,8 @@ const persistedStateOptions = { postStatus: postStatusModule, editStatus: editStatusModule, statusHistory: statusHistoryModule, - chats: chatsModule + chats: chatsModule, + announcements: announcementsModule }, plugins, strict: false // Socket modifies itself, let's ignore this for now. diff --git a/src/modules/announcements.js b/src/modules/announcements.js new file mode 100644 index 0000000000..2ab2600cf7 --- /dev/null +++ b/src/modules/announcements.js @@ -0,0 +1,60 @@ +import { set } from 'vue' + +const FETCH_ANNOUNCEMENT_INTERVAL_MS = 1000 * 60 * 5 + +export const defaultState = { + announcements: [], + fetchAnnouncementsTimer: undefined +} + +export const mutations = { + setAnnouncements (state, announcements) { + set(state, 'announcements', announcements) + }, + setAnnouncementRead (state, { id, read }) { + if (!state.announcements[id]) { + return + } + + set(state.announcements[id], 'read', read) + }, + setFetchAnnouncementsTimer (state, timer) { + set(state, 'fetchAnnouncementsTimer', announcements) + } +} + +const announcements = { + state: defaultState, + mutations, + actions: { + fetchAnnouncements (store) { + return store.rootState.api.backendInteractor.fetchAnnouncements() + .then(announcements => { + store.commit('setAnnouncements', announcements) + }) + }, + markAnnouncementAsRead (store, id) { + return store.rootState.api.backendInteractor.dismissAnnouncement({ id }) + .then(() => { + store.commit('setAnnouncementRead', { id, read: true }) + }) + }, + startFetchingAnnouncements (store) { + if (store.state.fetchAnnouncementsTimer) { + return + } + + const interval = setInterval(() => store.dispatch('fetchAnnouncements'), FETCH_ANNOUNCEMENT_INTERVAL_MS) + store.commit('setFetchAnnouncementsTimer', interval) + + return store.dispatch('fetchAnnouncements') + }, + stopFetchingAnnouncements (store) { + const interval = store.state.fetchAnnouncementsTimer + store.commit('setFetchAnnouncementsTimer', undefined) + clearInterval(interval) + } + } +} + +export default announcements diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 1ec77b37e1..10d4363262 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -90,6 +90,8 @@ const MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks' const MASTODON_LISTS_URL = '/api/v1/lists' const MASTODON_STREAMING = '/api/v1/streaming' const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers' +const MASTODON_ANNOUNCEMENTS_URL = '/api/v1/announcements' +const MASTODON_ANNOUNCEMENTS_DISMISS_URL = id => `/api/v1/announcements/${id}/dismiss` const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/pleroma/statuses/${id}/reactions` const PLEROMA_EMOJI_REACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` @@ -1361,6 +1363,18 @@ const dismissNotification = ({ credentials, id }) => { }) } +const fetchAnnouncements = ({ credentials }) => { + return promisedRequest({ url: MASTODON_ANNOUNCEMENTS_URL, credentials }) +} + +const dismissAnnouncement = ({ id, credentials }) => { + return promisedRequest({ + url: MASTODON_ANNOUNCEMENTS_DISMISS_URL(id), + credentials, + method: 'POST' + }) +} + export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => { return Object.entries({ ...(credentials @@ -1687,7 +1701,9 @@ const apiService = { readChat, deleteChatMessage, setReportState, - fetchUserInLists + fetchUserInLists, + fetchAnnouncements, + dismissAnnouncement } export default apiService From e5b49ae34b0c750b7b3259044104ebb7e4b872c7 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 17 Mar 2022 14:02:21 -0400 Subject: [PATCH 03/31] Add English translations for announcements --- src/i18n/en.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/i18n/en.json b/src/i18n/en.json index 5e295b5ecb..7fd900a905 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -32,6 +32,11 @@ }, "staff": "Staff" }, + "announcements": { + "page_header": "Announcements", + "title": "Announcement", + "mark_as_read_action": "Mark as read" + }, "shoutbox": { "title": "Shoutbox" }, @@ -162,7 +167,8 @@ "mobile_sidebar": "Toggle mobile sidebar", "mobile_notifications": "Open notifications", "mobile_notifications": "Open notifications (there are unread ones)", - "mobile_notifications_close": "Close notifications" + "mobile_notifications_close": "Close notifications", + "announcements": "Announcements" }, "notifications": { "broken_favorite": "Unknown status, searching for it…", From e494e746439e3e622c5b12a182b6c6a9de540821 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 17 Mar 2022 14:54:52 -0400 Subject: [PATCH 04/31] Implement posting announcements --- .../announcements_page/announcements_page.js | 28 +++++++ .../announcements_page/announcements_page.vue | 76 +++++++++++++++++++ src/modules/announcements.js | 6 ++ src/services/api/api.service.js | 27 ++++++- 4 files changed, 136 insertions(+), 1 deletion(-) diff --git a/src/components/announcements_page/announcements_page.js b/src/components/announcements_page/announcements_page.js index a2a218fd50..ec03836106 100644 --- a/src/components/announcements_page/announcements_page.js +++ b/src/components/announcements_page/announcements_page.js @@ -1,16 +1,44 @@ +import { mapState } from 'vuex' import Announcement from '../announcement/announcement.vue' const AnnouncementsPage = { components: { Announcement }, + data () { + return { + newAnnouncement: { + content: '' + }, + posting: false, + error: undefined + } + }, mounted () { this.$store.dispatch('fetchAnnouncements') }, computed: { + ...mapState({ + currentUser: state => state.users.currentUser + }), announcements () { return this.$store.state.announcements.announcements } + }, + methods: { + postAnnouncement () { + this.posting = true + this.$store.dispatch('postAnnouncement', this.newAnnouncement) + .catch(error => { + this.error = error.error + }) + .finally(() => { + this.posting = false + }) + }, + clearError () { + this.error = undefined + } } } diff --git a/src/components/announcements_page/announcements_page.vue b/src/components/announcements_page/announcements_page.vue index 16b2f955f6..1b3bd578bd 100644 --- a/src/components/announcements_page/announcements_page.vue +++ b/src/components/announcements_page/announcements_page.vue @@ -6,6 +6,51 @@
    +
    +
    +
    +

    {{ $t('announcements.post_form_header') }}

    +
    +
    +