mirror of
https://github.com/nextcloud/spreed.git
synced 2025-12-17 21:12:20 +01:00
Merge pull request #16313 from nextcloud/feat/noid/split-chat-backend
feat(splitview): add option to toggle between chat views
This commit is contained in:
commit
a3d644d89e
29 changed files with 195 additions and 14 deletions
|
|
@ -201,3 +201,4 @@
|
|||
## 23
|
||||
* `pinned-messages` - Whether messages can be pinned
|
||||
* `federated-shared-items` - Whether shared items endpoints can be called in a federated conversation
|
||||
* `config => chat => style` (local) - User selected chat style (split or unified for now)
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ class Capabilities implements IPublicCapability {
|
|||
'has-translation-task-providers',
|
||||
'typing-privacy',
|
||||
'summary-threshold',
|
||||
'style',
|
||||
],
|
||||
'conversations' => [
|
||||
'can-create',
|
||||
|
|
@ -263,6 +264,7 @@ class Capabilities implements IPublicCapability {
|
|||
'has-translation-task-providers' => false,
|
||||
'typing-privacy' => Participant::PRIVACY_PUBLIC,
|
||||
'summary-threshold' => max(1, $this->appConfig->getAppValueInt('summary_threshold', 100)),
|
||||
'style' => $this->talkConfig->getChatStyle($user?->getUID()),
|
||||
],
|
||||
'conversations' => [
|
||||
'can-create' => $user instanceof IUser && !$this->talkConfig->isNotAllowedToCreateConversations($user),
|
||||
|
|
|
|||
|
|
@ -757,6 +757,28 @@ class Config {
|
|||
return UserPreference::CONVERSATION_LIST_STYLE_TWO_LINES;
|
||||
}
|
||||
|
||||
/**
|
||||
* User setting for chat style
|
||||
*
|
||||
* @param ?string $userId
|
||||
* @return UserPreference::CHAT_STYLE_*
|
||||
*/
|
||||
public function getChatStyle(?string $userId): string {
|
||||
if ($userId !== null) {
|
||||
$userSetting = $this->config->getUserValue(
|
||||
$userId,
|
||||
'spreed',
|
||||
UserPreference::CHAT_STYLE,
|
||||
UserPreference::CHAT_STYLE_SPLIT
|
||||
);
|
||||
|
||||
if (in_array($userSetting, [UserPreference::CHAT_STYLE_SPLIT, UserPreference::CHAT_STYLE_UNIFIED], true)) {
|
||||
return $userSetting;
|
||||
}
|
||||
}
|
||||
return UserPreference::CHAT_STYLE_SPLIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* User setting falling back to admin defined app config
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ class ConfigLexicon implements ILexicon {
|
|||
public function getUserConfigs(): array {
|
||||
return [
|
||||
new Entry(UserPreference::PLAY_SOUNDS, ValueType::BOOL, true),
|
||||
new Entry(UserPreference::CHAT_STYLE, ValueType::STRING, UserPreference::CHAT_STYLE_SPLIT),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -524,6 +524,7 @@ namespace OCA\Talk;
|
|||
* has-translation-task-providers: bool,
|
||||
* typing-privacy: int,
|
||||
* summary-threshold: positive-int,
|
||||
* style: 'split'|'unified',
|
||||
* },
|
||||
* conversations: array{
|
||||
* can-create: bool,
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ class BeforePreferenceSetEventListener implements IEventListener {
|
|||
if ($key === UserPreference::CONVERSATIONS_LIST_STYLE) {
|
||||
return $value === UserPreference::CONVERSATION_LIST_STYLE_TWO_LINES || $value === UserPreference::CONVERSATION_LIST_STYLE_COMPACT;
|
||||
}
|
||||
if ($key === UserPreference::CHAT_STYLE) {
|
||||
return $value === UserPreference::CHAT_STYLE_SPLIT || $value === UserPreference::CHAT_STYLE_UNIFIED;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class UserPreference {
|
|||
public const BLUR_VIRTUAL_BACKGROUND = 'blur_virtual_background';
|
||||
public const CALLS_START_WITHOUT_MEDIA = 'calls_start_without_media';
|
||||
public const CONVERSATIONS_LIST_STYLE = 'conversations_list_style';
|
||||
public const CHAT_STYLE = 'chat_style';
|
||||
public const PLAY_SOUNDS = 'play_sounds';
|
||||
public const TYPING_PRIVACY = 'typing_privacy';
|
||||
public const READ_STATUS_PRIVACY = 'read_status_privacy';
|
||||
|
|
@ -20,4 +21,7 @@ class UserPreference {
|
|||
|
||||
public const CONVERSATION_LIST_STYLE_TWO_LINES = 'two-lines';
|
||||
public const CONVERSATION_LIST_STYLE_COMPACT = 'compact';
|
||||
|
||||
public const CHAT_STYLE_SPLIT = 'split';
|
||||
public const CHAT_STYLE_UNIFIED = 'unified';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -230,7 +230,8 @@
|
|||
"has-translation-providers",
|
||||
"has-translation-task-providers",
|
||||
"typing-privacy",
|
||||
"summary-threshold"
|
||||
"summary-threshold",
|
||||
"style"
|
||||
],
|
||||
"properties": {
|
||||
"max-length": {
|
||||
|
|
@ -255,6 +256,13 @@
|
|||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1
|
||||
},
|
||||
"style": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"split",
|
||||
"unified"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -163,7 +163,8 @@
|
|||
"has-translation-providers",
|
||||
"has-translation-task-providers",
|
||||
"typing-privacy",
|
||||
"summary-threshold"
|
||||
"summary-threshold",
|
||||
"style"
|
||||
],
|
||||
"properties": {
|
||||
"max-length": {
|
||||
|
|
@ -188,6 +189,13 @@
|
|||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1
|
||||
},
|
||||
"style": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"split",
|
||||
"unified"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -163,7 +163,8 @@
|
|||
"has-translation-providers",
|
||||
"has-translation-task-providers",
|
||||
"typing-privacy",
|
||||
"summary-threshold"
|
||||
"summary-threshold",
|
||||
"style"
|
||||
],
|
||||
"properties": {
|
||||
"max-length": {
|
||||
|
|
@ -188,6 +189,13 @@
|
|||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1
|
||||
},
|
||||
"style": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"split",
|
||||
"unified"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -206,7 +206,8 @@
|
|||
"has-translation-providers",
|
||||
"has-translation-task-providers",
|
||||
"typing-privacy",
|
||||
"summary-threshold"
|
||||
"summary-threshold",
|
||||
"style"
|
||||
],
|
||||
"properties": {
|
||||
"max-length": {
|
||||
|
|
@ -231,6 +232,13 @@
|
|||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1
|
||||
},
|
||||
"style": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"split",
|
||||
"unified"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -163,7 +163,8 @@
|
|||
"has-translation-providers",
|
||||
"has-translation-task-providers",
|
||||
"typing-privacy",
|
||||
"summary-threshold"
|
||||
"summary-threshold",
|
||||
"style"
|
||||
],
|
||||
"properties": {
|
||||
"max-length": {
|
||||
|
|
@ -188,6 +189,13 @@
|
|||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1
|
||||
},
|
||||
"style": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"split",
|
||||
"unified"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -206,7 +206,8 @@
|
|||
"has-translation-providers",
|
||||
"has-translation-task-providers",
|
||||
"typing-privacy",
|
||||
"summary-threshold"
|
||||
"summary-threshold",
|
||||
"style"
|
||||
],
|
||||
"properties": {
|
||||
"max-length": {
|
||||
|
|
@ -231,6 +232,13 @@
|
|||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1
|
||||
},
|
||||
"style": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"split",
|
||||
"unified"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -364,7 +364,8 @@
|
|||
"has-translation-providers",
|
||||
"has-translation-task-providers",
|
||||
"typing-privacy",
|
||||
"summary-threshold"
|
||||
"summary-threshold",
|
||||
"style"
|
||||
],
|
||||
"properties": {
|
||||
"max-length": {
|
||||
|
|
@ -389,6 +390,13 @@
|
|||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1
|
||||
},
|
||||
"style": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"split",
|
||||
"unified"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
10
openapi.json
10
openapi.json
|
|
@ -323,7 +323,8 @@
|
|||
"has-translation-providers",
|
||||
"has-translation-task-providers",
|
||||
"typing-privacy",
|
||||
"summary-threshold"
|
||||
"summary-threshold",
|
||||
"style"
|
||||
],
|
||||
"properties": {
|
||||
"max-length": {
|
||||
|
|
@ -348,6 +349,13 @@
|
|||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1
|
||||
},
|
||||
"style": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"split",
|
||||
"unified"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ export const mockedCapabilities: Capabilities = {
|
|||
'has-translation-task-providers': true,
|
||||
'typing-privacy': 0,
|
||||
'summary-threshold': 100,
|
||||
style: 'split',
|
||||
},
|
||||
conversations: {
|
||||
'can-create': true,
|
||||
|
|
|
|||
|
|
@ -55,11 +55,20 @@
|
|||
v-if="!isGuest && supportConversationsListStyle"
|
||||
id="talk_appearance"
|
||||
:name="t('spreed', 'Appearance & Sounds')">
|
||||
<NcFormBoxSwitch
|
||||
:model-value="conversationsListStyle"
|
||||
:label="t('spreed', 'Compact conversations list')"
|
||||
:disabled="appearanceLoading"
|
||||
@update:model-value="toggleConversationsListStyle" />
|
||||
<NcFormBox>
|
||||
<NcFormBoxSwitch
|
||||
:model-value="conversationsListStyle"
|
||||
:label="t('spreed', 'Compact conversations list')"
|
||||
:disabled="appearanceLoading"
|
||||
@update:model-value="toggleConversationsListStyle" />
|
||||
<!-- FIXME: remove v-if after implementing split view -->
|
||||
<NcFormBoxSwitch
|
||||
v-if="false"
|
||||
:model-value="chatSplitViewEnabled"
|
||||
:label="t('spreed', 'Show your chat in split view')"
|
||||
:disabled="chatAppearanceLoading"
|
||||
@update:model-value="toggleChatStyle" />
|
||||
</NcFormBox>
|
||||
|
||||
<NcFormBox>
|
||||
<NcFormBoxSwitch
|
||||
|
|
@ -167,7 +176,7 @@ import NcHotkeyList from '@nextcloud/vue/components/NcHotkeyList'
|
|||
import NcKbd from '@nextcloud/vue/components/NcKbd'
|
||||
import IconFolderOpenOutline from 'vue-material-design-icons/FolderOpenOutline.vue'
|
||||
import IconMicrophoneOutline from 'vue-material-design-icons/MicrophoneOutline.vue'
|
||||
import { CONVERSATION, PRIVACY } from '../../constants.ts'
|
||||
import { CHAT_STYLE, CONVERSATION, PRIVACY } from '../../constants.ts'
|
||||
import { getTalkConfig, getTalkVersion } from '../../services/CapabilitiesManager.ts'
|
||||
import { useCustomSettings } from '../../services/SettingsAPI.ts'
|
||||
import { useActorStore } from '../../stores/actor.ts'
|
||||
|
|
@ -228,6 +237,7 @@ export default {
|
|||
return {
|
||||
showSettings: false,
|
||||
attachmentFolderLoading: true,
|
||||
chatAppearanceLoading: false,
|
||||
appearanceLoading: false,
|
||||
privacyLoading: false,
|
||||
playSoundsLoading: false,
|
||||
|
|
@ -267,6 +277,10 @@ export default {
|
|||
hideMediaSettings() {
|
||||
return !this.settingsStore.showMediaSettings
|
||||
},
|
||||
|
||||
chatSplitViewEnabled() {
|
||||
return this.settingsStore.chatStyle === CHAT_STYLE.SPLIT
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
|
@ -350,6 +364,17 @@ export default {
|
|||
this.appearanceLoading = false
|
||||
},
|
||||
|
||||
async toggleChatStyle(value) {
|
||||
this.chatAppearanceLoading = true
|
||||
try {
|
||||
await this.settingsStore.updateChatStyle(value ? CHAT_STYLE.SPLIT : CHAT_STYLE.UNIFIED)
|
||||
showSuccess(t('spreed', 'Your personal setting has been saved'))
|
||||
} catch (exception) {
|
||||
showError(t('spreed', 'Error while setting personal setting'))
|
||||
}
|
||||
this.chatAppearanceLoading = false
|
||||
},
|
||||
|
||||
async togglePlaySounds() {
|
||||
this.playSoundsLoading = true
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,11 @@ export const CHAT = {
|
|||
FETCH_NEW: 1,
|
||||
} as const
|
||||
|
||||
export const CHAT_STYLE = {
|
||||
SPLIT: 'split',
|
||||
UNIFIED: 'unified',
|
||||
} as const
|
||||
|
||||
export const CALL = {
|
||||
RECORDING: {
|
||||
OFF: 0,
|
||||
|
|
|
|||
|
|
@ -108,6 +108,14 @@ async function setConversationsListStyle(value: string) {
|
|||
return setUserConfig('spreed', 'conversations_list_style', value)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
async function setChatStyle(value: string) {
|
||||
return setUserConfig('spreed', 'chat_style', value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user config using provisioning API
|
||||
*
|
||||
|
|
@ -124,6 +132,7 @@ async function setUserConfig(appId: string, configKey: string, configValue: stri
|
|||
export {
|
||||
setAttachmentFolder,
|
||||
setBlurVirtualBackground,
|
||||
setChatStyle,
|
||||
setConversationsListStyle,
|
||||
setPlaySounds,
|
||||
setReadStatusPrivacy,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { getTalkConfig } from '../services/CapabilitiesManager.ts'
|
|||
import {
|
||||
setAttachmentFolder,
|
||||
setBlurVirtualBackground,
|
||||
setChatStyle,
|
||||
setConversationsListStyle,
|
||||
setReadStatusPrivacy,
|
||||
setStartWithoutMedia,
|
||||
|
|
@ -20,6 +21,7 @@ import {
|
|||
|
||||
type PRIVACY_KEYS = typeof PRIVACY[keyof typeof PRIVACY]
|
||||
type LIST_STYLE_OPTIONS = 'two-lines' | 'compact'
|
||||
type CHAT_STYLE_OPTIONS = 'split' | 'unified'
|
||||
|
||||
/**
|
||||
* Store for shared items shown in RightSidebar
|
||||
|
|
@ -31,6 +33,7 @@ export const useSettingsStore = defineStore('settings', () => {
|
|||
const startWithoutMedia = ref<boolean | undefined>(getTalkConfig('local', 'call', 'start-without-media'))
|
||||
const blurVirtualBackgroundEnabled = ref<boolean | undefined>(getTalkConfig('local', 'call', 'blur-virtual-background'))
|
||||
const conversationsListStyle = ref<LIST_STYLE_OPTIONS | undefined>(getTalkConfig('local', 'conversations', 'list-style'))
|
||||
const chatStyle = ref<CHAT_STYLE_OPTIONS | undefined>(getTalkConfig('local', 'chat', 'style') ?? 'split')
|
||||
|
||||
const attachmentFolder = ref<string>(loadState('spreed', 'attachment_folder', ''))
|
||||
const attachmentFolderFreeSpace = ref<number>(loadState('spreed', 'attachment_folder_free_space', 0))
|
||||
|
|
@ -105,6 +108,16 @@ export const useSettingsStore = defineStore('settings', () => {
|
|||
attachmentFolder.value = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the conversations list style setting for the user
|
||||
*
|
||||
* @param value - new selected state
|
||||
*/
|
||||
async function updateChatStyle(value: CHAT_STYLE_OPTIONS) {
|
||||
await setChatStyle(value)
|
||||
chatStyle.value = value
|
||||
}
|
||||
|
||||
return {
|
||||
readStatusPrivacy,
|
||||
typingStatusPrivacy,
|
||||
|
|
@ -114,6 +127,7 @@ export const useSettingsStore = defineStore('settings', () => {
|
|||
conversationsListStyle,
|
||||
attachmentFolder,
|
||||
attachmentFolderFreeSpace,
|
||||
chatStyle,
|
||||
|
||||
updateReadStatusPrivacy,
|
||||
updateTypingStatusPrivacy,
|
||||
|
|
@ -122,5 +136,6 @@ export const useSettingsStore = defineStore('settings', () => {
|
|||
updateStartWithoutMedia,
|
||||
updateConversationsListStyle,
|
||||
updateAttachmentFolder,
|
||||
updateChatStyle,
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -249,6 +249,8 @@ export type components = {
|
|||
"typing-privacy": number;
|
||||
/** Format: int64 */
|
||||
"summary-threshold": number;
|
||||
/** @enum {string} */
|
||||
style: "split" | "unified";
|
||||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ export type components = {
|
|||
"typing-privacy": number;
|
||||
/** Format: int64 */
|
||||
"summary-threshold": number;
|
||||
/** @enum {string} */
|
||||
style: "split" | "unified";
|
||||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ export type components = {
|
|||
"typing-privacy": number;
|
||||
/** Format: int64 */
|
||||
"summary-threshold": number;
|
||||
/** @enum {string} */
|
||||
style: "split" | "unified";
|
||||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ export type components = {
|
|||
"typing-privacy": number;
|
||||
/** Format: int64 */
|
||||
"summary-threshold": number;
|
||||
/** @enum {string} */
|
||||
style: "split" | "unified";
|
||||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ export type components = {
|
|||
"typing-privacy": number;
|
||||
/** Format: int64 */
|
||||
"summary-threshold": number;
|
||||
/** @enum {string} */
|
||||
style: "split" | "unified";
|
||||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
|
|
|
|||
|
|
@ -195,6 +195,8 @@ export type components = {
|
|||
"typing-privacy": number;
|
||||
/** Format: int64 */
|
||||
"summary-threshold": number;
|
||||
/** @enum {string} */
|
||||
style: "split" | "unified";
|
||||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
|
|
|
|||
|
|
@ -2391,6 +2391,8 @@ export type components = {
|
|||
"typing-privacy": number;
|
||||
/** Format: int64 */
|
||||
"summary-threshold": number;
|
||||
/** @enum {string} */
|
||||
style: "split" | "unified";
|
||||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
|
|
|
|||
|
|
@ -1869,6 +1869,8 @@ export type components = {
|
|||
"typing-privacy": number;
|
||||
/** Format: int64 */
|
||||
"summary-threshold": number;
|
||||
/** @enum {string} */
|
||||
style: "split" | "unified";
|
||||
};
|
||||
conversations: {
|
||||
"can-create": boolean;
|
||||
|
|
|
|||
|
|
@ -104,6 +104,11 @@ class CapabilitiesTest extends TestCase {
|
|||
->method('isBreakoutRoomsEnabled')
|
||||
->willReturn(false);
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('getChatStyle')
|
||||
->with(null)
|
||||
->willReturn('split');
|
||||
|
||||
$this->serverConfig->expects($this->any())
|
||||
->method('getAppValue')
|
||||
->willReturnMap([
|
||||
|
|
@ -181,6 +186,7 @@ class CapabilitiesTest extends TestCase {
|
|||
'has-translation-task-providers' => false,
|
||||
'typing-privacy' => 0,
|
||||
'summary-threshold' => 100,
|
||||
'style' => 'split',
|
||||
],
|
||||
'conversations' => [
|
||||
'can-create' => false,
|
||||
|
|
@ -247,6 +253,11 @@ class CapabilitiesTest extends TestCase {
|
|||
->with('uid')
|
||||
->willReturn('/Talk');
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('getChatStyle')
|
||||
->with('uid')
|
||||
->willReturn('split');
|
||||
|
||||
$this->talkConfig->expects($this->once())
|
||||
->method('isNotAllowedToCreateConversations')
|
||||
->with($user)
|
||||
|
|
@ -353,6 +364,7 @@ class CapabilitiesTest extends TestCase {
|
|||
'has-translation-task-providers' => false,
|
||||
'typing-privacy' => 0,
|
||||
'summary-threshold' => 100,
|
||||
'style' => 'split',
|
||||
],
|
||||
'conversations' => [
|
||||
'can-create' => $canCreate,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue