Formatted, updated convs & comms list to not depend on each other, updated legal & login screen for better mobile support

This commit is contained in:
Alois 2026-04-05 12:15:15 +02:00
commit 741afdeaa9
9 changed files with 54 additions and 22 deletions

View file

@ -107,18 +107,23 @@ export default function Form() {
const response = await fetch( const response = await fetch(
`https://omega.tensamin.net/api/get/id/${inputParse.data.username}`, `https://omega.tensamin.net/api/get/id/${inputParse.data.username}`,
); );
const data = await response.json(); const rawData = await response.arrayBuffer();
const parse = fetchedUser.safeParse(data);
const bytes = new Uint8Array(rawData);
const jsonString = new TextDecoder().decode(bytes);
const data = JSON.parse(jsonString);
const parse = fetchedUser.shape.data.safeParse(data);
if (!parse.success) { if (!parse.success) {
log(0, "Login", "red", "Invalid response from server"); log(0, "Login", "red", "Invalid response from server", parse.error);
toast("error", "Invalid response from server"); toast("error", "Invalid response from server");
return; return;
} }
const user = parse.data; const user = parse.data;
await save("user_id", user.data.user_id); await save("user_id", user.user_id);
await save("private_key", inputParse.data.private_key); await save("private_key", inputParse.data.private_key);
navigate({ to: "/" }); navigate({ to: "/" });
@ -131,7 +136,7 @@ export default function Form() {
); );
return ( return (
<div className="flex gap-15"> <div className="flex md:flex-row flex-col gap-15">
<div <div
onClick={() => uploadRef.current?.click()} onClick={() => uploadRef.current?.click()}
className="flex flex-col gap-3 cursor-pointer w-55 aspect-square bg-input/13 hover:bg-input/30 transition-all duration-300 ease-in-out border-3 items-center justify-center rounded-lg" className="flex flex-col gap-3 cursor-pointer w-55 aspect-square bg-input/13 hover:bg-input/30 transition-all duration-300 ease-in-out border-3 items-center justify-center rounded-lg"

View file

@ -7,8 +7,12 @@ import List from "@/features/conversation/list/body";
import { import {
Sidebar as SidebarRoot, Sidebar as SidebarRoot,
SidebarContent, SidebarContent,
SidebarFooter,
} from "@tensamin/ui/cmp/sidebar"; } from "@tensamin/ui/cmp/sidebar";
import { isTauri } from "@tauri-apps/api/core"; import { isTauri } from "@tauri-apps/api/core";
import { useIsMobile } from "@tensamin/ui/utils";
import { MobileNavbar } from "./navbar";
import { useLocation } from "@tanstack/react-router";
/** /**
* Renders the conversation sidebar with account summary and conversation list. * Renders the conversation sidebar with account summary and conversation list.
@ -17,14 +21,19 @@ import { isTauri } from "@tauri-apps/api/core";
export default function Sidebar() { export default function Sidebar() {
const [userId, setUserId] = React.useState<undefined | number>(undefined); const [userId, setUserId] = React.useState<undefined | number>(undefined);
const { load } = useStorage(); const { load } = useStorage();
const isMobile = useIsMobile();
React.useEffect(() => { React.useEffect(() => {
load("user_id").then(setUserId); load("user_id").then(setUserId);
}, [load]); }, [load]);
const location = useLocation();
return ( return (
<SidebarRoot> <SidebarRoot>
<SidebarContent className={isTauri() ? "pt-[env(safe-area-inset-top)]" : "pt-2"}> <SidebarContent
className={isTauri() ? "pt-[env(safe-area-inset-top)]" : "pt-2"}
>
<div className="h-full w-full flex flex-col gap-3 p-2 pt-0!"> <div className="h-full w-full flex flex-col gap-3 p-2 pt-0!">
<div> <div>
<Wrapper <Wrapper
@ -38,6 +47,11 @@ export default function Sidebar() {
</div> </div>
</div> </div>
</SidebarContent> </SidebarContent>
{isMobile && location.pathname === "/chat" && (
<SidebarFooter className="p-0!">
<MobileNavbar />
</SidebarFooter>
)}
</SidebarRoot> </SidebarRoot>
); );
} }

View file

@ -18,14 +18,12 @@ export default function List() {
>("conversations"); >("conversations");
const { conversations, communities } = useConversation(); const { conversations, communities } = useConversation();
const items = category === "conversations" ? conversations : communities;
const scrollRef = React.useRef<HTMLDivElement | null>(null); const scrollRef = React.useRef<HTMLDivElement | null>(null);
const virtualizer = useVirtualizer({ const virtualizer = useVirtualizer({
count: count: items?.length || 0,
category === "conversations"
? conversations?.length || 0
: communities?.length || 0,
estimateSize: () => 60, estimateSize: () => 60,
getScrollElement: () => scrollRef.current, getScrollElement: () => scrollRef.current,
}); });
@ -38,7 +36,7 @@ export default function List() {
id="conversation-list" id="conversation-list"
className="overflow-y-auto flex-1 h-full" className="overflow-y-auto flex-1 h-full"
> >
{communities === null || conversations === null ? ( {items === null ? (
<div className="flex items-center justify-center pt-5"> <div className="flex items-center justify-center pt-5">
<p className="text-foreground/45 flex gap-1 items-center justify-center"> <p className="text-foreground/45 flex gap-1 items-center justify-center">
<Loader2 size={18} className="animate-spin" /> <Loader2 size={18} className="animate-spin" />
@ -52,8 +50,7 @@ export default function List() {
height: `${virtualizer.getTotalSize()}px`, height: `${virtualizer.getTotalSize()}px`,
}} }}
> >
{communities && {items &&
conversations &&
virtualizer.getVirtualItems().map((virtualItem) => { virtualizer.getVirtualItems().map((virtualItem) => {
const itemKey = `${category}-${virtualItem.index}`; const itemKey = `${category}-${virtualItem.index}`;
@ -69,12 +66,12 @@ export default function List() {
}} }}
> >
{category === "conversations" ? ( {category === "conversations" ? (
conversations[virtualItem.index] ? ( conversations?.[virtualItem.index] ? (
<ConversationModal <ConversationModal
userId={conversations[virtualItem.index].user_id} userId={conversations[virtualItem.index].user_id}
/> />
) : null ) : null
) : communities[virtualItem.index] ? ( ) : communities?.[virtualItem.index] ? (
<CommunityModal <CommunityModal
community={communities[virtualItem.index]} community={communities[virtualItem.index]}
/> />

View file

@ -162,7 +162,7 @@ export default function Screen(props: { children: React.ReactNode }) {
} }
return ( return (
<div className="w-full h-full flex items-center justify-center"> <div className="w-full h-full flex items-center justify-center py-15">
<div className="h-full flex flex-col gap-15 p-10 md:p-40 w-full lg:w-2/3"> <div className="h-full flex flex-col gap-15 p-10 md:p-40 w-full lg:w-2/3">
<h1 className="text-3xl md:text-4xl font-bold"> <h1 className="text-3xl md:text-4xl font-bold">
Privacy Policy & ToS Privacy Policy & ToS
@ -173,11 +173,13 @@ export default function Screen(props: { children: React.ReactNode }) {
<div className="w-full h-full flex flex-col items-center justify-center gap-5"> <div className="w-full h-full flex flex-col items-center justify-center gap-5">
<div className="justify-start items-start flex flex-col gap-2"> <div className="justify-start items-start flex flex-col gap-2">
<BigCheckbox <BigCheckbox
id="acceptPP"
checked={acceptedPP} checked={acceptedPP}
onChange={acceptPP} onChange={acceptPP}
label="I agree to the Privacy Policy" label="I agree to the Privacy Policy"
/> />
<BigCheckbox <BigCheckbox
id="acceptTOS"
checked={acceptedTOS} checked={acceptedTOS}
onChange={acceptTOS} onChange={acceptTOS}
label="I agree to the Terms of Service" label="I agree to the Terms of Service"
@ -225,10 +227,12 @@ function ContinueButton({
} }
export function BigCheckbox({ export function BigCheckbox({
id,
label, label,
checked, checked,
onChange, onChange,
}: { }: {
id: string;
label: string; label: string;
checked: boolean; checked: boolean;
onChange: (checked: boolean) => void; onChange: (checked: boolean) => void;
@ -236,11 +240,14 @@ export function BigCheckbox({
return ( return (
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Checkbox <Checkbox
id={id}
checked={checked} checked={checked}
onCheckedChange={onChange} onCheckedChange={onChange}
className="size-5.5 rounded-md flex items-center justify-center" className="size-5.5 rounded-md flex items-center justify-center"
/> />
<Label className="text-lg">{label}</Label> <Label htmlFor={id} className="text-lg">
{label}
</Label>
</div> </div>
); );
} }

View file

@ -15,9 +15,12 @@ import { useTTP } from "@tensamin/ttp/context";
import { useState } from "react"; import { useState } from "react";
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { isTauri } from "@tauri-apps/api/core"; import { isTauri } from "@tauri-apps/api/core";
import { useStorage } from "@tensamin/storage/context";
// The page // The page
export default function Page() { export default function Page() {
const { clear } = useStorage();
return ( return (
<div className={`px-3 flex gap-2 ${!isTauri() && "pt-3"}`}> <div className={`px-3 flex gap-2 ${!isTauri() && "pt-3"}`}>
<AddConversationButton /> <AddConversationButton />
@ -25,6 +28,12 @@ export default function Page() {
<Button variant="outline" onClick={() => location.reload()}> <Button variant="outline" onClick={() => location.reload()}>
Reload Reload
</Button> </Button>
<Button
variant="destructive"
onClick={() => clear().then(() => location.reload())}
>
Logout
</Button>
</div> </div>
); );
} }

View file

@ -11,8 +11,8 @@ export default function Page() {
<Form /> <Form />
<div className="flex"> <div className="flex">
<a target="_blank" href="https://docs.tensamin.net/installation/#iota"> <a target="_blank" href="https://docs.tensamin.net/installation/#iota">
<p className="text-sm underline text-foreground/75"> <p className="text-sm underline text-foreground/75 text-center whitespace-pre-wrap">
Don't have an account yet? Click here to get help setting up an Don't have an account yet?{"\n"}Click here to get help setting up an
Iota. Iota.
</p> </p>
</a> </a>

View file

@ -223,7 +223,7 @@ export default function Provider(props: { children: ReactNode }) {
{ {
expected: userIdValue, expected: userIdValue,
received: nextState.chat_partner_id, received: nextState.chat_partner_id,
} },
); );
return; return;
} }

View file

@ -171,7 +171,7 @@ export default function Screen() {
const host = scrollRef.current; const host = scrollRef.current;
if (!host || !el) return; if (!host || !el) return;
host.style.maxHeight = `calc(100vh - ${el.scrollHeight + 50}px)`; host.style.maxHeight = `calc(100vh - ${el.scrollHeight + 50}px - env(safe-area-inset-bottom) - env(safe-area-inset-top))`;
}, []); }, []);
// Render // Render