From 9fbca3fd95c2719cb10c7037be6a376b3c0a0582 Mon Sep 17 00:00:00 2001
From: naskya <m@naskya.net>
Date: Wed, 21 Feb 2024 03:25:45 +0900
Subject: [PATCH] refactor: drop lang from user_profile

---
 docs/api-change.md                                  |  1 +
 docs/changelog.md                                   |  1 +
 docs/downgrade.sql                                  |  4 ++++
 .../1708452631156-drop-user-profile-language.js     | 13 +++++++++++++
 .../backend/src/models/entities/user-profile.ts     |  6 ------
 packages/backend/src/models/repositories/user.ts    |  1 -
 packages/backend/src/models/schema/user.ts          |  6 ------
 .../backend/src/server/api/endpoints/i/update.ts    |  7 -------
 .../backend/src/services/send-email-notification.ts |  4 ++--
 packages/client/src/pages/settings/profile.vue      | 11 -----------
 10 files changed, 21 insertions(+), 33 deletions(-)
 create mode 100644 packages/backend/migration/1708452631156-drop-user-profile-language.js

diff --git a/docs/api-change.md b/docs/api-change.md
index 4d6c0d63e8..86ae3859a5 100644
--- a/docs/api-change.md
+++ b/docs/api-change.md
@@ -9,6 +9,7 @@ Breaking changes are indicated by the :warning: icon.
 	- `mod`: `add` permission + edit the name/category/tag/license of the existing custom emojis
 	- `full`: `mod` permission + delete existing custom emojis
 - Emoji moderators are able to access to the endpoints under `admin/emoji/`
+- Removed `lang` from the response of `i` and the request parameter of `i/update`.
 
 ## v20240217
 
diff --git a/docs/changelog.md b/docs/changelog.md
index e1de713f2b..517a565e7b 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -5,6 +5,7 @@ Critical security updates are indicated by the :warning: icon.
 ## Unreleased
 
 - Add the ability to give regular (non-moderator) users permission to manage custom emojis
+- Fix a bug that made impossible to update user profiles under some conditions
 
 ## :warning: v20240217-1
 
diff --git a/docs/downgrade.sql b/docs/downgrade.sql
index 1840414a6d..030d939afc 100644
--- a/docs/downgrade.sql
+++ b/docs/downgrade.sql
@@ -1,12 +1,16 @@
 BEGIN;
 
 DELETE FROM "migrations" WHERE name IN (
+		'DropUserProfileLanguage1708452631156',
 		'EmojiModerator1692825433698',
     'RemoveNsfwDetection1705848938166',
     'FirefishUrlMove1707850084123',
     'RemoveNativeUtilsMigration1705877093218'
 );
 
+-- drop-user-profile-language
+ALTER TABLE "user_profile" ADD COLUMN "lang" character varying(32);
+
 -- emoji-moderator
 ALTER TABLE "user" DROP COLUMN "emojiModPerm";
 DROP TYPE "public"."user_emojimodperm_enum";
diff --git a/packages/backend/migration/1708452631156-drop-user-profile-language.js b/packages/backend/migration/1708452631156-drop-user-profile-language.js
new file mode 100644
index 0000000000..37e701ef15
--- /dev/null
+++ b/packages/backend/migration/1708452631156-drop-user-profile-language.js
@@ -0,0 +1,13 @@
+export class DropUserProfileLanguage1708452631156 {
+	name = "DropUserProfileLanguage1708452631156";
+
+	async up(queryRunner) {
+		await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "lang"`);
+	}
+
+	async down(queryRunner) {
+		await queryRunner.query(
+			`ALTER TABLE "user_profile" ADD COLUMN "lang" character varying(32)`,
+		);
+	}
+}
diff --git a/packages/backend/src/models/entities/user-profile.ts b/packages/backend/src/models/entities/user-profile.ts
index 40c500995f..7774ca5086 100644
--- a/packages/backend/src/models/entities/user-profile.ts
+++ b/packages/backend/src/models/entities/user-profile.ts
@@ -54,12 +54,6 @@ export class UserProfile {
 		verified?: boolean;
 	}[];
 
-	@Column("varchar", {
-		length: 32,
-		nullable: true,
-	})
-	public lang: string | null;
-
 	@Column("varchar", {
 		length: 512,
 		nullable: true,
diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts
index dbacb4e5d9..dbc4a10d4d 100644
--- a/packages/backend/src/models/repositories/user.ts
+++ b/packages/backend/src/models/repositories/user.ts
@@ -504,7 +504,6 @@ export const UserRepository = db.getRepository(User).extend({
 						description: profile!.description,
 						location: profile!.location,
 						birthday: profile!.birthday,
-						lang: profile!.lang,
 						fields: profile!.fields,
 						followersCount: followersCount || 0,
 						followingCount: followingCount || 0,
diff --git a/packages/backend/src/models/schema/user.ts b/packages/backend/src/models/schema/user.ts
index c591d8520a..533e50bccf 100644
--- a/packages/backend/src/models/schema/user.ts
+++ b/packages/backend/src/models/schema/user.ts
@@ -204,12 +204,6 @@ export const packedUserDetailedNotMeOnlySchema = {
 			optional: false,
 			example: "2018-03-12",
 		},
-		lang: {
-			type: "string",
-			nullable: true,
-			optional: false,
-			example: "ja-JP",
-		},
 		fields: {
 			type: "array",
 			nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index a0bf2e8c1f..d7974ce6cc 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -11,7 +11,6 @@ import type { User } from "@/models/entities/user.js";
 import type { UserProfile } from "@/models/entities/user-profile.js";
 import { notificationTypes } from "@/types.js";
 import { normalizeForSearch } from "@/misc/normalize-for-search.js";
-import { langmap } from "@/misc/langmap.js";
 import { verifyLink } from "@/services/fetch-rel-me.js";
 import { ApiError } from "@/server/api/error.js";
 import define from "@/server/api/define.js";
@@ -88,11 +87,6 @@ export const paramDef = {
 		description: { ...Users.descriptionSchema, nullable: true },
 		location: { ...Users.locationSchema, nullable: true },
 		birthday: { ...Users.birthdaySchema, nullable: true },
-		lang: {
-			type: "string",
-			enum: Object.keys(langmap),
-			nullable: true,
-		},
 		avatarId: { type: "string", format: "misskey:id", nullable: true },
 		bannerId: { type: "string", format: "misskey:id", nullable: true },
 		fields: {
@@ -159,7 +153,6 @@ export default define(meta, paramDef, async (ps, _user, token) => {
 
 	if (ps.name !== undefined) updates.name = ps.name;
 	if (ps.description !== undefined) profileUpdates.description = ps.description;
-	if (typeof ps.lang === "string") profileUpdates.lang = ps.lang;
 	if (ps.location !== undefined) profileUpdates.location = ps.location;
 	if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
 	if (ps.ffVisibility !== undefined)
diff --git a/packages/backend/src/services/send-email-notification.ts b/packages/backend/src/services/send-email-notification.ts
index a2f422a962..aa8fa35ffe 100644
--- a/packages/backend/src/services/send-email-notification.ts
+++ b/packages/backend/src/services/send-email-notification.ts
@@ -12,7 +12,7 @@ async function follow(userId: User["id"], follower: User) {
 	/*
 	const userProfile = await UserProfiles.findOneByOrFail({ userId: userId });
 	if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return;
-	const locale = locales[userProfile.lang || 'ja-JP'];
+	const locale = locales['en-US'];
 	const i18n = new I18n(locale);
 	// TODO: render user information html
 	sendEmail(userProfile.email, i18n.t('_email._follow.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`);
@@ -23,7 +23,7 @@ async function receiveFollowRequest(userId: User["id"], follower: User) {
 	/*
 	const userProfile = await UserProfiles.findOneByOrFail({ userId: userId });
 	if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return;
-	const locale = locales[userProfile.lang || 'ja-JP'];
+	const locale = locales['en-US'];
 	const i18n = new I18n(locale);
 	// TODO: render user information html
 	sendEmail(userProfile.email, i18n.t('_email._receiveFollowRequest.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`);
diff --git a/packages/client/src/pages/settings/profile.vue b/packages/client/src/pages/settings/profile.vue
index 6ade669b24..4b06ad82fc 100644
--- a/packages/client/src/pages/settings/profile.vue
+++ b/packages/client/src/pages/settings/profile.vue
@@ -74,13 +74,6 @@
 			<template #prefix><i :class="icon('ph-cake')"></i></template>
 		</FormInput>
 
-		<FormSelect v-model="profile.lang" class="_formBlock">
-			<template #label>{{ i18n.ts.language }}</template>
-			<option v-for="x in Object.keys(langmap)" :key="x" :value="x">
-				{{ langmap[x].nativeName }}
-			</option>
-		</FormSelect>
-
 		<FormSlot class="_formBlock">
 			<FormFolder>
 				<template #icon><i :class="icon('ph-table')"></i></template>
@@ -163,7 +156,6 @@ import MkButton from "@/components/MkButton.vue";
 import FormInput from "@/components/form/input.vue";
 import FormTextarea from "@/components/form/textarea.vue";
 import FormSwitch from "@/components/form/switch.vue";
-import FormSelect from "@/components/form/select.vue";
 import FormSplit from "@/components/form/split.vue";
 import FormFolder from "@/components/form/folder.vue";
 import FormSlot from "@/components/form/slot.vue";
@@ -171,7 +163,6 @@ import { selectFile } from "@/scripts/select-file";
 import * as os from "@/os";
 import { i18n } from "@/i18n";
 import { $i } from "@/reactiveAccount";
-import { langmap } from "@/scripts/langmap";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { host } from "@/config";
 import icon from "@/scripts/icon";
@@ -181,7 +172,6 @@ const profile = reactive({
 	description: $i?.description,
 	location: $i?.location,
 	birthday: $i?.birthday,
-	lang: $i?.lang,
 	isBot: $i?.isBot,
 	isCat: $i?.isCat,
 	speakAsCat: $i?.speakAsCat,
@@ -238,7 +228,6 @@ function save() {
 		description: convertEmptyStringToNull(profile.description),
 		location: convertEmptyStringToNull(profile.location),
 		birthday: convertEmptyStringToNull(profile.birthday),
-		lang: convertEmptyStringToNull(profile.lang),
 		isBot: !!profile.isBot,
 		isCat: !!profile.isCat,
 		speakAsCat: profile.isCat ? !!profile.speakAsCat : undefined,