{ "version": 3, "sources": ["src/app/cards/bulk-selection.service.ts"], "sourcesContent": ["import {Injectable} from '@angular/core';\r\nimport {NavigationStart, Router} from '@angular/router';\r\nimport {ReplaySubject} from 'rxjs';\r\nimport {filter} from 'rxjs/operators';\r\nimport {Action, ActionFactoryService, ActionItem} from '../_services/action-factory.service';\r\n\r\ntype DataSource = 'volume' | 'chapter' | 'special' | 'series' | 'bookmark' | 'sideNavStream' | 'collection' | 'readingList';\r\n\r\n/**\r\n * Responsible for handling selections on cards. Can handle multiple card sources next to each other in different loops.\r\n * This will clear selections between pages.\r\n *\r\n * Remarks: Page which renders cards is responsible for listening for shift keydown/keyup and updating our state variable.\r\n */\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class BulkSelectionService {\r\n\r\n private debug: boolean = false;\r\n private prevIndex: number = 0;\r\n private prevDataSource!: DataSource;\r\n private selectedCards: { [key: string]: {[key: number]: boolean} } = {};\r\n private dataSourceMax: { [key: string]: number} = {};\r\n public isShiftDown: boolean = false;\r\n\r\n private actionsSource = new ReplaySubject[]>(1);\r\n public actions$ = this.actionsSource.asObservable();\r\n\r\n private selectionsSource = new ReplaySubject(1);\r\n /**\r\n * Number of active selections\r\n */\r\n public selections$ = this.selectionsSource.asObservable();\r\n\r\n constructor(router: Router, private actionFactory: ActionFactoryService) {\r\n router.events\r\n .pipe(filter(event => event instanceof NavigationStart))\r\n .subscribe(() => {\r\n this.deselectAll();\r\n this.dataSourceMax = {};\r\n this.prevIndex = 0;\r\n });\r\n }\r\n\r\n handleCardSelection(dataSource: DataSource, index: number, maxIndex: number, wasSelected: boolean) {\r\n if (this.isShiftDown) {\r\n\r\n if (dataSource === this.prevDataSource) {\r\n this.debugLog('Selecting ' + dataSource + ' cards from ' + this.prevIndex + ' to ' + index + ' as ' + !wasSelected);\r\n this.selectCards(dataSource, this.prevIndex, index, !wasSelected);\r\n } else {\r\n const isForwardSelection = index > this.prevIndex;\r\n\r\n if (isForwardSelection) {\r\n this.debugLog('Selecting ' + this.prevDataSource + ' cards from ' + this.prevIndex + ' to ' + this.dataSourceMax[this.prevDataSource]);\r\n this.selectCards(this.prevDataSource, this.prevIndex, this.dataSourceMax[this.prevDataSource], !wasSelected);\r\n this.debugLog('Selecting ' + dataSource + ' cards from ' + 0 + ' to ' + index);\r\n this.selectCards(dataSource, 0, index, !wasSelected);\r\n } else {\r\n this.debugLog('Selecting ' + this.prevDataSource + ' cards from ' + 0 + ' to ' + this.prevIndex);\r\n this.selectCards(this.prevDataSource, this.prevIndex, 0, !wasSelected);\r\n this.debugLog('Selecting ' + dataSource + ' cards from ' + index + ' to ' + maxIndex);\r\n this.selectCards(dataSource, index, maxIndex, !wasSelected);\r\n }\r\n }\r\n } else {\r\n this.debugLog('Selecting ' + dataSource + ' cards at ' + index);\r\n this.selectCards(dataSource, index, index, !wasSelected);\r\n }\r\n this.prevIndex = index;\r\n this.prevDataSource = dataSource;\r\n this.dataSourceMax[dataSource] = maxIndex;\r\n this.actionsSource.next(this.getActions(() => {}));\r\n }\r\n\r\n isCardSelected(dataSource: DataSource, index: number) {\r\n if (this.selectedCards.hasOwnProperty(dataSource) && this.selectedCards[dataSource].hasOwnProperty(index)) {\r\n return this.selectedCards[dataSource][index];\r\n }\r\n return false;\r\n }\r\n\r\n selectCards(dataSource: DataSource, from: number, to: number, value: boolean) {\r\n if (!this.selectedCards.hasOwnProperty(dataSource)) {\r\n this.selectedCards[dataSource] = {};\r\n }\r\n\r\n if (from === to) {\r\n this.selectedCards[dataSource][to] = value;\r\n this.selectionsSource.next(this.totalSelections());\r\n return;\r\n }\r\n\r\n if (from > to) {\r\n for (let i = to; i <= from; i++) {\r\n this.selectedCards[dataSource][i] = value;\r\n }\r\n }\r\n\r\n for (let i = from; i <= to; i++) {\r\n this.selectedCards[dataSource][i] = value;\r\n }\r\n this.selectionsSource.next(this.totalSelections());\r\n }\r\n\r\n deselectAll() {\r\n this.selectedCards = {};\r\n this.selectionsSource.next(0);\r\n }\r\n\r\n hasSelections() {\r\n const keys = Object.keys(this.selectedCards);\r\n return keys.filter(key => {\r\n return Object.values(this.selectedCards[key]).filter(item => item).length > 0;\r\n }).length > 0;\r\n }\r\n\r\n totalSelections() {\r\n let sum = 0;\r\n const keys = Object.keys(this.selectedCards);\r\n keys.forEach(key => {\r\n sum += Object.values(this.selectedCards[key]).filter(item => item).length;\r\n });\r\n return sum;\r\n }\r\n\r\n getSelectedCardsForSource(dataSource: DataSource) {\r\n if (!this.selectedCards.hasOwnProperty(dataSource)) return [];\r\n\r\n const ret = [];\r\n for(let k in this.selectedCards[dataSource]) {\r\n if (this.selectedCards[dataSource][k]) {\r\n ret.push(k);\r\n }\r\n }\r\n\r\n return ret;\r\n }\r\n\r\n /**\r\n * Returns the appropriate set of supported actions for the given mix of cards\r\n * @param callback\r\n */\r\n getActions(callback: (action: ActionItem, data: any) => void) {\r\n const allowedActions = [Action.AddToReadingList, Action.MarkAsRead, Action.MarkAsUnread, Action.AddToCollection,\r\n Action.Delete, Action.AddToWantToReadList, Action.RemoveFromWantToReadList];\r\n\r\n if (Object.keys(this.selectedCards).filter(item => item === 'series').length > 0) {\r\n return this.applyFilterToList(this.actionFactory.getSeriesActions(callback), allowedActions);\r\n }\r\n\r\n if (Object.keys(this.selectedCards).filter(item => item === 'bookmark').length > 0) {\r\n return this.actionFactory.getBookmarkActions(callback);\r\n }\r\n\r\n if (Object.keys(this.selectedCards).filter(item => item === 'sideNavStream').length > 0) {\r\n return this.applyFilterToList(this.actionFactory.getSideNavStreamActions(callback), [Action.MarkAsInvisible, Action.MarkAsVisible]);\r\n }\r\n\r\n if (Object.keys(this.selectedCards).filter(item => item === 'collection').length > 0) {\r\n return this.applyFilterToList(this.actionFactory.getCollectionTagActions(callback), [Action.Promote, Action.UnPromote, Action.Delete]);\r\n }\r\n\r\n if (Object.keys(this.selectedCards).filter(item => item === 'readingList').length > 0) {\r\n return this.applyFilterToList(this.actionFactory.getReadingListActions(callback), [Action.Promote, Action.UnPromote, Action.Delete]);\r\n }\r\n\r\n // Chapter/Volume\r\n return this.applyFilterToList(this.actionFactory.getVolumeActions(callback), [...allowedActions, Action.SendTo]);\r\n }\r\n\r\n private debugLog(message: string, extraData?: any) {\r\n if (!this.debug) return;\r\n\r\n if (extraData !== undefined) {\r\n console.log(message, extraData);\r\n } else {\r\n console.log(message);\r\n }\r\n }\r\n\r\n private applyFilter(action: ActionItem, allowedActions: Array) {\r\n let hasValidAction = false;\r\n\r\n // Check if the current action is valid or a submenu\r\n if (action.action === Action.Submenu || allowedActions.includes(action.action)) {\r\n hasValidAction = true;\r\n }\r\n\r\n // If the action has children, filter them recursively\r\n if (action.children && action.children.length > 0) {\r\n action.children = action.children.filter((childAction) => this.applyFilter(childAction, allowedActions));\r\n\r\n // If no valid children remain, the parent submenu should not be considered valid\r\n if (action.children.length === 0 && action.action === Action.Submenu) {\r\n hasValidAction = false;\r\n }\r\n }\r\n\r\n // Return whether this action or its children are valid\r\n return hasValidAction;\r\n }\r\n\r\n\tprivate applyFilterToList(list: Array>, allowedActions: Array): Array> {\r\n\t\tconst actions = list.map((a) => {\r\n\t\t\treturn { ...a };\r\n\t\t});\r\n return actions.filter(action => this.applyFilter(action, allowedActions));\r\n\t}\r\n}\r\n"], "mappings": "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", "names": ["BulkSelectionService", "constructor", "router", "actionFactory", "debug", "prevIndex", "selectedCards", "dataSourceMax", "isShiftDown", "actionsSource", "ReplaySubject", "actions$", "asObservable", "selectionsSource", "selections$", "events", "pipe", "filter", "event", "NavigationStart", "subscribe", "deselectAll", "handleCardSelection", "dataSource", "index", "maxIndex", "wasSelected", "prevDataSource", "debugLog", "selectCards", "next", "getActions", "isCardSelected", "hasOwnProperty", "from", "to", "value", "totalSelections", "i", "hasSelections", "Object", "keys", "key", "values", "item", "length", "sum", "forEach", "getSelectedCardsForSource", "ret", "k", "push", "callback", "allowedActions", "Action", "AddToReadingList", "MarkAsRead", "MarkAsUnread", "AddToCollection", "Delete", "AddToWantToReadList", "RemoveFromWantToReadList", "applyFilterToList", "getSeriesActions", "getBookmarkActions", "getSideNavStreamActions", "MarkAsInvisible", "MarkAsVisible", "getCollectionTagActions", "Promote", "UnPromote", "getReadingListActions", "getVolumeActions", "SendTo", "message", "extraData", "undefined", "console", "log", "applyFilter", "action", "hasValidAction", "Submenu", "includes", "children", "childAction", "list", "map", "a", "__spreadValues", "\u0275\u0275inject", "Router", "ActionFactoryService", "factory", "\u0275fac", "providedIn"] }