refactor(@desktop/chat-messages): add/remove reactions

This commit is contained in:
Sale Djenic 2021-12-20 15:21:35 +01:00
parent 13543ae14f
commit 179b0f5a36
24 changed files with 475 additions and 226 deletions

View File

@ -74,6 +74,18 @@ method init*(self: Controller) =
return return
self.delegate.onChatUnmuted() self.delegate.onChatUnmuted()
self.events.on(SIGNAL_MESSAGE_REACTION_ADDED) do(e:Args):
let args = MessageAddRemoveReactionArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.onReactionAdded(args.messageId, args.emojiId, args.reactionId)
self.events.on(SIGNAL_MESSAGE_REACTION_REMOVED) do(e:Args):
let args = MessageAddRemoveReactionArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.onReactionRemoved(args.messageId, args.emojiId, args.reactionId)
method getMyChatId*(self: Controller): string = method getMyChatId*(self: Controller): string =
return self.chatId return self.chatId

View File

@ -58,6 +58,18 @@ method init*(self: Controller) =
return return
self.delegate.onPinUnpinMessage(args.messageId, false) self.delegate.onPinUnpinMessage(args.messageId, false)
self.events.on(SIGNAL_MESSAGE_REACTION_ADDED) do(e:Args):
let args = MessageAddRemoveReactionArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.onReactionAdded(args.messageId, args.emojiId, args.reactionId)
self.events.on(SIGNAL_MESSAGE_REACTION_REMOVED) do(e:Args):
let args = MessageAddRemoveReactionArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.onReactionRemoved(args.messageId, args.emojiId, args.reactionId)
method getChatId*(self: Controller): string = method getChatId*(self: Controller): string =
return self.chatId return self.chatId
@ -71,20 +83,10 @@ method belongsToCommunity*(self: Controller): bool =
return self.belongsToCommunity return self.belongsToCommunity
method addReaction*(self: Controller, messageId: string, emojiId: int) = method addReaction*(self: Controller, messageId: string, emojiId: int) =
let (res, err) = self.messageService.addReaction(self.chatId, messageId, emojiId) self.messageService.addReaction(self.chatId, messageId, emojiId)
if(err.len != 0):
error "an error has occurred while saving reaction: ", err
return
self.delegate.onReactionAdded(messageId, emojiId, res) method removeReaction*(self: Controller, messageId: string, emojiId: int, reactionId: string) =
self.messageService.removeReaction(reactionId, self.chatId, messageId, emojiId)
method removeReaction*(self: Controller, messageId: string, reactionId: string) =
let (res, err) = self.messageService.removeReaction(reactionId)
if(err.len != 0):
error "an error has occurred while removing reaction: ", err
return
self.delegate.onReactionRemoved(messageId, reactionId)
method pinUnpinMessage*(self: Controller, messageId: string, pin: bool) = method pinUnpinMessage*(self: Controller, messageId: string, pin: bool) =
self.messageService.pinUnpinMessage(self.chatId, messageId, pin) self.messageService.pinUnpinMessage(self.chatId, messageId, pin)

View File

@ -27,7 +27,7 @@ method belongsToCommunity*(self: AccessInterface): bool {.base.} =
method addReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} = method addReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method removeReaction*(self: AccessInterface, messageId: string, reactionId: string) {.base.} = method removeReaction*(self: AccessInterface, messageId: string, emojiId: int, reactionId: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method pinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} = method pinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} =

View File

@ -1,9 +1,10 @@
import NimQml import NimQml, chronicles
import io_interface import io_interface
import ../io_interface as delegate_interface import ../io_interface as delegate_interface
import view, controller import view, controller
import ../../../../shared_models/message_model import ../../../../shared_models/message_model
import ../../../../shared_models/message_item import ../../../../shared_models/message_item
import ../../../../shared_models/message_reaction_item
import ../../../../../global/global_singleton import ../../../../../global/global_singleton
import ../../../../../../app_service/service/contacts/service as contact_service import ../../../../../../app_service/service/contacts/service as contact_service
@ -14,6 +15,9 @@ import eventemitter
export io_interface export io_interface
logScope:
topics = "messages-module"
const CHAT_IDENTIFIER_MESSAGE_ID = "chat-identifier-message-id" const CHAT_IDENTIFIER_MESSAGE_ID = "chat-identifier-message-id"
type type
@ -92,7 +96,14 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
for r in reactions: for r in reactions:
if(r.messageId == m.id): if(r.messageId == m.id):
item.addReaction(r.emojiId, m.`from`, r.id) var emojiIdAsEnum: EmojiId
if(message_reaction_item.toEmojiIdAsEnum(r.emojiId, emojiIdAsEnum)):
let userWhoAddedThisReaction = self.controller.getContactById(r.`from`)
let didIReactWithThisEmoji = userWhoAddedThisReaction.id == singletonInstance.userProfile.getPubKey()
item.addReaction(emojiIdAsEnum, didIReactWithThisEmoji, userWhoAddedThisReaction.id,
userWhoAddedThisReaction.userNameOrAlias(), r.id)
else:
error "wrong emoji id found when loading messages"
for p in pinnedMessages: for p in pinnedMessages:
if(p.message.id == m.id): if(p.message.id == m.id):
@ -109,26 +120,33 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
self.view.model().prependItems(viewItems) self.view.model().prependItems(viewItems)
method toggleReaction*(self: Module, messageId: string, emojiId: int) = method toggleReaction*(self: Module, messageId: string, emojiId: int) =
let item = self.view.model().getItemWithMessageId(messageId) var emojiIdAsEnum: EmojiId
let myName = singletonInstance.userProfile.getName() if(message_reaction_item.toEmojiIdAsEnum(emojiId, emojiIdAsEnum)):
if(item.shouldAddReaction(emojiId, myName)): let item = self.view.model().getItemWithMessageId(messageId)
self.controller.addReaction(messageId, emojiId) let myPublicKey = singletonInstance.userProfile.getPubKey()
if(item.shouldAddReaction(emojiIdAsEnum, myPublicKey)):
self.controller.addReaction(messageId, emojiId)
else:
let reactionId = item.getReactionId(emojiIdAsEnum, myPublicKey)
self.controller.removeReaction(messageId, emojiId, reactionId)
else: else:
let reactionId = item.getReactionId(emojiId, myName) error "wrong emoji id found on reaction added response", emojiId
self.controller.removeReaction(messageId, reactionId)
method onReactionAdded*(self: Module, messageId: string, emojiId: int, reactionId: string) = method onReactionAdded*(self: Module, messageId: string, emojiId: int, reactionId: string) =
let myName = singletonInstance.userProfile.getName() var emojiIdAsEnum: EmojiId
self.view.model().addReaction(messageId, emojiId, myName, reactionId) if(message_reaction_item.toEmojiIdAsEnum(emojiId, emojiIdAsEnum)):
let myPublicKey = singletonInstance.userProfile.getPubKey()
let myName = singletonInstance.userProfile.getName()
self.view.model().addReaction(messageId, emojiIdAsEnum, true, myPublicKey, myName, reactionId)
else:
error "wrong emoji id found on reaction added response", emojiId
method onReactionRemoved*(self: Module, messageId: string, reactionId: string) = method onReactionRemoved*(self: Module, messageId: string, emojiId: int, reactionId: string) =
self.view.model().removeReaction(messageId, reactionId) var emojiIdAsEnum: EmojiId
if(message_reaction_item.toEmojiIdAsEnum(emojiId, emojiIdAsEnum)):
method getNamesReactedWithEmojiIdForMessageId*(self: Module, messageId: string, emojiId: int): seq[string] = self.view.model().removeReaction(messageId, emojiIdAsEnum, reactionId)
let pubKeysForEmojiId = self.view.model().getPubKeysReactedWithEmojiIdForMessageId(messageId, emojiId) else:
for pk in pubKeysForEmojiId: error "wrong emoji id found on reaction remove response", emojiId
let (name, _, _) = self.controller.getContactNameAndImage(pk)
result.add(name)
method pinUnpinMessage*(self: Module, messageId: string, pin: bool) = method pinUnpinMessage*(self: Module, messageId: string, pin: bool) =
self.controller.pinUnpinMessage(messageId, pin) self.controller.pinUnpinMessage(messageId, pin)

View File

@ -7,7 +7,7 @@ method newMessagesLoaded*(self: AccessInterface, messages: seq[MessageDto], reac
method onReactionAdded*(self: AccessInterface, messageId: string, emojiId: int, reactionId: string) {.base.} = method onReactionAdded*(self: AccessInterface, messageId: string, emojiId: int, reactionId: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method onReactionRemoved*(self: AccessInterface, messageId: string, reactionId: string) {.base.} = method onReactionRemoved*(self: AccessInterface, messageId: string, emojiId: int, reactionId: string) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method onPinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} = method onPinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} =

View File

@ -7,10 +7,6 @@ method toggleReaction*(self: AccessInterface, messageId: string, emojiId: int) {
method pinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} = method pinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getNamesReactedWithEmojiIdForMessageId*(self: AccessInterface, messageId: string, emojiId: int): seq[string]
{.base.} =
raise newException(ValueError, "No implementation available")
method getChatType*(self: AccessInterface): int {.base.} = method getChatType*(self: AccessInterface): int {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")

View File

@ -36,9 +36,6 @@ QtObject:
proc toggleReaction*(self: View, messageId: string, emojiId: int) {.slot.} = proc toggleReaction*(self: View, messageId: string, emojiId: int) {.slot.} =
self.delegate.toggleReaction(messageId, emojiId) self.delegate.toggleReaction(messageId, emojiId)
proc getNamesReactedWithEmojiIdForMessageId*(self: View, messageId: string, emojiId: int): string {.slot.} =
return $(%* self.delegate.getNamesReactedWithEmojiIdForMessageId(messageId, emojiId))
proc pinMessage*(self: View, messageId: string) {.slot.} = proc pinMessage*(self: View, messageId: string) {.slot.} =
self.delegate.pinUnpinMessage(messageId, true) self.delegate.pinUnpinMessage(messageId, true)

View File

@ -4,6 +4,7 @@ import ../io_interface as delegate_interface
import view, controller import view, controller
import ../../../shared_models/message_model as pinned_msg_model import ../../../shared_models/message_model as pinned_msg_model
import ../../../shared_models/message_item as pinned_msg_item import ../../../shared_models/message_item as pinned_msg_item
import ../../../shared_models/message_reaction_item as pinned_msg_reaction_item
import ../../../../global/global_singleton import ../../../../global/global_singleton
import input_area/module as input_area_module import input_area/module as input_area_module
@ -151,8 +152,14 @@ proc buildPinnedMessageItem(self: Module, messageId: string, item: var pinned_ms
for r in reactions: for r in reactions:
if(r.messageId == m.id): if(r.messageId == m.id):
# m.`from` should be replaced by appropriate ens/alias when we have that part refactored var emojiIdAsEnum: pinned_msg_reaction_item.EmojiId
item.addReaction(r.emojiId, m.`from`, r.id) if(pinned_msg_reaction_item.toEmojiIdAsEnum(r.emojiId, emojiIdAsEnum)):
let userWhoAddedThisReaction = self.controller.getContactById(r.`from`)
let didIReactWithThisEmoji = userWhoAddedThisReaction.id == singletonInstance.userProfile.getPubKey()
item.addReaction(emojiIdAsEnum, didIReactWithThisEmoji, userWhoAddedThisReaction.id,
userWhoAddedThisReaction.userNameOrAlias(), r.id)
else:
error "wrong emoji id found when loading messages"
return true return true
@ -194,3 +201,19 @@ method onChatMuted*(self: Module) =
method onChatUnmuted*(self: Module) = method onChatUnmuted*(self: Module) =
self.view.setMuted(false) self.view.setMuted(false)
method onReactionAdded*(self: Module, messageId: string, emojiId: int, reactionId: string) =
var emojiIdAsEnum: EmojiId
if(pinned_msg_reaction_item.toEmojiIdAsEnum(emojiId, emojiIdAsEnum)):
let myPublicKey = singletonInstance.userProfile.getPubKey()
let myName = singletonInstance.userProfile.getName()
self.view.pinnedModel().addReaction(messageId, emojiIdAsEnum, true, myPublicKey, myName, reactionId)
else:
error "(pinned) wrong emoji id found on reaction added response", emojiId
method onReactionRemoved*(self: Module, messageId: string, emojiId: int, reactionId: string) =
var emojiIdAsEnum: EmojiId
if(pinned_msg_reaction_item.toEmojiIdAsEnum(emojiId, emojiIdAsEnum)):
self.view.pinnedModel().removeReaction(messageId, emojiIdAsEnum, reactionId)
else:
error "(pinned) wrong emoji id found on reaction remove response", emojiId

View File

@ -14,3 +14,9 @@ method onChatMuted*(self: AccessInterface) {.base.} =
method onChatUnmuted*(self: AccessInterface) {.base.} = method onChatUnmuted*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method onReactionAdded*(self: AccessInterface, messageId: string, emojiId: int, reactionId: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onReactionRemoved*(self: AccessInterface, messageId: string, emojiId: int, reactionId: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,4 +1,6 @@
import Tables, json, strformat import json, strformat
import message_reaction_model, message_reaction_item
type type
ContentType* {.pure.} = enum ContentType* {.pure.} = enum
@ -38,8 +40,7 @@ type
timestamp: int64 timestamp: int64
contentType: ContentType contentType: ContentType
messageType: int messageType: int
reactions: OrderedTable[int, seq[tuple[publicKey: string, reactionId: string]]] # [emojiId, list of [user publicKey reacted with the emojiId, reaction id]] reactionsModel: MessageReactionModel
reactionIds: seq[string]
pinned: bool pinned: bool
proc initItem*(id, responseToMessageWithId, senderId, senderDisplayName, senderLocalName, senderIcon: string, proc initItem*(id, responseToMessageWithId, senderId, senderDisplayName, senderLocalName, senderIcon: string,
@ -62,6 +63,7 @@ proc initItem*(id, responseToMessageWithId, senderId, senderDisplayName, senderL
result.contentType = contentType result.contentType = contentType
result.messageType = messageType result.messageType = messageType
result.pinned = false result.pinned = false
result.reactionsModel = newMessageReactionModel()
proc `$`*(self: Item): string = proc `$`*(self: Item): string =
result = fmt"""Item( result = fmt"""Item(
@ -79,7 +81,8 @@ proc `$`*(self: Item): string =
timestamp:{$self.timestamp}, timestamp:{$self.timestamp},
contentType:{$self.contentType.int}, contentType:{$self.contentType.int},
messageType:{$self.messageType}, messageType:{$self.messageType},
pinned:{$self.pinned} pinned:{$self.pinned},
messageReactions: [{$self.reactionsModel}]
)""" )"""
proc id*(self: Item): string {.inline.} = proc id*(self: Item): string {.inline.} =
@ -139,67 +142,21 @@ proc pinned*(self: Item): bool {.inline.} =
proc `pinned=`*(self: Item, value: bool) {.inline.} = proc `pinned=`*(self: Item, value: bool) {.inline.} =
self.pinned = value self.pinned = value
proc shouldAddReaction*(self: Item, emojiId: int, publicKey: string): bool = proc reactionsModel*(self: Item): MessageReactionModel {.inline.} =
for k, values in self.reactions: self.reactionsModel
if(k != emojiId):
continue
for t in values: proc shouldAddReaction*(self: Item, emojiId: EmojiId, userPublicKey: string): bool =
if(t.publicKey == publicKey): return self.reactionsModel.shouldAddReaction(emojiId, userPublicKey)
return false
return true proc getReactionId*(self: Item, emojiId: EmojiId, userPublicKey: string): string =
return self.reactionsModel.getReactionId(emojiId, userPublicKey)
proc getReactionId*(self: Item, emojiId: int, publicKey: string): string = proc addReaction*(self: Item, emojiId: EmojiId, didIReactWithThisEmoji: bool, userPublicKey: string,
for k, values in self.reactions: userDisplayName: string, reactionId: string) =
if(k != emojiId): self.reactionsModel.addReaction(emojiId, didIReactWithThisEmoji, userPublicKey, userDisplayName, reactionId)
continue
for t in values: proc removeReaction*(self: Item, emojiId: EmojiId, reactionId: string) =
if(t.publicKey == publicKey): self.reactionsModel.removeReaction(emojiId, reactionId)
return t.reactionId
# we should never be here, since this is a controlled call
return ""
proc addReaction*(self: Item, emojiId: int, publicKey: string, reactionId: string) =
if(not self.reactions.contains(emojiId)):
self.reactions[emojiId] = @[]
self.reactions[emojiId].add((publicKey, reactionId))
proc removeReaction*(self: Item, reactionId: string) =
var key = -1
var index = -1
for k, values in self.reactions:
var i = -1
for t in values:
i += 1
if(t.reactionId == reactionId):
key = k
index = i
break
if(key == -1 or index == -1):
return
self.reactions[key].del(index)
if(self.reactions[key].len == 0):
self.reactions.del(key)
proc getPubKeysReactedWithEmojiId*(self: Item, emojiId: int): seq[string] =
if(not self.reactions.contains(emojiId)):
return
for v in self.reactions[emojiId]:
result.add(v.publicKey)
proc getCountsForReactions*(self: Item): seq[JsonNode] =
for k, v in self.reactions:
if(self.reactions[k].len == 0):
continue
result.add(%* {"emojiId": k, "counts": v.len})
proc toJsonNode*(self: Item): JsonNode = proc toJsonNode*(self: Item): JsonNode =
result = %* { result = %* {

View File

@ -1,6 +1,6 @@
import NimQml, Tables, json, strutils, strformat import NimQml, Tables, json, strutils, strformat
import message_item import message_item, message_reaction_item
type type
ModelRole {.pure.} = enum ModelRole {.pure.} = enum
@ -24,7 +24,7 @@ type
# GapFrom # GapFrom
# GapTo # GapTo
Pinned Pinned
CountsForReactions Reactions
QtObject: QtObject:
type type
@ -80,7 +80,7 @@ QtObject:
# ModelRole.GapFrom.int:"gapFrom", # ModelRole.GapFrom.int:"gapFrom",
# ModelRole.GapTo.int:"gapTo", # ModelRole.GapTo.int:"gapTo",
ModelRole.Pinned.int:"pinned", ModelRole.Pinned.int:"pinned",
ModelRole.CountsForReactions.int:"countsForReactions", ModelRole.Reactions.int:"reactions",
}.toTable }.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant = method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -134,8 +134,8 @@ QtObject:
# result = newQVariant(item.gapTo) # result = newQVariant(item.gapTo)
of ModelRole.Pinned: of ModelRole.Pinned:
result = newQVariant(item.pinned) result = newQVariant(item.pinned)
of ModelRole.CountsForReactions: of ModelRole.Reactions:
result = newQVariant($(%* item.getCountsForReactions)) result = newQVariant(item.reactionsModel)
proc findIndexForMessageId(self: Model, messageId: string): int = proc findIndexForMessageId(self: Model, messageId: string): int =
for i in 0 ..< self.items.len: for i in 0 ..< self.items.len:
@ -187,30 +187,26 @@ QtObject:
return self.items[ind] return self.items[ind]
proc addReaction*(self: Model, messageId: string, emojiId: int, name: string, reactionId: string) = proc addReaction*(self: Model, messageId: string, emojiId: EmojiId, didIReactWithThisEmoji: bool,
userPublicKey: string, userDisplayName: string, reactionId: string) =
let ind = self.findIndexForMessageId(messageId) let ind = self.findIndexForMessageId(messageId)
if(ind == -1): if(ind == -1):
return return
self.items[ind].addReaction(emojiId, name, reactionId) self.items[ind].addReaction(emojiId, didIReactWithThisEmoji, userPublicKey, userDisplayName, reactionId)
let index = self.createIndex(ind, 0, nil) let index = self.createIndex(ind, 0, nil)
self.dataChanged(index, index, @[ModelRole.CountsForReactions.int]) self.dataChanged(index, index, @[ModelRole.Reactions.int])
proc removeReaction*(self: Model, messageId: string, reactionId: string) = proc removeReaction*(self: Model, messageId: string, emojiId: EmojiId, reactionId: string) =
let ind = self.findIndexForMessageId(messageId) let ind = self.findIndexForMessageId(messageId)
if(ind == -1): if(ind == -1):
return return
self.items[ind].removeReaction(reactionId) self.items[ind].removeReaction(emojiId, reactionId)
let index = self.createIndex(ind, 0, nil) let index = self.createIndex(ind, 0, nil)
self.dataChanged(index, index, @[ModelRole.CountsForReactions.int]) self.dataChanged(index, index, @[ModelRole.Reactions.int])
proc getPubKeysReactedWithEmojiIdForMessageId*(self: Model, messageId: string, emojiId: int): seq[string] =
for i in 0 ..< self.items.len:
if(self.items[i].id == messageId):
return self.items[i].getPubKeysReactedWithEmojiId(emojiId)
proc pinUnpinMessage*(self: Model, messageId: string, pin: bool) = proc pinUnpinMessage*(self: Model, messageId: string, pin: bool) =
let ind = self.findIndexForMessageId(messageId) let ind = self.findIndexForMessageId(messageId)

View File

@ -0,0 +1,88 @@
import json, strformat
type
EmojiId* {.pure.} = enum
Heart = 1,
Thumbsup,
Thumbsdown,
Laughing,
Cry,
Angry
type
ReactionDetails* = object
publicKey: string
displayName: string
reactionId: string
type
MessageReactionItem* = object
emojiId: EmojiId
didIReactWithThisEmoji: bool
reactions: seq[ReactionDetails]
proc toEmojiIdAsEnum*(emojiId: int, emojiIdAsEnum: var EmojiId): bool =
if(emojiId >= ord(low(EmojiId)) or emojiId <= ord(high(EmojiId))):
emojiIdAsEnum = EmojiId(emojiId)
return true
return false
proc initMessageReactionItem*(emojiId: EmojiId): MessageReactionItem =
result.emojiId = emojiId
proc `$`*(self: MessageReactionItem): string =
var reactions = ""
for r in self.reactions:
reactions = reactions & "displayName: " & r.displayName & " publicKey: " & r.publicKey & " reactionId: " &
r.reactionId & "\n"
result = fmt"""MessageReactionItem(
emojiId: {self.emojiId},
didIReactWithThisEmoji: {self.didIReactWithThisEmoji},
reactionsCount: {self.reactions.len},
reactions: {reactions}
]"""
proc emojiId*(self: MessageReactionItem): EmojiId {.inline.} =
self.emojiId
proc didIReactWithThisEmoji*(self: MessageReactionItem): bool {.inline.} =
self.didIReactWithThisEmoji
proc numberOfReactions*(self: MessageReactionItem): int {.inline.} =
self.reactions.len
proc jsonArrayOfUsersReactedWithThisEmoji*(self: MessageReactionItem): JsonNode {.inline.} =
var users: seq[string]
for r in self.reactions:
users.add(r.displayName)
return %* users
proc shouldAddReaction*(self: MessageReactionItem, userPublicKey: string): bool =
for r in self.reactions:
if (r.publicKey == userPublicKey):
return false
return true
proc getReactionId*(self: MessageReactionItem, userPublicKey: string): string =
for r in self.reactions:
if (r.publicKey == userPublicKey):
return r.reactionId
return ""
proc addReaction*(self: var MessageReactionItem, didIReactWithThisEmoji: bool, userPublicKey: string,
userDisplayName: string, reactionId: string) =
self.didIReactWithThisEmoji = didIReactWithThisEmoji
self.reactions.add(ReactionDetails(publicKey: userPublicKey, displayName: userDisplayName, reactionId: reactionId))
proc removeReaction*(self: var MessageReactionItem, reactionId: string) =
var index = -1
for i in 0..<self.reactions.len:
if (self.reactions[i].reactionId == reactionId):
index = i
break
if(index == -1):
return
self.reactions.delete(index)

View File

@ -0,0 +1,146 @@
import NimQml, Tables, json, strutils, strformat
import message_reaction_item
type
ModelRole {.pure.} = enum
EmojiId = UserRole + 1
DidIReactWithThisEmoji
NumberOfReactions
JsonArrayOfUsersReactedWithThisEmoji
QtObject:
type
MessageReactionModel* = ref object of QAbstractListModel
items: seq[MessageReactionItem]
proc delete(self: MessageReactionModel) =
self.items = @[]
self.QAbstractListModel.delete
proc setup(self: MessageReactionModel) =
self.QAbstractListModel.setup
proc newMessageReactionModel*(): MessageReactionModel =
new(result, delete)
result.setup
proc `$`*(self: MessageReactionModel): string =
for i in 0 ..< self.items.len:
result &= fmt"""
[{i}]:({$self.items[i]})
"""
proc countChanged(self: MessageReactionModel) {.signal.}
proc getCount(self: MessageReactionModel): int {.slot.} =
self.items.len
QtProperty[int] count:
read = getCount
notify = countChanged
method rowCount(self: MessageReactionModel, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: MessageReactionModel): Table[int, string] =
{
ModelRole.EmojiId.int:"emojiId",
ModelRole.DidIReactWithThisEmoji.int:"didIReactWithThisEmoji",
ModelRole.NumberOfReactions.int:"numberOfReactions",
ModelRole.JsonArrayOfUsersReactedWithThisEmoji.int: "jsonArrayOfUsersReactedWithThisEmoji"
}.toTable
method data(self: MessageReactionModel, index: QModelIndex, role: int): QVariant =
if (not index.isValid):
return
if (index.row < 0 or index.row >= self.items.len):
return
let item = self.items[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.EmojiId:
result = newQVariant(item.emojiId.int)
of ModelRole.DidIReactWithThisEmoji:
result = newQVariant(item.didIReactWithThisEmoji)
of ModelRole.NumberOfReactions:
result = newQVariant(item.numberOfReactions)
of ModelRole.JsonArrayOfUsersReactedWithThisEmoji:
# Would be good if we could return QVariant of array (seq) here, but it's not supported in our NimQml,
# because of that we're returning json array as a string.
result = newQVariant($item.jsonArrayOfUsersReactedWithThisEmoji)
proc reactionItemWithEmojiIdExists(self: MessageReactionModel, emojiId: EmojiId): bool =
for it in self.items:
if(it.emojiId == emojiId):
return true
return false
proc getIndexOfTheItemWithEmojiId(self: MessageReactionModel, emojiId: EmojiId): int =
for i in 0..<self.items.len:
if(self.items[i].emojiId == emojiId):
return i
return -1
proc findPositionForTheItemWithEmojiId(self: MessageReactionModel, emojiId: EmojiId): int =
if(self.items.len == 0):
return 0
for i in 0..<self.items.len:
if(emojiId < self.items[i].emojiId):
return i
return self.items.len
proc shouldAddReaction*(self: MessageReactionModel, emojiId: EmojiId, userPublicKey: string): bool =
let ind = self.getIndexOfTheItemWithEmojiId(emojiId)
if(ind == -1):
return true
return self.items[ind].shouldAddReaction(userPublicKey)
proc getReactionId*(self: MessageReactionModel, emojiId: EmojiId, userPublicKey: string): string =
let ind = self.getIndexOfTheItemWithEmojiId(emojiId)
if(ind == -1):
return ""
return self.items[ind].getReactionId(userPublicKey)
proc addReaction*(self: MessageReactionModel, emojiId: EmojiId, didIReactWithThisEmoji: bool, userPublicKey: string,
userDisplayName: string, reactionId: string) =
if(self.reactionItemWithEmojiIdExists(emojiId)):
let ind = self.getIndexOfTheItemWithEmojiId(emojiId)
if(ind == -1):
return
self.items[ind].addReaction(didIReactWithThisEmoji, userPublicKey, userDisplayName, reactionId)
let index = self.createIndex(ind, 0, nil)
self.dataChanged(index, index, @[]) # all roles
else:
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
var item = initMessageReactionItem(emojiId)
item.addReaction(didIReactWithThisEmoji, userPublicKey, userDisplayName, reactionId)
let position = self.findPositionForTheItemWithEmojiId(emojiId) # Model should maintain items based on the emoji id.
self.beginInsertRows(parentModelIndex, position, position)
self.items.insert(item, position)
self.endInsertRows()
self.countChanged()
proc removeReaction*(self: MessageReactionModel, emojiId: EmojiId, reactionId: string) =
let ind = self.getIndexOfTheItemWithEmojiId(emojiId)
if(ind == -1):
return
self.items[ind].removeReaction(reactionId)
if(self.items[ind].numberOfReactions() == 0):
# remove item if there are no reactions for this emoji id
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginRemoveRows(parentModelIndex, ind, ind)
self.items.delete(ind)
self.endRemoveRows()
self.countChanged()

View File

@ -26,6 +26,8 @@ const SIGNAL_MESSAGE_PINNED* = "new-messagePinned"
const SIGNAL_MESSAGE_UNPINNED* = "new-messageUnpinned" const SIGNAL_MESSAGE_UNPINNED* = "new-messageUnpinned"
const SIGNAL_SEARCH_MESSAGES_LOADED* = "new-searchMessagesLoaded" const SIGNAL_SEARCH_MESSAGES_LOADED* = "new-searchMessagesLoaded"
const SIGNAL_MESSAGES_MARKED_AS_READ* = "new-messagesMarkedAsRead" const SIGNAL_MESSAGES_MARKED_AS_READ* = "new-messagesMarkedAsRead"
const SIGNAL_MESSAGE_REACTION_ADDED* = "new-messageReactionAdded"
const SIGNAL_MESSAGE_REACTION_REMOVED* = "new-messageReactionRemoved"
type type
SearchMessagesLoadedArgs* = ref object of Args SearchMessagesLoadedArgs* = ref object of Args
@ -46,6 +48,12 @@ type
allMessagesMarked*: bool allMessagesMarked*: bool
messagesIds*: seq[string] messagesIds*: seq[string]
MessageAddRemoveReactionArgs* = ref object of Args
chatId*: string
messageId*: string
emojiId*: int
reactionId*: string
QtObject: QtObject:
type Service* = ref object of QObject type Service* = ref object of QObject
events: EventEmitter events: EventEmitter
@ -157,14 +165,13 @@ QtObject:
self.asyncLoadMoreMessagesForChat(chatId) self.asyncLoadMoreMessagesForChat(chatId)
proc addReaction*(self: Service, chatId: string, messageId: string, emojiId: int): proc addReaction*(self: Service, chatId: string, messageId: string, emojiId: int) =
tuple[result: string, error: string] =
try: try:
let response = status_go.addReaction(chatId, messageId, emojiId) let response = status_go.addReaction(chatId, messageId, emojiId)
result.error = "response doesn't contain \"error\""
if(response.result.contains("error")): if(response.result.contains("error")):
result.error = response.result["error"].getStr let errMsg = response.result["error"].getStr
error "error: ", methodName="addReaction", errDesription = errMsg
return return
var reactionsArr: JsonNode var reactionsArr: JsonNode
@ -172,24 +179,31 @@ QtObject:
if(response.result.getProp("emojiReactions", reactionsArr)): if(response.result.getProp("emojiReactions", reactionsArr)):
reactions = map(reactionsArr.getElems(), proc(x: JsonNode): ReactionDto = x.toReactionDto()) reactions = map(reactionsArr.getElems(), proc(x: JsonNode): ReactionDto = x.toReactionDto())
var reactionId: string
if(reactions.len > 0): if(reactions.len > 0):
result.result = reactions[0].id reactionId = reactions[0].id
let data = MessageAddRemoveReactionArgs(chatId: chatId, messageId: messageId, emojiId: emojiId,
reactionId: reactionId)
self.events.emit(SIGNAL_MESSAGE_REACTION_ADDED, data)
except Exception as e: except Exception as e:
result.error = e.msg
error "error: ", methodName="addReaction", errName = e.name, errDesription = e.msg error "error: ", methodName="addReaction", errName = e.name, errDesription = e.msg
proc removeReaction*(self: Service, reactionId: string): tuple[result: string, error: string] = proc removeReaction*(self: Service, reactionId: string, chatId: string, messageId: string, emojiId: int) =
try: try:
let response = status_go.removeReaction(reactionId) let response = status_go.removeReaction(reactionId)
result.error = "response doesn't contain \"error\""
if(response.result.contains("error")): if(response.result.contains("error")):
result.error = response.result["error"].getStr let errMsg = response.result["error"].getStr
error "error: ", methodName="removeReaction", errDesription = errMsg
return return
let data = MessageAddRemoveReactionArgs(chatId: chatId, messageId: messageId, emojiId: emojiId,
reactionId: reactionId)
self.events.emit(SIGNAL_MESSAGE_REACTION_REMOVED, data)
except Exception as e: except Exception as e:
result.error = e.msg
error "error: ", methodName="removeReaction", errName = e.name, errDesription = e.msg error "error: ", methodName="removeReaction", errName = e.name, errDesription = e.msg
proc pinUnpinMessage*(self: Service, chatId: string, messageId: string, pin: bool) = proc pinUnpinMessage*(self: Service, chatId: string, messageId: string, pin: bool) =

View File

@ -125,6 +125,7 @@ ModalPopup {
messageOutgoingStatus: model.outgoingStatus messageOutgoingStatus: model.outgoingStatus
messageContentType: model.contentType messageContentType: model.contentType
pinnedMessage: model.pinned pinnedMessage: model.pinned
reactionsModel: model.reactions
// This is possible since we have all data loaded before we load qml. // This is possible since we have all data loaded before we load qml.
// When we fetch messages to fulfill a gap we have to set them at once. // When we fetch messages to fulfill a gap we have to set them at once.
@ -163,6 +164,7 @@ ModalPopup {
} }
MessageContextMenuView { MessageContextMenuView {
id: msgContextMenu id: msgContextMenu
reactionModel: root.rootStore.emojiReactionsModel
pinnedPopup: true pinnedPopup: true
pinnedMessage: true pinnedMessage: true
onShouldCloseParentPopup: { onShouldCloseParentPopup: {
@ -176,6 +178,10 @@ ModalPopup {
onUnpinMessage: { onUnpinMessage: {
popup.messageStore.unpinMessage(messageId) popup.messageStore.unpinMessage(messageId)
} }
onToggleReaction: {
popup.messageStore.toggleReaction(messageId, emojiId)
}
} }
} }

View File

@ -77,4 +77,57 @@ QtObject {
return messageModule.unpinMessage(messageId) return messageModule.unpinMessage(messageId)
} }
function toggleReaction(messageId, emojiId) {
if(!messageModule)
return
return messageModule.toggleReaction(messageId, emojiId)
}
function lastTwoItems(nodes) {
//% " and "
return nodes.join(qsTrId("-and-"));
}
function showReactionAuthors(jsonArrayOfUsersReactedWithThisEmoji, emojiId) {
let listOfUsers = JSON.parse(jsonArrayOfUsersReactedWithThisEmoji)
if (listOfUsers.error) {
console.error("error parsing users who reacted to a message, error: ", obj.error)
return
}
let tooltip
if (listOfUsers.length === 1) {
tooltip = listOfUsers[0]
} else if (listOfUsers.length === 2) {
tooltip = lastTwoItems(listOfUsers);
} else {
var leftNode = [];
var rightNode = [];
const maxReactions = 12
let maximum = Math.min(maxReactions, listOfUsers.length)
if (listOfUsers.length > maxReactions) {
leftNode = listOfUsers.slice(0, maxReactions);
rightNode = listOfUsers.slice(maxReactions, listOfUsers.length);
return (rightNode.length === 1) ?
lastTwoItems([leftNode.join(", "), rightNode[0]]) :
//% "%1 more"
lastTwoItems([leftNode.join(", "), qsTrId("-1-more").arg(rightNode.length)]);
}
leftNode = listOfUsers.slice(0, maximum - 1);
rightNode = listOfUsers.slice(maximum - 1, listOfUsers.length);
tooltip = lastTwoItems([leftNode.join(", "), rightNode[0]])
}
//% " reacted with "
tooltip += qsTrId("-reacted-with-");
let emojiHtml = Emoji.getEmojiFromId(emojiId);
if (emojiHtml) {
tooltip += emojiHtml;
}
return tooltip
}
} }

View File

@ -67,46 +67,6 @@ QtObject {
// chatsModelInst.messageView.deleteMessage(messageId); // chatsModelInst.messageView.deleteMessage(messageId);
} }
function lastTwoItems(nodes) {
//% " and "
return nodes.join(qsTrId("-and-"));
}
function showReactionAuthors(fromAccounts, emojiId) {
let tooltip
if (fromAccounts.length === 1) {
tooltip = fromAccounts[0]
} else if (fromAccounts.length === 2) {
tooltip = lastTwoItems(fromAccounts);
} else {
var leftNode = [];
var rightNode = [];
const maxReactions = 12
let maximum = Math.min(maxReactions, fromAccounts.length)
if (fromAccounts.length > maxReactions) {
leftNode = fromAccounts.slice(0, maxReactions);
rightNode = fromAccounts.slice(maxReactions, fromAccounts.length);
return (rightNode.length === 1) ?
lastTwoItems([leftNode.join(", "), rightNode[0]]) :
//% "%1 more"
lastTwoItems([leftNode.join(", "), qsTrId("-1-more").arg(rightNode.length)]);
}
leftNode = fromAccounts.slice(0, maximum - 1);
rightNode = fromAccounts.slice(maximum - 1, fromAccounts.length);
tooltip = lastTwoItems([leftNode.join(", "), rightNode[0]])
}
//% " reacted with "
tooltip += qsTrId("-reacted-with-");
let emojiHtml = Emoji.getEmojiFromId(emojiId);
if (emojiHtml) {
tooltip += emojiHtml;
}
return tooltip
}
function getCommunity(communityId) { function getCommunity(communityId) {
// Not Refactored Yet // Not Refactored Yet
// try { // try {

View File

@ -207,6 +207,10 @@ ColumnLayout {
messageToPin: messageId messageToPin: messageId
}) })
} }
onToggleReaction: {
messageStore.toggleReaction(messageId, emojiId)
}
} }
StatusImageModal { StatusImageModal {

View File

@ -312,6 +312,7 @@ Item {
messageOutgoingStatus: model.outgoingStatus messageOutgoingStatus: model.outgoingStatus
messageContentType: model.contentType messageContentType: model.contentType
pinnedMessage: model.pinned pinnedMessage: model.pinned
reactionsModel: model.reactions
// This is possible since we have all data loaded before we load qml. // This is possible since we have all data loaded before we load qml.
// When we fetch messages to fulfill a gap we have to set them at once. // When we fetch messages to fulfill a gap we have to set them at once.

View File

@ -19,6 +19,7 @@ Item {
signal toggleReaction(int emojiID) signal toggleReaction(int emojiID)
signal setMessageActive(string messageId, bool active) signal setMessageActive(string messageId, bool active)
property var store
property bool isCurrentUser property bool isCurrentUser
property var emojiReactionsModel property var emojiReactionsModel
property bool isMessageActive property bool isMessageActive
@ -38,14 +39,14 @@ Item {
width: emojiImage.width + emojiCount.width + (root.imageMargin * 2) + + 8 width: emojiImage.width + emojiCount.width + (root.imageMargin * 2) + + 8
height: 20 height: 20
radius: 10 radius: 10
color: modelData.currentUserReacted ? color: model.didIReactWithThisEmoji ?
(isHovered ? Style.current.emojiReactionActiveBackgroundHovered : Style.current.secondaryBackground) : (isHovered ? Style.current.emojiReactionActiveBackgroundHovered : Style.current.secondaryBackground) :
(isHovered ? Style.current.emojiReactionBackgroundHovered : Style.current.emojiReactionBackground) (isHovered ? Style.current.emojiReactionBackgroundHovered : Style.current.emojiReactionBackground)
StatusQ.StatusToolTip { StatusQ.StatusToolTip {
visible: mouseArea.containsMouse visible: mouseArea.containsMouse
maxWidth: 400 maxWidth: 400
text: showReactionAuthors(modelData.fromAccounts, modelData.emojiId) text: root.store.showReactionAuthors(model.jsonArrayOfUsersReactedWithThisEmoji, model.emojiId)
} }
// Rounded corner to cover one corner // Rounded corner to cover one corner
@ -64,7 +65,7 @@ Item {
// This is a workaround to get a "border" around the rectangle including the weird rectangle // This is a workaround to get a "border" around the rectangle including the weird rectangle
Loader { Loader {
active: modelData.currentUserReacted active: model.didIReactWithThisEmoji
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: -1 anchors.topMargin: -1
anchors.left: parent.left anchors.left: parent.left
@ -100,7 +101,7 @@ Item {
height: 15 height: 15
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: { source: {
switch (modelData.emojiId) { switch (model.emojiId) {
case 1: return Style.svg("emojiReactions/heart") case 1: return Style.svg("emojiReactions/heart")
case 2: return Style.svg("emojiReactions/thumbsUp") case 2: return Style.svg("emojiReactions/thumbsUp")
case 3: return Style.svg("emojiReactions/thumbsDown") case 3: return Style.svg("emojiReactions/thumbsDown")
@ -117,12 +118,12 @@ Item {
StyledText { StyledText {
id: emojiCount id: emojiCount
text: modelData.count text: model.numberOfReactions
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: emojiImage.right anchors.left: emojiImage.right
anchors.leftMargin: root.imageMargin anchors.leftMargin: root.imageMargin
font.pixelSize: 12 font.pixelSize: 12
color: modelData.currentUserReacted ? Style.current.textColorTertiary : Style.current.textColor color: model.didIReactWithThisEmoji ? Style.current.textColorTertiary : Style.current.textColor
} }
MouseArea { MouseArea {
@ -139,7 +140,7 @@ Item {
emojiContainer.isHovered = false emojiContainer.isHovered = false
} }
onClicked: { onClicked: {
toggleReaction(modelData.emojiId) toggleReaction(model.emojiId)
} }
} }
} }

View File

@ -648,7 +648,7 @@ Item {
Loader { Loader {
id: emojiReactionLoader id: emojiReactionLoader
active: emojiReactionsModel.length active: reactionsModel.count > 0
anchors.bottom: messageContainer.bottom anchors.bottom: messageContainer.bottom
anchors.bottomMargin: Style.current.halfPadding anchors.bottomMargin: Style.current.halfPadding
anchors.left: messageContainer.left anchors.left: messageContainer.left
@ -657,7 +657,8 @@ Item {
sourceComponent: Component { sourceComponent: Component {
EmojiReactionsPanel { EmojiReactionsPanel {
id: emojiRect id: emojiRect
emojiReactionsModel: emojiReactionsModel store: messageStore
emojiReactionsModel: reactionsModel
onHoverChanged: { onHoverChanged: {
setHovered(messageId, hovered) setHovered(messageId, hovered)
} }
@ -670,8 +671,8 @@ Item {
root.messageContextMenu.setXPosition = function() { return (root.messageContextMenu.parent.x + 4)} root.messageContextMenu.setXPosition = function() { return (root.messageContextMenu.parent.x + 4)}
root.messageContextMenu.setYPosition = function() { return (-root.messageContextMenu.height - 4)} root.messageContextMenu.setYPosition = function() { return (-root.messageContextMenu.height - 4)}
} }
// Not Refactored Yet
// onToggleReaction: rootStore.chatsModelInst.toggleReaction(messageId, emojiID) onToggleReaction: messageStore.toggleReaction(messageId, emojiID)
onSetMessageActive: { onSetMessageActive: {
setMessageActive(messageId, active);; setMessageActive(messageId, active);;

View File

@ -57,6 +57,7 @@ StatusPopupMenu {
signal shouldCloseParentPopup() signal shouldCloseParentPopup()
signal createOneToOneChat(string chatId, string ensName) signal createOneToOneChat(string chatId, string ensName)
signal showReplyArea() signal showReplyArea()
signal toggleReaction(string messageId, int emojiId)
onHeightChanged: { onHeightChanged: {
root.y = setYPosition() root.y = setYPosition()
@ -85,8 +86,7 @@ StatusPopupMenu {
emojiId: model.emojiId emojiId: model.emojiId
reactedByUser: !!root.emojiReactionsReactedByUser[model.emojiId] reactedByUser: !!root.emojiReactionsReactedByUser[model.emojiId]
onCloseModal: { onCloseModal: {
// Not Refactored Yet root.toggleReaction(root.messageId, emojiId)
// chatsModel.toggleReaction(SelectedMessage.messageId, emojiId)
root.close() root.close()
} }
} }

View File

@ -33,6 +33,7 @@ Column {
property string messageOutgoingStatus: "" property string messageOutgoingStatus: ""
property int messageContentType: 1 property int messageContentType: 1
property bool pinnedMessage: false property bool pinnedMessage: false
property var reactionsModel
property int prevMessageIndex: -1 property int prevMessageIndex: -1
property var prevMessageAsJsonObj property var prevMessageAsJsonObj
@ -129,40 +130,6 @@ Column {
property var imageClick: function () {} property var imageClick: function () {}
property var scrollToBottom: function () {} property var scrollToBottom: function () {}
property var emojiReactionsModel: {
// Not Refactored Yet
return []
// if (!emojiReactions) {
// return []
// }
// try {
// // group by id
// var allReactions = Object.values(JSON.parse(emojiReactions))
// var byEmoji = {}
// allReactions.forEach(function (reaction) {
// if (!byEmoji[reaction.emojiId]) {
// byEmoji[reaction.emojiId] = {
// emojiId: reaction.emojiId,
// fromAccounts: [],
// count: 0,
// currentUserReacted: false
// }
// }
// byEmoji[reaction.emojiId].count++;
// byEmoji[reaction.emojiId].fromAccounts.push(root.chatsModel.userNameOrAlias(reaction.from));
// if (!byEmoji[reaction.emojiId].currentUserReacted && reaction.from === userProfile.pubKey) {
// byEmoji[reaction.emojiId].currentUserReacted = true
// }
// })
// return Object.values(byEmoji)
// } catch (e) {
// console.error('Error parsing emoji reactions', e)
// return []
// }
}
property var clickMessage: function(isProfileClick, property var clickMessage: function(isProfileClick,
isSticker = false, isSticker = false,
isImage = false, isImage = false,
@ -378,8 +345,7 @@ Column {
// root.parent.clickMessage(isProfileClick, isSticker, isImage, image, emojiOnly, hideEmojiPicker, isReply); // root.parent.clickMessage(isProfileClick, isSticker, isImage, image, emojiOnly, hideEmojiPicker, isReply);
} }
onSetMessageActive: { onSetMessageActive: {
// Not Refactored Yet - Should do it via messageStore root.setMessageActive(messageId, active);
// root.messageStore.setMessageActive(messageId, active);;
} }
} }
} }

View File

@ -180,8 +180,10 @@ MouseArea {
Component { Component {
id: emojiReactionsComponent id: emojiReactionsComponent
EmojiReactionsPanel { EmojiReactionsPanel {
// isMessageActive: root.isMessageActive store: messageStore
// emojiReactionsModel: root.emojiReactionsModel emojiReactionsModel: reactionsModel
isMessageActive: isMessageActive
isCurrentUser: isCurrentUser
onAddEmojiClicked: { onAddEmojiClicked: {
root.addEmoji(false, false, false, null, true, false); root.addEmoji(false, false, false, null, true, false);
// Set parent, X & Y positions for the messageContextMenu // Set parent, X & Y positions for the messageContextMenu
@ -189,12 +191,12 @@ MouseArea {
messageContextMenu.setXPosition = function() { return (messageContextMenu.parent.x + 4)} messageContextMenu.setXPosition = function() { return (messageContextMenu.parent.x + 4)}
messageContextMenu.setYPosition = function() { return (-messageContextMenu.height - 4)} messageContextMenu.setYPosition = function() { return (-messageContextMenu.height - 4)}
} }
// Not Refactored Yet
// onToggleReaction: chatsModel.toggleReaction(messageId, emojiID)
// onSetMessageActive: { onToggleReaction: messageStore.toggleReaction(messageId, emojiID)
// root.setMessageActive(messageId, active);;
// } onSetMessageActive: {
root.setMessageActive(messageId, active);
}
} }
} }