Remove countries-list in favor of an app constant

This commit is contained in:
Kalle 2025-05-31 12:32:21 +03:00
parent 15ee83560a
commit 225abdad26
7 changed files with 280 additions and 45 deletions

View File

@ -1,16 +1,11 @@
import { type LoaderFunctionArgs, redirect } from "@remix-run/node";
import { countries } from "countries-list";
import { requireUserId } from "~/features/auth/core/user.server";
import * as UserRepository from "~/features/user-page/UserRepository.server";
import { i18next } from "~/modules/i18n/i18next.server";
import { translatedCountry } from "~/utils/i18n.server";
import { notFoundIfFalsy } from "~/utils/remix.server";
import { userPage } from "~/utils/urls";
import { userParamsSchema } from "../user-page-schemas.server";
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
const locale = await i18next.getLocale(request);
const user = await requireUserId(request);
const { identifier } = userParamsSchema.parse(params);
const userToBeEdited = notFoundIfFalsy(
@ -29,15 +24,5 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
user: userProfile,
favoriteBadgeIds: userProfile.favoriteBadgeIds,
discordUniqueName: userProfile.discordUniqueName,
countries: Object.entries(countries)
.map(([code, country]) => ({
code,
name:
translatedCountry({
countryCode: code,
language: locale,
}) ?? country.name,
}))
.sort((a, b) => a.name.localeCompare(b.name)),
};
};

View File

@ -19,12 +19,14 @@ import { USER } from "~/constants";
import type { Tables } from "~/db/tables";
import { BADGE } from "~/features/badges/badges-contants";
import { BadgesSelector } from "~/features/badges/components/BadgesSelector";
import { useIsMounted } from "~/hooks/useIsMounted";
import type { MainWeaponId } from "~/modules/in-game-lists/types";
import { useHasRole } from "~/modules/permissions/hooks";
import invariant from "~/utils/invariant";
import { rawSensToString } from "~/utils/strings";
import { FAQ_PAGE } from "~/utils/urls";
import type { UserPageLoaderData } from "../loaders/u.$identifier.server";
import { COUNTRY_CODES } from "../user-page-constants";
import { action } from "../actions/u.$identifier.edit.server";
import { loader } from "../loaders/u.$identifier.edit.server";
@ -210,16 +212,26 @@ function SensSelects() {
}
function CountrySelect() {
const { t } = useTranslation(["user"]);
const { t, i18n } = useTranslation(["user"]);
const data = useLoaderData<typeof loader>();
const isMounted = useIsMounted();
const displayName = new Intl.DisplayNames(i18n.language, { type: "region" });
// TODO: if react-aria-components start supporting "suppressHydrationWarning" it would likely be a better solution here
const items = COUNTRY_CODES.map((countryCode) => ({
name: isMounted
? (displayName.of(countryCode) ?? countryCode)
: countryCode,
id: countryCode,
key: countryCode,
})).sort((a, b) =>
a.name.localeCompare(b.name, i18n.language, { sensitivity: "base" }),
);
return (
<SendouSelect
items={data.countries.map((country) => ({
...country,
id: country.code,
key: country.code,
}))}
items={items}
label={t("user:country")}
search={{
placeholder: t("user:forms.country.search.placeholder"),

View File

@ -1,2 +1,262 @@
export const MATCHES_PER_SEASONS_PAGE = 8;
export const DEFAULT_BUILD_SORT = ["WEAPON_POOL", "UPDATED_AT"] as const;
/**
* An array of ISO 3166-1 alpha-2 country codes.
* Each entry is a two-letter uppercase string representing a country or territory. Sorted alphabetically.
*
* @see {@link https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2}
* @see {@link https://github.com/annexare/Countries}
*/
export const COUNTRY_CODES = [
"AD",
"AE",
"AF",
"AG",
"AI",
"AL",
"AM",
"AO",
"AQ",
"AR",
"AS",
"AT",
"AU",
"AW",
"AX",
"AZ",
"BA",
"BB",
"BD",
"BE",
"BF",
"BG",
"BH",
"BI",
"BJ",
"BL",
"BM",
"BN",
"BO",
"BQ",
"BR",
"BS",
"BT",
"BV",
"BW",
"BY",
"BZ",
"CA",
"CC",
"CD",
"CF",
"CG",
"CH",
"CI",
"CK",
"CL",
"CM",
"CN",
"CO",
"CR",
"CU",
"CV",
"CW",
"CX",
"CY",
"CZ",
"DE",
"DJ",
"DK",
"DM",
"DO",
"DZ",
"EC",
"EE",
"EG",
"EH",
"ER",
"ES",
"ET",
"FI",
"FJ",
"FK",
"FM",
"FO",
"FR",
"GA",
"GB",
"GD",
"GE",
"GF",
"GG",
"GH",
"GI",
"GL",
"GM",
"GN",
"GP",
"GQ",
"GR",
"GS",
"GT",
"GU",
"GW",
"GY",
"HK",
"HM",
"HN",
"HR",
"HT",
"HU",
"ID",
"IE",
"IL",
"IM",
"IN",
"IO",
"IQ",
"IR",
"IS",
"IT",
"JE",
"JM",
"JO",
"JP",
"KE",
"KG",
"KH",
"KI",
"KM",
"KN",
"KP",
"KR",
"KW",
"KY",
"KZ",
"LA",
"LB",
"LC",
"LI",
"LK",
"LR",
"LS",
"LT",
"LU",
"LV",
"LY",
"MA",
"MC",
"MD",
"ME",
"MF",
"MG",
"MH",
"MK",
"ML",
"MM",
"MN",
"MO",
"MP",
"MQ",
"MR",
"MS",
"MT",
"MU",
"MV",
"MW",
"MX",
"MY",
"MZ",
"NA",
"NC",
"NE",
"NF",
"NG",
"NI",
"NL",
"NO",
"NP",
"NR",
"NU",
"NZ",
"OM",
"PA",
"PE",
"PF",
"PG",
"PH",
"PK",
"PL",
"PM",
"PN",
"PR",
"PS",
"PT",
"PW",
"PY",
"QA",
"RE",
"RO",
"RS",
"RU",
"RW",
"SA",
"SB",
"SC",
"SD",
"SE",
"SG",
"SH",
"SI",
"SJ",
"SK",
"SL",
"SM",
"SN",
"SO",
"SR",
"SS",
"ST",
"SV",
"SX",
"SY",
"SZ",
"TC",
"TD",
"TF",
"TG",
"TH",
"TJ",
"TK",
"TL",
"TM",
"TN",
"TO",
"TR",
"TT",
"TV",
"TW",
"TZ",
"UA",
"UG",
"UM",
"US",
"UY",
"UZ",
"VA",
"VC",
"VE",
"VG",
"VI",
"VN",
"VU",
"WF",
"WS",
"XK",
"YE",
"YT",
"ZA",
"ZM",
"ZW",
] as const;

View File

@ -1,4 +1,3 @@
import { countries } from "countries-list";
import { z } from "zod";
import { USER } from "~/constants";
import "~/styles/u-edit.css";
@ -23,6 +22,7 @@ import {
HIGHLIGHT_CHECKBOX_NAME,
HIGHLIGHT_TOURNAMENT_CHECKBOX_NAME,
} from "./components/UserResultsTable";
import { COUNTRY_CODES } from "./user-page-constants";
export const userParamsSchema = z.object({ identifier: z.string() });
@ -41,9 +41,7 @@ export const userEditActionSchema = z
falsyToNull,
z
.string()
.refine(
(val) => !val || Object.keys(countries).some((code) => val === code),
)
.refine((val) => !val || COUNTRY_CODES.includes(val as any))
.nullable(),
),
bio: z.preprocess(

View File

@ -1,12 +0,0 @@
// .server because these are dangerous to use on client due to hydration mismatches
// (Node.js and browser can display different language depending on implementation)
export function translatedCountry({
language,
countryCode,
}: {
language: string;
countryCode: string;
}) {
return new Intl.DisplayNames([language], { type: "region" }).of(countryCode);
}

7
package-lock.json generated
View File

@ -26,7 +26,6 @@
"better-sqlite3": "^11.9.1",
"clsx": "^2.1.1",
"compressorjs": "^1.2.1",
"countries-list": "^3.1.1",
"date-fns": "^4.1.0",
"fuse.js": "^7.1.0",
"gray-matter": "^4.0.3",
@ -9108,12 +9107,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/countries-list": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/countries-list/-/countries-list-3.1.1.tgz",
"integrity": "sha512-nPklKJ5qtmY5MdBKw1NiBAoyx5Sa7p2yPpljZyQ7gyCN1m+eMFs9I6CT37Mxt8zvR5L3VzD3DJBE4WQzX3WF4A==",
"license": "MIT"
},
"node_modules/crelt": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",

View File

@ -45,7 +45,6 @@
"better-sqlite3": "^11.9.1",
"clsx": "^2.1.1",
"compressorjs": "^1.2.1",
"countries-list": "^3.1.1",
"date-fns": "^4.1.0",
"fuse.js": "^7.1.0",
"gray-matter": "^4.0.3",