From 1d7f736deece130190c2a7b64d527ea225a6667c Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sun, 6 Mar 2022 13:38:55 +0200 Subject: [PATCH] Can edit weapons in SendouQ settings --- app/components/Button.tsx | 5 ++- app/components/Combobox.tsx | 58 +++++++++++++++++++++++++++ app/constants.ts | 1 + app/models/User.server.ts | 4 +- app/routes/play/settings.tsx | 77 +++++++++++++++++++++++++++++++++--- app/styles/global.css | 47 +++++++++++++++++++++- app/styles/layout.css | 2 +- app/styles/play-settings.css | 35 +++++++++++++++- package-lock.json | 14 +++++++ package.json | 1 + 10 files changed, 232 insertions(+), 12 deletions(-) create mode 100644 app/components/Combobox.tsx diff --git a/app/components/Button.tsx b/app/components/Button.tsx index f3762862a..7842d582a 100644 --- a/app/components/Button.tsx +++ b/app/components/Button.tsx @@ -39,10 +39,11 @@ export function Button(props: ButtonProps) { minimal: variant === "minimal", "minimal-success": variant === "minimal-success", "minimal-destructive": variant === "minimal-destructive", - loading: loading, + "disabled-opaque": props.disabled, + loading, tiny, })} - disabled={loading} + disabled={props.disabled || loading} {...rest} > {icon && React.cloneElement(icon, { className: "button-icon" })} diff --git a/app/components/Combobox.tsx b/app/components/Combobox.tsx new file mode 100644 index 000000000..887ce0fff --- /dev/null +++ b/app/components/Combobox.tsx @@ -0,0 +1,58 @@ +import { Combobox as HeadlessCombobox } from "@headlessui/react"; +import * as React from "react"; +import Fuse from "fuse.js"; +import clsx from "clsx"; + +const MAX_RESULTS_SHOWN = 6; + +export function Combobox({ + options, + onChange, + inputName, + placeholder, +}: { + options: string[] | readonly string[]; + onChange: (value: string) => void; + inputName: string; + placeholder: string; +}) { + const [query, setQuery] = React.useState(""); + + const filteredOptions = (() => { + if (!query) return []; + + const fuse = new Fuse(options); + return fuse + .search(query) + .slice(0, MAX_RESULTS_SHOWN) + .map((res) => res.item); + })(); + + return ( + + setQuery(event.target.value)} + placeholder={placeholder} + className="combobox-input" + name={inputName} + /> + + {filteredOptions.map((option) => ( + + {({ active }) => ( +
  • {option}
  • + )} +
    + ))} +
    +
    + ); +} diff --git a/app/constants.ts b/app/constants.ts index c738f7660..d93ba77b6 100644 --- a/app/constants.ts +++ b/app/constants.ts @@ -23,6 +23,7 @@ export const MMR_TOPX_VISIBILITY_CUTOFF = 50; export const LFG_AMOUNT_OF_STAGES_TO_GENERATE = 7; export const MINI_BIO_MAX_LENGTH = 280; +export const LFG_WEAPON_POOL_MAX_LENGTH = 3; export const CLOSE_MMR_LIMIT = 250; export const BIT_HIGHER_MMR_LIMIT = 750; diff --git a/app/models/User.server.ts b/app/models/User.server.ts index e84ed5c12..c955423ba 100644 --- a/app/models/User.server.ts +++ b/app/models/User.server.ts @@ -24,9 +24,11 @@ export function findById(userId?: string) { export function update({ userId, miniBio, + weapons, }: { userId: string; miniBio: Nullable; + weapons: string[]; }) { - return db.user.update({ where: { id: userId }, data: { miniBio } }); + return db.user.update({ where: { id: userId }, data: { miniBio, weapons } }); } diff --git a/app/routes/play/settings.tsx b/app/routes/play/settings.tsx index f58e33d6a..aee2ee91f 100644 --- a/app/routes/play/settings.tsx +++ b/app/routes/play/settings.tsx @@ -10,16 +10,23 @@ import { useTransition, } from "remix"; import { Button } from "~/components/Button"; -import { MINI_BIO_MAX_LENGTH } from "~/constants"; +import { + LFG_WEAPON_POOL_MAX_LENGTH, + MINI_BIO_MAX_LENGTH, + weapons, +} from "~/constants"; import styles from "~/styles/play-settings.css"; import { falsyToNull, makeTitle, parseRequestFormData, requireUser, + safeJSONParse, } from "~/utils"; import * as User from "~/models/User.server"; import { z } from "zod"; +import { Combobox } from "~/components/Combobox"; +import { WeaponImage } from "~/components/WeaponImage"; export const meta: MetaFunction = () => { return { @@ -36,6 +43,10 @@ const settingsActionSchema = z.object({ falsyToNull, z.string().max(MINI_BIO_MAX_LENGTH).nullable() ), + weapons: z.preprocess( + safeJSONParse, + z.array(z.enum(weapons)).max(LFG_WEAPON_POOL_MAX_LENGTH) + ), }); export const action: ActionFunction = async ({ request, context }) => { @@ -45,32 +56,46 @@ export const action: ActionFunction = async ({ request, context }) => { schema: settingsActionSchema, }); - await User.update({ userId: user.id, miniBio: data.miniBio }); + await User.update({ + userId: user.id, + miniBio: data.miniBio, + weapons: data.weapons, + }); return null; }; export type SettingsLoaderData = { miniBio?: string; + weapons: string[]; }; export const loader: ActionFunction = async ({ context }) => { const user = requireUser(context); - const { miniBio } = (await User.findById(user.id)) ?? {}; + const { miniBio, weapons } = (await User.findById(user.id)) ?? {}; - return json({ miniBio: miniBio ?? undefined }); + return json({ + miniBio: miniBio ?? undefined, + weapons: weapons ?? [], + }); }; export default function PlaySettingsPage() { const data = useLoaderData(); const transition = useTransition(); const [miniBio, setMiniBio] = React.useState(data.miniBio ?? ""); + const [weaponPool, setWeaponPool] = React.useState(data.weapons); return (
    -