github.com/minio/console@v1.4.1/web-app/src/screens/Console/ObjectBrowser/objectBrowserSlice.ts (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2022 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 import { createSlice, PayloadAction } from "@reduxjs/toolkit"; 18 import { IFileItem, ObjectBrowserState } from "./types"; 19 import { 20 BucketObjectItem, 21 IRestoreLocalObjectList, 22 } from "../Buckets/ListBuckets/Objects/ListObjects/types"; 23 import { 24 BucketVersioningResponse, 25 GetBucketRetentionConfig, 26 } from "api/consoleApi"; 27 import { AppState } from "store"; 28 29 const defaultRewind = { 30 rewindEnabled: false, 31 bucketToRewind: "", 32 dateToRewind: null, 33 }; 34 35 const initialState: ObjectBrowserState = { 36 selectedBucket: "", 37 versionsMode: false, 38 reloadObjectsList: false, 39 requestInProgress: true, 40 objectDetailsOpen: false, 41 loadingVersions: true, 42 loadingObjectInfo: true, 43 connectionError: false, 44 rewind: { 45 ...defaultRewind, 46 }, 47 objectManager: { 48 objectsToManage: [], 49 managerOpen: false, 50 newItems: false, 51 startedItems: [], 52 currentDownloads: [], 53 currentUploads: [], 54 }, 55 searchObjects: "", 56 versionedFile: "", 57 searchVersions: "", 58 selectedVersion: "", 59 showDeleted: false, 60 selectedInternalPaths: null, 61 simplePath: null, 62 // object browser 63 records: [], 64 loadingVersioning: true, 65 versionInfo: {}, 66 lockingEnabled: false, 67 loadingLocking: true, 68 selectedObjects: [], 69 downloadRenameModal: null, 70 selectedPreview: null, 71 previewOpen: false, 72 shareFileModalOpen: false, 73 anonymousAccessOpen: false, 74 retentionConfig: { 75 mode: undefined, 76 unit: undefined, 77 validity: 0, 78 }, 79 longFileOpen: false, 80 maxShareLinkExpTime: 0, 81 }; 82 83 export const objectBrowserSlice = createSlice({ 84 name: "objectBrowser", 85 initialState, 86 reducers: { 87 setRewindEnable: ( 88 state, 89 action: PayloadAction<{ 90 state: boolean; 91 bucket: string; 92 dateRewind: any; 93 }>, 94 ) => { 95 state.rewind.rewindEnabled = action.payload.state; 96 state.rewind.bucketToRewind = action.payload.bucket; 97 state.rewind.dateToRewind = action.payload.dateRewind; 98 }, 99 resetRewind: (state) => { 100 state.rewind.rewindEnabled = false; 101 state.rewind.bucketToRewind = ""; 102 state.rewind.dateToRewind = null; 103 }, 104 setVersionsModeEnabled: ( 105 state, 106 action: PayloadAction<{ 107 status: boolean; 108 objectName?: string; 109 }>, 110 ) => { 111 let objN = ""; 112 if (action.payload.objectName) { 113 objN = action.payload.objectName; 114 } 115 const objectN = !action.payload.status ? "" : objN; 116 state.versionsMode = action.payload.status; 117 state.versionedFile = objectN; 118 state.selectedVersion = ""; 119 }, 120 setNewObject: (state, action: PayloadAction<IFileItem>) => { 121 state.objectManager.objectsToManage.push(action.payload); 122 state.objectManager.newItems = true; 123 }, 124 updateProgress: ( 125 state, 126 action: PayloadAction<{ 127 instanceID: string; 128 progress: number; 129 }>, 130 ) => { 131 const itemUpdate = state.objectManager.objectsToManage.findIndex( 132 (item) => item.instanceID === action.payload.instanceID, 133 ); 134 135 if (itemUpdate === -1) { 136 return; 137 } 138 139 state.objectManager.objectsToManage[itemUpdate].percentage = 140 action.payload.progress; 141 state.objectManager.objectsToManage[itemUpdate].waitingForFile = false; 142 }, 143 completeObject: (state, action: PayloadAction<string>) => { 144 const objectToComplete = state.objectManager.objectsToManage.findIndex( 145 (item) => item.instanceID === action.payload, 146 ); 147 148 if (objectToComplete === -1) { 149 return; 150 } 151 152 state.objectManager.objectsToManage[objectToComplete].percentage = 100; 153 state.objectManager.objectsToManage[objectToComplete].waitingForFile = 154 false; 155 state.objectManager.objectsToManage[objectToComplete].done = true; 156 157 // We cancel from in-progress lists 158 const type = state.objectManager.objectsToManage[objectToComplete].type; 159 const ID = state.objectManager.objectsToManage[objectToComplete].ID; 160 161 if (type === "download") { 162 state.objectManager.currentDownloads = 163 state.objectManager.currentDownloads.filter((item) => item !== ID); 164 } else if (type === "upload") { 165 state.objectManager.currentUploads = 166 state.objectManager.currentUploads.filter((item) => item !== ID); 167 } 168 }, 169 failObject: ( 170 state, 171 action: PayloadAction<{ instanceID: string; msg: string }>, 172 ) => { 173 const objectToFail = state.objectManager.objectsToManage.findIndex( 174 (item) => item.instanceID === action.payload.instanceID, 175 ); 176 177 state.objectManager.objectsToManage[objectToFail].failed = true; 178 state.objectManager.objectsToManage[objectToFail].waitingForFile = false; 179 state.objectManager.objectsToManage[objectToFail].done = true; 180 state.objectManager.objectsToManage[objectToFail].errorMessage = 181 action.payload.msg; 182 183 // We cancel from in-progress lists 184 const type = state.objectManager.objectsToManage[objectToFail].type; 185 const ID = state.objectManager.objectsToManage[objectToFail].ID; 186 187 if (type === "download") { 188 state.objectManager.currentDownloads = 189 state.objectManager.currentDownloads.filter((item) => item !== ID); 190 } else if (type === "upload") { 191 state.objectManager.currentUploads = 192 state.objectManager.currentUploads.filter((item) => item !== ID); 193 } 194 }, 195 cancelObjectInList: (state, action: PayloadAction<string>) => { 196 const objectToCancel = state.objectManager.objectsToManage.findIndex( 197 (item) => item.instanceID === action.payload, 198 ); 199 200 if (objectToCancel === -1) { 201 return { ...state }; 202 } 203 204 state.objectManager.objectsToManage[objectToCancel].cancelled = true; 205 state.objectManager.objectsToManage[objectToCancel].done = true; 206 state.objectManager.objectsToManage[objectToCancel].percentage = 0; 207 208 // We cancel from in-progress lists 209 const type = state.objectManager.objectsToManage[objectToCancel].type; 210 const ID = state.objectManager.objectsToManage[objectToCancel].ID; 211 212 if (type === "download") { 213 state.objectManager.currentDownloads = 214 state.objectManager.currentDownloads.filter((item) => item !== ID); 215 } else if (type === "upload") { 216 state.objectManager.currentUploads = 217 state.objectManager.currentUploads.filter((item) => item !== ID); 218 } 219 }, 220 deleteFromList: (state, action: PayloadAction<string>) => { 221 const notObject = state.objectManager.objectsToManage.filter( 222 (element) => element.instanceID !== action.payload, 223 ); 224 225 state.objectManager.objectsToManage = notObject; 226 state.objectManager.managerOpen = 227 notObject.length === 0 ? false : state.objectManager.managerOpen; 228 }, 229 cleanList: (state) => { 230 const nonCompletedList = state.objectManager.objectsToManage.filter( 231 (item) => item.percentage !== 100, 232 ); 233 state.objectManager.objectsToManage = nonCompletedList; 234 state.objectManager.managerOpen = 235 nonCompletedList.length === 0 ? false : state.objectManager.managerOpen; 236 state.objectManager.newItems = false; 237 }, 238 toggleList: (state) => { 239 state.objectManager.managerOpen = !state.objectManager.managerOpen; 240 state.objectManager.newItems = false; 241 }, 242 openList: (state) => { 243 state.objectManager.managerOpen = true; 244 }, 245 closeList: (state) => { 246 state.objectManager.managerOpen = false; 247 }, 248 setSearchObjects: (state, action: PayloadAction<string>) => { 249 state.searchObjects = action.payload; 250 }, 251 setRequestInProgress: (state, action: PayloadAction<boolean>) => { 252 state.requestInProgress = action.payload; 253 }, 254 setSearchVersions: (state, action: PayloadAction<string>) => { 255 state.searchVersions = action.payload; 256 }, 257 setSelectedVersion: (state, action: PayloadAction<string>) => { 258 state.selectedVersion = action.payload; 259 }, 260 setShowDeletedObjects: (state, action: PayloadAction<boolean>) => { 261 state.showDeleted = action.payload; 262 }, 263 setLoadingVersions: (state, action: PayloadAction<boolean>) => { 264 state.loadingVersions = action.payload; 265 }, 266 setLoadingObjectInfo: (state, action: PayloadAction<boolean>) => { 267 state.loadingObjectInfo = action.payload; 268 }, 269 setObjectDetailsView: (state, action: PayloadAction<boolean>) => { 270 state.objectDetailsOpen = action.payload; 271 state.selectedInternalPaths = action.payload 272 ? state.selectedInternalPaths 273 : null; 274 }, 275 setSelectedObjectView: (state, action: PayloadAction<string | null>) => { 276 state.selectedInternalPaths = action.payload; 277 }, 278 setSimplePathHandler: (state, action: PayloadAction<string>) => { 279 state.simplePath = action.payload; 280 }, 281 newDownloadInit: (state, action: PayloadAction<string>) => { 282 state.objectManager.currentDownloads = [ 283 ...state.objectManager.currentDownloads, 284 action.payload, 285 ]; 286 }, 287 newUploadInit: (state, action: PayloadAction<string>) => { 288 state.objectManager.currentUploads = [ 289 ...state.objectManager.currentUploads, 290 action.payload, 291 ]; 292 }, 293 setRecords: (state, action: PayloadAction<BucketObjectItem[]>) => { 294 state.records = action.payload; 295 }, 296 setLoadingVersioning: (state, action: PayloadAction<boolean>) => { 297 state.loadingVersioning = action.payload; 298 }, 299 setIsVersioned: ( 300 state, 301 action: PayloadAction<BucketVersioningResponse>, 302 ) => { 303 state.versionInfo = action.payload; 304 }, 305 setLockingEnabled: (state, action: PayloadAction<boolean | undefined>) => { 306 state.lockingEnabled = action.payload; 307 }, 308 setLoadingLocking: (state, action: PayloadAction<boolean>) => { 309 state.loadingLocking = action.payload; 310 }, 311 newMessage: (state, action: PayloadAction<BucketObjectItem[]>) => { 312 state.records = [...state.records, ...action.payload]; 313 }, 314 resetMessages: (state) => { 315 state.records = []; 316 }, 317 setReloadObjectsList: (state, action: PayloadAction<boolean>) => { 318 state.reloadObjectsList = action.payload; 319 320 // If we initialize a request, then we must clean the records list 321 if (action.payload) { 322 state.records = []; 323 } 324 }, 325 setSelectedObjects: (state, action: PayloadAction<string[]>) => { 326 state.selectedObjects = action.payload; 327 }, 328 setDownloadRenameModal: ( 329 state, 330 action: PayloadAction<BucketObjectItem | null>, 331 ) => { 332 state.downloadRenameModal = action.payload; 333 }, 334 setSelectedPreview: ( 335 state, 336 action: PayloadAction<BucketObjectItem | null>, 337 ) => { 338 state.selectedPreview = action.payload; 339 }, 340 setPreviewOpen: (state, action: PayloadAction<boolean>) => { 341 state.previewOpen = action.payload; 342 }, 343 setShareFileModalOpen: (state, action: PayloadAction<boolean>) => { 344 state.shareFileModalOpen = action.payload; 345 }, 346 restoreLocalObjectList: ( 347 state, 348 action: PayloadAction<IRestoreLocalObjectList>, 349 ) => { 350 const indexToReplace = state.records.findIndex( 351 (element) => element.name === action.payload.prefix, 352 ); 353 354 if (indexToReplace >= 0) { 355 state.records[indexToReplace].delete_flag = 356 action.payload.objectInfo.is_delete_marker; 357 state.records[indexToReplace].size = 358 action.payload.objectInfo.size || 0; 359 } 360 }, 361 setRetentionConfig: ( 362 state, 363 action: PayloadAction<GetBucketRetentionConfig | null>, 364 ) => { 365 state.retentionConfig = action.payload; 366 }, 367 setSelectedBucket: (state, action: PayloadAction<string>) => { 368 state.selectedBucket = action.payload; 369 }, 370 setLongFileOpen: (state, action: PayloadAction<boolean>) => { 371 state.longFileOpen = action.payload; 372 }, 373 setAnonymousAccessOpen: (state, action: PayloadAction<boolean>) => { 374 state.anonymousAccessOpen = action.payload; 375 }, 376 setMaxShareLinkExpTime: (state, action: PayloadAction<number>) => { 377 state.maxShareLinkExpTime = action.payload; 378 }, 379 errorInConnection: (state, action: PayloadAction<boolean>) => { 380 state.connectionError = action.payload; 381 if (action.payload) { 382 state.requestInProgress = false; 383 state.loadingObjectInfo = false; 384 state.objectDetailsOpen = false; 385 } 386 }, 387 }, 388 }); 389 export const { 390 setRewindEnable, 391 resetRewind, 392 setVersionsModeEnabled, 393 setNewObject, 394 updateProgress, 395 completeObject, 396 failObject, 397 deleteFromList, 398 cleanList, 399 toggleList, 400 openList, 401 closeList, 402 setSearchObjects, 403 setRequestInProgress, 404 cancelObjectInList, 405 setSearchVersions, 406 setSelectedVersion, 407 setShowDeletedObjects, 408 setLoadingVersions, 409 setLoadingObjectInfo, 410 setObjectDetailsView, 411 setSelectedObjectView, 412 setSimplePathHandler, 413 newDownloadInit, 414 newUploadInit, 415 setRecords, 416 resetMessages, 417 setLoadingVersioning, 418 setIsVersioned, 419 setLoadingLocking, 420 setLockingEnabled, 421 newMessage, 422 setSelectedObjects, 423 setDownloadRenameModal, 424 setSelectedPreview, 425 setPreviewOpen, 426 setShareFileModalOpen, 427 setReloadObjectsList, 428 restoreLocalObjectList, 429 setRetentionConfig, 430 setSelectedBucket, 431 setLongFileOpen, 432 setAnonymousAccessOpen, 433 setMaxShareLinkExpTime, 434 errorInConnection, 435 } = objectBrowserSlice.actions; 436 437 export const maxShareLinkExpTime = (state: AppState) => 438 state.objectBrowser.maxShareLinkExpTime; 439 440 export default objectBrowserSlice.reducer;