From 8f056d107110b7f62f1304de0fc59ace69c9a115 Mon Sep 17 00:00:00 2001
From: Philipinho <16838612+Philipinho@users.noreply.github.com>
Date: Wed, 3 Jul 2024 11:00:42 +0100
Subject: [PATCH] add full page width preference
---
.../components/layouts/global/top-menu.tsx | 47 ++++++-------------
.../src/features/editor/full-editor.tsx | 15 +++++-
.../features/editor/styles/editor.module.css | 1 -
.../features/user/atoms/current-user-atom.ts | 13 ++++-
.../user/components/page-width-pref.tsx | 42 +++++++++++++++++
.../src/features/user/types/user.types.ts | 9 +++-
.../settings/account/account-preferences.tsx | 4 ++
.../src/core/user/dto/update-user.dto.ts | 6 ++-
apps/server/src/core/user/user.service.ts | 17 +++++++
.../src/database/repos/user/user.repo.ts | 19 ++++++++
10 files changed, 135 insertions(+), 38 deletions(-)
create mode 100644 apps/client/src/features/user/components/page-width-pref.tsx
diff --git a/apps/client/src/components/layouts/global/top-menu.tsx b/apps/client/src/components/layouts/global/top-menu.tsx
index 8977580..cd7527c 100644
--- a/apps/client/src/components/layouts/global/top-menu.tsx
+++ b/apps/client/src/components/layouts/global/top-menu.tsx
@@ -1,5 +1,6 @@
-import { Avatar, Group, Menu, rem, UnstyledButton, Text } from "@mantine/core";
+import { Group, Menu, UnstyledButton, Text } from "@mantine/core";
import {
+ IconBrush,
IconChevronDown,
IconLogout,
IconSettings,
@@ -38,10 +39,7 @@ export default function TopMenu() {
{workspace.name}
-
+
@@ -51,12 +49,7 @@ export default function TopMenu() {
- }
+ leftSection={}
>
Workspace settings
@@ -64,12 +57,7 @@ export default function TopMenu() {
- }
+ leftSection={}
>
Manage members
@@ -98,27 +86,22 @@ export default function TopMenu() {
- }
+ leftSection={}
>
My profile
+ }
+ >
+ My preferences
+
+
-
- }
- >
+ }>
Logout
diff --git a/apps/client/src/features/editor/full-editor.tsx b/apps/client/src/features/editor/full-editor.tsx
index d2d54eb..2368425 100644
--- a/apps/client/src/features/editor/full-editor.tsx
+++ b/apps/client/src/features/editor/full-editor.tsx
@@ -2,6 +2,9 @@ import classes from "@/features/editor/styles/editor.module.css";
import React from "react";
import { TitleEditor } from "@/features/editor/title-editor";
import PageEditor from "@/features/editor/page-editor";
+import { Container } from "@mantine/core";
+import { useAtom } from "jotai/index";
+import { userAtom } from "@/features/user/atoms/current-user-atom.ts";
const MemoizedTitleEditor = React.memo(TitleEditor);
const MemoizedPageEditor = React.memo(PageEditor);
@@ -21,8 +24,16 @@ export function FullEditor({
spaceSlug,
editable,
}: FullEditorProps) {
+ const [user] = useAtom(userAtom);
+ const fullPageWidth = user.settings?.preferences?.fullPageWidth;
+
return (
-
+
-
+
);
}
diff --git a/apps/client/src/features/editor/styles/editor.module.css b/apps/client/src/features/editor/styles/editor.module.css
index acc381a..c281ba3 100644
--- a/apps/client/src/features/editor/styles/editor.module.css
+++ b/apps/client/src/features/editor/styles/editor.module.css
@@ -1,5 +1,4 @@
.editor {
- max-width: 800px;
height: 100%;
padding: 8px 20px;
margin: 64px auto;
diff --git a/apps/client/src/features/user/atoms/current-user-atom.ts b/apps/client/src/features/user/atoms/current-user-atom.ts
index aa7ffff..49eab9d 100644
--- a/apps/client/src/features/user/atoms/current-user-atom.ts
+++ b/apps/client/src/features/user/atoms/current-user-atom.ts
@@ -1,5 +1,16 @@
import { atomWithStorage } from "jotai/utils";
import { ICurrentUser } from "@/features/user/types/user.types";
+import { focusAtom } from "jotai-optics";
-export const currentUserAtom = atomWithStorage("currentUser", null);
+export const currentUserAtom = atomWithStorage(
+ "currentUser",
+ null,
+);
+
+export const userAtom = focusAtom(currentUserAtom, (optic) =>
+ optic.prop("user"),
+);
+export const workspaceAtom = focusAtom(currentUserAtom, (optic) =>
+ optic.prop("workspace"),
+);
diff --git a/apps/client/src/features/user/components/page-width-pref.tsx b/apps/client/src/features/user/components/page-width-pref.tsx
new file mode 100644
index 0000000..fecd231
--- /dev/null
+++ b/apps/client/src/features/user/components/page-width-pref.tsx
@@ -0,0 +1,42 @@
+import { Group, Text, Switch } from "@mantine/core";
+import { useAtom } from "jotai/index";
+import { userAtom } from "@/features/user/atoms/current-user-atom.ts";
+import { updateUser } from "@/features/user/services/user-service.ts";
+import React, { useState } from "react";
+
+export default function PageWidthPref() {
+ return (
+
+
+ Full page width
+
+ Choose your preferred page width.
+
+
+
+
+
+ );
+}
+
+function WidthToggle() {
+ const [user, setUser] = useAtom(userAtom);
+ const [checked, setChecked] = useState(
+ user.settings?.preferences?.fullPageWidth,
+ );
+
+ const handleChange = async (event: React.ChangeEvent) => {
+ const value = event.currentTarget.checked;
+ const updatedUser = await updateUser({ fullPageWidth: value });
+ setChecked(value);
+ setUser(updatedUser);
+ };
+
+ return (
+
+ );
+}
diff --git a/apps/client/src/features/user/types/user.types.ts b/apps/client/src/features/user/types/user.types.ts
index 0a92c47..a729073 100644
--- a/apps/client/src/features/user/types/user.types.ts
+++ b/apps/client/src/features/user/types/user.types.ts
@@ -7,7 +7,7 @@ export interface IUser {
emailVerifiedAt: Date;
avatarUrl: string;
timezone: string;
- settings: any;
+ settings: IUserSettings;
invitedById: string;
lastLoginAt: string;
lastActiveAt: Date;
@@ -17,9 +17,16 @@ export interface IUser {
workspaceId: string;
deactivatedAt: Date;
deletedAt: Date;
+ fullPageWidth: boolean; // used for update
}
export interface ICurrentUser {
user: IUser;
workspace: IWorkspace;
}
+
+export interface IUserSettings {
+ preferences: {
+ fullPageWidth: boolean;
+ };
+}
diff --git a/apps/client/src/pages/settings/account/account-preferences.tsx b/apps/client/src/pages/settings/account/account-preferences.tsx
index 7e4df93..d312e18 100644
--- a/apps/client/src/pages/settings/account/account-preferences.tsx
+++ b/apps/client/src/pages/settings/account/account-preferences.tsx
@@ -1,11 +1,15 @@
import SettingsTitle from "@/components/settings/settings-title.tsx";
import AccountTheme from "@/features/user/components/account-theme.tsx";
+import PageWidthPref from "@/features/user/components/page-width-pref.tsx";
+import { Divider } from "@mantine/core";
export default function AccountPreferences() {
return (
<>
+
+
>
);
}
diff --git a/apps/server/src/core/user/dto/update-user.dto.ts b/apps/server/src/core/user/dto/update-user.dto.ts
index d83b4f2..c8ded9f 100644
--- a/apps/server/src/core/user/dto/update-user.dto.ts
+++ b/apps/server/src/core/user/dto/update-user.dto.ts
@@ -1,6 +1,6 @@
import { OmitType, PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from '../../auth/dto/create-user.dto';
-import { IsOptional, IsString } from 'class-validator';
+import { IsBoolean, IsOptional, IsString } from 'class-validator';
export class UpdateUserDto extends PartialType(
OmitType(CreateUserDto, ['password'] as const),
@@ -8,4 +8,8 @@ export class UpdateUserDto extends PartialType(
@IsOptional()
@IsString()
avatarUrl: string;
+
+ @IsOptional()
+ @IsBoolean()
+ fullPageWidth: boolean;
}
diff --git a/apps/server/src/core/user/user.service.ts b/apps/server/src/core/user/user.service.ts
index 662fff7..9b6527b 100644
--- a/apps/server/src/core/user/user.service.ts
+++ b/apps/server/src/core/user/user.service.ts
@@ -20,10 +20,19 @@ export class UserService {
workspaceId: string,
) {
const user = await this.userRepo.findById(userId, workspaceId);
+
if (!user) {
throw new NotFoundException('User not found');
}
+ // preference update
+ if (typeof updateUserDto.fullPageWidth !== 'undefined') {
+ return this.updateUserPageWidthPreference(
+ userId,
+ updateUserDto.fullPageWidth,
+ );
+ }
+
if (updateUserDto.name) {
user.name = updateUserDto.name;
}
@@ -42,4 +51,12 @@ export class UserService {
await this.userRepo.updateUser(updateUserDto, userId, workspaceId);
return user;
}
+
+ async updateUserPageWidthPreference(userId: string, fullPageWidth: boolean) {
+ return this.userRepo.updatePreference(
+ userId,
+ 'fullPageWidth',
+ fullPageWidth,
+ );
+ }
}
diff --git a/apps/server/src/database/repos/user/user.repo.ts b/apps/server/src/database/repos/user/user.repo.ts
index 8ec07a9..a521d67 100644
--- a/apps/server/src/database/repos/user/user.repo.ts
+++ b/apps/server/src/database/repos/user/user.repo.ts
@@ -15,6 +15,7 @@ import {
executeWithPagination,
PaginationResult,
} from '@docmost/db/pagination/pagination';
+import { sql } from 'kysely';
@Injectable()
export class UserRepo {
@@ -157,6 +158,24 @@ export class UserRepo {
return result;
}
+ async updatePreference(
+ userId: string,
+ prefKey: string,
+ prefValue: string | boolean,
+ ) {
+ return await this.db
+ .updateTable('users')
+ .set({
+ settings: sql`COALESCE(settings, '{}'::jsonb)
+ || jsonb_build_object('preferences', COALESCE(settings->'preferences', '{}'::jsonb)
+ || jsonb_build_object('${sql.raw(prefKey)}', ${sql.lit(prefValue)}))`,
+ updatedAt: new Date(),
+ })
+ .where('id', '=', userId)
+ .returning(this.baseFields)
+ .executeTakeFirst();
+ }
+
/*
async getSpaceIds(
workspaceId: string,