mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-05 20:56:13 -05:00
* Tables * Clocks * Maplist preference selector * Fix SSR * Nav icon * RankedOrScrim * Map pool * Create group * Redirect logic * Persist map pool * Advance from preparing page * Rename query * Fix merge * Fix migration order * Seed groups * Find looking groups SQL * Renders something * More UI work * Back to 30min * Likes/dislikes * Always return own group * Fix like order * 3 tc/rm/cb -> 2 * Show only 3 weapons * Pass group size * Handle both liked and liked by same group * Fix SQL * Group preference frontend work * Morphing * Styling * Don't show group controls if not manager * Give/remove manager * Leave group * Leave with confirm * Delete likes when morphing groups * Clocks consistency * Remove bad invariant * Persist settings to local storage * Fix initial value flashing * Fix never resolving loading indicator * REFRESH_GROUP * Flip animations * Tweaks * Auto refresh logic * Groups of 4 seed * Reduce throwing * Load full groups initial * Create match * Match UI initial * Score reporter initial * Push footer down on match page * Score reporter knows when set ended * Score reporting untested * Show score after report * Align better * Look again with same group functionality * More migrations * Team on match page * Show confirmer before reporting score * Report weapons * Report weapos again by admin + skill changing * Handle no tiebreaker given to MapPool * Remove unranked * Remove support for "team id skill" * no-wrap -> nowrap * Preparing page work * Use common GroupCard component * Add some metas * MemberAdder in looking page * Fix GroupCard actions * Fix SZ only map list including other modes * Add season info * Prompt login * Joining team * Manage group on preparing page * Manage group on preparing page * Seed past matches * Add to seed * No map list preference when full group + fix expiry * Fix skill matchesCount calculation * Tiers initial work * Some progress on tiers * Tiering logic * MMR in group cards * Name to challenge * Team MMR * Big team rank icons * Adjust todos * Match score report with confirm * Allow regular members to report score * Handle reporting weapons edge cases * Add tier images * Improve GroupCard spacing * Refactor looking page * Looking mobile UI * Calculate skill only for current season * Divide groups visually when reporting weapons * Fix match page weapons sorting * Add cache to user skills+tier calculation * Admin report match score * Initial leaderboard * Cached leaderboard * Weapon category lb's * Populate SkillTeamUser in SendouQ * Team leaderboard filtered down * Add TODOs * Seasons initlal * Season weapons initial * Weapons stylized * Show rest weapons as + * Hide peak if same as current * Load matches SQL initial * Season matches UI initial * Take user id in account * Add weapons * Paginated matches * Fix pages count logic * Scroll top on data change * Day headers for matches * Link from user page to user seasons page * Summarize maps + ui initial * Map stats * Player info tabs * MMR chart * Chart adjustments * Handle basing team MMR on player MMR * Set initial MMR * Add info about discord to match page * Season support to tournaments * Get tournament skills as well for the graph * WIP * New team rating logic + misc other * tiered -> tiered.server * Update season starting time * TODOs * Add rules page * Hide elements correctly when off-season * Fix crash when only one player with skill * How-to video * Fix StartRank showing when not logged in * Make user leaderboard the default * Make Skill season non-nullable * Add suggested pass to match * Add rule * identifierToUserIds helper * Fix tiers not showing
86 lines
2.3 KiB
TypeScript
86 lines
2.3 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];
|
|
}
|
|
|
|
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[] {
|
|
return [...new Set(arr)];
|
|
}
|
|
|
|
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];
|
|
}
|