Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(chat): render and store incoming messages #4137

Merged
merged 5 commits into from
Aug 5, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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