mirror of
https://github.com/lxsang/antos-frontend.git
synced 2025-07-27 03:09:45 +02:00
switch to es6 from coffeescript
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
coffee_files = dialog.coffee main.coffee
|
||||
module_files = dialog.js main.js
|
||||
|
||||
jsfiles =
|
||||
libfiles =
|
||||
|
||||
cssfiles = main.css
|
||||
|
||||
|
@ -1,84 +0,0 @@
|
||||
# Copyright 2017-2018 Xuan Sang LE <xsang.le AT gmail DOT com>
|
||||
|
||||
# AnTOS Web desktop is is licensed under the GNU General Public
|
||||
# License v3.0, see the LICENCE file for more information
|
||||
|
||||
# This program is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of
|
||||
# the License, or (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
#along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
|
||||
class RepositoryDialog extends this.OS.GUI.subwindows.SelectionDialog
|
||||
constructor: () ->
|
||||
super()
|
||||
|
||||
main: () ->
|
||||
super.main()
|
||||
@list = @find "list"
|
||||
$((@find "btnOk")).hide()
|
||||
@list.set "buttons", [
|
||||
{
|
||||
text: "+",
|
||||
onbtclick: () =>
|
||||
@openDialog("PromptDialog", {
|
||||
title: __("Add repository"),
|
||||
label: __("Format : [name] url")
|
||||
}).then (e) =>
|
||||
m = e.match /\[([^\]]*)\]\s*(.+)/
|
||||
if not m or m.length isnt 3
|
||||
return @error __("Wrong format: it should be [name] url")
|
||||
repo = {
|
||||
url: m[2],
|
||||
text: m[1]
|
||||
}
|
||||
@systemsetting.system.repositories.push repo
|
||||
@list.push repo
|
||||
},
|
||||
{
|
||||
text: "-",
|
||||
onbtclick: () =>
|
||||
el = @list.get "selectedItem"
|
||||
return unless el
|
||||
selidx = $(el).index()
|
||||
return unless selidx >= 0
|
||||
@systemsetting.system.repositories.splice selidx, selidx
|
||||
@list.remove el
|
||||
},
|
||||
{
|
||||
iconclass: "fa fa-pencil",
|
||||
onbtclick: () => @editRepo()
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
editRepo: () ->
|
||||
el = @list.get "selectedItem"
|
||||
return unless el
|
||||
selidx = $(el).index()
|
||||
return unless selidx >= 0
|
||||
data = el.get "data"
|
||||
sel = @systemsetting.system.repositories[selidx]
|
||||
@openDialog("PromptDialog", {
|
||||
title: __("Edit repository"),
|
||||
label: __("Format : [name] url"),
|
||||
value: "[#{data.text}] #{data.url}"
|
||||
}).then (e) =>
|
||||
m = e.match /\[([^\]]*)\]\s*(.+)/
|
||||
if not m or m.length isnt 3
|
||||
return @error __("Wrong format: it should be [name] url")
|
||||
data.text = m[1]
|
||||
data.url = m[2]
|
||||
@list.update()
|
||||
@list.unselect()
|
||||
|
||||
onexit: (e) ->
|
||||
@parent.refreshRepoList()
|
||||
super.onexit e
|
100
src/packages/MarketPlace/dialog.js
Normal file
100
src/packages/MarketPlace/dialog.js
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
// Copyright 2017-2018 Xuan Sang LE <xsang.le AT gmail DOT com>
|
||||
|
||||
// AnTOS Web desktop is is licensed under the GNU General Public
|
||||
// License v3.0, see the LICENCE file for more information
|
||||
|
||||
// This program is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
//along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
|
||||
class RepositoryDialog extends this.OS.GUI.subwindows.SelectionDialog {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
main() {
|
||||
super.main();
|
||||
this.list = this.find("list");
|
||||
$((this.find("btnOk"))).hide();
|
||||
return this.list.set("buttons", [
|
||||
{
|
||||
text: "+",
|
||||
onbtclick: () => {
|
||||
return this.openDialog("PromptDialog", {
|
||||
title: __("Add repository"),
|
||||
label: __("Format : [name] url")
|
||||
}).then(e => {
|
||||
const m = e.match(/\[([^\]]*)\]\s*(.+)/);
|
||||
if (!m || (m.length !== 3)) {
|
||||
return this.error(__("Wrong format: it should be [name] url"));
|
||||
}
|
||||
const repo = {
|
||||
url: m[2],
|
||||
text: m[1]
|
||||
};
|
||||
this.systemsetting.system.repositories.push(repo);
|
||||
return this.list.push(repo);
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "-",
|
||||
onbtclick: () => {
|
||||
const el = this.list.get("selectedItem");
|
||||
if (!el) { return; }
|
||||
const selidx = $(el).index();
|
||||
if (!(selidx >= 0)) { return; }
|
||||
this.systemsetting.system.repositories.splice(selidx, selidx);
|
||||
return this.list.remove(el);
|
||||
}
|
||||
},
|
||||
{
|
||||
iconclass: "fa fa-pencil",
|
||||
onbtclick: () => this.editRepo()
|
||||
}
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
editRepo() {
|
||||
const el = this.list.get("selectedItem");
|
||||
if (!el) { return; }
|
||||
const selidx = $(el).index();
|
||||
if (!(selidx >= 0)) { return; }
|
||||
const data = el.get("data");
|
||||
const sel = this.systemsetting.system.repositories[selidx];
|
||||
return this.openDialog("PromptDialog", {
|
||||
title: __("Edit repository"),
|
||||
label: __("Format : [name] url"),
|
||||
value: `[${data.text}] ${data.url}`
|
||||
}).then(e => {
|
||||
const m = e.match(/\[([^\]]*)\]\s*(.+)/);
|
||||
if (!m || (m.length !== 3)) {
|
||||
return this.error(__("Wrong format: it should be [name] url"));
|
||||
}
|
||||
data.text = m[1];
|
||||
data.url = m[2];
|
||||
this.list.update();
|
||||
return this.list.unselect();
|
||||
});
|
||||
}
|
||||
|
||||
onexit(e) {
|
||||
this.parent.refreshRepoList();
|
||||
return super.onexit(e);
|
||||
}
|
||||
}
|
@ -1,357 +0,0 @@
|
||||
# Copyright 2017-2018 Xuan Sang LE <xsang.le AT gmail DOT com>
|
||||
|
||||
# AnTOS Web desktop is is licensed under the GNU General Public
|
||||
# License v3.0, see the LICENCE file for more information
|
||||
|
||||
# This program is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of
|
||||
# the License, or (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
#along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
|
||||
class MarketPlace extends this.OS.GUI.BaseApplication
|
||||
constructor: (args) ->
|
||||
super "MarketPlace", args
|
||||
|
||||
main: () ->
|
||||
@installdir = @systemsetting.system.pkgpaths.user
|
||||
# test repository
|
||||
@apps_meta = []
|
||||
@repo = @find "repo"
|
||||
@repo.set "onlistselect", (e) =>
|
||||
data = e.data.item.get("data")
|
||||
return unless data
|
||||
@fetchApps data
|
||||
|
||||
@refreshRepoList()
|
||||
|
||||
@applist = @find "applist"
|
||||
@applist.set "onlistselect", (e) =>
|
||||
data = e.data.item.get("data")
|
||||
@appDetail data
|
||||
|
||||
@container = @find "container"
|
||||
@appname = @find "appname"
|
||||
@appdesc = @find "app-desc"
|
||||
@appdetail = @find "app-detail"
|
||||
@btinstall = @find "bt-install"
|
||||
@btremove = @find "bt-remove"
|
||||
@btexec = @find "bt-exec"
|
||||
@searchbox = @find "searchbox"
|
||||
($ @container).css "visibility", "hidden"
|
||||
@btexec.set "onbtclick", (e) =>
|
||||
el = @applist.get "selectedItem"
|
||||
return unless el
|
||||
app = el.get("data")
|
||||
@_gui.launch app.pkgname if app.pkgname
|
||||
|
||||
@btinstall.set "onbtclick", (e) =>
|
||||
if @btinstall.get "dirty"
|
||||
return @updatePackage()
|
||||
.then () => @notify __("Package updated")
|
||||
.catch (e) => @error e.toString(), e
|
||||
@remoteInstall()
|
||||
.then (n) => @notify __("Package installed: {0}", n)
|
||||
.catch (e) => @error e.toString(), e
|
||||
|
||||
@btremove.set "onbtclick", (e) =>
|
||||
@uninstall()
|
||||
.then () => @notify __("Packaged uninstalled")
|
||||
.catch (e) => @error e.toString(), e
|
||||
|
||||
@bindKey "CTRL-R", () =>
|
||||
@menuOptionsHandle "repos"
|
||||
|
||||
$(@searchbox).keyup (e) => @search e
|
||||
|
||||
refreshRepoList: () ->
|
||||
list = (v for v in @systemsetting.system.repositories)
|
||||
list.unshift {
|
||||
text: "Installed"
|
||||
}
|
||||
@repo.set "data", list
|
||||
|
||||
search: (e) ->
|
||||
switch e.which
|
||||
when 37
|
||||
e.preventDefault()
|
||||
when 38
|
||||
@applist.selectPrev()
|
||||
e.preventDefault()
|
||||
when 39
|
||||
e.preventDefault()
|
||||
when 40
|
||||
@applist.selectNext()
|
||||
e.preventDefault()
|
||||
when 13
|
||||
e.preventDefault()
|
||||
else
|
||||
text = @searchbox.value
|
||||
@applist.set "data", (v for v in @apps_meta) if text.length is 2
|
||||
return if text.length < 3
|
||||
result = []
|
||||
term = new RegExp text, 'i'
|
||||
result.push v for v in @apps_meta when v.text.match term
|
||||
@applist.set "data", result
|
||||
|
||||
|
||||
fetchApps: (data) ->
|
||||
if not data.url
|
||||
pkgcache = @systemsetting.system.packages
|
||||
list = []
|
||||
for k, v of pkgcache
|
||||
list.push {
|
||||
pkgname: if v.pkgname then v.pkgname else v.app,
|
||||
name: v.name,
|
||||
text: v.name,
|
||||
icon: v.icon,
|
||||
iconclass: v.iconclass,
|
||||
category: v.category,
|
||||
author: v.info.author,
|
||||
version: v.version,
|
||||
description: "#{v.path}/REAME.md"
|
||||
}
|
||||
@apps_meta = list
|
||||
@applist.set "data", list
|
||||
return
|
||||
|
||||
@_api.get (data.url + "?_=" + (new Date().getTime())) , "json"
|
||||
.then ( d ) =>
|
||||
for v in d
|
||||
v.text = v.name
|
||||
v.iconclass = "fa fa-adn"
|
||||
@apps_meta = d
|
||||
@applist.set "data", d
|
||||
.catch (e) =>
|
||||
@error __("Fail to fetch packages list from: {0}", data.url), e
|
||||
|
||||
appDetail: (d) ->
|
||||
($ @container).css "visibility", "visible"
|
||||
( $ @appname ).html d.name
|
||||
(@find "vstat").set "text", ""
|
||||
if d.description
|
||||
d.description.asFileHandle().read().then (text) =>
|
||||
converter = new showdown.Converter()
|
||||
($ @appdesc).html(converter.makeHtml text)
|
||||
.catch (e) =>
|
||||
@notify __("Unable to read package description")
|
||||
($ @appdesc).empty()
|
||||
else
|
||||
($ @appdesc).empty()
|
||||
pkgcache = @systemsetting.system.packages
|
||||
@btinstall.set "text", "__(Install)"
|
||||
@btinstall.set "dirty", false
|
||||
if pkgcache[d.pkgname]
|
||||
vs = pkgcache[d.pkgname].version
|
||||
ovs = d.version
|
||||
($ @btinstall).hide()
|
||||
if vs and ovs
|
||||
vs = vs.__v()
|
||||
ovs = ovs.__v()
|
||||
if ovs.nt vs
|
||||
@btinstall.set "dirty", true
|
||||
@btinstall.set "text", "__(Update)"
|
||||
($ @btinstall).show()
|
||||
(@find "vstat").set "text",
|
||||
__("Your application version is older ({0} < {1})", vs, ovs)
|
||||
($ @btremove).show()
|
||||
($ @btexec).show()
|
||||
else
|
||||
($ @btinstall).show()
|
||||
($ @btremove).hide()
|
||||
($ @btexec).hide()
|
||||
|
||||
($ @appdetail).empty()
|
||||
for k, v of d when k isnt "name" and k isnt "description" and k isnt "domel"
|
||||
($ @appdetail).append(
|
||||
$("<li>")
|
||||
.append(($ "<span class= 'info-header'>").html k)
|
||||
.append $("<span>").html v
|
||||
)
|
||||
|
||||
menu: () ->
|
||||
return [
|
||||
{
|
||||
text: "__(Options)", child: [
|
||||
{ text: "__(Repositories)", shortcut: "C-R", id: "repos" },
|
||||
{ text: "__(Install from zip)", shortcut: "C-I", id: "install" }
|
||||
] , onchildselect: (e) =>
|
||||
@menuOptionsHandle e.data.item.get("data").id
|
||||
}
|
||||
]
|
||||
|
||||
menuOptionsHandle: (id) ->
|
||||
switch id
|
||||
when "repos"
|
||||
@openDialog new RepositoryDialog(), {
|
||||
title: __("Repositories"),
|
||||
data: @systemsetting.system.repositories
|
||||
}
|
||||
when "install"
|
||||
@localInstall().then (n) =>
|
||||
@notify __("Package installed: {0}", n)
|
||||
.catch (e) => @error __("Unable to install package"), e
|
||||
else
|
||||
|
||||
remoteInstall: () ->
|
||||
el = @applist.get "selectedItem"
|
||||
return unless el
|
||||
app = el.get "data"
|
||||
return unless app
|
||||
# get blob file
|
||||
new Promise (resolve, reject) =>
|
||||
@_api.blob app.download + "?_=" + (new Date().getTime())
|
||||
.then (data) =>
|
||||
@install data, app
|
||||
.then (n) -> resolve(n)
|
||||
.catch (e) -> reject(__e e)
|
||||
.catch (e) -> reject __e e
|
||||
|
||||
localInstall: () ->
|
||||
new Promise (resolve, reject) =>
|
||||
@openDialog("FileDialog", {
|
||||
title: "__(Select package archive)",
|
||||
mimes: [".*/zip"]
|
||||
}).then (d) =>
|
||||
d.file.path.asFileHandle().read("binary").then (data) =>
|
||||
@install data
|
||||
.then (n) =>
|
||||
@repo.unselect()
|
||||
@repo.set "selected", 0
|
||||
apps = (v.pkgname for v in @applist.get("data"))
|
||||
idx = apps.indexOf n
|
||||
if idx >= 0
|
||||
@applist.set "selected", idx
|
||||
resolve(n)
|
||||
.catch (e) -> reject(__e e)
|
||||
.catch (e) -> reject __e e
|
||||
.catch (e) -> reject __e e
|
||||
|
||||
install: (data, meta) ->
|
||||
new Promise (resolve, reject) =>
|
||||
JSZip.loadAsync(data).then (zip) =>
|
||||
zip.file("package.json").async("string").then (d) =>
|
||||
v = JSON.parse d
|
||||
pth = "#{@installdir}/#{v.app}"
|
||||
dir = [pth]
|
||||
files = []
|
||||
for name, file of zip.files
|
||||
if file.dir
|
||||
dir.push(pth + "/" + name)
|
||||
else
|
||||
files.push name
|
||||
# create all directory
|
||||
@mkdirs(dir).then () =>
|
||||
@installFile(v.app, zip, files).then () =>
|
||||
app_meta = {
|
||||
pkgname: v.app,
|
||||
name: v.name,
|
||||
text: v.name,
|
||||
icon: v.icon,
|
||||
iconclass: v.iconclass,
|
||||
category: v.category,
|
||||
author: v.info.author,
|
||||
version: v.version,
|
||||
description: if meta then meta.description else undefined,
|
||||
download: if meta then meta.download else undefined
|
||||
}
|
||||
v.text = v.name
|
||||
v.filename = v.app
|
||||
v.type = "app"
|
||||
v.mime = "antos/app"
|
||||
v.iconclass = "fa fa-adn" unless v.iconclass or v.icon
|
||||
v.path = pth
|
||||
@systemsetting.system.packages[v.app] = v
|
||||
@appDetail app_meta
|
||||
resolve(v.name)
|
||||
.catch (e) -> reject __e e
|
||||
.catch (e) -> reject __e e
|
||||
.catch (err) -> reject __e err
|
||||
.catch (e) -> reject __e e
|
||||
|
||||
uninstall: () ->
|
||||
new Promise (resolve, reject) =>
|
||||
el = @applist.get "selectedItem"
|
||||
return unless el
|
||||
sel = el.get "data"
|
||||
return unless sel
|
||||
name = sel.pkgname
|
||||
app = @systemsetting.system.packages[sel.pkgname]
|
||||
return unless app
|
||||
@openDialog("YesNoDialog", {
|
||||
title: __("Uninstall") ,
|
||||
text: __("Uninstall: {0}?", app.name)
|
||||
}).then (d) =>
|
||||
return unless d
|
||||
app.path.asFileHandle().remove().then (r) =>
|
||||
if r.error
|
||||
return reject @_api.throwe __("Cannot uninstall package: {0}", r.error)
|
||||
@notify __("Package uninstalled")
|
||||
# stop all the services if any
|
||||
if app.services
|
||||
for srv in app.services
|
||||
@_gui.unloadApp srv
|
||||
|
||||
delete @systemsetting.system.packages[name]
|
||||
@_gui.unloadApp name
|
||||
if sel.download
|
||||
@appDetail sel
|
||||
else
|
||||
@applist.remove el
|
||||
($ @container).css "visibility", "hidden"
|
||||
resolve()
|
||||
.catch (e) -> reject __e e
|
||||
.catch (e) -> reject __e e
|
||||
|
||||
updatePackage: () ->
|
||||
new Promise (resolve, reject) =>
|
||||
@uninstall().then () =>
|
||||
@remoteInstall()
|
||||
.then () -> resolve()
|
||||
.catch (e) -> reject __e e
|
||||
.catch (e) -> reject __e e
|
||||
|
||||
mkdirs: (list) ->
|
||||
new Promise (resolve, reject) =>
|
||||
return resolve() if list.length is 0
|
||||
dir = (list.splice 0, 1)[0].asFileHandle()
|
||||
path = dir.parent()
|
||||
dname = dir.basename
|
||||
path.asFileHandle().mk dname
|
||||
.then (r) =>
|
||||
return reject(@_api.throwe __("Cannot create {0}", "#{path}/#{dir}")) if r.error
|
||||
@mkdirs list
|
||||
.then () -> resolve()
|
||||
.catch (e) -> reject __e e
|
||||
.catch (e) -> reject __e e
|
||||
|
||||
installFile: (n, zip, files) ->
|
||||
new Promise (resolve, reject) =>
|
||||
return resolve() if files.length is 0
|
||||
file = (files.splice 0, 1)[0]
|
||||
path = "#{@installdir}/#{n}/#{file}"
|
||||
zip.file(file).async("uint8array").then (d) =>
|
||||
fp = path.asFileHandle()
|
||||
fp.cache = new Blob [d], { type: "octet/stream" }
|
||||
fp.write "text/plain"
|
||||
.then (r) =>
|
||||
return reject @_api.throwe(__("Cannot install {0}", path)) if r.error
|
||||
@installFile n, zip, files
|
||||
.then () -> resolve()
|
||||
.catch (e) -> reject( __e e)
|
||||
.catch (e) -> reject __e e
|
||||
.catch (e) -> reject __e e
|
||||
|
||||
MarketPlace.dependencies = [
|
||||
"os://scripts/jszip.min.js",
|
||||
"os://scripts/showdown.min.js"
|
||||
]
|
||||
MarketPlace.singleton = true
|
||||
this.OS.register "MarketPlace", MarketPlace
|
433
src/packages/MarketPlace/main.js
Normal file
433
src/packages/MarketPlace/main.js
Normal file
@ -0,0 +1,433 @@
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS101: Remove unnecessary use of Array.from
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS205: Consider reworking code to avoid use of IIFEs
|
||||
* DS208: Avoid top-level this
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
// Copyright 2017-2018 Xuan Sang LE <xsang.le AT gmail DOT com>
|
||||
|
||||
// AnTOS Web desktop is is licensed under the GNU General Public
|
||||
// License v3.0, see the LICENCE file for more information
|
||||
|
||||
// This program is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
//along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
|
||||
class MarketPlace extends this.OS.GUI.BaseApplication {
|
||||
constructor(args) {
|
||||
super("MarketPlace", args);
|
||||
}
|
||||
|
||||
main() {
|
||||
this.installdir = this.systemsetting.system.pkgpaths.user;
|
||||
// test repository
|
||||
this.apps_meta = [];
|
||||
this.repo = this.find("repo");
|
||||
this.repo.set("onlistselect", e => {
|
||||
const data = e.data.item.get("data");
|
||||
if (!data) { return; }
|
||||
return this.fetchApps(data);
|
||||
});
|
||||
|
||||
this.refreshRepoList();
|
||||
|
||||
this.applist = this.find("applist");
|
||||
this.applist.set("onlistselect", e => {
|
||||
const data = e.data.item.get("data");
|
||||
return this.appDetail(data);
|
||||
});
|
||||
|
||||
this.container = this.find("container");
|
||||
this.appname = this.find("appname");
|
||||
this.appdesc = this.find("app-desc");
|
||||
this.appdetail = this.find("app-detail");
|
||||
this.btinstall = this.find("bt-install");
|
||||
this.btremove = this.find("bt-remove");
|
||||
this.btexec = this.find("bt-exec");
|
||||
this.searchbox = this.find("searchbox");
|
||||
($(this.container)).css("visibility", "hidden");
|
||||
this.btexec.set("onbtclick", e => {
|
||||
const el = this.applist.get("selectedItem");
|
||||
if (!el) { return; }
|
||||
const app = el.get("data");
|
||||
if (app.pkgname) { return this._gui.launch(app.pkgname); }
|
||||
});
|
||||
|
||||
this.btinstall.set("onbtclick", e => {
|
||||
if (this.btinstall.get("dirty")) {
|
||||
return this.updatePackage()
|
||||
.then(() => this.notify(__("Package updated")))
|
||||
.catch(e => this.error(e.toString(), e));
|
||||
}
|
||||
return this.remoteInstall()
|
||||
.then(n => this.notify(__("Package installed: {0}", n)))
|
||||
.catch(e => this.error(e.toString(), e));
|
||||
});
|
||||
|
||||
this.btremove.set("onbtclick", e => {
|
||||
return this.uninstall()
|
||||
.then(() => this.notify(__("Packaged uninstalled")))
|
||||
.catch(e => this.error(e.toString(), e));
|
||||
});
|
||||
|
||||
this.bindKey("CTRL-R", () => {
|
||||
return this.menuOptionsHandle("repos");
|
||||
});
|
||||
|
||||
return $(this.searchbox).keyup(e => this.search(e));
|
||||
}
|
||||
|
||||
refreshRepoList() {
|
||||
const list = (Array.from(this.systemsetting.system.repositories));
|
||||
list.unshift({
|
||||
text: "Installed"
|
||||
});
|
||||
return this.repo.set("data", list);
|
||||
}
|
||||
|
||||
search(e) {
|
||||
let v;
|
||||
switch (e.which) {
|
||||
case 37:
|
||||
return e.preventDefault();
|
||||
case 38:
|
||||
this.applist.selectPrev();
|
||||
return e.preventDefault();
|
||||
case 39:
|
||||
return e.preventDefault();
|
||||
case 40:
|
||||
this.applist.selectNext();
|
||||
return e.preventDefault();
|
||||
case 13:
|
||||
return e.preventDefault();
|
||||
default:
|
||||
var text = this.searchbox.value;
|
||||
if (text.length === 2) { this.applist.set("data", ((() => {
|
||||
const result1 = [];
|
||||
for (v of Array.from(this.apps_meta)) { result1.push(v);
|
||||
}
|
||||
return result1;
|
||||
})())); }
|
||||
if (text.length < 3) { return; }
|
||||
var result = [];
|
||||
var term = new RegExp(text, 'i');
|
||||
for (v of Array.from(this.apps_meta)) { if (v.text.match(term)) { result.push(v); } }
|
||||
return this.applist.set("data", result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fetchApps(data) {
|
||||
let v;
|
||||
if (!data.url) {
|
||||
const pkgcache = this.systemsetting.system.packages;
|
||||
const list = [];
|
||||
for (let k in pkgcache) {
|
||||
v = pkgcache[k];
|
||||
list.push({
|
||||
pkgname: v.pkgname ? v.pkgname : v.app,
|
||||
name: v.name,
|
||||
text: v.name,
|
||||
icon: v.icon,
|
||||
iconclass: v.iconclass,
|
||||
category: v.category,
|
||||
author: v.info.author,
|
||||
version: v.version,
|
||||
description: `${v.path}/REAME.md`
|
||||
});
|
||||
}
|
||||
this.apps_meta = list;
|
||||
this.applist.set("data", list);
|
||||
return;
|
||||
}
|
||||
|
||||
return this._api.get((data.url + "?_=" + (new Date().getTime())) , "json")
|
||||
.then(d => {
|
||||
for (v of Array.from(d)) {
|
||||
v.text = v.name;
|
||||
v.iconclass = "fa fa-adn";
|
||||
}
|
||||
this.apps_meta = d;
|
||||
return this.applist.set("data", d);
|
||||
}).catch(e => {
|
||||
return this.error(__("Fail to fetch packages list from: {0}", data.url), e);
|
||||
});
|
||||
}
|
||||
|
||||
appDetail(d) {
|
||||
($(this.container)).css("visibility", "visible");
|
||||
( $(this.appname) ).html(d.name);
|
||||
(this.find("vstat")).set("text", "");
|
||||
if (d.description) {
|
||||
d.description.asFileHandle().read().then(text => {
|
||||
const converter = new showdown.Converter();
|
||||
return ($(this.appdesc)).html(converter.makeHtml(text));
|
||||
}).catch(e => {
|
||||
this.notify(__("Unable to read package description"));
|
||||
return ($(this.appdesc)).empty();
|
||||
});
|
||||
} else {
|
||||
($(this.appdesc)).empty();
|
||||
}
|
||||
const pkgcache = this.systemsetting.system.packages;
|
||||
this.btinstall.set("text", "__(Install)");
|
||||
this.btinstall.set("dirty", false);
|
||||
if (pkgcache[d.pkgname]) {
|
||||
let vs = pkgcache[d.pkgname].version;
|
||||
let ovs = d.version;
|
||||
($(this.btinstall)).hide();
|
||||
if (vs && ovs) {
|
||||
vs = vs.__v();
|
||||
ovs = ovs.__v();
|
||||
if (ovs.nt(vs)) {
|
||||
this.btinstall.set("dirty", true);
|
||||
this.btinstall.set("text", "__(Update)");
|
||||
($(this.btinstall)).show();
|
||||
(this.find("vstat")).set("text",
|
||||
__("Your application version is older ({0} < {1})", vs, ovs));
|
||||
}
|
||||
}
|
||||
($(this.btremove)).show();
|
||||
($(this.btexec)).show();
|
||||
} else {
|
||||
($(this.btinstall)).show();
|
||||
($(this.btremove)).hide();
|
||||
($(this.btexec)).hide();
|
||||
}
|
||||
|
||||
($(this.appdetail)).empty();
|
||||
return (() => {
|
||||
const result = [];
|
||||
for (let k in d) {
|
||||
const v = d[k];
|
||||
if ((k !== "name") && (k !== "description") && (k !== "domel")) {
|
||||
result.push(($(this.appdetail)).append(
|
||||
$("<li>")
|
||||
.append(($("<span class= 'info-header'>")).html(k))
|
||||
.append($("<span>").html(v))
|
||||
));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
}
|
||||
|
||||
menu() {
|
||||
return [
|
||||
{
|
||||
text: "__(Options)", child: [
|
||||
{ text: "__(Repositories)", shortcut: "C-R", id: "repos" },
|
||||
{ text: "__(Install from zip)", shortcut: "C-I", id: "install" }
|
||||
] , onchildselect: e => {
|
||||
return this.menuOptionsHandle(e.data.item.get("data").id);
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
menuOptionsHandle(id) {
|
||||
switch (id) {
|
||||
case "repos":
|
||||
return this.openDialog(new RepositoryDialog(), {
|
||||
title: __("Repositories"),
|
||||
data: this.systemsetting.system.repositories
|
||||
});
|
||||
case "install":
|
||||
return this.localInstall().then(n => {
|
||||
return this.notify(__("Package installed: {0}", n));
|
||||
}).catch(e => this.error(__("Unable to install package"), e));
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
remoteInstall() {
|
||||
const el = this.applist.get("selectedItem");
|
||||
if (!el) { return; }
|
||||
const app = el.get("data");
|
||||
if (!app) { return; }
|
||||
// get blob file
|
||||
return new Promise((resolve, reject) => {
|
||||
return this._api.blob(app.download + "?_=" + (new Date().getTime()))
|
||||
.then(data => {
|
||||
return this.install(data, app)
|
||||
.then(n => resolve(n))
|
||||
.catch(e => reject(__e(e)));
|
||||
}).catch(e => reject(__e(e)));
|
||||
});
|
||||
}
|
||||
|
||||
localInstall() {
|
||||
return new Promise((resolve, reject) => {
|
||||
return this.openDialog("FileDialog", {
|
||||
title: "__(Select package archive)",
|
||||
mimes: [".*/zip"]
|
||||
}).then(d => {
|
||||
return d.file.path.asFileHandle().read("binary").then(data => {
|
||||
return this.install(data)
|
||||
.then(n => {
|
||||
this.repo.unselect();
|
||||
this.repo.set("selected", 0);
|
||||
const apps = (Array.from(this.applist.get("data")).map((v) => v.pkgname));
|
||||
const idx = apps.indexOf(n);
|
||||
if (idx >= 0) {
|
||||
this.applist.set("selected", idx);
|
||||
}
|
||||
return resolve(n);
|
||||
}).catch(e => reject(__e(e)))
|
||||
.catch(e => reject(__e(e)));
|
||||
}).catch(e => reject(__e(e)));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
install(data, meta) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return JSZip.loadAsync(data).then(zip => {
|
||||
return zip.file("package.json").async("string").then(d => {
|
||||
let name;
|
||||
const v = JSON.parse(d);
|
||||
const pth = `${this.installdir}/${v.app}`;
|
||||
const dir = [pth];
|
||||
const files = [];
|
||||
for (name in zip.files) {
|
||||
const file = zip.files[name];
|
||||
if (file.dir) {
|
||||
dir.push(pth + "/" + name);
|
||||
} else {
|
||||
files.push(name);
|
||||
}
|
||||
}
|
||||
// create all directory
|
||||
return this.mkdirs(dir).then(() => {
|
||||
return this.installFile(v.app, zip, files).then(() => {
|
||||
const app_meta = {
|
||||
pkgname: v.app,
|
||||
name: v.name,
|
||||
text: v.name,
|
||||
icon: v.icon,
|
||||
iconclass: v.iconclass,
|
||||
category: v.category,
|
||||
author: v.info.author,
|
||||
version: v.version,
|
||||
description: meta ? meta.description : undefined,
|
||||
download: meta ? meta.download : undefined
|
||||
};
|
||||
v.text = v.name;
|
||||
v.filename = v.app;
|
||||
v.type = "app";
|
||||
v.mime = "antos/app";
|
||||
if (!v.iconclass && !v.icon) { v.iconclass = "fa fa-adn"; }
|
||||
v.path = pth;
|
||||
this.systemsetting.system.packages[v.app] = v;
|
||||
this.appDetail(app_meta);
|
||||
return resolve(v.name);
|
||||
}).catch(e => reject(__e(e)));
|
||||
}).catch(e => reject(__e(e)));
|
||||
}).catch(err => reject(__e(err)));
|
||||
}).catch(e => reject(__e(e)));
|
||||
});
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const el = this.applist.get("selectedItem");
|
||||
if (!el) { return; }
|
||||
const sel = el.get("data");
|
||||
if (!sel) { return; }
|
||||
const name = sel.pkgname;
|
||||
const app = this.systemsetting.system.packages[sel.pkgname];
|
||||
if (!app) { return; }
|
||||
return this.openDialog("YesNoDialog", {
|
||||
title: __("Uninstall") ,
|
||||
text: __("Uninstall: {0}?", app.name)
|
||||
}).then(d => {
|
||||
if (!d) { return; }
|
||||
return app.path.asFileHandle().remove().then(r => {
|
||||
if (r.error) {
|
||||
return reject(this._api.throwe(__("Cannot uninstall package: {0}", r.error)));
|
||||
}
|
||||
this.notify(__("Package uninstalled"));
|
||||
// stop all the services if any
|
||||
if (app.services) {
|
||||
for (let srv of Array.from(app.services)) {
|
||||
this._gui.unloadApp(srv);
|
||||
}
|
||||
}
|
||||
|
||||
delete this.systemsetting.system.packages[name];
|
||||
this._gui.unloadApp(name);
|
||||
if (sel.download) {
|
||||
this.appDetail(sel);
|
||||
} else {
|
||||
this.applist.remove(el);
|
||||
($(this.container)).css("visibility", "hidden");
|
||||
}
|
||||
return resolve();
|
||||
}).catch(e => reject(__e(e)));
|
||||
}).catch(e => reject(__e(e)));
|
||||
});
|
||||
}
|
||||
|
||||
updatePackage() {
|
||||
return new Promise((resolve, reject) => {
|
||||
return this.uninstall().then(() => {
|
||||
return this.remoteInstall()
|
||||
.then(() => resolve())
|
||||
.catch(e => reject(__e(e)));
|
||||
}).catch(e => reject(__e(e)));
|
||||
});
|
||||
}
|
||||
|
||||
mkdirs(list) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (list.length === 0) { return resolve(); }
|
||||
const dir = (list.splice(0, 1))[0].asFileHandle();
|
||||
const path = dir.parent();
|
||||
const dname = dir.basename;
|
||||
return path.asFileHandle().mk(dname)
|
||||
.then(r => {
|
||||
if (r.error) { return reject(this._api.throwe(__("Cannot create {0}", `${path}/${dir}`))); }
|
||||
return this.mkdirs(list)
|
||||
.then(() => resolve())
|
||||
.catch(e => reject(__e(e)));
|
||||
}).catch(e => reject(__e(e)));
|
||||
});
|
||||
}
|
||||
|
||||
installFile(n, zip, files) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (files.length === 0) { return resolve(); }
|
||||
const file = (files.splice(0, 1))[0];
|
||||
const path = `${this.installdir}/${n}/${file}`;
|
||||
return zip.file(file).async("uint8array").then(d => {
|
||||
const fp = path.asFileHandle();
|
||||
fp.cache = new Blob([d], { type: "octet/stream" });
|
||||
return fp.write("text/plain")
|
||||
.then(r => {
|
||||
if (r.error) { return reject(this._api.throwe(__("Cannot install {0}", path))); }
|
||||
return this.installFile(n, zip, files)
|
||||
.then(() => resolve())
|
||||
.catch(e => reject( __e(e)));
|
||||
}).catch(e => reject(__e(e)));
|
||||
}).catch(e => reject(__e(e)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MarketPlace.dependencies = [
|
||||
"os://scripts/jszip.min.js",
|
||||
"os://scripts/showdown.min.js"
|
||||
];
|
||||
MarketPlace.singleton = true;
|
||||
this.OS.register("MarketPlace", MarketPlace);
|
Reference in New Issue
Block a user