Commit 9e2e0556 authored by Grant's avatar Grant
Browse files

add basic profile modal

parent 169c19b8
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ import { AuthErrors } from "./AuthErrors";
import "../lib/keybinds";
import { PixelWhoisSidebar } from "./PixelWhoisSidebar";
import { KeybindModal } from "./KeybindModal";
import { ProfileModal } from "./Profile/ProfileModal";

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

@@ -142,6 +143,8 @@ const AppInner = () => {
      <KeybindModal />
      <AuthErrors />

      <ProfileModal />

      <ToastContainer position="top-left" />
    </>
  );
+50 −0
Original line number Diff line number Diff line
import {
  Button,
  Modal,
  ModalBody,
  ModalContent,
  ModalFooter,
  ModalHeader,
} from "@nextui-org/react";
import { useAppContext } from "../../contexts/AppContext";
import { useEffect, useState } from "react";
import { IUser, UserCard } from "./UserCard";
import { api, handleError } from "../../lib/utils";

export const ProfileModal = () => {
  const { profile, setProfile } = useAppContext();
  const [user, setUser] = useState<IUser>();

  useEffect(() => {
    if (!profile) {
      setUser(undefined);
      return;
    }

    api<{ user: IUser }>("/api/user/" + profile).then(({ status, data }) => {
      if (status === 200 && data.success) {
        setUser(data.user);
      } else {
        handleError({ status, data });
      }
    });
  }, [profile]);

  return (
    <Modal isOpen={!!profile} onClose={() => setProfile()} placement="center">
      <ModalContent>
        {(onClose) => (
          <>
            <ModalHeader className="flex flex-col gap-1">Profile</ModalHeader>
            <ModalBody>
              {user ? <UserCard user={user} /> : <>Loading...</>}
            </ModalBody>
            <ModalFooter>
              <Button onPress={onClose}>Close</Button>
            </ModalFooter>
          </>
        )}
      </ModalContent>
    </Modal>
  );
};
+10 −4
Original line number Diff line number Diff line
import { faMessage, faWarning, faX } from "@fortawesome/free-solid-svg-icons";
import { faMessage, faWarning } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button, Link, Spinner } from "@nextui-org/react";
import { ClientConfig } from "@sc07-canvas/lib/src/net";
@@ -6,7 +6,7 @@ import { MouseEvent, useEffect, useState } from "react";
import { toast } from "react-toastify";
import { useAppContext } from "../../contexts/AppContext";

interface IUser {
export interface IUser {
  sub: string;
  display_name?: string;
  picture_url?: string;
@@ -25,7 +25,7 @@ const getMatrixLink = (user: IUser, config: ClientConfig) => {
 * @returns
 */
export const UserCard = ({ user }: { user: IUser }) => {
  const { config } = useAppContext();
  const { config, setProfile } = useAppContext();
  const [messageStatus, setMessageStatus] = useState<
    "loading" | "no_account" | "has_account" | "error"
  >("loading");
@@ -68,6 +68,10 @@ export const UserCard = ({ user }: { user: IUser }) => {
    }
  };

  const openProfile = () => {
    setProfile(user.sub);
  };

  return (
    <div className="flex flex-col gap-1">
      <div className="flex flex-row gap-2">
@@ -101,7 +105,9 @@ export const UserCard = ({ user }: { user: IUser }) => {
          )}
        </div>
      </div>
      <Button size="sm">View Profile</Button>
      <Button size="sm" onPress={openProfile}>
        View Profile
      </Button>
    </div>
  );
};
+10 −1
Original line number Diff line number Diff line
@@ -13,15 +13,17 @@ import { api } from "../lib/utils";
interface IAppContext {
  config?: ClientConfig;
  user?: AuthSession;
  connected: boolean;

  canvasPosition?: ICanvasPosition;
  setCanvasPosition: (v: ICanvasPosition) => void;
  cursorPosition?: IPosition;
  setCursorPosition: (v?: IPosition) => void;
  pixels: { available: number };
  undo?: { available: true; expireAt: number };

  loadChat: boolean;
  setLoadChat: (v: boolean) => void;
  connected: boolean;

  settingsSidebar: boolean;
  setSettingsSidebar: (v: boolean) => void;
@@ -35,6 +37,9 @@ interface IAppContext {
  heatmapOverlay: IMapOverlay;
  setHeatmapOverlay: React.Dispatch<React.SetStateAction<IMapOverlay>>;

  profile?: string; // sub
  setProfile: (v?: string) => void;

  hasAdmin: boolean;
}

@@ -93,6 +98,8 @@ export const AppContext = ({ children }: PropsWithChildren) => {
    loading: false,
  });

  const [profile, setProfile] = useState<string>();

  const [hasAdmin, setHasAdmin] = useState(false);

  useEffect(() => {
@@ -195,6 +202,8 @@ export const AppContext = ({ children }: PropsWithChildren) => {
        setBlankOverlay,
        heatmapOverlay,
        setHeatmapOverlay,
        profile,
        setProfile,
      }}
    >
      {!config && (
+9 −0
Original line number Diff line number Diff line
import { toast } from "react-toastify";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const api = async <T = unknown, Error = string>(
  endpoint: string,
@@ -36,3 +38,10 @@ export const api = async <T = unknown, Error = string>(
export type EnforceObjectType<T> = <V extends { [k: string]: T }>(
  v: V
) => { [k in keyof V]: T };

export const handleError = (api_response: Awaited<ReturnType<typeof api>>) => {
  toast.error(
    `Error: [${api_response.status}] ` +
      ("error" in api_response.data ? api_response.data.error : "Unknown Error")
  );
};
Loading