Compare commits
5 Commits
dc11f5695d
...
6f4737d75c
Author | SHA1 | Date | |
---|---|---|---|
6f4737d75c | |||
20bb05ddc6 | |||
5d415a2359 | |||
1070c9a8f9 | |||
fcd00d9e9c |
62
package-lock.json
generated
62
package-lock.json
generated
@ -10,6 +10,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "11.11.4",
|
"@emotion/react": "11.11.4",
|
||||||
"@emotion/styled": "11.11.0",
|
"@emotion/styled": "11.11.0",
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
||||||
|
"@fortawesome/free-brands-svg-icons": "^6.6.0",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
||||||
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
"@mui/icons-material": "5.15.11",
|
"@mui/icons-material": "5.15.11",
|
||||||
"@mui/lab": "5.0.0-alpha.166",
|
"@mui/lab": "5.0.0-alpha.166",
|
||||||
"@mui/material": "5.15.11",
|
"@mui/material": "5.15.11",
|
||||||
@ -1093,6 +1097,64 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
|
||||||
"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
|
"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@fortawesome/fontawesome-common-types": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/fontawesome-svg-core": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/free-brands-svg-icons": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-1MPD8lMNW/earme4OQi1IFHtmHUwAKgghXlNwWi9GO7QkTfD+IIaYpIai4m2YJEzqfEji3jFHX1DZI5pbY/biQ==",
|
||||||
|
"license": "(CC-BY-4.0 AND MIT)",
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/free-solid-svg-icons": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==",
|
||||||
|
"license": "(CC-BY-4.0 AND MIT)",
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/react-fontawesome": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
|
||||||
|
"react": ">=16.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.11.14",
|
"version": "0.11.14",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "11.11.4",
|
"@emotion/react": "11.11.4",
|
||||||
"@emotion/styled": "11.11.0",
|
"@emotion/styled": "11.11.0",
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
||||||
|
"@fortawesome/free-brands-svg-icons": "^6.6.0",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
||||||
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
"@mui/icons-material": "5.15.11",
|
"@mui/icons-material": "5.15.11",
|
||||||
"@mui/lab": "5.0.0-alpha.166",
|
"@mui/lab": "5.0.0-alpha.166",
|
||||||
"@mui/material": "5.15.11",
|
"@mui/material": "5.15.11",
|
||||||
|
@ -18,11 +18,6 @@
|
|||||||
|
|
||||||
--text-color: #{$text-color};
|
--text-color: #{$text-color};
|
||||||
--input-text-color: #{$input-text-color};
|
--input-text-color: #{$input-text-color};
|
||||||
|
|
||||||
--review-feedback-correct: #{$review-feedback-correct};
|
|
||||||
--review-feedback-incorrect: #{$review-feedback-incorrect};
|
|
||||||
--review-feedback-neutral: #{$review-feedback-neutral};
|
|
||||||
--review-feedback-selected-color: #{$review-feedback-selected-color};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
|
18
src/App.tsx
18
src/App.tsx
@ -50,22 +50,12 @@ const App = () => {
|
|||||||
return `${appPublicRoutes.login}?callbackPath=${callbackPathEncoded}`
|
return `${appPublicRoutes.login}?callbackPath=${callbackPathEncoded}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const publicRoutesList = recursiveRouteRenderer({
|
// Hide route only if loggedIn and r.hiddenWhenLoggedIn are both true
|
||||||
routes: publicRoutes,
|
const publicRoutesList = recursiveRouteRenderer(publicRoutes, (r) => {
|
||||||
renderConditionCallback: (r) => {
|
return !authState.loggedIn || !r.hiddenWhenLoggedIn
|
||||||
if (authState?.loggedIn) {
|
|
||||||
if (!r.hiddenWhenLoggedIn) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const privateRouteList = recursiveRouteRenderer({ routes: privateRoutes })
|
const privateRouteList = recursiveRouteRenderer(privateRoutes)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
|
10
src/components/FontAwesomeIconStack/index.tsx
Normal file
10
src/components/FontAwesomeIconStack/index.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { PropsWithChildren } from 'react'
|
||||||
|
|
||||||
|
import styles from './styles.module.scss'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Component overlays FontAwesomeIcon icons on top of each other
|
||||||
|
*/
|
||||||
|
export const FontAwesomeIconStack = ({ children }: PropsWithChildren) => {
|
||||||
|
return <div className={styles.iconStackContainer}>{children}</div>
|
||||||
|
}
|
7
src/components/FontAwesomeIconStack/styles.module.scss
Normal file
7
src/components/FontAwesomeIconStack/styles.module.scss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.iconStackContainer {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: local('Roboto-Bold'), url(./assets/Roboto-font/Robot-Bold.ttf);
|
src: local('Roboto-Bold'), url(./assets/Roboto-font/Roboto-Bold.ttf);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
@ -8,6 +8,8 @@ import {
|
|||||||
} from 'react-router-dom'
|
} from 'react-router-dom'
|
||||||
import styles from './style.module.scss'
|
import styles from './style.module.scss'
|
||||||
import { appPublicRoutes } from '../../routes'
|
import { appPublicRoutes } from '../../routes'
|
||||||
|
import { faClose } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
|
||||||
function useRouteMatch(patterns: readonly string[]) {
|
function useRouteMatch(patterns: readonly string[]) {
|
||||||
const { pathname } = useLocation()
|
const { pathname } = useLocation()
|
||||||
@ -68,16 +70,7 @@ export const Modal = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg
|
<FontAwesomeIcon icon={faClose} />
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="-96 0 512 512"
|
|
||||||
width="1em"
|
|
||||||
height="1em"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
{/* Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. */}
|
|
||||||
<path d="M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z"></path>
|
|
||||||
</svg>
|
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
@ -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 ||
|
||||||
|
@ -8,6 +8,17 @@ import { Container } from '../../components/Container'
|
|||||||
import styles from './style.module.scss'
|
import styles from './style.module.scss'
|
||||||
import bg_l from '../../assets/images/bg_l.svg'
|
import bg_l from '../../assets/images/bg_l.svg'
|
||||||
import bg_r from '../../assets/images/bg_r.svg'
|
import bg_r from '../../assets/images/bg_r.svg'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
import { faOsi } from '@fortawesome/free-brands-svg-icons'
|
||||||
|
import {
|
||||||
|
faCheck,
|
||||||
|
faMobileScreenButton,
|
||||||
|
faShieldHeart,
|
||||||
|
faSlash,
|
||||||
|
faUsers,
|
||||||
|
faWifi
|
||||||
|
} from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { FontAwesomeIconStack } from '../../components/FontAwesomeIconStack'
|
||||||
|
|
||||||
export const LandingPage = () => {
|
export const LandingPage = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@ -19,18 +30,7 @@ export const LandingPage = () => {
|
|||||||
|
|
||||||
const cards = [
|
const cards = [
|
||||||
{
|
{
|
||||||
icon: (
|
icon: <FontAwesomeIcon width={25} height={25} icon={faOsi} />,
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 512 512"
|
|
||||||
width="1em"
|
|
||||||
height="1em"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
{/* <!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--> */}
|
|
||||||
<path d="M8 266.4C10.3 130.6 105.4 34 221.8 18.3c138.8-18.6 255.6 75.8 278 201.1 21.3 118.8-44 230-151.6 274-9.3 3.8-14.4 1.7-18-7.7q-26.7-69.5-53.4-139c-3.1-8.1-1-13.2 7-16.8 24.2-11 39.3-29.4 43.3-55.8a71.5 71.5 0 0 0 -64.5-82.2c-39-3.4-71.8 23.7-77.5 59.7-5.2 33 11.1 63.7 41.9 77.7 9.6 4.4 11.5 8.6 7.8 18.4q-26.9 69.9-53.7 139.9c-2.6 6.9-8.3 9.3-15.5 6.5-52.6-20.3-101.4-61-130.8-119-24.9-49.2-25.2-87.7-26.8-108.7zm20.9-1.9c.4 6.6 .6 14.3 1.3 22.1 6.3 71.9 49.6 143.5 131 183.1 3.2 1.5 4.4 .8 5.6-2.3q22.4-58.7 45-117.3c1.3-3.3 .6-4.8-2.4-6.7-31.6-19.9-47.3-48.5-45.6-86 1-21.6 9.3-40.5 23.8-56.3 30-32.7 77-39.8 115.5-17.6a91.6 91.6 0 0 1 45.2 90.4c-3.6 30.6-19.3 53.9-45.7 69.8-2.7 1.6-3.5 2.9-2.3 6q22.8 58.8 45.2 117.7c1.2 3.1 2.4 3.8 5.6 2.3 35.5-16.6 65.2-40.3 88.1-72 34.8-48.2 49.1-101.9 42.3-161-13.7-117.5-119.4-214.8-255.5-198-106.1 13-195.3 102.5-197.1 225.8z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
title: <>Open Source</>,
|
title: <>Open Source</>,
|
||||||
description: (
|
description: (
|
||||||
<>
|
<>
|
||||||
@ -44,16 +44,7 @@ export const LandingPage = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
<svg
|
<FontAwesomeIcon width={25} height={25} icon={faMobileScreenButton} />
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 384 512"
|
|
||||||
width="1em"
|
|
||||||
height="1em"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
{/* <!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--> */}
|
|
||||||
<path d="M16 64C16 28.7 44.7 0 80 0L304 0c35.3 0 64 28.7 64 64l0 384c0 35.3-28.7 64-64 64L80 512c-35.3 0-64-28.7-64-64L16 64zM224 448a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM304 64L80 64l0 320 224 0 0-320z" />
|
|
||||||
</svg>
|
|
||||||
),
|
),
|
||||||
title: <>Multi-Device</>,
|
title: <>Multi-Device</>,
|
||||||
description: (
|
description: (
|
||||||
@ -64,18 +55,7 @@ export const LandingPage = () => {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: <FontAwesomeIcon width={25} height={25} icon={faShieldHeart} />,
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 512 512"
|
|
||||||
width="1em"
|
|
||||||
height="1em"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
{/* Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. */}
|
|
||||||
<path d="M256.3-.0068C261.9-.0507 267.3 1.386 272.1 4.066L476.5 90.53C487.7 95.27 495.2 105.1 495.9 118.1C501.6 213.6 466.7 421.9 272.5 507.7C267.6 510.5 261.1 512.1 256.3 512C250.5 512.1 244.9 510.5 239.1 507.7C45.8 421.9 10.95 213.6 16.57 118.1C17.28 105.1 24.83 95.27 36.04 90.53L240.4 4.066C245.2 1.386 250.7-.0507 256.3-.0068H256.3zM266.1 363.4L364.2 263.6C392.2 234.7 390.5 186.6 358.1 159.5C331.8 135.8 291.5 140.2 266.1 166.5L256.4 176.1L245.9 166.5C221.4 140.2 180.2 135.8 153 159.5C121.5 186.6 119.8 234.7 147.8 263.6L244.2 363.4C251.2 369.5 260.8 369.5 266.1 363.4V363.4z"></path>
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
title: <>Secure & Private</>,
|
title: <>Secure & Private</>,
|
||||||
description: (
|
description: (
|
||||||
<>
|
<>
|
||||||
@ -85,18 +65,7 @@ export const LandingPage = () => {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: <FontAwesomeIcon width={25} height={25} icon={faCheck} />,
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 448 512"
|
|
||||||
width="1em"
|
|
||||||
height="1em"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
{/* <!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--> */}
|
|
||||||
<path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
title: <>Verifiable</>,
|
title: <>Verifiable</>,
|
||||||
description: (
|
description: (
|
||||||
<>
|
<>
|
||||||
@ -107,24 +76,10 @@ export const LandingPage = () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
<svg
|
<FontAwesomeIconStack>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<FontAwesomeIcon width={25} height={25} icon={faSlash} />
|
||||||
width="1em"
|
<FontAwesomeIcon width={25} height={25} icon={faWifi} />
|
||||||
height="1em"
|
</FontAwesomeIconStack>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
strokeWidth="2"
|
|
||||||
stroke="currentColor"
|
|
||||||
fill="none"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
>
|
|
||||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
||||||
<line x1="12" y1="18" x2="12.01" y2="18"></line>
|
|
||||||
<path d="M9.172 15.172a4 4 0 0 1 5.656 0"></path>
|
|
||||||
<path d="M6.343 12.343a7.963 7.963 0 0 1 3.864 -2.14m4.163.155a7.965 7.965 0 0 1 3.287 2"></path>
|
|
||||||
<path d="M3.515 9.515A12 12 0 0 1 7.059 7.06m3.101-.92a12 12 0 0 1 10.325 3.374"></path>
|
|
||||||
<line x1="3" y1="3" x2="21" y2="21"></line>
|
|
||||||
</svg>
|
|
||||||
),
|
),
|
||||||
title: <>Works Offline</>,
|
title: <>Works Offline</>,
|
||||||
description: (
|
description: (
|
||||||
@ -135,18 +90,7 @@ export const LandingPage = () => {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: <FontAwesomeIcon width={25} height={25} icon={faUsers} />,
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 640 512"
|
|
||||||
width="1em"
|
|
||||||
height="1em"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
{/* <!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--> */}
|
|
||||||
<path d="M144 0a80 80 0 1 1 0 160A80 80 0 1 1 144 0zM512 0a80 80 0 1 1 0 160A80 80 0 1 1 512 0zM0 298.7C0 239.8 47.8 192 106.7 192l42.7 0c15.9 0 31 3.5 44.6 9.7c-1.3 7.2-1.9 14.7-1.9 22.3c0 38.2 16.8 72.5 43.3 96c-.2 0-.4 0-.7 0L21.3 320C9.6 320 0 310.4 0 298.7zM405.3 320c-.2 0-.4 0-.7 0c26.6-23.5 43.3-57.8 43.3-96c0-7.6-.7-15-1.9-22.3c13.6-6.3 28.7-9.7 44.6-9.7l42.7 0C592.2 192 640 239.8 640 298.7c0 11.8-9.6 21.3-21.3 21.3l-213.3 0zM224 224a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zM128 485.3C128 411.7 187.7 352 261.3 352l117.3 0C452.3 352 512 411.7 512 485.3c0 14.7-11.9 26.7-26.7 26.7l-330.7 0c-14.7 0-26.7-11.9-26.7-26.7z" />
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
title: <>Multi-Party Signing</>,
|
title: <>Multi-Party Signing</>,
|
||||||
description: (
|
description: (
|
||||||
<>
|
<>
|
||||||
|
@ -92,11 +92,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.connectionStatusConnected {
|
.connectionStatusConnected {
|
||||||
background-color: $review-feedback-correct;
|
background-color: $relay-status-connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
.connectionStatusNotConnected {
|
.connectionStatusNotConnected {
|
||||||
background-color: $review-feedback-incorrect;
|
background-color: $relay-status-notconnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
.connectionStatusUnknown {
|
.connectionStatusUnknown {
|
||||||
|
@ -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))
|
||||||
|
@ -41,42 +41,45 @@ export const getProfileRoute = (hexKey: string) =>
|
|||||||
export const getProfileSettingsRoute = (hexKey: string) =>
|
export const getProfileSettingsRoute = (hexKey: string) =>
|
||||||
appPrivateRoutes.profileSettings.replace(':npub', hexToNpub(hexKey))
|
appPrivateRoutes.profileSettings.replace(':npub', hexToNpub(hexKey))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper type allows for extending react-router-dom's **RouteProps** with generic type
|
||||||
|
*/
|
||||||
type CustomRouteProps<T> = T &
|
type CustomRouteProps<T> = T &
|
||||||
Omit<RouteProps, 'children'> & {
|
Omit<RouteProps, 'children'> & {
|
||||||
children?: Array<CustomRouteProps<T>>
|
children?: Array<CustomRouteProps<T>>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CustomRoutes<T> {
|
/**
|
||||||
routes?: CustomRouteProps<T>[]
|
* This function maps over nested routes with optional condition for rendering
|
||||||
renderConditionCallback?: (route: CustomRouteProps<T>) => boolean
|
* @param {CustomRouteProps<T>[]} routes - routes list
|
||||||
}
|
* @param {RenderConditionCallbackFn} renderConditionCallbackFn - render condition callback (default true)
|
||||||
|
*/
|
||||||
type PublicRouteProps = CustomRouteProps<{
|
export function recursiveRouteRenderer<T>(
|
||||||
hiddenWhenLoggedIn?: boolean
|
routes?: CustomRouteProps<T>[],
|
||||||
}>
|
renderConditionCallbackFn: (route: CustomRouteProps<T>) => boolean = () =>
|
||||||
|
true
|
||||||
export function recursiveRouteRenderer<T>({
|
) {
|
||||||
routes,
|
|
||||||
renderConditionCallback = () => true
|
|
||||||
}: CustomRoutes<T>) {
|
|
||||||
if (!routes) return null
|
if (!routes) return null
|
||||||
|
|
||||||
|
// Callback allows us to pass arbitrary conditions for each route's rendering
|
||||||
|
// Skipping the callback will by default evaluate to true (show route)
|
||||||
return routes.map((route, index) =>
|
return routes.map((route, index) =>
|
||||||
renderConditionCallback(route) ? (
|
renderConditionCallbackFn(route) ? (
|
||||||
<Route
|
<Route
|
||||||
key={`${route.path}${index}`}
|
key={`${route.path}${index}`}
|
||||||
path={route.path}
|
path={route.path}
|
||||||
element={route.element}
|
element={route.element}
|
||||||
>
|
>
|
||||||
{recursiveRouteRenderer({
|
{recursiveRouteRenderer(route.children, renderConditionCallbackFn)}
|
||||||
routes: route.children,
|
|
||||||
renderConditionCallback
|
|
||||||
})}
|
|
||||||
</Route>
|
</Route>
|
||||||
) : null
|
) : null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PublicRouteProps = CustomRouteProps<{
|
||||||
|
hiddenWhenLoggedIn?: boolean
|
||||||
|
}>
|
||||||
|
|
||||||
export const publicRoutes: PublicRouteProps[] = [
|
export const publicRoutes: PublicRouteProps[] = [
|
||||||
{
|
{
|
||||||
path: appPublicRoutes.landingPage,
|
path: appPublicRoutes.landingPage,
|
||||||
|
@ -13,7 +13,5 @@ $overlay-background-color: #ffffff;
|
|||||||
$text-color: #434343;
|
$text-color: #434343;
|
||||||
$input-text-color: #717171;
|
$input-text-color: #717171;
|
||||||
|
|
||||||
$review-feedback-correct: #178b13;
|
$relay-status-connected: #178b13;
|
||||||
$review-feedback-incorrect: #d82222;
|
$relay-status-notconnected: #d82222;
|
||||||
$review-feedback-neutral: #f39220;
|
|
||||||
$review-feedback-selected-color: #fff;
|
|
||||||
|
Loading…
Reference in New Issue
Block a user