From e556b9c613ee4e638c847ee79963a284e746d372 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Tue, 3 Nov 2026 22:42:12 +0000 Subject: [PATCH] Use Lucide for the icons that still came from the system Share is share on iOS and share-2 on Android, New commit is square-pen, the score card badge is badge-check, the scorecard is a slightly bigger gamepad and This Device uses tablet-smartphone. The Apple logo stays. The other icons that fell back to SF Symbols or Material now have their Lucide SVGs too. On your own profile, Scheduled commits moves out of the header into the nav bar next to Starred, and Share and Account go into a new menu. Co-Authored-By: Claude Opus 5.5 --- app/Icons/Lucide.php | 79 ++++++++----------- app/NativeComponents/Log.php | 4 +- app/NativeComponents/Profile.php | 33 +++++--- resources/icons/arrow-up-right.svg | 1 + resources/icons/badge-check.svg | 1 + resources/icons/building-2.svg | 1 + resources/icons/calendar-days.svg | 1 + resources/icons/chevron-down.svg | 1 + resources/icons/chevron-left.svg | 1 + resources/icons/chevron-right.svg | 1 + resources/icons/corner-down-right.svg | 1 + resources/icons/gamepad.svg | 1 + resources/icons/list-ordered.svg | 1 + resources/icons/map-pin.svg | 1 + resources/icons/share.svg | 1 + resources/icons/tablet-smartphone.svg | 1 + .../partials/organization-header.blade.php | 2 +- .../native/partials/profile-header.blade.php | 11 +-- .../native/partials/score-card.blade.php | 4 +- resources/views/native/this-device.blade.php | 3 +- tests/Feature/CustomIconsTest.php | 12 +++ tests/Feature/Screens/LogTest.php | 4 +- tests/Feature/Screens/ProfileSafetyTest.php | 4 +- tests/Feature/Screens/ProfileShareTest.php | 17 ++-- tests/Feature/Screens/ProfileTest.php | 16 ++-- 25 files changed, 112 insertions(+), 90 deletions(-) create mode 100644 resources/icons/arrow-up-right.svg create mode 100644 resources/icons/badge-check.svg create mode 100644 resources/icons/building-2.svg create mode 100644 resources/icons/calendar-days.svg create mode 100644 resources/icons/chevron-down.svg create mode 100644 resources/icons/chevron-left.svg create mode 100644 resources/icons/chevron-right.svg create mode 100644 resources/icons/corner-down-right.svg create mode 100644 resources/icons/gamepad.svg create mode 100644 resources/icons/list-ordered.svg create mode 100644 resources/icons/map-pin.svg create mode 100644 resources/icons/share.svg create mode 100644 resources/icons/tablet-smartphone.svg diff --git a/app/Icons/Lucide.php b/app/Icons/Lucide.php index cab46406..b6ea5bb9 100644 --- a/app/Icons/Lucide.php +++ b/app/Icons/Lucide.php @@ -2,21 +2,18 @@ namespace App\Icons; -use Native\Mobile\Icon\AndroidSymbol; use Native\Mobile\Icon\Icon; /** * The Lucide icons the web draws. * - * Most are Lucide's own drawings. Their SVGs, from lucide-react 1.57.0, are in - * resources/icons, and the NativePHP build turns each one into a template image - * on iOS and a VectorDrawable on Android, so both platforms draw the web's - * strokes, tinted like any other icon. `$filled` is the on state of a star, a - * bookmark or a pin, which the web draws as the same shape with `fill-current`. + * Their SVGs, from lucide-react 1.57.0, are in resources/icons, and the + * NativePHP build turns each one into a template image on iOS and a + * VectorDrawable on Android, so both platforms draw the web's strokes, tinted + * like any other icon. `$filled` is the on state of a star, a bookmark or a + * pin, which the web draws as the same shape with `fill-current`. * - * The rest still map to the closest SF Symbol and outlined Material icon. To - * draw one of them as the web does, add its SVG to resources/icons and name it - * in custom(). + * A new case needs its SVG in resources/icons and a line in custom(). */ enum Lucide { @@ -39,11 +36,12 @@ enum Lucide case ArrowUp; case Pin; case ListOrdered; + /** lucide-react 1.57 draws Building2 as building-complex, so that is the drawing in building-2.svg. */ case Building2; case MapPin; case CalendarDays; case Clock3; - /** The web draws GitHub's own mark, which neither system has, so this is the code glyph. */ + /** Lucide has no brand marks, so this is GitHub's own, as the web draws it. */ case Github; case Quote; case Check; @@ -109,7 +107,11 @@ enum Lucide case Phone; case PhoneOff; case MicOff; + case Share; case Share2; + case BadgeCheck; + case Gamepad; + case TabletSmartphone; /** * The icon the web's inbox draws for a kind of notification. @@ -154,11 +156,11 @@ public static function forBadge(string $icon): self } /** - * The `custom:` icon drawn from resources/icons/.svg, or null for a system glyph. + * The `custom:` icon drawn from resources/icons/.svg. */ - public function custom(bool $filled = false): ?string + public function custom(bool $filled = false): string { - $name = match ($this) { + return Icon::custom(match ($this) { self::GitCommitVertical => 'git-commit-vertical', self::Bell => 'bell', self::MessagesSquare => 'messages-square', @@ -169,8 +171,18 @@ public function custom(bool $filled = false): ?string self::Star => $filled ? 'star-filled' : 'star', self::Bookmark => $filled ? 'bookmark-filled' : 'bookmark', self::Trash2 => 'trash-2', + self::CornerDownRight => 'corner-down-right', + self::ChevronRight => 'chevron-right', + self::ChevronLeft => 'chevron-left', + self::ChevronDown => 'chevron-down', + self::ArrowUpRight => 'arrow-up-right', self::ArrowUp => 'arrow-up', self::ChevronsUp => 'chevrons-up', + self::ListOrdered => 'list-ordered', + self::Building2 => 'building-2', + self::MapPin => 'map-pin', + self::CalendarDays => 'calendar-days', + self::Github => 'github', self::Quote => 'quote', self::Check => 'check', self::X => 'x', @@ -234,44 +246,21 @@ public function custom(bool $filled = false): ?string self::Phone => 'phone', self::PhoneOff => 'phone-off', self::MicOff => 'mic-off', + self::Share => 'share', self::Share2 => 'share-2', - default => null, - }; - - return $name === null ? null : Icon::custom($name); + self::BadgeCheck => 'badge-check', + self::Gamepad => 'gamepad', + self::TabletSmartphone => 'tablet-smartphone', + }); } - public function ios(bool $filled = false): Ios|string + public function ios(bool $filled = false): string { - return $this->custom($filled) ?? match ($this) { - self::CornerDownRight => Ios::ArrowTurnDownRight, - self::ChevronRight => Ios::ChevronRight, - self::ChevronLeft => Ios::ChevronLeft, - self::ChevronDown => Ios::ChevronDown, - self::ArrowUpRight => Ios::ArrowUpRight, - self::ListOrdered => Ios::ListNumber, - self::Building2 => Ios::Building2, - self::MapPin => Ios::Mappin, - self::CalendarDays => Ios::Calendar, - self::Github => Ios::ChevronLeftForwardslashChevronRight, - default => Ios::Circle, - }; + return $this->custom($filled); } - public function android(bool $filled = false): AndroidSymbol|string + public function android(bool $filled = false): string { - return $this->custom($filled) ?? match ($this) { - self::CornerDownRight => AndroidOutlined::SubdirectoryArrowRight, - self::ChevronRight => AndroidOutlined::ChevronRight, - self::ChevronLeft => AndroidOutlined::ChevronLeft, - self::ChevronDown => AndroidOutlined::ExpandMore, - self::ArrowUpRight => AndroidOutlined::ArrowOutward, - self::ListOrdered => AndroidOutlined::FormatListNumbered, - self::Building2 => AndroidOutlined::Business, - self::MapPin => AndroidOutlined::LocationOn, - self::CalendarDays => AndroidOutlined::CalendarToday, - self::Github => AndroidOutlined::Code, - default => AndroidOutlined::Circle, - }; + return $this->custom($filled); } } diff --git a/app/NativeComponents/Log.php b/app/NativeComponents/Log.php index d8d7f11c..050e79e2 100644 --- a/app/NativeComponents/Log.php +++ b/app/NativeComponents/Log.php @@ -15,8 +15,6 @@ use App\Devnull\ShownTabs; use App\Devnull\Stash; use App\Devnull\Timeline; -use App\Icons\Android; -use App\Icons\Ios; use App\Icons\Lucide; use App\Models\PendingWrite; use App\NativeComponents\Concerns\DrawsSkeletonFirst; @@ -341,7 +339,7 @@ public function navigationOptions(): ?NavBarOptions ) ->action( NavAction::make('compose') - ->icon(ios: Ios::SquareAndPencil, android: Android::Edit) + ->icon(ios: Lucide::SquarePen->ios(), android: Lucide::SquarePen->android()) ->a11yLabel('New commit') ->press('composeInSheet'), ); diff --git a/app/NativeComponents/Profile.php b/app/NativeComponents/Profile.php index e3c65351..390823e4 100644 --- a/app/NativeComponents/Profile.php +++ b/app/NativeComponents/Profile.php @@ -15,8 +15,6 @@ use App\Devnull\SiteLink; use App\Devnull\Stash; use App\Devnull\Timeline; -use App\Icons\AndroidOutlined; -use App\Icons\Ios; use App\Icons\Lucide; use App\NativeComponents\Concerns\DrawsSkeletonFirst; use App\NativeComponents\Concerns\ExpandsThreads; @@ -183,7 +181,12 @@ public function navigationOptions(): ?NavBarOptions { if ($this->isMe()) { return NavBarOptions::make() - ->action($this->shareAction()->a11yLabel('Share profile')) + ->action( + NavAction::make('scheduled') + ->icon(ios: Lucide::CalendarDays->ios(), android: Lucide::CalendarDays->android()) + ->a11yLabel('Scheduled commits') + ->press('openScheduledCommits'), + ) ->action( NavAction::make('starred') ->icon(ios: Lucide::Star->ios(), android: Lucide::Star->android()) @@ -191,10 +194,16 @@ public function navigationOptions(): ?NavBarOptions ->press('openStarred'), ) ->action( - NavAction::make('account') - ->icon(ios: Lucide::Settings2->ios(), android: Lucide::Settings2->android()) - ->a11yLabel('Account') - ->press('openAccount'), + NavAction::make('more') + ->icon(ios: Lucide::Ellipsis->ios(), android: Lucide::Ellipsis->android()) + ->a11yLabel('More options') + ->items([ + $this->shareAction()->label('Share profile'), + NavAction::make('account') + ->label('Account') + ->icon(ios: Lucide::Settings2->ios(), android: Lucide::Settings2->android()) + ->press('openAccount'), + ]), ); } @@ -207,14 +216,14 @@ public function navigationOptions(): ?NavBarOptions } /** - * Share profile, with the phone's own share icon: first in the nav bar on - * your own profile, first in the ⋯ on someone else's. It's always there, as - * the link needs nothing but the username. + * Share profile, under the share shape each phone uses (Lucide's share on + * iOS, share-2 on Android), first in the ⋯ of every profile. It's always + * there, as the link needs nothing but the username. */ private function shareAction(): NavAction { return NavAction::make('share') - ->icon(ios: Ios::SquareAndArrowUp, android: AndroidOutlined::Share) + ->icon(ios: Lucide::Share->ios(), android: Lucide::Share2->android()) ->press('shareProfile'); } @@ -569,7 +578,7 @@ public function editOnGitHub(): void } /** - * Your scheduled commits, beneath this tab, from the calendar beside Edit on GitHub. + * Your scheduled commits, beneath this tab, from the calendar in your profile's nav bar. */ public function openScheduledCommits(): void { diff --git a/resources/icons/arrow-up-right.svg b/resources/icons/arrow-up-right.svg new file mode 100644 index 00000000..de3008c9 --- /dev/null +++ b/resources/icons/arrow-up-right.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/badge-check.svg b/resources/icons/badge-check.svg new file mode 100644 index 00000000..b6ed2a23 --- /dev/null +++ b/resources/icons/badge-check.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/building-2.svg b/resources/icons/building-2.svg new file mode 100644 index 00000000..a404d78d --- /dev/null +++ b/resources/icons/building-2.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/calendar-days.svg b/resources/icons/calendar-days.svg new file mode 100644 index 00000000..78c4ba1e --- /dev/null +++ b/resources/icons/calendar-days.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/chevron-down.svg b/resources/icons/chevron-down.svg new file mode 100644 index 00000000..a81a4b08 --- /dev/null +++ b/resources/icons/chevron-down.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/chevron-left.svg b/resources/icons/chevron-left.svg new file mode 100644 index 00000000..00b99544 --- /dev/null +++ b/resources/icons/chevron-left.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/chevron-right.svg b/resources/icons/chevron-right.svg new file mode 100644 index 00000000..42563645 --- /dev/null +++ b/resources/icons/chevron-right.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/corner-down-right.svg b/resources/icons/corner-down-right.svg new file mode 100644 index 00000000..abb0cdfe --- /dev/null +++ b/resources/icons/corner-down-right.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/gamepad.svg b/resources/icons/gamepad.svg new file mode 100644 index 00000000..7f20f6c3 --- /dev/null +++ b/resources/icons/gamepad.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/list-ordered.svg b/resources/icons/list-ordered.svg new file mode 100644 index 00000000..fcc7f9ea --- /dev/null +++ b/resources/icons/list-ordered.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/map-pin.svg b/resources/icons/map-pin.svg new file mode 100644 index 00000000..9029b306 --- /dev/null +++ b/resources/icons/map-pin.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/share.svg b/resources/icons/share.svg new file mode 100644 index 00000000..00827401 --- /dev/null +++ b/resources/icons/share.svg @@ -0,0 +1 @@ + diff --git a/resources/icons/tablet-smartphone.svg b/resources/icons/tablet-smartphone.svg new file mode 100644 index 00000000..8daf843f --- /dev/null +++ b/resources/icons/tablet-smartphone.svg @@ -0,0 +1 @@ + diff --git a/resources/views/native/partials/organization-header.blade.php b/resources/views/native/partials/organization-header.blade.php index d0a74b47..295a56b4 100644 --- a/resources/views/native/partials/organization-header.blade.php +++ b/resources/views/native/partials/organization-header.blade.php @@ -20,7 +20,7 @@ - + @if ($organization->isFollowing) Unfollow @else diff --git a/resources/views/native/partials/profile-header.blade.php b/resources/views/native/partials/profile-header.blade.php index 9ce5d609..78c88ffc 100644 --- a/resources/views/native/partials/profile-header.blade.php +++ b/resources/views/native/partials/profile-header.blade.php @@ -1,7 +1,7 @@ {{-- The profile's header, which the pager draws above its tabs (see profile.blade.php): the avatar ringed in their - lane, Follow (or, on your own, Scheduled commits and Edit on GitHub), who they are, what GitHub says about them, - their counts and, in pastéis mode, the pastéis they've been given, then their activity, streaks and achievements. + lane, Follow (or, on your own, Edit on GitHub), who they are, what GitHub says about them, their counts and, in + pastéis mode, the pastéis they've been given, then their activity, streaks and achievements. It scrolls up with the tab you're on until the tabs pin under the nav bar. --}} @@ -33,11 +33,8 @@ @if ($sketched) {{-- Follow waits for devnull to say whether you already do. --}} @elseif ($isMe) - {{-- Your scheduled commits, which only you see, beside Edit on GitHub. --}} - - - Edit on GitHub - + {{-- Edit on GitHub, where the web puts Follow. Your scheduled commits are in the nav bar. --}} + Edit on GitHub @else {{-- The web's Remote, beside Follow, when you can push to them. --}} diff --git a/resources/views/native/partials/score-card.blade.php b/resources/views/native/partials/score-card.blade.php index 74fa6455..c946d18f 100644 --- a/resources/views/native/partials/score-card.blade.php +++ b/resources/views/native/partials/score-card.blade.php @@ -23,7 +23,7 @@ Nata Nu/⁠⁠ers @if ($card->signed) - + Signed by their device @endif @@ -35,7 +35,7 @@ @if ($playable) - + Beat {{ $score }} @endif diff --git a/resources/views/native/this-device.blade.php b/resources/views/native/this-device.blade.php index cededaeb..f5c11075 100644 --- a/resources/views/native/this-device.blade.php +++ b/resources/views/native/this-device.blade.php @@ -1,6 +1,5 @@ @use('App\Devnull\Lane') @use('App\Icons\Ios') -@use('App\Icons\Android') @use('App\Icons\Lucide') {{-- @@ -309,7 +308,7 @@ - Your devices on dev-null.sh + Your devices on dev-null.sh diff --git a/tests/Feature/CustomIconsTest.php b/tests/Feature/CustomIconsTest.php index ff75efd8..eae61c26 100644 --- a/tests/Feature/CustomIconsTest.php +++ b/tests/Feature/CustomIconsTest.php @@ -35,6 +35,18 @@ function referencedCustomIcons(): array expect($missing)->toBe([]); }); +it('draws no system icon but the Apple logo, which Sign in with Apple has to show', function () { + $systemIcons = collect([...File::allFiles(app_path()), ...File::allFiles(resource_path('views'))]) + ->reject(fn (SplFileInfo $file): bool => str_starts_with($file->getPathname(), app_path('Icons'))) + ->flatMap(function (SplFileInfo $file): array { + preg_match_all('/\b(?:Ios|Android|AndroidOutlined)::(?!AppleLogo\b)\w+/', (string) file_get_contents($file->getPathname()), $matches); + + return array_map(fn (string $icon): string => $file->getFilename().': '.$icon, $matches[0]); + }); + + expect($systemIcons->all())->toBe([]); +}); + it('converts each SVG in resources/icons for both platforms', function (string $path) { // The build runs the same conversion and stops at the first SVG it can't draw. $icon = SvgParser::parse((string) file_get_contents($path), 'resources/icons/'.basename($path)); diff --git a/tests/Feature/Screens/LogTest.php b/tests/Feature/Screens/LogTest.php index 6ea91a05..3b16f0db 100644 --- a/tests/Feature/Screens/LogTest.php +++ b/tests/Feature/Screens/LogTest.php @@ -453,7 +453,7 @@ ->and($icon('star-c1')['props']['color'] ?? null)->toBe($muted) ->and($text('star-c1', '1')['props']['color'] ?? null)->toBe($muted) ->and($icon('reply-to-c1')['props']['name'] ?? null)->toBe('custom:message-square-reply') - ->and($icon('replies-c2')['props']['name'] ?? null)->toBe('chevron.right') + ->and($icon('replies-c2')['props']['name'] ?? null)->toBe('custom:chevron-right') ->and($text('replies-c2', '1 reply')['props']['color'] ?? null)->toBe($primary) ->and($icon('more-c1')['props']['name'] ?? null)->toBe('custom:ellipsis') ->and($icon('more-c1')['props']['color'] ?? null)->toBe($muted); @@ -587,7 +587,7 @@ ->assertSee('This commit was deleted.') ->assertSee('Hide replies'); - expect(findNode(nodeByRef($screen, 'replies-c2'), fn (array $node): bool => ($node['type'] ?? null) === 'icon')['props']['name'] ?? null)->toBe('chevron.down'); + expect(findNode(nodeByRef($screen, 'replies-c2'), fn (array $node): bool => ($node['type'] ?? null) === 'icon')['props']['name'] ?? null)->toBe('custom:chevron-down'); // Its replies get the same actions, and open their own page. $screen->tap('star-r1'); diff --git a/tests/Feature/Screens/ProfileSafetyTest.php b/tests/Feature/Screens/ProfileSafetyTest.php index 6b0848d2..9dec539c 100644 --- a/tests/Feature/Screens/ProfileSafetyTest.php +++ b/tests/Feature/Screens/ProfileSafetyTest.php @@ -71,13 +71,13 @@ function profileMenu(TestableComponent $screen): ?array } /** - * The safety items of the profile's ⋯ menu, by id, after Share profile, or null when it has none. + * The safety items of the profile's ⋯ menu, by id, leaving out Share profile and your own Account, or null when it has none. * * @return array>|null */ function safetyMenu(TestableComponent $screen): ?array { - $items = collect(profileMenu($screen) ?? [])->except('share')->all(); + $items = collect(profileMenu($screen) ?? [])->except(['share', 'account'])->all(); return $items === [] ? null : $items; } diff --git a/tests/Feature/Screens/ProfileShareTest.php b/tests/Feature/Screens/ProfileShareTest.php index 29b35400..b0e08243 100644 --- a/tests/Feature/Screens/ProfileShareTest.php +++ b/tests/Feature/Screens/ProfileShareTest.php @@ -38,7 +38,7 @@ function shareMenuIds(TestableComponent $screen): ?array return $menu === null ? null : array_map(fn (array $item): string => $item['props']['id'], $menu['children'] ?? []); } -it("starts someone else's ⋯ with Share profile, under the phone's own share icon", function (string $platform, string $icon) { +it("starts someone else's ⋯ with Share profile, under share on iOS and share-2 on Android", function (string $platform, string $icon) { fakeApi(['GET /api/v1/users/ada' => [shareProfileJson()], 'GET /api/v1/users/ada/commits' => [pageJson([])]]); $screen = Native::visit('/log/users/ada', platform: $platform); @@ -47,22 +47,23 @@ function shareMenuIds(TestableComponent $screen): ?array expect(shareMenuIds($screen))->toBe(['share', 'mute', 'block']) ->and($share['props'])->toMatchArray(['label' => 'Share profile', 'icon' => $icon]); })->with([ - 'iOS' => ['ios', 'square.and.arrow.up'], - 'Android' => ['android', 'share'], + 'iOS' => ['ios', 'custom:share'], + 'Android' => ['android', 'custom:share-2'], ]); -it('puts Share profile first in the nav bar of your own profile, before Starred and Account, with no ⋯', function () { +it('puts Scheduled commits and Starred in the nav bar of your own profile, then a ⋯ with Share profile and Account', function () { fakeApi([ 'GET /api/v1/users/local_dev' => [shareProfileJson('local_dev', 'https://dev-null.sh/@local_dev')], 'GET /api/v1/users/local_dev/commits' => [pageJson([])], ]); $screen = Native::visit('/profile', platform: 'ios'); - $actions = findNodes($screen->tree(), fn (array $node): bool => ($node['type'] ?? null) === 'top_bar_action'); + $actions = array_map(fn (array $node): string => $node['props']['id'], findNodes($screen->tree(), fn (array $node): bool => ($node['type'] ?? null) === 'top_bar_action')); + $share = findNode($screen->tree(), fn (array $node): bool => ($node['props']['id'] ?? null) === 'share'); - expect(array_map(fn (array $node): string => $node['props']['id'], $actions))->toBe(['share', 'starred', 'account']) - ->and($actions[0]['props'])->toMatchArray(['icon' => 'square.and.arrow.up', 'a11y_label' => 'Share profile']) - ->and(shareMenuIds($screen))->toBeNull(); + expect(array_values(array_diff($actions, shareMenuIds($screen) ?? [])))->toBe(['scheduled', 'starred', 'more']) + ->and(shareMenuIds($screen))->toBe(['share', 'account']) + ->and($share['props'])->toMatchArray(['label' => 'Share profile', 'icon' => 'custom:share']); $screen->press('shareProfile') ->assertNativeCalled('Share.Url', fn (array $params): bool => $params['url'] === 'https://dev-null.sh/@local_dev') diff --git a/tests/Feature/Screens/ProfileTest.php b/tests/Feature/Screens/ProfileTest.php index ca6b090e..4589ce86 100644 --- a/tests/Feature/Screens/ProfileTest.php +++ b/tests/Feature/Screens/ProfileTest.php @@ -406,7 +406,7 @@ function adaCommitJson(array $overrides = []): array ->assertNativeCalled('Browser.OpenInApp', fn (array $params): bool => $params['url'] === 'https://github.com/settings/profile'); }); -it('opens your scheduled commits beneath the Profile tab from the calendar beside Edit on GitHub', function () { +it('opens your scheduled commits beneath the Profile tab from the calendar in your nav bar', function () { fakeApi([ 'GET /api/v1/users/local_dev' => [profileJson(user: ['id' => userJson()['id'], 'handle' => 'local_dev'])], 'GET /api/v1/users/local_dev/commits' => [pageJson([])], @@ -414,13 +414,17 @@ function adaCommitJson(array $overrides = []): array 'GET /api/v1/users/ada/commits' => [pageJson([])], ]); - expect(nodeByRef(Native::visit('/profile'), 'profile-scheduled')['props']['a11y_label'] ?? null)->toBe('Scheduled commits'); + $action = fn (TestableComponent $screen): ?array => findNode($screen->tree(), fn (array $node): bool => ($node['type'] ?? null) === 'top_bar_action' && ($node['props']['id'] ?? null) === 'scheduled'); - Native::visit('/profile')->tap('profile-scheduled')->assertNavigatedTo('/profile/scheduled'); - Native::visit('/profile/users/ada')->assertMissingElement('button', fn (array $node): bool => ($node['ref'] ?? null) === 'profile-scheduled'); + $screen = Native::visit('/profile', platform: 'ios'); + + expect($action($screen)['props'] ?? null)->toMatchArray(['icon' => 'custom:calendar-days', 'a11y_label' => 'Scheduled commits']) + ->and($action(Native::visit('/profile/users/ada')))->toBeNull(); + + $screen->press('openScheduledCommits')->assertNavigatedTo('/profile/scheduled'); }); -it('leads from your own profile to your account with the Settings2 icon', function () { +it('leads from the ⋯ on your own profile to your account with the Settings2 icon', function () { fakeApi([ 'GET /api/v1/users/local_dev' => [profileJson(user: ['handle' => 'local_dev'])], 'GET /api/v1/users/local_dev/commits' => [pageJson([])], @@ -432,7 +436,7 @@ function adaCommitJson(array $overrides = []): array $screen = Native::visit('/profile', platform: 'ios'); - expect($action($screen)['props'] ?? null)->toMatchArray(['icon' => 'custom:settings-2', 'a11y_label' => 'Account']) + expect($action($screen)['props'] ?? null)->toMatchArray(['icon' => 'custom:settings-2', 'label' => 'Account']) ->and($action(Native::visit('/profile', platform: 'android'))['props']['icon'] ?? null)->toBe('custom:settings-2') ->and($action(Native::visit('/log/users/ada')))->toBeNull();