165 lines
5.1 KiB
Vue
165 lines
5.1 KiB
Vue
<script setup>
|
|
|
|
import { ref, onMounted, useTemplateRef } from 'vue';
|
|
import { FormGroup, TextInput, Button, TagInput } from 'pankow';
|
|
import AppsModel from '../../models/AppsModel.js';
|
|
|
|
const props = defineProps([ 'app' ]);
|
|
|
|
const appsModel = AppsModel.create();
|
|
|
|
let iconChanged = false;
|
|
|
|
const busy = ref(false);
|
|
const labelError = ref('');
|
|
const tagsError = ref('');
|
|
const iconError = ref('');
|
|
const label = ref('');
|
|
const tags = ref([]);
|
|
const iconUrl = ref('');
|
|
const iconFileInput = useTemplateRef('iconFileInput');
|
|
|
|
function onResetIcon() {
|
|
const accessToken = localStorage.token;
|
|
|
|
iconChanged = true;
|
|
iconUrl.value = props.app.iconUrl + '&original=true&access_token=' + accessToken;
|
|
}
|
|
|
|
function onShowIconSelector() {
|
|
iconFileInput.value.click();
|
|
}
|
|
|
|
function onIconChanged(event) {
|
|
const fr = new FileReader();
|
|
fr.onload = function () {
|
|
const image = new Image();
|
|
image.onload = function () {
|
|
const size = 512;
|
|
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = size;
|
|
canvas.height = size;
|
|
|
|
const imageDimensionRatio = image.width / image.height;
|
|
const canvasDimensionRatio = canvas.width / canvas.height;
|
|
let renderableHeight, renderableWidth, xStart, yStart;
|
|
|
|
if (imageDimensionRatio > canvasDimensionRatio) {
|
|
renderableHeight = canvas.height;
|
|
renderableWidth = image.width * (renderableHeight / image.height);
|
|
xStart = (canvas.width - renderableWidth) / 2;
|
|
yStart = 0;
|
|
} else if (imageDimensionRatio < canvasDimensionRatio) {
|
|
renderableWidth = canvas.width;
|
|
renderableHeight = image.height * (renderableWidth / image.width);
|
|
xStart = 0;
|
|
yStart = (canvas.height - renderableHeight) / 2;
|
|
} else {
|
|
renderableHeight = canvas.height;
|
|
renderableWidth = canvas.width;
|
|
xStart = 0;
|
|
yStart = 0;
|
|
}
|
|
|
|
var ctx = canvas.getContext('2d');
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
ctx.drawImage(image, xStart, yStart, renderableWidth, renderableHeight);
|
|
|
|
iconUrl.value = canvas.toDataURL('image/png');
|
|
iconChanged = true;
|
|
};
|
|
|
|
image.src = fr.result;
|
|
};
|
|
fr.readAsDataURL(event.target.files[0]);
|
|
}
|
|
|
|
async function onSubmit() {
|
|
busy.value = true;
|
|
labelError.value = '';
|
|
tagsError.value = '';
|
|
iconError.value = '';
|
|
|
|
if (label.value !== props.app.label) {
|
|
const [error] = await appsModel.configure(props.app.id, 'label', { label: label.value });
|
|
if (error) {
|
|
labelError.value = error.body ? error.body.message : 'Internal error';
|
|
busy.value = false;
|
|
return console.error(error);
|
|
}
|
|
}
|
|
|
|
if (tags.value.join() !== props.app.tags.join()) {
|
|
const [error] = await appsModel.configure(props.app.id, 'tags', { tags: tags.value });
|
|
if (error) {
|
|
tagsError.value = error.body ? error.body.message : 'Internal error';
|
|
busy.value = false;
|
|
return console.error(error);
|
|
}
|
|
}
|
|
|
|
if (iconChanged) {
|
|
let tmp;
|
|
if (iconUrl.value.indexOf('data:image') === 0) {
|
|
tmp = iconUrl.value.replace(/^data:image\/[a-z]+;base64,/, '');
|
|
} else {
|
|
tmp = '';
|
|
}
|
|
|
|
const [error] = await appsModel.configure(props.app.id, 'icon', { icon: tmp });
|
|
if (error) {
|
|
iconError.value = error.body ? error.body.message : 'Internal error';
|
|
busy.value = false;
|
|
return console.error(error);
|
|
}
|
|
|
|
iconChanged = false;
|
|
}
|
|
|
|
busy.value = false;
|
|
}
|
|
|
|
onMounted(() => {
|
|
label.value = props.app.label;
|
|
tags.value = props.app.tags;
|
|
iconUrl.value = props.app.iconUrl;
|
|
});
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<form @submit.prevent="onSubmit()" autocomplete="off">
|
|
<fieldset :disabled="busy">
|
|
<input type="submit" style="display: none;"/>
|
|
|
|
<FormGroup>
|
|
<label for="labelInput">{{ $t('app.display.label') }}</label>
|
|
<TextInput id="labelInput" v-model="label"/>
|
|
<div class="text-error" v-if="labelError">{{ labelError }}</div>
|
|
</FormGroup>
|
|
|
|
<FormGroup>
|
|
<label for="tagsInput">{{ $t('app.display.tags') }}</label>
|
|
<TagInput id="tagsInput" :placeholder="$t('app.display.tagsPlaceholder')" v-model="tags" v-tooltip="$t('app.display.tagsTooltip')"/>
|
|
<div class="text-error" v-if="tagsError">{{ tagsError }}</div>
|
|
</FormGroup>
|
|
|
|
<FormGroup>
|
|
<label for="previewIcon">{{ $t('app.display.icon') }}</label>
|
|
<div id="previewIcon" class="app-custom-icon" @click="onShowIconSelector()">
|
|
<img :src="iconUrl" onerror="this.src = '/img/appicon_fallback.png'"/>
|
|
<i class="picture-edit-indicator fa fa-pencil-alt"></i>
|
|
</div>
|
|
<div class="text-error" v-if="iconError">{{ iconError }}</div>
|
|
<div class="actionable" @click="onResetIcon()">{{ $t('app.display.iconResetAction') }}</div>
|
|
<input type="file" ref="iconFileInput" @change="onIconChanged($event)" style="display: none" accept="image/webp, image/png, image/gif, image/jpeg"/>
|
|
</FormGroup>
|
|
</fieldset>
|
|
</form>
|
|
<br/>
|
|
<Button @click="onSubmit()" :loading="busy" :disabled="busy">{{ $t('app.display.saveAction') }}</Button>
|
|
</div>
|
|
</template>
|