Skip to content

Commit

Permalink
Merge pull request #48665 from nextcloud/feat/settings/app_api_apps_m…
Browse files Browse the repository at this point in the history
…anagement

feat(settings): migrate AppAPI ExApps management to settings
  • Loading branch information
andrey18106 authored Oct 29, 2024
2 parents 2d5060d + c2f1c3d commit d266779
Show file tree
Hide file tree
Showing 115 changed files with 910 additions and 213 deletions.
11 changes: 11 additions & 0 deletions apps/settings/lib/Controller/AppSettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\L10N\IFactory;
use OCP\Server;
use OCP\Util;
use Psr\Log\LoggerInterface;

Expand Down Expand Up @@ -78,6 +79,8 @@ public function __construct(
}

/**
* @psalm-suppress UndefinedClass AppAPI is shipped since 30.0.1
*
* @return TemplateResponse
*/
#[NoCSRFRequired]
Expand All @@ -89,6 +92,13 @@ public function viewApps(): TemplateResponse {
$this->initialState->provideInitialState('appstoreDeveloperDocs', $this->urlGenerator->linkToDocs('developer-manual'));
$this->initialState->provideInitialState('appstoreUpdateCount', count($this->getAppsWithUpdates()));

if ($this->appManager->isInstalled('app_api')) {
try {
Server::get(\OCA\AppAPI\Service\ExAppsPageService::class)->provideAppApiState($this->initialState);
} catch (\Psr\Container\NotFoundExceptionInterface|\Psr\Container\ContainerExceptionInterface $e) {
}
}

$policy = new ContentSecurityPolicy();
$policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com');

Expand Down Expand Up @@ -438,6 +448,7 @@ private function getAppsForCategory($requestedCategory = ''): array {

$formattedApps[] = [
'id' => $app['id'],
'app_api' => false,
'name' => $app['translations'][$currentLanguage]['name'] ?? $app['translations']['en']['name'],
'description' => $app['translations'][$currentLanguage]['description'] ?? $app['translations']['en']['description'],
'summary' => $app['translations'][$currentLanguage]['summary'] ?? $app['translations']['en']['summary'],
Expand Down
44 changes: 42 additions & 2 deletions apps/settings/src/app-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,54 @@ export interface IAppstoreApp {
preview?: string
screenshot?: string

app_api: boolean
active: boolean
internal: boolean
removeable: boolean
removable: boolean
installed: boolean
canInstall: boolean
canUninstall: boolean
canUnInstall: boolean
isCompatible: boolean
needsDownload: boolean
update?: string

appstoreData: Record<string, never>
releases?: IAppstoreAppRelease[]
}

export interface IComputeDevice {
id: string,
label: string,
}

export interface IDeployConfig {
computeDevice: IComputeDevice,
net: string,
nextcloud_url: string,
}

export interface IDeployDaemon {
accepts_deploy_id: string,
deploy_config: IDeployConfig,
display_name: string,
host: string,
id: number,
name: string,
protocol: string,
}

export interface IExAppStatus {
action: string
deploy: number
deploy_start_time: number
error: string
init: number
init_start_time: number
type: string
}

export interface IAppstoreExApp extends IAppstoreApp {
daemon: IDeployDaemon | null | undefined
status: IExAppStatus | Record<string, never>
error: string
}
18 changes: 10 additions & 8 deletions apps/settings/src/components/AppList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,10 @@

<script>
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { useAppsStore } from '../store/apps-store'
import AppItem from './AppList/AppItem.vue'
import pLimit from 'p-limit'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import AppManagement from '../mixins/AppManagement'
import { useAppApiStore } from '../store/app-api-store'

export default {
name: 'AppList',
Expand All @@ -153,8 +152,6 @@ export default {
NcButton,
},

mixins: [AppManagement],

props: {
category: {
type: String,
Expand All @@ -163,9 +160,9 @@ export default {
},

setup() {
const store = useAppsStore()
const appApiStore = useAppApiStore()
return {
store,
appApiStore,
}
},

Expand All @@ -179,7 +176,10 @@ export default {
return this.apps.filter(app => app.update).length
},
loading() {
return this.$store.getters.loading('list')
if (!this.$store.getters['appApiApps/isAppApiEnabled']) {
return this.$store.getters.loading('list')
}
return this.$store.getters.loading('list') || this.appApiStore.getLoading('list')
},
hasPendingUpdate() {
return this.apps.filter(app => app.update).length > 0
Expand All @@ -188,7 +188,9 @@ export default {
return this.hasPendingUpdate && this.useListView
},
apps() {
const apps = this.$store.getters.getAllApps
// Exclude ExApps from the list if AppAPI is disabled
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) {
const sortStringA = '' + (a.active ? 0 : 1) + (a.update ? 0 : 1) + a.name
Expand Down
37 changes: 37 additions & 0 deletions apps/settings/src/components/AppList/AppDaemonBadge.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!--
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<span v-if="daemon"
class="app-daemon-badge"
:title="daemon.name">
<NcIconSvgWrapper :path="mdiFileChart" :size="20" inline />
{{ daemon.display_name }}
</span>
</template>

<script setup lang="ts">
import type { IDeployDaemon } from '../../app-types.ts'
import { mdiFileChart } from '@mdi/js'
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'

defineProps<{
daemon?: IDeployDaemon
}>()
</script>

<style scoped lang="scss">
.app-daemon-badge {
color: var(--color-text-maxcontrast);
background-color: transparent;
border: 1px solid var(--color-text-maxcontrast);
border-radius: var(--border-radius);

display: flex;
flex-direction: row;
gap: 6px;
padding: 3px 6px;
width: fit-content;
}
</style>
36 changes: 27 additions & 9 deletions apps/settings/src/components/AppList/AppItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
<component :is="dataItemTag"
class="app-image app-image-icon"
:headers="getDataItemHeaders(`app-table-col-icon`)">
<div v-if="(listView && !app.preview) || (!listView && !screenshotLoaded)" class="icon-settings-dark" />
<div v-if="!app?.app_api && shouldDisplayDefaultIcon" class="icon-settings-dark" />
<NcIconSvgWrapper v-else-if="app.app_api && shouldDisplayDefaultIcon"
:path="mdiCogOutline"
:size="listView ? 24 : 48"
style="min-width: auto; min-height: auto; height: 100%;" />

<svg v-else-if="listView && app.preview"
<svg v-else-if="listView && app.preview && !app.app_api"
width="32"
height="32"
viewBox="0 0 32 32">
Expand Down Expand Up @@ -71,10 +75,11 @@
<div v-if="app.error" class="warning">
{{ app.error }}
</div>
<div v-if="isLoading" class="icon icon-loading-small" />
<div v-if="isLoading || isInitializing" class="icon icon-loading-small" />
<NcButton v-if="app.update"
type="primary"
:disabled="installing || isLoading"
:disabled="installing || isLoading || !defaultDeployDaemonAccessible || isManualInstall"
:title="updateButtonText"
@click.stop="update(app.id)">
{{ t('settings', 'Update to {update}', {update:app.update}) }}
</NcButton>
Expand All @@ -86,23 +91,23 @@
{{ t('settings', 'Remove') }}
</NcButton>
<NcButton v-if="app.active"
:disabled="installing || isLoading"
:disabled="installing || isLoading || isInitializing || isDeploying"
@click.stop="disable(app.id)">
{{ t('settings','Disable') }}
{{ disableButtonText }}
</NcButton>
<NcButton v-if="!app.active && (app.canInstall || app.isCompatible)"
:title="enableButtonTooltip"
:aria-label="enableButtonTooltip"
type="primary"
:disabled="!app.canInstall || installing || isLoading"
:disabled="!app.canInstall || installing || isLoading || !defaultDeployDaemonAccessible || isInitializing || isDeploying"
@click.stop="enable(app.id)">
{{ enableButtonText }}
</NcButton>
<NcButton v-else-if="!app.active"
:title="forceEnableButtonTooltip"
:aria-label="forceEnableButtonTooltip"
type="secondary"
:disabled="installing || isLoading"
:disabled="installing || isLoading || !defaultDeployDaemonAccessible"
@click.stop="forceEnable(app.id)">
{{ forceEnableButtonText }}
</NcButton>
Expand All @@ -118,13 +123,17 @@ import AppLevelBadge from './AppLevelBadge.vue'
import AppManagement from '../../mixins/AppManagement.js'
import SvgFilterMixin from '../SvgFilterMixin.vue'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
import { mdiCogOutline } from '@mdi/js'
import { useAppApiStore } from '../../store/app-api-store.ts'

export default {
name: 'AppItem',
components: {
AppLevelBadge,
AppScore,
NcButton,
NcIconSvgWrapper,
},
mixins: [AppManagement, SvgFilterMixin],
props: {
Expand Down Expand Up @@ -155,7 +164,13 @@ export default {
},
setup() {
const store = useAppsStore()
return { store }
const appApiStore = useAppApiStore()

return {
store,
appApiStore,
mdiCogOutline,
}
},
data() {
return {
Expand All @@ -174,6 +189,9 @@ export default {
withSidebar() {
return !!this.$route.params.id
},
shouldDisplayDefaultIcon() {
return (this.listView && !this.app.preview) || (!this.listView && !this.screenshotLoaded)
},
},
watch: {
'$route.params.id'(id) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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/dist/Components/NcAppSidebarTab.js'
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'

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>
Loading

0 comments on commit d266779

Please sign in to comment.