diff --git a/src/app/@dataflow/extra/clipboard-flow.ts b/src/app/@dataflow/extra/clipboard-flow.ts new file mode 100644 index 0000000..999a972 --- /dev/null +++ b/src/app/@dataflow/extra/clipboard-flow.ts @@ -0,0 +1,80 @@ +import { NothingFlow } from '../core'; +import { OperationsListFlowOutItemNode } from '../rclone'; +import { NavigationFlowOutNode } from './navigation-flow'; + +export type IManipulate = 'copy' | 'move' | 'del'; + +export interface ClipboardItem { + oper: IManipulate; + key: string; + srcRemote: string; + srcItem: OperationsListFlowOutItemNode; + dst?: NavigationFlowOutNode; +} + +export class Clipboard { + public get values(): ClipboardItem[] { + return Array.from(this.data.values()); + } + + public get size(): number { + return this.data.size; + } + private data = new Map(); + + public static genKey(remote: string, path: string) { + return JSON.stringify({ r: remote, p: path }); + } + + public add( + o: IManipulate, + remote: string, + row: OperationsListFlowOutItemNode, + dst?: NavigationFlowOutNode + ) { + const key = Clipboard.genKey(remote, row.Path); + this.data.set(key, { + oper: o, + key, + srcItem: { ...row }, + srcRemote: remote, + dst: { ...dst }, + }); + } + + public pop(remote: string, path: string): ClipboardItem { + const key = Clipboard.genKey(remote, path); + if (!this.data.has(key)) return undefined; + const ans = this.data.get(key); + this.data.delete(key); + return ans; + } + + public getManipulation(remote: string, path: string): IManipulate { + const key = Clipboard.genKey(remote, path); + if (!this.data.has(key)) return undefined; + return this.data.get(key).oper; + } + + public countManipulation(o: IManipulate): number { + let cnt = 0; + this.data.forEach(x => (x.oper === o ? cnt++ : null)); + return cnt; + } + + public clear(...opers: IManipulate[]) { + if (opers.length === 0) this.data.clear(); + else + this.values + .filter(x => opers.some(y => x.oper === y)) + .forEach(x => { + this.data.delete(x.key); + }); + } +} + +export interface ClipboardFlowNode { + clipboard: Clipboard; +} + +export abstract class ClipboardFlow extends NothingFlow {} diff --git a/src/app/@dataflow/extra/index.ts b/src/app/@dataflow/extra/index.ts index d4964ac..6789270 100644 --- a/src/app/@dataflow/extra/index.ts +++ b/src/app/@dataflow/extra/index.ts @@ -1,3 +1,5 @@ export * from './users-flow'; export * from './navigation-flow'; export * from './current-user-flow'; +export * from './clipboard-flow'; +export * from './operations-list-extends-flow'; diff --git a/src/app/@dataflow/extra/operations-list-extends-flow.ts b/src/app/@dataflow/extra/operations-list-extends-flow.ts new file mode 100644 index 0000000..7dc4a6c --- /dev/null +++ b/src/app/@dataflow/extra/operations-list-extends-flow.ts @@ -0,0 +1,54 @@ +import * as moment from 'moment'; +import { Observable, of } from 'rxjs'; +import { getIconForFile, getIconForFolder } from 'vscode-icons-js'; +import { FormatBytes } from '../../utils/format-bytes'; +import { BareFlow, CombErr, NothingFlow } from '../core'; +import { + OperationsListFlowInNode, + OperationsListFlowOutItemNode, + OperationsListFlowOutNode, +} from '../rclone'; +import { ClipboardFlowNode, IManipulate } from './clipboard-flow'; + +export interface OperationsListExtendsFlowInNode + extends OperationsListFlowOutNode, + OperationsListFlowInNode, + ClipboardFlowNode {} + +export interface OperationsListExtendsFlowOutItemNode extends OperationsListFlowOutItemNode { + SizeHumanReadable: string; + ModTimeHumanReadable: string; + ModTimeMoment: moment.Moment; + TypeIcon: string; + Manipulation: IManipulate; +} + +export interface OperationsListExtendsFlowOutNode extends OperationsListFlowInNode { + list: OperationsListExtendsFlowOutItemNode[]; +} + +export abstract class OperationsListExtendsFlow extends BareFlow< + OperationsListExtendsFlowInNode, + OperationsListExtendsFlowOutNode +> { + // public prerequest$: Observable>; + protected request( + pre: CombErr + ): Observable> { + if (pre[1].length !== 0 || pre[1].length !== 0) return pre as any; + const list = pre[0].list.map( + (item): OperationsListExtendsFlowOutItemNode => { + const ModTimeMoment = moment(item.ModTime); + return { + ...item, + SizeHumanReadable: FormatBytes(item.Size), + ModTimeMoment, + ModTimeHumanReadable: ModTimeMoment.fromNow(), + Manipulation: pre[0].clipboard.getManipulation(pre[0].remote, item.Path), + TypeIcon: item.IsDir ? getIconForFolder(item.Name) : getIconForFile(item.Name), + }; + } + ); + return of([{ ...pre[0], list }, []]); + } +} diff --git a/src/app/pages/manager/clipboard/clipboard-remotes-table/clipboard-remotes-table.component.ts b/src/app/pages/manager/clipboard/clipboard-remotes-table/clipboard-remotes-table.component.ts index 0bd0528..0a12979 100644 --- a/src/app/pages/manager/clipboard/clipboard-remotes-table/clipboard-remotes-table.component.ts +++ b/src/app/pages/manager/clipboard/clipboard-remotes-table/clipboard-remotes-table.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { API, APIDefinition, Columns, Config, DefaultConfig } from 'ngx-easy-table'; -import { ClipboardItem, ClipboardService, IManipulate } from '../clipboard.service'; +import { ClipboardItem, IManipulate } from '../../../../@dataflow/extra'; +import { ClipboardService } from '../clipboard.service'; interface IRemotesTableItem { remote: string; diff --git a/src/app/pages/manager/clipboard/clipboard.component.ts b/src/app/pages/manager/clipboard/clipboard.component.ts index b55eb8d..3f0edf6 100644 --- a/src/app/pages/manager/clipboard/clipboard.component.ts +++ b/src/app/pages/manager/clipboard/clipboard.component.ts @@ -1,5 +1,6 @@ import { Component, EventEmitter, OnInit, Output } from '@angular/core'; -import { ClipboardService, IManipulate } from './clipboard.service'; +import { IManipulate } from '../../../@dataflow/extra'; +import { ClipboardService } from './clipboard.service'; @Component({ selector: 'app-manager-clipboard', diff --git a/src/app/pages/manager/clipboard/clipboard.service.ts b/src/app/pages/manager/clipboard/clipboard.service.ts index f05ca06..f612a55 100644 --- a/src/app/pages/manager/clipboard/clipboard.service.ts +++ b/src/app/pages/manager/clipboard/clipboard.service.ts @@ -1,82 +1,8 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; import { mapTo } from 'rxjs/operators'; -import { CombErr, NothingFlow } from '../../../@dataflow/core'; -import { NavigationFlowOutNode } from '../../../@dataflow/extra'; -import { OperationsListFlowOutItemNode } from '../../../@dataflow/rclone'; - -export type IManipulate = 'copy' | 'move' | 'del'; - -export interface ClipboardItem { - oper: IManipulate; - key: string; - srcRemote: string; - srcItem: OperationsListFlowOutItemNode; - dst?: NavigationFlowOutNode; -} - -export class Clipboard { - public get values(): ClipboardItem[] { - return Array.from(this.data.values()); - } - - public get size(): number { - return this.data.size; - } - private data = new Map(); - - public static genKey(remote: string, path: string) { - return JSON.stringify({ r: remote, p: path }); - } - - public add( - o: IManipulate, - remote: string, - row: OperationsListFlowOutItemNode, - dst?: NavigationFlowOutNode - ) { - const key = Clipboard.genKey(remote, row.Path); - this.data.set(key, { - oper: o, - key, - srcItem: { ...row }, - srcRemote: remote, - dst: { ...dst }, - }); - } - - public pop(remote: string, path: string): ClipboardItem { - const key = Clipboard.genKey(remote, path); - if (!this.data.has(key)) return undefined; - const ans = this.data.get(key); - this.data.delete(key); - return ans; - } - - public getManipulation(remote: string, path: string): IManipulate { - const key = Clipboard.genKey(remote, path); - if (!this.data.has(key)) return undefined; - return this.data.get(key).oper; - } - - public countManipulation(o: IManipulate): number { - let cnt = 0; - this.data.forEach(x => (x.oper === o ? cnt++ : null)); - return cnt; - } - - public clear(...opers: IManipulate[]) { - if (opers.length === 0) this.data.clear(); - else - this.values - .filter(x => opers.some(y => x.oper === y)) - .forEach(x => { - this.data.delete(x.key); - }); - } -} - -export abstract class ClipboardFlow extends NothingFlow<{ clipboard: Clipboard }> {} +import { CombErr } from '../../../@dataflow/core'; +import { Clipboard, ClipboardFlow, ClipboardFlowNode } from '../../../@dataflow/extra'; @Injectable({ providedIn: 'root', @@ -93,7 +19,7 @@ export class ClipboardService extends Clipboard { const outer = this; this.clipboard$ = new (class extends ClipboardFlow { public prerequest$ = outer.trigger.pipe( - mapTo>([{ clipboard: outer }, []]) + mapTo>([{ clipboard: outer }, []]) ); })(); this.clipboard$.deploy(); diff --git a/src/app/pages/manager/fileMode/fileMode.component.ts b/src/app/pages/manager/fileMode/fileMode.component.ts index ed09e34..7347a83 100644 --- a/src/app/pages/manager/fileMode/fileMode.component.ts +++ b/src/app/pages/manager/fileMode/fileMode.component.ts @@ -2,16 +2,23 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angu import { combineLatest, Subject } from 'rxjs'; import { filter, map } from 'rxjs/operators'; import { CombErr } from '../../../@dataflow/core'; -import { NavigationFlow, NavigationFlowOutNode } from '../../../@dataflow/extra'; +import { + IManipulate, + NavigationFlow, + NavigationFlowOutNode, + OperationsListExtendsFlow, + OperationsListExtendsFlowInNode, +} from '../../../@dataflow/extra'; import { OperationsListFlow, OperationsListFlowInNode } from '../../../@dataflow/rclone'; import { ConnectionService } from '../../connection.service'; -import { ClipboardService, IManipulate } from '../clipboard/clipboard.service'; +import { ClipboardService } from '../clipboard/clipboard.service'; import { ListViewComponent } from './listView/listView.component'; @Component({ selector: 'app-manager-file-mode', template: ` - + + `, styles: [], }) @@ -23,7 +30,8 @@ export class FileModeComponent implements OnInit { @Output() jump = new EventEmitter(); private listTrigger = new Subject(); - list$: OperationsListFlow; + private list$: OperationsListFlow; + listExtends$: OperationsListExtendsFlow; @ViewChild(ListViewComponent) listView: ListViewComponent; @@ -55,6 +63,22 @@ export class FileModeComponent implements OnInit { ); })(); this.list$.deploy(); + + this.listExtends$ = new (class extends OperationsListExtendsFlow { + public prerequest$ = combineLatest([ + outer.list$.getSupersetOutput(), + outer.clipboard.clipboard$.getOutput(), + ]).pipe( + map( + ([listNode, cbNode]): CombErr => [ + { ...listNode[0], ...cbNode[0] }, + [].concat(listNode[1], cbNode[1]), + ] + ) + ); + })(); + this.listExtends$.deploy(); + this.listTrigger.next(1); } } diff --git a/src/app/pages/manager/fileMode/listView/listView.component.ts b/src/app/pages/manager/fileMode/listView/listView.component.ts index 6ab8004..eb0f6a4 100644 --- a/src/app/pages/manager/fileMode/listView/listView.component.ts +++ b/src/app/pages/manager/fileMode/listView/listView.component.ts @@ -7,14 +7,16 @@ import { Output, ViewChild, } from '@angular/core'; -import * as moment from 'moment'; import { API, APIDefinition, Columns, Config, DefaultConfig } from 'ngx-easy-table'; -import { combineLatest, Subscription } from 'rxjs'; -import { getIconForFile, getIconForFolder } from 'vscode-icons-js'; -import { NavigationFlowOutNode } from '../../../../@dataflow/extra'; -import { OperationsListFlow, OperationsListFlowOutItemNode } from '../../../../@dataflow/rclone'; -import { FormatBytes } from '../../../../utils/format-bytes'; -import { ClipboardService, IManipulate } from '../../clipboard/clipboard.service'; +import { Subscription } from 'rxjs'; +import { + IManipulate, + NavigationFlowOutNode, + OperationsListExtendsFlow, + OperationsListExtendsFlowOutItemNode, +} from '../../../../@dataflow/extra'; +import { OperationsListFlowOutItemNode } from '../../../../@dataflow/rclone'; +import { ClipboardService } from '../../clipboard/clipboard.service'; @Component({ selector: 'app-manager-list-view', @@ -81,12 +83,8 @@ export class ListViewComponent implements OnInit, OnDestroy { { key: 'MimeType', title: 'MIME Type', width: '17%' }, ]; - public data: (OperationsListFlowOutItemNode & { + public data: (OperationsListExtendsFlowOutItemNode & { check: boolean; - SizeHumanReadable: string; - ModTimeHumanReadable: string; - ModTimeMoment: moment.Moment; - TypeIcon: string; ManipulateIcon: string; })[]; public checkAll = false; @@ -97,9 +95,10 @@ export class ListViewComponent implements OnInit, OnDestroy { @Output() jump = new EventEmitter(); private remote: string; - @Input() list$: OperationsListFlow; + @Input() listExtends$: OperationsListExtendsFlow; private listScrb: Subscription; + resetCurrentPage() { this.checkAll = false; // TODO: not work around. this.table.apiEvent({ @@ -152,27 +151,17 @@ export class ListViewComponent implements OnInit, OnDestroy { } ngOnInit() { - this.listScrb = combineLatest([ - this.list$.getSupersetOutput(), - this.clipboardService.clipboard$.getOutput(), - ]).subscribe(([listNode, cbNode]) => { - if (listNode[1].length !== 0 || cbNode[1].length !== 0) { + this.listScrb = this.listExtends$.getOutput().subscribe(listNode => { + if (listNode[1].length !== 0) { this.data = undefined; this.checkAll = false; + return; } this.remote = listNode[0].remote; this.data = listNode[0].list as any; - this.data.forEach(item => { - item.check = false; - item.SizeHumanReadable = FormatBytes(item.Size); - item.ModTimeMoment = moment(item.ModTime); - item.ModTimeHumanReadable = item.ModTimeMoment.fromNow(); - item.ManipulateIcon = this.manipulate2Icon( - cbNode[0].clipboard.getManipulation(this.remote, item.Path) - ); - item.TypeIcon = item.IsDir - ? getIconForFolder(item.Name) - : (item.TypeIcon = getIconForFile(item.Name)); + this.data.forEach(x => { + x.check = false; + x.ManipulateIcon = this.manipulate2Icon(x.Manipulation); }); this.checkAll = false; }); diff --git a/src/app/pages/manager/manager.component.ts b/src/app/pages/manager/manager.component.ts index 529664c..7ada22a 100644 --- a/src/app/pages/manager/manager.component.ts +++ b/src/app/pages/manager/manager.component.ts @@ -4,10 +4,10 @@ import { ResponsiveSizeInfoRx } from 'ngx-responsive'; import { Observable, Subject } from 'rxjs'; import { map, withLatestFrom } from 'rxjs/operators'; import { CombErr } from '../../@dataflow/core'; -import { NavigationFlow, NavigationFlowOutNode } from '../../@dataflow/extra'; +import { IManipulate, NavigationFlow, NavigationFlowOutNode } from '../../@dataflow/extra'; import { OperationsMkdirFlow, OperationsMkdirFlowInNode } from '../../@dataflow/rclone'; import { ConnectionService } from '../connection.service'; -import { ClipboardService, IManipulate } from './clipboard/clipboard.service'; +import { ClipboardService } from './clipboard/clipboard.service'; import { FileModeComponent } from './fileMode/fileMode.component'; import { HomeModeComponent } from './homeMode/homeMode.component'; import { TaskService } from './tasks/tasks.service'; diff --git a/src/app/pages/manager/tasks/tasks.component.ts b/src/app/pages/manager/tasks/tasks.component.ts index a81d874..e0047c1 100644 --- a/src/app/pages/manager/tasks/tasks.component.ts +++ b/src/app/pages/manager/tasks/tasks.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { Columns, Config } from 'ngx-easy-table'; -import { ClipboardItem, IManipulate } from '../clipboard/clipboard.service'; +import { ClipboardItem, IManipulate } from '../../../@dataflow/extra'; import { TaskService } from './tasks.service'; @Component({ diff --git a/src/app/pages/manager/tasks/tasks.service.ts b/src/app/pages/manager/tasks/tasks.service.ts index 82d7e58..cacf85b 100644 --- a/src/app/pages/manager/tasks/tasks.service.ts +++ b/src/app/pages/manager/tasks/tasks.service.ts @@ -11,7 +11,12 @@ import { withLatestFrom, } from 'rxjs/operators'; import { CombErr, NothingFlow } from '../../../@dataflow/core'; -import { NavigationFlowOutNode } from '../../../@dataflow/extra'; +import { + Clipboard, + ClipboardItem, + IManipulate, + NavigationFlowOutNode, +} from '../../../@dataflow/extra'; import { AsyncPostFlowOutNode, OperationsCopyfileFlow, @@ -28,12 +33,7 @@ import { SyncMoveFlowInNode, } from '../../../@dataflow/rclone'; import { ConnectionService } from '../../connection.service'; -import { - Clipboard, - ClipboardItem, - ClipboardService, - IManipulate, -} from '../clipboard/clipboard.service'; +import { ClipboardService } from '../clipboard/clipboard.service'; export interface TasksPoolNode { failure: Clipboard;