From 8d2959edfca601fabfa1777f98e6cbc565132114 Mon Sep 17 00:00:00 2001 From: Jesse Chan Date: Tue, 20 Oct 2020 13:22:31 +0800 Subject: [PATCH] API: activity-stream: migrate all diff actions to JSON Patch --- .../torrent-details-modal/TorrentFiles.tsx | 14 +-- .../javascript/stores/TorrentFilterStore.ts | 26 +---- client/src/javascript/stores/TorrentStore.ts | 36 ++---- .../javascript/stores/TransferDataStore.ts | 23 +--- package-lock.json | 12 +- package.json | 2 +- server/middleware/clientActivityStream.ts | 8 +- server/routes/api/torrents.test.ts | 38 +++---- server/services/historyService.ts | 10 +- server/services/taxonomyService.ts | 23 +--- server/services/torrentService.ts | 105 +----------------- shared/constants/diffActionTypes.ts | 18 --- shared/types/ServerEvents.ts | 14 ++- shared/types/Taxonomy.ts | 10 -- shared/types/Torrent.ts | 15 --- shared/types/TransferData.ts | 4 - shared/util/objectUtil.ts | 88 --------------- 17 files changed, 75 insertions(+), 371 deletions(-) delete mode 100644 shared/constants/diffActionTypes.ts delete mode 100644 shared/util/objectUtil.ts diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentFiles.tsx b/client/src/javascript/components/modals/torrent-details-modal/TorrentFiles.tsx index 4c948202..76957346 100644 --- a/client/src/javascript/components/modals/torrent-details-modal/TorrentFiles.tsx +++ b/client/src/javascript/components/modals/torrent-details-modal/TorrentFiles.tsx @@ -1,5 +1,4 @@ import classnames from 'classnames'; -import {deepEqual} from 'fast-equals'; import {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl'; import React from 'react'; @@ -51,17 +50,10 @@ class TorrentFiles extends React.Component { - const taxonomyKey = key as keyof TaxonomyDiffs; - const changes = value as TaxonomyDiffs[keyof TaxonomyDiffs]; - - if (changes == null) { - return; - } - - changes.forEach((change) => { - if (change.action === 'ITEM_REMOVED') { - delete this.taxonomy[taxonomyKey][change.data]; - } else { - this.taxonomy[taxonomyKey] = { - ...this.taxonomy[taxonomyKey], - ...change.data, - }; - } - }); - }); + handleTorrentTaxonomyDiffChange(diff: Operation[]) { + jsonpatch.applyPatch(this.taxonomy, diff); // TODO: This logic is duplicated. Also update it to check for changed // trackers. diff --git a/client/src/javascript/stores/TorrentStore.ts b/client/src/javascript/stores/TorrentStore.ts index 48a2b86c..4a3593c3 100644 --- a/client/src/javascript/stores/TorrentStore.ts +++ b/client/src/javascript/stores/TorrentStore.ts @@ -1,5 +1,7 @@ +import {applyOperation, Operation} from 'fast-json-patch'; + import type {FloodSettings} from '@shared/types/FloodSettings'; -import type {TorrentDetails, TorrentProperties, TorrentListDiff, TorrentList} from '@shared/types/Torrent'; +import type {TorrentDetails, TorrentProperties, TorrentList} from '@shared/types/Torrent'; import AlertStore from './AlertStore'; import AppDispatcher from '../dispatcher/AppDispatcher'; @@ -203,32 +205,14 @@ class TorrentStoreClass extends BaseStore { this.fetchTorrentDetails(hash, true); } - handleTorrentListDiffChange(torrentListDiff: TorrentListDiff) { - Object.keys(torrentListDiff).forEach((torrentHash) => { - const diff = torrentListDiff[torrentHash]; + handleTorrentListDiffChange(torrentListDiffs: Operation[]) { + torrentListDiffs.forEach((diff) => { + applyOperation(this.torrents, diff); + const [, changedTorrentHash, changedProperty] = diff.path.split('/'); - switch (diff.action) { - case 'TORRENT_LIST_ACTION_TORRENT_ADDED': - this.torrents[torrentHash] = diff.data; - break; - case 'TORRENT_LIST_ACTION_TORRENT_DELETED': - if (this.selectedTorrents.includes(torrentHash)) { - this.selectedTorrents = this.selectedTorrents.filter((hash: string) => hash !== torrentHash); - } - - delete this.torrents[torrentHash]; - break; - case 'TORRENT_LIST_ACTION_TORRENT_DETAIL_UPDATED': - if (diff.data == null || this.torrents[torrentHash] == null) { - break; - } - this.torrents[torrentHash] = { - ...this.torrents[torrentHash], - ...diff.data, - }; - break; - default: - break; + if (changedProperty != null) { + // Spread to decouple the object references so components know the properties have changed. + this.torrents[changedTorrentHash] = {...this.torrents[changedTorrentHash]}; } }); diff --git a/client/src/javascript/stores/TransferDataStore.ts b/client/src/javascript/stores/TransferDataStore.ts index 773f2f1f..fc9461c7 100644 --- a/client/src/javascript/stores/TransferDataStore.ts +++ b/client/src/javascript/stores/TransferDataStore.ts @@ -1,9 +1,6 @@ -import type { - TransferDirection, - TransferHistory, - TransferSummary, - TransferSummaryDiff, -} from '@shared/types/TransferData'; +import jsonpatch, {Operation} from 'fast-json-patch'; + +import type {TransferDirection, TransferHistory, TransferSummary} from '@shared/types/TransferData'; import AppDispatcher from '../dispatcher/AppDispatcher'; import BaseStore from './BaseStore'; @@ -50,18 +47,8 @@ class TransferDataStoreClass extends BaseStore { this.emit('CLIENT_TRANSFER_HISTORY_REQUEST_SUCCESS'); } - handleTransferSummaryDiffChange(diff: TransferSummaryDiff) { - diff.forEach((change) => { - if (change.action === 'ITEM_REMOVED') { - delete this.transferSummary[change.data]; - } else { - this.transferSummary = { - ...this.transferSummary, - ...change.data, - }; - } - }); - + handleTransferSummaryDiffChange(diff: Operation[]) { + jsonpatch.applyPatch(this.transferSummary, diff); this.appendCurrentTransferRateToHistory(); this.emit('CLIENT_TRANSFER_SUMMARY_CHANGE'); } diff --git a/package-lock.json b/package-lock.json index 44e0d712..bcc8e5c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8726,12 +8726,6 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "fast-equals": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-2.0.0.tgz", - "integrity": "sha512-u6RBd8cSiLLxAiC04wVsLV6GBFDOXcTCgWkd3wEoFXgidPSoAJENqC9m7Jb2vewSvjBIfXV6icKeh3GTKfIaXA==", - "dev": true - }, "fast-glob": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", @@ -8746,6 +8740,12 @@ "picomatch": "^2.2.1" } }, + "fast-json-patch": { + "version": "3.0.0-1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.0.0-1.tgz", + "integrity": "sha512-6pdFb07cknxvPzCeLsFHStEy+MysPJPgZQ9LbQ/2O67unQF93SNqfdSqnPPl71YMHX+AD8gbl7iuoGFzHEdDuw==", + "dev": true + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", diff --git a/package.json b/package.json index aa8203c2..0f995c0a 100644 --- a/package.json +++ b/package.json @@ -132,7 +132,7 @@ "eslint-plugin-react-hooks": "^4.1.2", "express": "^4.17.1", "express-rate-limit": "^5.1.3", - "fast-equals": "^2.0.0", + "fast-json-patch": "^3.0.0-1", "fast-sort": "^2.2.0", "feedsub": "^0.7.2", "file-loader": "^6.1.1", diff --git a/server/middleware/clientActivityStream.ts b/server/middleware/clientActivityStream.ts index e0652f26..8ca76c11 100644 --- a/server/middleware/clientActivityStream.ts +++ b/server/middleware/clientActivityStream.ts @@ -1,9 +1,9 @@ +import type {Operation} from 'fast-json-patch'; import type {Request, Response} from 'express'; import type TypedEmitter from 'typed-emitter'; import type {HistorySnapshot} from '@shared/constants/historySnapshotTypes'; -import type {TransferHistory, TransferSummaryDiff} from '@shared/types/TransferData'; -import type {TorrentListDiff} from '@shared/types/Torrent'; +import type {TransferHistory} from '@shared/types/TransferData'; import DiskUsage from '../models/DiskUsage'; import ServerEvent from '../models/ServerEvent'; @@ -107,7 +107,7 @@ export default async (req: Request { + ({id, diff}: {id: number; diff: Operation[]}) => { serverEvent.emit(id, 'TORRENT_LIST_DIFF_CHANGE', diff); }, ); @@ -120,7 +120,7 @@ export default async (req: Request { + ({id, diff}: {id: number; diff: Operation[]}) => { serverEvent.emit(id, 'TRANSFER_SUMMARY_DIFF_CHANGE', diff); }, ); diff --git a/server/routes/api/torrents.test.ts b/server/routes/api/torrents.test.ts index 47245308..e8eb57de 100644 --- a/server/routes/api/torrents.test.ts +++ b/server/routes/api/torrents.test.ts @@ -51,6 +51,20 @@ const activityStream = new stream.PassThrough(); const rl = readline.createInterface({input: activityStream}); request.get('/api/activity-stream').send().set('Cookie', [authToken]).pipe(activityStream); +const watchTorrentList = (op: 'add' | 'remove' | 'replace' | 'move' | 'copy' | 'test'): Promise => { + return new Promise((resolve) => { + let eventDetected = false; + rl.on('line', (input) => { + if (eventDetected && input.includes(`"op":"${op}"`)) { + resolve(); + } + if (input.includes('TORRENT_LIST_DIFF_CHANGE')) { + eventDetected = true; + } + }); + }); +}; + describe('POST /api/torrents/add-urls', () => { const addTorrentByURLOptions: AddTorrentByURLOptions = { urls: torrentURLs, @@ -60,13 +74,7 @@ describe('POST /api/torrents/add-urls', () => { start: false, }; - const torrentAdded = new Promise((resolve) => { - rl.on('line', (input) => { - if (input.includes('TORRENT_LIST_ACTION_TORRENT_ADDED')) { - resolve(); - } - }); - }); + const torrentAdded = watchTorrentList('add'); it('Adds torrents to disallowed path via URLs', (done) => { request @@ -149,13 +157,7 @@ describe('POST /api/torrents/add-files', () => { start: false, }; - const torrentAdded = new Promise((resolve) => { - rl.on('line', (input) => { - if (input.includes('TORRENT_LIST_ACTION_TORRENT_ADDED')) { - resolve(); - } - }); - }); + const torrentAdded = watchTorrentList('add'); it('Adds torrents to disallowed path via files', (done) => { request @@ -231,13 +233,7 @@ describe('POST /api/torrents/create', () => { isPrivate: false, }; - const torrentAdded = new Promise((resolve) => { - rl.on('line', (input) => { - if (input.includes('TORRENT_LIST_ACTION_TORRENT_ADDED')) { - resolve(); - } - }); - }); + const torrentAdded = watchTorrentList('add'); it('Creates a torrent', (done) => { fs.writeFileSync(path.join(tempDirectory, 'dummy'), 'test'); diff --git a/server/services/historyService.ts b/server/services/historyService.ts index 2b3ed566..58da6198 100644 --- a/server/services/historyService.ts +++ b/server/services/historyService.ts @@ -1,12 +1,12 @@ -import type {DiffAction} from '@shared/constants/diffActionTypes'; +import jsonpatch, {Operation} from 'fast-json-patch'; + import type {HistorySnapshot} from '@shared/constants/historySnapshotTypes'; -import type {TransferHistory, TransferSummary, TransferSummaryDiff} from '@shared/types/TransferData'; +import type {TransferHistory, TransferSummary} from '@shared/types/TransferData'; import BaseService from './BaseService'; import config from '../../config'; import HistoryEra from '../models/HistoryEra'; import historySnapshotTypes from '../../shared/constants/historySnapshotTypes'; -import objectUtil from '../../shared/util/objectUtil'; type HistorySnapshotEvents = { // TODO: Switch to string literal template type when TypeScript 4.1 is released. @@ -15,7 +15,7 @@ type HistorySnapshotEvents = { }; interface HistoryServiceEvents extends HistorySnapshotEvents { - TRANSFER_SUMMARY_DIFF_CHANGE: (payload: {id: number; diff: TransferSummaryDiff}) => void; + TRANSFER_SUMMARY_DIFF_CHANGE: (payload: {id: number; diff: Operation[]}) => void; FETCH_TRANSFER_SUMMARY_SUCCESS: () => void; FETCH_TRANSFER_SUMMARY_ERROR: () => void; } @@ -182,7 +182,7 @@ class HistoryService extends BaseService { } handleFetchTransferSummarySuccess(nextTransferSummary: TransferSummary) { - const summaryDiff = objectUtil.getDiff(this.transferSummary, nextTransferSummary) as DiffAction; + const summaryDiff = jsonpatch.compare(this.transferSummary, nextTransferSummary); if (summaryDiff.length > 0) { this.emit('TRANSFER_SUMMARY_DIFF_CHANGE', { diff --git a/server/services/taxonomyService.ts b/server/services/taxonomyService.ts index 89d05151..516e5c08 100644 --- a/server/services/taxonomyService.ts +++ b/server/services/taxonomyService.ts @@ -1,13 +1,14 @@ +import jsonpatch, {Operation} from 'fast-json-patch'; + import BaseService from './BaseService'; -import objectUtil from '../../shared/util/objectUtil'; import torrentStatusMap from '../../shared/constants/torrentStatusMap'; -import type {Taxonomy, TaxonomyDiffs} from '../../shared/types/Taxonomy'; +import type {Taxonomy} from '../../shared/types/Taxonomy'; import type {TorrentStatus} from '../../shared/constants/torrentStatusMap'; import type {TorrentProperties, TorrentList} from '../../shared/types/Torrent'; interface TaxonomyServiceEvents { - TAXONOMY_DIFF_CHANGE: (payload: {id: number; diff: TaxonomyDiffs}) => void; + TAXONOMY_DIFF_CHANGE: (payload: {id: number; diff: Operation[]}) => void; } class TaxonomyService extends BaseService { @@ -81,21 +82,9 @@ class TaxonomyService extends BaseService { this.taxonomy.tagCounts.all = length; this.taxonomy.trackerCounts.all = length; - let didDiffChange = false; - const taxonomyDiffs = Object.keys(this.taxonomy).reduce((accumulator, key) => { - const countType = key as keyof Taxonomy; - const countDiff = objectUtil.getDiff(this.lastTaxonomy[countType], this.taxonomy[countType]); + const taxonomyDiffs = jsonpatch.compare(this.lastTaxonomy, this.taxonomy); - if (countDiff.length > 0) { - didDiffChange = true; - } - - return Object.assign(accumulator, { - [countType]: countDiff, - }); - }, {} as TaxonomyDiffs); - - if (didDiffChange) { + if (taxonomyDiffs.length > 0) { this.emit('TAXONOMY_DIFF_CHANGE', { diff: taxonomyDiffs, id: Date.now(), diff --git a/server/services/torrentService.ts b/server/services/torrentService.ts index a228cbdd..3deec0bf 100644 --- a/server/services/torrentService.ts +++ b/server/services/torrentService.ts @@ -1,6 +1,6 @@ -import {deepEqual} from 'fast-equals'; +import jsonpatch, {Operation} from 'fast-json-patch'; -import type {TorrentProperties, TorrentListDiff, TorrentListSummary} from '@shared/types/Torrent'; +import type {TorrentProperties, TorrentListSummary} from '@shared/types/Torrent'; import BaseService from './BaseService'; import config from '../../config'; @@ -9,7 +9,7 @@ import hasTorrentFinished from '../util/torrentPropertiesUtil'; interface TorrentServiceEvents { FETCH_TORRENT_LIST_SUCCESS: () => void; FETCH_TORRENT_LIST_ERROR: () => void; - TORRENT_LIST_DIFF_CHANGE: (payload: {id: number; diff: TorrentListDiff}) => void; + TORRENT_LIST_DIFF_CHANGE: (payload: {id: number; diff: Operation[]}) => void; newListener: (event: keyof Omit) => void; removeListener: (event: keyof Omit) => void; } @@ -56,51 +56,6 @@ class TorrentService extends BaseService { }; } - assignDeletedTorrentsToDiff( - diff: TorrentListDiff, - nextTorrentListSummary: this['torrentListSummary'], - newTorrentCount: number, - ): TorrentListDiff { - const prevTorrentCount = Object.keys(this.torrentListSummary.torrents).length; - const nextTorrentCount = Object.keys(nextTorrentListSummary.torrents).length; - - // We need to look for deleted torrents in two scenarios: - // 1. the next list length is less than than the current length - // 2. at least one new torrent was added and the next list length is - // equal to or greater than the current list length. - // - // We definitely don't need to look for deleted torrents if the number - // of new torrents is equal to the difference between next torrent list - // length and previous torrent list length. - let shouldLookForDeletedTorrents = nextTorrentCount < prevTorrentCount; - - if (newTorrentCount > 0) { - if (nextTorrentCount >= prevTorrentCount) { - shouldLookForDeletedTorrents = true; - } - - if (newTorrentCount === nextTorrentCount - prevTorrentCount) { - shouldLookForDeletedTorrents = false; - } - } - - let diffWithDeleted = diff; - - if (shouldLookForDeletedTorrents) { - Object.keys(this.torrentListSummary.torrents).forEach((hash) => { - if (nextTorrentListSummary.torrents[hash] == null) { - diffWithDeleted = Object.assign(diffWithDeleted, { - [hash]: { - action: 'TORRENT_LIST_ACTION_TORRENT_DELETED', - }, - }); - } - }, {}); - } - - return diffWithDeleted; - } - deferFetchTorrentList(interval = config.torrentClientPollInterval || 2000) { if (this.pollEnabled) { this.pollTimeout = setTimeout(this.fetchTorrentList, interval); @@ -134,56 +89,6 @@ class TorrentService extends BaseService { return this.torrentListSummary.torrents; } - getTorrentListDiff(nextTorrentListSummary: this['torrentListSummary']) { - let newTorrentCount = 0; - - // Get the diff... - const diff = Object.keys(nextTorrentListSummary.torrents).reduce((accumulator, hash) => { - const currentTorrentProperties = this.torrentListSummary.torrents[hash]; - const nextTorrentProperties = nextTorrentListSummary.torrents[hash]; - - // If the current torrent list doesn't contain any details for this - // hash, then it's a brand new torrent, so every detail is part of the - // diff. - if (currentTorrentProperties == null) { - accumulator[hash] = { - action: 'TORRENT_LIST_ACTION_TORRENT_ADDED', - data: nextTorrentProperties, - }; - - // Track the number of new torrents added. - newTorrentCount += 1; - } else { - let changed = false; - let changedProperties: Partial = {}; - - Object.keys(nextTorrentProperties).forEach((key) => { - const property = key as keyof TorrentProperties; - // If one of the details is unequal, we need to add it to the diff. - if (!deepEqual(currentTorrentProperties[property], nextTorrentProperties[property])) { - // Add the diff details. - changed = true; - changedProperties = { - ...changedProperties, - [property]: nextTorrentProperties[property], - }; - } - }); - - if (changed) { - accumulator[hash] = { - action: 'TORRENT_LIST_ACTION_TORRENT_DETAIL_UPDATED', - data: changedProperties, - }; - } - } - - return accumulator; - }, {} as TorrentListDiff); - - return this.assignDeletedTorrentsToDiff(diff, nextTorrentListSummary, newTorrentCount); - } - getTorrentListSummary() { return this.torrentListSummary; } @@ -204,8 +109,8 @@ class TorrentService extends BaseService { } handleFetchTorrentListSuccess(nextTorrentListSummary: this['torrentListSummary']) { - const diff = this.getTorrentListDiff(nextTorrentListSummary); - if (Object.keys(diff).length > 0) { + const diff = jsonpatch.compare(this.torrentListSummary.torrents, nextTorrentListSummary.torrents); + if (diff.length > 0) { this.emit('TORRENT_LIST_DIFF_CHANGE', {diff, id: nextTorrentListSummary.id}); } diff --git a/shared/constants/diffActionTypes.ts b/shared/constants/diffActionTypes.ts deleted file mode 100644 index aa354730..00000000 --- a/shared/constants/diffActionTypes.ts +++ /dev/null @@ -1,18 +0,0 @@ -import objectUtil from '../util/objectUtil'; - -const diffActionTypes = ['ITEM_ADDED', 'ITEM_CHANGED', 'ITEM_REMOVED'] as const; - -export default objectUtil.createStringMapFromArray(diffActionTypes); - -export type DiffActionType = typeof diffActionTypes[number]; - -export type DiffAction = Array< - | { - action: Exclude; - data: T; - } - | { - action: 'ITEM_REMOVED'; - data: keyof T; - } ->; diff --git a/shared/types/ServerEvents.ts b/shared/types/ServerEvents.ts index 6701f0ef..d240f02d 100644 --- a/shared/types/ServerEvents.ts +++ b/shared/types/ServerEvents.ts @@ -1,8 +1,10 @@ +import type {Operation} from 'fast-json-patch'; + import type {Disks} from './DiskUsage'; import type {NotificationCount} from './Notification'; -import type {Taxonomy, TaxonomyDiffs} from './Taxonomy'; -import type {TorrentList, TorrentListDiff} from './Torrent'; -import type {TransferHistory, TransferSummary, TransferSummaryDiff} from './TransferData'; +import type {Taxonomy} from './Taxonomy'; +import type {TorrentList} from './Torrent'; +import type {TransferHistory, TransferSummary} from './TransferData'; // type: data export interface ServerEvents { @@ -12,10 +14,10 @@ export interface ServerEvents { DISK_USAGE_CHANGE: Disks; NOTIFICATION_COUNT_CHANGE: NotificationCount; TAXONOMY_FULL_UPDATE: Taxonomy; - TAXONOMY_DIFF_CHANGE: TaxonomyDiffs; + TAXONOMY_DIFF_CHANGE: Operation[]; TORRENT_LIST_FULL_UPDATE: TorrentList; - TORRENT_LIST_DIFF_CHANGE: TorrentListDiff; + TORRENT_LIST_DIFF_CHANGE: Operation[]; TRANSFER_HISTORY_FULL_UPDATE: TransferHistory; TRANSFER_SUMMARY_FULL_UPDATE: TransferSummary; - TRANSFER_SUMMARY_DIFF_CHANGE: TransferSummaryDiff; + TRANSFER_SUMMARY_DIFF_CHANGE: Operation[]; } diff --git a/shared/types/Taxonomy.ts b/shared/types/Taxonomy.ts index 02d5fd1e..8788ec3b 100644 --- a/shared/types/Taxonomy.ts +++ b/shared/types/Taxonomy.ts @@ -1,15 +1,5 @@ -import type {DiffAction} from '../constants/diffActionTypes'; - export interface Taxonomy { statusCounts: Record; tagCounts: Record; trackerCounts: Record; } - -type TaxonomyDiff = DiffAction | null; - -export interface TaxonomyDiffs { - statusCounts: TaxonomyDiff<'statusCounts'>; - tagCounts: TaxonomyDiff<'tagCounts'>; - trackerCounts: TaxonomyDiff<'trackerCounts'>; -} diff --git a/shared/types/Torrent.ts b/shared/types/Torrent.ts index 2a898798..6dc5d1da 100644 --- a/shared/types/Torrent.ts +++ b/shared/types/Torrent.ts @@ -58,21 +58,6 @@ export interface TorrentProperties { upTotal: number; } -export interface TorrentListDiff { - [hash: string]: - | { - action: 'TORRENT_LIST_ACTION_TORRENT_ADDED'; - data: TorrentProperties; - } - | { - action: 'TORRENT_LIST_ACTION_TORRENT_DELETED'; - } - | { - action: 'TORRENT_LIST_ACTION_TORRENT_DETAIL_UPDATED'; - data: Partial; - }; -} - export interface TorrentList { [hash: string]: TorrentProperties; } diff --git a/shared/types/TransferData.ts b/shared/types/TransferData.ts index 8d807cab..8092952d 100644 --- a/shared/types/TransferData.ts +++ b/shared/types/TransferData.ts @@ -1,5 +1,3 @@ -import {DiffAction} from '@shared/constants/diffActionTypes'; - export interface TransferSummary { // Global download rate downRate: number; @@ -15,8 +13,6 @@ export interface TransferSummary { upTotal: number; } -export type TransferSummaryDiff = DiffAction>; - export type TransferDirection = 'upload' | 'download'; export type TransferData = Record; diff --git a/shared/util/objectUtil.ts b/shared/util/objectUtil.ts deleted file mode 100644 index 164b3f6a..00000000 --- a/shared/util/objectUtil.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type {DiffAction} from '../constants/diffActionTypes'; - -type KeyFromValue> = { - [K in keyof T]: V extends T[K] ? K : never; -}[keyof T]; - -const objectUtil = { - createStringMapFromArray: (array: Readonly>): Readonly<{[key in T]: key}> => { - return array.reduce((memo, key) => { - return Object.assign(memo, { - [key]: key, - }); - }, {} as Partial<{[key in T]: key}>) as Readonly<{[key in T]: key}>; - }, - - getDiff:

( - prevObject: Partial>, - nextObject: Partial>, - ) => { - const prevObjectKeys = Object.keys(prevObject); - const nextObjectKeys = Object.keys(nextObject); - - let shouldCheckForRemovals = nextObjectKeys.length < prevObjectKeys.length; - - const diff = nextObjectKeys.reduce((accumulator, key) => { - const prevValue = prevObject[key as P]; - const nextValue = nextObject[key as N]; - - if (prevValue == null) { - shouldCheckForRemovals = true; - - accumulator.push({ - action: 'ITEM_ADDED', - data: { - [key]: nextValue, - }, - } as { - action: 'ITEM_ADDED'; - data: { - [key in N]: typeof nextObject[key]; - }; - }); - } else if (prevValue !== nextValue) { - accumulator.push({ - action: 'ITEM_CHANGED', - data: { - [key]: nextValue, - }, - } as { - action: 'ITEM_CHANGED'; - data: { - [key in N]: typeof nextObject[key]; - }; - }); - } - - return accumulator; - }, [] as DiffAction>); - - if (shouldCheckForRemovals) { - prevObjectKeys.forEach((key) => { - if (nextObject[key as N] == null) { - diff.push({ - action: 'ITEM_REMOVED', - data: key as N, - }); - } - }); - } - - return diff; - }, - - reflect: , K extends keyof T>( - object: T, - ): {[value in T[K]]: KeyFromValue} & {[key in K]: T[key]} => { - return Object.assign( - object, - Object.keys(object).reduce((memo, key) => { - return Object.assign(memo, { - [object[key as K]]: key, - }); - }, {} as {[value in T[K]]: KeyFromValue}), - ); - }, -}; - -export default objectUtil;