From acb45590adf5789b787eff0fc6c72efbdef2b562 Mon Sep 17 00:00:00 2001
From: Kalle <38327916+Sendouc@users.noreply.github.com>
Date: Sat, 5 Mar 2022 14:59:44 +0200
Subject: [PATCH] Mini bio in SendouQ
---
app/components/play/GroupMembers.tsx | 4 +
app/constants.ts | 2 +
app/core/play/utils.ts | 11 +-
app/models/User.server.ts | 10 ++
app/routes/play.tsx | 7 +-
app/routes/play/looking.tsx | 11 +-
app/routes/play/match.$id.tsx | 4 +-
app/routes/play/settings.tsx | 105 +++++++++++++++++-
app/styles/global.css | 15 +++
app/styles/play-layout.css | 13 ++-
app/styles/play-settings.css | 25 +++++
app/utils/index.ts | 7 +-
.../migration.sql | 3 +
prisma/schema.prisma | 2 +
14 files changed, 194 insertions(+), 25 deletions(-)
create mode 100644 app/styles/play-settings.css
create mode 100644 prisma/migrations/20220305103335_add_mini_bio_weapons/migration.sql
diff --git a/app/components/play/GroupMembers.tsx b/app/components/play/GroupMembers.tsx
index eb5762c46..0b776b2df 100644
--- a/app/components/play/GroupMembers.tsx
+++ b/app/components/play/GroupMembers.tsx
@@ -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"] }) {
))}{" "}
)}
+
+ {member.miniBio &&
{member.miniBio}}
+
);
})}
diff --git a/app/constants.ts b/app/constants.ts
index 1b7e04f00..c738f7660 100644
--- a/app/constants.ts
+++ b/app/constants.ts
@@ -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;
diff --git a/app/core/play/utils.ts b/app/core/play/utils.ts
index 825d6c63c..bef7ab29e 100644
--- a/app/core/play/utils.ts
+++ b/app/core/play/utils.ts
@@ -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(),
diff --git a/app/models/User.server.ts b/app/models/User.server.ts
index 0a355683e..f5a86f714 100644
--- a/app/models/User.server.ts
+++ b/app/models/User.server.ts
@@ -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;
+}) {
+ return db.user.update({ where: { id: userId }, data: { miniBio } });
+}
diff --git a/app/routes/play.tsx b/app/routes/play.tsx
index 2381fdd8a..cbbc7a2d0 100644
--- a/app/routes/play.tsx
+++ b/app/routes/play.tsx
@@ -35,12 +35,7 @@ export default function PlayLayoutPage() {
)}
{user && (
-
+
Settings
)}
diff --git a/app/routes/play/looking.tsx b/app/routes/play/looking.tsx
index 4f07dc363..33ca73d1d 100644
--- a/app/routes/play/looking.tsx
+++ b/app/routes/play/looking.tsx
@@ -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,
diff --git a/app/routes/play/match.$id.tsx b/app/routes/play/match.$id.tsx
index 28c9a22a9..6a7b93e80 100644
--- a/app/routes/play/match.$id.tsx
+++ b/app/routes/play/match.$id.tsx
@@ -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",
diff --git a/app/routes/play/settings.tsx b/app/routes/play/settings.tsx
index 1bec9b48c..f58e33d6a 100644
--- a/app/routes/play/settings.tsx
+++ b/app/routes/play/settings.tsx
@@ -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({ miniBio: miniBio ?? undefined });
+};
+
export default function PlaySettingsPage() {
- return Coming SOON
;
+ const data = useLoaderData();
+ const transition = useTransition();
+ const [miniBio, setMiniBio] = React.useState(data.miniBio ?? "");
+
+ return (
+
+ );
}
diff --git a/app/styles/global.css b/app/styles/global.css
index 5f9b11772..a8da5589b 100644
--- a/app/styles/global.css
+++ b/app/styles/global.css
@@ -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 {
diff --git a/app/styles/play-layout.css b/app/styles/play-layout.css
index b53bf727c..b9aac11ba 100644
--- a/app/styles/play-layout.css
+++ b/app/styles/play-layout.css
@@ -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 {
diff --git a/app/styles/play-settings.css b/app/styles/play-settings.css
new file mode 100644
index 000000000..a00e05ccc
--- /dev/null
+++ b/app/styles/play-settings.css
@@ -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);
+}
diff --git a/app/utils/index.ts b/app/utils/index.ts
index db4427ee0..0ec976e12 100644
--- a/app/utils/index.ts
+++ b/app/utils/index.ts
@@ -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 = {
[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;
}
diff --git a/prisma/migrations/20220305103335_add_mini_bio_weapons/migration.sql b/prisma/migrations/20220305103335_add_mini_bio_weapons/migration.sql
new file mode 100644
index 000000000..ab05d8f1e
--- /dev/null
+++ b/prisma/migrations/20220305103335_add_mini_bio_weapons/migration.sql
@@ -0,0 +1,3 @@
+-- AlterTable
+ALTER TABLE "User" ADD COLUMN "miniBio" TEXT,
+ADD COLUMN "weapons" TEXT[];
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index d2846c5be..764b56ce6 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -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")