diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts
index dacdb9e9d..e2eaa0362 100644
--- a/src/api/routes/users/#id/profile.ts
+++ b/src/api/routes/users/#id/profile.ts
@@ -20,158 +20,165 @@ import { route } from "@spacebar/api";
import {
Badge,
Config,
+ emitEvent,
FieldErrors,
+ handleFile,
Member,
PrivateUserProjection,
+ PublicUser,
+ PublicUserProjection,
+ Relationship,
+ RelationshipType,
User,
UserProfileModifySchema,
UserUpdateEvent,
- emitEvent,
- handleFile,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
+import { In } from "typeorm";
const router: Router = Router({ mergeParams: true });
-router.get(
- "/",
- route({ responses: { 200: { body: "UserProfileResponse" } } }),
- async (req: Request, res: Response) => {
- if (req.params.id === "@me") req.params.id = req.user_id;
+router.get("/", route({ responses: { 200: { body: "UserProfileResponse" } } }), async (req: Request, res: Response) => {
+ if (req.params.id === "@me") req.params.id = req.user_id;
- const { guild_id, with_mutual_guilds } = req.query;
+ const { guild_id, with_mutual_guilds, with_mutual_friends, with_mutual_friends_count } = req.query;
- const user = await User.getPublicUser(req.params.id, {
- relations: ["connected_accounts"],
- });
+ const user = await User.getPublicUser(req.params.id, {
+ relations: ["connected_accounts"],
+ });
- const mutual_guilds: object[] = [];
- let premium_guild_since;
+ const mutual_guilds: object[] = [];
+ let premium_guild_since;
- if (with_mutual_guilds == "true") {
- const requested_member = await Member.find({
- where: { id: req.params.id },
- });
- const self_member = await Member.find({
- where: { id: req.user_id },
- });
+ if (with_mutual_guilds == "true") {
+ const requested_member = await Member.find({
+ where: { id: req.params.id },
+ });
+ const self_member = await Member.find({
+ where: { id: req.user_id },
+ });
- for (const rmem of requested_member) {
- if (rmem.premium_since) {
- if (premium_guild_since) {
- if (premium_guild_since > rmem.premium_since) {
- premium_guild_since = rmem.premium_since;
- }
- } else {
+ for (const rmem of requested_member) {
+ if (rmem.premium_since) {
+ if (premium_guild_since) {
+ if (premium_guild_since > rmem.premium_since) {
premium_guild_since = rmem.premium_since;
}
+ } else {
+ premium_guild_since = rmem.premium_since;
}
- for (const smem of self_member) {
- if (smem.guild_id === rmem.guild_id) {
- mutual_guilds.push({
- id: rmem.guild_id,
- nick: rmem.nick,
- });
- }
+ }
+ for (const smem of self_member) {
+ if (smem.guild_id === rmem.guild_id) {
+ mutual_guilds.push({
+ id: rmem.guild_id,
+ nick: rmem.nick,
+ });
}
}
}
+ }
- const guild_member =
- guild_id && typeof guild_id == "string"
- ? await Member.findOneOrFail({
- where: { id: req.params.id, guild_id: guild_id },
- relations: ["roles"],
- })
- : undefined;
+ const guild_member =
+ guild_id && typeof guild_id == "string"
+ ? await Member.findOneOrFail({
+ where: { id: req.params.id, guild_id: guild_id },
+ relations: ["roles"],
+ })
+ : undefined;
- // TODO: make proper DTO's in util?
+ // TODO: make proper DTO's in util?
- const userProfile = {
- bio: req.user_bot ? null : user.bio,
- accent_color: user.accent_color,
- banner: user.banner,
- pronouns: user.pronouns,
- theme_colors: user.theme_colors?.map((t) => Number(t)), // these are strings for some reason, they should be numbers
- };
+ const userProfile = {
+ bio: req.user_bot ? null : user.bio,
+ accent_color: user.accent_color,
+ banner: user.banner,
+ pronouns: user.pronouns,
+ theme_colors: user.theme_colors?.map((t) => Number(t)), // these are strings for some reason, they should be numbers
+ };
- const guildMemberProfile = {
- accent_color: null,
- banner: guild_member?.banner || null,
- bio: guild_member?.bio || "",
- guild_id,
- };
+ const guildMemberProfile = {
+ accent_color: null,
+ banner: guild_member?.banner || null,
+ bio: guild_member?.bio || "",
+ guild_id,
+ };
- const badges = await Badge.find();
+ const badges = await Badge.find();
- res.json({
- connected_accounts: user.connected_accounts.filter(
- (x) => x.visibility != 0,
- ),
- premium_guild_since: premium_guild_since, // TODO
- premium_since: user.premium_since, // TODO
- mutual_guilds: mutual_guilds, // TODO {id: "", nick: null} when ?with_mutual_guilds=true
- user: user.toPublicUser(),
- premium_type: user.premium_type,
- profile_themes_experiment_bucket: 4, // TODO: This doesn't make it available, for some reason?
- user_profile: userProfile,
- guild_member: guild_member?.toPublicMember(),
- guild_member_profile: guild_id && guildMemberProfile,
- badges: badges.filter((x) => user.badge_ids?.includes(x.id)),
- });
- },
-);
+ let mutual_friends: PublicUser[] = [];
+ let mutual_friends_count = 0;
-router.patch(
- "/",
- route({ requestBody: "UserProfileModifySchema" }),
- async (req: Request, res: Response) => {
- const body = req.body as UserProfileModifySchema;
+ if (with_mutual_friends == "true" || with_mutual_friends_count == "true") {
+ const relationshipsSelf = await Relationship.find({ where: { from_id: req.user_id, type: RelationshipType.friends } });
+ const relationshipsUser = await Relationship.find({ where: { from_id: req.params.id, type: RelationshipType.friends } });
+ const relationshipsIntersection = relationshipsSelf.filter((r1) => relationshipsUser.some((r2) => r2.to_id === r1.to_id));
+ if (with_mutual_friends_count) mutual_friends_count = relationshipsIntersection.length;
+ if (with_mutual_friends) {
+ const users = await User.find({ where: { id: In(relationshipsIntersection.map((r) => r.to_id)) }, select: PublicUserProjection });
+ mutual_friends = users.map((u) => u.toPublicUser());
+ }
+ }
- if (body.banner)
- body.banner = await handleFile(
- `/banners/${req.user_id}`,
- body.banner as string,
- );
- const user = await User.findOneOrFail({
- where: { id: req.user_id },
- select: [...PrivateUserProjection, "data"],
- });
+ res.json({
+ connected_accounts: user.connected_accounts.filter((x) => x.visibility != 0),
+ premium_guild_since: premium_guild_since, // TODO
+ premium_since: user.premium_since, // TODO
+ mutual_guilds: with_mutual_guilds ? mutual_guilds : undefined, // TODO {id: "", nick: null} when ?with_mutual_guilds=true
+ mutual_friends: with_mutual_friends ? mutual_friends : undefined,
+ mutual_friends_count: with_mutual_friends_count ? mutual_friends_count : undefined,
+ user: user.toPublicUser(),
+ premium_type: user.premium_type,
+ profile_themes_experiment_bucket: 4, // TODO: This doesn't make it available, for some reason?
+ user_profile: userProfile,
+ guild_member: guild_member?.toPublicMember(),
+ guild_member_profile: guild_id && guildMemberProfile,
+ badges: badges.filter((x) => user.badge_ids?.includes(x.id)),
+ });
+});
- if (body.bio) {
- const { maxBio } = Config.get().limits.user;
- if (body.bio.length > maxBio) {
- throw FieldErrors({
- bio: {
- code: "BIO_INVALID",
- message: `Bio must be less than ${maxBio} in length`,
- },
- });
- }
+router.patch("/", route({ requestBody: "UserProfileModifySchema" }), async (req: Request, res: Response) => {
+ const body = req.body as UserProfileModifySchema;
+
+ if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string);
+ const user = await User.findOneOrFail({
+ where: { id: req.user_id },
+ select: [...PrivateUserProjection, "data"],
+ });
+
+ if (body.bio) {
+ const { maxBio } = Config.get().limits.user;
+ if (body.bio.length > maxBio) {
+ throw FieldErrors({
+ bio: {
+ code: "BIO_INVALID",
+ message: `Bio must be less than ${maxBio} in length`,
+ },
+ });
}
+ }
- user.assign(body);
- await user.save();
+ user.assign(body);
+ await user.save();
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- delete user.data;
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ delete user.data;
- // TODO: send update member list event in gateway
- await emitEvent({
- event: "USER_UPDATE",
- user_id: req.user_id,
- data: user,
- } as UserUpdateEvent);
+ // TODO: send update member list event in gateway
+ await emitEvent({
+ event: "USER_UPDATE",
+ user_id: req.user_id,
+ data: user,
+ } as UserUpdateEvent);
- res.json({
- accent_color: user.accent_color,
- bio: user.bio,
- banner: user.banner,
- theme_colors: user.theme_colors,
- pronouns: user.pronouns,
- });
- },
-);
+ res.json({
+ accent_color: user.accent_color,
+ bio: user.bio,
+ banner: user.banner,
+ theme_colors: user.theme_colors,
+ pronouns: user.pronouns,
+ });
+});
export default router;
|