feat: add UserAvatar, UserIconButton
Closes #68 - Only use getRoboHashPicture function (set 1) for Avatars
This commit is contained in:
parent
5d415a2359
commit
20bb05ddc6
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 { useSelector } from 'react-redux'
|
||||||
import { useNavigate } from 'react-router-dom'
|
|
||||||
import { getProfileRoute } from '../routes'
|
|
||||||
import { State } from '../store/rootReducer'
|
import { State } from '../store/rootReducer'
|
||||||
import { hexToNpub } from '../utils'
|
|
||||||
|
import styles from './username.module.scss'
|
||||||
|
import { AvatarIconButton } from './UserAvatarIconButton'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
username: string
|
username: string
|
||||||
@ -11,19 +11,15 @@ type Props = {
|
|||||||
handleClick: (event: React.MouseEvent<HTMLElement>) => void
|
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 Username = ({ username, avatarContent, handleClick }: Props) => {
|
||||||
const hexKey = useSelector((state: State) => state.auth.usersPubkey)
|
const hexKey = useSelector((state: State) => state.auth.usersPubkey)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={styles.container}>
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'end',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '12px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
<Typography
|
||||||
variant="h6"
|
variant="h6"
|
||||||
sx={{
|
sx={{
|
||||||
@ -35,79 +31,16 @@ const Username = ({ username, avatarContent, handleClick }: Props) => {
|
|||||||
>
|
>
|
||||||
{username}
|
{username}
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton
|
<AvatarIconButton
|
||||||
|
src={avatarContent}
|
||||||
|
hexKey={hexKey}
|
||||||
aria-label="account of current user"
|
aria-label="account of current user"
|
||||||
aria-controls="menu-appbar"
|
aria-controls="menu-appbar"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
onClick={handleClick}
|
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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Username
|
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 { useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
||||||
import { UserComponent } from '../../components/username'
|
import { UserAvatar } from '../../components/UserAvatar'
|
||||||
import { MetadataController, NostrController } from '../../controllers'
|
import { MetadataController, NostrController } from '../../controllers'
|
||||||
import { appPrivateRoutes } from '../../routes'
|
import { appPrivateRoutes } from '../../routes'
|
||||||
import { State } from '../../store/rootReducer'
|
import { State } from '../../store/rootReducer'
|
||||||
@ -799,7 +799,7 @@ const DisplayUser = ({
|
|||||||
return (
|
return (
|
||||||
<TableRow key={index}>
|
<TableRow key={index}>
|
||||||
<TableCell className={styles.tableCell}>
|
<TableCell className={styles.tableCell}>
|
||||||
<UserComponent
|
<UserAvatar
|
||||||
pubkey={user.pubkey}
|
pubkey={user.pubkey}
|
||||||
name={
|
name={
|
||||||
userMeta?.display_name ||
|
userMeta?.display_name ||
|
||||||
@ -954,7 +954,7 @@ const SignerRow = ({
|
|||||||
sx={{ display: 'flex', alignItems: 'center', gap: '10px' }}
|
sx={{ display: 'flex', alignItems: 'center', gap: '10px' }}
|
||||||
>
|
>
|
||||||
<DragHandle />
|
<DragHandle />
|
||||||
<UserComponent
|
<UserAvatar
|
||||||
pubkey={user.pubkey}
|
pubkey={user.pubkey}
|
||||||
name={
|
name={
|
||||||
userMeta?.display_name ||
|
userMeta?.display_name ||
|
||||||
|
@ -5,7 +5,7 @@ import { Event, kinds, verifyEvent } from 'nostr-tools'
|
|||||||
import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react'
|
import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { UserComponent } from '../../components/username'
|
import { UserAvatar } from '../../components/UserAvatar'
|
||||||
import { MetadataController } from '../../controllers'
|
import { MetadataController } from '../../controllers'
|
||||||
import { useAppSelector } from '../../hooks'
|
import { useAppSelector } from '../../hooks'
|
||||||
import { appPrivateRoutes, appPublicRoutes } from '../../routes'
|
import { appPrivateRoutes, appPublicRoutes } from '../../routes'
|
||||||
@ -290,7 +290,7 @@ const DisplaySigit = ({ meta, profiles, setProfiles }: SigitProps) => {
|
|||||||
(function () {
|
(function () {
|
||||||
const profile = profiles[submittedBy]
|
const profile = profiles[submittedBy]
|
||||||
return (
|
return (
|
||||||
<UserComponent
|
<UserAvatar
|
||||||
pubkey={submittedBy}
|
pubkey={submittedBy}
|
||||||
name={
|
name={
|
||||||
profile?.display_name ||
|
profile?.display_name ||
|
||||||
@ -370,7 +370,7 @@ const DisplaySigner = ({ meta, profile, pubkey }: DisplaySignerProps) => {
|
|||||||
<Typography variant="button" className={styles.status}>
|
<Typography variant="button" className={styles.status}>
|
||||||
{signStatus}
|
{signStatus}
|
||||||
</Typography>
|
</Typography>
|
||||||
<UserComponent
|
<UserAvatar
|
||||||
pubkey={pubkey}
|
pubkey={pubkey}
|
||||||
name={
|
name={
|
||||||
profile?.display_name ||
|
profile?.display_name ||
|
||||||
|
@ -30,7 +30,7 @@ import saveAs from 'file-saver'
|
|||||||
import { kinds, Event } from 'nostr-tools'
|
import { kinds, Event } from 'nostr-tools'
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { UserComponent } from '../../../components/username'
|
import { UserAvatar } from '../../../components/UserAvatar'
|
||||||
import { MetadataController } from '../../../controllers'
|
import { MetadataController } from '../../../controllers'
|
||||||
import { npubToHex, shorten, hexToNpub, parseJson } from '../../../utils'
|
import { npubToHex, shorten, hexToNpub, parseJson } from '../../../utils'
|
||||||
import styles from '../style.module.scss'
|
import styles from '../style.module.scss'
|
||||||
@ -172,7 +172,7 @@ export const DisplayMeta = ({
|
|||||||
{(function () {
|
{(function () {
|
||||||
const profile = metadata[submittedBy]
|
const profile = metadata[submittedBy]
|
||||||
return (
|
return (
|
||||||
<UserComponent
|
<UserAvatar
|
||||||
pubkey={submittedBy}
|
pubkey={submittedBy}
|
||||||
name={
|
name={
|
||||||
profile?.display_name ||
|
profile?.display_name ||
|
||||||
@ -372,7 +372,7 @@ const DisplayUser = ({
|
|||||||
return (
|
return (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell className={styles.tableCell}>
|
<TableCell className={styles.tableCell}>
|
||||||
<UserComponent
|
<UserAvatar
|
||||||
pubkey={user.pubkey}
|
pubkey={user.pubkey}
|
||||||
name={
|
name={
|
||||||
userMeta?.display_name ||
|
userMeta?.display_name ||
|
||||||
|
@ -14,7 +14,7 @@ import { Event, kinds, verifyEvent } from 'nostr-tools'
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
||||||
import { UserComponent } from '../../components/username'
|
import { UserAvatar } from '../../components/UserAvatar'
|
||||||
import { MetadataController } from '../../controllers'
|
import { MetadataController } from '../../controllers'
|
||||||
import {
|
import {
|
||||||
CreateSignatureEventContent,
|
CreateSignatureEventContent,
|
||||||
@ -403,7 +403,7 @@ export const VerifyPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<UserComponent
|
<UserAvatar
|
||||||
pubkey={pubkey}
|
pubkey={pubkey}
|
||||||
name={
|
name={
|
||||||
profile?.display_name || profile?.name || shorten(hexToNpub(pubkey))
|
profile?.display_name || profile?.name || shorten(hexToNpub(pubkey))
|
||||||
|
Loading…
Reference in New Issue
Block a user