diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts index 38e85996e..a979b68dc 100644 --- a/src/lib/components/index.ts +++ b/src/lib/components/index.ts @@ -22,6 +22,7 @@ export { default as PopupButton } from "$lib/components/ui/PopupButton.svelte" export { default as Modal } from "$lib/components/ui/Modal.svelte" export { default as Titlebar } from "$lib/components/ui/Titlebar.svelte" export { default as ContextMenu } from "$lib/components/ui/ContextMenu.svelte" +export { default as MessageText } from "$lib/components/messaging/message/MessageText.svelte" export { default as Message } from "$lib/components/messaging/message/Message.svelte" export { default as MessageReactions } from "$lib/components/messaging/message/MessageReactions.svelte" export { default as MessageGroup } from "$lib/components/messaging/MessageGroup.svelte" diff --git a/src/lib/components/messaging/message/MessageText.svelte b/src/lib/components/messaging/message/MessageText.svelte new file mode 100644 index 000000000..d865440c7 --- /dev/null +++ b/src/lib/components/messaging/message/MessageText.svelte @@ -0,0 +1,58 @@ + + +{#each texts as line} + {#if getValidPaymentRequest(line) != undefined} + + {:else if type === MessageType.CDN} +
+ +
+ {:else if type === MessageType.CALL_START} + +
+ + +
+ {:else} + + {/if} +{/each} + + diff --git a/src/lib/lang/en.json b/src/lib/lang/en.json index 296e95f76..69cf328ee 100644 --- a/src/lib/lang/en.json +++ b/src/lib/lang/en.json @@ -353,7 +353,8 @@ "everybodyDeniedTheCall": "Everybody Denied the call. Disconnecting...", "acceptedCall": "Joined, loading...", "connecting": "Connecting...", - "noResponse": "No response" + "noResponse": "No response", + "join": "Join" }, "notifications": { "name": "Notifications", diff --git a/src/lib/media/Voice.ts b/src/lib/media/Voice.ts index 404c06907..28ecdd54a 100644 --- a/src/lib/media/Voice.ts +++ b/src/lib/media/Voice.ts @@ -443,7 +443,7 @@ export class VoiceRTC { * Actually making the call */ async makeCall(call: boolean = true) { - if (!this.toCall) { + if (!this.toCall && !call) { log.error("Calling not setup") return } @@ -457,7 +457,10 @@ export class VoiceRTC { // Create a new call room this.createAndSetRoom() if (call) { - this.inviteToCall(this.toCall) + this.inviteToCall(this.toCall!) + const formattedEndTime = new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false }) + const text = get(_)("settings.calling.startCallMessage", { values: { value: formattedEndTime } }) + await RaygunStoreInstance.send(this.channel!, text.split("\n"), []) } const timeoutWhenCallIsNull = setTimeout(() => { if (this.call === null || this.call.empty) { diff --git a/src/lib/mock/messages.ts b/src/lib/mock/messages.ts index c577aac2d..5f38d1225 100644 --- a/src/lib/mock/messages.ts +++ b/src/lib/mock/messages.ts @@ -1,5 +1,5 @@ import { Appearance, MessageAttachmentKind, Shape } from "$lib/enums" -import type { MessageGroup } from "$lib/types" +import { MessageType, type MessageGroup } from "$lib/types" import { mock_users } from "./users" import { v4 as uuidv4 } from "uuid" @@ -37,6 +37,7 @@ export let mock_messages: MessageGroup[] = [ }, attachments: [], pinned: false, + type: MessageType.DEFAULT, }, { id: uuidv4(), @@ -63,6 +64,7 @@ export let mock_messages: MessageGroup[] = [ }, ], pinned: false, + type: MessageType.DEFAULT, }, { id: uuidv4(), @@ -83,6 +85,7 @@ export let mock_messages: MessageGroup[] = [ }, ], pinned: false, + type: MessageType.DEFAULT, }, { id: uuidv4(), @@ -96,6 +99,7 @@ export let mock_messages: MessageGroup[] = [ reactions: {}, attachments: [], pinned: false, + type: MessageType.DEFAULT, }, { id: uuidv4(), @@ -109,6 +113,7 @@ export let mock_messages: MessageGroup[] = [ reactions: {}, attachments: [], pinned: false, + type: MessageType.DEFAULT, }, ], }, @@ -151,6 +156,7 @@ export let mock_messages: MessageGroup[] = [ }, ], pinned: true, + type: MessageType.DEFAULT, }, { id: uuidv4(), @@ -171,6 +177,7 @@ export let mock_messages: MessageGroup[] = [ }, ], pinned: false, + type: MessageType.DEFAULT, }, { id: uuidv4(), @@ -191,6 +198,7 @@ export let mock_messages: MessageGroup[] = [ }, ], pinned: false, + type: MessageType.DEFAULT, }, { id: uuidv4(), @@ -204,6 +212,7 @@ export let mock_messages: MessageGroup[] = [ reactions: {}, attachments: [], pinned: false, + type: MessageType.DEFAULT, }, ], }, @@ -235,10 +244,12 @@ export let mock_messages: MessageGroup[] = [ reactions: {}, attachments: [], pinned: false, + type: MessageType.DEFAULT, }, reactions: {}, attachments: [], pinned: true, + type: MessageType.DEFAULT, }, ], }, diff --git a/src/lib/types/index.ts b/src/lib/types/index.ts index aa812690d..1f31c0a33 100644 --- a/src/lib/types/index.ts +++ b/src/lib/types/index.ts @@ -15,7 +15,9 @@ import { CommunitySettingsRoute, } from "$lib/enums" import type { Cancellable } from "$lib/utils/CancellablePromise" -import type { Writable } from "svelte/store" +import { tempCDN } from "$lib/utils/CommonVariables" +import { get, type Writable } from "svelte/store" +import { _ } from "svelte-i18n" export interface Serialize { serialize(): any @@ -359,6 +361,28 @@ export type MessageDetails = { remote: boolean } +export enum MessageType { + DEFAULT, + SYSTEM, // Until warp supports some built in system message method its client sided + CALL_START, + CDN, +} + +export function messageTypeFromTexts(texts: string[]): MessageType { + if (texts.some(text => text.includes(tempCDN))) { + return MessageType.CDN + } + const endCallReg = new RegExp(get(_)("settings.calling.endCallMessage", { values: { formattedEndTime: "(.*)", duration: "(.*)" } })) + if (texts.some(text => text.includes("giphy.com")) || texts.some(text => text.includes(get(_)("settings.calling.callMissed"))) || texts.some(text => text.match(endCallReg))) { + return MessageType.SYSTEM + } + const startCallReg = new RegExp(get(_)("settings.calling.startCallMessage", { values: { value: "(.*)" } })) + if (texts.some(text => text.match(startCallReg))) { + return MessageType.CALL_START + } + return MessageType.DEFAULT +} + export type Message = { id: string details: MessageDetails @@ -367,6 +391,7 @@ export type Message = { attachments: Attachment[] text: string[] pinned: boolean + type: MessageType } export function mentions_user(message: Message, user: string): boolean { diff --git a/src/lib/wasm/RaygunStore.ts b/src/lib/wasm/RaygunStore.ts index 0c8304d9d..6dfb48aa0 100644 --- a/src/lib/wasm/RaygunStore.ts +++ b/src/lib/wasm/RaygunStore.ts @@ -6,7 +6,7 @@ import { UIStore } from "../state/ui" import { ConversationStore } from "../state/conversation" import { MessageOptions } from "warp-wasm" import { ChatType, MessageAttachmentKind, Route } from "$lib/enums" -import { type User, type Chat, defaultChat, type Message, mentions_user, type Attachment } from "$lib/types" +import { type User, type Chat, defaultChat, type Message, mentions_user, type Attachment, messageTypeFromTexts } from "$lib/types" import { WarpError, handleErrors } from "./HandleWarpErrors" import { failure, success, type Result } from "$lib/utils/Result" import { create_cancellable_handler, type Cancellable } from "$lib/utils/CancellablePromise" @@ -769,6 +769,7 @@ class RaygunStore { reactions: message.reactions(), attachments: attachments.map(f => this.convertWarpAttachment(f)), pinned: message.pinned(), + type: messageTypeFromTexts(message.lines()), } } diff --git a/src/routes/chat/+page.svelte b/src/routes/chat/+page.svelte index 10bfc869c..f7f248af3 100644 --- a/src/routes/chat/+page.svelte +++ b/src/routes/chat/+page.svelte @@ -5,11 +5,26 @@ import { animationDuration } from "$lib/globals/animations" import { slide } from "svelte/transition" import { Chatbar, Sidebar, Topbar, Profile } from "$lib/layouts" - import { ImageEmbed, ChatPreview, Conversation, Message, MessageGroup, MessageReactions, MessageReplyContainer, ProfilePicture, Modal, ProfilePictureMany, ChatFilter, ContextMenu, EmojiGroup } from "$lib/components" + import { + ImageEmbed, + ChatPreview, + Conversation, + Message as MessageComponent, + MessageGroup, + MessageReactions, + MessageReplyContainer, + ProfilePicture, + Modal, + ProfilePictureMany, + ChatFilter, + ContextMenu, + EmojiGroup, + MessageText, + } from "$lib/components" import CreateTransaction from "$lib/components/wallet/CreateTransaction.svelte" import { Button, FileInput, Icon, Label, Text } from "$lib/elements" import CallScreen from "$lib/components/calling/CallScreen.svelte" - import { type MessageGroup as MessageGroupType } from "$lib/types" + import { MessageType, type MessageGroup as MessageGroupType } from "$lib/types" import EncryptedNotice from "$lib/components/messaging/EncryptedNotice.svelte" import { Store } from "$lib/state/Store" import { derived, get } from "svelte/store" @@ -21,19 +36,17 @@ import ViewMembers from "$lib/components/group/ViewMembers.svelte" import Market from "$lib/components/market/Market.svelte" import { RaygunStoreInstance } from "$lib/wasm/RaygunStore" - import type { Attachment, FileInfo, Message as MessageType, User } from "$lib/types" + import type { Attachment, Message, User } from "$lib/types" import Input from "$lib/elements/Input/Input.svelte" import PendingMessage from "$lib/components/messaging/message/PendingMessage.svelte" import PendingMessageGroup from "$lib/components/messaging/PendingMessageGroup.svelte" import FileUploadPreview from "$lib/elements/FileUploadPreview.svelte" import StoreResolver from "$lib/components/utils/StoreResolver.svelte" - import { getValidPaymentRequest } from "$lib/utils/Wallet" import { onMount } from "svelte" import PinnedMessages from "$lib/components/messaging/PinnedMessages.svelte" import { MessageEvent } from "warp-wasm" import { debounce, getTimeAgo } from "$lib/utils/Functions" import Controls from "$lib/layouts/Controls.svelte" - import { tempCDN } from "$lib/utils/CommonVariables" import { checkMobile } from "$lib/utils/Mobile" import BrowseFiles from "../files/BrowseFiles.svelte" import AttachmentRenderer from "$lib/components/messaging/AttachmentRenderer.svelte" @@ -68,7 +81,7 @@ // TODO(Lucas): Need to improve that for chats when not necessary all users are friends $: loading = get(UIStore.state.chats).length > 0 && !$activeChat.users.slice(1).some(userId => $users[userId]?.name !== undefined) - $: chatName = $activeChat.kind === ChatType.DirectMessage ? $users[$activeChat.users[1]]?.name : $activeChat.name ?? $users[$activeChat.users[1]]?.name + $: chatName = $activeChat.kind === ChatType.DirectMessage ? $users[$activeChat.users[1]]?.name : ($activeChat.name ?? $users[$activeChat.users[1]]?.name) $: statusMessage = $activeChat.kind === ChatType.DirectMessage ? $users[$activeChat.users[1]]?.profile?.status_message : $activeChat.motd $: pinned = getPinned($conversation) @@ -92,7 +105,7 @@ let editing_text: string | undefined = undefined $: emojis = UIStore.getMostUsed() $: own_user = Store.state.user - let replyTo: MessageType | undefined = undefined + let replyTo: Message | undefined = undefined let reactingTo: string | undefined let fileUpload: FileInput @@ -150,7 +163,7 @@ }) } - function build_context_items(message: MessageType, file?: Attachment) { + function build_context_items(message: Message, file?: Attachment) { return [ message.pinned ? { @@ -204,11 +217,7 @@ : []), ...(message.details.origin === $own_user.key ? [ - ...(!message.text.some(text => text.includes("giphy.com")) && - !message.text.some(text => text.includes(tempCDN)) && - !message.text.some(text => text.includes(get(_)("settings.calling.callMissed"))) && - !message.text.some(text => text.includes(get(_)("settings.calling.endCallMessage"))) && - !message.text.some(text => text.includes(get(_)("settings.calling.startCallMessage"))) + ...(message.type === MessageType.DEFAULT ? [ { id: "edit", @@ -222,9 +231,7 @@ }, ] : []), - ...(!message.text.some(text => text.includes(get(_)("settings.calling.callMissed"))) && - !message.text.some(text => text.includes(get(_)("settings.calling.endCallMessage"))) && - !message.text.some(text => text.includes(get(_)("settings.calling.startCallMessage"))) + ...(message.type === MessageType.DEFAULT ? [ { id: "delete", @@ -305,7 +312,7 @@ }, 500) }) - function getPinned(conversation: ConversationMessages | undefined): MessageType[] { + function getPinned(conversation: ConversationMessages | undefined): Message[] { if (!conversation) return [] return conversation!.messages.flatMap(g => g.messages.filter(m => m.pinned)) } @@ -319,7 +326,7 @@ function splitUnreads(groups: MessageGroupType[]): [MessageGroupType[], MessageGroupType[]] { let splitMessages = (group: MessageGroupType) => { - return group.messages.reduce<[MessageType[], MessageType[]]>( + return group.messages.reduce<[Message[], Message[]]>( ([read, unreads], message) => { if (message.details.at > $activeChat.last_view_date) { unreads.push(message) @@ -688,11 +695,11 @@ {#if group.messages[0].inReplyTo} Store.getUser(v)} let:resolved> - + {#each group.messages[0].inReplyTo.text as line} {/each} - + {/if} @@ -715,17 +722,17 @@ {#if message.inReplyTo && idx !== 0} Store.getUser(v)} let:resolved> - + {#each message.inReplyTo.text as line} {/each} - + {/if} {#if message.text.length > 0 || message.attachments.length > 0} - edit_message(message.id, editing_text ? editing_text : "")} /> {:else} - {#each message.text as line} - {#if getValidPaymentRequest(line) != undefined} - - {:else if !line.includes(tempCDN)} - - {:else if line.includes(tempCDN)} -
- -
- {/if} - {/each} - + {#if message.attachments.length > 0} (fileToShare = [e.detail, $activeChat.id])} /> {/if} {/if} -
+ reactTo(message.id, emoji, true)} close={close} on:openPicker={_ => (reactingTo = message.id)}> @@ -1056,8 +1052,4 @@ } } } - - .sticker { - width: var(--sticker-width-rendered); - }