diff options
author | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2020-01-18 10:21:27 +0100 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2020-08-14 09:56:00 +0200 |
commit | 228a96508a22b3805505ce61e0d36eba1e703460 (patch) | |
tree | c957d471036a4041217007f52f374e21fd10b8bf /apps | |
parent | b13aa660c91873437afec36ef6466ef609b7959c (diff) | |
download | nextcloud-server-228a96508a22b3805505ce61e0d36eba1e703460.tar.gz nextcloud-server-228a96508a22b3805505ce61e0d36eba1e703460.zip |
Use appsidebar for apps
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Diffstat (limited to 'apps')
-rw-r--r-- | apps/settings/src/components/AppDetails.vue | 57 | ||||
-rw-r--r-- | apps/settings/src/components/AppList/AppItem.vue | 14 | ||||
-rw-r--r-- | apps/settings/src/mixins/AppManagement.js (renamed from apps/settings/src/components/AppManagement.vue) | 64 | ||||
-rw-r--r-- | apps/settings/src/views/Apps.vue | 132 |
4 files changed, 174 insertions, 93 deletions
diff --git a/apps/settings/src/components/AppDetails.vue b/apps/settings/src/components/AppDetails.vue index 2c99e9c0c92..fa586b3d0a1 100644 --- a/apps/settings/src/components/AppDetails.vue +++ b/apps/settings/src/components/AppDetails.vue @@ -22,46 +22,6 @@ <template> <div id="app-details-view" style="padding: 20px;"> - <h2> - <div v-if="!app.preview" class="icon-settings-dark" /> - <svg v-if="app.previewAsIcon && app.preview" - width="32" - height="32" - viewBox="0 0 32 32"> - <defs><filter :id="filterId"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /></filter></defs> - <image x="0" - y="0" - width="32" - height="32" - preserveAspectRatio="xMinYMin meet" - :filter="filterUrl" - :xlink:href="app.preview" - class="app-icon" /> - </svg> - {{ app.name }} - </h2> - <img v-if="screenshotLoaded" :src="app.screenshot" width="100%"> - <div v-if="app.level === 300 || app.level === 200 || hasRating" class="app-level"> - <span v-if="app.level === 300" - v-tooltip.auto="t('settings', 'This app is supported via your current Nextcloud subscription.')" - class="supported icon-checkmark-color"> - {{ t('settings', 'Supported') }}</span> - <span v-if="app.level === 200" - v-tooltip.auto="t('settings', 'Featured apps are developed by and within the community. They offer central functionality and are ready for production use.')" - class="official icon-checkmark"> - {{ t('settings', 'Featured') }}</span> - <AppScore v-if="hasRating" :score="app.appstoreData.ratingOverall" /> - </div> - - <div v-if="author" class="app-author"> - {{ t('settings', 'by') }} - <span v-for="(a, index) in author" :key="index"> - <a v-if="a['@attributes'] && a['@attributes']['homepage']" :href="a['@attributes']['homepage']">{{ a['@value'] }}</a><span v-else-if="a['@value']">{{ a['@value'] }}</span><span v-else>{{ a }}</span><span v-if="index+1 < author.length">, </span> - </span> - </div> - <div v-if="licence" class="app-licence"> - {{ licence }} - </div> <div class="actions"> <div class="actions-buttons"> <input v-if="app.update" @@ -192,24 +152,26 @@ import marked from 'marked' import dompurify from 'dompurify' import AppScore from './AppList/AppScore' -import AppManagement from './AppManagement' +import AppManagement from '../mixins/AppManagement' import PrefixMixin from './PrefixMixin' -import SvgFilterMixin from './SvgFilterMixin' export default { name: 'AppDetails', + components: { Multiselect, AppScore, }, - mixins: [AppManagement, PrefixMixin, SvgFilterMixin], + mixins: [AppManagement, PrefixMixin], + props: ['category', 'app'], + data() { return { groupCheckedAppsData: false, - screenshotLoaded: false, } }, + computed: { appstoreUrl() { return `https://apps.nextcloud.com/apps/${this.app.id}` @@ -309,13 +271,6 @@ export default { if (this.app.groups.length > 0) { this.groupCheckedAppsData = true } - if (this.app.screenshot) { - const image = new Image() - image.onload = (e) => { - this.screenshotLoaded = true - } - image.src = this.app.screenshot - } }, } </script> diff --git a/apps/settings/src/components/AppList/AppItem.vue b/apps/settings/src/components/AppList/AppItem.vue index eeaa3ae3899..41ba0d9578a 100644 --- a/apps/settings/src/components/AppList/AppItem.vue +++ b/apps/settings/src/components/AppList/AppItem.vue @@ -69,38 +69,38 @@ <div v-if="app.error" class="warning"> {{ app.error }} </div> - <div v-if="loading(app.id)" class="icon icon-loading-small" /> + <div v-if="isLoading" class="icon icon-loading-small" /> <input v-if="app.update" class="update primary" type="button" :value="t('settings', 'Update to {update}', {update:app.update})" - :disabled="installing || loading(app.id)" + :disabled="installing || isLoading" @click.stop="update(app.id)"> <input v-if="app.canUnInstall" class="uninstall" type="button" :value="t('settings', 'Remove')" - :disabled="installing || loading(app.id)" + :disabled="installing || isLoading" @click.stop="remove(app.id)"> <input v-if="app.active" class="enable" type="button" :value="t('settings','Disable')" - :disabled="installing || loading(app.id)" + :disabled="installing || isLoading" @click.stop="disable(app.id)"> <input v-if="!app.active && (app.canInstall || app.isCompatible)" v-tooltip.auto="enableButtonTooltip" class="enable" type="button" :value="enableButtonText" - :disabled="!app.canInstall || installing || loading(app.id)" + :disabled="!app.canInstall || installing || isLoading" @click.stop="enable(app.id)"> <input v-else-if="!app.active" v-tooltip.auto="forceEnableButtonTooltip" class="enable force" type="button" :value="forceEnableButtonText" - :disabled="installing || loading(app.id)" + :disabled="installing || isLoading" @click.stop="forceEnable(app.id)"> </div> </div> @@ -108,7 +108,7 @@ <script> import AppScore from './AppScore' -import AppManagement from '../AppManagement' +import AppManagement from '../../mixins/AppManagement' import SvgFilterMixin from '../SvgFilterMixin' export default { diff --git a/apps/settings/src/components/AppManagement.vue b/apps/settings/src/mixins/AppManagement.js index 6bf1eee83cf..0bdb238601d 100644 --- a/apps/settings/src/components/AppManagement.vue +++ b/apps/settings/src/mixins/AppManagement.js @@ -1,40 +1,37 @@ -<!-- - - @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/>. - - - --> +/** + * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.net> + * @author John Molakvoæ <skjnldsv@protonmail.com> + * + * @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/>. + * + */ -<script> export default { computed: { appGroups() { return this.app.groups.map(group => { return { id: group, name: group } }) }, - loading() { - const self = this - return function(id) { - return self.$store.getters.loading(id) - } - }, installing() { return this.$store.getters.loading('install') }, + isLoading() { + return this.app && this.$store.getters.loading(this.app.id) + }, enableButtonText() { if (this.app.needsDownload) { return t('settings', 'Download and enable') @@ -61,11 +58,19 @@ export default { return base }, }, + + data() { + return { + groupCheckedAppsData: false, + } + }, + mounted() { - if (this.app.groups.length > 0) { + if (this.app && this.app.groups && this.app.groups.length > 0) { this.groupCheckedAppsData = true } }, + methods: { asyncFindGroup(query) { return this.$store.dispatch('getGroups', { search: query, limit: 5, offset: 0 }) @@ -135,4 +140,3 @@ export default { }, }, } -</script> diff --git a/apps/settings/src/views/Apps.vue b/apps/settings/src/views/Apps.vue index 2c7dd9dae0e..13ddd8bfaf8 100644 --- a/apps/settings/src/views/Apps.vue +++ b/apps/settings/src/views/Apps.vue @@ -22,7 +22,7 @@ <template> <Content app-name="settings" - :class="{ 'with-app-sidebar': currentApp}" + :class="{ 'with-app-sidebar': app}" :content-class="{ 'icon-loading': loadingList }" :navigation-class="{ 'icon-loading': loading }"> <AppNavigation> @@ -87,15 +87,70 @@ </template> </AppNavigation> <AppContent class="app-settings-content" :class="{ 'icon-loading': loadingList }"> - <AppList :category="category" :app="currentApp" :search="searchQuery" /> + <AppList :category="category" :app="app" :search="searchQuery" /> </AppContent> - <AppSidebar v-if="id && currentApp" @close="hideAppDetails"> - <AppDetails :category="category" :app="currentApp" /> + <AppSidebar + 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 #primary-actions> + <div v-if="app.level === 300 || app.level === 200 || hasRating" class="app-level"> + <span v-if="app.level === 300" + v-tooltip.auto="t('settings', 'This app is supported via your current Nextcloud subscription.')" + class="supported icon-checkmark-color"> + {{ t('settings', 'Supported') }}</span> + <span v-if="app.level === 200" + v-tooltip.auto="t('settings', 'Featured apps are developed by and within the community. They offer central functionality and are ready for production use.')" + class="official icon-checkmark"> + {{ t('settings', 'Featured') }}</span> + <AppScore v-if="hasRating" :score="app.appstoreData.ratingOverall" /> + </div> + </template> + <template #secondary-actions> + <ActionButton v-if="app.update" + :disabled="installing || isLoading" + icon="icon-download" + @click="update(app.id)"> + {{ t('settings', 'Update to {version}', {version: app.update}) }} + </ActionButton> + <ActionButton v-if="app.canUnInstall" + :disabled="installing || isLoading" + icon="icon-delete" + @click="remove(app.id)"> + {{ t('settings', 'Remove') }} + </ActionButton> + <ActionButton v-if="app.active" + :disabled="installing || isLoading" + icon="icon-close" + @click="disable(app.id)"> + {{ t('settings','Disable') }} + </ActionButton> + <ActionButton v-if="!app.active && (app.canInstall || app.isCompatible)" + v-tooltip.auto="enableButtonTooltip" + :disabled="!app.canInstall || installing || isLoading" + icon="icon-checkmark" + @click="enable(app.id)"> + {{ enableButtonText }} + </ActionButton> + <ActionButton v-else-if="!app.active" + v-tooltip.auto="forceEnableButtonTooltip" + :disabled="installing || isLoading" + icon="icon-checkmark" + @click="forceEnable(app.id)"> + {{ forceEnableButtonText }} + </ActionButton> + </template> + <AppDetails :category="category" :app="app" /> </AppSidebar> </Content> </template> <script> +import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' import AppContent from '@nextcloud/vue/dist/Components/AppContent' import AppNavigation from '@nextcloud/vue/dist/Components/AppNavigation' import AppNavigationCounter from '@nextcloud/vue/dist/Components/AppNavigationCounter' @@ -108,12 +163,14 @@ import VueLocalStorage from 'vue-localstorage' import AppList from '../components/AppList' import AppDetails from '../components/AppDetails' +import AppManagement from '../mixins/AppManagement' Vue.use(VueLocalStorage) export default { name: 'Apps', components: { + ActionButton, AppContent, AppDetails, AppList, @@ -124,6 +181,7 @@ export default { AppSidebar, Content, }, + mixins: [AppManagement], props: { category: { type: String, @@ -146,7 +204,7 @@ export default { loadingList() { return this.$store.getters.loading('list') }, - currentApp() { + app() { return this.apps.find(app => app.id === this.id) }, categories() { @@ -161,6 +219,30 @@ export default { settings() { return this.$store.getters.getServerData }, + + // sidebar app binding + appSidebar() { + const author = Array.isArray(this.app.author) + ? this.app.author[0]['@value'] + ? this.app.author.map(author => author['@value']).join(', ') + : this.app.author.join(', ') + : this.app.author['@value'] + ? this.app.author['@value'] + : this.app.author + const license = t('settings', '{license}-licensed', { license: ('' + this.app.licence).toUpperCase() }) + + const subtitle = t('settings', 'by {author}\n{license}', { author, license }) + + return { + subtitle, + background: this.app.screenshot + ? this.app.screenshot + : this.app.preview, + compact: !this.app.screenshot, + title: this.app.name, + + } + }, }, watch: { category(val, old) { @@ -195,3 +277,43 @@ export default { }, } </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 { + filter: invert(1); + background-size: 32px; + } + } + + // default icon slot styling + &.app-sidebar--without-background { + .app-sidebar-header__figure { + display: flex; + align-items: center; + justify-content: center; + &--default-app-icon { + height: 32px; + width: 32px; + background-size: 32px; + } + } + } + + // allow multi line subtitle for the license + .app-sidebar-header__subtitle { + white-space: pre-line !important; + line-height: 16px; + overflow: visible !important; + height: 22px; + } +} + +</style> |