Refactor hook names and make their usage more uniform
Also ensure that potential "loading" states and IndexedDB error states are marked to be handled properly
This commit is contained in:
parent
b21ec1b7ac
commit
a8f3bbac49
@ -2,18 +2,22 @@ export interface ExpressionSetCardProps {
|
||||
name: string;
|
||||
description: string;
|
||||
expression_count: number;
|
||||
expression_count_loading?: boolean;
|
||||
}
|
||||
|
||||
export function ExpressionSetCard({
|
||||
name,
|
||||
description,
|
||||
expression_count,
|
||||
expression_count_loading,
|
||||
}: ExpressionSetCardProps) {
|
||||
return (
|
||||
<article className="content-card">
|
||||
<h2 className="content-row text-title margin-small">{name}</h2>
|
||||
<span className="content-row text-meta margin-small">
|
||||
{expression_count} expressions(s)
|
||||
{expression_count_loading
|
||||
? "loading"
|
||||
: `${expression_count} expressions(s)`}
|
||||
</span>
|
||||
<p className="content-row text-details margin-paragraph">{description}</p>
|
||||
</article>
|
||||
|
@ -1,9 +1,9 @@
|
||||
export * from "./useExpressionCategories";
|
||||
export * from "./useAllExpressions";
|
||||
export * from "./useAllExpressionSets";
|
||||
export * from "./useCategoriesByExpressionId";
|
||||
export * from "./useExpressionById";
|
||||
export * from "./useExpressionFilterQueryIds";
|
||||
export * from "./useExpressionQueryId";
|
||||
export * from "./useExpressions";
|
||||
export * from "./useExpressionSet";
|
||||
export * from "./useExpressionSetQueryId";
|
||||
export * from "./useExpressionSets";
|
||||
export * from "./useExpressionsInSet";
|
||||
export * from "./useExpressionsByExpressionSetId";
|
||||
export * from "./useExpressionSetById";
|
||||
export * from "./useQueryExpressionId";
|
||||
export * from "./useQueryExpressionIdFilters";
|
||||
export * from "./useQueryExpressionSetId";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { database } from "../model";
|
||||
|
||||
export function useExpressionSets() {
|
||||
export function useAllExpressionSets() {
|
||||
return useLiveQuery(() => database.expression_sets.toArray());
|
||||
}
|
6
src/hooks/useAllExpressions.ts
Normal file
6
src/hooks/useAllExpressions.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { database } from "../model";
|
||||
|
||||
export function useAllExpressions() {
|
||||
return useLiveQuery(() => database.expressions.toArray());
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { database } from "../model";
|
||||
|
||||
export function useExpressionCategories(expression_id: number) {
|
||||
export function useCategoriesByExpressionId(expression_id: number) {
|
||||
return useLiveQuery(() => {
|
||||
return database.expression_to_category
|
||||
.where({ expression_id })
|
@ -1,9 +1,8 @@
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { database, IndexedExpression } from "../model";
|
||||
import { database } from "../model";
|
||||
|
||||
export function useExpressionById(id?: number): IndexedExpression | undefined {
|
||||
export function useExpressionById(expression_id: number) {
|
||||
return useLiveQuery(() => {
|
||||
if (id === undefined) return undefined;
|
||||
return database.expressions.where({ id }).first();
|
||||
}, [id]);
|
||||
return database.expressions.where({ id: expression_id }).first();
|
||||
}, [expression_id]);
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { database } from "../model";
|
||||
|
||||
export function useExpressionSet(id: number) {
|
||||
return useLiveQuery(
|
||||
() => database.expression_sets.where({ id }).first(),
|
||||
[id]
|
||||
);
|
||||
}
|
9
src/hooks/useExpressionSetById.ts
Normal file
9
src/hooks/useExpressionSetById.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { database } from "../model";
|
||||
|
||||
export function useExpressionSetById(expression_set_id: number) {
|
||||
return useLiveQuery(
|
||||
() => database.expression_sets.where({ id: expression_set_id }).first(),
|
||||
[expression_set_id]
|
||||
);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { database } from "../model";
|
||||
|
||||
export function useExpressions() {
|
||||
return useLiveQuery(
|
||||
() => database.expressions.toArray(),
|
||||
[database.expressions]
|
||||
);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { database } from "../model";
|
||||
|
||||
export function useExpressionsInSet(expression_set_id: number) {
|
||||
export function useExpressionsByExpressionSetId(expression_set_id: number) {
|
||||
return useLiveQuery(() => {
|
||||
return database.expression_to_expression_set
|
||||
.where({ expression_set_id })
|
@ -1,7 +1,7 @@
|
||||
import { useContext } from "react";
|
||||
import { AppRouting } from "../model/routing";
|
||||
|
||||
export function useExpressionQueryId(): number | undefined {
|
||||
export function useQueryExpressionId() {
|
||||
const { route } = useContext(AppRouting);
|
||||
return route.options?.expression_card_id;
|
||||
return route.options?.expression_id || 0;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { useContext } from "react";
|
||||
import { AppRouting } from "../model/routing";
|
||||
|
||||
export function useExpressionFilterQueryIds(): number[] {
|
||||
export function useQueryExpressionIdFilters() {
|
||||
const { route } = useContext(AppRouting);
|
||||
return route.options?.expression_id_filters || [];
|
||||
return route.options?.expression_id_filters || ([] as number[]);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { useContext } from "react";
|
||||
import { AppRouting } from "../model/routing";
|
||||
|
||||
export function useExpressionSetQueryId(): number {
|
||||
export function useQueryExpressionSetId() {
|
||||
const { route } = useContext(AppRouting);
|
||||
return route.options?.expression_set_id || 0;
|
||||
}
|
@ -14,7 +14,7 @@ export enum AppPath {
|
||||
|
||||
export interface RouteOptions {
|
||||
// Used in cards view
|
||||
expression_card_id?: number;
|
||||
expression_id?: number;
|
||||
|
||||
// Used in practice view
|
||||
expression_set_id?: number;
|
||||
|
@ -17,6 +17,8 @@ export function AddExpressionView() {
|
||||
useState<string | undefined>(undefined);
|
||||
const [error, setError] = useState<any>(undefined);
|
||||
|
||||
// TODO waiting fetch completion elements
|
||||
|
||||
return (
|
||||
<form className="page-with-bottom-navigation">
|
||||
<section className="padding-small scroll">
|
||||
|
@ -1,19 +1,21 @@
|
||||
import { useContext, useMemo } from "react";
|
||||
import { ExpressionCard } from "../../components";
|
||||
import { useExpressions } from "../../hooks";
|
||||
import { useAllExpressions } 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 expressions = useAllExpressions();
|
||||
const expression_list = useMemo(
|
||||
() => (expressions || []).concat().sort(sort_function),
|
||||
[expressions]
|
||||
);
|
||||
|
||||
if (!expressions) return null; // LOADING
|
||||
// TODO query error view?
|
||||
|
||||
if (!expressions.length) {
|
||||
return <ErrorView message="No expression cards yet" />;
|
||||
}
|
||||
@ -27,7 +29,7 @@ export function ExpressionCardListView() {
|
||||
onClick={() =>
|
||||
setRoute({
|
||||
path: AppPath.CardView,
|
||||
options: { expression_card_id: id },
|
||||
options: { expression_id: id },
|
||||
})
|
||||
}
|
||||
>
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { ExpressionCard } from "../../components";
|
||||
import { ExpressionDescription } from "../../components/ExpressionDescription";
|
||||
import { useExpressionById, useExpressionQueryId } from "../../hooks";
|
||||
import { ErrorView } from "../ErrorView";
|
||||
import { useExpressionById, useQueryExpressionId } from "../../hooks";
|
||||
|
||||
export function ExpressionCardView() {
|
||||
const expression_id = useExpressionQueryId();
|
||||
const expression_id = useQueryExpressionId();
|
||||
const expression = useExpressionById(expression_id);
|
||||
if (!expression) return <ErrorView message="No valid card selected" />;
|
||||
|
||||
if (expression === undefined) return null; // LOADING
|
||||
// TODO query error view?
|
||||
|
||||
return (
|
||||
<div className="page-with-padding content-list scroll">
|
||||
<ExpressionCard
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { useCallback, useContext } from "react";
|
||||
import {
|
||||
useExpressionFilterQueryIds,
|
||||
useExpressionSetQueryId,
|
||||
useQueryExpressionIdFilters,
|
||||
useQueryExpressionSetId,
|
||||
} from "../../hooks";
|
||||
import { assignExpressionToSet } from "../../model";
|
||||
import { AppRouting } from "../../model/routing";
|
||||
|
||||
// TODO fix promotion algorithm so it uses a destination expression_set_id
|
||||
// TODO ensure that the destination expression set exists
|
||||
|
||||
export interface DemoteExpressionButtonProps {
|
||||
expression_id: number;
|
||||
@ -16,8 +16,8 @@ export function DemoteExpressionButton({
|
||||
expression_id,
|
||||
}: DemoteExpressionButtonProps) {
|
||||
const { route, setRoute } = useContext(AppRouting);
|
||||
const expression_set_id = useExpressionSetQueryId();
|
||||
const expression_id_filters = useExpressionFilterQueryIds();
|
||||
const expression_set_id = useQueryExpressionSetId();
|
||||
const expression_id_filters = useQueryExpressionIdFilters();
|
||||
const handleClick = useCallback(() => {
|
||||
if (expression_set_id === 1) {
|
||||
setRoute({
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
useExpressionCategories,
|
||||
useExpressionFilterQueryIds,
|
||||
useExpressionSetQueryId,
|
||||
useExpressionsInSet,
|
||||
useCategoriesByExpressionId,
|
||||
useQueryExpressionIdFilters,
|
||||
useQueryExpressionSetId,
|
||||
useExpressionsByExpressionSetId,
|
||||
} from "../../hooks";
|
||||
import { IndexedExpression } from "../../model";
|
||||
import { sample } from "../../util";
|
||||
@ -11,18 +11,13 @@ import { ErrorView } from "../ErrorView";
|
||||
import { ExpressionPracticeCardView } from "./ExpressionPracticeCardView";
|
||||
|
||||
export function ExpressionPracticeView() {
|
||||
const expression_set_id = useExpressionSetQueryId();
|
||||
const filter_ids = useExpressionFilterQueryIds();
|
||||
const expressions = useExpressionsInSet(expression_set_id);
|
||||
const expression_set_id = useQueryExpressionSetId();
|
||||
const filter_ids = useQueryExpressionIdFilters();
|
||||
const expressions = useExpressionsByExpressionSetId(expression_set_id);
|
||||
|
||||
// Fallback rendering for content not yet fetched
|
||||
if (!expressions) return null;
|
||||
if (!filter_ids) return null;
|
||||
if (!expressions) return null; // LOADING
|
||||
// TODO query error view?
|
||||
|
||||
// Fallback views for expression set content not found
|
||||
if (!expression_set_id) {
|
||||
return <ErrorView message="Expression set not found" />;
|
||||
}
|
||||
const filtered_expressions = expressions.filter(
|
||||
(expression) => !filter_ids.includes(expression.id!)
|
||||
);
|
||||
@ -45,7 +40,7 @@ function ExpressionPracticeViewImpl({
|
||||
select_from_expressions: expressions,
|
||||
}: ExpressionPracticeViewImplProps) {
|
||||
const expression = useMemo(() => sample(expressions), [expressions]);
|
||||
const categories = useExpressionCategories(expression.id!);
|
||||
const categories = useCategoriesByExpressionId(expression.id!);
|
||||
if (!categories) return null; // Loading
|
||||
|
||||
// Delegate internal interaction state to next component so it resets
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useCallback } from "react";
|
||||
import { useExpressionSetQueryId } from "../../hooks";
|
||||
import { useQueryExpressionSetId } from "../../hooks";
|
||||
import { assignExpressionToSet, database, removeExpression } from "../../model";
|
||||
|
||||
export interface PromoteExpressionButtonProps {
|
||||
@ -12,7 +12,7 @@ export interface PromoteExpressionButtonProps {
|
||||
export function PromoteExpressionButton({
|
||||
expression_id,
|
||||
}: PromoteExpressionButtonProps) {
|
||||
const expression_set_id = useExpressionSetQueryId();
|
||||
const expression_set_id = useQueryExpressionSetId();
|
||||
const handleClick = useCallback(async () => {
|
||||
const existing_next_level = await database.expression_sets
|
||||
.where({ id: expression_set_id + 1 })
|
||||
|
@ -1,26 +1,21 @@
|
||||
import { useContext } from "react";
|
||||
import { ExpressionSetInfo } from "../../components";
|
||||
import {
|
||||
useExpressionSet,
|
||||
useExpressionSetQueryId,
|
||||
useExpressionsInSet,
|
||||
useExpressionSetById,
|
||||
useQueryExpressionSetId,
|
||||
useExpressionsByExpressionSetId,
|
||||
} from "../../hooks";
|
||||
import { IndexedExpressionSet } from "../../model";
|
||||
import { AppPath, AppRouting } from "../../model/routing";
|
||||
import { ErrorView } from "../ErrorView";
|
||||
|
||||
export function ExpressionSetDetailsView() {
|
||||
const expression_set_id = useExpressionSetQueryId();
|
||||
const expression_set = useExpressionSet(expression_set_id);
|
||||
const expressions = useExpressionsInSet(expression_set_id);
|
||||
const expression_set_id = useQueryExpressionSetId();
|
||||
const expression_set = useExpressionSetById(expression_set_id);
|
||||
const expressions = useExpressionsByExpressionSetId(expression_set_id);
|
||||
|
||||
// Fallback for expression set not found
|
||||
if (!expressions) return null;
|
||||
if (!expression_set) {
|
||||
return <ErrorView message="Expression set not found" />;
|
||||
}
|
||||
if (!expression_set || !expressions) return null; // LOADING
|
||||
// TODO query error view?
|
||||
|
||||
// Fallback for expression set empty
|
||||
return (
|
||||
<ExpressionSetDetailsViewImpl
|
||||
expression_set={expression_set}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useContext } from "react";
|
||||
import { ExpressionSetCard } from "../../components";
|
||||
import { useExpressionsInSet } from "../../hooks";
|
||||
import { useExpressionsByExpressionSetId } from "../../hooks";
|
||||
import { IndexedExpressionSet } from "../../model";
|
||||
import { AppPath, AppRouting } from "../../model/routing";
|
||||
|
||||
@ -10,7 +10,7 @@ export function ExpressionSetLink({
|
||||
name,
|
||||
}: IndexedExpressionSet) {
|
||||
const { setRoute } = useContext(AppRouting);
|
||||
const expressions = useExpressionsInSet(id!) || [];
|
||||
const expressions = useExpressionsByExpressionSetId(id!);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -24,7 +24,8 @@ export function ExpressionSetLink({
|
||||
<ExpressionSetCard
|
||||
name={name}
|
||||
description={description}
|
||||
expression_count={expressions.length}
|
||||
expression_count={expressions?.length || 0}
|
||||
expression_count_loading={expressions === undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,10 +1,13 @@
|
||||
import { useExpressionSets } from "../../hooks";
|
||||
import { useAllExpressionSets } from "../../hooks";
|
||||
import { ErrorView } from "../ErrorView";
|
||||
import { ExpressionSetLink } from "./ExpressionSetLink";
|
||||
|
||||
export function ExpressionSetListView() {
|
||||
const expression_sets = useExpressionSets();
|
||||
const expression_sets = useAllExpressionSets();
|
||||
|
||||
if (!expression_sets) return null; // LOADING
|
||||
// TODO query error view?
|
||||
|
||||
if (!expression_sets.length) {
|
||||
return <ErrorView message="No expression sets found" />;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user