mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-24 17:26:06 +08:00
214c8b2aaa
Summary: Resolves T1200 and probably others. This diff moves all the window management / hot loading into a new class called the WindowManager and also changes the way the app transitions between onboarding and main window. When you log out, the main window clears config and clearing the config causes the window manager to close the main window and open the login window. When it detects a token again, it opens the main window. This means you can't: - Open the main window from the login window - Open mailto: links and accidentally see the main window or a composer, since the draft store isn't running anywhere. - Don't need to worry about properly resetting thigns when namespaces change, since the window is now actually re-created from scratch with the new auth token. Be a little more defensive about namespace checks in draft-store Move window code to window-manager Rename AtomApplication to just `Application` Specs fix Test Plan: Run tests, would be good to have more for this. Reviewers: evan Reviewed By: evan Maniphest Tasks: T1200 Differential Revision: https://review.inboxapp.com/D1501
238 lines
8.1 KiB
CoffeeScript
238 lines
8.1 KiB
CoffeeScript
ChildProcess = require 'child_process'
|
|
fs = require 'fs-plus'
|
|
path = require 'path'
|
|
|
|
appFolder = path.resolve(process.execPath, '..')
|
|
rootAtomFolder = path.resolve(appFolder, '..')
|
|
binFolder = path.join(rootAtomFolder, 'bin')
|
|
updateDotExe = path.join(rootAtomFolder, 'Update.exe')
|
|
exeName = path.basename(process.execPath)
|
|
|
|
if process.env.SystemRoot
|
|
system32Path = path.join(process.env.SystemRoot, 'System32')
|
|
regPath = path.join(system32Path, 'reg.exe')
|
|
setxPath = path.join(system32Path, 'setx.exe')
|
|
else
|
|
regPath = 'reg.exe'
|
|
setxPath = 'setx.exe'
|
|
|
|
# Registry keys used for context menu
|
|
fileKeyPath = 'HKCU\\Software\\Classes\\*\\shell\\Atom'
|
|
directoryKeyPath = 'HKCU\\Software\\Classes\\directory\\shell\\Atom'
|
|
backgroundKeyPath = 'HKCU\\Software\\Classes\\directory\\background\\shell\\Atom'
|
|
environmentKeyPath = 'HKCU\\Environment'
|
|
|
|
# Spawn a command and invoke the callback when it completes with an error
|
|
# and the output from standard out.
|
|
spawn = (command, args, callback) ->
|
|
stdout = ''
|
|
|
|
try
|
|
spawnedProcess = ChildProcess.spawn(command, args)
|
|
catch error
|
|
# Spawn can throw an error
|
|
process.nextTick -> callback?(error, stdout)
|
|
return
|
|
|
|
spawnedProcess.stdout.on 'data', (data) -> stdout += data
|
|
|
|
error = null
|
|
spawnedProcess.on 'error', (processError) -> error ?= processError
|
|
spawnedProcess.on 'close', (code, signal) ->
|
|
error ?= new Error("Command failed: #{signal ? code}") if code isnt 0
|
|
error?.code ?= code
|
|
error?.stdout ?= stdout
|
|
callback?(error, stdout)
|
|
|
|
# Spawn reg.exe and callback when it completes
|
|
spawnReg = (args, callback) ->
|
|
spawn(regPath, args, callback)
|
|
|
|
# Spawn setx.exe and callback when it completes
|
|
spawnSetx = (args, callback) ->
|
|
spawn(setxPath, args, callback)
|
|
|
|
# Spawn the Update.exe with the given arguments and invoke the callback when
|
|
# the command completes.
|
|
spawnUpdate = (args, callback) ->
|
|
spawn(updateDotExe, args, callback)
|
|
|
|
# Install the Open with Atom explorer context menu items via the registry.
|
|
installContextMenu = (callback) ->
|
|
addToRegistry = (args, callback) ->
|
|
args.unshift('add')
|
|
args.push('/f')
|
|
spawnReg(args, callback)
|
|
|
|
installMenu = (keyPath, arg, callback) ->
|
|
args = [keyPath, '/ve', '/d', 'Open with Atom']
|
|
addToRegistry args, ->
|
|
args = [keyPath, '/v', 'Icon', '/d', process.execPath]
|
|
addToRegistry args, ->
|
|
args = ["#{keyPath}\\command", '/ve', '/d', "#{process.execPath} \"#{arg}\""]
|
|
addToRegistry(args, callback)
|
|
|
|
installMenu fileKeyPath, '%1', ->
|
|
installMenu directoryKeyPath, '%1', ->
|
|
installMenu(backgroundKeyPath, '%V', callback)
|
|
|
|
# Get the user's PATH environment variable registry value.
|
|
getPath = (callback) ->
|
|
spawnReg ['query', environmentKeyPath, '/v', 'Path'], (error, stdout) ->
|
|
if error?
|
|
if error.code is 1
|
|
# The query failed so the Path does not exist yet in the registry
|
|
return callback(null, '')
|
|
else
|
|
return callback(error)
|
|
|
|
# Registry query output is in the form:
|
|
#
|
|
# HKEY_CURRENT_USER\Environment
|
|
# Path REG_SZ C:\a\folder\on\the\path;C\another\folder
|
|
#
|
|
|
|
lines = stdout.split(/[\r\n]+/).filter (line) -> line
|
|
segments = lines[lines.length - 1]?.split(' ')
|
|
if segments[1] is 'Path' and segments.length >= 3
|
|
pathEnv = segments?[3..].join(' ')
|
|
callback(null, pathEnv)
|
|
else
|
|
callback(new Error('Registry query for PATH failed'))
|
|
|
|
# Uninstall the Open with Atom explorer context menu items via the registry.
|
|
uninstallContextMenu = (callback) ->
|
|
deleteFromRegistry = (keyPath, callback) ->
|
|
spawnReg(['delete', keyPath, '/f'], callback)
|
|
|
|
deleteFromRegistry fileKeyPath, ->
|
|
deleteFromRegistry directoryKeyPath, ->
|
|
deleteFromRegistry(backgroundKeyPath, callback)
|
|
|
|
# Add atom and apm to the PATH
|
|
#
|
|
# This is done by adding .cmd shims to the root bin folder in the Atom
|
|
# install directory that point to the newly installed versions inside
|
|
# the versioned app directories.
|
|
addCommandsToPath = (callback) ->
|
|
installCommands = (callback) ->
|
|
atomCommandPath = path.join(binFolder, 'atom.cmd')
|
|
relativeAtomPath = path.relative(binFolder, path.join(appFolder, 'resources', 'cli', 'atom.cmd'))
|
|
atomCommand = "@echo off\r\n\"%~dp0\\#{relativeAtomPath}\" %*"
|
|
|
|
atomShCommandPath = path.join(binFolder, 'atom')
|
|
relativeAtomShPath = path.relative(binFolder, path.join(appFolder, 'resources', 'cli', 'atom.sh'))
|
|
atomShCommand = "#!/bin/sh\r\n\"$0/../#{relativeAtomShPath.replace(/\\/g, '/')}\" \"$@\""
|
|
|
|
apmCommandPath = path.join(binFolder, 'apm.cmd')
|
|
relativeApmPath = path.relative(binFolder, path.join(process.resourcesPath, 'app', 'apm', 'bin', 'apm.cmd'))
|
|
apmCommand = "@echo off\r\n\"%~dp0\\#{relativeApmPath}\" %*"
|
|
|
|
apmShCommandPath = path.join(binFolder, 'apm')
|
|
relativeApmShPath = path.relative(binFolder, path.join(appFolder, 'resources', 'cli', 'apm.sh'))
|
|
apmShCommand = "#!/bin/sh\r\n\"$0/../#{relativeApmShPath.replace(/\\/g, '/')}\" \"$@\""
|
|
|
|
fs.writeFile atomCommandPath, atomCommand, ->
|
|
fs.writeFile atomShCommandPath, atomShCommand, ->
|
|
fs.writeFile apmCommandPath, apmCommand, ->
|
|
fs.writeFile apmShCommandPath, apmShCommand, ->
|
|
callback()
|
|
|
|
addBinToPath = (pathSegments, callback) ->
|
|
pathSegments.push(binFolder)
|
|
newPathEnv = pathSegments.join(';')
|
|
spawnSetx(['Path', newPathEnv], callback)
|
|
|
|
installCommands (error) ->
|
|
return callback(error) if error?
|
|
|
|
getPath (error, pathEnv) ->
|
|
return callback(error) if error?
|
|
|
|
pathSegments = pathEnv.split(/;+/).filter (pathSegment) -> pathSegment
|
|
if pathSegments.indexOf(binFolder) is -1
|
|
addBinToPath(pathSegments, callback)
|
|
else
|
|
callback()
|
|
|
|
# Remove atom and apm from the PATH
|
|
removeCommandsFromPath = (callback) ->
|
|
getPath (error, pathEnv) ->
|
|
return callback(error) if error?
|
|
|
|
pathSegments = pathEnv.split(/;+/).filter (pathSegment) ->
|
|
pathSegment and pathSegment isnt binFolder
|
|
newPathEnv = pathSegments.join(';')
|
|
|
|
if pathEnv isnt newPathEnv
|
|
spawnSetx(['Path', newPathEnv], callback)
|
|
else
|
|
callback()
|
|
|
|
# Create a desktop and start menu shortcut by using the command line API
|
|
# provided by Squirrel's Update.exe
|
|
createShortcuts = (callback) ->
|
|
spawnUpdate(['--createShortcut', exeName], callback)
|
|
|
|
# Update the desktop and start menu shortcuts by using the command line API
|
|
# provided by Squirrel's Update.exe
|
|
updateShortcuts = (callback) ->
|
|
if homeDirectory = fs.getHomeDirectory()
|
|
desktopShortcutPath = path.join(homeDirectory, 'Desktop', 'Atom.lnk')
|
|
# Check if the desktop shortcut has been previously deleted and
|
|
# and keep it deleted if it was
|
|
fs.exists desktopShortcutPath, (desktopShortcutExists) ->
|
|
createShortcuts ->
|
|
if desktopShortcutExists
|
|
callback()
|
|
else
|
|
# Remove the unwanted desktop shortcut that was recreated
|
|
fs.unlink(desktopShortcutPath, callback)
|
|
else
|
|
createShortcuts(callback)
|
|
|
|
# Remove the desktop and start menu shortcuts by using the command line API
|
|
# provided by Squirrel's Update.exe
|
|
removeShortcuts = (callback) ->
|
|
spawnUpdate(['--removeShortcut', exeName], callback)
|
|
|
|
exports.spawn = spawnUpdate
|
|
|
|
# Is the Update.exe installed with Atom?
|
|
exports.existsSync = ->
|
|
fs.existsSync(updateDotExe)
|
|
|
|
# Restart Atom using the version pointed to by the atom.cmd shim
|
|
exports.restartAtom = (app) ->
|
|
if projectPath = global.application?.lastFocusedWindow?.projectPath
|
|
args = [projectPath]
|
|
app.once 'will-quit', -> spawn(path.join(binFolder, 'atom.cmd'), args)
|
|
app.quit()
|
|
|
|
# Handle squirrel events denoted by --squirrel-* command line arguments.
|
|
exports.handleStartupEvent = (app, squirrelCommand) ->
|
|
switch squirrelCommand
|
|
when '--squirrel-install'
|
|
createShortcuts ->
|
|
installContextMenu ->
|
|
addCommandsToPath ->
|
|
app.quit()
|
|
true
|
|
when '--squirrel-updated'
|
|
updateShortcuts ->
|
|
installContextMenu ->
|
|
addCommandsToPath ->
|
|
app.quit()
|
|
true
|
|
when '--squirrel-uninstall'
|
|
removeShortcuts ->
|
|
uninstallContextMenu ->
|
|
removeCommandsFromPath ->
|
|
app.quit()
|
|
true
|
|
when '--squirrel-obsolete'
|
|
app.quit()
|
|
true
|
|
else
|
|
false
|