feat(status): make the set-status menu work

feat(user): optimise get() function for react
qol(todos): update todos
This commit is contained in:
Alois 2026-08-10 13:48:32 +02:00
commit 1c90a0c859
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
30 changed files with 572 additions and 324 deletions

View file

@ -15,7 +15,10 @@ export function Basic({
user,
extra,
}: {
user: User;
user: Pick<
User,
"Display" | "Username" | "Avatar" | "OnlineStatus" | "Status"
>;
extra?: React.ReactNode;
}) {
const display = user.Display || user.Username || "Unknown";
@ -57,8 +60,9 @@ export function Basic({
</TooltipContent>
</Tooltip>
</div>
<div className="flex w-full flex-col items-start justify-center gap-1 text-[15px]">
<div className="flex w-full flex-col items-start justify-center text-[15px]">
<p>{display}</p>
<p className="text-xs text-muted-foreground">{user.Status}</p>
</div>
<div className="pr-1">{extra}</div>
</Button>

View file

@ -4,7 +4,20 @@ import { Text } from "@methanium/ui/markdown";
import { ChevronDown, ChevronUp } from "lucide-react";
import { useState } from "react";
export default function Profile({ user }: { user: User }) {
export default function Profile({
user,
}: {
user: Pick<
User,
| "UserId"
| "Display"
| "Username"
| "Avatar"
| "About"
| "IotaId"
| "PublicKey"
>;
}) {
const [showAdvancedInformation, setShowAdvancedInformation] = useState(false);
return (

View file

@ -91,6 +91,15 @@ export default function Navbar({ forMobile }: { forMobile: boolean }) {
{pathname === "/chat" && id && (
<Wrapper
userId={id}
fields={[
"UserId",
"Display",
"Username",
"Avatar",
"About",
"IotaId",
"PublicKey",
]}
component={(user) =>
isMobile ? (
<p className="font-medium text-[1.07rem]">{user?.Display}</p>

View file

@ -35,7 +35,7 @@ import SidebarBox from "@tensamin/call/sidebarBox";
import { useShowMobileNavbar } from "@/routes/app/useShowMobileNavbar";
import { Ellipsis, Check } from "lucide-react";
import { useState } from "react";
import type { User } from "@tensamin/user/context";
import { useUser, type User } from "@tensamin/user/context";
import { mtp, userPresencePreferenceSchema } from "@tensamin/shared/data";
import { useMTP } from "@tensamin/mtp";
import {
@ -64,7 +64,7 @@ function StatusDialog({
saveSucceeded,
setSaveSucceeded,
}: {
user: User;
user: Pick<User, "UserId" | "OnlineStatus" | "Status">;
open: boolean;
onOpenChange: (open: boolean) => void;
send: ReturnType<typeof useMTP>["send"];
@ -77,6 +77,8 @@ function StatusDialog({
saveSucceeded: boolean;
setSaveSucceeded: (value: boolean) => void;
}) {
const { updateProfile, updateState } = useUser();
return (
<Dialog
open={open}
@ -137,9 +139,10 @@ function StatusDialog({
<DialogClose render={<Button variant="destructive">Cancel</Button>} />
<Button
onClick={async () => {
const payload = { UserState: draftOnlineStatus };
const validation = mtp.SetUserState.request.safeParse(payload);
const validation = mtp.ChangeUserData.request.safeParse({
OnlineStatus: draftOnlineStatus,
Status: draftStatus,
});
if (!validation.success) {
setSaveSucceeded(false);
@ -150,17 +153,9 @@ function StatusDialog({
}
try {
await send("SetUserState", validation.data);
const profileValidation = mtp.ChangeUserData.request.safeParse({
Status: draftStatus,
});
if (!profileValidation.success) {
throw new Error(
profileValidation.error.issues[0]?.message ??
"Invalid status data",
);
}
await send("ChangeUserData", profileValidation.data);
await send("ChangeUserData", validation.data);
updateState(user.UserId, draftOnlineStatus);
await updateProfile(user.UserId, { Status: draftStatus });
setSaveSucceeded(true);
setErrorMessage("");
} catch (err) {
@ -209,6 +204,14 @@ export default function Sidebar() {
<Wrapper
loading={<Loading />}
userId={"own"}
fields={[
"UserId",
"Display",
"Username",
"Avatar",
"OnlineStatus",
"Status",
]}
component={(user) => (
<>
<Basic

View file

@ -2,7 +2,7 @@ export const onlineStatusOptions = [
{ label: "Online", value: "user_online" },
{ label: "Idle", value: "user_idle" },
{ label: "Do not disturb", value: "user_dnd" },
{ label: "Away", value: "user_wc" },
{ label: "On the toilet", value: "user_wc" },
{ label: "Offline", value: "user_invisible" },
] as const;

View file

@ -28,6 +28,13 @@ export default function ConversationModal({ userId }: { userId: number }) {
<Wrapper
loading={<Loading />}
userId={userId}
fields={[
"Display",
"Username",
"Avatar",
"OnlineStatus",
"Status",
]}
component={(user) => <Basic user={user} />}
/>
</div>