aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorAndrey Borysenko <andrey18106x@gmail.com>2024-10-21 15:39:49 +0300
committerAndrey Borysenko <andrey18106x@gmail.com>2024-10-30 20:36:56 +0200
commit606a9a3d648baf86233c763a848db901354550d5 (patch)
treed38180a9d808f790ff6ec5c77d330cf6003c34f1 /apps
parentc53f5c43b4bd461f5dc6e42bae6a05a63b8701c5 (diff)
downloadnextcloud-server-606a9a3d648baf86233c763a848db901354550d5.tar.gz
nextcloud-server-606a9a3d648baf86233c763a848db901354550d5.zip
WIP: migrate to Pinia, minor fixes
Signed-off-by: Andrey Borysenko <andrey18106x@gmail.com>
Diffstat (limited to 'apps')
-rw-r--r--apps/settings/src/components/AppList.vue12
-rw-r--r--apps/settings/src/components/AppList/AppItem.vue6
-rw-r--r--apps/settings/src/components/AppStoreSidebar/AppDeployDaemonTab.vue8
-rw-r--r--apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue14
-rw-r--r--apps/settings/src/composables/useAppIcon.ts6
-rw-r--r--apps/settings/src/mixins/AppManagement.js104
-rw-r--r--apps/settings/src/store/app-api-store.ts295
-rw-r--r--apps/settings/src/store/index.js2
-rw-r--r--apps/settings/src/views/AppStore.vue11
-rw-r--r--apps/settings/src/views/AppStoreSidebar.vue4
10 files changed, 396 insertions, 66 deletions
diff --git a/apps/settings/src/components/AppList.vue b/apps/settings/src/components/AppList.vue
index 3f8fa85039b..718ac44fa93 100644
--- a/apps/settings/src/components/AppList.vue
+++ b/apps/settings/src/components/AppList.vue
@@ -143,6 +143,7 @@ import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import AppItem from './AppList/AppItem.vue'
import pLimit from 'p-limit'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
+import { useAppApiStore } from '../store/app-api-store'
export default {
name: 'AppList',
@@ -158,6 +159,13 @@ export default {
},
},
+ setup() {
+ const appApiStore = useAppApiStore()
+ return {
+ appApiStore,
+ }
+ },
+
data() {
return {
search: '',
@@ -171,7 +179,7 @@ export default {
if (!this.$store.getters['appApiApps/isAppApiEnabled']) {
return this.$store.getters.loading('list')
}
- return this.$store.getters.loading('list') || this.$store.getters['appApiApps/loading']('list')
+ return this.$store.getters.loading('list') || this.appApiStore.getLoading('list')
},
hasPendingUpdate() {
return this.apps.filter(app => app.update).length > 0
@@ -181,7 +189,7 @@ export default {
},
apps() {
// Exclude ExApps from the list if AppAPI is disabled
- const exApps = this.$store.getters.isAppApiEnabled ? this.$store.getters['appApiApps/getAllApps'] : []
+ const exApps = this.$store.getters.isAppApiEnabled ? this.appApiStore.getAllApps : []
const apps = [...this.$store.getters.getAllApps, ...exApps]
.filter(app => app.name.toLowerCase().search(this.search.toLowerCase()) !== -1)
.sort(function(a, b) {
diff --git a/apps/settings/src/components/AppList/AppItem.vue b/apps/settings/src/components/AppList/AppItem.vue
index 0ff4e7346cd..0c5d39b6862 100644
--- a/apps/settings/src/components/AppList/AppItem.vue
+++ b/apps/settings/src/components/AppList/AppItem.vue
@@ -83,7 +83,7 @@
@click.stop="update(app.id)">
{{ t('settings', 'Update to {update}', {update:app.update}) }}
</NcButton>
- <NcButton v-if="app.canUnInstall"
+ <NcButton v-if="app.canUnInstall || app.canUninstall"
class="uninstall"
type="tertiary"
:disabled="installing || isLoading"
@@ -91,7 +91,7 @@
{{ t('settings', 'Remove') }}
</NcButton>
<NcButton v-if="app.active"
- :disabled="installing || isLoading || isInitializing || isDeploying"
+ :disabled="installing || isLoading || isInitializing || isDeploying"
@click.stop="disable(app.id)">
{{ disableButtonText }}
</NcButton>
@@ -184,7 +184,7 @@ export default {
return !!this.$route.params.id
},
shouldDisplayDefaultIcon() {
- return this.listView && !this.app.preview || !this.listView && !this.screenshotLoaded
+ return (this.listView && !this.app.preview) || (!this.listView && !this.screenshotLoaded)
},
},
watch: {
diff --git a/apps/settings/src/components/AppStoreSidebar/AppDeployDaemonTab.vue b/apps/settings/src/components/AppStoreSidebar/AppDeployDaemonTab.vue
index 36087cdd617..a082ab326cc 100644
--- a/apps/settings/src/components/AppStoreSidebar/AppDeployDaemonTab.vue
+++ b/apps/settings/src/components/AppStoreSidebar/AppDeployDaemonTab.vue
@@ -16,7 +16,8 @@
<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>: {{ app?.daemon.deploy_config?.computeDevice?.id !== 'cpu' || 'false' }}</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>
@@ -28,10 +29,13 @@ import NcAppSidebarTab from '@nextcloud/vue/dist/Components/NcAppSidebarTab.js'
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
import { mdiFileChart } from '@mdi/js'
+import { ref } from 'vue'
-defineProps<{
+const props = defineProps<{
app: IAppstoreExApp,
}>()
+
+const gpuSupport = ref(props.app?.daemon?.deploy_config?.computeDevice?.id !== 'cpu' || false)
</script>
<style scoped lang="scss">
diff --git a/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue b/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue
index b732c5b82f5..4772b97ecf6 100644
--- a/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue
+++ b/apps/settings/src/components/AppStoreSidebar/AppDetailsTab.vue
@@ -49,7 +49,7 @@
:value="t('settings', 'Update to {version}', { version: app.update })"
:disabled="installing || isLoading || isManualInstall"
@click="update(app.id)">
- <input v-if="app.canUnInstall"
+ <input v-if="app.canUnInstall || app.canUninstall"
class="uninstall"
type="button"
:value="t('settings', 'Remove')"
@@ -78,7 +78,10 @@
:disabled="installing || isLoading"
@click="forceEnable(app.id)">
</div>
- <NcCheckboxRadioSwitch v-if="app.canUnInstall"
+ <p v-if="!defaultDeployDaemonAccessible" class="warning">
+ {{ t('settings', 'Default Deploy daemon is not accessible') }}
+ </p>
+ <NcCheckboxRadioSwitch v-if="app.canUnInstall || app.canUninstall"
:checked="removeData"
:disabled="installing || isLoading || !defaultDeployDaemonAccessible"
@update:checked="toggleRemoveData">
@@ -110,7 +113,7 @@
<NcDateTime :timestamp="lastModified" />
</div>
- <div class="app-details__section">
+ <div v-if="appAuthors" class="app-details__section">
<h4>
{{ t('settings', 'Author') }}
</h4>
@@ -119,7 +122,7 @@
</p>
</div>
- <div class="app-details__section">
+ <div v-if="appCategories" class="app-details__section">
<h4>
{{ t('settings', 'Categories') }}
</h4>
@@ -194,6 +197,7 @@ import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadi
import AppManagement from '../../mixins/AppManagement.js'
import { mdiBug, mdiFeatureSearch, mdiStar, mdiTextBox, mdiTooltipQuestion } from '@mdi/js'
import { useAppsStore } from '../../store/apps-store'
+import { useAppApiStore } from '../../store/app-api-store'
export default {
name: 'AppDetailsTab',
@@ -217,9 +221,11 @@ export default {
setup() {
const store = useAppsStore()
+ const appApiStore = useAppApiStore()
return {
store,
+ appApiStore,
mdiBug,
mdiFeatureSearch,
diff --git a/apps/settings/src/composables/useAppIcon.ts b/apps/settings/src/composables/useAppIcon.ts
index 60c3ee6e3ea..b5e211aa1bc 100644
--- a/apps/settings/src/composables/useAppIcon.ts
+++ b/apps/settings/src/composables/useAppIcon.ts
@@ -29,9 +29,9 @@ export function useAppIcon(app: Ref<IAppstoreApp>) {
path = mdiCogOutline
} else {
path = [app.value?.category ?? []].flat()
- .map((name) => AppstoreCategoryIcons[name])
- .filter((icon) => !!icon)
- .at(0)
+ .map((name) => AppstoreCategoryIcons[name])
+ .filter((icon) => !!icon)
+ .at(0)
?? (!app.value?.app_api ? mdiCog : mdiCogOutline)
}
return path ? `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="${path}" /></svg>` : null
diff --git a/apps/settings/src/mixins/AppManagement.js b/apps/settings/src/mixins/AppManagement.js
index 6d4b97ea82d..1c88267bdbf 100644
--- a/apps/settings/src/mixins/AppManagement.js
+++ b/apps/settings/src/mixins/AppManagement.js
@@ -5,33 +5,40 @@
import { showError } from '@nextcloud/dialogs'
import rebuildNavigation from '../service/rebuild-navigation.js'
+import { useAppApiStore } from '../store/app-api-store'
export default {
+ setup() {
+ const appApiStore = useAppApiStore()
+ return {
+ appApiStore,
+ }
+ },
computed: {
appGroups() {
return this.app.groups.map(group => { return { id: group, name: group } })
},
installing() {
if (this.app?.app_api) {
- return this.app && this.$store.getters['appApiApps/loading']('install')
+ return this.app && this?.appApiStore.getLoading('install') === true
}
return this.$store.getters.loading('install')
},
isLoading() {
if (this.app?.app_api) {
- return this.app && this.$store.getters['appApiApps/loading'](this.app.id)
+ return this.app && this?.appApiStore.getLoading(this.app.id) === true
}
return this.app && this.$store.getters.loading(this.app.id)
},
isInitializing() {
if (this.app?.app_api) {
- return this.app && Object.hasOwn(this.app?.status, 'action') && (this.app.status.action === 'init' || this.app.status.action === 'healthcheck')
+ return this.app && (this.app?.status?.action === 'init' || this.app?.status?.action === 'healthcheck')
}
return false
},
isDeploying() {
if (this.app?.app_api) {
- return this.app && Object.hasOwn(this.app?.status, 'action') && this.app.status.action === 'deploy'
+ return this.app && this.app?.status?.action === 'deploy'
}
return false
},
@@ -90,7 +97,7 @@ export default {
return t('settings', 'Allow untested app')
},
enableButtonTooltip() {
- if (this.app.needsDownload) {
+ if (!this.app?.app_api && this.app.needsDownload) {
return t('settings', 'The app will be downloaded from the App Store')
}
return null
@@ -107,10 +114,11 @@ export default {
if (this.app?.daemon && this.app?.daemon?.accepts_deploy_id === 'manual-install') {
return true
}
- if (this.app?.daemon?.accepts_deploy_id === 'docker-install') {
- return this.$store.getters['appApiApps/getDaemonAccessible'] === true
+ if (this.app?.daemon?.accepts_deploy_id === 'docker-install'
+ && this.appApiStore.getDefaultDaemon?.name === this.app?.daemon?.name) {
+ return this?.appApiStore.getDaemonAccessible === true
}
- return this.$store.getters['appApiApps/getDaemonAccessible']
+ return this?.appApiStore.getDaemonAccessible
}
return true
},
@@ -177,63 +185,73 @@ export default {
this.$store.dispatch('enableApp', { appId: this.app.id, groups: currentGroups })
},
forceEnable(appId) {
- let type = 'forceEnableApp'
if (this.app?.app_api) {
- type = 'appApiApps/forceEnableApp'
+ this.appApiStore.forceEnableApp(appId)
+ .then(() => { rebuildNavigation() })
+ .catch((error) => { showError(error) })
+ } else {
+ this.$store.dispatch('forceEnableApp', { appId, groups: [] })
+ .then((response) => { rebuildNavigation() })
+ .catch((error) => { showError(error) })
}
- this.$store.dispatch(type, { appId, groups: [] })
- .then((response) => { rebuildNavigation() })
- .catch((error) => { showError(error) })
},
enable(appId) {
- let type = 'enableApp'
if (this.app?.app_api) {
- type = 'appApiApps/enableApp'
+ this.appApiStore.enableApp(appId)
+ .then(() => { rebuildNavigation() })
+ .catch((error) => { showError(error) })
+ } else {
+ this.$store.dispatch('enableApp', { appId, groups: [] })
+ .then((response) => { rebuildNavigation() })
+ .catch((error) => { showError(error) })
}
- this.$store.dispatch(type, { appId, groups: [] })
- .then((response) => { rebuildNavigation() })
- .catch((error) => { showError(error) })
},
disable(appId) {
- let type = 'disableApp'
if (this.app?.app_api) {
- type = 'appApiApps/disableApp'
+ this.appApiStore.disableApp(appId)
+ .then(() => { rebuildNavigation() })
+ .catch((error) => { showError(error) })
+ } else {
+ this.$store.dispatch('disableApp', { appId })
+ .then((response) => { rebuildNavigation() })
+ .catch((error) => { showError(error) })
}
- this.$store.dispatch(type, { appId })
- .then((response) => { rebuildNavigation() })
- .catch((error) => { showError(error) })
},
remove(appId, removeData = false) {
- let type = 'uninstallApp'
- let payload = { appId }
if (this.app?.app_api) {
- type = 'appApiApps/uninstallApp'
- payload = { appId, removeData }
+ this.appApiStore.uninstallApp(appId, removeData)
+ .then(() => { rebuildNavigation() })
+ .catch((error) => { showError(error) })
+ } else {
+ this.$store.dispatch('appApiApps/uninstallApp', { appId, removeData })
+ .then((response) => { rebuildNavigation() })
+ .catch((error) => { showError(error) })
}
- this.$store.dispatch(type, payload)
- .then((response) => { rebuildNavigation() })
- .catch((error) => { showError(error) })
},
install(appId) {
- let type = 'enableApp'
if (this.app?.app_api) {
- type = 'appApiApps/enableApp'
+ this.appApiStore.enableApp(appId)
+ .then(() => { rebuildNavigation() })
+ .catch((error) => { showError(error) })
+ } else {
+ this.$store.dispatch('enableApp', { appId })
+ .then((response) => { rebuildNavigation() })
+ .catch((error) => { showError(error) })
}
- this.$store.dispatch(type, { appId })
- .then((response) => { rebuildNavigation() })
- .catch((error) => { showError(error) })
},
update(appId) {
- let type = 'updateApp'
if (this.app?.app_api) {
- type = 'appApiApps/updateApp'
+ this.appApiStore.updateApp(appId)
+ .then(() => { rebuildNavigation() })
+ .catch((error) => { showError(error) })
+ } else {
+ this.$store.dispatch('updateApp', { appId })
+ .catch((error) => { showError(error) })
+ .then(() => {
+ rebuildNavigation()
+ this.store.updateCount = Math.max(this.store.updateCount - 1, 0)
+ })
}
- this.$store.dispatch(type, { appId })
- .catch((error) => { showError(error) })
- .then(() => {
- rebuildNavigation()
- this.store.updateCount = Math.max(this.store.updateCount - 1, 0)
- })
},
},
}
diff --git a/apps/settings/src/store/app-api-store.ts b/apps/settings/src/store/app-api-store.ts
new file mode 100644
index 00000000000..e056f5bc660
--- /dev/null
+++ b/apps/settings/src/store/app-api-store.ts
@@ -0,0 +1,295 @@
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import axios from '@nextcloud/axios'
+import { confirmPassword } from '@nextcloud/password-confirmation'
+import { showError, showInfo } from '@nextcloud/dialogs'
+import { loadState } from '@nextcloud/initial-state'
+import { translate as t } from '@nextcloud/l10n'
+import { generateUrl } from '@nextcloud/router'
+import { defineStore } from 'pinia'
+
+import api from './api'
+import logger from '../logger'
+
+import type { IAppstoreExApp, IDeployDaemon, IExAppStatus } from '../app-types'
+import { reactive } from 'vue'
+
+interface AppApiState {
+ apps: IAppstoreExApp[]
+ updateCount: number
+ loading: Record<string, boolean>
+ loadingList: boolean
+ statusUpdater: number | null | undefined
+ daemonAccessible: boolean
+ defaultDaemon: IDeployDaemon | null
+}
+
+export const useAppApiStore = defineStore('app-api-apps', {
+ state: (): AppApiState => ({
+ apps: [],
+ updateCount: loadState('settings', 'appstoreExAppUpdateCount', 0),
+ loading: reactive({}),
+ loadingList: false,
+ statusUpdater: null,
+ daemonAccessible: loadState('settings', 'defaultDaemonConfigAccessible', false),
+ defaultDaemon: loadState('settings', 'defaultDaemonConfig', null),
+ }),
+
+ getters: {
+ getLoading: (state) => (id: string) => state.loading[id] ?? false,
+ getAllApps: (state) => state.apps,
+ getUpdateCount: (state) => state.updateCount,
+ getDaemonAccessible: (state) => state.daemonAccessible,
+ getDefaultDaemon: (state) => state.defaultDaemon,
+ getAppStatus: (state) => (appId: string) =>
+ state.apps.find((app) => app.id === appId)?.status || null,
+ getStatusUpdater: (state) => state.statusUpdater,
+ getInitializingOrDeployingApps: (state) =>
+ state.apps.filter((app) =>
+ app?.status?.action
+ && (app?.status?.action === 'deploy' || app.status.action === 'init' || app.status.action === 'healthcheck')
+ && app.status.type !== '',
+ ),
+ },
+
+ actions: {
+ appsApiFailure(error: any) {
+ showError(t('settings', 'An error occurred during the request. Unable to proceed.') + '<br>' + error.error.response.data.data.message, { isHTML: true })
+ logger.error(error)
+ },
+
+ setError(appId: string | string[], error: string) {
+ const appIds = Array.isArray(appId) ? appId : [appId]
+ appIds.forEach((_id) => {
+ const app = this.apps.find((app) => app.id === _id)
+ if (app) {
+ app.error = error
+ }
+ })
+ },
+
+ enableApp(appId: string) {
+ this.loading[appId] = true
+ this.loading.install = true
+ return confirmPassword().then(() => {
+
+ return axios.post(generateUrl(`/apps/app_api/apps/enable/${appId}`))
+ .then((response) => {
+ this.loading[appId] = false
+ this.loading.install = false
+
+ const app = this.apps.find((app) => app.id === appId)
+ if (app) {
+ if (!app.installed) {
+ app.installed = true
+ app.needsDownload = false
+ app.daemon = this.defaultDaemon
+ app.status = {
+ type: 'install',
+ action: 'deploy',
+ init: 0,
+ deploy: 0,
+ } as IExAppStatus
+ }
+ app.active = true
+ app.canUninstall = false
+ app.removable = true
+ app.error = ''
+ }
+
+ this.updateAppsStatus()
+
+ return axios.get(generateUrl('apps/files'))
+ .then(() => {
+ if (response.data.update_required) {
+ showInfo(
+ t('settings', 'The app has been enabled but needs to be updated.'),
+ {
+ onClick: () => window.location.reload(),
+ close: false,
+ },
+ )
+ setTimeout(() => {
+ location.reload()
+ }, 5000)
+ }
+ })
+ .catch(() => {
+ this.setError(appId, t('settings', 'Error: This app cannot be enabled because it makes the server unstable'))
+ })
+ })
+ .catch((error) => {
+ this.loading[appId] = false
+ this.loading.install = false
+ this.setError(appId, error.response.data.data.message)
+ this.appsApiFailure({ appId, error })
+ })
+ })
+ },
+
+ forceEnableApp(appId: string) {
+ this.loading[appId] = true
+ this.loading.install = true
+ return confirmPassword().then(() => {
+
+ return api.post(generateUrl('/apps/app_api/apps/force'), { appId })
+ .then(() => {
+ location.reload()
+ })
+ .catch((error) => {
+ this.loading[appId] = false
+ this.loading.install = false
+ this.setError(appId, error.response.data.data.message)
+ this.appsApiFailure({ appId, error })
+ })
+ })
+ },
+
+ disableApp(appId: string) {
+ this.loading[appId] = true
+ return confirmPassword().then(() => {
+
+ return api.get(generateUrl(`apps/app_api/apps/disable/${appId}`))
+ .then(() => {
+ this.loading[appId] = false
+ const app = this.apps.find((app) => app.id === appId)
+ if (app) {
+ app.active = false
+ if (app.removable) {
+ app.canUninstall = true
+ }
+ }
+ return true
+ })
+ .catch((error) => {
+ this.loading[appId] = false
+ this.appsApiFailure({ appId, error })
+ })
+ })
+ },
+
+ uninstallApp(appId: string, removeData: boolean) {
+ this.loading[appId] = true
+ return confirmPassword().then(() => {
+
+ return api.get(generateUrl(`/apps/app_api/apps/uninstall/${appId}?removeData=${removeData}`))
+ .then(() => {
+ this.loading[appId] = false
+ const app = this.apps.find((app) => app.id === appId)
+ if (app) {
+ app.active = false
+ app.needsDownload = true
+ app.installed = false
+ app.canUninstall = false
+ app.canInstall = true
+ app.daemon = null
+ app.status = {}
+ if (app.update !== null) {
+ this.updateCount--
+ }
+ app.update = null
+ }
+ return true
+ })
+ .catch((error) => {
+ this.loading[appId] = false
+ this.appsApiFailure({ appId, error })
+ })
+ })
+ },
+
+ updateApp(appId: string) {
+ this.loading[appId] = true
+ this.loading.install = true
+ return confirmPassword().then(() => {
+
+ return api.get(generateUrl(`/apps/app_api/apps/update/${appId}`))
+ .then(() => {
+ this.loading.install = false
+ this.loading[appId] = false
+ const app = this.apps.find((app) => app.id === appId)
+ if (app) {
+ const version = app.update
+ app.update = null
+ app.version = version || app.version
+ app.status = {
+ type: 'update',
+ action: 'deploy',
+ init: 0,
+ deploy: 0,
+ } as IExAppStatus
+ app.error = ''
+ }
+ this.updateCount--
+ this.updateAppsStatus()
+ return true
+ })
+ .catch((error) => {
+ this.loading[appId] = false
+ this.loading.install = false
+ this.appsApiFailure({ appId, error })
+ })
+ })
+ },
+
+ async fetchAllApps() {
+ this.loadingList = true
+ try {
+ const response = await api.get(generateUrl('/apps/app_api/apps/list'))
+ this.apps = response.data.apps
+ this.loadingList = false
+ return true
+ } catch (error) {
+ logger.error(error as string)
+ showError(t('settings', 'An error occurred during the request. Unable to proceed.'))
+ this.loadingList = false
+ }
+ },
+
+ async fetchAppStatus(appId: string) {
+ return api.get(generateUrl(`/apps/app_api/apps/status/${appId}`))
+ .then((response) => {
+ const app = this.apps.find((app) => app.id === appId)
+ if (app) {
+ app.status = response.data
+ }
+ const initializingOrDeployingApps = this.getInitializingOrDeployingApps
+ console.debug('initializingOrDeployingApps after setAppStatus', initializingOrDeployingApps)
+ if (initializingOrDeployingApps.length === 0) {
+ console.debug('clearing interval')
+ clearInterval(this.statusUpdater as number)
+ this.statusUpdater = null
+ }
+ if (Object.hasOwn(response.data, 'error')
+ && response.data.error !== ''
+ && initializingOrDeployingApps.length === 1) {
+ clearInterval(this.statusUpdater as number)
+ this.statusUpdater = null
+ }
+ })
+ .catch((error) => {
+ this.appsApiFailure({ appId, error })
+ this.apps = this.apps.filter((app) => app.id !== appId)
+ this.updateAppsStatus()
+ })
+ },
+
+ updateAppsStatus() {
+ clearInterval(this.statusUpdater as number)
+ const initializingOrDeployingApps = this.getInitializingOrDeployingApps
+ if (initializingOrDeployingApps.length === 0) {
+ return
+ }
+ this.statusUpdater = setInterval(() => {
+ const initializingOrDeployingApps = this.getInitializingOrDeployingApps
+ console.debug('initializingOrDeployingApps', initializingOrDeployingApps)
+ initializingOrDeployingApps.forEach(app => {
+ this.fetchAppStatus(app.id)
+ })
+ }, 2000) as unknown as number
+ },
+ },
+})
diff --git a/apps/settings/src/store/index.js b/apps/settings/src/store/index.js
index 62dab7246fc..910185edb51 100644
--- a/apps/settings/src/store/index.js
+++ b/apps/settings/src/store/index.js
@@ -7,7 +7,6 @@ import Vue from 'vue'
import Vuex, { Store } from 'vuex'
import users from './users.js'
import apps from './apps.js'
-import appApiApps from './app_api_apps.js'
import settings from './users-settings.js'
import oc from './oc.js'
import { showError } from '@nextcloud/dialogs'
@@ -36,7 +35,6 @@ export const useStore = () => {
modules: {
users,
apps,
- appApiApps,
settings,
oc,
},
diff --git a/apps/settings/src/views/AppStore.vue b/apps/settings/src/views/AppStore.vue
index 0c922ce7296..62a9a9290be 100644
--- a/apps/settings/src/views/AppStore.vue
+++ b/apps/settings/src/views/AppStore.vue
@@ -34,9 +34,11 @@ import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import AppList from '../components/AppList.vue'
import AppStoreDiscoverSection from '../components/AppStoreDiscover/AppStoreDiscoverSection.vue'
+import { useAppApiStore } from '../store/app-api-store.ts'
const route = useRoute()
const store = useAppsStore()
+const appApiStore = useAppApiStore()
/**
* ID of the current active category, default is `discover`
@@ -62,15 +64,12 @@ onBeforeMount(() => {
(instance?.proxy as any).$store.dispatch('getAllApps')
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((instance?.proxy as any).$store.getters.isAppApiEnabled) {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (instance?.proxy as any).$store.dispatch('appApiApps/getAllApps');
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (instance?.proxy as any).$store.dispatch('appApiApps/updateAppsStatus')
+ appApiStore.fetchAllApps()
+ appApiStore.updateAppsStatus()
}
})
onBeforeUnmount(() => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- clearInterval((instance?.proxy as any).$store.getters('appApiApps/getStatusUpdater'))
+ clearInterval(appApiStore.getStatusUpdater)
})
</script>
diff --git a/apps/settings/src/views/AppStoreSidebar.vue b/apps/settings/src/views/AppStoreSidebar.vue
index c003174a1d2..1021837faaa 100644
--- a/apps/settings/src/views/AppStoreSidebar.vue
+++ b/apps/settings/src/views/AppStoreSidebar.vue
@@ -56,16 +56,18 @@ import AppLevelBadge from '../components/AppList/AppLevelBadge.vue'
import AppDaemonBadge from '../components/AppList/AppDaemonBadge.vue'
import { useAppIcon } from '../composables/useAppIcon.ts'
import { useStore } from '../store'
+import { useAppApiStore } from '../store/app-api-store.ts'
const route = useRoute()
const router = useRouter()
const store = useAppsStore()
+const appApiStore = useAppApiStore()
const legacyStore = useStore()
const appId = computed(() => route.params.id ?? '')
const app = computed(() => {
if (legacyStore.getters.isAppApiEnabled) {
- const exApp = legacyStore.getters['appApiApps/getAllApps']
+ const exApp = appApiStore.getAllApps
.find((app) => app.id === appId.value) ?? null
if (exApp) {
return exApp