feat(quest): add support for private images in answers

This commit is contained in:
2026-07-31 02:14:34 +02:00
parent de0eaee521
commit 45865d3122
9 changed files with 53 additions and 14 deletions
+2
View File
@@ -216,6 +216,8 @@
"finalize": "Antwort finalisieren", "finalize": "Antwort finalisieren",
"finalize_explain": "Dies gibt den Admins die Möglichkeit deine Antwort zu bewerten und dir Punkte zu geben. Du kannst deine Antwort anschließend nicht mehr ändern!", "finalize_explain": "Dies gibt den Admins die Möglichkeit deine Antwort zu bewerten und dir Punkte zu geben. Du kannst deine Antwort anschließend nicht mehr ändern!",
"last_update": "Letztes Update: {date} von {name}", "last_update": "Letztes Update: {date} von {name}",
"private_image": "Privates Bild",
"private_image_explain": "Private Bilder werden in der öffentlichen Galerie, Diashow und im Showcase ausgeblendet. Admins können sie weiterhin zur Bewertung sehen.",
"required_answer": "Erforderliche Antwort", "required_answer": "Erforderliche Antwort",
"review": "Bewertung", "review": "Bewertung",
"reviewed_by": "von {name} am {date}", "reviewed_by": "von {name} am {date}",
+2
View File
@@ -216,6 +216,8 @@
"finalize": "Finalize answer", "finalize": "Finalize answer",
"finalize_explain": "This allows the admins to review your answer and give you points. You cannot change your answer afterwards!", "finalize_explain": "This allows the admins to review your answer and give you points. You cannot change your answer afterwards!",
"last_update": "Last update: {date} by {name}", "last_update": "Last update: {date} by {name}",
"private_image": "Private image",
"private_image_explain": "Private images are hidden from the public gallery, diashow and showcase. Admins can still see them for review.",
"required_answer": "Required Answer", "required_answer": "Required Answer",
"review": "Review", "review": "Review",
"reviewed_by": "by {name} at {date}", "reviewed_by": "by {name} at {date}",
@@ -72,6 +72,11 @@ useHead({
const answer = ref(data.value?.answer?.text); const answer = ref(data.value?.answer?.text);
const final = ref(data.value?.answer?.final || false); const final = ref(data.value?.answer?.final || false);
const privateImage = ref(data.value?.answer?.picture?.private ?? false);
const privateChanged = computed(
() => (data.value?.answer?.picture?.private ?? false) !== privateImage.value
);
const textType = { const textType = {
TEXT: 'text', TEXT: 'text',
@@ -114,6 +119,7 @@ async function submit() {
if (final.value) { if (final.value) {
body.set('final', 'true'); body.set('final', 'true');
} }
body.set('private', privateImage.value ? 'true' : 'false');
if (newImage.value) { if (newImage.value) {
uploadingImage.value = true; uploadingImage.value = true;
compressing.value = true; compressing.value = true;
@@ -141,6 +147,7 @@ async function submit() {
await refresh(); await refresh();
answer.value = data.value?.answer?.text; answer.value = data.value?.answer?.text;
privateImage.value = data.value?.answer?.picture?.private ?? false;
newImage.value = undefined; newImage.value = undefined;
} catch (e) { } catch (e) {
if (xhr.status === 418) { if (xhr.status === 418) {
@@ -325,6 +332,13 @@ async function submit() {
type="single" type="single"
file-types="jpg,png,jpeg" file-types="jpg,png,jpeg"
/> />
<VaCheckbox
v-if="newImageURL || data.answer?.picture"
v-model="privateImage"
:disabled="uploading || data.answer?.final"
:label="t('page.quest.private_image')"
:messages="t('page.quest.private_image_explain')"
/>
<p v-if="data.answer?.updatedAt"> <p v-if="data.answer?.updatedAt">
{{ {{
t('page.quest.last_update', { t('page.quest.last_update', {
@@ -352,7 +366,8 @@ async function submit() {
!( !(
newImage || newImage ||
data.answer?.text !== answer || data.answer?.text !== answer ||
data.answer?.final !== final data.answer?.final !== final ||
privateChanged
) || uploading ) || uploading
" "
:loading="uploading" :loading="uploading"
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "public"."File" ADD COLUMN "private" BOOLEAN NOT NULL DEFAULT false;
+1
View File
@@ -45,6 +45,7 @@ model File {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
uuid String uuid String
url String url String
private Boolean @default(false)
creator User @relation(fields: [creatorId], references: [id], onUpdate: Cascade, onDelete: Cascade) creator User @relation(fields: [creatorId], references: [id], onUpdate: Cascade, onDelete: Cascade)
creatorId Int creatorId Int
+2 -2
View File
@@ -77,7 +77,7 @@ export default defineEventHandler(async (event) => {
where: { where: {
final: true, final: true,
picture: { picture: {
isNot: null private: false
} }
} }
} }
@@ -95,7 +95,7 @@ export default defineEventHandler(async (event) => {
some: { some: {
final: true, final: true,
picture: { picture: {
isNot: null private: false
} }
} }
} }
+4 -2
View File
@@ -138,7 +138,8 @@ export default defineEventHandler(async (event) => {
where: { where: {
review: { review: {
showcase: true showcase: true
} },
picture: { private: false }
}, },
orderBy: [ orderBy: [
{ {
@@ -217,6 +218,7 @@ export default defineEventHandler(async (event) => {
pictureCount, pictureCount,
totalScore, totalScore,
leaderboard, leaderboard,
...hunt ...hunt,
quests: hunt.quests.filter((q) => q.answers.length > 0)
}; };
}); });
+2 -1
View File
@@ -71,7 +71,8 @@ export default defineEventHandler(async (event) => {
text: true, text: true,
picture: { picture: {
select: { select: {
url: true url: true,
private: true
} }
}, },
review: { review: {
+17 -3
View File
@@ -101,7 +101,8 @@ export default defineEventHandler(async (event) => {
.mime(['image/png', 'image/jpeg', 'image/jpg']) .mime(['image/png', 'image/jpeg', 'image/jpg'])
.max(10_000_000) .max(10_000_000)
.optional(), .optional(),
final: zh.boolAsString.optional() final: zh.boolAsString.optional(),
private: zh.boolAsString.default(false)
}) })
.parse(formDataObj); .parse(formDataObj);
@@ -110,7 +111,8 @@ export default defineEventHandler(async (event) => {
? quest.answers![0]! ? quest.answers![0]!
: { : {
updatedAt: null, updatedAt: null,
id: null id: null,
picture: null
}; };
if ( if (
@@ -158,7 +160,8 @@ export default defineEventHandler(async (event) => {
const file = await tx.file.create({ const file = await tx.file.create({
data: { data: {
...picture, ...picture,
answerId: prevAnswer.id answerId: prevAnswer.id,
private: data.private
}, },
select: { id: true } select: { id: true }
}); });
@@ -190,6 +193,7 @@ export default defineEventHandler(async (event) => {
data: { data: {
...picture, ...picture,
answerId: answer.id, answerId: answer.id,
private: data.private,
answers: { answers: {
connect: answer connect: answer
} }
@@ -210,6 +214,16 @@ export default defineEventHandler(async (event) => {
lang: data.lang lang: data.lang
} }
}); });
if (prevAnswer.picture) {
await prisma.file.update({
where: {
id: prevAnswer.picture.id
},
data: {
private: data.private
}
});
}
} else { } else {
await prisma.questAnswer.create({ await prisma.questAnswer.create({
data: { data: {