diff --git a/src/assets/icons/ic_arrow_narrow.vue b/src/assets/icons/ic_arrow_narrow.vue new file mode 100644 index 0000000..99bdca5 --- /dev/null +++ b/src/assets/icons/ic_arrow_narrow.vue @@ -0,0 +1,5 @@ + diff --git a/src/assets/translations/en.json b/src/assets/translations/en.json index c0cde6d..ab3763e 100644 --- a/src/assets/translations/en.json +++ b/src/assets/translations/en.json @@ -21,7 +21,8 @@ "close": "close", "notify": "Notify", "different_browser_title": "Try different browser", - "different_browser_content": "Some features may break. Copy and open link in a different browser." + "different_browser_content": "Some features may break. Copy and open link in a different browser.", + "retry": "Retry" }, "menu": { "start_private_chat": "Direct Message with this user", diff --git a/src/components/file_mode/SendAttachmentsLayout.vue b/src/components/file_mode/SendAttachmentsLayout.vue index 668545c..db446fd 100644 --- a/src/components/file_mode/SendAttachmentsLayout.vue +++ b/src/components/file_mode/SendAttachmentsLayout.vue @@ -22,7 +22,7 @@ @dragleave.prevent="dropTarget = false" @dragenter.prevent="dropTarget = true" > - +
diff --git a/src/components/file_mode/ThumbnailView.vue b/src/components/file_mode/ThumbnailView.vue index f6e83f2..93d03c6 100644 --- a/src/components/file_mode/ThumbnailView.vue +++ b/src/components/file_mode/ThumbnailView.vue @@ -29,48 +29,58 @@ import { EventAttachment } from "../../models/eventAttachment"; import { useThumbnail } from "../messages/composition/useThumbnail"; import { Attachment } from "../../models/attachment"; import ImageWithProgress from "../ImageWithProgress.vue"; + +function isEventAttachment(source: EventAttachment | Attachment | undefined): source is EventAttachment { + return (source as EventAttachment).event !== undefined; +} + +function isAttachment(source: EventAttachment | Attachment | undefined): source is Attachment { + return (source as Attachment).file !== undefined; +} const $$sanitize: any = inject("globalSanitize"); const thumbnailRef = useTemplateRef("thumbnailRef"); interface ThumbnailProps { - item?: EventAttachment; - file?: Attachment; + item?: EventAttachment | Attachment; previewOnly?: boolean; } type ThumbnailEmits = { - (event: "itemclick", value: { item: EventAttachment }): void; + (event: "itemclick", value: { item: EventAttachment | Attachment }): void; }; const props = defineProps(); -const { item, file, previewOnly = false } = props; +const { item, previewOnly = false } = props; const emits = defineEmits(); -let { isVideo, isImage, fileTypeIcon, fileTypeIconClass, fileName, fileSize } = useThumbnail(file?.file ?? item?.event); +let { isVideo, isImage, fileTypeIcon, fileTypeIconClass, fileName, fileSize } = useThumbnail(isEventAttachment(props.item) ? props.item.event : isAttachment(props.item) ? props.item.file : undefined); + const fileURL: Ref = ref(undefined); const source: Ref = ref(undefined); const poster: Ref = ref(undefined); const updateSource = () => { - if (props.item) { - if (isVideo.value || props.item.src) { - source.value = props.item.src; + if (isEventAttachment(props.item)) { + const eventAttachment = props.item; + if (isVideo.value || eventAttachment.src) { + source.value = eventAttachment.src; } else if (previewOnly) { - props.item.loadThumbnail().then((url) => { + eventAttachment.loadThumbnail().then((url) => { source.value = url.data; }) } else if (isImage.value) { - props.item.loadSrc().then((url) => { + eventAttachment.loadSrc().then((url) => { source.value = url.data; }) } - } else if (props.file) { + } else if (isAttachment(props.item)) { + const attachment = props.item; if (fileURL.value) { URL.revokeObjectURL(fileURL.value); } - fileURL.value = URL.createObjectURL(props.file.file); + fileURL.value = URL.createObjectURL(attachment.file); source.value = fileURL.value; } else { source.value = undefined; @@ -78,11 +88,12 @@ const updateSource = () => { }; const updatePoster = () => { - if (props.item && isVideo.value) { - if (props.item.thumbnail) { - poster.value = props.item.thumbnail; + if (isEventAttachment(props.item) && isVideo.value) { + const eventAttachment = props.item; + if (eventAttachment.thumbnail) { + poster.value = eventAttachment.thumbnail; } else { - props.item + eventAttachment .loadThumbnail() .then((url) => { poster.value = url.data; @@ -99,7 +110,7 @@ updatePoster(); watch(props, (props: ThumbnailProps) => { - const updates = useThumbnail(props.file?.file ?? props.item?.event); + const updates = useThumbnail(isEventAttachment(props.item) ? props.item.event : isAttachment(props.item) ? props.item.file : undefined); isVideo.value = updates.isVideo.value; isImage.value = updates.isImage.value; fileTypeIcon = updates.fileTypeIcon; @@ -111,23 +122,24 @@ watch(props, (props: ThumbnailProps) => { }); const loadingProgress = computed(() => { - if (item) { - return previewOnly ? item.thumbnailProgress : item.srcProgress; + if (isEventAttachment(item)) { + const eventAttachment = item; + return previewOnly ? eventAttachment.thumbnailProgress : eventAttachment.srcProgress; } return -1; }); onMounted(() => { - if (thumbnailRef.value && (item as EventAttachment)) { + if (thumbnailRef.value && item) { const hammerInstance = singleOrDoubleTapRecognizer(thumbnailRef.value); hammerInstance.on("singletap doubletap", (ev: any) => { if (ev.type === "singletap") { - emits("itemclick", { item: item as EventAttachment }); + emits("itemclick", { item: item }); } }); } - if (!previewOnly && item && (item as EventAttachment)) { - (item as EventAttachment).loadSrc(); + if (!previewOnly && item && isEventAttachment(item)) { + item.loadSrc(); } }); diff --git a/src/components/messages/composition/MessageThreadSending.vue b/src/components/messages/composition/MessageThreadSending.vue index c73154c..39463b1 100644 --- a/src/components/messages/composition/MessageThreadSending.vue +++ b/src/components/messages/composition/MessageThreadSending.vue @@ -1,5 +1,5 @@