Skip to content

Commit

Permalink
add community api
Browse files Browse the repository at this point in the history
  • Loading branch information
Flemmli97 committed Dec 12, 2024
1 parent 2f8c6ac commit 467809d
Show file tree
Hide file tree
Showing 3 changed files with 349 additions and 185 deletions.
1 change: 0 additions & 1 deletion src/lib/layouts/Chatbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@
UIStore.mutateChat(chat.id, c => {
c.last_view_date = new Date()
})
ConversationStore.addPendingMessages(chat.id, res.message, txt)
})
if (!isStickerOrGif) {
chatMessages.update(messages => {
Expand Down
228 changes: 201 additions & 27 deletions src/lib/wasm/CommunitiesStore.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { get, writable, type Writable } from "svelte/store"
import { get, type Writable } from "svelte/store"
import * as wasm from "warp-wasm"
import { WarpStore } from "./WarpStore"
import { type Cancellable } from "$lib/utils/CancellablePromise"
import { failure, success, type Result } from "$lib/utils/Result"
import { handleErrors, WarpError } from "./HandleWarpErrors"
import { MessageOptions } from "warp-wasm"
import { convertWarpAttachment, createFileAttachHandler, createFileDownloadHandler, createFileDownloadHandlerRaw, type FetchMessageResponse, type FetchMessagesConfig, type FileAttachment, type SendMessageResult } from "./RaygunStore"
import { messageTypeFromTexts, type Message, type Reaction } from "$lib/types"
import { MultipassStoreInstance } from "./MultipassStore"
import { Store } from "$lib/state/Store"
import { Appearance } from "$lib/enums"

class CommunitiesStore {
private raygunWritable: Writable<wasm.RayGunBox | null>
Expand All @@ -12,9 +17,6 @@ class CommunitiesStore {
this.raygunWritable = raygun
}

// async getCommunityStream(community_id: string): Promise<AsyncIterator> {

// }
async createCommunity(name: string) {
return this.get(r => r.create_community(name), `Error creating a community with name ${name}`)
}
Expand Down Expand Up @@ -136,28 +138,166 @@ class CommunitiesStore {
return this.get(r => r.edit_community_channel_description(community_id, channel_id, description), `Error editing channel ${channel_id} description for community ${community_id}`)
}

async grantCommunityChannelPermission(community_id: string, channel_id: string, permission: CommunityChannelPermission, role_id: string)
async revokeCommunityChannelPermission(community_id: string, channel_id: string, permission: CommunityChannelPermission, role_id: string)
async grantCommunityChannelPermissionForAll(community_id: string, channel_id: string, permission: CommunityChannelPermission)
async revokeCommunityChannelPermissionForAll(community_id: string, channel_id: string, permission: CommunityChannelPermission)

async getCommunityChannelMessage(community_id: string, channel_id: string, message_id: string)
async getCommunityChannelMessages(community_id: string, channel_id: string, options: MessageOptions)
async getCommunityChannelMessageCount(community_id: string, channel_id: string)
async getCommunityChannelMessageReference(community_id: string, channel_id: string, message_id: string)
async getCommunityChannelMessageReferences(community_id: string, channel_id: string, options: MessageOptions)

async communityChannelMessageStatus(community_id: string, channel_id: string, message_id: string)
async sendCommunityChannelMessage(community_id: string, channel_id: string, message: string[])
async editCommunityChannelMessage(community_id: string, channel_id: string, message_id: string, message: string[])
async replyToCommunityChannelMessage(community_id: string, channel_id: string, message_id: string, message: string[])
async deleteCommunityChannelMessage(community_id: string, channel_id: string, message_id: string)
async pinCommunityChannelMessage(community_id: string, channel_id: string, message_id: string, state: PinState)
async reactToCommunityChannelMessage(community_id: string, channel_id: string, message_id: string, state: ReactionState, emoji: string)
async sendCommunityChannelMesssageEvent(community_id: string, channel_id: string, event: MessageEvent)
async cancelCommunityChannelMesssageEvent(community_id: string, channel_id: string, event: MessageEvent)
async attachToCommunityChannelMessage(community_id: string, channel_id: string, message_id: string | undefined, files: AttachmentFile[], message: string[])
async downloadFromCommunityChannelMessage(community_id: string, channel_id: string, message_id: string, file: string) {}
async grantCommunityChannelPermission(community_id: string, channel_id: string, permission: CommunityChannelPermission, role_id: string) {
return this.get(r => r.grant_community_channel_permission(community_id, channel_id, permission, role_id), `Error granting channel permission for role ${role_id} in community ${community_id}`)
}
async revokeCommunityChannelPermission(community_id: string, channel_id: string, permission: CommunityChannelPermission, role_id: string) {
return this.get(r => r.revoke_community_channel_permission(community_id, channel_id, permission, role_id), `Error revoking channel permission for role ${role_id} in for community ${community_id}`)
}
async grantCommunityChannelPermissionForAll(community_id: string, channel_id: string, permission: CommunityChannelPermission) {
return this.get(r => r.grant_community_channel_permission_for_all(community_id, channel_id, permission), `Error granting channel permission for community ${community_id}`)
}
async revokeCommunityChannelPermissionForAll(community_id: string, channel_id: string, permission: CommunityChannelPermission) {
return this.get(r => r.revoke_community_channel_permission_for_all(community_id, channel_id, permission), `Error revoking channel permission for community ${community_id}`)
}

async getCommunityChannelMessage(community_id: string, channel_id: string, message_id: string) {
return this.get(r => r.get_community_channel_message(community_id, channel_id, message_id), `Error revoking channel permission for community ${community_id}`)
}

async fetchCommunityMessages(community_id: string, channel_id: string, config: FetchMessagesConfig): Promise<Result<WarpError, FetchMessageResponse>> {
return this.get(async r => {
let message_options = new MessageOptions()
switch (config.type) {
case "Between": {
message_options.set_date_range(config.from, config.to) //TODO verify that js Date can be parsed to rust DateTime::<Utc>
break
}
case "MostRecent": {
let total_messages = await r.get_community_channel_message_count(community_id, channel_id)
message_options.set_range(Math.min(0, total_messages - config.amount), total_messages)
break
}
case "Earlier": {
message_options.set_date_range(new Date(), config.start_date)
message_options.set_reverse()
message_options.set_limit(config.limit)
break
}
case "Later": {
message_options.set_date_range(config.start_date, new Date())
message_options.set_limit(config.limit)
break
}
}

let messages = await this.getMessages(r, community_id, channel_id, message_options)
if (config.type === "Earlier") {
messages = messages.reverse()
}
let has_more = "limit" in config ? messages.length >= config.limit : false

let opt = new MessageOptions()
opt.set_limit(1)
opt.set_last_message()
let most_recent = await this.getMessages(r, community_id, channel_id, opt)
return {
messages: messages,
has_more: has_more,
most_recent: most_recent[most_recent.length].id,
}
}, "Error fetching messages")
}

private async getMessages(raygun: wasm.RayGunBox, community_id: string, channel_id: string, options: MessageOptions) {
let msgs = await raygun.get_community_channel_messages(community_id, channel_id, options)
let messages: Message[] = []
if (msgs.variant() === wasm.MessagesEnum.List) {
let warpMsgs = msgs.messages()!
messages = (await Promise.all(warpMsgs.map(async msg => await this.convertCommunityMessage(community_id, channel_id, msg)))).filter((m: Message | null): m is Message => m !== null)
}
return messages
}

async communityChannelMessageStatus(community_id: string, channel_id: string, message_id: string) {
return this.get(r => r.community_channel_message_status(community_id, channel_id, message_id), `Error fetching message status for message ${message_id} in community ${community_id}`)
}
async sendCommunityChannelMessage(community_id: string, channel_id: string, message: string[], attachments?: FileAttachment[]) {
return await this.get(async r => {
return await this.sendTo(r, community_id, channel_id, message, { attachments })
}, `Error sending message in channel ${channel_id} for community ${community_id}`)
}
async editCommunityChannelMessage(community_id: string, channel_id: string, message_id: string, message: string[]) {
return this.get(r => r.edit_community_channel_message(community_id, channel_id, message_id, message), `Error revoking channel permission for community ${community_id}`)
}
async replyToCommunityChannelMessage(community_id: string, channel_id: string, message_id: string, message: string[], attachments?: FileAttachment[]) {
return await this.get(async r => {
return await this.sendTo(r, community_id, channel_id, message, { attachments, replyTo: message_id })
}, `Error replying to message ${message_id} in channel ${channel_id} for community ${community_id}`)
}
async deleteCommunityChannelMessage(community_id: string, channel_id: string, message_id: string) {
return this.get(r => r.delete_community_channel_message(community_id, channel_id, message_id), `Error deleting message ${message_id} for channel ${channel_id} in community ${community_id}`)
}
async pinCommunityChannelMessage(community_id: string, channel_id: string, message_id: string, pin: boolean) {
return this.get(r => r.pin_community_channel_message(community_id, channel_id, message_id, pin ? wasm.PinState.Pin : wasm.PinState.Unpin), `Error pinning message for community ${community_id}`)
}
async reactToCommunityChannelMessage(community_id: string, channel_id: string, message_id: string, state: wasm.ReactionState, emoji: string) {
let result = await this.get(r => r.react_to_community_channel_message(community_id, channel_id, message_id, state, emoji), "Error reacting to community message")
return result.map(_ => {
// TODO: Requires Store changes
// ConversationStore.editReaction(get(Store.state.activeChat).id, message_id, emoji, state == wasm.ReactionState.Add)
})
}

async downloadFromCommunityChannelMessage(community_id: string, channel_id: string, message_id: string, file: string, size?: number) {
return await this.get(async r => {
let result = await r.download_stream_from_community_channel_message(community_id, channel_id, message_id, file)
return createFileDownloadHandler(file, result, size)
}, `Error downloading community attachment from community ${community_id} for message ${message_id}`)
}

async getCommunityMessageAttachmentRaw(community_id: string, channel_id: string, message_id: string, file: string, options?: { size?: number; type?: string }) {
return await this.get(async r => {
let result = await r.download_stream_from_community_channel_message(community_id, channel_id, message_id, file)
return createFileDownloadHandlerRaw(file, result, options)
}, `Error downloading community attachment from community ${community_id} for message ${message_id}`)
}

async sendCommunityChannelMesssageEvent(community_id: string, channel_id: string, event: wasm.MessageEvent) {
return await this.get(r => r.send_community_channel_messsage_event(community_id, channel_id, event), `Error sending event ${event}`)
}
async cancelCommunityChannelMesssageEvent(community_id: string, channel_id: string, event: wasm.MessageEvent) {
return await this.get(r => r.cancel_community_channel_messsage_event(community_id, channel_id, event), `Error sending event ${event}`)
}

private async sendTo(raygun: wasm.RayGunBox, community_id: string, channel_id: string, message: string[], settings?: { attachments?: FileAttachment[]; replyTo?: string }): Promise<SendMessageResult> {
if (settings?.attachments && settings?.attachments.length > 0) {
let result = await raygun
.attach_to_community_channel_message(
community_id,
channel_id,
settings?.replyTo,
settings?.attachments.map(f => new wasm.AttachmentFile(f.file, f.attachment ? new wasm.AttachmentStream(f.attachment[1], f.attachment[0]) : undefined)),
message
)
.then(res => {
// message_sent event gets fired AFTER this returns
// TODO: Requires Store changes
// ConversationStore.addPendingMessages(conversation_id, res.get_message_id(), message)
createFileAttachHandler(res, (file, updater) => {
//TODO: Update file on store
})
return res
})
return {
message: result.get_message_id(),
progress: result,
}
}
return {
message: await (settings?.replyTo ? raygun.reply_to_community_channel_message(community_id, channel_id, settings.replyTo, message) : raygun.send_community_channel_message(community_id, channel_id, message)).then(messageId => {
// message_sent event gets fired BEFORE this returns
// So to
// 1. unify this system
// 2. keep it roughly the same as native (as on native due to some channel delays it handles message_sent after #send returns)
// We add the pending msg here and remove it in message_sent which has a short delay
// TODO: Requires Store changes
// ConversationStore.addPendingMessages(conversation_id, messageId, message)
return messageId
}),
}
}

/**
* Convenient helper method to get data from raygun
*/
Expand All @@ -172,6 +312,40 @@ class CommunitiesStore {
}
return failure(WarpError.RAYGUN_NOT_FOUND)
}

private async convertCommunityMessage(community_id: string, channel_id: string, message: wasm.Message | undefined): Promise<Message | null> {
if (!message) return null
let user = get(Store.state.user)
let remote = message.sender() !== user.key
if (remote) {
let sender = await MultipassStoreInstance.identity_from_did(message.sender())
if (sender) Store.updateUser(sender)
}
let attachments = message.attachments()
let reactions: { [key: string]: Reaction } = {}
message.reactions().forEach((dids, emoji) => {
reactions[emoji] = {
reactors: new Set(dids),
emoji: emoji,
highlight: Appearance.Default, //TODO
description: "", //TODO
}
})
return {
id: message.id(),
details: {
at: message.date(),
origin: message.sender(),
remote: remote,
},
text: message.lines(),
inReplyTo: message.replied() ? null : null, // fetch from ConversationStore
reactions: reactions,
attachments: attachments.map(f => convertWarpAttachment(f)),
pinned: message.pinned(),
type: messageTypeFromTexts(message.lines()),
}
}
}

export type CommunityPermission = wasm.CommunityPermission
Expand Down
Loading

0 comments on commit 467809d

Please sign in to comment.