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

269 lines
8.5 KiB
CoffeeScript
Raw Normal View History

_ = require 'underscore'
ipc = require 'ipc'
Reflux = require 'reflux'
path = require 'path'
fs = require 'fs-plus'
shell = require 'shell'
PluginsActions = require './plugins-actions'
{APMWrapper} = require 'nylas-exports'
dialog = require('remote').require('dialog')
module.exports =
PackagesStore = Reflux.createStore
init: ->
@_apm = new APMWrapper()
@_globalSearch = ""
@_installedSearch = ""
@_installing = {}
@_featured = {themes: [], packages: []}
@_newerVersions = []
@_searchResults = null
@_refreshFeatured()
@listenTo PluginsActions.refreshFeaturedPackages, @_refreshFeatured
@listenTo PluginsActions.refreshInstalledPackages, @_refreshInstalled
atom.commands.add 'body',
'application:create-package': => @_onCreatePackage()
atom.commands.add 'body',
'application:install-package': => @_onInstallPackage()
@listenTo PluginsActions.createPackage, @_onCreatePackage
@listenTo PluginsActions.updatePackage, @_onUpdatePackage
@listenTo PluginsActions.setGlobalSearchValue, @_onGlobalSearchChange
@listenTo PluginsActions.setInstalledSearchValue, @_onInstalledSearchChange
@listenTo PluginsActions.showPackage, (pkg) =>
dir = atom.packages.resolvePackagePath(pkg.name)
shell.showItemInFolder(dir) if dir
@listenTo PluginsActions.installPackage, (pkg) =>
@_installing[pkg.name] = true
@trigger(@)
@_apm.install pkg, (err) =>
if err
delete @_installing[pkg.name]
@_displayMessage("Sorry, an error occurred", err.toString())
else
if atom.packages.isPackageDisabled(pkg.name)
atom.packages.enablePackage(pkg.name)
@_onPackagesChanged()
@listenTo PluginsActions.uninstallPackage, (pkg) =>
if atom.packages.isPackageLoaded(pkg.name)
atom.packages.disablePackage(pkg.name)
atom.packages.unloadPackage(pkg.name)
@_apm.uninstall pkg, (err) =>
@_displayMessage("Sorry, an error occurred", err.toString()) if err
@_onPackagesChanged()
@listenTo PluginsActions.enablePackage, (pkg) ->
if atom.packages.isPackageDisabled(pkg.name)
atom.packages.enablePackage(pkg.name)
@listenTo PluginsActions.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
atom.packages.installPackageFromPath filenames[0], (err) =>
return if err
packageName = path.basename(filenames[0])
msg = "#{packageName} has been installed and enabled. No need to \
restart! If you don't see the package loaded, check the \
console for errors."
@_displayMessage("Package installed", msg)
_onCreatePackage: ->
if not atom.inDevMode()
btn = dialog.showMessageBox
type: 'warning'
message: "Run with debug flags?"
detail: "To develop plugins, you should run N1 with debug flags.
This gives you better error messages, the debug version of
React, and more. You can disable it at any time from the
Developer menu."
buttons: ["OK", "Cancel"]
if btn is 0
ipc.send('command', 'application:toggle-dev')
return
packagesDir = path.join(atom.getConfigDirPath(), 'dev', '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 @_displayMessage('Invalid package location', 'Sorry, you must
create packages in the packages folder.')
if atom.packages.resolvePackagePath(packageName)
return @_displayMessage('Invalid package name', 'Sorry, you must
give your package a unqiue name.')
if packageName.indexOf(' ') isnt -1
return @_displayMessage('Invalid package name', 'Sorry, package names
cannot contain spaces.')
fs.mkdir packageDir, (err) =>
return @_displayMessage('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
_displayMessage: (title, message) ->
dialog.showMessageBox
type: 'warning'
message: title
detail: message
buttons: ["OK"]