add capability to remove reactions

This commit is contained in:
marlon-erler
2026-04-07 18:48:01 +00:00
parent ad978807dc
commit 4bd7126a76
6 changed files with 81 additions and 64 deletions
+41 -34
View File
@@ -1260,7 +1260,10 @@
this.handleReaction = (reaction) => { this.handleReaction = (reaction) => {
if (!checkMatchesObjectStructure(reaction, ChatMessageReactionReference)) if (!checkMatchesObjectStructure(reaction, ChatMessageReactionReference))
return; return;
this.addReaction(reaction); const reactionPath = this.getReactionPath(reaction.fileId);
const action = reaction.isDeleting ? this.storageModel.remove : this.storageModel.writeStringifiable;
action(reactionPath, reaction);
this.reactionHandlerManager.trigger(reaction);
}; };
this.handleMessageSent = (chatMessage) => { this.handleMessageSent = (chatMessage) => {
chatMessage.status = "sent" /* Sent */; chatMessage.status = "sent" /* Sent */;
@@ -1296,11 +1299,6 @@
chatMessage.stringifiedFile chatMessage.stringifiedFile
); );
}; };
this.addReaction = async (reaction) => {
const reactionPath = this.getReactionPath(reaction.fileId);
this.storageModel.writeStringifiable(reactionPath, reaction);
this.reactionHandlerManager.trigger(reaction);
};
this.getNameAndChannel = () => { this.getNameAndChannel = () => {
const senderName = this.settingsModel.username; const senderName = this.settingsModel.username;
if (senderName == "") return false; if (senderName == "") return false;
@@ -1338,17 +1336,18 @@
chatMessage.body = decryptedBody; chatMessage.body = decryptedBody;
chatMessage.stringifiedFile = decryptedFile; chatMessage.stringifiedFile = decryptedFile;
}; };
this.sendReaction = async (messageId, content) => { this.sendReaction = async (messageId, content, isDeleting) => {
const nameAndChannel = this.getNameAndChannel(); const nameAndChannel = this.getNameAndChannel();
if (nameAndChannel == false) return; if (nameAndChannel == false) return;
const [senderName, combinedChannel] = nameAndChannel; const [senderName, combinedChannel] = nameAndChannel;
const reaction = _ChatModel.createMessageReaction( const reaction = _ChatModel.createMessageReaction(
messageId, messageId,
senderName, senderName,
content content,
isDeleting
); );
this.sendMessage("", reaction); this.sendMessage("", reaction);
this.addReaction(reaction); this.handleReaction(reaction);
}; };
this.subscribe = () => { this.subscribe = () => {
this.connectionModel.addChannel(this.info.primaryChannel); this.connectionModel.addChannel(this.info.primaryChannel);
@@ -1489,13 +1488,14 @@
}; };
} }
static { static {
this.createMessageReaction = (messageId, sender, content) => { this.createMessageReaction = (messageId, sender, content, isDeleting) => {
const fileContent = FileModel2.createFileContent(v4_default(), "reaction"); const fileContent = FileModel2.createFileContent(v4_default(), "reaction");
const reaction = { const reaction = {
...fileContent, ...fileContent,
messageId, messageId,
sender, sender,
content content,
isDeleting
}; };
return reaction; return reaction;
}; };
@@ -1526,7 +1526,8 @@
type: "reaction", type: "reaction",
messageId: "", messageId: "",
sender: "", sender: "",
content: "" content: "",
isDeleting: false
}; };
// src/Model/Chat/chatListModel.ts // src/Model/Chat/chatListModel.ts
@@ -2051,25 +2052,29 @@
this.isPresentingInfoModal.value = false; this.isPresentingInfoModal.value = false;
}; };
// reactions // reactions
this.addReaction = (reaction) => { this.handleReaction = (reaction) => {
function setReaction(mapState) {
if (reaction.isDeleting) {
mapState.remove(reaction.sender);
} else {
mapState.set(reaction.sender, reaction);
}
}
switch (reaction.content) { switch (reaction.content) {
case "\u{1F44D}" /* ThumbsUp */: case "\u{1F44D}" /* ThumbsUp */:
return this.reactionsThumbsUp.set(reaction.sender, reaction); return setReaction(this.reactionsThumbsUp);
case "\u2705" /* Check */: case "\u2705" /* Check */:
return this.reactionsCheck.set(reaction.sender, reaction); return setReaction(this.reactionsCheck);
case "\u2757\uFE0F" /* Attention */: case "\u2757\uFE0F" /* Attention */:
return this.reactionsAttention.set(reaction.sender, reaction); return setReaction(this.reactionsAttention);
case "\u203C\uFE0F" /* DoubleAttention */: case "\u203C\uFE0F" /* DoubleAttention */:
return this.reactionsDoubleAttention.set( return setReaction(this.reactionsDoubleAttention);
reaction.sender,
reaction
);
case "\u2753" /* Question */: case "\u2753" /* Question */:
return this.reactionsQuestion.set(reaction.sender, reaction); return setReaction(this.reactionsQuestion);
} }
}; };
this.sendReaction = (content) => { this.sendReaction = (content, isDeleting) => {
this.messagePageViewModel.sendReaction(this.chatMessage.id, content); this.messagePageViewModel.sendReaction(this.chatMessage.id, content, isDeleting);
}; };
// load // load
this.loadData = () => { this.loadData = () => {
@@ -2109,8 +2114,8 @@
this.chatViewModel.chatModel.addMessage(chatMessage); this.chatViewModel.chatModel.addMessage(chatMessage);
messageViewModel.loadData(); messageViewModel.loadData();
}; };
this.sendReaction = (messageId, content) => { this.sendReaction = (messageId, content, isDeleting) => {
this.chatViewModel.chatModel.sendReaction(messageId, content); this.chatViewModel.chatModel.sendReaction(messageId, content, isDeleting);
}; };
// view // view
this.showChatMessage = (chatMessage) => { this.showChatMessage = (chatMessage) => {
@@ -2131,10 +2136,10 @@
); );
} }
}; };
this.showReaction = (reaction) => { this.handleReaction = (reaction) => {
const messageViewModel = this.chatMessageViewModels.value.get(reaction.messageId); const messageViewModel = this.chatMessageViewModels.value.get(reaction.messageId);
if (messageViewModel == void 0) return; if (messageViewModel == void 0) return;
messageViewModel.addReaction(reaction); messageViewModel.handleReaction(reaction);
}; };
// load // load
this.loadData = () => { this.loadData = () => {
@@ -2143,7 +2148,7 @@
this.showChatMessage(chatMessage); this.showChatMessage(chatMessage);
} }
for (const reaction of this.chatViewModel.chatModel.reactions) { for (const reaction of this.chatViewModel.chatModel.reactions) {
this.showReaction(reaction); this.handleReaction(reaction);
} }
}; };
this.chatViewModel = chatViewModel; this.chatViewModel = chatViewModel;
@@ -3309,7 +3314,7 @@
); );
chatModel.reactionHandlerManager.addHandler( chatModel.reactionHandlerManager.addHandler(
(reaction) => { (reaction) => {
this.messagePageViewModel.showReaction(reaction); this.messagePageViewModel.handleReaction(reaction);
} }
); );
this.loadPageSelection(); this.loadPageSelection();
@@ -3934,8 +3939,11 @@
// src/View/Components/messageReactionButton.tsx // src/View/Components/messageReactionButton.tsx
function MessageReactionButton(chatMessageViewModel, content) { function MessageReactionButton(chatMessageViewModel, content) {
let audioLabel;
let count;
let isActive;
function sendReaction() { function sendReaction() {
chatMessageViewModel.sendReaction(content); chatMessageViewModel.sendReaction(content, isActive.value);
} }
function getUsername() { function getUsername() {
return chatMessageViewModel.messagePageViewModel.chatViewModel.settingsViewModel.username.value; return chatMessageViewModel.messagePageViewModel.chatViewModel.settingsViewModel.username.value;
@@ -3943,9 +3951,6 @@
function checkIsHighlighted(mapState) { function checkIsHighlighted(mapState) {
return mapState.value.has(getUsername()); return mapState.value.has(getUsername());
} }
let audioLabel;
let count;
let isActive;
switch (content) { switch (content) {
case "\u{1F44D}" /* ThumbsUp */: { case "\u{1F44D}" /* ThumbsUp */: {
audioLabel = translations.chatPage.message.thumbsUpReaction; audioLabel = translations.chatPage.message.thumbsUpReaction;
@@ -3979,7 +3984,9 @@
count = chatMessageViewModel.reactionsDoubleAttentionCount; count = chatMessageViewModel.reactionsDoubleAttentionCount;
isActive = createProxyState( isActive = createProxyState(
[chatMessageViewModel.reactionsDoubleAttention], [chatMessageViewModel.reactionsDoubleAttention],
() => checkIsHighlighted(chatMessageViewModel.reactionsDoubleAttention) () => checkIsHighlighted(
chatMessageViewModel.reactionsDoubleAttention
)
); );
break; break;
} }
+13 -8
View File
@@ -111,7 +111,11 @@ export default class ChatModel {
!checkMatchesObjectStructure(reaction, ChatMessageReactionReference) !checkMatchesObjectStructure(reaction, ChatMessageReactionReference)
) )
return; return;
this.addReaction(reaction); const reactionPath: string[] = this.getReactionPath(reaction.fileId);
const action = reaction.isDeleting ? this.storageModel.remove : this.storageModel.writeStringifiable;
action(reactionPath, reaction);
this.reactionHandlerManager.trigger(reaction);
}; };
handleMessageSent = (chatMessage: ChatMessage): void => { handleMessageSent = (chatMessage: ChatMessage): void => {
@@ -158,12 +162,6 @@ export default class ChatModel {
); );
}; };
addReaction = async (reaction: ChatMessageReaction): Promise<void> => {
const reactionPath: string[] = this.getReactionPath(reaction.fileId);
this.storageModel.writeStringifiable(reactionPath, reaction);
this.reactionHandlerManager.trigger(reaction);
};
getNameAndChannel = (): [string, string] | false => { getNameAndChannel = (): [string, string] | false => {
const senderName = this.settingsModel.username; const senderName = this.settingsModel.username;
if (senderName == "") return false; if (senderName == "") return false;
@@ -215,6 +213,7 @@ export default class ChatModel {
sendReaction = async ( sendReaction = async (
messageId: string, messageId: string,
content: ReactionSymbols, content: ReactionSymbols,
isDeleting: boolean,
): Promise<void> => { ): Promise<void> => {
const nameAndChannel = this.getNameAndChannel(); const nameAndChannel = this.getNameAndChannel();
if (nameAndChannel == false) return; if (nameAndChannel == false) return;
@@ -224,9 +223,10 @@ export default class ChatModel {
messageId, messageId,
senderName, senderName,
content, content,
isDeleting,
); );
this.sendMessage("", reaction); this.sendMessage("", reaction);
this.addReaction(reaction); this.handleReaction(reaction);
}; };
subscribe = (): void => { subscribe = (): void => {
@@ -408,6 +408,7 @@ export default class ChatModel {
messageId: string, messageId: string,
sender: string, sender: string,
content: ReactionSymbols, content: ReactionSymbols,
isDeleting: boolean,
): ChatMessageReaction => { ): ChatMessageReaction => {
const fileContent = FileModel.createFileContent(v4(), "reaction"); const fileContent = FileModel.createFileContent(v4(), "reaction");
const reaction: ChatMessageReaction = { const reaction: ChatMessageReaction = {
@@ -416,6 +417,7 @@ export default class ChatModel {
messageId, messageId,
sender, sender,
content, content,
isDeleting,
}; };
return reaction; return reaction;
}; };
@@ -463,6 +465,7 @@ export interface ChatMessageReaction
messageId: string; messageId: string;
sender: string; sender: string;
content: ReactionSymbols | string; content: ReactionSymbols | string;
isDeleting: boolean;
} }
// references // references
@@ -502,4 +505,6 @@ export const ChatMessageReactionReference: ChatMessageReaction = {
messageId: "", messageId: "",
sender: "", sender: "",
content: "", content: "",
isDeleting: false,
}; };
@@ -7,8 +7,12 @@ export function MessageReactionButton(
chatMessageViewModel: ChatMessageViewModel, chatMessageViewModel: ChatMessageViewModel,
content: ReactionSymbols, content: ReactionSymbols,
) { ) {
let audioLabel: string;
let count: React.State<number>;
let isActive: React.State<boolean>;
function sendReaction() { function sendReaction() {
chatMessageViewModel.sendReaction(content); chatMessageViewModel.sendReaction(content, isActive.value);
} }
function getUsername(): string { function getUsername(): string {
@@ -20,10 +24,6 @@ export function MessageReactionButton(
return mapState.value.has(getUsername()); return mapState.value.has(getUsername());
} }
let audioLabel: string;
let count: React.State<number>;
let isActive: React.State<boolean>;
switch (content) { switch (content) {
case ReactionSymbols.ThumbsUp: { case ReactionSymbols.ThumbsUp: {
audioLabel = translations.chatPage.message.thumbsUpReaction; audioLabel = translations.chatPage.message.thumbsUpReaction;
+16 -11
View File
@@ -76,26 +76,31 @@ export default class ChatMessageViewModel {
}; };
// reactions // reactions
addReaction = (reaction: ChatMessageReaction): void => { handleReaction = (reaction: ChatMessageReaction): void => {
function setReaction(mapState: React.MapState<any>) {
if (reaction.isDeleting) {
mapState.remove(reaction.sender);
} else {
mapState.set(reaction.sender, reaction);
}
}
switch (reaction.content) { switch (reaction.content) {
case ReactionSymbols.ThumbsUp: case ReactionSymbols.ThumbsUp:
return this.reactionsThumbsUp.set(reaction.sender, reaction); return setReaction(this.reactionsThumbsUp);
case ReactionSymbols.Check: case ReactionSymbols.Check:
return this.reactionsCheck.set(reaction.sender, reaction); return setReaction(this.reactionsCheck);
case ReactionSymbols.Attention: case ReactionSymbols.Attention:
return this.reactionsAttention.set(reaction.sender, reaction); return setReaction(this.reactionsAttention);
case ReactionSymbols.DoubleAttention: case ReactionSymbols.DoubleAttention:
return this.reactionsDoubleAttention.set( return setReaction(this.reactionsDoubleAttention);
reaction.sender,
reaction,
);
case ReactionSymbols.Question: case ReactionSymbols.Question:
return this.reactionsQuestion.set(reaction.sender, reaction); return setReaction(this.reactionsQuestion);
} }
}; };
sendReaction = (content: ReactionSymbols): void => { sendReaction = (content: ReactionSymbols, isDeleting: boolean): void => {
this.messagePageViewModel.sendReaction(this.chatMessage.id, content); this.messagePageViewModel.sendReaction(this.chatMessage.id, content, isDeleting);
}; };
// load // load
+1 -1
View File
@@ -181,7 +181,7 @@ export default class ChatViewModel {
); );
chatModel.reactionHandlerManager.addHandler( chatModel.reactionHandlerManager.addHandler(
(reaction: ChatMessageReaction) => { (reaction: ChatMessageReaction) => {
this.messagePageViewModel.showReaction(reaction); this.messagePageViewModel.handleReaction(reaction);
}, },
); );
+5 -5
View File
@@ -41,8 +41,8 @@ export default class MessagePageViewModel {
messageViewModel.loadData(); messageViewModel.loadData();
}; };
sendReaction = (messageId: string, content: ReactionSymbols): void => { sendReaction = (messageId: string, content: ReactionSymbols, isDeleting: boolean): void => {
this.chatViewModel.chatModel.sendReaction(messageId, content); this.chatViewModel.chatModel.sendReaction(messageId, content, isDeleting);
}; };
// view // view
@@ -68,11 +68,11 @@ export default class MessagePageViewModel {
} }
}; };
showReaction = (reaction: ChatMessageReaction): void => { handleReaction = (reaction: ChatMessageReaction): void => {
const messageViewModel: ChatMessageViewModel | undefined = const messageViewModel: ChatMessageViewModel | undefined =
this.chatMessageViewModels.value.get(reaction.messageId); this.chatMessageViewModels.value.get(reaction.messageId);
if (messageViewModel == undefined) return; if (messageViewModel == undefined) return;
messageViewModel.addReaction(reaction); messageViewModel.handleReaction(reaction);
}; };
// load // load
@@ -82,7 +82,7 @@ export default class MessagePageViewModel {
this.showChatMessage(chatMessage); this.showChatMessage(chatMessage);
} }
for (const reaction of this.chatViewModel.chatModel.reactions) { for (const reaction of this.chatViewModel.chatModel.reactions) {
this.showReaction(reaction); this.handleReaction(reaction);
} }
}; };