Implement lover relationship management features: Add endpoints for creating, acknowledging, and managing lover relationships in the FalukantController. Enhance backend models with RelationshipState for tracking relationship statuses. Update frontend components to display and manage lover details, including marriage satisfaction and household tension. Improve localization for new features in multiple languages.

This commit is contained in:
Torsten Schulz (local)
2026-03-20 11:37:46 +01:00
parent 98782bf28e
commit b3a91e1a05
29 changed files with 4551 additions and 86 deletions

View File

@@ -57,11 +57,12 @@ export default {
loading: false,
error: null,
isDragging: false,
_daemonMessageHandler: null
_daemonMessageHandler: null,
pendingFetchTimer: null
};
},
computed: {
...mapState(['socket', 'daemonSocket']),
...mapState(['socket', 'daemonSocket', 'user']),
isFalukantWidget() {
return this.endpoint && String(this.endpoint).includes('falukant');
},
@@ -89,25 +90,51 @@ export default {
if (this.isFalukantWidget) this.setupSocketListeners();
},
beforeUnmount() {
if (this.pendingFetchTimer) {
clearTimeout(this.pendingFetchTimer);
this.pendingFetchTimer = null;
}
if (this.isFalukantWidget) this.teardownSocketListeners();
},
methods: {
matchesCurrentUser(eventData) {
if (eventData?.user_id == null) {
return true;
}
const currentIds = [this.user?.id, this.user?.hashedId]
.filter(Boolean)
.map((value) => String(value));
return currentIds.includes(String(eventData.user_id));
},
setupSocketListeners() {
this.teardownSocketListeners();
const daemonEvents = ['falukantUpdateStatus', 'stock_change', 'familychanged'];
const daemonEvents = ['falukantUpdateStatus', 'falukantUpdateFamily', 'children_update', 'stock_change', 'familychanged'];
if (this.daemonSocket) {
this._daemonMessageHandler = (event) => {
if (event.data === 'ping') return;
try {
const data = JSON.parse(event.data);
if (daemonEvents.includes(data.event)) this.fetchData();
if (daemonEvents.includes(data.event) && this.matchesCurrentUser(data)) this.queueFetchData();
} catch (_) {}
};
this.daemonSocket.addEventListener('message', this._daemonMessageHandler);
}
if (this.socket) {
this.socket.on('falukantUpdateStatus', () => this.fetchData());
this.socket.on('falukantBranchUpdate', () => this.fetchData());
this._statusSocketHandler = (data) => {
if (this.matchesCurrentUser(data)) this.queueFetchData();
};
this._familySocketHandler = (data) => {
if (this.matchesCurrentUser(data)) this.queueFetchData();
};
this._childrenSocketHandler = (data) => {
if (this.matchesCurrentUser(data)) this.queueFetchData();
};
this._branchSocketHandler = () => this.queueFetchData();
this.socket.on('falukantUpdateStatus', this._statusSocketHandler);
this.socket.on('falukantUpdateFamily', this._familySocketHandler);
this.socket.on('children_update', this._childrenSocketHandler);
this.socket.on('falukantBranchUpdate', this._branchSocketHandler);
}
},
teardownSocketListeners() {
@@ -116,10 +143,21 @@ export default {
this._daemonMessageHandler = null;
}
if (this.socket) {
this.socket.off('falukantUpdateStatus');
this.socket.off('falukantBranchUpdate');
if (this._statusSocketHandler) this.socket.off('falukantUpdateStatus', this._statusSocketHandler);
if (this._familySocketHandler) this.socket.off('falukantUpdateFamily', this._familySocketHandler);
if (this._childrenSocketHandler) this.socket.off('children_update', this._childrenSocketHandler);
if (this._branchSocketHandler) this.socket.off('falukantBranchUpdate', this._branchSocketHandler);
}
},
queueFetchData() {
if (this.pendingFetchTimer) {
clearTimeout(this.pendingFetchTimer);
}
this.pendingFetchTimer = setTimeout(() => {
this.pendingFetchTimer = null;
this.fetchData();
}, 120);
},
async fetchData() {
if (!this.endpoint || this.pauseFetch) return;
this.loading = true;

View File

@@ -60,10 +60,11 @@ export default {
{ key: "children", icon: "👶", value: null },
],
unreadCount: 0,
pendingStatusRefresh: null,
};
},
computed: {
...mapState(["socket", "daemonSocket"]),
...mapState(["socket", "daemonSocket", "user"]),
...mapGetters(['menu']),
},
watch: {
@@ -100,6 +101,10 @@ export default {
beforeUnmount() {
this.teardownSocketListeners();
this.teardownDaemonListeners();
if (this.pendingStatusRefresh) {
clearTimeout(this.pendingStatusRefresh);
this.pendingStatusRefresh = null;
}
EventBus.off('open-falukant-messages', this.openMessages);
},
methods: {
@@ -169,15 +174,25 @@ export default {
setupSocketListeners() {
this.teardownSocketListeners();
if (!this.socket) return;
this.socket.on('falukantUpdateStatus', (data) => this.handleEvent({ event: 'falukantUpdateStatus', ...data }));
this.socket.on('stock_change', (data) => this.handleEvent({ event: 'stock_change', ...data }));
this.socket.on('familychanged', (data) => this.handleEvent({ event: 'familychanged', ...data }));
this._statusSocketHandler = (data) => this.handleEvent({ event: 'falukantUpdateStatus', ...data });
this._familySocketHandler = (data) => this.handleEvent({ event: 'falukantUpdateFamily', ...data });
this._childrenSocketHandler = (data) => this.handleEvent({ event: 'children_update', ...data });
this._stockSocketHandler = (data) => this.handleEvent({ event: 'stock_change', ...data });
this._familyChangedSocketHandler = (data) => this.handleEvent({ event: 'familychanged', ...data });
this.socket.on('falukantUpdateStatus', this._statusSocketHandler);
this.socket.on('falukantUpdateFamily', this._familySocketHandler);
this.socket.on('children_update', this._childrenSocketHandler);
this.socket.on('stock_change', this._stockSocketHandler);
this.socket.on('familychanged', this._familyChangedSocketHandler);
},
teardownSocketListeners() {
if (this.socket) {
this.socket.off('falukantUpdateStatus');
this.socket.off('stock_change');
this.socket.off('familychanged');
if (this._statusSocketHandler) this.socket.off('falukantUpdateStatus', this._statusSocketHandler);
if (this._familySocketHandler) this.socket.off('falukantUpdateFamily', this._familySocketHandler);
if (this._childrenSocketHandler) this.socket.off('children_update', this._childrenSocketHandler);
if (this._stockSocketHandler) this.socket.off('stock_change', this._stockSocketHandler);
if (this._familyChangedSocketHandler) this.socket.off('familychanged', this._familyChangedSocketHandler);
}
},
setupDaemonListeners() {
@@ -186,13 +201,22 @@ export default {
this._daemonHandler = (event) => {
try {
const data = JSON.parse(event.data);
if (['falukantUpdateStatus', 'stock_change', 'familychanged'].includes(data.event)) {
if (['falukantUpdateStatus', 'falukantUpdateFamily', 'children_update', 'stock_change', 'familychanged'].includes(data.event)) {
this.handleEvent(data);
}
} catch (_) {}
};
this.daemonSocket.addEventListener('message', this._daemonHandler);
},
matchesCurrentUser(eventData) {
if (eventData?.user_id == null) {
return true;
}
const currentIds = [this.user?.id, this.user?.hashedId]
.filter(Boolean)
.map((value) => String(value));
return currentIds.includes(String(eventData.user_id));
},
teardownDaemonListeners() {
const sock = this.daemonSocket;
if (sock && this._daemonHandler) {
@@ -200,12 +224,26 @@ export default {
this._daemonHandler = null;
}
},
queueStatusRefresh() {
if (this.pendingStatusRefresh) {
clearTimeout(this.pendingStatusRefresh);
}
this.pendingStatusRefresh = setTimeout(async () => {
this.pendingStatusRefresh = null;
await this.fetchStatus();
}, 120);
},
handleEvent(eventData) {
if (!this.matchesCurrentUser(eventData)) {
return;
}
switch (eventData.event) {
case 'falukantUpdateStatus':
case 'falukantUpdateFamily':
case 'children_update':
case 'stock_change':
case 'familychanged':
this.fetchStatus();
this.queueStatusRefresh();
break;
}
},