refactor(@desktop/chat-communities): messages added
Messages model added, not completely done. Also convenient methods for add/remove reactions are added.
This commit is contained in:
parent
12f9282a59
commit
9777191501
|
@ -7,23 +7,27 @@ import ../../../../../app_service/service/chat/service as chat_service
|
|||
import ../../../../../app_service/service/community/service as community_service
|
||||
import ../../../../../app_service/service/message/service as message_service
|
||||
|
||||
import eventemitter
|
||||
import status/[signals]
|
||||
|
||||
export controller_interface
|
||||
|
||||
type
|
||||
Controller* = ref object of controller_interface.AccessInterface
|
||||
delegate: io_interface.AccessInterface
|
||||
events: EventEmitter
|
||||
chatId: string
|
||||
belongsToCommunity: bool
|
||||
chatService: chat_service.ServiceInterface
|
||||
communityService: community_service.ServiceInterface
|
||||
messageService: message_service.Service
|
||||
|
||||
proc newController*(delegate: io_interface.AccessInterface, chatId: string, belongsToCommunity: bool,
|
||||
chatService: chat_service.ServiceInterface,
|
||||
communityService: community_service.ServiceInterface,
|
||||
messageService: message_service.Service): Controller =
|
||||
proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter, chatId: string,
|
||||
belongsToCommunity: bool, chatService: chat_service.ServiceInterface,
|
||||
communityService: community_service.ServiceInterface, messageService: message_service.Service): Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.chatId = chatId
|
||||
result.belongsToCommunity = belongsToCommunity
|
||||
result.chatService = chatService
|
||||
|
@ -34,7 +38,13 @@ method delete*(self: Controller) =
|
|||
discard
|
||||
|
||||
method init*(self: Controller) =
|
||||
discard
|
||||
self.events.on(SIGNAL_MESSAGES_LOADED) do(e:Args):
|
||||
let args = MessagesLoadedArgs(e)
|
||||
if(self.chatId != args.chatId):
|
||||
return
|
||||
|
||||
# We should handle only pinned messages within this module.
|
||||
echo "ChatContent...RECEIVED MESSAGES: ", repr(args)
|
||||
|
||||
method getChatId*(self: Controller): string =
|
||||
return self.chatId
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import chronicles
|
||||
import controller_interface
|
||||
import io_interface
|
||||
|
||||
|
@ -9,6 +10,9 @@ import status/[signals]
|
|||
|
||||
export controller_interface
|
||||
|
||||
logScope:
|
||||
topics = "messages-controller"
|
||||
|
||||
type
|
||||
Controller* = ref object of controller_interface.AccessInterface
|
||||
delegate: io_interface.AccessInterface
|
||||
|
@ -34,10 +38,29 @@ method delete*(self: Controller) =
|
|||
method init*(self: Controller) =
|
||||
self.events.on(SIGNAL_MESSAGES_LOADED) do(e:Args):
|
||||
let args = MessagesLoadedArgs(e)
|
||||
echo "RECEIVED MESSAGES ASYNC: ", repr(args)
|
||||
if(self.chatId != args.chatId):
|
||||
return
|
||||
|
||||
self.delegate.newMessagesLoaded(args.messages, args.reactions)
|
||||
|
||||
method getChatId*(self: Controller): string =
|
||||
return self.chatId
|
||||
|
||||
method belongsToCommunity*(self: Controller): bool =
|
||||
return self.belongsToCommunity
|
||||
return self.belongsToCommunity
|
||||
|
||||
method addReaction*(self: Controller, messageId: string, emojiId: int) =
|
||||
let (res, err) = 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, 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)
|
|
@ -16,4 +16,8 @@ method getChatId*(self: AccessInterface): string {.base.} =
|
|||
method belongsToCommunity*(self: AccessInterface): bool {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
|
||||
method addReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method removeReaction*(self: AccessInterface, messageId: string, reactionId: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -1,3 +1,5 @@
|
|||
import Tables, json
|
||||
|
||||
type
|
||||
ContentType* {.pure.} = enum
|
||||
FetchMoreMessagesButton = -2
|
||||
|
@ -16,7 +18,7 @@ type
|
|||
Edit = 11
|
||||
|
||||
type
|
||||
Item* = object
|
||||
Item* = ref object
|
||||
id: string
|
||||
`from`: string
|
||||
alias: string
|
||||
|
@ -32,9 +34,12 @@ type
|
|||
timestamp: int64
|
||||
contentType: ContentType
|
||||
messageType: int
|
||||
reactions: OrderedTable[int, seq[tuple[name: string, reactionId: string]]] # [emojiId, list of [names reacted with the emojiId, reaction id]]
|
||||
reactionIds: seq[string]
|
||||
|
||||
proc initItem*(id, `from`, alias, identicon, outgoingStatus, text: string, seen: bool, timestamp: int64,
|
||||
contentType: ContentType, messageType: int): Item =
|
||||
result = Item()
|
||||
result.id = id
|
||||
result.`from` = `from`
|
||||
result.alias = alias
|
||||
|
@ -74,4 +79,62 @@ proc contentType*(self: Item): ContentType {.inline.} =
|
|||
self.contentType
|
||||
|
||||
proc messageType*(self: Item): int {.inline.} =
|
||||
self.messageType
|
||||
self.messageType
|
||||
|
||||
proc shouldAddReaction*(self: Item, emojiId: int, name: string): bool =
|
||||
for k, values in self.reactions:
|
||||
if(k != emojiId):
|
||||
continue
|
||||
|
||||
for t in values:
|
||||
if(t.name == name):
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
proc getReactionId*(self: Item, emojiId: int, name: string): string =
|
||||
for k, values in self.reactions:
|
||||
if(k != emojiId):
|
||||
continue
|
||||
|
||||
for t in values:
|
||||
if(t.name == name):
|
||||
return t.reactionId
|
||||
|
||||
# we should never be here, since this is a controlled call
|
||||
return ""
|
||||
|
||||
proc addReaction*(self: Item, emojiId: int, name: string, reactionId: string) =
|
||||
if(not self.reactions.contains(emojiId)):
|
||||
self.reactions[emojiId] = @[]
|
||||
|
||||
self.reactions[emojiId].add((name, reactionId))
|
||||
|
||||
proc removeReaction*(self: Item, reactionId: string) =
|
||||
var key: int
|
||||
var index: int
|
||||
for k, values in self.reactions:
|
||||
var i = -1
|
||||
for t in values:
|
||||
i += 1
|
||||
if(t.reactionId == reactionId):
|
||||
key = k
|
||||
index = i
|
||||
|
||||
self.reactions[key].del(index)
|
||||
if(self.reactions[key].len == 0):
|
||||
self.reactions.del(key)
|
||||
|
||||
proc getNamesForReactions*(self: Item, emojiId: int): seq[string] =
|
||||
if(not self.reactions.contains(emojiId)):
|
||||
return
|
||||
|
||||
for v in self.reactions[emojiId]:
|
||||
result.add(v.name)
|
||||
|
||||
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})
|
|
@ -1,4 +1,4 @@
|
|||
import NimQml, Tables, strutils, strformat
|
||||
import NimQml, Tables, json, strutils, strformat
|
||||
|
||||
import item
|
||||
|
||||
|
@ -19,6 +19,7 @@ type
|
|||
# Image
|
||||
# GapFrom
|
||||
# GapTo
|
||||
CountsForReactions
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -55,7 +56,8 @@ QtObject:
|
|||
# ModelRole.StickerPack.int:"stickerPack",
|
||||
# ModelRole.Image.int:"image",
|
||||
# ModelRole.GapFrom.int:"gapFrom",
|
||||
# ModelRole.GapTo.int:"gapTo"
|
||||
# ModelRole.GapTo.int:"gapTo",
|
||||
ModelRole.CountsForReactions.int:"countsForReactions",
|
||||
}.toTable
|
||||
|
||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||
|
@ -98,4 +100,55 @@ QtObject:
|
|||
# of ModelRole.GapFrom:
|
||||
# result = newQVariant(item.gapFrom)
|
||||
# of ModelRole.GapTo:
|
||||
# result = newQVariant(item.gapTo)
|
||||
# result = newQVariant(item.gapTo)
|
||||
of ModelRole.CountsForReactions:
|
||||
result = newQVariant($(%* item.getCountsForReactions))
|
||||
|
||||
proc prependItems*(self: Model, items: seq[Item]) =
|
||||
let parentModelIndex = newQModelIndex()
|
||||
defer: parentModelIndex.delete
|
||||
|
||||
let first = 0
|
||||
let last = items.len - 1
|
||||
self.beginInsertRows(parentModelIndex, first, last)
|
||||
self.items = items & self.items
|
||||
self.endInsertRows()
|
||||
|
||||
proc findIndexForMessageId(self: Model, messageId: string): int =
|
||||
for i in 0 ..< self.items.len:
|
||||
if(self.items[i].id == messageId):
|
||||
return i
|
||||
|
||||
return -1
|
||||
|
||||
proc getItemWithMessageId*(self: Model, messageId: string): Item =
|
||||
let ind = self.findIndexForMessageId(messageId)
|
||||
if(ind == -1):
|
||||
return
|
||||
|
||||
return self.items[ind]
|
||||
|
||||
proc addReaction*(self: Model, messageId: string, emojiId: int, name: string, reactionId: string) =
|
||||
let ind = self.findIndexForMessageId(messageId)
|
||||
if(ind == -1):
|
||||
return
|
||||
|
||||
self.items[ind].addReaction(emojiId, name, reactionId)
|
||||
|
||||
let index = self.createIndex(ind, 0, nil)
|
||||
self.dataChanged(index, index, @[ModelRole.CountsForReactions.int])
|
||||
|
||||
proc removeReaction*(self: Model, messageId: string, reactionId: string) =
|
||||
let ind = self.findIndexForMessageId(messageId)
|
||||
if(ind == -1):
|
||||
return
|
||||
|
||||
self.items[ind].removeReaction(reactionId)
|
||||
|
||||
let index = self.createIndex(ind, 0, nil)
|
||||
self.dataChanged(index, index, @[ModelRole.CountsForReactions.int])
|
||||
|
||||
proc getNamesForReaction*(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].getNamesForReactions(emojiId)
|
|
@ -1,7 +1,7 @@
|
|||
import NimQml
|
||||
import io_interface
|
||||
import ../io_interface as delegate_interface
|
||||
import view, controller
|
||||
import view, controller, model, item
|
||||
import ../../../../../core/global_singleton
|
||||
|
||||
import ../../../../../../app_service/service/chat/service as chat_service
|
||||
|
@ -51,4 +51,36 @@ method viewDidLoad*(self: Module) =
|
|||
self.delegate.messagesDidLoad()
|
||||
|
||||
method getModuleAsVariant*(self: Module): QVariant =
|
||||
return self.viewVariant
|
||||
return self.viewVariant
|
||||
|
||||
method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: seq[ReactionDto]) =
|
||||
var viewItems: seq[Item]
|
||||
for m in messages:
|
||||
var item = initItem(m.id, m.`from`, m.alias, m.identicon, m.outgoingStatus, m.text, m.seen, m.timestamp,
|
||||
m.contentType.ContentType, m.messageType)
|
||||
|
||||
for r in reactions:
|
||||
if(r.messageId == m.id):
|
||||
# m.`from` should be replaced by appropriate ens/alias when we have that part refactored
|
||||
item.addReaction(r.emojiId, m.`from`, r.id)
|
||||
|
||||
# messages are sorted from the most recent to the least recent one
|
||||
viewItems = item & viewItems
|
||||
|
||||
self.view.model.prependItems(viewItems)
|
||||
|
||||
method toggleReaction*(self: Module, messageId: string, emojiId: int) =
|
||||
let item = self.view.model.getItemWithMessageId(messageId)
|
||||
let myName = "MY_NAME" #once we have "userProfile" merged, we will request alias/ens name from there
|
||||
if(item.shouldAddReaction(emojiId, myName)):
|
||||
self.controller.addReaction(messageId, emojiId)
|
||||
else:
|
||||
let reactionId = item.getReactionId(emojiId, myName)
|
||||
self.controller.removeReaction(messageId, reactionId)
|
||||
|
||||
method onReactionAdded*(self: Module, messageId: string, emojiId: int, reactionId: string) =
|
||||
let myName = "MY_NAME" #once we have "userProfile" merged, we will request alias/ens name from there
|
||||
self.view.model.addReaction(messageId, emojiId, myName, reactionId)
|
||||
|
||||
method onReactionRemoved*(self: Module, messageId: string, reactionId: string) =
|
||||
self.view.model.removeReaction(messageId, reactionId)
|
|
@ -0,0 +1,10 @@
|
|||
import ../../../../../../../app_service/service/message/dto/[message, reaction]
|
||||
|
||||
method newMessagesLoaded*(self: AccessInterface, messages: seq[MessageDto], reactions: seq[ReactionDto]) {.base.} =
|
||||
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, reactionId: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -1,2 +1,5 @@
|
|||
method viewDidLoad*(self: AccessInterface) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method toggleReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
|
@ -1,4 +1,4 @@
|
|||
import NimQml
|
||||
import NimQml, json
|
||||
import model
|
||||
import io_interface
|
||||
|
||||
|
@ -6,10 +6,12 @@ QtObject:
|
|||
type
|
||||
View* = ref object of QObject
|
||||
delegate: io_interface.AccessInterface
|
||||
model: Model
|
||||
model*: Model
|
||||
modelVariant: QVariant
|
||||
|
||||
proc delete*(self: View) =
|
||||
self.model.delete
|
||||
self.modelVariant.delete
|
||||
self.QObject.delete
|
||||
|
||||
proc newView*(delegate: io_interface.AccessInterface): View =
|
||||
|
@ -17,6 +19,20 @@ QtObject:
|
|||
result.QObject.setup
|
||||
result.delegate = delegate
|
||||
result.model = newModel()
|
||||
result.modelVariant = newQVariant(result.model)
|
||||
|
||||
proc load*(self: View) =
|
||||
self.delegate.viewDidLoad()
|
||||
self.delegate.viewDidLoad()
|
||||
|
||||
proc getModel(self: View): QVariant {.slot.} =
|
||||
return self.modelVariant
|
||||
|
||||
QtProperty[QVariant] model:
|
||||
read = getModel
|
||||
notify = modelChanged
|
||||
|
||||
proc toggleReaction*(self: View, messageId: string, emojiId: int) {.slot.} =
|
||||
self.delegate.toggleReaction(messageId, emojiId)
|
||||
|
||||
proc getNamesForReaction*(self: View, messageId: string, emojiId: int): string {.slot.} =
|
||||
return $(%* self.model.getNamesForReaction(messageId, emojiId))
|
|
@ -38,7 +38,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitt
|
|||
result.delegate = delegate
|
||||
result.view = view.newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.controller = controller.newController(result, chatId, belongsToCommunity, chatService, communityService,
|
||||
result.controller = controller.newController(result, events, chatId, belongsToCommunity, chatService, communityService,
|
||||
messageService)
|
||||
result.moduleLoaded = false
|
||||
|
||||
|
|
|
@ -67,8 +67,8 @@ proc buildChatUI(self: Module, events: EventEmitter, chatService: chat_service.S
|
|||
self.view.appendItem(item)
|
||||
self.addSubmodule(c.id, false, events, chatService, communityService, messageService)
|
||||
|
||||
# make the first chat active when load the app
|
||||
if(selectedItemId.len == 0):
|
||||
# make the first Public chat active when load the app
|
||||
if(selectedItemId.len == 0 and c.chatType == ChatType.Public):
|
||||
selectedItemId = item.id
|
||||
|
||||
self.setActiveItemSubItem(selectedItemId, "")
|
||||
|
|
|
@ -130,4 +130,37 @@ QtObject:
|
|||
# we're here if initial messages are not loaded yet
|
||||
self.loadMoreMessagesForChat(chatId)
|
||||
|
||||
|
||||
|
||||
proc addReaction*(self: Service, chatId: string, messageId: string, emojiId: int):
|
||||
tuple[result: string, error: string] =
|
||||
try:
|
||||
let response = status_go.addReaction(chatId, messageId, emojiId)
|
||||
|
||||
result.error = "response doesn't contain \"error\""
|
||||
if(response.result.contains("error")):
|
||||
result.error = response.result["error"].getStr
|
||||
return
|
||||
|
||||
var reactionsArr: JsonNode
|
||||
var reactions: seq[ReactionDto]
|
||||
if(response.result.getProp("emojiReactions", reactionsArr)):
|
||||
reactions = map(reactionsArr.getElems(), proc(x: JsonNode): ReactionDto = x.toReactionDto())
|
||||
|
||||
if(reactions.len > 0):
|
||||
result.result = reactions[0].id
|
||||
|
||||
except Exception as e:
|
||||
error "error: ", methodName="addReaction", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc removeReaction*(self: Service, reactionId: string):
|
||||
tuple[result: string, error: string] =
|
||||
try:
|
||||
let response = status_go.removeReaction(reactionId)
|
||||
|
||||
result.error = "response doesn't contain \"error\""
|
||||
if(response.result.contains("error")):
|
||||
result.error = response.result["error"].getStr
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
error "error: ", methodName="removeReaction", errName = e.name, errDesription = e.msg
|
Loading…
Reference in New Issue