Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore: Using common Component to structure Settings page #736

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 35 additions & 6 deletions vrc-get-gui/app/log/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import {Card, Typography} from "@material-tailwind/react";
import {HNavBar, VStack} from "@/components/layout";
import {HContent, HNavBar, HSection, HSectionText, VStack} from "@/components/layout";
import React, {useEffect} from "react";
import {LogEntry, utilGetLogEntries} from "@/lib/bindings";
import {notoSansMono} from "@/app/fonts";
Expand Down Expand Up @@ -41,20 +41,49 @@ export default function Page() {
return (
<VStack className={"m-4"}>
<HNavBar className={"flex-shrink-0"}>
<Typography className="cursor-pointer py-1.5 font-bold flex-grow-0">
<Typography variant="h4" className="cursor-pointer py-1.5 font-bold flex-grow-0">
{tc("logs")}
</Typography>
</HNavBar>
<main className="flex-shrink overflow-hidden flex flex-grow">
<HContent>
<HSection className="overflow-x-auto">
{logEntries.map((entry, i) => (
<HSectionText className="text-nowrap" key={i}>
{logEntryToText(entry)}
</HSectionText>
))}
</HSection>
</HContent>
{/* <main className="flex-shrink overflow-hidden flex flex-grow">
<Card className={`w-full overflow-x-auto overflow-y-scroll p-2 whitespace-pre ${notoSansMono.className} shadow-none`}>
{logEntries.map((entry) => logEntryToText(entry)).join("\n")}
{logEntries.map((entry) => logEntryToText(entry)).join("\n")}
</Card>
</main>
</main> */}
</VStack>
);
}

function logEntryToText(entry: LogEntry) {
return `${entry.time} [${entry.level.padStart(5, ' ')}] ${entry.target}: ${entry.message}`;
const level = entry.level
let levelClassName;
switch(level){
case "Debug":
levelClassName = "bg-blue-600"
break
case "Info":
levelClassName = "bg-green-600"
break
case "Warn":
levelClassName = "bg-orange-600"
break
case "Error":
levelClassName = "bg-red-600"
break
default:
levelClassName = "bg-inherit"
}

// Using a blank character to get space within a span component
return (<><span className={`mr-1 ${levelClassName}`}>⠀</span>{entry.time} [{level}] {entry.target}: {entry.message}</>)
}

207 changes: 72 additions & 135 deletions vrc-get-gui/app/repositories/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ import {
TauriRemoteRepositoryInfo,
TauriUserRepository
} from "@/lib/bindings";
import {HNavBar, VStack} from "@/components/layout";
import {HContent, HNavBar, HSection, VStack} from "@/components/layout";
import React, {Suspense, useMemo, useState} from "react";
import {MinusCircleIcon, PlusCircleIcon, XCircleIcon} from "@heroicons/react/24/outline";
import {nop} from "@/lib/nop";
import {toastError, toastSuccess, toastThrownError} from "@/lib/toast";
import {tc, tt} from "@/lib/i18n";
import {InputNoLabel} from "@/components/InputNoLabel";
import {loadManifestWithRetries} from "next/dist/server/load-components";
import Table from "@/components/Table";

export default function Page(props: {}) {
return <Suspense><PageBody {...props}/></Suspense>
Expand Down Expand Up @@ -143,152 +143,89 @@ function PageBody() {
const dialog = dialogBody ?
<Dialog handler={nop} open><DialogHeader>{tc("vpm repositories:button:add repository")}</DialogHeader>{dialogBody}</Dialog> : null;

const [removeDialogOpen, setRemoveDialogOpen] = useState("");


return (
<VStack className={"p-4 overflow-y-auto"}>
<HNavBar className={"flex-shrink-0"}>
<Typography className="cursor-pointer py-1.5 font-bold flex-grow-0">
<Typography variant="h4" className="cursor-pointer py-1.5 font-bold flex-grow-0">
{tc("vpm repositories:community repositories")}
</Typography>
<Button onClick={() => setState({type: 'enteringRepositoryInfo'})}>{tc("vpm repositories:button:add repository")}</Button>
</HNavBar>
<main className="flex-shrink flex-grow overflow-hidden flex">
<Card className="w-full overflow-x-auto overflow-y-scroll shadow-none">
<RepositoryTable
userRepos={result.data?.user_repositories || []}
hiddenUserRepos={hiddenUserRepos}
removeRepository={removeRepository}
refetch={() => result.refetch()}
<HContent>
<HSection>
<Table
header={["", tc("vpm repositories:community repositories"), tc("vpm repositories:url"), ""]}
layout={["50px", "1fr", "1fr", "50px"]}
rows={(result.data?.user_repositories ?? []).map((repo, repoIndex) => {
const id = `repository-${repo.id}`;

const selected = !hiddenUserRepos.has(repo.id);
const onChange = () => {
if (selected) {
environmentHideRepository(repo.id).then(() => result.refetch());
} else {
environmentShowRepository(repo.id).then(() => result.refetch());
}
}

let localDialog
if (removeDialogOpen === repo.id) {
localDialog = <Dialog handler={nop} open>
<DialogHeader>{tc("vpm repositories:remove repository")}</DialogHeader>
<DialogBody>
<Typography
className={"whitespace-normal font-normal"}>{tc("vpm repositories:dialog:confirm remove description", {name: repo.display_name})}</Typography>
</DialogBody>
<DialogFooter>
<Button onClick={() => setRemoveDialogOpen("")}>{tc("general:button:cancel")}</Button>
<Button onClick={() => {
removeRepository(repo.id)
setRemoveDialogOpen("");
}} className={"ml-2"}>{tc("vpm repositories:remove repository")}</Button>
</DialogFooter>
</Dialog>;
}


return [
(<Checkbox
key={`checkbox-${repoIndex}`}
ripple={false}
containerProps={{className: "p-0 rounded-none"}}
id={id}
checked={selected}
onChange={onChange}
/>),
(<Typography
key={`display-${repoIndex}`}
>
{repo.display_name}
</Typography>),
(<Typography key={`url-${repoIndex}`} className="font-normal">
{repo.url}
</Typography>),
(<>
<Tooltip key={`remove-${repoIndex}`} content={tc("vpm repositories:remove repository")}>
<IconButton variant={"text"} onClick={() =>setRemoveDialogOpen(repo.id)}>
<XCircleIcon className={"size-5 text-red-700"}/>
</IconButton>
</Tooltip>
{localDialog}
</>
)
]
})}
/>
{dialog}
</Card>
</main>
</HSection>
</HContent>
</VStack>
);
}

function RepositoryTable(
{
userRepos,
hiddenUserRepos,
removeRepository,
refetch,
}: {
userRepos: TauriUserRepository[],
hiddenUserRepos: Set<string>,
removeRepository: (id: string) => void,
refetch: () => void,
}
) {
const TABLE_HEAD = [
"", // checkbox
"general:name",
"vpm repositories:url",
"", // actions
];

return (
<table className="relative table-auto text-left">
<thead>
<tr>
{TABLE_HEAD.map((head, index) => (
<th key={index}
className={`sticky top-0 z-10 border-b border-blue-gray-100 bg-blue-gray-50 p-2.5`}>
<Typography variant="small" className="font-normal leading-none">{tc(head)}</Typography>
</th>
))}
</tr>
</thead>
<tbody>
{
userRepos.map((repo) =>
<RepositoryRow
key={repo.id}
repo={repo}
hiddenUserRepos={hiddenUserRepos}
remove={() => removeRepository(repo.id)}
refetch={refetch}
/>)
}
</tbody>
</table>
);
}

function RepositoryRow(
{
repo,
hiddenUserRepos,
remove,
refetch,
}: {
repo: TauriUserRepository,
hiddenUserRepos: Set<string>,
remove: () => void,
refetch: () => void,
}
) {
const cellClass = "p-2.5";
const id = `repository-${repo.id}`;

const [removeDialogOpen, setRemoveDialogOpen] = useState(false);

const selected = !hiddenUserRepos.has(repo.id);
const onChange = () => {
if (selected) {
environmentHideRepository(repo.id).then(refetch);
} else {
environmentShowRepository(repo.id).then(refetch);
}
}

let dialog;
if (removeDialogOpen) {
dialog = <Dialog handler={nop} open>
<DialogHeader>{tc("vpm repositories:remove repository")}</DialogHeader>
<DialogBody>
<Typography
className={"whitespace-normal font-normal"}>{tc("vpm repositories:dialog:confirm remove description", {name: repo.display_name})}</Typography>
</DialogBody>
<DialogFooter>
<Button onClick={() => setRemoveDialogOpen(false)}>{tc("general:button:cancel")}</Button>
<Button onClick={() => {
remove();
setRemoveDialogOpen(false);
}} className={"ml-2"}>{tc("vpm repositories:remove repository")}</Button>
</DialogFooter>
</Dialog>;
}

return (
<tr className="even:bg-blue-gray-50/50">
<td className={cellClass}>
<Checkbox ripple={false} containerProps={{className: "p-0 rounded-none"}} id={id}
checked={selected} onChange={onChange}/>
</td>
<td className={cellClass}>
<label htmlFor={id}>
<Typography className="font-normal">
{repo.display_name}
</Typography>
</label>
</td>
<td className={cellClass}>
<Typography className="font-normal">
{repo.url}
</Typography>
</td>
<td className={`${cellClass} w-0`}>
<Tooltip content={tc("vpm repositories:remove repository")}>
<IconButton onClick={() => setRemoveDialogOpen(true)} variant={"text"}>
<XCircleIcon className={"size-5 text-red-700"}/>
</IconButton>
</Tooltip>
</td>
{dialog}
</tr>
)
}

let globalHeaderId = 0;

function EnteringRepositoryInfo(
Expand Down