ImagePicker fixes and use it also in app icon settings
This commit is contained in:
@@ -2,14 +2,14 @@
|
||||
|
||||
import { ref, onMounted, useTemplateRef } from 'vue';
|
||||
import { FormGroup, TextInput, Button, TagInput } from 'pankow';
|
||||
import ImagePicker from '../ImagePicker.vue';
|
||||
import AppsModel from '../../models/AppsModel.js';
|
||||
|
||||
const props = defineProps([ 'app' ]);
|
||||
|
||||
const appsModel = AppsModel.create();
|
||||
|
||||
let iconChanged = false;
|
||||
|
||||
const imagePicker = useTemplateRef('imagePicker');
|
||||
const busy = ref(false);
|
||||
const labelError = ref('');
|
||||
const tagsError = ref('');
|
||||
@@ -17,62 +17,27 @@ const iconError = ref('');
|
||||
const label = ref('');
|
||||
const tags = ref([]);
|
||||
const iconUrl = ref('');
|
||||
const iconFileInput = useTemplateRef('iconFileInput');
|
||||
|
||||
function onResetIcon() {
|
||||
const accessToken = localStorage.token;
|
||||
let newIcon = null;
|
||||
|
||||
iconChanged = true;
|
||||
iconUrl.value = props.app.iconUrl + '&original=true&access_token=' + accessToken;
|
||||
function onIconChanged(file) {
|
||||
newIcon = file;
|
||||
}
|
||||
|
||||
function onShowIconSelector() {
|
||||
iconFileInput.value.click();
|
||||
}
|
||||
function getDataURLFromFile(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
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;
|
||||
reader.onload = function(event) {
|
||||
resolve(event.target.result);
|
||||
};
|
||||
|
||||
image.src = fr.result;
|
||||
};
|
||||
fr.readAsDataURL(event.target.files[0]);
|
||||
reader.onerror = function(event) {
|
||||
reject(event.target.error);
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
@@ -99,12 +64,12 @@ async function onSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
if (iconChanged) {
|
||||
if (newIcon) {
|
||||
let tmp;
|
||||
if (iconUrl.value.indexOf('data:image') === 0) {
|
||||
tmp = iconUrl.value.replace(/^data:image\/[a-z]+;base64,/, '');
|
||||
} else {
|
||||
if (newIcon === 'clear') {
|
||||
tmp = '';
|
||||
} else {
|
||||
tmp = (await getDataURLFromFile(newIcon)).replace(/^data:image\/[a-z]+;base64,/, '');
|
||||
}
|
||||
|
||||
const [error] = await appsModel.configure(props.app.id, 'icon', { icon: tmp });
|
||||
@@ -113,13 +78,16 @@ async function onSubmit() {
|
||||
busy.value = false;
|
||||
return console.error(error);
|
||||
}
|
||||
|
||||
iconChanged = false;
|
||||
}
|
||||
|
||||
busy.value = false;
|
||||
}
|
||||
|
||||
function onClearIcon() {
|
||||
imagePicker.value.clear(`${iconUrl.value}&original=true`);
|
||||
newIcon = 'clear';
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
label.value = props.app.label;
|
||||
tags.value = props.app.tags;
|
||||
@@ -147,14 +115,9 @@ onMounted(() => {
|
||||
</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="app-custom-icon-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"/>
|
||||
<label>{{ $t('app.display.icon') }}</label>
|
||||
<ImagePicker ref="imagePicker" :src="iconUrl" fallback-src="/img/appicon_fallback.png" @changed="onIconChanged" :size="512" display-height="128px"/>
|
||||
<div class="actionable" @click="onClearIcon()">{{ $t('app.display.iconResetAction') }}</div>
|
||||
</FormGroup>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user