Mailspring/internal_packages/settings/lib/settings-packages-store.coffee

274 lines
8.7 KiB
CoffeeScript

_ = require 'underscore'
ipc = require 'ipc'
Reflux = require 'reflux'
path = require 'path'
fs = require 'fs-plus'
shell = require 'shell'
SettingsActions = require './settings-actions'
APMWrapper = require './apm-wrapper'
dialog = require('remote').require('dialog')
module.exports =
SettingsPackagesStore = Reflux.createStore
init: ->
@_apm = new APMWrapper()
@_globalSearch = ""
@_installedSearch = ""
@_installing = {}
@_featured = {themes: [], packages: []}
@_newerVersions = []
@_searchResults = null
@_refreshFeatured()
@listenTo SettingsActions.refreshFeaturedPackages, @_refreshFeatured
@listenTo SettingsActions.refreshInstalledPackages, @_refreshInstalled
atom.commands.add 'body',
'application:create-package': => @_onCreatePackage()
atom.commands.add 'body',
'application:install-package': => @_onInstallPackage()
@listenTo SettingsActions.createPackage, @_onCreatePackage
@listenTo SettingsActions.updatePackage, @_onUpdatePackage
@listenTo SettingsActions.setGlobalSearchValue, @_onGlobalSearchChange
@listenTo SettingsActions.setInstalledSearchValue, @_onInstalledSearchChange
@listenTo SettingsActions.showPackage, (pkg) =>
dir = atom.packages.resolvePackagePath(pkg.name)
shell.showItemInFolder(dir) if dir
@listenTo SettingsActions.installPackage, (pkg) =>
@_installing[pkg.name] = true
@trigger(@)
@_apm.install pkg, (err) =>
if err
delete @_installing[pkg.name]
@_displayError(err)
else
if atom.packages.isPackageDisabled(pkg.name)
atom.packages.enablePackage(pkg.name)
@_onPackagesChanged()
@listenTo SettingsActions.uninstallPackage, (pkg) =>
if atom.packages.isPackageLoaded(pkg.name)
atom.packages.disablePackage(pkg.name)
atom.packages.unloadPackage(pkg.name)
@_apm.uninstall pkg, (err) =>
@_displayError(err) if err
@_onPackagesChanged()
@listenTo SettingsActions.enablePackage, (pkg) ->
if atom.packages.isPackageDisabled(pkg.name)
atom.packages.enablePackage(pkg.name)
@listenTo SettingsActions.disablePackage, (pkg) ->
unless atom.packages.isPackageDisabled(pkg.name)
atom.packages.disablePackage(pkg.name)
@_hasPrepared = false
# Getters
installed: ->
@_prepareIfFresh()
@_addPackageStates(@_filter(@_installed, @_installedSearch))
installedSearchValue: ->
@_installedSearch
featured: ->
@_prepareIfFresh()
@_addPackageStates(@_featured)
searchResults: ->
@_addPackageStates(@_searchResults)
globalSearchValue: ->
@_globalSearch
# Action Handlers
_prepareIfFresh: ->
return if @_hasPrepared
atom.packages.onDidActivatePackage(=> @_onPackagesChangedDebounced())
atom.packages.onDidDeactivatePackage(=> @_onPackagesChangedDebounced())
atom.packages.onDidLoadPackage(=> @_onPackagesChangedDebounced())
atom.packages.onDidUnloadPackage(=> @_onPackagesChangedDebounced())
@_onPackagesChanged()
@_hasPrepared = true
_filter: (hash, search) ->
result = {}
search = search.toLowerCase()
for key, pkgs of hash
result[key] = _.filter pkgs, (p) =>
search.length is 0 or p.name.toLowerCase().indexOf(search) isnt -1
result
_refreshFeatured: ->
@_apm.getFeatured({themes: false}).then (results) =>
@_featured.packages = results
@trigger()
.catch (err) =>
# We may be offline
@_apm.getFeatured({themes: true}).then (results) =>
@_featured.themes = results
@trigger()
.catch (err) =>
# We may be offline
_refreshInstalled: ->
@_onPackagesChanged()
_refreshSearch: ->
return unless @_globalSearch?.length > 0
@_apm.search(@_globalSearch).then (results) =>
@_searchResults =
packages: results.filter ({theme}) -> not theme
themes: results.filter ({theme}) -> theme
@trigger()
.catch (err) =>
# We may be offline
_refreshSearchThrottled: _.debounce((-> @_refreshSearch()), 400)
_onPackagesChanged: ->
@_apm.getInstalled().then (packages) =>
for category in ['dev', 'user', 'core']
packages[category] = packages[category].filter ({theme}) -> not theme
packages[category].forEach (pkg) =>
pkg.category = category
delete @_installing[pkg.name]
@_installed = packages
@trigger()
@_apm.getOutdated().then (packages) =>
@_newerVersions = {}
for pkg in packages
@_newerVersions[pkg.name] = pkg.latestVersion
@trigger()
_onPackagesChangedDebounced: _.debounce((-> @_onPackagesChanged()), 200)
_onInstalledSearchChange: (val) ->
@_installedSearch = val
@trigger()
_onUpdatePackage: (pkg) ->
@_apm.update(pkg, pkg.newerVersion)
_onInstallPackage: ->
dialog.showOpenDialog
title: "Choose a Package Directory"
properties: ['openDirectory']
, (filenames) =>
return if not filenames or filenames.length is 0
packagesDir = path.join(atom.getConfigDirPath(), 'packages')
fs.makeTreeSync(packagesDir)
packageSourceDir = filenames[0]
packageName = path.basename(packageSourceDir)
packageTargetDir = path.join(packagesDir, packageName)
if fs.existsSync(packageTargetDir)
msg = "A package named '#{packageName}' is already installed in \
~/.nylas/packages. Remove it before trying to install another \
package of the same name."
dialog.showErrorBox('Package already installed', msg)
return
fs.copySync(packageSourceDir, packageTargetDir)
@_apm.installDependenciesInPackageDirectory packageTargetDir, (err) =>
shell.showItemInFolder(packageTargetDir)
if err
dialog.showErrorBox('Package installation failed', err.toString())
return
else
atom.packages.enablePackage(packageTargetDir)
atom.packages.activatePackage(packageName)
msg = "#{packageName} has been installed and enabled. No need to \
restart! If you don't see the package loaded, check the \
console for errors."
dialog.showErrorBox('Package installed', msg)
_onCreatePackage: ->
packagesDir = path.join(atom.getConfigDirPath(), 'packages')
fs.makeTreeSync(packagesDir)
dialog.showSaveDialog
title: "Save New Package"
defaultPath: packagesDir
properties: ['createDirectory']
, (packageDir) =>
return unless packageDir
packageName = path.basename(packageDir)
if not packageDir.startsWith(packagesDir)
return dialog.showErrorBox('Invalid package location', 'Sorry, you must \
create packages in the dev packages folder.')
if atom.packages.resolvePackagePath(packageName)
return dialog.showErrorBox('Invalid package name', 'Sorry, you must \
give your package a unqiue name.')
fs.mkdir packageDir, (err) =>
return dialog.showErrorBox('Could not create package', err.toString()) if err
{resourcePath} = atom.getLoadSettings()
packageTemplatePath = path.join(resourcePath, 'static', 'package-template')
packageJSON =
name: packageName
main: "./lib/main"
version: '0.1.0'
repository:
type: 'git'
url: ''
engines:
atom: ">=#{atom.getVersion()}"
description: "Enter a description of your package!"
dependencies: {}
license: "MIT"
fs.copySync(packageTemplatePath, packageDir)
fs.writeFileSync(path.join(packageDir, 'package.json'), JSON.stringify(packageJSON, null, 2))
shell.showItemInFolder(packageDir)
_.defer ->
atom.packages.enablePackage(packageDir)
atom.packages.activatePackage(packageName)
_onGlobalSearchChange: (val) ->
# Clear previous search results data if this is a new
# search beginning from "".
if @_globalSearch.length is 0 and val.length > 0
@_searchResults = null
@_globalSearch = val
@_refreshSearchThrottled()
@trigger()
_addPackageStates: (pkgs) ->
installedNames = _.flatten(_.values(@_installed)).map (pkg) -> pkg.name
_.flatten(_.values(pkgs)).forEach (pkg) =>
pkg.enabled = !atom.packages.isPackageDisabled(pkg.name)
pkg.installed = pkg.name in installedNames
pkg.installing = @_installing[pkg.name]?
pkg.newerVersionAvailable = @_newerVersions[pkg.name]?
pkg.newerVersion = @_newerVersions[pkg.name]
pkgs
_displayError: (err) ->
console.error(err)
chosen = dialog.showMessageBox
type: 'warning'
message: "Sorry, an error occurred."
detail: err.toString()
buttons: ["OK"]