pdf inline improvements

This commit is contained in:
10G Meow 2025-10-26 23:03:44 +02:00
parent f05e27cde4
commit ec2308223f
5 changed files with 103 additions and 122 deletions

View file

@ -7,48 +7,21 @@
</div>
<div class="message">
<ThumbnailView class="clickable" v-on:itemclick="onItemClicked" :item="attachment" />
<ThumbnailView class="clickable" :item="attachment" />
<span class="edit-marker" v-if="event?.replacingEventId()">{{ $t("message.edited") }}</span>
</div>
</div>
<v-dialog v-model="showPdfDialog" fullscreen v-if="util.isMobileOrTabletBrowser()">
<v-card>
<v-toolbar :elevation="2" density="compact" class="position-fixed pdf-header" color="black">
<v-btn
icon="arrow_back"
@click="showPdfDialog = false"
></v-btn>
<v-toolbar-title>{{ attachment?.name }}</v-toolbar-title>
</v-toolbar>
<div class="pdf-container">
<div
v-for="(pageNum, index) in pageNums"
:key="pageNum"
:ref="(el) => { if ((el != null && el as Element)) pageRefs[index] = el as Element }"
>
<vue-pdf-embed
v-if="pageVisibility[pageNum]"
:source="doc"
:page="pageNum"
/>
</div>
</div>
</v-card>
</v-dialog>
</component>
</template>
<script setup lang="ts">
import { computed, inject, ref, Ref, nextTick, watch, onBeforeUnmount } from "vue";
import { computed, inject } from "vue";
import MessageIncoming from "./MessageIncoming.vue";
import MessageOutgoing from "./MessageOutgoing.vue";
import ThumbnailView from "../../file_mode/ThumbnailView.vue";
import { useI18n } from "vue-i18n";
import { MessageProps, useMessage } from "./useMessage";
import { KeanuEvent } from "../../../models/eventAttachment";
import VuePdfEmbed, { useVuePdfEmbed } from 'vue-pdf-embed';
import { Source } from 'vue-pdf-embed/types';
import util from "@/plugins/utils";
const { t } = useI18n();
const $matrix: any = inject("globalMatrix");
@ -68,85 +41,8 @@ const { event, isIncoming, attachment, inReplyToText, inReplyToSender, linkify }
const rootComponent = computed(() => {
return isIncoming.value ? MessageIncoming : MessageOutgoing;
});
const onItemClicked = () => {
if(util.isMobileOrTabletBrowser()) {
showPdfDialog.value = true
} else {
emits("download", event.value);
}
};
const showPdfDialog = ref(false)
const pageRefs: Ref<(Element)[]> = ref([]);
const pageVisibility: Ref<{ [n: number]: boolean }> = ref({});
let pageIntersectionObserver: IntersectionObserver | null = null;
const pdfSource: Ref<Source | null> = ref(null);
const { doc } = useVuePdfEmbed({
source: pdfSource,
});
const pageNums = computed(() =>
doc.value ? [...Array(doc.value.numPages + 1).keys()].slice(1) : []
);
const resetPageIntersectionObserver = () => {
if(!util.isMobileOrTabletBrowser()) return
pageIntersectionObserver?.disconnect()
pageIntersectionObserver = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const index = pageRefs.value.findIndex((el) => el === entry.target)
if (index !== -1) {
const pageNum = pageNums.value[index]
pageVisibility.value[pageNum] = true
}
}
})
})
pageRefs.value.forEach((element) => {
if (element && pageIntersectionObserver) pageIntersectionObserver.observe(element)
})
};
watch(pageNums, (newPageNums) => {
if(!util.isMobileOrTabletBrowser()) return
pageVisibility.value = { [newPageNums[0]]: true }
pageRefs.value = []
nextTick(resetPageIntersectionObserver)
});
watch(showPdfDialog, (pdfViewerShown) => {
if(!util.isMobileOrTabletBrowser()) return
if(attachment.value && pdfViewerShown && !pdfSource.value) {
pdfSource.value = attachment.value.src
}
});
onBeforeUnmount(() => {
pageIntersectionObserver?.disconnect()
});
</script>
<style lang="scss">
@use "@/assets/css/chat.scss" as *;
.pdf-header {
z-index: 1000;
}
.pdf-container {
padding: 24px 16px;
& > * {
margin: 30px auto 0 auto;
min-height: 400px;
}
.vue-pdf-embed {
border: 1px solid #ccc;
width: 100%;
}
}
</style>

View file

@ -161,6 +161,8 @@ const showMessageText = computed((): boolean => {
return true;
});
const isSinglePDF = computed(() => items.value.length === 1 && util.isFileTypePDF(items.value[0].event))
const showMultiview = computed((): boolean => {
if (["m.image", "m.video"].includes(event.value?.getContent().msgtype ?? "")) {
return true;
@ -169,10 +171,8 @@ const showMultiview = computed((): boolean => {
(isIncoming.value && props.room.displayType == ROOM_TYPE_FILE_MODE) ||
items.value?.length > 1 ||
(event.value && event.value.isRedacted()) ||
(props.room.displayType == ROOM_TYPE_CHANNEL &&
items.value.length == 1 &&
util.isFileTypePDF(items.value[0].event)) ||
messageText.value?.length > 0
(props.room.displayType == ROOM_TYPE_CHANNEL && isSinglePDF.value) ||
messageText.value?.length > 0 || isSinglePDF.value
);
});

View file

@ -70,5 +70,6 @@ export const useThumbnail = (source: KeanuEvent | File | undefined) => {
fileTypeIconClass,
fileName,
fileSize,
isPDF
};
};