Skip to content

Commit

Permalink
feat: 404 페이지, 존재하지 않는 스페이스 페이지 핸들링 (#166)
Browse files Browse the repository at this point in the history
* feat: yjs connection 연결 거부 시 재연결 시도하지 않고, 관련 정보 에러로 전달

* feat: 에러 페이지 섹션 구현

* feat: yjs connection 상태 추적

* feat: 스페이스 생성 로딩 상태 반영

* feat: space 에러 및 로딩 처리

* feat: not found 페이지 추가

* fix: 현재 동작하지 않는 팔레트 메뉴 버튼 삭제

* feat: space 로딩 스타일 변경

* fix: 잘못된 uid 생성 로직 수정
  • Loading branch information
hoqn authored Nov 28, 2024
1 parent cd0c601 commit e0f5b98
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 7 deletions.
2 changes: 2 additions & 0 deletions packages/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BrowserRouter, Route, Routes } from "react-router-dom";
import "./App.css";
import Editor from "./components/note/Editor.tsx";
import Home from "./pages/Home.tsx";
import NotFoundPage from "./pages/NotFound.tsx";
import SpacePage from "./pages/Space.tsx";

function App() {
Expand All @@ -12,6 +13,7 @@ function App() {
<Route path="/" element={<Home />} />
<Route path="/space/:spaceId" element={<SpacePage />} />
<Route path="/note/:noteId" element={<Editor />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</BrowserRouter>
);
Expand Down
30 changes: 30 additions & 0 deletions packages/frontend/src/assets/error-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions packages/frontend/src/components/ErrorSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ReactNode } from "react";

import buzzyLogo from "@/assets/error-logo.svg";

type ErrorSectionProps = {
description: string;
RestoreActions?: () => ReactNode;
};

export default function ErrorSection({
description,
RestoreActions,
}: ErrorSectionProps) {
return (
<div className="container mx-auto px-6 h-full text-center">
<div className="flex flex-col w-full h-full justify-center items-center">
<img src={buzzyLogo} className="w-36" />
<div className="mt-8 text-base">
<p>{description}</p>
</div>
{RestoreActions && (
<div className="mt-12">
<RestoreActions />
</div>
)}
</div>
</div>
);
}
3 changes: 2 additions & 1 deletion packages/frontend/src/components/space/SpaceView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,8 @@ export default function SpaceView({ spaceId, autofitTo }: SpaceViewProps) {
}}
>
<PaletteMenu
items={["note", "image", "url", "subspace"]}
// items={["note", "image", "url", "subspace"]}
items={["note", "subspace"]}
onSelect={handlePaletteSelect}
/>
</div>
Expand Down
16 changes: 15 additions & 1 deletion packages/frontend/src/hooks/yjs/useYjsConnection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ import * as Y from "yjs";
import { generateUserColor } from "@/lib/utils";

export default function useYjsConnection(docName: string) {
const [status, setStatus] = useState<
"connecting" | "connected" | "disconnected"
>("connecting");
const [error, setError] = useState<Error>();
const [yDoc, setYDoc] = useState<Y.Doc>();
const [yProvider, setYProvider] = useState<Y.AbstractConnector>();

useEffect(() => {
setStatus("connecting");

const doc = new Y.Doc();
const provider = new WebsocketProvider(
`ws://${import.meta.env.DEV ? "localhost" : "www.honeyflow.life"}/ws/space`,
Expand All @@ -28,9 +34,17 @@ export default function useYjsConnection(docName: string) {
if (event.status === "connected") {
awareness.setLocalStateField("color", generateUserColor());
}
setStatus(event.status);
},
);

provider.once("connection-close", (event: CloseEvent) => {
if (event.code === 1008) {
provider.shouldConnect = false;
setError(new Error("찾을 수 없거나 접근할 수 없는 스페이스예요."));
}
});

return () => {
if (provider.bcconnected || provider.wsconnected) {
provider.disconnect();
Expand All @@ -41,5 +55,5 @@ export default function useYjsConnection(docName: string) {
};
}, [docName]);

return { yProvider, yDoc, setYProvider, setYDoc };
return { status, error, yProvider, yDoc, setYProvider, setYDoc };
}
2 changes: 1 addition & 1 deletion packages/frontend/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export function createSafeContext<T>(defaultValue?: T) {
}

export function generateUniqueId() {
return Math.random().toString(36);
return Math.random().toString(36).slice(2);
}

// 노출과 명도는 유지, 색상만 랜덤
Expand Down
23 changes: 21 additions & 2 deletions packages/frontend/src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ type CreateSpaceButtonProps = {

function CreateSpaceButton({ navigate }: CreateSpaceButtonProps) {
const [spaceName, setSpaceName] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState("");

const handleCreateSpace = (e: FormEvent) => {
e.preventDefault();
const targetSpaceName = spaceName.trim();
Expand All @@ -41,13 +43,18 @@ function CreateSpaceButton({ navigate }: CreateSpaceButtonProps) {
return;
}

setIsLoading(true);

requestCreateSpace(targetSpaceName)
.then((res) => {
const { urlPath } = res;
navigate(`/space/${urlPath}`);
})
.catch((error) => {
setError(`스페이스 생성에 실패했어요. (${error})`);
})
.finally(() => {
setIsLoading(false);
});
};

Expand Down Expand Up @@ -77,9 +84,21 @@ function CreateSpaceButton({ navigate }: CreateSpaceButtonProps) {
{error && <p className="text-red-500 text-sm mt-2">{error}</p>}
<DialogFooter className="mt-4">
<DialogClose asChild>
<Button variant="ghost">취소</Button>
<Button
variant="ghost"
disabled={isLoading}
aria-disabled={isLoading}
>
취소
</Button>
</DialogClose>
<Button type="submit">생성</Button>
<Button
type="submit"
disabled={isLoading}
aria-disabled={isLoading}
>
생성
</Button>
</DialogFooter>
</form>
</DialogContent>
Expand Down
22 changes: 22 additions & 0 deletions packages/frontend/src/pages/NotFound.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Link } from "react-router-dom";

import { MoveLeftIcon } from "lucide-react";

import ErrorSection from "@/components/ErrorSection";
import { Button } from "@/components/ui/button";

export default function NotFoundPage() {
return (
<ErrorSection
description="찾을 수 없는 페이지예요"
RestoreActions={() => (
<Button asChild>
<Link to="/">
<MoveLeftIcon />
처음으로 돌아가기
</Link>
</Button>
)}
/>
);
}
34 changes: 32 additions & 2 deletions packages/frontend/src/pages/Space.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { useRef } from "react";
import { useParams } from "react-router-dom";
import { useNavigate, useParams } from "react-router-dom";

import { CircleDashedIcon, MoveLeftIcon } from "lucide-react";

import ErrorSection from "@/components/ErrorSection";
import SpacePageHeader from "@/components/space/SpacePageHeader";
import SpaceView from "@/components/space/SpaceView";
import { Button } from "@/components/ui/button";
import useYjsConnection from "@/hooks/yjs/useYjsConnection";
import { YjsStoreProvider } from "@/store/yjs";

Expand All @@ -11,15 +15,41 @@ interface SpacePageParams extends Record<string, string | undefined> {
}

export default function SpacePage() {
const navigate = useNavigate();
const { spaceId } = useParams<SpacePageParams>();

if (!spaceId) {
throw new Error("");
}

const { yDoc, yProvider, setYDoc, setYProvider } = useYjsConnection(spaceId);
const { error, status, yDoc, yProvider, setYDoc, setYProvider } =
useYjsConnection(spaceId);
const containerRef = useRef<HTMLDivElement>(null);

if (error) {
return (
<ErrorSection
description={error.message}
RestoreActions={() => (
<>
<Button variant="outline" onClick={() => navigate("/")}>
<MoveLeftIcon />
처음으로 돌아가기
</Button>
</>
)}
/>
);
}

if (status === "connecting") {
return (
<div className="flex justify-center items-center h-full">
<CircleDashedIcon className="animate-spin w-32 h-32 text-primary" />
</div>
);
}

return (
<YjsStoreProvider value={{ yDoc, yProvider, setYDoc, setYProvider }}>
<div className="w-full h-full" ref={containerRef}>
Expand Down

0 comments on commit e0f5b98

Please sign in to comment.