Created
December 3, 2022 04:17
-
-
Save laukaichung/87335dcae16875d9ac7cfcea7da76c40 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import rfdc from "rfdc"; | |
import { CrudInterface, Database, OmitId, Table } from "brackets-manager"; | |
import create from "zustand"; | |
import { devtools, persist, StateStorage } from "zustand/middleware"; | |
import { | |
Group, | |
Match, | |
MatchGame, | |
Participant, | |
Round, | |
Stage, | |
} from "brackets-model"; | |
import localForage from "localforage"; | |
const zustandStorage: StateStorage = { | |
getItem: async (name: string): Promise<string | null> => { | |
return (await localForage.getItem(name)) || null; | |
}, | |
setItem: async (name: string, value: string): Promise<void> => { | |
await localForage.setItem(name, value); | |
}, | |
removeItem: async (name: string): Promise<void> => { | |
await localForage.removeItem(name); | |
}, | |
}; | |
const clone = rfdc(); | |
class State implements Database { | |
stage: Stage[]; | |
group: Group[]; | |
round: Round[]; | |
match: Match[]; | |
match_game: MatchGame[]; | |
participant: Participant[]; | |
} | |
export const useBracketManagerStorage = create<State>()( | |
devtools( | |
persist( | |
(set, getState) => { | |
const state = getState(); | |
return { | |
stage: state?.stage || [], | |
group: state?.group || [], | |
round: state?.round || [], | |
match: state?.match || [], | |
match_game: state?.match_game || [], | |
participant: state?.participant || [], | |
}; | |
}, | |
{ | |
name: "tournament", | |
getStorage: () => zustandStorage, | |
} | |
) | |
) | |
); | |
export class ZustandDatabase implements CrudInterface { | |
/** | |
* @param data "import" data from external | |
*/ | |
setData(data: Database): void { | |
useBracketManagerStorage.setState(data); | |
} | |
/** | |
* @param partial Filter | |
*/ | |
makeFilter(partial: any): (entry: any) => boolean { | |
return (entry: any): boolean => { | |
let result = true; | |
for (const key of Object.keys(partial)) | |
result = result && entry[key] === partial[key]; | |
return result; | |
}; | |
} | |
/** | |
* Clearing all of the data | |
*/ | |
reset(): void { | |
useBracketManagerStorage.setState({ | |
participant: [], | |
stage: [], | |
group: [], | |
round: [], | |
match: [], | |
match_game: [], | |
}); | |
} | |
insert<T>(table: Table, value: OmitId<T>): Promise<number>; | |
/** | |
* Inserts multiple values in the database. | |
* | |
* @param table Where to insert. | |
* @param values What to insert. | |
*/ | |
insert<T>(table: Table, values: OmitId<T>[]): Promise<boolean>; | |
/** | |
* Implementation of insert | |
* | |
* @param table Where to insert. | |
* @param values What to insert. | |
*/ | |
insert<T>( | |
table: Table, | |
values: OmitId<T> | OmitId<T>[] | |
): Promise<number> | Promise<boolean> { | |
const data = useBracketManagerStorage.getState(); | |
let id = | |
data[table].length > 0 | |
? Math.max(...data[table].map((d) => d.id)) + 1 | |
: 0; | |
if (!Array.isArray(values)) { | |
try { | |
// @ts-ignore | |
data[table].push({ id, ...values }); | |
setState(data, table); | |
} catch (error) { | |
return new Promise<number>((resolve) => { | |
resolve(-1); | |
}); | |
} | |
return new Promise<number>((resolve) => { | |
resolve(id); | |
}); | |
} | |
try { | |
values.map((object) => { | |
// @ts-ignore | |
data[table].push({ id: id++, ...object }); | |
}); | |
setState(data, table); | |
} catch (error) { | |
return new Promise<boolean>((resolve) => { | |
resolve(false); | |
}); | |
} | |
return new Promise<boolean>((resolve) => { | |
resolve(true); | |
}); | |
} | |
/** | |
* Gets all data from a table in the database. | |
* | |
* @param table Where to get from. | |
*/ | |
select<T>(table: Table): Promise<T[] | null>; | |
/** | |
* Gets specific data from a table in the database. | |
* | |
* @param table Where to get from. | |
* @param id What to get. | |
*/ | |
select<T>(table: Table, id: number): Promise<T | null>; | |
/** | |
* Gets data from a table in the database with a filter. | |
* | |
* @param table Where to get from. | |
* @param filter An object to filter data. | |
*/ | |
select<T>(table: Table, filter: Partial<T>): Promise<T[] | null>; | |
/** | |
* @param table Where to get from. | |
* @param arg Arg. | |
*/ | |
select<T>(table: Table, arg?: number | Partial<T>): Promise<T[] | null> { | |
const data = useBracketManagerStorage.getState(); | |
try { | |
if (arg === undefined) { | |
return new Promise<T[]>((resolve) => { | |
// @ts-ignore | |
resolve(data[table].map(clone)); | |
}); | |
} | |
if (typeof arg === "number") { | |
return new Promise<T[]>((resolve) => { | |
console.log(table, data[table]); | |
// @ts-ignore | |
resolve(clone(data[table].find((d) => d.id === arg))); | |
}); | |
} | |
return new Promise<T[] | null>((resolve) => { | |
// @ts-ignore | |
resolve(data[table].filter(this.makeFilter(arg)).map(clone)); | |
}); | |
} catch (error) { | |
return new Promise<null>((resolve) => { | |
resolve(null); | |
}); | |
} | |
} | |
/** | |
* Updates data in a table. | |
* | |
* @param table Where to update. | |
* @param id What to update. | |
* @param value How to update. | |
*/ | |
update<T>(table: Table, id: number, value: T): Promise<boolean>; | |
/** | |
* Updates data in a table. | |
* | |
* @param table Where to update. | |
* @param filter An object to filter data. | |
* @param value How to update. | |
*/ | |
update<T>( | |
table: Table, | |
filter: Partial<T>, | |
value: Partial<T> | |
): Promise<boolean>; | |
/** | |
* Updates data in a table. | |
* | |
* @param table Where to update. | |
* @param arg | |
* @param value How to update. | |
*/ | |
update<T>( | |
table: Table, | |
arg: number | Partial<T>, | |
value?: Partial<T> | |
): Promise<boolean> { | |
const data = useBracketManagerStorage.getState(); | |
if (typeof arg === "number") { | |
try { | |
// @ts-ignore | |
data[table][arg] = value; | |
setState(data, table); | |
return new Promise<boolean>((resolve) => { | |
resolve(true); | |
}); | |
} catch (error) { | |
return new Promise<boolean>((resolve) => { | |
resolve(false); | |
}); | |
} | |
} | |
// @ts-ignore | |
const values = data[table].filter(this.makeFilter(arg)); | |
if (!values) { | |
return new Promise<boolean>((resolve) => { | |
resolve(false); | |
}); | |
} | |
values.forEach((v: { id: any }) => { | |
const existing = data[table][v.id]; | |
for (const key in value) { | |
if ( | |
// @ts-ignore | |
existing[key] && | |
// @ts-ignore | |
typeof existing[key] === "object" && | |
typeof value[key] === "object" | |
) { | |
// @ts-ignore | |
Object.assign(existing[key], value[key]); // For opponent objects, this does a deep merge of level 2. | |
} else { | |
// @ts-ignore | |
existing[key] = value[key]; // Otherwise, do a simple value assignment. | |
} | |
} | |
data[table][v.id] = existing; | |
setState(data, table); | |
}); | |
return new Promise<boolean>((resolve) => { | |
resolve(true); | |
}); | |
} | |
/** | |
* Empties a table completely. | |
* | |
* @param table Where to delete everything. | |
*/ | |
delete(table: Table): Promise<boolean>; | |
/** | |
* Delete data in a table, based on a filter. | |
* | |
* @param table Where to delete in. | |
* @param filter An object to filter data. | |
*/ | |
delete<T>(table: Table, filter: Partial<T>): Promise<boolean>; | |
/** | |
* Delete data in a table, based on a filter. | |
* | |
* @param table Where to delete in. | |
* @param filter An object to filter data. | |
*/ | |
delete<T>(table: Table, filter?: Partial<T>): Promise<boolean> { | |
const data = useBracketManagerStorage.getState(); | |
const values = data[table]; | |
if (!values) { | |
return new Promise<boolean>((resolve) => { | |
resolve(false); | |
}); | |
} | |
if (!filter) { | |
useBracketManagerStorage.setState({ | |
[table]: [], | |
}); | |
return new Promise<boolean>((resolve) => { | |
resolve(true); | |
}); | |
} | |
const predicate = this.makeFilter(filter); | |
const negativeFilter = (value: any): boolean => !predicate(value); | |
// @ts-ignore | |
data[table] = values.filter(negativeFilter); | |
setState(data, table); | |
return new Promise<boolean>((resolve) => { | |
resolve(true); | |
}); | |
} | |
} | |
function setState(data: State, table: Table) { | |
useBracketManagerStorage.setState({ | |
[table]: [...data[table]], | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment