mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-05 20:56:13 -05:00
* Renders groups * Bracket data refactoring * Starting bracket working (first bracket only) * TODOs + crash fix * Source bracket logic initial * Bracket progression (DE underground bracket) * Preview working for second bracket * Bracket nav initial * Check-in to bracket feature * Start Underground bracket * Team/teams pages tweaks to support underground bracket * Underground bracket finalization progress * Tournament class * id -> userId + more useOutletContext removed * Bracket loader refactored out * Migrate admin to useTournament * Bracket.settings * Slim tournament loader * Fix useEffect infinite loop * Adjust waiting for teams text * Refactor old tournament DB call from to admin * Admin action: check in/out from specific bracket * Standings work * Back button from match page -> correct bracket * Standings logic for DE grand finals * Standings + finalize bracket * Dev log * Unit tests utils etc. * Adjust TODOs * Fix round robin issues * Add RR tests * Round robin standings initial * Wins against tied + points tiebreaker progress * Fix losing state when switching between tabs * Add check-in indications to seeding page * Link to user page on seed tool * Submit points * Total points from bracket manager * findById gonezino * Ahead of time check-in * Couple todos * Reopen logic refactor * Tournament format settings * RR->SE placements, skipping underground bracket * Fix tournament team page round names * More teams to UG bracket if first round of DE only byes * Fix graphics bug * Fixes * Fix some E2E tests * Fix E2E tests
94 lines
2.5 KiB
TypeScript
94 lines
2.5 KiB
TypeScript
// TODO: when more examples of permissions profile difference between
|
|
// this implementation and one that takes arrays
|
|
|
|
import clone from "just-clone";
|
|
import shuffle from "just-shuffle";
|
|
import invariant from "tiny-invariant";
|
|
|
|
// (not all arrays need to necessarily run but they need to be defined)
|
|
export function allTruthy(arr: unknown[]) {
|
|
return arr.every(Boolean);
|
|
}
|
|
|
|
/** Mimics Array.prototype.at except throws an error if index out of bounds */
|
|
export function atOrError<T>(arr: T[], n: number) {
|
|
const result = at(arr, n);
|
|
if (result === undefined) {
|
|
throw new Error(`Index ${n} out of bounds. Array length is ${arr.length}`);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// https://github.com/tc39/proposal-relative-indexing-method#polyfill
|
|
/** Array.at polyfill */
|
|
function at<T>(arr: T[], n: number) {
|
|
// ToInteger() abstract op
|
|
n = Math.trunc(n) || 0;
|
|
// Allow negative indexing from the end
|
|
if (n < 0) n += arr.length;
|
|
// OOB access is guaranteed to return undefined
|
|
if (n < 0 || n >= arr.length) return undefined;
|
|
// Otherwise, this is just normal property access
|
|
return arr[n];
|
|
}
|
|
|
|
// TODO: i18n (at least for SendouQ)
|
|
export function joinListToNaturalString(arg: string[], lastSeparator = "and") {
|
|
if (arg.length === 1) return arg[0];
|
|
|
|
const list = [...arg];
|
|
const last = list.pop();
|
|
const commaJoined = list.join(", ");
|
|
|
|
return last ? `${commaJoined} ${lastSeparator} ${last}` : commaJoined;
|
|
}
|
|
|
|
export function normalizeFormFieldArray(
|
|
value: undefined | null | string | string[],
|
|
): string[] {
|
|
return value == null ? [] : typeof value === "string" ? [value] : value;
|
|
}
|
|
|
|
/** Can be used as a strongly typed array filter */
|
|
export function isDefined<T>(value: T | undefined | null): value is T {
|
|
return value !== null && value !== undefined;
|
|
}
|
|
|
|
export function removeDuplicates<T>(arr: T[]): T[] {
|
|
const seen = new Set<T>();
|
|
|
|
return arr.filter((item) => {
|
|
if (seen.has(item)) return false;
|
|
seen.add(item);
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
export function removeDuplicatesByProperty<T>(
|
|
arr: T[],
|
|
getter: (arg0: T) => number | string,
|
|
): T[] {
|
|
const seen = new Set();
|
|
return arr.filter((item) => {
|
|
const id = getter(item);
|
|
|
|
if (seen.has(id)) return false;
|
|
seen.add(id);
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
export function nullFilledArray(size: number) {
|
|
return new Array(size).fill(null);
|
|
}
|
|
|
|
export function pickRandomItem<T>(array: T[]): T {
|
|
invariant(array.length > 0, "Can't pick from empty array");
|
|
|
|
const shuffled = shuffle(clone(array));
|
|
|
|
return shuffled[0];
|
|
}
|