diff --git a/src/components/CreatedAt/CreatedAt.tsx b/src/components/CreatedAt/CreatedAt.tsx index c63e6b3c0..6195c5a7d 100644 --- a/src/components/CreatedAt/CreatedAt.tsx +++ b/src/components/CreatedAt/CreatedAt.tsx @@ -1,14 +1,15 @@ -import { getNowUtcTime, timeSince } from 'src/utils/utils'; +import { timeSince } from 'src/utils/utils'; +import { dateToUtcNumber, getNowUtcNumber } from 'src/utils/date'; import styles from './CreatedAt.module.scss'; export type Props = { - timeAt: string | number; + timeAt: string; }; function CreatedAt({ timeAt }: Props) { let timeAgoInMS = 0; - const time = getNowUtcTime() - new Date(timeAt).getTime(); + const time = getNowUtcNumber() - dateToUtcNumber(timeAt); if (time && time > 0) { timeAgoInMS = time; } @@ -16,9 +17,9 @@ function CreatedAt({ timeAt }: Props) { const timeSinceValue = timeSince(timeAgoInMS); return ( -
+ {timeSinceValue === 'now' ? timeSinceValue : `${timeSinceValue} ago`} -
+ ); } diff --git a/src/components/HydrogenBalance/HydrogenBalance.tsx b/src/components/HydrogenBalance/HydrogenBalance.tsx index 16c9a6e3e..f8c9cbcc1 100644 --- a/src/components/HydrogenBalance/HydrogenBalance.tsx +++ b/src/components/HydrogenBalance/HydrogenBalance.tsx @@ -1,13 +1,19 @@ import { useGetBalanceBostrom } from 'src/containers/sigma/hooks'; +import { Link } from 'react-router-dom'; +import { routes } from 'src/routes'; import IconsNumber from '../IconsNumber/IconsNumber'; -function HydrogenBalance({ address }) { +function HydrogenBalance({ address, className }) { const { totalAmountInLiquid } = useGetBalanceBostrom(address); return ( -
- -
+ + + ); } diff --git a/src/components/IconsNumber/IconsNumber.tsx b/src/components/IconsNumber/IconsNumber.tsx index e9abe63f4..a450256b2 100644 --- a/src/components/IconsNumber/IconsNumber.tsx +++ b/src/components/IconsNumber/IconsNumber.tsx @@ -1,7 +1,6 @@ import BigNumber from 'bignumber.js'; -import hydrogen from '../../image/hydrogen.svg'; -import { boot } from 'images/large-green.png'; import React from 'react'; +import hydrogen from '../../image/hydrogen.svg'; import Tooltip from '../tooltip/tooltip'; enum TypesEnum { @@ -63,7 +62,7 @@ const PREFIXES = [ }, ]; -export default function IconsNumber({ value, type }) { +export default function IconsNumber({ value, type, isVertical }) { const { prefix = 1, power = 1 } = PREFIXES.find((powerItem) => value >= powerItem.power) || {}; @@ -86,10 +85,14 @@ export default function IconsNumber({ value, type }) { {number}{' '} + {value?.toLocaleString()?.replaceAll(',', ' ')} + {icons[type]} + } > {i} diff --git a/src/components/MusicalAddress/MusicalAddress.module.scss b/src/components/MusicalAddress/MusicalAddress.module.scss index b7855da66..07527e17c 100644 --- a/src/components/MusicalAddress/MusicalAddress.module.scss +++ b/src/components/MusicalAddress/MusicalAddress.module.scss @@ -3,7 +3,7 @@ background: transparent; border: unset; // height: 20px; - font-size: 16px; + font-size: 14px; // padding: 100px; color: var(--primary-color); @@ -28,9 +28,9 @@ .containerSignatures { &ItemNote { - width: 7px; + width: 3px; border-radius: 2px; - max-height: 20px; + max-height: 15px; @media (max-width: 800px) { width: 5px; diff --git a/src/components/Tabs/TabItem/TabItem.tsx b/src/components/Tabs/TabItem/TabItem.tsx index 89241b7f8..2c85ad942 100644 --- a/src/components/Tabs/TabItem/TabItem.tsx +++ b/src/components/Tabs/TabItem/TabItem.tsx @@ -1,5 +1,6 @@ import cx from 'classnames'; import { Link } from 'react-router-dom'; +import { ReactNode } from 'react'; import styles from './TabItem.module.scss'; export const enum Position { @@ -9,7 +10,7 @@ export const enum Position { export type Props = { type?: Position; - text: string | JSX.Element; + text: string | ReactNode; step?: number; isSelected: boolean; to?: string; @@ -39,6 +40,13 @@ function TabItem({ }; } + if (disable && to) { + componentProps = { + ...componentProps, + onClick: (e) => e.preventDefault(), + }; + } + return ( void; key: string; - text?: string; + text?: ReactNode; }; type Props = { diff --git a/src/components/account/account.module.scss b/src/components/account/account.module.scss index 4e67923aa..be5a67013 100644 --- a/src/components/account/account.module.scss +++ b/src/components/account/account.module.scss @@ -15,4 +15,9 @@ color: var(--primary-color); padding: 0; white-space: nowrap; + + width: 100%; + overflow-x: hidden; + text-overflow: ellipsis; + text-align: center; } \ No newline at end of file diff --git a/src/components/actionBar/index.tsx b/src/components/actionBar/index.tsx index 9e6732196..4c700958a 100644 --- a/src/components/actionBar/index.tsx +++ b/src/components/actionBar/index.tsx @@ -7,12 +7,13 @@ import { Networks } from 'src/types/networks'; import usePassportByAddress from 'src/features/passport/hooks/usePassportByAddress'; import { selectCurrentAddress } from 'src/redux/features/pocket'; import { useAppSelector } from 'src/redux/hooks'; -import ButtonIcon from '../buttons/ButtonIcon'; -import styles from './styles.module.scss'; -import Button from '../btnGrd'; import { useSigningClient } from 'src/contexts/signerClient'; import { trimString } from 'src/utils/utils'; import { CHAIN_ID } from 'src/constants/config'; +import ButtonIcon from '../buttons/ButtonIcon'; +import styles from './styles.module.scss'; +import Button from '../btnGrd'; +import { createPortal } from 'react-dom'; const back = require('../../image/arrow-left-img.svg'); @@ -121,7 +122,7 @@ function ActionBar({ children, text, onClickBack, button }: Props) { const content = text || children; - return ( + const contentPortal = ( {/* */} @@ -150,6 +151,12 @@ function ActionBar({ children, text, onClickBack, button }: Props) { {/* */} ); + + // const portalEl = document.getElementById('portalActionBar'); + + // return portalEl ? createPortal(contentPortal, portalEl) : contentPortal; + + return contentPortal; } export default ActionBar; diff --git a/src/components/actionBar/styles.module.scss b/src/components/actionBar/styles.module.scss index 70a64fefe..1502c50f1 100644 --- a/src/components/actionBar/styles.module.scss +++ b/src/components/actionBar/styles.module.scss @@ -4,14 +4,12 @@ justify-content: center; width: 100%; position: fixed; - bottom: 0; + bottom: 20px; left: 0; padding: 10px 0; - background: linear-gradient( - 0deg, - rgba(0, 0, 0, 0.93) 76%, - rgba(0, 0, 0, 0) 100% - ); + background: linear-gradient(0deg, + rgba(0, 0, 0, 0.93) 76%, + rgba(0, 0, 0, 0) 100%); z-index: 2; &Content { @@ -49,4 +47,31 @@ .chooseAccount { padding: 0 5px; color: var(--primary-color); +} + +.ActionBarWrap { + display: flex; + align-items: center; + justify-content: center; + width: 62%; + position: fixed; + bottom: 20px; + left: 50%; + transform: translate(-50%, 10px); + // padding: 10px 0; + background: #000; + z-index: 3; + + &Content { + max-width: 1000px; + flex-grow: 1; + display: grid; + grid-template-columns: 0.7fr 1fr; + align-items: center; + justify-content: center; + position: relative; + padding: 10px 0px; + + } + } \ No newline at end of file diff --git a/src/components/appMenu/AppMenu.module.scss b/src/components/appMenu/AppMenu.module.scss deleted file mode 100644 index 324f29c3a..000000000 --- a/src/components/appMenu/AppMenu.module.scss +++ /dev/null @@ -1,49 +0,0 @@ -@import '../../style/mixins.scss'; - -.bookmarks { - display: flex; - flex-direction: column; - width: 100%; - margin-top: 20px; - flex-grow: 1; - overflow: auto; - position: relative; -} - -.bookmarks__item { - > div { - align-items: center; - flex-shrink: 0; - } - position: relative; - - color: #777777; - text-decoration: none; - font-size: 14px; - - &.active { - color: #fff; - - &::before { - content: ''; - left: 0; - height: 100%; - width: 3px; - background-color: #4ed6ae; - filter: blur(0.7px); - box-shadow: 3px 0px 20px 1.5px #4ed6ae; - position: absolute; - } - } - - &:hover { - color: #32d9ae; - } -} - -.external { - width: 20px; - height: 20px; - - @include withShareIcon; -} diff --git a/src/components/appMenu/AppMenu.tsx b/src/components/appMenu/AppMenu.tsx deleted file mode 100644 index 1ce97dccd..000000000 --- a/src/components/appMenu/AppMenu.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { useEffect, useState } from 'react'; -import { v4 as uuidv4 } from 'uuid'; -import { useLocation, NavLink, matchPath } from 'react-router-dom'; -import styles from './AppMenu.module.scss'; -import { Pane } from '@cybercongress/gravity'; -import cx from 'classnames'; -import { MenuItem, MenuItems } from 'src/containers/application/AppMenu'; - -interface Props { - item: MenuItem | MenuItem['subItems'][0]; - selected: boolean; - onClick: () => void; -} - -function Items({ item, selected, onClick }: Props) { - const isExternal = item.to.startsWith('http'); - - return ( - { - return cx(styles.bookmarks__item, { [styles.active]: selected }); - }} - onClick={onClick} - {...(isExternal && { target: '_blank', rel: 'noreferrer noopener' })} - > - -
- {item.icon && ( - img - )} - - {item.name} - - {isExternal && } - -
-
-
- ); -} - -const renderSubItems = ( - subItems: MenuItem['subItems'], - location, - onClickSubItem -) => { - return subItems.map((itemSub) => { - const { matchPathname } = itemSub; - return ( - onClickSubItem(itemSub.name)} - /> - ); - }); -}; - -// eslint-disable-next-line import/prefer-default-export -export function Bookmarks({ - items, - closeMenu, -}: { - items: MenuItems; - closeMenu: () => void; -}) { - const [selectedItem, setSelectedItem] = useState(''); - const [selectedItemSub, setSelectedItemSub] = useState(''); - const location = useLocation(); - - function onClickItem(itemKey: MenuItem['name']) { - setSelectedItem(itemKey); - setSelectedItemSub(''); - - const item = items.find((item) => item.name === itemKey); - - if (item && item.subItems.length === 0) { - closeMenu(); - } - } - - function onClickSubItem(itemKey: string) { - setSelectedItemSub(itemKey); - closeMenu(); - } - - useEffect(() => { - setSelectedItemSub(''); - }, [selectedItem]); - - return ( -
- {items.map((item) => { - const key = uuidv4(); - return ( -
- onClickItem(item.name)} - /> - {item.name === selectedItem && ( - - {renderSubItems(item.subItems, location, onClickSubItem)} - - )} -
- ); - })} -
- ); -} diff --git a/src/components/appMenu/CircularMenu.module.scss b/src/components/appMenu/CircularMenu.module.scss new file mode 100644 index 000000000..88bfe73e7 --- /dev/null +++ b/src/components/appMenu/CircularMenu.module.scss @@ -0,0 +1,93 @@ +@import '../../style/mixins.scss'; + +.circle { + position: fixed; + bottom: 0px; + z-index: -1; + width: 220px; + height: 220px; + display: inline-flex; + border-radius: 50%; +} + +.menu { + --diameter: 140px; + --i: 22deg; //elements angle + --delta: 0deg; //second menu layer angle + list-style-type: none; + padding: 0; + display: grid; + margin: auto; +} + +.menu li { + grid-area: 1/1; + transform: rotate(calc(var(--r) + var(--delta))) translateX(var(--diameter)) rotate(calc(-1 * (var(--r) + var(--delta)))); +} + +.menu li:nth-child(1) { + --r: calc(-5 * var(--i)); +} + +.menu li:nth-child(2) { + --r: calc(-4 * var(--i)); +} + +.menu li:nth-child(3) { + --r: calc(-3 * var(--i)); +} + +.menu li:nth-child(4) { + --r: calc(-2 * var(--i)); +} + +.menu li:nth-child(5) { + --r: calc(-1 * var(--i)); +} + +.menu li:nth-child(6) { + --r: calc(0 * var(--i)); +} + +.menu li:nth-child(7) { + --r: calc(1 * var(--i)); +} + +.menu_item { + box-shadow: 0px 0px 13px #ffffff54; + opacity: 1; + background-color: #101010; + border-radius: 50%; + width: 38px; + height: 38px; + justify-content: center; + align-items: center; + display: flex; +} + +.menu_item.active { + border: 1px solid #ffffff6b; + box-shadow: 0px 0px 8px #ffffff4d inset, 0px 0px 13px #ffffff4d; + width: 100%; + height: 100%; +} + + +.icon { + width: 30px; + height: 30px; + object-fit: fill; + border-radius: 50%; +} + +.external { + display: block; + position: absolute; + bottom: 0; + right: 0; + transform: translate(50%, 50%); + width: 20px; + height: 20px; + + @include withShareIcon; +} \ No newline at end of file diff --git a/src/components/appMenu/CircularMenu.tsx b/src/components/appMenu/CircularMenu.tsx new file mode 100644 index 000000000..0b6b4e861 --- /dev/null +++ b/src/components/appMenu/CircularMenu.tsx @@ -0,0 +1,93 @@ +import { useState, useEffect } from 'react'; +import itemsMenu from 'src/utils/appsMenu'; +import styles from './CircularMenu.module.scss'; +import { MenuItem } from 'src/types/menu'; +import { useLocation } from 'react-router-dom'; +import _ from 'lodash'; +import CircularMenuItem from './CircularMenuItem'; + +declare module 'react' { + interface CSSProperties { + '--diameter'?: string; + '--delta'?: string; + } +} + +function CircularMenu({ circleSize }) { + const [activeItem, setActiveItem] = useState(null); + const chunkSize = 7; + const linkChunks = _.chunk(itemsMenu(), chunkSize); + const location = useLocation(); + + const calculateDiameter = (index, circleSize) => { + const menuCircleDiameter = circleSize / 2 + 40 * (index + 1) - 10; + const nextLevelMenuAngle = index === 1 ? 11 : 0; + return { menuCircleDiameter, nextLevelMenuAngle }; + }; + + const isActiveItem = (item: MenuItem) => { + if (location.pathname === item.to) { + return true; + } + if ( + item.to === '/robot' && + (location.pathname.includes('@') || location.pathname.includes('neuron/')) + ) { + return true; + } + if (item.to === '/senate' && location.pathname.startsWith('/senate/')) { + return true; + } + return item.subItems?.some((subItem) => location.pathname === subItem.to); + }; + + useEffect(() => { + const activeMenuItem = itemsMenu().find((item) => isActiveItem(item)); + setActiveItem(activeMenuItem || null); + }, [location]); + + const handleItemClick = (item: MenuItem) => { + setActiveItem(item); + }; + + return ( +
+ {linkChunks.map((chunk, index) => { + const { menuCircleDiameter, nextLevelMenuAngle } = calculateDiameter( + index, + circleSize + ); + return ( +
+
+ {chunk.map((item, index) => { + const isSelected = activeItem?.name === item.name; + return ( +
  • + handleItemClick(item)} + selected={isSelected} + /> +
  • + ); + })} +
    +
    + ); + })} +
    + ); +} + +export default CircularMenu; diff --git a/src/components/appMenu/CircularMenuItem.tsx b/src/components/appMenu/CircularMenuItem.tsx new file mode 100644 index 000000000..b18e9e478 --- /dev/null +++ b/src/components/appMenu/CircularMenuItem.tsx @@ -0,0 +1,30 @@ +import { NavLink } from 'react-router-dom'; +import cx from 'classnames'; +import styles from './CircularMenu.module.scss'; +import { MenuItem } from 'src/types/menu'; + +interface Props { + item: MenuItem; + onClick: () => void; + selected: boolean; +} + +const CircularMenuItem = ({ item, onClick, selected }: Props) => { + const isExternal = item.to.startsWith('http'); + + return ( +
    + + img + {isExternal && } + +
    + ); +}; + +export default CircularMenuItem; diff --git a/src/components/appMenu/SubMenu.module.scss b/src/components/appMenu/SubMenu.module.scss new file mode 100644 index 000000000..13cbf1080 --- /dev/null +++ b/src/components/appMenu/SubMenu.module.scss @@ -0,0 +1,54 @@ +$icon-size: 30px; + +.subMenu { + position: absolute; + display: flex; + flex-direction: column; + gap: 5px; +} + +@mixin active-el() { + content: ''; + left: 0; + height: 100%; + width: 3px; + background-color: #4ed6ae; + filter: blur(0.7px); + box-shadow: 3px 0px 20px 1.5px #4ed6ae; + position: absolute; +} + +.navLinkItem { + display: grid; + grid-template-columns: 30px 1fr; + gap: 10px; + align-items: center; + padding-left: 20px; + font-size: 18px; + // >div { + // align-items: center; + // flex-shrink: 0; + // } + + position: relative; + + color: #777777; + + .icon { + width: $icon-size; + height: $icon-size; + object-fit: contain; + } + + .nameApp { + white-space: nowrap; + } + + &.active { + color: #fff; + + &::before { + @include active-el(); + } + } +} \ No newline at end of file diff --git a/src/components/appMenu/SubMenu.tsx b/src/components/appMenu/SubMenu.tsx new file mode 100644 index 000000000..5ccc218d4 --- /dev/null +++ b/src/components/appMenu/SubMenu.tsx @@ -0,0 +1,52 @@ +import { NavLink, useLocation } from 'react-router-dom'; +import { MenuItem } from 'src/types/menu'; +import { Pane } from '@cybercongress/gravity'; +import cx from 'classnames'; +import { useMemo } from 'react'; +import styles from './SubMenu.module.scss'; + +interface Props { + selectedApp: MenuItem; + closeMenu: () => void; +} + +function SubMenu({ selectedApp, closeMenu }: Props) { + const renderData = useMemo( + () => + selectedApp.subItems.length + ? [ + { + name: 'main', + to: selectedApp.to, + }, + ...selectedApp.subItems, + ] + : [], + [selectedApp] + ); + + return ( +
    + {renderData.map((item) => ( + + cx(styles.navLinkItem, { + [styles.active]: isActive, + }) + } + onClick={closeMenu} + > + {item.icon && ( + icon + )} + {item.name} + + ))} +
    + ); +} + +export default SubMenu; diff --git a/src/components/containerGradient/Display/Display.module.scss b/src/components/containerGradient/Display/Display.module.scss index ac628bb4a..11a60f623 100644 --- a/src/components/containerGradient/Display/Display.module.scss +++ b/src/components/containerGradient/Display/Display.module.scss @@ -30,6 +30,7 @@ content: ''; display: none; } + border-right: unset !important; } @@ -38,6 +39,7 @@ content: ''; display: none; } + border-left: unset !important; } @@ -67,4 +69,6 @@ padding-bottom: unset; } } -} + + @include blueScroll; +} \ No newline at end of file diff --git a/src/components/index.js b/src/components/index.js index 4368ea0b8..167f2cb7d 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -54,6 +54,7 @@ import ButtonSwap from './ButtonSwap'; import Slider from './Slider/Slider'; import CreatedAt from './CreatedAt/CreatedAt'; import Tabs from './Tabs/Tabs'; +import Time from './time/time'; import Row, { RowsContainer } from './Row/Row'; import Display from './containerGradient/Display/Display'; import DisplayTitle from './containerGradient/DisplayTitle/DisplayTitle'; @@ -110,6 +111,7 @@ export { Slider, CreatedAt, Tabs, + Time, Row, RowsContainer, Display, diff --git a/src/components/search/Spark/Spark.tsx b/src/components/search/Spark/Spark.tsx index a00ab0949..74e82ed3f 100644 --- a/src/components/search/Spark/Spark.tsx +++ b/src/components/search/Spark/Spark.tsx @@ -50,9 +50,11 @@ function Spark({ {/* TODO: refact. meta should be moved inside contentItem and exclude fetchParticle from that */} -
    - -
    + {!selfLinks && ( +
    + +
    + )} )} diff --git a/src/components/time/time.module.scss b/src/components/time/time.module.scss new file mode 100644 index 000000000..fc462b8c0 --- /dev/null +++ b/src/components/time/time.module.scss @@ -0,0 +1,9 @@ +.wrapper { + display: flex; + color: var(--blue-light); + gap: 5px; + + .prefix { + color: var(--grayscale-dark); + } +} \ No newline at end of file diff --git a/src/components/time/time.tsx b/src/components/time/time.tsx new file mode 100644 index 000000000..97c7687b7 --- /dev/null +++ b/src/components/time/time.tsx @@ -0,0 +1,17 @@ +import { formatNumber } from 'src/utils/utils'; +import { Link } from 'react-router-dom'; +import { convertTimestampToString } from 'src/utils/date'; +import styles from './time.module.scss'; + +function Time({ msTime, linkTo }: { msTime: number; linkTo: string }) { + const [valueTime, prefixTime] = convertTimestampToString(msTime).split(' '); + + return ( + + {formatNumber(valueTime)} + {prefixTime} + + ); +} + +export default Time; diff --git a/src/components/time/utils.ts b/src/components/time/utils.ts new file mode 100644 index 000000000..1ec3dc255 --- /dev/null +++ b/src/components/time/utils.ts @@ -0,0 +1,19 @@ +const unixTimestamp = (secondsTime: number) => { + const years = Math.floor(secondsTime / 31536000); + const months = Math.floor(secondsTime / 2592000); + const days = Math.floor(secondsTime / 86400); + const hours = Math.floor(((secondsTime % 31536000) % 86400) / 3600); + const minutes = Math.floor((((secondsTime % 31536000) % 86400) % 3600) / 60); + const seconds = Math.floor((((secondsTime % 31536000) % 86400) % 3600) % 60); + + return { + years, + months, + days, + hours, + minutes, + seconds, + }; +}; + +export default unixTimestamp; diff --git a/src/containers/application/App.tsx b/src/containers/application/App.tsx index 7d188dcbd..6df0fd0a1 100644 --- a/src/containers/application/App.tsx +++ b/src/containers/application/App.tsx @@ -2,27 +2,26 @@ import { useEffect } from 'react'; import { Link, Outlet, matchPath, useLocation } from 'react-router-dom'; import { AppDispatch } from 'src/redux/store'; -import { initPocket, selectCurrentAddress } from 'src/redux/features/pocket'; +import { initPocket } from 'src/redux/features/pocket'; import MainLayout from 'src/layouts/Main'; -import { useGetCommunity } from 'src/pages/robot/_refactor/account/hooks'; -import { setCommunity } from 'src/redux/features/currentAccount'; import { getPassport } from 'src/features/passport/passports.redux'; import { useQueryClient } from 'src/contexts/queryClient'; import { useAdviser } from 'src/features/adviser/context'; import { routes } from 'src/routes'; import { AdviserColors } from 'src/features/adviser/Adviser/Adviser'; import { useBackend } from 'src/contexts/backend/backend'; -import AdviserContainer from '../../features/adviser/AdviserContainer'; -import styles from './styles.scss'; import { useAppDispatch, useAppSelector } from 'src/redux/hooks'; import useSenseManager from 'src/features/sense/ui/useSenseManager'; // eslint-disable-next-line unused-imports/no-unused-imports, @typescript-eslint/no-unused-vars import { initCyblog } from 'src/utils/logging/bootstrap'; +import { setTimeHistoryRoute } from 'src/features/TimeHistory/redux/TimeHistory.redux'; import { PreviousPageProvider } from 'src/contexts/previousPage'; import { cybernetRoutes } from 'src/features/cybernet/ui/routes'; +import AdviserContainer from '../../features/adviser/AdviserContainer'; +import styles from './styles.scss'; export const PORTAL_ID = 'portal'; @@ -81,6 +80,10 @@ function App() { window.scrollTo(0, 0); }, [location.pathname]); + useEffect(() => { + dispatch(setTimeHistoryRoute(location.pathname)); + }, [location.pathname, dispatch]); + useEffect(() => { if (ipfsError && !location.pathname.includes('/drive')) { adviserContext.setAdviser( diff --git a/src/containers/application/AppSideBar.tsx b/src/containers/application/AppSideBar.tsx index dacff0de8..76f323214 100644 --- a/src/containers/application/AppSideBar.tsx +++ b/src/containers/application/AppSideBar.tsx @@ -2,7 +2,7 @@ import cx from 'classnames'; import styles from './styles.scss'; import useOnClickOutside from 'src/hooks/useOnClickOutside'; import { useRef } from 'react'; -import { menuButtonId } from './Header/SwitchNetwork/SwitchNetwork'; +import { menuButtonId } from './Header/CurrentApp/CurrentApp'; interface Props { children: React.ReactNode; diff --git a/src/containers/application/Header/Commander/Commander.module.scss b/src/containers/application/Header/Commander/Commander.module.scss index 615ed1cbd..46268448d 100644 --- a/src/containers/application/Header/Commander/Commander.module.scss +++ b/src/containers/application/Header/Commander/Commander.module.scss @@ -1,7 +1,9 @@ .wrapper { + width: 100%; + width: 62%; transform: translate(-50%, -80%); - // background: rgb(0 0 0 / 79%); + background: rgb(0 0 0 / 79%); margin-right: -50%; left: 50%; position: absolute; diff --git a/src/containers/application/Header/SwitchNetwork/SwitchNetwork.module.scss b/src/containers/application/Header/CurrentApp/CurrentApp.module.scss similarity index 86% rename from src/containers/application/Header/SwitchNetwork/SwitchNetwork.module.scss rename to src/containers/application/Header/CurrentApp/CurrentApp.module.scss index 3b8aa3d1f..34c89ddba 100644 --- a/src/containers/application/Header/SwitchNetwork/SwitchNetwork.module.scss +++ b/src/containers/application/Header/CurrentApp/CurrentApp.module.scss @@ -33,6 +33,9 @@ border: none; background: transparent; position: relative; + display: flex; + align-items: center; + justify-content: center; cursor: pointer; &::before { @@ -116,7 +119,7 @@ padding-left: 15px; width: 250px; padding-bottom: 15px; - opacity: 0; + // opacity: 0; transition: 0.2s; backdrop-filter: blur(7px); @@ -164,9 +167,8 @@ .tooltipContainer { position: absolute; - left: 0px !important; - // top: unset !important; - top: 90px !important; + left: 0px; + top: 130px; z-index: 3; } @@ -177,9 +179,30 @@ transition: 0.2s; } +.buttonWrapper { + display: grid; + gap: 25px; + grid-template-columns: 100px 1fr; + align-items: center; + height: 100px; + + @media (width < 768px) { + grid-template-columns: 100px; + } + +} + .buttonWrapper { @media (max-width: 480px) { height: unset !important; grid-template-columns: unset !important; } } + +.containerSubItems { + display: flex; + flex-direction: column; + background: #0000008c; + width: 250px; + backdrop-filter: blur(7px); +} \ No newline at end of file diff --git a/src/containers/application/Header/CurrentApp/CurrentApp.tsx b/src/containers/application/Header/CurrentApp/CurrentApp.tsx new file mode 100644 index 000000000..9c62b37bc --- /dev/null +++ b/src/containers/application/Header/CurrentApp/CurrentApp.tsx @@ -0,0 +1,56 @@ +import { useMemo } from 'react'; +import cx from 'classnames'; +import { Link, useLocation } from 'react-router-dom'; +import { routes } from 'src/routes'; +import { CHAIN_ID } from 'src/constants/config'; +import { useAppSelector } from 'src/redux/hooks'; +import usePassportByAddress from 'src/features/passport/hooks/usePassportByAddress'; +import { selectCurrentAddress } from 'src/redux/features/pocket'; +import SubMenu from 'src/components/appMenu/SubMenu'; +import useMediaQuery from '../../../../hooks/useMediaQuery'; +import styles from './CurrentApp.module.scss'; +import { selectNetworkImg } from '../../../../utils/utils'; +import ChainInfo from './ui/ChainInfo/ChainInfo'; +import findSelectAppByUrl from './utils/findSelectAppByUrl'; + +export const menuButtonId = 'menu-button'; + +function CurrentApp({ onClickOpenMenu, openMenu }) { + const mediaQuery = useMediaQuery('(min-width: 768px)'); + const location = useLocation(); + const address = useAppSelector(selectCurrentAddress); + const { passport } = usePassportByAddress(address); + + const getRoute = useMemo(() => { + const { pathname } = location; + + return findSelectAppByUrl(pathname, passport, address); + }, [location, address, passport]); + + return ( + <> +
    + + cyb + + {mediaQuery && } +
    + + {getRoute && getRoute[0] && ( +
    + +
    + )} + + ); +} + +export default CurrentApp; diff --git a/src/containers/application/Header/CurrentApp/ui/AppName/AppName.module.scss b/src/containers/application/Header/CurrentApp/ui/AppName/AppName.module.scss new file mode 100644 index 000000000..4f40e057e --- /dev/null +++ b/src/containers/application/Header/CurrentApp/ui/AppName/AppName.module.scss @@ -0,0 +1,6 @@ +.wrapper { + width: 100%; + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} \ No newline at end of file diff --git a/src/containers/application/Header/CurrentApp/ui/AppName/AppName.tsx b/src/containers/application/Header/CurrentApp/ui/AppName/AppName.tsx new file mode 100644 index 000000000..cee84b7bc --- /dev/null +++ b/src/containers/application/Header/CurrentApp/ui/AppName/AppName.tsx @@ -0,0 +1,37 @@ +import { useLocation } from 'react-router-dom'; +import { CHAIN_ID } from 'src/constants/config'; +import { PATTERN_CYBER } from 'src/constants/patterns'; +import { routes } from 'src/routes'; +import itemsMenu from 'src/utils/appsMenu'; +import findApp from 'src/utils/findApp'; +import { Helmet } from 'react-helmet'; +import styles from './AppName.module.scss'; + +function AppName() { + let { pathname } = useLocation(); + const isRobot = pathname.includes('@') || pathname.includes('neuron/'); + + if (isRobot) { + const pathnameArr = pathname.replace(/^\/|\/$/g, '').split('/'); + const findItem = pathnameArr[pathnameArr.length - 1]; + pathname = + findItem.includes('@') || findItem.match(PATTERN_CYBER) + ? routes.robot.path + : findItem; + } + + const value = findApp(itemsMenu(), pathname); + + const content = value[0]?.name || CHAIN_ID; + + return ( + <> + + {content || ''} + + {content} + + ); +} + +export default AppName; diff --git a/src/containers/application/Header/CurrentApp/ui/ChainInfo/ChainInfo.module.scss b/src/containers/application/Header/CurrentApp/ui/ChainInfo/ChainInfo.module.scss new file mode 100644 index 000000000..948d943cd --- /dev/null +++ b/src/containers/application/Header/CurrentApp/ui/ChainInfo/ChainInfo.module.scss @@ -0,0 +1,22 @@ +.containerBandwidthBar { + width: 100%; +} + +.btnContainerText { + border: none; + font-size: 16px; + background: transparent; + color: rgb(31, 203, 255); + cursor: pointer; +} + +.containerInfoSwitch { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + width: 75px; + gap: 10px; + color: var(--blue-light); +} \ No newline at end of file diff --git a/src/containers/application/Header/CurrentApp/ui/ChainInfo/ChainInfo.tsx b/src/containers/application/Header/CurrentApp/ui/ChainInfo/ChainInfo.tsx new file mode 100644 index 000000000..ee31153ea --- /dev/null +++ b/src/containers/application/Header/CurrentApp/ui/ChainInfo/ChainInfo.tsx @@ -0,0 +1,16 @@ +import { BandwidthBar } from 'src/components'; +import styles from './ChainInfo.module.scss'; +import AppName from '../AppName/AppName'; + +function ChainInfo() { + return ( +
    + +
    + +
    +
    + ); +} + +export default ChainInfo; diff --git a/src/containers/application/Header/CurrentApp/ui/IconMenu/IconMenu.module.scss b/src/containers/application/Header/CurrentApp/ui/IconMenu/IconMenu.module.scss new file mode 100644 index 000000000..76e3997cb --- /dev/null +++ b/src/containers/application/Header/CurrentApp/ui/IconMenu/IconMenu.module.scss @@ -0,0 +1,30 @@ + +.networkBtnImgIconMenu { + position: absolute; + top: 50%; + left: 50%; + transition: 0.2s; + transform: translate(-50%, -50%) rotate(-90deg); + + &Close { + transform: translate(-50%, -50%) rotate(0deg); + } + + div { + width: 35px; + height: 5px; + background-color: black; + margin: 6px 0; + } +} + + +@media (max-width: 768px) { + .networkBtnImgIconMenu { + div { + width: 25px; + height: 3px; + margin: 4px 0; + } + } +} diff --git a/src/containers/application/Header/CurrentApp/ui/IconMenu/IconMenu.tsx b/src/containers/application/Header/CurrentApp/ui/IconMenu/IconMenu.tsx new file mode 100644 index 000000000..5e12da01f --- /dev/null +++ b/src/containers/application/Header/CurrentApp/ui/IconMenu/IconMenu.tsx @@ -0,0 +1,18 @@ +import cx from 'classnames'; +import styles from './IconMenu.module.scss'; + +function IconMenu({ openMenu }: { openMenu: boolean }) { + return ( +
    +
    +
    +
    +
    + ); +} + +export default IconMenu; diff --git a/src/containers/application/Header/CurrentApp/utils/findSelectAppByUrl.ts b/src/containers/application/Header/CurrentApp/utils/findSelectAppByUrl.ts new file mode 100644 index 000000000..227e2da1d --- /dev/null +++ b/src/containers/application/Header/CurrentApp/utils/findSelectAppByUrl.ts @@ -0,0 +1,26 @@ +import { Nullable, Option } from 'src/types'; +import { Citizenship } from 'src/types/citizenship'; +import { routes } from 'src/routes'; +import findApp from 'src/utils/findApp'; +import reduceRobotSubItems from './reduceRobotSubItems'; + +const findSelectAppByUrl = ( + url: string, + passport: Nullable, + address: Option +) => { + let pathname = url; + const isRobot = url.includes('@') || url.includes('neuron/'); + + const itemsMenuObj = reduceRobotSubItems(passport, address); + + if (isRobot) { + pathname = routes.robot.path; + } + + const value = findApp(itemsMenuObj, pathname); + + return value; +}; + +export default findSelectAppByUrl; diff --git a/src/containers/application/Header/CurrentApp/utils/reduceRobotSubItems.ts b/src/containers/application/Header/CurrentApp/utils/reduceRobotSubItems.ts new file mode 100644 index 000000000..3a9e4eee6 --- /dev/null +++ b/src/containers/application/Header/CurrentApp/utils/reduceRobotSubItems.ts @@ -0,0 +1,36 @@ +import { CHAIN_ID } from 'src/constants/config'; +import { routes } from 'src/routes'; +import { Nullable, Option } from 'src/types'; +import { Citizenship } from 'src/types/citizenship'; +import { MenuItem, MenuItems } from 'src/types/menu'; +import { Networks } from 'src/types/networks'; +import itemsMenu from 'src/utils/appsMenu'; + +const reduceRobotSubItems = ( + passport: Nullable, + address: Option +) => { + const passportChain = CHAIN_ID === Networks.BOSTROM && passport; + + let linkApp: string; + if (passportChain) { + linkApp = routes.robotPassport.getLink(passport.extension.nickname); + } else if (address) { + linkApp = routes.neuron.getLink(address); + } + + return itemsMenu().reduce((acc: MenuItems, item: MenuItem) => { + if (item.to === routes.robot.path) { + item.subItems = !linkApp + ? [] + : item.subItems.map((item) => ({ + ...item, + to: `${linkApp}/${item.to}`, + })); + } + + return [...acc, { ...item }]; + }, []); +}; + +export default reduceRobotSubItems; diff --git a/src/containers/application/Header/Header.tsx b/src/containers/application/Header/Header.tsx index fb9356383..c353dd733 100644 --- a/src/containers/application/Header/Header.tsx +++ b/src/containers/application/Header/Header.tsx @@ -1,11 +1,10 @@ -import SwitchNetwork from './SwitchNetwork/SwitchNetwork'; +import { useEffect, useState } from 'react'; +import cx from 'classnames'; +import CurrentApp from './CurrentApp/CurrentApp'; import Electricity from '../../home/electricity'; import SwitchAccount from './SwitchAccount/SwitchAccount'; import Commander from './Commander/Commander'; import styles from './Header.module.scss'; -import { useEffect, useState } from 'react'; -import cx from 'classnames'; -import AdviserContainer from 'src/features/adviser/AdviserContainer'; type Props = { menuProps: { @@ -42,7 +41,7 @@ function Header({ menuProps }: Props) { [styles.scroll]: scroll, })} > - diff --git a/src/containers/application/Header/SwitchAccount/SwitchAccount.tsx b/src/containers/application/Header/SwitchAccount/SwitchAccount.tsx index cc2e3101a..ab222df6a 100644 --- a/src/containers/application/Header/SwitchAccount/SwitchAccount.tsx +++ b/src/containers/application/Header/SwitchAccount/SwitchAccount.tsx @@ -9,17 +9,17 @@ import useOnClickOutside from 'src/hooks/useOnClickOutside'; import { routes } from 'src/routes'; import Pill from 'src/components/Pill/Pill'; -import { useBackend } from 'src/contexts/backend/backend'; import { useSigningClient } from 'src/contexts/signerClient'; import useIsOnline from 'src/hooks/useIsOnline'; import { useAppSelector } from 'src/redux/hooks'; import BroadcastChannelSender from 'src/services/backend/channels/BroadcastChannelSender'; +import { useBackend } from 'src/contexts/backend/backend'; +import { AvataImgIpfs } from '../../../portal/components/avataIpfs'; +import styles from './SwitchAccount.module.scss'; +import networkStyles from '../CurrentApp/CurrentApp.module.scss'; import useMediaQuery from '../../../../hooks/useMediaQuery'; import robot from '../../../../image/temple/robot.png'; -import { AvataImgIpfs } from '../../../portal/components/avataIpfs'; import Karma from '../../Karma/Karma'; -import networkStyles from '../SwitchNetwork/SwitchNetwork.module.scss'; -import styles from './SwitchAccount.module.scss'; // should be refactored function AccountItem({ diff --git a/src/containers/application/Header/SwitchNetwork/SwitchNetwork.tsx b/src/containers/application/Header/SwitchNetwork/SwitchNetwork.tsx deleted file mode 100644 index 5e0640dc3..000000000 --- a/src/containers/application/Header/SwitchNetwork/SwitchNetwork.tsx +++ /dev/null @@ -1,208 +0,0 @@ -import React from 'react'; -import { usePopperTooltip } from 'react-popper-tooltip'; -import { Transition } from 'react-transition-group'; -import cx from 'classnames'; -import { useNetworks } from 'src/contexts/networks'; -import { fromBech32, selectNetworkImg } from '../../../../utils/utils'; -import { BandwidthBar } from '../../../../components'; -import styles from './SwitchNetwork.module.scss'; -import useMediaQuery from '../../../../hooks/useMediaQuery'; -import { - matchPath, - useLocation, - useNavigate, - useParams, -} from 'react-router-dom'; -import { useDispatch } from 'react-redux'; -import { initPocket } from 'src/redux/features/pocket'; -import { Networks } from 'src/types/networks'; -import { routes } from 'src/routes'; -import { CHAIN_ID } from 'src/constants/config'; - -export const menuButtonId = 'menu-button'; - -const forEachObjbech32 = (data, prefix) => { - const newObj = {}; - Object.keys(data).forEach((key) => { - const valueObj = data[key]; - if (Object.prototype.hasOwnProperty.call(valueObj, 'cyber')) { - const { bech32 } = valueObj.cyber; - const bech32NewPrefix = fromBech32(bech32, prefix); - newObj[key] = { - ...valueObj, - cyber: { - ...valueObj.cyber, - bech32: bech32NewPrefix, - }, - }; - } - }); - return newObj; -}; - -const updateAddress = (prefix: any) => { - const localStoragePocketAccount = localStorage.getItem('pocketAccount'); - const localStoragePocket = localStorage.getItem('pocket'); - - if (localStoragePocket !== null) { - const localStoragePocketData = JSON.parse(localStoragePocket); - const newObjPocketData = forEachObjbech32(localStoragePocketData, prefix); - localStorage.setItem('pocket', JSON.stringify(newObjPocketData)); - } - if (localStoragePocketAccount !== null) { - const localStoragePocketAccountData = JSON.parse(localStoragePocketAccount); - const newObjAccountData = forEachObjbech32( - localStoragePocketAccountData, - prefix - ); - localStorage.setItem('pocketAccount', JSON.stringify(newObjAccountData)); - } -}; - -function SwitchNetwork({ onClickOpenMenu, openMenu }) { - const mediaQuery = useMediaQuery('(min-width: 768px)'); - - const location = useLocation(); - // const navigate = useNavigate(); - const params = useParams(); - // const dispatch = useDispatch(); - - const [controlledVisible, setControlledVisible] = React.useState(false); - const { networks } = useNetworks(); - const { getTooltipProps, setTooltipRef, visible } = usePopperTooltip({ - trigger: 'click', - closeOnOutsideClick: false, - visible: controlledVisible, - onVisibleChange: setControlledVisible, - placement: 'bottom', - }); - - const onClickChain = async (chainId: Networks, prefix: any) => { - localStorage.setItem('chainId', chainId); - updateAddress(prefix); - - // dispatch(initPocket()); - - let redirectHref = location.pathname; - if (matchPath(routes.neuron.path, location.pathname)) { - const newAddress = fromBech32(params.address, prefix); - - redirectHref = routes.neuron.getLink(newAddress); - } else if (location.pathname.includes('@')) { - redirectHref = routes.robot.path; - } - - // TODO: remove reload page (need fix config) - window.location.pathname = redirectHref; - }; - - const renderItemChain = - networks && - Object.keys(networks) - .filter((itemKey) => itemKey !== CHAIN_ID) - .map((key) => ( - - )); - - return ( - <> -
    - - {mediaQuery && ( -
    - -
    - -
    -
    - )} -
    - - {/* {renderItemChain && Object.keys(renderItemChain).length > 0 && ( - - {(state) => { - return ( -
    -
    - {renderItemChain} -
    -
    - ); - }} -
    - )} */} - - ); -} - -export default SwitchNetwork; diff --git a/src/containers/application/Karma/Karma.module.scss b/src/containers/application/Karma/Karma.module.scss index eae5b50eb..94bf99bdc 100644 --- a/src/containers/application/Karma/Karma.module.scss +++ b/src/containers/application/Karma/Karma.module.scss @@ -1,4 +1,5 @@ .containerKarma { + display: flex; gap: 6px; color: #fff; font-size: 16px; diff --git a/src/containers/blok/index.tsx b/src/containers/blok/index.tsx index 6f71c014c..d17da0183 100644 --- a/src/containers/blok/index.tsx +++ b/src/containers/blok/index.tsx @@ -81,7 +81,17 @@ function Block() { }, [data]); if (loading) { - return ; + return ( +
    + +
    + ); } if (error) { diff --git a/src/containers/energy/index.tsx b/src/containers/energy/index.tsx index a12fb3ae5..64d75045f 100644 --- a/src/containers/energy/index.tsx +++ b/src/containers/energy/index.tsx @@ -92,29 +92,22 @@ function RoutedEnergy() { return ( <> -
    - - - - - {content} -
    + + + + {content} +
    ...
    ; + return ( +
    + +
    + ); } if (stagePortal === STAGE_INIT) { diff --git a/src/containers/sigma/hooks/useBalanceToken.js b/src/containers/sigma/hooks/useBalanceToken.js index b81a80bb8..5a59c99e5 100644 --- a/src/containers/sigma/hooks/useBalanceToken.js +++ b/src/containers/sigma/hooks/useBalanceToken.js @@ -61,7 +61,7 @@ function useBalanceToken(address, updateAddress) { const [balanceToken, setBalanceToken] = useState(initValueToken); useEffect(() => { - if (address !== null) { + if (address) { if (address.bech32) { setAddressActive(address.bech32); } else { diff --git a/src/containers/taverna/Taverna.module.scss b/src/containers/taverna/Taverna.module.scss new file mode 100644 index 000000000..1c318bc64 --- /dev/null +++ b/src/containers/taverna/Taverna.module.scss @@ -0,0 +1,11 @@ +.infiniteScroll { + margin-top: 12px; + + display: flex; + flex-direction: column; + align-items: center; + + >* { + width: 100%; + } +} \ No newline at end of file diff --git a/src/containers/taverna/index.tsx b/src/containers/taverna/index.tsx index 87b622e6d..44de233b1 100644 --- a/src/containers/taverna/index.tsx +++ b/src/containers/taverna/index.tsx @@ -8,12 +8,16 @@ import { Dots, SearchSnippet, ContainerGradientText, + Display, + SearchItem, } from '../../components'; import useGetTweets from './useGetTweets'; import ActionBarCont from '../market/actionBarContainer'; import useSetActiveAddress from '../../hooks/useSetActiveAddress'; import { CID_TWEET } from 'src/constants/app'; import { useAdviser } from 'src/features/adviser/context'; +import Spark from 'src/components/search/Spark/Spark'; +import styles from './Taverna.module.scss'; const LOAD_COUNT = 10; @@ -41,15 +45,13 @@ function Taverna() { setRankLink(null); }, [update]); - async function onClickRank(key: string) { + const onClickRank = async (key: string) => { if (rankLink === key) { setRankLink(null); } else { setRankLink(key); } - } - - // const d = new Date(); + }; const displayedPalettes = useMemo( () => @@ -61,56 +63,15 @@ function Taverna() { }) .slice(0, itemsToShow) .map((key) => { - // let timeAgoInMS = 0; - // const time = Date.parse(d) - Date.parse(tweets[key].time); - // if (time > 0) { - // timeAgoInMS = time; - // } return ( - - // - // {!mobile && ( - // - // onClickRank(key)} - // /> - // - // )} - // - // - // {timeSince(timeAgoInMS)} ago - // - // ); }), [itemsToShow, tweets] @@ -120,41 +81,28 @@ function Taverna() { return ; } - function loadMore() { + const loadMore = () => { setItemsToShow((i) => i + LOAD_COUNT); - } + }; return ( <> - - {/*
    */} - {/* */} - -
    - all loaded

    } - hasMore={Object.keys(tweets).length > itemsToShow} - loader={} - > - {Object.keys(tweets).length > 0 ? ( - displayedPalettes - ) : ( - - )} -
    -
    - - {/*
    */} - {/*
    */} -
    + + all loaded

    } + hasMore={Object.keys(tweets).length > itemsToShow} + loader={} + className={styles.infiniteScroll} + > + {Object.keys(tweets).length > 0 ? ( + displayedPalettes + ) : ( + + )} +
    +
    state.pocket); + const useGetAddress = defaultAccount?.account?.cyber?.bech32 || null; + const { passport } = usePassportByAddress(useGetAddress); + const useGetName = passport?.extension.nickname; + const [timeSeconds, setTimeSeconds] = useState(0); + + const linkAddress = useGetName + ? routes.robotPassport.getLink(useGetName) + : useGetAddress + ? routes.neuron.getLink(useGetAddress) + : undefined; + + const linkTime = linkAddress ? `${linkAddress}/time` : routes.robot.path; + + useEffect(() => { + const getTime = () => { + const utcTime = getNowUtcTime(); + setTimeSeconds(utcTime); + }; + getTime(); + + const timeInterval = setInterval(() => { + getTime(); + }, 60000); + + return () => { + clearInterval(timeInterval); + }; + }, []); + + return