Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/settings/src/app-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,5 @@ export interface IAppstoreApp {
isCompatible: boolean

appstoreData: Record<string, never>
releases: IAppstoreAppRelease[]
releases?: IAppstoreAppRelease[]
}
117 changes: 117 additions & 0 deletions apps/settings/src/components/AppStoreDiscover/AppLink.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<!--
- @copyright Copyright (c) 2024 Ferdinand Thiessen <[email protected]>
-
- @author Ferdinand Thiessen <[email protected]>
-
- @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: this.$route.params?.category ?? 'discover',
id: appId,
},
},
}
},
},
},
})
</script>
14 changes: 8 additions & 6 deletions apps/settings/src/components/AppStoreDiscover/PostType.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
},

Expand Down Expand Up @@ -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 ?? '')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import Markdown from '../Markdown.vue'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const props = defineProps<{ app: IAppstoreApp }>()

const hasChangelog = computed(() => Object.values(props.app.releases[0]?.translations ?? {}).some(({ changelog }) => !!changelog))
const hasChangelog = computed(() => Object.values(props.app.releases?.[0]?.translations ?? {}).some(({ changelog }) => !!changelog))

const createChangelogFromRelease = (release: IAppstoreAppRelease) => release.translations?.[getLanguage()]?.changelog ?? release.translations?.en?.changelog ?? ''
</script>
Expand Down
7 changes: 7 additions & 0 deletions apps/settings/src/router/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ const routes: RouteConfig[] = [
{
path: '/:index(index.php/)?settings/apps',
name: 'apps',
// redirect to our default route - the app discover section
redirect: {
name: 'apps-category',
params: {
category: 'discover',
},
},
components: {
default: AppStore,
navigation: AppStoreNavigation,
Expand Down
2 changes: 1 addition & 1 deletion apps/settings/src/store/apps-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const useAppsStore = defineStore('settings-apps', {
return this.categories.find(({ id }) => id === categoryId) ?? null
},

getAppById(appId: string) {
getAppById(appId: string): IAppstoreApp|null {
return this.apps.find(({ id }) => id === appId) ?? null
},
},
Expand Down
4 changes: 1 addition & 3 deletions apps/settings/src/views/AppStoreNavigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
<NcAppNavigation :aria-label="t('settings', 'Apps')">
<template #list>
<NcAppNavigationItem id="app-category-discover"
:to="{ name: 'apps' }"
:exact="true"
:to="{ name: 'apps-category', params: { category: 'discover'} }"
:name="APPS_SECTION_ENUM.discover">
<template #icon>
<NcIconSvgWrapper :path="APPSTORE_CATEGORY_ICONS.discover" />
</template>
</NcAppNavigationItem>
<NcAppNavigationItem id="app-category-installed"
:to="{ name: 'apps-category', params: { category: 'installed'} }"
:exact="true"
:name="APPS_SECTION_ENUM.installed">
<template #icon>
<NcIconSvgWrapper :path="APPSTORE_CATEGORY_ICONS.installed" />
Expand Down
2 changes: 1 addition & 1 deletion apps/settings/src/views/AppStoreSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const hasRating = computed(() => app.value.appstoreData?.ratingNumOverall > 5)
const rating = computed(() => app.value.appstoreData?.ratingNumRecent > 5
? app.value.appstoreData.ratingRecent
: (app.value.appstoreData?.ratingOverall ?? 0.5))
const showSidebar = computed(() => app.value)
const showSidebar = computed(() => app.value !== null)

const { appIcon } = useAppIcon(app)

Expand Down
4 changes: 2 additions & 2 deletions dist/8484-8484.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/8484-8484.js.map

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions dist/8753-8753.js

This file was deleted.

1 change: 0 additions & 1 deletion dist/8753-8753.js.map

This file was deleted.

Loading