Skip to content

Commit

Permalink
feat(chat): render and store incoming messages (#4137)
Browse files Browse the repository at this point in the history
* feat(chat): wip

* feat(chat): setup cid decoding on publish

* feat(chat): send payload via publish

* fix: remove console log

Co-authored-by: Andrew Ewing <drew@phenocode.com>
  • Loading branch information
josephmcg and aewing committed Aug 5, 2022
1 parent 8b64cdf commit bca00fe
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 83 deletions.
11 changes: 9 additions & 2 deletions components/views/chat/conversation/Conversation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ export default Vue.extend({
},
data() {
return {
messages: iridium.chat.messages?.[this.$route.params.id] ?? [],
conversation:
iridium.chat.state.conversations?.[this.$route.params.id] ?? [],
iridium.chat.state.conversations?.[this.$route.params.id] ?? {},
numMessages: MESSAGE_PAGE_SIZE,
isLoadingMore: false,
}
Expand All @@ -33,6 +32,14 @@ export default Vue.extend({
myDid(): string {
return iridium.connector?.id ?? ''
},
messages(): ConversationMessage[] {
if (!Object.keys(this.conversation).length) {
return []
}
return Object.values(this.conversation.message).sort(
(a, b) => a.at - b.at,
)
},
chatItems(): ChatItem[] {
return this.messages
.filter((message) => !message.replyToId)
Expand Down
5 changes: 3 additions & 2 deletions components/views/navigation/sidebar/list/List.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export default Vue.extend({
},
data: () => ({
conversations: iridium.chat.state.conversations,
messages: iridium.chat.messages,
}),
computed: {
sortedConversations(): Conversation[] {
Expand All @@ -28,7 +27,9 @@ export default Vue.extend({
})
},
lastMessageTimestamp(conversation: Conversation): number {
const messages = this.messages[conversation.id]
const messages = Object.values(
this.conversations[conversation.id].message,
).sort((a, b) => a.at - b.at)
return messages.at(-1)?.at ?? (conversation.updatedAt || 0)
},
},
Expand Down
17 changes: 9 additions & 8 deletions components/views/navigation/sidebar/list/item/Item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export default Vue.extend({
timeoutId: undefined as NodeJS.Timeout | undefined,
friends: iridium.friends.list,
groups: iridium.groups.state,
messages: iridium.chat.messages[this.conversation.id],
}
},
computed: {
Expand Down Expand Up @@ -66,19 +65,21 @@ export default Vue.extend({
},
]
},
lastMessage(): ConversationMessage | undefined {
if (!this.messages.length) {
return undefined
messages(): ConversationMessage[] {
if (!Object.keys(this.conversation).length) {
return []
}
return this.messages[this.messages.length - 1]
return Object.values(this.conversation.message).sort(
(a, b) => a.at - b.at,
)
},
lastMessageDisplay(): string {
if (!this.lastMessage) {
const lastMessage = this.messages.at(-1)
if (!lastMessage) {
return this.$t('messaging.say_hi') as string
}
return this.lastMessage.body || ''
return lastMessage.body || ''
// const sender = message.from === iridium.connector?.id ? 'me' : 'user'
Expand Down
1 change: 1 addition & 0 deletions libraries/Iridium/IridiumManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export class IridiumManager extends Emitter {
this.chat = new ChatManager(this)
this.files = new FilesManager(this)
this.settings = new SettingsManager(this)
window.i = this
}

/**
Expand Down
124 changes: 53 additions & 71 deletions libraries/Iridium/chat/ChatManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
} from '@satellite-im/iridium'
import type { SyncSubscriptionResponse } from '@satellite-im/iridium/src/sync/agent'
import type { EmitterCallback } from '@satellite-im/iridium'
// Iridium import above has static function called hash, use to hash this user id and the name of the chat

import {
Conversation,
Expand All @@ -21,10 +20,8 @@ import { IridiumManager } from '~/libraries/Iridium/IridiumManager'
import logger from '~/plugins/local/logger'

export type ConversationPubsubEvent = IridiumMessage<{
type: string
conversation: string
message?: string
payload?: any
message: ConversationMessage
type: 'chat/message'
}>

export type State = {
Expand All @@ -45,8 +42,6 @@ export default class ChatManager extends Emitter<ConversationMessage> {
conversations: {},
}

public messages: Conversations = {}

private _intervals: { [key: string]: any } = {}
private _subscriptions: {
[key: string]: { topic: string; connected: boolean }
Expand All @@ -57,7 +52,7 @@ export default class ChatManager extends Emitter<ConversationMessage> {
}

async init() {
await this.fetch()
this.state = ((await this.get()) as State) ?? initialState
const conversations = Object.values(this.state.conversations)
// listen for sync node subscription responses
this.iridium.connector?.p2p.on<
Expand Down Expand Up @@ -137,22 +132,6 @@ export default class ChatManager extends Emitter<ConversationMessage> {
)
}

async fetch() {
this.state = ((await this.get()) as State) ?? initialState
for (const conversation of Object.values(this.state.conversations)) {
Vue.set(
this.messages,
conversation.id,
Object.entries(conversation.message)
.map(([key, value]) => ({
...value,
id: key,
}))
.sort((a, b) => a.at - b.at),
)
}
}

get(path: string = '', options: any = {}) {
return this.iridium.connector?.get(`/chat${path}`, options)
}
Expand All @@ -163,38 +142,30 @@ export default class ChatManager extends Emitter<ConversationMessage> {

async onConversationMessage(
conversationId: string,
message: ConversationPubsubEvent,
{ from, payload }: ConversationPubsubEvent,
) {
console.info('onConversationMessage', message)
// if (!this.iridium.connector) return
// const { from, did, payload } = message
// const conversation = await this.getConversation(conversationId)
// if (!conversation || !conversation.participants.includes(did)) {
// throw new Error(ChatError.CONVERSATION_NOT_FOUND)
// }
// const { type, message: messageCID } = payload
// if (type === 'chat/message' && messageCID) {
// // TODO: type check the message?
// const msg = await this.iridium.connector.load(messageCID, {
// decrypt: true,
// })
// if (msg) {
// conversation.messages.push(messageCID)
// conversation.message[messageCID] = msg
// this.state.conversation[conversationId] = conversation
// await this.set(
// `/conversations/${conversationId}/messages`,
// conversation.messages,
// )
// await this.set(
// `/conversations/${conversationId}/message/${messageCID}`,
// msg,
// )
// await this.saveConversation(conversation)
// }
// }

// this.emit(`conversations/${conversationId}`, payload)
if (!this.iridium.connector) {
return
}
const conversation = this.getConversation(conversationId)
if (
!conversation ||
!conversation.participants.includes(didUtils.didString(from))
) {
throw new Error(ChatError.CONVERSATION_NOT_FOUND)
}
const { type, message } = payload.body
if (type === 'chat/message' && message) {
Vue.set(
this.state.conversations[conversationId].message,
message.id,
message,
)
this.set(
`/conversations/${conversationId}/message/${message.id}`,
message,
)
}
}

hasConversation(id: string) {
Expand Down Expand Up @@ -255,11 +226,10 @@ export default class ChatManager extends Emitter<ConversationMessage> {
)
}

Vue.set(this.messages, id, [])
Vue.set(this.state.conversations, id, conversation)
}

getConversation(id: string): Conversation {
getConversation(id: Conversation['id']): Conversation {
const conversation = this.state.conversations[id]
if (!conversation) {
throw new Error(ChatError.CONVERSATION_NOT_FOUND)
Expand Down Expand Up @@ -319,35 +289,47 @@ export default class ChatManager extends Emitter<ConversationMessage> {

const { conversationId } = payload
const conversation = this.getConversation(conversationId)
const message: Omit<ConversationMessage, 'id'> = {
...payload,
from: this.iridium.connector.id,
reactions: {},
attachments: [],
const messageID = await this.iridium.connector.store(
{
...payload,
from: this.iridium.connector.id,
reactions: {},
attachments: [],
},
{
encrypt: { recipients: conversation.participants },
},
)
if (!messageID) {
throw new Error(ChatError.MESSAGE_NOT_SENT)
}

if (!this._subscriptions[conversationId]) {
// we're not subscribed yet
throw new Error(`not yet subscribed to conversation ${conversationId}`)
}

const messageID = await this.iridium.connector.store(message, {
encrypt: { recipients: conversation.participants },
})
if (!messageID) {
throw new Error(ChatError.MESSAGE_NOT_SENT)
}
const messageCID = messageID.toString()
this.messages[conversationId].push({ ...message, id: messageCID })
const message: ConversationMessage = {
...payload,
from: this.iridium.connector.id,
reactions: {},
attachments: [],
id: messageCID,
}
Vue.set(
this.state.conversations[conversationId].message,
messageCID,
message,
)
this.set(`/conversations/${conversationId}/message/${messageCID}`, message)

// broadcast the message to connected peers
await this.iridium.connector.publish(
`/chat/conversations/${conversationId}`,
{
type: 'chat/message',
conversation: conversationId,
message: messageCID,
message,
},
{
encrypt: { recipients: conversation.participants },
Expand Down

0 comments on commit bca00fe

Please sign in to comment.