logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://anongit.hacktivis.me/git/pleroma-fe.git/
commit: 68093b627656af77d66b8f0b4805c05e36864295
parent 41f54b687bcad076110ad508bba90a6ed69615f9
Author: Henry Jameson <me@hjkos.com>
Date:   Thu, 16 Jan 2025 20:14:05 +0200

abstracted mute confirmation dialog into its own component. mutes in status actions work now

Diffstat:

Asrc/components/confirm_modal/mute_confirm.js111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/confirm_modal/mute_confirm.vue58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/settings_modal/tabs/general_tab.vue10++++++++++
Msrc/components/status_action_buttons/action_button.js7++++++-
Msrc/components/status_action_buttons/action_button_container.js70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/components/status_action_buttons/action_button_container.vue142+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/components/status_action_buttons/buttons_definitions.js5-----
Msrc/components/user_card/user_card.js31+++----------------------------
Msrc/components/user_card/user_card.scss5-----
Msrc/components/user_card/user_card.vue50+++++---------------------------------------------
Msrc/i18n/en.json2++
Msrc/modules/config.js2++
Msrc/modules/instance.js2++
13 files changed, 356 insertions(+), 139 deletions(-)

diff --git a/src/components/confirm_modal/mute_confirm.js b/src/components/confirm_modal/mute_confirm.js @@ -0,0 +1,111 @@ +import { unitToSeconds } from 'src/services/date_utils/date_utils.js' +import { mapGetters } from 'vuex' + +import ConfirmModal from './confirm_modal.vue' +import Select from 'src/components/select/select.vue' + +export default { + props: ['type', 'user'], + emits: ['hide', 'show', 'muted'], + data: () => ({ + showing: false, + muteExpiryAmount: 2, + muteExpiryUnit: 'hours' + }), + components: { + ConfirmModal, + Select + }, + computed: { + muteExpiryValue () { + unitToSeconds(this.muteExpiryUnit, this.muteExpiryAmount) + }, + muteExpiryUnits () { + return ['minutes', 'hours', 'days'] + }, + domain () { + return this.user.fqn.split('@')[1] + }, + keypath () { + if (this.type === 'domain') { + return 'status.mute_domain_confirm' + } else if (this.type === 'conversation') { + return 'status.mute_conversation_confirm' + } else { + return 'user_card.mute_confirm' + } + }, + userIsMuted () { + return this.$store.getters.relationship(this.user.id).muting + }, + conversationIsMuted () { + return this.status.conversation_muted + }, + domainIsMuted () { + return new Set(this.$store.state.users.currentUser.domainMutes).has(this.domain) + }, + shouldConfirm () { + switch (this.type) { + case 'domain': { + return this.mergedConfig.modalOnMuteDomain + } + case 'conversation': { + return this.mergedConfig.modalOnMuteConversation + } + default: { + return this.mergedConfig.modalOnMute + } + } + }, + ...mapGetters(['mergedConfig']) + }, + methods: { + optionallyPrompt () { + console.log('Triggered') + if (this.shouldConfirm) { + console.log('SHAWN!!') + this.show() + } else { + this.doMute() + } + }, + show () { + this.showing = true + this.$emit('show') + }, + hide () { + this.showing = false + this.$emit('hide') + }, + doMute () { + switch (this.type) { + case 'domain': { + if (!this.domainIsMuted) { + this.$store.dispatch('muteDomain', { id: this.domain, expiresIn: this.muteExpiryValue }) + } else { + this.$store.dispatch('unmuteDomain', { id: this.domain }) + } + break + } + case 'conversation': { + if (!this.conversationIsMuted) { + this.$store.dispatch('muteConversation', { id: this.status.id, expiresIn: this.muteExpiryValue }) + } else { + this.$store.dispatch('unmuteConversation', { id: this.status.id }) + } + break + } + default: { + if (!this.userIsMuted) { + this.$store.dispatch('muteUser', { id: this.user.id, expiresIn: this.muteExpiryValue }) + } else { + this.$store.dispatch('unmuteUser', { id: this.user.id }) + } + break + } + } + this.$emit('muted') + this.hide() + } + } +} diff --git a/src/components/confirm_modal/mute_confirm.vue b/src/components/confirm_modal/mute_confirm.vue @@ -0,0 +1,58 @@ +<template> + <confirm-modal + v-if="showing" + :title="$t('user_card.mute_confirm_title')" + :confirm-text="$t('user_card.mute_confirm_accept_button')" + :cancel-text="$t('user_card.mute_confirm_cancel_button')" + @accepted="doMute" + @cancelled="hide" + > + <i18n-t + :keypath="keypath" + tag="div" + > + <template #domain> + <span v-text="domain" /> + </template> + <template #user> + <span v-text="user.screen_name_ui" /> + </template> + </i18n-t> + <div class="mute-expiry" v-if="type !== 'domain'"> + <p> + <label> + {{ $t('user_card.mute_duration_prompt') }} + </label> + <input + v-model="muteExpiryAmount" + type="number" + class="input expiry-amount hide-number-spinner" + :min="0" + > + {{ ' ' }} + <Select + v-model="muteExpiryUnit" + unstyled="true" + class="expiry-unit" + > + <option + v-for="unit in muteExpiryUnits" + :key="unit" + :value="unit" + > + {{ $t(`time.unit.${unit}_short`, ['']) }} + </option> + </Select> + </p> + </div> + </confirm-modal> +</template> + +<script src="./mute_confirm.js" /> + +<style lang="scss"> +.expiry-amount { + width: 4em; + text-align: right; +} +</style> diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue @@ -117,6 +117,16 @@ </BooleanSetting> </li> <li> + <BooleanSetting path="modalOnMuteConversation"> + {{ $t('settings.confirm_dialogs_mute_conversation') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="modalOnMuteDomain"> + {{ $t('settings.confirm_dialogs_mute_domain') }} + </BooleanSetting> + </li> + <li> <BooleanSetting path="modalOnDelete"> {{ $t('settings.confirm_dialogs_delete') }} </BooleanSetting> diff --git a/src/components/status_action_buttons/action_button.js b/src/components/status_action_buttons/action_button.js @@ -84,8 +84,13 @@ export default { } ] }, + userIsMuted () { + return this.$store.getters.relationship(this.status.user.id).muting + }, + threadIsMuted () { + return this.status.thread_muted + }, buttonInnerClass () { - if (!this.extra) console.log(this.button.name) return [ this.button.name + '-button', { diff --git a/src/components/status_action_buttons/action_button_container.js b/src/components/status_action_buttons/action_button_container.js @@ -1,5 +1,6 @@ import ActionButton from './action_button.vue' import Popover from 'src/components/popover/popover.vue' +import MuteConfirm from 'src/components/confirm_modal/mute_confirm.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { @@ -17,7 +18,72 @@ library.add( export default { components: { ActionButton, - Popover + Popover, + MuteConfirm }, - props: ['button'] + props: ['button', 'status'], + mounted () { + if (this.button.name === 'mute') { + this.$store.dispatch('fetchDomainMutes') + } + }, + computed: { + buttonClass () { + return [ + this.button.name + '-button', + { + '-with-extra': this.button.name === 'bookmark', + '-extra': this.extra, + '-quick': !this.extra + } + ] + }, + user () { + return this.status.user + }, + userIsMuted () { + return this.$store.getters.relationship(this.user.id).muting + }, + conversationIsMuted () { + return this.status.thread_muted + }, + domain () { + return this.user.fqn.split('@')[1] + }, + domainIsMuted () { + return new Set(this.$store.state.users.currentUser.domainMutes).has(this.domain) + } + }, + methods: { + unmuteUser () { + return this.$store.dispatch('unmuteUser', this.user.id) + }, + unmuteThread () { + return this.$store.dispatch('unmuteConversation', this.user.id) + }, + unmuteDomain () { + return this.$store.dispatch('unmuteDomain', this.user.id) + }, + toggleUserMute () { + if (this.userIsMuted) { + this.unmuteUser() + } else { + this.$refs.confirmUser.optionallyPrompt() + } + }, + toggleConversationMute () { + if (this.conversationIsMuted) { + this.unmuteConversation() + } else { + this.$refs.confirmConversation.optionallyPrompt() + } + }, + toggleDomainMute () { + if (this.domainIsMuted) { + this.unmuteDomain() + } else { + this.$refs.confirmDomain.optionallyPrompt() + } + } + } } diff --git a/src/components/status_action_buttons/action_button_container.vue b/src/components/status_action_buttons/action_button_container.vue @@ -1,58 +1,94 @@ <template> - <Popover - trigger="hover" - :placement="$attrs.extra ? 'right' : 'top'" - v-if="button.dropdown?.()" - > - <template #trigger> - {{ props }} - <ActionButton - :button="button" - v-bind.prop="$attrs" - /> - </template> - <template #content> - <div - v-if="button.name === 'mute'" - class="dropdown-menu" - :id="`popup-menu-${randomSeed}`" - role="menu" - > - <div class="menu-item dropdown-item extra-action -icon"> - <button - class="main-button" - @click="() => {}" - > - <FAIcon icon="user" fixed-width /> - {{ $t('status.mute_user') }} - </button> - </div> - <div class="menu-item dropdown-item extra-action -icon"> - <button - class="main-button" - @click="() => {}" - > - <FAIcon icon="folder-tree" fixed-width /> - {{ $t('status.mute_conversation') }} - </button> - </div> - <div class="menu-item dropdown-item extra-action -icon"> - <button - class="main-button" - @click="() => {}" - > - <FAIcon icon="globe" fixed-width /> - {{ $t('status.mute_domain') }} - </button> + <div> + <Popover + trigger="hover" + :placement="$attrs.extra ? 'right' : 'top'" + v-if="button.dropdown?.()" + > + <template #trigger> + {{ props }} + <ActionButton + :button="button" + :status="status" + v-bind.prop="$attrs" + /> + </template> + <template #content> + <div + v-if="button.name === 'mute'" + class="dropdown-menu" + :id="`popup-menu-${randomSeed}`" + role="menu" + > + <div class="menu-item dropdown-item extra-action -icon"> + <button + class="main-button" + @click="toggleUserMute" + > + <FAIcon icon="user" fixed-width /> + <template v-if="userIsMuted"> + {{ $t('status.unmute_user') }} + </template> + <template v-else> + {{ $t('status.mute_user') }} + </template> + </button> + </div> + <div class="menu-item dropdown-item extra-action -icon"> + <button + class="main-button" + @click="toggleUserMute" + > + <FAIcon icon="folder-tree" fixed-width /> + <template v-if="threadIsMuted"> + {{ $t('status.unmute_conversation') }} + </template> + <template v-else> + {{ $t('status.mute_conversation') }} + </template> + </button> + </div> + <div class="menu-item dropdown-item extra-action -icon"> + <button + class="main-button" + @click="toggleDomainMute" + > + <FAIcon icon="globe" fixed-width /> + <template v-if="domainIsMuted"> + {{ $t('status.unmute_domain') }} + </template> + <template v-else> + {{ $t('status.mute_domain') }} + </template> + </button> + </div> </div> - </div> - </template> - </Popover> - <ActionButton - v-else - :button="button" - v-bind="$attrs" - /> + </template> + </Popover> + <ActionButton + v-else + :button="button" + :status="status" + v-bind="$attrs" + /> + <teleport to="#modal"> + <mute-confirm + type="conversation" + :status="this.status" + ref="confirmConversation" + /> + <mute-confirm + type="domain" + :user="this.user" + ref="confirmDomain" + /> + <mute-confirm + type="user" + :user="this.user" + ref="confirmUser" + /> + </teleport> + </div> </template> <script src="./action_button_container.js"/> diff --git a/src/components/status_action_buttons/buttons_definitions.js b/src/components/status_action_buttons/buttons_definitions.js @@ -94,11 +94,6 @@ export const BUTTONS = [{ toggleable: true, dropdown: true // action ({ status, dispatch, emit }) { - // if (status.thread_muted) { - // return dispatch('unmuteConversation', { id: status.id }) - // } else { - // return dispatch('muteConversation', { id: status.id }) - // } // } }, { // ========= diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js @@ -1,4 +1,3 @@ -import { unitToSeconds } from 'src/services/date_utils/date_utils.js' import UserAvatar from '../user_avatar/user_avatar.vue' import RemoteFollow from '../remote_follow/remote_follow.vue' import ProgressButton from '../progress_button/progress_button.vue' @@ -9,7 +8,7 @@ import UserNote from '../user_note/user_note.vue' import Select from '../select/select.vue' import UserLink from '../user_link/user_link.vue' import RichContent from 'src/components/rich_content/rich_content.jsx' -import ConfirmModal from '../confirm_modal/confirm_modal.vue' +import MuteConfirm from '../confirm_modal/mute_confirm.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' @@ -48,7 +47,6 @@ export default { data () { return { followRequestInProgress: false, - showingConfirmMute: false, muteExpiryAmount: 0, muteExpiryUnit: 'minutes' } @@ -141,12 +139,6 @@ export default { supportsNote () { return 'note' in this.relationship }, - shouldConfirmMute () { - return this.mergedConfig.modalOnMute - }, - muteExpiryUnits () { - return ['minutes', 'hours', 'days'] - }, ...mapGetters(['mergedConfig']) }, components: { @@ -160,28 +152,11 @@ export default { RichContent, UserLink, UserNote, - ConfirmModal + MuteConfirm }, methods: { - showConfirmMute () { - this.showingConfirmMute = true - }, - hideConfirmMute () { - this.showingConfirmMute = false - }, muteUser () { - if (!this.shouldConfirmMute) { - this.doMuteUser() - } else { - this.showConfirmMute() - } - }, - doMuteUser () { - this.$store.dispatch('muteUser', { - id: this.user.id, - expiresIn: this.shouldConfirmMute ? unitToSeconds(this.muteExpiryUnit, this.muteExpiryAmount) : 0 - }) - this.hideConfirmMute() + this.$refs.confirmation.optionallyPrompt() }, unmuteUser () { this.$store.dispatch('unmuteUser', this.user.id) diff --git a/src/components/user_card/user_card.scss b/src/components/user_card/user_card.scss @@ -325,8 +325,3 @@ text-decoration: none; } } - -.mute-expiry { - display: flex; - flex-direction: row; -} diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue @@ -311,51 +311,11 @@ /> </div> <teleport to="#modal"> - <confirm-modal - v-if="showingConfirmMute" - :title="$t('user_card.mute_confirm_title')" - :confirm-text="$t('user_card.mute_confirm_accept_button')" - :cancel-text="$t('user_card.mute_confirm_cancel_button')" - @accepted="doMuteUser" - @cancelled="hideConfirmMute" - > - <i18n-t - keypath="user_card.mute_confirm" - tag="div" - > - <template #user> - <span - v-text="user.screen_name_ui" - /> - </template> - </i18n-t> - <div - class="mute-expiry" - > - <label> - {{ $t('user_card.mute_duration_prompt') }} - </label> - <input - v-model="muteExpiryAmount" - type="number" - class="expiry-amount hide-number-spinner" - :min="0" - > - <Select - v-model="muteExpiryUnit" - unstyled="true" - class="expiry-unit" - > - <option - v-for="unit in muteExpiryUnits" - :key="unit" - :value="unit" - > - {{ $t(`time.${unit}_short`, ['']) }} - </option> - </Select> - </div> - </confirm-modal> + <mute-confirm + type="user" + :user="this.user" + ref="confirmation" + /> </teleport> </div> </template> diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -490,6 +490,8 @@ "confirm_dialogs_unfollow": "unfollowing a user", "confirm_dialogs_block": "blocking a user", "confirm_dialogs_mute": "muting a user", + "confirm_dialogs_mute_domain": "muting domains", + "confirm_dialogs_mute_conversation": "muting conversations", "confirm_dialogs_delete": "deleting a status", "confirm_dialogs_logout": "logging out", "confirm_dialogs_approve_follow": "approving a follower", diff --git a/src/modules/config.js b/src/modules/config.js @@ -137,6 +137,8 @@ export const defaultState = { modalOnUnfollow: undefined, // instance default modalOnBlock: undefined, // instance default modalOnMute: undefined, // instance default + modalOnMuteConversation: undefined, // instance default + modalOnMuteDomain: undefined, // instance default modalOnDelete: undefined, // instance default modalOnLogout: undefined, // instance default modalOnApproveFollow: undefined, // instance default diff --git a/src/modules/instance.js b/src/modules/instance.js @@ -77,6 +77,8 @@ const defaultState = { modalOnUnfollow: false, modalOnBlock: true, modalOnMute: false, + modalOnMuteConversation: false, + modalOnMuteDomain: true, modalOnDelete: true, modalOnLogout: true, modalOnApproveFollow: false,