summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2020-01-18 10:21:27 +0100
committerJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2020-08-14 09:56:00 +0200
commit228a96508a22b3805505ce61e0d36eba1e703460 (patch)
treec957d471036a4041217007f52f374e21fd10b8bf /apps
parentb13aa660c91873437afec36ef6466ef609b7959c (diff)
downloadnextcloud-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.vue57
-rw-r--r--apps/settings/src/components/AppList/AppItem.vue14
-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.vue132
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>