1
0
mirror of https://github.com/rhysd/Mstdn.git synced 2025-01-21 20:52:11 +01:00

load preload plugins after app gets idling

This commit is contained in:
rhysd 2017-04-24 18:31:55 +09:00
parent da55de25d1
commit f8e7d64b08
4 changed files with 80 additions and 22 deletions

View File

@ -264,14 +264,14 @@ body {
} }
``` ```
## Preload Plugin ## Preload Plugin (experimental)
You can make a Node.js package which is preloaded before loading inner mastodon page. You can make a Node.js package which is preloaded before loading inner mastodon page.
Preload plugin is enabled if `chromium_sandbox` option is set to `false`. Please read above Preload plugin is enabled if `chromium_sandbox` option is set to `false`. Please read above
configuration section before using any plugin. configuration section before using any plugin.
### How to make ### How to make a plugin
Create `node_modules` directory in your application directory at first. And then, please make Create `node_modules` directory in your application directory at first. And then, please make
`mstdn-preload-hello` directory. It consists a node package. `mstdn-preload-hello` directory. It consists a node package.

View File

@ -1,12 +1,11 @@
import {Config, Account} from '../main/config'; import {Config, Account} from '../main/config';
import * as Ipc from './ipc'; import * as Ipc from './ipc';
import setupKeymaps from './key_handler'; import setupKeymaps from './key_handler';
import loadPlugins from './plugins'; import PluginsLoader from './plugins';
import log from './log';
Ipc.on('mstdn:config', (c: Config, a: Account) => { Ipc.on('mstdn:config', (c: Config, a: Account) => {
const plugins = loadPlugins(c, a); const loader = new PluginsLoader(c, a);
log.info('Loaded plugins:', plugins); loader.loadAfterAppPrepared();
setupKeymaps(c, a); setupKeymaps(c, a);
document.title = `${document.title} @${a.name}@${a.host}`; document.title = `${document.title} @${a.name}@${a.host}`;
}); });

View File

@ -8,22 +8,72 @@ interface Plugins {
[module_path: string]: Plugin; [module_path: string]: Plugin;
} }
export default function loadPlugins(config: Config, account: Account): Plugins { export default class PluginsLoader {
const ret = {} as Plugins; preloads: Plugins;
loaded: boolean;
constructor(private config: Config, private account: Account) {
this.loaded = false;
this.preloads = {};
if (config.chromium_sandbox) { if (config.chromium_sandbox) {
log.info('Chromium sandbox is enabled. Preload plugin is disabled.'); log.info('Chromium sandbox is enabled. Preload plugin is disabled.');
return ret; return;
} }
const dir_base = path.join(config.__DATA_DIR!, 'node_modules'); const dir_base = path.join(config.__DATA_DIR!, 'node_modules');
for (const plugin of config.preload || []) { for (const plugin of config.preload || []) {
const plugin_path = path.join(dir_base, `mstdn-preload-${plugin}`); const plugin_path = path.join(dir_base, `mstdn-preload-${plugin}`);
try { try {
const preloadFunc = r(plugin_path) as Plugin; this.preloads[plugin_path] = r(plugin_path) as Plugin;
preloadFunc(config, account);
ret[plugin_path] = preloadFunc;
} catch (e) { } catch (e) {
log.error(`Failed to load plugin ${plugin_path}:`, e); log.error(`Failed to load plugin ${plugin_path}:`, e);
} }
} }
return ret; }
loadAfterAppPrepared() {
if (Object.keys(this.preloads).length === 0) {
log.info('No preload plugin found. Skip loading');
this.loaded = true;
return;
}
return new Promise<void>(resolve => {
// In order not to prevent application's initial loading, load preload plugins
// on an idle callback.
window.requestIdleCallback(() => {
log.debug('Start loading preload plugins', this.config, this.account);
if (this.tryLoading()) {
return resolve();
}
this.observeAppPrepared(this.tryLoading).then(resolve);
});
});
}
observeAppPrepared(callback: () => void) {
// TODO:
// Make an instance of MutationObserver to observe React root.
return Promise.resolve(callback());
}
tryLoading = () => {
if (document.querySelector('[data-react-class="Mastodon"]') === null) {
log.info('Root element of react app was not found. App seems not to be loaded yet.');
return false;
}
for (const key in this.preloads) {
const f = this.preloads[key];
try {
f(this.config, this.account);
} catch (e) {
log.error(`Error while loading preload plugin '${key}':`, e);
}
}
log.info('Preload plugins were loaded:', this.preloads);
return true;
}
} }

9
typings/request-idle-callback.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
interface RequestIdleCallback {
didTimeout?: boolean;
timeRemaining?: () => number;
}
interface Window {
requestIdleCallback(cb: (deadline: RequestIdleCallback) => any): NodeJS.Timer;
cancelIdleCallback(id: NodeJS.Timer): void;
}