Enhance notification display and localization in MessagesDialog component

- Updated the MessagesDialog component to display notifications with titles and descriptions, improving clarity and user experience.
- Enhanced the formatBody method to support new notification structures, including extraction and formatting of parameters for better message presentation.
- Added a new formatParams method to handle various parameter types, ensuring accurate representation of values in notifications.
- Updated localization files in both German and English to include structured titles and descriptions for random events, enriching the user experience with detailed information.
This commit is contained in:
Torsten Schulz (local)
2025-12-08 14:42:17 +01:00
parent bcb0b01324
commit 791314bef2
3 changed files with 238 additions and 25 deletions

View File

@@ -11,7 +11,10 @@
>
<ul class="messages">
<li v-for="n in messages" :key="n.id" :class="{ unread: !n.shown }">
<div class="body">{{ formatBody(n) }}</div>
<div class="body">
<div v-if="formatBody(n).title" class="notification-title">{{ formatBody(n).title }}</div>
<div class="notification-description">{{ formatBody(n).description || formatBody(n) }}</div>
</div>
<div class="footer">
<span>{{ formatDate(n.createdAt) }}</span>
</div>
@@ -118,11 +121,21 @@ export default {
} catch { return dt; }
},
formatBody(n) {
// Wenn die Notification bereits title und description hat (z.B. von WebSocket Events)
if (n.title && n.description) {
// Parameter aus effects oder anderen Feldern extrahieren
const params = this.extractParams(n);
return {
title: this.interpolateString(n.title, params),
description: this.interpolateString(n.description, params)
};
}
let raw = n.tr || '';
let key = raw;
let params = {};
// 1) JSON-Format unterstützen: {"tr":"production.overproduction","value":21}
// 1) JSON-Format unterstützen: {"tr":"random_event.windfall","amount":1000,"characterName":"Max"}
if (typeof raw === 'string') {
const trimmed = raw.trim();
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
@@ -131,7 +144,8 @@ export default {
if (parsed && parsed.tr) {
raw = parsed.tr;
key = parsed.tr;
params = { ...parsed };
// Alle anderen Felder als Parameter verwenden und formatieren
params = this.formatParams({ ...parsed });
delete params.tr;
}
} catch (e) {
@@ -153,7 +167,137 @@ export default {
}
}
// 3) Prüfe, ob es sich um ein random_event handelt mit title/description Struktur
if (key.startsWith('falukant.notifications.random_event.')) {
const eventId = key.replace('falukant.notifications.random_event.', '');
const eventKey = `falukant.notifications.random_event.${eventId}`;
try {
const titleKey = `${eventKey}.title`;
const descKey = `${eventKey}.description`;
if (this.$te(titleKey) && this.$te(descKey)) {
const title = this.$t(titleKey, params);
const description = this.$t(descKey, params);
return { title, description };
}
} catch (e) {
// Fallback zur alten Methode
}
}
// Fallback: Alte Methode für andere Notification-Typen
return this.$t(key, params);
},
formatParams(params) {
const formatted = {};
// Geldbeträge formatieren
if (params.amount !== undefined) {
formatted.amount = this.formatMoney(params.amount);
}
if (params.absolute !== undefined) {
formatted.amount = this.formatMoney(params.absolute);
}
if (params.percent !== undefined) {
formatted.amount = `${params.percent > 0 ? '+' : ''}${params.percent.toFixed(1)}%`;
}
// Gesundheit formatieren
if (params.change !== undefined) {
formatted.healthChange = params.change > 0 ? `+${params.change}` : `${params.change}`;
}
if (params.healthChange !== undefined) {
formatted.healthChange = params.healthChange > 0 ? `+${params.healthChange}` : `${params.healthChange}`;
}
// Schaden formatieren
if (params.inventory_damage_percent !== undefined) {
formatted.damagePercent = ` Lagerbestand beschädigt: ${params.inventory_damage_percent.toFixed(1)}%.`;
}
if (params.storage_destruction_percent !== undefined) {
formatted.destructionPercent = ` Lager zerstört: ${params.storage_destruction_percent.toFixed(1)}%.`;
}
// Alle anderen Parameter übernehmen
for (const [key, value] of Object.entries(params)) {
if (!formatted.hasOwnProperty(key) && key !== 'tr') {
formatted[key] = value;
}
}
return formatted;
},
extractParams(n) {
const params = {};
// Parameter aus effects extrahieren
if (n.effects && Array.isArray(n.effects)) {
for (const effect of n.effects) {
if (effect.type === 'money_change') {
if (effect.absolute !== undefined) {
params.amount = effect.absolute;
} else if (effect.percent !== undefined) {
params.percent = effect.percent;
}
} else if (effect.type === 'character_health_change') {
if (effect.character_id && n.character_id === effect.character_id) {
params.characterName = n.characterName || '';
}
if (effect.change !== undefined) {
params.change = effect.change;
}
} else if (effect.type === 'character_death') {
if (effect.character_id && n.character_id === effect.character_id) {
params.characterName = n.characterName || '';
}
} else if (effect.type === 'storage_damage') {
if (effect.inventory_damage_percent !== undefined) {
params.inventory_damage_percent = effect.inventory_damage_percent;
}
if (effect.storage_destruction_percent !== undefined) {
params.storage_destruction_percent = effect.storage_destruction_percent;
}
}
}
}
// Weitere Parameter aus der Notification selbst
if (n.region_id && n.regionName) {
params.regionName = n.regionName;
}
if (n.character_id && n.characterName) {
params.characterName = n.characterName;
}
if (n.amount !== undefined) {
params.amount = n.amount;
}
return this.formatParams(params);
},
interpolateString(str, params) {
if (!str || typeof str !== 'string') return str;
let result = str;
for (const [key, value] of Object.entries(params)) {
if (value !== undefined && value !== null && value !== '') {
result = result.replace(new RegExp(`\\{${key}\\}`, 'g'), String(value));
} else {
// Entferne Platzhalter, wenn kein Wert vorhanden ist
result = result.replace(new RegExp(`\\s*\\{${key}\\}\\s*`, 'g'), '');
}
}
// Entferne doppelte Leerzeichen und trimme
result = result.replace(/\s+/g, ' ').trim();
return result;
},
formatMoney(amount) {
return new Intl.NumberFormat(navigator.language, {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(amount);
}
},
computed: {
@@ -174,6 +318,9 @@ export default {
}
.messages > li { border: 1px solid #7BBE55; margin-bottom: .25em; padding: .5em; }
.messages > li.unread { font-weight: bold; }
.messages > li .body { display: flex; flex-direction: column; gap: 0.25em; }
.messages > li .notification-title { font-weight: bold; font-size: 1.05em; }
.messages > li .notification-description { color: #555; }
.messages > li .footer { color: #f9a22c; font-size: .8em; margin-top: .3em; display: flex; }
.messages > li .footer span:first-child { flex: 1; }
.empty { text-align: center; color: #777; padding: 1em; }