From 4a6c6b4b2aaaf6d290f30555fc28592d73b37b30 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 19 Feb 2024 21:11:09 +0800 Subject: [PATCH] feat: add default visibility workspace setting --- .golangci.yaml | 2 + api/v1/workspace_service.go | 11 + bin/slash/main.go | 2 +- frontend/locales/en.json | 3 +- frontend/locales/zh.json | 3 +- .../src/components/CreateCollectionDrawer.tsx | 12 + .../src/components/CreateShortcutDrawer.tsx | 12 + .../components/setting/WorkspaceSection.tsx | 27 +- proto/api/v1/workspace_service.proto | 5 +- proto/gen/api/v1/README.md | 3 +- proto/gen/api/v1/workspace_service.pb.go | 293 +++++++++--------- proto/gen/store/README.md | 16 +- proto/gen/store/workspace_setting.pb.go | 190 +++++++----- proto/store/workspace_setting.proto | 13 + store/db/postgres/workspace_setting.go | 4 + store/db/sqlite/workspace_setting.go | 4 + 16 files changed, 373 insertions(+), 227 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index e4a0f65..7a46259 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -69,6 +69,8 @@ linters-settings: disabled: true - name: var-naming disabled: true + - name: max-control-nesting + disabled: true - name: exported arguments: - "disableStutteringCheck" diff --git a/api/v1/workspace_service.go b/api/v1/workspace_service.go index a781d01..e0b0e05 100644 --- a/api/v1/workspace_service.go +++ b/api/v1/workspace_service.go @@ -68,6 +68,8 @@ func (s *APIV2Service) GetWorkspaceSetting(ctx context.Context, _ *apiv1pb.GetWo workspaceSetting.CustomStyle = v.GetCustomStyle() } else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_SCRIPT { workspaceSetting.CustomScript = v.GetCustomScript() + } else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_DEFAULT_VISIBILITY { + workspaceSetting.DefaultVisibility = apiv1pb.Visibility(v.GetDefaultVisibility()) } else if isAdmin { // For some settings, only admin can get the value. if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_LICENSE_KEY { @@ -131,6 +133,15 @@ func (s *APIV2Service) UpdateWorkspaceSetting(ctx context.Context, request *apiv }); err != nil { return nil, status.Errorf(codes.Internal, "failed to update workspace setting: %v", err) } + } else if path == "default_visibility" { + if _, err := s.Store.UpsertWorkspaceSetting(ctx, &storepb.WorkspaceSetting{ + Key: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_DEFAULT_VISIBILITY, + Value: &storepb.WorkspaceSetting_DefaultVisibility{ + DefaultVisibility: storepb.Visibility(request.Setting.DefaultVisibility), + }, + }); err != nil { + return nil, status.Errorf(codes.Internal, "failed to update workspace setting: %v", err) + } } else { return nil, status.Errorf(codes.InvalidArgument, "invalid path: %s", path) } diff --git a/bin/slash/main.go b/bin/slash/main.go index 760a1c3..d4f949f 100644 --- a/bin/slash/main.go +++ b/bin/slash/main.go @@ -36,7 +36,7 @@ var ( rootCmd = &cobra.Command{ Use: "slash", Short: `An open source, self-hosted bookmarks and link sharing platform.`, - Run: func(_cmd *cobra.Command, _args []string) { + Run: func(_ *cobra.Command, _ []string) { ctx, cancel := context.WithCancel(context.Background()) dbDriver, err := db.NewDBDriver(serverProfile) if err != nil { diff --git a/frontend/locales/en.json b/frontend/locales/en.json index fab869d..c39886c 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -76,7 +76,8 @@ "enable-user-signup": { "self": "Enable user signup", "description": "Once enabled, other users can signup." - } + }, + "default-visibility": "Default visibility" } } } diff --git a/frontend/locales/zh.json b/frontend/locales/zh.json index 6b5030d..ea9cf18 100644 --- a/frontend/locales/zh.json +++ b/frontend/locales/zh.json @@ -76,7 +76,8 @@ "enable-user-signup": { "self": "启用用户注册", "description": "允许其他用户注册新账号" - } + }, + "default-visibility": "默认可见性" } } } diff --git a/frontend/web/src/components/CreateCollectionDrawer.tsx b/frontend/web/src/components/CreateCollectionDrawer.tsx index e7dc1d9..c7a6fc0 100644 --- a/frontend/web/src/components/CreateCollectionDrawer.tsx +++ b/frontend/web/src/components/CreateCollectionDrawer.tsx @@ -5,6 +5,7 @@ import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; import useCollectionStore from "@/stores/v1/collection"; import useShortcutStore from "@/stores/v1/shortcut"; +import useWorkspaceStore from "@/stores/v1/workspace"; import { Collection } from "@/types/proto/api/v1/collection_service"; import { Visibility } from "@/types/proto/api/v1/common"; import { Shortcut } from "@/types/proto/api/v1/shortcut_service"; @@ -26,6 +27,7 @@ interface State { const CreateCollectionDrawer: React.FC = (props: Props) => { const { onClose, onConfirm, collectionId } = props; const { t } = useTranslation(); + const workspaceStore = useWorkspaceStore(); const collectionStore = useCollectionStore(); const shortcutList = useShortcutStore().getShortcutList(); const [state, setState] = useState({ @@ -49,6 +51,16 @@ const CreateCollectionDrawer: React.FC = (props: Props) => { }) .filter((shortcut) => !selectedShortcuts.find((selectedShortcut) => selectedShortcut.id === shortcut.id)); + useEffect(() => { + if (workspaceStore.setting.defaultVisibility !== Visibility.VISIBILITY_UNSPECIFIED) { + setPartialState({ + collectionCreate: Object.assign(state.collectionCreate, { + visibility: workspaceStore.setting.defaultVisibility, + }), + }); + } + }, []); + useEffect(() => { (async () => { if (collectionId) { diff --git a/frontend/web/src/components/CreateShortcutDrawer.tsx b/frontend/web/src/components/CreateShortcutDrawer.tsx index e6a68c5..e716f63 100644 --- a/frontend/web/src/components/CreateShortcutDrawer.tsx +++ b/frontend/web/src/components/CreateShortcutDrawer.tsx @@ -17,6 +17,7 @@ import { useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; import useShortcutStore, { getShortcutUpdateMask } from "@/stores/v1/shortcut"; +import useWorkspaceStore from "@/stores/v1/workspace"; import { Visibility } from "@/types/proto/api/v1/common"; import { Shortcut } from "@/types/proto/api/v1/shortcut_service"; import { convertVisibilityFromPb } from "@/utils/visibility"; @@ -49,6 +50,7 @@ const CreateShortcutDrawer: React.FC = (props: Props) => { }), }); const shortcutStore = useShortcutStore(); + const workspaceStore = useWorkspaceStore(); const [showOpenGraphMetadata, setShowOpenGraphMetadata] = useState(false); const shortcutList = shortcutStore.getShortcutList(); const [tag, setTag] = useState(""); @@ -57,6 +59,16 @@ const CreateShortcutDrawer: React.FC = (props: Props) => { const loadingState = useLoading(!isCreating); const requestState = useLoading(false); + useEffect(() => { + if (workspaceStore.setting.defaultVisibility !== Visibility.VISIBILITY_UNSPECIFIED) { + setPartialState({ + shortcutCreate: Object.assign(state.shortcutCreate, { + visibility: workspaceStore.setting.defaultVisibility, + }), + }); + } + }, []); + useEffect(() => { if (shortcutId) { const shortcut = shortcutStore.getShortcutById(shortcutId); diff --git a/frontend/web/src/components/setting/WorkspaceSection.tsx b/frontend/web/src/components/setting/WorkspaceSection.tsx index 80a221e..1c7d5b8 100644 --- a/frontend/web/src/components/setting/WorkspaceSection.tsx +++ b/frontend/web/src/components/setting/WorkspaceSection.tsx @@ -1,10 +1,11 @@ -import { Button, Checkbox, Input, Textarea } from "@mui/joy"; +import { Button, Checkbox, Input, Select, Textarea, Option } from "@mui/joy"; import { isEqual } from "lodash-es"; import { useRef, useState } from "react"; import toast from "react-hot-toast"; import { useTranslation } from "react-i18next"; import { workspaceServiceClient } from "@/grpcweb"; import useWorkspaceStore from "@/stores/v1/workspace"; +import { Visibility } from "@/types/proto/api/v1/common"; import { WorkspaceSetting } from "@/types/proto/api/v1/workspace_service"; const WorkspaceSection: React.FC = () => { @@ -35,6 +36,13 @@ const WorkspaceSection: React.FC = () => { }); }; + const handleDefaultVisibilityChange = async (value: Visibility) => { + setWorkspaceSetting({ + ...workspaceSetting, + defaultVisibility: value, + }); + }; + const handleSaveWorkspaceSetting = async () => { const updateMask: string[] = []; if (!isEqual(originalWorkspaceSetting.current.enableSignup, workspaceSetting.enableSignup)) { @@ -46,6 +54,9 @@ const WorkspaceSection: React.FC = () => { if (!isEqual(originalWorkspaceSetting.current.customStyle, workspaceSetting.customStyle)) { updateMask.push("custom_style"); } + if (!isEqual(originalWorkspaceSetting.current.defaultVisibility, workspaceSetting.defaultVisibility)) { + updateMask.push("default_visibility"); + } if (updateMask.length === 0) { toast.error("No changes made"); return; @@ -59,6 +70,7 @@ const WorkspaceSection: React.FC = () => { }) ).setting as WorkspaceSetting; setWorkspaceSetting(setting); + await workspaceStore.fetchWorkspaceSetting(); originalWorkspaceSetting.current = setting; toast.success("Workspace setting saved successfully"); } catch (error: any) { @@ -97,6 +109,19 @@ const WorkspaceSection: React.FC = () => { />

{t("settings.workspace.enable-user-signup.description")}

+
+
+ {t("settings.workspace.default-visibility")} +
+ +