From 23af996313efe7f9fe144c9a209e488102f506b7 Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Fri, 16 May 2025 16:45:05 +0200 Subject: [PATCH 1/5] feat: introduce API support for important/sensitive conversations Signed-off-by: Maksim Sukharev --- src/__mocks__/capabilities.ts | 1 + src/services/conversationsService.ts | 40 ++++++++++++++++++++++++++++ src/types/index.ts | 4 +++ 3 files changed, 45 insertions(+) diff --git a/src/__mocks__/capabilities.ts b/src/__mocks__/capabilities.ts index 48fc9036bca..f5930d60498 100644 --- a/src/__mocks__/capabilities.ts +++ b/src/__mocks__/capabilities.ts @@ -99,6 +99,7 @@ export const mockedCapabilities: Capabilities = { 'dashboard-event-rooms', 'mutual-calendar-events', 'upcoming-reminders', + 'sensitive-conversations', // Conditional features 'message-expiration', 'reactions', diff --git a/src/services/conversationsService.ts b/src/services/conversationsService.ts index 6966d6f0d55..fe040e2a03e 100644 --- a/src/services/conversationsService.ts +++ b/src/services/conversationsService.ts @@ -52,6 +52,10 @@ import type { setConversationPermissionsResponse, setConversationMessageExpirationParams, setConversationMessageExpirationResponse, + markConversationAsImportantResponse, + markConversationAsUnimportantResponse, + markConversationAsSensitiveResponse, + markConversationAsInsensitiveResponse, } from '../types/index.ts' /** @@ -191,6 +195,38 @@ async function removeFromFavorites(token: string): removeConversationFromFavorit return axios.delete(generateOcsUrl('apps/spreed/api/v4/room/{token}/favorite', { token })) } +/** + * Mark a conversation as important + * @param token The conversation token of the conversation to be favorites + */ +async function markAsImportant(token: string): markConversationAsImportantResponse { + return axios.post(generateOcsUrl('apps/spreed/api/v4/room/{token}/important', { token })) +} + +/** + * Unmark an important conversation + * @param token The token of the conversation to be removed from favorites + */ +async function markAsUnimportant(token: string): markConversationAsUnimportantResponse { + return axios.delete(generateOcsUrl('apps/spreed/api/v4/room/{token}/important', { token })) +} + +/** + * Mark a conversation as important + * @param token The token of the conversation to be favorites + */ +async function markAsSensitive(token: string): markConversationAsSensitiveResponse { + return axios.post(generateOcsUrl('apps/spreed/api/v4/room/{token}/sensitive', { token })) +} + +/** + * Remove a conversation from the favorites + * @param token The token of the conversation to be removed from favorites + */ +async function markAsInsensitive(token: string): markConversationAsInsensitiveResponse { + return axios.delete(generateOcsUrl('apps/spreed/api/v4/room/{token}/sensitive', { token })) +} + /** * Add a conversation to the archive * @param token The token of the conversation to be archived @@ -362,6 +398,10 @@ export { unbindConversationFromObject, addToFavorites, removeFromFavorites, + markAsImportant, + markAsUnimportant, + markAsSensitive, + markAsInsensitive, archiveConversation, unarchiveConversation, setNotificationLevel, diff --git a/src/types/index.ts b/src/types/index.ts index 26da0280231..e70630e8972 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -157,6 +157,10 @@ export type setConversationPermissionsParams = Required export type setConversationMessageExpirationParams = Required['requestBody']['content']['application/json'] export type setConversationMessageExpirationResponse = ApiResponse +export type markConversationAsImportantResponse = ApiResponse +export type markConversationAsUnimportantResponse = ApiResponse +export type markConversationAsSensitiveResponse = ApiResponse +export type markConversationAsInsensitiveResponse = ApiResponse export type JoinRoomFullResponse = { headers: ApiResponseHeaders, From e05dda6f05a59308baa4ef7e4e01fa809a6e47fd Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Fri, 16 May 2025 18:13:14 +0200 Subject: [PATCH 2/5] feat: add UI elements for important/sensitive conversations Signed-off-by: Maksim Sukharev --- .../NotificationsSettings.vue | 51 +++++++++++++ .../ConversationsList/Conversation.vue | 76 +++++++++++++++++++ src/store/conversationsStore.js | 34 +++++++++ 3 files changed, 161 insertions(+) diff --git a/src/components/ConversationSettings/NotificationsSettings.vue b/src/components/ConversationSettings/NotificationsSettings.vue index 38d513de0b2..32b07c21cf0 100644 --- a/src/components/ConversationSettings/NotificationsSettings.vue +++ b/src/components/ConversationSettings/NotificationsSettings.vue @@ -29,6 +29,32 @@ @update:model-value="setNotificationCalls"> {{ t('spreed', 'Notify about calls in this conversation') }} + + + {{ t('spreed', 'Important conversation') }} + + +

+ {{ t('spreed', '"Do not disturb" user status is ignored for important conversations') }} +

+ + + {{ t('spreed', 'Sensitive conversation') }} + + +

+ {{ t('spreed', 'Message preview will be disabled in conversation list and notifications') }} +

@@ -44,6 +70,9 @@ import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwit import { PARTICIPANT } from '../../constants.ts' import { hasTalkFeature } from '../../services/CapabilitiesManager.ts' +const supportImportantConversations = hasTalkFeature('local', 'important-conversations') +const supportSensitiveConversations = hasTalkFeature('local', 'sensitive-conversations') + const notificationLevels = [ { value: PARTICIPANT.NOTIFY.ALWAYS, label: t('spreed', 'All messages') }, { value: PARTICIPANT.NOTIFY.MENTION, label: t('spreed', '@-mentions only') }, @@ -67,6 +96,8 @@ export default { setup() { return { notificationLevels, + supportImportantConversations, + supportSensitiveConversations, } }, @@ -74,6 +105,8 @@ export default { return { notifyCalls: this.conversation.notificationCalls === PARTICIPANT.NOTIFY_CALLS.ON, notificationLevel: this.conversation.notificationLevel.toString(), + isImportant: this.conversation.isImportant, + isSensitive: this.conversation.isSensitive, } }, @@ -115,6 +148,24 @@ export default { const notificationCalls = isChecked ? PARTICIPANT.NOTIFY_CALLS.ON : PARTICIPANT.NOTIFY_CALLS.OFF await this.$store.dispatch('setNotificationCalls', { token: this.conversation.token, notificationCalls }) }, + + /** + * Toggle the important flag for the conversation + * + * @param {boolean} isImportant The important flag to set. + */ + async toggleImportant(isImportant) { + await this.$store.dispatch('toggleImportant', { token: this.conversation.token, isImportant }) + }, + + /** + * Toggle the sensitive flag for the conversation + * + * @param {boolean} isSensitive The sensitive flag to set. + */ + async toggleSensitive(isSensitive) { + await this.$store.dispatch('toggleSensitive', { token: this.conversation.token, isSensitive }) + } }, } diff --git a/src/components/LeftSidebar/ConversationsList/Conversation.vue b/src/components/LeftSidebar/ConversationsList/Conversation.vue index 40f1be4d6d7..8d7a20b9290 100644 --- a/src/components/LeftSidebar/ConversationsList/Conversation.vue +++ b/src/components/LeftSidebar/ConversationsList/Conversation.vue @@ -163,6 +163,33 @@ {{ t('spreed', 'Notify about calls') }} + + @@ -239,7 +266,9 @@ import IconDelete from 'vue-material-design-icons/Delete.vue' import IconExitToApp from 'vue-material-design-icons/ExitToApp.vue' import IconEye from 'vue-material-design-icons/Eye.vue' import IconEyeOff from 'vue-material-design-icons/EyeOff.vue' +import IconMessageAlert from 'vue-material-design-icons/MessageAlert.vue' import IconPhoneRing from 'vue-material-design-icons/PhoneRing.vue' +import IconShieldLock from 'vue-material-design-icons/ShieldLock.vue' import IconStar from 'vue-material-design-icons/Star.vue' import IconVideo from 'vue-material-design-icons/Video.vue' import IconVolumeHigh from 'vue-material-design-icons/VolumeHigh.vue' @@ -263,6 +292,8 @@ import { hasTalkFeature } from '../../../services/CapabilitiesManager.ts' import { copyConversationLinkToClipboard } from '../../../utils/handleUrl.ts' const supportsArchive = hasTalkFeature('local', 'archived-conversations-v2') +const supportImportantConversations = hasTalkFeature('local', 'important-conversations') +const supportSensitiveConversations = hasTalkFeature('local', 'sensitive-conversations') const notificationLevels = [ { value: PARTICIPANT.NOTIFY.ALWAYS, label: t('spreed', 'All messages') }, @@ -287,7 +318,9 @@ export default { IconExitToApp, IconEye, IconEyeOff, + IconMessageAlert, IconPhoneRing, + IconShieldLock, IconStar, IconVolumeHigh, IconVolumeOff, @@ -322,6 +355,7 @@ export default { canDeleteConversation: false, canLeaveConversation: false, hasCall: false, + isImportant: false, isSensitive: false, } }, @@ -345,6 +379,8 @@ export default { return { AVATAR, supportsArchive, + supportImportantConversations, + supportSensitiveConversations, submenu, isLeaveDialogOpen, isDeleteDialogOpen, @@ -374,6 +410,10 @@ export default { : t('spreed', 'Archive conversation') }, + labelImportantHint() { + return t('spreed', 'Ignore "Do not disturb"') + }, + dialogLeaveMessage() { return t('spreed', 'Do you really want to leave "{displayName}"?', this.item, undefined, { escape: false, @@ -529,6 +569,24 @@ export default { }) }, + /** + * Toggle the important flag for the conversation + * + * @param {boolean} isImportant The important flag to set. + */ + async toggleImportant(isImportant) { + await this.$store.dispatch('toggleImportant', { token: this.item.token, isImportant }) + }, + + /** + * Toggle the sensitive flag for the conversation + * + * @param {boolean} isSensitive The sensitive flag to set. + */ + async toggleSensitive(isSensitive) { + await this.$store.dispatch('toggleSensitive', { token: this.item.token, isSensitive }) + }, + onClick() { // add as temporary item that will refresh after the joining process is complete if (this.isSearchResult) { @@ -607,6 +665,24 @@ export default { white-space: nowrap; } } + + // FIXME: migrate to subline once it's ready + &__action--with-subline { + :deep(.action-button__longtext-wrapper) { + align-self: center; + padding-block: var(--default-grid-baseline); + line-height: 1; + } + :deep(.action-button__name) { + font-weight: 400; + } + :deep(.action-button__longtext) { + padding: 0; + color: var(--color-text-maxcontrast); + font-size: var(--font-size-small); + line-height: var(--default-font-size); + } + } } .text { diff --git a/src/store/conversationsStore.js b/src/store/conversationsStore.js index 3c4ca7e751d..293534f21b9 100644 --- a/src/store/conversationsStore.js +++ b/src/store/conversationsStore.js @@ -35,6 +35,10 @@ import { createConversation, addToFavorites, removeFromFavorites, + markAsImportant, + markAsUnimportant, + markAsSensitive, + markAsInsensitive, archiveConversation, unarchiveConversation, fetchConversations, @@ -581,6 +585,36 @@ const actions = { } }, + async toggleImportant(context, { token, isImportant }) { + if (!context.getters.conversations[token]) { + return + } + + try { + const response = isImportant + ? await markAsImportant(token) + : await markAsUnimportant(token) + context.commit('addConversation', response.data.ocs.data) + } catch (error) { + console.error('Error while changing the conversation important status: ', error) + } + }, + + async toggleSensitive(context, { token, isSensitive }) { + if (!context.getters.conversations[token]) { + return + } + + try { + const response = isSensitive + ? await markAsSensitive(token) + : await markAsInsensitive(token) + context.commit('addConversation', response.data.ocs.data) + } catch (error) { + console.error('Error while changing the conversation sensitive status: ', error) + } + }, + async toggleLobby({ commit, getters }, { token, enableLobby }) { if (!getters.conversations[token]) { return From d6225b7ab9d2ffb7beba5620df743f72ab47dd9f Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Sat, 17 May 2025 11:46:38 +0200 Subject: [PATCH 3/5] chore(NotificationsSettings): move template Signed-off-by: Maksim Sukharev --- .../NotificationsSettings.vue | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/components/ConversationSettings/NotificationsSettings.vue b/src/components/ConversationSettings/NotificationsSettings.vue index 32b07c21cf0..b710a05b465 100644 --- a/src/components/ConversationSettings/NotificationsSettings.vue +++ b/src/components/ConversationSettings/NotificationsSettings.vue @@ -3,61 +3,6 @@ - SPDX-License-Identifier: AGPL-3.0-or-later --> - - + +