1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 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 */ 15 16interface DataCollectionChangeListener { 17 batchUpdate(operations: BatchOperation[]): void; 18} 19 20interface ItemsDeleted { 21 kind: 'deleted'; 22 startIndex: number; 23 count: number; 24} 25 26interface ItemsAdded { 27 kind: 'added'; 28 startIndex: number; 29 count: number; 30} 31 32interface ItemUpdated { 33 kind: 'updated'; 34 index: number; 35} 36 37interface CollectionReloaded { 38 kind: 'reloaded'; 39 totalCount: number; 40} 41 42interface ItemsSwapped { 43 kind: 'swapped'; 44 a: number; 45 b: number; 46} 47 48interface ItemMoved { 49 kind: 'moved'; 50 from: number; 51 to: number; 52} 53 54type BatchOperation = ItemsDeleted | ItemsAdded | ItemUpdated | CollectionReloaded | ItemsSwapped | ItemMoved; 55 56// eslint-disable-next-line @typescript-eslint/no-unused-vars 57class DataSourceObserver implements DataChangeListener { 58 private dataSource: IDataSource; 59 60 constructor(private readonly simpleChangeListener: DataCollectionChangeListener) {} 61 62 onDataReloaded(): void { 63 this.simpleChangeListener.batchUpdate([ 64 { 65 kind: 'reloaded', 66 totalCount: this.dataSource.totalCount(), 67 }, 68 ]); 69 } 70 71 onDataAdded(index: number): void { 72 this.simpleChangeListener.batchUpdate([ 73 { 74 kind: 'added', 75 startIndex: index, 76 count: 1, 77 }, 78 ]); 79 } 80 81 onDataAdd(index: number): void { 82 this.onDataAdded(index); 83 } 84 85 onDataMoved(from: number, to: number): void { 86 this.simpleChangeListener.batchUpdate([ 87 { 88 kind: 'swapped', 89 a: from, 90 b: to, 91 }, 92 ]); 93 } 94 95 onDataMove(from: number, to: number): void { 96 this.onDataMoved(from, to); 97 } 98 99 onDataDeleted(index: number): void { 100 this.simpleChangeListener.batchUpdate([ 101 { 102 kind: 'deleted', 103 startIndex: index, 104 count: 1, 105 }, 106 ]); 107 } 108 109 onDataDelete(index: number): void { 110 this.onDataDeleted(index); 111 } 112 113 onDataChanged(index: number): void { 114 this.simpleChangeListener.batchUpdate([ 115 { 116 kind: 'updated', 117 index, 118 }, 119 ]); 120 } 121 122 onDataChange(index: number): void { 123 this.onDataChanged(index); 124 } 125 126 onDatasetChange(dataOperations: DataOperation[]): void { 127 const operations: BatchOperation[] = []; 128 dataOperations.forEach((operation) => { 129 switch (operation.type) { 130 case 'add': 131 case 'delete': 132 if (operation.count === undefined || operation.count > 0) { 133 operations.push({ 134 kind: operation.type === 'add' ? 'added' : 'deleted', 135 startIndex: operation.index, 136 count: operation.count ?? 1, 137 }); 138 } 139 break; 140 case 'change': 141 operations.push({ 142 kind: 'updated', 143 index: operation.index, 144 }); 145 break; 146 case 'reload': 147 operations.push({ 148 kind: 'reloaded', 149 totalCount: this.dataSource.totalCount(), 150 }); 151 break; 152 case 'exchange': 153 operations.push({ 154 kind: 'swapped', 155 a: operation.index.start, 156 b: operation.index.end, 157 }); 158 break; 159 case 'move': 160 operations.push({ 161 kind: 'moved', 162 from: operation.index.from, 163 to: operation.index.to, 164 }); 165 break; 166 default: 167 assertNever(operation); 168 } 169 }); 170 this.simpleChangeListener.batchUpdate(operations); 171 } 172 173 setDataSource(dataSource: IDataSource): void { 174 if (this.dataSource) { 175 this.dataSource.unregisterDataChangeListener(this); 176 } 177 this.dataSource = dataSource; 178 this.dataSource.registerDataChangeListener(this); 179 this.onDataReloaded(); 180 } 181} 182