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
28 changes: 15 additions & 13 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@ on:
required: true
type: string # android, ios, or both
source_branch:
description: >-
Branch, tag, or SHA for prepare, node-modules setup, and build checkout (empty uses the default triggering ref).
When build_number is set, the bump is applied locally on each build runner after checkout.
required: false
description: 'Branch, tag, or SHA to build'
required: true
type: string
default: ''
build_number:
description: >-
Optional (workflow_call only). From generate-build-version.yml. When non-empty, each matrix
Expand Down Expand Up @@ -43,10 +40,10 @@ on:
description: 'platform input (android, ios, or both)'
value: ${{ inputs.platform }}
source_branch_input:
description: 'source_branch input (empty means default ref was used)'
description: 'source_branch input passed to this workflow'
value: ${{ inputs.source_branch }}
checkout_ref:
description: 'Git ref used for checkout (source_branch or triggering ref)'
description: 'Git ref used for checkout (same as source_branch)'
value: ${{ jobs.emit-build-metadata.outputs.checkout_ref }}
built_commit_sha:
description: 'Resolved commit SHA at checkout_ref after build succeeded'
Expand All @@ -62,6 +59,11 @@ on:
value: ${{ jobs.emit-build-metadata.outputs.ios_version_code }}
workflow_dispatch:
inputs:
source_branch:
description: 'Branch, tag, or SHA to build'
required: true
type: string
default: 'main'
build_name:
required: true
type: choice
Expand Down Expand Up @@ -113,18 +115,18 @@ jobs:
signing_aws_secret: ${{ steps.config.outputs.signing_aws_secret }}
signing_android_keystore_path: ${{ steps.config.outputs.signing_android_keystore_path }}
script_name: ${{ steps.config.outputs.script_name }}
checkout_ref_for_setup: ${{ inputs.source_branch || github.ref_name }}
checkout_ref_for_setup: ${{ inputs.source_branch }}
steps:
- uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1
if: ${{ inputs.runner_provider == 'namespace' }}
with:
fetch-depth: 1
ref: ${{ inputs.source_branch || github.ref_name }}
ref: ${{ inputs.source_branch }}
- uses: actions/checkout@v4
if: ${{ inputs.runner_provider != 'namespace' }}
with:
fetch-depth: 1
ref: ${{ inputs.source_branch || github.ref_name }}
ref: ${{ inputs.source_branch }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
Expand Down Expand Up @@ -191,12 +193,12 @@ jobs:
- uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1
if: ${{ inputs.runner_provider == 'namespace' }}
with:
ref: ${{ inputs.source_branch || github.ref_name }}
ref: ${{ inputs.source_branch }}
submodules: recursive
- uses: actions/checkout@v4
if: ${{ inputs.runner_provider != 'namespace' }}
with:
ref: ${{ inputs.source_branch || github.ref_name }}
ref: ${{ inputs.source_branch }}
submodules: recursive

- name: Apply build number locally
Expand Down Expand Up @@ -434,7 +436,7 @@ jobs:
# Must match Apply build config / artifact naming so build.sh loads the same
# builds.yml entry (e.g. main-e2e-bs-with-srp), not generic main-e2e (sim-only).
BUILD_CONFIG_NAME: ${{ inputs.build_name }}
GIT_BRANCH: ${{ inputs.source_branch || github.ref_name }}
GIT_BRANCH: ${{ inputs.source_branch }}
# React Native 0.81's ReactAndroid/build.gradle.kts requests CMake 3.30.5
# via `System.getenv("CMAKE_VERSION") ?: "3.30.5"`. The self-hosted runner
# only ships CMake 3.22.1 in /opt/android-sdk/cmake/ and AGP cannot auto-
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/expo-dev-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ jobs:
with:
build_name: main-dev-expo
platform: both
source_branch: ${{ github.ref_name }}
runner_provider: ${{ inputs.runner_provider }}
secrets: inherit
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ const MultichainAddressRow = ({
setIconState('copy');
}, 400);

// Show toast if ref provided
if (copyParams.toastRef?.current) {
// Show legacy row-managed toast only when both ref and message are provided.
if (copyParams.toastRef?.current && copyParams.toastMessage) {
copyParams.toastRef.current.showToast({
variant: ToastVariants.Plain,
labelOptions: [{ label: copyParams.toastMessage }],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,13 @@ export interface CopyParams {
*/
callback: () => Promise<void>;
/**
* Technically optional to keep types simple but toast ref for showing toast notification
* should always be present if copyParams is used. This ensures consistent behavior
* Optional toast ref for legacy callers that need the row to show a toast.
*/
toastRef?: React.RefObject<ToastRef | null>;
/**
* Required toast message. Specify what is being copied e.g. "Address copied", "Private key copied", etc
* Toast message used when toastRef is provided.
*/
toastMessage: string;
toastMessage?: string;
}

export interface MultichainAddressRowProps {
Expand Down
133 changes: 74 additions & 59 deletions app/components/Nav/Main/MainNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ import {
import {
PredictScreenStack,
PredictModalStack,
PredictPreviewSheetProvider,
selectPredictEnabledFlag,
} from '../../UI/Predict';
import {
Expand Down Expand Up @@ -831,73 +832,87 @@ const HomeTabs = () => {
};

return (
<Tab.Navigator
initialRouteName={Routes.WALLET.HOME}
tabBar={renderTabBar}
screenOptions={{ headerShown: false }}
>
{/* Home Tab */}
<Tab.Screen
name={Routes.WALLET.HOME}
options={options.home}
component={WalletTabStackFlow}
/>

{/* Explore Tab (w/ hidden browser) */}
<>
/*
* PredictPreviewSheetProvider is mounted here (above Tab.Navigator) so its
* BottomSheet renders inside the full-viewport Home Stack.Screen card.
* BottomSheet uses `absolute inset-0` (see
* @metamask/design-system-react-native) and would be clipped by an
* individual tab's content area if mounted lower in the tree.
*
* A nested provider in PredictScreenStack still shadows this one for
* usage; the registration stack in PredictPreviewSheetContext keeps only
* the innermost (most recently mounted) provider active for state-based
* Retry toasts so we don't double-fire when both are mounted.
*/
<PredictPreviewSheetProvider>
<Tab.Navigator
initialRouteName={Routes.WALLET.HOME}
tabBar={renderTabBar}
screenOptions={{ headerShown: false }}
>
{/* Home Tab */}
<Tab.Screen
name={Routes.TRENDING_VIEW}
options={{
...options.trending,
isSelected: (rootScreenName) =>
[Routes.TRENDING_VIEW, Routes.BROWSER.HOME].includes(
rootScreenName,
),
}}
component={ExploreHome}
name={Routes.WALLET.HOME}
options={options.home}
component={WalletTabStackFlow}
/>
<Tab.Screen
name={Routes.BROWSER.HOME}
options={{
...options.browser,
isHidden: true,
}}
component={BrowserFlow}
layout={({ children }) => <UnmountOnBlur>{children}</UnmountOnBlur>}
/>
</>

{/* Trade Tab */}
<Tab.Screen
name={Routes.MODAL.TRADE_WALLET_ACTIONS}
options={options.trade}
component={WalletTabStackFlow}
/>
{/* Explore Tab (w/ hidden browser) */}
<>
<Tab.Screen
name={Routes.TRENDING_VIEW}
options={{
...options.trending,
isSelected: (rootScreenName) =>
[Routes.TRENDING_VIEW, Routes.BROWSER.HOME].includes(
rootScreenName,
),
}}
component={ExploreHome}
/>
<Tab.Screen
name={Routes.BROWSER.HOME}
options={{
...options.browser,
isHidden: true,
}}
component={BrowserFlow}
layout={({ children }) => <UnmountOnBlur>{children}</UnmountOnBlur>}
/>
</>

{/* Activity Tab (replaced by Money when feature flag is on) */}
{isMoneyHomeScreenEnabled ? (
{/* Trade Tab */}
<Tab.Screen
name={Routes.MONEY.ROOT}
options={options.money}
component={MoneyAccountStackGate}
name={Routes.MODAL.TRADE_WALLET_ACTIONS}
options={options.trade}
component={WalletTabStackFlow}
/>
) : (

{/* Activity Tab (replaced by Money when feature flag is on) */}
{isMoneyHomeScreenEnabled ? (
<Tab.Screen
name={Routes.MONEY.ROOT}
options={options.money}
component={MoneyAccountStackGate}
/>
) : (
<Tab.Screen
name={Routes.TRANSACTIONS_VIEW}
options={options.activity}
component={TransactionsHome}
layout={({ children }) => <UnmountOnBlur>{children}</UnmountOnBlur>}
/>
)}

{/* Rewards Tab */}
<Tab.Screen
name={Routes.TRANSACTIONS_VIEW}
options={options.activity}
component={TransactionsHome}
layout={({ children }) => <UnmountOnBlur>{children}</UnmountOnBlur>}
name={Routes.REWARDS_VIEW}
options={options.rewards}
component={RewardsHome}
layout={({ children }) => UnmountOnBlurComponent(children)}
/>
)}

{/* Rewards Tab */}
<Tab.Screen
name={Routes.REWARDS_VIEW}
options={options.rewards}
component={RewardsHome}
layout={({ children }) => UnmountOnBlurComponent(children)}
/>
</Tab.Navigator>
</Tab.Navigator>
</PredictPreviewSheetProvider>
);
};

Expand Down
20 changes: 14 additions & 6 deletions app/components/Nav/Main/MainNavigator.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,20 @@ jest.mock('../../UI/Perps', () => ({
selectPerpsEnabledFlag: (state: unknown) => mockSelectPerpsEnabledFlag(state),
}));

jest.mock('../../UI/Predict', () => ({
PredictScreenStack: () => 'PredictScreenStack',
PredictModalStack: () => 'PredictModalStack',
selectPredictEnabledFlag: (state: unknown) =>
mockSelectPredictEnabledFlag(state),
}));
jest.mock('../../UI/Predict', () => {
const { Fragment } = jest.requireActual('react');
return {
PredictScreenStack: () => 'PredictScreenStack',
PredictModalStack: () => 'PredictModalStack',
PredictPreviewSheetProvider: ({
children,
}: {
children: React.ReactNode;
}) => jest.requireActual('react').createElement(Fragment, null, children),
selectPredictEnabledFlag: (state: unknown) =>
mockSelectPredictEnabledFlag(state),
};
});

jest.mock('../../UI/MarketInsights', () => ({
MarketInsightsView: () => 'MarketInsightsView',
Expand Down
Loading
Loading