Added deeplinks, added qr code login, added settings, other stuff

This commit is contained in:
Alois 2026-04-10 19:44:08 +02:00
commit 8ff8bd9528
32 changed files with 917 additions and 78 deletions

View file

@ -7,31 +7,31 @@ import { SidebarProvider } from "@tensamin/ui/cmp/sidebar";
import { useIsMobile, cn } from "@tensamin/ui/utils";
import { isTauri } from "@tauri-apps/api/core";
import { useLocation } from "@tanstack/react-router";
import { useLocation, useMatches } from "@tanstack/react-router";
/**
* Executes Layout.
* @param props Parameter props.
* @returns unknown.
*/
export default function Layout(props: { children: ReactNode }) {
export default function Layout({ children }: { children: ReactNode }) {
const isMobile = useIsMobile();
const location = useLocation();
const showMobileNavbar = useShowMobileNavbar();
return (
<div className="w-full h-full flex bg-sidebar">
<SidebarProvider>
<Sidebar />
<div
// Background of ui that is overlapping with the system ui
className={cn(
"w-full h-full flex flex-col",
isTauri() && isMobile && "pt-[env(safe-area-inset-top)]",
isMobile && showMobileNavbar && "bg-background",
)}
>
{!isMobile && <Navbar forMobile={false} />}
{isMobile && location.pathname === "/chat" && (
<Navbar forMobile={true} />
)}
{isMobile && !showMobileNavbar && <Navbar forMobile={true} />}
<div
className={cn(
@ -39,11 +39,26 @@ export default function Layout(props: { children: ReactNode }) {
!isMobile && "rounded-tl-3xl border-t border-l",
)}
>
{props.children}
{children}
</div>
{isMobile && location.pathname !== "/chat" && <MobileNavbar />}
{isMobile && showMobileNavbar && <MobileNavbar />}
</div>
</SidebarProvider>
</div>
);
}
export function useShowMobileNavbar(): boolean {
const matches = useMatches();
const location = useLocation();
const value = matches.some(
(match) =>
match.pathname === location.pathname &&
// @ts-expect-error Stuff
match.staticData?.showMobileNavbar === true,
);
return value;
}

View file

@ -0,0 +1,12 @@
import { Switch } from "@/features/settings/components";
export default function Page() {
return (
<div>
<Switch
label="Reverse Enter Key Behavior"
id="settings.reverse_enter_behavior"
/>
</div>
);
}

View file

@ -0,0 +1,7 @@
import { SettingsSidebar } from "@/features/settings/layout";
import { useIsMobile } from "@tensamin/ui/utils";
export default function Page() {
const isMobile = useIsMobile();
return isMobile && <SettingsSidebar />;
}

View file

@ -0,0 +1,111 @@
import { useStorage } from "@tensamin/storage/context";
import { useEffect, useState } from "react";
import QRCode from "qrcode";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@tensamin/ui/cmp/alert-dialog";
import { Button } from "@tensamin/ui/cmp/button";
export default function Page() {
return (
<div>
<QrCodeLogin />
</div>
);
}
// QR Code Login
const generateQR = async (text: string): Promise<string> => {
try {
const url = await QRCode.toDataURL(text, {
errorCorrectionLevel: "H",
margin: 1,
color: {
dark: "#000000FF",
light: "#FFFFFFFF",
},
});
return url;
} catch (err) {
console.error(err);
throw err;
}
};
function QrCodeLogin() {
const { load } = useStorage();
const [userId, setUserId] = useState<number>(0);
const [privateKey, setPrivateKey] = useState<string>("");
const [qrCodeBase64, setQrCodeBase64] = useState<string | undefined>(
undefined,
);
useEffect(() => {
load("private_key").then((value) => {
if (value) {
setPrivateKey(value);
}
});
load("user_id").then((value) => {
if (value) {
setUserId(value);
}
});
}, [load]);
useEffect(() => {
if (userId && privateKey) {
generateQR(`tensamin://tu::${userId}::${privateKey}`).then(
setQrCodeBase64,
);
}
}, [userId, privateKey]);
const [qrCodeVisible, setQrCodeVisible] = useState(false);
return (
<div className="relative rounded-lg w-50 border-3 aspect-square overflow-hidden">
{qrCodeBase64 && (
<img className="w-full h-full" src={qrCodeBase64} alt="QR Code" />
)}
{!qrCodeVisible && (
<>
<div className="absolute top-0 left-0 w-full h-full backdrop-blur-sm bg-background/50" />
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<AlertDialog>
<AlertDialogTrigger render={<Button>Show QR Code</Button>} />
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
<AlertDialogDescription>
This will expose your private key! This is the same as
sharing the .tu file! It's very tedious to change the
private key, so only do this if you are sure no one can
steal it!
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
render={
<Button onClick={() => setQrCodeVisible(true)}>
Show QR Code
</Button>
}
/>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</>
)}
</div>
);
}