diff options
Diffstat (limited to 'apps/theming/src/components/BackgroundSettings.vue')
-rw-r--r-- | apps/theming/src/components/BackgroundSettings.vue | 114 |
1 files changed, 88 insertions, 26 deletions
diff --git a/apps/theming/src/components/BackgroundSettings.vue b/apps/theming/src/components/BackgroundSettings.vue index 9890f9ad3f0..e1d8b731bc4 100644 --- a/apps/theming/src/components/BackgroundSettings.vue +++ b/apps/theming/src/components/BackgroundSettings.vue @@ -1,10 +1,10 @@ <!-- - @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net> - - @copyright Copyright (c) 2022 Greta Doci <gretadoci@gmail.com> - - - @author Julius Härtl <jus@bitgrid.net> - - @author Greta Doci <gretadoci@gmail.com> - @author Christopher Ng <chrng8@gmail.com> + - @author Greta Doci <gretadoci@gmail.com> + - @author John Molakvoæ <skjnldsv@protonmail.com> + - @author Julius Härtl <jus@bitgrid.net> - - @license GNU AGPL version 3 or any later version - @@ -24,13 +24,16 @@ --> <template> - <div class="background-selector"> + <div class="background-selector" data-user-theming-background-settings> <!-- Custom background --> <button class="background background__filepicker" - :class="{ 'background--active': backgroundImage === 'custom' }" + :class="{ 'icon-loading': loading === 'custom', 'background--active': backgroundImage === 'custom' }" + :data-color-bright="invertTextColor(Theming.color)" + data-user-theming-background-custom tabindex="0" @click="pickFile"> {{ t('theming', 'Custom background') }} + <Check :size="44" /> </button> <!-- Default background --> @@ -38,6 +41,7 @@ :class="{ 'icon-loading': loading === 'default', 'background--active': backgroundImage === 'default' }" :data-color-bright="invertTextColor(Theming.defaultColor)" :style="{ '--border-color': Theming.defaultColor }" + data-user-theming-background-default tabindex="0" @click="setDefault"> {{ t('theming', 'Default background') }} @@ -50,6 +54,7 @@ :data-color="Theming.color" :data-color-bright="invertTextColor(Theming.color)" :style="{ backgroundColor: Theming.color, '--border-color': Theming.color}" + data-user-theming-background-color tabindex="0"> {{ t('theming', 'Change color') }} </button> @@ -61,6 +66,7 @@ v-tooltip="shippedBackground.details.attribution" :class="{ 'icon-loading': loading === shippedBackground.name, 'background--active': backgroundImage === shippedBackground.name }" :data-color-bright="shippedBackground.details.theming === 'dark'" + :data-user-theming-background-shipped="shippedBackground.name" :style="{ backgroundImage: 'url(' + shippedBackground.preview + ')', '--border-color': shippedBackground.details.primary_color }" class="background background__shipped" tabindex="0" @@ -70,16 +76,17 @@ <!-- Remove background --> <button class="background background__delete" + data-user-theming-background-clear tabindex="0" @click="removeBackground"> {{ t('theming', 'Remove background') }} - <Close :size="24" /> + <Close :size="32" /> </button> </div> </template> <script> -import { generateFilePath, generateUrl } from '@nextcloud/router' +import { generateFilePath, generateRemoteUrl, generateUrl } from '@nextcloud/router' import { loadState } from '@nextcloud/initial-state' import axios from '@nextcloud/axios' import Check from 'vue-material-design-icons/Check.vue' @@ -87,6 +94,10 @@ import Close from 'vue-material-design-icons/Close.vue' import debounce from 'debounce' import NcColorPicker from '@nextcloud/vue/dist/Components/NcColorPicker' import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip' +import Vibrant from 'node-vibrant' +import { Palette } from 'node-vibrant/lib/color' +import { getFilePickerBuilder } from '@nextcloud/dialogs' +import { getCurrentUser } from '@nextcloud/auth' const backgroundColor = loadState('theming', 'backgroundColor') const backgroundImage = loadState('theming', 'backgroundImage') @@ -95,6 +106,12 @@ const themingDefaultBackground = loadState('theming', 'themingDefaultBackground' const defaultShippedBackground = loadState('theming', 'defaultShippedBackground') const prefixWithBaseUrl = (url) => generateFilePath('theming', '', 'img/background/') + url +const picker = getFilePickerBuilder(t('theming', 'Select a background from your files')) + .setMultiSelect(false) + .setModal(true) + .setType(1) + .setMimeTypeFilter(['image/png', 'image/gif', 'image/jpeg', 'image/svg+xml', 'image/svg']) + .build() export default { name: 'BackgroundSettings', @@ -213,9 +230,9 @@ export default { this.update(result.data) }, - async setFile(path) { + async setFile(path, color = null) { this.loading = 'custom' - const result = await axios.post(generateUrl('/apps/theming/background/custom'), { value: path }) + const result = await axios.post(generateUrl('/apps/theming/background/custom'), { value: path, color }) this.update(result.data) }, @@ -228,19 +245,55 @@ export default { async pickColor(event) { this.loading = 'color' const color = event?.target?.dataset?.color || this.Theming?.color || '#0082c9' - const result = await axios.post(generateUrl('/apps/theming/background/color'), { value: color }) + const result = await axios.post(generateUrl('/apps/theming/background/color'), { color }) this.update(result.data) }, debouncePickColor: debounce(function() { this.pickColor(...arguments) }, 200), - pickFile() { - window.OC.dialogs.filepicker(t('theming', 'Select a background from your files'), (path, type) => { - if (type === OC.dialogs.FILEPICKER_TYPE_CHOOSE) { - this.setFile(path) - } - }, false, ['image/png', 'image/gif', 'image/jpeg', 'image/svg'], true, OC.dialogs.FILEPICKER_TYPE_CHOOSE) + async pickFile() { + const path = await picker.pick() + this.loading = 'custom' + + // Extract primary color from image + let response = null + let color = null + try { + const fileUrl = generateRemoteUrl('dav/files/' + getCurrentUser().uid + path) + response = await axios.get(fileUrl, { responseType: 'blob' }) + const blobUrl = URL.createObjectURL(response.data) + const palette = await this.getColorPaletteFromBlob(blobUrl) + + // DarkVibrant is accessible AND visually pleasing + // Vibrant is not accessible enough and others are boring + color = palette?.DarkVibrant?.hex + this.setFile(path, color) + + // Log data + console.debug('Extracted colour', color, 'from custom image', path, palette) + } catch (error) { + this.setFile(path) + console.error('Unable to extract colour from custom image', { error, path, response, color }) + } + }, + + /** + * Extract a Vibrant color palette from a blob URL + * + * @param {string} blobUrl the blob URL + * @return {Promise<Palette>} + */ + getColorPaletteFromBlob(blobUrl) { + return new Promise((resolve, reject) => { + const vibrant = new Vibrant(blobUrl) + vibrant.getPalette((error, palette) => { + if (error) { + reject(error) + } + resolve(palette) + }) + }) }, }, } @@ -263,6 +316,13 @@ export default { background-position: center center; background-size: cover; + &__filepicker { + &.background--active { + color: white; + background-image: var(--image-background); + } + } + &__default { background-color: var(--color-primary-default); background-image: var(--image-background-default); @@ -277,6 +337,12 @@ export default { background-color: var(--color-primary-default); } + // Over a background image + &__default, + &__shipped { + color: white; + } + // Text and svg icon dark on bright background &[data-color-bright] { color: black; @@ -294,18 +360,14 @@ export default { margin: 4px; } - &__default, - &__shipped { - color: white; - span { - display: none; - } + &__filepicker span, + &__default span, + &__shipped span { + display: none; } - &--active:not(.icon-loading) { - span { - display: block; - } + &--active:not(.icon-loading) span { + display: block !important; } } } |