grungnet/src/contexts/TokenDataContext.tsx
Mitchell McCaffrey 029a992381 Typescript
2021-07-17 18:39:49 +10:00

184 lines
5.0 KiB
TypeScript

import React, {
useEffect,
useState,
useContext,
useCallback,
useMemo,
} from "react";
import { useLiveQuery } from "dexie-react-hooks";
import { useDatabase } from "./DatabaseContext";
import { removeGroupsItems } from "../helpers/group";
import { Token } from "../types/Token";
import { Group } from "../types/Group";
export type AddTokenEventHandler = (token: Token) => Promise<void>;
export type RemoveTokensEventHandler = (ids: string[]) => Promise<void>;
export type UpdateTokenEventHandler = (
id: string,
update: Partial<Token>
) => Promise<void>;
export type GetTokenEventHandler = (
tokenId: string
) => Promise<Token | undefined>;
export type UpdateTokenGroupsEventHandler = (groups: Group[]) => Promise<void>;
export type UpdateTokensHiddenEventHandler = (
ids: string[],
hideInSidebar: boolean
) => Promise<void>;
type TokenDataContextValue = {
tokens: Token[];
addToken: AddTokenEventHandler;
tokenGroups: Group[];
removeTokens: RemoveTokensEventHandler;
updateToken: UpdateTokenEventHandler;
getToken: GetTokenEventHandler;
tokensById: Record<string, Token>;
tokensLoading: boolean;
updateTokenGroups: UpdateTokenGroupsEventHandler;
updateTokensHidden: UpdateTokensHiddenEventHandler;
};
const TokenDataContext =
React.createContext<TokenDataContextValue | undefined>(undefined);
export function TokenDataProvider({ children }: { children: React.ReactNode }) {
const { database } = useDatabase();
const tokensQuery = useLiveQuery<Token[]>(
() => database?.table("tokens").toArray() || [],
[database]
);
const tokens = useMemo(() => tokensQuery || [], [tokensQuery]);
const tokensLoading = useMemo(() => !tokensQuery, [tokensQuery]);
const tokenGroupQuery = useLiveQuery(
() => database?.table("groups").get("tokens"),
[database]
);
const [tokenGroups, setTokenGroups] = useState<Group[]>([]);
useEffect(() => {
async function updateTokenGroups() {
const group = await database?.table("groups").get("tokens");
setTokenGroups(group.items);
}
if (database && tokenGroupQuery) {
updateTokenGroups();
}
}, [tokenGroupQuery, database]);
const getToken = useCallback<GetTokenEventHandler>(
async (tokenId) => {
let token = await database?.table("tokens").get(tokenId);
return token;
},
[database]
);
// Add token and add it to the token group
const addToken = useCallback<AddTokenEventHandler>(
async (token) => {
if (database) {
await database.table("tokens").add(token);
const group = await database.table("groups").get("tokens");
await database.table("groups").update("tokens", {
items: [{ id: token.id, type: "item" }, ...group.items],
});
}
},
[database]
);
const removeTokens = useCallback<RemoveTokensEventHandler>(
async (ids) => {
if (database) {
const tokens = await database.table("tokens").bulkGet(ids);
let assetIds = [];
for (let token of tokens) {
if (token.type === "file") {
assetIds.push(token.file);
assetIds.push(token.thumbnail);
}
}
const group = await database.table("groups").get("tokens");
let items = removeGroupsItems(group.items, ids);
await database.table("groups").update("tokens", { items });
await database.table("tokens").bulkDelete(ids);
await database.table("assets").bulkDelete(assetIds);
}
},
[database]
);
const updateToken = useCallback<UpdateTokenEventHandler>(
async (id, update) => {
await database?.table("tokens").update(id, update);
},
[database]
);
const updateTokensHidden = useCallback<UpdateTokensHiddenEventHandler>(
async (ids: string[], hideInSidebar: boolean) => {
await Promise.all(
ids.map((id) => database?.table("tokens").update(id, { hideInSidebar }))
);
},
[database]
);
const updateTokenGroups = useCallback<UpdateTokenGroupsEventHandler>(
async (groups: Group[]) => {
// Update group state immediately to avoid animation delay
setTokenGroups(groups);
await database?.table("groups").update("tokens", { items: groups });
},
[database]
);
const [tokensById, setTokensById] = useState({});
useEffect(() => {
setTokensById(
tokens.reduce((obj: Record<string, Token>, token: Token) => {
obj[token.id] = token;
return obj;
}, {})
);
}, [tokens]);
const value = {
tokens,
addToken,
tokenGroups,
removeTokens,
updateToken,
tokensById,
tokensLoading,
getToken,
updateTokenGroups,
updateTokensHidden,
};
return (
<TokenDataContext.Provider value={value}>
{children}
</TokenDataContext.Provider>
);
}
export function useTokenData() {
const context = useContext(TokenDataContext);
if (context === undefined) {
throw new Error("useTokenData must be used within a TokenDataProvider");
}
return context;
}
export default TokenDataContext;