diff --git a/package.json b/package.json index 1ef35f4a..09ba18ac 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,10 @@ "@fortawesome/fontawesome-svg-core": "1.3.0", "@fortawesome/free-regular-svg-icons": "5.15.4", "@fortawesome/free-solid-svg-icons": "5.15.4", - "@fortawesome/vue-fontawesome": "3.0.0-5", + "@fortawesome/vue-fontawesome": "3.0.1", "@kazvmoe-infra/pinch-zoom-element": "1.2.0", - "@vuelidate/core": "2.0.0-alpha.41", - "@vuelidate/validators": "2.0.0-alpha.27", + "@vuelidate/core": "2.0.0-alpha.42", + "@vuelidate/validators": "2.0.0-alpha.30", "body-scroll-lock": "2.7.1", "chromatism": "3.0.0", "click-outside-vue3": "4.0.1", @@ -86,7 +86,7 @@ "html-webpack-plugin": "3.2.0", "http-proxy-middleware": "0.21.0", "inject-loader": "2.0.1", - "iso-639-1": "2.1.13", + "iso-639-1": "2.1.15", "isparta-loader": "2.0.0", "json-loader": "0.5.7", "karma": "6.3.17", @@ -107,8 +107,8 @@ "ora": "0.4.1", "postcss-loader": "3.0.0", "raw-loader": "0.5.1", - "sass": "1.20.1", - "sass-loader": "7.2.0", + "sass": "1.53.0", + "sass-loader": "7.3.1", "selenium-server": "2.53.1", "semver": "5.7.1", "serviceworker-webpack-plugin": "1.0.1", @@ -123,7 +123,7 @@ "vue-style-loader": "4.1.2", "webpack": "4.46.0", "webpack-dev-middleware": "3.7.3", - "webpack-hot-middleware": "2.24.3", + "webpack-hot-middleware": "2.25.1", "webpack-merge": "0.20.0" }, "engines": { diff --git a/src/App.scss b/src/App.scss index 5cd0b96e..7e6d0dfc 100644 --- a/src/App.scss +++ b/src/App.scss @@ -310,7 +310,6 @@ nav { border-top-right-radius: 0; } - .underlay, #sidebar, #notifs-column { display: none; diff --git a/src/boot/after_store.js b/src/boot/after_store.js index f655c38f..b8bbbe38 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -370,6 +370,7 @@ const afterStoreSetup = async ({ store, i18n }) => { // Start fetching things that don't need to block the UI store.dispatch('fetchMutes') + store.dispatch('startFetchingAnnouncements') getTOS({ store }) getStickers({ store }) diff --git a/src/boot/routes.js b/src/boot/routes.js index 1ab8209d..00f553c2 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -23,6 +23,7 @@ import RemoteUserResolver from 'components/remote_user_resolver/remote_user_reso import Lists from 'components/lists/lists.vue' import ListTimeline from 'components/list_timeline/list_timeline.vue' import ListEdit from 'components/list_edit/list_edit.vue' +import AnnouncementsPage from 'components/announcements_page/announcements_page.vue' export default (store) => { const validateAuthenticatedRoute = (to, from, next) => { @@ -72,10 +73,11 @@ 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: 'user-profile', path: '/:_(users)?/:name', component: UserProfile }, { name: 'lists', path: '/lists', component: Lists }, { name: 'list-timeline', path: '/lists/:id', component: ListTimeline }, - { name: 'list-edit', path: '/lists/:id/edit', component: ListEdit } + { name: 'list-edit', path: '/lists/:id/edit', component: ListEdit }, + { name: 'announcements', path: '/announcements', component: AnnouncementsPage }, + { name: 'user-profile', path: '/:_(users)?/:name', component: UserProfile } ] if (store.state.instance.pleromaChatMessagesAvailable) { diff --git a/src/components/announcement/announcement.js b/src/components/announcement/announcement.js new file mode 100644 index 00000000..993e3655 --- /dev/null +++ b/src/components/announcement/announcement.js @@ -0,0 +1,105 @@ +import { mapState } from 'vuex' +import AnnouncementEditor from '../announcement_editor/announcement_editor.vue' +import RichContent from '../rich_content/rich_content.jsx' +import localeService from '../../services/locale/locale.service.js' + +const Announcement = { + components: { + AnnouncementEditor, + RichContent + }, + data () { + return { + editing: false, + editedAnnouncement: { + content: '', + startsAt: undefined, + endsAt: undefined, + allDay: undefined + }, + editError: '' + } + }, + props: { + announcement: Object + }, + computed: { + ...mapState({ + currentUser: state => state.users.currentUser + }), + content () { + return this.announcement.content + }, + isRead () { + return this.announcement.read + }, + publishedAt () { + const time = this.announcement['published_at'] + if (!time) { + return + } + + return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale)) + }, + startsAt () { + const time = this.announcement['starts_at'] + if (!time) { + return + } + + return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale)) + }, + endsAt () { + const time = this.announcement['ends_at'] + if (!time) { + return + } + + return this.formatTimeOrDate(time, localeService.internalToBrowserLocale(this.$i18n.locale)) + }, + inactive () { + return this.announcement.inactive + } + }, + methods: { + markAsRead () { + if (!this.isRead) { + return this.$store.dispatch('markAnnouncementAsRead', this.announcement.id) + } + }, + deleteAnnouncement () { + return this.$store.dispatch('deleteAnnouncement', this.announcement.id) + }, + formatTimeOrDate (time, locale) { + const d = new Date(time) + return this.announcement['all_day'] ? d.toLocaleDateString(locale) : d.toLocaleString(locale) + }, + enterEditMode () { + this.editedAnnouncement.content = this.announcement.pleroma['raw_content'] + this.editedAnnouncement.startsAt = this.announcement['starts_at'] + this.editedAnnouncement.endsAt = this.announcement['ends_at'] + this.editedAnnouncement.allDay = this.announcement['all_day'] + this.editing = true + }, + submitEdit () { + this.$store.dispatch('editAnnouncement', { + id: this.announcement.id, + ...this.editedAnnouncement + }) + .then(() => { + this.editing = false + }) + .catch(error => { + this.editError = error.error + }) + }, + cancelEdit () { + this.editing = false + }, + clearError () { + this.editError = undefined + } + } +} + +export default Announcement diff --git a/src/components/announcement/announcement.vue b/src/components/announcement/announcement.vue new file mode 100644 index 00000000..5f64232a --- /dev/null +++ b/src/components/announcement/announcement.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/src/components/announcement_editor/announcement_editor.js b/src/components/announcement_editor/announcement_editor.js new file mode 100644 index 00000000..79a03afe --- /dev/null +++ b/src/components/announcement_editor/announcement_editor.js @@ -0,0 +1,13 @@ +import Checkbox from '../checkbox/checkbox.vue' + +const AnnouncementEditor = { + components: { + Checkbox + }, + props: { + announcement: Object, + disabled: Boolean + } +} + +export default AnnouncementEditor diff --git a/src/components/announcement_editor/announcement_editor.vue b/src/components/announcement_editor/announcement_editor.vue new file mode 100644 index 00000000..0f29f9f7 --- /dev/null +++ b/src/components/announcement_editor/announcement_editor.vue @@ -0,0 +1,60 @@ +