diff --git a/changelog.d/timed.add b/changelog.d/timed.add new file mode 100644 index 000000000..66cb50c9f --- /dev/null +++ b/changelog.d/timed.add @@ -0,0 +1 @@ +Support for expiring mutes and blocks (if available) diff --git a/src/App.scss b/src/App.scss index 1b3133a7c..d56306c9e 100644 --- a/src/App.scss +++ b/src/App.scss @@ -679,11 +679,6 @@ option { } } -.btn-block { - display: block; - width: 100%; -} - .btn-group { position: relative; display: inline-flex; diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 65c4c9205..fd4c57229 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -274,6 +274,7 @@ const getNodeInfo = async ({ store }) => { store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled }) store.dispatch('setInstanceOption', { name: 'quotingAvailable', value: features.includes('quote_posting') }) store.dispatch('setInstanceOption', { name: 'groupActorAvailable', value: features.includes('pleroma:group_actors') }) + store.dispatch('setInstanceOption', { name: 'blockExpiration', value: features.includes('pleroma:block_expiration') }) store.dispatch('setInstanceOption', { name: 'localBubbleInstances', value: metadata.localBubbleInstances ?? [] }) const uploadLimits = metadata.uploadLimits diff --git a/src/components/account_actions/account_actions.js b/src/components/account_actions/account_actions.js index 9a63f57eb..f8ad0e11b 100644 --- a/src/components/account_actions/account_actions.js +++ b/src/components/account_actions/account_actions.js @@ -3,6 +3,7 @@ import ProgressButton from '../progress_button/progress_button.vue' import Popover from '../popover/popover.vue' import UserListMenu from 'src/components/user_list_menu/user_list_menu.vue' import ConfirmModal from '../confirm_modal/confirm_modal.vue' +import UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faEllipsisV @@ -27,15 +28,10 @@ const AccountActions = { ProgressButton, Popover, UserListMenu, - ConfirmModal + ConfirmModal, + UserTimedFilterModal }, methods: { - showConfirmBlock () { - this.showingConfirmBlock = true - }, - hideConfirmBlock () { - this.showingConfirmBlock = false - }, showConfirmRemoveUserFromFollowers () { this.showingConfirmRemoveFollower = true }, @@ -49,10 +45,14 @@ const AccountActions = { this.$store.dispatch('hideReblogs', this.user.id) }, blockUser () { - if (!this.shouldConfirmBlock) { - this.doBlockUser() + if (this.$refs.timedBlockDialog) { + this.$refs.timedBlockDialog.optionallyPrompt() } else { - this.showConfirmBlock() + if (!this.shouldConfirmBlock) { + this.doBlockUser() + } else { + this.showingConfirmBlock = true + } } }, doBlockUser () { @@ -91,6 +91,7 @@ const AccountActions = { return this.$store.getters.mergedConfig.modalOnRemoveUserFromFollowers }, ...mapState({ + blockExpirationSupported: state => state.instance.blockExpiration, pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable }) } diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue index fd4837ee4..f3cca45d0 100644 --- a/src/components/account_actions/account_actions.vue +++ b/src/components/account_actions/account_actions.vue @@ -96,7 +96,8 @@ + diff --git a/src/components/block_card/block_card.js b/src/components/block_card/block_card.js index 0bf4e37bc..b5be9e7b7 100644 --- a/src/components/block_card/block_card.js +++ b/src/components/block_card/block_card.js @@ -1,12 +1,9 @@ +import { mapState } from 'vuex' + import BasicUserCard from '../basic_user_card/basic_user_card.vue' const BlockCard = { props: ['userId'], - data () { - return { - progress: false - } - }, computed: { user () { return this.$store.getters.findUser(this.userId) @@ -16,23 +13,32 @@ const BlockCard = { }, blocked () { return this.relationship.blocking - } + }, + blockExpiryAvailable () { + return this.user.block_expires_at !== undefined + }, + blockExpiry () { + return this.user.block_expires_at == null + ? this.$t('user_card.block_expires_forever') + : this.$t('user_card.block_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()]) + }, + ...mapState({ + blockExpirationSupported: state => state.instance.blockExpiration, + }) }, components: { BasicUserCard }, methods: { unblockUser () { - this.progress = true - this.$store.dispatch('unblockUser', this.user.id).then(() => { - this.progress = false - }) + this.$store.dispatch('unblockUser', this.user.id) }, blockUser () { - this.progress = true - this.$store.dispatch('blockUser', this.user.id).then(() => { - this.progress = false - }) + if (this.blockExpirationSupported) { + this.$refs.timedBlockDialog.optionallyPrompt() + } else { + this.$store.dispatch('blockUser', this.user.id) + } } } } diff --git a/src/components/block_card/block_card.vue b/src/components/block_card/block_card.vue index b14ef8448..9f64a8e8c 100644 --- a/src/components/block_card/block_card.vue +++ b/src/components/block_card/block_card.vue @@ -1,33 +1,32 @@ diff --git a/src/components/confirm_modal/confirm_modal.vue b/src/components/confirm_modal/confirm_modal.vue index 9af00edc4..992792ede 100644 --- a/src/components/confirm_modal/confirm_modal.vue +++ b/src/components/confirm_modal/confirm_modal.vue @@ -11,6 +11,7 @@ -
-

- - - {{ ' ' }} - -

-
diff --git a/src/components/mute_card/mute_card.js b/src/components/mute_card/mute_card.js index cbec0e9bb..895586888 100644 --- a/src/components/mute_card/mute_card.js +++ b/src/components/mute_card/mute_card.js @@ -1,12 +1,8 @@ import BasicUserCard from '../basic_user_card/basic_user_card.vue' +import UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue' const MuteCard = { props: ['userId'], - data () { - return { - progress: false - } - }, computed: { user () { return this.$store.getters.findUser(this.userId) @@ -16,23 +12,26 @@ const MuteCard = { }, muted () { return this.relationship.muting + }, + muteExpiryAvailable () { + return this.user.mute_expires_at !== undefined + }, + muteExpiry () { + return this.user.mute_expires_at == null + ? this.$t('user_card.mute_expires_forever') + : this.$t('user_card.mute_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()]) } }, components: { - BasicUserCard + BasicUserCard, + UserTimedFilterModal }, methods: { unmuteUser () { - this.progress = true - this.$store.dispatch('unmuteUser', this.userId).then(() => { - this.progress = false - }) + this.$store.dispatch('unmuteUser', this.userId) }, muteUser () { - this.progress = true - this.$store.dispatch('muteUser', this.userId).then(() => { - this.progress = false - }) + this.$refs.timedMuteDialog.optionallyPrompt() } } } diff --git a/src/components/mute_card/mute_card.vue b/src/components/mute_card/mute_card.vue index 97e91cbca..60638d3ba 100644 --- a/src/components/mute_card/mute_card.vue +++ b/src/components/mute_card/mute_card.vue @@ -1,33 +1,32 @@ diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js index 5ff137062..9a4ed814d 100644 --- a/src/components/settings_modal/tabs/filtering_tab.js +++ b/src/components/settings_modal/tabs/filtering_tab.js @@ -1,5 +1,6 @@ import { cloneDeep } from 'lodash' import { mapState, mapActions } from 'pinia' +import { mapState as mapVuexState } from 'vuex' import { v4 as uuidv4 } from 'uuid'; import { useServerSideStorageStore } from 'src/stores/serverSideStorage' @@ -30,6 +31,11 @@ const FilteringTab = { value: mode, label: this.$t(`settings.reply_visibility_${mode}`) })), + muteBlockLv1Options: ['ask', 'forever', 'temporarily'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`user_card.mute_block_${mode}`) + })), muteFiltersDraftObject: cloneDeep(useServerSideStorageStore().prefsStorage.simple.muteFilters), muteFiltersDraftDirty: Object.fromEntries( Object.entries( @@ -93,6 +99,43 @@ const FilteringTab = { muteFiltersObject: store => store.prefsStorage.simple.muteFilters } ), + ...mapVuexState({ + blockExpirationSupported: state => state.instance.blockExpiration + }), + onMuteDefaultActionLv1: { + get () { + const value = this.$store.state.config.onMuteDefaultAction + if (value === 'ask' || value === 'forever') { + return value + } else { + return 'temporarily' + } + }, + set (value) { + let realValue = value + if (value !== 'ask' && value !== 'forever') { + realValue = '14d' + } + this.$store.dispatch('setOption', { name: 'onMuteDefaultAction', value: realValue }) + } + }, + onBlockDefaultActionLv1: { + get () { + const value = this.$store.state.config.onBlockDefaultAction + if (value === 'ask' || value === 'forever') { + return value + } else { + return 'temporarily' + } + }, + set (value) { + let realValue = value + if (value !== 'ask' && value !== 'forever') { + realValue = '14d' + } + this.$store.dispatch('setOption', { name: 'onBlockDefaultAction', value: realValue }) + } + }, muteFiltersDraft () { return Object.entries(this.muteFiltersDraftObject) }, @@ -107,7 +150,7 @@ const FilteringTab = { ...mapActions(useServerSideStorageStore, ['setPreference', 'unsetPreference', 'pushServerSideStorage']), getDatetimeLocal (timestamp) { const date = new Date(timestamp) - let fmt = new Intl.NumberFormat("en-US", {minimumIntegerDigits: 2}) + const fmt = new Intl.NumberFormat("en-US", {minimumIntegerDigits: 2}) const datetime = [ date.getFullYear(), '-', diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue index 6db82d3c4..2b05d1906 100644 --- a/src/components/settings_modal/tabs/filtering_tab.vue +++ b/src/components/settings_modal/tabs/filtering_tab.vue @@ -80,6 +80,66 @@

{{ $t('settings.filter.mute_filter') }}

diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index 55c76225c..353a891f6 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -8,7 +8,8 @@ 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 MuteConfirm from '../confirm_modal/mute_confirm.vue' +import UserTimedFilterModal from 'src/components/user_timed_filter_modal/user_timed_filter_modal.vue' + import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import { mapGetters } from 'vuex' import { usePostStatusStore } from 'src/stores/post_status' @@ -48,6 +49,19 @@ export default { 'onClose', 'hasNoteEditor' ], + components: { + UserAvatar, + RemoteFollow, + ModerationTools, + AccountActions, + ProgressButton, + FollowButton, + Select, + RichContent, + UserLink, + UserNote, + UserTimedFilterModal + }, data () { return { followRequestInProgress: false, @@ -145,24 +159,27 @@ export default { supportsNote () { return 'note' in this.relationship }, + muteExpiryAvailable () { + return this.user.mute_expires_at !== undefined + }, + muteExpiry () { + return this.user.mute_expires_at == null + ? this.$t('user_card.mute_expires_forever') + : this.$t('user_card.mute_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()]) + }, + blockExpiryAvailable () { + return this.user.block_expires_at !== undefined + }, + blockExpiry () { + return this.user.block_expires_at == null + ? this.$t('user_card.block_expires_forever') + : this.$t('user_card.block_expires_at', [new Date(this.user.mute_expires_at).toLocaleString()]) + }, ...mapGetters(['mergedConfig']) }, - components: { - UserAvatar, - RemoteFollow, - ModerationTools, - AccountActions, - ProgressButton, - FollowButton, - Select, - RichContent, - UserLink, - UserNote, - MuteConfirm - }, methods: { muteUser () { - this.$refs.confirmation.optionallyPrompt() + this.$refs.timedMuteDialog.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 index d263be1c0..418bef08b 100644 --- a/src/components/user_card/user_card.scss +++ b/src/components/user_card/user_card.scss @@ -8,6 +8,11 @@ --_still-image-label-visibility: hidden; } + .btn-mute, .btn-mention { + display: block; + width: 100%; + } + .panel-heading { padding: 0.5em 0; text-align: center; diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 36935424d..e51a16271 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -136,6 +136,18 @@ size="sm" /> + + {{ muteExpiry }} + + + {{ blockExpiry }} +