Skip to content

Commit

Permalink
fixup! [ci skip] WIP WIP WIP WIP WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
shuji-koike committed Jan 23, 2022
1 parent 41e456b commit 6e74564
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 79 deletions.
4 changes: 2 additions & 2 deletions packages/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ const routes = (
</Routes>
)

interface AppState {
export interface AppState extends Record<string, unknown> {
match?: Match | null
setMatch: (match: Match | null | undefined) => void
setMatch: React.Dispatch<React.SetStateAction<AppState["match"]>>
}

export const AppContext = React.createContext<AppState>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import React from "react"

import { useAuth } from "../hooks"

export const AuthButton: React.VFC<{
export const AuthAvatar: React.VFC<{
diameter?: number
}> = ({ diameter = 32 }) => {
const user = useAuth()
Expand Down
4 changes: 2 additions & 2 deletions packages/app/components/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import React from "react"
import ReactDOM from "react-dom"
import { useLocation, NavLink, Link } from "react-router-dom"

import { AuthButton } from "./auth"
import { AuthAvatar } from "./AuthAvatar"

interface LayoutState {
hideHeader: boolean
Expand Down Expand Up @@ -72,7 +72,7 @@ export const Layout: React.VFC<{
alignItems="center"
margin="0 32px"
/>
<AuthButton />
<AuthAvatar />
</Toolbar>
</AppBar>
<Drawer
Expand Down
36 changes: 36 additions & 0 deletions packages/app/demo/DemoFilePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from "react"

import { openDemo, isValidFile } from "./io"

export const DemoFilePicker: React.VFC<{
setMatch?: (match: Match | null) => void
onLoad?: (match: Match, name: string) => void
}> = ({ setMatch, onLoad }) => {
const [output, setOutput] = React.useState<string[]>([])
const [files, setFiles] = React.useState<File[]>()
React.useEffect(() => {
const file = files && files[0]
if (!file) return
if (!isValidFile(file)) return console.error("invalid file")
openDemo(file, setOutput, setMatch).then((match) => {
setMatch?.(match)
if (match) onLoad?.(match, file.name)
})
}, [files])
return (
<>
<input
type="file"
accept=".dem,.json,.gz"
disabled={!!files?.length}
onChange={(e) => setFiles([...(e.currentTarget.files || [])])}
/>
{output.length > 0 && (
<pre>
<p>Converting DEM file `{files?.[0]?.name}`</p>
{output}
</pre>
)}
</>
)
}
39 changes: 0 additions & 39 deletions packages/app/demo/FilePicker.tsx

This file was deleted.

46 changes: 20 additions & 26 deletions packages/app/demo/io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ export async function openDemo(
onOutput?: (arr: string[]) => void,
onRoundEnd?: (match: Match) => void
): Promise<Match | null> {
if (!file) return null
if (typeof file === "string") {
if (/^(public|private|sandbox)/.test(file)) {
return await storageFetch(file)
}
return fetch(file).then(parseJson)
if (/^(public|private|sandbox)/.test(file))
return getDownloadURL(ref(getStorage(), file)).then(fetch).then(parseJson)
return parseJson(await fetch(file))
}
if (file instanceof Response) return parseJson(file)
if (file instanceof File && file.name.endsWith(".json"))
Expand All @@ -31,8 +31,9 @@ export async function openDemo(
return parseJson(file)
if (file instanceof File && file.name.endsWith(".dem"))
return parseDemo(file, onOutput, onRoundEnd)
if (file) console.warn("openDemo", "unsupported file type!")
return null
if (file instanceof File) throw new Error("unsupported file type!")
const never: never = file
throw new Error(never)
}

async function parseJson(
Expand All @@ -51,8 +52,7 @@ async function parseJson(
if (data.name.endsWith(".gz")) return data.arrayBuffer().then(parseJson)
else return data.text().then(parseJson)
const never: never = data
console.error(never)
throw new Error()
throw new Error(never)
}

export function parseDemo(
Expand Down Expand Up @@ -89,31 +89,29 @@ export async function storagePut(
path: string,
data: string | Match,
compress = true
): Promise<void> {
): Promise<string> {
const json = typeof data === "string" ? data : JSON.stringify(data)
if (compress || /\.gz$/.test(path)) {
if (compress || /\.gz$/i.test(path)) {
await uploadBytes(ref(getStorage(), path), await gzip(json))
} else {
await uploadString(ref(getStorage(), path), json)
}
return path
}

export async function storagePutPublicMatch(match: Match, file: File | string) {
await storagePut(
`public/${new Date().getTime()}-${toName(file)}.json.gz`,
match
)
export async function storagePutPublicMatch(
match: Match,
file: File | string
): Promise<string> {
const path = `public/${new Date().getTime()}-${toName(file)}.json.gz`
return await storagePut(path, match)
}

function toName(file: File | string) {
return encodeURIComponent(typeof file === "string" ? file : file.name)
}

export function storageFetch(path: string): Promise<Match | null> {
return getDownloadURL(ref(getStorage(), path)).then(fetch).then(parseJson)
}

export function fileTypeFilter(file: unknown): boolean {
export function isValidFile(file: unknown): file is File {
if (file instanceof File) return /\.(dem|json)(\.gz)?$/i.test(file.name)
return false
}
Expand All @@ -125,14 +123,10 @@ export function setStorage(match: Match | null): Match | null {

export async function gzip(input: string): Promise<Uint8Array> {
await initGzip(await fetch(gzipWasm))
const output = compressStringGzip(input)
if (!output) throw new Error()
return output
return compressStringGzip(input) ?? Promise.reject()
}

export async function gunzip(input: Uint8Array): Promise<Uint8Array> {
await initGzip(await fetch(gzipWasm))
const output = decompressGzip(input)
if (!output) throw new Error()
return output
return decompressGzip(input) ?? Promise.reject()
}
11 changes: 11 additions & 0 deletions packages/app/hooks/useLocationState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useLocation } from "react-router"

export function useLocationState<T extends Record<string, unknown>>(
validate?: (x: unknown) => x is T
): T {
const { state } = useLocation()
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
if (!validate) return (state ?? {}) as T
if (validate?.(state)) return state
throw new Error()
}
4 changes: 3 additions & 1 deletion packages/app/pages/DemoList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ const DemoItem: React.VFC<{
nodeId={nodeId}
icon={
<FontAwesomeIcon
icon={/\.dem(\.json)?(\.gz)?$/.test(file) ? faFileImage : faFileExcel}
icon={
/\.dem(\.json)?(\.gz)?$/i.test(file) ? faFileImage : faFileExcel
}
/>
}
onClick={() => navigate(`/dem/${file}`)}
Expand Down
13 changes: 9 additions & 4 deletions packages/app/pages/DemoPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@ import React from "react"
import { useParams } from "react-router-dom"

import sample from "../../../static/sample.dem.json?url"
import { AppContext } from "../app"
import { AppContext, AppState } from "../app"
import { MatchView } from "../demo/MatchView"
import { openDemo } from "../demo/io"
import { useLocationState } from "../hooks/useLocationState"

export const DemoPage: React.VFC<{ path?: string }> = ({ path }) => {
const state = useLocationState<AppState>()
const { "*": paramPath } = useParams<"*">()
const { match, setMatch } = React.useContext(AppContext)
React.useEffect(() => {
openDemo(paramPath === "sample" ? sample : path || paramPath).then(setMatch)
}, [path])
return <MatchView match={match} />
if (!state.match)
Promise.resolve(paramPath === "sample" ? sample : path || paramPath)
.then(openDemo)
.then(setMatch)
}, [path, paramPath])
return <MatchView match={state.match ?? match} />
}
20 changes: 16 additions & 4 deletions packages/app/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@ import { Alert } from "@mui/lab"
import { getAnalytics, logEvent } from "firebase/analytics"
import React from "react"
import { isChrome } from "react-device-detect"
import { useNavigate } from "react-router"
import { useLocation, useNavigate } from "react-router"

import { AppContext } from "../app"
import { FilePicker } from "../demo/FilePicker"
import { DemoFilePicker } from "../demo/DemoFilePicker"
import { MatchView } from "../demo/MatchView"
import { storagePutPublicMatch } from "../demo/io"

export const Home: React.VFC = () => {
const location = useLocation()
const navigate = useNavigate()
const { match } = React.useContext(AppContext)
const { match, setMatch } = React.useContext(AppContext)
React.useEffect(() => {
if (match) logEvent(getAnalytics(), "view_item")
}, [match])
React.useEffect(() => {
if (match) setMatch(undefined)
}, [location.pathname])
return match ? (
<MatchView match={match} />
) : (
Expand All @@ -23,7 +27,15 @@ export const Home: React.VFC = () => {
<Alert color="warning">Only Google Chrome is supported!</Alert>
)}
<p>Click the button below and select a DEM file.</p>
<FilePicker onLoad={storagePutPublicMatch} />
<DemoFilePicker
setMatch={setMatch}
onLoad={async (match, name) => {
const path = /\.dem$/i.test(name)
? await storagePutPublicMatch(match, name)
: name
navigate(`/dem/${path}`, { state: { match } })
}}
/>
{import.meta.env.DEV && (
<nav className="debug">
<button onClick={() => navigate("/dem/sample")}>
Expand Down

0 comments on commit 6e74564

Please sign in to comment.