mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 02:00:46 +00:00
replaced logout/profile buttons with profile pic dropdown
This commit is contained in:
1 parent
754787a325
commit
c063029856
13 files changed
+349
-206
No files matched your search
@@ -7,6 +7,7 @@ interface Props {
|
||||
username?: string;
|
||||
avatarIndex?: number;
|
||||
backgroundIndex?: number;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
function Gravatar(props: Props) {
|
||||
@@ -35,6 +36,7 @@ function Gravatar(props: Props) {
|
||||
height={props.size}
|
||||
width={props.size}
|
||||
style={{ backgroundColor: getBackgroundColor(props.backgroundIndex) }}
|
||||
onClick={props.onClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import { GetSessionInfo, GetSessionInfoIsOk, SessionInfoSuccessResponse } from '../api/session/GetSessionInfo'
|
||||
import { StudioRolloutHostnameAllowed } from '../utils/StudioRolloutHostnameAllowed';
|
||||
import {
|
||||
GetSessionInfo,
|
||||
GetSessionInfoIsOk,
|
||||
SessionInfoSuccessResponse,
|
||||
} from "../api/session/GetSessionInfo";
|
||||
import { StudioRolloutHostnameAllowed } from "../utils/StudioRolloutHostnameAllowed";
|
||||
|
||||
// A lot of the APIs return null or leave values absent.
|
||||
// A lot of the APIs return null or leave values absent.
|
||||
// I need to pick a single strategy and stick with it.
|
||||
type MaybeString = string | null | undefined;
|
||||
|
||||
/**
|
||||
* SessionWrapper can wrap each of the following states:
|
||||
*
|
||||
*
|
||||
* 1) logged-in session
|
||||
* 2) non-existing session (logged out)
|
||||
* 3) lookup errors, server errors, 401, etc. (logged out)
|
||||
*
|
||||
*
|
||||
* It then makes frontend permission checking simple.
|
||||
*/
|
||||
export class SessionWrapper {
|
||||
@@ -24,7 +28,7 @@ export class SessionWrapper {
|
||||
}
|
||||
|
||||
// Perform lookup and return a wrapper of a valid, inval
|
||||
public static async lookupSession() : Promise<SessionWrapper> {
|
||||
public static async lookupSession(): Promise<SessionWrapper> {
|
||||
let response = await GetSessionInfo();
|
||||
if (GetSessionInfoIsOk(response)) {
|
||||
return SessionWrapper.wrapResponse(response);
|
||||
@@ -33,165 +37,198 @@ export class SessionWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
public static emptySession() : SessionWrapper {
|
||||
public static emptySession(): SessionWrapper {
|
||||
return new SessionWrapper();
|
||||
}
|
||||
|
||||
public static wrapResponse(sessionStateResponse: SessionInfoSuccessResponse) : SessionWrapper {
|
||||
public static wrapResponse(
|
||||
sessionStateResponse: SessionInfoSuccessResponse
|
||||
): SessionWrapper {
|
||||
return new SessionWrapper(sessionStateResponse);
|
||||
}
|
||||
|
||||
public isLoggedIn() : boolean {
|
||||
public isLoggedIn(): boolean {
|
||||
return this.sessionStateResponse?.logged_in || false;
|
||||
}
|
||||
|
||||
// Feature flag rollout for Storyteller Studio
|
||||
// This depends on the domain name and the backend.
|
||||
public canAccessStudio() : boolean {
|
||||
public canAccessStudio(): boolean {
|
||||
const hostnameAllowed = StudioRolloutHostnameAllowed();
|
||||
const userAllowed = this.sessionStateResponse?.user?.can_access_studio || false;
|
||||
const userAllowed =
|
||||
this.sessionStateResponse?.user?.can_access_studio || false;
|
||||
return hostnameAllowed && userAllowed;
|
||||
}
|
||||
|
||||
// Username is all lowercase
|
||||
public getUsername() : string | undefined {
|
||||
public getUsername(): string | undefined {
|
||||
return this.sessionStateResponse?.user?.username;
|
||||
}
|
||||
|
||||
// Display name has capitalization
|
||||
public getDisplayName() : string | undefined {
|
||||
public getDisplayName(): string | undefined {
|
||||
return this.sessionStateResponse?.user?.display_name;
|
||||
}
|
||||
|
||||
public getEmailGravatarHash() : string | undefined {
|
||||
public getEmailGravatarHash(): string | undefined {
|
||||
return this.sessionStateResponse?.user?.email_gravatar_hash;
|
||||
}
|
||||
|
||||
public canEditUserProfile(username: string) : boolean {
|
||||
public canEditUserProfile(username: string): boolean {
|
||||
if (this.getUsername() === username) {
|
||||
return true;
|
||||
}
|
||||
return this.canEditOtherUsersProfiles();
|
||||
}
|
||||
|
||||
public canEditMediaFileByUserToken(userToken: MaybeString) : boolean {
|
||||
return this.canEditOtherUsersTtsModels() || this.userTokenMatches(userToken);
|
||||
public canEditMediaFileByUserToken(userToken: MaybeString): boolean {
|
||||
return (
|
||||
this.canEditOtherUsersTtsModels() || this.userTokenMatches(userToken)
|
||||
);
|
||||
}
|
||||
|
||||
public canEditWeightByUserToken(userToken: MaybeString) : boolean {
|
||||
return this.canEditOtherUsersTtsModels() || this.userTokenMatches(userToken);
|
||||
public canEditWeightByUserToken(userToken: MaybeString): boolean {
|
||||
return (
|
||||
this.canEditOtherUsersTtsModels() || this.userTokenMatches(userToken)
|
||||
);
|
||||
}
|
||||
|
||||
public canEditTtsModelByUserToken(userToken: MaybeString) : boolean {
|
||||
return this.canEditOtherUsersTtsModels() || this.userTokenMatches(userToken);
|
||||
public canEditTtsModelByUserToken(userToken: MaybeString): boolean {
|
||||
return (
|
||||
this.canEditOtherUsersTtsModels() || this.userTokenMatches(userToken)
|
||||
);
|
||||
}
|
||||
|
||||
public canDeleteTtsModelByUserToken(userToken: MaybeString) : boolean {
|
||||
return this.canDeleteOtherUsersTtsModels() || this.userTokenMatches(userToken);
|
||||
public canDeleteTtsModelByUserToken(userToken: MaybeString): boolean {
|
||||
return (
|
||||
this.canDeleteOtherUsersTtsModels() || this.userTokenMatches(userToken)
|
||||
);
|
||||
}
|
||||
|
||||
public canDeleteTtsResultByUserToken(userToken: MaybeString) : boolean {
|
||||
return this.canDeleteOtherUsersTtsResults() || this.userTokenMatches(userToken);
|
||||
public canDeleteTtsResultByUserToken(userToken: MaybeString): boolean {
|
||||
return (
|
||||
this.canDeleteOtherUsersTtsResults() || this.userTokenMatches(userToken)
|
||||
);
|
||||
}
|
||||
|
||||
public canDeleteW2lTemplateByUserToken(userToken: MaybeString) : boolean {
|
||||
return this.canDeleteOtherUsersW2lTemplates() || this.userTokenMatches(userToken);
|
||||
public canDeleteW2lTemplateByUserToken(userToken: MaybeString): boolean {
|
||||
return (
|
||||
this.canDeleteOtherUsersW2lTemplates() || this.userTokenMatches(userToken)
|
||||
);
|
||||
}
|
||||
|
||||
public canDeleteW2lResultByUserToken(userToken: MaybeString) : boolean {
|
||||
return this.canDeleteOtherUsersW2lResults() || this.userTokenMatches(userToken);
|
||||
public canDeleteW2lResultByUserToken(userToken: MaybeString): boolean {
|
||||
return (
|
||||
this.canDeleteOtherUsersW2lResults() || this.userTokenMatches(userToken)
|
||||
);
|
||||
}
|
||||
|
||||
public canUseTts() : boolean {
|
||||
public canUseTts(): boolean {
|
||||
return this.sessionStateResponse?.user?.can_use_tts || true; // NB: Default true.
|
||||
}
|
||||
|
||||
public canUseW2l() : boolean {
|
||||
public canUseW2l(): boolean {
|
||||
return this.sessionStateResponse?.user?.can_use_w2l || true; // NB: Default true
|
||||
}
|
||||
|
||||
public canUploadTtsModels() : boolean {
|
||||
public canUploadTtsModels(): boolean {
|
||||
return this.sessionStateResponse?.user?.can_upload_tts_models || false;
|
||||
}
|
||||
|
||||
public canUploadW2lTemplates() : boolean {
|
||||
public canUploadW2lTemplates(): boolean {
|
||||
return this.sessionStateResponse?.user?.can_upload_w2l_templates || false;
|
||||
}
|
||||
|
||||
public canBanUsers() : boolean {
|
||||
public canBanUsers(): boolean {
|
||||
return this.sessionStateResponse?.user?.can_ban_users || false;
|
||||
}
|
||||
|
||||
public canEditCategories() : boolean {
|
||||
public canEditCategories(): boolean {
|
||||
// TODO / NB: There aren't fine-grained controls over categories.
|
||||
return this.canBanUsers();
|
||||
}
|
||||
|
||||
public canApproveW2lTemplates() : boolean {
|
||||
public canApproveW2lTemplates(): boolean {
|
||||
return this.sessionStateResponse?.user?.can_approve_w2l_templates || false;
|
||||
}
|
||||
|
||||
public canEditOtherUsersProfiles() : boolean {
|
||||
return this.sessionStateResponse?.user?.can_edit_other_users_profiles || false;
|
||||
public canEditOtherUsersProfiles(): boolean {
|
||||
return (
|
||||
this.sessionStateResponse?.user?.can_edit_other_users_profiles || false
|
||||
);
|
||||
}
|
||||
|
||||
public canEditOtherUsersTtsModels() : boolean {
|
||||
return this.sessionStateResponse?.user?.can_edit_other_users_tts_models || false;
|
||||
public canEditOtherUsersTtsModels(): boolean {
|
||||
return (
|
||||
this.sessionStateResponse?.user?.can_edit_other_users_tts_models || false
|
||||
);
|
||||
}
|
||||
|
||||
public canDeleteOtherUsersTtsModels() : boolean {
|
||||
return this.sessionStateResponse?.user?.can_delete_other_users_tts_models || false;
|
||||
public canDeleteOtherUsersTtsModels(): boolean {
|
||||
return (
|
||||
this.sessionStateResponse?.user?.can_delete_other_users_tts_models ||
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public canDeleteOtherUsersTtsResults() : boolean {
|
||||
return this.sessionStateResponse?.user?.can_delete_other_users_tts_results || false;
|
||||
public canDeleteOtherUsersTtsResults(): boolean {
|
||||
return (
|
||||
this.sessionStateResponse?.user?.can_delete_other_users_tts_results ||
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public canDeleteOtherUsersW2lTemplates() : boolean {
|
||||
return this.sessionStateResponse?.user?.can_delete_other_users_w2l_templates || false;
|
||||
public canDeleteOtherUsersW2lTemplates(): boolean {
|
||||
return (
|
||||
this.sessionStateResponse?.user?.can_delete_other_users_w2l_templates ||
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public canDeleteOtherUsersW2lResults() : boolean {
|
||||
return this.sessionStateResponse?.user?.can_delete_other_users_w2l_results || false;
|
||||
public canDeleteOtherUsersW2lResults(): boolean {
|
||||
return (
|
||||
this.sessionStateResponse?.user?.can_delete_other_users_w2l_results ||
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
public canEditTtsResultAsUserOrMod(creatorUserToken: MaybeString) : boolean {
|
||||
|
||||
public canEditTtsResultAsUserOrMod(creatorUserToken: MaybeString): boolean {
|
||||
if (this.userTokenMatches(creatorUserToken)) {
|
||||
return true;
|
||||
}
|
||||
return this.canDeleteOtherUsersTtsResults(); // TODO: There is no granular permission for this yet.
|
||||
}
|
||||
|
||||
public deleteTtsResultAsMod(creatorUserToken: MaybeString) : boolean {
|
||||
public deleteTtsResultAsMod(creatorUserToken: MaybeString): boolean {
|
||||
if (this.userTokenMatches(creatorUserToken)) {
|
||||
return true;
|
||||
}
|
||||
return this.canDeleteOtherUsersTtsResults();
|
||||
}
|
||||
|
||||
public deleteTtsModelAsMod(creatorUserToken: MaybeString) : boolean {
|
||||
public deleteTtsModelAsMod(creatorUserToken: MaybeString): boolean {
|
||||
if (this.userTokenMatches(creatorUserToken)) {
|
||||
return true;
|
||||
}
|
||||
return this.canDeleteOtherUsersTtsModels();
|
||||
}
|
||||
|
||||
public canEditW2lResultAsUserOrMod(creatorUserToken: MaybeString) : boolean {
|
||||
public canEditW2lResultAsUserOrMod(creatorUserToken: MaybeString): boolean {
|
||||
if (this.userTokenMatches(creatorUserToken)) {
|
||||
return true;
|
||||
}
|
||||
return this.canDeleteOtherUsersW2lResults(); // TODO: There is no granular permission for this yet.
|
||||
}
|
||||
|
||||
public canDeleteW2lResultAsUserOrMod(creatorUserToken: MaybeString) : boolean {
|
||||
public canDeleteW2lResultAsUserOrMod(creatorUserToken: MaybeString): boolean {
|
||||
if (this.userTokenMatches(creatorUserToken)) {
|
||||
return true;
|
||||
}
|
||||
return this.canDeleteOtherUsersW2lResults();
|
||||
}
|
||||
|
||||
public userTokenMatches(otherUserToken: MaybeString) : boolean {
|
||||
// Default to false if user token on either side is falsey.
|
||||
public userTokenMatches(otherUserToken: MaybeString): boolean {
|
||||
// Default to false if user token on either side is falsey.
|
||||
if (!otherUserToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
@import "scss/colours.scss";
|
||||
|
||||
.fy-dropdown-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 100%;
|
||||
background-color: $panel-inner;
|
||||
border: 1px solid lighten($panel-inner, 8%);
|
||||
border-radius: 0.5rem;
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fy-dropdown-item {
|
||||
padding: 10px 15px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
color: $white !important;
|
||||
transition: 0.15s all;
|
||||
cursor: pointer;
|
||||
font-size: 15px !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
.fy-dropdown-item:hover {
|
||||
background-color: lighten($panel-inner, 8%);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import React from "react";
|
||||
import { a, useSpring } from "@react-spring/web";
|
||||
import { Link } from "react-router-dom";
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-common-types";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import "./DropdownMenu.scss";
|
||||
|
||||
interface DropdownItem {
|
||||
id: number;
|
||||
name: string;
|
||||
link?: string;
|
||||
onClick?: () => void;
|
||||
icon?: IconDefinition;
|
||||
}
|
||||
|
||||
interface DropdownMenuProps {
|
||||
items: DropdownItem[];
|
||||
className?: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function DropdownMenu({
|
||||
items,
|
||||
className,
|
||||
onClose,
|
||||
}: DropdownMenuProps) {
|
||||
const fadeIn = useSpring({
|
||||
from: { opacity: 0 },
|
||||
to: { opacity: 1 },
|
||||
config: { duration: 100 },
|
||||
});
|
||||
|
||||
return (
|
||||
<a.div style={fadeIn} className={`fy-dropdown-menu ${className}`.trim()}>
|
||||
{items.map(item => (
|
||||
<>
|
||||
{item.onClick ? (
|
||||
<div
|
||||
key={item.id}
|
||||
onClick={e => {
|
||||
if (item.onClick) {
|
||||
e.preventDefault();
|
||||
item.onClick();
|
||||
}
|
||||
onClose();
|
||||
}}
|
||||
className="fy-dropdown-item"
|
||||
>
|
||||
<span>
|
||||
{item.icon && (
|
||||
<FontAwesomeIcon icon={item.icon} className="me-2 fs-7" />
|
||||
)}
|
||||
{item.name}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{item.link && (
|
||||
<Link
|
||||
key={item.id}
|
||||
to={item.link}
|
||||
className="fy-dropdown-item"
|
||||
onClick={() => onClose()}
|
||||
>
|
||||
<span>
|
||||
{item.icon && (
|
||||
<FontAwesomeIcon icon={item.icon} className="me-2 fs-7" />
|
||||
)}
|
||||
{item.name}
|
||||
</span>
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</a.div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./DropdownMenu";
|
||||
@@ -1,40 +0,0 @@
|
||||
import React from "react";
|
||||
import { a, useSpring } from "@react-spring/web";
|
||||
import { Link } from "react-router-dom";
|
||||
import "./NavItem.scss";
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-common-types";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
interface DropdownItem {
|
||||
id: number;
|
||||
name: string;
|
||||
link: string;
|
||||
icon?: IconDefinition;
|
||||
}
|
||||
|
||||
interface DropdownMenuProps {
|
||||
items: DropdownItem[];
|
||||
}
|
||||
|
||||
export default function DropdownMenu({ items }: DropdownMenuProps) {
|
||||
const fadeIn = useSpring({
|
||||
from: { opacity: 0 },
|
||||
to: { opacity: 1 },
|
||||
config: { duration: 100 }, // Adjust timing as needed
|
||||
});
|
||||
|
||||
return (
|
||||
<a.div style={fadeIn} className="fy-dropdown-menu">
|
||||
{items.map(item => (
|
||||
<Link key={item.id} to={item.link} className="fy-dropdown-item">
|
||||
<span>
|
||||
{item.icon && (
|
||||
<FontAwesomeIcon icon={item.icon} className="me-2 fs-7" />
|
||||
)}
|
||||
{item.name}
|
||||
</span>
|
||||
</Link>
|
||||
))}
|
||||
</a.div>
|
||||
);
|
||||
}
|
||||
@@ -17,32 +17,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.fy-dropdown-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 100%;
|
||||
background-color: $panel-inner;
|
||||
border: 1px solid lighten($panel-inner, 8%);
|
||||
border-radius: 0.5rem;
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fy-nav-item:hover .fy-dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.fy-dropdown-item {
|
||||
padding: 10px 15px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
color: $white !important;
|
||||
transition: 0.15s all;
|
||||
}
|
||||
|
||||
.fy-dropdown-item:hover {
|
||||
background-color: lighten($panel-inner, 8%);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import DropdownMenu from "./DropdownMenu";
|
||||
import DropdownMenu from "../DropdownMenu/DropdownMenu";
|
||||
import "./NavItem.scss";
|
||||
import { IconDefinition } from "@fortawesome/fontawesome-common-types";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
@@ -41,7 +41,9 @@ export default function NavItem({
|
||||
{label} <FontAwesomeIcon icon={faCaretDown} className="ms-1 fs-7" />
|
||||
</span>
|
||||
|
||||
{isHovered && <DropdownMenu items={dropdownItems} />}
|
||||
{isHovered && (
|
||||
<DropdownMenu items={dropdownItems} onClose={() => {}} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./NavItem";
|
||||
@@ -0,0 +1,16 @@
|
||||
.fy-profile-dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
transition: 0.15s all;
|
||||
}
|
||||
|
||||
.fy-profile-pic {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fy-profile-dropdown-menu {
|
||||
display: block;
|
||||
right: 0;
|
||||
left: auto;
|
||||
margin-top: 6px;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import DropdownMenu from "../DropdownMenu";
|
||||
import { Gravatar } from "@storyteller/components/src/elements/Gravatar";
|
||||
import "./ProfileDropdown.scss";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faCaretDown,
|
||||
faSignOutAlt,
|
||||
faUser,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
interface ProfileDropdownProps {
|
||||
username: string;
|
||||
displayName: string;
|
||||
avatarIndex: number;
|
||||
backgroundColorIndex: number;
|
||||
emailHash: string;
|
||||
logoutHandler: () => void;
|
||||
}
|
||||
|
||||
export default function ProfileDropdown({
|
||||
username,
|
||||
displayName,
|
||||
avatarIndex,
|
||||
backgroundColorIndex,
|
||||
emailHash,
|
||||
logoutHandler,
|
||||
}: ProfileDropdownProps) {
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const profileOptions = [
|
||||
{
|
||||
id: 1,
|
||||
name: "My Profile",
|
||||
link: `/profile/${displayName}`,
|
||||
icon: faUser,
|
||||
},
|
||||
{ id: 2, name: "Logout", onClick: logoutHandler, icon: faSignOutAlt },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="fy-profile-dropdown" ref={dropdownRef}>
|
||||
<div
|
||||
onClick={() => {
|
||||
setIsOpen(!isOpen);
|
||||
}}
|
||||
className="fy-profile-pic d-flex gap-2 align-items-center"
|
||||
>
|
||||
<Gravatar
|
||||
size={38}
|
||||
username={username}
|
||||
email_hash={emailHash}
|
||||
avatarIndex={avatarIndex}
|
||||
backgroundIndex={backgroundColorIndex}
|
||||
/>
|
||||
<FontAwesomeIcon icon={faCaretDown} />
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<DropdownMenu
|
||||
items={profileOptions}
|
||||
className="fy-profile-dropdown-menu"
|
||||
onClose={() => {
|
||||
setIsOpen(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./ProfileDropdown";
|
||||
@@ -3,9 +3,7 @@ import {
|
||||
faFaceViewfinder,
|
||||
faMessageDots,
|
||||
faSearch,
|
||||
faSignOutAlt,
|
||||
faStar,
|
||||
faUser,
|
||||
faWandMagicSparkles,
|
||||
faWaveformLines,
|
||||
faXmark,
|
||||
@@ -14,11 +12,11 @@ import { Button } from "components/common";
|
||||
import SearchBar from "components/common/SearchBar";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import { WebUrl } from "common/WebUrl";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { Logout } from "@storyteller/components/src/api/session/Logout";
|
||||
import { useDomainConfig } from "context/DomainConfigContext";
|
||||
import NavItem from "../../common/NavItem/NavItem";
|
||||
import ProfileDropdown from "components/common/ProfileDropdown";
|
||||
|
||||
interface TopNavProps {
|
||||
sessionWrapper: SessionWrapper;
|
||||
@@ -101,91 +99,28 @@ export default function TopNav({
|
||||
await Logout();
|
||||
querySessionCallback();
|
||||
querySessionSubscriptionsCallback();
|
||||
// PosthogClient.reset();
|
||||
// Analytics.accountLogout();
|
||||
history.push("/");
|
||||
};
|
||||
|
||||
const loggedIn = sessionWrapper.isLoggedIn();
|
||||
|
||||
let userOrLoginButton = (
|
||||
<>
|
||||
<Button
|
||||
label="Login"
|
||||
small
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
history.push("/login");
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
let signupOrLogOutButton = (
|
||||
<>
|
||||
<Button
|
||||
label="Sign Up"
|
||||
small
|
||||
onClick={() => {
|
||||
history.push("/signup");
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
if (loggedIn) {
|
||||
let displayName = sessionWrapper.getDisplayName();
|
||||
// let gravatarHash = props.sessionWrapper.getEmailGravatarHash();
|
||||
// let gravatar = <span />;
|
||||
|
||||
if (displayName === undefined) {
|
||||
displayName = "My Account";
|
||||
}
|
||||
|
||||
let url = WebUrl.userProfilePage(displayName);
|
||||
userOrLoginButton = (
|
||||
<>
|
||||
<Button
|
||||
icon={faUser}
|
||||
label="My Profile"
|
||||
small
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
history.push(url);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
signupOrLogOutButton = (
|
||||
<>
|
||||
<Button
|
||||
icon={faSignOutAlt}
|
||||
label="Logout"
|
||||
small
|
||||
variant="danger"
|
||||
onClick={async () => {
|
||||
await logoutHandler();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
let profileDropdown = <></>;
|
||||
|
||||
if (sessionWrapper.isLoggedIn()) {
|
||||
let displayName = sessionWrapper.getDisplayName();
|
||||
if (displayName === undefined) {
|
||||
displayName = "My Account";
|
||||
}
|
||||
let url = WebUrl.userProfilePage(displayName);
|
||||
userOrLoginButton = (
|
||||
<Button
|
||||
icon={faUser}
|
||||
label="My Profile"
|
||||
small
|
||||
variant="secondary"
|
||||
onClick={() => history.push(url)}
|
||||
className="d-none d-lg-block"
|
||||
let username = sessionWrapper.getUsername();
|
||||
let emailHash = sessionWrapper.getEmailGravatarHash();
|
||||
let avatarIndex = 0; //temporary
|
||||
let backgroundColorIndex = 0; //temporary
|
||||
|
||||
profileDropdown = (
|
||||
<ProfileDropdown
|
||||
username={username || ""}
|
||||
displayName={displayName || ""}
|
||||
avatarIndex={avatarIndex}
|
||||
backgroundColorIndex={backgroundColorIndex}
|
||||
emailHash={emailHash || ""}
|
||||
logoutHandler={logoutHandler}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -269,8 +204,27 @@ export default function TopNav({
|
||||
|
||||
<div className="d-flex align-items-center gap-2">
|
||||
<div className="d-none d-lg-flex gap-2">
|
||||
{userOrLoginButton}
|
||||
{signupOrLogOutButton}
|
||||
{loggedIn ? (
|
||||
profileDropdown
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
label="Login"
|
||||
small
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
history.push("/login");
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
label="Sign Up"
|
||||
small
|
||||
onClick={() => {
|
||||
history.push("/signup");
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
icon={faSearch}
|
||||
|
||||
Reference in new issue
Block a user