diff --git a/server/router/api/v1/workspace_setting_service.go b/server/router/api/v1/workspace_setting_service.go index 04c8ce789..c14ac0a0e 100644 --- a/server/router/api/v1/workspace_setting_service.go +++ b/server/router/api/v1/workspace_setting_service.go @@ -46,6 +46,17 @@ func (s *APIV1Service) GetWorkspaceSetting(ctx context.Context, request *v1pb.Ge return nil, status.Errorf(codes.NotFound, "workspace setting not found") } + // For storage setting, only host can get it. + if workspaceSetting.Key == storepb.WorkspaceSettingKey_STORAGE { + user, err := s.GetCurrentUser(ctx) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err) + } + if user == nil || user.Role != store.RoleHost { + return nil, status.Errorf(codes.PermissionDenied, "permission denied") + } + } + return convertWorkspaceSettingFromStore(workspaceSetting), nil } diff --git a/web/src/components/Settings/MemoRelatedSettings.tsx b/web/src/components/Settings/MemoRelatedSettings.tsx new file mode 100644 index 000000000..32dc196a9 --- /dev/null +++ b/web/src/components/Settings/MemoRelatedSettings.tsx @@ -0,0 +1,81 @@ +import { Button, Input, Switch } from "@mui/joy"; +import { isEqual } from "lodash-es"; +import { useState } from "react"; +import { WorkspaceSettingPrefix, useWorkspaceSettingStore } from "@/store/v1"; +import { WorkspaceMemoRelatedSetting } from "@/types/proto/api/v1/workspace_setting_service"; +import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting"; +import { useTranslate } from "@/utils/i18n"; + +const MemoRelatedSettings = () => { + const t = useTranslate(); + const workspaceSettingStore = useWorkspaceSettingStore(); + const originalSetting = WorkspaceMemoRelatedSetting.fromPartial( + workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.MEMO_RELATED)?.memoRelatedSetting || {}, + ); + const [memoRelatedSetting, setMemoRelatedSetting] = useState(originalSetting); + + const updatePartialSetting = (partial: Partial) => { + const newWorkspaceMemoRelatedSetting = WorkspaceMemoRelatedSetting.fromPartial({ + ...memoRelatedSetting, + ...partial, + }); + setMemoRelatedSetting(newWorkspaceMemoRelatedSetting); + }; + + const updateSetting = async () => { + await workspaceSettingStore.setWorkspaceSetting({ + name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.MEMO_RELATED}`, + memoRelatedSetting, + }); + }; + + return ( +
+

Memo related settings

+
+ {t("setting.system-section.disable-public-memos")} + updatePartialSetting({ disallowPublicVisible: event.target.checked })} + /> +
+
+ {t("setting.system-section.display-with-updated-time")} + updatePartialSetting({ displayWithUpdateTime: event.target.checked })} + /> +
+
+ {t("setting.system-section.enable-auto-compact")} + updatePartialSetting({ enableAutoCompact: event.target.checked })} + /> +
+
+ {t("setting.system-section.enable-double-click-to-edit")} + updatePartialSetting({ enableDoubleClickEdit: event.target.checked })} + /> +
+
+ Content length limit(Byte) + updatePartialSetting({ contentLengthLimit: Number(event.target.value) })} + /> +
+
+ +
+
+ ); +}; + +export default MemoRelatedSettings; diff --git a/web/src/components/Settings/WorkspaceSection.tsx b/web/src/components/Settings/WorkspaceSection.tsx index 4b321710b..3a7e57817 100644 --- a/web/src/components/Settings/WorkspaceSection.tsx +++ b/web/src/components/Settings/WorkspaceSection.tsx @@ -1,10 +1,10 @@ -import { Button, Divider, Input, Switch, Textarea } from "@mui/joy"; +import { Button, Switch, Textarea } from "@mui/joy"; import { useState } from "react"; import { toast } from "react-hot-toast"; import { Link } from "react-router-dom"; import { workspaceSettingServiceClient } from "@/grpcweb"; import { WorkspaceSettingPrefix, useWorkspaceSettingStore } from "@/store/v1"; -import { WorkspaceGeneralSetting, WorkspaceMemoRelatedSetting } from "@/types/proto/api/v1/workspace_setting_service"; +import { WorkspaceGeneralSetting } from "@/types/proto/api/v1/workspace_setting_service"; import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting"; import { useTranslate } from "@/utils/i18n"; import { showCommonDialog } from "../Dialog/CommonDialog"; @@ -17,11 +17,6 @@ const WorkspaceSection = () => { const [workspaceGeneralSetting, setWorkspaceGeneralSetting] = useState( WorkspaceGeneralSetting.fromPartial(workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.GENERAL)?.generalSetting || {}), ); - const [workspaceMemoRelatedSetting, setWorkspaceMemoRelatedSetting] = useState( - WorkspaceMemoRelatedSetting.fromPartial( - workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.MEMO_RELATED)?.memoRelatedSetting || {}, - ), - ); const handleAllowSignUpChanged = async (value: boolean) => { const setting = { ...workspaceGeneralSetting, disallowSignup: !value }; @@ -104,56 +99,6 @@ const WorkspaceSection = () => { toast.success(t("message.update-succeed")); }; - const handleDisablePublicMemosChanged = async (value: boolean) => { - const update: WorkspaceMemoRelatedSetting = { ...workspaceMemoRelatedSetting, disallowPublicVisible: value }; - setWorkspaceMemoRelatedSetting(update); - await workspaceSettingStore.setWorkspaceSetting({ - name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.MEMO_RELATED}`, - memoRelatedSetting: update, - }); - }; - - const handleMemoDisplayWithUpdatedTs = async (value: boolean) => { - const update: WorkspaceMemoRelatedSetting = { ...workspaceMemoRelatedSetting, displayWithUpdateTime: value }; - setWorkspaceMemoRelatedSetting(update); - await workspaceSettingStore.setWorkspaceSetting({ - name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.MEMO_RELATED}`, - memoRelatedSetting: update, - }); - }; - - const handleMemoEnableAutoCompact = async (value: boolean) => { - const update: WorkspaceMemoRelatedSetting = { ...workspaceMemoRelatedSetting, enableAutoCompact: value }; - setWorkspaceMemoRelatedSetting(update); - await workspaceSettingStore.setWorkspaceSetting({ - name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.MEMO_RELATED}`, - memoRelatedSetting: update, - }); - }; - - const handleMemoEnableDoubleClickToEdit = async (value: boolean) => { - const update: WorkspaceMemoRelatedSetting = { ...workspaceMemoRelatedSetting, enableDoubleClickEdit: value }; - setWorkspaceMemoRelatedSetting(update); - await workspaceSettingStore.setWorkspaceSetting({ - name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.MEMO_RELATED}`, - memoRelatedSetting: update, - }); - }; - - const handleMemoContentLengthLimitChanges = async (value: number) => { - if (value < 8 * 1024) { - toast.error("Content length limit should be greater than 8KB"); - return; - } - - const update: WorkspaceMemoRelatedSetting = { ...workspaceMemoRelatedSetting, contentLengthLimit: value }; - setWorkspaceMemoRelatedSetting(update); - await workspaceSettingStore.setWorkspaceSetting({ - name: `${WorkspaceSettingPrefix}${WorkspaceSettingKey.MEMO_RELATED}`, - memoRelatedSetting: update, - }); - }; - return (

{t("common.basic")}

@@ -225,45 +170,6 @@ const WorkspaceSection = () => {
- -

Memo related settings

-
- {t("setting.system-section.disable-public-memos")} - handleDisablePublicMemosChanged(event.target.checked)} - /> -
-
- {t("setting.system-section.display-with-updated-time")} - handleMemoDisplayWithUpdatedTs(event.target.checked)} - /> -
-
- {t("setting.system-section.enable-auto-compact")} - handleMemoEnableAutoCompact(event.target.checked)} - /> -
-
- {t("setting.system-section.enable-double-click-to-edit")} - handleMemoEnableDoubleClickToEdit(event.target.checked)} - /> -
-
- Content length limit(Byte) - handleMemoContentLengthLimitChanges(Number(event.target.value))} - /> -
); }; diff --git a/web/src/locales/en.json b/web/src/locales/en.json index 42401b4fa..8b15ebaa2 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -297,7 +297,8 @@ "max-upload-size": "Maximum upload size (MiB)", "max-upload-size-hint": "Recommended value is 32 MiB.", "server-name": "Server Name" - } + }, + "memo-related": "Memo" }, "tag": { "all-tags": "All Tags", diff --git a/web/src/pages/Setting.tsx b/web/src/pages/Setting.tsx index d8cc48f01..fac115e75 100644 --- a/web/src/pages/Setting.tsx +++ b/web/src/pages/Setting.tsx @@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import Icon from "@/components/Icon"; import MobileHeader from "@/components/MobileHeader"; import MemberSection from "@/components/Settings/MemberSection"; +import MemoRelatedSettings from "@/components/Settings/MemoRelatedSettings"; import MyAccountSection from "@/components/Settings/MyAccountSection"; import PreferencesSection from "@/components/Settings/PreferencesSection"; import SSOSection from "@/components/Settings/SSOSection"; @@ -17,19 +18,20 @@ import { User_Role } from "@/types/proto/api/v1/user_service"; import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting"; import { useTranslate } from "@/utils/i18n"; -type SettingSection = "my-account" | "preference" | "member" | "system" | "storage" | "sso"; +type SettingSection = "my-account" | "preference" | "member" | "system" | "memo-related" | "storage" | "sso"; interface State { selectedSection: SettingSection; } const BASIC_SECTIONS: SettingSection[] = ["my-account", "preference"]; -const ADMIN_SECTIONS: SettingSection[] = ["member", "system", "storage", "sso"]; +const ADMIN_SECTIONS: SettingSection[] = ["member", "system", "memo-related", "storage", "sso"]; const SECTION_ICON_MAP: Record = { "my-account": Icon.User, preference: Icon.Cog, member: Icon.Users, system: Icon.Settings2, + "memo-related": Icon.Library, storage: Icon.Database, sso: Icon.Key, }; @@ -125,6 +127,8 @@ const Setting = () => { ) : state.selectedSection === "system" ? ( + ) : state.selectedSection === "memo-related" ? ( + ) : state.selectedSection === "storage" ? ( ) : state.selectedSection === "sso" ? (