mirror of
https://github.com/an-anime-team/an-anime-game-launcher.git
synced 2025-03-20 07:00:00 +03:00
Several changes
- added `vue-i18n` - made `Locales` class to get launcher locales - added checking that settings window is not already open in `Launcher.showSettings()` - improved `promisify()`; now it can work with `Promise` objects - improved `Process` class; added `Process.kill()` and `Process.running()` methods - improved `Window` class, fixed its output
This commit is contained in:
parent
1a99aae933
commit
0684774ea8
15 changed files with 246 additions and 28 deletions
|
@ -10,6 +10,7 @@
|
|||
"dependencies": {
|
||||
"js-md5": "^0.7.3",
|
||||
"vue": "^3.2.25",
|
||||
"vue-i18n": "^9.2.0-beta.25",
|
||||
"yaml": "^1.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
2
public/locales/en.yaml
Normal file
2
public/locales/en.yaml
Normal file
|
@ -0,0 +1,2 @@
|
|||
settings:
|
||||
test: Hello, World!
|
2
public/locales/ru.yaml
Normal file
2
public/locales/ru.yaml
Normal file
|
@ -0,0 +1,2 @@
|
|||
settings:
|
||||
test: Привет, Мир!
|
|
@ -9,7 +9,15 @@
|
|||
|
||||
<body>
|
||||
<div id="app">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1>{{ $t('settings.test') }}</h1>
|
||||
|
||||
<div class="locale-changer">
|
||||
<select v-model="$i18n.locale">
|
||||
<option v-for="locale in $i18n.availableLocales" :key="`locale-${locale}`" :value="locale">{{ locale }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<l-checkbox property="test"></l-checkbox>
|
||||
</div>
|
||||
|
||||
<script src="neutralino.js"></script>
|
||||
|
|
12
src/components/Checkbox.vue
Normal file
12
src/components/Checkbox.vue
Normal file
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<h1>{{ property }}</h1>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: ['property'],
|
||||
|
||||
});
|
||||
</script>
|
|
@ -39,7 +39,7 @@ promisify(async () => {
|
|||
});
|
||||
});
|
||||
|
||||
let app = createApp({
|
||||
const app = createApp({
|
||||
data: () => ({
|
||||
uri: {
|
||||
social: '',
|
||||
|
@ -81,7 +81,8 @@ let app = createApp({
|
|||
() => launcher.updateSocial(),
|
||||
() => launcher.updateBackground()
|
||||
],
|
||||
callAtOnce: true
|
||||
callAtOnce: true,
|
||||
interval: 500
|
||||
});
|
||||
|
||||
// Show window when all the stuff was completed
|
||||
|
|
|
@ -1,11 +1,31 @@
|
|||
import { createApp } from 'vue/dist/vue.esm-bundler';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
|
||||
import Window from '../ts/neutralino/Window';
|
||||
|
||||
createApp({
|
||||
import Checkbox from '../components/Checkbox.vue';
|
||||
import Locales from '../ts/core/Locales';
|
||||
|
||||
const app = createApp({
|
||||
data: () => ({
|
||||
title: 'about'
|
||||
}),
|
||||
|
||||
components: {
|
||||
'l-checkbox': Checkbox
|
||||
},
|
||||
|
||||
mounted: () => Window.current.show()
|
||||
}).mount('#app');
|
||||
});
|
||||
|
||||
Locales.get().then((locales) => {
|
||||
app.use(createI18n({
|
||||
locale: 'en',
|
||||
fallbackLocale: 'en',
|
||||
|
||||
// @ts-expect-error
|
||||
messages: locales
|
||||
}));
|
||||
|
||||
app.mount('#app');
|
||||
});
|
||||
|
|
|
@ -50,6 +50,13 @@ class Paths
|
|||
*/
|
||||
public static readonly shadersDir: string = `${this.appDir}/public/shaders`;
|
||||
|
||||
/**
|
||||
* Locales directory
|
||||
*
|
||||
* @default "[constants.paths.app]/public/locales"
|
||||
*/
|
||||
public static readonly localesDir: string = `${this.appDir}/public/locales`;
|
||||
|
||||
/**
|
||||
* Launcher data directory
|
||||
*
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Window from './neutralino/Window';
|
||||
import Process from './neutralino/Process';
|
||||
|
||||
import constants from './Constants';
|
||||
import Configs from './Configs';
|
||||
|
@ -7,6 +8,8 @@ import Background from './launcher/Background';
|
|||
import ProgressBar from './launcher/ProgressBar';
|
||||
import State from './launcher/State';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
export default class Launcher
|
||||
{
|
||||
public app;
|
||||
|
@ -14,6 +17,8 @@ export default class Launcher
|
|||
public state: State;
|
||||
public progressBar: ProgressBar;
|
||||
|
||||
protected settingsMenu?: Process;
|
||||
|
||||
public constructor(app)
|
||||
{
|
||||
this.app = app;
|
||||
|
@ -46,12 +51,36 @@ export default class Launcher
|
|||
t(0);
|
||||
}
|
||||
|
||||
public showSettings()
|
||||
public showSettings(): Promise<boolean>
|
||||
{
|
||||
Window.open('settings', {
|
||||
title: 'Settings',
|
||||
width: 900,
|
||||
height: 600
|
||||
return new Promise(async (resolve) => {
|
||||
if (this.settingsMenu && await this.settingsMenu.running())
|
||||
resolve(false);
|
||||
|
||||
else
|
||||
{
|
||||
this.settingsMenu = undefined;
|
||||
|
||||
const window = await Window.open('settings', {
|
||||
title: 'Settings',
|
||||
width: 900,
|
||||
height: 600,
|
||||
enableInspector: true
|
||||
});
|
||||
|
||||
if (window.status)
|
||||
{
|
||||
this.settingsMenu = new Process(window.data!.pid, 500);
|
||||
|
||||
/*this.settingsMenu.finish(() => {
|
||||
Window.current.show();
|
||||
})
|
||||
|
||||
Window.current.hide();*/
|
||||
}
|
||||
|
||||
resolve(window.status);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
57
src/ts/core/Locales.ts
Normal file
57
src/ts/core/Locales.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import YAML from 'yaml';
|
||||
|
||||
import constants from '../Constants';
|
||||
import promisify from './promisify';
|
||||
|
||||
type AvailableLocales =
|
||||
| 'en'
|
||||
| 'ru';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
export default class Locales
|
||||
{
|
||||
/**
|
||||
* Get locales
|
||||
*
|
||||
* @param locale - locale name to get. If null - then will be returned array of all available locales
|
||||
*/
|
||||
public static get(locale: AvailableLocales|null = null): Promise<object>
|
||||
{
|
||||
return new Promise((resolve) => {
|
||||
if (locale === null)
|
||||
{
|
||||
Neutralino.filesystem.readDirectory(constants.paths.localesDir)
|
||||
.then(async (folders: { entry: string, type: string }[]) => {
|
||||
folders = folders.filter((folder) => folder.type === 'FILE');
|
||||
|
||||
const pipeline = promisify({
|
||||
callbacks: folders.map((folder) => {
|
||||
return new Promise((resolve) => {
|
||||
Neutralino.filesystem.readFile(`${constants.paths.localesDir}/${folder.entry}`)
|
||||
.then((locale) => resolve(YAML.parse(locale)));
|
||||
});
|
||||
}),
|
||||
callAtOnce: true
|
||||
});
|
||||
|
||||
pipeline.then((locales) => {
|
||||
let result = {};
|
||||
|
||||
for (let i = 0; i < folders.length; i++)
|
||||
{
|
||||
const lang = folders[i].entry.substring(0, folders[i].entry.length - 5);
|
||||
|
||||
result[lang] = locales[i];
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
else Neutralino.filesystem.readFile(`${constants.paths.localesDir}/${locale}.yaml`)
|
||||
.then((locale) => resolve(YAML.parse(locale)));
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
type callback = () => any;
|
||||
|
||||
type PromiseOptions = {
|
||||
callbacks: callback[];
|
||||
callbacks: callback[]|Promise<any>[];
|
||||
|
||||
/**
|
||||
* If true, then all the callbacks will be called
|
||||
|
@ -24,21 +24,30 @@ type PromiseOptions = {
|
|||
/**
|
||||
* Make a promise from a function(s) and run it
|
||||
*/
|
||||
export default function promisify(callback: callback|PromiseOptions): Promise<any>
|
||||
export default function promisify(callback: callback|Promise<any>|PromiseOptions): Promise<any>
|
||||
{
|
||||
return new Promise(async (resolve) => {
|
||||
// promisify(() => { ... })
|
||||
if (typeof callback === 'function')
|
||||
resolve(await Promise.resolve(callback()));
|
||||
|
||||
// promisify(new Promise(...))
|
||||
else if (typeof callback['then'] === 'function')
|
||||
resolve(await callback);
|
||||
|
||||
// promisify({ callbacks: [ ... ] })
|
||||
else
|
||||
{
|
||||
let outputs = {};
|
||||
|
||||
// @ts-expect-error
|
||||
if (callback.callAtOnce)
|
||||
{
|
||||
// @ts-expect-error
|
||||
let remained = callback.callbacks.length;
|
||||
|
||||
for (let i = 0; i < callback.callbacks.length; ++i)
|
||||
// @ts-expect-error
|
||||
for (let i = 0; i < callback.callbacks.length; ++i) // @ts-expect-error
|
||||
promisify(callback.callbacks[i]).then((output) => {
|
||||
outputs[i] = output;
|
||||
|
||||
|
@ -46,18 +55,20 @@ export default function promisify(callback: callback|PromiseOptions): Promise<an
|
|||
});
|
||||
|
||||
const updater = () => {
|
||||
if (remained > 0)
|
||||
if (remained > 0) // @ts-expect-error
|
||||
setTimeout(updater, callback.interval ?? 100);
|
||||
|
||||
else resolve(outputs);
|
||||
};
|
||||
|
||||
// @ts-expect-error
|
||||
setTimeout(updater, callback.interval ?? 100);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
for (let i = 0; i < callback.callbacks.length; ++i)
|
||||
// @ts-expect-error
|
||||
for (let i = 0; i < callback.callbacks.length; ++i) // @ts-expect-error
|
||||
outputs[i] = await promisify(callback.callbacks[i]);
|
||||
|
||||
resolve(outputs);
|
||||
|
|
|
@ -8,12 +8,19 @@ type ProcessOptions = {
|
|||
|
||||
class Process
|
||||
{
|
||||
/**
|
||||
* Process ID
|
||||
*/
|
||||
public readonly id: number;
|
||||
|
||||
/**
|
||||
* Interval between process status update
|
||||
* Interval in ms between process status update
|
||||
*
|
||||
* null if you don't want to update process status
|
||||
*
|
||||
* @default 200
|
||||
*/
|
||||
public interval: number = 200;
|
||||
public interval: number|null;
|
||||
|
||||
protected _finished: boolean = false;
|
||||
|
||||
|
@ -27,15 +34,19 @@ class Process
|
|||
|
||||
protected onFinish?: (process: Process) => void;
|
||||
|
||||
public constructor(pid: number)
|
||||
public constructor(pid: number, interval: number|null = 200)
|
||||
{
|
||||
this.id = pid;
|
||||
this.interval = interval;
|
||||
|
||||
const updateStatus = async () => {
|
||||
Neutralino.os.execCommand(`ps -p ${this.id}`).then((output) => {
|
||||
const updateStatus = () => {
|
||||
this.running().then((running) => {
|
||||
// The process is still running
|
||||
if (output.stdOut.includes(this.id))
|
||||
setTimeout(updateStatus, this.interval);
|
||||
if (running)
|
||||
{
|
||||
if (this.interval)
|
||||
setTimeout(updateStatus, this.interval);
|
||||
}
|
||||
|
||||
// Otherwise the process was stopped
|
||||
else
|
||||
|
@ -48,7 +59,8 @@ class Process
|
|||
});
|
||||
};
|
||||
|
||||
setTimeout(updateStatus, this.interval);
|
||||
if (this.interval)
|
||||
setTimeout(updateStatus, this.interval);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,6 +72,44 @@ class Process
|
|||
|
||||
if (this._finished)
|
||||
callback(this);
|
||||
|
||||
// If user stopped process status auto-checking
|
||||
// then we should check it manually when this method was called
|
||||
else if (this.interval === null)
|
||||
{
|
||||
this.running().then((running) => {
|
||||
if (!running)
|
||||
{
|
||||
this._finished = true;
|
||||
|
||||
callback(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill process
|
||||
*/
|
||||
public kill(forced: boolean = false): Promise<void>
|
||||
{
|
||||
return new Promise((resolve) => {
|
||||
Neutralino.os.execCommand(`kill ${forced ? '-9' : '-15'} ${this.id}`).then(() => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the process is running
|
||||
*
|
||||
* This method doesn't call onFinish event
|
||||
*/
|
||||
public running(): Promise<boolean>
|
||||
{
|
||||
return new Promise((resolve) => {
|
||||
Neutralino.os.execCommand(`ps -p ${this.id}`).then((output) => {
|
||||
resolve(output.stdOut.includes(this.id));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,16 @@ type WindowOptions = WindowSize & {
|
|||
processArgs?: string;
|
||||
};
|
||||
|
||||
type WindowOpenResult = {
|
||||
status: boolean;
|
||||
data?: {
|
||||
pid: number;
|
||||
stdOut: string;
|
||||
stdErr: string;
|
||||
exitCode: number;
|
||||
};
|
||||
};
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
class Window
|
||||
|
@ -31,10 +41,10 @@ class Window
|
|||
return Neutralino.window;
|
||||
}
|
||||
|
||||
public static async open(name: string, options: WindowOptions = {}): Promise<boolean>
|
||||
public static open(name: string, options: WindowOptions = {}): Promise<WindowOpenResult>
|
||||
{
|
||||
return new Promise(async (resolve) => {
|
||||
const status = Neutralino.window.create(`/${name}.html`, {
|
||||
const status = await Neutralino.window.create(`/${name}.html`, {
|
||||
width: 600,
|
||||
height: 400,
|
||||
enableInspector: false,
|
||||
|
@ -48,14 +58,18 @@ class Window
|
|||
hidden: true
|
||||
});
|
||||
|
||||
resolve(status !== undefined);
|
||||
resolve({
|
||||
status: status !== undefined,
|
||||
data: status
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export type {
|
||||
WindowSize,
|
||||
WindowOptions
|
||||
WindowOptions,
|
||||
WindowOpenResult
|
||||
};
|
||||
|
||||
export default Window;
|
||||
|
|
4
src/ts/types/vue-sfc.d.ts
vendored
Normal file
4
src/ts/types/vue-sfc.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
declare module '*.vue'
|
||||
{
|
||||
export * from 'vue/dist/vue.esm-bundler';
|
||||
};
|
|
@ -47,7 +47,7 @@
|
|||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
"removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
|
||||
|
|
Loading…
Add table
Reference in a new issue