/ src / components / DreamTalk.jsx
DreamTalk.jsx
  1  import React, { useRef, useEffect, useState } from 'react';
  2  import { BLACK, WHITE, BLUE } from '../constants/colors';
  3  
  4  const CenterOverlay = ({ repoName, isVisible }) => (
  5    <div style={{
  6      position: 'absolute',
  7      top: '25%',
  8      left: '25%',
  9      width: '50%',
 10      height: '50%',
 11      borderRadius: '50%',
 12      backgroundColor: isVisible ? 'rgba(0, 0, 0, 0.3)' : 'transparent',
 13      display: 'flex',
 14      justifyContent: 'center',
 15      alignItems: 'center',
 16      opacity: isVisible ? 1 : 0,
 17      transition: 'opacity 0.3s ease',
 18      pointerEvents: 'none',
 19    }}>
 20      {isVisible && (
 21        <div style={{
 22          color: WHITE,
 23          fontSize: '14px',
 24          textAlign: 'center',
 25          padding: '5px',
 26        }}>
 27          {repoName}
 28        </div>
 29      )}
 30    </div>
 31  );
 32  
 33  const DreamTalk = ({ repoName, dreamTalkMedia, metadata, onClick, onRightClick, onMouseEnter, onMouseLeave, isHovered, borderColor, onFlip }) => {
 34    const containerRef = useRef(null);
 35    const mediaRef = useRef(null);
 36    const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
 37    const [isCenterHovered, setIsCenterHovered] = useState(false);
 38    const [currentMediaIndex, setCurrentMediaIndex] = useState(0);
 39    const [isComponentHovered, setIsComponentHovered] = useState(false);
 40  
 41    useEffect(() => {
 42      console.log('Current media index:', currentMediaIndex);
 43    }, [currentMediaIndex]);
 44  
 45    useEffect(() => {
 46      if (containerRef.current) {
 47        const updateDimensions = () => {
 48          const container = containerRef.current;
 49          const size = Math.min(container.offsetWidth, container.offsetHeight);
 50          setDimensions({ width: size, height: size });
 51        };
 52  
 53        updateDimensions();
 54        window.addEventListener('resize', updateDimensions);
 55        return () => window.removeEventListener('resize', updateDimensions);
 56      }
 57    }, []);
 58  
 59    const renderMedia = () => {
 60      if (!dreamTalkMedia || dreamTalkMedia.length === 0 || !dreamTalkMedia[currentMediaIndex]) {
 61        return null;
 62      }
 63  
 64      const currentMedia = dreamTalkMedia[currentMediaIndex];
 65  
 66      const commonStyle = {
 67        width: '100%',
 68        height: '100%',
 69        objectFit: 'cover',
 70      };
 71  
 72      switch (currentMedia.type) {
 73        case 'image/jpeg':
 74        case 'image/png':
 75        case 'image/gif':
 76        case 'image/webp':
 77          return <img key={`img-${currentMediaIndex}`} ref={mediaRef} src={currentMedia.data} alt={repoName} style={commonStyle} />;
 78        case 'audio/mpeg':
 79        case 'audio/wav':
 80          return (
 81            <div key={`audio-${currentMediaIndex}`} ref={mediaRef} style={{ ...commonStyle, display: 'flex', alignItems: 'center', justifyContent: 'center', background: BLACK }}>
 82              <audio controls src={currentMedia.data} style={{ width: '90%', maxWidth: '200px' }} />
 83            </div>
 84          );
 85        case 'video/mp4':
 86        case 'video/webm':
 87          return <video key={`video-${currentMediaIndex}`} ref={mediaRef} controls src={currentMedia.data} style={commonStyle} />;
 88        default:
 89          return null;
 90      }
 91    };
 92  
 93    const handlePrevMedia = (e) => {
 94      e.stopPropagation();
 95      setCurrentMediaIndex((prevIndex) => {
 96        const newIndex = prevIndex > 0 ? prevIndex - 1 : dreamTalkMedia.length - 1;
 97        console.log('Previous media index:', newIndex);
 98        return newIndex;
 99      });
100    };
101  
102    const handleNextMedia = (e) => {
103      e.stopPropagation();
104      setCurrentMediaIndex((prevIndex) => {
105        const newIndex = prevIndex < dreamTalkMedia.length - 1 ? prevIndex + 1 : 0;
106        console.log('Next media index:', newIndex);
107        return newIndex;
108      });
109    };
110  
111    useEffect(() => {
112      console.log('Current media index changed:', currentMediaIndex);
113      console.log('Current media:', dreamTalkMedia[currentMediaIndex]);
114    }, [currentMediaIndex, dreamTalkMedia]);
115  
116    const handleMouseEnter = () => {
117      setIsComponentHovered(true);
118      onMouseEnter();
119    };
120  
121    const handleMouseLeave = () => {
122      setIsComponentHovered(false);
123      onMouseLeave();
124    };
125  
126    return (
127      <div 
128        ref={containerRef}
129        className="dream-talk" 
130        onClick={() => onClick(repoName)}
131        onContextMenu={(e) => {
132          e.preventDefault();
133          onRightClick(e);
134        }}
135        onMouseEnter={handleMouseEnter}
136        onMouseLeave={handleMouseLeave}
137        style={{
138          position: 'relative',
139          overflow: 'hidden',
140          width: '100%',
141          height: '100%',
142          borderRadius: '50%',
143          border: `5px solid ${borderColor}`,
144          color: WHITE,
145          boxSizing: 'border-box',
146          background: BLACK,
147        }}
148      >
149        <div style={{
150          position: 'absolute',
151          top: '50%',
152          left: '50%',
153          transform: 'translate(-50%, -50%)',
154          width: `${dimensions.width * 0.8}px`,
155          height: `${dimensions.height * 0.8}px`,
156          borderRadius: '50%',
157          overflow: 'hidden',
158        }}>
159          {renderMedia()}
160        </div>
161        <div style={{
162          position: 'absolute',
163          top: '50%',
164          left: '50%',
165          transform: 'translate(-50%, -50%)',
166          width: `${dimensions.width * 0.8}px`,
167          height: `${dimensions.height * 0.8}px`,
168          background: 'radial-gradient(circle, rgba(0,0,0,0) 50%, rgba(0,0,0,0.5) 60%, rgba(0,0,0,1) 70%)',
169          pointerEvents: 'none',
170          borderRadius: '49%',
171        }} />
172        {dreamTalkMedia && dreamTalkMedia.length > 0 && isComponentHovered && (
173          <div style={{
174            position: 'absolute',
175            top: '50%',
176            left: '50%',
177            transform: 'translate(-50%, -50%)',
178            width: `${dimensions.width * 0.8}px`,
179            height: `${dimensions.height * 0.8}px`,
180            display: 'flex',
181            justifyContent: 'center',
182            alignItems: 'center',
183            background: 'rgba(0, 0, 0, 0.7)',
184            color: WHITE,
185            fontSize: '16px',
186            fontWeight: 'bold',
187            borderRadius: '50%',
188            transition: 'opacity 0.3s ease',
189          }}>
190            {repoName}
191          </div>
192        )}
193        <div
194          style={{
195            position: 'absolute',
196            top: '25%',
197            left: '25%',
198            width: '50%',
199            height: '50%',
200            borderRadius: '50%',
201            cursor: 'pointer',
202          }}
203          onMouseEnter={() => setIsCenterHovered(true)}
204          onMouseLeave={() => setIsCenterHovered(false)}
205        />
206        <CenterOverlay repoName={repoName} isVisible={isCenterHovered} />
207        <div style={{
208          position: 'absolute',
209          top: 0,
210          left: 0,
211          width: '100%',
212          height: '100%',
213          display: 'flex',
214          flexDirection: 'column',
215          justifyContent: 'center',
216          alignItems: 'center',
217          padding: '10px',
218          boxSizing: 'border-box',
219          background: !dreamTalkMedia || dreamTalkMedia.length === 0 ? 'rgba(0, 0, 0, 0.7)' : 'transparent',
220          opacity: !dreamTalkMedia || dreamTalkMedia.length === 0 ? 1 : 0,
221          transition: 'opacity 0.3s ease, background 0.3s ease',
222          overflow: 'hidden',
223        }}>
224          <div style={{
225            maxHeight: '100%',
226            overflow: 'auto',
227            display: 'flex',
228            flexDirection: 'column',
229            alignItems: 'center',
230            width: '100%',
231          }}>
232            <h2 style={{ 
233              fontSize: '16px', 
234              margin: '5px 0', 
235              padding: '3px', 
236              textAlign: 'center',
237              wordWrap: 'break-word',
238              overflowWrap: 'break-word',
239              maxWidth: '100%',
240            }}>
241              {repoName}
242            </h2>
243            {metadata && metadata.description && (
244              <p style={{
245                fontSize: '12px',
246                margin: '3px 0',
247                textAlign: 'center',
248                maxWidth: '100%',
249                overflow: 'hidden',
250                textOverflow: 'ellipsis',
251                display: '-webkit-box',
252                WebkitLineClamp: 3,
253                WebkitBoxOrient: 'vertical',
254              }}>
255                {metadata.description}
256              </p>
257            )}
258          </div>
259        </div>
260        {dreamTalkMedia && dreamTalkMedia.length > 1 && (
261          <>
262            <button
263              onClick={handlePrevMedia}
264              style={{
265                position: 'absolute',
266                left: '10px',
267                top: '50%',
268                transform: 'translateY(-50%)',
269                background: 'rgba(0, 0, 0, 0.5)',
270                color: WHITE,
271                border: 'none',
272                borderRadius: '50%',
273                width: '30px',
274                height: '30px',
275                fontSize: '20px',
276                cursor: 'pointer',
277                display: 'flex',
278                justifyContent: 'center',
279                alignItems: 'center',
280              }}
281            >
282              &#8249;
283            </button>
284            <button
285              onClick={handleNextMedia}
286              style={{
287                position: 'absolute',
288                right: '10px',
289                top: '50%',
290                transform: 'translateY(-50%)',
291                background: 'rgba(0, 0, 0, 0.5)',
292                color: WHITE,
293                border: 'none',
294                borderRadius: '50%',
295                width: '30px',
296                height: '30px',
297                fontSize: '20px',
298                cursor: 'pointer',
299                display: 'flex',
300                justifyContent: 'center',
301                alignItems: 'center',
302              }}
303            >
304              &#8250;
305            </button>
306          </>
307        )}
308        <div
309          style={{
310            position: 'absolute',
311            bottom: '10px',
312            left: '50%',
313            transform: 'translateX(-50%)',
314            opacity: 0,
315            transition: 'opacity 0.3s ease',
316          }}
317          className="flip-button-container"
318        >
319          <button
320            onClick={(e) => {
321              e.stopPropagation();
322              onFlip();
323            }}
324            style={{
325              background: BLUE,
326              color: WHITE,
327              border: 'none',
328              borderRadius: '5px',
329              padding: '5px 10px',
330              cursor: 'pointer',
331            }}
332          >
333            Flip
334          </button>
335        </div>
336        {dreamTalkMedia && dreamTalkMedia.length > 0 && (
337          <div style={{
338            position: 'absolute',
339            bottom: '5px',
340            left: '50%',
341            transform: 'translateX(-50%)',
342            color: WHITE,
343            fontSize: '12px',
344            background: 'rgba(0, 0, 0, 0.5)',
345            padding: '2px 5px',
346            borderRadius: '10px',
347          }}>
348            {currentMediaIndex + 1} / {dreamTalkMedia.length}
349          </div>
350        )}
351        <style>
352          {`
353            .dream-talk:hover .flip-button-container {
354              opacity: 1;
355            }
356          `}
357        </style>
358      </div>
359    );
360  };
361  
362  export default React.memo(DreamTalk);