diff --git a/dashboard/src/components/ApplinkDialog.vue b/dashboard/src/components/ApplinkDialog.vue
index a9f514e02..9e22e3997 100644
--- a/dashboard/src/components/ApplinkDialog.vue
+++ b/dashboard/src/components/ApplinkDialog.vue
@@ -7,6 +7,7 @@ const t = i18n.t;
import { computed, ref, useTemplateRef } from 'vue';
import { Dialog, FormGroup, InputDialog, MultiSelect, Radiobutton, TagInput, TextInput } from '@cloudron/pankow';
import { API_ORIGIN } from '../constants.js';
+import { getDataURLFromFile } from '../utils.js';
import ImagePicker from './ImagePicker.vue';
import ApplinksModel from '../models/ApplinksModel.js';
import UsersModel from '../models/UsersModel.js';
@@ -30,7 +31,7 @@ const id = ref('');
const upstreamUri = ref('');
const label = ref('');
const tags = ref([]);
-const iconFile = ref(null); // if set to '' we will reset
+const iconFile = ref(null);
const iconUrl = ref('');
const accessRestrictionOption = ref('');
const accessRestriction = ref({
@@ -52,30 +53,10 @@ const isValid = computed(() => {
return true;
});
-function getDataURLFromFile(file) {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
-
- reader.onload = function(event) {
- resolve(event.target.result);
- };
-
- reader.onerror = function(event) {
- reject(event.target.error);
- };
-
- reader.readAsDataURL(file);
- });
-}
-
function onIconChanged(file) {
iconFile.value = file;
}
-function onResetIcon() {
- iconFile.value = '';
-}
-
async function onSubmit() {
busy.value = true;
@@ -92,9 +73,9 @@ async function onSubmit() {
data.accessRestriction.groups = accessRestriction.value.groups.map(function (g) { return g.id; });
}
- if (iconFile.value === '') { // user reset the icon
+ if (iconFile.value === 'fallback') { // user reset the icon
data.icon = '';
- } else if (iconFile.value) { // user loaded custom icon
+ } else if (iconFile.value !== 'src') { // user loaded custom icon
data.icon = (await getDataURLFromFile(iconFile.value)).replace(/^data:image\/[a-z]+;base64,/, '');
}
@@ -110,8 +91,6 @@ async function onSubmit() {
emits('success');
applinkDialog.value.close();
- // clear this to retrigger ImagePicker loading
- iconUrl.value = '';
busy.value = false;
}
@@ -139,7 +118,7 @@ defineExpose({
upstreamUri.value = applink ? applink.upstreamUri : '';
label.value = applink ? applink.label : '';
iconUrl.value = applink ? applink.iconUrl : 'fallback';
- iconFile.value = null;
+ iconFile.value = applink?.iconUrl ? 'src' : 'fallback';
tags.value = applink ? applink.tags : [];
accessRestrictionOption.value = applink && applink.accessRestriction ? 'groups' : 'any';
accessRestriction.value = applink && applink.accessRestriction ? applink.accessRestriction : { users: [], groups: [] };
@@ -193,7 +172,7 @@ defineExpose({
-
+
diff --git a/dashboard/src/components/ImagePicker.vue b/dashboard/src/components/ImagePicker.vue
index 364f5339a..466710dec 100644
--- a/dashboard/src/components/ImagePicker.vue
+++ b/dashboard/src/components/ImagePicker.vue
@@ -28,6 +28,7 @@ const busy = ref(false);
watchEffect(() => {
internalSrc.value = props.src;
+ isChanged.value = false;
});
function dataURLtoFile(dataURL, filename) {
@@ -70,6 +71,7 @@ async function onSave() {
function onCancel() {
internalSrc.value = props.src || props.fallbackSrc;
isChanged.value = false;
+ emit('changed', 'src');
}
function onEdit() {
@@ -77,14 +79,17 @@ function onEdit() {
}
async function onUnset() {
- if (typeof props.unsetHandler !== 'function') return;
-
busy.value = true;
- const error = await props.unsetHandler();
- if (!error) isChanged.value = false;
+ if (typeof props.unsetHandler === 'function') {
+ const error = await props.unsetHandler();
+ if (!error) isChanged.value = false;
+ } else {
+ isChanged.value = false;
+ }
internalSrc.value = props.fallbackSrc;
+ emit('changed', 'fallback');
busy.value = false;
}
@@ -142,7 +147,7 @@ function onChanged(event) {
internalSrc.value = canvas.toDataURL('image/png');
isChanged.value = true;
- emit('changed', file); // <— notify parent
+ emit('changed', file);
};
image.src = fr.result;
@@ -178,6 +183,8 @@ function onError() {
+
+
diff --git a/dashboard/src/components/app/Display.vue b/dashboard/src/components/app/Display.vue
index 37040a608..540d7c7c1 100644
--- a/dashboard/src/components/app/Display.vue
+++ b/dashboard/src/components/app/Display.vue
@@ -5,6 +5,7 @@ import { FormGroup, TextInput, Button, TagInput } from '@cloudron/pankow';
import ImagePicker from '../ImagePicker.vue';
import AppsModel from '../../models/AppsModel.js';
import { API_ORIGIN } from '../../constants.js';
+import { getDataURLFromFile } from '../../utils.js';
const props = defineProps([ 'app' ]);
@@ -19,22 +20,6 @@ const label = ref('');
const tags = ref([]);
const iconUrl = ref('');
-function getDataURLFromFile(file) {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
-
- reader.onload = function(event) {
- resolve(event.target.result);
- };
-
- reader.onerror = function(event) {
- reject(event.target.error);
- };
-
- reader.readAsDataURL(file);
- });
-}
-
const haveValuesChanged = computed(() => {
return (label.value !== props.app.label) || tags.value.join() !== props.app.tags.join();
});
diff --git a/dashboard/src/utils.js b/dashboard/src/utils.js
index 84d099a01..6942cc609 100644
--- a/dashboard/src/utils.js
+++ b/dashboard/src/utils.js
@@ -589,6 +589,22 @@ function stripSsoInfo(notes, sso) {
return notes.replace(/.*?<\/sso>/gs, '').replace(//gs, '').replace(/<\/nosso>/gs, '');
}
+function getDataURLFromFile(file) {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+
+ reader.onload = function(event) {
+ resolve(event.target.result);
+ };
+
+ reader.onerror = function(event) {
+ reject(event.target.error);
+ };
+
+ reader.readAsDataURL(file);
+ });
+}
+
// named exports
export {
download,
@@ -599,6 +615,7 @@ export {
taskNameFromInstallationState,
redirectIfNeeded,
stripSsoInfo,
+ getDataURLFromFile
};
// default export
@@ -611,4 +628,5 @@ export default {
taskNameFromInstallationState,
redirectIfNeeded,
stripSsoInfo,
+ getDataURLFromFile
};