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:
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,