11import { userSettingsStore } from "@/store/UserSettings" ;
2- import { readFile , STATISTICS_FILEPATH , writeFile } from "./../storage/fileAccess" ;
3- import { getStoredJson , storeJson } from "@/storage/localStorage" ;
4- import { debounce , round , uniq } from "lodash-es" ;
2+ import { round } from "lodash-es" ;
53import { RequestStatus } from "./../typings/index" ;
64import { CfIpResponse } from "@/screens/TestRunScreen/model" ;
7- import { deepObserve , IDisposer } from "mobx-utils" ;
85import { action , computed , makeObservable , observable , reaction } from "mobx" ;
6+ import { TestStatisticsStorageSync } from "./TestStatisticsStorageSync" ;
97export type CfIpSummary = {
108 ip : string ;
119 respondSuccessCount : number ;
@@ -25,12 +23,10 @@ export type CfIpStatistics = CfIpSummary & {
2523 downloadSuccessRate : number ;
2624} ;
2725export type CfIpStatisticsMap = Record < string , CfIpSummary > ;
28- const STORAGE_KEY_TEST_STATISTICS = "cf-ip-tester-app__test-statistics" ;
26+
2927export class TestStatistics {
30- statistics : CfIpStatisticsMap = { } ;
31- private unsubscribeSaveToLocalStorage : IDisposer | undefined ;
32- private unsubscribeSaveToDevice : IDisposer | undefined ;
33- private filePath = STATISTICS_FILEPATH ;
28+ readonly statistics : CfIpStatisticsMap = { } ;
29+ private storageSync : TestStatisticsStorageSync ;
3430
3531 public get computedRecordList ( ) : CfIpStatistics [ ] {
3632 const result = this . getRawRecordList ( ) . map ( ( cfIpSummary ) => {
@@ -71,122 +67,26 @@ export class TestStatistics {
7167 addRecord : action ,
7268 updateRecord : action ,
7369 } ) ;
74- this . mergedDeviceDataWithStorage ( )
75- . catch ( ( err ) => {
76- console . log ( err , "merge statistics failed" ) ;
77- } )
78- . then ( ( ) => {
70+ this . storageSync = new TestStatisticsStorageSync ( this . statistics ) ;
71+ this . storageSync
72+ . mergedDeviceDataWithStorage ( )
73+ . then ( ( statistics ) => {
74+ this . changeStatistics ( statistics ) ;
7975 reaction (
8076 ( ) => userSettingsStore . userSetting . isSaveDataToDevice ,
81- this . onSavingDataToDeviceChange . bind ( this ) ,
77+ async ( isSaveDataToDevice ) => {
78+ const statistics = await this . storageSync . changeStoragePlace (
79+ isSaveDataToDevice
80+ ) ;
81+ this . changeStatistics ( statistics ) ;
82+ } ,
8283 { fireImmediately : true }
8384 ) ;
85+ } )
86+ . catch ( ( err ) => {
87+ console . log ( err , "merge statistics failed" ) ;
8488 } ) ;
8589 }
86- private async onSavingDataToDeviceChange ( isSaveDataToDevice : boolean ) {
87- if ( isSaveDataToDevice ) {
88- await this . autoSaveToDevice ( ) ;
89- this . unsubscribeSaveToLocalStorage ?.( ) ;
90- this . resetStorageData ( ) ;
91- return ;
92- }
93- this . autoSaveToLocalStorage ( ) ;
94- this . unsubscribeSaveToDevice ?.( ) ;
95- this . resetDeviceData ( ) ;
96- }
97- private async resetDeviceData ( ) {
98- await writeFile ( this . filePath , "" ) ;
99- }
100- private async resetStorageData ( ) {
101- await storeJson ( STORAGE_KEY_TEST_STATISTICS , { } ) ;
102- }
103- private autoSaveToLocalStorage ( ) {
104- this . unsubscribeSaveToLocalStorage = deepObserve ( this . statistics , ( ) => {
105- storeJson ( STORAGE_KEY_TEST_STATISTICS , this . statistics ) ;
106- } ) ;
107- }
108- private async autoSaveToDevice ( ) {
109- await this . mergedDeviceDataWithStorage ( ) ;
110- this . unsubscribeSaveToDevice = deepObserve (
111- this . statistics ,
112- debounce (
113- ( ) => {
114- writeFile ( this . filePath , JSON . stringify ( this . statistics ) ) ;
115- } ,
116- 3000 ,
117- { leading : true , trailing : true , maxWait : 5000 }
118- )
119- ) ;
120- }
121- private async mergedDeviceDataWithStorage ( ) {
122- const deviceData = await this . getDeviceData ( ) ;
123- const storageData = await getStoredJson < CfIpStatisticsMap > (
124- STORAGE_KEY_TEST_STATISTICS ,
125- { }
126- ) ;
127- const statistics = this . getDeviceStorageMergedData ( deviceData , storageData ) ;
128-
129- this . changeStatistics ( statistics ) ;
130- }
131- private getDeviceStorageMergedData (
132- statisticsMapA : CfIpStatisticsMap ,
133- statisticsMapB : CfIpStatisticsMap
134- ) : CfIpStatisticsMap {
135- const resultMap : CfIpStatisticsMap = { } ;
136- const uniqueKeys = uniq ( [
137- ...Object . keys ( statisticsMapA ) ,
138- ...Object . keys ( statisticsMapB ) ,
139- ] ) ;
140- uniqueKeys . forEach ( ( key ) => {
141- const mapAValue = statisticsMapA [ key ] ;
142- const mapBValue = statisticsMapB [ key ] ;
143- if ( ! mapAValue ) {
144- resultMap [ key ] = mapBValue ;
145- return ;
146- }
147- if ( ! mapBValue ) {
148- resultMap [ key ] = mapAValue ;
149- return ;
150- }
151- resultMap [ key ] = this . mergeStatisticsWithSameKeys ( mapAValue , mapBValue ) ;
152- } ) ;
153- return resultMap ;
154- }
155- private mergeStatisticsWithSameKeys (
156- statisticsA : CfIpSummary ,
157- statisticsB : CfIpSummary
158- ) : CfIpSummary {
159- return {
160- ip : statisticsA . ip ,
161- respondSuccessCount :
162- statisticsA . respondSuccessCount + statisticsB . respondSuccessCount ,
163- respondFailCount :
164- statisticsA . respondFailCount + statisticsB . respondFailCount ,
165- totalRespondTime :
166- statisticsA . totalRespondTime + statisticsB . totalRespondTime ,
167- downloadSuccessCount :
168- statisticsA . downloadSuccessCount + statisticsB . downloadSuccessCount ,
169- downloadFailCount :
170- statisticsA . downloadFailCount + statisticsB . downloadFailCount ,
171- totalDownloadSpeed : round (
172- statisticsA . totalDownloadSpeed + statisticsB . totalDownloadSpeed ,
173- 4
174- ) ,
175- } ;
176- }
177- private async getDeviceData ( ) : Promise < CfIpStatisticsMap > {
178- const jsonStr = await readFile ( this . filePath ) ;
179- if ( ! jsonStr ) {
180- return { } ;
181- }
182- let result = { } ;
183- try {
184- result = JSON . parse ( jsonStr ) ;
185- } catch ( error ) {
186- return { } ;
187- }
188- return result ;
189- }
19090 isRecordExist ( cfIpResponse : CfIpResponse ) {
19191 return ! ! this . statistics [ cfIpResponse . ip ] ;
19292 }
@@ -231,8 +131,16 @@ export class TestStatistics {
231131 private getRecord ( ip : string ) {
232132 return this . statistics [ ip ] ;
233133 }
134+ /**
135+ * @description keep the reference to make sure it trackable for other places, especially for TestStatisticsStorageSync. Seems not a good way, except provide vm to TestStatisticsStorageSync
136+ */
234137 changeStatistics ( statistics : CfIpStatisticsMap ) {
235- this . statistics = statistics ;
138+ Object . keys ( this . statistics ) . forEach ( ( key ) => {
139+ delete this . statistics [ key ] ;
140+ } ) ;
141+ Object . keys ( statistics ) . forEach ( ( key ) => {
142+ this . statistics [ key ] = statistics [ key ] ;
143+ } ) ;
236144 }
237145 private getRawRecordList ( ) {
238146 return Object . values ( this . statistics ) ;
@@ -250,11 +158,10 @@ export class TestStatistics {
250158 return cfIpResponse . downloadSpeedTestStatus === RequestStatus . Error ;
251159 }
252160 public async clear ( ) {
253- await this . resetDeviceData ( ) ;
254- await this . resetStorageData ( ) ;
161+ await this . storageSync . clear ( ) ;
255162 }
256163}
257164
258165const testStatisticsStore = new TestStatistics ( ) ;
259166
260- export { testStatisticsStore } ;
167+ export { testStatisticsStore } ;
0 commit comments