Formatted, updated convs & comms list to not depend on each other, updated legal & login screen for better mobile support
This commit is contained in:
parent
aa8dfe2fca
commit
741afdeaa9
9 changed files with 54 additions and 22 deletions
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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]}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue