1313 <!-- eslint-disable-next-line vue/no-v-html -->
1414 <p v-html =" descriptionDetail" />
1515
16- <div class =" theming__preview-list" >
17- <ItemPreview
18- v-for =" theme in themes"
19- :key =" theme.id"
20- :enforced =" theme.id === enforceTheme"
21- :selected =" selectedTheme.id === theme.id"
22- :theme =" theme"
23- :unique =" themes.length === 1"
24- type =" theme"
25- @change =" changeTheme" />
26- </div >
27-
28- <div class =" theming__preview-list" >
29- <ItemPreview
30- v-for =" theme in fonts"
31- :key =" theme.id"
32- :selected =" theme.enabled"
33- :theme =" theme"
34- :unique =" fonts.length === 1"
35- type =" font"
36- @change =" changeFont" />
37- </div >
16+ <ThemeList
17+ v-model =" selectedMainThemes"
18+ :label =" t('theming', 'Themes')"
19+ :themes =" mainThemes" />
20+
21+ <ThemeList
22+ v-model =" selectedSupplementaryThemes"
23+ :label =" t('theming', 'Supplementary themes')"
24+ :themes =" supplementaryThemes"
25+ multiple />
26+
27+ <ThemeList
28+ v-model =" selectedFontThemes"
29+ :label =" t('theming', 'Fonts')"
30+ :themes =" fontThemes" />
3831
3932 <h3 >{{ t('theming', 'Misc accessibility options') }}</h3 >
4033 <NcCheckboxRadioSwitch
8679</template >
8780
8881<script >
89- import axios , { isAxiosError } from ' @nextcloud/axios'
90- import { showError } from ' @nextcloud/dialogs'
82+ import axios from ' @nextcloud/axios'
9183import { loadState } from ' @nextcloud/initial-state'
9284import { generateOcsUrl } from ' @nextcloud/router'
9385import NcCheckboxRadioSwitch from ' @nextcloud/vue/components/NcCheckboxRadioSwitch'
9486import NcSettingsSection from ' @nextcloud/vue/components/NcSettingsSection'
9587import BackgroundSettings from ' ./components/BackgroundSettings.vue'
96- import ItemPreview from ' ./components/ItemPreview .vue'
88+ import ThemeList from ' ./components/ThemeList .vue'
9789import UserAppMenuSection from ' ./components/UserAppMenuSection.vue'
9890import UserPrimaryColor from ' ./components/UserPrimaryColor.vue'
9991import { refreshStyles } from ' ./helpers/refreshStyles.js'
10092import { logger } from ' ./logger.js'
10193
10294const availableThemes = loadState (' theming' , ' themes' , [])
103- const enforceTheme = loadState (' theming' , ' enforceTheme' , ' ' )
10495const shortcutsDisabled = loadState (' theming' , ' shortcutsDisabled' , false )
10596const enableBlurFilter = loadState (' theming' , ' enableBlurFilter' , ' ' )
10697
@@ -110,7 +101,7 @@ export default {
110101 name: ' UserTheming' ,
111102
112103 components: {
113- ItemPreview ,
104+ ThemeList ,
114105 NcCheckboxRadioSwitch,
115106 NcSettingsSection,
116107 BackgroundSettings,
@@ -119,30 +110,62 @@ export default {
119110 },
120111
121112 data () {
113+ if (availableThemes .every (({ type, enabled }) => type !== 1 || ! enabled)) {
114+ availableThemes .find (({ id, type }) => id === ' default' && type === 1 ).enabled = true
115+ }
116+
122117 return {
123118 availableThemes,
124119
125120 // Admin defined configs
126- enforceTheme,
127121 shortcutsDisabled,
128122 isUserThemingDisabled,
129-
130123 enableBlurFilter,
131124 }
132125 },
133126
134127 computed: {
135- themes () {
128+ mainThemes () {
136129 return this .availableThemes .filter ((theme ) => theme .type === 1 )
137130 },
138131
139- fonts () {
132+ fontThemes () {
140133 return this .availableThemes .filter ((theme ) => theme .type === 2 )
141134 },
142135
143- // Selected theme, fallback on first (default) if none
144- selectedTheme () {
145- return this .themes .find ((theme ) => theme .enabled === true ) || this .themes [0 ]
136+ supplementaryThemes () {
137+ return this .availableThemes .filter ((theme ) => theme .type === 3 )
138+ },
139+
140+ selectedMainThemes: {
141+ get () {
142+ return this .mainThemes .filter (({ enabled }) => enabled)
143+ },
144+
145+ set (themes ) {
146+ logger .debug (' SETTING main' , { themes })
147+ this .updateThemes (this .mainThemes , themes)
148+ },
149+ },
150+
151+ selectedFontThemes: {
152+ get () {
153+ return this .fontThemes .filter (({ enabled }) => enabled)
154+ },
155+
156+ set (themes ) {
157+ this .updateThemes (this .fontThemes , themes)
158+ },
159+ },
160+
161+ selectedSupplementaryThemes: {
162+ get () {
163+ return this .supplementaryThemes .filter (({ enabled }) => enabled)
164+ },
165+
166+ set (themes ) {
167+ this .updateThemes (this .supplementaryThemes , themes)
168+ },
146169 },
147170
148171 description () {
@@ -182,38 +205,19 @@ export default {
182205 },
183206
184207 methods: {
208+ t,
209+
185210 // Refresh server-side generated theming CSS
186211 async refreshGlobalStyles () {
187212 await refreshStyles ()
188213 this .$nextTick (() => this .$refs .primaryColor .reload ())
189214 },
190215
191- changeTheme ({ enabled, id }) {
192- // Reset selected and select new one
193- this .themes .forEach ((theme ) => {
194- if (theme .id === id && enabled) {
195- theme .enabled = true
196- return
197- }
198- theme .enabled = false
199- })
200-
201- this .updateBodyAttributes ()
202- this .selectItem (enabled, id)
203- },
204-
205- changeFont ({ enabled, id }) {
206- // Reset selected and select new one
207- this .fonts .forEach ((font ) => {
208- if (font .id === id && enabled) {
209- font .enabled = true
210- return
211- }
212- font .enabled = false
213- })
214-
216+ updateThemes (allThemes , selectedThemes ) {
217+ for (const theme of allThemes) {
218+ theme .enabled = selectedThemes .includes (theme)
219+ }
215220 this .updateBodyAttributes ()
216- this .selectItem (enabled, id)
217221 },
218222
219223 async changeShortcutsDisabled (newState ) {
@@ -256,47 +260,23 @@ export default {
256260 },
257261
258262 updateBodyAttributes () {
259- const enabledThemesIDs = this .themes .filter ((theme ) => theme .enabled === true ).map ((theme ) => theme .id )
260- const enabledFontsIDs = this .fonts .filter ((font ) => font .enabled === true ).map ((font ) => font .id )
263+ const enabledThemesIDs = [
264+ ... this .selectedMainThemes .map ((theme ) => theme .id ),
265+ ... this .selectedSupplementaryThemes .map ((theme ) => theme .id ),
266+ ... this .selectedFontThemes .map ((theme ) => theme .id ),
267+ ]
261268
262- this .themes .forEach ((theme ) => {
269+ this .mainThemes .forEach ((theme ) => {
270+ document .body .toggleAttribute (` data-theme-${ theme .id } ` , theme .enabled )
271+ })
272+ this .supplementaryThemes .forEach ((theme ) => {
263273 document .body .toggleAttribute (` data-theme-${ theme .id } ` , theme .enabled )
264274 })
265- this .fonts .forEach ((font ) => {
275+ this .fontThemes .forEach ((font ) => {
266276 document .body .toggleAttribute (` data-theme-${ font .id } ` , font .enabled )
267277 })
268278
269- document .body .setAttribute (' data-themes' , [... enabledThemesIDs, ... enabledFontsIDs].join (' ,' ))
270- },
271-
272- /**
273- * Commit a change and force reload css
274- * Fetching the file again will trigger the server update
275- *
276- * @param {boolean} enabled the theme state
277- * @param {string} themeId the theme ID to change
278- */
279- async selectItem (enabled , themeId ) {
280- try {
281- if (enabled) {
282- await axios ({
283- url: generateOcsUrl (' apps/theming/api/v1/theme/{themeId}/enable' , { themeId }),
284- method: ' PUT' ,
285- })
286- } else {
287- await axios ({
288- url: generateOcsUrl (' apps/theming/api/v1/theme/{themeId}' , { themeId }),
289- method: ' DELETE' ,
290- })
291- }
292- } catch (error) {
293- logger .error (' theming: Unable to apply setting.' , { error })
294- let message = t (' theming' , ' Unable to apply the setting.' )
295- if (isAxiosError (error) && error .response .data .ocs ? .meta ? .message ) {
296- message = ` ${ error .response .data .ocs .meta .message } . ${ message} `
297- }
298- showError (message)
299- }
279+ document .body .setAttribute (' data-themes' , enabledThemesIDs .join (' ,' ))
300280 },
301281 },
302282}
@@ -318,26 +298,11 @@ export default {
318298 text-decoration : underline ;
319299 }
320300 }
321-
322- & __preview- list {
323- -- gap: 30px ;
324- display: grid;
325- margin- top: var (-- gap);
326- column- gap: var (-- gap);
327- row- gap: var (-- gap);
328- }
329301}
330302
331303.background {
332304 & __grid {
333305 margin-top : 30px ;
334306 }
335307}
336-
337- @media (max - width : 1440px ) {
338- .theming__preview - list {
339- display: flex;
340- flex- direction: column;
341- }
342- }
343308 </style >
0 commit comments