diff options
Diffstat (limited to 'apps/settings/src/composables')
-rw-r--r-- | apps/settings/src/composables/useAppIcon.ts | 62 | ||||
-rw-r--r-- | apps/settings/src/composables/useGetLocalizedValue.ts | 29 | ||||
-rw-r--r-- | apps/settings/src/composables/useGroupsNavigation.ts | 59 |
3 files changed, 150 insertions, 0 deletions
diff --git a/apps/settings/src/composables/useAppIcon.ts b/apps/settings/src/composables/useAppIcon.ts new file mode 100644 index 00000000000..b5e211aa1bc --- /dev/null +++ b/apps/settings/src/composables/useAppIcon.ts @@ -0,0 +1,62 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import type { Ref } from 'vue' +import type { IAppstoreApp } from '../app-types.ts' + +import { mdiCog, mdiCogOutline } from '@mdi/js' +import { computed, ref, watchEffect } from 'vue' +import AppstoreCategoryIcons from '../constants/AppstoreCategoryIcons.ts' +import logger from '../logger.ts' + +/** + * Get the app icon raw SVG for use with `NcIconSvgWrapper` (do never use without sanitizing) + * It has a fallback to the categroy icon. + * + * @param app The app to get the icon for + */ +export function useAppIcon(app: Ref<IAppstoreApp>) { + const appIcon = ref<string|null>(null) + + /** + * Fallback value if no app icon available + */ + const categoryIcon = computed(() => { + let path: string + if (app.value?.app_api) { + // Use different default icon for ExApps (AppAPI) + path = mdiCogOutline + } else { + path = [app.value?.category ?? []].flat() + .map((name) => AppstoreCategoryIcons[name]) + .filter((icon) => !!icon) + .at(0) + ?? (!app.value?.app_api ? mdiCog : mdiCogOutline) + } + return path ? `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="${path}" /></svg>` : null + }) + + watchEffect(async () => { + // Note: Only variables until the first `await` will be watched! + if (!app.value?.preview) { + appIcon.value = categoryIcon.value + } else { + appIcon.value = null + // Now try to load the real app icon + try { + const response = await window.fetch(app.value.preview) + const blob = await response.blob() + const rawSvg = await blob.text() + appIcon.value = rawSvg.replaceAll(/fill="#(fff|ffffff)([a-z0-9]{1,2})?"/ig, 'fill="currentColor"') + } catch (error) { + appIcon.value = categoryIcon.value + logger.error('Could not load app icon', { error }) + } + } + }) + + return { + appIcon, + } +} diff --git a/apps/settings/src/composables/useGetLocalizedValue.ts b/apps/settings/src/composables/useGetLocalizedValue.ts new file mode 100644 index 00000000000..a8a6f39f380 --- /dev/null +++ b/apps/settings/src/composables/useGetLocalizedValue.ts @@ -0,0 +1,29 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import type { ILocalizedValue } from '../constants/AppDiscoverTypes' + +import { getLanguage } from '@nextcloud/l10n' +import { computed, type Ref } from 'vue' + +/** + * Helper to get the localized value for the current users language + * @param dict The dictionary to get the value from + * @param language The language to use + */ +const getLocalizedValue = <T, >(dict: ILocalizedValue<T>, language: string) => dict[language] ?? dict[language.split('_')[0]] ?? dict.en ?? null + +/** + * Get the localized value of the dictionary provided + * @param dict Dictionary + * @return String or null if invalid dictionary + */ +export const useLocalizedValue = <T, >(dict: Ref<ILocalizedValue<T|undefined>|undefined|null>) => { + /** + * Language of the current user + */ + const language = getLanguage() + + return computed(() => !dict?.value ? null : getLocalizedValue<T>(dict.value as ILocalizedValue<T>, language)) +} diff --git a/apps/settings/src/composables/useGroupsNavigation.ts b/apps/settings/src/composables/useGroupsNavigation.ts new file mode 100644 index 00000000000..d9f0637843b --- /dev/null +++ b/apps/settings/src/composables/useGroupsNavigation.ts @@ -0,0 +1,59 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import type { ComputedRef, Ref } from 'vue' +import type { IGroup } from '../views/user-types' + +import { computed } from 'vue' + +/** + * Format a group to a menu entry + * + * @param group the group + */ +function formatGroupMenu(group?: IGroup) { + if (typeof group === 'undefined') { + return null + } + + return { + id: group.id, + title: group.name, + usercount: group.usercount ?? 0, + count: Math.max(0, (group.usercount ?? 0) - (group.disabled ?? 0)), + } +} + +export const useFormatGroups = (groups: Ref<IGroup[]>|ComputedRef<IGroup[]>) => { + /** + * All non-disabled non-admin groups + */ + const userGroups = computed(() => { + const formatted = groups.value + // filter out disabled and admin + .filter(group => group.id !== 'disabled' && group.id !== '__nc_internal_recent' && group.id !== 'admin') + // format group + .map(group => formatGroupMenu(group)) + // remove invalid + .filter(group => group !== null) + return formatted as NonNullable<ReturnType<typeof formatGroupMenu>>[] + }) + + /** + * The admin group if found otherwise null + */ + const adminGroup = computed(() => formatGroupMenu(groups.value.find(group => group.id === 'admin'))) + + /** + * The group of disabled users + */ + const disabledGroup = computed(() => formatGroupMenu(groups.value.find(group => group.id === 'disabled'))) + + /** + * The group of recent users + */ + const recentGroup = computed(() => formatGroupMenu(groups.value.find(group => group.id === '__nc_internal_recent'))) + + return { adminGroup, recentGroup, disabledGroup, userGroups } +} |