mirror of
https://github.com/lxsang/antos-frontend.git
synced 2024-11-16 01:38:21 +01:00
feat: use a separated setting file for each application instead of a single system setting files
All checks were successful
gitea-sync/antos/pipeline/head This commit looks good
All checks were successful
gitea-sync/antos/pipeline/head This commit looks good
This commit is contained in:
parent
9fa766963a
commit
46e7e6d94f
53
d.ts/antos.d.ts
vendored
53
d.ts/antos.d.ts
vendored
@ -1037,8 +1037,9 @@ declare namespace OS {
|
||||
* @param {string} path VFS path to the scheme file
|
||||
* @param {BaseModel} app the target application
|
||||
* @param {(HTMLElement | string)} parent The parent HTML element where the application is rendered.
|
||||
* @return {Promise<any>} a promise object
|
||||
*/
|
||||
function loadScheme(path: string, app: BaseModel, parent: HTMLElement | string): void;
|
||||
function loadScheme(path: string, app: BaseModel, parent: HTMLElement | string): Promise<any>;
|
||||
/**
|
||||
* Clear the current system theme
|
||||
*
|
||||
@ -2289,6 +2290,19 @@ declare namespace OS {
|
||||
* @returns {*}
|
||||
*/
|
||||
function switcher(...args: string[]): any;
|
||||
/**
|
||||
* A watcher is a Proxy wrapper to an object
|
||||
*
|
||||
* It is used to automatically detect changes in the
|
||||
* target object and notify the change to a callback
|
||||
* handler
|
||||
*
|
||||
* @export
|
||||
* @param {Object} target object
|
||||
* @param {(obj: Object, key: string, value: any, path: any[]) => void} callback function
|
||||
* @returns {Proxy} the wrapper object
|
||||
*/
|
||||
function watcher(target: GenericObject<any>, callback: (obj: Object, key: any, value: any, path: any[]) => void): Object;
|
||||
}
|
||||
}
|
||||
/// <reference types="jquery" />
|
||||
@ -2311,9 +2325,19 @@ declare namespace OS {
|
||||
*/
|
||||
abstract class BaseApplication extends BaseModel {
|
||||
/**
|
||||
* Placeholder of all settings specific to the application.
|
||||
* The settings stored in this object will be saved to system
|
||||
* setting when logout and can be reused in the next login session
|
||||
* Watcher of all settings specific to the application.
|
||||
* The settings stored in this object will be saved to application folder
|
||||
* in JSON format as .settings.json and will be loaded automatically
|
||||
* when application is initialized.
|
||||
*
|
||||
* This object is globally acessible to all processes of the same application
|
||||
*
|
||||
* @type {GenericObject<any>}
|
||||
* @memberof BaseApplication
|
||||
*/
|
||||
static setting_wdg: GenericObject<any>;
|
||||
/**
|
||||
* Reference to per application setting i.e. setting_wdg
|
||||
*
|
||||
* @type {GenericObject<any>}
|
||||
* @memberof BaseApplication
|
||||
@ -2352,16 +2376,16 @@ declare namespace OS {
|
||||
* @returns {void}
|
||||
* @memberof BaseApplication
|
||||
*/
|
||||
init(): void;
|
||||
init(): Promise<any>;
|
||||
/**
|
||||
* Render the application UI by first loading its scheme
|
||||
* and then mount this scheme to the DOM tree
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
* @returns {Promise<any>}
|
||||
* @memberof BaseApplication
|
||||
*/
|
||||
protected loadScheme(): void;
|
||||
protected loadScheme(): Promise<any>;
|
||||
/**
|
||||
* API function to perform an heavy task.
|
||||
* This function will create a Task that is tracked by any
|
||||
@ -2420,17 +2444,6 @@ declare namespace OS {
|
||||
* @memberof BaseApplication
|
||||
*/
|
||||
protected applyAllSetting(): void;
|
||||
/**
|
||||
* Set a setting value to the application setting
|
||||
* registry
|
||||
*
|
||||
* @protected
|
||||
* @param {string} k setting name
|
||||
* @param {*} v setting value
|
||||
* @returns {void}
|
||||
* @memberof BaseApplication
|
||||
*/
|
||||
protected registry(k: string, v: any): void;
|
||||
/**
|
||||
* Show the appliation
|
||||
*
|
||||
@ -2803,10 +2816,10 @@ declare namespace OS {
|
||||
*
|
||||
* @protected
|
||||
* @param {string} p VFS path to the UI scheme definition
|
||||
* @returns {void}
|
||||
* @returns {Promise<any>}
|
||||
* @memberof BaseModel
|
||||
*/
|
||||
protected render(p: string): void;
|
||||
protected render(p: string): Promise<any>;
|
||||
/**
|
||||
* Exit the model
|
||||
*
|
||||
|
@ -35,9 +35,21 @@ namespace OS {
|
||||
*/
|
||||
export abstract class BaseApplication extends BaseModel {
|
||||
/**
|
||||
* Placeholder of all settings specific to the application.
|
||||
* The settings stored in this object will be saved to system
|
||||
* setting when logout and can be reused in the next login session
|
||||
* Watcher of all settings specific to the application.
|
||||
* The settings stored in this object will be saved to application folder
|
||||
* in JSON format as .settings.json and will be loaded automatically
|
||||
* when application is initialized.
|
||||
*
|
||||
* This object is globally acessible to all processes of the same application
|
||||
*
|
||||
* @type {GenericObject<any>}
|
||||
* @memberof BaseApplication
|
||||
*/
|
||||
static setting_wdg: GenericObject<any>;
|
||||
|
||||
|
||||
/**
|
||||
* Reference to per application setting i.e. setting_wdg
|
||||
*
|
||||
* @type {GenericObject<any>}
|
||||
* @memberof BaseApplication
|
||||
@ -69,10 +81,7 @@ namespace OS {
|
||||
*/
|
||||
constructor(name: string, args: AppArgumentsType[]) {
|
||||
super(name, args);
|
||||
if (!setting.applications[this.name]) {
|
||||
setting.applications[this.name] = {};
|
||||
}
|
||||
this.setting = setting.applications[this.name];
|
||||
this.setting = (this.constructor as any).setting_wdg;
|
||||
this.keycomb = {};
|
||||
}
|
||||
|
||||
@ -87,43 +96,52 @@ namespace OS {
|
||||
* @returns {void}
|
||||
* @memberof BaseApplication
|
||||
*/
|
||||
init(): void {
|
||||
this.off("*");
|
||||
this.on("exit", () => this.quit(false));
|
||||
// first register some base event to the app
|
||||
this.on("focus", () => {
|
||||
//if(this.sysdock.selectedApp != this)
|
||||
this.sysdock.selectedApp = this;
|
||||
(this.scheme as GUI.tag.WindowTag).onmenuopen = (el) => el.nodes = this.baseMenu() || [];
|
||||
OS.PM.pidactive = this.pid;
|
||||
this.trigger("focused", undefined);
|
||||
if (this.dialog) {
|
||||
return this.dialog.show();
|
||||
init(): Promise<any> {
|
||||
return new Promise(async (ok, nok) =>{
|
||||
try {
|
||||
this.off("*");
|
||||
this.on("exit", () => this.quit(false));
|
||||
// first register some base event to the app
|
||||
this.on("focus", () => {
|
||||
//if(this.sysdock.selectedApp != this)
|
||||
this.sysdock.selectedApp = this;
|
||||
(this.scheme as GUI.tag.WindowTag).onmenuopen = (el) => el.nodes = this.baseMenu() || [];
|
||||
OS.PM.pidactive = this.pid;
|
||||
this.trigger("focused", undefined);
|
||||
if (this.dialog) {
|
||||
return this.dialog.show();
|
||||
}
|
||||
});
|
||||
this.on("hide", () => {
|
||||
this.sysdock.selectedApp = null;
|
||||
if (this.dialog) {
|
||||
return this.dialog.hide();
|
||||
}
|
||||
});
|
||||
this.on("menuselect", (d) => {
|
||||
switch (d.data.item.data.dataid) {
|
||||
case `${this.name}-about`:
|
||||
return this.openDialog("AboutDialog");
|
||||
case `${this.name}-exit`:
|
||||
return this.trigger("exit", undefined);
|
||||
}
|
||||
});
|
||||
this.on("apptitlechange", () => this.sysdock.update(this));
|
||||
this.subscribe("appregistry", (m) => {
|
||||
if (m.name === this.name) {
|
||||
this.applySetting(m.message as string);
|
||||
}
|
||||
});
|
||||
|
||||
this.updateLocale(this.systemsetting.system.locale);
|
||||
await this.loadScheme();
|
||||
this.applyAllSetting();
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
nok(__e(e));
|
||||
}
|
||||
});
|
||||
this.on("hide", () => {
|
||||
this.sysdock.selectedApp = null;
|
||||
if (this.dialog) {
|
||||
return this.dialog.hide();
|
||||
}
|
||||
});
|
||||
this.on("menuselect", (d) => {
|
||||
switch (d.data.item.data.dataid) {
|
||||
case `${this.name}-about`:
|
||||
return this.openDialog("AboutDialog");
|
||||
case `${this.name}-exit`:
|
||||
return this.trigger("exit", undefined);
|
||||
}
|
||||
});
|
||||
this.on("apptitlechange", () => this.sysdock.update(this));
|
||||
this.subscribe("appregistry", (m) => {
|
||||
if (m.name === this.name) {
|
||||
this.applySetting(m.message as string);
|
||||
}
|
||||
});
|
||||
|
||||
this.updateLocale(this.systemsetting.system.locale);
|
||||
return this.loadScheme();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,10 +149,10 @@ namespace OS {
|
||||
* and then mount this scheme to the DOM tree
|
||||
*
|
||||
* @protected
|
||||
* @returns {void}
|
||||
* @returns {Promise<any>}
|
||||
* @memberof BaseApplication
|
||||
*/
|
||||
protected loadScheme(): void {
|
||||
protected loadScheme(): Promise<any> {
|
||||
//now load the scheme
|
||||
const path = `${this.meta().path}/scheme.html`;
|
||||
return this.render(path);
|
||||
@ -270,21 +288,6 @@ namespace OS {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a setting value to the application setting
|
||||
* registry
|
||||
*
|
||||
* @protected
|
||||
* @param {string} k setting name
|
||||
* @param {*} v setting value
|
||||
* @returns {void}
|
||||
* @memberof BaseApplication
|
||||
*/
|
||||
protected registry(k: string, v: any): void {
|
||||
this.setting[k] = v;
|
||||
return this.publish("appregistry", k);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the appliation
|
||||
*
|
||||
|
@ -288,7 +288,7 @@ namespace OS {
|
||||
return GUI.htmlToScheme(this.markup, this, this.host);
|
||||
} else {
|
||||
// a file handle
|
||||
return this.render(this.markup.path);
|
||||
this.render(this.markup.path);
|
||||
}
|
||||
} else if (
|
||||
this.constructor.scheme
|
||||
|
@ -322,10 +322,10 @@ namespace OS {
|
||||
*
|
||||
* @protected
|
||||
* @param {string} p VFS path to the UI scheme definition
|
||||
* @returns {void}
|
||||
* @returns {Promise<any>}
|
||||
* @memberof BaseModel
|
||||
*/
|
||||
protected render(p: string): void {
|
||||
protected render(p: string): Promise<any> {
|
||||
return GUI.loadScheme(p, this, this.host);
|
||||
}
|
||||
|
||||
|
@ -1234,30 +1234,37 @@ namespace OS {
|
||||
* @returns {Promise<any>} a promise on the result data
|
||||
*/
|
||||
export function post(p: string, d: any): Promise<any> {
|
||||
return API.Task(function (resolve, reject) {
|
||||
return $.ajax({
|
||||
type: "POST",
|
||||
url: p,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(
|
||||
d,
|
||||
function (k, v) {
|
||||
if (k === "domel") {
|
||||
return undefined;
|
||||
}
|
||||
return v;
|
||||
},
|
||||
4
|
||||
),
|
||||
dataType: "json",
|
||||
success: null,
|
||||
})
|
||||
.done(function (data) {
|
||||
return resolve(data);
|
||||
return API.Task(async (resolve, reject) => {
|
||||
try
|
||||
{
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: p,
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(
|
||||
d,
|
||||
function (k, v) {
|
||||
if (k === "domel") {
|
||||
return undefined;
|
||||
}
|
||||
return v;
|
||||
},
|
||||
4
|
||||
),
|
||||
dataType: "json",
|
||||
success: null,
|
||||
})
|
||||
.fail(function (j, s, e) {
|
||||
return reject(API.throwe(s));
|
||||
});
|
||||
.done(function (data) {
|
||||
return resolve(data);
|
||||
})
|
||||
.fail(function (j, s, e) {
|
||||
reject(e);
|
||||
});
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
reject(__e(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1776,5 +1783,38 @@ namespace OS {
|
||||
});
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* A watcher is a Proxy wrapper to an object
|
||||
*
|
||||
* It is used to automatically detect changes in the
|
||||
* target object and notify the change to a callback
|
||||
* handler
|
||||
*
|
||||
* @export
|
||||
* @param {Object} target object
|
||||
* @param {(obj: Object, key: string, value: any, path: any[]) => void} callback function
|
||||
* @returns {Proxy} the wrapper object
|
||||
*/
|
||||
export function watcher(target: GenericObject<any>, callback: (obj: Object, key: any, value: any, path:any[]) => void): Object
|
||||
{
|
||||
const create_handle_for = (path:any[]) => {
|
||||
return {
|
||||
get: (obj: Object, key: any) => {
|
||||
if(typeof obj[key] === "object" && obj[key] !== null) {
|
||||
return new Proxy(obj[key], create_handle_for(path.concat(key)));
|
||||
}
|
||||
return obj[key];
|
||||
},
|
||||
set: (obj: Object, prop:any, value: any) => {
|
||||
obj[prop] = value;
|
||||
callback(obj, prop, value, path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
return new Proxy(target, create_handle_for([]));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -180,23 +180,24 @@ namespace OS {
|
||||
* @param {string} path VFS path to the scheme file
|
||||
* @param {BaseModel} app the target application
|
||||
* @param {(HTMLElement | string)} parent The parent HTML element where the application is rendered.
|
||||
* @return {Promise<any>} a promise object
|
||||
*/
|
||||
export function loadScheme(
|
||||
path: string,
|
||||
app: BaseModel,
|
||||
parent: HTMLElement | string
|
||||
): void {
|
||||
path.asFileHandle()
|
||||
.read()
|
||||
.then(function (x) {
|
||||
if (!x) {
|
||||
return;
|
||||
}
|
||||
): Promise<any> {
|
||||
return new Promise(async (ok,nok) =>{
|
||||
try {
|
||||
const x = await path.asFileHandle().read();
|
||||
htmlToScheme(x, app, parent);
|
||||
})
|
||||
.catch((e) => {
|
||||
announcer.oserror(__("Cannot load scheme: {0}", path), e);
|
||||
});
|
||||
ok(true);
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
nok(__e(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -724,6 +725,45 @@ namespace OS {
|
||||
e);
|
||||
return reject(e);
|
||||
}
|
||||
const mt = application[app].meta;
|
||||
// load application setting if any
|
||||
let settings = {};
|
||||
try
|
||||
{
|
||||
console.log("load setting for", app);
|
||||
if(mt.path.asFileHandle().protocol === "home")
|
||||
{
|
||||
settings = await `${mt.path}/.settings.json`.asFileHandle().read("json");
|
||||
}
|
||||
else
|
||||
{
|
||||
// system package
|
||||
settings = await `home:///.antos/settings/${app}.json`.asFileHandle().read("json");
|
||||
}
|
||||
}
|
||||
catch(_)
|
||||
{
|
||||
}
|
||||
application[app].setting_wdg = API.watcher(settings, (o,k,v,p) => {
|
||||
console.log("Changed detected", o, k,v, p);
|
||||
let key = k;
|
||||
if(p.length > 0)
|
||||
{
|
||||
key = p[0];
|
||||
}
|
||||
const data: API.AnnouncementDataType<any> = {} as API.AnnouncementDataType<any>;
|
||||
data.icon = undefined;
|
||||
if (mt && mt.icon) {
|
||||
data.icon = `${mt.path}/${mt.icon}`;
|
||||
}
|
||||
data.id = 0;
|
||||
data.name = app;
|
||||
data.message = key;
|
||||
data.iconclass = mt?mt.iconclass:undefined;
|
||||
data.u_data = undefined;
|
||||
console.log(data);
|
||||
return announcer.trigger("appregistry", data);
|
||||
});
|
||||
const p = await PM.createProcess(
|
||||
app,
|
||||
application[app],
|
||||
|
@ -136,6 +136,25 @@ namespace OS {
|
||||
if (i >= 0) {
|
||||
if (application[app.name].type === ModelType.Application) {
|
||||
GUI.undock(app as application.BaseApplication);
|
||||
// save setting file if any
|
||||
if(PM.processes[app.name].length == 1)
|
||||
{
|
||||
const app_class = application[app.name] as typeof OS.application.BaseApplication;
|
||||
let file = `${app_class.meta.path}/.settings.json`.asFileHandle();
|
||||
if(file.protocol !== "home")
|
||||
{
|
||||
file = `home:///.antos/settings/${app.name}.json`.asFileHandle();
|
||||
}
|
||||
file.cache = app_class.setting_wdg;
|
||||
//file.cache = JSON.stringify(app_class.setting_wdg, undefined, 4);
|
||||
console.log("save setting file");
|
||||
file
|
||||
.write("object")
|
||||
.catch((e) =>{
|
||||
return announcer.osinfo(
|
||||
__("Unable to save settings for application {0}: {1}", app.name, e.toString()));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
GUI.detachservice(app as application.BaseService);
|
||||
}
|
||||
|
@ -398,7 +398,7 @@ namespace OS {
|
||||
*/
|
||||
export function resetSetting(): void {
|
||||
setting.desktop = {
|
||||
path: "home://.desktop",
|
||||
path: "home://.antos/desktop",
|
||||
menu: [],
|
||||
showhidden: false,
|
||||
};
|
||||
@ -476,13 +476,13 @@ namespace OS {
|
||||
menu: [],
|
||||
packages: {},
|
||||
pkgpaths: {
|
||||
user: "home://.packages",
|
||||
user: "home://.antos/packages",
|
||||
system: "os://packages",
|
||||
},
|
||||
repositories: [],
|
||||
startup: {
|
||||
apps: [],
|
||||
services: ["Syslog/PushNotification", "Syslog/Calendar"],
|
||||
services: ["SystemServices/PushNotification", "SystemServices/Calendar"],
|
||||
pinned: [],
|
||||
},
|
||||
};
|
||||
|
@ -564,13 +564,13 @@ namespace OS {
|
||||
switch (data.dataid) {
|
||||
case `${this.name}-hidden`:
|
||||
//@.view.set "showhidden", e.item.data.checked
|
||||
return this.registry("showhidden", data.checked);
|
||||
this.setting.showhidden = data.checked;
|
||||
//@.setting.showhidden = e.item.data.checked
|
||||
case `${this.name}-refresh`:
|
||||
this.view.path = this.currdir.path;
|
||||
return;
|
||||
case `${this.name}-nav`:
|
||||
return this.registry("nav", data.checked);
|
||||
this.setting.nav = data.checked;
|
||||
}
|
||||
}
|
||||
//@setting.nav = e.item.data.checked
|
||||
|
Loading…
Reference in New Issue
Block a user