123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- <!--
- - @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
- -
- - @author Julius Härtl <jus@bitgrid.net>
- -
- - @license GNU AGPL version 3 or any later version
- -
- - 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>
- <NcContent app-name="settings"
- :class="{ 'with-app-sidebar': app}">
- <!-- Categories & filters -->
- <NcAppNavigation :class="{ 'icon-loading': loading }"
- :aria-label="t('settings', 'Apps')">
- <template #list>
- <NcAppNavigationItem id="app-category-your-apps"
- :to="{ name: 'apps' }"
- :exact="true"
- icon="icon-category-installed"
- :name="$options.APPS_SECTION_ENUM.installed" />
- <NcAppNavigationItem id="app-category-enabled"
- :to="{ name: 'apps-category', params: { category: 'enabled' } }"
- icon="icon-category-enabled"
- :name="$options.APPS_SECTION_ENUM.enabled" />
- <NcAppNavigationItem id="app-category-disabled"
- :to="{ name: 'apps-category', params: { category: 'disabled' } }"
- icon="icon-category-disabled"
- :name="$options.APPS_SECTION_ENUM.disabled" />
- <NcAppNavigationItem v-if="updateCount > 0"
- id="app-category-updates"
- :to="{ name: 'apps-category', params: { category: 'updates' } }"
- icon="icon-download"
- :name="$options.APPS_SECTION_ENUM.updates">
- <template #counter>
- <NcCounterBubble>{{ updateCount }}</NcCounterBubble>
- </template>
- </NcAppNavigationItem>
- <NcAppNavigationItem v-if="isSubscribed"
- id="app-category-supported"
- :to="{ name: 'apps-category', params: { category: 'supported' } }"
- :name="$options.APPS_SECTION_ENUM.supported">
- <template #icon>
- <IconStarShooting :size="20" />
- </template>
- </NcAppNavigationItem>
- <NcAppNavigationItem id="app-category-your-bundles"
- :to="{ name: 'apps-category', params: { category: 'app-bundles' } }"
- icon="icon-category-app-bundles"
- :name="$options.APPS_SECTION_ENUM['app-bundles']" />
- <NcAppNavigationSpacer />
- <!-- App store categories -->
- <template v-if="appstoreEnabled">
- <NcAppNavigationItem id="app-category-featured"
- :to="{ name: 'apps-category', params: { category: 'featured' } }"
- icon="icon-favorite"
- :name="$options.APPS_SECTION_ENUM.featured" />
- <NcAppNavigationItem v-for="cat in categories"
- :key="'icon-category-' + cat.id"
- :icon="'icon-category-' + cat.id"
- :to="{
- name: 'apps-category',
- params: { category: cat.id },
- }"
- :name="cat.displayName" />
- </template>
- <NcAppNavigationItem id="app-developer-docs"
- :name="t('settings', 'Developer documentation') + ' ↗'"
- @click="openDeveloperDocumentation" />
- </template>
- </NcAppNavigation>
- <!-- Apps list -->
- <NcAppContent class="app-settings-content"
- :class="{ 'icon-loading': loadingList }"
- :page-heading="pageHeading">
- <AppList :category="category" :app="app" :search="searchQuery" />
- </NcAppContent>
- <!-- Selected app details -->
- <NcAppSidebar v-if="id && app"
- v-bind="appSidebar"
- :class="{'app-sidebar--without-background': !appSidebar.background}"
- @close="hideAppDetails">
- <template v-if="!appSidebar.background" #header>
- <div class="app-sidebar-header__figure--default-app-icon icon-settings-dark" />
- </template>
- <template #description>
- <!-- Featured/Supported badges -->
- <AppLevelBadge :level="app.level" />
- <AppScore v-if="hasRating" :score="app.appstoreData.ratingOverall" />
- <div class="app-version">
- <p>{{ app.version }}</p>
- </div>
- </template>
- <!-- Tab content -->
- <NcAppSidebarTab id="desc"
- icon="icon-category-office"
- :name="t('settings', 'Details')"
- :order="0">
- <AppDetails :app="app" />
- </NcAppSidebarTab>
- <NcAppSidebarTab v-if="app.appstoreData && app.releases[0].translations.en.changelog"
- id="desca"
- icon="icon-category-organization"
- :name="t('settings', 'Changelog')"
- :order="1">
- <div v-for="release in app.releases" :key="release.version" class="app-sidebar-tabs__release">
- <h2>{{ release.version }}</h2>
- <Markdown v-if="changelog(release)" :min-heading="3" :text="changelog(release)" />
- </div>
- </NcAppSidebarTab>
- </NcAppSidebar>
- </NcContent>
- </template>
- <script>
- import { subscribe, unsubscribe } from '@nextcloud/event-bus'
- import Vue from 'vue'
- import VueLocalStorage from 'vue-localstorage'
- import NcAppContent from '@nextcloud/vue/dist/Components/NcAppContent.js'
- import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation.js'
- import NcAppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
- import NcAppNavigationSpacer from '@nextcloud/vue/dist/Components/NcAppNavigationSpacer.js'
- import NcAppSidebar from '@nextcloud/vue/dist/Components/NcAppSidebar.js'
- import NcAppSidebarTab from '@nextcloud/vue/dist/Components/NcAppSidebarTab.js'
- import NcCounterBubble from '@nextcloud/vue/dist/Components/NcCounterBubble.js'
- import NcContent from '@nextcloud/vue/dist/Components/NcContent.js'
- import IconStarShooting from 'vue-material-design-icons/StarShooting.vue'
- import AppList from '../components/AppList.vue'
- import AppDetails from '../components/AppDetails.vue'
- import AppManagement from '../mixins/AppManagement.js'
- import AppLevelBadge from '../components/AppList/AppLevelBadge.vue'
- import AppScore from '../components/AppList/AppScore.vue'
- import Markdown from '../components/Markdown.vue'
- import { APPS_SECTION_ENUM } from './../constants/AppsConstants.js'
- import { loadState } from '@nextcloud/initial-state'
- Vue.use(VueLocalStorage)
- const appstoreEnabled = loadState('settings', 'appstoreEnabled')
- const developerDocumentation = loadState('settings', 'appstoreDeveloperDocs')
- export default {
- name: 'Apps',
- APPS_SECTION_ENUM,
- components: {
- NcAppContent,
- AppDetails,
- AppList,
- AppLevelBadge,
- IconStarShooting,
- NcAppNavigation,
- NcAppNavigationItem,
- NcAppNavigationSpacer,
- NcCounterBubble,
- AppScore,
- NcAppSidebar,
- NcAppSidebarTab,
- NcContent,
- Markdown,
- },
- mixins: [AppManagement],
- props: {
- category: {
- type: String,
- default: 'installed',
- },
- id: {
- type: String,
- default: '',
- },
- },
- data() {
- return {
- searchQuery: '',
- screenshotLoaded: false,
- }
- },
- computed: {
- appstoreEnabled() {
- return appstoreEnabled
- },
- pageHeading() {
- if (this.$options.APPS_SECTION_ENUM[this.category]) {
- return this.$options.APPS_SECTION_ENUM[this.category]
- }
- const category = this.$store.getters.getCategoryById(this.category)
- return category.displayName
- },
- loading() {
- return this.$store.getters.loading('categories')
- },
- loadingList() {
- return this.$store.getters.loading('list')
- },
- app() {
- return this.apps.find(app => app.id === this.id)
- },
- categories() {
- return this.$store.getters.getCategories
- },
- apps() {
- return this.$store.getters.getAllApps
- },
- updateCount() {
- return this.$store.getters.getUpdateCount
- },
- hasRating() {
- return this.app.appstoreData && this.app.appstoreData.ratingNumOverall > 5
- },
- // sidebar app binding
- appSidebar() {
- const authorName = (xmlNode) => {
- if (xmlNode['@value']) {
- // Complex node (with email or homepage attribute)
- return xmlNode['@value']
- }
- // Simple text node
- return xmlNode
- }
- const author = Array.isArray(this.app.author)
- ? this.app.author.map(authorName).join(', ')
- : authorName(this.app.author)
- const license = t('settings', '{license}-licensed', { license: ('' + this.app.licence).toUpperCase() })
- const subname = t('settings', 'by {author}\n{license}', { author, license })
- return {
- background: this.app.screenshot && this.screenshotLoaded
- ? this.app.screenshot
- : this.app.preview,
- compact: !(this.app.screenshot && this.screenshotLoaded),
- name: this.app.name,
- subname,
- }
- },
- changelog() {
- return (release) => release.translations.en.changelog
- },
- /**
- * Check if the current instance has a support subscription from the Nextcloud GmbH
- */
- isSubscribed() {
- // For customers of the Nextcloud GmbH the app level will be set to `300` for apps that are supported in their subscription
- return this.apps.some(app => app.level === 300)
- },
- },
- watch: {
- category() {
- this.searchQuery = ''
- },
- app() {
- this.screenshotLoaded = false
- if (this.app?.releases && this.app?.screenshot) {
- const image = new Image()
- image.onload = () => {
- this.screenshotLoaded = true
- }
- image.src = this.app.screenshot
- }
- },
- },
- beforeMount() {
- this.$store.dispatch('getCategories', { shouldRefetchCategories: true })
- this.$store.dispatch('getAllApps')
- this.$store.dispatch('getGroups', { offset: 0, limit: 5 })
- },
- mounted() {
- subscribe('nextcloud:unified-search.search', this.setSearch)
- subscribe('nextcloud:unified-search.reset', this.resetSearch)
- },
- beforeDestroy() {
- unsubscribe('nextcloud:unified-search.search', this.setSearch)
- unsubscribe('nextcloud:unified-search.reset', this.resetSearch)
- },
- methods: {
- setSearch({ query }) {
- this.searchQuery = query
- },
- resetSearch() {
- this.searchQuery = ''
- },
- hideAppDetails() {
- this.$router.push({
- name: 'apps-category',
- params: { category: this.category },
- })
- },
- openDeveloperDocumentation() {
- window.open(developerDocumentation)
- },
- },
- }
- </script>
- <style lang="scss" scoped>
- .app-sidebar::v-deep {
- &:not(.app-sidebar--without-background) {
- // with full screenshot, let's fill the figure
- :not(.app-sidebar-header--compact) .app-sidebar-header__figure {
- background-size: cover;
- }
- // revert sidebar app icon so it is black
- .app-sidebar-header--compact .app-sidebar-header__figure {
- background-size: 32px;
- filter: var(--background-invert-if-bright);
- }
- }
- .app-sidebar-header__description {
- .app-version {
- padding-left: 10px;
- }
- }
- // default icon slot styling
- &.app-sidebar--without-background {
- .app-sidebar-header__figure {
- display: flex;
- align-items: center;
- justify-content: center;
- &--default-app-icon {
- width: 32px;
- height: 32px;
- background-size: 32px;
- }
- }
- }
- // TODO: migrate to components
- .app-sidebar-header__desc {
- // allow multi line subtitle for the license
- .app-sidebar-header__subtitle {
- overflow: visible !important;
- height: auto;
- white-space: normal !important;
- line-height: 16px;
- }
- }
- .app-sidebar-header__action {
- // align with tab content
- margin: 0 20px;
- input {
- margin: 3px;
- }
- }
- }
- // Align the appNavigation toggle with the apps header toolbar
- .app-navigation::v-deep button.app-navigation-toggle {
- top: 8px;
- right: -8px;
- }
- .app-sidebar-tabs__release {
- h2 {
- border-bottom: 1px solid var(--color-border);
- }
- // Overwrite changelog heading styles
- ::v-deep {
- h3 {
- font-size: 20px;
- }
- h4 {
- font-size: 17px;
- }
- }
- }
- </style>
|