diff options
Diffstat (limited to 'apps/settings/src/views/AppStoreSidebar.vue')
-rw-r--r-- | apps/settings/src/views/AppStoreSidebar.vue | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/apps/settings/src/views/AppStoreSidebar.vue b/apps/settings/src/views/AppStoreSidebar.vue new file mode 100644 index 00000000000..b4041066c67 --- /dev/null +++ b/apps/settings/src/views/AppStoreSidebar.vue @@ -0,0 +1,159 @@ +<!-- + - SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> +<template> + <!-- Selected app details --> + <NcAppSidebar v-if="showSidebar" + class="app-sidebar" + :class="{ 'app-sidebar--with-screenshot': hasScreenshot }" + :active.sync="activeTab" + :background="hasScreenshot ? app.screenshot : undefined" + :compact="!hasScreenshot" + :name="app.name" + :title="app.name" + :subname="licenseText" + :subtitle="licenseText" + @close="hideAppDetails"> + <!-- Fallback icon incase no app icon is available --> + <template v-if="!hasScreenshot" #header> + <NcIconSvgWrapper class="app-sidebar__fallback-icon" + :svg="appIcon ?? ''" + :size="64" /> + </template> + + <template #description> + <!-- Featured/Supported badges --> + <div class="app-sidebar__badges"> + <AppLevelBadge :level="app.level" /> + <AppDaemonBadge v-if="app.app_api && app.daemon" :daemon="app.daemon" /> + <AppScore v-if="hasRating" :score="rating" /> + </div> + </template> + + <!-- Tab content --> + <AppDescriptionTab :app="app" /> + <AppDetailsTab :app="app" /> + <AppReleasesTab :app="app" /> + <AppDeployDaemonTab :app="app" /> + </NcAppSidebar> +</template> + +<script setup lang="ts"> +import { translate as t } from '@nextcloud/l10n' +import { computed, onMounted, ref, watch } from 'vue' +import { useRoute, useRouter } from 'vue-router/composables' +import { useAppsStore } from '../store/apps-store' + +import NcAppSidebar from '@nextcloud/vue/components/NcAppSidebar' +import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper' +import AppScore from '../components/AppList/AppScore.vue' +import AppDescriptionTab from '../components/AppStoreSidebar/AppDescriptionTab.vue' +import AppDetailsTab from '../components/AppStoreSidebar/AppDetailsTab.vue' +import AppReleasesTab from '../components/AppStoreSidebar/AppReleasesTab.vue' +import AppDeployDaemonTab from '../components/AppStoreSidebar/AppDeployDaemonTab.vue' +import AppLevelBadge from '../components/AppList/AppLevelBadge.vue' +import AppDaemonBadge from '../components/AppList/AppDaemonBadge.vue' +import { useAppIcon } from '../composables/useAppIcon.ts' +import { useStore } from '../store' +import { useAppApiStore } from '../store/app-api-store.ts' + +const route = useRoute() +const router = useRouter() +const store = useAppsStore() +const appApiStore = useAppApiStore() +const legacyStore = useStore() + +const appId = computed(() => route.params.id ?? '') +const app = computed(() => { + if (legacyStore.getters.isAppApiEnabled) { + const exApp = appApiStore.getAllApps + .find((app) => app.id === appId.value) ?? null + if (exApp) { + return exApp + } + } + return store.getAppById(appId.value)! +}) +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 !== null) + +const { appIcon } = useAppIcon(app) + +/** + * The second text line shown on the sidebar + */ +const licenseText = computed(() => { + if (!app.value) { + return '' + } + if (app.value.license !== '') { + return t('settings', 'Version {version}, {license}-licensed', { version: app.value.version, license: app.value.licence.toString().toUpperCase() }) + } + return t('settings', 'Version {version}', { version: app.value.version }) +}) + +const activeTab = ref('details') +watch([app], () => { activeTab.value = 'details' }) + +/** + * Hide the details sidebar by pushing a new route + */ +const hideAppDetails = () => { + router.push({ + name: 'apps-category', + params: { category: route.params.category }, + }) +} + +/** + * Whether the app screenshot is loaded + */ +const screenshotLoaded = ref(false) +const hasScreenshot = computed(() => app.value?.screenshot && screenshotLoaded.value) +/** + * Preload the app screenshot + */ +const loadScreenshot = () => { + if (app.value?.releases && app.value?.screenshot) { + const image = new Image() + image.onload = () => { + screenshotLoaded.value = true + } + image.src = app.value.screenshot + } +} +// Watch app and set screenshot loaded when +watch([app], loadScreenshot) +onMounted(loadScreenshot) +</script> + +<style scoped lang="scss"> +.app-sidebar { + // If a screenshot is available it should cover the whole figure + &--with-screenshot { + :deep(.app-sidebar-header__figure) { + background-size: cover; + } + } + + &__fallback-icon { + // both 100% to center the icon + width: 100%; + height: 100%; + } + + &__badges { + display: flex; + flex-direction: row; + gap: 12px; + } + + &__version { + color: var(--color-text-maxcontrast); + } +} +</style> |