From 8e5d02be2dd4f8d35e3861e41efcdb40b44ebd10 Mon Sep 17 00:00:00 2001 From: Peter Ringelmann Date: Wed, 6 May 2026 11:22:05 +0200 Subject: [PATCH] feat: add app store link for non-admins -e Signed-off-by: Peter Ringelmann --- core/src/components/AppItem.vue | 5 +++++ core/src/components/AppMenu.vue | 25 +++++++++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/core/src/components/AppItem.vue b/core/src/components/AppItem.vue index 1aa146febe0..64922cad6a5 100644 --- a/core/src/components/AppItem.vue +++ b/core/src/components/AppItem.vue @@ -11,6 +11,8 @@ 'app-item--outlined': outlined, }" :href="app.href" + :target="newTab ? '_blank' : undefined" + :rel="newTab ? 'noopener noreferrer' : undefined" :aria-current="app.active ? 'page' : undefined" :tabindex="tabindex" :title="app.name" @@ -41,6 +43,8 @@ import { computed } from 'vue' const props = withDefaults(defineProps<{ app: INavigationEntry + /** When true, the link opens in a new tab with rel="noopener noreferrer". Used for external destinations (e.g. the app store). */ + newTab?: boolean /** When true, render the circle as an outline only (used for "More apps" / utility entries). */ outlined?: boolean /** @@ -50,6 +54,7 @@ const props = withDefaults(defineProps<{ */ tabindex?: number }>(), { + newTab: false, outlined: false, tabindex: -1, }) diff --git a/core/src/components/AppMenu.vue b/core/src/components/AppMenu.vue index 14e20908593..cda65f5bab9 100644 --- a/core/src/components/AppMenu.vue +++ b/core/src/components/AppMenu.vue @@ -39,7 +39,8 @@ :key="item.id" ref="items" :app="item" - :outlined="item.id === 'more-apps'" + :outlined="item.id === 'more-apps' || item.id === 'app-store'" + :newTab="item.id === 'more-apps' || item.id === 'app-store'" :tabindex="i === focusedIndex ? 0 : -1" /> @@ -109,7 +110,9 @@ export default defineComponent({ // The current-app button lives outside the slot, so we track the // source and restore focus manually via setReturnFocus. openedFrom: null as 'waffle' | 'currentApp' | null, - // Synthetic admin-only tile linking to the app store; not a real nav entry. + // Synthetic tile appended to the grid: admins jump to the local + // app management page; everyone else lands on apps.nextcloud.com + // (external, opens in a new tab via the per-tile newTab flag). moreAppsEntry: { id: 'more-apps', active: false, @@ -121,6 +124,17 @@ export default defineComponent({ unread: 0, } as INavigationEntry, + appStoreEntry: { + id: 'app-store', + active: false, + order: Number.MAX_SAFE_INTEGER, + href: 'https://apps.nextcloud.com/', + icon: generateFilePath('settings', 'img', 'apps.svg'), + type: 'link', + name: t('core', 'App store'), + unread: 0, + } as INavigationEntry, + // `placement: bottom-start` swaps the anchor edge under RTL but the // skidding sign isn't auto-mirrored, so we flip it here. Snapshot // at init: Nextcloud's language doesn't change at runtime. @@ -133,9 +147,12 @@ export default defineComponent({ return this.appList.find((app) => app.active) }, - // Stable-ordered list that focusedIndex indexes into; adds "More apps" for admins. + // Stable-ordered list that focusedIndex indexes into. The trailing + // utility tile is "More apps" (local app management) for admins and + // "App store" (apps.nextcloud.com) for everyone else. gridItems(): INavigationEntry[] { - return this.isAdmin ? [...this.appList, this.moreAppsEntry] : [...this.appList] + const tail = this.isAdmin ? this.moreAppsEntry : this.appStoreEntry + return [...this.appList, tail] }, },