Commit 732feacd authored by Grant's avatar Grant
Browse files

chat initial (related #6)

parent 5ba2eb08
Loading
Loading
Loading
Loading
+115 −9
Original line number Diff line number Diff line
import { Header } from "./Header";
import { AppContext } from "../contexts/AppContext";
import { AppContext, useAppContext } from "../contexts/AppContext";
import { CanvasWrapper } from "./CanvasWrapper";
import { TemplateContext } from "../contexts/TemplateContext";
import { SettingsSidebar } from "./Settings/SettingsSidebar";
import { DebugModal } from "./Debug/DebugModal";
import { ToolbarWrapper } from "./Toolbar/ToolbarWrapper";
import React, { lazy, useEffect } from "react";
import { ChatContext } from "../contexts/ChatContext";

const Chat = lazy(() => import("./Chat/Chat"));

const DynamicallyLoadChat = () => {
  const { loadChat } = useAppContext();

  return <React.Suspense>{loadChat && <Chat />}</React.Suspense>;
};

const App = () => {
  useEffect(() => {
    // detect auth callback for chat, regardless of it being loaded
    // callback token expires quickly, so we should exchange it as quick as possible
    (async () => {
      const params = new URLSearchParams(window.location.search);
      if (params.has("loginToken")) {
        // login button opens a new tab that redirects here
        // if we're that tab, we should try to close this tab when we're done
        // should work because this tab is opened by JS
        const shouldCloseWindow =
          window.location.pathname.startsWith("/chat_callback");

        // token provided by matrix's /sso/redirect
        const token = params.get("loginToken")!;

        // immediately remove from url to prevent reloading
        window.history.replaceState({}, "", "/");

        const loginReq = await fetch(
          `https://${import.meta.env.VITE_MATRIX_HOST}/_matrix/client/v3/login`,
          {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              type: "m.login.token",
              token,
            }),
          }
        );

        const loginRes = await loginReq.json();

        console.log("[Chat] Matrix login", loginReq.status);

        switch (loginReq.status) {
          case 200: {
            // success
            console.log("[Chat] Logged in successfully", loginRes);

            localStorage.setItem(
              "matrix.access_token",
              loginRes.access_token + ""
            );
            localStorage.setItem("matrix.device_id", loginRes.device_id + "");
            localStorage.setItem("matrix.user_id", loginRes.user_id + "");

            if (shouldCloseWindow) {
              console.log(
                "[Chat] Path matches autoclose, attempting to close window..."
              );
              window.close();
              alert("You can close this window and return to the other tab :)");
            } else {
              console.log(
                "[Chat] Path doesn't match autoclose, not doing anything"
              );
            }
            break;
          }
          case 400:
          case 403:
            console.log("[Chat] Matrix login", loginRes);
            alert(
              "[Chat] Failed to login\n" +
                loginRes.errcode +
                " " +
                loginRes.error
            );
            break;
          case 429:
            alert(
              "[Chat] Failed to login, ratelimited.\nTry again in " +
                Math.floor(loginRes.retry_after_ms / 1000) +
                "s\n" +
                loginRes.errcode +
                " " +
                loginRes.error
            );
            break;
          default:
            alert(
              "Error " +
                loginReq.status +
                " returned when trying to login to chat"
            );
        }
      }
    })();
  }, []);

  return (
    <AppContext>
      <ChatContext>
        <TemplateContext>
          <Header />
          <CanvasWrapper />
          <ToolbarWrapper />

          {/* <DynamicallyLoadChat /> */}

          <DebugModal />
          <SettingsSidebar />
        </TemplateContext>
      </ChatContext>
    </AppContext>
  );
};
+13 −0
Original line number Diff line number Diff line
import { useEffect, useRef, useState } from "react";

const Chat = () => {
  const ref = useRef<HTMLDivElement | null>(null);

  return (
    <div ref={ref} style={{ position: "fixed", top: 0, left: 0, zIndex: 999 }}>
      chat
    </div>
  );
};

export default Chat;
+22 −0
Original line number Diff line number Diff line
import { Button } from "@nextui-org/react";
import { useChatContext } from "../../contexts/ChatContext";

const InnerChatSettings = () => {
  const { user, doLogin, doLogout } = useChatContext();

  return (
    <>
      {!user && <Button onClick={doLogin}>Login</Button>}
      {user && (
        <>
          <div className="flex gap-1">
            <div className="flex-grow">{user.userId}</div>
            <Button onClick={doLogout}>Logout</Button>
          </div>
        </>
      )}
    </>
  );
};

export default InnerChatSettings;
+19 −0
Original line number Diff line number Diff line
import { Badge, Button } from "@nextui-org/react";
import { useChatContext } from "../../contexts/ChatContext";

const OpenChatButton = () => {
  const { notificationCount } = useChatContext();

  return (
    <Badge
      content={notificationCount}
      isInvisible={notificationCount === 0}
      color="danger"
      size="sm"
    >
      <Button>Chat</Button>
    </Badge>
  );
};

export default OpenChatButton;
+10 −0
Original line number Diff line number Diff line
@@ -2,6 +2,15 @@ import { Button } from "@nextui-org/react";
import { useAppContext } from "../contexts/AppContext";
import { User } from "./Header/User";
import { Debug } from "@sc07-canvas/lib/src/debug";
import React, { lazy } from "react";

const OpenChatButton = lazy(() => import("./Chat/OpenChatButton"));

const DynamicChat = () => {
  const { loadChat } = useAppContext();

  return <React.Suspense>{loadChat && <OpenChatButton />}</React.Suspense>;
};

export const Header = () => {
  const { setSettingsSidebar } = useAppContext();
@@ -14,6 +23,7 @@ export const Header = () => {
        <User />
        <Button onClick={() => setSettingsSidebar(true)}>Settings</Button>
        <Button onClick={() => Debug.openDebugTools()}>debug</Button>
        <DynamicChat />
      </div>
    </header>
  );
Loading