aboutsummaryrefslogtreecommitdiffstats
path: root/apps/settings/src/composables
diff options
context:
space:
mode:
Diffstat (limited to 'apps/settings/src/composables')
-rw-r--r--apps/settings/src/composables/useAppIcon.ts62
-rw-r--r--apps/settings/src/composables/useGetLocalizedValue.ts29
-rw-r--r--apps/settings/src/composables/useGroupsNavigation.ts59
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 }
+}