mirror of
https://github.com/logos-messaging/nim-chat-poc.git
synced 2026-01-04 07:03:13 +00:00
Wire delivery acknowledgements
This commit is contained in:
parent
f52150ba83
commit
aefacba0e4
@ -35,6 +35,8 @@ type
|
|||||||
sender: string
|
sender: string
|
||||||
content: string
|
content: string
|
||||||
timestamp: DateTime
|
timestamp: DateTime
|
||||||
|
id: string
|
||||||
|
isAcknowledged: bool
|
||||||
|
|
||||||
ConvoInfo = object
|
ConvoInfo = object
|
||||||
name: string
|
name: string
|
||||||
@ -71,13 +73,15 @@ proc `==`(a,b: ConvoInfo):bool =
|
|||||||
# Data Management
|
# Data Management
|
||||||
#################################################
|
#################################################
|
||||||
|
|
||||||
proc addMessage(conv: var ConvoInfo, sender: string, content: string) =
|
proc addMessage(conv: var ConvoInfo, messageId: MessageId, sender: string, content: string) =
|
||||||
|
|
||||||
let now = now()
|
let now = now()
|
||||||
conv.messages.add(Message(
|
conv.messages.add(Message(
|
||||||
sender: sender,
|
sender: sender,
|
||||||
|
id: messageId,
|
||||||
content: content,
|
content: content,
|
||||||
timestamp: now
|
timestamp: now,
|
||||||
|
isAcknowledged: false
|
||||||
))
|
))
|
||||||
conv.lastMsgTime = now
|
conv.lastMsgTime = now
|
||||||
|
|
||||||
@ -117,10 +121,13 @@ proc createConvo(app: ChatApp) {.async.} =
|
|||||||
discard await app.client.newPrivateConversation(toBundle(app.inputInviteBuffer.strip()).get())
|
discard await app.client.newPrivateConversation(toBundle(app.inputInviteBuffer.strip()).get())
|
||||||
|
|
||||||
proc sendMessage(app: ChatApp, convoInfo: ptr ConvoInfo, msg: string) {.async.} =
|
proc sendMessage(app: ChatApp, convoInfo: ptr ConvoInfo, msg: string) {.async.} =
|
||||||
convoInfo[].addMessage("You", app.inputBuffer)
|
|
||||||
|
|
||||||
|
var msgId = ""
|
||||||
if convoInfo.convo != nil:
|
if convoInfo.convo != nil:
|
||||||
await convoInfo.convo.sendMessage(app.client.ds, initTextFrame(msg).toContentFrame())
|
msgId = await convoInfo.convo.sendMessage(app.client.ds, initTextFrame(msg).toContentFrame())
|
||||||
|
|
||||||
|
convoInfo[].addMessage(msgId, "You", app.inputBuffer)
|
||||||
|
|
||||||
|
|
||||||
proc setupChatSdk(app: ChatApp) =
|
proc setupChatSdk(app: ChatApp) =
|
||||||
|
|
||||||
@ -149,6 +156,16 @@ proc setupChatSdk(app: ChatApp) =
|
|||||||
app.client.onDeliveryAck(proc(convo: Conversation, msgId: string) {.async.} =
|
app.client.onDeliveryAck(proc(convo: Conversation, msgId: string) {.async.} =
|
||||||
info "DeliveryAck", msgId=msgId
|
info "DeliveryAck", msgId=msgId
|
||||||
app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: fmt"Ack:{msgId}"))
|
app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: fmt"Ack:{msgId}"))
|
||||||
|
|
||||||
|
var s = ""
|
||||||
|
var msgs = addr app.conversations[convo.id()].messages
|
||||||
|
for i in countdown(msgs[].high, 0):
|
||||||
|
s = fmt"{s},{msgs[i].id}"
|
||||||
|
var m = addr msgs[i]
|
||||||
|
|
||||||
|
if m.id == msgId:
|
||||||
|
m.isAcknowledged = true
|
||||||
|
break # Stop after
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -265,11 +282,12 @@ proc drawMsgPane( app: ChatApp, layout: Pane) =
|
|||||||
|
|
||||||
let m = convo.messages[i]
|
let m = convo.messages[i]
|
||||||
let timeStr = m.timestamp.format("HH:mm:ss")
|
let timeStr = m.timestamp.format("HH:mm:ss")
|
||||||
|
var deliveryIcon = " "
|
||||||
var remainingText = m.content
|
var remainingText = m.content
|
||||||
|
|
||||||
if m.sender == "You":
|
if m.sender == "You":
|
||||||
tb.setForegroundColor(fgYellow)
|
tb.setForegroundColor(fgYellow)
|
||||||
|
deliveryIcon = if m.isAcknowledged: "✔" else: "◯"
|
||||||
else:
|
else:
|
||||||
tb.setForegroundColor(fgGreen)
|
tb.setForegroundColor(fgGreen)
|
||||||
|
|
||||||
@ -278,7 +296,7 @@ proc drawMsgPane( app: ChatApp, layout: Pane) =
|
|||||||
app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: fmt" TOO LONG: {convo.name}"))
|
app.logMsgs.add(LogEntry(level: "info",ts: now(), msg: fmt" TOO LONG: {convo.name}"))
|
||||||
|
|
||||||
return
|
return
|
||||||
tb.write(x, y, fmt"[{timeStr}] {m.sender}")
|
tb.write(x, y, fmt"[{timeStr}] {deliveryIcon} {m.sender}")
|
||||||
y = y + 1
|
y = y + 1
|
||||||
|
|
||||||
while remainingText.len > 0:
|
while remainingText.len > 0:
|
||||||
@ -300,11 +318,12 @@ proc drawMsgPane( app: ChatApp, layout: Pane) =
|
|||||||
|
|
||||||
let m = convo.messages[i]
|
let m = convo.messages[i]
|
||||||
let timeStr = m.timestamp.format("HH:mm:ss")
|
let timeStr = m.timestamp.format("HH:mm:ss")
|
||||||
|
var deliveryIcon = " "
|
||||||
var remainingText = m.content
|
var remainingText = m.content
|
||||||
|
|
||||||
if m.sender == "You":
|
if m.sender == "You":
|
||||||
tb.setForegroundColor(fgYellow)
|
tb.setForegroundColor(fgYellow)
|
||||||
|
deliveryIcon = if m.isAcknowledged: "✔" else: "◯"
|
||||||
else:
|
else:
|
||||||
tb.setForegroundColor(fgGreen)
|
tb.setForegroundColor(fgGreen)
|
||||||
|
|
||||||
@ -322,7 +341,7 @@ proc drawMsgPane( app: ChatApp, layout: Pane) =
|
|||||||
tb.write(x+3, y, line)
|
tb.write(x+3, y, line)
|
||||||
y = y - 1
|
y = y - 1
|
||||||
|
|
||||||
tb.write(x, y, fmt"[{timeStr}] {m.sender}")
|
tb.write(x, y, fmt"[{timeStr}] {deliveryIcon} {m.sender}")
|
||||||
y = y - 2
|
y = y - 2
|
||||||
|
|
||||||
proc drawMsgInput( app: ChatApp, layout: Pane) =
|
proc drawMsgInput( app: ChatApp, layout: Pane) =
|
||||||
@ -528,59 +547,10 @@ proc initChatApp(client: Client): Future[ChatApp] {.async.} =
|
|||||||
|
|
||||||
|
|
||||||
# Add some sample conversations with messages
|
# Add some sample conversations with messages
|
||||||
var conv1 = ConvoInfo(name: "Alice", messages: @[])
|
var conv1 = ConvoInfo(name: "ReadMe", messages: @[])
|
||||||
conv1.addMessage("36Alice", "Hey there! How are you doing?")
|
conv1.addMessage("","Bob", "TODO")
|
||||||
conv1.addMessage("35You", "I'm doing well, thanks! Working on a new project.")
|
|
||||||
conv1.addMessage("34Alice", "That sounds exciting! What kind of project?")
|
|
||||||
conv1.addMessage("33You", "A terminal-based chat application in Nim!")
|
|
||||||
conv1.addMessage("32Alice", "Hey there! How are you doing?")
|
|
||||||
conv1.addMessage("31You", "I'm doing well, thanks! Working on a new project.")
|
|
||||||
conv1.addMessage("30Alice", "That sounds exciting! What kind of project?")
|
|
||||||
conv1.addMessage("29You", "A terminal-based chat application in Nim!")
|
|
||||||
conv1.addMessage("28Alice", "Hey there! How are you doing?")
|
|
||||||
conv1.addMessage("27You", "I'm doing well, thanks! Working on a new project.")
|
|
||||||
conv1.addMessage("26Alice", "That sounds exciting! What kind of project?")
|
|
||||||
conv1.addMessage("25You", "A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim! A terminal-based chat application in Nim!")
|
|
||||||
conv1.addMessage("24Alice", "Hey there! How are you doing?")
|
|
||||||
conv1.addMessage("23You", "I'm doing well, thanks! Working on a new project.")
|
|
||||||
conv1.addMessage("22Alice", "That sounds exciting! What kind of project?")
|
|
||||||
conv1.addMessage("21You", "A terminal-based chat application in Nim!")
|
|
||||||
conv1.addMessage("20Alice", "Hey there! How are you doing?")
|
|
||||||
conv1.addMessage("19You", "I'm doing well, thanks! Working on a new project.")
|
|
||||||
conv1.addMessage("18Alice", "That sounds exciting! What kind of project?")
|
|
||||||
conv1.addMessage("17You", "A terminal-based chat application in Nim!")
|
|
||||||
conv1.addMessage("16Alice", "Hey there! How are you doing?")
|
|
||||||
conv1.addMessage("15You", "I'm doing well, thanks! Working on a new project.")
|
|
||||||
conv1.addMessage("14Alice", "That sounds exciting! What kind of project?")
|
|
||||||
conv1.addMessage("13You", "A terminal-based chat application in Nim!")
|
|
||||||
conv1.addMessage("12Alice", "Hey there! How are you doing?")
|
|
||||||
conv1.addMessage("11You", "I'm doing well, thanks! Working on a new project.")
|
|
||||||
conv1.addMessage("10Alice", "That sounds exciting! What kind of project?")
|
|
||||||
conv1.addMessage("9You", "The system architecture consists of three main components: the client interface, the processing engine, and the data storage layer. Each component communicates through well-defined APIs that ensure scalability and maintainability. The client interface handles user interactions and validates input data before forwarding requests to the processing engine.")
|
|
||||||
conv1.addMessage("8Alice", "Hey there! How are you doing?")
|
|
||||||
conv1.addMessage("7You", "I'm doing well, thanks! Working on a new project.")
|
|
||||||
conv1.addMessage("6Alice", "That sounds exciting! What kind of project?")
|
|
||||||
conv1.addMessage("5You", "A terminal-based chat application in Nim!")
|
|
||||||
conv1.addMessage("4Alice", "Hey there! How are you doing?")
|
|
||||||
conv1.addMessage("3You", "I'm doing well, thanks! Working on a new project.")
|
|
||||||
conv1.addMessage("2Alice", "That sounds exciting! What kind of project?")
|
|
||||||
conv1.addMessage("1You", "A terminal-based chat application in Nim!")
|
|
||||||
app.conversations[conv1.name] = conv1
|
app.conversations[conv1.name] = conv1
|
||||||
|
|
||||||
var conv2 = ConvoInfo(name: "Bob", messages: @[])
|
|
||||||
conv2.addMessage("Bob", "Did you see the game last night?")
|
|
||||||
conv2.addMessage("You", "The system architecture consists of three main components: the client interface, the processing engine, and the data storage layer. Each component communicates through well-defined APIs that ensure scalability and maintainability. The client interface handles user interactions and validates input data before forwarding requests to the processing engine")
|
|
||||||
conv2.addMessage("Bob", "The home team crushed it! 3-0")
|
|
||||||
app.conversations[conv2.name] = conv2
|
|
||||||
|
|
||||||
var conv3 = ConvoInfo(name: "Development Team", messages: @[])
|
|
||||||
conv3.addMessage("Sarah", "Morning standup in 10 minutes")
|
|
||||||
conv3.addMessage("Mike", "I'll be there. Just finishing up the last bug fix.")
|
|
||||||
conv3.addMessage("You", "On my way!")
|
|
||||||
conv3.addMessage("Sarah", "Great! Let's review the sprint progress today.")
|
|
||||||
app.conversations[conv3.name] = conv3
|
|
||||||
|
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -4,11 +4,12 @@ import chat_sdk/[
|
|||||||
delivery/waku_client,
|
delivery/waku_client,
|
||||||
identity,
|
identity,
|
||||||
links,
|
links,
|
||||||
proto_types
|
proto_types,
|
||||||
|
types
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
export client, conversations, waku_client, identity, links
|
export client, conversations, waku_client, identity, links
|
||||||
|
|
||||||
#export specific frames need by applications
|
#export specific frames need by applications
|
||||||
export ContentFrame
|
export ContentFrame, MessageId
|
||||||
|
|||||||
@ -231,8 +231,6 @@ proc newPrivateConversation*(client: Client,
|
|||||||
proc parseMessage(client: Client, msg: ChatPayload) {.raises: [ValueError,
|
proc parseMessage(client: Client, msg: ChatPayload) {.raises: [ValueError,
|
||||||
SerializationError].} =
|
SerializationError].} =
|
||||||
## Receives a incoming payload, decodes it, and processes it.
|
## Receives a incoming payload, decodes it, and processes it.
|
||||||
info "Parse", clientId = client.getId(), msg = msg,
|
|
||||||
contentTopic = msg.contentTopic
|
|
||||||
|
|
||||||
let envelope = decode(msg.bytes, WapEnvelopeV1).valueOr:
|
let envelope = decode(msg.bytes, WapEnvelopeV1).valueOr:
|
||||||
raise newException(ValueError, "Failed to decode WapEnvelopeV1: " & error)
|
raise newException(ValueError, "Failed to decode WapEnvelopeV1: " & error)
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import strutils
|
|||||||
import ../proto_types
|
import ../proto_types
|
||||||
import ../delivery/waku_client
|
import ../delivery/waku_client
|
||||||
import ../utils
|
import ../utils
|
||||||
|
import ../types
|
||||||
|
|
||||||
type
|
type
|
||||||
ConvoTypes* = enum
|
ConvoTypes* = enum
|
||||||
@ -25,6 +26,6 @@ method id*(self: Conversation): string {.raises: [Defect, ValueError].} =
|
|||||||
panic("ProgramError: Missing concrete implementation")
|
panic("ProgramError: Missing concrete implementation")
|
||||||
|
|
||||||
method sendMessage*(convo: Conversation, ds: WakuClient,
|
method sendMessage*(convo: Conversation, ds: WakuClient,
|
||||||
content_frame: ContentFrame) {.async, base, gcsafe.} =
|
content_frame: ContentFrame) : Future[MessageId] {.async, base, gcsafe.} =
|
||||||
# TODO: make this a compile time check
|
# TODO: make this a compile time check
|
||||||
panic("ProgramError: Missing concrete implementation")
|
panic("ProgramError: Missing concrete implementation")
|
||||||
|
|||||||
@ -116,7 +116,7 @@ proc initPrivateV1*(owner: Identity, participant: PublicKey,
|
|||||||
raise newException(ValueError, "bad sds channel")
|
raise newException(ValueError, "bad sds channel")
|
||||||
|
|
||||||
proc sendFrame(self: PrivateV1, ds: WakuClient,
|
proc sendFrame(self: PrivateV1, ds: WakuClient,
|
||||||
msg: PrivateV1Frame): Future[void]{.async.} =
|
msg: PrivateV1Frame): Future[MessageId]{.async.} =
|
||||||
|
|
||||||
let frameBytes = encode(msg)
|
let frameBytes = encode(msg)
|
||||||
let msgId = self.calcMsgId(frameBytes)
|
let msgId = self.calcMsgId(frameBytes)
|
||||||
@ -130,6 +130,9 @@ proc sendFrame(self: PrivateV1, ds: WakuClient,
|
|||||||
discard ds.sendPayload(self.getTopic(), encryptedBytes.toEnvelope(
|
discard ds.sendPayload(self.getTopic(), encryptedBytes.toEnvelope(
|
||||||
self.getConvoId()))
|
self.getConvoId()))
|
||||||
|
|
||||||
|
result = msgId
|
||||||
|
|
||||||
|
|
||||||
method id*(self: PrivateV1): string =
|
method id*(self: PrivateV1): string =
|
||||||
return getConvoIdRaw(self.participants, self.discriminator)
|
return getConvoIdRaw(self.participants, self.discriminator)
|
||||||
|
|
||||||
@ -166,13 +169,13 @@ proc handleFrame*[T: ConversationStore](convo: PrivateV1, client: T,
|
|||||||
|
|
||||||
|
|
||||||
method sendMessage*(convo: PrivateV1, ds: WakuClient,
|
method sendMessage*(convo: PrivateV1, ds: WakuClient,
|
||||||
content_frame: ContentFrame) {.async.} =
|
content_frame: ContentFrame) : Future[MessageId] {.async.} =
|
||||||
|
|
||||||
try:
|
try:
|
||||||
let frame = PrivateV1Frame(sender: @(convo.owner.getPubkey().bytes()),
|
let frame = PrivateV1Frame(sender: @(convo.owner.getPubkey().bytes()),
|
||||||
timestamp: getCurrentTimestamp(), content: content_frame)
|
timestamp: getCurrentTimestamp(), content: content_frame)
|
||||||
|
|
||||||
await convo.sendFrame(ds, frame)
|
result = await convo.sendFrame(ds, frame)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "Unknown error in PrivateV1:SendMessage"
|
error "Unknown error in PrivateV1:SendMessage"
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import
|
|||||||
crypto,
|
crypto,
|
||||||
delivery/waku_client,
|
delivery/waku_client,
|
||||||
proto_types,
|
proto_types,
|
||||||
|
types,
|
||||||
utils
|
utils
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
@ -104,5 +105,6 @@ proc handleFrame*[T: ConversationStore](convo: Inbox, client: T, bytes: seq[
|
|||||||
|
|
||||||
|
|
||||||
method sendMessage*(convo: Inbox, ds: WakuClient,
|
method sendMessage*(convo: Inbox, ds: WakuClient,
|
||||||
content_frame: ContentFrame) {.async.} =
|
content_frame: ContentFrame) : Future[MessageId] {.async.} =
|
||||||
warn "Cannot send message to Inbox"
|
warn "Cannot send message to Inbox"
|
||||||
|
result = "program_error"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user