summary refs log tree commit diff
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/entities/UserSettingsProtos.ts170
-rw-r--r--src/util/entities/index.ts1
-rw-r--r--src/util/migration/postgres/1752157979333-UserSettingsProtos.ts17
-rw-r--r--src/util/schemas/SettingsProtoUpdateSchema.ts47
-rw-r--r--src/util/schemas/index.ts1
-rw-r--r--src/util/schemas/responses/SettingsProtoUpdateResponse.ts53
-rw-r--r--src/util/schemas/responses/index.ts1
7 files changed, 290 insertions, 0 deletions
diff --git a/src/util/entities/UserSettingsProtos.ts b/src/util/entities/UserSettingsProtos.ts
new file mode 100644

index 00000000..b7b8f607 --- /dev/null +++ b/src/util/entities/UserSettingsProtos.ts
@@ -0,0 +1,170 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +import { Column, Entity, JoinColumn, OneToOne, RelationId } from "typeorm"; +import { BaseClassWithoutId, PrimaryIdColumn } from "./BaseClass"; +import { dbEngine } from "@spacebar/util"; +import { User } from "./User"; +import { + FrecencyUserSettings, + PreloadedUserSettings, + PreloadedUserSettings_LaunchPadMode, + PreloadedUserSettings_SwipeRightToLeftMode, + PreloadedUserSettings_Theme, + PreloadedUserSettings_TimestampHourCycle, + PreloadedUserSettings_UIDensity, +} from "discord-protos"; + +@Entity({ + name: "user_settings_protos", + engine: dbEngine, +}) +export class UserSettingsProtos extends BaseClassWithoutId { + @OneToOne(() => User, { + cascade: true, + orphanedRowAction: "delete", + eager: false, + }) + @JoinColumn({ name: "user_id" }) + user: User; + + @PrimaryIdColumn({ type: "text" }) + user_id: string; + + @Column({ nullable: true, type: String, name: "userSettings" }) + _userSettings: string | undefined; + + @Column({ nullable: true, type: String, name: "frecencySettings" }) + _frecencySettings: string | undefined; + + // @Column({nullable: true, type: "simple-json"}) + // testSettings: {}; + + bigintReplacer(_key: string, value: any): any { + if (typeof value === "bigint") { + return (value as bigint).toString(); + } else if (value instanceof Uint8Array) { + return { + __type: "Uint8Array", + data: Array.from(value as Uint8Array) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""), + }; + } else { + return value; + } + } + + bigintReviver(_key: string, value: any): any { + if (typeof value === "string" && /^\d+n$/.test(value)) { + return BigInt((value as string).slice(0, -1)); + } else if ( + typeof value === "object" && + value !== null && + "__type" in value + ) { + if (value.__type === "Uint8Array" && "data" in value) { + return new Uint8Array( + value.data + .match(/.{1,2}/g)! + .map((byte: string) => parseInt(byte, 16)), + ); + } + } + return value; + } + + get userSettings(): PreloadedUserSettings | undefined { + if (!this._userSettings) return undefined; + return PreloadedUserSettings.fromJson( + JSON.parse(this._userSettings, this.bigintReviver), + ); + } + + set userSettings(value: PreloadedUserSettings | undefined) { + if (value) { + // this._userSettings = JSON.stringify(value, this.bigintReplacer); + this._userSettings = PreloadedUserSettings.toJsonString(value); + } else { + this._userSettings = undefined; + } + } + + get frecencySettings(): FrecencyUserSettings | undefined { + if (!this._frecencySettings) return undefined; + return FrecencyUserSettings.fromJson( + JSON.parse(this._frecencySettings, this.bigintReviver), + ); + } + + set frecencySettings(value: FrecencyUserSettings | undefined) { + if (value) { + this._frecencySettings = JSON.stringify(value, this.bigintReplacer); + } else { + this._frecencySettings = undefined; + } + } + + static async getOrCreate(user_id: string): Promise<UserSettingsProtos> { + const user = await User.findOneOrFail({ + where: { id: user_id }, + select: { settings: true }, + }); + let userSettings = await UserSettingsProtos.findOne({ + where: { user_id }, + }); + let modified = false; + if (!userSettings) { + userSettings = UserSettingsProtos.create({ + user_id, + }); + modified = true; + } + + if (!userSettings.userSettings) { + userSettings.userSettings = PreloadedUserSettings.create({ + ads: { + alwaysDeliver: false, + }, + appearance: { + developerMode: user.settings?.developer_mode ?? true, + theme: PreloadedUserSettings_Theme.DARK, + mobileRedesignDisabled: true, + launchPadMode: + PreloadedUserSettings_LaunchPadMode.LAUNCH_PAD_DISABLED, + swipeRightToLeftMode: + PreloadedUserSettings_SwipeRightToLeftMode.SWIPE_RIGHT_TO_LEFT_REPLY, + timestampHourCycle: + PreloadedUserSettings_TimestampHourCycle.AUTO, + uiDensity: + PreloadedUserSettings_UIDensity.UI_DENSITY_COMPACT, + }, + }); + modified = true; + } + + if (!userSettings.frecencySettings) { + userSettings.frecencySettings = FrecencyUserSettings.create({}); + modified = true; + } + + if (modified) userSettings = await userSettings.save(); + + return userSettings; + } +} diff --git a/src/util/entities/index.ts b/src/util/entities/index.ts
index dd967ce5..4c95e83a 100644 --- a/src/util/entities/index.ts +++ b/src/util/entities/index.ts
@@ -55,6 +55,7 @@ export * from "./TeamMember"; export * from "./Template"; export * from "./User"; export * from "./UserSettings"; +export * from "./UserSettingsProtos"; export * from "./ValidRegistrationTokens"; export * from "./VoiceState"; export * from "./Webhook"; diff --git a/src/util/migration/postgres/1752157979333-UserSettingsProtos.ts b/src/util/migration/postgres/1752157979333-UserSettingsProtos.ts new file mode 100644
index 00000000..0633e144 --- /dev/null +++ b/src/util/migration/postgres/1752157979333-UserSettingsProtos.ts
@@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class UserSettingsProtos1752157979333 implements MigrationInterface { + name = 'UserSettingsProtos1752157979333' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`DROP TABLE IF EXISTS "user_settings_protos"`); + await queryRunner.query(`CREATE TABLE "user_settings_protos" ("user_id" character varying NOT NULL, "userSettings" text, "frecencySettings" text, CONSTRAINT "PK_8ff3d1961a48b693810c9f99853" PRIMARY KEY ("user_id"))`); + await queryRunner.query(`ALTER TABLE "user_settings_protos" ADD CONSTRAINT "FK_8ff3d1961a48b693810c9f99853" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "user_settings_protos" DROP CONSTRAINT "FK_8ff3d1961a48b693810c9f99853"`); + await queryRunner.query(`DROP TABLE "user_settings_protos"`); + } + +} diff --git a/src/util/schemas/SettingsProtoUpdateSchema.ts b/src/util/schemas/SettingsProtoUpdateSchema.ts new file mode 100644
index 00000000..2b930bc0 --- /dev/null +++ b/src/util/schemas/SettingsProtoUpdateSchema.ts
@@ -0,0 +1,47 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +import { JsonValue } from "@protobuf-ts/runtime"; + +export interface SettingsProtoUpdateSchema { + settings: string; + required_data_version?: number; +} + +export interface SettingsProtoUpdateJsonSchema { + settings: JsonValue; + required_data_version?: number; +} + +// TODO: these dont work with schema validation +// typed JSON schemas: +// export interface SettingsProtoUpdatePreloadedUserSettingsSchema { +// settings: PreloadedUserSettings; +// required_data_version?: number; +// } +// +// export interface SettingsProtoUpdateFrecencyUserSettingsSchema { +// settings: FrecencyUserSettings; +// required_data_version?: number; +// } + +// TODO: what is this? +// export interface SettingsProtoUpdateTestSettingsSchema { +// settings: {}; +// required_data_version?: number; +// } \ No newline at end of file diff --git a/src/util/schemas/index.ts b/src/util/schemas/index.ts
index a7aaa1fa..4088c158 100644 --- a/src/util/schemas/index.ts +++ b/src/util/schemas/index.ts
@@ -69,6 +69,7 @@ export * from "./responses"; export * from "./RoleModifySchema"; export * from "./RolePositionUpdateSchema"; export * from "./SelectProtocolSchema"; +export * from "./SettingsProtoUpdateSchema"; export * from "./StreamCreateSchema"; export * from "./StreamDeleteSchema"; export * from "./StreamWatchSchema"; diff --git a/src/util/schemas/responses/SettingsProtoUpdateResponse.ts b/src/util/schemas/responses/SettingsProtoUpdateResponse.ts new file mode 100644
index 00000000..42f8621a --- /dev/null +++ b/src/util/schemas/responses/SettingsProtoUpdateResponse.ts
@@ -0,0 +1,53 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +import { JsonValue } from "@protobuf-ts/runtime"; + +export interface SettingsProtoResponse { + settings: string; +} + +export interface SettingsProtoUpdateResponse extends SettingsProtoResponse { + out_of_date?: boolean; +} + +export interface SettingsProtoJsonResponse { + settings: JsonValue; +} + +export interface SettingsProtoUpdateJsonResponse extends SettingsProtoJsonResponse { + out_of_date?: boolean; +} + +// TODO: these dont work with schemas validation +// Typed JSON schemas: +// export interface SettingsProtoUpdatePreloadedUserSettingsJsonResponse { +// settings: PreloadedUserSettings; +// out_of_date?: boolean; +// } +// +// export interface SettingsProtoUpdateFrecencyUserSettingsJsonResponse { +// settings: FrecencyUserSettings; +// out_of_date?: boolean; +// } + +// TODO: what is this? +// export interface SettingsProtoUpdateTestSettingsJsonResponse { +// settings: {}; +// out_of_date?: boolean; +// } diff --git a/src/util/schemas/responses/index.ts b/src/util/schemas/responses/index.ts
index 29537cf7..8949924b 100644 --- a/src/util/schemas/responses/index.ts +++ b/src/util/schemas/responses/index.ts
@@ -47,6 +47,7 @@ export * from "./LocationMetadataResponse"; export * from "./MemberJoinGuildResponse"; export * from "./OAuthAuthorizeResponse"; export * from "./RefreshUrlsResponse"; +export * from "./SettingsProtoUpdateResponse"; export * from "./TeamListResponse"; export * from "./Tenor"; export * from "./TokenResponse";