11"use client" ;
22
3+ import React , { useMemo } from 'react' ;
34import ReactECharts from 'echarts-for-react' ;
45import type { EChartsOption , LineSeriesOption } from 'echarts' ;
56import { useTheme } from 'next-themes' ;
@@ -10,57 +11,70 @@ interface EchartsTrendChartProps {
1011 trendData : TrendEntry [ ] ;
1112}
1213
14+ const truncateToSecond = ( timestamp : number ) : number => {
15+ const date = new Date ( timestamp ) ;
16+ date . setMilliseconds ( 0 ) ;
17+ return date . getTime ( ) ;
18+ } ;
19+
20+
1321const EchartsTrendChart : React . FC < EchartsTrendChartProps > = ( { trendData } ) => {
1422 const { theme } = useTheme ( ) ;
1523
16- const truncateToMinute = ( timestamp : number ) : number => {
17- const date = new Date ( timestamp ) ;
18- date . setSeconds ( 0 , 0 ) ;
19- return date . getTime ( ) ;
20- } ;
21-
22- const allTimePoints = new Set < number > ( ) ;
23- trendData . forEach ( user => {
24- user . history . forEach ( point => {
25- allTimePoints . add ( truncateToMinute ( new Date ( point . time ) . getTime ( ) ) ) ;
24+ const sortedTimePoints = useMemo ( ( ) => {
25+ const allTimePoints = new Set < number > ( ) ;
26+ trendData . forEach ( user => {
27+ user . history . forEach ( point => {
28+ allTimePoints . add ( truncateToSecond ( new Date ( point . time ) . getTime ( ) ) ) ;
29+ } ) ;
2630 } ) ;
27- } ) ;
31+ allTimePoints . add ( truncateToSecond ( new Date ( ) . getTime ( ) ) ) ;
2832
29- allTimePoints . add ( truncateToMinute ( new Date ( ) . getTime ( ) ) ) ;
33+ return Array . from ( allTimePoints ) . sort ( ( a , b ) => a - b ) ;
34+ } , [ trendData ] ) ;
3035
31- const sortedTimePoints = Array . from ( allTimePoints ) . sort ( ( a , b ) => a - b ) ;
36+ const seriesData : LineSeriesOption [ ] = useMemo ( ( ) => {
37+ const sortedUserHistories = trendData . map ( user => ( {
38+ ...user ,
39+ history : user . history
40+ . map ( p => ( { ...p , time : truncateToSecond ( new Date ( p . time ) . getTime ( ) ) } ) )
41+ . sort ( ( a , b ) => a . time - b . time ) ,
42+ } ) ) ;
3243
44+ const historyPointers : Record < string , number > = Object . fromEntries ( trendData . map ( u => [ u . user_id , 0 ] ) ) ;
45+ const currentUserScores : Record < string , number > = Object . fromEntries ( trendData . map ( u => [ u . user_id , 0 ] ) ) ;
46+ const seriesDataMap : Record < string , [ number , number ] [ ] > = Object . fromEntries ( trendData . map ( u => [ u . user_id , [ ] ] ) ) ;
3347
34- const seriesData : LineSeriesOption [ ] = trendData . map ( user => {
35- const data = sortedTimePoints . map ( time => {
36- const lastPoint = [ ...user . history ]
37- . filter ( p => new Date ( p . time ) . getTime ( ) <= time )
38- . pop ( ) ;
39- const score = lastPoint ? lastPoint . score : 0 ;
40- return [ time , score ] ;
41- } ) ;
42- return {
48+ for ( const masterTime of sortedTimePoints ) {
49+ for ( const user of sortedUserHistories ) {
50+ while (
51+ historyPointers [ user . user_id ] < user . history . length &&
52+ user . history [ historyPointers [ user . user_id ] ] . time <= masterTime
53+ ) {
54+ currentUserScores [ user . user_id ] = user . history [ historyPointers [ user . user_id ] ] . score ;
55+ historyPointers [ user . user_id ] ++ ;
56+ }
57+ seriesDataMap [ user . user_id ] . push ( [ masterTime , currentUserScores [ user . user_id ] ] ) ;
58+ }
59+ }
60+
61+ return trendData . map ( ( user ) : LineSeriesOption => ( {
4362 name : user . nickname ,
4463 type : 'line' ,
4564 step : 'end' ,
4665 symbol : 'none' ,
47- data : data ,
48- } ;
49- } ) ;
66+ data : seriesDataMap [ user . user_id ] ,
67+ } ) ) ;
68+ } , [ trendData , sortedTimePoints ] ) ;
5069
5170 const option : EChartsOption = {
5271 backgroundColor : 'transparent' ,
5372 tooltip : {
5473 trigger : 'axis' ,
55- axisPointer : {
56- type : 'cross' ,
57- label : {
58- backgroundColor : '#6a7985'
59- }
60- } ,
6174 formatter : ( params : any ) => {
6275 const time = format ( new Date ( params [ 0 ] . axisValue ) , 'yyyy-MM-dd HH:mm:ss' ) ;
6376 let tooltipHtml = `${ time } <br/>` ;
77+ params . sort ( ( a : any , b : any ) => b . value [ 1 ] - a . value [ 1 ] ) ;
6478 params . forEach ( ( param : any ) => {
6579 tooltipHtml += `${ param . marker } ${ param . seriesName } : <strong>${ param . value [ 1 ] } </strong><br/>` ;
6680 } ) ;
@@ -69,18 +83,11 @@ const EchartsTrendChart: React.FC<EchartsTrendChartProps> = ({ trendData }) => {
6983 } ,
7084 legend : {
7185 data : trendData . map ( user => user . nickname ) ,
72- textStyle : {
73- color : theme === 'dark' ? '#ccc' : '#333' ,
74- } ,
86+ textStyle : { color : theme === 'dark' ? '#ccc' : '#333' } ,
7587 bottom : 45 ,
7688 type : 'scroll' ,
7789 } ,
78- grid : {
79- left : '3%' ,
80- right : '50px' ,
81- bottom : 80 ,
82- containLabel : true
83- } ,
90+ grid : { left : '3%' , right : '50px' , bottom : 80 , containLabel : true } ,
8491 toolbox : {
8592 feature : {
8693 saveAsImage : {
@@ -90,48 +97,20 @@ const EchartsTrendChart: React.FC<EchartsTrendChartProps> = ({ trendData }) => {
9097 }
9198 }
9299 } ,
93- xAxis : [
94- {
95- type : 'time' ,
96- axisLabel : {
97- color : theme === 'dark' ? '#ccc' : '#333' ,
98- formatter : ( value : number ) => {
99- return format ( new Date ( value ) , 'yyyy-MM-dd HH:mm' ) ;
100- }
101- }
102- }
103- ] ,
104- yAxis : [
105- {
106- type : 'value' ,
107- axisLabel : {
108- color : theme === 'dark' ? '#ccc' : '#333'
109- }
100+ xAxis : [ {
101+ type : 'time' ,
102+ axisLabel : {
103+ color : theme === 'dark' ? '#ccc' : '#333' ,
104+ formatter : ( value : number ) => format ( new Date ( value ) , 'HH:mm:ss' )
110105 }
111- ] ,
106+ } ] ,
107+ yAxis : [ {
108+ type : 'value' ,
109+ axisLabel : { color : theme === 'dark' ? '#ccc' : '#333' }
110+ } ] ,
112111 dataZoom : [
113- {
114- type : 'slider' ,
115- xAxisIndex : 0 ,
116- start : 0 ,
117- end : 100 ,
118- bottom : 10 ,
119- height : 20 ,
120- } ,
121- {
122- type : 'slider' ,
123- yAxisIndex : 0 ,
124- start : 0 ,
125- end : 100 ,
126- right : 10 ,
127- width : 20 ,
128- } ,
129- {
130- type : 'inside' ,
131- xAxisIndex : 0 ,
132- start : 0 ,
133- end : 100 ,
134- } ,
112+ { type : 'slider' , xAxisIndex : 0 , start : 0 , end : 100 , bottom : 10 , height : 20 } ,
113+ { type : 'inside' , xAxisIndex : 0 , start : 0 , end : 100 } ,
135114 ] ,
136115 series : seriesData ,
137116 } ;
0 commit comments