diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/settings/src/components/AppStoreDiscover/AppLink.vue | 117 | ||||
-rw-r--r-- | apps/settings/src/components/AppStoreDiscover/PostType.vue | 14 |
2 files changed, 125 insertions, 6 deletions
diff --git a/apps/settings/src/components/AppStoreDiscover/AppLink.vue b/apps/settings/src/components/AppStoreDiscover/AppLink.vue new file mode 100644 index 00000000000..88006b1d39b --- /dev/null +++ b/apps/settings/src/components/AppStoreDiscover/AppLink.vue @@ -0,0 +1,117 @@ +<!-- + - @copyright Copyright (c) 2024 Ferdinand Thiessen <opensource@fthiessen.de> + - + - @author Ferdinand Thiessen <opensource@fthiessen.de> + - + - @license AGPL-3.0-or-later + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> +<template> + <a v-if="linkProps" v-bind="linkProps"> + <slot /> + </a> + <RouterLink v-else-if="routerProps" v-bind="routerProps"> + <slot /> + </RouterLink> +</template> + +<script lang="ts"> +import type { RouterLinkProps } from 'vue-router/types/router.js' + +import { loadState } from '@nextcloud/initial-state' +import { generateUrl } from '@nextcloud/router' +import { defineComponent } from 'vue' +import { RouterLink } from 'vue-router' + +const knownRoutes = Object.fromEntries( + Object.entries( + loadState<Record<string, { app?: string, href: string }>>('core', 'apps'), + ).map(([k, v]) => [v.app ?? k, v.href]), +) + +/** + * This component either shows a native link to the installed app or external size - or a router link to the appstore page of the app if not installed + */ +export default defineComponent({ + name: 'AppLink', + + components: { RouterLink }, + + props: { + href: { + type: String, + required: true, + }, + }, + + data() { + return { + routerProps: undefined as RouterLinkProps|undefined, + linkProps: undefined as Record<string, string>|undefined, + } + }, + + watch: { + href: { + immediate: true, + handler() { + const match = this.href.match(/^app:\/\/([^/]+)(\/.+)?$/) + this.routerProps = undefined + this.linkProps = undefined + + // not an app url + if (match === null) { + this.linkProps = { + href: this.href, + target: '_blank', + rel: 'noreferrer noopener', + } + return + } + + const appId = match[1] + // Check if specific route was requested + if (match[2]) { + // we do no know anything about app internal path so we only allow generic app paths + this.linkProps = { + href: generateUrl(`/apps/${appId}${match[2]}`), + } + return + } + + // If we know any route for that app we open it + if (appId in knownRoutes) { + this.linkProps = { + href: knownRoutes[appId], + } + return + } + + // Fallback to show the app store entry + this.routerProps = { + to: { + name: 'apps-details', + params: { + category: 'installed', + id: appId, + }, + }, + } + }, + }, + }, +}) +</script> diff --git a/apps/settings/src/components/AppStoreDiscover/PostType.vue b/apps/settings/src/components/AppStoreDiscover/PostType.vue index 443daa6287f..df4755483f8 100644 --- a/apps/settings/src/components/AppStoreDiscover/PostType.vue +++ b/apps/settings/src/components/AppStoreDiscover/PostType.vue @@ -23,18 +23,18 @@ <article :id="domId" class="app-discover-post" :class="{ 'app-discover-post--reverse': media && media.alignment === 'start' }"> - <component :is="link ? 'a' : 'div'" + <component :is="link ? 'AppLink' : 'div'" v-if="headline || text" :href="link" - :target="link ? '_blank' : undefined" class="app-discover-post__text"> - <component :is="inline ? 'h4' : 'h3'">{{ translatedHeadline }}</component> + <component :is="inline ? 'h4' : 'h3'"> + {{ translatedHeadline }} + </component> <p>{{ translatedText }}</p> </component> - <component :is="mediaLink ? 'a' : 'div'" + <component :is="mediaLink ? 'AppLink' : 'div'" v-if="mediaSources" :href="mediaLink" - :target="mediaLink ? '_blank' : undefined" class="app-discover-post__media" :class="{ 'app-discover-post__media--fullwidth': isFullWidth, @@ -79,9 +79,11 @@ import { commonAppDiscoverProps } from './common' import { useLocalizedValue } from '../../composables/useGetLocalizedValue' import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js' +import AppLink from './AppLink.vue' export default defineComponent({ components: { + AppLink, NcIconSvgWrapper, }, @@ -116,8 +118,8 @@ export default defineComponent({ setup(props) { const translatedHeadline = useLocalizedValue(computed(() => props.headline)) const translatedText = useLocalizedValue(computed(() => props.text)) - const localizedMedia = useLocalizedValue(computed(() => props.media?.content)) + const mediaSources = computed(() => localizedMedia.value !== null ? [localizedMedia.value.src].flat() : undefined) const mediaAlt = computed(() => localizedMedia.value?.alt ?? '') |