diff --git a/app/components/BuildCard.tsx b/app/components/BuildCard.tsx index 47fc71730..1ddd38ea3 100644 --- a/app/components/BuildCard.tsx +++ b/app/components/BuildCard.tsx @@ -210,7 +210,10 @@ export function BuildCard({ build, owner, canEdit = false }: BuildProps) { {data.builds.length < BUILD.MAX_COUNT ? ( } > {t("addBuild")} ) : ( - <> - {t("reachBuildMaxCount")} - - + + + {t("addBuild")} + + } + triggerClassName="tiny" + > + {t("reachBuildMaxCount")} + )} )} @@ -169,3 +206,146 @@ function BuildsFilters({ ); } + +const MISSING_SORT_VALUE = "null"; +function ChangeSortingDialog({ close }: { close: () => void }) { + const data = useLoaderData(); + const [buildSorting, setBuildSorting] = React.useState< + ReadonlyArray + >(() => { + if (!data.buildSorting) return [...DEFAULT_BUILD_SORT, null]; + if (data.buildSorting.length === BUILD_SORT_IDENTIFIERS.length) + return data.buildSorting; + + return [...data.buildSorting, null]; + }); + const { t } = useTranslation(["common", "user"]); + const fetcher = useFetcher(); + + React.useEffect(() => { + if (fetcher.state !== "loading") return; + + close(); + }, [fetcher.state, close]); + + const canAddMoreSorting = buildSorting.length < BUILD_SORT_IDENTIFIERS.length; + + const changeSorting = (idx: number, newIdentifier: BuildSort | null) => { + const newSorting = buildSorting.map((oldIdentifier, i) => + i === idx ? newIdentifier : oldIdentifier, + ); + + if (canAddMoreSorting && newSorting[newSorting.length - 1] !== null) { + newSorting.push(null); + } + + setBuildSorting(newSorting); + }; + + const deleteLastSorting = () => { + setBuildSorting((prev) => [...prev.filter(Boolean).slice(0, -1), null]); + }; + + return ( + + + +

{t("user:builds.sorting.header")}

+
+
+ + {t("user:builds.sorting.info")} + + + {buildSorting.map((sort, i) => { + const isLast = i === buildSorting.length - 1; + const isSecondToLast = i === buildSorting.length - 2; + + if (isLast && canAddMoreSorting) { + return ( + + !buildSorting.slice(0, -1).includes(identifier), + )} + value={sort} + changeValue={(newValue) => changeSorting(i, newValue)} + /> + ); + } + + return ( +
+
+ {i + 1}) {t(`user:builds.sorting.${sort}`)} +
+ {(isLast && !canAddMoreSorting) || + (canAddMoreSorting && isSecondToLast) ? ( +
+ ); + })} +
+ +
+ + {t("common:actions.save")} + + +
+
+
+
+ ); +} + +function ChangeSortingDialogSelect({ + identifiers, + value, + changeValue, +}: { + identifiers: BuildSort[]; + value: BuildSort | null; + changeValue: (value: BuildSort | null) => void; +}) { + const { t } = useTranslation(["user"]); + + return ( + + ); +} diff --git a/app/features/user-page/user-page-constants.ts b/app/features/user-page/user-page-constants.ts index b7a373794..aa4368dda 100644 --- a/app/features/user-page/user-page-constants.ts +++ b/app/features/user-page/user-page-constants.ts @@ -1 +1,2 @@ export const MATCHES_PER_SEASONS_PAGE = 8; +export const DEFAULT_BUILD_SORT = ["WEAPON_POOL", "UPDATED_AT"] as const; diff --git a/app/utils/zod.ts b/app/utils/zod.ts index 157ffd533..4514aa871 100644 --- a/app/utils/zod.ts +++ b/app/utils/zod.ts @@ -166,6 +166,12 @@ export function toArray(value: T | Array) { return [value]; } +export function emptyArrayToNull(value: unknown) { + if (Array.isArray(value) && value.length === 0) return null; + + return value; +} + export function checkboxValueToBoolean(value: unknown) { if (!value) return false; diff --git a/locales/en/user.json b/locales/en/user.json index 5c5794e1a..ee33443c6 100644 --- a/locales/en/user.json +++ b/locales/en/user.json @@ -63,5 +63,19 @@ "seasons.noReportedWeapons": "No reported weapons yet", "seasons.clickARow": "Click a row to see weapon usage stats", "seasons.loading": "Loading...", - "seasons.matchBeingProcessed": "This match has not been processed yet" + "seasons.matchBeingProcessed": "This match has not been processed yet", + + "builds.sorting.changeButton": "Change sorting", + "builds.sorting.header": "Change build sorting", + "builds.sorting.backToDefaults": "Back to defaults", + "builds.sorting.info": "Change how builds are ordered on your profile. Affects both you as well as visitors viewing your builds.", + "builds.sorting.UPDATED_AT": "Last updated", + "builds.sorting.TOP_500": "X Rank Top 500", + "builds.sorting.WEAPON_POOL": "Weapon pool", + "builds.sorting.WEAPON_IN_GAME_ORDER": "Weapons in-game order", + "builds.sorting.ALPHABETICAL_TITLE": "Title alphabetical", + "builds.sorting.MODE": "Mode", + "builds.sorting.HEADGEAR_ID": "Headgear in-game order", + "builds.sorting.CLOTHES_ID": "Clothing in-game order", + "builds.sorting.SHOES_ID": "Shoes in-game order" } diff --git a/migrations/064-build-sorting.js b/migrations/064-build-sorting.js new file mode 100644 index 000000000..cc15efb93 --- /dev/null +++ b/migrations/064-build-sorting.js @@ -0,0 +1,5 @@ +export function up(db) { + db.transaction(() => { + db.prepare(/* sql */ `alter table "User" add "buildSorting" text`).run(); + })(); +}