Skip to content

Commit

Permalink
refactor: skeleton localization
Browse files Browse the repository at this point in the history
  • Loading branch information
3Shain committed Mar 8, 2023
1 parent 4a55a50 commit ddc6ce6
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 40 deletions.
28 changes: 20 additions & 8 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import "./app.css";
import { CURRENT_YAAGL_VERSION } from "./constants";
import { createUpdater, downloadProgram } from "./updater";
import { createCommonUpdateUI } from "./common-update-ui";
import { createLocale } from "./locale";
import zh_CN from "./locale/zh_CN";

export async function createApp() {

await setKey("singleton", null);

let aria2_port = 6868;
Expand All @@ -35,11 +36,12 @@ export async function createApp() {
}
});

const locale = createLocale(zh_CN);
const github = await createGithubEndpoint();
const aria2_session = await resolve("./aria2.session");
await appendFile(aria2_session, "");
const pid = (await exec("echo", ["$PPID"])).stdOut.split("\n")[0];
await spawn("./sidecar/aria2/aria2c", [
const apid = await spawn("./sidecar/aria2/aria2c", [
// "-q",
"-d",
"/",
Expand All @@ -58,24 +60,33 @@ export async function createApp() {
"--stop-with-process",
pid,
]);
addTerminationHook(async () => {
// double insurance (esp. for self restart)
await log("killing process " + apid);
await exec("kill", [apid + ""]);
return true;
});
const aria2 = await Promise.race([
createAria2({ host: "127.0.0.1", port: aria2_port }),
timeout(10000),
]).catch(() => Promise.reject(new Error("Fail to launch aria2.")));
await log(`Launched aria2 version ${aria2.version.version}`);

const { latest, downloadUrl } = await createUpdater({
const { latest, downloadUrl, description, version } = await createUpdater({
github,
aria2,
});
if (!latest) {
if (
await prompt(
"NEW Version available",
"Would you like to update to the latest version?"
await locale.prompt(
"NEW_VERSION_AVALIABLE",
"NEW_VERSION_AVALIABLE_DESC",
[version, description]
)
) {
return createCommonUpdateUI(() => downloadProgram(aria2, downloadUrl));
return createCommonUpdateUI(locale, () =>
downloadProgram(aria2, downloadUrl)
);
}
}

Expand All @@ -86,13 +97,14 @@ export async function createApp() {
installDir: await resolve("./wine"), // CHECK: hardcoded path?
prefix: prefixPath,
});
return await createLauncher({ aria2, wine });
return await createLauncher({ aria2, wine, locale });
} else {
return await createWineInstallProgram({
aria2,
wineUpdateTarGzFile: wineUpdate,
wineAbsPrefix: prefixPath,
wineTag: wineUpdateTag,
locale,
});
}
}
14 changes: 10 additions & 4 deletions src/common-update-ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import { Box, VStack, Image } from "@hope-ui/solid";
import { createSignal, onMount, Show } from "solid-js";
import { fatal, log, shutdown, wait } from "./utils";
import s from "./assets/Nahida.cr.png";
import { Locale, LocaleTextKey } from "./locale";

export function createCommonUpdateUI(program: () => CommonUpdateProgram) {
export function createCommonUpdateUI(
locale: Locale,
program: () => CommonUpdateProgram
) {
let confirmRestart: (v: any) => void;
const confirmRestartPromise = new Promise((res) => {
confirmRestart = res;
Expand All @@ -25,7 +29,7 @@ export function createCommonUpdateUI(program: () => CommonUpdateProgram) {
setProgress(0);
break;
case "setStateText":
setStatusText(text[1]); //FIXME: locales
setStatusText(locale.format(text[1], text.slice(2)));
break;
}
}
Expand Down Expand Up @@ -59,7 +63,9 @@ export function createCommonUpdateUI(program: () => CommonUpdateProgram) {
<Show
when={!done()}
fallback={
<Center><Button onClick={confirmRestart!}>重启以完成安装</Button></Center>
<Center>
<Button onClick={confirmRestart!}>{locale.get("RESTART_TO_INSTALL")}</Button>
</Center>
}
>
<Progress value={progress()} indeterminate={progress() == 0}>
Expand All @@ -77,5 +83,5 @@ export type CommonUpdateProgram = AsyncGenerator<CommonProgressUICommand, void>;

export type CommonProgressUICommand =
| ["setProgress", number]
| ["setStateText", string, ...string[]]
| ["setStateText", LocaleTextKey, ...string[]]
| ["setUndeterminedProgress"];
72 changes: 59 additions & 13 deletions src/launcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
fatal,
setKey,
removeFile,
wait,
} from "./utils";
import {
Box,
Expand All @@ -43,6 +44,7 @@ import {
} from "./patch";
import { md5 } from "./utils/unix";
import { CommonUpdateProgram } from "./common-update-ui";
import { Locale } from "./locale";

const IconSetting = createIcon({
viewBox: "0 0 1024 1024",
Expand All @@ -59,35 +61,56 @@ const IconSetting = createIcon({

const CURRENT_SUPPORTED_VERSION = "3.5.0";

export async function checkGameState() {
export async function checkGameState(locale: Locale) {
let gameDir = "";
try {
gameDir = await getKey("game_install_dir");
} catch {
return {
gameInstalled: false,
} as const;
}
try {
const gameDir = await getKey("game_install_dir");
return {
gameInstalled: true,
gameInstallDir: gameDir,
gameVersion: await getGameVersion(join(gameDir, CN_SERVER.dataDir)), //FIXME:
} as const;
} catch {
await locale.alert("CANT_OPEN_GAME_FILE", "CANT_OPEN_GAME_FILE_DESC");
const selection = await openDir(locale.get("SELECT_INSTALLATION_DIR"));
if (selection != gameDir) {
await locale.alert("GAME_DIR_CHANGED", "GAME_DIR_CHANGED_DESC");
return {
gameInstalled: false,
} as const;
}
return {
gameInstalled: false,
gameInstalled: true,
gameInstallDir: gameDir,
gameVersion: await getGameVersion(join(gameDir, CN_SERVER.dataDir)), //FIXME:
} as const;
}
}

export async function createLauncher({
aria2,
wine,
locale,
}: {
aria2: Aria2;
wine: Wine;
locale: Locale;
}) {
const server = CN_SERVER;
const b: ServerContentData = await (await fetch(server.bg_url)).json();
const c: ServerVersionData = await (await fetch(server.url)).json();
const GAME_LATEST_VERSION = c.data.game.latest.version;
await waitImageReady(b.data.adv.background);

const { gameInstalled, gameInstallDir, gameVersion } = await checkGameState();
const { gameInstalled, gameInstallDir, gameVersion } = await checkGameState(
locale
);

return function Laucnher() {
// const bh = 40 / window.devicePixelRatio;
Expand Down Expand Up @@ -121,7 +144,7 @@ export async function createLauncher({
setProgress(0);
break;
case "setStateText":
setStatusText(text[1]); //FIXME: locales
setStatusText(locale.format(text[1], text.slice(2)));
break;
}
}
Expand Down Expand Up @@ -149,25 +172,29 @@ export async function createLauncher({
});
});
} else {
const selection = await openDir("SELECT_INSTALLATION_DIR");
const selection = await openDir(locale.get("SELECT_INSTALLATION_DIR"));
if (!selection.startsWith("/")) {
await alert("PATH_INVALID", "PLEASE_SELECT_A_DIR");
await locale.alert("PATH_INVALID", "PLEASE_SELECT_A_DIR");
return;
}
try {
await stats(join(selection, "pkg_version"));
} catch {
await alert("NOT_SUPPORTED_YET", "DOWNLOAD_FUNCTION_TBD");
await locale.alert("NOT_SUPPORTED_YET", "DOWNLOAD_FUNCTION_TBD");
return;
}
const gameVersion = await getGameVersion(
join(selection, server.dataDir)
);
if (gt(gameVersion, CURRENT_SUPPORTED_VERSION)) {
await alert("UNSUPPORTED_VERSION", "PLEASE_WAIT_FOR_LAUNCHER_UPDATE");
await locale.alert(
"UNSUPPORTED_VERSION",
"PLEASE_WAIT_FOR_LAUNCHER_UPDATE",
[gameVersion]
);
return;
} else if (lt(gameVersion, GAME_LATEST_VERSION)) {
await alert("NOT_SUPPORTED_YET", "UPGRADE_FUNCTION_TBD");
await locale.alert("NOT_SUPPORTED_YET", "UPGRADE_FUNCTION_TBD");
return;
}
try {
Expand Down Expand Up @@ -241,7 +268,9 @@ export async function createLauncher({
disabled={programBusy()}
onClick={() => onButtonClick().catch(fatal)}
>
{_gameInstalled() ? "LAUNCH" : "INSTALL"}
{_gameInstalled()
? locale.get("LAUNCH")
: locale.get("INSTALL")}
</Button>
<Show when={false && _gameInstalled()}>
<IconButton
Expand Down Expand Up @@ -332,8 +361,13 @@ async function* checkIntegrityProgram({
remoteName: string;
md5: string;
}[] = [];
yield ["setStateText", "SCANNING_FILES"];
let count = 0;
yield [
"setStateText",
"SCANNING_FILES",
String(count),
String(entries.length),
];
for (const entry of entries) {
const localPath = join(gameDir, entry.remoteName);
try {
Expand All @@ -350,14 +384,20 @@ async function* checkIntegrityProgram({
toFix.push(entry);
}
count++;
yield [
"setStateText",
"SCANNING_FILES",
String(count),
String(entries.length),
];
yield ["setProgress", (count / entries.length) * 100];
}
if (toFix.length == 0) {
return;
}
yield ["setUndeterminedProgress"];

yield ["setStateText", "FIXING_FILES"];
yield ["setStateText", "FIXING_FILES", String(count), String(entries.length)];
count = 0;
for (const entry of toFix) {
const localPath = join(gameDir, entry.remoteName);
Expand All @@ -368,6 +408,12 @@ async function* checkIntegrityProgram({
uri: remotePath,
absDst: localPath,
})) {
yield [
"setStateText",
"FIXING_FILES",
String(count),
String(entries.length),
];
yield [
"setProgress",
Number((progress.completedLength * BigInt(100)) / progress.totalLength),
Expand Down
42 changes: 37 additions & 5 deletions src/locale/en.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
export default {
LAUNCH: "Launch Game",
UPDATING: "Updating",
DOWNLOADING: "Downloading"
}
import zh_CN from "./zh_CN";

const en: typeof zh_CN = {
LAUNCH: "Launch Game",
INSTALL: "Install Game",
UPDATING: "Updating",
DOWNLOADING: "Downloading",
FIXING_FILES: "Fixing game files {0}/{1}",
PATCHING: "Patching game files",
GAME_RUNNING: "Game is running (DO NOT CLOSE THE LAUNCHER)",
REVERT_PATCHING: "Reverting patches",
SCANNING_FILES: "Checking game integrity. Completed files {0}/{1}",
DOWNLOADING_ENVIRONMENT: "Downloading enviroment files",
DOWNLOADING_ENVIRONMENT_SPEED: "Downloading environment files ({0}/s)",
EXTRACT_ENVIRONMENT: "",
CONFIGURING_ENVIRONMENT: "",
RESTART_TO_INSTALL: "Restart Now",
PATH_INVALID: "",
PLEASE_SELECT_A_DIR: "",
NOT_SUPPORTED_YET: "",
PLEASE_WAIT_FOR_LAUNCHER_UPDATE: "",
UNSUPPORTED_VERSION: "",
SELECT_INSTALLATION_DIR: "",
CANT_OPEN_GAME_FILE: "",
CANT_OPEN_GAME_FILE_DESC: "",
GAME_DIR_CHANGED: "",
GAME_DIR_CHANGED_DESC: "",

NEW_VERSION_AVALIABLE: "",
NEW_VERSION_AVALIABLE_DESC: "",

DOWNLOADING_UPDATE_FILE: "",

DOWNLOAD_FUNCTION_TBD: "",
UPGRADE_FUNCTION_TBD: "",
};
export default en;
33 changes: 30 additions & 3 deletions src/locale/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import zh_CN from "./zh_CN";
import en from "./en";
import { alert as ualert, prompt as uprompt ,formatString } from "../utils";

// https://stackoverflow.com/questions/67027081/how-to-assert-two-interfaces-contain-the-same-keys-in-typescript
type AssertKeysEqual<
Expand All @@ -8,11 +9,37 @@ type AssertKeysEqual<
> = never
type Assertion = AssertKeysEqual<typeof zh_CN,typeof en>

export type LocaleTextKey = keyof typeof zh_CN;

export const locale = {
zh_CN,
en
}

export function lstr(templates: string[], ...args: string[]) {

}
export function createLocale(locale: typeof zh_CN) {

function alert(title: LocaleTextKey, content: LocaleTextKey, intrp: string[] = []) {
return ualert(locale[title], formatString(locale[content], intrp));
}

function prompt(title: LocaleTextKey, content: LocaleTextKey, intrp: string[] = []) {
return uprompt(locale[title], formatString(locale[content], intrp));
}

function format(key: LocaleTextKey, intrp: string[]) {
return formatString(locale[key], intrp);
}

function get(key: LocaleTextKey) {
return locale[key];
}

return {
alert,
prompt,
format,
get
};
}

export type Locale = ReturnType<typeof createLocale>;
Loading

0 comments on commit ddc6ce6

Please sign in to comment.