diff --git a/src/components/common/messaging/MessageBox.tsx b/src/components/common/messaging/MessageBox.tsx index 70ee0633..c38ed11f 100644 --- a/src/components/common/messaging/MessageBox.tsx +++ b/src/components/common/messaging/MessageBox.tsx @@ -55,11 +55,11 @@ export type UploadState = | { type: "none" } | { type: "attached"; files: File[] } | { - type: "uploading"; - files: File[]; - percent: number; - cancel: CancelTokenSource; - } + type: "uploading"; + files: File[]; + percent: number; + cancel: CancelTokenSource; + } | { type: "sending"; files: File[] } | { type: "failed"; files: File[]; error: string }; @@ -257,25 +257,27 @@ export default observer(({ channel }: Props) => { ); } - console.log(channel) - if (!channel.havePermission("SendMessage") && channel.recipient?.relationship == "Blocked" || channel.recipient?.relationship == "BlockedOther"){ - return ( - - - - - - - -
- -
-
- - ); - } + console.log(channel) //|| channel.channel_type != "DirectMessage" + if (channel.channel_type != "SavedMessages") + if (!channel.havePermission("SendMessage") && channel.channel_type == "TextChannel" || channel.recipient?.relationship == "Blocked" || channel.recipient?.relationship == "BlockedOther") { + + return ( + + + + + + + +
+ +
+
+ + ); + } // Push message content to draft. const setMessage = useCallback( (content?: string) => { @@ -297,9 +299,9 @@ export default observer(({ channel }: Props) => { const text = action === "quote" ? `${content - .split("\n") - .map((x) => `> ${x}`) - .join("\n")}\n\n` + .split("\n") + .map((x) => `> ${x}`) + .join("\n")}\n\n` : `${content} `; if (!state.draft.has(channel._id)) { @@ -353,8 +355,8 @@ export default observer(({ channel }: Props) => { toReplace == "" ? msg.content.toString() + newText : msg.content - .toString() - .replace(new RegExp(toReplace, flags), newText); + .toString() + .replace(new RegExp(toReplace, flags), newText); if (newContent != msg.content) { if (newContent.length == 0) { @@ -432,10 +434,10 @@ export default observer(({ channel }: Props) => { files, percent: Math.round( (i * 100 + (100 * e.loaded) / e.total) / - Math.min( - files.length, - CAN_UPLOAD_AT_ONCE, - ), + Math.min( + files.length, + CAN_UPLOAD_AT_ONCE, + ), ), cancel, }), @@ -628,42 +630,42 @@ export default observer(({ channel }: Props) => { {/* {channel.havePermission("UploadFiles") ? ( */} - - - setUploadState({ type: "none" }) - } - onChange={(files) => - setUploadState({ type: "attached", files }) - } - cancel={() => - uploadState.type === "uploading" && - uploadState.cancel.cancel("cancel") - } - append={(files) => { - if (files.length === 0) return; + + + setUploadState({ type: "none" }) + } + onChange={(files) => + setUploadState({ type: "attached", files }) + } + cancel={() => + uploadState.type === "uploading" && + uploadState.cancel.cancel("cancel") + } + append={(files) => { + if (files.length === 0) return; - if (uploadState.type === "none") { - setUploadState({ type: "attached", files }); - } else if (uploadState.type === "attached") { - setUploadState({ - type: "attached", - files: [...uploadState.files, ...files], - }); - } - }} - /> - + if (uploadState.type === "none") { + setUploadState({ type: "attached", files }); + } else if (uploadState.type === "attached") { + setUploadState({ + type: "attached", + files: [...uploadState.files, ...files], + }); + } + }} + /> + {/* ) : ( )} */} @@ -726,13 +728,13 @@ export default observer(({ channel }: Props) => { placeholder={ channel.channel_type === "DirectMessage" ? translate("app.main.channel.message_who", { - person: channel.recipient?.username, - }) + person: channel.recipient?.username, + }) : channel.channel_type === "SavedMessages" - ? translate("app.main.channel.message_saved") - : translate("app.main.channel.message_where", { - channel_name: channel.name ?? undefined, - }) + ? translate("app.main.channel.message_saved") + : translate("app.main.channel.message_where", { + channel_name: channel.name ?? undefined, + }) } disabled={ uploadState.type === "uploading" || diff --git a/src/components/common/messaging/PinMessageBox.tsx b/src/components/common/messaging/PinMessageBox.tsx new file mode 100644 index 00000000..4fa9ba28 --- /dev/null +++ b/src/components/common/messaging/PinMessageBox.tsx @@ -0,0 +1,162 @@ +import { + InfoCircle, + UserPlus, + UserMinus, + ArrowToRight, + ArrowToLeft, + UserX, + ShieldX, + EditAlt, + Edit, + MessageSquareEdit, + Key, +} from "@styled-icons/boxicons-solid"; +import { observer } from "mobx-react-lite"; +import { Message, Channel, API } from "revolt.js"; +import styled from "styled-components/macro"; +import { decodeTime } from "ulid"; + +import { useTriggerEvents } from "preact-context-menu"; +import { Text } from "preact-i18n"; + +import { Row } from "@revoltchat/ui"; + +import { TextReact } from "../../../lib/i18n"; + +import { useApplicationState } from "../../../mobx/State"; + +import { dayjs } from "../../../context/Locale"; + +import Markdown from "../../markdown/Markdown"; +import Tooltip from "../Tooltip"; +import UserShort from "../user/UserShort"; +import MessageBase, { MessageDetail, MessageInfo } from "./MessageBase"; +import { Pin } from "@styled-icons/boxicons-regular"; +import { useHistory } from "react-router-dom"; + +const SystemContent = styled.div` + gap: 4px; + display: flex; + padding: 2px 0; + flex-wrap: wrap; + align-items: center; + flex-direction: row; + font-size: 14px; + color: var(--secondary-foreground); + + span { + font-weight: 600; + color: var(--foreground); + } + + svg { + margin-inline-end: 4px; + } + + svg, + span { + cursor: pointer; + } + + span:hover { + text-decoration: underline; + } +`; + +interface Props { + attachContext?: boolean; + message: Message; + highlight?: boolean; + hideInfo?: boolean; + channel: Channel +} + +const iconDictionary = { + user_added: UserPlus, + user_remove: UserMinus, + user_joined: ArrowToRight, + user_left: ArrowToLeft, + user_kicked: UserX, + user_banned: ShieldX, + channel_renamed: EditAlt, + channel_description_changed: Edit, + channel_icon_changed: MessageSquareEdit, + channel_ownership_changed: Key, + text: InfoCircle, +}; + +export const PinMessageBox = observer( + ({ attachContext, message, channel, highlight, hideInfo }: Props) => { + const data: any = message.system + if (!data) return null; + const history = useHistory(); + + + let children = null; + let userName = message.client ? message.client.user?.username : "" + + + if (data.type as string == "message_pinned") { + children = children = ( +
{ + if (channel.channel_type === "TextChannel") { + history.push( + `/server/${channel.server_id}/channel/${channel._id}/${data.id}`, + ); + } else { + history.push(`/channel/${channel._id}/${data.id}`); + } + }} + > + +
+ ); + } + if (data.type as string == "message_unpinned") { + children = children = ( +
{ + if (channel.channel_type === "TextChannel") { + history.push( + `/server/${channel.server_id}/channel/${channel._id}/${data.id}`, + ); + } else { + history.push(`/channel/${channel._id}/${data.id}`); + } + }} + > + +
+ ); + } + + + + return ( + + + + {!hideInfo && ( + + + {/* */} + + )} + + + {children} + + ); + }, +); diff --git a/src/components/common/messaging/bars/PinnedMessage.tsx b/src/components/common/messaging/bars/PinnedMessage.tsx new file mode 100644 index 00000000..793ac0ae --- /dev/null +++ b/src/components/common/messaging/bars/PinnedMessage.tsx @@ -0,0 +1,423 @@ +import { LeftArrow, LeftArrowAlt, Pin, UpArrowAlt } from "@styled-icons/boxicons-regular"; +import { observer } from "mobx-react-lite"; +import { useHistory } from "react-router-dom"; +import { Channel } from "revolt.js"; +import { decodeTime } from "ulid"; + +import { Text } from "preact-i18n"; +import { useEffect, useState } from "preact/hooks"; + +import { internalSubscribe } from "../../../../lib/eventEmitter"; +import { getRenderer } from "../../../../lib/renderer/Singleton"; + +import { dayjs } from "../../../../context/Locale"; +import styled, { css } from "styled-components/macro"; + +import classNames from "classnames"; +import { isTouchscreenDevice } from "../../../../lib/isTouchscreenDevice"; +import { useClient } from "../../../../controllers/client/ClientController"; +import { Message } from "revolt.js/esm"; +export const PinBar = styled.div<{ position: "top" | "bottom"; accent?: boolean }>` + z-index: 2; + position: relative; + + @keyframes bottomBounce { + 0% { + transform: translateY(33px); + } + 100% { + transform: translateY(0px); + } + } + + @keyframes topBounce { + 0% { + transform: translateY(-33px); + } + 100% { + transform: translateY(0px); + } + } + + ${(props) => + props.position === "top" && + css` + top: 0; + + + animation: topBounce 1s cubic-bezier(0.2, 0.9, 0.5, 1.16) + forwards; + `} + + ${(props) => + props.position === "bottom" && + css` + top: -28px; + animation: bottomBounce 340ms cubic-bezier(0.2, 0.9, 0.5, 1.16) + forwards; + + ${() => + isTouchscreenDevice && + css` + top: -90px; + `} + `} + + > div { + min-height: 120px; + max-height: 200px; + + height: auto; + width: 40%; + right : 0px !important; + position: absolute; + display: block; + align-items: center; + cursor: pointer; + font-size: 12px; + font-weight: 600; + padding: 0 8px; + user-select: none; + justify-content: space-between; + transition: color ease-in-out 0.08s; + + white-space: nowrap; + overflow: scroll; + text-overflow: ellipsis; + + ${(props) => + props.accent + ? css` + color: var(--accent-contrast); + background-color: var(--hover)!important; + backdrop-filter: blur(20px); + ` + : css` + color: var(--secondary-foreground); + background-color: rgba( + var(--secondary-background-rgb), + max(var(--min-opacity), 0.9) + ); + backdrop-filter: blur(20px); + `} + + ${(props) => + props.position === "top" + ? css` + top: 48px; + border-radius: 0 0 var(--border-radius) + var(--border-radius); + ` + : css` + border-radius: var(--border-radius) var(--border-radius) 0 + 0; + `} + + ${() => + isTouchscreenDevice && + css` + top: 56px; + `} + + > div { + display: flex; + align-items: center; + gap: 6px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &:hover { + color: var(--primary-text); + } + + &:active { + transform: translateY(1px); + } + + ${() => + isTouchscreenDevice && + css` + height: 34px; + padding: 0 12px; + `} + } + + @media only screen and (max-width: 800px) { + .right > span { + display: none; + } + } +`; + + + + + +export const PinIcon = styled.div<{ position: "top" | "bottom", accent?: boolean }>` + z-index: 2; + position: relative; + + @keyframes bottomBounce { + 0% { + transform: translateY(33px); + } + 100% { + transform: translateY(0px); + } + } + + @keyframes topBounce { + 0% { + transform: translateY(-33px); + } + 100% { + transform: translateY(0px); + } + } + ${(props) => + props.accent + ? css` + color: var(--accent-contrast); + background-color: var(--hover)!important; + backdrop-filter: blur(20px); + ` + : css` + color: var(--secondary-foreground); + background-color: rgba( + var(--secondary-background-rgb), + max(var(--min-opacity), 0.9) + ); + backdrop-filter: blur(20px); + `} + + ${(props) => + props.position === "top" && + css` + top: 5; + animation: topBounce 1s cubic-bezier(0.2, 0.9, 0.5, 1.16) + forwards; + `} + + + > div { + height: auto; + width: auto; + right : 5px !important; + position: absolute; + display: flex; + align-items: center; + cursor: pointer; + font-size: 12px; + font-weight: 600; + padding: 8px 8px; + user-select: none; + justify-content: space-between; + transition: color ease-in-out 0.08s; + + white-space: nowrap; + + ${(props) => + props.accent + ? css` + color: var(--accent-contrast); + background-color: var(--hover)!important; + backdrop-filter: blur(20px); + ` + : css` + color: var(--secondary-foreground); + background-color: rgba( + var(--secondary-background-rgb), + max(var(--min-opacity), 0.9) + ); + backdrop-filter: blur(20px); + `} + + ${(props) => + props.position === "top" + ? css` + top: 52px; + border-radius: 0 0 var(--border-radius) + var(--border-radius); + ` + : css` + border-radius: var(--border-radius) var(--border-radius) 0 + 0; + `} + + ${() => + isTouchscreenDevice && + css` + top: 56px; + `} + + + } + + @media only screen and (max-width: 800px) { + .right > span { + display: none; + } + } +`; + + + + +export default observer( + ({ channel }: { channel: Channel; }) => { + const [hidden, setHidden] = useState(true); + const unhide = () => setHidden(false); + + // useEffect(() => setHidden(false), [last_id]); + // useEffect(() => internalSubscribe("NewMessages", "hide", hide), []); + // useEffect(() => { + // const onKeyDown = (e: KeyboardEvent) => + // e.key === "Escape" && hide(); + + // document.addEventListener("keydown", onKeyDown); + // return () => document.removeEventListener("keydown", onKeyDown); + // }, []); + + // const extendedMessage = new MessageExtendedClass(client); + + + // useEffect(() => { + // if (last_id) { + // try { + // setTimeAgo(dayjs(decodeTime(last_id)).fromNow()); + // } catch (err) { } + // } + // }, [last_id]); + + const renderer = getRenderer(channel); + const history = useHistory(); + if (renderer.state !== "RENDER") return null; + // if (!last_id) return null; + // if (hidden) return null; + + + // renderer.messages.slice().reverse().map((res, i) => { + // console.log(res, 8989) + // }) + function truncateText(text: string, chars: number) { + if (text.length > chars) { + return text.slice(0, chars) + ".."; + } + return text; + } + + + + let pinFound = false + return ( + <> + +
unhide()} + > + + +
+
+ + + {!hidden && +
+
setHidden(true)} + style={{ + backgroundColor: "var(--block)", + width: "100%", + position: "sticky", + top: "0px", + display: "flex", + + justifyContent: "space-between", + borderRadius: "5px", + padding: "8px 8px" + + }}> + + setHidden(true)} /> + + + +
+ + + +
+ { + + renderer.messages.slice().reverse().map((msg, i) => { + if (msg.is_pinned) { + // console.log(msg, 8989) + let content = msg.content ? truncateText(msg.content, 20) : "" + pinFound = true + return ( + +
{ + // setHidden(true); + if (channel.channel_type === "TextChannel") { + history.push( + `/server/${channel.server_id}/channel/${channel._id}/${msg._id}`, + ); + } else { + history.push(`/channel/${channel._id}/${msg._id}`); + } + }} + + + style={{ display: 'flex', paddingTop: "5px" }}> + <>. {" "} + +
+ ) + } + + }) + + + + } + + {!renderer.atTop &&
{ + // setHidden(true); + renderer.loadTop() + }} + + + style={{ display: 'flex', paddingTop: "5px", justifyContent: "center" }}> + + +
} + +
+ + + + +
+
} + + ); + }, +); diff --git a/src/lib/ContextMenus.tsx b/src/lib/ContextMenus.tsx index b51fd8cc..e3344df5 100644 --- a/src/lib/ContextMenus.tsx +++ b/src/lib/ContextMenus.tsx @@ -58,6 +58,7 @@ type Action = | { action: "mark_as_read"; channel: Channel } | { action: "mark_server_as_read"; server: Server } | { action: "mark_unread"; message: Message } + | { action: "pin_message"; channel: any; message: any } | { action: "retry_message"; message: QueuedMessage } | { action: "cancel_message"; message: QueuedMessage } | { action: "mention"; user: string } @@ -87,32 +88,32 @@ type Action = | { action: "create_channel"; target: Server } | { action: "create_category"; target: Server } | { - action: "create_invite"; - target: Channel; - } + action: "create_invite"; + target: Channel; + } | { action: "leave_group"; target: Channel } | { - action: "delete_channel"; - target: Channel; - } + action: "delete_channel"; + target: Channel; + } | { action: "close_dm"; target: Channel } | { action: "leave_server"; target: Server } | { action: "delete_server"; target: Server } | { action: "edit_identity"; target: Member } | { - action: "open_notification_options"; - channel?: Channel; - server?: Server; - } + action: "open_notification_options"; + channel?: Channel; + server?: Server; + } | { action: "open_settings" } | { action: "open_channel_settings"; id: string } | { action: "open_server_settings"; id: string } | { action: "open_server_channel_settings"; server: string; id: string } | { - action: "set_notification_state"; - key: string; - state?: NotificationState; - } + action: "set_notification_state"; + key: string; + state?: NotificationState; + } | { action: "report"; target: User | Server | Message; messageId?: string }; // ! FIXME: I dare someone to re-write this @@ -202,8 +203,50 @@ export default function ContextMenus() { internalEmit("NewMessages", "mark", unread_id); data.message.channel?.ack(unread_id, true); } + case "pin_message": + { + + + const messages = getRenderer( + data.message.channel!, + ).messages; + const index = messages.findIndex( + (x) => x._id === data.message._id, + ); + let message + + if (index > 0) { + message = messages[index]; + } + + internalEmit("MessageBox", "pin", message); + + // data.message.channel?.ack(pin_id, true); + } break; + + case "unpin_message": + { + + + const messages = getRenderer( + data.message.channel!, + ).messages; + const index = messages.findIndex( + (x) => x._id === data.message._id, + ); + let message + + if (index > 0) { + message = messages[index]; + } + + internalEmit("MessageBox", "unpin", message); + + // data.message.channel?.ack(pin_id, true); + } + break; case "retry_message": { const nonce = data.message.id; @@ -513,9 +556,8 @@ export default function ContextMenus() { "Open User in Admin Panel" ) : ( )} @@ -573,7 +615,7 @@ export default function ContextMenus() { const user = uid ? client.users.get(uid) : undefined; const serverChannel = targetChannel && - (targetChannel.channel_type === "TextChannel") + (targetChannel.channel_type === "TextChannel") ? targetChannel : undefined; @@ -585,8 +627,8 @@ export default function ContextMenus() { (server ? server.permission : serverChannel - ? serverChannel.server?.permission - : 0) || 0; + ? serverChannel.server?.permission + : 0) || 0; const userPermissions = (user ? user.permission : 0) || 0; if (unread) { @@ -810,6 +852,24 @@ export default function ContextMenus() { action: "mark_unread", message, }); + if (sendPermission) { + + + if (message.is_pinned) { + generateAction({ + action: "unpin_message", + channel, + message + }); + } else { + generateAction({ + action: "pin_message", + channel, + message + }); + } + + } if ( typeof message.content === "string" && @@ -880,8 +940,8 @@ export default function ContextMenus() { type === "Image" ? "open_image" : type === "Video" - ? "open_video" - : "open_file", + ? "open_video" + : "open_file", ); generateAction( @@ -892,8 +952,8 @@ export default function ContextMenus() { type === "Image" ? "save_image" : type === "Video" - ? "save_video" - : "save_file", + ? "save_video" + : "save_file", ); generateAction( @@ -929,8 +989,8 @@ export default function ContextMenus() { type === "Image" ? "open_image" : type === "Video" - ? "open_video" - : "open_file", + ? "open_video" + : "open_file", ); generateAction( @@ -941,8 +1001,8 @@ export default function ContextMenus() { type === "Image" ? "save_image" : type === "Video" - ? "save_video" - : "save_file", + ? "save_video" + : "save_file", ); generateAction( @@ -1130,8 +1190,8 @@ export default function ContextMenus() { type: cid ? "channel" : message - ? "message" - : "user", + ? "message" + : "user", }, "admin", ); @@ -1158,8 +1218,8 @@ export default function ContextMenus() { cid ? "copy_cid" : message - ? "copy_mid" - : "copy_uid", + ? "copy_mid" + : "copy_uid", ); } } diff --git a/src/lib/eventEmitter.ts b/src/lib/eventEmitter.ts index c54460a9..85cebc7c 100644 --- a/src/lib/eventEmitter.ts +++ b/src/lib/eventEmitter.ts @@ -25,6 +25,8 @@ export function internalEmit(ns: string, event: string, ...args: unknown[]) { // - Intermediate/open_profile // - Intermediate/navigate // - MessageBox/append +// - MessageBox/pin +// - MessageBox/unpin // - TextArea/focus // - ReplyBar/add // - Modal/close diff --git a/src/lib/renderer/simple/SimpleRenderer.ts b/src/lib/renderer/simple/SimpleRenderer.ts index 98db77b6..19222589 100644 --- a/src/lib/renderer/simple/SimpleRenderer.ts +++ b/src/lib/renderer/simple/SimpleRenderer.ts @@ -28,6 +28,7 @@ export const SimpleRenderer: RendererRoutines = { renderer.channel .fetchMessagesWithUsers({}) .then(({ messages }) => { + console.log(messages, 9090); messages.reverse(); runInAction(() => { diff --git a/src/pages/channels/Channel.tsx b/src/pages/channels/Channel.tsx index ae219cb2..32418e39 100644 --- a/src/pages/channels/Channel.tsx +++ b/src/pages/channels/Channel.tsx @@ -26,6 +26,7 @@ import { PageHeader } from "../../components/ui/Header"; import { useClient } from "../../controllers/client/ClientController"; import ChannelHeader from "./ChannelHeader"; import { MessageArea } from "./messaging/MessageArea"; +import PinnedMessage from "../../components/common/messaging/bars/PinnedMessage"; const ChannelMain = styled.div.attrs({ "data-component": "channel" })` flex-grow: 1; @@ -99,7 +100,7 @@ export const Channel = observer( const client = useClient(); const state = useApplicationState(); - if (!client.channels.exists(id) && client.servers.get(server_id)) { + if (!client.channels.exists(id) && server_id) { if (server_id) { const server = client.servers.get(server_id); if (server && server.channel_ids.length > 0) { @@ -110,7 +111,7 @@ export const Channel = observer( target_id = last_id; } } - + return ( { + diff --git a/src/pages/channels/messaging/MessageArea.tsx b/src/pages/channels/messaging/MessageArea.tsx index d0ae8a5f..375d1a39 100644 --- a/src/pages/channels/messaging/MessageArea.tsx +++ b/src/pages/channels/messaging/MessageArea.tsx @@ -23,11 +23,12 @@ import { internalEmit, internalSubscribe } from "../../../lib/eventEmitter"; import { getRenderer } from "../../../lib/renderer/Singleton"; import { ScrollState } from "../../../lib/renderer/types"; -import { useSession } from "../../../controllers/client/ClientController"; +import { useClient, useSession } from "../../../controllers/client/ClientController"; import RequiresOnline from "../../../controllers/client/jsx/RequiresOnline"; import { modalController } from "../../../controllers/modals/ModalController"; import ConversationStart from "./ConversationStart"; import MessageRenderer from "./MessageRenderer"; +import { Message } from "revolt.js/esm"; const Area = styled.div.attrs({ "data-scroll-offset": "with-padding" })` height: 100%; @@ -115,8 +116,8 @@ export const MessageArea = observer(({ last_id, channel }: Props) => { 101, ref.current ? ref.current.scrollTop + - (ref.current.scrollHeight - - scrollState.current.previousHeight) + (ref.current.scrollHeight - + scrollState.current.previousHeight) : 101, ), { @@ -148,20 +149,48 @@ export const MessageArea = observer(({ last_id, channel }: Props) => { const atBottom = (offset = 0) => ref.current ? Math.floor(ref.current?.scrollHeight - ref.current?.scrollTop) - - offset <= - ref.current?.clientHeight + offset <= + ref.current?.clientHeight : true; const atTop = (offset = 0) => ref.current ? ref.current.scrollTop <= offset : false; + const client = useClient() + function pin(message: Message) { + client.api.post(`/channels/${message.channel_id}/messages/${message._id}/pin` as any) + message.is_pinned = true + } + function unpin(message: Message) { + client.api.delete(`/channels/${message.channel_id}/messages/${message._id}/pin` as any) + message.is_pinned = false + } // ? Handle global jump to bottom, e.g. when editing last message in chat. useEffect(() => { + return internalSubscribe("MessageArea", "jump_to_bottom", () => setScrollState({ type: "ScrollToBottom" }), ); }, [setScrollState]); + useEffect(() => { + + + return internalSubscribe( + "MessageBox", + "pin", + pin as (...args: unknown[]) => void, + ); + }, []); + useEffect(() => { + + + return internalSubscribe( + "MessageBox", + "unpin", + unpin as (...args: unknown[]) => void, + ); + }, []); // ? Handle events from renderer. useLayoutEffect( () => setScrollState(renderer.scrollState), diff --git a/src/pages/channels/messaging/MessageRenderer.tsx b/src/pages/channels/messaging/MessageRenderer.tsx index 950c56d5..c9ad66a3 100644 --- a/src/pages/channels/messaging/MessageRenderer.tsx +++ b/src/pages/channels/messaging/MessageRenderer.tsx @@ -23,6 +23,7 @@ import { useClient } from "../../../controllers/client/ClientController"; import RequiresOnline from "../../../controllers/client/jsx/RequiresOnline"; import ConversationStart from "./ConversationStart"; import MessageEditor from "./MessageEditor"; +import { PinMessageBox } from "../../../components/common/messaging/PinMessageBox"; interface Props { last_id?: string; @@ -150,8 +151,9 @@ export default observer(({ last_id, renderer, highlight }: Props) => { ); blocked = 0; } + let lastPinned = null - for (const message of renderer.messages) { + for (const [i, message] of renderer.messages.entries()) { if (previous) { compare( message._id, @@ -162,8 +164,21 @@ export default observer(({ last_id, renderer, highlight }: Props) => { previous.masquerade, ); } + // console.log(renderer.messages[i].content, 7979) - if (message.author_id === "00000000000000000000000000") { + + if (message.system?.type as any == "message_pinned" || message.system?.type as any == "message_unpinned") { + render.push( + + , + ); + } else if (message.author_id === "00000000000000000000000000") { render.push( { attachContext highlight={highlight === message._id} />, - ); + ) } else if (message.author?.relationship === "Blocked") { blocked++; } else { @@ -204,6 +219,7 @@ export default observer(({ last_id, renderer, highlight }: Props) => { const nonces = renderer.messages.map((x) => x.nonce); if (renderer.atBottom) { for (const msg of queue.get(renderer.channel._id)) { + if (nonces.includes(msg.id)) continue; if (previous) { @@ -222,6 +238,7 @@ export default observer(({ last_id, renderer, highlight }: Props) => { } as MessageI; } + render.push( = 0.14.0" + react-dom: ">= 0.14.0" + checksum: 42d9b3182b9d2495bf0d7914c9f370da51d8bdb853a3eba2acaf433894ae760386a075ba103185be825b33d42f50d85ef462087f261656d433f4c74dab23861f + languageName: node + linkType: hard + "react-fast-compare@npm:^3.1.1": version: 3.2.0 resolution: "react-fast-compare@npm:3.2.0" @@ -9068,12 +9092,12 @@ __metadata: linkType: hard "typescript@npm:^4.6.2": - version: 4.6.4 - resolution: "typescript@npm:4.6.4" + version: 4.9.5 + resolution: "typescript@npm:4.9.5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: e7bfcc39cd4571a63a54e5ea21f16b8445268b9900bf55aee0e02ad981be576acc140eba24f1af5e3c1457767c96cea6d12861768fb386cf3ffb34013718631a + checksum: ee000bc26848147ad423b581bd250075662a354d84f0e06eb76d3b892328d8d4440b7487b5a83e851b12b255f55d71835b008a66cbf8f255a11e4400159237db languageName: node linkType: hard @@ -9088,12 +9112,12 @@ __metadata: linkType: hard "typescript@patch:typescript@^4.6.2#~builtin": - version: 4.6.4 - resolution: "typescript@patch:typescript@npm%3A4.6.4#~builtin::version=4.6.4&hash=bda367" + version: 4.9.5 + resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin::version=4.9.5&hash=bda367" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 1cb434fbc637d347be90e3a0c6cd05e33c38f941713c8786d3031faf1842c2c148ba91d2fac01e7276b0ae3249b8633f1660e32686cc7a8c6a8fd5361dc52c66 + checksum: 2eee5c37cad4390385db5db5a8e81470e42e8f1401b0358d7390095d6f681b410f2c4a0c496c6ff9ebd775423c7785cdace7bcdad76c7bee283df3d9718c0f20 languageName: node linkType: hard @@ -9104,6 +9128,15 @@ __metadata: languageName: node linkType: hard +"ua-parser-js@npm:^1.0.33": + version: 1.0.40 + resolution: "ua-parser-js@npm:1.0.40" + bin: + ua-parser-js: script/cli.js + checksum: ae555a33dc9395dd877e295d6adbf5634e047aad7c3358328830218f3ca3a6233e35848cd355465a7612f269860e8029984389282940c7a27c9af4dfcdbba8c3 + languageName: node + linkType: hard + "ulid@npm:^2.3.0": version: 2.3.0 resolution: "ulid@npm:2.3.0" @@ -9125,10 +9158,12 @@ __metadata: languageName: node linkType: hard -"undici@npm:^4.14.1": - version: 4.16.0 - resolution: "undici@npm:4.16.0" - checksum: 5e88c2b3381085e25ed1d1a308610ac7ee985f478ac705af7a8e03213536e10f73ef8dd8d85e6ed38948d1883fa0ae935e04357c317b0f5d3d3c0211d0c8c393 +"undici@npm:^5.4.0": + version: 5.28.4 + resolution: "undici@npm:5.28.4" + dependencies: + "@fastify/busboy": ^2.0.0 + checksum: a8193132d84540e4dc1895ecc8dbaa176e8a49d26084d6fbe48a292e28397cd19ec5d13bc13e604484e76f94f6e334b2bdc740d5f06a6e50c44072818d0c19f9 languageName: node linkType: hard @@ -9794,17 +9829,17 @@ __metadata: linkType: hard "ws@npm:^8.2.2": - version: 8.2.2 - resolution: "ws@npm:8.2.2" + version: 8.18.0 + resolution: "ws@npm:8.18.0" peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 + utf-8-validate: ">=5.0.2" peerDependenciesMeta: bufferutil: optional: true utf-8-validate: optional: true - checksum: 25e764c631141bdca45badc86e69437b8791e57e461f9a16c0f7cd779baf70c3fbba07ecdd9e0d34fea1155ddcf62ef165cd7f81b68ed545bc7d455c15a85fb0 + checksum: 91d4d35bc99ff6df483bdf029b9ea4bfd7af1f16fc91231a96777a63d263e1eabf486e13a2353970efc534f9faa43bdbf9ee76525af22f4752cbc5ebda333975 languageName: node linkType: hard @@ -9822,10 +9857,10 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.0.0": - version: 21.0.1 - resolution: "yargs-parser@npm:21.0.1" - checksum: c3ea2ed12cad0377ce3096b3f138df8267edf7b1aa7d710cd502fe16af417bafe4443dd71b28158c22fcd1be5dfd0e86319597e47badf42ff83815485887323a +"yargs-parser@npm:^21.0.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c languageName: node linkType: hard