Inits FileUploadStore from current state in the filesystem

- Removes uploads when draft is deleted
- Misc fixes and renaming
This commit is contained in:
Juan Tejada 2016-01-26 18:48:21 -08:00
parent 752a749da7
commit 63d36a78c3
5 changed files with 60 additions and 26 deletions

View file

@ -441,7 +441,7 @@ class ComposerView extends React.Component
<button className="btn btn-toolbar btn-attach" style={order: 50} <button className="btn btn-toolbar btn-attach" style={order: 50}
title="Attach file" title="Attach file"
onClick={@_selectFileForUpload}><RetinaImg name="icon-composer-attachment.png" mode={RetinaImg.Mode.ContentIsMask} /></button> onClick={@_selectAttachment}><RetinaImg name="icon-composer-attachment.png" mode={RetinaImg.Mode.ContentIsMask} /></button>
<div style={order: 0, flex: 1} /> <div style={order: 0, flex: 1} />
@ -506,6 +506,7 @@ class ComposerView extends React.Component
files: draft.files files: draft.files
subject: draft.subject subject: draft.subject
accounts: @_getAccountsForSend() accounts: @_getAccountsForSend()
uploads: FileUploadStore.uploadsForMessage(@props.draftClientId) ? []
if !@state.populated if !@state.populated
_.extend state, _.extend state,
@ -615,14 +616,14 @@ class ComposerView extends React.Component
_onDrop: (e) => _onDrop: (e) =>
# Accept drops of real files from other applications # Accept drops of real files from other applications
for file in e.dataTransfer.files for file in e.dataTransfer.files
Actions.attachFile({filePath: file.path, messageClientId: @props.draftClientId}) Actions.addAttachment({filePath: file.path, messageClientId: @props.draftClientId})
# Accept drops from attachment components / images within the app # Accept drops from attachment components / images within the app
if (uri = @_nonNativeFilePathForDrop(e)) if (uri = @_nonNativeFilePathForDrop(e))
Actions.attachFile({filePath: uri, messageClientId: @props.draftClientId}) Actions.addAttachment({filePath: uri, messageClientId: @props.draftClientId})
_onFilePaste: (path) => _onFilePaste: (path) =>
Actions.attachFile({filePath: path, messageClientId: @props.draftClientId}) Actions.addAttachment({filePath: path, messageClientId: @props.draftClientId})
_onChangeParticipants: (changes={}) => _onChangeParticipants: (changes={}) =>
@_addToProxy(changes) @_addToProxy(changes)
@ -744,8 +745,8 @@ class ComposerView extends React.Component
_destroyDraft: => _destroyDraft: =>
Actions.destroyDraft(@props.draftClientId) Actions.destroyDraft(@props.draftClientId)
_selectFileForUpload: => _selectAttachment: =>
Actions.selectFileForUpload({messageClientId: @props.draftClientId}) Actions.selectAttachment({messageClientId: @props.draftClientId})
undo: (event) => undo: (event) =>
event.preventDefault() event.preventDefault()

View file

@ -27,7 +27,7 @@ class FileUpload extends React.Component
</div> </div>
_onClickRemove: => _onClickRemove: =>
Actions.removeFileFromUpload @props.upload Actions.removeAttachment @props.upload
_extension: => _extension: =>
path.extname(@props.upload.filename)[1..-1] path.extname(@props.upload.filename)[1..-1]

View file

@ -445,9 +445,9 @@ class Actions
# File Actions # File Actions
# Some file actions only need to be processed in their current window # Some file actions only need to be processed in their current window
@attachFile: ActionScopeWindow @addAttachment: ActionScopeWindow
@selectFileForUpload: ActionScopeWindow @selectAttachment: ActionScopeWindow
@removeFileFromUpload: ActionScopeWindow @removeAttachment: ActionScopeWindow
@fetchAndOpenFile: ActionScopeWindow @fetchAndOpenFile: ActionScopeWindow
@fetchAndSaveFile: ActionScopeWindow @fetchAndSaveFile: ActionScopeWindow
@fetchFile: ActionScopeWindow @fetchFile: ActionScopeWindow

View file

@ -5,14 +5,16 @@ mkdirp = require 'mkdirp'
NylasStore = require 'nylas-store' NylasStore = require 'nylas-store'
Actions = require '../actions' Actions = require '../actions'
Utils = require '../models/utils' Utils = require '../models/utils'
Message = require '../models/message'
DatabaseStore = require './database-store'
UPLOAD_DIR = path.join(NylasEnv.getConfigDirPath(), 'uploads') UPLOAD_DIR = path.join(NylasEnv.getConfigDirPath(), 'uploads')
class Upload class Upload
constructor: (@messageClientId, @originPath, @stats, @uploadDir = UPLOAD_DIR) -> constructor: (@messageClientId, @originPath, @stats, @id, @uploadDir = UPLOAD_DIR) ->
@id = Utils.generateTempId() @id ?= Utils.generateTempId()
@filename = path.basename(@originPath) @filename = path.basename(@originPath)
@draftUploadDir = path.join(@uploadDir, @messageClientId) @draftUploadDir = path.join(@uploadDir, @messageClientId)
@targetDir = path.join(@draftUploadDir, @id) @targetDir = path.join(@draftUploadDir, @id)
@ -23,30 +25,38 @@ class Upload
class FileUploadStore extends NylasStore class FileUploadStore extends NylasStore
constructor: -> constructor: ->
@listenTo Actions.selectFileForUpload, @_onSelectFileForUpload @listenTo Actions.selectAttachment, @_onSelectAttachment
@listenTo Actions.attachFile, @_onAttachFile @listenTo Actions.addAttachment, @_onAddAttachment
@listenTo Actions.removeFileFromUpload, @_onRemoveUpload @listenTo Actions.removeAttachment, @_onRemoveAttachment
@listenTo DatabaseStore, @_onDataChanged
# We don't save uploads to the DB, we keep it in memory in the store. mkdirp.sync(UPLOAD_DIR)
# The key is the messageClientId. The value is a hash of paths and @_fileUploads = @_getFileUploadsFromFs()
# corresponding upload data.
@_fileUploads = {}
mkdirp(UPLOAD_DIR)
uploadsForMessage: (messageClientId) -> uploadsForMessage: (messageClientId) ->
@_fileUploads[messageClientId] ? [] @_fileUploads[messageClientId] ? []
_onSelectFileForUpload: ({messageClientId}) ->
# Handlers
_onDataChanged: (change) =>
return unless change.objectClass is Message.name and change.type is 'unpersist'
change.objects.forEach (message) =>
uploads = @_fileUploads[message.clientId]
if uploads?
uploads.forEach (upload) => @_onRemoveAttachment(upload)
_onSelectAttachment: ({messageClientId}) ->
@_verifyId(messageClientId) @_verifyId(messageClientId)
# When the dialog closes, it triggers `Actions.attachFile` # When the dialog closes, it triggers `Actions.addAttachment`
NylasEnv.showOpenDialog {properties: ['openFile', 'multiSelections']}, (pathsToOpen) -> NylasEnv.showOpenDialog {properties: ['openFile', 'multiSelections']}, (pathsToOpen) ->
return if not pathsToOpen? return if not pathsToOpen?
pathsToOpen = [pathsToOpen] if _.isString(pathsToOpen) pathsToOpen = [pathsToOpen] if _.isString(pathsToOpen)
pathsToOpen.forEach (filePath) -> pathsToOpen.forEach (filePath) ->
Actions.attachFile({messageClientId, filePath}) Actions.addAttachment({messageClientId, filePath})
_onAttachFile: ({messageClientId, filePath}) -> _onAddAttachment: ({messageClientId, filePath}) ->
@_verifyId(messageClientId) @_verifyId(messageClientId)
@_getFileStats({messageClientId, filePath}) @_getFileStats({messageClientId, filePath})
.then(@_makeUpload) .then(@_makeUpload)
@ -56,7 +66,7 @@ class FileUploadStore extends NylasStore
.then(@_saveUpload) .then(@_saveUpload)
.catch(@_onAttachFileError) .catch(@_onAttachFileError)
_onRemoveUpload: (upload) -> _onRemoveAttachment: (upload) ->
return unless (@_fileUploads[upload.messageClientId] ? []).length > 0 return unless (@_fileUploads[upload.messageClientId] ? []).length > 0
@_deleteUpload(upload) @_deleteUpload(upload)
.then (upload) => .then (upload) =>
@ -77,6 +87,29 @@ class FileUploadStore extends NylasStore
message: 'Cannot Attach File', message: 'Cannot Attach File',
detail: message detail: message
# Helpers
_getTempIdDirsFor: (path) ->
fs.readdirSync(path).filter((dir) -> Utils.isTempId(dir))
_getFileUploadsFromFs: (rootDir = UPLOAD_DIR)->
# TODO This function makes my eyes hurt. Refactor
uploads = {}
dirs = @_getTempIdDirsFor(rootDir)
for messageClientId in dirs
uploads[messageClientId] = []
messageDir = path.join(rootDir, messageClientId)
uploadIds = @_getTempIdDirsFor(messageDir)
for uploadId in uploadIds
uploadDir = path.join(messageDir, uploadId)
for filename in fs.readdirSync(uploadDir)
uploadPath = path.join(uploadDir, filename)
stats = fs.statSync(uploadPath)
uploads[messageClientId].push(
new Upload(messageClientId, uploadPath, stats, uploadId)
)
return uploads
_verifyId: (messageClientId) -> _verifyId: (messageClientId) ->
if messageClientId.blank? if messageClientId.blank?
throw new Error "You need to pass the ID of the message (draft) this Action refers to" throw new Error "You need to pass the ID of the message (draft) this Action refers to"

View file

@ -169,7 +169,7 @@ class SendDraftTask extends Task
# Remove attachments we were waiting to upload # Remove attachments we were waiting to upload
# Call the Action to do this # Call the Action to do this
for upload in @uploads for upload in @uploads
Actions.removeFileFromUpload(upload.messageClientId, upload.id) Actions.removeAttachment(upload.messageClientId, upload.id)
return Promise.resolve(Task.Status.Success) return Promise.resolve(Task.Status.Success)