feat: show last message and unread message count

This commit is contained in:
Richard Ramos 2020-05-22 15:57:35 -04:00 committed by Iuri Matias
parent ac5c6389d3
commit 2eee8c7a2d
7 changed files with 151 additions and 50 deletions

18
src/app/chat/chatItem.nim Normal file
View File

@ -0,0 +1,18 @@
type ChatItem* = ref object
name*: string
lastMessage*: string
timestamp*: int64
unviewedMessagesCount*: int
proc newChatItem*(): ChatItem =
new(result)
result.name = ""
result.lastMessage = ""
result.timestamp = 0
result.unviewedMessagesCount = 0
proc findByName*(self: seq[ChatItem], name: string): int =
result = -1
for item in self:
inc result
if(item.name == name): break

View File

@ -2,9 +2,10 @@ import NimQml
import json, eventemitter import json, eventemitter
import ../../status/chat as status_chat import ../../status/chat as status_chat
import view import view
import chatItem
import messages import messages
import ../signals/types import ../signals/types
import ../../models/chat import ../../models/chat as chat_model
type ChatController* = ref object of SignalSubscriber type ChatController* = ref object of SignalSubscriber
view*: ChatsView view*: ChatsView
@ -40,9 +41,18 @@ proc load*(self: ChatController, chatId: string) =
discard self.view.joinChat(chatId) discard self.view.joinChat(chatId)
self.view.setActiveChannelByIndex(0) self.view.setActiveChannelByIndex(0)
proc onSignal(self: ChatController, data: Signal) = method onSignal(self: ChatController, data: Signal) =
var chatSignal = cast[ChatSignal](data) var messageSignal = cast[MessageSignal](data)
for message in chatSignal.messages:
for c in messageSignal.chats:
let channel = newChatitem()
channel.name = c.name
channel.lastMessage = c.lastMessage.text
channel.timestamp = c.timestamp
channel.unviewedMessagesCount = c.unviewedMessagesCount
self.view.updateChat(channel)
for message in messageSignal.messages:
let chatMessage = newChatMessage() let chatMessage = newChatMessage()
chatMessage.userName = message.alias chatMessage.userName = message.alias
chatMessage.message = message.text chatMessage.message = message.text

View File

@ -2,17 +2,21 @@ import NimQml
import Tables import Tables
import messages import messages
import messageList import messageList
import chatItem
import ../../models/chat import ../../models/chat
type type
RoleNames {.pure.} = enum RoleNames {.pure.} = enum
Name = UserRole + 1, Name = UserRole + 1,
LastMessage = UserRole + 2
Timestamp = UserRole + 3
UnreadMessages = UserRole + 4
QtObject: QtObject:
type type
ChatsView* = ref object of QAbstractListModel ChatsView* = ref object of QAbstractListModel
model: ChatModel model: ChatModel
names*: seq[string] chats: seq[ChatItem]
callResult: string callResult: string
messageList: Table[string, ChatMessageList] messageList: Table[string, ChatMessageList]
activeChannel: string activeChannel: string
@ -24,7 +28,7 @@ QtObject:
proc newChatsView*(model: ChatModel): ChatsView = proc newChatsView*(model: ChatModel): ChatsView =
new(result, delete) new(result, delete)
result.model = model result.model = model
result.names = @[] result.chats = @[]
result.activeChannel = "" result.activeChannel = ""
result.messageList = initTable[string, ChatMessageList]() result.messageList = initTable[string, ChatMessageList]()
result.setup() result.setup()
@ -33,17 +37,29 @@ QtObject:
if not self.messageList.hasKey(channel): if not self.messageList.hasKey(channel):
self.messageList[channel] = newChatMessageList() self.messageList[channel] = newChatMessageList()
method rowCount(self: ChatsView, index: QModelIndex = nil): int = self.names.len method rowCount(self: ChatsView, index: QModelIndex = nil): int = self.chats.len
method data(self: ChatsView, index: QModelIndex, role: int): QVariant = method data(self: ChatsView, index: QModelIndex, role: int): QVariant =
if not index.isValid: if not index.isValid:
return return
if index.row < 0 or index.row >= self.names.len: if index.row < 0 or index.row >= self.chats.len:
return return
return newQVariant(self.names[index.row])
let chatItem = self.chats[index.row]
let chatItemRole = role.RoleNames
case chatItemRole:
of RoleNames.Name: result = newQVariant(chatItem.name)
of RoleNames.Timestamp: result = newQVariant($chatItem.timestamp)
of RoleNames.LastMessage: result = newQVariant(chatItem.lastMessage)
of RoleNames.UnreadMessages: result = newQVariant(chatItem.unviewedMessagesCount)
method roleNames(self: ChatsView): Table[int, string] = method roleNames(self: ChatsView): Table[int, string] =
{ RoleNames.Name.int:"name"}.toTable {
RoleNames.Name.int:"name",
RoleNames.Timestamp.int:"timestamp",
RoleNames.LastMessage.int: "lastMessage",
RoleNames.UnreadMessages.int: "unviewedMessagesCount"
}.toTable
proc onSend*(self: ChatsView, inputJSON: string) {.slot.} = proc onSend*(self: ChatsView, inputJSON: string) {.slot.} =
discard self.model.sendMessage(self.activeChannel, inputJSON) discard self.model.sendMessage(self.activeChannel, inputJSON)
@ -57,8 +73,8 @@ QtObject:
proc activeChannelChanged*(self: ChatsView) {.signal.} proc activeChannelChanged*(self: ChatsView) {.signal.}
proc setActiveChannelByIndex*(self: ChatsView, index: int) {.slot.} = proc setActiveChannelByIndex*(self: ChatsView, index: int) {.slot.} =
if self.activeChannel == self.names[index]: return if self.activeChannel == self.chats[index].name: return
self.activeChannel = self.names[index] self.activeChannel = self.chats[index].name
self.activeChannelChanged() self.activeChannelChanged()
QtProperty[string] activeChannel: QtProperty[string] activeChannel:
@ -80,18 +96,27 @@ QtObject:
proc addToList(self: ChatsView, channel: string): int = proc addToList(self: ChatsView, channel: string): int =
if(self.activeChannel == ""): self.setActiveChannel(channel) if(self.activeChannel == ""): self.setActiveChannel(channel)
var chatItem = newChatItem()
self.beginInsertRows(newQModelIndex(), self.names.len, self.names.len) chatItem.name = channel
self.names.add(channel)
self.upsertChannel(channel) self.upsertChannel(channel)
self.beginInsertRows(newQModelIndex(), self.chats.len, self.chats.len)
self.chats.add(chatItem)
self.endInsertRows() self.endInsertRows()
result = self.names.len - 1 result = self.chats.len - 1
proc joinChat*(self: ChatsView, channel: string): int {.slot.} = proc joinChat*(self: ChatsView, channel: string): int {.slot.} =
self.setActiveChannel(channel) self.setActiveChannel(channel)
if self.model.hasChannel(channel): if self.model.hasChannel(channel):
result = self.names.find(channel) result = self.chats.findByName(channel)
else: else:
self.model.join(channel) self.model.join(channel)
result = self.addToList(channel) result = self.addToList(channel)
proc updateChat*(self: ChatsView, chat: ChatItem) =
var idx = self.chats.findByName(chat.name)
if idx > -1:
self.chats[idx] = chat
var x = self.createIndex(idx,0,nil)
var y = self.createIndex(idx,0,nil)
self.dataChanged(x, y, @[RoleNames.Timestamp.int, RoleNames.LastMessage.int, RoleNames.UnreadMessages.int])

View File

@ -1,33 +1,57 @@
import json import json
import types import types
proc toMessage(jsonMsg: JsonNode): Message
proc toChat(jsonChat: JsonNode): Chat
proc fromEvent*(event: JsonNode): Signal = proc fromEvent*(event: JsonNode): Signal =
var signal:ChatSignal = ChatSignal() var signal:MessageSignal = MessageSignal()
signal.messages = @[] signal.messages = @[]
if event["event"]{"messages"} != nil: if event["event"]{"messages"} != nil:
for jsonMsg in event["event"]["messages"]: for jsonMsg in event["event"]["messages"]:
let msg = Message( signal.messages.add(jsonMsg.toMessage)
alias: jsonMsg{"alias"}.getStr,
chatId: jsonMsg{"chatId"}.getStr, if event["event"]{"chats"} != nil:
clock: $jsonMsg{"clock"}.getInt, for jsonChat in event["event"]["chats"]:
contentType: jsonMsg{"contentType"}.getInt, signal.chats.add(jsonChat.toChat)
ensName: jsonMsg{"ensName"}.getStr,
fromAuthor: jsonMsg{"from"}.getStr,
id: jsonMsg{"identicon"}.getStr,
identicon: jsonMsg{"identicon"}.getStr,
lineCount: jsonMsg{"lineCount"}.getInt,
localChatId: jsonMsg{"localChatId"}.getStr,
messageType: jsonMsg{"messageType"}.getStr,
replace: jsonMsg{"replace"}.getStr,
responseTo: jsonMsg{"responseTo"}.getStr,
rtl: jsonMsg{"rtl"}.getBool,
seen: jsonMsg{"seen"}.getBool,
text: jsonMsg{"text"}.getStr,
timestamp: $jsonMsg{"timestamp"}.getInt,
whisperTimestamp: $jsonMsg{"whisperTimestamp"}.getInt,
isCurrentUser: false # TODO: this must compare the fromAuthor against current user because the messages received from the mailserver will arrive as signals too, and those include the current user messages
)
signal.messages.add(msg)
result = signal result = signal
proc toChat(jsonChat: JsonNode): Chat =
result = Chat(
id: jsonChat{"id"}.getStr,
name: jsonChat{"name"}.getStr,
color: jsonChat{"color"}.getStr,
active: jsonChat{"active"}.getBool,
chatType: ChatType(jsonChat{"chatType"}.getInt),
timestamp: jsonChat{"timestamp"}.getBiggestInt,
lastClockValue: jsonChat{"lastClockValue"}.getBiggestInt,
deletedAtClockValue: jsonChat{"deletedAtClockValue"}.getBiggestInt,
unviewedMessagesCount: jsonChat{"unviewedMessagesCount"}.getInt,
lastMessage: jsonChat{"lastMessage"}.toMessage
)
proc toMessage(jsonMsg: JsonNode): Message =
result = Message(
alias: jsonMsg{"alias"}.getStr,
chatId: jsonMsg{"chatId"}.getStr,
clock: $jsonMsg{"clock"}.getInt,
contentType: jsonMsg{"contentType"}.getInt,
ensName: jsonMsg{"ensName"}.getStr,
fromAuthor: jsonMsg{"from"}.getStr,
id: jsonMsg{"identicon"}.getStr,
identicon: jsonMsg{"identicon"}.getStr,
lineCount: jsonMsg{"lineCount"}.getInt,
localChatId: jsonMsg{"localChatId"}.getStr,
messageType: jsonMsg{"messageType"}.getStr,
replace: jsonMsg{"replace"}.getStr,
responseTo: jsonMsg{"responseTo"}.getStr,
rtl: jsonMsg{"rtl"}.getBool,
seen: jsonMsg{"seen"}.getBool,
text: jsonMsg{"text"}.getStr,
timestamp: $jsonMsg{"timestamp"}.getInt,
whisperTimestamp: $jsonMsg{"whisperTimestamp"}.getInt,
isCurrentUser: false # TODO: this must compare the fromAuthor against current user because the messages received from the mailserver will arrive as signals too, and those include the current user messages
)

View File

@ -1,3 +1,5 @@
import chronicles
type SignalSubscriber* = ref object of RootObj type SignalSubscriber* = ref object of RootObj
type Signal* = ref object of RootObj type Signal* = ref object of RootObj
@ -30,10 +32,32 @@ type Message* = object
whisperTimestamp*: string whisperTimestamp*: string
isCurrentUser*: bool isCurrentUser*: bool
type ChatSignal* = ref object of Signal
messages*: seq[Message]
# Override this method # Override this method
method onSignal*(self: SignalSubscriber, data: Signal) {.base.} = method onSignal*(self: SignalSubscriber, data: Signal) {.base.} =
echo "Received a signal" # TODO: log signal received discard
# TODO: log signal received
type ChatType* = enum
ChatTypeOneToOne = 1,
ChatTypePublic = 2,
ChatTypePrivateGroupChat = 3
type Chat* = object
id*: string # ID is the id of the chat, for public chats it is the name e.g. status, for one-to-one is the hex encoded public key and for group chats is a random uuid appended with the hex encoded pk of the creator of the chat
name*: string
color*: string
active*: bool # indicates whether the chat has been soft deleted
chatType*: ChatType
timestamp*: int64 # indicates the last time this chat has received/sent a message
lastClockValue*: int64 # indicates the last clock value to be used when sending messages
deletedAtClockValue*: int64 # indicates the clock value at time of deletion, messages with lower clock value of this should be discarded
unviewedMessagesCount*: int
lastMessage*: Message
# Group chat fields
# members ?
# membershipUpdateEvents # ?
type MessageSignal* = ref object of Signal
messages*: seq[Message]
chats*: seq[Chat]

View File

@ -75,7 +75,6 @@ proc mainProc() =
appState.subscribe(proc () = appState.subscribe(proc () =
for channel in appState.channels: for channel in appState.channels:
echo channel.name
chat.load(channel.name) chat.load(channel.name)
) )

View File

@ -181,7 +181,7 @@ SplitView {
} }
Text { Text {
id: lastChatMessage id: lastChatMessage
text: "Chatting blah blah..." text: lastMessage || qsTr("No messages")
anchors.right: contactNumberChatsCircle.left anchors.right: contactNumberChatsCircle.left
anchors.rightMargin: Theme.smallPadding anchors.rightMargin: Theme.smallPadding
elide: Text.ElideRight elide: Text.ElideRight
@ -194,7 +194,7 @@ SplitView {
} }
Text { Text {
id: contactTime id: contactTime
text: "12:22 AM" text: timestamp
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Theme.padding anchors.rightMargin: Theme.padding
anchors.top: parent.top anchors.top: parent.top
@ -212,9 +212,10 @@ SplitView {
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: Theme.padding anchors.rightMargin: Theme.padding
color: Theme.blue color: Theme.blue
visible: unviewedMessagesCount > 0
Text { Text {
id: contactNumberChats id: contactNumberChats
text: qsTr("1") text: unviewedMessagesCount
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: "white" color: "white"