mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-07 19:55:46 -05:00
Mini bio in SendouQ
This commit is contained in:
@@ -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>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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 } });
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
25
app/styles/play-settings.css
Normal file
25
app/styles/play-settings.css
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "miniBio" TEXT,
|
||||
ADD COLUMN "weapons" TEXT[];
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user