nextcloud/core/src/components/setup/RecommendedApps.vue

218 lines
5.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
- @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
-
- @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
-
- @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>
<div class="body-login-container">
<h2>{{ t('core', 'Recommended apps') }}</h2>
<p v-if="loadingApps" class="loading text-center">
{{ t('core', 'Loading apps …') }}
</p>
<p v-else-if="loadingAppsError" class="loading-error text-center">
{{ t('core', 'Could not fetch list of apps from the App Store.') }}
</p>
<p v-else class="text-center">
{{ t('core', 'Installing apps …') }}
</p>
<div v-for="app in recommendedApps" :key="app.id" class="app">
<img :src="customIcon(app.id)" alt="">
<div class="info">
<h3>
{{ app.name }}
<span v-if="app.loading" class="icon icon-loading-small-dark" />
<span v-else-if="app.active" class="icon icon-checkmark-white" />
</h3>
<p v-html="customDescription(app.id)" />
<p v-if="app.installationError">
<strong>{{ t('core', 'App download or installation failed') }}</strong>
</p>
<p v-else-if="!app.isCompatible">
<strong>{{ t('core', 'Cannot install this app because it is not compatible') }}</strong>
</p>
<p v-else-if="!app.canInstall">
<strong>{{ t('core', 'Cannot install this app') }}</strong>
</p>
</div>
</div>
<p class="text-center">
<a :href="defaultPageUrl">{{ t('core', 'Cancel') }}</a>
</p>
</div>
</template>
<script>
import axios from '@nextcloud/axios'
import { generateUrl, imagePath } from '@nextcloud/router'
import { loadState } from '@nextcloud/initial-state'
import pLimit from 'p-limit'
import { translate as t } from '@nextcloud/l10n'
import logger from '../../logger'
const recommended = {
calendar: {
description: t('core', 'Schedule work & meetings, synced with all your devices.'),
icon: imagePath('core', 'places/calendar.svg'),
},
contacts: {
description: t('core', 'Keep your colleagues and friends in one place without leaking their private info.'),
icon: imagePath('core', 'places/contacts.svg'),
},
mail: {
description: t('core', 'Simple email app nicely integrated with Files, Contacts and Calendar.'),
icon: imagePath('core', 'actions/mail.svg'),
},
spreed: {
description: t('core', 'Chatting, video calls, screensharing, online meetings and web conferencing in your browser and with mobile apps.'),
},
richdocuments: {
description: t('core', 'Collaboratively edit office documents.'),
},
richdocumentscode: {
description: t('core', 'Local document editing back-end used by the Collabora Online app.'),
},
}
const recommendedIds = Object.keys(recommended)
const defaultPageUrl = loadState('core', 'defaultPageUrl')
export default {
name: 'RecommendedApps',
data() {
return {
loadingApps: true,
loadingAppsError: false,
apps: [],
defaultPageUrl,
}
},
computed: {
recommendedApps() {
return this.apps.filter(app => recommendedIds.includes(app.id))
},
},
mounted() {
return axios.get(generateUrl('settings/apps/list'))
.then(resp => resp.data)
.then(data => {
logger.info(`${data.apps.length} apps fetched`)
this.apps = data.apps.map(app => Object.assign(app, { loading: false, installationError: false }))
logger.debug(`${this.recommendedApps.length} recommended apps found`, { apps: this.recommendedApps })
this.installApps()
})
.catch(error => {
logger.error('could not fetch app list', { error })
this.loadingAppsError = true
})
.then(() => {
this.loadingApps = false
})
},
methods: {
installApps() {
const limit = pLimit(1)
const installing = this.recommendedApps
.filter(app => !app.active && app.isCompatible && app.canInstall)
.map(app => limit(() => {
logger.info(`installing ${app.id}`)
app.loading = true
return axios.post(generateUrl('settings/apps/enable'), { appIds: [app.id], groups: [] })
.catch(error => {
logger.error(`could not install ${app.id}`, { error })
app.installationError = true
})
.then(() => {
logger.info(`installed ${app.id}`)
app.loading = false
})
}))
logger.debug(`installing ${installing.length} recommended apps`)
Promise.all(installing)
.then(() => {
logger.info('all recommended apps installed, redirecting …')
window.location = defaultPageUrl
})
.catch(error => logger.error('could not install recommended apps', { error }))
},
customIcon(appId) {
if (!(appId in recommended) || !recommended[appId].icon) {
logger.warn(`no app icon for recommended app ${appId}`)
return imagePath('core', 'places/default-app-icon.svg')
}
return recommended[appId].icon
},
customDescription(appId) {
if (!(appId in recommended)) {
logger.warn(`no app description for recommended app ${appId}`)
return ''
}
return recommended[appId].description
},
},
}
</script>
<style lang="scss" scoped>
.body-login-container {
}
p.loading, p.loading-error {
height: 100px;
}
.text-center {
text-align: center;
}
.app {
display: flex;
flex-direction: row;
img {
height: 50px;
width: 50px;
filter: invert(1);
}
img, .info {
padding: 12px;
}
.info {
h3, p {
text-align: left;
}
h3 {
color: #fff;
margin-top: 0;
}
h3 > span.icon {
display: inline-block;
}
}
}
</style>