diff options
Diffstat (limited to 'src/components')
27 files changed, 469 insertions, 37 deletions
diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js index 68654f69..9baf63f2 100644 --- a/src/components/emoji_input/emoji_input.js +++ b/src/components/emoji_input/emoji_input.js @@ -1,4 +1,5 @@ import Completion from '../../services/completion/completion.js' +import genRandomSeed from '../../services/random_seed/random_seed.service.js' import EmojiPicker from '../emoji_picker/emoji_picker.vue' import Popover from 'src/components/popover/popover.vue' import ScreenReaderNotice from 'src/components/screen_reader_notice/screen_reader_notice.vue' @@ -110,7 +111,7 @@ const EmojiInput = { }, data () { return { - randomSeed: `${Math.random()}`.replace('.', '-'), + randomSeed: genRandomSeed(), input: undefined, caretEl: undefined, highlighted: -1, diff --git a/src/components/emoji_picker/emoji_picker.vue b/src/components/emoji_picker/emoji_picker.vue index b8d33309..0788f34c 100644 --- a/src/components/emoji_picker/emoji_picker.vue +++ b/src/components/emoji_picker/emoji_picker.vue @@ -3,7 +3,7 @@ ref="popover" trigger="click" popover-class="emoji-picker popover-default" - :trigger-attrs="{ 'aria-hidden': true }" + :trigger-attrs="{ 'aria-hidden': true, tabindex: -1 }" @show="onPopoverShown" @close="onPopoverClosed" > diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js index 48b960b2..e2c88ceb 100644 --- a/src/components/extra_buttons/extra_buttons.js +++ b/src/components/extra_buttons/extra_buttons.js @@ -1,4 +1,5 @@ import Popover from '../popover/popover.vue' +import genRandomSeed from '../../services/random_seed/random_seed.service.js' import ConfirmModal from '../confirm_modal/confirm_modal.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -40,7 +41,8 @@ const ExtraButtons = { data () { return { expanded: false, - showingDeleteDialog: false + showingDeleteDialog: false, + randomSeed: genRandomSeed() } }, methods: { @@ -152,6 +154,15 @@ const ExtraButtons = { editingAvailable () { return this.$store.state.instance.editingAvailable }, shouldConfirmDelete () { return this.$store.getters.mergedConfig.modalOnDelete + }, + triggerAttrs () { + return { + title: this.$t('status.more_actions'), + id: `popup-trigger-${this.randomSeed}`, + 'aria-controls': `popup-menu-${this.randomSeed}`, + 'aria-expanded': this.expanded, + 'aria-haspopup': 'menu' + } } } } diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue index c1c15c0f..b7d3b1d3 100644 --- a/src/components/extra_buttons/extra_buttons.vue +++ b/src/components/extra_buttons/extra_buttons.vue @@ -2,6 +2,7 @@ <Popover class="ExtraButtons" trigger="click" + :trigger-attrs="triggerAttrs" placement="top" :offset="{ y: 5 }" :bound-to="{ x: 'container' }" @@ -10,10 +11,15 @@ @close="onClose" > <template #content="{close}"> - <div class="dropdown-menu"> + <div + class="dropdown-menu" + role="menu" + :id="`popup-menu-${randomSeed}`" + > <button v-if="canMute && !status.thread_muted" class="button-default dropdown-item dropdown-item-icon" + role="menuitem" @click.prevent="muteConversation" > <FAIcon @@ -24,6 +30,7 @@ <button v-if="canMute && status.thread_muted" class="button-default dropdown-item dropdown-item-icon" + role="menuitem" @click.prevent="unmuteConversation" > <FAIcon @@ -34,6 +41,7 @@ <button v-if="!status.pinned && canPin" class="button-default dropdown-item dropdown-item-icon" + role="menuitem" @click.prevent="pinStatus" @click="close" > @@ -45,6 +53,7 @@ <button v-if="status.pinned && canPin" class="button-default dropdown-item dropdown-item-icon" + role="menuitem" @click.prevent="unpinStatus" @click="close" > @@ -57,6 +66,7 @@ <button v-if="!status.bookmarked" class="button-default dropdown-item dropdown-item-icon" + role="menuitem" @click.prevent="bookmarkStatus" @click="close" > @@ -68,6 +78,7 @@ <button v-if="status.bookmarked" class="button-default dropdown-item dropdown-item-icon" + role="menuitem" @click.prevent="unbookmarkStatus" @click="close" > @@ -80,6 +91,7 @@ <button v-if="ownStatus && editingAvailable" class="button-default dropdown-item dropdown-item-icon" + role="menuitem" @click.prevent="editStatus" @click="close" > @@ -91,6 +103,7 @@ <button v-if="isEdited && editingAvailable" class="button-default dropdown-item dropdown-item-icon" + role="menuitem" @click.prevent="showStatusHistory" @click="close" > @@ -102,6 +115,7 @@ <button v-if="canDelete" class="button-default dropdown-item dropdown-item-icon" + role="menuitem" @click.prevent="deleteStatus" @click="close" > @@ -112,6 +126,7 @@ </button> <button class="button-default dropdown-item dropdown-item-icon" + role="menuitem" @click.prevent="copyLink" @click="close" > @@ -123,6 +138,7 @@ <a v-if="!status.is_local" class="button-default dropdown-item dropdown-item-icon" + role="menuitem" title="Source" :href="status.external_url" target="_blank" @@ -134,6 +150,7 @@ </a> <button class="button-default dropdown-item dropdown-item-icon" + role="menuitem" @click.prevent="reportStatus" @click="close" > diff --git a/src/components/extra_notifications/extra_notifications.js b/src/components/extra_notifications/extra_notifications.js new file mode 100644 index 00000000..1bb0f837 --- /dev/null +++ b/src/components/extra_notifications/extra_notifications.js @@ -0,0 +1,48 @@ +import { mapGetters } from 'vuex' + +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faUserPlus, + faComments, + faBullhorn +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faUserPlus, + faComments, + faBullhorn +) + +const ExtraNotifications = { + computed: { + shouldShowChats () { + return this.mergedConfig.showExtraNotifications && this.mergedConfig.showChatsInExtraNotifications && this.unreadChatCount + }, + shouldShowAnnouncements () { + return this.mergedConfig.showExtraNotifications && this.mergedConfig.showAnnouncementsInExtraNotifications && this.unreadAnnouncementCount + }, + shouldShowFollowRequests () { + return this.mergedConfig.showExtraNotifications && this.mergedConfig.showFollowRequestsInExtraNotifications && this.followRequestCount + }, + hasAnythingToShow () { + return this.shouldShowChats || this.shouldShowAnnouncements || this.shouldShowFollowRequests + }, + shouldShowCustomizationTip () { + return this.mergedConfig.showExtraNotificationsTip && this.hasAnythingToShow + }, + currentUser () { + return this.$store.state.users.currentUser + }, + ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount', 'followRequestCount', 'mergedConfig']) + }, + methods: { + openNotificationSettings () { + return this.$store.dispatch('openSettingsModalTab', 'notifications') + }, + dismissConfigurationTip () { + return this.$store.dispatch('setOption', { name: 'showExtraNotificationsTip', value: false }) + } + } +} + +export default ExtraNotifications diff --git a/src/components/extra_notifications/extra_notifications.vue b/src/components/extra_notifications/extra_notifications.vue new file mode 100644 index 00000000..6e0456a5 --- /dev/null +++ b/src/components/extra_notifications/extra_notifications.vue @@ -0,0 +1,113 @@ +<template> + <div class="ExtraNotifications"> + <div + v-if="shouldShowChats" + class="notification unseen" + > + <div class="notification-overlay" /> + <router-link + class="button-unstyled -link extra-notification" + :to="{ name: 'chats', params: { username: currentUser.screen_name } }" + > + <FAIcon + fixed-width + class="fa-scale-110 icon" + icon="comments" + /> + {{ $tc('notifications.unread_chats', unreadChatCount, { num: unreadChatCount }) }} + </router-link> + </div> + <div + v-if="shouldShowAnnouncements" + class="notification unseen" + > + <div class="notification-overlay" /> + <router-link + class="button-unstyled -link extra-notification" + :to="{ name: 'announcements' }" + > + <FAIcon + fixed-width + class="fa-scale-110 icon" + icon="bullhorn" + /> + {{ $tc('notifications.unread_announcements', unreadAnnouncementCount, { num: unreadAnnouncementCount }) }} + </router-link> + </div> + <div + v-if="shouldShowFollowRequests" + class="notification unseen" + > + <div class="notification-overlay" /> + <router-link + class="button-unstyled -link extra-notification" + :to="{ name: 'friend-requests' }" + > + <FAIcon + fixed-width + class="fa-scale-110 icon" + icon="user-plus" + /> + {{ $tc('notifications.unread_follow_requests', followRequestCount, { num: followRequestCount }) }} + </router-link> + </div> + <i18n-t + v-if="shouldShowCustomizationTip" + tag="span" + class="notification tip extra-notification" + keypath="notifications.configuration_tip" + > + <template #theSettings> + <button + class="button-unstyled -link" + @click="openNotificationSettings" + > + {{ $t('notifications.configuration_tip_settings') }} + </button> + </template> + <template #dismiss> + <button + class="button-unstyled -link" + @click="dismissConfigurationTip" + > + {{ $t('notifications.configuration_tip_dismiss') }} + </button> + </template> + </i18n-t> + </div> +</template> + +<script src="./extra_notifications.js" /> + +<style lang="scss"> +@import "../../variables"; + +.ExtraNotifications { + width: 100%; + display: flex; + flex-direction: column; + align-items: stretch; + + .notification { + width: 100%; + border-bottom: 1px solid; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + display: flex; + flex-direction: column; + align-items: stretch; + } + + .extra-notification { + padding: 1em; + } + + .icon { + margin-right: 0.5em; + } + + .tip { + display: inline; + } +} +</style> diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue index 0e58476f..9e9ec7aa 100644 --- a/src/components/global_notice_list/global_notice_list.vue +++ b/src/components/global_notice_list/global_notice_list.vue @@ -32,7 +32,7 @@ top: calc(var(--navbar-height) + 0.5em); width: 100%; pointer-events: none; - z-index: var(--ZI_navbar_popovers); + z-index: var(--ZI_modals_popovers); display: flex; flex-direction: column; align-items: center; diff --git a/src/components/interactions/interactions.vue b/src/components/interactions/interactions.vue index b7291c02..35d03562 100644 --- a/src/components/interactions/interactions.vue +++ b/src/components/interactions/interactions.vue @@ -39,6 +39,7 @@ <Notifications ref="notifications" :no-heading="true" + :no-extra="true" :minimal-mode="true" :filter-mode="filterMode" /> diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index dad1f6aa..b5325116 100644 --- a/src/components/mobile_nav/mobile_nav.js +++ b/src/components/mobile_nav/mobile_nav.js @@ -1,7 +1,10 @@ import SideDrawer from '../side_drawer/side_drawer.vue' import Notifications from '../notifications/notifications.vue' import ConfirmModal from '../confirm_modal/confirm_modal.vue' -import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils' +import { + unseenNotificationsFromStore, + countExtraNotifications +} from '../../services/notification_utils/notification_utils' import GestureService from '../../services/gesture_service/gesture_service' import NavigationPins from 'src/components/navigation/navigation_pins.vue' import { mapGetters } from 'vuex' @@ -50,7 +53,7 @@ const MobileNav = { return unseenNotificationsFromStore(this.$store) }, unseenNotificationsCount () { - return this.unseenNotifications.length + return this.unseenNotifications.length + countExtraNotifications(this.$store) }, hideSitename () { return this.$store.state.instance.hideSitename }, sitename () { return this.$store.state.instance.name }, diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index d499d3d6..571df0f1 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -1,12 +1,14 @@ import { computed } from 'vue' import { mapGetters } from 'vuex' import Notification from '../notification/notification.vue' +import ExtraNotifications from '../extra_notifications/extra_notifications.vue' import NotificationFilters from './notification_filters.vue' import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js' import { notificationsFromStore, filteredNotificationsFromStore, - unseenNotificationsFromStore + unseenNotificationsFromStore, + countExtraNotifications } from '../../services/notification_utils/notification_utils.js' import FaviconService from '../../services/favicon_service/favicon_service.js' import { library } from '@fortawesome/fontawesome-svg-core' @@ -23,7 +25,8 @@ const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30 const Notifications = { components: { Notification, - NotificationFilters + NotificationFilters, + ExtraNotifications }, props: { // Disables panel styles, unread mark, potentially other notification-related actions @@ -31,6 +34,11 @@ const Notifications = { minimalMode: Boolean, // Custom filter mode, an array of strings, possible values 'mention', 'repeat', 'like', 'follow', used to override global filter for use in "Interactions" timeline filterMode: Array, + // Do not show extra notifications + noExtra: { + type: Boolean, + default: false + }, // Disable teleporting (i.e. for /users/user/notifications) disableTeleport: Boolean }, @@ -65,11 +73,17 @@ const Notifications = { filteredNotifications () { return filteredNotificationsFromStore(this.$store, this.filterMode) }, + unseenCountBadgeText () { + return `${this.unseenCount ? this.unseenCount : ''}${this.extraNotificationsCount ? '*' : ''}` + }, unseenCount () { return this.unseenNotifications.length }, + extraNotificationsCount () { + return countExtraNotifications(this.$store) + }, unseenCountTitle () { - return this.unseenCount + (this.unreadChatCount) + this.unreadAnnouncementCount + return this.unseenNotifications.length + (this.unreadChatCount) + this.unreadAnnouncementCount }, loading () { return this.$store.state.statuses.notifications.loading @@ -94,6 +108,9 @@ const Notifications = { return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount) }, noSticky () { return this.$store.getters.mergedConfig.disableStickyHeaders }, + showExtraNotifications () { + return !this.noExtra + }, ...mapGetters(['unreadChatCount', 'unreadAnnouncementCount']) }, mounted () { diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue index 633efca6..999f8e9c 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -17,9 +17,9 @@ <div class="title"> {{ $t('notifications.notifications') }} <span - v-if="unseenCount" + v-if="unseenCountBadgeText" class="badge badge-notification unseen-count" - >{{ unseenCount }}</span> + >{{ unseenCountBadgeText }}</span> </div> <div v-if="showScrollTop" @@ -55,6 +55,13 @@ role="feed" > <div + v-if="showExtraNotifications" + role="listitem" + class="notification" + > + <extra-notifications /> + </div> + <div v-for="notification in notificationsToDisplay" :key="notification.id" role="listitem" diff --git a/src/components/poll/poll.js b/src/components/poll/poll.js index e4d6869a..f6001f56 100644 --- a/src/components/poll/poll.js +++ b/src/components/poll/poll.js @@ -1,4 +1,5 @@ import Timeago from 'components/timeago/timeago.vue' +import genRandomSeed from '../../services/random_seed/random_seed.service.js' import RichContent from 'components/rich_content/rich_content.jsx' import { forEach, map } from 'lodash' @@ -13,7 +14,7 @@ export default { return { loading: false, choices: [], - randomSeed: `${Math.random()}`.replace('.', '-') + randomSeed: genRandomSeed() } }, created () { diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index ba49961d..5564b118 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -1,4 +1,5 @@ import statusPoster from '../../services/status_poster/status_poster.service.js' +import genRandomSeed from '../../services/random_seed/random_seed.service.js' import MediaUpload from '../media_upload/media_upload.vue' import ScopeSelector from '../scope_selector/scope_selector.vue' import EmojiInput from '../emoji_input/emoji_input.vue' @@ -162,7 +163,7 @@ const PostStatusForm = { } return { - randomSeed: `${Math.random()}`.replace('.', '-'), + randomSeed: genRandomSeed(), dropFiles: [], uploadingFiles: false, error: null, diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue index 947536a1..1b0674e6 100644 --- a/src/components/react_button/react_button.vue +++ b/src/components/react_button/react_button.vue @@ -11,6 +11,8 @@ /> <span class="button-unstyled popover-trigger" + role="button" + :tabindex="0" :title="$t('tool_tip.add_reaction')" @click.stop.prevent="show" > diff --git a/src/components/report/report.js b/src/components/report/report.js index 76055764..5685aa25 100644 --- a/src/components/report/report.js +++ b/src/components/report/report.js @@ -1,6 +1,7 @@ import Select from '../select/select.vue' import StatusContent from '../status_content/status_content.vue' import Timeago from '../timeago/timeago.vue' +import RichContent from 'src/components/rich_content/rich_content.jsx' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' const Report = { @@ -10,10 +11,12 @@ const Report = { components: { Select, StatusContent, - Timeago + Timeago, + RichContent }, computed: { report () { + console.log(this.$store.state.reports.reports[this.reportId] || {}) return this.$store.state.reports.reports[this.reportId] || {} }, state: { diff --git a/src/components/settings_modal/admin_tabs/frontends_tab.js b/src/components/settings_modal/admin_tabs/frontends_tab.js index a2c27c2a..8163af59 100644 --- a/src/components/settings_modal/admin_tabs/frontends_tab.js +++ b/src/components/settings_modal/admin_tabs/frontends_tab.js @@ -4,6 +4,7 @@ import IntegerSetting from '../helpers/integer_setting.vue' import StringSetting from '../helpers/string_setting.vue' import GroupSetting from '../helpers/group_setting.vue' import Popover from 'src/components/popover/popover.vue' +import PanelLoading from 'src/components/panel_loading/panel_loading.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' import { library } from '@fortawesome/fontawesome-svg-core' @@ -22,12 +23,18 @@ const FrontendsTab = { defaultSource: 'admin' } }, + data () { + return { + working: false + } + }, components: { BooleanSetting, ChoiceSetting, IntegerSetting, StringSetting, GroupSetting, + PanelLoading, Popover }, created () { @@ -42,18 +49,56 @@ const FrontendsTab = { ...SharedComputedObject() }, methods: { + canInstall (frontend) { + const fe = this.frontends.find(f => f.name === frontend.name) + if (!fe) return false + return fe.refs.includes(frontend.ref) + }, + getSuggestedRef (frontend) { + const defaultFe = this.adminDraft[':pleroma'][':frontends'][':primary'] + if (defaultFe?.name === frontend.name && this.canInstall(defaultFe)) { + return defaultFe.ref + } else { + return frontend.refs[0] + } + }, update (frontend, suggestRef) { - const ref = suggestRef || frontend.refs[0] + const ref = suggestRef || this.getSuggestedRef(frontend) const { name } = frontend const payload = { name, ref } + this.working = true this.$store.state.api.backendInteractor.installFrontend({ payload }) - .then((externalUser) => { + .finally(() => { + this.working = false + }) + .then(async (response) => { this.$store.dispatch('loadFrontendsStuff') + if (response.error) { + const reason = await response.error.json() + this.$store.dispatch('pushGlobalNotice', { + level: 'error', + messageKey: 'admin_dash.frontend.failure_installing_frontend', + messageArgs: { + version: name + '/' + ref, + reason: reason.error + }, + timeout: 5000 + }) + } else { + this.$store.dispatch('pushGlobalNotice', { + level: 'success', + messageKey: 'admin_dash.frontend.success_installing_frontend', + messageArgs: { + version: name + '/' + ref + }, + timeout: 2000 + }) + } }) }, setDefault (frontend, suggestRef) { - const ref = suggestRef || frontend.refs[0] + const ref = suggestRef || this.getSuggestedRef(frontend) const { name } = frontend this.$store.commit('updateAdminDraft', { path: [':pleroma', ':frontends', ':primary'], value: { name, ref } }) diff --git a/src/components/settings_modal/admin_tabs/frontends_tab.scss b/src/components/settings_modal/admin_tabs/frontends_tab.scss index e3e04bc6..420d20b3 100644 --- a/src/components/settings_modal/admin_tabs/frontends_tab.scss +++ b/src/components/settings_modal/admin_tabs/frontends_tab.scss @@ -3,6 +3,22 @@ padding: 0; } + .relative { + position: relative; + } + + .overlay { + position: absolute; + background: var(--bg); + // fix buttons showing through + z-index: 2; + opacity: 0.9; + top: 0; + bottom: 0; + left: 0; + right: 0; + } + dd { text-overflow: ellipsis; word-wrap: nowrap; diff --git a/src/components/settings_modal/admin_tabs/frontends_tab.vue b/src/components/settings_modal/admin_tabs/frontends_tab.vue index 13b8fa6b..dd4c9790 100644 --- a/src/components/settings_modal/admin_tabs/frontends_tab.vue +++ b/src/components/settings_modal/admin_tabs/frontends_tab.vue @@ -10,7 +10,6 @@ <li> <h3>{{ $t('admin_dash.frontend.default_frontend') }}</h3> <p>{{ $t('admin_dash.frontend.default_frontend_tip') }}</p> - <p>{{ $t('admin_dash.frontend.default_frontend_tip2') }}</p> <ul class="setting-list"> <li> <StringSetting path=":pleroma.:frontends.:primary.name" /> @@ -24,7 +23,8 @@ </ul> </li> </ul> - <div class="setting-list"> + <div class="setting-list relative"> + <PanelLoading class="overlay" v-if="working"/> <h3>{{ $t('admin_dash.frontend.available_frontends') }}</h3> <ul class="cards-list"> <li @@ -33,9 +33,9 @@ > <strong>{{ frontend.name }}</strong> {{ ' ' }} - <span v-if="adminDraft[':pleroma'][':frontends'][':primary'].name === frontend.name"> + <span v-if="adminDraft[':pleroma'][':frontends'][':primary']?.name === frontend.name"> <i18n-t - v-if="adminDraft[':pleroma'][':frontends'][':primary'].ref === frontend.refs[0]" + v-if="adminDraft[':pleroma'][':frontends'][':primary']?.ref === frontend.refs[0]" keypath="admin_dash.frontend.is_default" /> <i18n-t @@ -86,6 +86,11 @@ ? $t('admin_dash.frontend.reinstall') : $t('admin_dash.frontend.install') }} + <code> + {{ + getSuggestedRef(frontend) + }} + </code> </button> <Popover v-if="frontend.refs.length > 1" @@ -93,13 +98,14 @@ class="button-dropdown" placement="bottom" > - <template #content> + <template #content="{close}"> <div class="dropdown-menu"> <button v-for="ref in frontend.refs" :key="ref" class="button-default dropdown-item" - @click="update(frontend, ref)" + @click.prevent="update(frontend, ref)" + @click="close" > <i18n-t keypath="admin_dash.frontend.install_version"> <template #version> @@ -128,14 +134,19 @@ class="button button-default btn" type="button" :disabled=" - adminDraft[':pleroma'][':frontends'][':primary'].name === frontend.name && - adminDraft[':pleroma'][':frontends'][':primary'].ref === frontend.refs[0] + adminDraft[':pleroma'][':frontends'][':primary']?.name === frontend.name && + adminDraft[':pleroma'][':frontends'][':primary']?.ref === frontend.refs[0] " @click="setDefault(frontend)" > {{ $t('admin_dash.frontend.set_default') }} + <code> + {{ + getSuggestedRef(frontend) + }} + </code> </button> {{ ' ' }} <Popover @@ -144,13 +155,14 @@ class="button-dropdown" placement="bottom" > - <template #content> + <template #content="{close}"> <div class="dropdown-menu"> <button - v-for="ref in frontend.refs.slice(1)" + v-for="ref in frontend.installedRefs || frontend.refs" :key="ref" class="button-default dropdown-item" - @click="setDefault(frontend, ref)" + @click.prevent="setDefault(frontend, ref)" + @click="close" > <i18n-t keypath="admin_dash.frontend.set_default_version"> <template #version> diff --git a/src/components/settings_modal/admin_tabs/instance_tab.vue b/src/components/settings_modal/admin_tabs/instance_tab.vue index a6be776b..a0e3351e 100644 --- a/src/components/settings_modal/admin_tabs/instance_tab.vue +++ b/src/components/settings_modal/admin_tabs/instance_tab.vue @@ -6,6 +6,10 @@ <li> <StringSetting path=":pleroma.:instance.:name" /> </li> + <!-- See https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3963 --> + <li v-if="adminDraft[':pleroma'][':instance'][':favicon'] !== undefined"> + <AttachmentSetting compact path=":pleroma.:instance.:favicon" /> + </li> <li> <StringSetting path=":pleroma.:instance.:email" /> </li> @@ -16,7 +20,7 @@ <StringSetting path=":pleroma.:instance.:short_description" /> </li> <li> - <AttachmentSetting path=":pleroma.:instance.:instance_thumbnail" /> + <AttachmentSetting compact path=":pleroma.:instance.:instance_thumbnail" /> </li> <li> <AttachmentSetting path=":pleroma.:instance.:background_image" /> diff --git a/src/components/settings_modal/helpers/attachment_setting.js b/src/components/settings_modal/helpers/attachment_setting.js index ac5c6f86..c4c04b2b 100644 --- a/src/components/settings_modal/helpers/attachment_setting.js +++ b/src/components/settings_modal/helpers/attachment_setting.js @@ -7,6 +7,7 @@ export default { ...Setting, props: { ...Setting.props, + compact: Boolean, acceptTypes: { type: String, required: false, diff --git a/src/components/settings_modal/helpers/attachment_setting.vue b/src/components/settings_modal/helpers/attachment_setting.vue index bbc5172c..b50231f2 100644 --- a/src/components/settings_modal/helpers/attachment_setting.vue +++ b/src/components/settings_modal/helpers/attachment_setting.vue @@ -2,6 +2,7 @@ <span v-if="matchesExpertLevel" class="AttachmentSetting" + :class="{ '-compact': compact }" > <label :for="path" @@ -24,8 +25,8 @@ {{ backendDescriptionDescription + ' ' }} </p> <div class="attachment-input"> - <div>{{ $t('settings.url') }}</div> - <div class="controls"> + <div class="controls control-field"> + <label for="path">{{ $t('settings.url') }}</label> <input :id="path" class="string-input" @@ -40,7 +41,7 @@ /> <ProfileSettingIndicator :is-profile="isProfileSetting" /> </div> - <div>{{ $t('settings.preview') }}</div> + <div v-if="!compact">{{ $t('settings.preview') }}</div> <Attachment class="attachment" :compact="compact" @@ -50,7 +51,7 @@ @setMedia="onMedia" @naturalSizeLoad="onNaturalSizeLoad" /> - <div class="controls"> + <div class="controls control-upload"> <MediaUpload ref="mediaUpload" class="media-upload-icon" @@ -84,6 +85,35 @@ width: 20em; } + &.-compact { + .attachment-input { + flex-direction: row; + align-items: flex-end; + } + + .attachment { + flex: 0; + order: 0; + display: block; + min-width: 4em; + height: 4em; + align-self: center; + margin-bottom: 0; + } + + .control-field { + order: 1; + min-width: 12em; + margin-left: 0.5em; + } + + .control-upload { + order: 2; + min-width: 12em; + padding: 0 0.5em; + } + } + .controls { margin-bottom: 0.5em; diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue index 41d1b54f..89fdef1a 100644 --- a/src/components/settings_modal/tabs/filtering_tab.vue +++ b/src/components/settings_modal/tabs/filtering_tab.vue @@ -91,6 +91,11 @@ {{ $t('settings.hide_attachments_in_convo') }} </BooleanSetting> </li> + <li> + <BooleanSetting path="hideScrobbles"> + {{ $t('settings.hide_scrobbles') }} + </BooleanSetting> + </li> </ul> </div> <div diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue index fcb92135..4dfba444 100644 --- a/src/components/settings_modal/tabs/notifications_tab.vue +++ b/src/components/settings_modal/tabs/notifications_tab.vue @@ -51,6 +51,47 @@ </li> </ul> </li> + <li> + <BooleanSetting path="showExtraNotifications"> + {{ $t('settings.notification_show_extra') }} + </BooleanSetting> + </li> + <li> + <ul class="setting-list suboptions"> + <li> + <BooleanSetting + path="showChatsInExtraNotifications" + :disabled="!mergedConfig.showExtraNotifications" + > + {{ $t('settings.notification_extra_chats') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="showAnnouncementsInExtraNotifications" + :disabled="!mergedConfig.showExtraNotifications" + > + {{ $t('settings.notification_extra_announcements') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="showFollowRequestsInExtraNotifications" + :disabled="!mergedConfig.showExtraNotifications" + > + {{ $t('settings.notification_extra_follow_requests') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="showExtraNotificationsTip" + :disabled="!mergedConfig.showExtraNotifications" + > + {{ $t('settings.notification_extra_tip') }} + </BooleanSetting> + </li> + </ul> + </li> </ul> </div> diff --git a/src/components/status/status.js b/src/components/status/status.js index e722a635..a339694d 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -39,7 +39,8 @@ import { faThumbtack, faChevronUp, faChevronDown, - faAngleDoubleRight + faAngleDoubleRight, + faPlay } from '@fortawesome/free-solid-svg-icons' library.add( @@ -59,7 +60,8 @@ library.add( faThumbtack, faChevronUp, faChevronDown, - faAngleDoubleRight + faAngleDoubleRight, + faPlay ) const camelCase = name => name.charAt(0).toUpperCase() + name.slice(1) @@ -415,6 +417,12 @@ const Status = { }, shouldDisplayQuote () { return this.quotedStatus && this.displayQuote + }, + scrobblePresent () { + return !this.mergedConfig.hideScrobbles && this.status.user.latestScrobble && this.status.user.latestScrobble.artist + }, + scrobble () { + return this.status.user.latestScrobble } }, methods: { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index c49a9e7b..26fafc91 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -250,6 +250,47 @@ </span> </div> <div + v-if="scrobblePresent" + class="status-rich-presence" + > + <a + v-if="scrobble.externalLink" + :href="scrobble.externalLink" + target="_blank" + > + {{ scrobble.artist }} — {{ scrobble.title }} + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="play" + /> + <span class="status-rich-presence-time"> + <Timeago + template-key="time.in_past" + :time="scrobble.created_at" + :auto-update="60" + /> + </span> + </a> + <span v-if="!scrobble.externalLink"> + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="music" + /> + {{ scrobble.artist }} — {{ scrobble.title }} + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="play" + /> + <span class="status-rich-presence-time"> + <Timeago + template-key="time.in_past" + :time="scrobble.created_at" + :auto-update="60" + /> + </span> + </span> + </div> + <div v-if="isReply || hasMentionsLine" class="heading-reply-row" > diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js index 89f0aa51..8d8a91dc 100644 --- a/src/components/status_content/status_content.js +++ b/src/components/status_content/status_content.js @@ -73,6 +73,10 @@ const StatusContent = { }, computed: { ...controlledOrUncontrolledGetters(['showingTall', 'expandingSubject', 'showingLongSubject']), + statusCard () { + if (!this.status.card) return null + return this.status.card.url === this.status.quote_url ? null : this.status.card + }, hideAttachments () { return (this.mergedConfig.hideAttachments && !this.inConversation) || (this.mergedConfig.hideAttachmentsInConv && this.inConversation) diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue index c0e5c0b9..e977d489 100644 --- a/src/components/status_content/status_content.vue +++ b/src/components/status_content/status_content.vue @@ -43,7 +43,7 @@ /> <div - v-if="status.card && !noHeading && !compact" + v-if="statusCard && !noHeading && !compact" class="link-preview media-body" > <link-preview |
