Skip to content

Commit

Permalink
feat: add 2d view for mindset
Browse files Browse the repository at this point in the history
  • Loading branch information
Rassl committed Nov 27, 2024
1 parent 4865261 commit 37770d7
Show file tree
Hide file tree
Showing 14 changed files with 431 additions and 242 deletions.
33 changes: 30 additions & 3 deletions src/components/mindset/components/Marker/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { memo } from 'react'
import styled from 'styled-components'
import { Flex } from '~/components/common/Flex'
import { Tooltip } from '~/components/common/ToolTip'
import { useSchemaStore } from '~/stores/useSchemaStore'
import { colors } from '~/utils/colors'

type Props = {
type: string
name: string
left: number
}

type BadgeProps = {
Expand All @@ -13,7 +17,7 @@ type BadgeProps = {
label: string
}

export const Marker = ({ type }: Props) => {
export const Marker = memo(({ type, name, left }: Props) => {
const [normalizedSchemasByType] = useSchemaStore((s) => [s.normalizedSchemasByType])

const primaryColor = normalizedSchemasByType[type]?.primary_color
Expand All @@ -26,8 +30,16 @@ export const Marker = ({ type }: Props) => {
color: primaryColor ?? colors.THING,
}

return <Badge {...badgeProps} label={type} />
}
return (
<MarkerWrapper style={{ left: `${left}%` }}>
<Tooltip content={`${name || type}`}>
<Badge {...badgeProps} label={type} />
</Tooltip>
</MarkerWrapper>
)
})

Marker.displayName = 'Marker'

const Badge = ({ iconStart, color, label }: BadgeProps) => (
<EpisodeWrapper color={color}>
Expand All @@ -52,3 +64,18 @@ const EpisodeWrapper = styled(Flex).attrs({
object-fit: contain;
}
`

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%;
display: flex;
align-items: center;
justify-content: center;
`
36 changes: 24 additions & 12 deletions src/components/mindset/components/MediaPlayer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, useEffect, useMemo, useRef, useState } from 'react'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import ReactPlayer from 'react-player'

import styled from 'styled-components'
Expand Down Expand Up @@ -45,7 +45,6 @@ type Props = {
}

const MediaPlayerComponent = ({ mediaUrl }: Props) => {
const playerRef = useRef<ReactPlayer | null>(null)
const wrapperRef = useRef<HTMLDivElement | null>(null)
const [isFocused, setIsFocused] = useState(false)
const [isFullScreen, setIsFullScreen] = useState(false)
Expand Down Expand Up @@ -79,6 +78,8 @@ const MediaPlayerComponent = ({ mediaUrl }: Props) => {
resetPlayer,
isSeeking,
setIsSeeking,
setPlayerRef,
playerRef,
} = usePlayerStore((s) => s)

useEffect(() => () => resetPlayer(), [resetPlayer])
Expand All @@ -93,21 +94,21 @@ const MediaPlayerComponent = ({ mediaUrl }: Props) => {
}, [playingNode, setPlayingTime, setDuration, setIsReady, isReady])

useEffect(() => {
if (isSeeking && playerRef.current) {
playerRef.current.seekTo(playingTime, 'seconds')
if (isSeeking && playerRef) {
playerRef.seekTo(playingTime, 'seconds')
setIsSeeking(false)
}
}, [playingTime, isSeeking, setIsSeeking])
}, [playingTime, isSeeking, setIsSeeking, playerRef])

useEffect(() => {
if (isReady && NodeStartTime && playerRef.current && !hasSeekedToStart) {
if (isReady && NodeStartTime && playerRef && !hasSeekedToStart) {
const startTimeInSeconds = videoTimeToSeconds(NodeStartTime)

playerRef.current.seekTo(startTimeInSeconds, 'seconds')
playerRef.seekTo(startTimeInSeconds, 'seconds')
setPlayingTime(startTimeInSeconds)
setHasSeekedToStart(true)
}
}, [isReady, NodeStartTime, setPlayingTime, hasSeekedToStart])
}, [isReady, NodeStartTime, setPlayingTime, hasSeekedToStart, playerRef])

const togglePlay = () => {
setIsPlaying(!isPlaying)
Expand Down Expand Up @@ -142,6 +143,8 @@ const MediaPlayerComponent = ({ mediaUrl }: Props) => {

setPlayingTime(currentTime)

return

const edge = findCurrentEdge(edges, currentTime)

if (edge) {
Expand All @@ -153,17 +156,17 @@ const MediaPlayerComponent = ({ mediaUrl }: Props) => {
}

const handleReady = () => {
if (playerRef.current) {
if (playerRef) {
setStatus('ready')

const videoDuration = playerRef.current.getDuration()
const videoDuration = playerRef.getDuration()

setDuration(videoDuration)

if (NodeStartTime && !hasSeekedToStart) {
const startTimeInSeconds = videoTimeToSeconds(NodeStartTime)

playerRef.current.seekTo(startTimeInSeconds, 'seconds')
playerRef.seekTo(startTimeInSeconds, 'seconds')
setPlayingTime(startTimeInSeconds)
setHasSeekedToStart(true)
}
Expand Down Expand Up @@ -222,14 +225,23 @@ const MediaPlayerComponent = ({ mediaUrl }: Props) => {
togglePlay()
}

const playerRefCallback = useCallback(
(player: ReactPlayer) => {
if (!playerRef && player) {
setPlayerRef(player) // Update the store with the player instance
}
},
[setPlayerRef, playerRef],
)

return mediaUrl ? (
<Wrapper ref={wrapperRef} onBlur={() => setIsFocused(false)} onFocus={() => setIsFocused(true)} tabIndex={0}>
<Cover isFullScreen={isFullScreen}>
<Avatar size={120} src={playingNode?.image_url || ''} type="clip" />
</Cover>
<PlayerWrapper isFullScreen={isFullScreen} onClick={handlePlayerClick}>
<ReactPlayer
ref={playerRef}
ref={playerRefCallback}
controls
height={!isFullScreen ? '219px' : window.screen.height}
onBuffer={() => setStatus('buffering')}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,64 +1,57 @@
import { LinearProgress } from '@mui/material'
import { Slider } from '@mui/material'
import styled from 'styled-components'
import { Flex } from '~/components/common/Flex'
import { Tooltip } from '~/components/common/ToolTip'
import { NodeExtended } from '~/types'
import { colors } from '~/utils'
import { Marker } from '../../Marker'

type Props = {
duration: number
progress: number
markers: NodeExtended[]
playingTIme: number
handleProgressChange: (_: Event, value: number | number[]) => void
}

export const ProgressBar = ({ duration, progress, markers }: Props) => (
export const ProgressBar = ({ duration, markers, handleProgressChange, playingTIme }: Props) => (
<ProgressWrapper>
<Progress value={progress} variant="determinate" />
<ProgressSlider max={duration} onChange={handleProgressChange} value={playingTIme} />
{markers.map((node) => {
const position = ((node?.start || 0) / duration) * 100
const type = node?.node_type || ''
const name = node?.properties?.name || ''

return (
<MarkerWrapper key={node?.ref_id} style={{ left: `${position}%` }}>
<Tooltip content={`${node?.properties?.name || type}`}>
<Marker type={type} />
</Tooltip>
</MarkerWrapper>
)
return <Marker key={node.ref_id} left={position} name={name} type={type} />
})}
</ProgressWrapper>
)

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%;
display: flex;
align-items: center;
justify-content: center;
const ProgressSlider = styled(Slider)`
&& {
z-index: 20;
color: ${colors.white};
height: 3px;
width: calc(100% - 12px);
box-sizing: border-box;
.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;
}
}
}
`
30 changes: 20 additions & 10 deletions src/components/mindset/components/PlayerContols/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IconButton } from '@mui/material'
import { useCallback } from 'react'
import styled from 'styled-components'
import ChevronLeftIcon from '~/components/Icons/ChevronLeftIcon'
import ChevronRightIcon from '~/components/Icons/ChevronRightIcon.js'
Expand All @@ -7,25 +8,28 @@ 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'

type Props = {
markers: NodeExtended[]
}

export const PlayerControl = ({ markers }: Props) => {
const { isPlaying, setIsPlaying, playingTime, playingNode, duration } = usePlayerStore((s) => s)
const { isPlaying, setIsPlaying, playingTime, playingNode, duration, playerRef } = usePlayerStore((s) => s)

const [start, end] = playingNode?.properties?.timestamp
? (playingNode.properties.timestamp as string).split('-').map((time) => videoTimeToSeconds(time))
: [0, duration]
const showPlayer = playingNode

const startTime = ((playingTime - start) / (end - start)) * 100
const handleProgressChange = useCallback(
(_: Event, value: number | number[]) => {
const newValue = Array.isArray(value) ? value[0] : value

const showPlayer = playingNode
if (playerRef) {
playerRef.seekTo(newValue, 'seconds')
}
},
[playerRef],
)

return showPlayer ? (
<Wrapper>
Expand All @@ -43,8 +47,14 @@ export const PlayerControl = ({ markers }: Props) => {
</Action>
<ChevronRightIcon />
</Controls>
<ProgressBar duration={duration} markers={markers} progress={startTime} />
{false && <ProgressBarCanvas duration={duration} progress={startTime} />}
{true && (
<ProgressBar
duration={duration}
handleProgressChange={handleProgressChange}
markers={markers}
playingTIme={playingTime}
/>
)}
</Wrapper>
) : null
}
Expand Down
45 changes: 45 additions & 0 deletions src/components/mindset/components/Scene/Board/Edges/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useMemo } from 'react'
import { Vector3 } from 'three'

type Props = {
sourcePosition: { x: number; y: number; z: number }
targetPosition: { x: number; y: number; z: number }
color?: string
arrowSize?: number
}

export const Edge = ({ sourcePosition, targetPosition, color = 'white', arrowSize = 1 }: Props) => {
const points = useMemo(() => {
const start = new Vector3(sourcePosition.x, sourcePosition.y, sourcePosition.z)
const end = new Vector3(targetPosition.x, targetPosition.y, targetPosition.z)
const direction = new Vector3().subVectors(end, start).normalize()

// Calculate arrowhead points
const arrowLeft = new Vector3()
.copy(direction)
.multiplyScalar(-arrowSize)
.applyAxisAngle(new Vector3(0, 0, 1), Math.PI / 6)

const arrowRight = new Vector3()
.copy(direction)
.multiplyScalar(-arrowSize)
.applyAxisAngle(new Vector3(0, 0, 1), -Math.PI / 6)

// Return line points + arrowhead points
return [start, end, end.clone(), end.clone().add(arrowLeft), end.clone(), end.clone().add(arrowRight)]
}, [sourcePosition, targetPosition, arrowSize])

return (
<line>
<bufferGeometry>
<bufferAttribute
array={new Float32Array(points.flatMap((p) => [p.x, p.y, p.z]))}
attach="attributes-position"
count={points.length}
itemSize={3}
/>
</bufferGeometry>
<lineBasicMaterial color={color} />
</line>
)
}
Loading

0 comments on commit 37770d7

Please sign in to comment.