Skip to content

Commit

Permalink
Merge pull request #102 from tgxn/account_menu
Browse files Browse the repository at this point in the history
user menu
  • Loading branch information
tgxn authored Nov 4, 2023
2 parents 0532e66 + 7cf24ff commit 58b0bee
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 168 deletions.
3 changes: 2 additions & 1 deletion src/components/Display.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,14 @@ export const FediverseChipLink = ({ href, size = "md", ...props }) => {
);
};

export function UserAvatar({ source, ...props }) {
export function UserAvatar({ source, size, ...props }) {
return (
<Avatar
component="span"
size="sm"
src={source}
sx={{
"--Avatar-size": size ? size : "24px",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
Expand Down
241 changes: 134 additions & 107 deletions src/components/Header/AccountMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,140 @@ import { useDispatch, useSelector } from "react-redux";

import { useQueryClient } from "@tanstack/react-query";

import { LemmyHttp } from "lemmy-js-client";
import { Toaster, toast } from "sonner";

import { BrowserRouter as Router, useNavigate, useLocation } from "react-router-dom";

import Typography from "@mui/joy/Typography";
import Chip from "@mui/joy/Chip";
import Sheet from "@mui/joy/Sheet";
import Box from "@mui/joy/Box";
import Divider from "@mui/joy/Divider";
import Switch from "@mui/joy/Switch";
import Button from "@mui/joy/Button";
import Menu from "@mui/joy/Menu";
import MenuItem from "@mui/joy/MenuItem";
import IconButton from "@mui/joy/IconButton";
import CircularProgress from "@mui/joy/CircularProgress";

import CachedIcon from "@mui/icons-material/Cached";
import LogoutIcon from "@mui/icons-material/Logout";
import ArrowDropDown from "@mui/icons-material/ArrowDropDown";
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
import ListItemDecorator from "@mui/joy/ListItemDecorator";
import ListItemContent from "@mui/joy/ListItemContent";

// user role icons
import VerifiedUserIcon from "@mui/icons-material/VerifiedUser";
import SupervisedUserCircleIcon from "@mui/icons-material/SupervisedUserCircle";
import AccountBoxIcon from "@mui/icons-material/AccountBox";
import DashboardIcon from "@mui/icons-material/Dashboard";
import SwitchAccountIcon from "@mui/icons-material/SwitchAccount";

import GitHubIcon from "@mui/icons-material/GitHub";

import FlagIcon from "@mui/icons-material/Flag";
import HowToRegIcon from "@mui/icons-material/HowToReg";
import ArrowDropDown from "@mui/icons-material/ArrowDropDown";

import { logoutCurrent, selectUsers } from "../../reducers/accountReducer";

import { LemmyHttp } from "lemmy-js-client";

import { useLemmyHttp, refreshAllData } from "../../hooks/useLemmyHttp";
import { getSiteData } from "../../hooks/getSiteData";

import { HeaderChip } from "../Display.jsx";
import { UserAvatar } from "../Display.jsx";
import { BasicInfoTooltip } from "../Tooltip.jsx";

import { parseActorId } from "../../utils.js";
import { parseActorId, getUserRole } from "../../utils.js";

import { addUser, setAccountIsLoading, setUsers, setCurrentUser } from "../../reducers/accountReducer";
import { setAccountIsLoading, setCurrentUser } from "../../reducers/accountReducer";

import ConfigModal from "./ConfigModal.jsx";
import { RoleIcons } from "../Shared/Icons.jsx";

export default function AccountMenu({ anchorEl, open, onClose }) {
function UserListItem({ user, onClose }) {
const dispatch = useDispatch();

const queryClient = useQueryClient();

const { baseUrl, siteData, localPerson, userRole } = getSiteData();

const roleIcon = React.useMemo(() => {
const personRole = getUserRole(user);
console.log("personRole", personRole);

let userIcon = RoleIcons[personRole]();
console.log("userIcon", userIcon);
return userIcon;
}, [user]);
// if (userRole == "admin") {
// userTooltip = "admin";
// userIcon = <VerifiedUserIcon />;
// }
// if (userRole == "mod") {
// userTooltip = "mod";
// userIcon = <SupervisedUserCircleIcon />;
// }

// const parsedActor = parseActorId(localPerson.actor_id);

return (
<MenuItem
sx={{
color: "text.body",
}}
disabled={user.site.my_user?.local_user_view?.person.actor_id == localPerson.actor_id}
onClick={async () => {
onClose();

// delete cache for current user
queryClient.invalidateQueries({ queryKey: ["lemmyHttp", localPerson.id] });
dispatch(logoutCurrent());

dispatch(setAccountIsLoading(true));

try {
const lemmyClient = new LemmyHttp(`https://${user.base}`);

const getSite = await lemmyClient.getSite({
auth: user.jwt,
});

if (!getSite.my_user) {
// set instance base to the current instance
// setInstanceBase(user.base);
// setUsername(user.site.my_user.local_user_view?.person.name);

throw new Error("jwt does not provide auth, re-authenticate");
}

// if (saveSession) {
// dispatch(addUser(user.base, auth.jwt, getSite));
// } else {
// dispatch(setCurrentUser(user.base, auth.jwt, getSite));
dispatch(setCurrentUser(user.base, user.jwt, getSite));
// }
} catch (e) {
toast(typeof e == "string" ? e : e.message);
} finally {
// setIsLoading(false);

dispatch(setAccountIsLoading(false));
}
}}
>
<ListItemDecorator>
<UserAvatar source={user.site.my_user?.local_user_view?.person.avatar} />
</ListItemDecorator>
{/* {user.site.my_user?.local_user_view?.person.actor_id == localPerson.actor_id ? (
<SwitchAccountIcon sx={{ mr: 1 }} />
) : (
<SwitchAccountIcon sx={{ mr: 1 }} />
)} */}
<ListItemContent>
{user.site.my_user?.local_user_view?.person.name}@{user.base}
</ListItemContent>
{roleIcon}
</MenuItem>
);
}

export default function AccountMenu() {
const users = useSelector(selectUsers);

const { mutate: refreshMutate } = refreshAllData();
const { localPerson, userRole } = getSiteData();

const [isLoading, setIsLoading] = React.useState(false);
const [anchorEl, setAnchorEl] = React.useState(null);
const [menuOpen, setMenuOpen] = React.useState(false);

const { baseUrl, siteData, localPerson, userRole } = getSiteData();
const handleClick = (event) => {
if (menuOpen) return handleClose();

setAnchorEl(event.currentTarget);
setMenuOpen(true);
};

const handleClose = () => {
setMenuOpen(false);
setAnchorEl(null);
};

let userTooltip = "user";
let userIcon = <AccountBoxIcon />;
Expand All @@ -79,82 +153,35 @@ export default function AccountMenu({ anchorEl, open, onClose }) {
const parsedActor = parseActorId(localPerson.actor_id);

return (
<Menu id="user-menu" anchorEl={anchorEl} open={open} onClose={onClose} placement="bottom-end">
{users && users.length > 0 && (
<>
{users.map((user, index) => {
return (
<MenuItem
key={index}
sx={{
color: "text.body",
}}
disabled={user.site.my_user?.local_user_view?.person.actor_id == localPerson.actor_id}
onClick={async () => {
onClose();

queryClient.invalidateQueries({ queryKey: ["lemmyHttp", localPerson.id] });
dispatch(logoutCurrent());

setIsLoading(true);

dispatch(setAccountIsLoading(true));

try {
const lemmyClient = new LemmyHttp(`https://${user.base}`);

const getSite = await lemmyClient.getSite({
auth: user.jwt,
});

if (!getSite.my_user) {
// set instance base to the current instance
// setInstanceBase(user.base);
// setUsername(user.site.my_user.local_user_view?.person.name);

throw new Error("jwt does not provide auth, re-authenticate");
}

// if (saveSession) {
// dispatch(addUser(user.base, auth.jwt, getSite));
// } else {
// dispatch(setCurrentUser(user.base, auth.jwt, getSite));
dispatch(setCurrentUser(user.base, user.jwt, getSite));
// }
} catch (e) {
toast(typeof e == "string" ? e : e.message);
} finally {
// setIsLoading(false);

dispatch(setAccountIsLoading(false));
}
}}
>
{user.site.my_user?.local_user_view?.person.actor_id == localPerson.actor_id ? (
<SwitchAccountIcon sx={{ mr: 1 }} />
) : (
<SwitchAccountIcon sx={{ mr: 1 }} />
)}
{user.site.my_user?.local_user_view?.person.name}@{user.base}
</MenuItem>
);
})}
</>
)}
{/*
<MenuItem
<>
<BasicInfoTooltip title={"Open User Switcher"} placement="bottom" variant="soft">
<Button
aria-controls={menuOpen ? "user-menu" : undefined}
aria-haspopup="true"
aria-expanded={menuOpen ? "true" : undefined}
size="sm"
variant="outlined"
color="neutral"
onClick={handleClick}
startDecorator={<UserAvatar size="20px" source={localPerson?.avatar} />}
endDecorator={<ArrowDropDown />}
sx={{
color: "text.body",
}}
onClick={() => {
handleClose();
queryClient.invalidateQueries({ queryKey: ["lemmyHttp"] });
dispatch(logoutCurrent());
mx: 1,
borderRadius: 4,
}}
>
End Session
</MenuItem> */}
</Menu>
{parsedActor.actorName}@{parsedActor.actorBaseUrl} ({userTooltip})
</Button>
</BasicInfoTooltip>
<Menu id="user-menu" anchorEl={anchorEl} open={menuOpen} onClose={handleClose} placement="bottom-end">
{users && users.length > 0 && (
<>
{users.map((user, index) => {
return <UserListItem user={user} key={index} onClose={handleClose} />;
})}
</>
)}
</Menu>
</>
);
}
41 changes: 2 additions & 39 deletions src/components/Header/SiteMenu.jsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,21 @@
import React from "react";

import { useDispatch, useSelector } from "react-redux";
import { useNavigate, useLocation } from "react-router-dom";

import { useQueryClient } from "@tanstack/react-query";

import { Toaster, toast } from "sonner";

import { BrowserRouter as Router, useNavigate, useLocation } from "react-router-dom";

import Typography from "@mui/joy/Typography";
import Chip from "@mui/joy/Chip";
import Sheet from "@mui/joy/Sheet";
import Box from "@mui/joy/Box";
import Button from "@mui/joy/Button";
import Menu from "@mui/joy/Menu";
import MenuItem from "@mui/joy/MenuItem";
import IconButton from "@mui/joy/IconButton";
import CircularProgress from "@mui/joy/CircularProgress";

import CachedIcon from "@mui/icons-material/Cached";
import LogoutIcon from "@mui/icons-material/Logout";
import ArrowDropDown from "@mui/icons-material/ArrowDropDown";
import OpenInNewIcon from "@mui/icons-material/OpenInNew";

// user role icons
import VerifiedUserIcon from "@mui/icons-material/VerifiedUser";
import SupervisedUserCircleIcon from "@mui/icons-material/SupervisedUserCircle";
import AccountBoxIcon from "@mui/icons-material/AccountBox";
import DashboardIcon from "@mui/icons-material/Dashboard";
import SwitchAccountIcon from "@mui/icons-material/SwitchAccount";

import GitHubIcon from "@mui/icons-material/GitHub";

import FlagIcon from "@mui/icons-material/Flag";
import HowToRegIcon from "@mui/icons-material/HowToReg";

import { logoutCurrent, selectUsers } from "../../reducers/accountReducer";

import { LemmyHttp } from "lemmy-js-client";

import { useLemmyHttp, refreshAllData } from "../../hooks/useLemmyHttp";
import { useLemmyHttp } from "../../hooks/useLemmyHttp";
import { getSiteData } from "../../hooks/getSiteData";

import { HeaderChip } from "../Display.jsx";
import { BasicInfoTooltip } from "../Tooltip.jsx";

import { parseActorId } from "../../utils.js";

import { addUser, setAccountIsLoading, setUsers, setCurrentUser } from "../../reducers/accountReducer";

export default function SiteMenu() {
// const dispatch = useDispatch();
// const queryClient = useQueryClient();

const location = useLocation();
const navigate = useNavigate();

Expand Down
22 changes: 1 addition & 21 deletions src/components/Header/UserMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,27 +100,7 @@ export default function UserMenu() {
</IconButton>
</BasicInfoTooltip>

<BasicInfoTooltip title={"Open User Switcher"} placement="bottom" variant="soft">
<Button
aria-controls={menuOpen ? "user-menu" : undefined}
aria-haspopup="true"
aria-expanded={menuOpen ? "true" : undefined}
size="sm"
variant="outlined"
color="neutral"
onClick={handleClick}
startDecorator={userIcon}
endDecorator={<ArrowDropDown />}
sx={{
mx: 1,
borderRadius: 4,
}}
>
{parsedActor.actorName}@{parsedActor.actorBaseUrl} ({userTooltip})
</Button>
</BasicInfoTooltip>

<AccountMenu anchorEl={anchorEl} open={menuOpen} onClose={handleClose} />
<AccountMenu />

<BasicInfoTooltip title="End Session" placement="bottom" variant="soft">
<IconButton
Expand Down
Loading

0 comments on commit 58b0bee

Please sign in to comment.