From 776bee889e8b4490fa840e88fc4573f5f220cac5 Mon Sep 17 00:00:00 2001 From: floatingghost Date: Fri, 26 Aug 2022 11:58:33 +0000 Subject: [PATCH] Confirmation dialogs (#140) supercedes #135 adapted from https://git.pleroma.social/pleroma/pleroma-fe/-/merge_requests/1431 Co-authored-by: Tusooa Zhu Co-authored-by: FloatingGhost Reviewed-on: https://akkoma.dev/AkkomaGang/pleroma-fe/pulls/140 --- index.html | 1 + src/App.vue | 2 +- .../account_actions/account_actions.js | 31 +++++++- .../account_actions/account_actions.vue | 21 ++++++ src/components/confirm_modal/confirm_modal.js | 37 ++++++++++ .../confirm_modal/confirm_modal.vue | 39 ++++++++++ src/components/desktop_nav/desktop_nav.js | 12 +++- src/components/desktop_nav/desktop_nav.vue | 12 ++++ src/components/dialog_modal/dialog_modal.vue | 4 +- src/components/extra_buttons/extra_buttons.js | 34 +++++++-- .../extra_buttons/extra_buttons.vue | 12 ++++ src/components/follow_button/follow_button.js | 25 ++++++- .../follow_button/follow_button.vue | 21 ++++++ .../follow_request_card.js | 49 ++++++++++++- .../follow_request_card.vue | 22 ++++++ src/components/mobile_nav/mobile_nav.js | 28 +++++++- src/components/mobile_nav/mobile_nav.vue | 20 ++++++ src/components/notification/notification.js | 45 +++++++++++- src/components/notification/notification.vue | 22 ++++++ .../retweet_button/retweet_button.js | 24 ++++++- .../retweet_button/retweet_button.vue | 12 ++++ .../settings_modal/tabs/general_tab.vue | 71 +++++++++++++++++++ src/components/user_card/user_card.js | 24 ++++++- src/components/user_card/user_card.vue | 21 ++++++ src/i18n/en.json | 37 ++++++++++ src/modules/config.js | 8 +++ src/modules/instance.js | 8 +++ 27 files changed, 619 insertions(+), 23 deletions(-) create mode 100644 src/components/confirm_modal/confirm_modal.js create mode 100644 src/components/confirm_modal/confirm_modal.vue diff --git a/index.html b/index.html index 40db0bbe..1b1385de 100644 --- a/index.html +++ b/index.html @@ -15,6 +15,7 @@
+ diff --git a/src/App.vue b/src/App.vue index c3cf33f8..ed4f318e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -59,7 +59,7 @@ - diff --git a/src/components/account_actions/account_actions.js b/src/components/account_actions/account_actions.js index 8fe0fe5e..c227c409 100644 --- a/src/components/account_actions/account_actions.js +++ b/src/components/account_actions/account_actions.js @@ -1,6 +1,8 @@ import ProgressButton from '../progress_button/progress_button.vue' import Popover from '../popover/popover.vue' +import ConfirmModal from '../confirm_modal/confirm_modal.vue' import { library } from '@fortawesome/fontawesome-svg-core' +import { mapState } from 'vuex' import { faEllipsisV } from '@fortawesome/free-solid-svg-icons' @@ -14,13 +16,22 @@ const AccountActions = { 'user', 'relationship' ], data () { - return { } + return { + showingConfirmBlock: false + } }, components: { ProgressButton, - Popover + Popover, + ConfirmModal }, methods: { + showConfirmBlock () { + this.showingConfirmBlock = true + }, + hideConfirmBlock () { + this.showingConfirmBlock = false + }, showRepeats () { this.$store.dispatch('showReblogs', this.user.id) }, @@ -28,7 +39,15 @@ const AccountActions = { this.$store.dispatch('hideReblogs', this.user.id) }, blockUser () { + if (!this.shouldConfirmBlock) { + this.doBlockUser() + } else { + this.showConfirmBlock() + } + }, + doBlockUser () { this.$store.dispatch('blockUser', this.user.id) + this.hideConfirmBlock() }, unblockUser () { this.$store.dispatch('unblockUser', this.user.id) @@ -36,6 +55,14 @@ const AccountActions = { reportUser () { this.$store.dispatch('openUserReportingModal', { userId: this.user.id }) } + }, + computed: { + shouldConfirmBlock () { + return this.$store.getters.mergedConfig.modalOnBlock + }, + ...mapState({ + pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable + }) } } diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue index afd8bb1b..3e65aa62 100644 --- a/src/components/account_actions/account_actions.vue +++ b/src/components/account_actions/account_actions.vue @@ -59,6 +59,27 @@ + + + + + + + diff --git a/src/components/confirm_modal/confirm_modal.js b/src/components/confirm_modal/confirm_modal.js new file mode 100644 index 00000000..96ddc118 --- /dev/null +++ b/src/components/confirm_modal/confirm_modal.js @@ -0,0 +1,37 @@ +import DialogModal from '../dialog_modal/dialog_modal.vue' + +/** + * This component emits the following events: + * cancelled, emitted when the action should not be performed; + * accepted, emitted when the action should be performed; + * + * The caller should close this dialog after receiving any of the two events. + */ +const ConfirmModal = { + components: { + DialogModal + }, + props: { + title: { + type: String + }, + cancelText: { + type: String + }, + confirmText: { + type: String + } + }, + computed: { + }, + methods: { + onCancel () { + this.$emit('cancelled') + }, + onAccept () { + this.$emit('accepted') + } + } +} + +export default ConfirmModal diff --git a/src/components/confirm_modal/confirm_modal.vue b/src/components/confirm_modal/confirm_modal.vue new file mode 100644 index 00000000..5783c794 --- /dev/null +++ b/src/components/confirm_modal/confirm_modal.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/src/components/desktop_nav/desktop_nav.js b/src/components/desktop_nav/desktop_nav.js index 3307b5d5..9ba5abc4 100644 --- a/src/components/desktop_nav/desktop_nav.js +++ b/src/components/desktop_nav/desktop_nav.js @@ -1,4 +1,5 @@ import SearchBar from 'components/search_bar/search_bar.vue' +import ConfirmModal from '../confirm_modal/confirm_modal.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faSignInAlt, @@ -38,7 +39,8 @@ library.add( export default { components: { - SearchBar + SearchBar, + ConfirmModal }, data: () => ({ searchBarHidden: true, @@ -48,7 +50,8 @@ export default { window.CSS.supports('-moz-mask-size', 'contain') || window.CSS.supports('-ms-mask-size', 'contain') || window.CSS.supports('-o-mask-size', 'contain') - ) + ), + showingConfirmLogout: false }), computed: { enableMask () { return this.supportsMask && this.$store.state.instance.logoMask }, @@ -92,7 +95,10 @@ export default { hideSitename () { return this.$store.state.instance.hideSitename }, logoLeft () { return this.$store.state.instance.logoLeft }, currentUser () { return this.$store.state.users.currentUser }, - privateMode () { return this.$store.state.instance.private } + privateMode () { return this.$store.state.instance.private }, + shouldConfirmLogout () { + return this.$store.getters.mergedConfig.modalOnLogout + } }, methods: { scrollToTop () { diff --git a/src/components/desktop_nav/desktop_nav.vue b/src/components/desktop_nav/desktop_nav.vue index 25b4fe8e..32e3c73f 100644 --- a/src/components/desktop_nav/desktop_nav.vue +++ b/src/components/desktop_nav/desktop_nav.vue @@ -167,6 +167,18 @@ + + + {{ $t('login.logout_confirm') }} + + diff --git a/src/components/dialog_modal/dialog_modal.vue b/src/components/dialog_modal/dialog_modal.vue index 06b270c3..1a42b0e5 100644 --- a/src/components/dialog_modal/dialog_modal.vue +++ b/src/components/dialog_modal/dialog_modal.vue @@ -39,7 +39,7 @@ right: 0; top: 0; background: rgba(27,31,35,.5); - z-index: 99; + z-index: 2000; } } @@ -51,7 +51,7 @@ margin: 15vh auto; position: fixed; transform: translateX(-50%); - z-index: 999; + z-index: 2001; cursor: default; display: block; background-color: $fallback--bg; diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js index dd45b6b9..f7045110 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 ConfirmModal from '../confirm_modal/confirm_modal.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faEllipsisH, @@ -25,15 +26,35 @@ library.add( ) const ExtraButtons = { - props: [ 'status' ], - components: { Popover }, + props: ['status'], + components: { + Popover, + ConfirmModal + }, + data () { + return { + expanded: false, + showingDeleteDialog: false + } + }, methods: { deleteStatus () { - const confirmed = window.confirm(this.$t('status.delete_confirm')) - if (confirmed) { - this.$store.dispatch('deleteStatus', { id: this.status.id }) + if (this.shouldConfirmDelete) { + this.showDeleteStatusConfirmDialog() + } else { + this.doDeleteStatus() } }, + doDeleteStatus () { + this.$store.dispatch('deleteStatus', { id: this.status.id }) + this.hideDeleteStatusConfirmDialog() + }, + showDeleteStatusConfirmDialog () { + this.showingDeleteDialog = true + }, + hideDeleteStatusConfirmDialog () { + this.showingDeleteDialog = false + }, pinStatus () { this.$store.dispatch('pinStatus', this.status.id) .then(() => this.$emit('onSuccess')) @@ -91,6 +112,9 @@ const ExtraButtons = { }, statusLink () { return `${this.$store.state.instance.server}${this.$router.resolve({ name: 'conversation', params: { id: this.status.id } }).href}` + }, + shouldConfirmDelete () { + return this.$store.getters.mergedConfig.modalOnDelete } } } diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue index a3c3c767..2b574bbd 100644 --- a/src/components/extra_buttons/extra_buttons.vue +++ b/src/components/extra_buttons/extra_buttons.vue @@ -125,6 +125,18 @@ icon="ellipsis-h" /> + + + {{ $t('status.delete_confirm') }} + + diff --git a/src/components/follow_button/follow_button.js b/src/components/follow_button/follow_button.js index 3edbcb86..443aa9bc 100644 --- a/src/components/follow_button/follow_button.js +++ b/src/components/follow_button/follow_button.js @@ -1,12 +1,20 @@ +import ConfirmModal from '../confirm_modal/confirm_modal.vue' import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate' export default { props: ['relationship', 'user', 'labelFollowing', 'buttonClass'], + components: { + ConfirmModal + }, data () { return { - inProgress: false + inProgress: false, + showingConfirmUnfollow: false } }, computed: { + shouldConfirmUnfollow () { + return this.$store.getters.mergedConfig.modalOnUnfollow + }, isPressed () { return this.inProgress || this.relationship.following }, @@ -35,6 +43,12 @@ export default { } }, methods: { + showConfirmUnfollow () { + this.showingConfirmUnfollow = true + }, + hideConfirmUnfollow () { + this.showingConfirmUnfollow = false + }, onClick () { this.relationship.following || this.relationship.requested ? this.unfollow() : this.follow() }, @@ -45,12 +59,21 @@ export default { }) }, unfollow () { + if (this.shouldConfirmUnfollow) { + this.showConfirmUnfollow() + } else { + this.doUnfollow() + } + }, + doUnfollow () { const store = this.$store this.inProgress = true requestUnfollow(this.relationship.id, store).then(() => { this.inProgress = false store.commit('removeStatus', { timeline: 'friends', userId: this.relationship.id }) }) + + this.hideConfirmUnfollow() } } } diff --git a/src/components/follow_button/follow_button.vue b/src/components/follow_button/follow_button.vue index 965d5256..e421c15b 100644 --- a/src/components/follow_button/follow_button.vue +++ b/src/components/follow_button/follow_button.vue @@ -7,6 +7,27 @@ @click="onClick" > {{ label }} + + + + + + + diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index cbd75311..b0873bb1 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -1,10 +1,18 @@ import BasicUserCard from '../basic_user_card/basic_user_card.vue' +import ConfirmModal from '../confirm_modal/confirm_modal.vue' import { notificationsFromStore } from '../../services/notification_utils/notification_utils.js' const FollowRequestCard = { props: ['user'], components: { - BasicUserCard + BasicUserCard, + ConfirmModal + }, + data () { + return { + showingApproveConfirmDialog: false, + showingDenyConfirmDialog: false + } }, methods: { findFollowRequestNotificationId () { @@ -13,7 +21,26 @@ const FollowRequestCard = { ) return notif && notif.id }, + showApproveConfirmDialog () { + this.showingApproveConfirmDialog = true + }, + hideApproveConfirmDialog () { + this.showingApproveConfirmDialog = false + }, + showDenyConfirmDialog () { + this.showingDenyConfirmDialog = true + }, + hideDenyConfirmDialog () { + this.showingDenyConfirmDialog = false + }, approveUser () { + if (this.shouldConfirmApprove) { + this.showApproveConfirmDialog() + } else { + this.doApprove() + } + }, + doApprove () { this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) @@ -25,14 +52,34 @@ const FollowRequestCard = { notification.type = 'follow' } }) + this.hideApproveConfirmDialog() }, denyUser () { + if (this.shouldConfirmDeny) { + this.showDenyConfirmDialog() + } else { + this.doDeny() + } + }, + doDeny () { const notifId = this.findFollowRequestNotificationId() this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) .then(() => { this.$store.dispatch('dismissNotificationLocal', { id: notifId }) this.$store.dispatch('removeFollowRequest', this.user) }) + this.hideDenyConfirmDialog() + } + }, + computed: { + mergedConfig () { + return this.$store.getters.mergedConfig + }, + shouldConfirmApprove () { + return this.mergedConfig.modalOnApproveFollow + }, + shouldConfirmDeny () { + return this.mergedConfig.modalOnDenyFollow } } } diff --git a/src/components/follow_request_card/follow_request_card.vue b/src/components/follow_request_card/follow_request_card.vue index 1b12ba4b..835471e7 100644 --- a/src/components/follow_request_card/follow_request_card.vue +++ b/src/components/follow_request_card/follow_request_card.vue @@ -14,6 +14,28 @@ {{ $t('user_card.deny') }} + + + {{ $t('user_card.approve_confirm', { user: user.screen_name_ui }) }} + + + {{ $t('user_card.deny_confirm', { user: user.screen_name_ui }) }} + + diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index 7a356921..bba520d4 100644 --- a/src/components/mobile_nav/mobile_nav.js +++ b/src/components/mobile_nav/mobile_nav.js @@ -1,7 +1,9 @@ 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 GestureService from '../../services/gesture_service/gesture_service' +import { mapGetters } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes, @@ -18,11 +20,13 @@ library.add( const MobileNav = { components: { SideDrawer, - Notifications + Notifications, + ConfirmModal }, data: () => ({ notificationsCloseGesture: undefined, - notificationsOpen: false + notificationsOpen: false, + showingConfirmLogout: false }), created () { this.notificationsCloseGesture = GestureService.swipeGesture( @@ -47,7 +51,11 @@ const MobileNav = { hideSiteName () { return this.mergedConfig.hideSiteName }, - sitename () { return this.$store.state.instance.name } + sitename () { return this.$store.state.instance.name }, + shouldConfirmLogout () { + return this.$store.getters.mergedConfig.modalOnLogout + }, + ...mapGetters(['unreadChatCount']) }, methods: { toggleMobileSidebar () { @@ -73,9 +81,23 @@ const MobileNav = { scrollToTop () { window.scrollTo(0, 0) }, + showConfirmLogout () { + this.showingConfirmLogout = true + }, + hideConfirmLogout () { + this.showingConfirmLogout = false + }, logout () { + if (!this.shouldConfirmLogout) { + this.doLogout() + } else { + this.showConfirmLogout() + } + }, + doLogout () { this.$router.replace('/main/public') this.$store.dispatch('logout') + this.hideConfirmLogout() }, markNotificationsAsSeen () { // this.$refs.notifications.markAsSeen() diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue index 308c8d57..e6a8e694 100644 --- a/src/components/mobile_nav/mobile_nav.vue +++ b/src/components/mobile_nav/mobile_nav.vue @@ -76,6 +76,18 @@ ref="sideDrawer" :logout="logout" /> + + + {{ $t('login.logout_confirm') }} + + @@ -206,6 +218,14 @@ } } } + .confirm-modal.dark-overlay { + &::before { + z-index: 3000; + } + .dialog-modal.panel { + z-index: 3001; + } + } } diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 398bb7a9..af612ccb 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -5,6 +5,7 @@ import UserAvatar from '../user_avatar/user_avatar.vue' import UserCard from '../user_card/user_card.vue' import Timeago from '../timeago/timeago.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' +import ConfirmModal from '../confirm_modal/confirm_modal.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' @@ -36,7 +37,9 @@ const Notification = { return { userExpanded: false, betterShadow: this.$store.state.interface.browserSupport.cssFilter, - unmuted: false + unmuted: false, + showingApproveConfirmDialog: false, + showingDenyConfirmDialog: false } }, props: [ 'notification' ], @@ -46,7 +49,8 @@ const Notification = { UserCard, Timeago, Status, - RichContent + RichContent, + ConfirmModal }, methods: { toggleUserExpanded () { @@ -61,7 +65,26 @@ const Notification = { toggleMute () { this.unmuted = !this.unmuted }, + showApproveConfirmDialog () { + this.showingApproveConfirmDialog = true + }, + hideApproveConfirmDialog () { + this.showingApproveConfirmDialog = false + }, + showDenyConfirmDialog () { + this.showingDenyConfirmDialog = true + }, + hideDenyConfirmDialog () { + this.showingDenyConfirmDialog = false + }, approveUser () { + if (this.shouldConfirmApprove) { + this.showApproveConfirmDialog() + } else { + this.doApprove() + } + }, + doApprove () { this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.dispatch('removeFollowRequest', this.user) this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id }) @@ -71,13 +94,22 @@ const Notification = { notification.type = 'follow' } }) + this.hideApproveConfirmDialog() }, denyUser () { + if (this.shouldConfirmDeny) { + this.showDenyConfirmDialog() + } else { + this.doDeny() + } + }, + doDeny () { this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) .then(() => { this.$store.dispatch('dismissNotificationLocal', { id: this.notification.id }) this.$store.dispatch('removeFollowRequest', this.user) }) + this.hideDenyConfirmDialog() } }, computed: { @@ -107,6 +139,15 @@ const Notification = { isStatusNotification () { return isStatusNotification(this.notification.type) }, + mergedConfig () { + return this.$store.getters.mergedConfig + }, + shouldConfirmApprove () { + return this.mergedConfig.modalOnApproveFollow + }, + shouldConfirmDeny () { + return this.mergedConfig.modalOnDenyFollow + }, ...mapState({ currentUser: state => state.users.currentUser }) diff --git a/src/components/notification/notification.vue b/src/components/notification/notification.vue index 72e31a0c..76101865 100644 --- a/src/components/notification/notification.vue +++ b/src/components/notification/notification.vue @@ -231,6 +231,28 @@ + + + {{ $t('user_card.approve_confirm', { user: user.screen_name_ui }) }} + + + {{ $t('user_card.deny_confirm', { user: user.screen_name_ui }) }} + + diff --git a/src/components/retweet_button/retweet_button.js b/src/components/retweet_button/retweet_button.js index 4f71af0a..9dc4d091 100644 --- a/src/components/retweet_button/retweet_button.js +++ b/src/components/retweet_button/retweet_button.js @@ -1,3 +1,4 @@ +import ConfirmModal from '../confirm_modal/confirm_modal.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faRetweet } from '@fortawesome/free-solid-svg-icons' @@ -5,13 +6,24 @@ library.add(faRetweet) const RetweetButton = { props: ['status', 'loggedIn', 'visibility'], + components: { + ConfirmModal + }, data () { return { - animated: false + animated: false, + showingConfirmDialog: false } }, methods: { retweet () { + if (!this.status.repeated && this.shouldConfirmRepeat) { + this.showConfirmDialog() + } else { + this.doRetweet() + } + }, + doRetweet () { if (!this.status.repeated) { this.$store.dispatch('retweet', { id: this.status.id }) } else { @@ -21,6 +33,13 @@ const RetweetButton = { setTimeout(() => { this.animated = false }, 500) + this.hideConfirmDialog() + }, + showConfirmDialog () { + this.showingConfirmDialog = true + }, + hideConfirmDialog () { + this.showingConfirmDialog = false } }, computed: { @@ -29,6 +48,9 @@ const RetweetButton = { }, mergedConfig () { return this.$store.getters.mergedConfig + }, + shouldConfirmRepeat () { + return this.mergedConfig.modalOnRepeat } } } diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue index e8a77e10..6bb7a283 100644 --- a/src/components/retweet_button/retweet_button.vue +++ b/src/components/retweet_button/retweet_button.vue @@ -33,6 +33,18 @@ > {{ status.repeat_num }} + + + {{ $t('status.repeat_confirm') }} + + diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index 4f5a5bc7..1e696d1a 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -172,6 +172,77 @@ {{ $t('settings.autohide_floating_post_button') }} +
  • +

    {{ $t('settings.columns') }}

    +
  • +
  • + + {{ $t('settings.disable_sticky_headers') }} + +
  • +
  • + + {{ $t('settings.show_scrollbars') }} + +
  • +
  • + + {{ $t('settings.right_sidebar') }} + +
  • +
  • + + {{ $t('settings.third_column_mode') }} + +
  • +
  • +

    {{ $t('settings.confirmation_dialogs') }}

    +
  • +
  • + {{ $t('settings.confirm_dialogs') }} +
      +
    • + + {{ $t('settings.confirm_dialogs_repeat') }} + +
    • +
    • + + {{ $t('settings.confirm_dialogs_unfollow') }} + +
    • +
    • + + {{ $t('settings.confirm_dialogs_block') }} + +
    • +
    • + + {{ $t('settings.confirm_dialogs_mute') }} + +
    • +
    • + + {{ $t('settings.confirm_dialogs_delete') }} + +
    • +
    • + + {{ $t('settings.confirm_dialogs_approve_follow') }} + +
    • +
    • + + {{ $t('settings.confirm_dialogs_deny_follow') }} + +
    • +
    +
  • diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index ef898f96..97e619a2 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -6,6 +6,7 @@ import ModerationTools from '../moderation_tools/moderation_tools.vue' import AccountActions from '../account_actions/account_actions.vue' import Select from '../select/select.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' +import ConfirmModal from '../confirm_modal/confirm_modal.vue' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import { mapGetters } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' @@ -32,7 +33,8 @@ export default { data () { return { followRequestInProgress: false, - betterShadow: this.$store.state.interface.browserSupport.cssFilter + betterShadow: this.$store.state.interface.browserSupport.cssFilter, + showingConfirmMute: false } }, created () { @@ -113,6 +115,9 @@ export default { hideFollowersCount () { return this.isOtherUser && this.user.hide_followers_count }, + shouldConfirmMute () { + return this.mergedConfig.modalOnMute + }, ...mapGetters(['mergedConfig']) }, components: { @@ -123,14 +128,29 @@ export default { ProgressButton, FollowButton, Select, - RichContent + RichContent, + ConfirmModal }, methods: { refetchRelationship () { return this.$store.dispatch('fetchUserRelationship', this.user.id) }, + showConfirmMute () { + this.showingConfirmMute = true + }, + hideConfirmMute () { + this.showingConfirmMute = false + }, muteUser () { + if (!this.shouldConfirmMute) { + this.doMuteUser() + } else { + this.showConfirmMute() + } + }, + doMuteUser () { this.$store.dispatch('muteUser', this.user.id) + this.hideConfirmMute() }, unmuteUser () { this.$store.dispatch('unmuteUser', this.user.id) diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 0f776e55..c04250aa 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -295,6 +295,27 @@ :handle-links="true" />
    + + + + + + + diff --git a/src/i18n/en.json b/src/i18n/en.json index 84e4a185..7d845ca4 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -394,7 +394,17 @@ "chatMessageRadius": "Chat message", "checkboxRadius": "Checkboxes", "collapse_subject": "Collapse posts with subjects", + "columns": "Columns", "composing": "Composing", + "confirmation_dialogs": "Confirmation options", + "confirm_dialogs": "Require confirmation for:", + "confirm_dialogs_repeat": "Repeating a post", + "confirm_dialogs_unfollow": "Unfollowing someone", + "confirm_dialogs_block": "Blocking someone", + "confirm_dialogs_mute": "Muting someone", + "confirm_dialogs_delete": "Deleting a post", + "confirm_dialogs_approve_follow": "Accepting a follow request", + "confirm_dialogs_deny_follow": "Rejecting a follow request", "confirm_new_password": "Confirm new password", "conversation_display": "Conversation display style", "conversation_display_linear": "Linear-style", @@ -819,6 +829,9 @@ "copy_link": "Copy link to post", "delete": "Delete post", "delete_confirm": "Do you really want to delete this post?", + "delete_confirm_title": "Confirm deletion", + "delete_confirm_accept_button": "Yes, delete it", + "delete_confirm_cancel_button": "No, keep it", "expand": "Expand", "external_source": "External source", "favorites": "Favorites", @@ -840,6 +853,10 @@ "replies_list": "Replies:", "replies_list_with_others": "Replies (+{numReplies} other): | Replies (+{numReplies} others):", "reply_to": "Reply to", + "repeat_confirm": "Do you really want to repeat this post?", + "repeat_confirm_title": "Confirm repeat", + "repeat_confirm_accept_button": "Yes, repeat it", + "repeat_confirm_cancel_button": "No, don't repeat", "show_all_attachments": "Show all attachments", "show_all_conversation": "Show full conversation ({numStatus} other post) | Show full conversation ({numStatus} other posts)", "show_all_conversation_with_icon": "{icon} {text}", @@ -948,12 +965,24 @@ "strip_media": "Remove media from posts" }, "approve": "Approve", + "approve_confirm_title": "Approve follow request", + "approve_confirm": "Are you sure you want to let this user follow you?", + "approve_confirm_accept_button": "Yes, accept", + "approve_confirm_cancel_button": "No, cancel", "block": "Block", + "block_confirm": "Are you sure you want to block {user}?", + "block_confirm_title": "Block user", + "block_confirm_cancel_button": "No, don't block", + "block_confirm_accept_button": "Yes, block", "block_progress": "Blocking…", "blocked": "Blocked!", "bot": "Bot", "deactivated": "Deactivated", "deny": "Deny", + "deny_confirm_title": "Deny follow request", + "deny_confirm": "Are you sure you want to deny this user's follow request?", + "deny_confirm_accept_button": "Yes, deny", + "deny_confirm_cancel_button": "No, cancel", "domain_muted": "Unblock domain", "edit_profile": "Edit profile", "favorites": "Favorites", @@ -979,6 +1008,10 @@ "mention": "Mention", "message": "Message", "mute": "Mute", + "mute_confirm": "Are you sure you want to mute {user}?", + "mute_confirm_title": "Mute user", + "mute_confirm_cancel_button": "No, don't mute", + "mute_confirm_accept_button": "Yes, mute", "mute_domain": "Block domain", "mute_progress": "Muting…", "muted": "Muted", @@ -991,6 +1024,10 @@ "subscribe": "Subscribe", "unblock": "Unblock", "unblock_progress": "Unblocking…", + "unfollow_confirm": "Are you sure you want to unfollow {user}?", + "unfollow_confirm_title": "Unfollow user", + "unfollow_confirm_cancel_button": "No, don't unfollow", + "unfollow_confirm_accept_button": "Yes, unfollow", "unmute": "Unmute", "unmute_progress": "Unmuting…", "unsubscribe": "Unsubscribe" diff --git a/src/modules/config.js b/src/modules/config.js index 5b60520b..f97e5a8f 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -81,6 +81,14 @@ export const defaultState = { minimalScopesMode: undefined, // instance default // This hides statuses filtered via a word filter hideFilteredStatuses: undefined, // instance default + modalOnRepeat: undefined, // instance default + modalOnUnfollow: undefined, // instance default + modalOnBlock: undefined, // instance default + modalOnMute: undefined, // instance default + modalOnDelete: undefined, // instance default + modalOnLogout: undefined, // instance default + modalOnApproveFollow: undefined, // instance default + modalOnDenyFollow: undefined, // instance default playVideosInModal: false, useOneClickNsfw: false, useContainFit: true, diff --git a/src/modules/instance.js b/src/modules/instance.js index faa8692a..f9118e16 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -38,6 +38,14 @@ const defaultState = { hideSiteName: false, hideUserStats: false, muteBotStatuses: false, + modalOnRepeat: false, + modalOnUnfollow: false, + modalOnBlock: true, + modalOnMute: false, + modalOnDelete: true, + modalOnLogout: true, + modalOnApproveFollow: false, + modalOnDenyFollow: false, loginMethod: 'password', logo: '/static/logo.svg', logoMargin: '.2em',