diff options
author | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2020-04-09 12:58:05 +0200 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2020-08-14 11:44:16 +0200 |
commit | 8e7c95effbae92e7002bfd8253d5f3c0ebc7a7a4 (patch) | |
tree | 6ff31de1d0a186cd8e0abe74bcbdf661e8b6f540 /apps/settings/src | |
parent | 228a96508a22b3805505ce61e0d36eba1e703460 (diff) | |
download | nextcloud-server-8e7c95effbae92e7002bfd8253d5f3c0ebc7a7a4.tar.gz nextcloud-server-8e7c95effbae92e7002bfd8253d5f3c0ebc7a7a4.zip |
Use appsidebar for apps
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Diffstat (limited to 'apps/settings/src')
-rw-r--r-- | apps/settings/src/components/AppDetails.vue | 147 | ||||
-rw-r--r-- | apps/settings/src/views/Apps.vue | 110 |
2 files changed, 147 insertions, 110 deletions
diff --git a/apps/settings/src/components/AppDetails.vue b/apps/settings/src/components/AppDetails.vue index fa586b3d0a1..55519bf9f80 100644 --- a/apps/settings/src/components/AppDetails.vue +++ b/apps/settings/src/components/AppDetails.vue @@ -21,76 +21,74 @@ --> <template> - <div id="app-details-view" style="padding: 20px;"> - <div class="actions"> - <div class="actions-buttons"> + <div class="app-details"> + <div class="app-details__actions"> + <div v-if="app.active && canLimitToGroups(app)" class="app-details__actions-groups"> + <input :id="prefix('groups_enable', app.id)" + v-model="groupCheckedAppsData" + type="checkbox" + :value="app.id" + class="groups-enable__checkbox checkbox" + @change="setGroupLimit"> + <label :for="prefix('groups_enable', app.id)">{{ t('settings', 'Limit to groups') }}</label> + <input type="hidden" + class="group_select" + :title="t('settings', 'All')" + value=""> + <Multiselect v-if="isLimitedToGroups(app)" + :options="groups" + :value="appGroups" + :options-limit="5" + :placeholder="t('settings', 'Limit app usage to groups')" + label="name" + track-by="id" + class="multiselect-vue" + :multiple="true" + :close-on-select="false" + :tag-width="60" + @select="addGroupLimitation" + @remove="removeGroupLimitation" + @search-change="asyncFindGroup"> + <span slot="noResult">{{ t('settings', 'No results') }}</span> + </Multiselect> + </div> + <div class="app-details__actions-manage"> <input v-if="app.update" class="update primary" type="button" - :value="t('settings', 'Update to {version}', {version: app.update})" - :disabled="installing || loading(app.id)" + :value="t('settings', 'Update to {version}', { version: app.update })" + :disabled="installing || isLoading" @click="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="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="disable(app.id)"> <input v-if="!app.active && (app.canInstall || app.isCompatible)" v-tooltip.auto="enableButtonTooltip" class="enable primary" type="button" :value="enableButtonText" - :disabled="!app.canInstall || installing || loading(app.id)" + :disabled="!app.canInstall || installing || isLoading" @click="enable(app.id)"> - <input v-else-if="!app.active" + <input v-else-if="!app.active && !app.canInstall" v-tooltip.auto="forceEnableButtonTooltip" class="enable force" type="button" :value="forceEnableButtonText" - :disabled="installing || loading(app.id)" + :disabled="installing || isLoading" @click="forceEnable(app.id)"> </div> - <div class="app-groups"> - <div v-if="app.active && canLimitToGroups(app)" class="groups-enable"> - <input :id="prefix('groups_enable', app.id)" - v-model="groupCheckedAppsData" - type="checkbox" - :value="app.id" - class="groups-enable__checkbox checkbox" - @change="setGroupLimit"> - <label :for="prefix('groups_enable', app.id)">{{ t('settings', 'Limit to groups') }}</label> - <input type="hidden" - class="group_select" - :title="t('settings', 'All')" - value=""> - <Multiselect v-if="isLimitedToGroups(app)" - :options="groups" - :value="appGroups" - :options-limit="5" - :placeholder="t('settings', 'Limit app usage to groups')" - label="name" - track-by="id" - class="multiselect-vue" - :multiple="true" - :close-on-select="false" - :tag-width="60" - @select="addGroupLimitation" - @remove="removeGroupLimitation" - @search-change="asyncFindGroup"> - <span slot="noResult">{{ t('settings', 'No results') }}</span> - </Multiselect> - </div> - </div> </div> - <ul class="app-dependencies"> + <ul class="app-details__dependencies"> <li v-if="app.missingMinOwnCloudVersion"> {{ t('settings', 'This app has no minimum Nextcloud version assigned. This will be an error in the future.') }} </li> @@ -107,7 +105,7 @@ </li> </ul> - <p class="documentation"> + <p class="app-details__documentation"> <a v-if="!app.internal" class="appslink" :href="appstoreUrl" @@ -142,7 +140,7 @@ rel="noreferrer noopener">{{ t('settings', 'Developer documentation') }} ↗</a> </p> - <div class="app-description" v-html="renderMarkdown" /> + <div class="app-details__description" v-html="renderMarkdown" /> </div> </template> @@ -151,7 +149,6 @@ import { Multiselect } from '@nextcloud/vue' import marked from 'marked' import dompurify from 'dompurify' -import AppScore from './AppList/AppScore' import AppManagement from '../mixins/AppManagement' import PrefixMixin from './PrefixMixin' @@ -160,11 +157,15 @@ export default { components: { Multiselect, - AppScore, }, mixins: [AppManagement, PrefixMixin], - props: ['category', 'app'], + props: { + app: { + type: Object, + required: true, + }, + }, data() { return { @@ -182,9 +183,6 @@ export default { } return null }, - hasRating() { - return this.app.appstoreData && this.app.appstoreData.ratingNumOverall > 5 - }, author() { if (typeof this.app.author === 'string') { return [ @@ -275,16 +273,45 @@ export default { } </script> -<style scoped> - .force { - background: var(--color-main-background); - border-color: var(--color-error); - color: var(--color-error); +<style scoped lang="scss"> +.app-details { + padding: 20px; + + &__actions { + // app management + &-manage { + // if too many, shrink them and ellipsis + display: flex; + input { + flex: 0 1 auto; + min-width: 0; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + } } - .force:hover, - .force:active { - background: var(--color-error); - border-color: var(--color-error) !important; - color: var(--color-main-background); + &__dependencies { + opacity: .7; } + &__documentation { + padding-top: 20px; + } + &__description { + padding-top: 20px; + } +} + +.force { + color: var(--color-error); + border-color: var(--color-error); + background: var(--color-main-background); +} +.force:hover, +.force:active { + color: var(--color-main-background); + border-color: var(--color-error) !important; + background: var(--color-error); +} + </style> diff --git a/apps/settings/src/views/Apps.vue b/apps/settings/src/views/Apps.vue index 13ddd8bfaf8..313c58afbd9 100644 --- a/apps/settings/src/views/Apps.vue +++ b/apps/settings/src/views/Apps.vue @@ -25,6 +25,7 @@ :class="{ 'with-app-sidebar': app}" :content-class="{ 'icon-loading': loadingList }" :navigation-class="{ 'icon-loading': loading }"> + <!-- Categories & filters --> <AppNavigation> <template #list> <AppNavigationItem @@ -86,9 +87,13 @@ :title="t('settings', 'Developer documentation') + ' ↗'" /> </template> </AppNavigation> + + <!-- Apps list --> <AppContent class="app-settings-content" :class="{ 'icon-loading': loadingList }"> <AppList :category="category" :app="app" :search="searchQuery" /> </AppContent> + + <!-- Selected app details --> <AppSidebar v-if="id && app" v-bind="appSidebar" @@ -97,7 +102,9 @@ <template v-if="!appSidebar.background" #header> <div class="app-sidebar-header__figure--default-app-icon icon-settings-dark" /> </template> + <template #primary-actions> + <!-- Featured/Supported badges --> <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.')" @@ -110,47 +117,14 @@ <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" /> + + <!-- Tab content --> + <AppDetails :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' @@ -164,13 +138,14 @@ import VueLocalStorage from 'vue-localstorage' import AppList from '../components/AppList' import AppDetails from '../components/AppDetails' import AppManagement from '../mixins/AppManagement' +import AppScore from '../components/AppList/AppScore' Vue.use(VueLocalStorage) export default { name: 'Apps', + components: { - ActionButton, AppContent, AppDetails, AppList, @@ -178,10 +153,13 @@ export default { AppNavigationCounter, AppNavigationItem, AppNavigationSpacer, + AppScore, AppSidebar, Content, }, + mixins: [AppManagement], + props: { category: { type: String, @@ -192,11 +170,14 @@ export default { default: '', }, }, + data() { return { searchQuery: '', + screenshotLoaded: false, } }, + computed: { loading() { return this.$store.getters.loading('categories') @@ -220,6 +201,10 @@ export default { return this.$store.getters.getServerData }, + hasRating() { + return this.app.appstoreData && this.app.appstoreData.ratingNumOverall > 5 + }, + // sidebar app binding appSidebar() { const author = Array.isArray(this.app.author) @@ -235,20 +220,33 @@ export default { return { subtitle, - background: this.app.screenshot + background: this.app.screenshot && this.screenshotLoaded ? this.app.screenshot : this.app.preview, - compact: !this.app.screenshot, + compact: !(this.app.screenshot && this.screenshotLoaded), title: this.app.name, } }, }, + watch: { category(val, old) { this.setSearch('') }, + + app() { + this.screenshotLoaded = false + if (this.app && this.app.screenshot) { + const image = new Image() + image.onload = (e) => { + this.screenshotLoaded = true + } + image.src = this.app.screenshot + } + }, }, + beforeMount() { this.$store.dispatch('getCategories') this.$store.dispatch('getAllApps') @@ -261,6 +259,7 @@ export default { */ this.appSearch = new OCA.Search(this.setSearch, this.resetSearch) }, + methods: { setSearch(query) { this.searchQuery = query @@ -279,17 +278,17 @@ export default { </script> <style lang="scss" scoped> - -#app-sidebar::v-deep { +.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 + 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; + + filter: invert(1); } } @@ -300,19 +299,30 @@ export default { align-items: center; justify-content: center; &--default-app-icon { - height: 32px; width: 32px; + height: 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; + // 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; + } } } |