From 6273360319556522687893777cee04d96dc97104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Wed, 20 Nov 2024 15:54:39 +0300 Subject: [PATCH 01/17] feat: graph-mindset --- src/components/AppContainer/index.tsx | 15 ++++++++------- src/modules/components/header/index.tsx | 21 +++++++++++++++++++++ src/modules/index.tsx | 8 ++++++++ 3 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 src/modules/components/header/index.tsx create mode 100644 src/modules/index.tsx diff --git a/src/components/AppContainer/index.tsx b/src/components/AppContainer/index.tsx index a9678ab2d..54e46da98 100644 --- a/src/components/AppContainer/index.tsx +++ b/src/components/AppContainer/index.tsx @@ -5,20 +5,21 @@ import { AppProviders } from '../App/Providers' import { AuthGuard } from '../Auth' const LazyApp = lazy(() => import('../App').then(({ App }) => ({ default: App }))) +const LazyMindSet = lazy(() => import('../../modules').then(({ MindSet }) => ({ default: MindSet }))) export const AppContainer = () => { const App = + const MindSet = return ( Loading...}> - - - - - - - + + + {App}} path="/" /> + {App}} path="/search" /> + {App}} path="*" /> + diff --git a/src/modules/components/header/index.tsx b/src/modules/components/header/index.tsx new file mode 100644 index 000000000..eb2b5a733 --- /dev/null +++ b/src/modules/components/header/index.tsx @@ -0,0 +1,21 @@ +import styled from 'styled-components' +import { Flex } from '~/components/common/Flex' +import { Text } from '~/components/common/Text' + +export const Header = () => ( + + + Graph Mindset + + +) + +const Head = styled(Flex).attrs({ + align: 'center', + direction: 'row', + grow: 1, + justify: 'flex-start', +})` + height: 64px; + padding: 20px 23px; +` diff --git a/src/modules/index.tsx b/src/modules/index.tsx new file mode 100644 index 000000000..25e8ac196 --- /dev/null +++ b/src/modules/index.tsx @@ -0,0 +1,8 @@ +import { Flex } from '~/components/common/Flex' +import { Header } from './components/header' + +export const MindSet = () => ( + +
+ +) From b6441cfc09a31199a5dfc1edb7f91dbf222352ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Wed, 20 Nov 2024 18:24:56 +0300 Subject: [PATCH 02/17] feat: updated stcurture --- src/components/AppContainer/index.tsx | 2 +- src/modules/{ => mindset}/components/header/index.tsx | 0 src/modules/{ => mindset}/index.tsx | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename src/modules/{ => mindset}/components/header/index.tsx (100%) rename src/modules/{ => mindset}/index.tsx (100%) diff --git a/src/components/AppContainer/index.tsx b/src/components/AppContainer/index.tsx index 54e46da98..deba4d948 100644 --- a/src/components/AppContainer/index.tsx +++ b/src/components/AppContainer/index.tsx @@ -5,7 +5,7 @@ import { AppProviders } from '../App/Providers' import { AuthGuard } from '../Auth' const LazyApp = lazy(() => import('../App').then(({ App }) => ({ default: App }))) -const LazyMindSet = lazy(() => import('../../modules').then(({ MindSet }) => ({ default: MindSet }))) +const LazyMindSet = lazy(() => import('../../modules/mindset').then(({ MindSet }) => ({ default: MindSet }))) export const AppContainer = () => { const App = diff --git a/src/modules/components/header/index.tsx b/src/modules/mindset/components/header/index.tsx similarity index 100% rename from src/modules/components/header/index.tsx rename to src/modules/mindset/components/header/index.tsx diff --git a/src/modules/index.tsx b/src/modules/mindset/index.tsx similarity index 100% rename from src/modules/index.tsx rename to src/modules/mindset/index.tsx From 30b5ae143d57ef4bff9e449d7c4b23e762374a26 Mon Sep 17 00:00:00 2001 From: Shoaibdev7 Date: Fri, 22 Nov 2024 00:06:42 +0500 Subject: [PATCH 03/17] fix(logo): render Icon and Title --- public/svg-icons/Logo.svg | 12 ++++ src/components/Icons/Logo.tsx | 55 ++++++++++++++ .../mindset/components/header/index.tsx | 72 +++++++++++++++++-- 3 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 public/svg-icons/Logo.svg create mode 100644 src/components/Icons/Logo.tsx diff --git a/public/svg-icons/Logo.svg b/public/svg-icons/Logo.svg new file mode 100644 index 000000000..342459b7f --- /dev/null +++ b/public/svg-icons/Logo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/components/Icons/Logo.tsx b/src/components/Icons/Logo.tsx new file mode 100644 index 000000000..2cbae33ad --- /dev/null +++ b/src/components/Icons/Logo.tsx @@ -0,0 +1,55 @@ +/* eslint-disable */ +import React from 'react'; + +const Logo: React.FC> = (props) => ( + + + + + + + + + + + + +); + +export default Logo; diff --git a/src/modules/mindset/components/header/index.tsx b/src/modules/mindset/components/header/index.tsx index eb2b5a733..db7e13a93 100644 --- a/src/modules/mindset/components/header/index.tsx +++ b/src/modules/mindset/components/header/index.tsx @@ -1,14 +1,39 @@ +import { useEffect, useState } from 'react' import styled from 'styled-components' import { Flex } from '~/components/common/Flex' import { Text } from '~/components/common/Text' +import Logo from '~/components/Icons/Logo' +import { getAboutData } from '~/network/fetchSourcesData' +import { colors } from '~/utils/colors' -export const Header = () => ( - - - Graph Mindset - - -) +export const Header = () => { + const [title, setTitle] = useState('') + + useEffect(() => { + const fetchTitle = async () => { + try { + const response = await getAboutData() + + setTitle(response.title || 'Graph Mindset') + } catch (error) { + console.error('Failed to fetch title:', error) + } + } + + fetchTitle() + }, []) + + return ( + + + + + + + {title} + + ) +} const Head = styled(Flex).attrs({ align: 'center', @@ -18,4 +43,37 @@ const Head = styled(Flex).attrs({ })` height: 64px; padding: 20px 23px; + gap: 0px; +` + +const LogoButton = styled(Flex)` + align-items: center; + justify-content: center; + cursor: pointer; +` + +const IconWrapper = styled.div` + display: flex; + align-items: center; + justify-content: center; + + svg { + width: 30px; + height: 27px; + color: ${colors.white}; + } +` + +const StyledText = styled(Text)` + width: 127px; + height: 24px; + color: ${colors.white}; + font-family: Barlow; + font-size: 22px; + font-style: normal; + font-weight: 700; + line-height: 24px; + letter-spacing: 0.22px; + margin-left: 16px; + white-space: nowrap; ` From 64c7b02813c8c7b7fb046745b398c0b63221673e Mon Sep 17 00:00:00 2001 From: Shoaibdev7 Date: Fri, 22 Nov 2024 01:17:33 +0500 Subject: [PATCH 04/17] fix(logo): render Icon and Title --- public/svg-icons/Logo.svg | 12 ----- .../mindset/components/header/Icon}/Logo.tsx | 16 +++---- .../mindset/components/header/index.tsx | 44 +++++-------------- 3 files changed, 17 insertions(+), 55 deletions(-) delete mode 100644 public/svg-icons/Logo.svg rename src/{components/Icons => modules/mindset/components/header/Icon}/Logo.tsx (91%) diff --git a/public/svg-icons/Logo.svg b/public/svg-icons/Logo.svg deleted file mode 100644 index 342459b7f..000000000 --- a/public/svg-icons/Logo.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/src/components/Icons/Logo.tsx b/src/modules/mindset/components/header/Icon/Logo.tsx similarity index 91% rename from src/components/Icons/Logo.tsx rename to src/modules/mindset/components/header/Icon/Logo.tsx index 2cbae33ad..70b13f9be 100644 --- a/src/components/Icons/Logo.tsx +++ b/src/modules/mindset/components/header/Icon/Logo.tsx @@ -1,14 +1,8 @@ /* eslint-disable */ -import React from 'react'; +import React from 'react' -const Logo: React.FC> = (props) => ( - +export const Logo: React.FC> = (props) => ( + > = (props) => ( fill="currentColor" /> -); +) -export default Logo; +export default Logo diff --git a/src/modules/mindset/components/header/index.tsx b/src/modules/mindset/components/header/index.tsx index db7e13a93..5feb22db4 100644 --- a/src/modules/mindset/components/header/index.tsx +++ b/src/modules/mindset/components/header/index.tsx @@ -1,39 +1,19 @@ -import { useEffect, useState } from 'react' import styled from 'styled-components' import { Flex } from '~/components/common/Flex' import { Text } from '~/components/common/Text' -import Logo from '~/components/Icons/Logo' -import { getAboutData } from '~/network/fetchSourcesData' import { colors } from '~/utils/colors' - -export const Header = () => { - const [title, setTitle] = useState('') - - useEffect(() => { - const fetchTitle = async () => { - try { - const response = await getAboutData() - - setTitle(response.title || 'Graph Mindset') - } catch (error) { - console.error('Failed to fetch title:', error) - } - } - - fetchTitle() - }, []) - - return ( - - - - - - - {title} - - ) -} +import { Logo } from './Icon/Logo' + +export const Header = () => ( + + + + + + + Graph Mindset + +) const Head = styled(Flex).attrs({ align: 'center', From b14a4e3d8930de6f88a61234f945ea0933ee5ccd Mon Sep 17 00:00:00 2001 From: MahtabBukhari Date: Fri, 22 Nov 2024 10:07:38 +0500 Subject: [PATCH 05/17] fix(episode): render Episode on the left hand side panel --- .../components/MediaPlayer/ToolBar/index.tsx | 250 +++++++++++++++ .../mindset/components/MediaPlayer/index.tsx | 295 ++++++++++++++++++ .../mindset/components/sidebar/index.tsx | 56 ++++ src/modules/mindset/index.tsx | 2 + 4 files changed, 603 insertions(+) create mode 100644 src/modules/mindset/components/MediaPlayer/ToolBar/index.tsx create mode 100644 src/modules/mindset/components/MediaPlayer/index.tsx create mode 100644 src/modules/mindset/components/sidebar/index.tsx diff --git a/src/modules/mindset/components/MediaPlayer/ToolBar/index.tsx b/src/modules/mindset/components/MediaPlayer/ToolBar/index.tsx new file mode 100644 index 000000000..1ca5f3319 --- /dev/null +++ b/src/modules/mindset/components/MediaPlayer/ToolBar/index.tsx @@ -0,0 +1,250 @@ +import { IconButton, Slider } from '@mui/material' +import { FC, useState } from 'react' +import styled from 'styled-components' +import ExitFullScreen from '~/components/Icons/ExitFullScreen' +import FullScreenIcon from '~/components/Icons/FullScreenIcon' +import PauseIcon from '~/components/Icons/PauseIcon' +import PlayIcon from '~/components/Icons/PlayIcon' +import VolumeIcon from '~/components/Icons/VolumeIcon' +import MuteVolumeIcon from '~/components/Icons/MuteVolumeIcon' +import { Flex } from '~/components/common/Flex' +import { colors } from '~/utils' +import { secondsToMediaTime } from '~/utils/secondsToMediaTime' + +type Props = { + isPlaying: boolean + isFullScreen: boolean + setIsPlaying: () => void + handleProgressChange: (_: Event, value: number | number[]) => void + handleVolumeChange: (_: Event, value: number | number[]) => void + playingTime: number + duration: number + onFullScreenClick: () => void + showToolbar: boolean +} + +export const Toolbar: FC = ({ + isPlaying, + isFullScreen, + setIsPlaying, + playingTime, + duration, + handleProgressChange, + handleVolumeChange, + onFullScreenClick, + showToolbar, +}) => { + const [volume, setVolume] = useState(0.5) + const [isMuted, setIsMuted] = useState(false) + const [previousVolume, setPreviousVolume] = useState(0.5) + + const volumeChangeHandler = (event: Event, value: number | number[]) => { + const newValue = Array.isArray(value) ? value[0] : value + + setVolume(newValue) + handleVolumeChange(event, newValue) + + if (isMuted) { + setIsMuted(false) + } + } + + const toggleMute = () => { + if (isMuted) { + setVolume(previousVolume) + handleVolumeChange(new Event('input'), previousVolume) + } else { + setPreviousVolume(volume) + setVolume(0) + handleVolumeChange(new Event('input'), 0) + } + + setIsMuted(!isMuted) + } + + return ( + + {(!showToolbar || isFullScreen) && ( + + )} + + + + {!isPlaying ? : } + + + {secondsToMediaTime(playingTime)} + / + {secondsToMediaTime(duration)} + + + + + + {isMuted ? ( + + + + ) : ( + + )} + + + + {!isFullScreen ? : } + + + + ) +} + +const Wrapper = styled(Flex)<{ showToolbar: boolean }>` + height: 60px; + padding: 12px 16px; + ${(props) => + props.showToolbar && + ` + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index:1; + background-color: rgba(0, 0, 0, 0.6); + `} + + &.error-wrapper { + color: ${colors.primaryRed}; + } +` + +const VolumeWrapper = styled.span`` + +const MuteVolumeWrapper = styled.span` + color: gray; +` + +const Action = styled(IconButton)` + && { + font-size: 36px; + padding: 2px; + margin-left: 8px; + } +` + +const VolumeControl = styled(Flex)` + height: 28px; + font-size: 26px; + border-radius: 200px; + color: ${colors.white}; + margin-left: auto; + + .volume-slider { + display: none; + color: ${colors.white}; + height: 3px; + .MuiSlider-track { + border: none; + } + .MuiSlider-thumb { + width: 2px; + height: 10px; + background-color: ${colors.white}; + &:before { + box-shadow: '0 4px 8px rgba(0,0,0,0.4)'; + } + &:hover, + &.Mui-focusVisible, + &.Mui-active { + box-shadow: none; + } + } + } + + &:hover { + background: rgba(42, 44, 55, 1); + .volume-slider { + width: 62px; + margin-right: 4px; + display: block; + } + } +` + +const Fullscreen = styled(Flex)` + cursor: pointer; + padding: 8px; + font-size: 32px; + color: #d9d9d9; +` + +const ProgressSlider = styled(Slider)<{ isFullScreen: boolean }>` + && { + z-index: 20; + color: ${colors.white}; + height: 3px; + width: calc(100% - 12px); + margin: ${(props) => (props.isFullScreen ? '80px auto' : '-12px auto')}; + box-sizing: border-box; + + ${(props) => + props.isFullScreen && + ` + width: calc(100% - 80px) + padding: 12px auto; + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index:1; + `} + + .MuiSlider-track { + border: none; + } + .MuiSlider-thumb { + width: 10px; + height: 10px; + background-color: ${colors.white}; + &:before { + box-shadow: '0 4px 8px rgba(0,0,0,0.4)'; + } + &:hover, + &.Mui-focusVisible, + &.Mui-active { + box-shadow: none; + } + } + } +` + +const TimeStamp = styled(Flex)` + color: ${colors.white}; + font-size: 13px; + margin-left: 16px; + font-weight: 500; + + .separator { + color: ${colors.GRAY6}; + margin: 0 4px; + } + + .duration { + color: ${colors.GRAY6}; + } +` diff --git a/src/modules/mindset/components/MediaPlayer/index.tsx b/src/modules/mindset/components/MediaPlayer/index.tsx new file mode 100644 index 000000000..60bde0e04 --- /dev/null +++ b/src/modules/mindset/components/MediaPlayer/index.tsx @@ -0,0 +1,295 @@ +import { memo, useEffect, useRef, useState } from 'react' +import ReactPlayer from 'react-player' +import { ClipLoader } from 'react-spinners' +import styled from 'styled-components' +import { Avatar } from '~/components/common/Avatar' +import { Flex } from '~/components/common/Flex' +import { usePlayerStore } from '~/stores/usePlayerStore' +import { colors, videoTimeToSeconds } from '~/utils' +import { Toolbar } from './ToolBar' +import { useSelectedNode } from '~/stores/useGraphStore' + +type FullScreenProps = { + isFullScreen: boolean +} + +const MediaPlayerComponent = () => { + const playerRef = useRef(null) + const wrapperRef = useRef(null) + const [isFocused, setIsFocused] = useState(false) + const [isFullScreen, setIsFullScreen] = useState(false) + const [isMouseNearBottom, setIsMouseNearBottom] = useState(false) + const [status, setStatus] = useState<'buffering' | 'error' | 'ready'>('ready') + const [isReady, setIsReady] = useState(false) + const [NodeStartTime, setNodeStartTime] = useState('') + const [hasSeekedToStart, setHasSeekedToStart] = useState(false) + const selectedNode = useSelectedNode() + + useEffect(() => { + const timestamp = '00:02:00-00:12:00' + + const startTime = timestamp?.split('-')[0] as string + + setNodeStartTime(startTime as string) + }, [selectedNode]) + + const { + isPlaying, + playingTime, + duration, + setIsPlaying, + setPlayingTime, + setDuration, + playingNode, + volume, + setVolume, + setHasError, + resetPlayer, + isSeeking, + setIsSeeking, + } = usePlayerStore((s) => s) + + const mediaUrl = 'https://www.youtube.com/watch?v=o8Y0E5sPHr4' + + const isYouTubeVideo = true + + useEffect(() => () => resetPlayer(), [resetPlayer]) + + useEffect(() => { + if (playingNode && !isReady) { + setPlayingTime(0) + setDuration(0) + setIsReady(false) + setHasSeekedToStart(false) + } + }, [playingNode, setPlayingTime, setDuration, setIsReady, isReady]) + + useEffect(() => { + if (isSeeking && playerRef.current) { + playerRef.current.seekTo(playingTime, 'seconds') + setIsSeeking(false) + } + }, [playingTime, isSeeking, setIsSeeking]) + + useEffect(() => { + if (isReady && NodeStartTime && playerRef.current && !hasSeekedToStart) { + const startTimeInSeconds = videoTimeToSeconds(NodeStartTime) + + playerRef.current.seekTo(startTimeInSeconds, 'seconds') + setPlayingTime(startTimeInSeconds) + setHasSeekedToStart(true) + } + }, [isReady, NodeStartTime, setPlayingTime, hasSeekedToStart]) + + const togglePlay = () => { + setIsPlaying(!isPlaying) + } + + const handlePlay = () => { + setIsPlaying(true) + } + + const handlePause = () => { + setIsPlaying(false) + } + + const handleProgressChange = (_: Event, value: number | number[]) => { + const newValue = Array.isArray(value) ? value[0] : value + + setPlayingTime(newValue) + + if (playerRef.current && !isSeeking) { + playerRef.current.seekTo(newValue, 'seconds') + } + } + + const handleVolumeChange = (_: Event, value: number | number[]) => { + const newValue = Array.isArray(value) ? value[0] : value + + setVolume(newValue) + } + + const handleError = () => { + setHasError(true) + setStatus('error') + } + + const handleProgress = (progress: { playedSeconds: number }) => { + if (!isSeeking) { + const currentTime = progress.playedSeconds + + setPlayingTime(currentTime) + } + } + + const handleReady = () => { + if (playerRef.current) { + setStatus('ready') + + const videoDuration = playerRef.current.getDuration() + + setDuration(videoDuration) + + if (NodeStartTime && !hasSeekedToStart) { + const startTimeInSeconds = videoTimeToSeconds(NodeStartTime) + + playerRef.current.seekTo(startTimeInSeconds, 'seconds') + setPlayingTime(startTimeInSeconds) + setHasSeekedToStart(true) + } + } + } + + const toggleFullScreen = () => { + if (wrapperRef.current) { + if (!document.fullscreenElement) { + wrapperRef.current.requestFullscreen().then(() => { + document.addEventListener('fullscreenchange', handleFullScreenChange) + }) + } else { + document.exitFullscreen() + setTimeout(() => setIsFullScreen(false), 300) + } + } + } + + const handleFullScreenChange = () => { + setIsFullScreen(!!document.fullscreenElement) + document.removeEventListener('fullscreenchange', handleFullScreenChange) + } + + useEffect(() => () => { + document.removeEventListener('fullscreenchange', handleFullScreenChange) + }) + + useEffect(() => { + const handleMouseMove = (event: MouseEvent) => { + if (isFullScreen) { + const windowHeight = window.screen.height + const mousePositionY = event.clientY + const distanceFromBottom = windowHeight - mousePositionY + const threshold = 50 + + setIsMouseNearBottom(distanceFromBottom <= threshold) + } + } + + document.addEventListener('mousemove', handleMouseMove) + + return () => { + document.removeEventListener('mousemove', handleMouseMove) + } + }, [isFullScreen, isMouseNearBottom]) + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (isFullScreen && event.key === 'Escape') { + event.preventDefault() + event.stopPropagation() + } else if (isFocused && event.key === ' ') { + event.preventDefault() + togglePlay() + } + } + + document.addEventListener('fullscreenchange', handleFullScreenChange) + document.addEventListener('keydown', handleKeyDown) + + return () => { + document.removeEventListener('fullscreenchange', handleFullScreenChange) + document.removeEventListener('keydown', handleKeyDown) + } + }) + + const handlePlayerClick = () => { + togglePlay() + } + + return mediaUrl ? ( + setIsFocused(false)} onFocus={() => setIsFocused(true)} tabIndex={0}> + + + + + setStatus('buffering')} + onBufferEnd={() => setStatus('ready')} + onError={handleError} + onPause={handlePause} + onPlay={handlePlay} + onProgress={handleProgress} + onReady={handleReady} + playing={isPlaying} + url={mediaUrl || ''} + volume={volume} + width="100%" + /> + + {status === 'error' ? ( + Error happened, please try later + ) : null} + {status === 'ready' ? ( + + ) : null} + {status === 'buffering' && !isYouTubeVideo ? ( + + + + ) : null} + + ) : null +} + +const Wrapper = styled(Flex)` + border-bottom: 1px solid rgba(0, 0, 0, 0.25); + background: rgba(0, 0, 0, 0.2); + position: relative; + overflow: hidden; + height: auto; + &:focus { + outline: none; + } +` + +const Cover = styled(Flex)` + position: absolute; + top: ${(props) => (props.isFullScreen ? '38%' : '18%')}; + left: 50%; + transform: translateX(-50%); + z-index: -1; +` + +const Buffering = styled(Flex)` + position: absolute; + top: ${(props) => (props.isFullScreen ? '43%' : '39%')}; + left: 50%; + transform: translateX(-50%); + z-index: 1; +` + +const ErrorWrapper = styled(Flex)` + height: 60px; + padding: 12px 16px; + color: ${colors.primaryRed}; +` + +const PlayerWrapper = styled.div<{ isFullScreen: boolean }>` + margin: ${(props) => (props.isFullScreen ? '80px auto' : '0')}; + width: 100%; + cursor: pointer; +` + +export const MediaPlayer = memo(MediaPlayerComponent) diff --git a/src/modules/mindset/components/sidebar/index.tsx b/src/modules/mindset/components/sidebar/index.tsx new file mode 100644 index 000000000..1f4096038 --- /dev/null +++ b/src/modules/mindset/components/sidebar/index.tsx @@ -0,0 +1,56 @@ +import styled from 'styled-components' +import { Flex } from '~/components/common/Flex' +import { colors } from '~/utils' +import { MENU_WIDTH } from '~/components/App/SideBar' +import { Text } from '~/components/common/Text' +import { MediaPlayer } from '~/modules/mindset/components/MediaPlayer' + +export const SideBar = () => ( + + What is the Deep State Party and what are their goals? + John Mearsheimer and Jeffrey Sachs | All-In Summit 2024 + + + + +) + +const Wrapper = styled(Flex)(({ theme }) => ({ + position: 'relative', + display: 'flex', + alignItems: 'flex-start', + padding: '20px', + background: colors.BG1, + height: '100vh', + width: '100%', + zIndex: 30, + [theme.breakpoints.up('sm')]: { + width: MENU_WIDTH, + }, +})) + +const Summary = styled(Text)` + font-size: 20px; + font-weight: 700; + overflow-wrap: break-word; + white-space: normal; + word-break: break-word; + margin-right: 10px; +` + +const EpisodeTitle = styled(Text)` + margin-top: 15px; + font-size: 14px; + font-weight: 500; + line-height: 19.6px; +` + +const MediaWrapper = styled(Flex)(({ theme }) => ({ + background: colors.BG1, + width: '100%', + margin: '16px auto', + zIndex: 29, + [theme.breakpoints.up('sm')]: { + width: '390px', + }, +})) diff --git a/src/modules/mindset/index.tsx b/src/modules/mindset/index.tsx index 25e8ac196..ead6335a5 100644 --- a/src/modules/mindset/index.tsx +++ b/src/modules/mindset/index.tsx @@ -1,8 +1,10 @@ import { Flex } from '~/components/common/Flex' import { Header } from './components/header' +import { SideBar } from './components/sidebar' export const MindSet = () => (
+ ) From 28b3b34c69e25c2f6ffd30631ad525f543cb7f26 Mon Sep 17 00:00:00 2001 From: MahtabBukhari Date: Fri, 22 Nov 2024 15:09:20 +0500 Subject: [PATCH 06/17] fix(put-node): remove background --- src/modules/mindset/components/sidebar/index.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/mindset/components/sidebar/index.tsx b/src/modules/mindset/components/sidebar/index.tsx index 1f4096038..74b4ca81b 100644 --- a/src/modules/mindset/components/sidebar/index.tsx +++ b/src/modules/mindset/components/sidebar/index.tsx @@ -20,7 +20,7 @@ const Wrapper = styled(Flex)(({ theme }) => ({ display: 'flex', alignItems: 'flex-start', padding: '20px', - background: colors.BG1, + background: 'transparent', height: '100vh', width: '100%', zIndex: 30, @@ -32,6 +32,7 @@ const Wrapper = styled(Flex)(({ theme }) => ({ const Summary = styled(Text)` font-size: 20px; font-weight: 700; + line-height: 24.2px; overflow-wrap: break-word; white-space: normal; word-break: break-word; @@ -39,10 +40,10 @@ const Summary = styled(Text)` ` const EpisodeTitle = styled(Text)` - margin-top: 15px; + margin-top: 20px; font-size: 14px; font-weight: 500; - line-height: 19.6px; + line-height: 16.94px; ` const MediaWrapper = styled(Flex)(({ theme }) => ({ From 29ec91d97fbc4a1dea9ddfd749817be923b0da5b Mon Sep 17 00:00:00 2001 From: MahtabBukhari Date: Fri, 22 Nov 2024 18:00:40 +0500 Subject: [PATCH 07/17] fix(put-node): update style layout --- .../mindset/components/MediaPlayer/index.tsx | 65 +------------------ .../mindset/components/sidebar/index.tsx | 4 +- 2 files changed, 6 insertions(+), 63 deletions(-) diff --git a/src/modules/mindset/components/MediaPlayer/index.tsx b/src/modules/mindset/components/MediaPlayer/index.tsx index 60bde0e04..ada20d42c 100644 --- a/src/modules/mindset/components/MediaPlayer/index.tsx +++ b/src/modules/mindset/components/MediaPlayer/index.tsx @@ -1,12 +1,12 @@ import { memo, useEffect, useRef, useState } from 'react' import ReactPlayer from 'react-player' -import { ClipLoader } from 'react-spinners' + import styled from 'styled-components' import { Avatar } from '~/components/common/Avatar' import { Flex } from '~/components/common/Flex' import { usePlayerStore } from '~/stores/usePlayerStore' import { colors, videoTimeToSeconds } from '~/utils' -import { Toolbar } from './ToolBar' + import { useSelectedNode } from '~/stores/useGraphStore' type FullScreenProps = { @@ -36,13 +36,11 @@ const MediaPlayerComponent = () => { const { isPlaying, playingTime, - duration, setIsPlaying, setPlayingTime, setDuration, playingNode, volume, - setVolume, setHasError, resetPlayer, isSeeking, @@ -51,8 +49,6 @@ const MediaPlayerComponent = () => { const mediaUrl = 'https://www.youtube.com/watch?v=o8Y0E5sPHr4' - const isYouTubeVideo = true - useEffect(() => () => resetPlayer(), [resetPlayer]) useEffect(() => { @@ -93,22 +89,6 @@ const MediaPlayerComponent = () => { setIsPlaying(false) } - const handleProgressChange = (_: Event, value: number | number[]) => { - const newValue = Array.isArray(value) ? value[0] : value - - setPlayingTime(newValue) - - if (playerRef.current && !isSeeking) { - playerRef.current.seekTo(newValue, 'seconds') - } - } - - const handleVolumeChange = (_: Event, value: number | number[]) => { - const newValue = Array.isArray(value) ? value[0] : value - - setVolume(newValue) - } - const handleError = () => { setHasError(true) setStatus('error') @@ -140,19 +120,6 @@ const MediaPlayerComponent = () => { } } - const toggleFullScreen = () => { - if (wrapperRef.current) { - if (!document.fullscreenElement) { - wrapperRef.current.requestFullscreen().then(() => { - document.addEventListener('fullscreenchange', handleFullScreenChange) - }) - } else { - document.exitFullscreen() - setTimeout(() => setIsFullScreen(false), 300) - } - } - } - const handleFullScreenChange = () => { setIsFullScreen(!!document.fullscreenElement) document.removeEventListener('fullscreenchange', handleFullScreenChange) @@ -214,7 +181,7 @@ const MediaPlayerComponent = () => { setStatus('buffering')} onBufferEnd={() => setStatus('ready')} onError={handleError} @@ -231,24 +198,6 @@ const MediaPlayerComponent = () => { {status === 'error' ? ( Error happened, please try later ) : null} - {status === 'ready' ? ( - - ) : null} - {status === 'buffering' && !isYouTubeVideo ? ( - - - - ) : null} ) : null } @@ -272,14 +221,6 @@ const Cover = styled(Flex)` z-index: -1; ` -const Buffering = styled(Flex)` - position: absolute; - top: ${(props) => (props.isFullScreen ? '43%' : '39%')}; - left: 50%; - transform: translateX(-50%); - z-index: 1; -` - const ErrorWrapper = styled(Flex)` height: 60px; padding: 12px 16px; diff --git a/src/modules/mindset/components/sidebar/index.tsx b/src/modules/mindset/components/sidebar/index.tsx index 74b4ca81b..45911c5c3 100644 --- a/src/modules/mindset/components/sidebar/index.tsx +++ b/src/modules/mindset/components/sidebar/index.tsx @@ -30,8 +30,9 @@ const Wrapper = styled(Flex)(({ theme }) => ({ })) const Summary = styled(Text)` + font-family: Inter; font-size: 20px; - font-weight: 700; + font-weight: Bold; line-height: 24.2px; overflow-wrap: break-word; white-space: normal; @@ -40,6 +41,7 @@ const Summary = styled(Text)` ` const EpisodeTitle = styled(Text)` + font-family: Inter; margin-top: 20px; font-size: 14px; font-weight: 500; From a85ac69d17e6268cacf42afbb24a4f4f559fd82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Thu, 21 Nov 2024 16:39:23 +0300 Subject: [PATCH 08/17] feat: board in progress --- .../mindset/components/Scene/Board/index.tsx | 109 ++++++++++++++++++ .../mindset/components/Scene/index.tsx | 17 +++ src/modules/mindset/index.tsx | 2 + 3 files changed, 128 insertions(+) create mode 100644 src/modules/mindset/components/Scene/Board/index.tsx create mode 100644 src/modules/mindset/components/Scene/index.tsx diff --git a/src/modules/mindset/components/Scene/Board/index.tsx b/src/modules/mindset/components/Scene/Board/index.tsx new file mode 100644 index 000000000..8821b3bd9 --- /dev/null +++ b/src/modules/mindset/components/Scene/Board/index.tsx @@ -0,0 +1,109 @@ +// @ts-nocheck +// @ts-ignore +/* eslint-disable react/prop-types */ +import { Canvas } from '@react-three/fiber' +import { useRef, useState } from 'react' +import * as THREE from 'three' + +export const Board = () => { + const data = Array.from({ length: 20 }, (_, i) => ({ + position: [i * 3, 0, 0], // Layered left to right + color: i % 2 === 0 ? 'red' : 'blue', + })) + + const groupRef = useRef() + const cameraRef = useRef() + const [scale, setScale] = useState(1) + const [position, setPosition] = useState({ x: 0, y: 0 }) + const isDragging = useRef(false) + const dragStart = useRef({ x: 0, y: 0 }) + const raycaster = new THREE.Raycaster() + const pointer = new THREE.Vector2() + + const handleWheel = (event) => { + event.nativeEvent.preventDefault() + + // Convert pointer position to normalized device coordinates + const rect = event.nativeEvent.target.getBoundingClientRect() + + pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1 + pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1 + + // Use raycaster to find the point in the world space + raycaster.setFromCamera(pointer, cameraRef.current) + + const intersects = raycaster.intersectObjects(groupRef.current.children, true) + + if (intersects.length > 0) { + const targetPoint = intersects[0].point + + const zoomFactor = event.deltaY > 0 ? 1.1 : 0.9 // Adjust zoom sensitivity + + setScale((prevScale) => Math.min(Math.max(prevScale * zoomFactor, 0.5), 5)) // Clamp scale between 0.5 and 5 + + // Update position to zoom toward target point + setPosition((prevPosition) => ({ + x: prevPosition.x + (targetPoint.x - prevPosition.x) * (1 - zoomFactor), + y: prevPosition.y + (targetPoint.y - prevPosition.y) * (1 - zoomFactor), + })) + } + } + + const handlePointerDown = (event) => { + isDragging.current = true + + dragStart.current = { + x: event.clientX - position.x, + y: event.clientY - position.y, + } + } + + const handlePointerMove = (event) => { + if (isDragging.current) { + setPosition({ + x: event.clientX - dragStart.current.x, + y: event.clientY - dragStart.current.y, + }) + } + } + + const handlePointerUp = () => { + isDragging.current = false + } + + return ( + + {/* Add an Orthographic camera for 2D interaction */} + + + + + + + ) +} + +const Nodes = ({ data }) => ( + + {data.map((node, index) => ( + // eslint-disable-next-line react/no-array-index-key + + ))} + +) + +const Node = ({ position, color }) => ( + + + + +) diff --git a/src/modules/mindset/components/Scene/index.tsx b/src/modules/mindset/components/Scene/index.tsx new file mode 100644 index 000000000..d201d35da --- /dev/null +++ b/src/modules/mindset/components/Scene/index.tsx @@ -0,0 +1,17 @@ +import { OrbitControls, OrthographicCamera } from '@react-three/drei' +import { Canvas } from '@react-three/fiber' +import { Board } from './Board' + +export const Scene = () => ( + <> + {false && ( + + + + + + + )} + + +) diff --git a/src/modules/mindset/index.tsx b/src/modules/mindset/index.tsx index ead6335a5..48a6d660c 100644 --- a/src/modules/mindset/index.tsx +++ b/src/modules/mindset/index.tsx @@ -1,10 +1,12 @@ import { Flex } from '~/components/common/Flex' import { Header } from './components/header' +import { Scene } from './components/Scene' import { SideBar } from './components/sidebar' export const MindSet = () => (
+ ) From c145c1ede2b45b953b319171df8c1d9cf5faf413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Fri, 22 Nov 2024 23:03:24 +0300 Subject: [PATCH 09/17] feat: node in progress/r3f --- .../components/Scene/Board/Node/index.tsx | 91 ++++++++++ .../Scene/Board/RoundedImage/index.tsx | 23 +++ .../Scene/Board/RoundedRectangle/index.tsx | 35 ++++ .../mindset/components/Scene/Board/index.tsx | 167 +++++++----------- .../mindset/components/Scene/index.tsx | 58 ++++-- src/modules/mindset/data/index.tsx | 60 +++++++ src/types/index.ts | 1 + 7 files changed, 319 insertions(+), 116 deletions(-) create mode 100644 src/modules/mindset/components/Scene/Board/Node/index.tsx create mode 100644 src/modules/mindset/components/Scene/Board/RoundedImage/index.tsx create mode 100644 src/modules/mindset/components/Scene/Board/RoundedRectangle/index.tsx create mode 100644 src/modules/mindset/data/index.tsx diff --git a/src/modules/mindset/components/Scene/Board/Node/index.tsx b/src/modules/mindset/components/Scene/Board/Node/index.tsx new file mode 100644 index 000000000..585afc976 --- /dev/null +++ b/src/modules/mindset/components/Scene/Board/Node/index.tsx @@ -0,0 +1,91 @@ +import { Html, Text } from '@react-three/drei' +import { useThree } from '@react-three/fiber' +import { OrthographicCamera } from 'three' +import { Flex } from '~/components/common/Flex' +import { fontProps } from '~/components/Universe/Graph/Cubes/Text/constants' +import { RoundedImage } from '../RoundedImage' +import { RoundedRectangle } from '../RoundedRectangle' + +type Props = { + width: number + height: number + position: [number, number, number] + url: string + onButtonClick: () => void + name: string + type: string +} + +export const Node = ({ width, height, position, url, onButtonClick, name, type }: Props) => { + const { camera, size } = useThree() + + // Function to calculate the distance between the camera and the node + const getPixelSize = (worldSize: number, worldHeight: number) => { + const ortographicCamera = camera as OrthographicCamera + const visibleWidth = ortographicCamera.right - ortographicCamera.left + const visibleHeight = ortographicCamera.top - ortographicCamera.bottom + + return { + pixelWidth: (worldSize / visibleWidth) * size.width, + pixelHeight: (worldHeight / visibleHeight) * size.height, + } + } + + // Calculate pixel dimensions for the node + const { pixelWidth, pixelHeight } = getPixelSize(width, height) + + return ( + + {/* Background Rectangle */} + + + {/* Rounded Image */} + + + {/* Text */} + <> + + {name} + + + {type} + + + + {/* Html */} + + onButtonClick()} + style={{ + fontSize: '20px', + color: 'white', + fontWeight: 600, + width: `${pixelWidth}px`, + height: `${pixelHeight}px`, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + borderRadius: '8px', // Optional for rounded corners + pointerEvents: 'auto', // Allow interaction + }} + > + {type} + + + + ) +} diff --git a/src/modules/mindset/components/Scene/Board/RoundedImage/index.tsx b/src/modules/mindset/components/Scene/Board/RoundedImage/index.tsx new file mode 100644 index 000000000..eb205feeb --- /dev/null +++ b/src/modules/mindset/components/Scene/Board/RoundedImage/index.tsx @@ -0,0 +1,23 @@ +import { Image } from '@react-three/drei' + +type Props = { + position: [number, number, number] + scale: [number, number, number] + url: string +} + +export const RoundedImage = ({ position, scale, url }: Props) => ( + + {/* Circle mask */} + + {/* Adjust radius as needed */} + + + + {/* Image */} + + +) diff --git a/src/modules/mindset/components/Scene/Board/RoundedRectangle/index.tsx b/src/modules/mindset/components/Scene/Board/RoundedRectangle/index.tsx new file mode 100644 index 000000000..e48a40f98 --- /dev/null +++ b/src/modules/mindset/components/Scene/Board/RoundedRectangle/index.tsx @@ -0,0 +1,35 @@ +import { Shape } from 'three' + +type Props = { + width: number + height: number + radius: number + color: string +} + +const createRoundedRect = (width: number, height: number, radius: number) => { + const shape = new Shape() + + shape.moveTo(-width / 2 + radius, -height / 2) + shape.lineTo(width / 2 - radius, -height / 2) + shape.quadraticCurveTo(width / 2, -height / 2, width / 2, -height / 2 + radius) + shape.lineTo(width / 2, height / 2 - radius) + shape.quadraticCurveTo(width / 2, height / 2, width / 2 - radius, height / 2) + shape.lineTo(-width / 2 + radius, height / 2) + shape.quadraticCurveTo(-width / 2, height / 2, -width / 2, height / 2 - radius) + shape.lineTo(-width / 2, -height / 2 + radius) + shape.quadraticCurveTo(-width / 2, -height / 2, -width / 2 + radius, -height / 2) + + return shape +} + +export const RoundedRectangle = ({ width, height, radius, color }: Props) => { + const roundedRectShape = createRoundedRect(width, height, radius) + + return ( + + + + + ) +} diff --git a/src/modules/mindset/components/Scene/Board/index.tsx b/src/modules/mindset/components/Scene/Board/index.tsx index 8821b3bd9..84bcca3df 100644 --- a/src/modules/mindset/components/Scene/Board/index.tsx +++ b/src/modules/mindset/components/Scene/Board/index.tsx @@ -1,109 +1,68 @@ -// @ts-nocheck -// @ts-ignore -/* eslint-disable react/prop-types */ -import { Canvas } from '@react-three/fiber' -import { useRef, useState } from 'react' -import * as THREE from 'three' - -export const Board = () => { - const data = Array.from({ length: 20 }, (_, i) => ({ - position: [i * 3, 0, 0], // Layered left to right - color: i % 2 === 0 ? 'red' : 'blue', - })) - - const groupRef = useRef() - const cameraRef = useRef() - const [scale, setScale] = useState(1) - const [position, setPosition] = useState({ x: 0, y: 0 }) - const isDragging = useRef(false) - const dragStart = useRef({ x: 0, y: 0 }) - const raycaster = new THREE.Raycaster() - const pointer = new THREE.Vector2() - - const handleWheel = (event) => { - event.nativeEvent.preventDefault() - - // Convert pointer position to normalized device coordinates - const rect = event.nativeEvent.target.getBoundingClientRect() - - pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1 - pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1 - - // Use raycaster to find the point in the world space - raycaster.setFromCamera(pointer, cameraRef.current) - - const intersects = raycaster.intersectObjects(groupRef.current.children, true) - - if (intersects.length > 0) { - const targetPoint = intersects[0].point - - const zoomFactor = event.deltaY > 0 ? 1.1 : 0.9 // Adjust zoom sensitivity - - setScale((prevScale) => Math.min(Math.max(prevScale * zoomFactor, 0.5), 5)) // Clamp scale between 0.5 and 5 - - // Update position to zoom toward target point - setPosition((prevPosition) => ({ - x: prevPosition.x + (targetPoint.x - prevPosition.x) * (1 - zoomFactor), - y: prevPosition.y + (targetPoint.y - prevPosition.y) * (1 - zoomFactor), - })) - } - } - - const handlePointerDown = (event) => { - isDragging.current = true - - dragStart.current = { - x: event.clientX - position.x, - y: event.clientY - position.y, - } - } - - const handlePointerMove = (event) => { - if (isDragging.current) { - setPosition({ - x: event.clientX - dragStart.current.x, - y: event.clientY - dragStart.current.y, - }) - } - } +import { useThree } from '@react-three/fiber' +import { useMemo } from 'react' +import { edges, maxTimestamp, minTimestamp, nodes, normalizeTimestamp } from '~/modules/mindset/data' +import { Node } from './Node' +import { RoundedRectangle } from './RoundedRectangle' + +type Props = { + w: number + gap: number +} - const handlePointerUp = () => { - isDragging.current = false - } +export const Board = ({ w = 16, gap = 6 }: Props) => { + const { width } = useThree((state) => state.viewport) + const xW = w + gap + + console.log(width, xW) + + const rangeMin = 0 + const rangeMax = 50 + + const positions = useMemo( + () => + edges + .filter((edge) => edge?.properties?.mentionedStart && edge?.properties?.mentionedEnd) + .map((edge) => { + const st: number = (edge?.properties?.mentionedStart || 0) as number + const ed: number = (edge?.properties?.mentionedEnd || 0) as number + + return { + source: edge.source, + target: edge.target, + xStart: normalizeTimestamp(st, minTimestamp, maxTimestamp, rangeMin, rangeMax), + xEnd: normalizeTimestamp(ed, minTimestamp, maxTimestamp, rangeMin, rangeMax), + } + }), + [rangeMin, rangeMax], + ) return ( - - {/* Add an Orthographic camera for 2D interaction */} - - - - - - + true && ( + <> + {nodes.map((node, i) => { + const hasTimeStamp = positions.some((p) => p.source === nodes[i].ref_id) + + const position = hasTimeStamp ? positions.find((p) => p.source === nodes[i].ref_id)?.xStart || 0 : i * 35 + + const y = hasTimeStamp ? 0 : 15 + + return ( + + ) + })} + + + + + ) ) } - -const Nodes = ({ data }) => ( - - {data.map((node, index) => ( - // eslint-disable-next-line react/no-array-index-key - - ))} - -) - -const Node = ({ position, color }) => ( - - - - -) diff --git a/src/modules/mindset/components/Scene/index.tsx b/src/modules/mindset/components/Scene/index.tsx index d201d35da..2ce5855a2 100644 --- a/src/modules/mindset/components/Scene/index.tsx +++ b/src/modules/mindset/components/Scene/index.tsx @@ -1,17 +1,51 @@ import { OrbitControls, OrthographicCamera } from '@react-three/drei' import { Canvas } from '@react-three/fiber' +import { useState } from 'react' +import { Vector3 } from 'three' import { Board } from './Board' -export const Scene = () => ( - <> - {false && ( - - - - - +export const Scene = () => { + const [cameraX, setCameraX] = useState(0) // State for horizontal camera movement + const cameraPosition = new Vector3(cameraX, 0, 50) + + const handleSliderChange = (event: React.ChangeEvent) => { + setCameraX(Number(event.target.value)) + } + + // Parameters for OrthographicCamera + const frustumSize = 50 + const aspect = window.innerWidth / window.innerHeight + const left = (-frustumSize * aspect) / 2 + const right = (frustumSize * aspect) / 2 + const top = frustumSize / 2 + const bottom = -frustumSize / 2 + + return ( +
+ {/* Slider for controlling camera X-axis */} +
+ +
+ + console.log('missed')}> + {false && } + + + + + + {/* Add Axis Helper */} + - )} - - -) +
+ ) +} diff --git a/src/modules/mindset/data/index.tsx b/src/modules/mindset/data/index.tsx new file mode 100644 index 000000000..2aaa7c25c --- /dev/null +++ b/src/modules/mindset/data/index.tsx @@ -0,0 +1,60 @@ +import { Link } from '~/types' + +// const transcript = 'Entrepreneurship is being revolutionized by Blockchain and AI, as they open new frontiers for innovation and secure digital solutions.' + +export const nodes = [ + { ref_id: '1', node_type: 'Person', name: 'Charlie' }, + { ref_id: '2', node_type: 'Person', name: 'Alice' }, + { ref_id: '3', node_type: 'Topic', name: 'Entrepreneurship' }, + { ref_id: '4', node_type: 'Topic', name: 'Blockchain' }, + { ref_id: '5', node_type: 'Topic', name: 'AI' }, + { ref_id: '6', node_type: 'Clip', name: 'Clip' }, +] + +export const edges: Link[] = [ + { + ref_id: '1', + edge_type: 'Mentioned', + source: '3', + target: '6', + properties: { mentionedStart: 0.5, mentionedEnd: 2.0 }, + }, + { + ref_id: '2', + edge_type: 'Mentioned', + source: '4', + target: '6', + properties: { mentionedStart: 4.6, mentionedEnd: 9.0 }, + }, + { + ref_id: '3', + edge_type: 'Mentioned', + source: '5', + target: '6', + properties: { mentionedStart: 9.1, mentionedEnd: 10.0 }, + }, + { + ref_id: '4', + edge_type: 'Related', + source: '1', + target: '6', + }, +] + +export const data = { + nodes, + edges, +} + +const timestamps = edges.flatMap((e) => { + const { mentionedStart, mentionedEnd } = e.properties ?? {} + + return mentionedStart !== undefined && mentionedEnd !== undefined ? [mentionedStart, mentionedEnd] : [0, 0] +}) + +export const minTimestamp = Math.min(...(timestamps as number[])) + +export const maxTimestamp = Math.max(...(timestamps as number[])) + +export const normalizeTimestamp = (value: number, min: number, max: number, rangeMin: number, rangeMax: number) => + rangeMin + ((value - min) / (max - min)) * (rangeMax - rangeMin) diff --git a/src/types/index.ts b/src/types/index.ts index 152948948..481961dcf 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -160,6 +160,7 @@ export type Link = { sourcePosition?: Vector3 targetPosition?: Vector3 onlyVisibleOnSelect?: boolean + properties?: { [key: string]: unknown } } export type GraphData = { From 647fb22d63d02c953bc96f23bf5e7c785e902fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Sat, 23 Nov 2024 12:18:49 +0300 Subject: [PATCH 10/17] feat: zoom/scroll error --- .../Scene/Board/Node/Content/index.tsx | 47 +++++++ .../components/Scene/Board/Node/index.tsx | 41 ++---- .../Scene/Board/RoundedRectangle/index.tsx | 2 + .../mindset/components/Scene/Board/index.tsx | 83 ++++++++++-- .../mindset/components/Scene/index.tsx | 65 ++++++---- src/modules/mindset/data/index.tsx | 120 +++++++++++++++--- 6 files changed, 269 insertions(+), 89 deletions(-) create mode 100644 src/modules/mindset/components/Scene/Board/Node/Content/index.tsx diff --git a/src/modules/mindset/components/Scene/Board/Node/Content/index.tsx b/src/modules/mindset/components/Scene/Board/Node/Content/index.tsx new file mode 100644 index 000000000..319977691 --- /dev/null +++ b/src/modules/mindset/components/Scene/Board/Node/Content/index.tsx @@ -0,0 +1,47 @@ +import styled from 'styled-components' +import { Flex } from '~/components/common/Flex' +import PlusIcon from '~/components/Icons/PlusIcon' +import { colors } from '~/utils' + +type Props = { + name: string + url?: string +} + +export const Content = ({ name, url }: Props) => ( + + {url && } +
{name}
+
+ +
+
+) + +const Wrapper = styled(Flex)` + position: relative; + padding: 14px; + padding-right: 28px; + flex: 1; + width: 100%; + box-sizing: border-box; + + .title { + margin-top: 8px; + } + + .image { + width: 32px; + height: 32px; + border-radius: 50%; + margin-bottom: 8px; + object-fit: cover; + } + + .action-btn { + top: 14px; + right: 14px; + color: ${colors.GRAY6}; + position: absolute; + } +` diff --git a/src/modules/mindset/components/Scene/Board/Node/index.tsx b/src/modules/mindset/components/Scene/Board/Node/index.tsx index 585afc976..0dc4cef52 100644 --- a/src/modules/mindset/components/Scene/Board/Node/index.tsx +++ b/src/modules/mindset/components/Scene/Board/Node/index.tsx @@ -1,10 +1,9 @@ -import { Html, Text } from '@react-three/drei' +import { Html } from '@react-three/drei' import { useThree } from '@react-three/fiber' import { OrthographicCamera } from 'three' import { Flex } from '~/components/common/Flex' -import { fontProps } from '~/components/Universe/Graph/Cubes/Text/constants' -import { RoundedImage } from '../RoundedImage' import { RoundedRectangle } from '../RoundedRectangle' +import { Content } from './Content' type Props = { width: number @@ -14,11 +13,14 @@ type Props = { onButtonClick: () => void name: string type: string + color: string } -export const Node = ({ width, height, position, url, onButtonClick, name, type }: Props) => { +export const Node = ({ width, height, position, url, onButtonClick, name, type, color }: Props) => { const { camera, size } = useThree() + console.log(url, type) + // Function to calculate the distance between the camera and the node const getPixelSize = (worldSize: number, worldHeight: number) => { const ortographicCamera = camera as OrthographicCamera @@ -37,34 +39,7 @@ export const Node = ({ width, height, position, url, onButtonClick, name, type } return ( {/* Background Rectangle */} - - - {/* Rounded Image */} - - - {/* Text */} - <> - - {name} - - - {type} - - + {/* Html */} @@ -83,7 +58,7 @@ export const Node = ({ width, height, position, url, onButtonClick, name, type } pointerEvents: 'auto', // Allow interaction }} > - {type} + diff --git a/src/modules/mindset/components/Scene/Board/RoundedRectangle/index.tsx b/src/modules/mindset/components/Scene/Board/RoundedRectangle/index.tsx index e48a40f98..f7fba1f57 100644 --- a/src/modules/mindset/components/Scene/Board/RoundedRectangle/index.tsx +++ b/src/modules/mindset/components/Scene/Board/RoundedRectangle/index.tsx @@ -24,6 +24,8 @@ const createRoundedRect = (width: number, height: number, radius: number) => { } export const RoundedRectangle = ({ width, height, radius, color }: Props) => { + console.log(width, height) + const roundedRectShape = createRoundedRect(width, height, radius) return ( diff --git a/src/modules/mindset/components/Scene/Board/index.tsx b/src/modules/mindset/components/Scene/Board/index.tsx index 84bcca3df..ca9eee5db 100644 --- a/src/modules/mindset/components/Scene/Board/index.tsx +++ b/src/modules/mindset/components/Scene/Board/index.tsx @@ -1,22 +1,54 @@ import { useThree } from '@react-three/fiber' -import { useMemo } from 'react' -import { edges, maxTimestamp, minTimestamp, nodes, normalizeTimestamp } from '~/modules/mindset/data' +import { useEffect, useMemo } from 'react' +import { edges, edgesMention, maxTimestamp, minTimestamp, nodes, normalizeTimestamp } from '~/modules/mindset/data' import { Node } from './Node' -import { RoundedRectangle } from './RoundedRectangle' type Props = { w: number - gap: number } -export const Board = ({ w = 16, gap = 6 }: Props) => { - const { width } = useThree((state) => state.viewport) - const xW = w + gap +const totalDuration = 185 + +export const Board = ({ w = 1 }: Props) => { + const state = useThree() + + console.log(state) + + const { width } = state.viewport + const { camera } = state + const xW = w + + useEffect(() => { + const orthoCamera = camera as THREE.OrthographicCamera + + const handleWheel = (event: WheelEvent) => { + event.preventDefault() // Prevent default scrolling behavior + + if (event.ctrlKey) { + // Zoom the camera when ctrlKey is pressed + orthoCamera.zoom += event.deltaY * -0.01 // Adjust zoom level + orthoCamera.zoom = Math.max(0.5, Math.min(orthoCamera.zoom, 5)) // Clamp zoom + } else { + // Move the camera left/right when ctrlKey is NOT pressed + orthoCamera.position.x += event.deltaX * 0.01 // Horizontal movement + } + + orthoCamera.updateProjectionMatrix() // Update projection matrix + } + + // Add the event listener + window.addEventListener('wheel', handleWheel, { passive: false }) + + return () => { + // Cleanup event listener + window.removeEventListener('wheel', handleWheel) + } + }, [camera]) console.log(width, xW) const rangeMin = 0 - const rangeMax = 50 + const rangeMax = 97.52 * 10 const positions = useMemo( () => @@ -36,6 +68,8 @@ export const Board = ({ w = 16, gap = 6 }: Props) => { [rangeMin, rangeMax], ) + console.log(-width) + return ( true && ( <> @@ -46,10 +80,11 @@ export const Board = ({ w = 16, gap = 6 }: Props) => { const y = hasTimeStamp ? 0 : 15 - return ( + return true ? null : ( { /> ) })} - - + {edgesMention.map((e) => { + const node = nodes.find((i) => i.ref_id === e.source) + + const x = (e.mentionedStart / totalDuration) * width + + console.log(x) + + const y = -5 + + return node ? ( + + ) : null + })} + + {/* Radius: Half of viewport width */} + ) diff --git a/src/modules/mindset/components/Scene/index.tsx b/src/modules/mindset/components/Scene/index.tsx index 2ce5855a2..6a4603881 100644 --- a/src/modules/mindset/components/Scene/index.tsx +++ b/src/modules/mindset/components/Scene/index.tsx @@ -1,25 +1,14 @@ -import { OrbitControls, OrthographicCamera } from '@react-three/drei' -import { Canvas } from '@react-three/fiber' -import { useState } from 'react' -import { Vector3 } from 'three' +import { Canvas, useThree } from '@react-three/fiber' +import { useEffect, useState } from 'react' import { Board } from './Board' export const Scene = () => { const [cameraX, setCameraX] = useState(0) // State for horizontal camera movement - const cameraPosition = new Vector3(cameraX, 0, 50) const handleSliderChange = (event: React.ChangeEvent) => { setCameraX(Number(event.target.value)) } - // Parameters for OrthographicCamera - const frustumSize = 50 - const aspect = window.innerWidth / window.innerHeight - const left = (-frustumSize * aspect) / 2 - const right = (frustumSize * aspect) / 2 - const top = frustumSize / 2 - const bottom = -frustumSize / 2 - return (
{/* Slider for controlling camera X-axis */} @@ -27,19 +16,9 @@ export const Scene = () => {
- console.log('missed')}> - {false && } - - + + + @@ -49,3 +28,37 @@ export const Scene = () => { ) } + +// Custom camera component +const DynamicOrthographicCamera = ({ x }: { x: number }) => { + const { size, viewport, camera } = useThree() + const frustumSize = 50 + + const aspect = size.width / size.height + + console.log(aspect, size.width) + + // Dynamically calculate camera bounds + const left = (-frustumSize * aspect) / 2 + const right = (frustumSize * aspect) / 2 + const top = frustumSize / 2 + const bottom = -frustumSize / 2 + + useEffect(() => { + // Update camera bounds dynamically + const orthoCamera = camera as THREE.OrthographicCamera + + orthoCamera.left = left + orthoCamera.right = right + orthoCamera.top = top + orthoCamera.bottom = bottom + orthoCamera.position.x = x + orthoCamera.updateProjectionMatrix() + }, [camera, left, right, top, bottom, x]) + + useEffect(() => { + console.log('Viewport Width in World Units:', viewport.width) + }, [viewport.width]) + + return null +} diff --git a/src/modules/mindset/data/index.tsx b/src/modules/mindset/data/index.tsx index 2aaa7c25c..5b5db56e9 100644 --- a/src/modules/mindset/data/index.tsx +++ b/src/modules/mindset/data/index.tsx @@ -3,44 +3,128 @@ import { Link } from '~/types' // const transcript = 'Entrepreneurship is being revolutionized by Blockchain and AI, as they open new frontiers for innovation and secure digital solutions.' export const nodes = [ - { ref_id: '1', node_type: 'Person', name: 'Charlie' }, - { ref_id: '2', node_type: 'Person', name: 'Alice' }, - { ref_id: '3', node_type: 'Topic', name: 'Entrepreneurship' }, - { ref_id: '4', node_type: 'Topic', name: 'Blockchain' }, - { ref_id: '5', node_type: 'Topic', name: 'AI' }, - { ref_id: '6', node_type: 'Clip', name: 'Clip' }, + { ref_id: '1', node_type: 'Clip', name: 'Podcast' }, + { ref_id: '2', node_type: 'Topic', name: 'Bitcoin' }, + { ref_id: '3', node_type: 'Topic', name: 'Blockchain' }, + { ref_id: '4', node_type: 'Topic', name: 'Hard Money' }, + { ref_id: '5', node_type: 'Topic', name: 'Digital Currency' }, + { ref_id: '6', node_type: 'Topic', name: 'Government Control' }, + { ref_id: '7', node_type: 'Topic', name: 'Inflation' }, + { ref_id: '8', node_type: 'Topic', name: 'Public Network' }, + { ref_id: '9', node_type: 'Topic', name: 'Energy Consumption' }, + { ref_id: '10', node_type: 'Topic', name: 'Immutability' }, + { ref_id: '11', node_type: 'Topic', name: 'Scarcity' }, + { ref_id: '12', node_type: 'Topic', name: 'Decentralization' }, + { ref_id: '13', node_type: 'Topic', name: 'Investment Risks' }, + { ref_id: '14', node_type: 'Topic', name: 'Adoption' }, + { ref_id: '15', node_type: 'Person', name: 'Satoshi Nakamoto' }, ] export const edges: Link[] = [ + // Bitcoin as a digital currency { ref_id: '1', edge_type: 'Mentioned', - source: '3', - target: '6', - properties: { mentionedStart: 0.5, mentionedEnd: 2.0 }, + target: '1', // Clip + source: '2', // Bitcoin + properties: { mentionedStart: 7.68, mentionedEnd: 19.619 }, }, + + // Bitcoin's hard money nature { ref_id: '2', edge_type: 'Mentioned', - source: '4', - target: '6', - properties: { mentionedStart: 4.6, mentionedEnd: 9.0 }, + target: '1', // Clip + source: '4', // Hard Money + properties: { mentionedStart: 28.019, mentionedEnd: 38.04 }, }, + + // Blockchain as a public ledger { ref_id: '3', edge_type: 'Mentioned', - source: '5', - target: '6', - properties: { mentionedStart: 9.1, mentionedEnd: 10.0 }, + target: '1', // Clip + source: '3', // Blockchain + properties: { mentionedStart: 50.82, mentionedEnd: 56.52 }, }, + + // Scarcity of Bitcoin { ref_id: '4', - edge_type: 'Related', - source: '1', - target: '6', + edge_type: 'Mentioned', + target: '1', // Clip + source: '11', // Scarcity + properties: { mentionedStart: 19.439, mentionedEnd: 25.619 }, + }, + + // Government control contrasted with Bitcoin + { + ref_id: '5', + edge_type: 'Mentioned', + target: '1', // Clip + source: '6', // Government Control + properties: { mentionedStart: 34.619, mentionedEnd: 43.02 }, + }, + + // Energy consumption in Bitcoin mining + { + ref_id: '6', + edge_type: 'Mentioned', + target: '1', // Clip + source: '9', // Energy Consumption + properties: { mentionedStart: 31.8, mentionedEnd: 38.04 }, + }, + + // Immutability ensured by the blockchain + { + ref_id: '7', + edge_type: 'Mentioned', + target: '1', // Clip + source: '10', // Immutability + properties: { mentionedStart: 56.52, mentionedEnd: 60.48 }, + }, + + // Decentralization of Bitcoin's public network + { + ref_id: '8', + edge_type: 'Mentioned', + target: '1', // Clip + source: '12', // Decentralization + properties: { mentionedStart: 90.72, mentionedEnd: 97.52 }, + }, + + // Bitcoin investment risks + { + ref_id: '9', + edge_type: 'Mentioned', + target: '1', // Clip + source: '13', // Investment Risks + properties: { mentionedStart: 126.44, mentionedEnd: 133.48 }, + }, + + // Bitcoin adoption rates and potential + { + ref_id: '10', + edge_type: 'Mentioned', + target: '1', // Clip + source: '14', // Adoption + properties: { mentionedStart: 122.44, mentionedEnd: 130.48 }, + }, + + // Satoshi Nakamoto mentioned as the creator + { + ref_id: '11', + edge_type: 'Mentioned', + target: '1', // Clip + source: '15', // Satoshi Nakamoto + properties: { mentionedStart: 7.68, mentionedEnd: 19.619 }, }, ] +export const edgesMention: Array<{ source: string; mentionedStart: number }> = edges + .filter((e) => e.properties?.mentionedStart) + .map((edge) => ({ source: edge.source, mentionedStart: edge.properties?.mentionedStart as number })) + export const data = { nodes, edges, From e8e1aada0afa9d27aa280fb66cb79fe5928118cc Mon Sep 17 00:00:00 2001 From: MuhammadUmer44 Date: Sun, 24 Nov 2024 00:25:20 +0500 Subject: [PATCH 11/17] feat(landing-page): add landing page with validation --- .../mindset/components/Icon/ChevronRight.tsx | 13 ++ .../components/{header => }/Icon/Logo.tsx | 0 .../mindset/components/LandingPage/index.tsx | 113 ++++++++++++++++++ .../components/LandingPage/utils/index.tsx | 47 ++++++++ .../mindset/components/header/index.tsx | 35 +++++- src/modules/mindset/index.tsx | 2 + src/utils/colors/index.tsx | 3 + 7 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/modules/mindset/components/Icon/ChevronRight.tsx rename src/modules/mindset/components/{header => }/Icon/Logo.tsx (100%) create mode 100644 src/modules/mindset/components/LandingPage/index.tsx create mode 100644 src/modules/mindset/components/LandingPage/utils/index.tsx diff --git a/src/modules/mindset/components/Icon/ChevronRight.tsx b/src/modules/mindset/components/Icon/ChevronRight.tsx new file mode 100644 index 000000000..e6f7e4aaa --- /dev/null +++ b/src/modules/mindset/components/Icon/ChevronRight.tsx @@ -0,0 +1,13 @@ +/* eslint-disable */ +import React from 'react' + +export const ChevronRight: React.FC> = (props) => ( + + + +) + +export default ChevronRight diff --git a/src/modules/mindset/components/header/Icon/Logo.tsx b/src/modules/mindset/components/Icon/Logo.tsx similarity index 100% rename from src/modules/mindset/components/header/Icon/Logo.tsx rename to src/modules/mindset/components/Icon/Logo.tsx diff --git a/src/modules/mindset/components/LandingPage/index.tsx b/src/modules/mindset/components/LandingPage/index.tsx new file mode 100644 index 000000000..8e61f31fb --- /dev/null +++ b/src/modules/mindset/components/LandingPage/index.tsx @@ -0,0 +1,113 @@ +import { useState } from 'react' +import styled from 'styled-components' +import { Flex } from '~/components/common/Flex' +import { colors } from '~/utils/colors' +import { ChevronRight } from '../Icon/ChevronRight' +import { isValidMediaUrl } from './utils' + +export const LandingPage = () => { + const [inputValue, setInputValue] = useState('') + const [error, setError] = useState(false) + + const handleInputChange = (e: React.ChangeEvent) => { + const { value } = e.target + + setInputValue(value) + setError(value !== '' && !isValidMediaUrl(value)) + } + + const handleSubmit = () => { + if (isValidMediaUrl(inputValue)) { + console.log('Valid media URL:', inputValue) + } + } + + return ( + + Ideas have shapes + + e.key === 'Enter' && handleSubmit()} + placeholder="Paste podcast or video link" + value={inputValue} + /> + + + + + + ) +} + +const Wrapper = styled(Flex)` + background: #16161de3; + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + color: #fff; + align-items: center; + justify-content: center; + font-size: 32px; + font-style: normal; + font-weight: 700; + line-height: 16px; + font-family: 'Barlow'; + z-index: 40; +` + +const Title = styled(Flex)` + color: ${colors.white}; + font-family: Barlow; + font-size: 32px; + font-weight: 700; + margin-bottom: 40px; + text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25); +` + +const Input = styled.input<{ error?: boolean }>` + width: 100%; + max-width: 450px; + padding: 12px 28px 12px 16px; + border-radius: 100px; + border: 1px solid ${(props) => (props.error ? 'red' : colors.DIVIDER_4)}; + background: ${colors.INPUT_BG}; + color: ${colors.white}; + font-family: Barlow; + font-size: 16px; + + &::placeholder { + color: ${colors.INPUT_PLACEHOLDER}; + } + + &:focus { + outline: none; + border-color: ${(props) => (props.error ? 'red' : colors.primaryBlue)}; + } +` + +const InputWrapper = styled.div` + position: relative; + width: 450px; + display: flex; + align-items: center; +` + +const IconWrapper = styled.div<{ error?: boolean }>` + position: absolute; + right: 16px; + top: 50%; + transform: translateY(-50%); + color: ${colors.white}; + font-size: 20px; + cursor: ${(props) => (props.error ? 'not-allowed' : 'pointer')}; + + svg { + width: 8px; + height: 17px; + color: ${colors.GRAY6}; + } +` diff --git a/src/modules/mindset/components/LandingPage/utils/index.tsx b/src/modules/mindset/components/LandingPage/utils/index.tsx new file mode 100644 index 000000000..2c5b80d9a --- /dev/null +++ b/src/modules/mindset/components/LandingPage/utils/index.tsx @@ -0,0 +1,47 @@ +const protocol = /^(https?:\/\/)/ +const subDomain = /(www\.)?/ +const rootDomain = /[\w-]+(\.[\w-]+)*/ +const topLevelDomains = /(?:\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61})[a-zA-Z0-9](?:\.[a-zA-Z]{2,})/ +const path = /(\/[^\s?]*)?/ +const query = /(\?[^\s]*)?/ + +const youtubeRegex = /(https?:\/\/)?(www\.)?youtube\.com\/watch\?v=([A-Za-z0-9_-]+)/ +const youtubeLiveRegex = /(https?:\/\/)?(www\.)?youtube\.com\/live\/([A-Za-z0-9_-]+)/ +const youtubeShortRegex = /(https?:\/\/)?(www\.)?youtu\.be\/([A-Za-z0-9_-]+)/ +const mp3Regex = /(https?:\/\/)?([A-Za-z0-9_-]+)\.mp3/ + +const urlRegex = new RegExp( + `${protocol.source}${subDomain.source}${rootDomain.source}${topLevelDomains.source}?${path.source}${query.source}$`, + 'i', +) + +export const validateUrl = (input: string): boolean => { + try { + const match = input?.match(urlRegex) + + if (!match) { + return false + } + + const url = new URL(input) + const domain = url.hostname + + if (domain?.startsWith('www.')) { + return (domain?.match(/\./g) || []).length >= 2 + } + + return (domain?.match(/\./g) || []).length >= 1 + } catch { + return false + } +} + +export const isValidMediaUrl = (url: string): boolean => { + if (!validateUrl(url)) { + return false + } + + const mediaPatterns = [youtubeRegex, youtubeLiveRegex, youtubeShortRegex, mp3Regex] + + return mediaPatterns.some((pattern) => pattern.test(url)) +} diff --git a/src/modules/mindset/components/header/index.tsx b/src/modules/mindset/components/header/index.tsx index 5feb22db4..5b633e8f4 100644 --- a/src/modules/mindset/components/header/index.tsx +++ b/src/modules/mindset/components/header/index.tsx @@ -1,8 +1,9 @@ import styled from 'styled-components' import { Flex } from '~/components/common/Flex' import { Text } from '~/components/common/Text' +import ClearIcon from '~/components/Icons/ClearIcon' import { colors } from '~/utils/colors' -import { Logo } from './Icon/Logo' +import { Logo } from '../Icon/Logo' export const Header = () => ( @@ -12,6 +13,9 @@ export const Header = () => ( Graph Mindset + + + ) @@ -24,6 +28,8 @@ const Head = styled(Flex).attrs({ height: 64px; padding: 20px 23px; gap: 0px; + z-index: 50; + position: relative; ` const LogoButton = styled(Flex)` @@ -57,3 +63,30 @@ const StyledText = styled(Text)` margin-left: 16px; white-space: nowrap; ` + +const CloseButton = styled.div` + position: absolute; + right: 16px; + top: 40%; + transform: translateY(-50%); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border-radius: 50%; + background: ${colors.BUTTON1}; + transition: background 0.2s; + + &:hover { + background: ${colors.BUTTON1_PRESS}; + } + + svg { + fill: none; + color: ${colors.white} !important; + width: 16px; + height: 16px; + } +` diff --git a/src/modules/mindset/index.tsx b/src/modules/mindset/index.tsx index ead6335a5..d299fd20e 100644 --- a/src/modules/mindset/index.tsx +++ b/src/modules/mindset/index.tsx @@ -1,10 +1,12 @@ import { Flex } from '~/components/common/Flex' import { Header } from './components/header' +import { LandingPage } from './components/LandingPage' import { SideBar } from './components/sidebar' export const MindSet = () => (
+ ) diff --git a/src/utils/colors/index.tsx b/src/utils/colors/index.tsx index 47f7ce070..38cbea411 100644 --- a/src/utils/colors/index.tsx +++ b/src/utils/colors/index.tsx @@ -108,6 +108,9 @@ export const colors = { createTestButton: 'rgb(178, 255, 102)', MESSAGE_BG: 'rgba(22, 22, 29, 0.89)', MESSAGE_BG_HOVER: 'rgba(35, 37, 47, 0.3)', + DIVIDER_4: 'rgba(46, 55, 67, 1)', + INPUT_BG: 'rgba(255, 255, 255, 0.05)', + INPUT_PLACEHOLDER: 'rgba(255, 255, 255, 0.5)', } as const export type ColorName = keyof typeof colors From 7822f0f5fe9cf5f07308a1decc98fc60157f2c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Sun, 24 Nov 2024 02:34:10 +0300 Subject: [PATCH 12/17] feat: add 3d mindset --- .../Universe/Graph/Cubes/Text/index.tsx | 76 ++++++--------- src/components/Universe/Graph/Cubes/index.tsx | 8 +- .../mindset/components/MediaPlayer/index.tsx | 58 +++++++++++- .../ProgressBar/Marker/index.tsx | 54 +++++++++++ .../PlayerContols/ProgressBar/index.tsx | 64 +++++++++++++ .../components/PlayerContols/index.tsx | 83 +++++++++++++++++ .../mindset/components/Scene/index.tsx | 2 +- .../components/sidebar/Transcript/index.tsx | 40 ++++++++ .../mindset/components/sidebar/index.tsx | 16 ++-- src/modules/mindset/data/index.tsx | 53 ++++++----- src/modules/mindset/data/transcript/index.tsx | 76 +++++++++++++++ src/modules/mindset/index.tsx | 93 +++++++++++++++++-- src/stores/useDataStore/index.ts | 35 ++++--- src/stores/useGraphStore/index.ts | 7 ++ src/types/index.ts | 4 +- 15 files changed, 548 insertions(+), 121 deletions(-) create mode 100644 src/modules/mindset/components/PlayerContols/ProgressBar/Marker/index.tsx create mode 100644 src/modules/mindset/components/PlayerContols/ProgressBar/index.tsx create mode 100644 src/modules/mindset/components/PlayerContols/index.tsx create mode 100644 src/modules/mindset/components/sidebar/Transcript/index.tsx create mode 100644 src/modules/mindset/data/transcript/index.tsx diff --git a/src/components/Universe/Graph/Cubes/Text/index.tsx b/src/components/Universe/Graph/Cubes/Text/index.tsx index e257a4373..9483e09ac 100644 --- a/src/components/Universe/Graph/Cubes/Text/index.tsx +++ b/src/components/Universe/Graph/Cubes/Text/index.tsx @@ -1,10 +1,10 @@ import { Billboard, Plane, Svg, Text } from '@react-three/drei' import { useFrame } from '@react-three/fiber' -import { memo, useMemo, useRef } from 'react' +import { memo, useRef } from 'react' import { Mesh, MeshBasicMaterial, Vector3 } from 'three' import { Icons } from '~/components/Icons' import { useNodeTypes } from '~/stores/useDataStore' -import { useGraphStore, useSelectedNode, useSelectedNodeRelativeIds } from '~/stores/useGraphStore' +import { useGraphStore } from '~/stores/useGraphStore' import { useSchemaStore } from '~/stores/useSchemaStore' import { NodeExtended } from '~/types' import { colors } from '~/utils/colors' @@ -46,7 +46,6 @@ const COLORS_MAP = [ type Props = { node: NodeExtended hide?: boolean - isHovered: boolean ignoreDistance: boolean } @@ -68,23 +67,22 @@ function splitStringIntoThreeParts(text: string): string { return `${firstPart}\n${secondPart}\n${thirdPart}` } -export const TextNode = memo(({ node, hide, isHovered, ignoreDistance }: Props) => { +export const TextNode = memo(({ node, hide, ignoreDistance }: Props) => { const svgRef = useRef(null) const ringRef = useRef(null) const circleRef = useRef(null) - const selectedNode = useSelectedNode() + + console.log('rerender') const nodePositionRef = useRef(new Vector3()) const { texture } = useTexture(node.properties?.image_url || '') - const selectedNodeRelativeIds = useSelectedNodeRelativeIds() - const isRelative = selectedNodeRelativeIds.includes(node?.ref_id || '') - const isSelected = !!selectedNode && selectedNode?.ref_id === node.ref_id - const showSelectionGraph = useGraphStore((s) => s.showSelectionGraph) const { normalizedSchemasByType } = useSchemaStore((s) => s) useFrame(({ camera, clock }) => { + const { selectedNode, hoveredNode, activeEdge } = useGraphStore.getState() + const checkDistance = () => { const nodePosition = nodePositionRef.current.setFromMatrixPosition(ringRef.current!.matrixWorld) @@ -95,55 +93,36 @@ export const TextNode = memo(({ node, hide, isHovered, ignoreDistance }: Props) // Set visibility based on distance } - if (isHovered) { + const isActive = + node.ref_id === selectedNode?.ref_id || + node.ref_id === hoveredNode?.ref_id || + activeEdge?.target === node.ref_id || + activeEdge?.source === node.ref_id + + if (isActive) { if (ringRef.current) { ringRef.current.visible = true } - const scale = 1 + 0.2 * Math.sin(clock.getElapsedTime() * 2) // Adjust frequency and amplitude + const scale = 3 + 0.2 * Math.sin(clock.getElapsedTime() * 2) // Adjust frequency and amplitude if (circleRef.current) { + circleRef.current.visible = true circleRef.current.scale.set(scale, scale, scale) } return } + if (circleRef.current) { + circleRef.current.visible = false + } + checkDistance() }) const nodeTypes = useNodeTypes() - const textScale = useMemo(() => { - if (!node.name) { - return 0 - } - - let scale = (node.edge_count || 1) * 20 - - if (showSelectionGraph && isSelected) { - scale = 40 - } else if (!isSelected && isRelative) { - scale = 0 - } - - const nodeScale = scale / Math.sqrt(node.name.length) - - return Math.min(Math.max(nodeScale, 20), 30) - }, [node.edge_count, node.name, isSelected, isRelative, showSelectionGraph]) - - const fillOpacity = useMemo(() => { - if (selectedNode && !isSelected) { - return 0.2 - } - - if (!isHovered) { - return 0.2 - } - - return 1 - }, [isSelected, selectedNode, isHovered]) - const primaryColor = normalizedSchemasByType[node.node_type]?.primary_color const primaryIcon = normalizedSchemasByType[node.node_type]?.icon @@ -161,12 +140,11 @@ export const TextNode = memo(({ node, hide, isHovered, ignoreDistance }: Props) return ( - {isHovered ? ( - - - - - ) : null} + + + + + {node.properties?.image_url && node.node_type === 'Person' && texture ? ( diff --git a/src/components/Universe/Graph/Cubes/index.tsx b/src/components/Universe/Graph/Cubes/index.tsx index ac96d9c86..c7517eccb 100644 --- a/src/components/Universe/Graph/Cubes/index.tsx +++ b/src/components/Universe/Graph/Cubes/index.tsx @@ -117,13 +117,7 @@ export const Cubes = memo(() => { - + ) })} diff --git a/src/modules/mindset/components/MediaPlayer/index.tsx b/src/modules/mindset/components/MediaPlayer/index.tsx index ada20d42c..cc9da5bcf 100644 --- a/src/modules/mindset/components/MediaPlayer/index.tsx +++ b/src/modules/mindset/components/MediaPlayer/index.tsx @@ -1,4 +1,4 @@ -import { memo, useEffect, useRef, useState } from 'react' +import { memo, useEffect, useMemo, useRef, useState } from 'react' import ReactPlayer from 'react-player' import styled from 'styled-components' @@ -7,7 +7,34 @@ import { Flex } from '~/components/common/Flex' import { usePlayerStore } from '~/stores/usePlayerStore' import { colors, videoTimeToSeconds } from '~/utils' -import { useSelectedNode } from '~/stores/useGraphStore' +import { useDataStore } from '~/stores/useDataStore' +import { useGraphStore, useSelectedNode } from '~/stores/useGraphStore' +import { Link } from '~/types' + +const findCurrentEdge = (sortedEdges: Link[], playerProgress: number): Link | null => { + // Sort edges by mentionedStart (preprocessing step) + + let low = 0 + let high = sortedEdges.length - 1 + + while (low <= high) { + const mid = Math.floor((low + high) / 2) + const edge = sortedEdges[mid] + const { mentionedStart, mentionedEnd } = edge.properties as { mentionedStart: number; mentionedEnd: number } + + if (playerProgress >= mentionedStart && playerProgress <= mentionedEnd) { + return edge // Found the corresponding edge + } + + if (playerProgress < mentionedStart) { + high = mid - 1 // Search in the left half + } else { + low = mid + 1 // Search in the right half + } + } + + return null // No matching edge found +} type FullScreenProps = { isFullScreen: boolean @@ -24,9 +51,12 @@ const MediaPlayerComponent = () => { const [NodeStartTime, setNodeStartTime] = useState('') const [hasSeekedToStart, setHasSeekedToStart] = useState(false) const selectedNode = useSelectedNode() + const { setActiveEdge } = useGraphStore((s) => s) + + const { dataInitial } = useDataStore((s) => s) useEffect(() => { - const timestamp = '00:02:00-00:12:00' + const timestamp = '00:00:00-00:12:00' const startTime = timestamp?.split('-')[0] as string @@ -47,7 +77,7 @@ const MediaPlayerComponent = () => { setIsSeeking, } = usePlayerStore((s) => s) - const mediaUrl = 'https://www.youtube.com/watch?v=o8Y0E5sPHr4' + const mediaUrl = 'https://www.youtube.com/watch?v=BL5vUVQvmX4' useEffect(() => () => resetPlayer(), [resetPlayer]) @@ -94,11 +124,29 @@ const MediaPlayerComponent = () => { setStatus('error') } + const edges = useMemo(() => { + const edgesFiltered = dataInitial?.links.filter((link) => link?.properties?.mentionedStart) || [] + + const sortedEdges = edgesFiltered + .slice() + .sort((a, b) => (a?.properties?.mentionedStart as number) - (b?.properties?.mentionedStart as number)) + + return sortedEdges + }, [dataInitial]) + const handleProgress = (progress: { playedSeconds: number }) => { if (!isSeeking) { const currentTime = progress.playedSeconds setPlayingTime(currentTime) + + const edge = findCurrentEdge(edges, currentTime) + + if (edge) { + setActiveEdge(edge) + } + + // find playing link and set it to state } } @@ -180,7 +228,7 @@ const MediaPlayerComponent = () => { setStatus('buffering')} onBufferEnd={() => setStatus('ready')} diff --git a/src/modules/mindset/components/PlayerContols/ProgressBar/Marker/index.tsx b/src/modules/mindset/components/PlayerContols/ProgressBar/Marker/index.tsx new file mode 100644 index 000000000..0fd63daed --- /dev/null +++ b/src/modules/mindset/components/PlayerContols/ProgressBar/Marker/index.tsx @@ -0,0 +1,54 @@ +import styled from 'styled-components' +import { Flex } from '~/components/common/Flex' +import { useSchemaStore } from '~/stores/useSchemaStore' +import { colors } from '~/utils/colors' + +type Props = { + type: string +} + +type BadgeProps = { + iconStart: string + color: string + label: string +} + +export const Marker = ({ type }: Props) => { + const [normalizedSchemasByType] = useSchemaStore((s) => [s.normalizedSchemasByType]) + + const primaryColor = normalizedSchemasByType[type]?.primary_color + const primaryIcon = normalizedSchemasByType[type]?.icon + + const icon = primaryIcon ? `svg-icons/${primaryIcon}.svg` : null + + const badgeProps: Omit = { + iconStart: icon ?? 'thing_badge.svg', + color: primaryColor ?? colors.THING, + } + + return +} + +const Badge = ({ iconStart, color, label }: BadgeProps) => ( + + {label} + +) + +const EpisodeWrapper = styled(Flex).attrs({ + direction: 'row', +})<{ color: string }>` + cursor: pointer; + background: ${({ color }) => color}; + border-radius: 3px; + overflow: hidden; + justify-content: center; + align-items: center; + padding: 0 4px; + + .badge__img { + width: 10px; + height: 10px; + object-fit: contain; + } +` diff --git a/src/modules/mindset/components/PlayerContols/ProgressBar/index.tsx b/src/modules/mindset/components/PlayerContols/ProgressBar/index.tsx new file mode 100644 index 000000000..bddec54f4 --- /dev/null +++ b/src/modules/mindset/components/PlayerContols/ProgressBar/index.tsx @@ -0,0 +1,64 @@ +import { LinearProgress } from '@mui/material' +import styled from 'styled-components' +import { Flex } from '~/components/common/Flex' +import { Tooltip } from '~/components/common/ToolTip' +import { nodesWithTimestamp } from '~/modules/mindset/data' +import { colors } from '~/utils' +import { Marker } from './Marker' + +type Props = { + duration: number + progress: number +} + +export const ProgressBar = ({ duration, progress }: Props) => { + console.log('s') + + return ( + + + {nodesWithTimestamp.map((node) => { + const position = ((node?.mentionedStart || 0) / duration) * 100 + const type = node?.node_type || '' + + return ( + + + + + + ) + })} + + ) +} + +const Progress = styled(LinearProgress)` + && { + height: 2px; + background-color: ${colors.white}; + color: blue; + flex-grow: 1; + + .MuiLinearProgress-bar { + background: ${colors.GRAY6}; + } + } +` + +const ProgressWrapper = styled(Flex)` + position: relative; + flex: 1 1 100%; +` + +const MarkerWrapper = styled.div` + position: absolute; + top: -4px; /* Adjust as needed to center above the progress bar */ + width: 8px; + height: 8px; + background-color: ${colors.white}; + border-radius: 50%; + transform: translateX(-50%); /* Center the marker horizontally */ + transform: translateX(-50%) translateY(-50%); + top: 50%; +` diff --git a/src/modules/mindset/components/PlayerContols/index.tsx b/src/modules/mindset/components/PlayerContols/index.tsx new file mode 100644 index 000000000..c2456ea27 --- /dev/null +++ b/src/modules/mindset/components/PlayerContols/index.tsx @@ -0,0 +1,83 @@ +import { IconButton } from '@mui/material' +import styled from 'styled-components' +import ChevronLeftIcon from '~/components/Icons/ChevronLeftIcon' +import ChevronRightIcon from '~/components/Icons/ChevronRightIcon.js' +import PauseIcon from '~/components/Icons/PauseIcon' +import PlayIcon from '~/components/Icons/PlayIcon' +import { Flex } from '~/components/common/Flex' +import { usePlayerStore } from '~/stores/usePlayerStore' +import { videoTimeToSeconds } from '~/utils' +import { colors } from '~/utils/colors' +import { ProgressBar } from './ProgressBar' + +export const PlayerControl = () => { + const { isPlaying, setIsPlaying, playingTime, playingNode, duration } = usePlayerStore((s) => s) + + const [start, end] = playingNode?.properties?.timestamp + ? (playingNode.properties.timestamp as string).split('-').map((time) => videoTimeToSeconds(time)) + : [0, duration] + + const startTime = ((playingTime - start) / (end - start)) * 100 + + console.log(startTime) + + const showPlayer = playingNode + + return showPlayer ? ( + + + + { + setIsPlaying(!isPlaying) + e.stopPropagation() + }} + size="small" + > + {isPlaying ? : } + + + + + + ) : null +} + +const Wrapper = styled(Flex).attrs({ + direction: 'row', + align: 'center', + justify: 'space-between', +})` + padding: 20px; + margin: 20px; + background: ${colors.BG2}; + height: 96px; + border-radius: 8px; + box-sizing: border-box; +` + +const Controls = styled(Flex).attrs({ + direction: 'row', + align: 'center', + justify: 'flex-start', +})` + width: 142px; + height: 54px; + background: ${colors.BG1}; + border-radius: 40px; + margin-right: 54px; + color: ${colors.white}; + font-size: 20px; + padding: 12px; + justify-content: space-between; + box-sizing: border-box; +` + +const Action = styled(IconButton)` + && { + font-size: 36px; + padding: 2px; + overflow: hidden; + } +` diff --git a/src/modules/mindset/components/Scene/index.tsx b/src/modules/mindset/components/Scene/index.tsx index 6a4603881..fad461158 100644 --- a/src/modules/mindset/components/Scene/index.tsx +++ b/src/modules/mindset/components/Scene/index.tsx @@ -10,7 +10,7 @@ export const Scene = () => { } return ( -
+
{/* Slider for controlling camera X-axis */}
diff --git a/src/modules/mindset/components/sidebar/Transcript/index.tsx b/src/modules/mindset/components/sidebar/Transcript/index.tsx new file mode 100644 index 000000000..3f19fe5c7 --- /dev/null +++ b/src/modules/mindset/components/sidebar/Transcript/index.tsx @@ -0,0 +1,40 @@ +import styled from 'styled-components' +import { Flex } from '~/components/common/Flex' +import { transcript } from '~/modules/mindset/data/transcript' +import { usePlayerStore } from '~/stores/usePlayerStore' +import { colors } from '~/utils' + +export const Transcript = () => { + const data = transcript + + const { playingTime } = usePlayerStore((s) => s) + + return ( + + Transcript ({playingTime}) + + {data.map((tr) => ( + {tr.text} + ))} + + + ) +} + +const Wrapper = styled(Flex)` + .heading { + font-weight: 700; + font-size: 12px; + } + color: ${colors.white}; + background: ${colors.BG1}; + border-radius: 8px; + padding: 24px; + overflow: scroll; + flex: 1 1 100%; +` + +const TranscriptWrapper = styled(Flex)` + flex-wrap: wrap; + flex: 1 1 100%; +` diff --git a/src/modules/mindset/components/sidebar/index.tsx b/src/modules/mindset/components/sidebar/index.tsx index 45911c5c3..ddcbc7bc9 100644 --- a/src/modules/mindset/components/sidebar/index.tsx +++ b/src/modules/mindset/components/sidebar/index.tsx @@ -1,29 +1,28 @@ import styled from 'styled-components' -import { Flex } from '~/components/common/Flex' -import { colors } from '~/utils' import { MENU_WIDTH } from '~/components/App/SideBar' +import { Flex } from '~/components/common/Flex' import { Text } from '~/components/common/Text' import { MediaPlayer } from '~/modules/mindset/components/MediaPlayer' +import { Transcript } from './Transcript' export const SideBar = () => ( - - What is the Deep State Party and what are their goals? - John Mearsheimer and Jeffrey Sachs | All-In Summit 2024 + + What is the Deep State Party and what are their goals? + John Mearsheimer and Jeffrey Sachs | All-In Summit 2024 + ) const Wrapper = styled(Flex)(({ theme }) => ({ position: 'relative', display: 'flex', - alignItems: 'flex-start', padding: '20px', background: 'transparent', - height: '100vh', width: '100%', - zIndex: 30, + [theme.breakpoints.up('sm')]: { width: MENU_WIDTH, }, @@ -49,7 +48,6 @@ const EpisodeTitle = styled(Text)` ` const MediaWrapper = styled(Flex)(({ theme }) => ({ - background: colors.BG1, width: '100%', margin: '16px auto', zIndex: 29, diff --git a/src/modules/mindset/data/index.tsx b/src/modules/mindset/data/index.tsx index 5b5db56e9..8f6c7adc7 100644 --- a/src/modules/mindset/data/index.tsx +++ b/src/modules/mindset/data/index.tsx @@ -3,21 +3,21 @@ import { Link } from '~/types' // const transcript = 'Entrepreneurship is being revolutionized by Blockchain and AI, as they open new frontiers for innovation and secure digital solutions.' export const nodes = [ - { ref_id: '1', node_type: 'Clip', name: 'Podcast' }, - { ref_id: '2', node_type: 'Topic', name: 'Bitcoin' }, - { ref_id: '3', node_type: 'Topic', name: 'Blockchain' }, - { ref_id: '4', node_type: 'Topic', name: 'Hard Money' }, - { ref_id: '5', node_type: 'Topic', name: 'Digital Currency' }, - { ref_id: '6', node_type: 'Topic', name: 'Government Control' }, - { ref_id: '7', node_type: 'Topic', name: 'Inflation' }, - { ref_id: '8', node_type: 'Topic', name: 'Public Network' }, - { ref_id: '9', node_type: 'Topic', name: 'Energy Consumption' }, - { ref_id: '10', node_type: 'Topic', name: 'Immutability' }, - { ref_id: '11', node_type: 'Topic', name: 'Scarcity' }, - { ref_id: '12', node_type: 'Topic', name: 'Decentralization' }, - { ref_id: '13', node_type: 'Topic', name: 'Investment Risks' }, - { ref_id: '14', node_type: 'Topic', name: 'Adoption' }, - { ref_id: '15', node_type: 'Person', name: 'Satoshi Nakamoto' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '1', node_type: 'Clip', name: 'Podcast' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '2', node_type: 'Topic', name: 'Bitcoin' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '3', node_type: 'Topic', name: 'Blockchain' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '4', node_type: 'Topic', name: 'Hard Money' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '5', node_type: 'Topic', name: 'Digital Currency' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '6', node_type: 'Topic', name: 'Government Control' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '7', node_type: 'Topic', name: 'Inflation' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '8', node_type: 'Topic', name: 'Public Network' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '9', node_type: 'Topic', name: 'Energy Consumption' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '10', node_type: 'Topic', name: 'Immutability' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '11', node_type: 'Topic', name: 'Scarcity' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '12', node_type: 'Topic', name: 'Decentralization' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '13', node_type: 'Topic', name: 'Investment Risks' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '14', node_type: 'Topic', name: 'Adoption' }, + { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '15', node_type: 'Person', name: 'Satoshi Nakamoto' }, ] export const edges: Link[] = [ @@ -53,7 +53,7 @@ export const edges: Link[] = [ ref_id: '4', edge_type: 'Mentioned', target: '1', // Clip - source: '11', // Scarcity + source: '5', // Scarcity properties: { mentionedStart: 19.439, mentionedEnd: 25.619 }, }, @@ -110,21 +110,24 @@ export const edges: Link[] = [ source: '14', // Adoption properties: { mentionedStart: 122.44, mentionedEnd: 130.48 }, }, - - // Satoshi Nakamoto mentioned as the creator - { - ref_id: '11', - edge_type: 'Mentioned', - target: '1', // Clip - source: '15', // Satoshi Nakamoto - properties: { mentionedStart: 7.68, mentionedEnd: 19.619 }, - }, ] export const edgesMention: Array<{ source: string; mentionedStart: number }> = edges .filter((e) => e.properties?.mentionedStart) .map((edge) => ({ source: edge.source, mentionedStart: edge.properties?.mentionedStart as number })) +export const nodesWithTimestamp = nodes + .filter((node) => edgesMention.some((ed) => ed.source === node.ref_id)) + .map((node) => { + const edge = edgesMention.find((ed) => node.ref_id === ed.source) + + if (edge) { + return { ...node, mentionedStart: edge.mentionedStart } + } + + return null + }) + export const data = { nodes, edges, diff --git a/src/modules/mindset/data/transcript/index.tsx b/src/modules/mindset/data/transcript/index.tsx new file mode 100644 index 000000000..0e097e8c0 --- /dev/null +++ b/src/modules/mindset/data/transcript/index.tsx @@ -0,0 +1,76 @@ +export const transcript = [ + { text: 'when money is easy to make Society', timestamp: 179 }, + { text: 'begins to break', timestamp: 2760 }, + { text: 'well one solution is Bitcoin', timestamp: 7680 }, + { text: 'back in 2008 I became fed up with', timestamp: 11219 }, + { text: 'government money with the corruption the', timestamp: 13620 }, + { text: 'manipulation so I created a digital', timestamp: 15599 }, + { text: 'currency digital money that can be sent', timestamp: 17760 }, + { text: 'directly from one person to another', timestamp: 19619 }, + { text: 'without any bank or government involved', timestamp: 21119 }, + { text: "and the best part it's hard money", timestamp: 23400 }, + { text: 'Bitcoin is very hard to make more of', timestamp: 28019 }, + { text: 'each new coin gets added to the supply', timestamp: 29939 }, + { text: 'only after a computer works very hard to', timestamp: 31800 }, + { text: "solve a math problem where there's no", timestamp: 34200 }, + { text: 'shortcut and solving it costs a lot of', timestamp: 35880 }, + { text: 'energy and time', timestamp: 38040 }, + { text: "okay but if it's on a computer can't I", timestamp: 40559 }, + { text: 'just copy and paste', timestamp: 43020 }, + { text: 'not with Bitcoin you', timestamp: 46559 }, + { text: 'have public record of every Bitcoin ever', timestamp: 48600 }, + { text: "created it's called the blockchain it's", timestamp: 50820 }, + { text: 'like a puzzle and each Bitcoin has its', timestamp: 54300 }, + { text: 'own unique shape and because everyone', timestamp: 56520 }, + { text: 'has a copy of the public record if', timestamp: 58260 }, + { text: 'someone tries to fake a Bitcoin', timestamp: 60480 }, + { text: "it won't fit the puzzle and will be", timestamp: 65099 }, + { text: 'rejected by the network before anyone', timestamp: 67140 }, + { text: 'can use it', timestamp: 69060 }, + { text: "that's why Bitcoin is so safe from", timestamp: 70860 }, + { text: 'criminals and the government you said', timestamp: 72960 }, + { text: 'criminals twice', timestamp: 75420 }, + { text: 'dollars which can be printed endlessly', timestamp: 77439 }, + { text: 'there will only ever be 21 million', timestamp: 79720 }, + { text: "Bitcoin it's almost impossible to", timestamp: 81720 }, + { text: 'inflate the only way to get it is to', timestamp: 83759 }, + { text: 'earn it or buy it from someone who has', timestamp: 85619 }, + { text: 'no offense but if you', timestamp: 89820 }, + { text: "if you coin couldn't you manipulate it", timestamp: 91320 }, + { text: "just like the government I don't control", timestamp: 92880 }, + { text: "it no one person does it's controlled by", timestamp: 94619 }, + { text: 'a public network of Bitcoin users that', timestamp: 97619 }, + { text: 'anyone can join so if someone wanted to', timestamp: 99720 }, + { text: "change something in bitcoin's code they", timestamp: 101939 }, + { text: 'would need to get the majority of the', timestamp: 103920 }, + { text: 'millions of Bitcoin users to agree to it', timestamp: 104659 }, + { text: "all the change doesn't happen and when", timestamp: 104752 }, + { text: 'has the majority of us agreed on', timestamp: 104860 }, + { text: 'anything', timestamp: 105360 }, + { text: "touche wow I didn't know made up money", timestamp: 105720 }, + { text: 'could make so much sense today millions', timestamp: 105960 }, + { text: 'of people send Bitcoin instantly and', timestamp: 106180 }, + { text: 'cheaply to each other around the world', timestamp: 106399 }, + { text: 'without any bank or government involved', timestamp: 106620 }, + { text: 'and because Bitcoin is hard money you', timestamp: 106859 }, + { text: "can't just print more of the more people", timestamp: 107360 }, + { text: 'who use it the more valuable it becomes', timestamp: 107960 }, + { text: "Emily if we take Lyle's Bitcoin we could", timestamp: 108640 }, + { text: 'be rich I could pay for college entirely', timestamp: 109040 }, + { text: 'you could Outsource your movie and pay', timestamp: 109440 }, + { text: 'to have your name in the credits slow', timestamp: 109840 }, + { text: "down I'm not saying that Bitcoin will", timestamp: 110240 }, + { text: 'make you rich quick or even at all right', timestamp: 110640 }, + { text: 'now only about one percent of the world', timestamp: 110780 }, + { text: "owns Bitcoin and because it's still", timestamp: 111280 }, + { text: 'being adopted some days it goes up and', timestamp: 111580 }, + { text: 'even down also Investments or', timestamp: 111820 }, + { text: 'potentially lucrative can be risky', timestamp: 112380 }, + { text: 'oh I think I get it good money should be', timestamp: 112879 }, + { text: 'easy to use but hard to create and', timestamp: 113459 }, + { text: "because Bitcoin is digital it's fast and", timestamp: 113800 }, + { text: 'cheap to use and the blockchain makes it', timestamp: 113900 }, + { text: 'hard to inflate thanks Mr Satoshi', timestamp: 114379 }, + { text: 'what Mr Satoshi', timestamp: 114819 }, + { text: 'gone or was he Aaron even here', timestamp: 115340 }, +] diff --git a/src/modules/mindset/index.tsx b/src/modules/mindset/index.tsx index 48a6d660c..4dc720c31 100644 --- a/src/modules/mindset/index.tsx +++ b/src/modules/mindset/index.tsx @@ -1,12 +1,85 @@ +import { useCallback, useEffect, useRef, useState } from 'react' import { Flex } from '~/components/common/Flex' -import { Header } from './components/header' +import { Universe } from '~/components/Universe' +import { useDataStore } from '~/stores/useDataStore' +import { usePlayerStore } from '~/stores/usePlayerStore' +import { FetchDataResponse } from '~/types' +import { Header } from './components/Header' +import { PlayerControl } from './components/PlayerContols' import { Scene } from './components/Scene' -import { SideBar } from './components/sidebar' - -export const MindSet = () => ( - -
- - - -) +import { SideBar } from './components/Sidebar' +import { edges, nodes } from './data' + +export const MindSet = () => { + const { addNewNode, isFetching } = useDataStore((s) => s) + const [showTwoD, setShowTwoD] = useState(false) + + const queueRef = useRef(null) + const timerRef = useRef(null) + + const { setPlayingNode } = usePlayerStore((s) => s) + + const handleNewNodeCreated = useCallback( + (data: FetchDataResponse) => { + if (isFetching) { + return + } + + if (!queueRef.current) { + queueRef.current = { nodes: [], edges: [] } + } + + if (data.edges) { + queueRef.current.edges.push(...data.edges) + } + + if (data.nodes) { + queueRef.current.nodes.push(...data.nodes) + } + + if (timerRef.current) { + clearTimeout(timerRef.current) + } + + timerRef.current = setTimeout(() => { + // Combine all queued data into a single update + + if (queueRef.current) { + const { nodes: newNodes, edges: newEdges } = queueRef.current + const batchedData = { nodes: newNodes, edges: newEdges } + + queueRef.current = { nodes: [], edges: [] } + addNewNode(batchedData) + } + }, 3000) // Adjust delay as necessary + }, + [addNewNode, isFetching], + ) + + useEffect(() => { + handleNewNodeCreated({ + nodes, + edges, + }) + + setPlayingNode(nodes.find((i) => i.node_type === 'Clip') || null) + }, [handleNewNodeCreated, setPlayingNode]) + + return ( + + + setShowTwoD(!showTwoD)}> +
+ + + + + + {showTwoD ? : } + + + + + + ) +} diff --git a/src/stores/useDataStore/index.ts b/src/stores/useDataStore/index.ts index 2ccb256a4..dd9a7decf 100644 --- a/src/stores/useDataStore/index.ts +++ b/src/stores/useDataStore/index.ts @@ -1,15 +1,13 @@ -// @ts-nocheck -// @ts-ignore - import { isEqual } from 'lodash' import { create } from 'zustand' import { devtools } from 'zustand/middleware' import { fetchGraphData } from '~/network/fetchGraphData' -import { FilterParams, GraphData, Link, NodeExtended, NodeType, Sources, Trending, TStats } from '~/types' +import { FetchDataResponse, FilterParams, Link, NodeExtended, NodeType, Sources, Trending, TStats } from '~/types' import { useAiSummaryStore } from '../useAiSummaryStore' import { useAppStore } from '../useAppStore' +import { useUserStore } from '../useUserStore' -const deduplicateByRefId = (items) => { +const deduplicateByRefId = (items: Array) => { const uniqueMap = new Map() items.forEach((item) => { @@ -61,7 +59,6 @@ export type DataStore = { runningProjectMessages: string[] setTrendingTopics: (trendingTopics: Trending[]) => void - setDataNew: (data: GraphData) => void resetDataNew: () => void setStats: (stats: TStats) => void setSidebarFilter: (filter: string) => void @@ -114,14 +111,17 @@ const defaultData: Omit< | 'removeNode' | 'setAbortRequests' | 'nextPage' - | 'setDataNew' | 'resetDataNew' | 'setSeedQuestions' + | 'setRunningProjectId' + | 'setRunningProjectMessages' + | 'resetRunningProjectMessages' + | 'abortFetchData' + | 'resetGraph' + | 'resetData' > = { categoryFilter: null, dataInitial: null, - currentPage: 0, - itemsPerPage: 300, runningProjectMessages: [], filters: { skip: 0, @@ -149,6 +149,8 @@ const defaultData: Omit< dataNew: null, seedQuestions: null, runningProjectId: '', + hideNodeDetails: false, + nodeTypes: [], } let abortController: AbortController | null = null @@ -298,13 +300,16 @@ export const useDataStore = create()( } }, resetGraph: () => { + const { setAbortRequests } = get() + const { setBudget } = useUserStore.getState() + set({ filters: defaultData.filters, dataInitial: null, dataNew: null, }) - get().fetchData() + get().fetchData(setBudget, setAbortRequests) }, resetData: () => { @@ -315,17 +320,19 @@ export const useDataStore = create()( }) }, - setPage: (page: number) => set({ currentPage: page }), nextPage: () => { - const { filters, fetchData } = get() + const { filters, fetchData, setAbortRequests } = get() + const { setBudget } = useUserStore.getState() set({ filters: { ...filters, skip: filters.skip + 1 } }) - fetchData() + fetchData(setBudget, setAbortRequests) }, resetDataNew: () => null, setFilters: (filters: Partial) => { + const { setBudget } = useUserStore.getState() + set((state) => ({ filters: { ...state.filters, ...filters, skip: 0 } })) - get().fetchData(get().setBudget, get().setAbortRequests) + get().fetchData(setBudget, get().setAbortRequests) }, setSidebarFilterCounts: (sidebarFilterCounts) => set({ sidebarFilterCounts }), setTrendingTopics: (trendingTopics) => set({ trendingTopics }), diff --git a/src/stores/useGraphStore/index.ts b/src/stores/useGraphStore/index.ts index d2c062aa7..915a94fdd 100644 --- a/src/stores/useGraphStore/index.ts +++ b/src/stores/useGraphStore/index.ts @@ -80,6 +80,7 @@ export type GraphStore = { simulation: ForceSimulation | null simulationHelpers: SimulationHelpers isHovering: boolean + activeEdge: Link | null setDisableCameraRotation: (rotation: boolean) => void setScrollEventsDisabled: (rotation: boolean) => void @@ -88,6 +89,7 @@ export type GraphStore = { setGraphRadius: (graphRadius: number) => void setHoveredNode: (hoveredNode: NodeExtended | null) => void setSelectedNode: (selectedNode: NodeExtended | null) => void + setActiveEdge: (edge: Link | null) => void setCameraFocusTrigger: (_: boolean) => void setNearbyNodeIds: (_: string[]) => void setShowSelectionGraph: (_: boolean) => void @@ -104,6 +106,7 @@ const defaultData: Omit< | 'setDisableCameraRotation' | 'setHoveredNode' | 'setSelectedNode' + | 'setActiveEdge' | 'setCameraFocusTrigger' | 'setGraphRadius' | 'setGraphStyle' @@ -123,6 +126,7 @@ const defaultData: Omit< graphStyle: (localStorage.getItem('graphStyle') as GraphStyle) || 'sphere', hoveredNode: null, selectedNode: null, + activeEdge: null, cameraFocusTrigger: false, nearbyNodeIds: [], showSelectionGraph: false, @@ -144,6 +148,9 @@ export const useGraphStore = create()((set, get) => ({ setHoveredNode: (hoveredNode) => { set({ hoveredNode }) }, + setActiveEdge: (activeEdge) => { + set({ activeEdge }) + }, setSelectedNode: (selectedNode) => { const { selectedNode: stateSelectedNode, simulation } = get() diff --git a/src/types/index.ts b/src/types/index.ts index 481961dcf..57ff75543 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -15,10 +15,12 @@ export type FilterParams = { skip: number limit: number depth: string + includeContent: string sort_by: string top_node_count: string include_properties: string - node_type: string[] | string + node_type: string[] + search_method: string free?: string word?: string // Add other optional filter properties as needed } From dfb3368b46df9bcb7f6854db2b5774d464d4cb97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Mon, 25 Nov 2024 00:30:14 +0300 Subject: [PATCH 13/17] feat: connected landing page with graph --- src/components/AppContainer/index.tsx | 4 +- .../mindset/components/LandingPage/index.tsx | 77 ++++++++- .../ProgressBar => }/Marker/index.tsx | 0 .../mindset/components/MediaPlayer/index.tsx | 22 +-- .../PlayerContols/CanvasProgressbar/index.tsx | 86 ++++++++++ .../PlayerContols/ProgressBar/index.tsx | 13 +- .../components/PlayerContols/index.tsx | 11 +- .../mindset/components/Scene/Board/index.tsx | 8 +- .../mindset/components/sidebar/index.tsx | 25 +-- src/modules/mindset/data/index.tsx | 53 +++--- src/modules/mindset/index.tsx | 153 +++++++++++++----- src/network/fetchGraphData/index.ts | 2 +- src/network/fetchSourcesData/index.ts | 7 + src/stores/useMindsetStore/index.ts | 24 +++ src/types/index.ts | 4 + 15 files changed, 391 insertions(+), 98 deletions(-) rename src/modules/mindset/components/{PlayerContols/ProgressBar => }/Marker/index.tsx (100%) create mode 100644 src/modules/mindset/components/PlayerContols/CanvasProgressbar/index.tsx create mode 100644 src/stores/useMindsetStore/index.ts diff --git a/src/components/AppContainer/index.tsx b/src/components/AppContainer/index.tsx index deba4d948..080fef5da 100644 --- a/src/components/AppContainer/index.tsx +++ b/src/components/AppContainer/index.tsx @@ -11,11 +11,13 @@ export const AppContainer = () => { const App = const MindSet = + const path = window.location?.hostname === 'graphmindset.sphinx.chat' ? '/' : '/mindset' + return ( Loading...
}> - + {App}} path="/" /> {App}} path="/search" /> {App}} path="*" /> diff --git a/src/modules/mindset/components/LandingPage/index.tsx b/src/modules/mindset/components/LandingPage/index.tsx index 8e61f31fb..09ccdd5a5 100644 --- a/src/modules/mindset/components/LandingPage/index.tsx +++ b/src/modules/mindset/components/LandingPage/index.tsx @@ -1,13 +1,49 @@ import { useState } from 'react' +import { FieldValues } from 'react-hook-form' import styled from 'styled-components' import { Flex } from '~/components/common/Flex' +import { NODE_ADD_ERROR } from '~/constants' +import { api } from '~/network/api' +import { useDataStore } from '~/stores/useDataStore' +import { useMindsetStore } from '~/stores/useMindsetStore' +import { SubmitErrRes } from '~/types' import { colors } from '~/utils/colors' import { ChevronRight } from '../Icon/ChevronRight' import { isValidMediaUrl } from './utils' +export type FormData = { + input: string + inputType: string + source: string + longitude: string + latitude: string +} + +const handleSubmitForm = async (data: FieldValues): Promise => { + const endPoint = 'add_node' + + const body: { [index: string]: unknown } = {} + + body.media_url = data.source + body.content_type = 'audio_video' + + const res: SubmitErrRes = await api.post(`/${endPoint}`, JSON.stringify(body)) + + if (res.error) { + const { message } = res.error + + throw new Error(message) + } + + return res +} + export const LandingPage = () => { const [inputValue, setInputValue] = useState('') const [error, setError] = useState(false) + const [requestError, setRequestError] = useState('') + const { setRunningProjectId } = useDataStore((s) => s) + const { setSelectedEpisodeId, setSelectedEpisodeLink } = useMindsetStore((s) => s) const handleInputChange = (e: React.ChangeEvent) => { const { value } = e.target @@ -16,9 +52,45 @@ export const LandingPage = () => { setError(value !== '' && !isValidMediaUrl(value)) } - const handleSubmit = () => { + const handleSubmit = async () => { if (isValidMediaUrl(inputValue)) { - console.log('Valid media URL:', inputValue) + try { + const res = await handleSubmitForm({ source: inputValue }) + + console.log(res) + + if (res.data.project_id) { + setRunningProjectId(res.data.project_id) + } + + if (res.data.ref_id) { + setSelectedEpisodeId(res.data.ref_id) + setSelectedEpisodeLink(inputValue) + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { + console.log(err) + + let errorMessage = NODE_ADD_ERROR + + if (err?.status === 400) { + const res = await err.json() + + console.log(res) + + errorMessage = res.errorCode || res?.status || NODE_ADD_ERROR + + if (res.data.ref_id) { + setSelectedEpisodeId(res.data.ref_id) + setSelectedEpisodeLink(inputValue) + } + } else if (err instanceof Error) { + errorMessage = err.message + } + + setRequestError(String(errorMessage)) + } } } @@ -37,6 +109,7 @@ export const LandingPage = () => { + {requestError &&
{requestError}
} ) } diff --git a/src/modules/mindset/components/PlayerContols/ProgressBar/Marker/index.tsx b/src/modules/mindset/components/Marker/index.tsx similarity index 100% rename from src/modules/mindset/components/PlayerContols/ProgressBar/Marker/index.tsx rename to src/modules/mindset/components/Marker/index.tsx diff --git a/src/modules/mindset/components/MediaPlayer/index.tsx b/src/modules/mindset/components/MediaPlayer/index.tsx index cc9da5bcf..b2c85a582 100644 --- a/src/modules/mindset/components/MediaPlayer/index.tsx +++ b/src/modules/mindset/components/MediaPlayer/index.tsx @@ -12,7 +12,7 @@ import { useGraphStore, useSelectedNode } from '~/stores/useGraphStore' import { Link } from '~/types' const findCurrentEdge = (sortedEdges: Link[], playerProgress: number): Link | null => { - // Sort edges by mentionedStart (preprocessing step) + // Sort edges by start (preprocessing step) let low = 0 let high = sortedEdges.length - 1 @@ -20,13 +20,13 @@ const findCurrentEdge = (sortedEdges: Link[], playerProgress: number): Link | nu while (low <= high) { const mid = Math.floor((low + high) / 2) const edge = sortedEdges[mid] - const { mentionedStart, mentionedEnd } = edge.properties as { mentionedStart: number; mentionedEnd: number } + const { start, end } = edge as { start: number; end: number } - if (playerProgress >= mentionedStart && playerProgress <= mentionedEnd) { + if (playerProgress >= start && playerProgress <= end) { return edge // Found the corresponding edge } - if (playerProgress < mentionedStart) { + if (playerProgress < start) { high = mid - 1 // Search in the left half } else { low = mid + 1 // Search in the right half @@ -40,7 +40,11 @@ type FullScreenProps = { isFullScreen: boolean } -const MediaPlayerComponent = () => { +type Props = { + mediaUrl: string +} + +const MediaPlayerComponent = ({ mediaUrl }: Props) => { const playerRef = useRef(null) const wrapperRef = useRef(null) const [isFocused, setIsFocused] = useState(false) @@ -77,8 +81,6 @@ const MediaPlayerComponent = () => { setIsSeeking, } = usePlayerStore((s) => s) - const mediaUrl = 'https://www.youtube.com/watch?v=BL5vUVQvmX4' - useEffect(() => () => resetPlayer(), [resetPlayer]) useEffect(() => { @@ -125,11 +127,9 @@ const MediaPlayerComponent = () => { } const edges = useMemo(() => { - const edgesFiltered = dataInitial?.links.filter((link) => link?.properties?.mentionedStart) || [] + const edgesFiltered = dataInitial?.links.filter((link) => link?.start) || [] - const sortedEdges = edgesFiltered - .slice() - .sort((a, b) => (a?.properties?.mentionedStart as number) - (b?.properties?.mentionedStart as number)) + const sortedEdges = edgesFiltered.slice().sort((a, b) => (a?.start as number) - (b?.start as number)) return sortedEdges }, [dataInitial]) diff --git a/src/modules/mindset/components/PlayerContols/CanvasProgressbar/index.tsx b/src/modules/mindset/components/PlayerContols/CanvasProgressbar/index.tsx new file mode 100644 index 000000000..5f7185b45 --- /dev/null +++ b/src/modules/mindset/components/PlayerContols/CanvasProgressbar/index.tsx @@ -0,0 +1,86 @@ +import { Html, OrbitControls } from '@react-three/drei' +import { Canvas, useThree } from '@react-three/fiber' +import { useEffect, useState } from 'react' +import { Tooltip } from '~/components/common/ToolTip' +import { nodesWithTimestamp } from '~/modules/mindset/data' +import { Marker } from '../../Marker' + +type Props = { + duration: number + progress: number +} + +const FullWidthZoom = ({ progressBarWidth }: { progressBarWidth: number }) => { + const { size, camera } = useThree() + const [minZoom, setMinZoom] = useState(camera.zoom) + + useEffect(() => { + // Calculate the minimum zoom based on the progress bar width and viewport size + const calculatedZoom = size.width / progressBarWidth + + setMinZoom(calculatedZoom) + camera.zoom = calculatedZoom + camera.updateProjectionMatrix() + }, [size, camera, progressBarWidth]) + + return +} + +export const ProgressBarCanvas = ({ duration, progress }: Props) => { + const progressBarWidth = 100 // Width of the progress bar in world units + + return ( + + + + + {/* Full Width Zoom Calculation */} + + + {/* Progress Bar */} + + {/* Full progress bar width */} + + + + {/* Progress */} + + + + + + {/* Markers */} + {nodesWithTimestamp.map((node) => { + const position = ((node.start || 0) / duration) * progressBarWidth - progressBarWidth / 2 + const type = node?.node_type || '' + + return ( + + {/* Marker size */} + + {/* Tooltip */} + +
+ + + +
+ +
+ ) + })} +
+ ) +} diff --git a/src/modules/mindset/components/PlayerContols/ProgressBar/index.tsx b/src/modules/mindset/components/PlayerContols/ProgressBar/index.tsx index bddec54f4..3d4ef0ab7 100644 --- a/src/modules/mindset/components/PlayerContols/ProgressBar/index.tsx +++ b/src/modules/mindset/components/PlayerContols/ProgressBar/index.tsx @@ -2,28 +2,29 @@ import { LinearProgress } from '@mui/material' import styled from 'styled-components' import { Flex } from '~/components/common/Flex' import { Tooltip } from '~/components/common/ToolTip' -import { nodesWithTimestamp } from '~/modules/mindset/data' import { colors } from '~/utils' -import { Marker } from './Marker' +import { Marker } from '../../Marker' +import { NodeExtended } from '~/types' type Props = { duration: number progress: number + markers: NodeExtended[] } -export const ProgressBar = ({ duration, progress }: Props) => { +export const ProgressBar = ({ duration, progress, markers }: Props) => { console.log('s') return ( - {nodesWithTimestamp.map((node) => { - const position = ((node?.mentionedStart || 0) / duration) * 100 + {markers.map((node) => { + const position = ((node?.start || 0) / duration) * 100 const type = node?.node_type || '' return ( - + diff --git a/src/modules/mindset/components/PlayerContols/index.tsx b/src/modules/mindset/components/PlayerContols/index.tsx index c2456ea27..b7670479a 100644 --- a/src/modules/mindset/components/PlayerContols/index.tsx +++ b/src/modules/mindset/components/PlayerContols/index.tsx @@ -6,11 +6,17 @@ import PauseIcon from '~/components/Icons/PauseIcon' import PlayIcon from '~/components/Icons/PlayIcon' import { Flex } from '~/components/common/Flex' import { usePlayerStore } from '~/stores/usePlayerStore' +import { NodeExtended } from '~/types' import { videoTimeToSeconds } from '~/utils' import { colors } from '~/utils/colors' +import { ProgressBarCanvas } from './CanvasProgressbar' import { ProgressBar } from './ProgressBar' -export const PlayerControl = () => { +type Props = { + markers: NodeExtended[] +} + +export const PlayerControl = ({ markers }: Props) => { const { isPlaying, setIsPlaying, playingTime, playingNode, duration } = usePlayerStore((s) => s) const [start, end] = playingNode?.properties?.timestamp @@ -39,7 +45,8 @@ export const PlayerControl = () => { - + + {false && } ) : null } diff --git a/src/modules/mindset/components/Scene/Board/index.tsx b/src/modules/mindset/components/Scene/Board/index.tsx index ca9eee5db..47e170d05 100644 --- a/src/modules/mindset/components/Scene/Board/index.tsx +++ b/src/modules/mindset/components/Scene/Board/index.tsx @@ -53,10 +53,10 @@ export const Board = ({ w = 1 }: Props) => { const positions = useMemo( () => edges - .filter((edge) => edge?.properties?.mentionedStart && edge?.properties?.mentionedEnd) + .filter((edge) => edge?.properties?.start && edge?.properties?.end) .map((edge) => { - const st: number = (edge?.properties?.mentionedStart || 0) as number - const ed: number = (edge?.properties?.mentionedEnd || 0) as number + const st: number = (edge?.properties?.start || 0) as number + const ed: number = (edge?.properties?.end || 0) as number return { source: edge.source, @@ -97,7 +97,7 @@ export const Board = ({ w = 1 }: Props) => { {edgesMention.map((e) => { const node = nodes.find((i) => i.ref_id === e.source) - const x = (e.mentionedStart / totalDuration) * width + const x = (e.start / totalDuration) * width console.log(x) diff --git a/src/modules/mindset/components/sidebar/index.tsx b/src/modules/mindset/components/sidebar/index.tsx index ddcbc7bc9..d34ba07ad 100644 --- a/src/modules/mindset/components/sidebar/index.tsx +++ b/src/modules/mindset/components/sidebar/index.tsx @@ -3,18 +3,23 @@ import { MENU_WIDTH } from '~/components/App/SideBar' import { Flex } from '~/components/common/Flex' import { Text } from '~/components/common/Text' import { MediaPlayer } from '~/modules/mindset/components/MediaPlayer' +import { useMindsetStore } from '~/stores/useMindsetStore' import { Transcript } from './Transcript' -export const SideBar = () => ( - - - What is the Deep State Party and what are their goals? - John Mearsheimer and Jeffrey Sachs | All-In Summit 2024 - - - - -) +export const SideBar = () => { + const { selectedEpisodeLink, selectedEpisode } = useMindsetStore((s) => s) + + return ( + + + {selectedEpisode?.node_type && {selectedEpisode?.node_type}} + {selectedEpisode?.name && {selectedEpisode?.name}} + {selectedEpisodeLink && } + + + + ) +} const Wrapper = styled(Flex)(({ theme }) => ({ position: 'relative', diff --git a/src/modules/mindset/data/index.tsx b/src/modules/mindset/data/index.tsx index 8f6c7adc7..6ae9d1491 100644 --- a/src/modules/mindset/data/index.tsx +++ b/src/modules/mindset/data/index.tsx @@ -1,8 +1,8 @@ -import { Link } from '~/types' +import { Link, NodeExtended } from '~/types' // const transcript = 'Entrepreneurship is being revolutionized by Blockchain and AI, as they open new frontiers for innovation and secure digital solutions.' -export const nodes = [ +export const nodes: NodeExtended[] = [ { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '1', node_type: 'Clip', name: 'Podcast' }, { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '2', node_type: 'Topic', name: 'Bitcoin' }, { edge_count: 0, x: 0, y: 0, z: 0, label: 'label', ref_id: '3', node_type: 'Topic', name: 'Blockchain' }, @@ -27,7 +27,8 @@ export const edges: Link[] = [ edge_type: 'Mentioned', target: '1', // Clip source: '2', // Bitcoin - properties: { mentionedStart: 7.68, mentionedEnd: 19.619 }, + start: 7.68, + end: 19.619, }, // Bitcoin's hard money nature @@ -36,7 +37,8 @@ export const edges: Link[] = [ edge_type: 'Mentioned', target: '1', // Clip source: '4', // Hard Money - properties: { mentionedStart: 28.019, mentionedEnd: 38.04 }, + start: 28.019, + end: 38.04, }, // Blockchain as a public ledger @@ -45,7 +47,8 @@ export const edges: Link[] = [ edge_type: 'Mentioned', target: '1', // Clip source: '3', // Blockchain - properties: { mentionedStart: 50.82, mentionedEnd: 56.52 }, + start: 50.82, + end: 56.52, }, // Scarcity of Bitcoin @@ -54,7 +57,8 @@ export const edges: Link[] = [ edge_type: 'Mentioned', target: '1', // Clip source: '5', // Scarcity - properties: { mentionedStart: 19.439, mentionedEnd: 25.619 }, + start: 19.439, + end: 25.619, }, // Government control contrasted with Bitcoin @@ -63,7 +67,8 @@ export const edges: Link[] = [ edge_type: 'Mentioned', target: '1', // Clip source: '6', // Government Control - properties: { mentionedStart: 34.619, mentionedEnd: 43.02 }, + start: 34.619, + end: 43.02, }, // Energy consumption in Bitcoin mining @@ -72,7 +77,8 @@ export const edges: Link[] = [ edge_type: 'Mentioned', target: '1', // Clip source: '9', // Energy Consumption - properties: { mentionedStart: 31.8, mentionedEnd: 38.04 }, + start: 31.8, + end: 38.04, }, // Immutability ensured by the blockchain @@ -81,7 +87,8 @@ export const edges: Link[] = [ edge_type: 'Mentioned', target: '1', // Clip source: '10', // Immutability - properties: { mentionedStart: 56.52, mentionedEnd: 60.48 }, + start: 56.52, + end: 60.48, }, // Decentralization of Bitcoin's public network @@ -90,7 +97,8 @@ export const edges: Link[] = [ edge_type: 'Mentioned', target: '1', // Clip source: '12', // Decentralization - properties: { mentionedStart: 90.72, mentionedEnd: 97.52 }, + start: 90.72, + end: 97.52, }, // Bitcoin investment risks @@ -99,7 +107,8 @@ export const edges: Link[] = [ edge_type: 'Mentioned', target: '1', // Clip source: '13', // Investment Risks - properties: { mentionedStart: 126.44, mentionedEnd: 133.48 }, + start: 126.44, + end: 133.48, }, // Bitcoin adoption rates and potential @@ -108,25 +117,23 @@ export const edges: Link[] = [ edge_type: 'Mentioned', target: '1', // Clip source: '14', // Adoption - properties: { mentionedStart: 122.44, mentionedEnd: 130.48 }, + start: 122.44, + end: 130.48, }, ] -export const edgesMention: Array<{ source: string; mentionedStart: number }> = edges - .filter((e) => e.properties?.mentionedStart) - .map((edge) => ({ source: edge.source, mentionedStart: edge.properties?.mentionedStart as number })) +export const edgesMention: Array<{ source: string; start: number }> = edges + .filter((e) => e?.start) + .map((edge) => ({ source: edge.source, start: edge?.start as number })) -export const nodesWithTimestamp = nodes +export const nodesWithTimestamp: NodeExtended[] = (nodes || []) .filter((node) => edgesMention.some((ed) => ed.source === node.ref_id)) .map((node) => { const edge = edgesMention.find((ed) => node.ref_id === ed.source) - if (edge) { - return { ...node, mentionedStart: edge.mentionedStart } - } - - return null + return { ...node, start: edge?.start || 0 } }) + .filter((node) => node) export const data = { nodes, @@ -134,9 +141,9 @@ export const data = { } const timestamps = edges.flatMap((e) => { - const { mentionedStart, mentionedEnd } = e.properties ?? {} + const { start, end } = e.properties ?? {} - return mentionedStart !== undefined && mentionedEnd !== undefined ? [mentionedStart, mentionedEnd] : [0, 0] + return start !== undefined && end !== undefined ? [start, end] : [0, 0] }) export const minTimestamp = Math.min(...(timestamps as number[])) diff --git a/src/modules/mindset/index.tsx b/src/modules/mindset/index.tsx index 23a763d3f..73cef2e97 100644 --- a/src/modules/mindset/index.tsx +++ b/src/modules/mindset/index.tsx @@ -1,40 +1,31 @@ -import { useCallback, useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { Socket } from 'socket.io-client' import { Flex } from '~/components/common/Flex' import { Universe } from '~/components/Universe' +import { useSocket } from '~/hooks/useSockets' import { fetchNodeEdges } from '~/network/fetchGraphData' +import { getNode } from '~/network/fetchSourcesData' import { useDataStore } from '~/stores/useDataStore' +import { useMindsetStore } from '~/stores/useMindsetStore' import { usePlayerStore } from '~/stores/usePlayerStore' -import { FetchDataResponse } from '~/types' +import { FetchDataResponse, NodeExtended } from '~/types' import { Header } from './components/Header' import { LandingPage } from './components/LandingPage' import { PlayerControl } from './components/PlayerContols' import { Scene } from './components/Scene' import { SideBar } from './components/Sidebar' -import { edges, nodes } from './data' export const MindSet = () => { - const { addNewNode, isFetching } = useDataStore((s) => s) + const { addNewNode, isFetching, runningProjectId, dataInitial } = useDataStore((s) => s) const [showTwoD, setShowTwoD] = useState(false) + const { selectedEpisodeId, setSelectedEpisode } = useMindsetStore((s) => s) + const socket: Socket | undefined = useSocket() const queueRef = useRef(null) const timerRef = useRef(null) const { setPlayingNode } = usePlayerStore((s) => s) - useEffect(() => { - const init = async () => { - try { - const data = await fetchNodeEdges('b8769aa3-ef03-4d7c-83d6-8e62c6957e62', 0, 50) - - console.log(data) - } catch (error) { - console.log(error) - } - } - - init() - }, []) - const handleNewNodeCreated = useCallback( (data: FetchDataResponse) => { if (isFetching) { @@ -72,31 +63,117 @@ export const MindSet = () => { [addNewNode, isFetching], ) + const handleNodeUpdated = useCallback((node: NodeExtended) => { + console.log(node, 'uuuuuupdate') + }, []) + useEffect(() => { - handleNewNodeCreated({ - nodes, - edges, - }) + const init = async () => { + try { + const data = await fetchNodeEdges(selectedEpisodeId, 0, 50) + + if (data) { + data.nodes = data?.nodes.map((i) => ({ ...i, node_type: 'Topic' })) + handleNewNodeCreated(data) + } + } catch (error) { + console.log(error) + } + } + + if (selectedEpisodeId) { + init() + } + }, [selectedEpisodeId, handleNewNodeCreated]) + + useEffect(() => { + const init = async () => { + try { + const data = await getNode(selectedEpisodeId) + + if (data) { + setPlayingNode(data) + setSelectedEpisode(data) + } + } catch (error) { + console.log(error) + } + } + + if (selectedEpisodeId) { + init() + } + }, [selectedEpisodeId, setPlayingNode, setSelectedEpisode]) + + useEffect(() => { + if (socket) { + socket.on('new_node_created', handleNewNodeCreated) + socket.on('node_updated', handleNodeUpdated) + } + + return () => { + if (socket) { + socket.off() + } + } + }, [socket, handleNodeUpdated, handleNewNodeCreated]) + + useEffect(() => { + if (runningProjectId) { + try { + socket?.emit('update_project_id', { id: runningProjectId }) + } catch (error) { + console.error(error) + } + } + }, [runningProjectId, socket]) + + const markers = useMemo(() => { + if (dataInitial) { + const edgesMention: Array<{ source: string; start: number }> = dataInitial.links + .filter((e) => e?.start) + .map((edge) => ({ source: edge.source, start: edge?.start as number })) + + const nodesWithTimestamps = dataInitial.nodes + .filter((node) => dataInitial.links.some((ed) => ed.source === node.ref_id)) + .map((node) => { + const edge = edgesMention.find((ed) => node.ref_id === ed.source) + + if (edge) { + return { ...node, start: edge.start } + } + + return null + }) + .filter((node) => node) + + return nodesWithTimestamps + } - setPlayingNode(nodes.find((i) => i.node_type === 'Clip') || null) - }, [handleNewNodeCreated, setPlayingNode]) + return [] + }, [dataInitial]) return ( - - setShowTwoD(!showTwoD)}> -
- - - - - - {showTwoD ? : } - - - - - {false && } + {selectedEpisodeId ? ( + <> + + setShowTwoD(!showTwoD)}> +
+ + + + + + {showTwoD ? : } + + + + + + ) : ( + + )} ) } diff --git a/src/network/fetchGraphData/index.ts b/src/network/fetchGraphData/index.ts index b262a8d2a..86a1187ed 100644 --- a/src/network/fetchGraphData/index.ts +++ b/src/network/fetchGraphData/index.ts @@ -55,7 +55,7 @@ const fetchNodes = async ( export const fetchNodeEdges = async (refId: string, skip: number, limit = 5): Promise => { try { const response = await api.get( - `/prediction/graph/edges/${refId}?skip=${skip}&limit=${limit}&sort_by="edge_count&include_properties=true&includeContent=true"`, + `/prediction/graph/edges/${refId}?skip=${skip}&limit=${limit}&sort_by="edge_count&include_properties=true&includeContent=true&depth=1"`, ) return response diff --git a/src/network/fetchSourcesData/index.ts b/src/network/fetchSourcesData/index.ts index 926f7ea55..9f083aa47 100644 --- a/src/network/fetchSourcesData/index.ts +++ b/src/network/fetchSourcesData/index.ts @@ -4,6 +4,7 @@ import { FetchEdgeTypesResponse, FetchRadarResponse, FetchTopicResponse, + NodeExtended, NodeRequest, RadarRequest, SubmitErrRes, @@ -314,6 +315,12 @@ export const deleteNode = async (id: string) => { return response } +export const getNode = async (id: string) => { + const response = await api.get(`/node/${id}`) + + return response +} + export const getPriceData = async (endpoint: string) => { const response = await api.get(`/getprice?endpoint=${endpoint}&method=post`) diff --git a/src/stores/useMindsetStore/index.ts b/src/stores/useMindsetStore/index.ts new file mode 100644 index 000000000..2b936cbfc --- /dev/null +++ b/src/stores/useMindsetStore/index.ts @@ -0,0 +1,24 @@ +import { create } from 'zustand' +import { NodeExtended } from '~/types' + +type MindsetStore = { + selectedEpisodeId: string + selectedEpisode: NodeExtended | null + selectedEpisodeLink: string + setSelectedEpisodeId: (id: string) => void + setSelectedEpisode: (node: NodeExtended) => void + setSelectedEpisodeLink: (link: string) => void +} + +const defaultData: Omit = { + selectedEpisodeId: '', + selectedEpisodeLink: '', + selectedEpisode: null, +} + +export const useMindsetStore = create((set) => ({ + ...defaultData, + setSelectedEpisodeId: (selectedEpisodeId) => set({ selectedEpisodeId }), + setSelectedEpisodeLink: (selectedEpisodeLink) => set({ selectedEpisodeLink }), + setSelectedEpisode: (selectedEpisode) => set({ selectedEpisode }), +})) diff --git a/src/types/index.ts b/src/types/index.ts index 57ff75543..01d126700 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -143,6 +143,8 @@ export type NodeExtended = Node & { x?: number y?: number z?: number + start?: number + end?: number longitude?: number latitude?: number coordinates?: Coordinates @@ -152,6 +154,8 @@ export type NodeExtended = Node & { export type Link = { index?: T extends string ? never : number + start?: number + end?: number source: T target: T color?: number From 4585dcda3ca662a345a94554f48856effc7806ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Mon, 25 Nov 2024 00:35:31 +0300 Subject: [PATCH 14/17] feat: fix build errors --- .../Universe/Graph/Cubes/SelectionDataNodes/index.tsx | 2 +- src/modules/mindset/index.tsx | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/Universe/Graph/Cubes/SelectionDataNodes/index.tsx b/src/components/Universe/Graph/Cubes/SelectionDataNodes/index.tsx index fadc7dae5..e392edd48 100644 --- a/src/components/Universe/Graph/Cubes/SelectionDataNodes/index.tsx +++ b/src/components/Universe/Graph/Cubes/SelectionDataNodes/index.tsx @@ -118,7 +118,7 @@ export const SelectionDataNodes = memo(() => { {selectionGraphData?.nodes.map((node) => ( - + ))} diff --git a/src/modules/mindset/index.tsx b/src/modules/mindset/index.tsx index 73cef2e97..67f1910cf 100644 --- a/src/modules/mindset/index.tsx +++ b/src/modules/mindset/index.tsx @@ -139,11 +139,7 @@ export const MindSet = () => { .map((node) => { const edge = edgesMention.find((ed) => node.ref_id === ed.source) - if (edge) { - return { ...node, start: edge.start } - } - - return null + return { ...node, start: edge?.start || 0 } }) .filter((node) => node) From a4a74ec827013807aeb46f400c6e509dd4dfadd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Mon, 25 Nov 2024 00:44:25 +0300 Subject: [PATCH 15/17] fix: remove extra consoles --- src/components/App/index.tsx | 8 -------- src/components/Auth/index.tsx | 2 +- .../ModalsContainer/AddNodeEdgeModal/Body/index.tsx | 5 ----- .../CreateBountyModal/CreateBounty/index.tsx | 2 +- src/modules/mindset/components/LandingPage/index.tsx | 6 ------ 5 files changed, 2 insertions(+), 21 deletions(-) diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index 94c170e9a..4e0464179 100644 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -167,8 +167,6 @@ export const App = () => { queueRef.current = { nodes: [], edges: [] } - console.log(batchedData) - addNewNode(batchedData) } }, 3000) // Adjust delay as necessary @@ -299,8 +297,6 @@ export const App = () => { } ws.onmessage = (event) => { - console.log('Message from server:', event.data) - const data = JSON.parse(event.data) if (data.type === 'ping') { @@ -319,10 +315,6 @@ export const App = () => { ws.onerror = (error) => { console.error('WebSocket error:', error) } - - ws.onclose = () => { - console.log('WebSocket connection closed') - } }, [runningProjectId, setRunningProjectMessages]) useEffect(() => { diff --git a/src/components/Auth/index.tsx b/src/components/Auth/index.tsx index 5fce6b550..d1eebbb76 100644 --- a/src/components/Auth/index.tsx +++ b/src/components/Auth/index.tsx @@ -113,7 +113,7 @@ export const AuthGuard = ({ children }: PropsWithChildren) => { await handleAuth() } catch (error) { - console.log(error) + console.error(error) } } diff --git a/src/components/ModalsContainer/AddNodeEdgeModal/Body/index.tsx b/src/components/ModalsContainer/AddNodeEdgeModal/Body/index.tsx index c184629db..69b8b4d04 100644 --- a/src/components/ModalsContainer/AddNodeEdgeModal/Body/index.tsx +++ b/src/components/ModalsContainer/AddNodeEdgeModal/Body/index.tsx @@ -76,11 +76,6 @@ export const Body = () => { : { to: nodeFrom.ref_id, from: selectedToNode?.ref_id }), }) - const { ref_id: id } = nodeFrom - const { ref_id: selectedId } = selectedToNode - - console.log(id, selectedId) - closeHandler() } catch (error) { console.warn(error) diff --git a/src/components/ModalsContainer/CreateBountyModal/CreateBounty/index.tsx b/src/components/ModalsContainer/CreateBountyModal/CreateBounty/index.tsx index 686b1175a..cc3996bcb 100644 --- a/src/components/ModalsContainer/CreateBountyModal/CreateBounty/index.tsx +++ b/src/components/ModalsContainer/CreateBountyModal/CreateBounty/index.tsx @@ -52,7 +52,7 @@ export const CreateBounty: FC = ({ errMessage, handleClose }) => { setOptions(newOptions) } } catch (error) { - console.log('Error from get user details: ', error) + console.error('Error from get user details: ', error) } } diff --git a/src/modules/mindset/components/LandingPage/index.tsx b/src/modules/mindset/components/LandingPage/index.tsx index 09ccdd5a5..7550b5bfd 100644 --- a/src/modules/mindset/components/LandingPage/index.tsx +++ b/src/modules/mindset/components/LandingPage/index.tsx @@ -57,8 +57,6 @@ export const LandingPage = () => { try { const res = await handleSubmitForm({ source: inputValue }) - console.log(res) - if (res.data.project_id) { setRunningProjectId(res.data.project_id) } @@ -70,15 +68,11 @@ export const LandingPage = () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (err: any) { - console.log(err) - let errorMessage = NODE_ADD_ERROR if (err?.status === 400) { const res = await err.json() - console.log(res) - errorMessage = res.errorCode || res?.status || NODE_ADD_ERROR if (res.data.ref_id) { From a63b335652ae36a2e11af90ecd4335f2d320ab0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Mon, 25 Nov 2024 01:01:30 +0300 Subject: [PATCH 16/17] feat: remove warnings --- .../EditNodeNameModal/Body/index.tsx | 4 +- .../RemoveNodeModal/Body/index.tsx | 2 +- .../Universe/Graph/Cubes/Text/index.tsx | 2 - .../Universe/Graph/UI/NodeControls/index.tsx | 2 +- .../components/PlayerContols/index.tsx | 2 - .../components/Scene/Board/Node/index.tsx | 2 +- .../mindset/components/Scene/Board/index.tsx | 113 ++++++++---------- .../mindset/components/Scene/index.tsx | 2 +- src/modules/mindset/index.tsx | 4 +- src/stores/useDataStore/index.ts | 4 +- src/stores/useGraphStore/index.ts | 4 +- src/stores/useTopicsStore/index.ts | 2 +- src/testSphinxBridge/saveLsat.ts | 2 +- src/transformers/splitGraph.ts | 25 ---- src/utils/relayHelper/index.ts | 6 +- 15 files changed, 63 insertions(+), 113 deletions(-) diff --git a/src/components/ModalsContainer/EditNodeNameModal/Body/index.tsx b/src/components/ModalsContainer/EditNodeNameModal/Body/index.tsx index 8f1a22810..7db7173a7 100644 --- a/src/components/ModalsContainer/EditNodeNameModal/Body/index.tsx +++ b/src/components/ModalsContainer/EditNodeNameModal/Body/index.tsx @@ -1,5 +1,5 @@ import { Button, Skeleton } from '@mui/material' -import React, { useEffect, useState } from 'react' +import { useEffect, useState } from 'react' import { FormProvider, useForm } from 'react-hook-form' import { ClipLoader } from 'react-spinners' import styled from 'styled-components' @@ -61,7 +61,7 @@ export const Body = () => { setActualTopicNode(node) } catch (error) { - console.log(error) + console.error(error) } finally { setTopicIsLoading(false) } diff --git a/src/components/ModalsContainer/RemoveNodeModal/Body/index.tsx b/src/components/ModalsContainer/RemoveNodeModal/Body/index.tsx index 650afdabe..c82f9d466 100644 --- a/src/components/ModalsContainer/RemoveNodeModal/Body/index.tsx +++ b/src/components/ModalsContainer/RemoveNodeModal/Body/index.tsx @@ -53,7 +53,7 @@ export const Body = () => { setActualNode(selectedNode) } } catch (error) { - console.log(error) + console.error(error) } finally { setTopicIsLoading(false) } diff --git a/src/components/Universe/Graph/Cubes/Text/index.tsx b/src/components/Universe/Graph/Cubes/Text/index.tsx index 9483e09ac..64f3b8808 100644 --- a/src/components/Universe/Graph/Cubes/Text/index.tsx +++ b/src/components/Universe/Graph/Cubes/Text/index.tsx @@ -72,8 +72,6 @@ export const TextNode = memo(({ node, hide, ignoreDistance }: Props) => { const ringRef = useRef(null) const circleRef = useRef(null) - console.log('rerender') - const nodePositionRef = useRef(new Vector3()) const { texture } = useTexture(node.properties?.image_url || '') diff --git a/src/components/Universe/Graph/UI/NodeControls/index.tsx b/src/components/Universe/Graph/UI/NodeControls/index.tsx index d66fa1972..7b3fc5cb2 100644 --- a/src/components/Universe/Graph/UI/NodeControls/index.tsx +++ b/src/components/Universe/Graph/UI/NodeControls/index.tsx @@ -56,7 +56,7 @@ export const NodeControls = memo(() => { } } } catch (error) { - console.log(error) + console.error(error) } }, [addNewNode, selectedNode?.ref_id, selectionGraphData.nodes.length]) diff --git a/src/modules/mindset/components/PlayerContols/index.tsx b/src/modules/mindset/components/PlayerContols/index.tsx index b7670479a..060f72367 100644 --- a/src/modules/mindset/components/PlayerContols/index.tsx +++ b/src/modules/mindset/components/PlayerContols/index.tsx @@ -25,8 +25,6 @@ export const PlayerControl = ({ markers }: Props) => { const startTime = ((playingTime - start) / (end - start)) * 100 - console.log(startTime) - const showPlayer = playingNode return showPlayer ? ( diff --git a/src/modules/mindset/components/Scene/Board/Node/index.tsx b/src/modules/mindset/components/Scene/Board/Node/index.tsx index 0dc4cef52..e07cda450 100644 --- a/src/modules/mindset/components/Scene/Board/Node/index.tsx +++ b/src/modules/mindset/components/Scene/Board/Node/index.tsx @@ -19,7 +19,7 @@ type Props = { export const Node = ({ width, height, position, url, onButtonClick, name, type, color }: Props) => { const { camera, size } = useThree() - console.log(url, type) + console.info(url, type) // Function to calculate the distance between the camera and the node const getPixelSize = (worldSize: number, worldHeight: number) => { diff --git a/src/modules/mindset/components/Scene/Board/index.tsx b/src/modules/mindset/components/Scene/Board/index.tsx index 47e170d05..302b6738a 100644 --- a/src/modules/mindset/components/Scene/Board/index.tsx +++ b/src/modules/mindset/components/Scene/Board/index.tsx @@ -3,20 +3,13 @@ import { useEffect, useMemo } from 'react' import { edges, edgesMention, maxTimestamp, minTimestamp, nodes, normalizeTimestamp } from '~/modules/mindset/data' import { Node } from './Node' -type Props = { - w: number -} - const totalDuration = 185 -export const Board = ({ w = 1 }: Props) => { +export const Board = () => { const state = useThree() - console.log(state) - const { width } = state.viewport const { camera } = state - const xW = w useEffect(() => { const orthoCamera = camera as THREE.OrthographicCamera @@ -45,8 +38,6 @@ export const Board = ({ w = 1 }: Props) => { } }, [camera]) - console.log(width, xW) - const rangeMin = 0 const rangeMax = 97.52 * 10 @@ -68,60 +59,54 @@ export const Board = ({ w = 1 }: Props) => { [rangeMin, rangeMax], ) - console.log(-width) - return ( - true && ( - <> - {nodes.map((node, i) => { - const hasTimeStamp = positions.some((p) => p.source === nodes[i].ref_id) - - const position = hasTimeStamp ? positions.find((p) => p.source === nodes[i].ref_id)?.xStart || 0 : i * 35 - - const y = hasTimeStamp ? 0 : 15 - - return true ? null : ( - - ) - })} - {edgesMention.map((e) => { - const node = nodes.find((i) => i.ref_id === e.source) - - const x = (e.start / totalDuration) * width - - console.log(x) - - const y = -5 - - return node ? ( - - ) : null - })} - - {/* Radius: Half of viewport width */} - - - - ) + <> + {nodes.map((node, i) => { + const hasTimeStamp = positions.some((p) => p.source === nodes[i].ref_id) + + const position = hasTimeStamp ? positions.find((p) => p.source === nodes[i].ref_id)?.xStart || 0 : i * 35 + + const y = hasTimeStamp ? 0 : 15 + + return true ? null : ( + + ) + })} + {edgesMention.map((e) => { + const node = nodes.find((i) => i.ref_id === e.source) + + const x = (e.start / totalDuration) * width + + const y = -5 + + return node ? ( + null} + position={[x, y, 0]} + type={node.node_type} + url="logo.png" + width={5} + /> + ) : null + })} + + {/* Radius: Half of viewport width */} + + + ) } diff --git a/src/modules/mindset/components/Scene/index.tsx b/src/modules/mindset/components/Scene/index.tsx index fad461158..6d5f5913d 100644 --- a/src/modules/mindset/components/Scene/index.tsx +++ b/src/modules/mindset/components/Scene/index.tsx @@ -18,7 +18,7 @@ export const Scene = () => { - + diff --git a/src/modules/mindset/index.tsx b/src/modules/mindset/index.tsx index 67f1910cf..60a208275 100644 --- a/src/modules/mindset/index.tsx +++ b/src/modules/mindset/index.tsx @@ -77,7 +77,7 @@ export const MindSet = () => { handleNewNodeCreated(data) } } catch (error) { - console.log(error) + console.error(error) } } @@ -96,7 +96,7 @@ export const MindSet = () => { setSelectedEpisode(data) } } catch (error) { - console.log(error) + console.error(error) } } diff --git a/src/stores/useDataStore/index.ts b/src/stores/useDataStore/index.ts index dd9a7decf..adc0a774f 100644 --- a/src/stores/useDataStore/index.ts +++ b/src/stores/useDataStore/index.ts @@ -286,7 +286,7 @@ export const useDataStore = create()( sidebarFilterCounts, }) } catch (error) { - console.log(error) + console.error(error) if (error !== 'abort') { set({ isLoadingNew: false, isFetching: false }) @@ -347,7 +347,7 @@ export const useDataStore = create()( setHideNodeDetails: (hideNodeDetails) => set({ hideNodeDetails }), setSeedQuestions: (questions) => set({ seedQuestions: questions }), updateNode: (updatedNode) => { - console.log(updatedNode) + console.info(updatedNode) }, addNewNode: (data) => { const { dataInitial: existingData, filters } = get() diff --git a/src/stores/useGraphStore/index.ts b/src/stores/useGraphStore/index.ts index 915a94fdd..67dae899e 100644 --- a/src/stores/useGraphStore/index.ts +++ b/src/stores/useGraphStore/index.ts @@ -210,7 +210,7 @@ export const useGraphStore = create()((set, get) => ({ simulationHelpers.simulationRestart() } catch (error) { - console.log(error) + console.error(error) // eslint-disable-next-line no-debugger } @@ -314,8 +314,6 @@ export const useGraphStore = create()((set, get) => ({ }, }, simulationCreate: (nodes, links) => { - console.log('created') - const structuredNodes = structuredClone(nodes) const structuredLinks = structuredClone(links) diff --git a/src/stores/useTopicsStore/index.ts b/src/stores/useTopicsStore/index.ts index 04f22b13a..5120004d0 100644 --- a/src/stores/useTopicsStore/index.ts +++ b/src/stores/useTopicsStore/index.ts @@ -67,7 +67,7 @@ export const useTopicsStore = create((set, get) => ({ set({ loading: false }) } catch (error) { - console.log(error) + console.error(error) } }, setFilters: (filters: Partial) => set({ filters: { ...get().filters, page: 0, ...filters } }), diff --git a/src/testSphinxBridge/saveLsat.ts b/src/testSphinxBridge/saveLsat.ts index c8346a19c..ebb2e99b7 100644 --- a/src/testSphinxBridge/saveLsat.ts +++ b/src/testSphinxBridge/saveLsat.ts @@ -28,7 +28,7 @@ export async function saveLsat(invoice: string, macaroon: string, host: string) return null } catch (error) { - console.log(JSON.stringify(error)) + console.error(JSON.stringify(error)) return null } diff --git a/src/transformers/splitGraph.ts b/src/transformers/splitGraph.ts index 07eb941b6..620540edc 100644 --- a/src/transformers/splitGraph.ts +++ b/src/transformers/splitGraph.ts @@ -195,31 +195,6 @@ export const generateSplitGraphPositions = (nodes: NodeExtended[], links: Link[] return updated }) - // const links = generateLinksFromNodeData(updatedNodes, true, true) - - // do links - const updatedLinks = links.map((l: Link) => { - const sourceNode = updatedNodes.find((f) => f.ref_id === l.sourceRef) - const targetNode = updatedNodes.find((f) => f.ref_id === l.targetRef) - let onlyVisibleOnSelect = false - - if (sourceNode?.node_type === 'Guest' || sourceNode?.node_type === 'Topic') { - onlyVisibleOnSelect = true - } - - const sourcePosition = new Vector3(sourceNode?.x || 0, sourceNode?.y || 0, sourceNode?.z || 0) - const targetPosition = new Vector3(targetNode?.x || 0, targetNode?.y || 0, targetNode?.z || 0) - - return { - ...l, - onlyVisibleOnSelect, - sourcePosition, - targetPosition, - } - }) - - console.log(updatedLinks) - // sort back to weighted sort updatedNodes.sort((a, b) => (b.weight || 0) - (a.weight || 0)) diff --git a/src/utils/relayHelper/index.ts b/src/utils/relayHelper/index.ts index f807afdac..597000a0a 100644 --- a/src/utils/relayHelper/index.ts +++ b/src/utils/relayHelper/index.ts @@ -27,10 +27,6 @@ export const saveSearchTerm = async () => { await executeIfProd(async () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - const res = await sphinx.enable(true) - - if (!res) { - console.log('Sphinx enable failed, means no pubkey and no budget (including budget of 0)') - } + await sphinx.enable(true) }) } From 9f4201303e0023d45066652ae9e5a4a05279a090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Mon, 25 Nov 2024 01:11:51 +0300 Subject: [PATCH 17/17] feat: move mindset to main --- src/components/AppContainer/index.tsx | 2 +- .../header => components/mindset/components/Header}/index.tsx | 0 .../mindset/components/Icon/ChevronRight.tsx | 0 src/{modules => components}/mindset/components/Icon/Logo.tsx | 0 .../mindset/components/LandingPage/index.tsx | 0 .../mindset/components/LandingPage/utils/index.tsx | 0 src/{modules => components}/mindset/components/Marker/index.tsx | 0 .../mindset/components/MediaPlayer/ToolBar/index.tsx | 0 .../mindset/components/MediaPlayer/index.tsx | 0 .../components/PlayerContols/CanvasProgressbar/index.tsx | 2 +- .../mindset/components/PlayerContols/ProgressBar/index.tsx | 0 .../mindset/components/PlayerContols/index.tsx | 0 .../mindset/components/Scene/Board/Node/Content/index.tsx | 0 .../mindset/components/Scene/Board/Node/index.tsx | 0 .../mindset/components/Scene/Board/RoundedImage/index.tsx | 0 .../mindset/components/Scene/Board/RoundedRectangle/index.tsx | 0 .../mindset/components/Scene/Board/index.tsx | 2 +- src/{modules => components}/mindset/components/Scene/index.tsx | 0 .../mindset/components/Sidebar}/Transcript/index.tsx | 2 +- .../sidebar => components/mindset/components/Sidebar}/index.tsx | 2 +- src/{modules => components}/mindset/data/index.tsx | 0 src/{modules => components}/mindset/data/transcript/index.tsx | 0 src/{modules => components}/mindset/index.tsx | 0 23 files changed, 5 insertions(+), 5 deletions(-) rename src/{modules/mindset/components/header => components/mindset/components/Header}/index.tsx (100%) rename src/{modules => components}/mindset/components/Icon/ChevronRight.tsx (100%) rename src/{modules => components}/mindset/components/Icon/Logo.tsx (100%) rename src/{modules => components}/mindset/components/LandingPage/index.tsx (100%) rename src/{modules => components}/mindset/components/LandingPage/utils/index.tsx (100%) rename src/{modules => components}/mindset/components/Marker/index.tsx (100%) rename src/{modules => components}/mindset/components/MediaPlayer/ToolBar/index.tsx (100%) rename src/{modules => components}/mindset/components/MediaPlayer/index.tsx (100%) rename src/{modules => components}/mindset/components/PlayerContols/CanvasProgressbar/index.tsx (97%) rename src/{modules => components}/mindset/components/PlayerContols/ProgressBar/index.tsx (100%) rename src/{modules => components}/mindset/components/PlayerContols/index.tsx (100%) rename src/{modules => components}/mindset/components/Scene/Board/Node/Content/index.tsx (100%) rename src/{modules => components}/mindset/components/Scene/Board/Node/index.tsx (100%) rename src/{modules => components}/mindset/components/Scene/Board/RoundedImage/index.tsx (100%) rename src/{modules => components}/mindset/components/Scene/Board/RoundedRectangle/index.tsx (100%) rename src/{modules => components}/mindset/components/Scene/Board/index.tsx (98%) rename src/{modules => components}/mindset/components/Scene/index.tsx (100%) rename src/{modules/mindset/components/sidebar => components/mindset/components/Sidebar}/Transcript/index.tsx (93%) rename src/{modules/mindset/components/sidebar => components/mindset/components/Sidebar}/index.tsx (95%) rename src/{modules => components}/mindset/data/index.tsx (100%) rename src/{modules => components}/mindset/data/transcript/index.tsx (100%) rename src/{modules => components}/mindset/index.tsx (100%) diff --git a/src/components/AppContainer/index.tsx b/src/components/AppContainer/index.tsx index 080fef5da..c9f8ea3d9 100644 --- a/src/components/AppContainer/index.tsx +++ b/src/components/AppContainer/index.tsx @@ -5,7 +5,7 @@ import { AppProviders } from '../App/Providers' import { AuthGuard } from '../Auth' const LazyApp = lazy(() => import('../App').then(({ App }) => ({ default: App }))) -const LazyMindSet = lazy(() => import('../../modules/mindset').then(({ MindSet }) => ({ default: MindSet }))) +const LazyMindSet = lazy(() => import('../mindset').then(({ MindSet }) => ({ default: MindSet }))) export const AppContainer = () => { const App = diff --git a/src/modules/mindset/components/header/index.tsx b/src/components/mindset/components/Header/index.tsx similarity index 100% rename from src/modules/mindset/components/header/index.tsx rename to src/components/mindset/components/Header/index.tsx diff --git a/src/modules/mindset/components/Icon/ChevronRight.tsx b/src/components/mindset/components/Icon/ChevronRight.tsx similarity index 100% rename from src/modules/mindset/components/Icon/ChevronRight.tsx rename to src/components/mindset/components/Icon/ChevronRight.tsx diff --git a/src/modules/mindset/components/Icon/Logo.tsx b/src/components/mindset/components/Icon/Logo.tsx similarity index 100% rename from src/modules/mindset/components/Icon/Logo.tsx rename to src/components/mindset/components/Icon/Logo.tsx diff --git a/src/modules/mindset/components/LandingPage/index.tsx b/src/components/mindset/components/LandingPage/index.tsx similarity index 100% rename from src/modules/mindset/components/LandingPage/index.tsx rename to src/components/mindset/components/LandingPage/index.tsx diff --git a/src/modules/mindset/components/LandingPage/utils/index.tsx b/src/components/mindset/components/LandingPage/utils/index.tsx similarity index 100% rename from src/modules/mindset/components/LandingPage/utils/index.tsx rename to src/components/mindset/components/LandingPage/utils/index.tsx diff --git a/src/modules/mindset/components/Marker/index.tsx b/src/components/mindset/components/Marker/index.tsx similarity index 100% rename from src/modules/mindset/components/Marker/index.tsx rename to src/components/mindset/components/Marker/index.tsx diff --git a/src/modules/mindset/components/MediaPlayer/ToolBar/index.tsx b/src/components/mindset/components/MediaPlayer/ToolBar/index.tsx similarity index 100% rename from src/modules/mindset/components/MediaPlayer/ToolBar/index.tsx rename to src/components/mindset/components/MediaPlayer/ToolBar/index.tsx diff --git a/src/modules/mindset/components/MediaPlayer/index.tsx b/src/components/mindset/components/MediaPlayer/index.tsx similarity index 100% rename from src/modules/mindset/components/MediaPlayer/index.tsx rename to src/components/mindset/components/MediaPlayer/index.tsx diff --git a/src/modules/mindset/components/PlayerContols/CanvasProgressbar/index.tsx b/src/components/mindset/components/PlayerContols/CanvasProgressbar/index.tsx similarity index 97% rename from src/modules/mindset/components/PlayerContols/CanvasProgressbar/index.tsx rename to src/components/mindset/components/PlayerContols/CanvasProgressbar/index.tsx index 5f7185b45..fd4200e2a 100644 --- a/src/modules/mindset/components/PlayerContols/CanvasProgressbar/index.tsx +++ b/src/components/mindset/components/PlayerContols/CanvasProgressbar/index.tsx @@ -2,7 +2,7 @@ import { Html, OrbitControls } from '@react-three/drei' import { Canvas, useThree } from '@react-three/fiber' import { useEffect, useState } from 'react' import { Tooltip } from '~/components/common/ToolTip' -import { nodesWithTimestamp } from '~/modules/mindset/data' +import { nodesWithTimestamp } from '~/components/mindset/data' import { Marker } from '../../Marker' type Props = { diff --git a/src/modules/mindset/components/PlayerContols/ProgressBar/index.tsx b/src/components/mindset/components/PlayerContols/ProgressBar/index.tsx similarity index 100% rename from src/modules/mindset/components/PlayerContols/ProgressBar/index.tsx rename to src/components/mindset/components/PlayerContols/ProgressBar/index.tsx diff --git a/src/modules/mindset/components/PlayerContols/index.tsx b/src/components/mindset/components/PlayerContols/index.tsx similarity index 100% rename from src/modules/mindset/components/PlayerContols/index.tsx rename to src/components/mindset/components/PlayerContols/index.tsx diff --git a/src/modules/mindset/components/Scene/Board/Node/Content/index.tsx b/src/components/mindset/components/Scene/Board/Node/Content/index.tsx similarity index 100% rename from src/modules/mindset/components/Scene/Board/Node/Content/index.tsx rename to src/components/mindset/components/Scene/Board/Node/Content/index.tsx diff --git a/src/modules/mindset/components/Scene/Board/Node/index.tsx b/src/components/mindset/components/Scene/Board/Node/index.tsx similarity index 100% rename from src/modules/mindset/components/Scene/Board/Node/index.tsx rename to src/components/mindset/components/Scene/Board/Node/index.tsx diff --git a/src/modules/mindset/components/Scene/Board/RoundedImage/index.tsx b/src/components/mindset/components/Scene/Board/RoundedImage/index.tsx similarity index 100% rename from src/modules/mindset/components/Scene/Board/RoundedImage/index.tsx rename to src/components/mindset/components/Scene/Board/RoundedImage/index.tsx diff --git a/src/modules/mindset/components/Scene/Board/RoundedRectangle/index.tsx b/src/components/mindset/components/Scene/Board/RoundedRectangle/index.tsx similarity index 100% rename from src/modules/mindset/components/Scene/Board/RoundedRectangle/index.tsx rename to src/components/mindset/components/Scene/Board/RoundedRectangle/index.tsx diff --git a/src/modules/mindset/components/Scene/Board/index.tsx b/src/components/mindset/components/Scene/Board/index.tsx similarity index 98% rename from src/modules/mindset/components/Scene/Board/index.tsx rename to src/components/mindset/components/Scene/Board/index.tsx index 302b6738a..849c1577a 100644 --- a/src/modules/mindset/components/Scene/Board/index.tsx +++ b/src/components/mindset/components/Scene/Board/index.tsx @@ -1,6 +1,6 @@ import { useThree } from '@react-three/fiber' import { useEffect, useMemo } from 'react' -import { edges, edgesMention, maxTimestamp, minTimestamp, nodes, normalizeTimestamp } from '~/modules/mindset/data' +import { edges, edgesMention, maxTimestamp, minTimestamp, nodes, normalizeTimestamp } from '~/components/mindset/data' import { Node } from './Node' const totalDuration = 185 diff --git a/src/modules/mindset/components/Scene/index.tsx b/src/components/mindset/components/Scene/index.tsx similarity index 100% rename from src/modules/mindset/components/Scene/index.tsx rename to src/components/mindset/components/Scene/index.tsx diff --git a/src/modules/mindset/components/sidebar/Transcript/index.tsx b/src/components/mindset/components/Sidebar/Transcript/index.tsx similarity index 93% rename from src/modules/mindset/components/sidebar/Transcript/index.tsx rename to src/components/mindset/components/Sidebar/Transcript/index.tsx index 3f19fe5c7..84472cabc 100644 --- a/src/modules/mindset/components/sidebar/Transcript/index.tsx +++ b/src/components/mindset/components/Sidebar/Transcript/index.tsx @@ -1,6 +1,6 @@ import styled from 'styled-components' import { Flex } from '~/components/common/Flex' -import { transcript } from '~/modules/mindset/data/transcript' +import { transcript } from '~/components/mindset/data/transcript' import { usePlayerStore } from '~/stores/usePlayerStore' import { colors } from '~/utils' diff --git a/src/modules/mindset/components/sidebar/index.tsx b/src/components/mindset/components/Sidebar/index.tsx similarity index 95% rename from src/modules/mindset/components/sidebar/index.tsx rename to src/components/mindset/components/Sidebar/index.tsx index d34ba07ad..06dc065ff 100644 --- a/src/modules/mindset/components/sidebar/index.tsx +++ b/src/components/mindset/components/Sidebar/index.tsx @@ -2,7 +2,7 @@ import styled from 'styled-components' import { MENU_WIDTH } from '~/components/App/SideBar' import { Flex } from '~/components/common/Flex' import { Text } from '~/components/common/Text' -import { MediaPlayer } from '~/modules/mindset/components/MediaPlayer' +import { MediaPlayer } from '~/components/mindset/components/MediaPlayer' import { useMindsetStore } from '~/stores/useMindsetStore' import { Transcript } from './Transcript' diff --git a/src/modules/mindset/data/index.tsx b/src/components/mindset/data/index.tsx similarity index 100% rename from src/modules/mindset/data/index.tsx rename to src/components/mindset/data/index.tsx diff --git a/src/modules/mindset/data/transcript/index.tsx b/src/components/mindset/data/transcript/index.tsx similarity index 100% rename from src/modules/mindset/data/transcript/index.tsx rename to src/components/mindset/data/transcript/index.tsx diff --git a/src/modules/mindset/index.tsx b/src/components/mindset/index.tsx similarity index 100% rename from src/modules/mindset/index.tsx rename to src/components/mindset/index.tsx