Bring back the postinstall dialog

This commit is contained in:
Johannes Zellner
2025-04-23 15:32:42 +02:00
parent 5cdbfc0af7
commit 775a066a9a
5 changed files with 115 additions and 9 deletions
@@ -99,10 +99,11 @@ async function submit() {
if (manifest.value.id === PROXY_APP_ID) config.upstreamUri = upstreamUri.value;
const [error] = await appsModel.install(manifest.value, config);
const [error, result] = await appsModel.install(manifest.value, config);
if (!error) {
dialog.value.close();
localStorage['confirmPostInstall_' + result.id] = true;
return window.location.href = '#/apps';
}
@@ -0,0 +1,99 @@
<script setup>
import { ref, useTemplateRef } from 'vue';
import { marked } from 'marked';
import { Dialog } from 'pankow';
const dialog = useTemplateRef('dialog');
const app = ref(null);
const hasPendingCheclistItems = ref(false);
const acknowledge = ref(false);
function onConfirm() {
delete localStorage['confirmPostInstall_' + app.value.id];
dialog.value.close();
window.open('https://' + app.value.fqdn);
}
defineExpose({
async open(a, ack = false) {
app.value = a;
acknowledge.value = ack;
hasPendingCheclistItems.value = !Object.keys(a.checklist).find((i) => a.checklist[i].acknowledged);
dialog.value.open();
}
});
</script>
<template>
<Dialog ref="dialog"
:confirm-label="acknowledge ? $t('app.appInfo.openAction', { app: app.manifest.title }) : null"
:reject-label="$t('main.dialog.close')"
:reject-style="acknowledge ? 'secondary' : ''"
@confirm="onConfirm"
>
<div v-if="app">
<div class="app-info-header">
<img :src="app.iconUrl" onerror="this.onerror=null;this.src='img/appicon_fallback.png'" class="app-info-icon"/>
<div class="app-info-title">
{{ app.manifest.title }}<br/>
<span class="text-muted text-small">{{ $t('app.appInfo.package') }} <a :href="`/#/appstore/${app.manifest.id}?version=${app.manifest.version}`">v{{ app.manifest.version }}</a> </span>
<br/>
<span v-if="app.manifest.documentationUrl" class="text-small"><a target="_blank" :href="app.manifest.documentationUrl">{{ $t('app.docsAction') }}</a> </span>
<br/>
</div>
</div>
<div v-html="marked.parse(app.manifest.postInstallMessage)"></div>
<div v-if="app.manifest.documentationUrl" v-html="$t('app.appInfo.appDocsUrl', { docsUrl: app.manifest.documentationUrl, title: app.manifest.title, forumUrl: (app.manifest.forumUrl || 'https://forum.cloudron.io') })"></div>
<div class="app-info-checklist" v-show="hasPendingCheclistItems">
<label class="control-label">{{ $t('app.appInfo.checklist') }}</label>
<div v-for="(item, key) in app.checklist" :key="key">
<div class="checklist-item" v-show="!item.acknowledged">
<span v-html="marked.parse(item.message)"></span>
</div>
</div>
</div>
</div>
</Dialog>
</template>
<style scoped>
.app-info-header {
display: flex;
margin-bottom: 20px;
}
.app-info-title {
margin-left: 15px;
}
.app-info-icon {
width: 64px;
height: 64px;
}
.app-info-checklist {
margin-top: 20px;
margin-bottom: 5px;
}
.checklist-item {
padding: 8px;
border: none;
border-left: 2px solid rgb(255, 76, 76);
background-color: #ff000014;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
}
.checklist-item > span > * {
margin: 0;
}
</style>
+1 -1
View File
@@ -184,7 +184,7 @@ function create() {
if (result.status !== 202) return [result];
return [null];
return [null, result.body];
},
async list() {
let error, result;
+6 -2
View File
@@ -4,8 +4,9 @@ import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;
import { ref, onMounted, onBeforeUnmount, computed } from 'vue';
import { ref, onMounted, onBeforeUnmount, computed, useTemplateRef } from 'vue';
import { Button, ButtonGroup } from 'pankow';
import PostInstallDialog from '../components/PostInstallDialog.vue';
import Access from '../components/app/Access.vue';
import Backups from '../components/app/Backups.vue';
import Cron from '../components/app/Cron.vue';
@@ -41,6 +42,7 @@ const hasLocalStorage = ref(false);
const hasOptionalServices = ref(false);
const hasEmail = ref(false);
const busyStopTask = ref(false);
const postInstallDialog = useTemplateRef('postInstallDialog');
const isAppStopped = computed(() => {
return appsModel.isStopped(app.value);
@@ -91,7 +93,7 @@ async function refresh() {
if (result.manifest?.postInstallMessage) {
infoMenu.value.push({
label: t('app.firstTimeSetupAction'),
// TODO action
action: () => postInstallDialog.value.open(app.value),
});
}
@@ -165,6 +167,8 @@ onBeforeUnmount(() => {
<template>
<div class="configure-outer">
<PostInstallDialog ref="postInstallDialog"/>
<div class="configure-inner" v-if="!busy">
<div class="titlebar">
<div style="display: flex; flex-grow: 1;">
+7 -5
View File
@@ -9,6 +9,7 @@ import DomainsModel from '../models/DomainsModel.js';
import ProfileModel from '../models/ProfileModel.js';
import UpdaterModel from '../models/UpdaterModel.js';
import ApplinkDialog from '../components/ApplinkDialog.vue';
import PostInstallDialog from '../components/PostInstallDialog.vue';
const appsModel = AppsModel.create();
const domainsModel = DomainsModel.create();
@@ -126,6 +127,7 @@ const appProgressMessage = AppsModel.appProgressMessage;
const updateInfo = ref({});
const applinkDialog = useTemplateRef('applinkDialog');
const postInstallDialog = useTemplateRef('postInstallDialog');
// hook for applinks otherwise it is a link
function openAppEdit(app, event) {
@@ -157,11 +159,10 @@ function onOpenApp(app, event) {
return stopEvent();
}
// TODO
// if (app.pendingPostInstallConfirmation && $scope.appPostInstallConfirm) {
// $scope.appPostInstallConfirm.show(app);
// return stopEvent();
// }
if (localStorage['confirmPostInstall_' + app.id]) {
postInstallDialog.value.open(app, true);
return stopEvent();
}
}
function isOperator(app) {
@@ -242,6 +243,7 @@ onUnmounted(() => {
<template>
<div class="content">
<ApplinkDialog ref="applinkDialog" @success="refreshApps()"/>
<PostInstallDialog ref="postInstallDialog"/>
<h1 class="view-header">
{{ $t('apps.title') }}