Skip to content

Commit

Permalink
optimized cubeView data loading and migrated to React 18 in anticipat…
Browse files Browse the repository at this point in the history
…ion of fileExplorer drag-n-drop
  • Loading branch information
dsoskey committed May 18, 2024
1 parent 8f5af66 commit 266f872
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 103 deletions.
184 changes: 129 additions & 55 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
"@types/lodash": "^4.14.175",
"@types/nearley": "^2.11.2",
"@types/prismjs": "^1.26.0",
"@types/react": "^17.0.27",
"@types/react-dom": "^17.0.10",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.3.0",
"@types/react-router": "^5.1.17",
"@types/react-router-dom": "^5.3.1",
"@types/uuid": "^9.0.6",
Expand Down Expand Up @@ -77,10 +77,12 @@
"nearley": "^2.20.1",
"neverthrow": "^6.0.0",
"prismjs": "^1.29.0",
"react": "^17.0.2",
"react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1",
"react-colorful": "^5.6.1",
"react-dom": "^17.0.2",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.3.1",
"react-markdown": "^8.0.7",
"react-router-dom": "^6.16.0",
"remark": "^15.0.1",
Expand Down
6 changes: 1 addition & 5 deletions src/api/local/syntaxHighlighting.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import React from 'react'
import Prism, { Environment, Grammar } from 'prismjs'
import 'prismjs/components/prism-regex.js'
import { FILTER_KEYWORDS, KEYWORDS_TO_IMPLEMENT, OPERATORS } from 'mtgql'
import { FILTER_KEYWORDS, OPERATORS } from 'mtgql'
import { extensionDocs, syntaxDocs } from './syntaxDocs'
import { Router as RemixRouter } from '@remix-run/router'

export type Language = 'regex' | 'scryfall' | 'scryfall-extended' | 'scryfall-extended-multi'
const keywordRegex = Object.values(FILTER_KEYWORDS).join('|')

console.debug(`local supports ${Object.values(FILTER_KEYWORDS).length} keywords`)
console.debug(
`${Object.values(KEYWORDS_TO_IMPLEMENT).length} keywords to add to local syntax`
)
const operators = Object.values(OPERATORS).join('|')

const scryfallRegex = {
Expand Down
47 changes: 42 additions & 5 deletions src/api/local/useCogDB.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { createContext, SetStateAction, useEffect, useRef, useState } from 'react'
import { createContext, SetStateAction, useRef, useState } from 'react'
import { Setter, TaskStatus } from '../../types'
import { cogDB, isScryfallManifest, Manifest } from './db'
import { migrateCubes, putFile } from './populate'
import { NormedCard, isOracleVal } from 'mtgql'
import { QueryReport, useReporter } from '../useReporter'
import { isFunction } from 'lodash'
import _isFunction from 'lodash/isFunction'
import { useLocalStorage } from './useLocalStorage'
import { defaultPromise } from '../context'

export const DB_LOAD_MESSAGES = [
"loading cubes",
Expand Down Expand Up @@ -35,6 +36,8 @@ export interface CogDB {
memStatus: TaskStatus
dbReport: QueryReport
memory: NormedCard[]
cardByOracle: (id: string) => NormedCard | undefined
bulkCardByOracle: (oracleIds: string[]) => Promise<NormedCard[]>
setMemory: Setter<NormedCard[]>
loadFilter: string
setLoadFilter: Setter<string>
Expand All @@ -51,6 +54,11 @@ const defaultDB: CogDB = {
dbStatus: 'unstarted',
memStatus: 'unstarted',
memory: [],
cardByOracle: () => {
console.error("CogDB.cardByOracle called without a provider!")
return undefined
},
bulkCardByOracle: defaultPromise("CogDB.bulkCardByOracle"),
dbReport: null,
setMemory: () => console.error("CogDB.setMemory called without a provider!"),
cardByName: () => {
Expand Down Expand Up @@ -104,7 +112,7 @@ export const useCogDB = (): CogDB => {
}
}
const setMemory = (setto: SetStateAction<NormedCard[]>) => {
const res = isFunction(setto) ? setto(memory) : setto
const res = _isFunction(setto) ? setto(memory) : setto
rawSetMemory(res)
resetIndex()
res.forEach(addCardToIndex)
Expand Down Expand Up @@ -243,6 +251,7 @@ export const useCogDB = (): CogDB => {
}
})
} else {
setDbStatus("success")
console.debug("posting start message to worker")
worker.onmessage = handleLoadDB
worker.postMessage({ type: 'load', data: { filter: loadFilter } })
Expand Down Expand Up @@ -282,21 +291,49 @@ export const useCogDB = (): CogDB => {
worker.postMessage({ type: 'init', data: { bulkType: manifest.type, targets, filter } })
}

useEffect(() => { resetDB() }, [])

const cardByName = (name: string, fuzzy: boolean = false): NormedCard | undefined => {
// todo: add fuzzing
const fuzzed = fuzzy ? name : name
return oracleToCard.current[nameToOracle.current[fuzzed]]
}

const cardByOracle = (oracleId: string): NormedCard | undefined => {
return oracleToCard.current[oracleId];
}

// this looks pretty generalizable ngl
const bulkCardByOracle = async (oracleIds: string[]) => {
const memOracles = oracleIds.map(cardByOracle)
const missingMemoryIndices = memOracles
.map((card, index) => card === undefined ? index : -1)
.filter(index => index !== -1)

if (missingMemoryIndices.length) {
const oraclesToCheckDB = missingMemoryIndices.map(index => oracleIds[index]);
const newOracles = (await cogDB.card.bulkGet(oraclesToCheckDB)) ?? [];
const missingIndexes = newOracles
.map((card, index) => card === undefined ? index : -1)
.filter(index => index !== -1)
if (missingIndexes.length) return Promise.reject(missingIndexes);

for (let i = 0; i < missingMemoryIndices.length; i++){
const index = missingMemoryIndices[i]
memOracles[index] = newOracles[i]
}
}

return memOracles
}

return {
dbStatus,
saveToDB,
memStatus,
manifest,
setManifest,
memory,
cardByOracle,
bulkCardByOracle,
setMemory,
resetDB,
loadManifest,
Expand Down
3 changes: 3 additions & 0 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export const App = () => {
const dismissMessage = (messageId: string) => {
setMessages(prev => prev.filter(it => it.id !== messageId))
}
useEffect(() => {
cogDB.resetDB()
}, [])

return (
<CogDBContext.Provider value={cogDB}>
Expand Down
14 changes: 6 additions & 8 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react'
import { render } from 'react-dom'
import { App } from './app'
import * as Scry from 'scryfall-sdk'
import { App } from './app'
import Prism from 'prismjs'
import {
hookReactDOM,
Expand All @@ -17,6 +16,7 @@ import 'mana-font/css/mana.min.css'
import { FlagContextProvider } from './ui/flags'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { loadTheme } from './ui/component/theme'
import { createRoot } from 'react-dom/client'

const router = createBrowserRouter([
{ path: "*", Component: App }
Expand All @@ -33,9 +33,7 @@ Scry.setTimeout(50)

loadTheme();

render(
<FlagContextProvider>
<RouterProvider router={router} />
</FlagContextProvider>,
document.getElementById('app')
)
const root = createRoot(document.getElementById('app'));
root.render(<FlagContextProvider>
<RouterProvider router={router} />
</FlagContextProvider>)
5 changes: 1 addition & 4 deletions src/ui/component/masthead.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CoglibIcon } from './coglibIcon'
import { Link } from 'react-router-dom'
import React, { useContext, useEffect } from 'react'
import { FlagContext } from '../flags'
import { useLocation, useParams } from 'react-router'
import { useLocation } from 'react-router'
import { CogDBContext } from '../../api/local/useCogDB'
import { Dropdown } from './dropdown'
import _cloneDeep from 'lodash/cloneDeep'
Expand All @@ -22,13 +22,10 @@ export const Masthead = () => {
const { adminMode } = useContext(FlagContext).flags
const { pathname } = useLocation()
const topPath = pathname.replace("/","").split("/")[0]
const params = useParams()
const [lastVistedCubes, setLastVisitedCubes] = useLocalStorage<string[]>("recent-cubes.coglib.sosk.watch",[]);
useEffect(() => {
if (pathname.startsWith("/data/cube/")) {
const cubeId = pathname.split("/").pop();
console.log(pathname)
console.log(params);
setLastVisitedCubes(prev=> {
const next = _cloneDeep(prev);
const prevIndex = next.findIndex(it => it === cubeId);
Expand Down
42 changes: 20 additions & 22 deletions src/ui/cubeView.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useContext, useEffect, useMemo, useState } from 'react'
import { useParams } from 'react-router'
import { useLiveQuery } from 'dexie-react-hooks'
import _sortBy from 'lodash/sortBy'
import _groupBy from 'lodash/groupBy'
import { Card, NormedCard, QueryRunner, SearchError, SortFunctions } from 'mtgql'
import { cogDB as cogDBClient, Manifest, toManifest } from '../api/local/db'
import { cogDB as cogDBClient, Manifest } from '../api/local/db'
import { CardImageView } from './cardBrowser/cardViews/cardImageView'
import { groupBy, sortBy } from 'lodash'
import './cubeView.css'
import { Input } from './component/input'
import { CUBE_SOURCE_TO_LABEL, cubeLink } from './component/cube/sourceIcon'
Expand Down Expand Up @@ -75,7 +76,7 @@ function LoadingError({ cardCount, refreshCubeCards }) {
}
export function CubeView() {
const { key } = useParams();
const { dbStatus } = useContext(CogDBContext);
const { dbStatus, bulkCardByOracle } = useContext(CogDBContext);
const viewport = useViewportListener();
const [searchError, setSearchError] = useState<SearchError | undefined>()
const [loadingError, setLoadingError] = useState<React.ReactNode>(undefined);
Expand All @@ -92,7 +93,7 @@ export function CubeView() {
const [cardsPerRow, setCardsPerRow] = useLocalStorage('cards-per-row', 4)

const sorted: OrderedCard[] = useMemo(() => {
return sortBy(
return _sortBy(
filteredCards ?? cards,
ordering.map(it => orderValToKey[it]??it),
) as OrderedCard[]
Expand All @@ -109,22 +110,13 @@ export function CubeView() {
};
const clearFilter = () => setFilteredCards(undefined);

const refreshCubeCards = async() => {
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);
const oracleIds = cube.cards?.map(it=>it.oracle_id) ?? cube.oracle_ids;
const newOracles = await bulkCardByOracle(oracleIds);
const byOracle = _groupBy(newOracles, "oracle_id");
setOracles(byOracle);
if (needsMigration) {
const cards = newOracles.map(it => ({ oracle_id: it.oracle_id, print_id: it.printings[0].id }));
await cogDBClient.cube.put({
Expand Down Expand Up @@ -161,10 +153,16 @@ export function CubeView() {
}
setLoadingError(undefined)
} catch (e) {
console.error(e)
setLoadingError(<div className="alert">
{e.message}
</div>)
if (Array.isArray(e)) {
setLoadingError(<LoadingError
cardCount={e.length}
refreshCubeCards={refreshCubeCards}
/>)
} else {
setLoadingError(<div className="alert">
{e.message}
</div>)
}
}
};

Expand Down

0 comments on commit 266f872

Please sign in to comment.