Skip to content

Slackクローン 8. ツールバーコンポーネント

公開日

表紙

8. ツールバーコンポーネント

ワークスペースにアクセスした後の画面レイアウトを作成する。
ツールバーの作成まで実施。

ワークスペース画面作成

convexの取得処理

idを指定して取得できるようにする。

convex/workspaces.ts
export const getById = query({
  args: {
    id: v.id("workspaces"),
  },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) {
      throw new Error("Unauthorized");
    }
    return await ctx.db.get(args.id);
  },
});

ワークスペースのレイアウト作成

モックレベルで作成。
ツールバーがクライアント側で動作する仕組みとなるので use client を記述する。

src/app/workspace/[workspaceId]/layout.tsx
"use client";
import { Toolbar } from "./toolbar";
interface WorkSpaceIdLayoutProps {
  children: React.ReactNode;
}
const WorkSpaceIdLayout: React.FC<WorkSpaceIdLayoutProps> = ({ children }) => {
  return (
    <div className="h-full">
      <Toolbar />
      {children}
    </div>
  );
};
export default WorkSpaceIdLayout;

ワークスペースページ作成

データを取得して表示できるようにするのみ。

src/app/workspace/[workspaceId]/page.tsx
"use client";
import { useGetWorkspace } from "@/features/workspaces/api/use-get-workspace";
import { useWorkspaceId } from "@/hooks/use-workspace-id";
const WorkspaceIdPage = () => {
  const workspaceId = useWorkspaceId();
  const { data } = useGetWorkspace({ id: workspaceId });
  return (
    <div>
      <span>ID: {workspaceId}</span>
      <span>DATA: {JSON.stringify(data)}</span>
    </div>
  );
};

ツールバー作成

ワークスペースを特定し、検索フィールドにワークスペース名を設定。

src/app/workspace/[workspaceId]/toolbar.tsx
import { Button } from "@/components/ui/button";
import { useGetWorkspace } from "@/features/workspaces/api/use-get-workspace";
import { useWorkspaceId } from "@/hooks/use-workspace-id";
import { Info, Search } from "lucide-react";
export const Toolbar = () => {
  const workspaceId = useWorkspaceId();
  const { data } = useGetWorkspace({ id: workspaceId });
  return (
    <nav className="bg-[#481349] flex items-center justify-between h-10 p-1.5">
      <div className="flex-1" />
      <div className="min-w-[280px] max-[642px] grow-[2] shrink">
        <Button
          size={"sm"}
          className="bg-accent/25 hover:bg-accent-25 w-full justify-start h-7 px-2"
        >
          <Search className="size-4 text-white mr-2" />
          <span className="text-white text-xs">Search {data?.name}</span>
        </Button>
      </div>
      <div className="ml-auto flex-1 flex items-center justify-end">
        <Button variant={"transparent"} size={"iconSm"}>
          <Info className="size-5 text-white" />
        </Button>
      </div>
    </nav>
  );
};

shadcn/uiのボタンコンポーネントに定義を追加

Slackに寄せるため transparent iconSm という定義を追加する。

src/components/ui/button.tsx
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
        slack: "bg-emerald-500 text-white hover:bg-emerald-500/80",
        transparent: "bg-transparent hover:bg-accent/10 text-accent",
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-8 rounded-md px-3",
        lg: "h-11 rounded-md px-8",
        icon: "h-10 w-10",
        iconSm: "h-8 w-8",
      },
    },
    defaultVariants: {

ワークスペースを取得するAPIを作成

convexからデータを取得するAPIを作成する。
useQuery を使うとデータが更新された際に、自動的に反映することができる。
WebSocket? ポーリング? 上手いことやってくれる。

src/features/workspaces/api/use-get-workspace.ts
import { useQuery } from "convex/react";
import { api } from "../../../../convex/_generated/api";
import { Id } from "../../../../convex/_generated/dataModel";
interface UserGetWorkspaceProps {
  id: Id<"workspaces">;
}
export const useGetWorkspace = ({ id }: UserGetWorkspaceProps) => {
  const data = useQuery(api.workspaces.getById, { id });
  const isLoading = data === undefined;
  return { data, isLoading };
};

ワークスペースID取得のhookを作成

hookでは存在するワークスペースであるかをチェックし、問題なければIDを返す。

src/hooks/use-workspace-id.ts
import { useParams } from "next/navigation";
import { Id } from "../../convex/_generated/dataModel";
export const useWorkspaceId = () => {
  const params = useParams();
  return params.workspaceId as Id<"workspaces">;
};