do not save immediately when image is part of a form

This commit is contained in:
Girish Ramakrishnan
2025-09-17 11:08:27 +02:00
parent 1cac2f6170
commit 51d0658bdb
2 changed files with 29 additions and 21 deletions
+1
View File
@@ -35,6 +35,7 @@ defineExpose({ reset });
watchEffect(() => {
internalSrc.value = props.src;
isChanged.value = false;
});
function dataURLtoFile(dataURL, filename) {
+28 -21
View File
@@ -20,8 +20,13 @@ const label = ref('');
const tags = ref([]);
const iconUrl = ref('');
let iconFile = 'src';
function onIconChanged(file) {
iconFile = file;
}
const haveValuesChanged = computed(() => {
return (label.value !== props.app.label) || tags.value.join() !== props.app.tags.join();
return (label.value !== props.app.label) || (tags.value.join() !== props.app.tags.join()) || (iconFile !== 'src');
});
async function onSubmit() {
@@ -30,6 +35,25 @@ async function onSubmit() {
tagsError.value = '';
iconError.value = '';
if (iconFile !== 'src') {
if (iconFile === 'fallback') { // user reset the icon
const [error] = await appsModel.configure(props.app.id, 'icon', { icon: '' });
if (error) {
iconError.value = error.body ? error.body.message : 'Internal error';
return error;
}
} else { // user loaded custom icon
const tmp = (await getDataURLFromFile(iconFile)).replace(/^data:image\/[a-z]+;base64,/, '');
const [error] = await appsModel.configure(props.app.id, 'icon', { icon: tmp });
if (error) {
iconError.value = error.body ? error.body.message : 'Internal error';
return error;
}
}
iconUrl.value = `${API_ORIGIN}/api/v1/apps/${props.app.id}/icon?ts=${Date.now()}`; // will also reset the ImagePicker
iconFile = 'src';
}
if (label.value !== props.app.label) {
const [error] = await appsModel.configure(props.app.id, 'label', { label: label.value });
if (error) {
@@ -51,28 +75,11 @@ async function onSubmit() {
busy.value = false;
}
async function onIconSubmit(icon) {
const tmp = (await getDataURLFromFile(icon)).replace(/^data:image\/[a-z]+;base64,/, '');
const [error] = await appsModel.configure(props.app.id, 'icon', { icon: tmp });
if (error) {
iconError.value = error.body ? error.body.message : 'Internal error';
return error;
}
}
async function onIconUnset() {
const [error] = await appsModel.configure(props.app.id, 'icon', { icon: '' });
if (error) {
iconError.value = error.body ? error.body.message : 'Internal error';
return error;
}
iconUrl.value = props.app.iconUrl ? `${API_ORIGIN}/api/v1/apps/${props.app.id}/icon?ts=${Date.now()}` : `${API_ORIGIN}/img/appicon_fallback.png`; // calculate full icon url with cache busting
}
onMounted(() => {
label.value = props.app.label;
tags.value = props.app.tags;
// iconUrl is the appstore icon or user icon. there is no way to differentiate. when null (custom apps), neither is set.
// the delete button in the icon can be used to unset user icon and use the appstore icon. unfortunately, it will also appear for the appstore icon
iconUrl.value = props.app.iconUrl;
});
@@ -82,7 +89,7 @@ onMounted(() => {
<div>
<div style="display: inline-block;">
<label>{{ $t('app.display.icon') }}</label>
<ImagePicker ref="imagePicker" mode="editable" :src="iconUrl" fallback-src="/img/appicon_fallback.png" :save-handler="onIconSubmit" :unset-handler="onIconUnset" :size="512" display-height="128px"/>
<ImagePicker ref="imagePicker" mode="simple" :src="iconUrl" fallback-src="/img/appicon_fallback.png" @changed="onIconChanged" size="512" display-height="96px"/>
</div>
<form @submit.prevent="onSubmit()" autocomplete="off">