Skip to content

Commit

Permalink
cubeView loader bug fixes, polished alert styling
Browse files Browse the repository at this point in the history
  • Loading branch information
dsoskey committed Apr 22, 2024
1 parent e00a0fb commit 27a49d8
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 42 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
git clone git+https://github.com/dsoskey/cogwork-librarian.git
cd cogwork-librarian
npm install
npm run dev-server
npm run start-dev
```

## tests to run
Expand Down
28 changes: 26 additions & 2 deletions src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -586,10 +586,34 @@ textarea.coglib-prism-theme {
margin: var(--spacing-200);
}

.alert::before {
content: "⚠ ";
}
.alert {
color: var(--light-red);
background-color: whitesmoke;
--alert-foreground: var(--light-red);
--alert-background: whitesmoke;
color: var(--alert-foreground);
background-color: var(--alert-background);
padding: var(--spacing-200) var(--spacing-300);
font-weight: bold;
width: fit-content;
}

.alert * {
color: inherit;
background-color: inherit;
}

.alert button:hover:not(:disabled) {
color: var(--alert-background);
background-color: var(--alert-foreground);
border-bottom-color: var(--alert-background);
border-right-color: var(--alert-background);
}

.alert *:disabled {
color: color-mix(in srgb, var(--alert-foreground), black 20%);
background-color: color-mix(in srgb, var(--alert-background), black 20%);
}

.cube-import ul {
Expand Down
121 changes: 83 additions & 38 deletions src/ui/cubeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useContext, useEffect, useMemo, useState } from 'react'
import { useParams } from 'react-router'
import { useLiveQuery } from 'dexie-react-hooks'
import { Card, NormedCard, QueryRunner, SearchError, SortFunctions } from 'mtgql'
import { cogDB as cogDBClient } from '../api/local/db'
import { cogDB as cogDBClient, Manifest, toManifest } from '../api/local/db'
import { CardImageView } from './cardBrowser/cardViews/cardImageView'
import { groupBy, sortBy } from 'lodash'
import './cubeView.css'
Expand All @@ -15,7 +15,7 @@ import { LoaderText } from './component/loaders'
import { useBulkCubeImporter } from '../api/cubecobra/useBulkCubeImporter'
import { Multiselect } from './component/multiselect'
import { useLocalStorage } from '../api/local/useLocalStorage'
import { useSearchParams } from 'react-router-dom'
import { Link, useSearchParams } from 'react-router-dom'
import { CopyToClipboardButton } from './component/copyToClipboardButton'
import { DBStatusLoader } from './component/dbStatusLoader'
import { CogDBContext } from '../api/local/useCogDB'
Expand Down Expand Up @@ -57,10 +57,25 @@ const SORT_OPTIONS = [
"edhrec",
]

function LoadingError({ cardCount, refreshCubeCards }) {
const { loadManifest, memStatus, dbStatus } = useContext(CogDBContext)

const onClick = async () => {
await loadManifest({ type: "default_cards" } as Manifest, ["db", "memory"], "")
await refreshCubeCards()
}

return <div className="alert">
Your local database is missing {cardCount} cards.{" "}
<Link to="/data/card">Go to data</Link> to refresh your database then load this page again or
{" "}<button disabled={memStatus === "loading" || dbStatus === 'loading'} onClick={onClick}>load default database</button>
</div>
}
export function CubeView() {
const { key } = useParams();
const { dbStatus } = useContext(CogDBContext);
const [error, setError] = useState<SearchError | undefined>()
const [searchError, setSearchError] = useState<SearchError | undefined>()
const [loadingError, setLoadingError] = useState<React.ReactNode>(undefined);
const cube = useLiveQuery(() => cogDBClient.getCube(key), [key, dbStatus], null);
const needsMigration = cube && cube.cards === undefined;
const [oracles, setOracles] = useState<{ [key: string]: NormedCard[] }>({})
Expand All @@ -84,48 +99,73 @@ export function CubeView() {
// This technically works with OrderedCard because the query runner spreads its props.
// @ts-ignore
.map(setFilteredCards)
.mapErr(setError)
.mapErr(setSearchError)
return [];
};
const clearFilter = () => setFilteredCards(undefined);

useEffect(() => {
const funk = async() => {
try {
const next: OrderedCard[] = [];
const newOracles = (await cogDBClient.card.bulkGet(cube.cards?.map(it=>it.oracle_id) ?? cube.oracle_ids)) ?? [];
const oracleIdToNormed = groupBy(newOracles, "oracle_id")
setOracles(oracleIdToNormed);
if (needsMigration) {
const cards = newOracles.map(it => ({ oracle_id: it.oracle_id, print_id: it.printings[0].id }));
await cogDBClient.cube.put({
...cube,
cards
});
} else {
const printToCard: { [key: string]: Card } = {};
for (const normCard of newOracles) {
for (const print of normCard.printings) {
printToCard[print.id] = {...normCard, ...print};
}
const refreshCubeCards = async() => {
try {
const next: OrderedCard[] = [];
const newOracles = (await cogDBClient.card.bulkGet(cube.cards?.map(it=>it.oracle_id) ?? cube.oracle_ids)) ?? [];
const missingIndexes = newOracles
.map((card, index) => card === undefined ? index : -1)
.filter(index => index !== -1)
if (missingIndexes.length) {
setLoadingError(<LoadingError
cardCount={missingIndexes.length}
refreshCubeCards={refreshCubeCards}
/>)
return;
}
const oracleIdToNormed = groupBy(newOracles, "oracle_id")
setOracles(oracleIdToNormed);
if (needsMigration) {
const cards = newOracles.map(it => ({ oracle_id: it.oracle_id, print_id: it.printings[0].id }));
await cogDBClient.cube.put({
...cube,
cards
});
} else {
const printToCard: { [key: string]: Card } = {};
for (const normCard of newOracles) {
for (const print of normCard.printings) {
printToCard[print.id] = {...normCard, ...print};
}
}

for (let i = 0; i < cube.cards.length; i++) {
const printId = cube.cards[i].print_id
if (printId in printToCard) {
next.push({ ...printToCard[printId], index: i });
}
const missingPrints = []
for (let i = 0; i < cube.cards.length; i++) {
const printId = cube.cards[i].print_id
if (printId in printToCard) {
next.push({ ...printToCard[printId], index: i });
} else {
missingPrints.push(printId);
}
}

if (missingPrints.length) {
setLoadingError(<LoadingError
cardCount={missingPrints.length}
refreshCubeCards={refreshCubeCards}
/>)

} else {
setCards(next);
}

} catch (e) {
console.error(e)
}
};
if (cube) funk().catch(console.error);
}, [cube])
setLoadingError(undefined)
} catch (e) {
console.error(e)
setLoadingError(<div className="alert">
{e.message}
</div>)
}
};

useEffect(() => {
if (cube && dbStatus === "success") refreshCubeCards().catch(console.error);
}, [cube, dbStatus])

const onPrintSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
const normedCard = oracles[activeCard.oracle_id][0]
Expand Down Expand Up @@ -156,11 +196,11 @@ export function CubeView() {
return <>
<div className='cube-view-root'>
{cube === null && <div className='header'><h2><LoaderText /></h2></div>}
{cube === undefined && <NotFound cubekey={key} error={error} />}
{cube === undefined && <NotFound cubekey={key} error={searchError} />}
{cube && <>
<div className='header'>
<h2>{cube.key}</h2>
{error && <div className="alert">{error.message}</div>}
{searchError && <div className="alert">{searchError.message}</div>}
{<div>
a {cube.cards?.length ?? cube.print_ids?.length ?? cube.oracle_ids.length} card cube
from{" "}
Expand Down Expand Up @@ -201,10 +241,15 @@ export function CubeView() {
})}
</Multiselect>
{filteredCards && <div>filter matched {filteredCards.length} of {cube.print_ids.length}</div>}
{cards.length === 0 && error === undefined && dbStatus !== "loading" && <LoaderText text="Loading cards"/>}
{cards.length === 0
&& searchError === undefined
&& loadingError === undefined
&& dbStatus !== "loading" &&
<LoaderText text="Loading cards"/>}
{loadingError}
{dbStatus === "loading" && <div className="cube-db-status"><DBStatusLoader /></div>}
</div>
{sorted.length > 0 && error === undefined && <div className='result-container'>
{sorted.length > 0 && searchError === undefined && <div className='result-container'>
<div className="card-image-container">
{sorted.map((card, index) =>
<CardImageView
Expand Down
4 changes: 3 additions & 1 deletion src/ui/data/cardDataView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,9 @@ export const CardDataView = () => {
description="load cards that match this query into memory on page load">
<Input onChange={e => setLoadFilter(e.target.value)} value={loadFilter} language="scryfall" placeholder="No filter will be applied"/>
</FormField>
<button onClick={() => window.location.reload()}>reload page</button>
<button
disabled={dbStatus === 'loading' || memStatus === 'loading'}
onClick={() => window.location.reload()}>reload page</button>
<button
disabled={dbStatus === 'loading' || memStatus === 'loading' || !dbDirty}
onClick={() => saveToDB()}
Expand Down

0 comments on commit 27a49d8

Please sign in to comment.