diff --git a/app/src/interfaces/image/image.vue b/app/src/interfaces/image/image.vue index d4be98475d..8221eaf97c 100644 --- a/app/src/interfaces/image/image.vue +++ b/app/src/interfaces/image/image.vue @@ -45,7 +45,53 @@ /> - + @@ -56,6 +102,7 @@ import formatFilesize from '@/utils/format-filesize'; import i18n from '@/lang'; import FileLightbox from '@/views/private/components/file-lightbox'; import ImageEditor from '@/views/private/components/image-editor'; +import ModalBrowse from '@/views/private/components/modal-browse'; import { nanoid } from 'nanoid'; import getRootPath from '@/utils/get-root-path'; @@ -69,7 +116,7 @@ type Image = { }; export default defineComponent({ - components: { FileLightbox, ImageEditor }, + components: { FileLightbox, ImageEditor, ModalBrowse }, props: { value: { type: String, @@ -81,6 +128,7 @@ export default defineComponent({ }, }, setup(props, { emit }) { + const activeDialog = ref<'choose' | 'url' | null>(null); const loading = ref(false); const image = ref(null); const error = ref(null); @@ -132,6 +180,8 @@ export default defineComponent({ } ); + const { url, isValidURL, loading: urlLoading, error: urlError, importFromURL } = useURLImport(); + return { loading, image, @@ -144,6 +194,12 @@ export default defineComponent({ setImage, deselect, downloadSrc, + activeDialog, + setSelection, + url, + isValidURL, + urlLoading, + importFromURL, }; async function fetchImage() { @@ -182,6 +238,51 @@ export default defineComponent({ lightboxActive.value = false; editorActive.value = false; } + + function setSelection(selection: number[]) { + if (selection[0]) { + emit('input', selection[0]); + } else { + emit('input', null); + } + } + + function useURLImport() { + const url = ref(''); + const loading = ref(false); + const error = ref(null); + + const isValidURL = computed(() => { + try { + new URL(url.value); + return true; + } catch { + return false; + } + }); + + return { url, loading, error, isValidURL, importFromURL }; + + async function importFromURL() { + loading.value = true; + + try { + const response = await api.post(`/files/import`, { + url: url.value, + }); + + image.value = response.data.data; + + activeDialog.value = null; + url.value = ''; + emit('input', image.value?.id); + } catch (err) { + error.value = err; + } finally { + loading.value = false; + } + } + } }, }); @@ -301,4 +402,17 @@ img { .disabled-placeholder { height: var(--input-height-tall); } + +.options { + position: absolute; + top: 13px; + right: 13px; + color: var(--foreground-subdued); + cursor: pointer; + transition: color var(--medium) var(--transition); +} + +.image:hover .options { + color: var(--primary); +}