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
7 changes: 6 additions & 1 deletion resources/views/native/partials/commit-sheet.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@

It's full height for the forms, and short for the receipts. It's always on the screen, shown or not, so iOS can
slide it away and say when it's gone.

The sheet and its field are keyed, so nothing drawn above the field (Commit turning on, the issue badge, a banner
behind the sheet) gives it a new id. iOS and Android would build a new field for a new id, and the keyboard would
go down with the old one.
--}}
<native:bottom-sheet ref="sheet" :visible="$sheetOpen" :detents="in_array($sheetView, ['reported', 'issued'], true) ? '0.3' : 'large'" @dismiss="commitSheetClosed">
<native:bottom-sheet native:key="commit-sheet" ref="sheet" :visible="$sheetOpen" :detents="in_array($sheetView, ['reported', 'issued'], true) ? '0.3' : 'large'" @dismiss="commitSheetClosed">
@if ($sheetCommit || in_array($sheetView, ['compose', 'issued'], true))
@if (in_array($sheetView, ['quote', 'compose', 'reply'], true))
@php($quoting = $sheetView === 'quote')
Expand Down Expand Up @@ -66,6 +70,7 @@
@include('native.partials.composer-preview')
@else
<native:bare-text-input
native:key="sheet-body"
ref="sheet-body"
class="w-full text-theme-ink"
:placeholder="$issueCommand?->placeholder() ?? match (true) { $quoting => 'Add your take', $replying => 'Reply to '.$sheetCommit->author->handle(), default => 'What did you ship?' }"
Expand Down
20 changes: 20 additions & 0 deletions tests/Feature/Screens/CommitDetailTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,26 @@ function fakeThread(array $commit = [], array $replies = [], array $extra = []):
->and(nodeByRef($screen, 'thread-scroll')['id'])->not->toBe($before);
});

it('keeps the reply field the same view while the thread above it changes, so the keyboard stays up', function () {
fakeThread(['replies_count' => 1], replies: [replyJson(['id' => 'r1', 'parent_id' => 'c2', 'body' => 'First reply', 'author' => userJson(['username' => 'grace', 'name' => 'Grace'])])]);

$screen = Native::visit('/log/commits/c2')->tap('earlier-commits')->input('reply-input', 'Nice');
$field = idsDownTo($screen->tree(), 'reply-input');

// Tapping into the bar, which folds the earlier commits away above it.
$screen->pressUp('reply-field')->assertDontSee('The parent commit');
expect(idsDownTo($screen->tree(), 'reply-input'))->toBe($field);

// More of the thread drawn above it.
$screen->set('repliesShown', true)->assertSee('First reply');
expect(idsDownTo($screen->tree(), 'reply-input'))->toBe($field);

// A refresh failing, which puts its banner at the top of the screen.
$screen->set('refreshFailed', true);
expect(nodeByRef($screen, 'refresh-failed'))->not->toBeEmpty()
->and(idsDownTo($screen->tree(), 'reply-input'))->toBe($field);
});

it('runs the rail from the folded ancestors into the commit, which shows its avatar', function () {
$ada = userJson(['id' => 'u2', 'username' => 'ada', 'name' => 'Ada', 'lane' => 1]);
fakeApi([
Expand Down
20 changes: 20 additions & 0 deletions tests/Feature/Screens/ComposeSheetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,26 @@ function sheetCommitDisabled(TestableComponent $screen): bool
->and(sheetButtonCircle($android, 'sheet-cancel')['props'])->not->toHaveKey('glass');
});

it('keeps the field the same view while what is drawn above it changes, so the keyboard stays up', function () {
$screen = composeSheetOnLog();
$field = idsDownTo($screen->tree(), 'sheet-body');

// Commit turning on, which wraps its circle in a pressable.
$screen->input('sheet-body', 'Shipped');
expect(sheetCommitDisabled($screen))->toBeFalse()
->and(idsDownTo($screen->tree(), 'sheet-body'))->toBe($field);

// The issue badge over the field.
$screen->input('sheet-body', '/feature Dark mode');
expect(nodeByRef($screen, 'sheet-open-issue'))->not->toBeEmpty()
->and(idsDownTo($screen->tree(), 'sheet-body'))->toBe($field);

// A refresh failing behind the sheet, which puts its banner above it.
$screen->set('refreshFailed', true);
expect(nodeByRef($screen, 'refresh-failed'))->not->toBeEmpty()
->and(idsDownTo($screen->tree(), 'sheet-body'))->toBe($field);
});

it('only commits from a sheet that has a composer in it', function () {
fakeApi(['GET /api/v1/log/following' => [pageJson([commitJson(['id' => 'c1', 'author' => userJson(['id' => 'u2', 'username' => 'ada'])])])]]);

Expand Down
14 changes: 14 additions & 0 deletions tests/Feature/Screens/ConversationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,20 @@ function serveRemoteThread(array $thread, ?array $poll = null, int $pushStatus =
->and(sentTo('PUT', 'remotes/@ada/read'))->toBe(['{"through":2}']);
});

it('keeps the composer the same view while messages arrive above it, so the keyboard stays up', function () {
serveRemoteThread(
threadJson([remoteMessageJson(['id' => 1, 'body' => 'first'])], ['read_through_id' => 1]),
threadJson([remoteMessageJson(['id' => 2, 'body' => 'second'])]),
);

$screen = finishRefreshes(Native::visit('/remotes/conversations/ada'))->input('message-input', 'on my way');
$field = idsDownTo($screen->tree(), 'message-input');

finishRefreshes($screen->call('poll'))->assertSee('second');

expect(idsDownTo($screen->tree(), 'message-input'))->toBe($field);
});

it('waits longer between polls once nobody has written or typed for a minute', function () {
serveRemoteThread(threadJson([remoteMessageJson(['id' => 1])], ['read_through_id' => 1]));

Expand Down
22 changes: 22 additions & 0 deletions tests/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,28 @@ function findNodes(array $node, callable $matches): array
return $found;
}

/**
* The ids of every node from the root down to the one with the given ref. iOS and Android know a view by its id at
* each level, so a new id anywhere on the way down builds a new field, and the keyboard goes with the old one.
*
* @param array<string, mixed> $node
* @return list<int>|null
*/
function idsDownTo(array $node, string $ref): ?array
{
if (($node['ref'] ?? null) === $ref) {
return [$node['id']];
}

foreach ($node['children'] ?? [] as $child) {
if (($ids = idsDownTo($child, $ref)) !== null) {
return [$node['id'], ...$ids];
}
}

return null;
}

/**
* The rendered node with the given ref.
*
Expand Down
Loading