More work on CC display

This commit is contained in:
N-Pex 2025-09-09 11:34:48 +02:00
parent 27b27876c0
commit a27864e3d2
7 changed files with 45 additions and 20 deletions

View file

@ -500,8 +500,6 @@
"metadata_info_compressed": "Compressing the image automatically excludes its metadata.",
"metadata_info_original": "Sharing the original automatically includes its metadata.",
"exif_data": "Exif Data",
"content_credentials": "Content Credentials",
"content_credentials_info": "Source or history information is available for this media to be verified.",
"learn_more": "Learn more",
"ai_used": "Photo modified with AI",
"screenshot": "Screenshot. ",
@ -517,6 +515,8 @@
"cc_location": "Location"
},
"cc": {
"content_credentials": "Content Credentials",
"content_credentials_info": "Source or history information is available for this media to be verified.",
"metadata-stripped": "Image has been compressed and stripped of metadata.\n\nWe think this image has additional file information. If you want to learn more, ask the sender to share the original."
}
}

View file

@ -1,11 +1,11 @@
<template>
<div v-if="props.flags">
<div class="detail-title">
{{ t("file_mode.content_credentials") }}
{{ t("cc.content_credentials") }}
<v-icon>$vuetify.icons.ic_cr</v-icon>
</div>
<div class="detail-subtitle">
{{ t("file_mode.content_credentials_info") }}
<div class="detail-subtitle" v-if="hasC2PA">
{{ t("cc.content_credentials_info") }}
</div>
<div class="cc-detail-info" v-if="infoText !== undefined">
@ -29,8 +29,8 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import { ProofHintFlags } from "../../models/proof";
import { ref, Ref, watch } from "vue";
import { Proof, ProofHintFlags } from "../../models/proof";
import { computed, ref, Ref, watch } from "vue";
import dayjs from "dayjs";
import CCProperty from "./CCProperty.vue";
import relativeTime from "dayjs/plugin/relativeTime";
@ -40,6 +40,7 @@ dayjs.extend(relativeTime);
const { t } = useI18n();
const props = defineProps<{
proof?: Proof;
flags?: ProofHintFlags;
}>();
@ -47,6 +48,10 @@ const infoText: Ref<string | undefined> = ref(undefined);
const creationDate: Ref<string | undefined> = ref(undefined);
const valid: Ref<boolean> = ref(false);
const hasC2PA = computed(() => {
return props.proof?.integrity?.c2pa !== undefined;
});
watch(
props,
() => {

View file

@ -1,5 +1,5 @@
<template>
<div class="cc-summary" v-if="props.flags.length > 0 && infoText.length > 0">
<div class="cc-summary" v-if="flags.length > 0 && infoText.length > 0">
<v-icon class="intervention-icon">{{
showCheck ? "$vuetify.icons.ic_intervention_check" : "$vuetify.icons.ic_intervention"
}}</v-icon
@ -11,18 +11,30 @@
import { computed } from "vue";
import { ProofHintFlags } from "../../models/proof";
const props = defineProps<{
const { multiple, flags } = defineProps<{
multiple: boolean;
flags: ProofHintFlags[];
}>();
const showCheck = computed(() => {
return props.flags.some((f) => f?.valid);
if (!multiple && flags.length == 1) {
return flags[0].generatorSource === "c2pa";
} else if (multiple) {
return flags.some((f) => f.generatorSource === "c2pa")
}
return false;
});
const infoText = computed(() => {
if (props.flags.some((f) => f.generator === "ai")) {
if (!multiple && flags.length == 1) {
if (flags[0].generator === "ai") {
return "<b>This image is generated by AI.</b> Take a closer look at the file details.";
} else if (flags[0].generator === "screenshot") {
return "<b>This is a screenshot.</b> Take a closer look at the file details.";
}
} else if (flags.some((f) => f.generator === "ai")) {
return "<b>Contains AI generated media.</b> Take a closer look at the file details for each.";
} else if (props.flags.some((f) => f.generator === "screenshot")) {
} else if (flags.some((f) => f.generator === "screenshot")) {
return "<b>Contains screenshots.</b> Take a closer look at the file details for each.";
}
return "TODO - Content Credentials Info";

View file

@ -28,7 +28,7 @@
</div>
</div>
<C2PAInfo class="attachment-info__detail-box" v-if="hasC2PA" :flags="attachment.proofHintFlags" />
<C2PAInfo class="attachment-info__detail-box" v-if="showC2PAInfo" :proof="attachment.proof" :flags="attachment.proofHintFlags" />
<EXIFInfo class="attachment-info__detail-box" v-if="hasExif" :exif="attachment.proof?.integrity?.exif" />
</div>
</template>
@ -49,8 +49,8 @@ const { attachment } = defineProps<{
//console.error("ATTACHMENT", attachment.proof);
const hasC2PA = computed(() => {
return attachment.proof?.integrity?.c2pa !== undefined;
const showC2PAInfo = computed(() => {
return attachment.proof?.integrity?.c2pa !== undefined || attachment.proofHintFlags !== undefined;
});
const hasExif = computed(() => {

View file

@ -5,9 +5,9 @@
<v-progress-circular indeterminate class="mb-0"></v-progress-circular>
</div>
</div>
<div v-else-if="metaStripped" class="cc-detail-info white-space-pre">{{ t("cc.metadata-stripped") }}</div>
<div v-else>
<C2PAInfo class="attachment-info__detail-box" v-if="hasC2PA" :flags="attachment?.proofHintFlags" />
<div v-if="metaStripped" class="cc-detail-info white-space-pre">{{ t("cc.metadata-stripped") }}</div>
<C2PAInfo class="attachment-info__detail-box" :proof="attachment?.proof" :flags="attachment?.proofHintFlags" />
<EXIFInfo class="attachment-info__detail-box" v-if="hasExif" :exif="attachment?.proof?.integrity?.exif" />
</div>
</div>

View file

@ -1,7 +1,7 @@
<template>
<component :is="rootComponent" ref="root" v-bind="{ ...$props, ...$attrs }" v-if="showMultiview">
<div class="bubble">
<div class="bubble-inset" v-if="showCCSummary"><CCSummary :flags="proofHintFlags" /></div>
<div class="bubble-inset" v-if="showCCSummary"><CCSummary :multiple="items.length > 1" :flags="proofHintFlags" /></div>
<div class="original-message bubble-inset" v-if="inReplyToText">
<div class="original-message-sender">{{ inReplyToSender }}</div>
<div class="original-message-text" v-html="linkify($$sanitize(inReplyToText))" />
@ -158,7 +158,7 @@ const showMultiview = computed((): boolean => {
});
const showCCSummary = computed(() => {
return items.value?.some((i) => i.proofHintFlags !== undefined && i.proofHintFlags.valid);
return items.value?.some((i) => i.proofHintFlags !== undefined);
});
const proofHintFlags = computed(() => {

View file

@ -138,7 +138,7 @@ const ruleAiGenerated = (): FlagMatchRule[] => {
];
};
const aiHintFlags = (): FlagMatchRule[] => {
const ruleAiMeta = (): FlagMatchRule[] => {
const knownAIServices = [
"ChatGPT",
"OpenAI-API",
@ -292,9 +292,17 @@ export const extractProofHintFlags = (proof?: Proof): ProofHintFlags | undefined
if (matchFlag(ruleScreenshotMeta(), proof).result) {
generator = "screenshot";
generatorSource = "metadata";
} else if (matchFlag(ruleAiMeta(), proof).result) {
generator = "ai";
generatorSource = "metadata";
}
}
// Do we have any data? Else, return "undefined", we don't just want to send an object with all defaults.
if (source.length === 0 && dateCreated.length === 0 && generator === "unknown") {
return undefined;
}
const flags: ProofHintFlags = {
valid: valid,
device: source && source.length == 1 ? source[0].value : undefined,