Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/Service/QuickActionsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class QuickActionsService {
public const AVAILABLE_ACTION_STEPS = [
'markAsSpam',
'applyTag',
'removeTags',
'snooze',
'moveThread',
'deleteThread',
Expand Down
23 changes: 20 additions & 3 deletions src/components/Envelope.vue
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,6 @@ import useMainStore from '../store/mainStore.js'
import { mailboxHasRights } from '../util/acl.js'
import { flatRelativeDatetime, groupedRelativeDatetime, messageDateTime } from '../util/relativeDatetime.js'
import { translateTagDisplayName } from '../util/tag.js'
import { hiddenTags } from './tags.js'

export default {
name: 'Envelope',
Expand Down Expand Up @@ -852,7 +851,7 @@ export default {
},

tags() {
let tags = this.mainStore.getEnvelopeTags(this.data.databaseId).filter((tag) => tag.imapLabel && tag.imapLabel !== '$label1' && !(tag.displayName.toLowerCase() in hiddenTags))
let tags = this.mainStore.getEnvelopeExposedTags(this.data.databaseId)

// Don't show follow-up tag in unified mailbox as it has its own section at the top
if (this.mailbox.isUnified) {
Expand Down Expand Up @@ -1005,7 +1004,7 @@ export default {
const quickActions = this.mainStore.getQuickActions().filter((action) => action.accountId === this.data.accountId)
for (const action of quickActions) {
const check = action.actionSteps.every((step) => {
if (['markAsSpam', 'applyTag', 'markAsImportant', 'markAsFavorite'].includes(step.name) && !this.hasWriteAcl) {
if (['markAsSpam', 'applyTag', 'removeTags', 'markAsImportant', 'markAsFavorite'].includes(step.name) && !this.hasWriteAcl) {
return false
}
if (['markAsRead', 'markAsUnread'].includes(step.name) && !this.hasSeenAcl) {
Expand Down Expand Up @@ -1087,6 +1086,9 @@ export default {
showWarning(t('mail', 'Could not apply tag, configured tag not found'))
}
break
case 'removeTags':
await this.removeTags()
break
case 'markAsImportant':
if (!this.isImportant) {
if (this.layoutMessageViewThreaded) {
Expand Down Expand Up @@ -1154,6 +1156,21 @@ export default {
}
},

async removeTags() {
const threadEnvelopes = this.layoutMessageViewThreaded
? this.mainStore.getEnvelopesByThreadRootId(this.data.accountId, this.data.threadRootId)
: [this.data]
for (const envelope of threadEnvelopes) {
const promises = []
const tags = this.mainStore.getEnvelopeExposedTags(envelope.databaseId)
for (const tag of tags) {
const call = this.mainStore.removeEnvelopeTag({ envelope, imapLabel: tag.imapLabel })
promises.push(call)
}
await Promise.all(promises)
}
},

unselect() {
if (this.selected) {
this.$emit('update:selected', false)
Expand Down
3 changes: 1 addition & 2 deletions src/components/TagModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ import TagItem from './TagItem.vue'
import logger from '../logger.js'
import useMainStore from '../store/mainStore.js'
import { validateTag } from '../util/tag.js'
import { hiddenTags } from './tags.js'

function randomColor() {
let randomHexColor = ((1 << 24) * Math.random() | 0).toString(16)
Expand Down Expand Up @@ -114,7 +113,7 @@ export default {
computed: {
...mapStores(useMainStore),
tags() {
return this.mainStore.getTags.filter((tag) => tag.imapLabel !== '$label1' && !(tag.displayName.toLowerCase() in hiddenTags)).sort((a, b) => {
return this.mainStore.getExposedTags().sort((a, b) => {
if (a.isDefaultTag && !b.isDefaultTag) {
return -1
}
Expand Down
3 changes: 1 addition & 2 deletions src/components/ThreadEnvelope.vue
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,6 @@ import useOutboxStore from '../store/outboxStore.js'
import { mailboxHasRights } from '../util/acl.js'
import { translateTagDisplayName } from '../util/tag.js'
import { Text, toPlain } from '../util/text.js'
import { hiddenTags } from './tags.js'

// Ternary loading state
const Loading = Object.seal({
Expand Down Expand Up @@ -625,7 +624,7 @@ export default {
},

tags() {
return this.mainStore.getEnvelopeTags(this.envelope.databaseId).filter((tag) => tag.imapLabel !== '$label1' && !(tag.displayName.toLowerCase() in hiddenTags))
return this.mainStore.getEnvelopeExposedTags(this.envelope.databaseId)
},

hasChangedSubject() {
Expand Down
5 changes: 3 additions & 2 deletions src/components/quickActions/Action.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import CloseIcon from 'vue-material-design-icons/Close.vue'
import DragIcon from 'vue-material-design-icons/Drag.vue'
import Icon from './Icon.vue'
import useMainStore from '../../store/mainStore.js'
import { hiddenTags } from '../tags.js'

export default {
name: 'Action',
Expand Down Expand Up @@ -85,7 +84,7 @@ export default {

options() {
if (this.action.name === 'applyTag') {
return this.mainStore.getTags.filter((tag) => tag.imapLabel !== '$label1' && !(tag.displayName.toLowerCase() in hiddenTags)).map((tag) => ({
return this.mainStore.getExposedTags().map((tag) => ({
value: tag.displayName,
id: tag.id,
}))
Expand All @@ -109,6 +108,8 @@ export default {
return this.t('mail', 'Mark as spam')
case 'applyTag':
return this.t('mail', 'Tag')
case 'removeTags':
return this.t('mail', 'Remove tags')
case 'moveThread':
return this.t('mail', 'Move thread')
case 'deleteThread':
Expand Down
3 changes: 3 additions & 0 deletions src/components/quickActions/Icon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import EmailUnread from 'vue-material-design-icons/EmailOutline.vue'
import ImportantIcon from 'vue-material-design-icons/LabelVariant.vue'
import OpenInNewIcon from 'vue-material-design-icons/OpenInNew.vue'
import IconFavorite from 'vue-material-design-icons/Star.vue'
import TagOffIcon from 'vue-material-design-icons/TagOffOutline.vue'
import TagIcon from 'vue-material-design-icons/TagOutline.vue'
import IconDelete from 'vue-material-design-icons/TrashCanOutline.vue'
export default {
Expand All @@ -32,6 +33,8 @@ export default {
return AlertOctagonIcon
case 'applyTag':
return TagIcon
case 'removeTags':
return TagOffIcon
case 'markAsImportant':
return ImportantIcon
case 'markAsFavorite':
Expand Down
8 changes: 8 additions & 0 deletions src/components/quickActions/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@
</template>
{{ t('mail', 'Tag') }}
</NcActionButton>
<NcActionButton :close-after-click="true" @click="addQuickAction('removeTags')">
<template #icon>
<TagOffIcon :size="20" />
</template>
{{ t('mail', 'Remove tags') }}
</NcActionButton>
<NcActionButton v-if="!deletionAndMovingDisabled" :close-after-click="true" @click="addQuickAction('moveThread')">
<template #icon>
<OpenInNewIcon :size="20" />
Expand Down Expand Up @@ -129,6 +135,7 @@ import OpenInNewIcon from 'vue-material-design-icons/OpenInNew.vue'
import IconEdit from 'vue-material-design-icons/PencilOutline.vue'
import PlusIcon from 'vue-material-design-icons/Plus.vue'
import IconFavorite from 'vue-material-design-icons/Star.vue'
import TagOffIcon from 'vue-material-design-icons/TagOffOutline.vue'
import TagIcon from 'vue-material-design-icons/TagOutline.vue'
import IconDelete from 'vue-material-design-icons/TrashCanOutline.vue'
import Action from './Action.vue'
Expand All @@ -153,6 +160,7 @@ export default {
Draggable,
AlertOctagonIcon,
TagIcon,
TagOffIcon,
OpenInNewIcon,
ImportantIcon,
EmailRead,
Expand Down
8 changes: 8 additions & 0 deletions src/store/mainStore/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
where,
} from 'ramda'
import Vue from 'vue'
import { hiddenTags } from '../../components/tags.js'
import MailboxLockedError from '../../errors/MailboxLockedError.js'
import { matchError } from '../../errors/match.js'
import SyncIncompleteError from '../../errors/SyncIncompleteError.js'
Expand Down Expand Up @@ -2489,6 +2490,13 @@ export default function mainStoreActions() {
const tags = this.envelopes[id]?.tags ?? []
return tags.map((tagId) => this.tags[tagId])
},
getExposedTags() {
return this.getTags.filter((tag) => tag.imapLabel && tag.imapLabel !== '$label1' && !(tag.displayName.toLowerCase() in hiddenTags))
},
getEnvelopeExposedTags(id) {
const exposed = this.getExposedTags().map((t) => t.id)
return this.getEnvelopeTags(id).filter((t) => exposed.includes(t.id))
},
getTag(id) {
return this.tags[id]
},
Expand Down
Loading