44 -->
55
66<script setup lang="ts">
7- import type { NcSelectOption } from ' ../composables/useNcSelectModel.ts'
8-
97import { t } from ' @nextcloud/l10n'
108import { storeToRefs } from ' pinia'
11- import { computed , ref } from ' vue'
12- import NcButton from ' @nextcloud/vue/components/NcButton'
13- import NcCheckboxRadioSwitch from ' @nextcloud/vue/components/NcCheckboxRadioSwitch'
14- import NcNoteCard from ' @nextcloud/vue/components/NcNoteCard'
15- import NcTextField from ' @nextcloud/vue/components/NcTextField'
16- import IconBellRingOutline from ' vue-material-design-icons/BellRingOutline.vue'
17- import IconCardAccountPhoneOutline from ' vue-material-design-icons/CardAccountPhoneOutline.vue'
18- import IconMagnify from ' vue-material-design-icons/Magnify.vue'
19- import IconMinus from ' vue-material-design-icons/Minus.vue'
20- import IconPhoneRingOutline from ' vue-material-design-icons/PhoneRingOutline.vue'
21- import IconPlus from ' vue-material-design-icons/Plus.vue'
22- import IconRestore from ' vue-material-design-icons/Restore.vue'
9+ import NcFormBox from ' @nextcloud/vue/components/NcFormBox'
10+ import NcFormBoxSwitch from ' @nextcloud/vue/components/NcFormBoxSwitch'
11+ import NcFormGroup from ' @nextcloud/vue/components/NcFormGroup'
12+ import NcRadioGroup from ' @nextcloud/vue/components/NcRadioGroup'
13+ import NcRadioGroupButton from ' @nextcloud/vue/components/NcRadioGroupButton'
2314import IconThemeLightDark from ' vue-material-design-icons/ThemeLightDark.vue'
24- import IconVolumeHigh from ' vue-material-design-icons/VolumeHigh .vue'
25- import SettingsFormGroup from ' ./components/SettingsFormGroup .vue'
26- import SettingsSelect from ' ./components/SettingsSelect .vue'
27- import SettingsSubsection from ' ./components/SettingsSubsection .vue'
28- import { ZOOM_MAX , ZOOM_MIN } from ' ../../../constants.js '
29- import { useNcSelectModel } from ' ../composables/useNcSelectModel.ts '
15+ import IconWeatherNight from ' vue-material-design-icons/WeatherNight .vue'
16+ import IconWeatherSunny from ' vue-material-design-icons/WeatherSunny .vue'
17+ import DesktopSettingsSectionRelaunchNote from ' ./components/DesktopSettingsSectionRelaunchNote .vue'
18+ import UiFormBoxAudioOutput from ' ./components/UiFormBoxAudioOutput .vue'
19+ import UiFormBoxSelectNative from ' ./components/UiFormBoxSelectNative.vue '
20+ import UiFormGroupZoom from ' ./components/UiFormGroupZoom.vue '
3021import { useAppConfigStore } from ' ./appConfig.store.ts'
3122import { useAppConfigValue } from ' ./useAppConfigValue.ts'
3223
@@ -35,231 +26,80 @@ const isLinux = window.systemInfo.isLinux
3526const { isRelaunchRequired } = storeToRefs (useAppConfigStore ())
3627
3728const launchAtStartup = useAppConfigValue (' launchAtStartup' )
38-
3929const theme = useAppConfigValue (' theme' )
40- const themeOptions = [
41- { label: t (' talk_desktop' , ' System default' ), value: ' default' } as const ,
42- { label: t (' talk_desktop' , ' Light' ), value: ' light' } as const ,
43- { label: t (' talk_desktop' , ' Dark' ), value: ' dark' } as const ,
44- ]
45- const themeOption = useNcSelectModel (theme , themeOptions )
46-
4730const systemTitleBar = useAppConfigValue (' systemTitleBar' )
4831const monochromeTrayIcon = useAppConfigValue (' monochromeTrayIcon' )
49-
50- const zoomFactorConfig = useAppConfigValue (' zoomFactor' )
51- const zoomFactor = computed ({
52- get : () => zoomFactorConfig .value ,
53- set : (value : number ) => {
54- zoomFactorConfig .value = isFinite (value ) ? Math .min (Math .max (value , ZOOM_MIN ), ZOOM_MAX ) : 1
55- },
56- })
57- const zoomFactorPercentage = computed ({
58- get : () => Math .round (zoomFactor .value * 100 ).toString (),
59- set : (value : string ) => {
60- zoomFactor .value = parseFloat (value ) / 100
61- },
62- })
63- const ZOOM_STEP = Math .sqrt (1.2 )
64- const ctrl = window .systemInfo .isMac ? ' Ctrl/Cmd' : ' Ctrl'
65- const zoomHint = t (' talk_desktop' , ' Zoom can be also changed by {key} or mouse wheel. Reset by {resetKey}' , {
66- key: ` <kbd>${ctrl } + ±</kbd> ` ,
67- resetKey: ` <kbd>${ctrl } + 0</kbd> ` ,
68- }, undefined , { escape: false })
69-
70- const generalNotificationOptions = [
71- { label: t (' talk_desktop' , ' Always' ), value: ' always' } as const ,
72- { label: t (' talk_desktop' , ' When not in "Do not disturb"' ), value: ' respect-dnd' } as const ,
73- { label: t (' talk_desktop' , ' Never' ), value: ' never' } as const ,
74- ]
32+ const zoomFactor = useAppConfigValue (' zoomFactor' )
7533
7634const playSoundChat = useAppConfigValue (' playSoundChat' )
77- const playSoundChatOption = useNcSelectModel (playSoundChat , generalNotificationOptions )
78-
7935const playSoundCall = useAppConfigValue (' playSoundCall' )
80- const playSoundCallOption = useNcSelectModel (playSoundCall , generalNotificationOptions )
81-
8236const enableCallbox = useAppConfigValue (' enableCallbox' )
83- const enableCallboxOption = useNcSelectModel (enableCallbox , generalNotificationOptions )
37+ const notificationLevelOptions = [
38+ { label: t (' talk_desktop' , ' Always' ), value: ' always' },
39+ { label: t (' talk_desktop' , ' When not in "Do not disturb"' ), value: ' respect-dnd' },
40+ { label: t (' talk_desktop' , ' Never' ), value: ' never' },
41+ ]
8442
8543const secondarySpeaker = useAppConfigValue (' secondarySpeaker' )
86-
87- const EMPTY_DEVICE_OPTION = { value: null , label: t (' talk_desktop' , ' None' ) }
88- const secondarySpeakerOptions = ref <NcSelectOption <string | null >[]>([])
89-
9044const secondarySpeakerDevice = useAppConfigValue (' secondarySpeakerDevice' )
91- const secondarySpeakerDeviceOption = useNcSelectModel (secondarySpeakerDevice , secondarySpeakerOptions , EMPTY_DEVICE_OPTION )
92-
93- /**
94- * Enumerate available media devices (audio output) in format of NcSelectOption
95- */
96- async function initializeDevices() {
97- let stream = null
98- try {
99- stream = await navigator .mediaDevices .getUserMedia ({ audio: true })
100- const deviceOptions = (await navigator .mediaDevices .enumerateDevices () ?? [])
101- .filter ((device ) => device .kind === ' audiooutput' )
102- .map ((device ) => ({ value: device .deviceId , label: device .label }))
103-
104- secondarySpeakerOptions .value = [EMPTY_DEVICE_OPTION , ... deviceOptions ]
105- } catch (error ) {
106- console .error (' Error while requesting or initializing audio devices: ' , error )
107- secondarySpeakerOptions .value = [EMPTY_DEVICE_OPTION ]
108- } finally {
109- if (stream ) {
110- stream .getTracks ().forEach ((track ) => track .stop ())
111- }
112- }
113- }
114- initializeDevices ()
115-
116- /**
117- * Restart the app
118- */
119- function relaunch() {
120- window .TALK_DESKTOP .relaunchWindow ()
121- }
12245 </script >
12346
12447<template >
125- <div >
126- <NcNoteCard v-if =" isRelaunchRequired" type =" info" class =" relaunch-require-note-card" >
127- <div class =" relaunch-require-note-card__content" >
128- <span >{{ t('talk_desktop', 'Some changes require a relaunch to take effect') }}</span >
129- <NcButton
130- variant =" primary"
131- size =" small"
132- class =" relaunch-require-note-card__button"
133- @click =" relaunch" >
134- {{ t('talk_desktop', 'Restart') }}
135- </NcButton >
136- </div >
137- </NcNoteCard >
48+ <div class =" desktop-settings-section" >
49+ <DesktopSettingsSectionRelaunchNote v-if =" isRelaunchRequired" />
13850
139- <SettingsSubsection v-if =" !isLinux" :name =" t('talk_desktop', 'General')" >
140- <NcCheckboxRadioSwitch v-model =" launchAtStartup" type =" switch" >
141- {{ t('talk_desktop', 'Launch at startup') }}
142- </NcCheckboxRadioSwitch >
143- </SettingsSubsection >
51+ <NcFormBox v-if =" !isLinux" >
52+ <NcFormBoxSwitch v-model =" launchAtStartup" :label =" t('talk_desktop', 'Launch at startup')" />
53+ </NcFormBox >
14454
145- <SettingsSubsection :name =" t('talk_desktop', 'Appearance ')" >
146- <SettingsSelect v-model = " themeOption " :options = " themeOptions " : label =" t('talk_desktop', 'Theme') " >
147- <template #icon = " { size } " >
148- <IconThemeLightDark :size =" size " />
55+ <NcRadioGroup v-model = " theme " :label =" t('talk_desktop', 'Theme ')" >
56+ <NcRadioGroupButton : label =" t('talk_desktop', 'System default') " value = " default " >
57+ <template #icon >
58+ <IconThemeLightDark :size =" 20 " />
14959 </template >
150- </SettingsSelect >
151-
152- <NcCheckboxRadioSwitch v-model =" monochromeTrayIcon" type =" switch" >
153- {{ t('talk_desktop', 'Use monochrome tray icon') }}
154- </NcCheckboxRadioSwitch >
155-
156- <NcCheckboxRadioSwitch v-model =" systemTitleBar" type =" switch" >
157- {{ t('talk_desktop', 'Use system title bar') }}
158- </NcCheckboxRadioSwitch >
159-
160- <SettingsFormGroup :label =" t('talk_desktop', 'Zoom')" >
161- <template #icon =" { size } " >
162- <IconMagnify :size =" size" />
60+ </NcRadioGroupButton >
61+ <NcRadioGroupButton :label =" t('talk_desktop', 'Light')" value =" light" >
62+ <template #icon >
63+ <IconWeatherSunny :size =" 20" />
16364 </template >
164- <template #description >
165- <!-- eslint-disable-next-line vue/no-v-html -->
166- <span v-html =" zoomHint" />
65+ </NcRadioGroupButton >
66+ <NcRadioGroupButton :label =" t('talk_desktop', 'Dark')" value =" dark" >
67+ <template #icon >
68+ <IconWeatherNight :size =" 20" />
16769 </template >
168- <template #default =" { inputId , descriptionId } " >
169- <NcButton :aria-label =" t('talk_desktop', 'Zoom out')" variant =" tertiary" @click =" zoomFactor /= ZOOM_STEP" >
170- <template #icon >
171- <IconMinus :size =" 20" />
172- </template >
173- </NcButton >
174- <NcTextField
175- :id =" inputId"
176- class =" zoom-input"
177- :aria-describedby =" descriptionId"
178- label-outside
179- inputmode =" number"
180- :model-value =" zoomFactorPercentage"
181- @change =" zoomFactorPercentage = $event.target.value"
182- @blur =" $event.target.value = zoomFactorPercentage" />
183- <NcButton :aria-label =" t('talk_desktop', 'Zoom in')" variant =" tertiary" @click =" zoomFactor *= ZOOM_STEP" >
184- <template #icon >
185- <IconPlus :size =" 20" />
186- </template >
187- </NcButton >
188- <NcButton @click =" zoomFactor = 1" >
189- <template #icon >
190- <IconRestore :size =" 20" />
191- </template >
192- {{ t('talk_desktop', 'Reset') }}
193- </NcButton >
194- </template >
195- </SettingsFormGroup >
196- </SettingsSubsection >
197-
198- <SettingsSubsection :name =" t('talk_desktop', 'Notifications and sounds')" >
199- <SettingsSelect v-model =" playSoundChatOption" :options =" generalNotificationOptions" :label =" t('talk_desktop', 'Play chat notification sound')" >
200- <template #icon =" { size } " >
201- <IconBellRingOutline :size =" size" />
202- </template >
203- </SettingsSelect >
204-
205- <SettingsSelect v-model =" playSoundCallOption" :options =" generalNotificationOptions" :label =" t('talk_desktop', 'Play call notification sound')" >
206- <template #icon =" { size } " >
207- <IconPhoneRingOutline :size =" size" />
208- </template >
209- </SettingsSelect >
210-
211- <SettingsSelect v-model =" enableCallboxOption" :options =" generalNotificationOptions" :label =" t('talk_desktop', 'Show call notification popup')" >
212- <template #icon =" { size } " >
213- <IconCardAccountPhoneOutline :size =" size" />
214- </template >
215- </SettingsSelect >
216-
217- <NcCheckboxRadioSwitch v-model =" secondarySpeaker" type =" switch" >
218- {{ t('talk_desktop', 'Also repeat call notification on a secondary speaker') }}
219- </NcCheckboxRadioSwitch >
220-
221- <SettingsSelect
222- v-if =" secondarySpeaker"
223- v-model =" secondarySpeakerDeviceOption"
224- :options =" secondarySpeakerOptions"
225- :disabled =" secondarySpeakerOptions.length === 1"
226- :label =" t('talk_desktop', 'Secondary speaker')" >
227- <template #icon =" { size } " >
228- <IconVolumeHigh :size =" size" />
229- </template >
230- <template #action >
231- <NcButton variant =" tertiary" @click =" initializeDevices" >
232- <template #icon >
233- <IconRestore :size =" 20" />
234- </template >
235- </NcButton >
236- </template >
237- </SettingsSelect >
238- </SettingsSubsection >
70+ </NcRadioGroupButton >
71+ </NcRadioGroup >
72+
73+ <NcFormGroup :label =" t('talk_desktop', 'Appearance')" >
74+ <NcFormBox >
75+ <NcFormBoxSwitch v-model =" monochromeTrayIcon" :label =" t('talk_desktop', 'Use monochrome tray icon')" />
76+ <NcFormBoxSwitch v-model =" systemTitleBar" :label =" t('talk_desktop', 'Use system title bar')" />
77+ </NcFormBox >
78+ </NcFormGroup >
79+
80+ <UiFormGroupZoom v-model =" zoomFactor" />
81+
82+ <NcFormGroup :label =" t('talk_desktop', 'Notifications and sounds')" >
83+ <NcFormBox >
84+ <UiFormBoxSelectNative v-model =" playSoundChat" :label =" t('talk_desktop', 'Play chat notification sound')" :options =" notificationLevelOptions" />
85+ <UiFormBoxSelectNative v-model =" playSoundCall" :label =" t('talk_desktop', 'Play call notification sound')" :options =" notificationLevelOptions" />
86+ <UiFormBoxSelectNative v-model =" enableCallbox" :label =" t('talk_desktop', 'Show call notification popup')" :options =" notificationLevelOptions" />
87+ </NcFormBox >
88+
89+ <NcFormBox >
90+ <NcFormBoxSwitch v-model =" secondarySpeaker" :label =" t('talk_desktop', 'Also repeat call notification on a secondary speaker')" />
91+ </NcFormBox >
92+
93+ <UiFormBoxAudioOutput v-if =" secondarySpeaker" v-model =" secondarySpeakerDevice" :label =" t('talk_desktop', 'Secondary speaker')" />
94+ </NcFormGroup >
23995 </div >
24096</template >
24197
24298<style scoped>
243- .relaunch-require-note-card {
244- margin-block-start : 0 !important ;
245- }
246-
247- .relaunch-require-note-card > :deep(div ) {
248- flex : 1 ; /* TODO: fix in upstream */
249- }
250-
251- .relaunch-require-note-card__content {
99+ .desktop-settings-section {
252100 display : flex ;
253- gap : var (--default-grid-baseline );
254- align-items : flex-start ;
255- }
256-
257- .relaunch-require-note-card__button {
258- margin-inline-start : auto ;
259- flex : 0 0 auto ;
260- }
261-
262- .zoom-input {
263- width : 50px !important ;
101+ flex-direction : column ;
102+ justify-content : stretch ;
103+ gap : calc (6 * var (--default-grid-baseline ));
264104}
265105 </style >
0 commit comments