mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 17:51:38 +00:00
feat(mobile): session Dashboard on iOS and Android via authenticated Control UI webview (#112163)
* feat(mobile): session dashboard screens on iOS and Android via authenticated Control UI webview * fix(android): keep configured Control UI base path in session dashboard URL * docs(android): note system-trust boundary of the shared Control UI webview * fix(android): origin-only document-start rule for Control UI auth script * chore(i18n): refresh native inventory on rebased head * fix(ios): swiftlint closure form in session dashboard toolbar * fix(i18n): tolerate workflow-owned pending native rows in PR alignment checks * fix(android): KTX toUri per lint and refresh native inventory * fix(android): ktlint import order incl. main-inherited fleet test, refresh inventory
This commit is contained in:
committed by
GitHub
parent
0cf4b24ad6
commit
262deec72c
@@ -1231,14 +1231,55 @@ function formatProblems(problems: Array<readonly [string, string[]]>): string {
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export async function syncAndroidAppI18n(options: { check?: boolean } = {}) {
|
||||
/**
|
||||
* Managed rows (native_*) are owned end-to-end by the post-merge locale
|
||||
* refresh workflow, so a source PR may legitimately be missing (or still
|
||||
* carrying) managed rows the workflow will reconcile. Every other delta —
|
||||
* manual strings, attribute changes, value edits — is real drift.
|
||||
*/
|
||||
function onlyManagedRowsPending(current: string, expected: string): boolean {
|
||||
const currentRows = new Map(parseStrings(current).map((entry) => [entry.key, entry]));
|
||||
const expectedRows = new Map(parseStrings(expected).map((entry) => [entry.key, entry]));
|
||||
for (const [key, entry] of currentRows) {
|
||||
const expectedEntry = expectedRows.get(key);
|
||||
if (!expectedEntry) {
|
||||
if (!key.startsWith(MANAGED_PREFIX)) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (expectedEntry.attrs !== entry.attrs || expectedEntry.rawValue !== entry.rawValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const key of expectedRows.keys()) {
|
||||
if (!currentRows.has(key) && !key.startsWith(MANAGED_PREFIX)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function syncAndroidAppI18n(
|
||||
options: { check?: boolean; tolerateManagedPending?: boolean } = {},
|
||||
) {
|
||||
const catalog = await buildCatalog();
|
||||
const drift: string[] = [];
|
||||
let sawUnmanagedDrift = false;
|
||||
for (const [filePath, expected] of catalog.resources) {
|
||||
const current = await readFile(filePath, "utf8").catch(() => "");
|
||||
if (current === expected) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
options.check &&
|
||||
options.tolerateManagedPending &&
|
||||
filePath.endsWith("strings.xml") &&
|
||||
onlyManagedRowsPending(current, expected)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
sawUnmanagedDrift = true;
|
||||
drift.push(path.relative(ROOT, filePath).split(path.sep).join("/"));
|
||||
if (!options.check) {
|
||||
await writeFile(filePath, expected);
|
||||
@@ -1246,9 +1287,13 @@ export async function syncAndroidAppI18n(options: { check?: boolean } = {}) {
|
||||
}
|
||||
const currentKotlin = await readFile(GENERATED_KOTLIN_PATH, "utf8").catch(() => "");
|
||||
if (currentKotlin !== catalog.kotlin) {
|
||||
drift.push(path.relative(ROOT, GENERATED_KOTLIN_PATH).split(path.sep).join("/"));
|
||||
if (!options.check) {
|
||||
await writeFile(GENERATED_KOTLIN_PATH, catalog.kotlin);
|
||||
// The Kotlin map derives 1:1 from the same managed catalog: tolerate it
|
||||
// exactly when every resource delta above was managed-pending.
|
||||
if (!(options.check && options.tolerateManagedPending && !sawUnmanagedDrift)) {
|
||||
drift.push(path.relative(ROOT, GENERATED_KOTLIN_PATH).split(path.sep).join("/"));
|
||||
if (!options.check) {
|
||||
await writeFile(GENERATED_KOTLIN_PATH, catalog.kotlin);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.check && drift.length > 0) {
|
||||
@@ -1298,9 +1343,9 @@ export async function verifyAndroidAppI18n() {
|
||||
process.stdout.write(`android-app-i18n: sourceKeys=${baseKeys.size}\n`);
|
||||
}
|
||||
|
||||
export async function checkAndroidAppI18n() {
|
||||
export async function checkAndroidAppI18n(options: { tolerateManagedPending?: boolean } = {}) {
|
||||
await verifyAndroidAppI18n();
|
||||
await syncAndroidAppI18n({ check: true });
|
||||
await syncAndroidAppI18n({ check: true, ...options });
|
||||
const localeStrings = await Promise.all(LOCALES.map(readStrings));
|
||||
const base = expectDefined(localeStrings[0], "English Android string resources");
|
||||
const translations = localeStrings.slice(1);
|
||||
|
||||
Reference in New Issue
Block a user