Support for running proof check on client side

This commit is contained in:
N-Pex 2025-09-05 13:48:52 +02:00
parent 66eef037e0
commit 46479d4c37
6 changed files with 126 additions and 29 deletions

View file

@ -0,0 +1,61 @@
<template>
<div class="attachment-info">
<div v-if="loadingProof">
<div style="font-size: 0.7em; opacity: 0.7">
<v-progress-circular indeterminate class="mb-0"></v-progress-circular>
</div>
</div>
<div v-else>
<C2PAInfo class="attachment-info__detail-box" v-if="hasC2PA" :flags="attachment?.proofHintFlags" />
<EXIFInfo class="attachment-info__detail-box" v-if="hasExif" :exif="attachment?.proof?.integrity?.exif" />
</div>
</div>
</template>
<script setup lang="ts">
import C2PAInfo from "../content-credentials/C2PAInfo.vue";
import EXIFInfo from "../content-credentials/EXIFInfo.vue";
import { computed, onMounted, Ref, ref } from "vue";
import { EventAttachment } from "../../models/eventAttachment";
import proofmode from "../../plugins/proofmode";
import { extractProofHintFlags } from "../../models/proof";
const { attachment } = defineProps<{
attachment?: EventAttachment;
}>();
const loadingProof: Ref<boolean> = ref(false);
onMounted(() => {
if (attachment?.proofHintFlags && attachment.proof === undefined) {
const a = attachment;
loadingProof.value = true;
a.loadSrc()
.then((data) => {
if (data && data.data) {
return proofmode.proofCheckSource(data.data).then((res) => {
a.proof = res;
a.proofHintFlags = extractProofHintFlags(a.proof);
});
}
})
.catch(() => {})
.finally(() => {
loadingProof.value = false;
});
}
});
const hasC2PA = computed(() => {
return attachment?.proof?.integrity?.c2pa !== undefined;
});
const hasExif = computed(() => {
return attachment?.proof?.integrity?.exif !== undefined;
});
</script>
<style lang="scss">
@use "@/assets/css/chat.scss" as *;
@use "@/assets/css/sendattachments.scss" as *;
</style>

View file

@ -5,62 +5,76 @@
<v-icon @click.stop="$emit('close')" color="white" class="clickable">arrow_back</v-icon>
<div class="room-name no-upper">{{ displayDate }}</div>
<div>
<v-icon v-if="showInfoButton" color="white" class="clickable">info_outline</v-icon>
<v-icon @click.stop="showInfo = true" v-if="showInfoButton" color="white" class="clickable"
>info_outline</v-icon
>
<v-icon @click.stop="showMoreMenu = true" color="white" class="clickable">more_vert</v-icon>
</div>
</v-container>
</div>
<div class="gallery-current-item">
<ThumbnailView :item="items[currentItemIndex]" />
<ThumbnailView :item="currentAttachment" />
<div class="download-button clickable" @click.stop="downloadOne">
<v-icon color="black">arrow_downward</v-icon>
</div>
</div>
<div class="gallery-thumbnail-container">
<div
:class="{ 'file-drop-thumbnail': true, clickable: true, current: id == currentItemIndex }"
@click="currentItemIndex = id"
v-for="(currentImageInput, id) in items"
:key="id"
:class="{ 'file-drop-thumbnail': true, clickable: true, current: currentAttachment == attachment }"
@click="currentAttachment = attachment"
v-for="(attachment, index) in items"
:key="index"
>
<v-img
v-if="currentImageInput"
:src="currentImageInput.thumbnail ? currentImageInput.thumbnail : currentImageInput.src"
v-if="attachment"
:src="attachment.thumbnail ? attachment.thumbnail : attachment.src"
/>
</div>
</div>
<!-- MORE MENU POPUP -->
<MoreMenuPopup :show="showMoreMenu" :menuItems="moreMenuItems" :showProfile="false" @close="showMoreMenu = false" />
<v-bottom-sheet v-model="showInfo" theme="dark" height="80%">
<v-card class="text-center send-attachments-info-popup">
<v-card-title class="d-flex flex-column pa-0">
<div class="align-self-end done-button clickable" @click="showInfo = false">{{ $t("menu.done") }}</div>
<v-divider />
</v-card-title>
<v-card-title class="d-flex"> </v-card-title>
<v-card-text>
<EventAttachmentInfo :attachment="currentAttachment" />
</v-card-text>
</v-card>
</v-bottom-sheet>
</div>
</template>
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import MoreMenuPopup from "../MoreMenuPopup.vue";
import util, { CLIENT_EVENT_PROOF_HINT } from "../../plugins/utils";
import util from "../../plugins/utils";
import ThumbnailView from "./ThumbnailView.vue";
import { EventAttachment, KeanuEvent } from "../../models/eventAttachment";
import { computed, inject, onBeforeUnmount, onMounted, Ref, ref, watch } from "vue";
import EventAttachmentInfo from "./EventAttachmentInfo.vue";
const { t } = useI18n();
const $matrix: any = inject("globalMatrix");
const props = defineProps<{ originalEvent: KeanuEvent, items: EventAttachment[]; initialItem: EventAttachment | undefined }>();
const props = defineProps<{
originalEvent: KeanuEvent;
items: EventAttachment[];
initialItem: EventAttachment | undefined;
}>();
const currentItemIndex: Ref<number> = ref(0);
const currentAttachment: Ref<EventAttachment | undefined> = ref(undefined);
const showMoreMenu: Ref<boolean> = ref(false);
const showInfo: Ref<boolean> = ref(false);
onMounted(() => {
document.body.classList.add("dark");
if (props.initialItem) {
currentItemIndex.value = props.items.findIndex((v) => v === props.initialItem);
if (currentItemIndex.value < 0) {
currentItemIndex.value = 0;
}
}
currentAttachment.value = props.initialItem ? props.initialItem : props.items[0];
});
onBeforeUnmount(() => {
@ -72,11 +86,7 @@ const displayDate = computed(() => {
});
const showInfoButton = computed(() => {
const item = currentItemIndex.value >= 0 && currentItemIndex.value < props.items.length ? props.items[currentItemIndex.value] : undefined;
if (item) {
return item.event.getContent()[CLIENT_EVENT_PROOF_HINT] !== undefined;
}
return false;
return currentAttachment.value?.proofHintFlags !== undefined;
});
const moreMenuItems = computed(() => {
@ -94,15 +104,15 @@ const moreMenuItems = computed(() => {
watch(props.items, (newValue: EventAttachment[], oldValue: EventAttachment[]) => {
// Added or removed?
if (newValue && oldValue && newValue.length > oldValue.length) {
currentItemIndex.value = oldValue.length;
currentAttachment.value = newValue[oldValue.length];
} else if (newValue) {
currentItemIndex.value = newValue.length - 1;
currentAttachment.value = newValue[newValue.length - 1];
}
});
const downloadOne = () => {
if (currentItemIndex.value >= 0 && currentItemIndex.value < props.items.length) {
util.download($matrix.matrixClient, $matrix.useAuthedMedia, props.items[currentItemIndex.value].event);
if (currentAttachment.value) {
util.download($matrix.matrixClient, $matrix.useAuthedMedia, currentAttachment.value.event);
}
};