@@ -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(
|
||||
|
||||
@@ -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<WebSocket | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function connectTargetNode() {
|
||||
const target = await app.getUserById(id);
|
||||
if (target) {
|
||||
auth(
|
||||
target.node_address,
|
||||
targetNode,
|
||||
() => {},
|
||||
app.addMessage,
|
||||
() => {}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
connectTargetNode().then(() => {});
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<AppSidebar app={app}>
|
||||
@@ -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: {
|
||||
|
||||
+51
-43
@@ -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<UserProfile[]>([]);
|
||||
|
||||
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({
|
||||
<h2>{server.name}</h2>
|
||||
</div>
|
||||
) : (
|
||||
<div className="p-3 border-b flex items-center gap-2">
|
||||
<Search className="h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Find or start a conversation"
|
||||
className="h-8"
|
||||
/>
|
||||
<div className="p-3 border-b flex items-center">
|
||||
<div className="relative w-full">
|
||||
<Input
|
||||
placeholder="Find or start a conversation"
|
||||
className="pr-8 h-8"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
<Search className="absolute right-2 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<ScrollArea className="flex-1">
|
||||
@@ -231,22 +253,34 @@ export default function AppSidebar({
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{dms.map((id) => (
|
||||
{filteredDms.map((user) => (
|
||||
<DMItem
|
||||
name={
|
||||
getUser(id, app.profiles, app.setProfiles)
|
||||
?.display_name || id
|
||||
}
|
||||
id={id}
|
||||
name={user.display_name}
|
||||
id={user.id}
|
||||
status="online"
|
||||
avatar={
|
||||
getUser(id, app.profiles, app.setProfiles)
|
||||
?.avatar_url || ""
|
||||
}
|
||||
key={id}
|
||||
avatar={user.avatar_url}
|
||||
key={user.id}
|
||||
app={app}
|
||||
/>
|
||||
))}
|
||||
{filteredDms.length === 0 && query.trim() !== "" ? (
|
||||
<div className="px-3 py-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start gap-2 text-sm"
|
||||
onClick={async () => {
|
||||
const response = await axios.get("/api/profile", {
|
||||
params: { username: query },
|
||||
});
|
||||
const user = response.data as UserProfile;
|
||||
router.push(`/chat/${user.id}`);
|
||||
}}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
Start new DM with “{query}”
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -335,29 +369,3 @@ function ChannelIcon({ kind }: { kind: "text" | "voice" }) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getUser(
|
||||
id: string,
|
||||
userList: StringMap<UserProfile | null>,
|
||||
setUserList: React.Dispatch<
|
||||
React.SetStateAction<StringMap<UserProfile | null>>
|
||||
>
|
||||
) {
|
||||
if (userList[id] !== undefined) return userList[id];
|
||||
|
||||
const onResponse = (res: Response<UserProfile>) => {
|
||||
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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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", {
|
||||
|
||||
@@ -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<WebSocket | null>(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<UserProfile>) => {
|
||||
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;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
+13
-11
@@ -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<boolean | undefined>(undefined)
|
||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -21,4 +21,6 @@ export default interface App {
|
||||
|
||||
sidebarOpen: boolean;
|
||||
setSidebarOpen: SetState<boolean>;
|
||||
|
||||
getUserById: (userId: string) => Promise<UserProfile | null>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user