Skip to content

Commit de98c47

Browse files
feat: move sources under the main text (#30035)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> In What's Happening the sources will now be displayed right under the main text, instead of being sticky at the bottom. <img width="392" height="839" alt="image" src="https://github.com/user-attachments/assets/2deb72ad-d730-4cdc-b374-b0e465698c5a" /> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: no-changelog ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk UI/layout change limited to the expanded card; main risk is minor regressions in press target/spacing or date rendering in the new inline sources row. > > **Overview** > **UI change:** The expanded What’s Happening card now renders the sources row *inline under the description* (within the scroll area) instead of as a fixed/sticky footer, while keeping the same `onSourcesPress` behavior. > > Adds a memoized `formattedDate` for the inline row and updates the unit test to mock `formatRelativeTime` and assert the relative date text is shown when sources are present. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 3249a52. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: António Regadas <antonio.regadas@consensys.net>
1 parent dc0c439 commit de98c47

2 files changed

Lines changed: 70 additions & 55 deletions

File tree

app/components/Views/WhatsHappeningDetailView/components/WhatsHappeningExpandedCard.test.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ describe('WhatsHappeningExpandedCard', () => {
247247
});
248248
});
249249

250-
it('calls onSourcesPress with the item articles when the sources footer is pressed', () => {
250+
it('calls onSourcesPress with the item articles when the sources row is pressed', () => {
251251
const mockOnSourcesPress = jest.fn();
252252
const article = {
253253
title: 'Test article',
@@ -257,9 +257,10 @@ describe('WhatsHappeningExpandedCard', () => {
257257
};
258258
const item = { ...baseItem, articles: [article] };
259259

260-
const { getUniqueSourcesByFavicon } = jest.requireMock(
260+
const { formatRelativeTime, getUniqueSourcesByFavicon } = jest.requireMock(
261261
'../../../UI/MarketInsights/utils/marketInsightsFormatting',
262262
);
263+
(formatRelativeTime as jest.Mock).mockReturnValueOnce('1d ago');
263264
(getUniqueSourcesByFavicon as jest.Mock).mockReturnValueOnce([
264265
{ name: 'coindesk.com', type: 'news', url: 'https://coindesk.com' },
265266
]);
@@ -277,6 +278,7 @@ describe('WhatsHappeningExpandedCard', () => {
277278

278279
fireEvent.press(screen.getByText('coindesk.com'));
279280
expect(mockOnSourcesPress).toHaveBeenCalledWith([article]);
281+
expect(screen.getByText('1d ago')).toBeOnTheScreen();
280282
});
281283

282284
it('passes perpsPriceBySymbol from hook to PerpsRow', () => {

app/components/Views/WhatsHappeningDetailView/components/WhatsHappeningExpandedCard.tsx

Lines changed: 66 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ interface WhatsHappeningExpandedCardProps {
4242
cardHeight: number;
4343
source: WhatsHappeningSourceValue;
4444
/**
45-
* Called when the user taps the sources footer row. The parent is responsible
45+
* Called when the user taps the sources row. The parent is responsible
4646
* for rendering the bottom sheet so it is anchored to the screen root rather
4747
* than the card's positioning context.
4848
*/
@@ -81,6 +81,11 @@ const WhatsHappeningExpandedCard: React.FC<WhatsHappeningExpandedCardProps> = ({
8181
return remaining > 0 ? `${first.name} +${remaining}` : first.name;
8282
}, [uniqueSources]);
8383

84+
const formattedDate = useMemo(
85+
() => (item.date ? formatRelativeTime(item.date, { nowLabel: 'now' }) : ''),
86+
[item.date],
87+
);
88+
8489
const { perpsPriceBySymbol } = useWhatsHappeningAssetPrices(item);
8590

8691
const scrollBottomFadeColors = useMemo((): string[] => {
@@ -166,14 +171,66 @@ const WhatsHappeningExpandedCard: React.FC<WhatsHappeningExpandedCardProps> = ({
166171
{item.title}
167172
</Text>
168173

169-
{/* Description */}
170-
{item.description && (
171-
<Text
172-
variant={TextVariant.BodyMd}
173-
color={TextColor.TextAlternative}
174-
>
175-
{item.description}
176-
</Text>
174+
{/* Description + sources */}
175+
{(item.description || uniqueSources.length > 0) && (
176+
<Box>
177+
{item.description && (
178+
<Text
179+
variant={TextVariant.BodyMd}
180+
color={TextColor.TextAlternative}
181+
>
182+
{item.description}
183+
</Text>
184+
)}
185+
186+
{uniqueSources.length > 0 && (
187+
<Pressable
188+
onPress={() => onSourcesPress?.(item.articles)}
189+
accessibilityRole="button"
190+
>
191+
{({ pressed }) => (
192+
<Box
193+
flexDirection={BoxFlexDirection.Row}
194+
alignItems={BoxAlignItems.Center}
195+
justifyContent={BoxJustifyContent.Between}
196+
gap={2}
197+
twClassName={
198+
pressed ? 'pt-2 pb-4 opacity-60' : 'pt-2 pb-4'
199+
}
200+
>
201+
<Box
202+
flexDirection={BoxFlexDirection.Row}
203+
alignItems={BoxAlignItems.Center}
204+
gap={2}
205+
twClassName="flex-shrink"
206+
>
207+
<SourceLogoGroup sources={uniqueSources} />
208+
{sourceLabel ? (
209+
<Text
210+
variant={TextVariant.BodySm}
211+
color={TextColor.TextAlternative}
212+
numberOfLines={1}
213+
>
214+
{sourceLabel}
215+
</Text>
216+
) : null}
217+
</Box>
218+
219+
{formattedDate ? (
220+
<Text
221+
variant={TextVariant.BodySm}
222+
color={TextColor.TextAlternative}
223+
numberOfLines={1}
224+
twClassName="shrink-0"
225+
>
226+
{formattedDate}
227+
</Text>
228+
) : null}
229+
</Box>
230+
)}
231+
</Pressable>
232+
)}
233+
</Box>
177234
)}
178235

179236
{/* Related assets section */}
@@ -207,50 +264,6 @@ const WhatsHappeningExpandedCard: React.FC<WhatsHappeningExpandedCardProps> = ({
207264
style={tw.style('absolute left-0 right-0 bottom-0 h-10')}
208265
/>
209266
</Box>
210-
211-
{/* Fixed sources footer */}
212-
{uniqueSources.length > 0 && (
213-
<Box twClassName="px-5 pb-5 pt-4" gap={4}>
214-
<Pressable
215-
onPress={() => onSourcesPress?.(item.articles)}
216-
accessibilityRole="button"
217-
>
218-
{({ pressed }) => (
219-
<Box
220-
flexDirection={BoxFlexDirection.Row}
221-
alignItems={BoxAlignItems.Center}
222-
justifyContent={BoxJustifyContent.Between}
223-
twClassName={pressed ? 'opacity-60' : ''}
224-
>
225-
<Box
226-
flexDirection={BoxFlexDirection.Row}
227-
alignItems={BoxAlignItems.Center}
228-
gap={2}
229-
>
230-
<SourceLogoGroup sources={uniqueSources} />
231-
{sourceLabel ? (
232-
<Text
233-
variant={TextVariant.BodySm}
234-
color={TextColor.TextAlternative}
235-
>
236-
{sourceLabel}
237-
</Text>
238-
) : null}
239-
</Box>
240-
241-
{item.date ? (
242-
<Text
243-
variant={TextVariant.BodySm}
244-
color={TextColor.TextAlternative}
245-
>
246-
{formatRelativeTime(item.date, { nowLabel: 'now' })}
247-
</Text>
248-
) : null}
249-
</Box>
250-
)}
251-
</Pressable>
252-
</Box>
253-
)}
254267
</Box>
255268
</Box>
256269
);

0 commit comments

Comments
 (0)