diff --git a/apps/ikigai/components/DocumentV2/LeftSide/index.tsx b/apps/ikigai/components/DocumentV2/LeftSide/index.tsx index b4eee6d9..827054e2 100644 --- a/apps/ikigai/components/DocumentV2/LeftSide/index.tsx +++ b/apps/ikigai/components/DocumentV2/LeftSide/index.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { Divider, Tooltip, Typography } from "antd"; import { t, Trans } from "@lingui/macro"; import styled, { useTheme } from "styled-components"; -import { PlusOutlined, SettingOutlined } from "@ant-design/icons"; +import { PlusOutlined, SettingOutlined, SwapOutlined } from "@ant-design/icons"; import useAuthUserStore from "context/ZustandAuthStore"; import EditProfileModal from "components/UserCredential/EditProfileModal"; @@ -17,6 +17,7 @@ import { TextButtonWithHover } from "components/common/Button"; import usePermission from "hook/UsePermission"; import useSpaceStore from "context/ZustandSpaceStore"; import CreateContentButton from "components/common/LearningModuleDnd/CreateContentButton"; +import SwitchSpace from "components/SwitchSpace"; const LeftSide = () => { const allow = usePermission(); @@ -24,6 +25,7 @@ const LeftSide = () => { const me = useAuthUserStore((state) => state.currentUser?.userMe); const spaceDocuments = useDocumentStore((state) => state.spaceDocuments); const [openProfile, setOpenProfile] = useState(false); + const [openSwitchSpace, setOpenSwitchSpace] = useState(false); const setSpaceSettingVisible = useSpaceStore( (state) => state.setSpaceSettingVisible, ); @@ -32,35 +34,43 @@ const LeftSide = () => { return ( -
+
- setOpenProfile(true)} - name={myName} - avatar={me?.randomColor} - randomColor={me?.randomColor} - email={me?.email} - /> {spaceName} - {allow(SpaceActionPermission.MANAGE_SPACE_SETTING) && ( -
+
+ } - onClick={() => setSpaceSettingVisible(true)} + icon={ + + } + onClick={() => setOpenSwitchSpace(true)} /> -
- )} + + {allow(SpaceActionPermission.MANAGE_SPACE_SETTING) && ( + + + } + onClick={() => setSpaceSettingVisible(true)} + /> + + )} +
- +
{
- + { defaultCollapsed={true} /> + setOpenProfile(true)} + name={myName} + avatar={me?.randomColor} + randomColor={me?.randomColor} + email={me?.email} + />
{openProfile && ( { onClose={() => setOpenProfile(false)} /> )} + setOpenSwitchSpace(false)} + /> ); }; @@ -137,10 +158,10 @@ const ListModule = styled.div` display: flex; flex-direction: column; gap: 5px; + flex: 1; `; const SpaceInformation = styled.div` - display: flex; padding: 0 2px 0 10px; align-items: baseline; `; diff --git a/apps/ikigai/components/SwitchSpace.tsx b/apps/ikigai/components/SwitchSpace.tsx new file mode 100644 index 00000000..7d901e72 --- /dev/null +++ b/apps/ikigai/components/SwitchSpace.tsx @@ -0,0 +1,70 @@ +import { t, Trans } from "@lingui/macro"; +import { Typography } from "antd"; +import styled from "styled-components"; + +import Modal from "./common/Modal"; +import { useQuery } from "@apollo/client"; +import { GET_MY_SPACES } from "graphql/query/SpaceQuery"; +import { handleError } from "graphql/ApolloClient"; +import { GetMySpaces, GetMySpaces_spaceMine } from "graphql/types"; +import { formatDocumentRoute } from "config/Routes"; +import useSpaceStore from "context/ZustandSpaceStore"; + +export type SwitchSpaceProps = { + visible: boolean; + onClose: () => void; +}; + +const SwitchSpace = ({ visible, onClose }: SwitchSpaceProps) => { + const currentSpaceId = useSpaceStore((state) => state.spaceId); + const { data } = useQuery(GET_MY_SPACES, { + onError: handleError, + }); + + const onClick = (space: GetMySpaces_spaceMine) => { + if (space.id === currentSpaceId) return; + const documentPath = formatDocumentRoute(space.starterDocument.id); + window.location.replace(documentPath); + }; + + const spaces = data?.spaceMine || []; + return ( + +
+ + Click a space to switch + + {spaces.map((space) => ( + onClick(space)} + > + {space.name} + + ))} +
+
+ ); +}; + +const SpaceItemContainer = styled.div<{ $active: boolean }>` + width: 100%; + padding: 5px; + border-radius: 8px; + background: ${(props) => + props.$active ? props.theme.colors.primary[5] : "none"}; + margin-bottom: 5px; + + &:hover { + cursor: pointer; + background: ${(props) => props.theme.colors.primary[4]}; + } +`; + +export default SwitchSpace; diff --git a/apps/ikigai/graphql/graphql-schema.json b/apps/ikigai/graphql/graphql-schema.json index 521c03d4..f9386733 100644 --- a/apps/ikigai/graphql/graphql-schema.json +++ b/apps/ikigai/graphql/graphql-schema.json @@ -376,7 +376,7 @@ "deprecationReason": null }, { - "name": "spaceGetMySpaces", + "name": "spaceMine", "description": null, "args": [], "type": { diff --git a/apps/ikigai/graphql/query/SpaceQuery.ts b/apps/ikigai/graphql/query/SpaceQuery.ts index c282621d..405fbec1 100644 --- a/apps/ikigai/graphql/query/SpaceQuery.ts +++ b/apps/ikigai/graphql/query/SpaceQuery.ts @@ -74,3 +74,15 @@ export const GET_SPACE_INVITE_TOKENS = gql` } } `; + +export const GET_MY_SPACES = gql` + query GetMySpaces { + spaceMine { + id + name + starterDocument { + id + } + } + } +`; diff --git a/apps/ikigai/graphql/types.ts b/apps/ikigai/graphql/types.ts index 4e811046..4558cad3 100644 --- a/apps/ikigai/graphql/types.ts +++ b/apps/ikigai/graphql/types.ts @@ -1918,6 +1918,29 @@ export interface GetSpaceInviteTokensVariables { // @generated // This file was automatically generated and should not be edited. +// ==================================================== +// GraphQL query operation: GetMySpaces +// ==================================================== + +export interface GetMySpaces_spaceMine_starterDocument { + id: any; +} + +export interface GetMySpaces_spaceMine { + id: number; + name: string; + starterDocument: GetMySpaces_spaceMine_starterDocument; +} + +export interface GetMySpaces { + spaceMine: GetMySpaces_spaceMine[]; +} + +/* tslint:disable */ +/* eslint-disable */ +// @generated +// This file was automatically generated and should not be edited. + // ==================================================== // GraphQL query operation: GetHighlightDocument // ==================================================== diff --git a/graphql_server/src/graphql/space_action/mod.rs b/graphql_server/src/graphql/space_action/mod.rs index ac5f0d2f..c8fd03d7 100644 --- a/graphql_server/src/graphql/space_action/mod.rs +++ b/graphql_server/src/graphql/space_action/mod.rs @@ -47,11 +47,10 @@ impl Space { } async fn starter_document(&self, ctx: &Context<'_>) -> Result { - let user_id = get_user_id_from_ctx(ctx).await?; - space_quick_authorize(ctx, self.id, SpaceActionPermission::ViewSpaceContent).await?; + get_user_id_from_ctx(ctx).await?; let conn = get_conn_from_ctx(ctx).await?; - Document::get_or_create_starter_doc(&conn, user_id, self.id).format_err() + Document::get_or_create_starter_doc(&conn, self.creator_id, self.id).format_err() } async fn documents(&self, ctx: &Context<'_>) -> Result> { diff --git a/graphql_server/src/graphql/space_action/space_query.rs b/graphql_server/src/graphql/space_action/space_query.rs index c19f434c..ecee4581 100644 --- a/graphql_server/src/graphql/space_action/space_query.rs +++ b/graphql_server/src/graphql/space_action/space_query.rs @@ -28,7 +28,7 @@ impl SpaceQuery { Ok(class) } - async fn space_get_my_spaces(&self, ctx: &Context<'_>) -> Result> { + async fn space_mine(&self, ctx: &Context<'_>) -> Result> { let user_auth = get_user_auth_from_ctx(ctx).await?; let conn = get_conn_from_ctx(ctx).await?; Space::find_my_spaces(&conn, user_auth.id).format_err()