refactor(@desktop/chat-communities): pinned messages added

Option to pin/unpin message added on the backend side.
Pinned model added to the chat content module.
Message service updated accordingly.
This commit is contained in:
Sale Djenic 2021-11-04 16:03:24 +01:00
parent 9777191501
commit fd3303a4e8
18 changed files with 255 additions and 73 deletions

View File

@ -42,12 +42,29 @@ method init*(self: Controller) =
let args = MessagesLoadedArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.newPinnedMessagesLoaded(args.pinnedMessages)
# We should handle only pinned messages within this module.
echo "ChatContent...RECEIVED MESSAGES: ", repr(args)
self.events.on(SIGNAL_MESSAGE_PINNED) do(e:Args):
let args = MessagePinUnpinArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.onPinMessage(args.messageId)
self.events.on(SIGNAL_MESSAGE_UNPINNED) do(e:Args):
let args = MessagePinUnpinArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.onUnpinMessage(args.messageId)
method getChatId*(self: Controller): string =
return self.chatId
method belongsToCommunity*(self: Controller): bool =
return self.belongsToCommunity
method unpinMessage*(self: Controller, messageId: string) =
self.messageService.pinUnpinMessage(self.chatId, messageId, false)
method getMessageDetails*(self: Controller, messageId: string):
tuple[message: MessageDto, reactions: seq[ReactionDto], error: string] =
return self.messageService.getDetailsForMessage(self.chatId, messageId)

View File

@ -1,5 +1,4 @@
import ../../../../../app_service/service/chat/service_interface as chat_service
import ../../../../../app_service/service/community/service_interface as community_service
import ../../../../../app_service/service/message/dto/[message, reaction]
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
@ -16,3 +15,10 @@ method getChatId*(self: AccessInterface): string {.base.} =
method belongsToCommunity*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available")
method unpinMessage*(self: AccessInterface, messageId: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getMessageDetails*(self: AccessInterface, messageId: string):
tuple[message: MessageDto, reactions: seq[ReactionDto], error: string] {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,21 +0,0 @@
import NimQml
QtObject:
type
Item* = ref object of QObject
proc setup(self: Item) =
self.QObject.setup
proc delete*(self: Item) =
self.QObject.delete
proc newItem*(): Item =
new(result, delete)
result.setup()
proc id*(self: Item): string {.slot.} =
self.id
QtProperty[string] id:
read = id

View File

@ -40,8 +40,19 @@ method init*(self: Controller) =
let args = MessagesLoadedArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.newMessagesLoaded(args.messages, args.reactions, args.pinnedMessages)
self.delegate.newMessagesLoaded(args.messages, args.reactions)
self.events.on(SIGNAL_MESSAGE_PINNED) do(e:Args):
let args = MessagePinUnpinArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.onPinUnpinMessage(args.messageId, true)
self.events.on(SIGNAL_MESSAGE_UNPINNED) do(e:Args):
let args = MessagePinUnpinArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.onPinUnpinMessage(args.messageId, false)
method getChatId*(self: Controller): string =
return self.chatId
@ -64,3 +75,6 @@ method removeReaction*(self: Controller, messageId: string, reactionId: string)
return
self.delegate.onReactionRemoved(messageId, reactionId)
method pinUnpinMessage*(self: Controller, messageId: string, pin: bool) =
self.messageService.pinUnpinMessage(self.chatId, messageId, pin)

View File

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

View File

@ -1,7 +1,9 @@
import NimQml
import io_interface
import ../io_interface as delegate_interface
import view, controller, model, item
import view, controller
import ../../../../shared_models/message_model
import ../../../../shared_models/message_item
import ../../../../../core/global_singleton
import ../../../../../../app_service/service/chat/service as chat_service
@ -53,7 +55,8 @@ method viewDidLoad*(self: Module) =
method getModuleAsVariant*(self: Module): QVariant =
return self.viewVariant
method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: seq[ReactionDto]) =
method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: seq[ReactionDto],
pinnedMessages: seq[PinnedMessageDto]) =
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,
@ -64,6 +67,10 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
# m.`from` should be replaced by appropriate ens/alias when we have that part refactored
item.addReaction(r.emojiId, m.`from`, r.id)
for p in pinnedMessages:
if(p.message.id == m.id):
item.pinned = true
# messages are sorted from the most recent to the least recent one
viewItems = item & viewItems
@ -84,3 +91,9 @@ method onReactionAdded*(self: Module, messageId: string, emojiId: int, reactionI
method onReactionRemoved*(self: Module, messageId: string, reactionId: string) =
self.view.model.removeReaction(messageId, reactionId)
method pinUnpinMessage*(self: Module, messageId: string, pin: bool) =
self.controller.pinUnpinMessage(messageId, pin)
method onPinUnpinMessage*(self: Module, messageId: string, pin: bool) =
self.view.model.pinUnpinMessage(messageId, pin)

View File

@ -1,6 +1,7 @@
import ../../../../../../../app_service/service/message/dto/[message, reaction]
import ../../../../../../../app_service/service/message/dto/[message, reaction, pinned_message]
method newMessagesLoaded*(self: AccessInterface, messages: seq[MessageDto], reactions: seq[ReactionDto]) {.base.} =
method newMessagesLoaded*(self: AccessInterface, messages: seq[MessageDto], reactions: seq[ReactionDto],
pinnedMessages: seq[PinnedMessageDto]) {.base.} =
raise newException(ValueError, "No implementation available")
method onReactionAdded*(self: AccessInterface, messageId: string, emojiId: int, reactionId: string) {.base.} =
@ -8,3 +9,6 @@ method onReactionAdded*(self: AccessInterface, messageId: string, emojiId: int,
method onReactionRemoved*(self: AccessInterface, messageId: string, reactionId: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onPinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -3,3 +3,6 @@ method viewDidLoad*(self: AccessInterface) {.base.} =
method toggleReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} =
raise newException(ValueError, "No implementation available")
method pinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,5 +1,5 @@
import NimQml, json
import model
import ../../../../shared_models/message_model
import io_interface
QtObject:
@ -29,10 +29,15 @@ QtObject:
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))
proc pinMessage*(self: View, messageId: string) {.slot.} =
self.delegate.pinUnpinMessage(messageId, true)
proc unpinMessage*(self: View, messageId: string) {.slot.} =
self.delegate.pinUnpinMessage(messageId, false)

View File

@ -1,17 +0,0 @@
import NimQml
import item
QtObject:
type
Model* = ref object of QAbstractListModel
sections: seq[Item]
proc setup(self: Model) =
self.QAbstractListModel.setup
proc delete*(self: Model) =
self.QAbstractListModel.delete
proc newModel*(): Model =
new(result, delete)
result.setup

View File

@ -1,7 +1,9 @@
import NimQml, chronicles
import io_interface
import ../io_interface as delegate_interface
import view, controller, item, model
import view, controller
import ../../../shared_models/message_model as pinned_msg_model
import ../../../shared_models/message_item as pinned_msg_item
import ../../../../core/global_singleton
import input_area/module as input_area_module
@ -105,3 +107,44 @@ method getMessagesModule*(self: Module): QVariant =
method getUsersModule*(self: Module): QVariant =
return self.usersModule.getModuleAsVariant()
proc buildPinnedMessageItem(self: Module, messageId: string, item: var pinned_msg_item.Item): bool =
let (m, reactions, err) = self.controller.getMessageDetails(messageId)
if(err.len > 0):
return false
item = initItem(m.id, m.`from`, m.alias, m.identicon, m.outgoingStatus, m.text, m.seen, m.timestamp,
m.contentType.ContentType, m.messageType)
item.pinned = true
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)
return true
method newPinnedMessagesLoaded*(self: Module, pinnedMessages: seq[PinnedMessageDto]) =
var viewItems: seq[Item]
for p in pinnedMessages:
var item: pinned_msg_item.Item
if(not self.buildPinnedMessageItem(p.message.id, item)):
continue
viewItems = item & viewItems # messages are sorted from the most recent to the least recent one
self.view.model.prependItems(viewItems)
method unpinMessage*(self: Module, messageId: string) =
self.controller.unpinMessage(messageId)
method onUnpinMessage*(self: Module, messageId: string) =
self.view.model.removeItem(messageId)
method onPinMessage*(self: Module, messageId: string) =
var item: pinned_msg_item.Item
if(not self.buildPinnedMessageItem(messageId, item)):
return
self.view.model.appendItem(item)

View File

@ -0,0 +1,10 @@
import ../../../../../../app_service/service/message/dto/pinned_message
method newPinnedMessagesLoaded*(self: AccessInterface, pinnedMessages: seq[PinnedMessageDto]) {.base.} =
raise newException(ValueError, "No implementation available")
method onUnpinMessage*(self: AccessInterface, messageId: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onPinMessage*(self: AccessInterface, mmessageId: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -11,3 +11,6 @@ method getMessagesModule*(self: AccessInterface): QVariant {.base.} =
method getUsersModule*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")
method unpinMessage*(self: AccessInterface, messageId: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,22 +1,25 @@
import NimQml
import model
import ../../../shared_models/message_model as pinned_msg_model
import io_interface
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
model: Model
model*: pinned_msg_model.Model
modelVariant: QVariant
proc delete*(self: View) =
self.model.delete
self.modelVariant.delete
self.QObject.delete
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.QObject.setup
result.delegate = delegate
result.model = newModel()
result.model = pinned_msg_model.newModel()
result.modelVariant = newQVariant(result.model)
proc load*(self: View) =
self.delegate.viewDidLoad()
@ -40,3 +43,13 @@ QtObject:
QtProperty[QVariant] usersModule:
read = getUsersModule
proc getModel(self: View): QVariant {.slot.} =
return self.modelVariant
QtProperty[QVariant] model:
read = getModel
proc unpinMessage*(self: View, messageId: string) {.slot.} =
self.delegate.unpinMessage(messageId)

View File

@ -36,6 +36,7 @@ type
messageType: int
reactions: OrderedTable[int, seq[tuple[name: string, reactionId: string]]] # [emojiId, list of [names reacted with the emojiId, reaction id]]
reactionIds: seq[string]
pinned: bool
proc initItem*(id, `from`, alias, identicon, outgoingStatus, text: string, seen: bool, timestamp: int64,
contentType: ContentType, messageType: int): Item =
@ -50,6 +51,7 @@ proc initItem*(id, `from`, alias, identicon, outgoingStatus, text: string, seen:
result.timestamp = timestamp
result.contentType = contentType
result.messageType = messageType
result.pinned = false
proc id*(self: Item): string {.inline.} =
self.id
@ -81,6 +83,12 @@ proc contentType*(self: Item): ContentType {.inline.} =
proc messageType*(self: Item): int {.inline.} =
self.messageType
proc pinned*(self: Item): bool {.inline.} =
self.pinned
proc `pinned=`*(self: Item, value: bool) {.inline.} =
self.pinned = value
proc shouldAddReaction*(self: Item, emojiId: int, name: string): bool =
for k, values in self.reactions:
if(k != emojiId):

View File

@ -1,6 +1,6 @@
import NimQml, Tables, json, strutils, strformat
import NimQml, Tables, json, strutils
import item
import message_item
type
ModelRole {.pure.} = enum
@ -19,6 +19,7 @@ type
# Image
# GapFrom
# GapTo
Pinned
CountsForReactions
QtObject:
@ -57,6 +58,7 @@ QtObject:
# ModelRole.Image.int:"image",
# ModelRole.GapFrom.int:"gapFrom",
# ModelRole.GapTo.int:"gapTo",
ModelRole.Pinned.int:"pinned",
ModelRole.CountsForReactions.int:"countsForReactions",
}.toTable
@ -101,9 +103,18 @@ QtObject:
# result = newQVariant(item.gapFrom)
# of ModelRole.GapTo:
# result = newQVariant(item.gapTo)
of ModelRole.Pinned:
result = newQVariant(item.pinned)
of ModelRole.CountsForReactions:
result = newQVariant($(%* item.getCountsForReactions))
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 prependItems*(self: Model, items: seq[Item]) =
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
@ -114,12 +125,25 @@ QtObject:
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
proc appendItem*(self: Model, item: Item) =
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
return -1
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len)
self.items.add(item)
self.endInsertRows()
proc removeItem*(self: Model, messageId: string) =
let ind = self.findIndexForMessageId(messageId)
if(ind == -1):
return
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginRemoveRows(parentModelIndex, ind, ind)
self.items.delete(ind)
self.endRemoveRows()
proc getItemWithMessageId*(self: Model, messageId: string): Item =
let ind = self.findIndexForMessageId(messageId)
@ -152,3 +176,13 @@ QtObject:
for i in 0 ..< self.items.len:
if(self.items[i].id == messageId):
return self.items[i].getNamesForReactions(emojiId)
proc pinUnpinMessage*(self: Model, messageId: string, pin: bool) =
let ind = self.findIndexForMessageId(messageId)
if(ind == -1):
return
self.items[ind].pinned = pin
let index = self.createIndex(ind, 0, nil)
self.dataChanged(index, index, @[ModelRole.Pinned.int])

View File

@ -6,7 +6,7 @@ import ../../tasks/[qt, threadpool]
import status/statusgo_backend_new/messages as status_go
import ./dto/message as message_dto
import ./dto/pinnedMessage as pinned_msg_dto
import ./dto/pinned_message as pinned_msg_dto
import ./dto/reaction as reaction_dto
export message_dto
@ -21,7 +21,9 @@ logScope:
const MESSAGES_PER_PAGE = 20
# Signals which may be emitted by this service:
const SIGNAL_MESSAGES_LOADED* = "new-messagesLoaded" #Once we are done with refactoring we should remove "new-" from this signal name
const SIGNAL_MESSAGES_LOADED* = "new-messagesLoaded" #Once we are done with refactoring we should remove "new-" from all signals
const SIGNAL_MESSAGE_PINNED* = "new-messagePinned"
const SIGNAL_MESSAGE_UNPINNED* = "new-messageUnpinned"
type
MessagesLoadedArgs* = ref object of Args
@ -30,6 +32,10 @@ type
pinnedMessages*: seq[PinnedMessageDto]
reactions*: seq[ReactionDto]
MessagePinUnpinArgs* = ref object of Args
chatId*: string
messageId*: string
QtObject:
type Service* = ref object of QObject
events: EventEmitter
@ -150,10 +156,10 @@ QtObject:
result.result = reactions[0].id
except Exception as e:
result.error = 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): tuple[result: string, error: string] =
try:
let response = status_go.removeReaction(reactionId)
@ -163,4 +169,42 @@ QtObject:
return
except Exception as e:
result.error = e.msg
error "error: ", methodName="removeReaction", errName = e.name, errDesription = e.msg
proc pinUnpinMessage*(self: Service, chatId: string, messageId: string, pin: bool) =
try:
let response = status_go.pinUnpinMessage(messageId, chatId, pin)
var pinMessagesObj: JsonNode
if(response.result.getProp("pinMessages", pinMessagesObj)):
let data = MessagePinUnpinArgs(chatId: chatId, messageId: messageId)
var pinned = false
if(pinMessagesObj.getProp("pinned", pinned)):
if(pinned and pin):
self.events.emit(SIGNAL_MESSAGE_PINNED, data)
else:
if(not pinned and not pin):
self.events.emit(SIGNAL_MESSAGE_UNPINNED, data)
except Exception as e:
error "error: ", methodName="pinUnpinMessage", errName = e.name, errDesription = e.msg
proc getDetailsForMessage*(self: Service, chatId: string, messageId: string):
tuple[message: MessageDto, reactions: seq[ReactionDto], error: string] =
try:
let msgResponse = status_go.fetchMessageByMessageId(messageId)
if(msgResponse.error.isNil):
result.message = msgResponse.result.toMessageDto()
if(result.message.id.len == 0):
result.error = "message with id: " & messageId & " doesn't exist"
return
let reactionsResponse = status_go.fetchReactionsForMessageWithId(chatId, messageId)
if(reactionsResponse.error.isNil):
result.reactions = map(reactionsResponse.result.getElems(), proc(x: JsonNode): ReactionDto = x.toReactionDto())
except Exception as e:
result.error = e.msg
error "error: ", methodName="getDetailsForMessage", errName = e.name, errDesription = e.msg