feat: Determine if a message was sent

This commit is contained in:
Richard Ramos 2020-07-01 14:24:13 -04:00 committed by Iuri Matias
parent 7eb44366da
commit 64452e71b9
17 changed files with 202 additions and 55 deletions

View File

@ -1,6 +1,7 @@
import NimQml, eventemitter, chronicles, tables import NimQml, eventemitter, chronicles, tables
import ../../status/chat as chat_model import ../../status/chat as chat_model
import ../../status/mailservers as mailserver_model import ../../status/mailservers as mailserver_model
import ../../status/messages as messages_model
import ../../signals/types import ../../signals/types
import ../../status/libstatus/types as status_types import ../../status/libstatus/types as status_types
import ../../status/libstatus/wallet as status_wallet import ../../status/libstatus/wallet as status_wallet
@ -27,45 +28,8 @@ proc delete*(self: ChatController) =
delete self.variant delete self.variant
delete self.view delete self.view
proc handleChatEvents(self: ChatController) = include event_handling
# Display already saved messages include signal_handling
self.status.events.on("messagesLoaded") do(e:Args):
self.view.pushMessages(MsgsLoadedArgs(e).messages)
self.status.events.on("contactUpdate") do(e: Args):
var evArgs = ContactUpdateArgs(e)
self.view.updateUsernames(evArgs.contacts)
self.status.events.on("chatUpdate") do(e: Args):
var evArgs = ChatUpdateArgs(e)
self.view.updateUsernames(evArgs.contacts)
self.view.updateChats(evArgs.chats)
self.view.pushMessages(evArgs.messages)
self.status.events.on("chatHistoryCleared") do(e: Args):
var args = ChannelArgs(e)
self.view.clearMessages(args.chat.id)
self.status.events.on("channelJoined") do(e: Args):
var channel = ChannelArgs(e)
discard self.view.chats.addChatItemToList(channel.chat)
self.status.chat.chatMessages(channel.chat.id)
self.view.setActiveChannel(channel.chat.id)
self.status.events.on("channelLeft") do(e: Args):
discard self.view.chats.removeChatItemFromList(self.view.activeChannel.chatItem.id)
self.status.events.on("activeChannelChanged") do(e: Args):
self.view.setActiveChannel(ChatIdArg(e).chatId)
proc handleMailserverEvents(self: ChatController) =
self.status.events.on("mailserverTopics") do(e: Args):
self.status.mailservers.addTopics(TopicArgs(e).topics)
if(self.status.mailservers.isSelectedMailserverAvailable):
self.status.mailservers.requestMessages()
self.status.events.on("mailserverAvailable") do(e:Args):
self.status.mailservers.requestMessages()
proc init*(self: ChatController) = proc init*(self: ChatController) =
self.handleMailserverEvents() self.handleMailserverEvents()
@ -83,16 +47,10 @@ proc init*(self: ChatController) =
self.view.addRecentStickerToList(sticker) self.view.addRecentStickerToList(sticker)
self.status.chat.addStickerToRecent(sticker) self.status.chat.addStickerToRecent(sticker)
proc handleMessage(self: ChatController, data: MessageSignal) =
self.status.chat.update(data.chats, data.messages)
proc handleDiscoverySummary(self: ChatController, data: DiscoverySummarySignal) =
## Handle mailserver peers being added and removed
self.status.mailservers.peerSummaryChange(data.enodes)
method onSignal(self: ChatController, data: Signal) = method onSignal(self: ChatController, data: Signal) =
case data.signalType: case data.signalType:
of SignalType.Message: handleMessage(self, MessageSignal(data)) of SignalType.Message: handleMessage(self, MessageSignal(data))
of SignalType.DiscoverySummary: handleDiscoverySummary(self, DiscoverySummarySignal(data)) of SignalType.DiscoverySummary: handleDiscoverySummary(self, DiscoverySummarySignal(data))
of SignalType.EnvelopeSent: handleEnvelopeSent(self, EnvelopeSentSignal(data))
else: else:
warn "Unhandled signal received", signalType = data.signalType warn "Unhandled signal received", signalType = data.signalType

View File

@ -0,0 +1,48 @@
proc handleChatEvents(self: ChatController) =
# Display already saved messages
self.status.events.on("messagesLoaded") do(e:Args):
self.view.pushMessages(MsgsLoadedArgs(e).messages)
self.status.events.on("contactUpdate") do(e: Args):
var evArgs = ContactUpdateArgs(e)
self.view.updateUsernames(evArgs.contacts)
self.status.events.on("chatUpdate") do(e: Args):
var evArgs = ChatUpdateArgs(e)
self.view.updateUsernames(evArgs.contacts)
self.view.updateChats(evArgs.chats)
self.view.pushMessages(evArgs.messages)
self.status.events.on("chatHistoryCleared") do(e: Args):
var args = ChannelArgs(e)
self.view.clearMessages(args.chat.id)
self.status.events.on("channelJoined") do(e: Args):
var channel = ChannelArgs(e)
discard self.view.chats.addChatItemToList(channel.chat)
self.status.chat.chatMessages(channel.chat.id)
self.view.setActiveChannel(channel.chat.id)
self.status.events.on("channelLeft") do(e: Args):
discard self.view.chats.removeChatItemFromList(self.view.activeChannel.chatItem.id)
self.status.events.on("activeChannelChanged") do(e: Args):
self.view.setActiveChannel(ChatIdArg(e).chatId)
self.status.events.on("sendingMessage") do(e:Args):
var msg = MessageArgs(e)
self.status.messages.trackMessage(msg.id, msg.channel)
self.status.events.on("messageSent") do(e:Args):
var msg = MessageSentArgs(e)
self.view.markMessageAsSent(msg.chatId, msg.id)
proc handleMailserverEvents(self: ChatController) =
self.status.events.on("mailserverTopics") do(e: Args):
self.status.mailservers.addTopics(TopicArgs(e).topics)
if(self.status.mailservers.isSelectedMailserverAvailable):
self.status.mailservers.requestMessages()
self.status.events.on("mailserverAvailable") do(e:Args):
self.status.mailservers.requestMessages()

View File

@ -0,0 +1,10 @@
proc handleMessage(self: ChatController, data: MessageSignal) =
self.status.chat.update(data.chats, data.messages)
proc handleDiscoverySummary(self: ChatController, data: DiscoverySummarySignal) =
## Handle mailserver peers being added and removed
self.status.mailservers.peerSummaryChange(data.enodes)
proc handleEnvelopeSent(self: ChatController, data: EnvelopeSentSignal) =
self.status.messages.updateStatus(data.messageIds)

View File

@ -147,6 +147,9 @@ QtObject:
for k in self.messageList.keys: for k in self.messageList.keys:
self.messageList[k].updateUsernames(contacts) self.messageList[k].updateUsernames(contacts)
proc markMessageAsSent*(self:ChatsView, chat: string, messageId: string) =
self.messageList[chat].markMessageAsSent(messageId)
proc getMessageList(self: ChatsView): QVariant {.slot.} = proc getMessageList(self: ChatsView): QVariant {.slot.} =
self.upsertChannel(self.activeChannel.id) self.upsertChannel(self.activeChannel.id)
return newQVariant(self.messageList[self.activeChannel.id]) return newQVariant(self.messageList[self.activeChannel.id])
@ -160,7 +163,7 @@ QtObject:
self.messagePushed() self.messagePushed()
proc sendMessage*(self: ChatsView, message: string) {.slot.} = proc sendMessage*(self: ChatsView, message: string) {.slot.} =
discard self.status.chat.sendMessage(self.activeChannel.id, message) self.status.chat.sendMessage(self.activeChannel.id, message)
proc addRecentStickerToList*(self: ChatsView, sticker: Sticker) = proc addRecentStickerToList*(self: ChatsView, sticker: Sticker) =
self.stickers[RECENT_STICKERS].addStickerToList(sticker) self.stickers[RECENT_STICKERS].addStickerToList(sticker)

View File

@ -21,6 +21,7 @@ type
ChatId = UserRole + 10 ChatId = UserRole + 10
SectionIdentifier = UserRole + 11 SectionIdentifier = UserRole + 11
Id = UserRole + 12 Id = UserRole + 12
OutgoingStatus = UserRole + 13
QtObject: QtObject:
type type
@ -71,6 +72,7 @@ QtObject:
of ChatMessageRoles.ChatId: result = newQVariant(message.chatId) of ChatMessageRoles.ChatId: result = newQVariant(message.chatId)
of ChatMessageRoles.SectionIdentifier: result = newQVariant(sectionIdentifier(message)) of ChatMessageRoles.SectionIdentifier: result = newQVariant(sectionIdentifier(message))
of ChatMessageRoles.Id: result = newQVariant(message.id) of ChatMessageRoles.Id: result = newQVariant(message.id)
of ChatMessageRoles.OutgoingStatus: result = newQVariant(message.outgoingStatus)
method roleNames(self: ChatMessageList): Table[int, string] = method roleNames(self: ChatMessageList): Table[int, string] =
{ {
@ -85,7 +87,8 @@ QtObject:
ChatMessageRoles.FromAuthor.int:"fromAuthor", ChatMessageRoles.FromAuthor.int:"fromAuthor",
ChatMessageRoles.ChatId.int:"chatId", ChatMessageRoles.ChatId.int:"chatId",
ChatMessageRoles.SectionIdentifier.int: "sectionIdentifier", ChatMessageRoles.SectionIdentifier.int: "sectionIdentifier",
ChatMessageRoles.Id.int: "messageId" ChatMessageRoles.Id.int: "messageId",
ChatMessageRoles.OutgoingStatus.int: "outgoingStatus"
}.toTable }.toTable
proc add*(self: ChatMessageList, message: Message) = proc add*(self: ChatMessageList, message: Message) =
@ -104,6 +107,15 @@ QtObject:
self.messages = @[] self.messages = @[]
self.endResetModel() self.endResetModel()
proc markMessageAsSent*(self: ChatMessageList, messageId: string)=
let topLeft = self.createIndex(0, 0, nil)
let bottomRight = self.createIndex(self.messages.len, 0, nil)
for m in self.messages.mitems:
if m.id == messageId:
m.outgoingStatus = "sent"
self.dataChanged(topLeft, bottomRight, @[ChatMessageRoles.OutgoingStatus.int])
proc updateUsernames*(self: ChatMessageList, contacts: seq[Profile]) = proc updateUsernames*(self: ChatMessageList, contacts: seq[Profile]) =
let topLeft = self.createIndex(0, 0, nil) let topLeft = self.createIndex(0, 0, nil)
let bottomRight = self.createIndex(self.messages.len, 0, nil) let bottomRight = self.createIndex(self.messages.len, 0, nil)

View File

@ -107,6 +107,7 @@ proc mainProc() =
signalController.addSubscriber(SignalType.Message, chat) signalController.addSubscriber(SignalType.Message, chat)
signalController.addSubscriber(SignalType.Message, profile) signalController.addSubscriber(SignalType.Message, profile)
signalController.addSubscriber(SignalType.DiscoverySummary, chat) signalController.addSubscriber(SignalType.DiscoverySummary, chat)
signalController.addSubscriber(SignalType.EnvelopeSent, chat)
signalController.addSubscriber(SignalType.NodeLogin, login) signalController.addSubscriber(SignalType.NodeLogin, login)
signalController.addSubscriber(SignalType.NodeLogin, onboarding) signalController.addSubscriber(SignalType.NodeLogin, onboarding)
signalController.addSubscriber(SignalType.NodeStopped, login) signalController.addSubscriber(SignalType.NodeStopped, login)

View File

@ -1,6 +1,6 @@
import NimQml, tables, json, chronicles, strutils, json_serialization import NimQml, tables, json, chronicles, strutils, json_serialization
import ../status/libstatus/types as status_types import ../status/libstatus/types as status_types
import types, messages, discovery, whisperFilter import types, messages, discovery, whisperFilter, envelopes
logScope: logScope:
topics = "signals" topics = "signals"
@ -57,6 +57,8 @@ QtObject:
case signalType: case signalType:
of SignalType.Message: of SignalType.Message:
signal = messages.fromEvent(jsonSignal) signal = messages.fromEvent(jsonSignal)
of SignalType.EnvelopeSent:
signal = envelopes.fromEvent(jsonSignal)
of SignalType.WhisperFilterAdded: of SignalType.WhisperFilterAdded:
signal = whisperFilter.fromEvent(jsonSignal) signal = whisperFilter.fromEvent(jsonSignal)
of SignalType.Wallet: of SignalType.Wallet:

10
src/signals/envelopes.nim Normal file
View File

@ -0,0 +1,10 @@
import json
import types
proc fromEvent*(jsonSignal: JsonNode): Signal =
var signal:EnvelopeSentSignal = EnvelopeSentSignal()
if jsonSignal["event"].kind != JNull and jsonSignal["event"].hasKey("ids") and jsonSignal["event"]["ids"].kind != JNull:
for messageId in jsonSignal["event"]["ids"]:
signal.messageIds.add(messageId.getStr)
result = signal

View File

@ -140,7 +140,8 @@ proc toMessage*(jsonMsg: JsonNode): Message =
text: jsonMsg{"text"}.getStr, text: jsonMsg{"text"}.getStr,
timestamp: $jsonMsg{"timestamp"}.getInt, timestamp: $jsonMsg{"timestamp"}.getInt,
whisperTimestamp: $jsonMsg{"whisperTimestamp"}.getInt, whisperTimestamp: $jsonMsg{"whisperTimestamp"}.getInt,
isCurrentUser: $jsonMsg{"outgoingStatus"}.getStr == "sending", outgoingStatus: $jsonMsg{"outgoingStatus"}.getStr,
isCurrentUser: $jsonMsg{"outgoingStatus"}.getStr == "sending" or $jsonMsg{"outgoingStatus"}.getStr == "sent",
stickerHash: "", stickerHash: "",
parsedText: @[] parsedText: @[]
) )

View File

@ -17,6 +17,9 @@ type NodeSignal* = ref object of Signal
type WalletSignal* = ref object of Signal type WalletSignal* = ref object of Signal
content*: string content*: string
type EnvelopeSentSignal* = ref object of Signal
messageIds*: seq[string]
# Override this method # Override this method
method onSignal*(self: SignalSubscriber, data: Signal) {.base.} = method onSignal*(self: SignalSubscriber, data: Signal) {.base.} =
error "onSignal must be overriden in controller. Signal is unhandled" error "onSignal must be overriden in controller. Signal is unhandled"

View File

@ -5,6 +5,7 @@ import libstatus/types
import profile/profile import profile/profile
import chat/[chat, message] import chat/[chat, message]
import ../signals/messages import ../signals/messages
import ../signals/types as signal_types
import ens import ens
import eth/common/eth_types import eth/common/eth_types
@ -35,6 +36,10 @@ type
channels*: Table[string, Chat] channels*: Table[string, Chat]
msgCursor*: Table[string, string] msgCursor*: Table[string, string]
recentStickers*: seq[Sticker] recentStickers*: seq[Sticker]
MessageArgs* = ref object of Args
id*: string
channel*: string
include chat/utils include chat/utils
@ -148,10 +153,11 @@ proc clearHistory*(self: ChatModel, chatId: string) =
proc setActiveChannel*(self: ChatModel, chatId: string) = proc setActiveChannel*(self: ChatModel, chatId: string) =
self.events.emit("activeChannelChanged", ChatIdArg(chatId: chatId)) self.events.emit("activeChannelChanged", ChatIdArg(chatId: chatId))
proc sendMessage*(self: ChatModel, chatId: string, msg: string): string = proc sendMessage*(self: ChatModel, chatId: string, msg: string) =
var sentMessage = status_chat.sendChatMessage(chatId, msg) var response = status_chat.sendChatMessage(chatId, msg)
self.emitUpdate(sentMessage) var (chats, messages) = self.processChatUpdate(parseJson(response))
sentMessage self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[]))
self.events.emit("sendingMessage", MessageArgs(id: messages[0].id, channel: messages[0].chatId))
proc addStickerToRecent*(self: ChatModel, sticker: Sticker, save: bool = false) = proc addStickerToRecent*(self: ChatModel, sticker: Sticker, save: bool = false) =
self.recentStickers.insert(sticker, 0) self.recentStickers.insert(sticker, 0)
@ -164,7 +170,9 @@ proc addStickerToRecent*(self: ChatModel, sticker: Sticker, save: bool = false)
proc sendSticker*(self: ChatModel, chatId: string, sticker: Sticker) = proc sendSticker*(self: ChatModel, chatId: string, sticker: Sticker) =
var response = status_chat.sendStickerMessage(chatId, sticker) var response = status_chat.sendStickerMessage(chatId, sticker)
self.addStickerToRecent(sticker, save = true) self.addStickerToRecent(sticker, save = true)
self.emitUpdate(response) var (chats, messages) = self.processChatUpdate(parseJson(response))
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[]))
self.events.emit("sendingMessage", MessageArgs(id: messages[0].id, channel: messages[0].chatId))
proc chatMessages*(self: ChatModel, chatId: string, initialLoad:bool = true) = proc chatMessages*(self: ChatModel, chatId: string, initialLoad:bool = true) =
if not self.msgCursor.hasKey(chatId): if not self.msgCursor.hasKey(chatId):

View File

@ -41,6 +41,7 @@ type Message* = object
whisperTimestamp*: string whisperTimestamp*: string
isCurrentUser*: bool isCurrentUser*: bool
stickerHash*: string stickerHash*: string
outgoingStatus*: string
proc `$`*(self: Message): string = proc `$`*(self: Message): string =
result = fmt"Message(id:{self.id}, chatId:{self.chatId}, clock:{self.clock}, from:{self.fromAuthor}, type:{self.contentType})" result = fmt"Message(id:{self.id}, chatId:{self.chatId}, clock:{self.clock}, from:{self.fromAuthor}, type:{self.contentType})"

View File

@ -124,3 +124,7 @@ proc kickGroupMember*(chatId: string, pubKey: string): string =
proc makeAdmin*(chatId: string, pubKey: string): string = proc makeAdmin*(chatId: string, pubKey: string): string =
callPrivateRPC("addAdminsToGroupChat".prefix, %* [nil, chatId, [pubKey]]) callPrivateRPC("addAdminsToGroupChat".prefix, %* [nil, chatId, [pubKey]])
proc updateOutgoingMessageStatus*(messageId: string, status: string): string =
result = callPrivateRPC("updateMessageOutgoingStatus".prefix, %* [messageId, status])
# TODO: handle errors

48
src/status/messages.nim Normal file
View File

@ -0,0 +1,48 @@
import tables, sets, eventemitter
import libstatus/chat
type
MessageDetails = object
status: string
chatId: string
MessagesModel* = ref object
events*: EventEmitter
messages*: Table[string, MessageDetails]
confirmations*: HashSet[string]
MessageSentArgs* = ref object of Args
id*: string
chatId*: string
proc newMessagesModel*(events: EventEmitter): MessagesModel =
result = MessagesModel()
result.events = events
result.messages = initTable[string, MessageDetails]()
result.confirmations = initHashSet[string]()
proc delete*(self: MessagesModel) =
discard
# For each message sent we call trackMessage to register the message id,
# and wait until an EnvelopeSent signals is emitted for that message. However
# due to communication being async, it's possible that the signal arrives
# first, hence why we check if there's a confirmation (an envelope.sent)
# inside trackMessage to emit the "messageSent" event
proc trackMessage*(self: MessagesModel, id: string, chatId: string) =
self.messages[id] = MessageDetails(status: "sending", chatId: chatId)
if self.confirmations.contains(id):
self.confirmations.excl(id)
self.messages[id].status = "sent"
discard updateOutgoingMessageStatus(id, "sent")
self.events.emit("messageSent", MessageSentArgs(id: id, chatId: chatId))
proc updateStatus*(self: MessagesModel, messageIds: seq[string]) =
for messageId in messageIds:
if self.messages.hasKey(messageId):
self.messages[messageId].status = "sent"
discard updateOutgoingMessageStatus(messageId, "sent")
self.events.emit("messageSent", MessageSentArgs(id: messageId, chatId: self.messages[messageId].chatId))
else:
self.confirmations.incl(messageId)

View File

@ -9,12 +9,14 @@ import accounts as accounts
import wallet as wallet import wallet as wallet
import node as node import node as node
import mailservers as mailservers import mailservers as mailservers
import messages as messages
import contacts as contacts import contacts as contacts
import profile import profile
type Status* = ref object type Status* = ref object
events*: EventEmitter events*: EventEmitter
chat*: ChatModel chat*: ChatModel
messages*: MessagesModel
mailservers*: MailserverModel mailservers*: MailserverModel
accounts*: AccountModel accounts*: AccountModel
wallet*: WalletModel wallet*: WalletModel
@ -31,6 +33,7 @@ proc newStatusInstance*(): Status =
result.wallet.initEvents() result.wallet.initEvents()
result.node = node.newNodeModel() result.node = node.newNodeModel()
result.mailservers = mailservers.newMailserverModel(result.events) result.mailservers = mailservers.newMailserverModel(result.events)
result.messages = messages.newMessagesModel(result.events)
result.profile = profile.newProfileModel() result.profile = profile.newProfileModel()
result.contacts = contacts.newContactModel(result.events) result.contacts = contacts.newContactModel(result.events)

View File

@ -122,6 +122,7 @@ ScrollView {
timestamp: model.timestamp timestamp: model.timestamp
sticker: model.sticker sticker: model.sticker
contentType: model.contentType contentType: model.contentType
outgoingStatus: model.outgoingStatus
authorCurrentMsg: msgDelegate.ListView.section authorCurrentMsg: msgDelegate.ListView.section
authorPrevMsg: msgDelegate.ListView.previousSection authorPrevMsg: msgDelegate.ListView.previousSection
profileClick: profilePopup.openPopup.bind(profilePopup) profileClick: profilePopup.openPopup.bind(profilePopup)

View File

@ -18,6 +18,7 @@ Item {
property string sticker: "Qme8vJtyrEHxABcSVGPF95PtozDgUyfr1xGjePmFdZgk9v" property string sticker: "Qme8vJtyrEHxABcSVGPF95PtozDgUyfr1xGjePmFdZgk9v"
property int contentType: 1 // constants don't work in default props property int contentType: 1 // constants don't work in default props
property string chatId: "chatId" property string chatId: "chatId"
property string outgoingStatus: ""
property string authorCurrentMsg: "authorCurrentMsg" property string authorCurrentMsg: "authorCurrentMsg"
property string authorPrevMsg: "authorPrevMsg" property string authorPrevMsg: "authorPrevMsg"
@ -328,6 +329,39 @@ Item {
// Probably only want to show this when clicking? // Probably only want to show this when clicking?
visible: true visible: true
} }
StyledTextEdit {
id: sentMessage
color: Theme.darkGrey
text: qsTr("Sent")
anchors.top: contentType === Constants.stickerType ? stickerId.bottom : chatText.bottom
anchors.bottomMargin: Theme.padding
anchors.right: chatTime.left
anchors.rightMargin: Theme.padding
font.pixelSize: 10
readOnly: true
visible: isCurrentUser && outgoingStatus == "sent"
}
SVGImage {
id: sendingImg
visible: isCurrentUser && outgoingStatus == "sending"
anchors.top: chatText.top
anchors.right: chatText.left
anchors.rightMargin: 15
source: "../../../img/settings.svg"
width: 15
height: 15
fillMode: Image.Stretch
RotationAnimator {
target: sendingImg;
from: 0;
to: 360;
duration: 1200
running: true
loops: Animation.Infinite
}
}
} }
} }