1// Copyright (C) 2020 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {RecordConfig} from '../common/state'; 16import {validateRecordConfig} from '../controller/validate_config'; 17 18const LOCAL_STORAGE_RECORD_CONFIGS_KEY = 'recordConfigs'; 19 20class NamedRecordConfig { 21 title: string; 22 config: RecordConfig; 23 key: string; 24 25 constructor(title: string, config: RecordConfig, key: string) { 26 this.title = title; 27 this.config = this.validateData(config); 28 this.key = key; 29 } 30 31 private validateData(config: {}): RecordConfig { 32 const validConfig = validateRecordConfig(config); 33 if (validConfig.errorMessage) { 34 // TODO(bsebastien): Show a warning message to the user in the UI. 35 console.warn(validConfig.errorMessage); 36 } 37 return validConfig.config; 38 } 39 40 static isValid(jsonObject: object): jsonObject is NamedRecordConfig { 41 return (jsonObject as NamedRecordConfig).title !== undefined && 42 (jsonObject as NamedRecordConfig).config !== undefined && 43 (jsonObject as NamedRecordConfig).key !== undefined; 44 } 45} 46 47export class RecordConfigStore { 48 recordConfigs: NamedRecordConfig[]; 49 50 constructor() { 51 this.recordConfigs = []; 52 this.reloadFromLocalStorage(); 53 } 54 55 save(recordConfig: RecordConfig, title?: string): void { 56 // We reload from local storage in case of concurrent 57 // modifications of local storage from a different tab. 58 this.reloadFromLocalStorage(); 59 60 const config = new NamedRecordConfig( 61 title ? title : new Date().toJSON(), recordConfig, new Date().toJSON()); 62 63 this.recordConfigs.push(config); 64 window.localStorage.setItem( 65 LOCAL_STORAGE_RECORD_CONFIGS_KEY, JSON.stringify(this.recordConfigs)); 66 } 67 68 delete(key: string): void { 69 // We reload from local storage in case of concurrent 70 // modifications of local storage from a different tab. 71 this.reloadFromLocalStorage(); 72 73 let idx = -1; 74 for (let i = 0; i < this.recordConfigs.length; ++i) { 75 if (this.recordConfigs[i].key === key) { 76 idx = i; 77 break; 78 } 79 } 80 81 if (idx !== -1) { 82 this.recordConfigs.splice(idx, 1); 83 window.localStorage.setItem( 84 LOCAL_STORAGE_RECORD_CONFIGS_KEY, JSON.stringify(this.recordConfigs)); 85 } else { 86 // TODO(bsebastien): Show a warning message to the user in the UI. 87 console.warn('The config selected doesn\'t exist any more'); 88 } 89 } 90 91 private clearRecordConfigs(): void { 92 this.recordConfigs = []; 93 window.localStorage.setItem( 94 LOCAL_STORAGE_RECORD_CONFIGS_KEY, JSON.stringify([])); 95 } 96 97 reloadFromLocalStorage(): void { 98 const configsLocalStorage = 99 window.localStorage.getItem(LOCAL_STORAGE_RECORD_CONFIGS_KEY); 100 101 if (configsLocalStorage) { 102 try { 103 const validConfigLocalStorage: NamedRecordConfig[] = []; 104 const parsedConfigsLocalStorage = JSON.parse(configsLocalStorage); 105 106 // Check if it's an array. 107 if (!Array.isArray(parsedConfigsLocalStorage)) { 108 this.clearRecordConfigs(); 109 return; 110 } 111 112 for (let i = 0; i < parsedConfigsLocalStorage.length; ++i) { 113 if (!NamedRecordConfig.isValid(parsedConfigsLocalStorage[i])) { 114 continue; 115 } 116 validConfigLocalStorage.push(new NamedRecordConfig( 117 parsedConfigsLocalStorage[i].title, 118 parsedConfigsLocalStorage[i].config, 119 parsedConfigsLocalStorage[i].key)); 120 } 121 122 this.recordConfigs = validConfigLocalStorage; 123 window.localStorage.setItem( 124 LOCAL_STORAGE_RECORD_CONFIGS_KEY, 125 JSON.stringify(validConfigLocalStorage)); 126 } catch (e) { 127 this.clearRecordConfigs(); 128 } 129 } else { 130 this.clearRecordConfigs(); 131 } 132 } 133} 134 135// This class is a singleton to avoid many instances 136// conflicting as they attempt to edit localStorage. 137export const recordConfigStore = new RecordConfigStore(); 138