Mini bio in SendouQ

This commit is contained in:
Kalle
2022-03-05 14:59:44 +02:00
parent e16024a284
commit acb45590ad
14 changed files with 194 additions and 25 deletions

View File

@@ -2,6 +2,7 @@ import type { LookingLoaderDataGroup } from "~/routes/play/looking";
import { layoutIcon } from "~/utils";
import { oldSendouInkUserProfile } from "~/utils/urls";
import { Avatar } from "../Avatar";
import { Popover } from "../Popover";
import { WeaponImage } from "../WeaponImage";
export function GroupMembers({
@@ -88,6 +89,9 @@ function Contents({ members }: { members: LookingLoaderDataGroup["members"] }) {
))}{" "}
</div>
)}
<div className="play__card__info">
{member.miniBio && <Popover>{member.miniBio}</Popover>}
</div>
</div>
);
})}

View File

@@ -22,6 +22,8 @@ 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 CLOSE_MMR_LIMIT = 250;
export const BIT_HIGHER_MMR_LIMIT = 750;

View File

@@ -178,11 +178,14 @@ export function otherGroupsForResponse({
ownGroup.ranked && group.ranked && lookingForMatch
? undefined
: group.members.map((m) => {
const { skill, ...rest } = m.user;
return {
...rest,
MMR: skillArrayToMMR(skill),
miniBio: m.user.miniBio ?? undefined,
discordAvatar: m.user.discordAvatar,
discordId: m.user.discordId,
discordName: m.user.discordName,
id: m.user.id,
captain: m.captain,
MMR: skillArrayToMMR(m.user.skill),
};
}),
ranked: ranked(),

View File

@@ -19,3 +19,13 @@ export function findTrusters(userId: string) {
export function findById(userId: string) {
return db.user.findUnique({ where: { id: userId } });
}
export function update({
userId,
miniBio,
}: {
userId: string;
miniBio: Nullable<string>;
}) {
return db.user.update({ where: { id: userId }, data: { miniBio } });
}

View File

@@ -35,12 +35,7 @@ export default function PlayLayoutPage() {
</NavLink>
)}
{user && (
<NavLink
style={{ display: "none" }}
className="play-layout__link"
to="settings"
end
>
<NavLink className="play-layout__link" to="settings" end>
Settings
</NavLink>
)}

View File

@@ -219,6 +219,7 @@ export type LookingLoaderDataGroup = {
weapons?: string[];
peakXP?: number;
peakLP?: number;
miniBio?: string;
})[];
MMRRelation?: "LOWER" | "BIT_LOWER" | "CLOSE" | "BIT_HIGHER" | "HIGHER";
ranked?: boolean;
@@ -264,12 +265,14 @@ export const loader: LoaderFunction = async ({ context }) => {
ownGroup: {
id: ownGroup.id,
members: ownGroupWithMembers.members.map((m) => {
const { skill, ...rest } = m.user;
return {
...rest,
miniBio: m.user.miniBio ?? undefined,
discordAvatar: m.user.discordAvatar,
discordId: m.user.discordId,
discordName: m.user.discordName,
id: m.user.id,
captain: m.captain,
MMR: skillArrayToMMR(skill),
MMR: skillArrayToMMR(m.user.skill),
};
}),
ranked: ownGroup.ranked ?? undefined,

View File

@@ -58,9 +58,7 @@ export const meta: MetaFunction = ({
title: data?.isOwnMatch
? makeTitle(
`vs. ${listToUserReadableString(
data.groups[1].members.map(
(u) => `${u.discordName}#${u.discordDiscriminator}`
)
data.groups[1].members.map((u) => u.discordName)
)}`
)
: "Match",

View File

@@ -1,3 +1,106 @@
import clsx from "clsx";
import * as React from "react";
import {
ActionFunction,
Form,
json,
LinksFunction,
MetaFunction,
useLoaderData,
useTransition,
} from "remix";
import { Button } from "~/components/Button";
import { MINI_BIO_MAX_LENGTH } from "~/constants";
import styles from "~/styles/play-settings.css";
import {
falsyToNull,
makeTitle,
parseRequestFormData,
requireUser,
} from "~/utils";
import * as User from "~/models/User.server";
import { z } from "zod";
export const meta: MetaFunction = () => {
return {
title: makeTitle("SendouQ settings"),
};
};
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
};
const settingsActionSchema = z.object({
miniBio: z.preprocess(
falsyToNull,
z.string().max(MINI_BIO_MAX_LENGTH).nullable()
),
});
export const action: ActionFunction = async ({ request, context }) => {
const user = requireUser(context);
const data = await parseRequestFormData({
request,
schema: settingsActionSchema,
});
await User.update({ userId: user.id, miniBio: data.miniBio });
return null;
};
export type SettingsLoaderData = {
miniBio?: string;
};
export const loader: ActionFunction = async ({ context }) => {
const user = requireUser(context);
const { miniBio } = (await User.findById(user.id)) ?? {};
return json<SettingsLoaderData>({ miniBio: miniBio ?? undefined });
};
export default function PlaySettingsPage() {
return <h1 className="text-center">Coming SOON</h1>;
const data = useLoaderData<SettingsLoaderData>();
const transition = useTransition();
const [miniBio, setMiniBio] = React.useState(data.miniBio ?? "");
return (
<div>
<Form method="post">
<label className="play-settings__mini-bio-label" htmlFor="mini-bio">
SendouQ Bio
</label>
<div className="play-settings__explanation">
Include any information here that is useful for others to decide if
they should group up with you. Especially anything that gives an idea
about your current skill level.
</div>
<textarea
className="play-settings__mini-bio"
value={miniBio}
onChange={(e) => setMiniBio(e.target.value)}
id="mini-bio"
name="miniBio"
/>
<div
className={clsx("play-settings__character-count", {
error: miniBio.length > MINI_BIO_MAX_LENGTH,
})}
>
{miniBio.length}/{MINI_BIO_MAX_LENGTH}
</div>
<div className="mt-4">
<Button
loading={transition.state === "submitting"}
loadingText="Saving..."
>
Save
</Button>
</div>
</Form>
</div>
);
}

View File

@@ -188,6 +188,20 @@ button > .button-icon {
margin-inline-end: var(--s-1-5);
}
textarea {
padding: var(--s-2) var(--s-3);
border: 1px solid var(--border);
accent-color: var(--theme-secondary);
background-color: transparent;
border-radius: var(--rounded);
color: var(--text);
font-size: var(--fonts-sm);
outline: none;
overflow-wrap: normal;
overflow-x: scroll;
white-space: pre;
}
input:not(.plain) {
height: 1rem;
padding: var(--s-4) var(--s-3);
@@ -426,6 +440,7 @@ hr {
border-radius: var(--rounded);
font-size: var(--fonts-sm);
font-weight: var(--semi-bold);
white-space: pre-wrap;
}
.four-zero-one__container {

View File

@@ -1,14 +1,11 @@
.play-layout__link-container {
display: flex;
width: 100%;
justify-content: space-between;
margin-block-end: var(--s-4);
justify-content: space-evenly;
margin-block-end: var(--s-6);
}
.play-layout__link {
display: flex;
flex: 1;
justify-content: center;
font-size: var(--fonts-xs);
font-weight: var(--body);
}
@@ -79,7 +76,6 @@
justify-content: center;
background-color: var(--bg-darker);
border-radius: var(--rounded);
overflow-x: hidden;
padding-block-end: var(--s-1);
padding-block-start: var(--s-2);
}
@@ -137,6 +133,11 @@
.play__card__member-weapons {
display: flex;
gap: var(--s-1);
margin-block-end: var(--s-1);
}
.play__card__info {
margin-block-start: auto;
}
.play__card__member-weapon {

View File

@@ -0,0 +1,25 @@
.play-settings__mini-bio-label {
display: block;
font-size: var(--fonts-sm);
font-weight: var(--bold);
}
.play-settings__explanation {
color: var(--text-lighter);
font-size: var(--fonts-sm);
margin-block-end: var(--s-2);
}
.play-settings__mini-bio {
width: min(18rem, 100%);
height: 6rem;
}
.play-settings__character-count {
margin-top: -0.25rem;
font-size: var(--fonts-xs);
}
.play-settings__character-count.error {
color: var(--theme-error);
}

View File

@@ -121,6 +121,12 @@ export function safeJSONParse(value: unknown): unknown {
}
}
export function falsyToNull(value: unknown): unknown {
if (value) return value;
return null;
}
export type Serialized<T> = {
[P in keyof T]: T[P] extends Date
? string
@@ -172,5 +178,4 @@ export interface UserLean {
discordId: string;
discordAvatar: string | null;
discordName: string;
discordDiscriminator: string;
}

View File

@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "miniBio" TEXT,
ADD COLUMN "weapons" TEXT[];

View File

@@ -18,6 +18,8 @@ model User {
twitter String?
youtubeId String?
youtubeName String?
miniBio String?
weapons String[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
trustedUsers TrustRelationships[] @relation("trustGiver")