(fix): live reactions, message and call invites
All checks were successful
/ build-web (push) Successful in 7m40s
/ build-desktop (linux) (push) Successful in 12m10s
/ build-mobile (push) Successful in 19m43s
/ release (push) Successful in 3m35s

(fix): call ui
This commit is contained in:
Alois 2026-07-31 00:29:48 +02:00
commit f1874042ba
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
10 changed files with 203 additions and 88 deletions

View file

@ -102,6 +102,34 @@ function updateMessagesBySendTime<T extends EditableMessage>(
};
}
function updateMessageReaction<T extends EditableMessage>(
message: T,
reaction: string,
senderId: number,
accepted: boolean,
): T {
const current = message.Reactions ?? [];
const withoutReaction = current.filter(
(item) => item.Reaction !== reaction || item.SenderId !== senderId,
);
const next = accepted
? [...withoutReaction, { Reaction: reaction, SenderId: senderId }]
: withoutReaction;
if (
next.length === current.length &&
next.every(
(item, index) =>
item.Reaction === current[index]?.Reaction &&
item.SenderId === current[index]?.SenderId,
)
) {
return message;
}
return { ...message, Reactions: next };
}
function assertProtocolSuccess(type: string, response: { type: string }) {
if (response.type.startsWith("Error")) {
throw new Error(`${type} failed: ${response.type}`);
@ -553,6 +581,51 @@ export default function Provider({ children }: { children: ReactNode }) {
[currentChatSecret, userIdValue],
);
const applyLiveReaction = useCallback(
(
sendTime: number,
reaction: string,
senderId: number,
accepted: boolean,
) => {
setLiveMessagesState((current) =>
current.map((message) =>
message.SendTime === sendTime
? updateMessageReaction(message, reaction, senderId, accepted)
: message,
),
);
const queryKey = [
"chat-messages",
String(userIdValue),
currentChatSecret !== null,
] as const;
queryClient.setQueryData<InfiniteData<RawMessages>>(
queryKey,
(current) =>
current
? {
...current,
pages: current.pages.map((page) =>
page.map((message) =>
message.SendTime === sendTime
? updateMessageReaction(
message,
reaction,
senderId,
accepted,
)
: message,
),
),
}
: current,
);
},
[currentChatSecret, userIdValue],
);
const deleteMessage = useCallback(
async (sendTime: number) => {
try {
@ -743,20 +816,28 @@ export default function Provider({ children }: { children: ReactNode }) {
const rawData = message.data as {
ChatPartnerId: unknown;
SendTime: unknown;
Reaction: string;
SenderId: unknown;
Accepted: boolean;
};
const chatPartnerId = Number(rawData.ChatPartnerId);
const sendTime = Number(rawData.SendTime);
const senderId = Number(rawData.SenderId);
if (chatPartnerId !== userIdValue || !Number.isFinite(sendTime)) return;
if (
chatPartnerId !== userIdValue ||
!Number.isFinite(sendTime) ||
!Number.isFinite(senderId)
) {
return;
}
void send("MessageGet", { SendTime: sendTime })
.then((response) => {
assertProtocolSuccess("MessageGet", response);
editMessage(sendTime, { Reactions: response.data.Reactions ?? [] });
})
.catch((err) => {
log(1, "chat", "red", "Failed to refresh message reactions", err);
});
applyLiveReaction(
sendTime,
rawData.Reaction,
senderId,
rawData.Accepted,
);
return;
}
@ -819,9 +900,9 @@ export default function Provider({ children }: { children: ReactNode }) {
});
}, [
currentChatSecret,
applyLiveReaction,
editMessage,
removeMessage,
send,
subscribePush,
userIdValue,
]);