aboutsummaryrefslogtreecommitdiffstats
path: root/apps/settings/src/components/AppStoreSidebar
diff options
context:
space:
mode:
Diffstat (limited to 'apps/settings/src/components/AppStoreSidebar')
-rw-r--r--apps/settings/src/components/AppStoreSidebar/AppDeployDaemonTab.vue50
-rw-r--r--apps/settings/src/components/AppStoreSidebar/AppDeployOptionsModal.vue320
-rw-r--r--apps/settings/src/components/AppStoreSidebar/AppDescriptionTab.vue27
-rw-r--r--apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue143
-rw-r--r--apps/settings/src/components/AppStoreSidebar/AppReleasesTab.vue28
5 files changed, 482 insertions, 86 deletions
diff --git a/apps/settings/src/components/AppStoreSidebar/AppDeployDaemonTab.vue b/apps/settings/src/components/AppStoreSidebar/AppDeployDaemonTab.vue
new file mode 100644
index 00000000000..7c0b8ea4421
--- /dev/null
+++ b/apps/settings/src/components/AppStoreSidebar/AppDeployDaemonTab.vue
@@ -0,0 +1,50 @@
+<!--
+ - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+
+<template>
+ <NcAppSidebarTab v-if="app?.daemon"
+ id="daemon"
+ :name="t('settings', 'Daemon')"
+ :order="3">
+ <template #icon>
+ <NcIconSvgWrapper :path="mdiFileChart" :size="24" />
+ </template>
+ <div class="daemon">
+ <h4>{{ t('settings', 'Deploy Daemon') }}</h4>
+ <p><b>{{ t('settings', 'Type') }}</b>: {{ app?.daemon.accepts_deploy_id }}</p>
+ <p><b>{{ t('settings', 'Name') }}</b>: {{ app?.daemon.name }}</p>
+ <p><b>{{ t('settings', 'Display Name') }}</b>: {{ app?.daemon.display_name }}</p>
+ <p><b>{{ t('settings', 'GPUs support') }}</b>: {{ gpuSupport }}</p>
+ <p><b>{{ t('settings', 'Compute device') }}</b>: {{ app?.daemon?.deploy_config?.computeDevice?.label }}</p>
+ </div>
+ </NcAppSidebarTab>
+</template>
+
+<script setup lang="ts">
+import type { IAppstoreExApp } from '../../app-types'
+
+import NcAppSidebarTab from '@nextcloud/vue/components/NcAppSidebarTab'
+import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
+
+import { mdiFileChart } from '@mdi/js'
+import { ref } from 'vue'
+
+const props = defineProps<{
+ app: IAppstoreExApp,
+}>()
+
+const gpuSupport = ref(props.app?.daemon?.deploy_config?.computeDevice?.id !== 'cpu' || false)
+</script>
+
+<style scoped lang="scss">
+.daemon {
+ padding: 20px;
+
+ h4 {
+ font-weight: bold;
+ margin: 10px auto;
+ }
+}
+</style>
diff --git a/apps/settings/src/components/AppStoreSidebar/AppDeployOptionsModal.vue b/apps/settings/src/components/AppStoreSidebar/AppDeployOptionsModal.vue
new file mode 100644
index 00000000000..0544c3848be
--- /dev/null
+++ b/apps/settings/src/components/AppStoreSidebar/AppDeployOptionsModal.vue
@@ -0,0 +1,320 @@
+<!--
+ - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+
+<template>
+ <NcDialog :open="show"
+ size="normal"
+ :name="t('settings', 'Advanced deploy options')"
+ @update:open="$emit('update:show', $event)">
+ <div class="modal__content">
+ <p class="deploy-option__hint">
+ {{ configuredDeployOptions === null ? t('settings', 'Edit ExApp deploy options before installation') : t('settings', 'Configured ExApp deploy options. Can be set only during installation') }}.
+ <a v-if="deployOptionsDocsUrl" :href="deployOptionsDocsUrl">
+ {{ t('settings', 'Learn more') }}
+ </a>
+ </p>
+ <h3 v-if="environmentVariables.length > 0 || (configuredDeployOptions !== null && configuredDeployOptions.environment_variables.length > 0)">
+ {{ t('settings', 'Environment variables') }}
+ </h3>
+ <template v-if="configuredDeployOptions === null">
+ <div v-for="envVar in environmentVariables"
+ :key="envVar.envName"
+ class="deploy-option">
+ <NcTextField :label="envVar.displayName" :value.sync="deployOptions.environment_variables[envVar.envName]" />
+ <p class="deploy-option__hint">
+ {{ envVar.description }}
+ </p>
+ </div>
+ </template>
+ <fieldset v-else-if="Object.keys(configuredDeployOptions).length > 0"
+ class="envs">
+ <legend class="deploy-option__hint">
+ {{ t('settings', 'ExApp container environment variables') }}
+ </legend>
+ <NcTextField v-for="(value, key) in configuredDeployOptions.environment_variables"
+ :key="key"
+ :label="value.displayName ?? key"
+ :helper-text="value.description"
+ :value="value.value"
+ readonly />
+ </fieldset>
+ <template v-else>
+ <p class="deploy-option__hint">
+ {{ t('settings', 'No environment variables defined') }}
+ </p>
+ </template>
+
+ <h3>{{ t('settings', 'Mounts') }}</h3>
+ <template v-if="configuredDeployOptions === null">
+ <p class="deploy-option__hint">
+ {{ t('settings', 'Define host folder mounts to bind to the ExApp container') }}
+ </p>
+ <NcNoteCard type="info" :text="t('settings', 'Must exist on the Deploy daemon host prior to installing the ExApp')" />
+ <div v-for="mount in deployOptions.mounts"
+ :key="mount.hostPath"
+ class="deploy-option"
+ style="display: flex; align-items: center; justify-content: space-between; flex-direction: row;">
+ <NcTextField :label="t('settings', 'Host path')" :value.sync="mount.hostPath" />
+ <NcTextField :label="t('settings', 'Container path')" :value.sync="mount.containerPath" />
+ <NcCheckboxRadioSwitch :checked.sync="mount.readonly">
+ {{ t('settings', 'Read-only') }}
+ </NcCheckboxRadioSwitch>
+ <NcButton :aria-label="t('settings', 'Remove mount')"
+ style="margin-top: 6px;"
+ @click="removeMount(mount)">
+ <template #icon>
+ <NcIconSvgWrapper :path="mdiDeleteOutline" />
+ </template>
+ </NcButton>
+ </div>
+ <div v-if="addingMount" class="deploy-option">
+ <h4>
+ {{ t('settings', 'New mount') }}
+ </h4>
+ <div style="display: flex; align-items: center; justify-content: space-between; flex-direction: row;">
+ <NcTextField ref="newMountHostPath"
+ :label="t('settings', 'Host path')"
+ :aria-label="t('settings', 'Enter path to host folder')"
+ :value.sync="newMountPoint.hostPath" />
+ <NcTextField :label="t('settings', 'Container path')"
+ :aria-label="t('settings', 'Enter path to container folder')"
+ :value.sync="newMountPoint.containerPath" />
+ <NcCheckboxRadioSwitch :checked.sync="newMountPoint.readonly"
+ :aria-label="t('settings', 'Toggle read-only mode')">
+ {{ t('settings', 'Read-only') }}
+ </NcCheckboxRadioSwitch>
+ </div>
+ <div style="display: flex; align-items: center; margin-top: 4px;">
+ <NcButton :aria-label="t('settings', 'Confirm adding new mount')"
+ @click="addMountPoint">
+ <template #icon>
+ <NcIconSvgWrapper :path="mdiCheck" />
+ </template>
+ {{ t('settings', 'Confirm') }}
+ </NcButton>
+ <NcButton :aria-label="t('settings', 'Cancel adding mount')"
+ style="margin-left: 4px;"
+ @click="cancelAddMountPoint">
+ <template #icon>
+ <NcIconSvgWrapper :path="mdiClose" />
+ </template>
+ {{ t('settings', 'Cancel') }}
+ </NcButton>
+ </div>
+ </div>
+ <NcButton v-if="!addingMount"
+ :aria-label="t('settings', 'Add mount')"
+ style="margin-top: 5px;"
+ @click="startAddingMount">
+ <template #icon>
+ <NcIconSvgWrapper :path="mdiPlus" />
+ </template>
+ {{ t('settings', 'Add mount') }}
+ </NcButton>
+ </template>
+ <template v-else-if="configuredDeployOptions.mounts.length > 0">
+ <p class="deploy-option__hint">
+ {{ t('settings', 'ExApp container mounts') }}
+ </p>
+ <div v-for="mount in configuredDeployOptions.mounts"
+ :key="mount.hostPath"
+ class="deploy-option"
+ style="display: flex; align-items: center; justify-content: space-between; flex-direction: row;">
+ <NcTextField :label="t('settings', 'Host path')" :value.sync="mount.hostPath" readonly />
+ <NcTextField :label="t('settings', 'Container path')" :value.sync="mount.containerPath" readonly />
+ <NcCheckboxRadioSwitch :checked.sync="mount.readonly" disabled>
+ {{ t('settings', 'Read-only') }}
+ </NcCheckboxRadioSwitch>
+ </div>
+ </template>
+ <p v-else class="deploy-option__hint">
+ {{ t('settings', 'No mounts defined') }}
+ </p>
+ </div>
+
+ <template v-if="!app.active && (app.canInstall || app.isCompatible) && configuredDeployOptions === null" #actions>
+ <NcButton :title="enableButtonTooltip"
+ :aria-label="enableButtonTooltip"
+ type="primary"
+ :disabled="!app.canInstall || installing || isLoading || !defaultDeployDaemonAccessible || isInitializing || isDeploying"
+ @click.stop="submitDeployOptions">
+ {{ enableButtonText }}
+ </NcButton>
+ </template>
+ </NcDialog>
+</template>
+
+<script>
+import { computed, ref } from 'vue'
+
+import axios from '@nextcloud/axios'
+import { generateUrl } from '@nextcloud/router'
+import { loadState } from '@nextcloud/initial-state'
+import { emit } from '@nextcloud/event-bus'
+
+import NcDialog from '@nextcloud/vue/components/NcDialog'
+import NcTextField from '@nextcloud/vue/components/NcTextField'
+import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
+import NcButton from '@nextcloud/vue/components/NcButton'
+import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
+import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
+
+import { mdiPlus, mdiCheck, mdiClose, mdiDeleteOutline } from '@mdi/js'
+
+import { useAppApiStore } from '../../store/app-api-store.ts'
+import { useAppsStore } from '../../store/apps-store.ts'
+
+import AppManagement from '../../mixins/AppManagement.js'
+
+export default {
+ name: 'AppDeployOptionsModal',
+ components: {
+ NcDialog,
+ NcTextField,
+ NcButton,
+ NcNoteCard,
+ NcCheckboxRadioSwitch,
+ NcIconSvgWrapper,
+ },
+ mixins: [AppManagement],
+ props: {
+ app: {
+ type: Object,
+ required: true,
+ },
+ show: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ setup(props) {
+ // for AppManagement mixin
+ const store = useAppsStore()
+ const appApiStore = useAppApiStore()
+
+ const environmentVariables = computed(() => {
+ if (props.app?.releases?.length === 1) {
+ return props.app?.releases[0]?.environmentVariables || []
+ }
+ return []
+ })
+
+ const deployOptions = ref({
+ environment_variables: environmentVariables.value.reduce((acc, envVar) => {
+ acc[envVar.envName] = envVar.default || ''
+ return acc
+ }, {}),
+ mounts: [],
+ })
+
+ return {
+ environmentVariables,
+ deployOptions,
+ store,
+ appApiStore,
+ mdiPlus,
+ mdiCheck,
+ mdiClose,
+ mdiDeleteOutline,
+ }
+ },
+ data() {
+ return {
+ addingMount: false,
+ newMountPoint: {
+ hostPath: '',
+ containerPath: '',
+ readonly: false,
+ },
+ addingPortBinding: false,
+ configuredDeployOptions: null,
+ deployOptionsDocsUrl: loadState('settings', 'deployOptionsDocsUrl', null),
+ }
+ },
+ watch: {
+ show(newShow) {
+ if (newShow) {
+ this.fetchExAppDeployOptions()
+ } else {
+ this.configuredDeployOptions = null
+ }
+ },
+ },
+ methods: {
+ startAddingMount() {
+ this.addingMount = true
+ this.$nextTick(() => {
+ this.$refs.newMountHostPath.focus()
+ })
+ },
+ addMountPoint() {
+ this.deployOptions.mounts.push(this.newMountPoint)
+ this.newMountPoint = {
+ hostPath: '',
+ containerPath: '',
+ readonly: false,
+ }
+ this.addingMount = false
+ },
+ cancelAddMountPoint() {
+ this.newMountPoint = {
+ hostPath: '',
+ containerPath: '',
+ readonly: false,
+ }
+ this.addingMount = false
+ },
+ removeMount(mountToRemove) {
+ this.deployOptions.mounts = this.deployOptions.mounts.filter(mount => mount !== mountToRemove)
+ },
+ async fetchExAppDeployOptions() {
+ return axios.get(generateUrl(`/apps/app_api/apps/deploy-options/${this.app.id}`))
+ .then(response => {
+ this.configuredDeployOptions = response.data
+ })
+ .catch(() => {
+ this.configuredDeployOptions = null
+ })
+ },
+ async submitDeployOptions() {
+ await this.appApiStore.fetchDockerDaemons()
+ if (this.appApiStore.dockerDaemons.length === 1 && this.app.needsDownload) {
+ this.enable(this.app.id, this.appApiStore.dockerDaemons[0], this.deployOptions)
+ } else if (this.app.needsDownload) {
+ emit('showDaemonSelectionModal', this.deployOptions)
+ } else {
+ this.enable(this.app.id, this.app.daemon, this.deployOptions)
+ }
+ this.$emit('update:show', false)
+ },
+ },
+}
+</script>
+
+<style scoped>
+.deploy-option {
+ margin: calc(var(--default-grid-baseline) * 4) 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+
+ &__hint {
+ margin-top: 4px;
+ font-size: 0.8em;
+ color: var(--color-text-maxcontrast);
+ }
+}
+
+.envs {
+ width: 100%;
+ overflow: auto;
+ height: 100%;
+ max-height: 300px;
+
+ li {
+ margin: 10px 0;
+ }
+}
+</style>
diff --git a/apps/settings/src/components/AppStoreSidebar/AppDescriptionTab.vue b/apps/settings/src/components/AppStoreSidebar/AppDescriptionTab.vue
index 0466ddf4f4c..299d084ef9e 100644
--- a/apps/settings/src/components/AppStoreSidebar/AppDescriptionTab.vue
+++ b/apps/settings/src/components/AppStoreSidebar/AppDescriptionTab.vue
@@ -1,24 +1,7 @@
<!--
- - @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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
<NcAppSidebarTab id="desc"
@@ -39,8 +22,8 @@ import type { IAppstoreApp } from '../../app-types'
import { mdiTextShort } from '@mdi/js'
import { translate as t } from '@nextcloud/l10n'
-import NcAppSidebarTab from '@nextcloud/vue/dist/Components/NcAppSidebarTab.js'
-import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
+import NcAppSidebarTab from '@nextcloud/vue/components/NcAppSidebarTab'
+import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
import Markdown from '../Markdown.vue'
defineProps<{
diff --git a/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue b/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue
index 24387581cdd..eb66d8f3e3a 100644
--- a/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue
+++ b/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue
@@ -1,31 +1,14 @@
<!--
- - @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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
<NcAppSidebarTab id="details"
:name="t('settings', 'Details')"
:order="1">
<template #icon>
- <NcIconSvgWrapper :path="mdiTextBox" />
+ <NcIconSvgWrapper :path="mdiTextBoxOutline" />
</template>
<div class="app-details">
<div class="app-details__actions">
@@ -64,19 +47,19 @@
class="update primary"
type="button"
:value="t('settings', 'Update to {version}', { version: app.update })"
- :disabled="installing || isLoading"
+ :disabled="installing || isLoading || isManualInstall"
@click="update(app.id)">
<input v-if="app.canUnInstall"
class="uninstall"
type="button"
:value="t('settings', 'Remove')"
:disabled="installing || isLoading"
- @click="remove(app.id)">
+ @click="remove(app.id, removeData)">
<input v-if="app.active"
class="enable"
type="button"
- :value="t('settings','Disable')"
- :disabled="installing || isLoading"
+ :value="disableButtonText"
+ :disabled="installing || isLoading || isInitializing || isDeploying"
@click="disable(app.id)">
<input v-if="!app.active && (app.canInstall || app.isCompatible)"
:title="enableButtonTooltip"
@@ -84,8 +67,8 @@
class="enable primary"
type="button"
:value="enableButtonText"
- :disabled="!app.canInstall || installing || isLoading"
- @click="enable(app.id)">
+ :disabled="!app.canInstall || installing || isLoading || !defaultDeployDaemonAccessible || isInitializing || isDeploying"
+ @click="enableButtonAction">
<input v-else-if="!app.active && !app.canInstall"
:title="forceEnableButtonTooltip"
:aria-label="forceEnableButtonTooltip"
@@ -94,7 +77,25 @@
:value="forceEnableButtonText"
:disabled="installing || isLoading"
@click="forceEnable(app.id)">
+ <NcButton v-if="app?.app_api && (app.canInstall || app.isCompatible)"
+ :aria-label="t('settings', 'Advanced deploy options')"
+ type="secondary"
+ @click="() => showDeployOptionsModal = true">
+ <template #icon>
+ <NcIconSvgWrapper :path="mdiToyBrickPlusOutline" />
+ </template>
+ {{ t('settings', 'Deploy options') }}
+ </NcButton>
</div>
+ <p v-if="!defaultDeployDaemonAccessible" class="warning">
+ {{ t('settings', 'Default Deploy daemon is not accessible') }}
+ </p>
+ <NcCheckboxRadioSwitch v-if="app.canUnInstall"
+ :checked="removeData"
+ :disabled="installing || isLoading || !defaultDeployDaemonAccessible"
+ @update:checked="toggleRemoveData">
+ {{ t('settings', 'Delete data on remove') }}
+ </NcCheckboxRadioSwitch>
</div>
<ul class="app-details__dependencies">
@@ -114,7 +115,7 @@
</li>
</ul>
- <div v-if="lastModified" class="app-details__section">
+ <div v-if="lastModified && !app.shipped" class="app-details__section">
<h4>
{{ t('settings', 'Latest updated') }}
</h4>
@@ -161,7 +162,7 @@
:aria-label="t('settings', 'Report a bug')"
:title="t('settings', 'Report a bug')">
<template #icon>
- <NcIconSvgWrapper :path="mdiBug" />
+ <NcIconSvgWrapper :path="mdiBugOutline" />
</template>
</NcButton>
<NcButton :disabled="!app.bugs"
@@ -169,7 +170,7 @@
:aria-label="t('settings', 'Request feature')"
:title="t('settings', 'Request feature')">
<template #icon>
- <NcIconSvgWrapper :path="mdiFeatureSearch" />
+ <NcIconSvgWrapper :path="mdiFeatureSearchOutline" />
</template>
</NcButton>
<NcButton v-if="app.appstoreData?.discussion"
@@ -177,7 +178,7 @@
:aria-label="t('settings', 'Ask questions or discuss')"
:title="t('settings', 'Ask questions or discuss')">
<template #icon>
- <NcIconSvgWrapper :path="mdiTooltipQuestion" />
+ <NcIconSvgWrapper :path="mdiTooltipQuestionOutline" />
</template>
</NcButton>
<NcButton v-if="!app.internal"
@@ -190,20 +191,33 @@
</NcButton>
</div>
</div>
+
+ <AppDeployOptionsModal v-if="app?.app_api"
+ :show.sync="showDeployOptionsModal"
+ :app="app" />
+ <DaemonSelectionDialog v-if="app?.app_api"
+ :show.sync="showSelectDaemonModal"
+ :app="app"
+ :deploy-options="deployOptions" />
</div>
</NcAppSidebarTab>
</template>
<script>
-import NcAppSidebarTab from '@nextcloud/vue/dist/Components/NcAppSidebarTab.js'
-import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
-import NcDateTime from '@nextcloud/vue/dist/Components/NcDateTime.js'
-import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
-import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
+import { subscribe, unsubscribe } from '@nextcloud/event-bus'
+import NcAppSidebarTab from '@nextcloud/vue/components/NcAppSidebarTab'
+import NcButton from '@nextcloud/vue/components/NcButton'
+import NcDateTime from '@nextcloud/vue/components/NcDateTime'
+import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
+import NcSelect from '@nextcloud/vue/components/NcSelect'
+import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
+import AppDeployOptionsModal from './AppDeployOptionsModal.vue'
+import DaemonSelectionDialog from '../AppAPI/DaemonSelectionDialog.vue'
import AppManagement from '../../mixins/AppManagement.js'
-import { mdiBug, mdiFeatureSearch, mdiStar, mdiTextBox, mdiTooltipQuestion } from '@mdi/js'
+import { mdiBugOutline, mdiFeatureSearchOutline, mdiStar, mdiTextBoxOutline, mdiTooltipQuestionOutline, mdiToyBrickPlusOutline } from '@mdi/js'
import { useAppsStore } from '../../store/apps-store'
+import { useAppApiStore } from '../../store/app-api-store'
export default {
name: 'AppDetailsTab',
@@ -214,6 +228,9 @@ export default {
NcDateTime,
NcIconSvgWrapper,
NcSelect,
+ NcCheckboxRadioSwitch,
+ AppDeployOptionsModal,
+ DaemonSelectionDialog,
},
mixins: [AppManagement],
@@ -226,21 +243,28 @@ export default {
setup() {
const store = useAppsStore()
+ const appApiStore = useAppApiStore()
return {
store,
+ appApiStore,
- mdiBug,
- mdiFeatureSearch,
+ mdiBugOutline,
+ mdiFeatureSearchOutline,
mdiStar,
- mdiTextBox,
- mdiTooltipQuestion,
+ mdiTextBoxOutline,
+ mdiTooltipQuestionOutline,
+ mdiToyBrickPlusOutline,
}
},
data() {
return {
groupCheckedAppsData: false,
+ removeData: false,
+ showDeployOptionsModal: false,
+ showSelectDaemonModal: false,
+ deployOptions: null,
}
},
@@ -345,10 +369,45 @@ export default {
.sort((a, b) => a.name.localeCompare(b.name))
},
},
+ watch: {
+ 'app.id'() {
+ this.removeData = false
+ },
+ },
+ beforeUnmount() {
+ this.deployOptions = null
+ unsubscribe('showDaemonSelectionModal')
+ },
mounted() {
if (this.app.groups.length > 0) {
this.groupCheckedAppsData = true
}
+ subscribe('showDaemonSelectionModal', (deployOptions) => {
+ this.showSelectionModal(deployOptions)
+ })
+ },
+ methods: {
+ toggleRemoveData() {
+ this.removeData = !this.removeData
+ },
+ showSelectionModal(deployOptions = null) {
+ this.deployOptions = deployOptions
+ this.showSelectDaemonModal = true
+ },
+ async enableButtonAction() {
+ if (!this.app?.app_api) {
+ this.enable(this.app.id)
+ return
+ }
+ await this.appApiStore.fetchDockerDaemons()
+ if (this.appApiStore.dockerDaemons.length === 1 && this.app.needsDownload) {
+ this.enable(this.app.id, this.appApiStore.dockerDaemons[0])
+ } else if (this.app.needsDownload) {
+ this.showSelectionModal()
+ } else {
+ this.enable(this.app.id, this.app.daemon)
+ }
+ },
},
}
</script>
@@ -362,6 +421,7 @@ export default {
&-manage {
// if too many, shrink them and ellipsis
display: flex;
+ align-items: center;
input {
flex: 0 1 auto;
min-width: 0;
@@ -419,6 +479,7 @@ export default {
border-color: var(--color-error);
background: var(--color-main-background);
}
+
.force:hover,
.force:active {
color: var(--color-main-background);
diff --git a/apps/settings/src/components/AppStoreSidebar/AppReleasesTab.vue b/apps/settings/src/components/AppStoreSidebar/AppReleasesTab.vue
index 13e6ecff28f..e65df0341db 100644
--- a/apps/settings/src/components/AppStoreSidebar/AppReleasesTab.vue
+++ b/apps/settings/src/components/AppStoreSidebar/AppReleasesTab.vue
@@ -1,25 +1,7 @@
<!--
- - @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
- -
- - @author Julius Härtl <jus@bitgrid.net>
- - @author Ferdinand Thiessen <opensource@fthiessen.de>
- -
- - @license AGPL-3.0-or-later
- -
- - 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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
<NcAppSidebarTab v-if="hasChangelog"
id="changelog"
@@ -43,8 +25,8 @@ import { mdiClockFast } from '@mdi/js'
import { getLanguage, translate as t } from '@nextcloud/l10n'
import { computed } from 'vue'
-import NcAppSidebarTab from '@nextcloud/vue/dist/Components/NcAppSidebarTab.js'
-import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
+import NcAppSidebarTab from '@nextcloud/vue/components/NcAppSidebarTab'
+import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
import Markdown from '../Markdown.vue'
// eslint-disable-next-line @typescript-eslint/no-unused-vars