Text Channel UI
This commit is contained in:
+2
-2
@@ -168,11 +168,11 @@ export default class Enclave<P = Page> {
|
|||||||
console.log("msg", msg.messages);
|
console.log("msg", msg.messages);
|
||||||
Object.entries(msg.messages).forEach(([channelId, messages]) => {
|
Object.entries(msg.messages).forEach(([channelId, messages]) => {
|
||||||
if (!server.messages[channelId]) {
|
if (!server.messages[channelId]) {
|
||||||
server.messages[channelId] = [];
|
server.messages[channelId] = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
messages.forEach((message) => {
|
messages.forEach((message) => {
|
||||||
server.messages[channelId].push(message);
|
server.messages[channelId].add(message);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ export default class EnclaveServer {
|
|||||||
public isSecure: boolean;
|
public isSecure: boolean;
|
||||||
public websocket?: EnclaveWebSocket;
|
public websocket?: EnclaveWebSocket;
|
||||||
public meta?: ServerMeta;
|
public meta?: ServerMeta;
|
||||||
public messages: Record<string, StoredMessage[]>;
|
public messages: Record<string, Set<StoredMessage>>;
|
||||||
|
|
||||||
public constructor(hostname: string, isSecure: boolean) {
|
public constructor(hostname: string, isSecure: boolean) {
|
||||||
this.hostname = hostname;
|
this.hostname = hostname;
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
import Enclave from "@/app/app";
|
import Enclave from "@/app/app";
|
||||||
import { ChannelPageProps } from "@/components/page/PageView";
|
import { ChannelPageProps } from "@/components/page/PageView";
|
||||||
import { useEffect } from "react";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { StoredMessage } from "@/lib/types";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function TextChannel({
|
export default function TextChannel({
|
||||||
appRef,
|
appRef,
|
||||||
}: {
|
}: {
|
||||||
appRef: React.RefObject<Enclave<ChannelPageProps> | null>;
|
appRef: React.RefObject<Enclave<ChannelPageProps> | null>;
|
||||||
}) {
|
}) {
|
||||||
|
const [currentMessage, setCurrentMessage] = useState("");
|
||||||
const channel = appRef.current?.page?.channel;
|
const channel = appRef.current?.page?.channel;
|
||||||
|
|
||||||
if (!channel) return null;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!channel) return;
|
||||||
|
|
||||||
appRef.current?.server?.websocket?.send({
|
appRef.current?.server?.websocket?.send({
|
||||||
method: "GetMessages",
|
method: "GetMessages",
|
||||||
channel_id: channel.id,
|
channel_id: channel.id,
|
||||||
@@ -19,9 +23,77 @@ export default function TextChannel({
|
|||||||
});
|
});
|
||||||
|
|
||||||
appRef.current?.forceRender();
|
appRef.current?.forceRender();
|
||||||
}, []);
|
}, [channel]);
|
||||||
|
|
||||||
|
if (!channel) return null;
|
||||||
|
|
||||||
|
const sendMessage = () => {
|
||||||
|
appRef.current?.sendMessage(currentMessage, channel.id);
|
||||||
|
setCurrentMessage("");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>{JSON.stringify(appRef.current?.server?.messages[channel.id])}</div>
|
<div className="flex flex-col h-full">
|
||||||
|
<header className="px-3 pt-3 pb-3 text-sm text-muted-foreground border-b border-b-border">
|
||||||
|
<h2>{channel.name}</h2>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="h-full flex flex-col gap-2.5 px-3 pt-4 overflow-y-scroll">
|
||||||
|
{appRef.current?.server?.messages[channel.id] &&
|
||||||
|
Array.from(appRef.current?.server?.messages[channel.id]).map(
|
||||||
|
(message) => <TextMessage appRef={appRef} message={message} />,
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="pb-6 px-4 flex flex-row gap-3">
|
||||||
|
<Input
|
||||||
|
className="h-10"
|
||||||
|
placeholder={`Message ${channel.name}`}
|
||||||
|
value={currentMessage}
|
||||||
|
onChange={(e) => setCurrentMessage(e.target.value)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
sendMessage();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{currentMessage.trim() && <Button onClick={sendMessage}>Send</Button>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TextMessage({
|
||||||
|
appRef,
|
||||||
|
message,
|
||||||
|
}: {
|
||||||
|
appRef: React.RefObject<Enclave<ChannelPageProps> | null>;
|
||||||
|
message: StoredMessage;
|
||||||
|
}) {
|
||||||
|
const time = new Date(message.timestamp).toLocaleTimeString([], {
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "2-digit",
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="rounded-lg flex gap-3 px-3 py-3 hover:bg-muted/40">
|
||||||
|
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-medium text-muted-foreground">
|
||||||
|
{message.author.slice(0, 2).toUpperCase()}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<div className="flex items-baseline gap-2">
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{message.author.slice(0, 8)}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted-foreground">{time}</span>
|
||||||
|
</div>
|
||||||
|
<p className="whitespace-pre-wrap break-words text-sm leading-snug">
|
||||||
|
{message.content}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user