| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- 'use client';
- import { useEffect, useRef } from 'react';
- import { useCryptoContext } from '@/contexts/cryptoProvider';
- import useTrades from '@/hooks/useTrades';
- import './trade-history.scss';
- interface TradeSoundHandle {
- playTradeSound: (side: 'BID' | 'ASK', tradeVolume?: number) => void;
- muted: boolean;
- toggleMute: () => void;
- }
- interface TradeHistoryProps {
- tradeSound: TradeSoundHandle;
- }
- function formatPrice(price: number): string {
- if (price >= 1000) {
- return price.toLocaleString('ko-KR', { maximumFractionDigits: 0 });
- }
- if (price >= 1) {
- return price.toLocaleString('ko-KR', { maximumFractionDigits: 2 });
- }
- return price.toLocaleString('ko-KR', { maximumFractionDigits: 4 });
- }
- function formatSize(size: number): string {
- if (size >= 1) {
- return size.toFixed(4);
- }
- return size.toFixed(6);
- }
- function formatTime(timestamp: number): string {
- const date = new Date(timestamp);
- return date.toLocaleTimeString('ko-KR', {
- hour: '2-digit',
- minute: '2-digit',
- second: '2-digit',
- hour12: false,
- });
- }
- export default function TradeHistory({ tradeSound }: TradeHistoryProps) {
- const { selectedMarket, tickers } = useCryptoContext();
- const trades = useTrades(selectedMarket);
- const ticker = tickers.get(selectedMarket);
- const { playTradeSound, muted, toggleMute } = tradeSound;
- const prevTradeIdRef = useRef<number>(0);
- // 체결 시 효과음 재생
- useEffect(() => {
- if (trades.length === 0) return;
- const latest = trades[0];
- if (latest.sequentialId !== prevTradeIdRef.current && prevTradeIdRef.current !== 0) {
- playTradeSound(latest.askBid as 'BID' | 'ASK', latest.tradeVolume);
- }
- prevTradeIdRef.current = latest.sequentialId;
- }, [trades, playTradeSound]);
- // 체결 강도 계산: 매수 비율 (%)
- let tradeIntensity: number | null = null;
- let intensityClass = '';
- if (ticker) {
- const total = ticker.accAskVolume + ticker.accBidVolume;
- if (total > 0) {
- tradeIntensity = (ticker.accBidVolume / total) * 100;
- intensityClass = tradeIntensity >= 50 ? 'up' : 'down';
- }
- }
- return (
- <div className='trade-history'>
- <div className='trade-title'>
- <span className='trade-title-left'>
- <span>체결 내역</span>
- <button
- className={`sound-toggle ${muted ? 'muted' : 'active'}`}
- onClick={toggleMute}
- title={muted ? '효과음 켜기' : '효과음 끄기'}
- >
- {muted ? '🔇' : '🔊'}
- </button>
- </span>
- {tradeIntensity !== null && (
- <span className={`trade-intensity ${intensityClass}`}>
- 체결강도 {tradeIntensity.toFixed(1)}%
- </span>
- )}
- </div>
- <div className='trade-header'>
- <span>체결가</span>
- <span>체결량</span>
- <span>시간</span>
- </div>
- <div className='trade-body'>
- {trades.length === 0 ? (
- <div className='trade-loading'>준비 중...</div>
- ) : (
- trades.map((trade, i) => (
- <div
- key={`${trade.sequentialId}-${i}`}
- className={`trade-row ${trade.askBid === 'BID' ? 'buy' : 'sell'}`}
- >
- <span className='price'>{formatPrice(trade.tradePrice)}</span>
- <span className='size'>{formatSize(trade.tradeVolume)}</span>
- <span className='time'>{formatTime(trade.tradeTimestamp)}</span>
- </div>
- ))
- )}
- </div>
- </div>
- );
- }
|