1+ import { formatTimeYMD , MILL_PER_DAY } from '@util/time'
2+ import BaseDatabase from './common/base-database'
3+ import { REMAIN_WORD_PREFIX } from './common/constant'
4+
5+ const DB_KEY = REMAIN_WORD_PREFIX + 'TL'
6+
7+ type Item = {
8+ // start
9+ s : number
10+ // duration
11+ d : number
12+ }
13+
14+ type TimelineData = {
15+ [ date : string ] : {
16+ [ host : string ] : Item [ ]
17+ }
18+ }
19+
20+ // If two tick with the same host is near 1 sec, then merge them to one
21+ const MERGE_THRESHOLD = 1000
22+
23+ const canMerge = ( item : Item , tick : timer . timeline . Tick ) => {
24+ const { s : is , d : id } = item
25+ const { start } = tick
26+ return start >= is + id
27+ && start <= id + MERGE_THRESHOLD
28+ }
29+
30+ const isConflict = ( item : Item , tick : timer . timeline . Tick ) => {
31+ const { s : is , d : id } = item
32+ const { start } = tick
33+ return is <= start && start < is + id
34+ }
35+
36+ const merge = ( data : TimelineData , tick : timer . timeline . Tick ) => {
37+ const { start, duration, host } = tick
38+ const date = formatTimeYMD ( start )
39+ const hostData = data [ date ] ?? { }
40+ const items = hostData [ host ] ?? [ ]
41+ items . sort ( ( a , b ) => ( a ?. s ?? 0 ) - ( b ?. s ?? 0 ) )
42+ for ( const item of items ) {
43+ if ( isConflict ( item , tick ) ) {
44+ return
45+ }
46+ if ( canMerge ( item , tick ) ) {
47+ item . d = start + duration - item . s
48+ return
49+ }
50+ }
51+ // normal tick
52+ items . push ( { s : start , d : duration } )
53+ hostData [ host ] = items
54+ data [ date ] = hostData
55+ }
56+
57+ export const TIMELINE_LIFE_CYCLE = 3
58+
59+ const removeOutdated = ( data : TimelineData , currTime : number ) => {
60+ const minDate = formatTimeYMD ( currTime - MILL_PER_DAY * ( TIMELINE_LIFE_CYCLE - 1 ) )
61+ const keys = Object . keys ( data ) . filter ( k => k < minDate )
62+ keys . forEach ( key => delete data [ key ] )
63+ }
64+
65+ class TimelineDatabase extends BaseDatabase {
66+ private async getData ( ) : Promise < TimelineData > {
67+ const data = await this . storage . getOne < TimelineData > ( DB_KEY )
68+ return data ?? { }
69+ }
70+
71+ private setData ( data : TimelineData ) : Promise < void > {
72+ return this . setByKey ( DB_KEY , data )
73+ }
74+
75+ async batchSave ( ticks : timer . timeline . Tick [ ] ) {
76+ const data = await this . getData ( )
77+ ticks . forEach ( tick => {
78+ merge ( data , tick )
79+ removeOutdated ( data , tick . start )
80+ } )
81+ await this . setData ( data )
82+ }
83+
84+ async getAll ( ) : Promise < timer . timeline . Tick [ ] > {
85+ const data = await this . getData ( )
86+ const result : timer . timeline . Tick [ ] = [ ]
87+ Object . values ( data ) . forEach ( hostData => {
88+ Object . entries ( hostData ) . forEach ( ( [ host , items ] ) => {
89+ items . forEach ( ( { s : start , d : duration } ) => result . push ( { host, start, duration } ) )
90+ } )
91+ } )
92+ return result
93+ }
94+
95+ async importData ( _ : any ) : Promise < void > {
96+ // do nothing
97+ }
98+ }
99+ const timelineDatabase = new TimelineDatabase ( )
100+
101+ export default timelineDatabase
0 commit comments