Landing page - new design implementation #122
42
src/components/UserAvatar/index.tsx
Normal file
42
src/components/UserAvatar/index.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { getProfileRoute } from '../../routes'
|
||||
|
||||
import styles from './styles.module.scss'
|
||||
import React from 'react'
|
||||
import { AvatarIconButton } from '../UserAvatarIconButton'
|
||||
|
||||
interface UserAvatarProps {
|
||||
name: string
|
||||
pubkey: string
|
||||
image?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* This component will be used for the displaying username and profile picture.
|
||||
* Clicking will navigate to the user's profile.
|
||||
*/
|
||||
export const UserAvatar = ({ pubkey, name, image }: UserAvatarProps) => {
|
||||
const navigate = useNavigate()
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
e.stopPropagation()
|
||||
navigate(getProfileRoute(pubkey))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<AvatarIconButton
|
||||
src={image}
|
||||
hexKey={pubkey}
|
||||
aria-label={`account of user ${name}`}
|
||||
color="inherit"
|
||||
onClick={handleClick}
|
||||
/>
|
||||
{name ? (
|
||||
<label onClick={handleClick} className={styles.username}>
|
||||
{name}
|
||||
</label>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
13
src/components/UserAvatar/styles.module.scss
Normal file
13
src/components/UserAvatar/styles.module.scss
Normal file
@ -0,0 +1,13 @@
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.username {
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: var(--text-color);
|
||||
}
|
32
src/components/UserAvatarIconButton/index.tsx
Normal file
32
src/components/UserAvatarIconButton/index.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { IconButton, IconButtonProps } from '@mui/material'
|
||||
import styles from './style.module.scss'
|
||||
import { getRoboHashPicture } from '../../utils'
|
||||
|
||||
interface AvatarIconButtonProps extends IconButtonProps {
|
||||
src: string | undefined
|
||||
hexKey: string | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* This component displays profile image inside IconButton
|
||||
* @param {string | undefined} props.src - image source or robohash picture
|
||||
* @param {string | undefined} props.hexKey - robohash and affects border
|
||||
* @param {IconButtonProps} props - component extends mui's IconButton
|
||||
*/
|
||||
export const AvatarIconButton = (props: AvatarIconButtonProps) => {
|
||||
const { src, hexKey, ...rest } = props
|
||||
|
||||
return (
|
||||
<IconButton {...rest}>
|
||||
<img
|
||||
src={src || getRoboHashPicture(hexKey)}
|
||||
alt="user image"
|
||||
className={`${styles.icon}`}
|
||||
style={{
|
||||
borderStyle: hexKey ? 'solid' : 'none',
|
||||
borderColor: `#${hexKey?.substring(0, 6)}`
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
)
|
||||
}
|
7
src/components/UserAvatarIconButton/style.module.scss
Normal file
7
src/components/UserAvatarIconButton/style.module.scss
Normal file
@ -0,0 +1,7 @@
|
||||
.icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
border-width: 3px;
|
||||
overflow: hidden;
|
||||
}
|
7
src/components/username.module.scss
Normal file
7
src/components/username.module.scss
Normal file
@ -0,0 +1,7 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import { Box, IconButton, Typography, useTheme } from '@mui/material'
|
||||
import { Typography } from '@mui/material'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { getProfileRoute } from '../routes'
|
||||
import { State } from '../store/rootReducer'
|
||||
import { hexToNpub } from '../utils'
|
||||
|
||||
import styles from './username.module.scss'
|
||||
import { AvatarIconButton } from './UserAvatarIconButton'
|
||||
|
||||
type Props = {
|
||||
username: string
|
||||
@ -11,19 +11,15 @@ type Props = {
|
||||
handleClick: (event: React.MouseEvent<HTMLElement>) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* This component will be used for the displaying logged in user in AppBar.
|
||||
* Clicking will open the menu.
|
||||
*/
|
||||
const Username = ({ username, avatarContent, handleClick }: Props) => {
|
||||
const hexKey = useSelector((state: State) => state.auth.usersPubkey)
|
||||
s marked this conversation as resolved
Outdated
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'end',
|
||||
alignItems: 'center',
|
||||
gap: '12px'
|
||||
}}
|
||||
>
|
||||
<div className={styles.container}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{
|
||||
@ -35,79 +31,16 @@ const Username = ({ username, avatarContent, handleClick }: Props) => {
|
||||
>
|
||||
{username}
|
||||
</Typography>
|
||||
<IconButton
|
||||
<AvatarIconButton
|
||||
src={avatarContent}
|
||||
hexKey={hexKey}
|
||||
aria-label="account of current user"
|
||||
aria-controls="menu-appbar"
|
||||
aria-haspopup="true"
|
||||
onClick={handleClick}
|
||||
color="inherit"
|
||||
>
|
||||
<img
|
||||
src={avatarContent}
|
||||
alt="user-avatar"
|
||||
className="profile-image"
|
||||
style={{
|
||||
borderWidth: '3px',
|
||||
borderStyle: hexKey ? 'solid' : 'none',
|
||||
borderColor: `#${hexKey?.substring(0, 6)}`
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Username
|
||||
|
||||
type UserProps = {
|
||||
pubkey: string
|
||||
name: string
|
||||
image?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* This component will be used for the displaying username and profile picture.
|
||||
* If image is not available, robohash image will be displayed
|
||||
*/
|
||||
export const UserComponent = ({ pubkey, name, image }: UserProps) => {
|
||||
const theme = useTheme()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const npub = hexToNpub(pubkey)
|
||||
const roboImage = `https://robohash.org/${npub}.png?set=set3`
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
e.stopPropagation()
|
||||
// navigate to user's profile
|
||||
navigate(getProfileRoute(pubkey))
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{ display: 'flex', alignItems: 'center', gap: '10px', flexGrow: 1 }}
|
||||
>
|
||||
<img
|
||||
src={image || roboImage}
|
||||
alt="User Image"
|
||||
className="profile-image"
|
||||
style={{
|
||||
borderWidth: '3px',
|
||||
borderStyle: 'solid',
|
||||
borderColor: `#${pubkey.substring(0, 6)}`
|
||||
}}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
<Typography
|
||||
component="label"
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{name}
|
||||
</Typography>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ import { useSelector } from 'react-redux'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { toast } from 'react-toastify'
|
||||
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
||||
import { UserComponent } from '../../components/username'
|
||||
import { UserAvatar } from '../../components/UserAvatar'
|
||||
import { MetadataController, NostrController } from '../../controllers'
|
||||
import { appPrivateRoutes } from '../../routes'
|
||||
import { State } from '../../store/rootReducer'
|
||||
@ -799,7 +799,7 @@ const DisplayUser = ({
|
||||
return (
|
||||
<TableRow key={index}>
|
||||
<TableCell className={styles.tableCell}>
|
||||
<UserComponent
|
||||
<UserAvatar
|
||||
pubkey={user.pubkey}
|
||||
name={
|
||||
userMeta?.display_name ||
|
||||
@ -954,7 +954,7 @@ const SignerRow = ({
|
||||
sx={{ display: 'flex', alignItems: 'center', gap: '10px' }}
|
||||
>
|
||||
<DragHandle />
|
||||
<UserComponent
|
||||
<UserAvatar
|
||||
pubkey={user.pubkey}
|
||||
name={
|
||||
userMeta?.display_name ||
|
||||
|
@ -5,7 +5,7 @@ import { Event, kinds, verifyEvent } from 'nostr-tools'
|
||||
import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { toast } from 'react-toastify'
|
||||
import { UserComponent } from '../../components/username'
|
||||
import { UserAvatar } from '../../components/UserAvatar'
|
||||
import { MetadataController } from '../../controllers'
|
||||
import { useAppSelector } from '../../hooks'
|
||||
import { appPrivateRoutes, appPublicRoutes } from '../../routes'
|
||||
@ -290,7 +290,7 @@ const DisplaySigit = ({ meta, profiles, setProfiles }: SigitProps) => {
|
||||
(function () {
|
||||
const profile = profiles[submittedBy]
|
||||
return (
|
||||
<UserComponent
|
||||
<UserAvatar
|
||||
pubkey={submittedBy}
|
||||
name={
|
||||
profile?.display_name ||
|
||||
@ -370,7 +370,7 @@ const DisplaySigner = ({ meta, profile, pubkey }: DisplaySignerProps) => {
|
||||
<Typography variant="button" className={styles.status}>
|
||||
{signStatus}
|
||||
</Typography>
|
||||
<UserComponent
|
||||
<UserAvatar
|
||||
pubkey={pubkey}
|
||||
name={
|
||||
profile?.display_name ||
|
||||
|
@ -30,7 +30,7 @@ import saveAs from 'file-saver'
|
||||
import { kinds, Event } from 'nostr-tools'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { toast } from 'react-toastify'
|
||||
import { UserComponent } from '../../../components/username'
|
||||
import { UserAvatar } from '../../../components/UserAvatar'
|
||||
import { MetadataController } from '../../../controllers'
|
||||
import { npubToHex, shorten, hexToNpub, parseJson } from '../../../utils'
|
||||
import styles from '../style.module.scss'
|
||||
@ -172,7 +172,7 @@ export const DisplayMeta = ({
|
||||
{(function () {
|
||||
const profile = metadata[submittedBy]
|
||||
return (
|
||||
<UserComponent
|
||||
<UserAvatar
|
||||
pubkey={submittedBy}
|
||||
name={
|
||||
profile?.display_name ||
|
||||
@ -372,7 +372,7 @@ const DisplayUser = ({
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell className={styles.tableCell}>
|
||||
<UserComponent
|
||||
<UserAvatar
|
||||
pubkey={user.pubkey}
|
||||
name={
|
||||
userMeta?.display_name ||
|
||||
|
@ -14,7 +14,7 @@ import { Event, kinds, verifyEvent } from 'nostr-tools'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { toast } from 'react-toastify'
|
||||
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
||||
import { UserComponent } from '../../components/username'
|
||||
import { UserAvatar } from '../../components/UserAvatar'
|
||||
import { MetadataController } from '../../controllers'
|
||||
import {
|
||||
CreateSignatureEventContent,
|
||||
@ -403,7 +403,7 @@ export const VerifyPage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<UserComponent
|
||||
<UserAvatar
|
||||
pubkey={pubkey}
|
||||
name={
|
||||
profile?.display_name || profile?.name || shorten(hexToNpub(pubkey))
|
||||
|
Loading…
Reference in New Issue
Block a user
This and some of other inline styling in this file can be moved to a separate scss file.