diff --git a/src/app/api/profile/route.ts b/src/app/api/profile/route.ts index 46238aa..dcf9960 100644 --- a/src/app/api/profile/route.ts +++ b/src/app/api/profile/route.ts @@ -71,34 +71,33 @@ export async function POST(req: NextRequest) { */ export async function GET(req: NextRequest) { const url = new URL(req.url); - const query_id = url.searchParams.get("id"); + const id = url.searchParams.get("id"); + const username = url.searchParams.get("username"); const token = req.cookies.get("token")?.value; - if (!query_id && !token) + if (!token && !id && !username) return NextResponse.json( - { message: "Query arg should be token or id" }, + { message: "Query arg should be token or id or username" }, { status: StatusCodes.BAD_REQUEST } ); - let user_id = query_id; - if (!user_id && token) { - const { - data: { user }, - } = await supabase.auth.getUser(token); - user_id = user?.id ?? null; - } - - if (!user_id) - return NextResponse.json( - { message: "Invalid token" }, - { status: StatusCodes.BAD_REQUEST } - ); - - const { data: profile, error } = await supabase - .from("profiles") - .select("id, username, display_name, avatar_url, node_address") - .eq("id", user_id) - .maybeSingle(); + const { data: profile, error } = id + ? await supabase + .from("profiles") + .select("id, username, display_name, avatar_url, node_address") + .eq("id", id) + .maybeSingle() + : username + ? await supabase + .from("profiles") + .select("id, username, display_name, avatar_url, node_address") + .eq("username", username) + .maybeSingle() + : await supabase + .from("profiles") + .select("id, username, display_name, avatar_url, node_address") + .eq("id", (await supabase.auth.getUser(token)).data.user?.id) + .maybeSingle(); if (error) return NextResponse.json( diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx index 11df39b..db8929c 100644 --- a/src/app/chat/[id]/page.tsx +++ b/src/app/chat/[id]/page.tsx @@ -4,10 +4,30 @@ import MessageBox from "@/components/MessageBox"; import AppSidebar from "@/components/Sidebar"; import { useParams } from "next/navigation"; import useApp from "@/hooks/use-app"; +import { useEffect, useRef } from "react"; +import auth from "@/lib/auth"; export default function DMs() { const { id } = useParams<{ id: string }>(); const app = useApp(); + const targetNode = useRef(null); + + useEffect(() => { + async function connectTargetNode() { + const target = await app.getUserById(id); + if (target) { + auth( + target.node_address, + targetNode, + () => {}, + app.addMessage, + () => {} + ); + } + } + + connectTargetNode().then(() => {}); + }, [id]); return ( @@ -20,7 +40,7 @@ export default function DMs() { (m.from === id && m.channel_id === app.profile?.id) )} sendMessage={(message) => { - app.node.current?.send( + targetNode.current?.send( JSON.stringify({ type: "send_message", params: { diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index d7a27b5..0699d4c 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -37,6 +37,7 @@ import { get, Response } from "@/lib/request"; import { cn } from "@/lib/utils"; import App from "@/types/app"; import { ProfileSettings } from "@/types/settings"; +import axios from "axios"; export default function AppSidebar({ children, @@ -53,7 +54,9 @@ export default function AppSidebar({ }>) { const [servers, setServers] = useState<[string, string][]>([]); const [newServer, setNewServer] = useState({ name: "", ip: "" }); + const [query, setQuery] = useState(""); const router = useRouter(); + const [filteredDms, setFilteredDms] = useState([]); const dms = Array.from( new Set( @@ -63,6 +66,21 @@ export default function AppSidebar({ ) ); + useEffect(() => { + const load = async () => { + const users = await Promise.all(dms.map((id) => app.getUserById(id))); + setFilteredDms( + users.filter( + (user) => + user !== null && + (user?.username.toLowerCase().includes(query.toLowerCase()) || + user?.id.toLowerCase().includes(query.toLowerCase())) + ) as UserProfile[] + ); + }; + load(); + }, [dms]); + useEffect(() => { const s = Cookies.get("servers")?.split(","); if (s) setServers(s.map((x) => x.trim().split("@") as [string, string])); @@ -213,12 +231,16 @@ export default function AppSidebar({

{server.name}

) : ( -
- - +
+
+ setQuery(e.target.value)} + /> + +
)} @@ -231,22 +253,34 @@ export default function AppSidebar({ ) : ( <> - {dms.map((id) => ( + {filteredDms.map((user) => ( ))} + {filteredDms.length === 0 && query.trim() !== "" ? ( +
+ +
+ ) : null} )}
@@ -335,29 +369,3 @@ function ChannelIcon({ kind }: { kind: "text" | "voice" }) { return null; } } - -function getUser( - id: string, - userList: StringMap, - setUserList: React.Dispatch< - React.SetStateAction> - > -) { - if (userList[id] !== undefined) return userList[id]; - - const onResponse = (res: Response) => { - setUserList((prev) => { - prev[id] = res.data as UserProfile; - return prev; - }); - }; - - get(`/api/profile/?id=${id}`, onResponse) - ?.then(onResponse) - .catch((e) => { - setUserList((prev) => { - prev[id] = null; - return prev; - }); - }); -} diff --git a/src/components/auth/CreateAccount.tsx b/src/components/auth/CreateAccount.tsx index 2552658..a4e29ef 100644 --- a/src/components/auth/CreateAccount.tsx +++ b/src/components/auth/CreateAccount.tsx @@ -58,6 +58,8 @@ export default function CreateAccount() { }; const signup = async () => { + localStorage.removeItem("chat-messages"); + localStorage.removeItem("server-messages"); setFeedback(undefined); const validationError = validateInput(); if (validationError) { diff --git a/src/components/auth/Login.tsx b/src/components/auth/Login.tsx index ed410b3..5b0e83d 100644 --- a/src/components/auth/Login.tsx +++ b/src/components/auth/Login.tsx @@ -18,6 +18,8 @@ export default function Login() { const router = useRouter(); const login = async () => { + localStorage.removeItem("chat-messages"); + localStorage.removeItem("server-messages"); setFeedback(undefined); try { const res = await axios.get("/api/user", { diff --git a/src/hooks/use-app.ts b/src/hooks/use-app.ts index 35f1d58..59dc115 100644 --- a/src/hooks/use-app.ts +++ b/src/hooks/use-app.ts @@ -4,6 +4,7 @@ import { useMessages } from "./use-messages"; import { StringMap } from "@/types/typeUtils"; import useUser, { UserProfile } from "./get-user"; import { useClientSettings } from "./use-settings"; +import { get, Response } from "@/lib/request"; export default function useApp(): App { const node = useRef(null); @@ -24,5 +25,23 @@ export default function useApp(): App { addMessage, sidebarOpen, setSidebarOpen, + getUserById: async (id: string) => { + if (profiles[id] !== undefined) return profiles[id]; + + const onResponse = (res: Response) => { + setProfiles((prev) => { + prev[id] = res.data as UserProfile; + return prev; + }); + }; + + try { + const user = await get(`/api/profile/?id=${id}`, onResponse); + onResponse(user); + return user.data; + } catch { + return null; + } + }, }; } diff --git a/src/hooks/use-mobile.ts b/src/hooks/use-mobile.ts index 2b0fe1d..48fab93 100644 --- a/src/hooks/use-mobile.ts +++ b/src/hooks/use-mobile.ts @@ -1,19 +1,21 @@ -import * as React from "react" +import * as React from "react"; -const MOBILE_BREAKPOINT = 768 +const MOBILE_BREAKPOINT = 768; export function useIsMobile() { - const [isMobile, setIsMobile] = React.useState(undefined) + const [isMobile, setIsMobile] = React.useState( + undefined + ); React.useEffect(() => { - const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) + const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); const onChange = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) - } - mql.addEventListener("change", onChange) - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) - return () => mql.removeEventListener("change", onChange) - }, []) + setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); + }; + mql.addEventListener("change", onChange); + setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); + return () => mql.removeEventListener("change", onChange); + }, []); - return !!isMobile + return !!isMobile; } diff --git a/src/types/app.ts b/src/types/app.ts index 53fcaac..834fff5 100644 --- a/src/types/app.ts +++ b/src/types/app.ts @@ -21,4 +21,6 @@ export default interface App { sidebarOpen: boolean; setSidebarOpen: SetState; + + getUserById: (userId: string) => Promise; }