Add settings, cards list, card preview views

* Settings view is no longer the card creation view
* Add icons for the two settings options
This commit is contained in:
Thiago Chaves 2022-08-29 00:29:01 +03:00
parent 1abf35b35f
commit b21ec1b7ac
16 changed files with 185 additions and 2 deletions

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-plus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M14 3v4a1 1 0 0 0 1 1h4" />
<path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z" />
<line x1="12" y1="11" x2="12" y2="17" />
<line x1="9" y1="14" x2="15" y2="14" />
</svg>

After

Width:  |  Height:  |  Size: 502 B

11
public/icons/list.svg Normal file
View File

@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-list" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="9" y1="6" x2="20" y2="6" />
<line x1="9" y1="12" x2="20" y2="12" />
<line x1="9" y1="18" x2="20" y2="18" />
<line x1="5" y1="6" x2="5" y2="6.01" />
<line x1="5" y1="12" x2="5" y2="12.01" />
<line x1="5" y1="18" x2="5" y2="18.01" />
</svg>

After

Width:  |  Height:  |  Size: 541 B

View File

@ -6,7 +6,10 @@ import {
ExpressionSetListView, ExpressionSetListView,
} from "../../views"; } from "../../views";
import { AddExpressionView } from "../../views/AddExpressionView"; import { AddExpressionView } from "../../views/AddExpressionView";
import { ExpressionCardListView } from "../../views/ExpressionCardListView";
import { ExpressionCardView } from "../../views/ExpressionCardView";
import { HomeView } from "../../views/HomeView"; import { HomeView } from "../../views/HomeView";
import { SettingsView } from "../../views/SettingsView";
export function Page() { export function Page() {
const { route } = useContext(AppRouting); const { route } = useContext(AppRouting);
@ -22,7 +25,12 @@ export function Page() {
case AppPath.ExpressionSetsPractice: case AppPath.ExpressionSetsPractice:
return <ExpressionPracticeView />; return <ExpressionPracticeView />;
case AppPath.Settings: case AppPath.Settings:
// TODO this should split onto more views return <SettingsView />;
case AppPath.CreateCards:
return <AddExpressionView />; return <AddExpressionView />;
case AppPath.CardsList:
return <ExpressionCardListView />;
case AppPath.CardView:
return <ExpressionCardView />;
} }
} }

View File

@ -0,0 +1,26 @@
import { useContext } from "react";
import { AppPath, AppRouting } from "../../model/routing";
export interface SettingsSectionLinkProps {
text: string;
iconUrl?: string;
page: AppPath;
}
export function SettingsSectionLink({
text,
iconUrl,
page,
}: SettingsSectionLinkProps) {
const { setRoute } = useContext(AppRouting);
return (
<li onClick={() => setRoute({ path: page })}>
<div className="content-card">
<div className="content-row">
{iconUrl && <img src={iconUrl} width="24" height="24" alt="" />}
<h2 className="text-title padding-small">{text}</h2>
</div>
</div>
</li>
);
}

View File

@ -0,0 +1 @@
export * from "./SettingsSectionLink";

View File

@ -1,5 +1,8 @@
export * from "./useExpressionCategories"; export * from "./useExpressionCategories";
export * from "./useExpressionById";
export * from "./useExpressionFilterQueryIds"; export * from "./useExpressionFilterQueryIds";
export * from "./useExpressionQueryId";
export * from "./useExpressions";
export * from "./useExpressionSet"; export * from "./useExpressionSet";
export * from "./useExpressionSetQueryId"; export * from "./useExpressionSetQueryId";
export * from "./useExpressionSets"; export * from "./useExpressionSets";

View File

@ -0,0 +1,9 @@
import { useLiveQuery } from "dexie-react-hooks";
import { database, IndexedExpression } from "../model";
export function useExpressionById(id?: number): IndexedExpression | undefined {
return useLiveQuery(() => {
if (id === undefined) return undefined;
return database.expressions.where({ id }).first();
}, [id]);
}

View File

@ -0,0 +1,7 @@
import { useContext } from "react";
import { AppRouting } from "../model/routing";
export function useExpressionQueryId(): number | undefined {
const { route } = useContext(AppRouting);
return route.options?.expression_card_id;
}

View File

@ -0,0 +1,9 @@
import { useLiveQuery } from "dexie-react-hooks";
import { database } from "../model";
export function useExpressions() {
return useLiveQuery(
() => database.expressions.toArray(),
[database.expressions]
);
}

View File

@ -7,10 +7,16 @@ export enum AppPath {
ExpressionSetsPractice = "expression-sets/practice", ExpressionSetsPractice = "expression-sets/practice",
ExpressionSetsDetails = "expression-sets/details", ExpressionSetsDetails = "expression-sets/details",
Settings = "settings", Settings = "settings",
CreateCards = "settings", // TODO split from settings CardsList = "settings/cards",
CardView = "settings/view-card",
CreateCards = "settings/create-cards",
} }
export interface RouteOptions { export interface RouteOptions {
// Used in cards view
expression_card_id?: number;
// Used in practice view
expression_set_id?: number; expression_set_id?: number;
expression_id_filters?: number[]; expression_id_filters?: number[];
} }

View File

@ -0,0 +1,50 @@
import { useContext, useMemo } from "react";
import { ExpressionCard } from "../../components";
import { useExpressions } from "../../hooks";
import { IndexedExpression } from "../../model";
import { AppPath, AppRouting } from "../../model/routing";
import { ErrorView } from "../ErrorView";
export function ExpressionCardListView() {
const { setRoute } = useContext(AppRouting);
const expressions = useExpressions();
const expression_list = useMemo(
() => (expressions || []).concat().sort(sort_function),
[expressions]
);
if (!expressions) return null; // LOADING
if (!expressions.length) {
return <ErrorView message="No expression cards yet" />;
}
return (
<div className="page-with-padding content-list scroll">
<ul className="content-list">
{expression_list.map(({ prompt, id, description }) => (
<li
key={id}
onClick={() =>
setRoute({
path: AppPath.CardView,
options: { expression_card_id: id },
})
}
>
<ExpressionCard
prompt={prompt}
categories={[]}
description={description}
/>
</li>
))}
</ul>
</div>
);
}
function sort_function(a: IndexedExpression, b: IndexedExpression) {
if (a.prompt < b.prompt) return -1;
if (a.prompt > b.prompt) return 1;
return 0;
}

View File

@ -0,0 +1 @@
export * from "./ExpressionCardListView";

View File

@ -0,0 +1,20 @@
import { ExpressionCard } from "../../components";
import { ExpressionDescription } from "../../components/ExpressionDescription";
import { useExpressionById, useExpressionQueryId } from "../../hooks";
import { ErrorView } from "../ErrorView";
export function ExpressionCardView() {
const expression_id = useExpressionQueryId();
const expression = useExpressionById(expression_id);
if (!expression) return <ErrorView message="No valid card selected" />;
return (
<div className="page-with-padding content-list scroll">
<ExpressionCard
prompt={expression.prompt}
categories={[]}
description={<ExpressionDescription expression={expression} />}
show_description
/>
</div>
);
}

View File

@ -0,0 +1 @@
export * from "./ExpressionCardView";

View File

@ -0,0 +1,21 @@
import { SettingsSectionLink } from "../../components/SettingsSection";
import { AppPath } from "../../model/routing";
export function SettingsView() {
return (
<div className="page-with-padding content-list scroll">
<ul className="content-list">
<SettingsSectionLink
text="Create cards"
page={AppPath.CreateCards}
iconUrl="/icons/file-plus.svg"
/>
<SettingsSectionLink
text="Cards list"
page={AppPath.CardsList}
iconUrl="/icons/list.svg"
/>
</ul>
</div>
);
}

View File

@ -0,0 +1 @@
export * from "./SettingsView";