diff --git a/frontend/src/components/falukant/MessagesDialog.vue b/frontend/src/components/falukant/MessagesDialog.vue index 2dfb6db..8d59041 100644 --- a/frontend/src/components/falukant/MessagesDialog.vue +++ b/frontend/src/components/falukant/MessagesDialog.vue @@ -133,18 +133,40 @@ export default { return new Date(dt).toLocaleString(); } catch { return dt; } }, + /** Vereinigt Top-Level-Felder mit in `tr` eingebettetem JSON (Daemon liefert effects oft nur dort). */ + mergeNotificationPayload(n) { + if (!n || typeof n !== 'object') return n || {}; + let merged = { ...n }; + const raw = n.tr; + if (typeof raw === 'string') { + const trimmed = raw.trim(); + if (trimmed.startsWith('{') && trimmed.endsWith('}')) { + try { + const parsed = JSON.parse(trimmed); + if (parsed && typeof parsed === 'object') { + merged = { ...merged, ...parsed }; + } + } catch { + /* Rohstring bleibt in merged.tr */ + } + } + } + return merged; + }, + formatBody(n) { + const payload = this.mergeNotificationPayload(n); // Wenn die Notification bereits title und description hat (z.B. von WebSocket Events) - if (n.title && n.description) { + if (payload.title && payload.description) { // Parameter aus effects oder anderen Feldern extrahieren - const params = this.extractParams(n); + const params = this.extractParams(payload); return { - title: this.interpolateString(n.title, params), - description: this.interpolateString(n.description, params) + title: this.interpolateString(payload.title, params), + description: this.interpolateString(payload.description, params) }; } - let raw = n.tr || ''; + let raw = payload.tr || ''; let key = raw; let params = {}; @@ -162,7 +184,7 @@ export default { 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 }); + const extracted = this.extractParams({ ...payload, ...parsed, characterName: parsed.characterName || parsed.character_name || payload.characterName || payload.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; @@ -199,8 +221,8 @@ export default { 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.) - if ((!params || Object.keys(params).length === 0) && n) { - params = this.extractParams(n) || {}; + if ((!params || Object.keys(params).length === 0) && payload) { + params = this.extractParams(payload) || {}; } if (this.$te(titleKey) && this.$te(descKey)) { @@ -280,14 +302,19 @@ export default { }, extractParams(n) { + const base = this.mergeNotificationPayload(n); const params = {}; - - // Parameter aus effects extrahieren - if (n.effects && Array.isArray(n.effects)) { - for (const effect of n.effects) { + + // Parameter aus effects extrahieren (Daemon: effects oft nur im JSON in tr) + if (base.effects && Array.isArray(base.effects)) { + for (const effect of base.effects) { if (effect.type === 'money_change') { - if (effect.absolute !== undefined) { - params.amount = effect.absolute; + if (effect.absolute !== undefined && effect.absolute !== null) { + const absNum = Number(effect.absolute); + if (Number.isFinite(absNum)) { + // Gebuchter Betrag ist bei Verlust negativ; Anzeige „Verlust: …“ erwartet positive Zahl + params.amount = Math.abs(absNum); + } } else if (effect.percent !== undefined) { params.percent = effect.percent; } @@ -295,7 +322,7 @@ export default { 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}`; + params.characterName = params.characterName || base.characterName || `#${effect.character_id}`; } if (effect.change !== undefined) { params.change = effect.change; @@ -303,7 +330,7 @@ export default { } else if (effect.type === 'character_death') { if (effect.character_id) { params.character_id = effect.character_id; - params.characterName = params.characterName || n.characterName || `#${effect.character_id}`; + params.characterName = params.characterName || base.characterName || `#${effect.character_id}`; } } else if (effect.type === 'storage_damage') { if (effect.inventory_damage_percent !== undefined) { @@ -317,37 +344,37 @@ export default { } // Weitere Parameter aus der Notification selbst - if (n.region_id && n.regionName) { - params.regionName = n.regionName; + if (base.region_id && base.regionName) { + params.regionName = base.regionName; } - if (n.character_id && n.characterName) { - params.characterName = n.characterName; + if (base.character_id && base.characterName) { + params.characterName = base.characterName; } - if (n.amount !== undefined) { - params.amount = n.amount; + if (base.amount !== undefined) { + params.amount = base.amount; } - if (n.deceased && typeof n.deceased === 'object') { - if (n.deceased.display_name) { - params.characterName = n.deceased.display_name; + if (base.deceased && typeof base.deceased === 'object') { + if (base.deceased.display_name) { + params.characterName = base.deceased.display_name; } - if (n.deceased.region_label) { - params.regionLabel = n.deceased.region_label; + if (base.deceased.region_label) { + params.regionLabel = base.deceased.region_label; } - if (n.deceased.age_years !== undefined && n.deceased.age_years !== null) { - params.ageYears = n.deceased.age_years; + if (base.deceased.age_years !== undefined && base.deceased.age_years !== null) { + params.ageYears = base.deceased.age_years; } } - if (n.linked && typeof n.linked === 'object') { - if (Array.isArray(n.linked.spouses) && n.linked.spouses.length > 0) { - params.spouses = n.linked.spouses.join(', '); + if (base.linked && typeof base.linked === 'object') { + if (Array.isArray(base.linked.spouses) && base.linked.spouses.length > 0) { + params.spouses = base.linked.spouses.join(', '); } - if (Array.isArray(n.linked.children) && n.linked.children.length > 0) { - params.children = n.linked.children.join(', '); + if (Array.isArray(base.linked.children) && base.linked.children.length > 0) { + params.children = base.linked.children.join(', '); } - if (Array.isArray(n.linked.lovers) && n.linked.lovers.length > 0) { - params.lovers = n.linked.lovers.join(', '); + if (Array.isArray(base.linked.lovers) && base.linked.lovers.length > 0) { + params.lovers = base.linked.lovers.join(', '); } }