@@ -3,8 +3,10 @@ import React from 'react'
3
3
import CSSModules from 'browser/lib/CSSModules'
4
4
import styles from './StoragesTab.styl'
5
5
import dataApi from 'browser/main/lib/dataApi'
6
+ import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
6
7
import StorageItem from './StorageItem'
7
8
import i18n from 'browser/lib/i18n'
9
+ import fs from 'fs'
8
10
9
11
const electron = require ( 'electron' )
10
12
const { shell, remote } = electron
@@ -25,6 +27,20 @@ function browseFolder () {
25
27
} )
26
28
}
27
29
30
+ function humanFileSize ( bytes ) {
31
+ const threshold = 1000
32
+ if ( Math . abs ( bytes ) < threshold ) {
33
+ return bytes + ' B'
34
+ }
35
+ var units = [ 'kB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ]
36
+ var u = - 1
37
+ do {
38
+ bytes /= threshold
39
+ ++ u
40
+ } while ( Math . abs ( bytes ) >= threshold && u < units . length - 1 )
41
+ return bytes . toFixed ( 1 ) + ' ' + units [ u ]
42
+ }
43
+
28
44
class StoragesTab extends React . Component {
29
45
constructor ( props ) {
30
46
super ( props )
@@ -35,8 +51,26 @@ class StoragesTab extends React.Component {
35
51
name : 'Unnamed' ,
36
52
type : 'FILESYSTEM' ,
37
53
path : ''
38
- }
54
+ } ,
55
+ attachments : [ ]
39
56
}
57
+ this . loadAttachmentStorage ( )
58
+ }
59
+
60
+ loadAttachmentStorage ( ) {
61
+ const promises = [ ]
62
+ this . props . data . noteMap . map ( note => {
63
+ const promise = attachmentManagement . getAttachments (
64
+ note . content ,
65
+ note . storage ,
66
+ note . key
67
+ )
68
+ if ( promise ) promises . push ( promise )
69
+ } )
70
+
71
+ Promise . all ( promises )
72
+ . then ( data => this . setState ( { attachments : data } ) )
73
+ . catch ( console . error )
40
74
}
41
75
42
76
handleAddStorageButton ( e ) {
@@ -57,8 +91,61 @@ class StoragesTab extends React.Component {
57
91
e . preventDefault ( )
58
92
}
59
93
94
+ removeAllAttachments ( attachments ) {
95
+ const promises = [ ]
96
+ for ( const attachment of attachments ) {
97
+ for ( const file of attachment ) {
98
+ const promise = new Promise ( ( resolve , reject ) => {
99
+ fs . unlink ( file , ( err ) => {
100
+ if ( err ) {
101
+ console . error ( 'Could not delete "%s"' , file )
102
+ console . error ( err )
103
+ reject ( err )
104
+ return
105
+ }
106
+ resolve ( )
107
+ } )
108
+ } )
109
+ promises . push ( promise )
110
+ }
111
+ }
112
+ Promise . all ( promises )
113
+ . then ( ( ) => this . loadAttachmentStorage ( ) )
114
+ . catch ( console . error )
115
+ }
116
+
60
117
renderList ( ) {
61
118
const { data, boundingBox } = this . props
119
+ const { attachments } = this . state
120
+
121
+ const totalUnusedAttachments = attachments
122
+ . reduce ( ( acc , curr ) => acc + curr . attachmentsNotInNotePaths . length , 0 )
123
+ const totalInuseAttachments = attachments
124
+ . reduce ( ( acc , curr ) => acc + curr . attachmentsInNotePaths . length , 0 )
125
+ const totalAttachments = totalUnusedAttachments + totalInuseAttachments
126
+
127
+ const unusedAttachments = attachments . reduce ( ( acc , curr ) => {
128
+ acc . push ( curr . attachmentsNotInNotePaths )
129
+ return acc
130
+ } , [ ] )
131
+
132
+ const totalUnusedAttachmentsSize = attachments
133
+ . reduce ( ( acc , curr ) => {
134
+ return acc + curr . attachmentsNotInNotePaths . reduce ( ( racc , rcurr ) => {
135
+ const stats = fs . statSync ( rcurr )
136
+ const fileSizeInBytes = stats . size
137
+ return racc + fileSizeInBytes
138
+ } , 0 )
139
+ } , 0 )
140
+ const totalInuseAttachmentsSize = attachments
141
+ . reduce ( ( acc , curr ) => {
142
+ return acc + curr . attachmentsInNotePaths . reduce ( ( racc , rcurr ) => {
143
+ const stats = fs . statSync ( rcurr )
144
+ const fileSizeInBytes = stats . size
145
+ return racc + fileSizeInBytes
146
+ } , 0 )
147
+ } , 0 )
148
+ const totalAttachmentsSize = totalUnusedAttachmentsSize + totalInuseAttachmentsSize
62
149
63
150
if ( ! boundingBox ) { return null }
64
151
const storageList = data . storageMap . map ( ( storage ) => {
@@ -82,6 +169,19 @@ class StoragesTab extends React.Component {
82
169
< i className = 'fa fa-plus' /> { i18n . __ ( 'Add Storage Location' ) }
83
170
</ button >
84
171
</ div >
172
+ < div styleName = 'header' > { i18n . __ ( 'Attachment storage' ) } </ div >
173
+ < p styleName = 'list-attachment-label' >
174
+ Unused attachments size: { humanFileSize ( totalUnusedAttachmentsSize ) } ({ totalUnusedAttachments } items)
175
+ </ p >
176
+ < p styleName = 'list-attachment-label' >
177
+ In use attachments size: { humanFileSize ( totalInuseAttachmentsSize ) } ({ totalInuseAttachments } items)
178
+ </ p >
179
+ < p styleName = 'list-attachment-label' >
180
+ Total attachments size: { humanFileSize ( totalAttachmentsSize ) } ({ totalAttachments } items)
181
+ </ p >
182
+ < button styleName = 'list-attachement-clear-button' onClick = { ( ) => this . removeAllAttachments ( unusedAttachments ) } >
183
+ { i18n . __ ( 'Clear unused attachments' ) }
184
+ </ button >
85
185
</ div >
86
186
)
87
187
}
0 commit comments