mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
WIP: migrate to Pinia, minor fixes
Signed-off-by: Andrey Borysenko <andrey18106x@gmail.com>
This commit is contained in:
parent
e4d586697c
commit
c8b35fa22f
10 changed files with 396 additions and 66 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
295
apps/settings/src/store/app-api-store.ts
Normal file
295
apps/settings/src/store/app-api-store.ts
Normal file
|
|
@ -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
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue