Something went wrong. Try again.
Starship Simulator Controls thoriumsim.com
Something went wrong. Try again.
6.3 kB · 207 lines
TSX
at develop
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208import { q } from "@thorium/context/AppContext";import { useStation } from "@thorium/routes/station/useStation";import Button from "@thorium/ui/Button";import { InputField } from "@thorium/ui/Core";import { Icon } from "@thorium/ui/Icon";import Input from "@thorium/ui/Input";import Select from "@thorium/ui/Select";import { cn } from "@thorium/utils/cn";import { useMemo, useState } from "react";
type Conversation = { sender: string; recipient: string; lastTimestamp: number; messages: { content: string; sender: string; timestamp: number }[];};export function MessagingCore() { const { shipId } = useStation(); const [messageGroups] = q.messaging.messageGroups.useNetRequest({ shipId }); const [ship] = q.ship.get.useNetRequest({ shipId }); const [selectedConversationDestination, setSelectedConversationDestination] = useState< string | null >(null); const [sender, setSender] = useState(""); const [alertSender, setAlertSender] = useState(false);
const stations = ship?.stations.map((s) => s.name) || [];
const conversations = useConversations();
const selectedConversation = conversations.find( (c) => c.recipient === selectedConversationDestination, );
return ( <div className="grid h-full grid-cols-3 text-sm"> <div className="flex flex-col"> <ul className="list-group flex-1"> {conversations.map((c) => ( <li key={`${c.recipient}-${c.sender}`} className={cn("list-group-item list-group-item-small", { selected: selectedConversationDestination === c.recipient, })} onClick={() => { setSelectedConversationDestination(c.recipient); setSender(c.sender); }} > {stations.includes(c.sender) && stations.includes(c.recipient) ? ( <> {c.sender} <Icon name="arrow-left-right" /> {c.recipient} </> ) : ( c.recipient )} </li> ))} </ul> <Select label="Start Conversation" labelHidden size="xs" className="w-full" placeholder="Start Conversation" items={[ { header: "Stations", items: stations.map((s) => ({ id: s, label: s })), }, ...(messageGroups.length > 0 ? [ { header: "Message Groups", items: messageGroups.map((m) => ({ id: m, label: m })), }, ] : []), ]} selected={null} setSelected={(value) => setSelectedConversationDestination(value)} /> </div> <div className="col-span-2 flex flex-col"> <div className="flex flex-auto flex-col justify-end"> {!selectedConversationDestination ? null : !selectedConversation || selectedConversation.messages.length === 0 ? ( <div className="mb-4 self-center rounded-lg bg-gray-800 px-2 py-1"> Start of Conversation with {selectedConversationDestination} </div> ) : ( selectedConversation.messages.map((m) => ( <div key={m.timestamp} title={new Date(m.timestamp).toLocaleTimeString()}> <span className="font-bold">{m.sender}:</span> {m.content} </div> )) )} </div> <div> <form className="flex" onSubmit={(event) => { event.preventDefault(); const content = event.currentTarget.message.value; if (!selectedConversationDestination) return; if (!sender) { setAlertSender(true); setTimeout(() => { setAlertSender(false); }, 50); setTimeout(() => { setAlertSender(true); }, 100); setTimeout(() => { setAlertSender(false); }, 150); setTimeout(() => { setAlertSender(true); }, 200); setTimeout(() => { setAlertSender(false); }, 250); return; } if (!content.trim()) return;
q.messaging.sendInternalMessage.netSend({ shipId, content, destination: selectedConversationDestination, sender, }); event.currentTarget.message.value = ""; }} > <Input disabled={!selectedConversationDestination} className="input-xs flex-1" label="Message Input" labelHidden name="message" /> <Button disabled={!selectedConversationDestination} className="btn-xs" type="submit"> Send </Button> </form> <InputField prompt="What is the name of the sender?" promptValue={sender} alert={alertSender} onClick={(value) => (typeof value === "string" ? setSender(value) : null)} > {sender || <> </>} </InputField> </div> </div> </div> );}
export function useConversations(station?: string) { const { shipId } = useStation(); const [messages] = q.messaging.messages.useNetRequest({ shipId, station }); const [ship] = q.ship.get.useNetRequest({ shipId });
const stations = ship?.stations.map((s) => s.name) || [];
const conversations = useMemo(() => { const conversations = new Map<string, Conversation>(); for (const message of messages) { const key = stations.includes(message.sender) && stations.includes(message.destination) ? [message.sender, message.destination].sort().join("") : stations.includes(message.destination) ? message.destination : message.sender; const conversation = conversations.get(key) || { sender: stations.includes(message.sender) ? message.destination : message.sender, recipient: stations.includes(message.sender) ? message.sender : message.destination, messages: [], lastTimestamp: 0, };
conversation.messages.push({ content: message.content, sender: message.sender, timestamp: message.timestamp, }); conversation.lastTimestamp = Math.max(conversation.lastTimestamp, message.timestamp); conversation.sender = stations.includes(message.sender) ? message.destination : message.sender;
conversations.set(key, conversation); }
const sortedConversations: Conversation[] = []; for (const conversation of conversations.values()) { sortedConversations.push(conversation); } sortedConversations.sort((a, b) => a.lastTimestamp - b.lastTimestamp);
return sortedConversations; }, [messages, stations]);
return conversations;}