Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions scripts/generate-brand-assets.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,17 @@ const LEGACY_APPLICATION_ASSETS = [
'OpenBitFun-Installer/src-tauri/icons/icon.ico',
'OpenBitFun-Installer/src-tauri/icons/icon.icns',
'src/apps/mobile/harmonyos/AppScope/resources/base/media/openbitfun_icon.png',
'src/apps/mobile/harmonyos/AppScope/resources/base/media/openbitfun-app-icon.png',
'src/apps/mobile/harmonyos/AppScope/resources/base/media/background.png',
'src/apps/mobile/harmonyos/AppScope/resources/base/media/foreground.png',
'src/apps/mobile/harmonyos/AppScope/resources/base/media/layered_image.json',
'src/apps/mobile/harmonyos/AppScope/resources/base/media/openbitfun-app-icon.png',
'src/apps/mobile/harmonyos/entry/src/main/resources/base/media/openbitfun_icon.png',
'src/apps/mobile/harmonyos/entry/src/main/resources/base/media/openbitfun-app-icon.png',
'src/apps/mobile/harmonyos/entry/src/main/resources/base/media/openbitfun-start-window.png',
'src/apps/mobile/harmonyos/entry/src/main/resources/base/media/background.png',
'src/apps/mobile/harmonyos/entry/src/main/resources/base/media/foreground.png',
'src/apps/mobile/harmonyos/entry/src/main/resources/base/media/layered_image.json',
'src/apps/mobile/harmonyos/entry/src/main/resources/base/media/startIcon.png',
'src/apps/mobile/harmonyos/entry/src/main/resources/base/media/openbitfun-app-icon.png',
'src/apps/mobile/harmonyos/entry/src/main/resources/base/media/openbitfun-start-window.png',
'src/apps/mobile/ios/OpenBitFun/Resources.xcassets/AppIcon.appiconset/openbitfun_icon.png',
'src/apps/mobile/ios/OpenBitFun/Resources.xcassets/OpenBitFunLogo.imageset',
'src/apps/relay-server/static/assets/Logo-ICON-BOaKcXgO.png',
Expand Down
26 changes: 25 additions & 1 deletion scripts/generate-brand-assets.test.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { readFileSync, readdirSync } from 'node:fs';
import test from 'node:test';
import { canonicalizeIcns } from './icns-container.mjs';

Expand All @@ -8,6 +8,11 @@ const GENERATED_ICNS_FILES = [
'OpenBitFun-Installer/src-tauri/icons/openbitfun-app-icon.icns',
];

const HARMONY_MEDIA_DIRS = [
'src/apps/mobile/harmonyos/AppScope/resources/base/media',
'src/apps/mobile/harmonyos/entry/src/main/resources/base/media',
];

function createChunk(type, payload) {
const chunk = Buffer.alloc(8 + payload.length);
chunk.write(type, 0, 4, 'ascii');
Expand Down Expand Up @@ -45,6 +50,25 @@ test('generated macOS icons use the canonical ICNS layout', () => {
assert.ok(desktop.equals(installer), 'desktop and installer ICNS files differ');
});

test('HarmonyOS generated media use valid resource identifiers', () => {
for (const directory of HARMONY_MEDIA_DIRS) {
for (const fileName of readdirSync(directory)) {
const resourceName = fileName.replace(/\.[^.]+$/, '');
assert.match(
resourceName,
/^[a-zA-Z0-9_]+$/,
`${directory}/${fileName} is not a valid HarmonyOS resource name`,
);
}
}

const appConfig = readFileSync('src/apps/mobile/harmonyos/AppScope/app.json5', 'utf8');
const moduleConfig = readFileSync('src/apps/mobile/harmonyos/entry/src/main/module.json5', 'utf8');
assert.match(appConfig, /\$media:openbitfun_app_icon/);
assert.match(moduleConfig, /\$media:openbitfun_app_icon/);
assert.match(moduleConfig, /\$media:openbitfun_start_window/);
});

test('ICNS canonicalization rejects malformed containers', () => {
assert.throws(
() => canonicalizeIcns(Buffer.from('not-an-icns')),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,20 @@ export class DetectedRemoteUrlResult {
* What a scanned pair URL should do next. Applying the URL and starting a
* connect are separate steps: the detector never connects by itself.
*
* A OpenBitFun cloud session on this phone is already the account proof. An
* account-auth QR for that same user must not ask for the password again;
* it continues through the same-account device path instead of room pairing.
* An OpenBitFun cloud session on this phone is already the account proof. The
* account device directory is the authoritative membership check for a QR
* target; display usernames are not, because aliases and restored legacy
* records can legitimately differ. Reuse is still limited to the same relay,
* then device selection verifies the scanned desktop id through the
* authenticated directory instead of asking for the password based on a
* string mismatch.
*/
export class ConnectScanDecisionPolicy {
static decide(
accountAuth: boolean,
hasCloudAccountSession: boolean,
cloudUsername: string,
qrUsername: string,
_cloudUsername: string,
_qrUsername: string,
desktopId: string,
cloudRelayUrl: string = '',
qrRelayUrl: string = ''
Expand All @@ -38,7 +42,6 @@ export class ConnectScanDecisionPolicy {
return DetectedUrlAction.PAIR_NOW;
}
if (hasCloudAccountSession &&
ConnectScanDecisionPolicy.usernamesCompatible(cloudUsername, qrUsername) &&
ConnectScanDecisionPolicy.relayEndpointsCompatible(cloudRelayUrl, qrRelayUrl)) {
return desktopId.trim().length > 0 ?
DetectedUrlAction.USE_CLOUD_DEVICE :
Expand All @@ -47,14 +50,6 @@ export class ConnectScanDecisionPolicy {
return DetectedUrlAction.PROMPT_ACCOUNT_PASSWORD;
}

static usernamesCompatible(cloudUsername: string, qrUsername: string): boolean {
const qr = qrUsername.trim();
if (qr.length === 0) {
return true;
}
return cloudUsername.trim() === qr;
}

static relayEndpointsCompatible(cloudRelayUrl: string, qrRelayUrl: string): boolean {
const cloud = ConnectScanDecisionPolicy.normalizedRelayEndpoint(cloudRelayUrl);
const qr = ConnectScanDecisionPolicy.normalizedRelayEndpoint(qrRelayUrl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -775,9 +775,17 @@ export default function conversationPresentationUnitTest() {
)).assertEqual(DetectedUrlAction.PROMPT_ACCOUNT_PASSWORD);
});

it('still asks for a password when the QR belongs to a different account', 0, () => {
expect(ConnectScanDecisionPolicy.decide(true, true, 'alice', 'bob', 'desktop-1'))
.assertEqual(DetectedUrlAction.PROMPT_ACCOUNT_PASSWORD);
it('uses the authenticated device directory instead of display-name equality', 0, () => {
expect(ConnectScanDecisionPolicy.decide(
true, true, 'alice', 'bob', 'desktop-1',
'https://relay.example.com', 'wss://relay.example.com/ws'
))
.assertEqual(DetectedUrlAction.USE_CLOUD_DEVICE);
expect(ConnectScanDecisionPolicy.decide(
true, true, '', 'renamed-account', 'desktop-1',
'https://relay.example.com', 'https://relay.example.com/'
))
.assertEqual(DetectedUrlAction.USE_CLOUD_DEVICE);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ export default function transportAndGeneralChatUnitTest() {

expect(policy.shouldPromptForAccount(descriptor)).assertTrue();
expect(policy.shouldPromptForAccount(descriptor, true, 'alice', descriptor.relayUrl)).assertFalse();
expect(policy.shouldPromptForAccount(descriptor, true, 'bob', descriptor.relayUrl)).assertTrue();
expect(policy.shouldPromptForAccount(descriptor, true, 'bob', descriptor.relayUrl)).assertFalse();
});
});

Expand Down
Loading