-
Notifications
You must be signed in to change notification settings - Fork 19
Amam / Add native support bridge #423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Dependency Reviewβ No vulnerabilities or license issues or OpenSSF Scorecard issues found.OpenSSF Scorecard
Scanned Manifest Files |
|
Claude finished @nijil-deriv's task ββ View job Code Review CompleteI've reviewed the PR that adds native support bridge integration for mobile apps. Here's my detailed feedback: Tasks
SummaryThis PR implements a bridge communication layer between the web app and native mobile containers (likely Flutter), enabling navigation commands like "back to app" and "home". The implementation is generally solid with good test coverage, but there are several issues that should be addressed. High Priority Issues1. Missing Hook Tests π΄Location: The core Recommendation: Create
2. Security: Insufficient Input Validation π΄Location: const message: DerivAppChannelMessage = { event };
window.DerivAppChannel.postMessage(JSON.stringify(message));The hook accepts any event type matching the union type but doesn't validate it at runtime. If the event parameter is somehow manipulated or comes from an untrusted source, this could lead to unintended bridge commands. Recommendation: Add runtime validation: const validEvents = ['trading:back', 'trading:home'] as const;
if (!validEvents.includes(event)) {
console.error(`Invalid bridge event: ${event}`);
return false;
}3. Race Condition in ToggleMenuDrawer π΄Location: const handleLogout = React.useCallback(async () => {
toggleDrawer();
sendBridgeEvent('trading:back', async () => {
await logoutClient();
});
}, [logoutClient, toggleDrawer, sendBridgeEvent]);
Recommendation: const handleLogout = React.useCallback(async () => {
const success = sendBridgeEvent('trading:back', async () => {
await logoutClient();
});
if (success) {
toggleDrawer();
}
}, [logoutClient, toggleDrawer, sendBridgeEvent]);4. Inconsistent Fallback Execution π‘Location: The fallback in the catch block (lines 21-29) is executed even when the bridge successfully sent the message but threw an error afterward. This could result in double execution. Recommendation: Restructure to only execute fallback when bridge is unavailable or fails to send: if (!isDesktop && window.DerivAppChannel?.postMessage) {
try {
const message: DerivAppChannelMessage = { event };
window.DerivAppChannel.postMessage(JSON.stringify(message));
return true;
} catch (error) {
console.error('Failed to send bridge message:', error);
// Only fall through to fallback if bridge failed
}
}
// Execute fallback
if (fallback) {
try {
fallback();
return true;
} catch (fallbackError) {
console.error('Fallback execution failed:', fallbackError);
return false;
}
}Medium Priority Issues5. Type Safety: Missing Return Type Annotations π‘Location: The hook and its return functions lack explicit return type annotations, relying on inference. Recommendation: export const useMobileBridge = (): {
sendBridgeEvent: (event: 'trading:back' | 'trading:home', fallback?: () => void) => boolean;
isBridgeAvailable: () => boolean;
isDesktop: boolean;
} => {6. Async Fallback Not Awaited π‘Location: The fallback functions are called but not awaited, even though they can be async (as seen in Recommendation: const sendBridgeEvent = useCallback(
async (event: 'trading:back' | 'trading:home', fallback?: () => void | Promise<void>) => {
// ... existing logic
if (fallback) {
await fallback();
return true;
}
},
[isDesktop]
);7. Missing JSDoc Documentation π‘Location: The hook lacks documentation explaining its purpose, parameters, and return values, making it harder for other developers to understand the bridge integration. Recommendation: Add JSDoc: /**
* Hook for communicating with native mobile app containers via bridge
* @returns Object with sendBridgeEvent function, isBridgeAvailable check, and isDesktop flag
*/8. Accessibility Issue in BrandShortLogo π‘Location: <div onClick={handleLogoClick} style={{ cursor: 'pointer' }}>Using a Recommendation: Use a <button
onClick={handleLogoClick}
className="brand-logo-button"
>
<BrandDerivLogoCoralIcon width={24} height={24} />
</button>9. Console Errors in Production π‘Location:
Recommendation: Use a logging service or environment-based logging: if (process.env.NODE_ENV !== 'production') {
console.error('Failed to send bridge message:', error);
}Low Priority Issues10. Redundant isDesktop Exposure π΅Location: The hook exposes Recommendation: Remove 11. Test Mock Inconsistency π΅Location: Test files The Recommendation: Create a shared test helper for consistent mocking: // test-utils/mockMobileBridge.ts
export const mockMobileBridge = (options = {}) => ({
sendBridgeEvent: jest.fn((event, fallback) => {
if (options.executeFallback && fallback) fallback();
}),
isBridgeAvailable: jest.fn(() => options.bridgeAvailable ?? false),
isDesktop: options.isDesktop ?? true,
});12. Global Type Extension Could Be More Specific π΅Location: interface DerivAppChannelMessage {
event: 'trading:back' | 'trading:home';
// Add other event types as needed
}The comment suggests more events will be added, but there's no payload structure defined. Recommendation: Add a more extensible structure: interface DerivAppChannelMessage {
event: 'trading:back' | 'trading:home';
payload?: Record<string, unknown>;
}13. Unnecessary Fragment in AccountActions π΅Location: const LoggedOutView = () => (
<>
<LoginButtonV2 className='acc-info__button' />
</>
);Fragment is unnecessary when returning a single element. Recommendation: const LoggedOutView = () => <LoginButtonV2 className='acc-info__button' />;Performance Notesβ
Good: Security Assessmentπ‘ Medium Risk:
Recommendations:
Test Coverage Analysisβ Good Coverage:
β Missing:
Quick FixesHere are some safe patches you can apply immediately: 1. Add runtime validation to useMobileBridge.ts: const validEvents = ['trading:back', 'trading:home'] as const;
const sendBridgeEvent = useCallback((event: 'trading:back' | 'trading:home', fallback?: () => void) => {
if (!validEvents.includes(event)) {
console.error(`Invalid bridge event: ${event}`);
return false;
}
// ... rest of logic
}, [isDesktop]);2. Fix accessibility in brand-short-logo.tsx: <button
onClick={handleLogoClick}
style={{ cursor: 'pointer', border: 'none', background: 'none', padding: 0 }}
>
<BrandDerivLogoCoralIcon width={24} height={24} />
</button>3. Remove unnecessary fragment in account-actions.tsx: const LoggedOutView = () => <LoginButtonV2 className='acc-info__button' />;RecommendationsBefore Merging:
Post-Merge:
Overall, this is a well-structured PR with good test coverage for the component integrations. The main concerns are the missing hook tests and the potential race condition in the logout flow. The bridge integration pattern is solid and follows React best practices. |
Deploying derivatives-trader with Β
|
| Latest commit: |
01e06a9
|
| Status: | Β β Β Deploy successful! |
| Preview URL: | https://8ab358a7.derivatives-trader.pages.dev |
| Branch Preview URL: | https://amam-add-native-support-brid.derivatives-trader.pages.dev |
Description
(Brief description of what this PR does)
Related Issue: Fixes #(issue)
Type of Change
Testing
Screenshots
(Add screenshots if UI changes)
Before:
After: