Files
nextcloud-server-mirror/dist/core-recommendedapps.mjs.map
2026-10-07 08:58:40 +00:00

1 line
15 KiB
Plaintext
Raw Permalink 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.
{"version":3,"file":"core-recommendedapps.mjs","sources":["../build/frontend/core/src/components/setup/RecommendedApps.vue","../build/frontend/core/src/recommendedapps.ts"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n\n<script setup lang=\"ts\">\nimport type { IAppstoreApp } from '../../../../apps/appstore/src/apps.d.ts'\n\nimport { loadState } from '@nextcloud/initial-state'\nimport { t } from '@nextcloud/l10n'\nimport { imagePath } from '@nextcloud/router'\nimport { computed, onMounted, ref } from 'vue'\nimport NcButton from '@nextcloud/vue/components/NcButton'\nimport NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'\nimport * as appstoreApi from '../../../../apps/appstore/src/service/api.ts'\nimport { canInstall } from '../../../../apps/appstore/src/utils/appStatus.ts'\nimport { logger } from '../../utils/logger.ts'\n\ninterface RecommendedApp extends IAppstoreApp {\n\tloading: boolean\n\tisSelected: boolean\n\terror?: string\n}\n\ninterface Recommendation {\n\tname?: string\n\tdescription?: string\n\ticon?: string\n\t/** Installed along with the other apps, but not offered on its own */\n\thidden?: boolean\n\t/** The apps it is installed along with */\n\trequired?: string[]\n}\n\nconst recommended: Record<string, Recommendation> = {\n\tcalendar: {\n\t\tdescription: t('core', 'Schedule work & meetings, synced with all your devices.'),\n\t\ticon: imagePath('core', 'places/calendar.svg'),\n\t},\n\tcontacts: {\n\t\tdescription: t('core', 'Keep your colleagues and friends in one place without leaking their private info.'),\n\t\ticon: imagePath('core', 'places/contacts.svg'),\n\t},\n\tmail: {\n\t\tdescription: t('core', 'Simple email app nicely integrated with Files, Contacts and Calendar.'),\n\t\ticon: imagePath('core', 'actions/mail.svg'),\n\t},\n\tspreed: {\n\t\tdescription: t('core', 'Chatting, video calls, screen sharing, online meetings and web conferencing – in your browser and with mobile apps.'),\n\t\ticon: imagePath('core', 'apps/spreed.svg'),\n\t},\n\trichdocuments: {\n\t\tname: 'Nextcloud Office',\n\t\tdescription: t('core', 'Collaborative documents, spreadsheets and presentations, built on Collabora Online.'),\n\t\ticon: imagePath('core', 'apps/richdocuments.svg'),\n\t},\n\tnotes: {\n\t\tdescription: t('core', 'Distraction free note taking app.'),\n\t\ticon: imagePath('core', 'apps/notes.svg'),\n\t},\n\trichdocumentscode: {\n\t\thidden: true,\n\t\trequired: ['richdocuments'],\n\t},\n}\n\nconst defaultPageUrl = loadState<string>('core', 'defaultPageUrl')\n\nconst showInstallButton = ref(false)\nconst installingApps = ref(false)\nconst loadingApps = ref(true)\nconst loadingAppsError = ref(false)\nconst recommendedApps = ref<RecommendedApp[]>([])\n\nconst isAnyAppSelected = computed(() => recommendedApps.value.some((app) => app.isSelected && !app.active))\n\nonMounted(async () => {\n\ttry {\n\t\tconst apps = await appstoreApi.getApps()\n\t\tlogger.info(`${apps.length} apps fetched`)\n\n\t\trecommendedApps.value = apps\n\t\t\t.filter((app) => app.id in recommended)\n\t\t\t.map((app) => ({\n\t\t\t\t...app,\n\t\t\t\tloading: false,\n\t\t\t\tisSelected: app.isCompatible && !isHidden(app.id),\n\t\t\t}))\n\t\tlogger.debug(`${recommendedApps.value.length} recommended apps found`, { apps: recommendedApps.value })\n\n\t\tshowInstallButton.value = true\n\t} catch (error) {\n\t\tlogger.error('could not fetch app list', { error })\n\t\tloadingAppsError.value = true\n\t} finally {\n\t\tloadingApps.value = false\n\t}\n})\n\n/**\n * Install the selected apps and the hidden apps they require.\n */\nasync function installApps() {\n\tconst availableApps = recommendedApps.value.filter((app) => app.active || (app.isSelected && canInstall(app)))\n\tconst appsToInstall = [\n\t\t...availableApps.filter((app) => !app.active && app.isSelected),\n\t\t...recommendedApps.value.filter((app) => isHidden(app.id)\n\t\t\t&& (recommended[app.id]!.required ?? []).every((requiredAppId) => availableApps.some((requiredApp) => requiredApp.id === requiredAppId))),\n\t]\n\n\tlogger.debug(`Installing ${appsToInstall.length} recommended apps`, { appIds: appsToInstall.map((app) => app.id) })\n\tinstallingApps.value = true\n\tfor (const app of appsToInstall) {\n\t\tapp.loading = true\n\t}\n\n\tconst results = await Promise.allSettled(appsToInstall.map((app) => appstoreApi.enableApp(app.id)))\n\tresults.forEach((result, index) => {\n\t\tconst app = appsToInstall[index]!\n\t\tapp.loading = false\n\t\tif (result.status === 'fulfilled') {\n\t\t\tapp.active = true\n\t\t\treturn\n\t\t}\n\n\t\tif (result.reason instanceof Error && result.reason.message === 'Dialog closed') {\n\t\t\tlogger.info(`User cancelled the password confirmation for recommended app ${app.id}`)\n\t\t\tapp.error = t('core', 'Password confirmation was aborted')\n\t\t} else {\n\t\t\tlogger.error(`could not install recommended app ${app.id}`, { error: result.reason })\n\t\t\tapp.error = t('core', 'App download or installation failed')\n\t\t}\n\t\tapp.isSelected = false\n\t})\n\tinstallingApps.value = false\n}\n\n/**\n * @param appId - The app id\n */\nfunction customIcon(appId: string): string {\n\tconst icon = recommended[appId]?.icon\n\tif (!icon) {\n\t\tlogger.warn(`no app icon for recommended app ${appId}`)\n\t\treturn imagePath('core', 'places/default-app-icon.svg')\n\t}\n\treturn icon\n}\n\n/**\n * @param app - The app\n */\nfunction customName(app: RecommendedApp): string {\n\treturn recommended[app.id]?.name || app.name\n}\n\n/**\n * @param appId - The app id\n */\nfunction customDescription(appId: string): string {\n\tconst description = recommended[appId]?.description\n\tif (description === undefined) {\n\t\tlogger.warn(`no app description for recommended app ${appId}`)\n\t\treturn ''\n\t}\n\treturn description\n}\n\n/**\n * @param appId - The app id\n */\nfunction isHidden(appId: string): boolean {\n\treturn !!recommended[appId]?.hidden\n}\n\n/**\n * @param app - The app to (de)select for installation\n */\nfunction toggleSelect(app: RecommendedApp) {\n\t// nothing can be selected while the apps are loading or installing\n\tif (!showInstallButton.value) {\n\t\treturn\n\t}\n\tapp.isSelected = !app.isSelected\n}\n</script>\n\n<template>\n\t<div class=\"guest-box\" data-cy-setup-recommended-apps>\n\t\t<h2>{{ t('core', 'Recommended apps') }}</h2>\n\t\t<p v-if=\"loadingApps\" class=\"loading\" :class=\"[$style.status]\">\n\t\t\t{{ t('core', 'Loading apps …') }}\n\t\t</p>\n\t\t<p v-else-if=\"loadingAppsError\" :class=\"$style.status\">\n\t\t\t{{ t('core', 'Could not fetch list of apps from the App Store.') }}\n\t\t</p>\n\n\t\t<template v-for=\"app in recommendedApps\" :key=\"app.id\">\n\t\t\t<div v-if=\"!isHidden(app.id)\" :class=\"$style.app\">\n\t\t\t\t<img :class=\"$style.app__icon\" :src=\"customIcon(app.id)\" alt=\"\">\n\t\t\t\t<div :class=\"$style.app__info\">\n\t\t\t\t\t<h3>{{ customName(app) }}</h3>\n\t\t\t\t\t<p v-text=\"customDescription(app.id)\" />\n\t\t\t\t\t<p v-if=\"app.error\">\n\t\t\t\t\t\t<strong>{{ app.error }}</strong>\n\t\t\t\t\t</p>\n\t\t\t\t\t<p v-else-if=\"app.active\">\n\t\t\t\t\t\t<strong>{{ t('core', 'App already installed') }}</strong>\n\t\t\t\t\t</p>\n\t\t\t\t\t<p v-else-if=\"!app.isCompatible\">\n\t\t\t\t\t\t<strong>{{ t('core', 'Cannot install this app because it is not compatible') }}</strong>\n\t\t\t\t\t</p>\n\t\t\t\t\t<p v-else-if=\"!canInstall(app)\">\n\t\t\t\t\t\t<strong>{{ t('core', 'Cannot install this app') }}</strong>\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\t:class=\"$style.app__switch\"\n\t\t\t\t\t:modelValue=\"app.isSelected || app.active\"\n\t\t\t\t\t:disabled=\"!app.isCompatible || app.active\"\n\t\t\t\t\t:loading=\"app.loading\"\n\t\t\t\t\t@update:modelValue=\"toggleSelect(app)\" />\n\t\t\t</div>\n\t\t</template>\n\n\t\t<div :class=\"$style.actions\">\n\t\t\t<NcButton\n\t\t\t\tv-if=\"showInstallButton && !installingApps\"\n\t\t\t\tdata-cy-setup-recommended-apps-skip\n\t\t\t\t:href=\"defaultPageUrl\"\n\t\t\t\tvariant=\"tertiary\">\n\t\t\t\t{{ t('core', 'Skip') }}\n\t\t\t</NcButton>\n\n\t\t\t<NcButton\n\t\t\t\tv-if=\"showInstallButton\"\n\t\t\t\tdata-cy-setup-recommended-apps-install\n\t\t\t\t:disabled=\"installingApps || !isAnyAppSelected\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t@click=\"installApps\">\n\t\t\t\t{{ installingApps ? t('core', 'Installing apps …') : t('core', 'Install recommended apps') }}\n\t\t\t</NcButton>\n\t\t</div>\n\t</div>\n</template>\n\n<style module lang=\"scss\">\n.actions {\n\tdisplay: flex;\n\tjustify-content: end;\n\tmargin-top: 8px;\n}\n\n.status {\n\theight: 100px;\n\ttext-align: center;\n}\n\n.app {\n\tdisplay: flex;\n\tflex-direction: row;\n\n\t&__icon {\n\t\theight: 50px;\n\t\twidth: 50px;\n\t\tpadding: 12px;\n\t\tfilter: var(--background-invert-if-dark);\n\t}\n\n\t&__info {\n\t\tpadding: 12px;\n\n\t\th3, p {\n\t\t\ttext-align: start;\n\t\t}\n\n\t\th3 {\n\t\t\tmargin-top: 0;\n\t\t}\n\n\t\tp:last-child {\n\t\t\tmargin-top: 10px;\n\t\t}\n\t}\n\n\t&__switch {\n\t\tmargin-inline-start: auto;\n\t\tpadding: 0 2px;\n\t}\n}\n</style>\n","/**\n * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport axios from '@nextcloud/axios'\nimport { addPasswordConfirmationInterceptors } from '@nextcloud/password-confirmation'\nimport { createApp } from 'vue'\nimport RecommendedApps from './components/setup/RecommendedApps.vue'\n\naddPasswordConfirmationInterceptors(axios)\n\ncreateApp(RecommendedApps).mount('#recommended-apps')\n"],"names":["recommended","t","imagePath","defaultPageUrl","loadState","showInstallButton","ref","installingApps","loadingApps","loadingAppsError","recommendedApps","isAnyAppSelected","computed","app","onMounted","apps","appstoreApi.getApps","logger","isHidden","error","installApps","availableApps","canInstall","appsToInstall","requiredAppId","requiredApp","appstoreApi.enableApp","result","index","customIcon","appId","customName","customDescription","description","toggleSelect","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_unref","_normalizeClass","$style","_Fragment","_renderList","_toDisplayString","_hoisted_4","_hoisted_5","_hoisted_7","_hoisted_6","_createVNode","NcCheckboxRadioSwitch","$event","_createBlock","NcButton","addPasswordConfirmationInterceptors","axios","createApp","RecommendedApps"],"mappings":"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"}