Skip to content

Commit

Permalink
Merge branch 'develop' into feature-fe-#374
Browse files Browse the repository at this point in the history
  • Loading branch information
yewonJin authored Dec 3, 2024
2 parents 1ab0a62 + 8481d67 commit 944e494
Show file tree
Hide file tree
Showing 14 changed files with 310 additions and 67 deletions.
13 changes: 11 additions & 2 deletions apps/frontend/src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import { useSyncedUsers } from "@/entities/user";
import { useGetUser } from "@/features/auth";
import { useProtectedWorkspace } from "@/features/workspace";
import { CanvasView } from "@/widgets/CanvasView";
import { CanvasToolsView } from "@/widgets/CanvasToolsView";
import { EditorView } from "@/widgets/EditorView";
import { NodeToolsView } from "@/widgets/NodeToolsView";
import { PageSideBarView } from "@/widgets/PageSideBarView";
import { CanvasToolsView } from "@/widgets/CanvasToolsView";
import { SideWrapper } from "@/shared/ui";

function App() {
useSyncedUsers();
useGetUser();
const { isLoading } = useProtectedWorkspace();

if (isLoading) {
return (
<div className="fixed inset-0 flex items-center justify-center bg-white">
<div className="animate-pulse text-gray-400">Loading...</div>
</div>
);
}

return (
<div className="fixed inset-0 bg-white">
Expand Down
107 changes: 65 additions & 42 deletions apps/frontend/src/app/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,85 +10,104 @@

// Import Routes

import { Route as rootRoute } from "./routes/__root";
import { Route as IndexImport } from "./routes/index";
import { Route as WorkspaceWorkspaceIdImport } from "./routes/workspace/$workspaceId";
import { Route as rootRoute } from './routes/__root'
import { Route as IndexImport } from './routes/index'
import { Route as JoinIndexImport } from './routes/join/index'
import { Route as WorkspaceWorkspaceIdImport } from './routes/workspace/$workspaceId'

// Create/Update Routes

const IndexRoute = IndexImport.update({
id: "/",
path: "/",
id: '/',
path: '/',
getParentRoute: () => rootRoute,
} as any);
} as any)

const JoinIndexRoute = JoinIndexImport.update({
id: '/join/',
path: '/join/',
getParentRoute: () => rootRoute,
} as any)

const WorkspaceWorkspaceIdRoute = WorkspaceWorkspaceIdImport.update({
id: "/workspace/$workspaceId",
path: "/workspace/$workspaceId",
id: '/workspace/$workspaceId',
path: '/workspace/$workspaceId',
getParentRoute: () => rootRoute,
} as any);
} as any)

// Populate the FileRoutesByPath interface

declare module "@tanstack/react-router" {
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
"/": {
id: "/";
path: "/";
fullPath: "/";
preLoaderRoute: typeof IndexImport;
parentRoute: typeof rootRoute;
};
"/workspace/$workspaceId": {
id: "/workspace/$workspaceId";
path: "/workspace/$workspaceId";
fullPath: "/workspace/$workspaceId";
preLoaderRoute: typeof WorkspaceWorkspaceIdImport;
parentRoute: typeof rootRoute;
};
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
'/workspace/$workspaceId': {
id: '/workspace/$workspaceId'
path: '/workspace/$workspaceId'
fullPath: '/workspace/$workspaceId'
preLoaderRoute: typeof WorkspaceWorkspaceIdImport
parentRoute: typeof rootRoute
}
'/join/': {
id: '/join/'
path: '/join'
fullPath: '/join'
preLoaderRoute: typeof JoinIndexImport
parentRoute: typeof rootRoute
}
}
}

// Create and export the route tree

export interface FileRoutesByFullPath {
"/": typeof IndexRoute;
"/workspace/$workspaceId": typeof WorkspaceWorkspaceIdRoute;
'/': typeof IndexRoute
'/workspace/$workspaceId': typeof WorkspaceWorkspaceIdRoute
'/join': typeof JoinIndexRoute
}

export interface FileRoutesByTo {
"/": typeof IndexRoute;
"/workspace/$workspaceId": typeof WorkspaceWorkspaceIdRoute;
'/': typeof IndexRoute
'/workspace/$workspaceId': typeof WorkspaceWorkspaceIdRoute
'/join': typeof JoinIndexRoute
}

export interface FileRoutesById {
__root__: typeof rootRoute;
"/": typeof IndexRoute;
"/workspace/$workspaceId": typeof WorkspaceWorkspaceIdRoute;
__root__: typeof rootRoute
'/': typeof IndexRoute
'/workspace/$workspaceId': typeof WorkspaceWorkspaceIdRoute
'/join/': typeof JoinIndexRoute
}

export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath;
fullPaths: "/" | "/workspace/$workspaceId";
fileRoutesByTo: FileRoutesByTo;
to: "/" | "/workspace/$workspaceId";
id: "__root__" | "/" | "/workspace/$workspaceId";
fileRoutesById: FileRoutesById;
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/workspace/$workspaceId' | '/join'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/workspace/$workspaceId' | '/join'
id: '__root__' | '/' | '/workspace/$workspaceId' | '/join/'
fileRoutesById: FileRoutesById
}

export interface RootRouteChildren {
IndexRoute: typeof IndexRoute;
WorkspaceWorkspaceIdRoute: typeof WorkspaceWorkspaceIdRoute;
IndexRoute: typeof IndexRoute
WorkspaceWorkspaceIdRoute: typeof WorkspaceWorkspaceIdRoute
JoinIndexRoute: typeof JoinIndexRoute
}

const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
WorkspaceWorkspaceIdRoute: WorkspaceWorkspaceIdRoute,
};
JoinIndexRoute: JoinIndexRoute,
}

export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>();
._addFileTypes<FileRouteTypes>()

/* ROUTE_MANIFEST_START
{
Expand All @@ -97,14 +116,18 @@ export const routeTree = rootRoute
"filePath": "__root.tsx",
"children": [
"/",
"/workspace/$workspaceId"
"/workspace/$workspaceId",
"/join/"
]
},
"/": {
"filePath": "index.tsx"
},
"/workspace/$workspaceId": {
"filePath": "workspace/$workspaceId.tsx"
},
"/join/": {
"filePath": "join/index.tsx"
}
}
}
Expand Down
71 changes: 71 additions & 0 deletions apps/frontend/src/app/routes/join/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useEffect } from "react";
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

import { useValidateWorkspaceInviteLink } from "@/features/workspace";

const joinQueryClient = new QueryClient();

function JoinWrapper() {
return (
<QueryClientProvider client={joinQueryClient}>
<JoinComponent />
</QueryClientProvider>
);
}

export const Route = createFileRoute("/join/")({
validateSearch: (search: Record<string, unknown>) => {
const workspaceId = search.workspaceId as string;
const token = search.token as string;

if (!workspaceId || !token) {
throw new Error("유효한 링크가 아닙니다.");
}

return { workspaceId, token };
},
component: JoinWrapper,
});

function JoinComponent() {
const { workspaceId, token } = Route.useSearch();
const navigate = useNavigate();
const { mutate: validateInvite, isPending } =
useValidateWorkspaceInviteLink();

useEffect(() => {
if (!token || !workspaceId) {
navigate({ to: "/" });
return;
}

validateInvite(token, {
onSuccess: () => {
navigate({
to: "/workspace/$workspaceId",
params: { workspaceId },
replace: true,
});
},
onError: () => {
navigate({ to: "/" });
},
});
}, [token, workspaceId, validateInvite, navigate]);

return (
<div className="flex h-screen items-center justify-center">
<div className="text-center">
<div className="mb-2 text-lg font-medium">
워크스페이스 초대 확인 중
</div>
{isPending && (
<div className="text-sm text-gray-500">
워크스페이스 {workspaceId} 입장 중...
</div>
)}
</div>
</div>
);
}
26 changes: 26 additions & 0 deletions apps/frontend/src/features/auth/model/useAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";

import { getUser, logout } from "../api/authApi";

export const useGetUser = () => {
return useQuery({
queryKey: ["user"],
queryFn: getUser,
retry: false,
refetchOnWindowFocus: false,
});
};

export const useLogout = () => {
const queryClient = useQueryClient();

const logoutMutation = useMutation({
mutationFn: logout,
onSuccess: async () => {
queryClient.setQueryData(["user"], null);
await queryClient.invalidateQueries({ queryKey: ["user"] });
},
});

return logoutMutation;
};
12 changes: 12 additions & 0 deletions apps/frontend/src/features/workspace/api/workspaceApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
CreateWorkSpaceResponse,
CreateWorkSpaceResquest,
GetCurrentUserWorkspaceResponse,
GetUserWorkspaceResponse,
RemoveWorkSpaceResponse,
} from "../model/workspaceTypes";
Expand Down Expand Up @@ -30,3 +31,14 @@ export const getUserWorkspaces = async () => {
const res = await Get<GetUserWorkspaceResponse>(url);
return res.data;
};

export const getCurrentWorkspace = async (
workspaceId: string,
userId: string,
) => {
const url = `${BASE_URL}/${workspaceId}/${userId}`;

// Response type 바꾸기
const res = await Get<GetCurrentUserWorkspaceResponse>(url);
return res.data;
};
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { SetWorkspaceStatusResponse } from "../model/workspaceInviteTypes";
import { Post } from "@/shared/api";
import { Patch } from "@/shared/api";

export const setWorkspaceStatusToPrivate = async (id: string) => {
// TODO: URL 맞게 고치기.
const url = `/api/workspace/${id}/private`;
await Post<SetWorkspaceStatusResponse, null>(url);
await Patch<SetWorkspaceStatusResponse, null>(url);
};

export const setWorkspaceStatusToPublic = async (id: string) => {
// TODO: URL 맞게 고치기.
const url = `/api/workspace/${id}/public`;
await Post<SetWorkspaceStatusResponse, null>(url);
await Patch<SetWorkspaceStatusResponse, null>(url);
};
2 changes: 2 additions & 0 deletions apps/frontend/src/features/workspace/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export { useUserWorkspace } from "./model/workspaceQuries";
export { useProtectedWorkspace } from "./model/useProtectedWorkspace";
export { useValidateWorkspaceInviteLink } from "./model/workspaceMutations";

export { ShareTool } from "./ui/ShareTool";
export { WorkspaceAddButton } from "./ui/WorkspaceAddButton";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useEffect } from "react";
import { useNavigate } from "@tanstack/react-router";
import { useCurrentWorkspace } from "@/features/workspace/model/workspaceQuries";

export const useProtectedWorkspace = () => {
const navigate = useNavigate();
const { data: workspaceData, isLoading, error } = useCurrentWorkspace();

useEffect(() => {
if (!isLoading && (error || !workspaceData)) {
navigate({ to: "/" });
}
}, [isLoading, workspaceData, error, navigate]);

return {
isLoading,
workspaceData,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
createWorkspaceInviteLink,
validateWorkspaceInviteLink,
} from "../api/worskspaceInviteApi";
import { useWorkspace } from "@/shared/lib";

// response로 workspaceId가 오는데 userWorkspace를 어떻게 invalidate 할까?
// login state에 있는 userId로?
Expand Down Expand Up @@ -48,9 +49,9 @@ export const useValidateWorkspaceInviteLink = () => {

export const useToggleWorkspaceStatus = (
currentStatus: "public" | "private" | undefined,
currentWorkspaceId: string,
) => {
const queryClient = useQueryClient();
const currentWorkspaceId = useWorkspace();

return useMutation({
mutationFn: () => {
Expand All @@ -67,6 +68,7 @@ export const useToggleWorkspaceStatus = (
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["userWorkspace"] });
queryClient.invalidateQueries({ queryKey: ["currentWorkspace"] });
},
});
};
Loading

0 comments on commit 944e494

Please sign in to comment.