@@ -4,8 +4,10 @@ import {Device} from "../../../types/novacom";
44import { BehaviorSubject , Observable , Subject } from "rxjs" ;
55import { Attributes , FileEntry } from 'ssh2-streams' ;
66import * as path from 'path' ;
7+ import * as fs from 'fs' ;
78import { MessageDialogComponent } from "../../shared/components/message-dialog/message-dialog.component" ;
89import { NgbModal } from "@ng-bootstrap/ng-bootstrap" ;
10+ import { SelectionType , SortType , TableColumn } from "@swimlane/ngx-datatable" ;
911
1012@Component ( {
1113 selector : 'app-files' ,
@@ -17,15 +19,20 @@ export class FilesComponent implements OnInit {
1719 pwd : string ;
1820 files$ : Observable < FileItem [ ] > ;
1921 sizeOptions = { base : 2 , standard : "jedec" } ;
20- private dialog : Electron . Dialog ;
22+ columns : TableColumn [ ] = [ { prop : 'filename' , name : 'Name' } ] ;
23+ SortType = SortType ;
24+ SelectionType = SelectionType ;
25+ private remote : Electron . Remote ;
2126 private filesSubject : Subject < FileItem [ ] > ;
27+ private fs : typeof fs ;
2228
2329 constructor (
2430 private modalService : NgbModal ,
2531 private deviceManager : DeviceManagerService ,
2632 private electron : ElectronService ,
2733 ) {
28- this . dialog = electron . remote . dialog ;
34+ this . remote = electron . remote ;
35+ this . fs = electron . fs ;
2936 deviceManager . selected$ . subscribe ( ( selected ) => {
3037 this . device = selected ;
3138 this . cd ( '/media/developer' ) ;
@@ -38,6 +45,7 @@ export class FilesComponent implements OnInit {
3845 }
3946
4047 async cd ( dir : string ) : Promise < void > {
48+ if ( ! this . device ) return ;
4149 dir = path . normalize ( dir ) ;
4250 const sftp = await this . deviceManager . sftpSession ( this . device . name ) ;
4351 let list : FileItem [ ] ;
@@ -59,28 +67,58 @@ export class FilesComponent implements OnInit {
5967 return ;
6068 }
6169 this . pwd = dir ;
62- this . filesSubject . next ( list . sort ( ( a , b ) => {
63- const dirDiff = ( b . type == 'dir' ? 1000 : 0 ) - ( a . type == 'dir' ? 1000 : 0 ) ;
64- return dirDiff + ( a . filename > b . filename ? 1 : - 1 ) ;
65- } ) ) ;
70+ this . filesSubject . next ( list . sort ( this . compareName . bind ( this ) ) ) ;
6671 }
6772
68- async onselect ( file : FileItem ) : Promise < void > {
69- if ( file . type == 'dir' ) {
70- await this . cd ( path . resolve ( this . pwd , file . filename ) ) ;
71- } else if ( file . type == 'file' ) {
72- const returnValue = await this . dialog . showSaveDialog ( { defaultPath : file . filename } ) ;
73- if ( returnValue . canceled ) return ;
74- const sftp = await this . deviceManager . sftpSession ( this . device . name ) ;
75- await sftp . fastGet ( file . abspath , returnValue . filePath ) . finally ( ( ) => sftp . end ( ) )
76- . catch ( ( e ) => MessageDialogComponent . open ( this . modalService , {
77- title : 'Failed to download file' ,
78- message : e . message ?? String ( e ) ,
79- positive : 'OK' ,
80- } ) ) ;
73+ compareName ( a : FileItem , b : FileItem ) : number {
74+ const dirDiff = ( b . type == 'dir' ? 1000 : 0 ) - ( a . type == 'dir' ? 1000 : 0 ) ;
75+ return dirDiff + ( a . filename > b . filename ? 1 : - 1 ) ;
76+ }
77+
78+ compareSize ( a : FileItem , b : FileItem ) : number {
79+ return ( a . type == 'file' ? ( a . attrs ?. size ?? 0 ) : 0 ) - ( b . type == 'file' ? ( b . attrs . size ?? 0 ) : 0 ) ;
80+ }
81+
82+ async selectItem ( file : FileItem ) : Promise < void > {
83+
84+ }
85+
86+ async openItem ( file : FileItem ) : Promise < void > {
87+ switch ( file . type ) {
88+ case 'dir' : {
89+ await this . cd ( path . resolve ( this . pwd , file . filename ) ) ;
90+ break ;
91+ }
92+ case 'file' : {
93+ return await this . openFile ( file ) ;
94+ }
8195 }
8296 }
8397
98+ private async openFile ( file : FileItem ) {
99+ const tempDir = path . join ( this . remote . app . getPath ( 'temp' ) , `devmgr` ) ;
100+ if ( ! this . fs . existsSync ( tempDir ) ) {
101+ this . fs . mkdirSync ( tempDir ) ;
102+ }
103+ const tempPath = path . join ( tempDir , `${ Date . now ( ) } _${ file . filename } ` ) ;
104+ const session = await this . deviceManager . newSession2 ( this . device . name ) ;
105+ await session . get ( file . abspath , tempPath ) . finally ( ( ) => session . end ( ) ) ;
106+ await this . remote . shell . openPath ( tempPath ) ;
107+ }
108+
109+ private async downloadFile ( file : FileItem ) {
110+ const returnValue = await this . remote . dialog . showSaveDialog ( { defaultPath : file . filename } ) ;
111+ if ( returnValue . canceled ) return ;
112+ const session = await this . deviceManager . newSession2 ( this . device . name ) ;
113+ await session . get ( file . abspath , returnValue . filePath ) . finally ( ( ) => session . end ( ) )
114+ . catch ( ( e ) => MessageDialogComponent . open ( this . modalService , {
115+ title : 'Failed to download file' ,
116+ message : e . message ?? String ( e ) ,
117+ positive : 'OK' ,
118+ } ) ) ;
119+ return ;
120+ }
121+
84122 async breadcrumbNav ( segs : string [ ] ) : Promise < void > {
85123 await this . cd ( segs . length > 1 ? path . join ( '/' , ...segs ) : '/' ) ;
86124 }
@@ -133,13 +171,20 @@ export class FilesComponent implements OnInit {
133171 abspath : path . resolve ( dir , file . filename ) ,
134172 } ;
135173 }
174+
175+ async itemActivated ( file : FileItem , type : string ) : Promise < void > {
176+ switch ( type ) {
177+ case 'dblclick' :
178+ return this . openItem ( file ) ;
179+ }
180+ }
136181}
137182
138183type FileType = 'file' | 'dir' | 'device' | 'special' | 'invalid' ;
139184
140185declare interface FileItem {
141186 filename : string ;
142- attrs : Attributes ;
187+ attrs : Attributes | null ;
143188 link ?: LinkInfo ;
144189 type : FileType ;
145190 abspath : string ;
0 commit comments