Enhance MessagesDialog component to support HTML content and improve parameter extraction
- Updated notification description rendering to allow HTML content using v-html directive. - Refactored formatBody method to better handle JSON formatted notifications and extract parameters from nested structures. - Introduced new method for extracting parameters from value objects, improving compatibility with various notification types. - Enhanced description formatting to include details from effects, providing richer user feedback in notifications.
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
<li v-for="n in messages" :key="n.id" :class="{ unread: !n.shown }">
|
||||
<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 class="notification-description" v-html="formatBody(n).description || formatBody(n)"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<span>{{ formatDate(n.createdAt) }}</span>
|
||||
@@ -133,54 +133,52 @@ 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 parsed = null;
|
||||
let value = null;
|
||||
let key = raw;
|
||||
let params = {};
|
||||
|
||||
// 1) JSON-Format unterstützen: {"tr":"random_event.windfall","amount":1000,"characterName":"Max"}
|
||||
// 1) Parse JSON-Format: {"tr":"random_event.character_illness","value":{...}}
|
||||
if (typeof raw === 'string') {
|
||||
const trimmed = raw.trim();
|
||||
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed);
|
||||
parsed = JSON.parse(trimmed);
|
||||
if (parsed && parsed.tr) {
|
||||
raw = parsed.tr;
|
||||
key = parsed.tr;
|
||||
// Alle anderen Felder als Parameter verwenden und formatieren
|
||||
params = this.formatParams({ ...parsed });
|
||||
delete params.tr;
|
||||
// Merge in params extracted from nested structures (effects, character ids)
|
||||
try {
|
||||
const extracted = this.extractParams({ ...n, ...parsed, characterName: parsed.characterName || parsed.character_name || n.characterName || n.character_name });
|
||||
for (const [k, v] of Object.entries(extracted || {})) {
|
||||
if (!params.hasOwnProperty(k) || params[k] === undefined || params[k] === null || params[k] === '') {
|
||||
params[k] = v;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore extraction errors
|
||||
}
|
||||
value = parsed.value || {};
|
||||
|
||||
// Extrahiere Parameter aus value und effects
|
||||
params = this.extractParamsFromValue(value, n);
|
||||
}
|
||||
} catch (e) {
|
||||
// bei Parse-Fehler einfach weiter unten mit dem Rohwert arbeiten
|
||||
// Bei Parse-Fehler: Alte Struktur unterstützen
|
||||
try {
|
||||
parsed = JSON.parse(trimmed);
|
||||
if (parsed && parsed.tr) {
|
||||
key = parsed.tr;
|
||||
params = this.formatParams({ ...parsed });
|
||||
delete params.tr;
|
||||
}
|
||||
} catch (e2) {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Schlüssel normalisieren:
|
||||
// - wenn bereits ein voller i18n-Key wie "falukant.notifications.production.overproduction",
|
||||
// dann direkt verwenden
|
||||
// - sonst in den Namespace "falukant.notifications." hängen
|
||||
// 2) Wenn value.title und value.description vorhanden sind, verwende diese
|
||||
if (value && value.title && value.description) {
|
||||
// Parameter aus effects extrahieren und formatieren
|
||||
const formattedParams = this.formatParams(params);
|
||||
return {
|
||||
title: this.interpolateString(value.title, formattedParams),
|
||||
description: this.formatDescriptionWithEffects(value.description, value.effects || [], formattedParams)
|
||||
};
|
||||
}
|
||||
|
||||
// 3) Schlüssel normalisieren
|
||||
if (typeof key === 'string') {
|
||||
const trimmedKey = key.trim();
|
||||
if (trimmedKey.startsWith('falukant.')) {
|
||||
@@ -190,21 +188,35 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Prüfe, ob es sich um ein random_event handelt mit title/description Struktur
|
||||
// 4) 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 no params were parsed from JSON, try to extract them from the notification (effects, character_id, etc.)
|
||||
|
||||
// Wenn keine Parameter extrahiert wurden, versuche es aus der Notification
|
||||
if ((!params || Object.keys(params).length === 0) && n) {
|
||||
params = this.extractParams(n) || {};
|
||||
}
|
||||
|
||||
// Wenn value vorhanden ist, verwende effects für Details
|
||||
if (value && value.effects) {
|
||||
params = this.extractParamsFromValue(value, n);
|
||||
}
|
||||
|
||||
const formattedParams = this.formatParams(params);
|
||||
|
||||
if (this.$te(titleKey) && this.$te(descKey)) {
|
||||
const title = this.$t(titleKey, params);
|
||||
const description = this.$t(descKey, params);
|
||||
const title = this.$t(titleKey, formattedParams);
|
||||
let description = this.$t(descKey, formattedParams);
|
||||
|
||||
// Füge Effect-Details hinzu, falls vorhanden
|
||||
if (value && value.effects) {
|
||||
description = this.formatDescriptionWithEffects(description, value.effects, formattedParams);
|
||||
}
|
||||
|
||||
return { title, description };
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -213,42 +225,64 @@ export default {
|
||||
}
|
||||
|
||||
// Fallback: Alte Methode für andere Notification-Typen
|
||||
return this.$t(key, params);
|
||||
const formattedParams = this.formatParams(params);
|
||||
return this.$t(key, formattedParams);
|
||||
},
|
||||
|
||||
formatParams(params) {
|
||||
const formatted = {};
|
||||
|
||||
// Geldbeträge formatieren
|
||||
if (params.amount !== undefined) {
|
||||
if (params.amount !== undefined && params.amount !== null) {
|
||||
formatted.amount = this.formatMoney(params.amount);
|
||||
}
|
||||
if (params.absolute !== undefined) {
|
||||
if (params.absolute !== undefined && params.absolute !== null) {
|
||||
formatted.amount = this.formatMoney(params.absolute);
|
||||
}
|
||||
if (params.percent !== undefined) {
|
||||
formatted.amount = `${params.percent > 0 ? '+' : ''}${params.percent.toFixed(1)}%`;
|
||||
if (params.percent !== undefined && params.percent !== null) {
|
||||
formatted.percent = `${params.percent > 0 ? '+' : ''}${params.percent.toFixed(1)}%`;
|
||||
}
|
||||
|
||||
// Gesundheit formatieren
|
||||
if (params.change !== undefined) {
|
||||
if (params.change !== undefined && params.change !== null) {
|
||||
formatted.healthChange = params.change > 0 ? `+${params.change}` : `${params.change}`;
|
||||
formatted.change = formatted.healthChange;
|
||||
}
|
||||
if (params.healthChange !== undefined) {
|
||||
formatted.healthChange = params.healthChange > 0 ? `+${params.healthChange}` : `${params.healthChange}`;
|
||||
if (params.healthChange !== undefined && params.healthChange !== null && !formatted.healthChange) {
|
||||
formatted.healthChange = typeof params.healthChange === 'string'
|
||||
? params.healthChange
|
||||
: (params.healthChange > 0 ? `+${params.healthChange}` : `${params.healthChange}`);
|
||||
}
|
||||
|
||||
// Schaden formatieren
|
||||
if (params.inventory_damage_percent !== undefined) {
|
||||
// Charakternamen
|
||||
if (params.characterName) {
|
||||
formatted.characterName = params.characterName;
|
||||
}
|
||||
if (params.character_first_name || params.character_last_name) {
|
||||
const firstName = params.character_first_name || '';
|
||||
const lastName = params.character_last_name || '';
|
||||
formatted.characterName = `${firstName} ${lastName}`.trim() || formatted.characterName;
|
||||
}
|
||||
|
||||
// Regions-Informationen
|
||||
if (params.regionName) {
|
||||
formatted.regionName = params.regionName;
|
||||
}
|
||||
if (params.region_id) {
|
||||
formatted.region_id = params.region_id;
|
||||
}
|
||||
|
||||
// Schaden formatieren (für Kompatibilität mit alter Struktur)
|
||||
if (params.inventory_damage_percent !== undefined && params.inventory_damage_percent !== null) {
|
||||
formatted.damagePercent = ` Lagerbestand beschädigt: ${params.inventory_damage_percent.toFixed(1)}%.`;
|
||||
}
|
||||
if (params.storage_destruction_percent !== undefined) {
|
||||
if (params.storage_destruction_percent !== undefined && params.storage_destruction_percent !== null) {
|
||||
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') {
|
||||
if (!formatted.hasOwnProperty(key) && key !== 'tr' && value !== undefined && value !== null) {
|
||||
formatted[key] = value;
|
||||
}
|
||||
}
|
||||
@@ -256,10 +290,72 @@ export default {
|
||||
return formatted;
|
||||
},
|
||||
|
||||
extractParamsFromValue(value, n) {
|
||||
const params = {};
|
||||
|
||||
// Charakternamen aus value extrahieren
|
||||
if (value.character_first_name || value.character_last_name) {
|
||||
const firstName = value.character_first_name || '';
|
||||
const lastName = value.character_last_name || '';
|
||||
params.characterName = `${firstName} ${lastName}`.trim() || value.character_id ? `#${value.character_id}` : null;
|
||||
}
|
||||
if (value.character_id) {
|
||||
params.character_id = value.character_id;
|
||||
}
|
||||
|
||||
// Regions-Informationen
|
||||
if (value.region_id) {
|
||||
params.region_id = value.region_id;
|
||||
}
|
||||
if (value.regionName) {
|
||||
params.regionName = value.regionName;
|
||||
}
|
||||
|
||||
// Parameter aus effects extrahieren
|
||||
if (value.effects && Array.isArray(value.effects)) {
|
||||
for (const effect of value.effects) {
|
||||
if (effect.type === 'character_health_change') {
|
||||
// Charakternamen aus Effect haben Vorrang
|
||||
if (effect.character_first_name || effect.character_last_name) {
|
||||
const firstName = effect.character_first_name || '';
|
||||
const lastName = effect.character_last_name || '';
|
||||
params.characterName = `${firstName} ${lastName}`.trim();
|
||||
}
|
||||
if (effect.character_id) {
|
||||
params.character_id = effect.character_id;
|
||||
}
|
||||
if (effect.change !== undefined) {
|
||||
params.change = effect.change;
|
||||
params.healthChange = effect.change > 0 ? `+${effect.change}` : `${effect.change}`;
|
||||
}
|
||||
} else if (effect.type === 'character_death') {
|
||||
if (effect.character_first_name || effect.character_last_name) {
|
||||
const firstName = effect.character_first_name || '';
|
||||
const lastName = effect.character_last_name || '';
|
||||
params.characterName = `${firstName} ${lastName}`.trim();
|
||||
}
|
||||
if (effect.character_id) {
|
||||
params.character_id = effect.character_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Charakternamen aus Notification-Spalte
|
||||
if (!params.characterName && n.characterName) {
|
||||
params.characterName = n.characterName;
|
||||
}
|
||||
if (!params.characterName && n.character_id) {
|
||||
params.characterName = `#${n.character_id}`;
|
||||
}
|
||||
|
||||
return params;
|
||||
},
|
||||
|
||||
extractParams(n) {
|
||||
const params = {};
|
||||
|
||||
// Parameter aus effects extrahieren
|
||||
// Parameter aus effects extrahieren (alte Struktur)
|
||||
if (n.effects && Array.isArray(n.effects)) {
|
||||
for (const effect of n.effects) {
|
||||
if (effect.type === 'money_change') {
|
||||
@@ -270,7 +366,6 @@ export default {
|
||||
}
|
||||
} else if (effect.type === 'character_health_change') {
|
||||
if (effect.character_id) {
|
||||
// Prefer explicit characterName from notification, otherwise fall back to provided name or use id placeholder
|
||||
params.character_id = effect.character_id;
|
||||
params.characterName = params.characterName || n.characterName || `#${effect.character_id}`;
|
||||
}
|
||||
@@ -304,7 +399,54 @@ export default {
|
||||
params.amount = n.amount;
|
||||
}
|
||||
|
||||
return this.formatParams(params);
|
||||
return params;
|
||||
},
|
||||
|
||||
formatDescriptionWithEffects(baseDescription, effects, params) {
|
||||
if (!effects || !Array.isArray(effects) || effects.length === 0) {
|
||||
return baseDescription;
|
||||
}
|
||||
|
||||
let description = baseDescription;
|
||||
const effectDetails = [];
|
||||
|
||||
for (const effect of effects) {
|
||||
if (effect.type === 'character_health_change') {
|
||||
const charName = effect.character_first_name && effect.character_last_name
|
||||
? `${effect.character_first_name} ${effect.character_last_name}`.trim()
|
||||
: params.characterName || `#${effect.character_id}`;
|
||||
const change = effect.change > 0 ? `+${effect.change}` : `${effect.change}`;
|
||||
effectDetails.push(`${charName} hat ${change} Gesundheit verloren.`);
|
||||
} else if (effect.type === 'character_death') {
|
||||
const charName = effect.character_first_name && effect.character_last_name
|
||||
? `${effect.character_first_name} ${effect.character_last_name}`.trim()
|
||||
: params.characterName || `#${effect.character_id}`;
|
||||
effectDetails.push(`${charName} ist verstorben.`);
|
||||
} else if (effect.type === 'production_quality_change') {
|
||||
const change = effect.change > 0 ? `+${effect.change}` : `${effect.change}`;
|
||||
effectDetails.push(`Produktionsqualität: ${change}.`);
|
||||
} else if (effect.type === 'transport_speed_change') {
|
||||
const percent = effect.percent > 0 ? `+${effect.percent.toFixed(1)}%` : `${effect.percent.toFixed(1)}%`;
|
||||
effectDetails.push(`Transportgeschwindigkeit: ${percent}.`);
|
||||
} else if (effect.type === 'storage_damage') {
|
||||
const stockType = effect.stock_type || 'Lager';
|
||||
const inventoryDamage = effect.inventory_damage_percent ? `${effect.inventory_damage_percent.toFixed(1)}%` : '0%';
|
||||
const storageDestruction = effect.storage_destruction_percent ? `${effect.storage_destruction_percent.toFixed(1)}%` : '0%';
|
||||
const affected = effect.affected_stocks ? effect.affected_stocks.length : 0;
|
||||
const destroyed = effect.destroyed_stocks ? effect.destroyed_stocks.length : 0;
|
||||
effectDetails.push(`${stockType}: ${inventoryDamage} Lagerbestand beschädigt, ${storageDestruction} Lager zerstört (${affected} betroffen, ${destroyed} zerstört).`);
|
||||
} else if (effect.type === 'storage_capacity_change') {
|
||||
const percent = effect.percent > 0 ? `+${effect.percent.toFixed(1)}%` : `${effect.percent.toFixed(1)}%`;
|
||||
const affected = effect.affected_stocks ? effect.affected_stocks.length : 0;
|
||||
effectDetails.push(`Lagerkapazität: ${percent} (${affected} betroffen).`);
|
||||
}
|
||||
}
|
||||
|
||||
if (effectDetails.length > 0) {
|
||||
description += ' ' + effectDetails.join(' ');
|
||||
}
|
||||
|
||||
return description;
|
||||
},
|
||||
|
||||
interpolateString(str, params) {
|
||||
|
||||
Reference in New Issue
Block a user