diff --git a/package-lock.json b/package-lock.json index 11f2740..965e01e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,10 @@ "dependencies": { "@emotion/react": "11.11.4", "@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/lab": "5.0.0-alpha.166", "@mui/material": "5.15.11", @@ -1093,6 +1097,64 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", "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": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", diff --git a/package.json b/package.json index 7b1e1c2..f38c7c3 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,10 @@ "dependencies": { "@emotion/react": "11.11.4", "@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/lab": "5.0.0-alpha.166", "@mui/material": "5.15.11", diff --git a/public/favicon.png b/public/favicon.png index 40d7c8e..0cc6186 100644 Binary files a/public/favicon.png and b/public/favicon.png differ diff --git a/public/logo.png b/public/logo.png deleted file mode 100644 index 88d124a..0000000 Binary files a/public/logo.png and /dev/null differ diff --git a/public/logo.svg b/public/logo.svg new file mode 100644 index 0000000..3244e7a --- /dev/null +++ b/public/logo.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/App.css b/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/App.scss b/src/App.scss new file mode 100644 index 0000000..f5394f7 --- /dev/null +++ b/src/App.scss @@ -0,0 +1,65 @@ +@import './styles/colors.scss'; +@import './styles/sizes.scss'; +@import './styles/typography.scss'; + +:root { + // Import colors once as variables + --primary-main: #{$primary-main}; + --primary-light: #{$primary-light}; + --primary-dark: #{$primary-dark}; + + --secondary-main: #{$secondary-main}; + + --box-shadow-color: #{$box-shadow-color}; + --border-color: #{$border-color}; + + --body-background-color: #{$body-background-color}; + --overlay-background-color: #{$overlay-background-color}; + + --text-color: #{$text-color}; + --input-text-color: #{$input-text-color}; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + line-height: 1.2; +} + +h1, +h2, +h3, +h4, +h5, +h6, +p { + margin: 0; +} + +body { + color: $text-color; + font-family: $font-familiy; + letter-spacing: $letter-spacing; + font-size: $body-font-size; + font-weight: $body-font-weight; + line-height: $body-line-height; + + text-shadow: 0 0 1px rgba(0, 0, 0, 0.15); +} + +a { + font-weight: 500; + color: $primary-main; + text-decoration: none; + text-decoration-color: inherit; + transition: ease 0.4s; + + &:hover { + color: $primary-light; + text-decoration: underline; + text-decoration-color: inherit; + } +} diff --git a/src/App.tsx b/src/App.tsx index 8956df1..1523e6f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,10 +7,12 @@ import { appPrivateRoutes, appPublicRoutes, privateRoutes, - publicRoutes + publicRoutes, + recursiveRouteRenderer } from './routes' import { State } from './store/rootReducer' import { getNsecBunkerDelegatedKey, saveNsecBunkerDelegatedKey } from './utils' +import './App.scss' const App = () => { const authState = useSelector((state: State) => state.auth) @@ -48,39 +50,18 @@ const App = () => { return `${appPublicRoutes.login}?callbackPath=${callbackPathEncoded}` } + // Hide route only if loggedIn and r.hiddenWhenLoggedIn are both true + const publicRoutesList = recursiveRouteRenderer(publicRoutes, (r) => { + return !authState.loggedIn || !r.hiddenWhenLoggedIn + }) + + const privateRouteList = recursiveRouteRenderer(privateRoutes) + return ( }> - {authState?.loggedIn && - privateRoutes.map((route, index) => ( - - ))} - {publicRoutes.map((route, index) => { - if (authState?.loggedIn) { - if (!route.hiddenWhenLoggedIn) { - return ( - - ) - } - } else { - return ( - - ) - } - })} - + {authState?.loggedIn && privateRouteList} + {publicRoutesList} } /> diff --git a/src/assets/avenir-font/AvenirLTStd-Black.otf b/src/assets/avenir-font/AvenirLTStd-Black.otf deleted file mode 100644 index 714fdfb..0000000 Binary files a/src/assets/avenir-font/AvenirLTStd-Black.otf and /dev/null differ diff --git a/src/assets/avenir-font/AvenirLTStd-Book.otf b/src/assets/avenir-font/AvenirLTStd-Book.otf deleted file mode 100644 index 52ab53e..0000000 Binary files a/src/assets/avenir-font/AvenirLTStd-Book.otf and /dev/null differ diff --git a/src/assets/avenir-font/AvenirLTStd-Roman.otf b/src/assets/avenir-font/AvenirLTStd-Roman.otf deleted file mode 100644 index de238e6..0000000 Binary files a/src/assets/avenir-font/AvenirLTStd-Roman.otf and /dev/null differ diff --git a/src/assets/images/bg_l.svg b/src/assets/images/bg_l.svg new file mode 100644 index 0000000..07bf6c8 --- /dev/null +++ b/src/assets/images/bg_l.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/assets/images/bg_r.svg b/src/assets/images/bg_r.svg new file mode 100644 index 0000000..8e673c6 --- /dev/null +++ b/src/assets/images/bg_r.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/assets/images/placeholder.png b/src/assets/images/placeholder.png new file mode 100644 index 0000000..bbda3c4 Binary files /dev/null and b/src/assets/images/placeholder.png differ diff --git a/src/assets/roboto-font/Roboto-Bold.ttf b/src/assets/roboto-font/Roboto-Bold.ttf new file mode 100644 index 0000000..43da14d Binary files /dev/null and b/src/assets/roboto-font/Roboto-Bold.ttf differ diff --git a/src/assets/roboto-font/Roboto-Light.ttf b/src/assets/roboto-font/Roboto-Light.ttf new file mode 100644 index 0000000..e7307e7 Binary files /dev/null and b/src/assets/roboto-font/Roboto-Light.ttf differ diff --git a/src/assets/roboto-font/Roboto-Medium.ttf b/src/assets/roboto-font/Roboto-Medium.ttf new file mode 100644 index 0000000..ac0f908 Binary files /dev/null and b/src/assets/roboto-font/Roboto-Medium.ttf differ diff --git a/src/assets/roboto-font/Roboto-Regular.ttf b/src/assets/roboto-font/Roboto-Regular.ttf new file mode 100644 index 0000000..ddf4bfa Binary files /dev/null and b/src/assets/roboto-font/Roboto-Regular.ttf differ diff --git a/src/colors.scss b/src/colors.scss deleted file mode 100644 index d00f37b..0000000 --- a/src/colors.scss +++ /dev/null @@ -1,50 +0,0 @@ -$primary-main: var(--mui-palette-primary-main); -$primary-light: var(--mui-palette-primary-light); -$primary-dark: var(--mui-palette-primary-dark); - -$box-shadow-color: rgba(0, 0, 0, 0.1); -$border-color: #27323c; - -$page-background-color: #f4f4fb; -$modal-input-background: #f4f4fb; -$background-color: #fff; - -$text-color: #3e3e3e; -$input-text-color: #717171; -$info-text-color: #4169e1; - -$icon-color: #c1c1c1; -$zap-icon-color: #ffd700; - -$btn-background-color: $primary-main; -$btn-color: #fff; - -$progress-box-background-color: $primary-main; -$progress-box-inner-background-color: $primary-dark; -$rank-progress-background-color: #445c76; - -$correct-answer-background-color: $primary-light; -$incorrect-answer-background-color: #e84d67; -$incorrect-answer-label-color: #fff; - -$checkbox-border-color: #ccc; -$checkbox-hover-border-color: #888; -$checkbox-checked-background-color: $primary-main; -$checkbox-checked-color: #fff; - -$comment-notice-background-color: #0d6efd; -$comment-notice-color: #fff; -$comment-divider-color: #eee; - -$activated-topic-background-color: var(--mui-palette-info-dark); - -$error-msg-color: red; -$suggestion-box-heading-color: #dfe0e0; - -$callOut-success-background: $primary-light; -$callOut-success-color: $primary-dark; - -$review-feedback-correct: #178b13; -$review-feedback-incorrect: #d82222; -$review-feedback-neutral: #f39220; -$review-feedback-selected-color: #fff; diff --git a/src/components/AppBar/AppBar.tsx b/src/components/AppBar/AppBar.tsx index dd21b03..76d8370 100644 --- a/src/components/AppBar/AppBar.tsx +++ b/src/components/AppBar/AppBar.tsx @@ -34,6 +34,8 @@ import { } from '../../utils' import styles from './style.module.scss' import { setUserRobotImage } from '../../store/userRobotImage/action' +import { Container } from '../Container' +import { ButtonIcon } from '../ButtonIcon' const metadataController = new MetadataController() @@ -117,112 +119,124 @@ export const AppBar = () => { const isAuthenticated = authState?.loggedIn === true return ( - - - - Logo navigate('/')} /> - + + + + + Logo navigate('/')} /> + - - {!isAuthenticated && ( - - )} - - {isAuthenticated && ( - <> - - + {!isAuthenticated && ( + + )} - navigate(appPrivateRoutes.settings) + {isAuthenticated && ( + <> + + - Settings - - { - setAnchorElUser(null) - - navigate(appPublicRoutes.verify) - }} - sx={{ - justifyContent: 'center' - }} - > - Verify - - + {username} + + - Source + Profile - - - Logout - - - - )} - - + { + setAnchorElUser(null) + + navigate(appPrivateRoutes.settings) + }} + sx={{ + justifyContent: 'center' + }} + > + Settings + + { + setAnchorElUser(null) + + navigate(appPublicRoutes.verify) + }} + sx={{ + justifyContent: 'center' + }} + > + Verify + + + + Source + + + + Logout + + + + )} + + + ) } diff --git a/src/components/AppBar/style.module.scss b/src/components/AppBar/style.module.scss index 718b872..6b15c93 100644 --- a/src/components/AppBar/style.module.scss +++ b/src/components/AppBar/style.module.scss @@ -1,12 +1,14 @@ -@import '../../colors.scss'; +@import '../../styles/colors.scss'; +@import '../../styles/sizes.scss'; .AppBar { - background-color: $background-color !important; - z-index: 1400 !important; - height: 60px; + background-color: $overlay-background-color !important; + height: $header-height; flex-direction: row !important; align-items: center; + border-bottom: solid 1px rgba(0, 0, 0, 0.075); + .toolbar { flex-grow: 1; display: flex; diff --git a/src/components/ButtonIcon/index.tsx b/src/components/ButtonIcon/index.tsx new file mode 100644 index 0000000..67d1caf --- /dev/null +++ b/src/components/ButtonIcon/index.tsx @@ -0,0 +1,14 @@ +import styles from './style.module.scss' +import placeholder from '../../assets/images/placeholder.png' + +interface ButtonIconProps { + src?: string + alt?: string +} + +export const ButtonIcon = ({ + src = placeholder, + alt = '' +}: ButtonIconProps) => { + return {alt} +} diff --git a/src/components/ButtonIcon/style.module.scss b/src/components/ButtonIcon/style.module.scss new file mode 100644 index 0000000..817067c --- /dev/null +++ b/src/components/ButtonIcon/style.module.scss @@ -0,0 +1,5 @@ +.icon { + border-radius: 100px; + width: 20px; + height: 20px; +} diff --git a/src/components/Container/index.tsx b/src/components/Container/index.tsx new file mode 100644 index 0000000..57857b9 --- /dev/null +++ b/src/components/Container/index.tsx @@ -0,0 +1,24 @@ +import { CSSProperties, PropsWithChildren } from 'react' +import defaultStyle from './style.module.scss' + +interface ContainerProps { + style?: CSSProperties + className?: string +} + +export const Container = ({ + style = {}, + className = '', + children +}: PropsWithChildren) => { + return ( +
+ {children} +
+ ) +} diff --git a/src/components/Container/style.module.scss b/src/components/Container/style.module.scss new file mode 100644 index 0000000..4e574be --- /dev/null +++ b/src/components/Container/style.module.scss @@ -0,0 +1,8 @@ +@import '../../styles/sizes.scss'; + +.container { + width: 100%; + max-width: 1400px; + padding-inline: $default-container-padding-inline; + margin-inline: auto; +} diff --git a/src/components/FontAwesomeIconStack/index.tsx b/src/components/FontAwesomeIconStack/index.tsx new file mode 100644 index 0000000..bf12416 --- /dev/null +++ b/src/components/FontAwesomeIconStack/index.tsx @@ -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
{children}
+} diff --git a/src/components/FontAwesomeIconStack/styles.module.scss b/src/components/FontAwesomeIconStack/styles.module.scss new file mode 100644 index 0000000..ea68afd --- /dev/null +++ b/src/components/FontAwesomeIconStack/styles.module.scss @@ -0,0 +1,7 @@ +.iconStackContainer { + position: relative; + + > * { + position: absolute; + } +} diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx new file mode 100644 index 0000000..38298c2 --- /dev/null +++ b/src/components/Footer/Footer.tsx @@ -0,0 +1,138 @@ +import { Box, Button, Link as LinkMui } from '@mui/material' +import { Link } from 'react-router-dom' +import styles from './style.module.scss' +import { Container } from '../Container' + +export const Footer = () => ( +
+ + + + Logo + + + + + + + + + + + +
+ Built by  + + Nostr Dev + {' '} + 2024. +
+
+) diff --git a/src/components/Footer/style.module.scss b/src/components/Footer/style.module.scss new file mode 100644 index 0000000..f428af4 --- /dev/null +++ b/src/components/Footer/style.module.scss @@ -0,0 +1,49 @@ +@import '../../styles/colors.scss'; + +.borderTop { + border-top: solid 1px rgba(0, 0, 0, 0.075); +} + +.footer { + display: flex; + flex-direction: column; + align-items: center; + + font-size: 14px; +} + +.links { + font-weight: 500; + color: rgba(0, 0, 0, 0.5); + + > a + a { + margin-left: 25px; + } +} + +.nav { + color: rgba(0, 0, 0, 0.5); + + a { + width: 100%; + } +} + +.credits { + width: 100%; + text-align: center; + padding: 10px 0; + font-size: 12px; + color: rgba(0, 0, 0, 0.5); + font-weight: 500; +} + +.logo { + width: 100%; + max-width: 300px; + + > img { + width: 100%; + height: auto; + } +} diff --git a/src/components/Landing/CardComponent/CardComponent.tsx b/src/components/Landing/CardComponent/CardComponent.tsx new file mode 100644 index 0000000..c6d909a --- /dev/null +++ b/src/components/Landing/CardComponent/CardComponent.tsx @@ -0,0 +1,28 @@ +import { ReactElement } from 'react' + +import styles from './style.module.scss' + +interface CardComponentProps { + icon: ReactElement + title: ReactElement + description: ReactElement + actions?: ReactElement +} + +export const CardComponent = ({ + icon, + title, + description, + actions +}: CardComponentProps) => { + return ( +
+

+
{icon}
+ {title} +

+

{description}

+ {actions ?
{actions}
: null} +
+ ) +} diff --git a/src/components/Landing/CardComponent/style.module.scss b/src/components/Landing/CardComponent/style.module.scss new file mode 100644 index 0000000..b6c9d5e --- /dev/null +++ b/src/components/Landing/CardComponent/style.module.scss @@ -0,0 +1,64 @@ +@import '../../../styles/colors.scss'; + +.card { + border-radius: 4px; + padding: 25px; + + position: relative; + background: $overlay-background-color; + + transition: ease 0.2s; + + display: flex; + flex-direction: column; + gap: 15px; + + &:hover { + color: white; + background: $primary-main; + + .icon, + a { + color: inherit; + } + } + + button { + transition: + color 0s, + background-color 0.2s; + } + + a { + transition: none; + } +} + +.icon { + color: $primary-main; + font-size: 25px; + line-height: 1; + + height: 25px; + width: 25px; +} + +.title { + display: flex; + flex-direction: row; + grid-gap: 10px; + align-items: center; + + font-weight: bold; + font-size: 20px; +} + +.description { + font-size: 14px; + font-weight: 500; +} + +.actions { + margin-top: auto; + text-align: right; +} diff --git a/src/components/LoadingSpinner/index.tsx b/src/components/LoadingSpinner/index.tsx index 3c57bff..980a763 100644 --- a/src/components/LoadingSpinner/index.tsx +++ b/src/components/LoadingSpinner/index.tsx @@ -11,7 +11,7 @@ export const LoadingSpinner = (props: Props) => {
- {desc && {desc}} + {desc && {desc}}
) diff --git a/src/components/LoadingSpinner/style.module.scss b/src/components/LoadingSpinner/style.module.scss index b83b70b..75b2609 100644 --- a/src/components/LoadingSpinner/style.module.scss +++ b/src/components/LoadingSpinner/style.module.scss @@ -1,3 +1,5 @@ +@import '../../styles/colors.scss'; + .loadingSpinnerOverlay { position: fixed; top: 0; @@ -18,15 +20,21 @@ } .loadingSpinner { - border: 4px solid #f3f3f3; - border-top: 4px solid #3498db; - border-radius: 50%; + background: url('/favicon.png') no-repeat center / cover; width: 40px; height: 40px; animation: spin 1s linear infinite; } } +.loadingSpinnerDesc { + color: white; + margin-top: 13px; + + font-size: 16px; + font-weight: 400; +} + @keyframes spin { 0% { transform: rotate(0deg); diff --git a/src/components/UserAvatar/index.tsx b/src/components/UserAvatar/index.tsx new file mode 100644 index 0000000..65e993d --- /dev/null +++ b/src/components/UserAvatar/index.tsx @@ -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) => { + e.stopPropagation() + navigate(getProfileRoute(pubkey)) + } + + return ( +
+ + {name ? ( + + ) : null} +
+ ) +} diff --git a/src/components/UserAvatar/styles.module.scss b/src/components/UserAvatar/styles.module.scss new file mode 100644 index 0000000..6147819 --- /dev/null +++ b/src/components/UserAvatar/styles.module.scss @@ -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); +} diff --git a/src/components/UserAvatarIconButton/index.tsx b/src/components/UserAvatarIconButton/index.tsx new file mode 100644 index 0000000..a12564a --- /dev/null +++ b/src/components/UserAvatarIconButton/index.tsx @@ -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 ( + + user image + + ) +} diff --git a/src/components/UserAvatarIconButton/style.module.scss b/src/components/UserAvatarIconButton/style.module.scss new file mode 100644 index 0000000..f8213f0 --- /dev/null +++ b/src/components/UserAvatarIconButton/style.module.scss @@ -0,0 +1,7 @@ +.icon { + width: 40px; + height: 40px; + border-radius: 50%; + border-width: 3px; + overflow: hidden; +} diff --git a/src/components/username.module.scss b/src/components/username.module.scss new file mode 100644 index 0000000..835f863 --- /dev/null +++ b/src/components/username.module.scss @@ -0,0 +1,7 @@ +.container { + display: flex; + flex-direction: row; + justify-content: end; + align-items: center; + gap: 12px; +} diff --git a/src/components/username.tsx b/src/components/username.tsx index 768d1a9..7ee6dc3 100644 --- a/src/components/username.tsx +++ b/src/components/username.tsx @@ -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,92 +11,36 @@ type Props = { handleClick: (event: React.MouseEvent) => 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) return ( - - user-avatar +
{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) => { - e.stopPropagation() - // navigate to user's profile - navigate(getProfileRoute(pubkey)) - } - - return ( - - User Image - - {name} - - - ) -} diff --git a/src/index.css b/src/index.css index b71a3cb..d46826b 100644 --- a/src/index.css +++ b/src/index.css @@ -1,55 +1,52 @@ :root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-text-size-adjust: 100%; + text-size-adjust: 100%; + + word-break: break-word; } -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; +* { + box-sizing: border-box; } -a:hover { - color: #535bf2; + +@font-face { + font-family: 'Roboto'; + font-weight: 300; + src: local('Roboto-Light'), url(./assets/roboto-font/Roboto-Light.ttf); +} + +@font-face { + font-family: 'Roboto'; + font-weight: 400; + src: local('Roboto-Regular'), url(./assets/Roboto-font/Roboto-Regular.ttf); +} + +@font-face { + font-family: 'Roboto'; + font-weight: 500; + src: local('Roboto-Medium'), url(./assets/Roboto-font/Roboto-Medium.ttf); +} + +@font-face { + font-family: 'Roboto'; + font-weight: 700; + src: local('Roboto-Bold'), url(./assets/Roboto-font/Roboto-Bold.ttf); } body { margin: 0; - /* display: flex; - place-items: center; */ min-width: 320px; min-height: 100vh; - background-color: #f4f4fb; overflow-wrap: break-word; } -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} - .qrzap { position: fixed; top: 80px; @@ -57,21 +54,10 @@ button { z-index: 100; } -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} - -.main { - padding: 60px 0; +#root { + min-height: 100vh; + display: flex; + flex-direction: column; } .hide-mobile { @@ -90,6 +76,7 @@ button { * when this class is assigned to a component, user will not be able to select and copy the content from that component */ .no-select { + -webkit-user-select: none; user-select: none; } diff --git a/src/layouts/Main.tsx b/src/layouts/Main.tsx index 60b052b..c8f9f27 100644 --- a/src/layouts/Main.tsx +++ b/src/layouts/Main.tsx @@ -1,5 +1,3 @@ -import { Box } from '@mui/material' -import Container from '@mui/material/Container' import { Event, kinds } from 'nostr-tools' import { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' @@ -28,6 +26,8 @@ import { } from '../utils' import { useAppSelector } from '../hooks' import { SubCloser } from 'nostr-tools/abstract-pool' +import styles from './style.module.scss' +import { Footer } from '../components/Footer/Footer' export const MainLayout = () => { const dispatch: Dispatch = useDispatch() @@ -136,7 +136,7 @@ export const MainLayout = () => { } setIsLoading(true) - setLoadingSpinnerDesc(`Fetching user's app data`) + setLoadingSpinnerDesc(`Loading SIGit History`) getUsersAppData() .then((appData) => { if (appData) { @@ -152,19 +152,10 @@ export const MainLayout = () => { return ( <> - - - - - - +
+ +
+