Update messages data structure (#278)
* Update messages data structure * remove setting of DeletedChatMessage * delete unathorized events * use event pks * remove comment * remove else * fix ui * fix more ui * fix sendReaction
This commit is contained in:
parent
53b1ed4f1b
commit
fed1dd210f
|
@ -26,9 +26,13 @@ export type ChatMessage = ChatMessageProto & {
|
||||||
reactions: Reactions
|
reactions: Reactions
|
||||||
chatUuid: string
|
chatUuid: string
|
||||||
signer: string
|
signer: string
|
||||||
responseToMessage?: Omit<ChatMessage, 'responseToMessage'>
|
responseToMessage?: ChatMessage
|
||||||
|
edittedClock?: bigint
|
||||||
|
pinnedClock?: bigint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FetchedMessage = { messageId: string; timestamp?: Date }
|
||||||
|
|
||||||
export class Chat {
|
export class Chat {
|
||||||
private readonly client: Client
|
private readonly client: Client
|
||||||
|
|
||||||
|
@ -39,7 +43,14 @@ export class Chat {
|
||||||
public readonly symmetricKey: Uint8Array
|
public readonly symmetricKey: Uint8Array
|
||||||
public description: CommunityChat
|
public description: CommunityChat
|
||||||
public readonly chatCallbacks: Set<(description: CommunityChat) => void>
|
public readonly chatCallbacks: Set<(description: CommunityChat) => void>
|
||||||
public messages: ChatMessage[]
|
#messages: Map<string, ChatMessage>
|
||||||
|
#editTextEvents: Map<string, Pick<ChatMessage, 'clock' | 'signer' | 'text'>>
|
||||||
|
#pinEvents: Map<string, Pick<ChatMessage, 'clock' | 'pinned'>>
|
||||||
|
#reactEvents: Map<string, Pick<ChatMessage, 'clock' | 'reactions'>>
|
||||||
|
#deleteEvents: Map<string, Pick<ChatMessage, 'clock' | 'signer'>>
|
||||||
|
#fetchingMessages?: boolean
|
||||||
|
#previousFetchedStartTime?: Date
|
||||||
|
#oldestFetchedMessage?: FetchedMessage
|
||||||
public readonly messageCallbacks: Set<(messages: ChatMessage[]) => void>
|
public readonly messageCallbacks: Set<(messages: ChatMessage[]) => void>
|
||||||
|
|
||||||
constructor(options: {
|
constructor(options: {
|
||||||
|
@ -61,7 +72,11 @@ export class Chat {
|
||||||
this.description = options.description
|
this.description = options.description
|
||||||
|
|
||||||
this.chatCallbacks = new Set()
|
this.chatCallbacks = new Set()
|
||||||
this.messages = []
|
this.#messages = new Map()
|
||||||
|
this.#editTextEvents = new Map()
|
||||||
|
this.#pinEvents = new Map()
|
||||||
|
this.#reactEvents = new Map()
|
||||||
|
this.#deleteEvents = new Map()
|
||||||
this.messageCallbacks = new Set()
|
this.messageCallbacks = new Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,12 +102,40 @@ export class Chat {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns chat messages soreted in ascending order and reply references resolved.
|
||||||
|
*/
|
||||||
public getMessages = () => {
|
public getMessages = () => {
|
||||||
return this.messages
|
const messages: ChatMessage[] = []
|
||||||
|
|
||||||
|
for (const message of this.#messages.values()) {
|
||||||
|
// resolve references
|
||||||
|
const referencedMessage = this.#messages.get(message.responseTo)
|
||||||
|
if (referencedMessage) {
|
||||||
|
message.responseToMessage = referencedMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
messages.push(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort
|
||||||
|
messages.sort((a, b) => {
|
||||||
|
if (a.clock < b.clock) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.clock > b.clock) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMessage = (id: string) => {
|
public getMessage = (id: string) => {
|
||||||
return this.messages.find(message => message.messageId === id)
|
return this.#messages.get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
public onChange = (callback: (description: CommunityChat) => void) => {
|
public onChange = (callback: (description: CommunityChat) => void) => {
|
||||||
|
@ -117,26 +160,24 @@ export class Chat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetchMessages = async (
|
public fetchMessages = async (options: { start: Date }) => {
|
||||||
options: { start: Date },
|
const previousOldestMessage = this.#oldestFetchedMessage
|
||||||
callback: (messages: ChatMessage[]) => void
|
|
||||||
) => {
|
|
||||||
const startTime = options.start
|
const startTime = options.start
|
||||||
const endTime = new Date()
|
// nothing to fetch
|
||||||
|
if (
|
||||||
let _oldestClock: bigint | undefined
|
previousOldestMessage &&
|
||||||
let _oldestMessageTime: Date | undefined
|
previousOldestMessage.timestamp &&
|
||||||
|
previousOldestMessage.timestamp < options.start
|
||||||
if (this.messages.length) {
|
) {
|
||||||
_oldestClock = this.messages[0].clock
|
|
||||||
_oldestMessageTime = new Date(Number(this.messages[0].timestamp))
|
|
||||||
|
|
||||||
// already handled
|
|
||||||
if (_oldestMessageTime <= options.start) {
|
|
||||||
callback(this.messages)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let endTime: Date
|
||||||
|
if (this.#previousFetchedStartTime) {
|
||||||
|
endTime = this.#previousFetchedStartTime
|
||||||
|
} else {
|
||||||
|
endTime = new Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.client.waku.store.queryHistory([this.contentTopic], {
|
await this.client.waku.store.queryHistory([this.contentTopic], {
|
||||||
|
@ -149,31 +190,48 @@ export class Chat {
|
||||||
pageDirection: PageDirection.BACKWARD,
|
pageDirection: PageDirection.BACKWARD,
|
||||||
decryptionKeys: [this.symmetricKey],
|
decryptionKeys: [this.symmetricKey],
|
||||||
callback: (wakuMessages: WakuMessage[]) => {
|
callback: (wakuMessages: WakuMessage[]) => {
|
||||||
// oldest message first
|
let index = wakuMessages.length
|
||||||
for (const wakuMessage of wakuMessages) {
|
|
||||||
this.client.handleWakuMessage(wakuMessage)
|
this.#fetchingMessages = true
|
||||||
|
// most recent message first
|
||||||
|
while (--index >= 0) {
|
||||||
|
this.client.handleWakuMessage(wakuMessages[index])
|
||||||
}
|
}
|
||||||
|
this.#fetchingMessages = false
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// callback
|
this.#previousFetchedStartTime = startTime
|
||||||
// more not found
|
|
||||||
if (
|
|
||||||
_oldestClock &&
|
|
||||||
this.messages.length &&
|
|
||||||
_oldestClock >= this.messages[0].clock
|
|
||||||
) {
|
|
||||||
callback([])
|
|
||||||
|
|
||||||
|
// more chat messages not found
|
||||||
|
if (
|
||||||
|
previousOldestMessage &&
|
||||||
|
this.#oldestFetchedMessage &&
|
||||||
|
// same message
|
||||||
|
previousOldestMessage.messageId === this.#oldestFetchedMessage.messageId
|
||||||
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(this.messages)
|
const messages = this.emitMessages()
|
||||||
|
|
||||||
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
public emitMessages = (messages: ChatMessage[]) => {
|
public emitMessages = () => {
|
||||||
// fixme!: don't emit on backfill
|
if (this.#fetchingMessages) {
|
||||||
this.messageCallbacks.forEach(callback => callback([...messages]))
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.#messages.size) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = this.getMessages()
|
||||||
|
|
||||||
|
this.messageCallbacks.forEach(callback => callback(messages))
|
||||||
|
|
||||||
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleChange = (description: CommunityChat) => {
|
public handleChange = (description: CommunityChat) => {
|
||||||
|
@ -184,140 +242,182 @@ export class Chat {
|
||||||
this.emitChange(description)
|
this.emitChange(description)
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleNewMessage = (message: ChatMessage) => {
|
public handleNewMessage = (newMessage: ChatMessage, timestamp?: Date) => {
|
||||||
let messageIndex = this.messages.length
|
// fetching in progress
|
||||||
while (messageIndex > 0) {
|
if (this.#fetchingMessages) {
|
||||||
const _message = this.messages[messageIndex - 1]
|
this.#oldestFetchedMessage = this.getOldestFetchedMessage(
|
||||||
|
this.#oldestFetchedMessage,
|
||||||
if (_message.clock <= message.clock) {
|
newMessage.messageId,
|
||||||
break
|
timestamp
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
messageIndex--
|
// delete event received first
|
||||||
|
const deletedEvent = this.#deleteEvents.get(newMessage.messageId)
|
||||||
|
if (deletedEvent) {
|
||||||
|
if (this.isAuthor(newMessage, deletedEvent.signer)) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let responseToMessageIndex = this.messages.length
|
// delete unathorized event from stash
|
||||||
while (--responseToMessageIndex >= 0) {
|
this.#deleteEvents.delete(newMessage.messageId)
|
||||||
const _message = this.messages[responseToMessageIndex]
|
|
||||||
|
|
||||||
if (_message.messageId === message.responseTo) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responseToMessageIndex >= 0) {
|
// message already received
|
||||||
message.responseToMessage = this.messages[responseToMessageIndex]
|
const message = this.#messages.get(newMessage.messageId)
|
||||||
|
if (message) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// action events received prior
|
||||||
|
const editTextEvent = this.#editTextEvents.get(newMessage.messageId)
|
||||||
|
if (editTextEvent) {
|
||||||
|
if (this.isAuthor(newMessage, editTextEvent.signer)) {
|
||||||
|
newMessage.text = editTextEvent.text
|
||||||
|
newMessage.edittedClock = editTextEvent.clock
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally, delete event from stash whether it was authorized or not
|
||||||
|
this.#editTextEvents.delete(newMessage.messageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const pinEvent = this.#pinEvents.get(newMessage.messageId)
|
||||||
|
if (pinEvent) {
|
||||||
|
newMessage.pinned = pinEvent.pinned
|
||||||
|
newMessage.pinnedClock = pinEvent.clock
|
||||||
|
|
||||||
|
this.#pinEvents.delete(newMessage.messageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const reactEvent = this.#reactEvents.get(newMessage.messageId)
|
||||||
|
if (reactEvent) {
|
||||||
|
newMessage.reactions = reactEvent.reactions
|
||||||
|
|
||||||
|
this.#reactEvents.delete(newMessage.messageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// state
|
// state
|
||||||
this.messages.splice(messageIndex, 0, message)
|
this.#messages.set(newMessage.messageId, newMessage)
|
||||||
|
|
||||||
// callback
|
// callback
|
||||||
this.emitMessages(this.messages)
|
this.emitMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleEditedMessage = (
|
public handleEditedMessage = (
|
||||||
messageId: string,
|
messageId: string,
|
||||||
text: string,
|
text: string,
|
||||||
|
clock: bigint,
|
||||||
signerPublicKey: string
|
signerPublicKey: string
|
||||||
) => {
|
) => {
|
||||||
let messageIndex = this.messages.length
|
const message = this.#messages.get(messageId)
|
||||||
while (--messageIndex >= 0) {
|
|
||||||
const _message = this.messages[messageIndex]
|
|
||||||
|
|
||||||
if (_message.messageId === messageId) {
|
if (message && this.isAuthor(message, signerPublicKey)) {
|
||||||
break
|
message.text = text
|
||||||
}
|
|
||||||
}
|
this.emitMessages()
|
||||||
|
|
||||||
// original not found
|
|
||||||
if (messageIndex < 0) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isAuthor(this.messages[messageIndex], signerPublicKey)) {
|
const editTextEvent = this.#editTextEvents.get(messageId)
|
||||||
return
|
if (!editTextEvent || editTextEvent.clock < clock) {
|
||||||
}
|
this.#editTextEvents.set(messageId, {
|
||||||
|
clock,
|
||||||
this.messages[messageIndex] = {
|
signer: signerPublicKey,
|
||||||
...this.messages[messageIndex],
|
|
||||||
text,
|
text,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// callback
|
|
||||||
this.emitMessages(this.messages)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleDeletedMessage = (
|
public handleDeletedMessage = (
|
||||||
messageId: string,
|
messageId: string,
|
||||||
|
clock: bigint,
|
||||||
signerPublicKey: string
|
signerPublicKey: string
|
||||||
) => {
|
) => {
|
||||||
let messageIndex = this.messages.length
|
const message = this.#messages.get(messageId)
|
||||||
while (--messageIndex >= 0) {
|
if (message && this.isAuthor(message, signerPublicKey)) {
|
||||||
const _message = this.messages[messageIndex]
|
this.#messages.delete(messageId)
|
||||||
|
this.#deleteEvents.set(messageId, { clock, signer: signerPublicKey })
|
||||||
|
this.emitMessages()
|
||||||
|
|
||||||
if (_message.messageId === messageId) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messageIndex < 0) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isAuthor(this.messages[messageIndex], signerPublicKey)) {
|
const deleteEvent = this.#deleteEvents.get(messageId)
|
||||||
|
if (!deleteEvent || deleteEvent.clock > clock) {
|
||||||
|
this.#deleteEvents.set(messageId, { clock, signer: signerPublicKey })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public handlePinnedMessage = (
|
||||||
|
messageId: string,
|
||||||
|
clock: bigint,
|
||||||
|
pinned?: boolean
|
||||||
|
) => {
|
||||||
|
const message = this.#messages.get(messageId)
|
||||||
|
if (message) {
|
||||||
|
message.pinned = Boolean(pinned)
|
||||||
|
message.pinnedClock = clock
|
||||||
|
|
||||||
|
this.emitMessages()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.messages.splice(messageIndex, 1)
|
const pinEvent = this.#pinEvents.get(messageId)
|
||||||
|
if (!pinEvent || pinEvent.clock < clock) {
|
||||||
this.emitMessages(this.messages)
|
this.#pinEvents.set(messageId, {
|
||||||
|
clock,
|
||||||
|
pinned: Boolean(pinned),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public handlePinnedMessage = (messageId: string, pinned?: boolean) => {
|
|
||||||
let messageIndex = this.messages.length
|
|
||||||
while (--messageIndex >= 0) {
|
|
||||||
const _message = this.messages[messageIndex]
|
|
||||||
|
|
||||||
if (_message.messageId === messageId) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messageIndex < 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.messages[messageIndex].pinned = Boolean(pinned)
|
|
||||||
|
|
||||||
this.emitMessages(this.messages)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleEmojiReaction = (
|
public handleEmojiReaction = (
|
||||||
messageId: string,
|
messageId: string,
|
||||||
reaction: EmojiReaction,
|
reaction: EmojiReaction,
|
||||||
publicKey: string
|
clock: bigint,
|
||||||
|
signerPublicKey: string
|
||||||
) => {
|
) => {
|
||||||
let messageIndex = this.messages.length
|
const message = this.#messages.get(messageId)
|
||||||
while (--messageIndex >= 0) {
|
if (message) {
|
||||||
const _message = this.messages[messageIndex]
|
const reactions = getReactions(
|
||||||
|
reaction,
|
||||||
|
message.reactions,
|
||||||
|
signerPublicKey
|
||||||
|
)
|
||||||
|
message.reactions = reactions
|
||||||
|
|
||||||
if (_message.messageId === messageId) {
|
this.emitMessages()
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messageIndex < 0) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.messages[messageIndex].reactions = getReactions(
|
const reactEvent = this.#reactEvents.get(messageId)
|
||||||
|
if (!reactEvent) {
|
||||||
|
const reactions = getReactions(
|
||||||
reaction,
|
reaction,
|
||||||
this.messages[messageIndex].reactions,
|
{
|
||||||
publicKey
|
THUMBS_UP: new Set<string>(),
|
||||||
|
THUMBS_DOWN: new Set<string>(),
|
||||||
|
LOVE: new Set<string>(),
|
||||||
|
LAUGH: new Set<string>(),
|
||||||
|
SAD: new Set<string>(),
|
||||||
|
ANGRY: new Set<string>(),
|
||||||
|
},
|
||||||
|
signerPublicKey
|
||||||
)
|
)
|
||||||
|
|
||||||
this.emitMessages(this.messages)
|
this.#reactEvents.set(messageId, { clock, reactions })
|
||||||
|
} else {
|
||||||
|
const reactions = getReactions(
|
||||||
|
reaction,
|
||||||
|
reactEvent.reactions,
|
||||||
|
signerPublicKey
|
||||||
|
)
|
||||||
|
|
||||||
|
this.#reactEvents.set(messageId, { clock, reactions })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendTextMessage = async (text: string, responseTo?: string) => {
|
public sendTextMessage = async (text: string, responseTo?: string) => {
|
||||||
|
@ -400,25 +500,13 @@ export class Chat {
|
||||||
throw new Error('Text message cannot be edited without a created account')
|
throw new Error('Text message cannot be edited without a created account')
|
||||||
}
|
}
|
||||||
|
|
||||||
let messageIndex = this.messages.length
|
const message = this.#messages.get(messageId)
|
||||||
while (--messageIndex >= 0) {
|
|
||||||
const _message = this.messages[messageIndex]
|
|
||||||
|
|
||||||
if (_message.messageId === messageId) {
|
if (!message) {
|
||||||
break
|
throw new Error('Message not found')
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageIndex < 0) {
|
if (!this.isAuthor(message, `0x${this.client.account.publicKey}`)) {
|
||||||
throw new Error('Text message was not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!this.isAuthor(
|
|
||||||
this.messages[messageIndex],
|
|
||||||
`0x${this.client.account.publicKey}`
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
throw new Error('Text message can only be edited by its author')
|
throw new Error('Text message can only be edited by its author')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,25 +538,13 @@ export class Chat {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let messageIndex = this.messages.length
|
const message = this.#messages.get(messageId)
|
||||||
while (--messageIndex >= 0) {
|
|
||||||
const _message = this.messages[messageIndex]
|
|
||||||
|
|
||||||
if (_message.messageId === messageId) {
|
if (!message) {
|
||||||
break
|
throw new Error('Message not found')
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageIndex < 0) {
|
if (!this.isAuthor(message, `0x${this.client.account.publicKey}`)) {
|
||||||
throw new Error('Text message was not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!this.isAuthor(
|
|
||||||
this.messages[messageIndex],
|
|
||||||
`0x${this.client.account.publicKey}`
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
throw new Error('Text message can only be deleted by its author')
|
throw new Error('Text message can only be deleted by its author')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,14 +572,14 @@ export class Chat {
|
||||||
throw new Error('Account not initialized')
|
throw new Error('Account not initialized')
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = this.getMessage(messageId)
|
const message = this.#messages.get(messageId)
|
||||||
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
throw new Error('Message not found')
|
throw new Error('Message not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
const retracted = message.reactions[reaction].has(
|
const retracted = message.reactions[reaction].has(
|
||||||
this.client.account.publicKey
|
`0x${this.client.account.publicKey}`
|
||||||
)
|
)
|
||||||
|
|
||||||
const payload = EmojiReaction.encode({
|
const payload = EmojiReaction.encode({
|
||||||
|
@ -530,4 +606,33 @@ export class Chat {
|
||||||
): boolean => {
|
): boolean => {
|
||||||
return message.signer === signerPublicKey
|
return message.signer === signerPublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getOldestFetchedMessage(
|
||||||
|
oldestMessage: FetchedMessage | undefined,
|
||||||
|
messageId: string,
|
||||||
|
messageTimestamp?: Date
|
||||||
|
): FetchedMessage {
|
||||||
|
let message: FetchedMessage
|
||||||
|
|
||||||
|
if (!oldestMessage) {
|
||||||
|
message = {
|
||||||
|
messageId: messageId,
|
||||||
|
timestamp: messageTimestamp,
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
messageTimestamp &&
|
||||||
|
oldestMessage.timestamp &&
|
||||||
|
// is older
|
||||||
|
messageTimestamp < oldestMessage.timestamp
|
||||||
|
) {
|
||||||
|
message = {
|
||||||
|
messageId: messageId,
|
||||||
|
timestamp: messageTimestamp,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message = oldestMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
return message
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,12 +88,12 @@ export class Community {
|
||||||
}
|
}
|
||||||
|
|
||||||
public fetch = async () => {
|
public fetch = async () => {
|
||||||
|
// most recent page first
|
||||||
await this.client.waku.store.queryHistory([this.contentTopic], {
|
await this.client.waku.store.queryHistory([this.contentTopic], {
|
||||||
// oldest message first
|
|
||||||
callback: wakuMessages => {
|
callback: wakuMessages => {
|
||||||
let index = wakuMessages.length
|
let index = wakuMessages.length
|
||||||
|
|
||||||
// most recent page first
|
// most recent message first
|
||||||
while (--index >= 0) {
|
while (--index >= 0) {
|
||||||
this.client.handleWakuMessage(wakuMessages[index])
|
this.client.handleWakuMessage(wakuMessages[index])
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ export function handleWakuMessage(
|
||||||
decodedProtocol?.publicMessage ?? wakuMessage.payload,
|
decodedProtocol?.publicMessage ?? wakuMessage.payload,
|
||||||
signerPublicKeyBytes
|
signerPublicKeyBytes
|
||||||
)
|
)
|
||||||
|
const messageTimestamp = wakuMessage.timestamp
|
||||||
const signerPublicKey = `0x${bytesToHex(signerPublicKeyBytes)}`
|
const signerPublicKey = `0x${bytesToHex(signerPublicKeyBytes)}`
|
||||||
|
|
||||||
// already handled
|
// already handled
|
||||||
|
@ -112,7 +113,7 @@ export function handleWakuMessage(
|
||||||
})
|
})
|
||||||
|
|
||||||
// handle
|
// handle
|
||||||
chat.handleNewMessage(chatMessage)
|
chat.handleNewMessage(chatMessage, messageTimestamp)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -145,6 +146,7 @@ export function handleWakuMessage(
|
||||||
chat.handleEditedMessage(
|
chat.handleEditedMessage(
|
||||||
messageId,
|
messageId,
|
||||||
decodedPayload.text,
|
decodedPayload.text,
|
||||||
|
decodedPayload.clock,
|
||||||
signerPublicKey
|
signerPublicKey
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -176,7 +178,11 @@ export function handleWakuMessage(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chat.handleDeletedMessage(messageId, signerPublicKey)
|
chat.handleDeletedMessage(
|
||||||
|
messageId,
|
||||||
|
decodedPayload.clock,
|
||||||
|
signerPublicKey
|
||||||
|
)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -206,7 +212,11 @@ export function handleWakuMessage(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chat.handlePinnedMessage(messageId, decodedPayload.pinned)
|
chat.handlePinnedMessage(
|
||||||
|
messageId,
|
||||||
|
decodedPayload.clock,
|
||||||
|
decodedPayload.pinned
|
||||||
|
)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -236,7 +246,12 @@ export function handleWakuMessage(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chat.handleEmojiReaction(messageId, decodedPayload, signerPublicKey)
|
chat.handleEmojiReaction(
|
||||||
|
messageId,
|
||||||
|
decodedPayload,
|
||||||
|
decodedPayload.clock,
|
||||||
|
signerPublicKey
|
||||||
|
)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
import { useMatch } from 'react-router-dom'
|
||||||
|
|
||||||
import { UserProfileDialog } from '~/src/components/user-profile-dialog'
|
import { UserProfileDialog } from '~/src/components/user-profile-dialog'
|
||||||
import { useChatContext } from '~/src/contexts/chat-context'
|
import { useChatContext } from '~/src/contexts/chat-context'
|
||||||
import { BellIcon } from '~/src/icons/bell-icon'
|
import { BellIcon } from '~/src/icons/bell-icon'
|
||||||
|
@ -56,27 +58,22 @@ interface Props {
|
||||||
|
|
||||||
export const ChatMessage = (props: Props) => {
|
export const ChatMessage = (props: Props) => {
|
||||||
const { client, account } = useProtocol()
|
const { client, account } = useProtocol()
|
||||||
|
const { params } = useMatch(':id')!
|
||||||
|
|
||||||
|
const chatId = params.id!
|
||||||
const { message } = props
|
const { message } = props
|
||||||
|
|
||||||
const mention = false
|
const mention = false
|
||||||
const pinned = false
|
const pinned = false
|
||||||
|
|
||||||
const {
|
const { messageId, contentType, clock, reactions, signer, responseTo } =
|
||||||
messageId,
|
message
|
||||||
chatId,
|
|
||||||
contentType,
|
|
||||||
clock,
|
|
||||||
reactions,
|
|
||||||
sender,
|
|
||||||
responseTo,
|
|
||||||
} = message
|
|
||||||
|
|
||||||
// TODO: remove usage of 0x prefix
|
// TODO: remove usage of 0x prefix
|
||||||
const owner = '0x' + account?.publicKey === sender
|
const owner = '0x' + account?.publicKey === signer
|
||||||
const chat = client.community.getChatById(chatId)
|
const chat = client.community.getChat(chatId)
|
||||||
|
|
||||||
const member = client.community.getMember(sender) ?? {}
|
const member = client.community.getMember(signer) ?? {}
|
||||||
|
|
||||||
const [editing, setEditing] = useState(false)
|
const [editing, setEditing] = useState(false)
|
||||||
const [reacting, setReacting] = useState(false)
|
const [reacting, setReacting] = useState(false)
|
||||||
|
|
|
@ -29,8 +29,9 @@ export const MessageReply = (props: Props) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { contentType, text, sender } = message
|
const { contentType, text, signer } = message
|
||||||
const { username } = client.community.getMember(sender)
|
|
||||||
|
const { username } = client.community.getMember(signer)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
|
|
Loading…
Reference in New Issue