diff --git a/app/Devnull/Stash.php b/app/Devnull/Stash.php
index 899db42..cd2ed6d 100644
--- a/app/Devnull/Stash.php
+++ b/app/Devnull/Stash.php
@@ -7,8 +7,8 @@
/**
* What you wrote and didn't send, per user, as the web's useStash keeps it.
*
- * Drafts go by the web's names: 'commit' for the Log's composer, which its
- * sheet shares, 'quote-{id}' for a quote of a commit and 'reply-{id}' for a
+ * Drafts go by the web's names: 'commit' for a new commit, from the compose
+ * sheet, 'quote-{id}' for a quote of a commit and 'reply-{id}' for a
* reply to one. A screen keeps a draft as its field changes, and forgets it
* once it's sent or the field is emptied. Signing out forgets them all (see
* Account::forget).
diff --git a/app/NativeComponents/Concerns/OpensCommitSheet.php b/app/NativeComponents/Concerns/OpensCommitSheet.php
index b6db9f0..d919c90 100644
--- a/app/NativeComponents/Concerns/OpensCommitSheet.php
+++ b/app/NativeComponents/Concerns/OpensCommitSheet.php
@@ -20,9 +20,9 @@
* your new commits puts it in place (see showCommitted()).
*
* What you write in it is stashed as you type, as on the web (see Stash): a
- * quote per commit quoted, and a new commit as the Log's composer's draft,
- * which the sheet shares, as the web's dialog does. Opened again, it starts
- * from the draft, marked as restored. It's forgotten once it's sent.
+ * quote per commit quoted, and a new commit under the web's composer draft.
+ * Opened again, it starts from the draft, marked as restored. It's forgotten
+ * once it's sent.
*
* The sheet keeps its commit while it slides away, so it doesn't empty on the
* way down, and lets go of it once iOS says it's gone, however it was closed.
@@ -202,22 +202,7 @@ public function hideCommitSheet(): void
*/
public function commitSheetClosed(): void
{
- $composed = $this->sheetView === 'compose';
-
$this->forgetCommitSheet();
-
- if ($composed) {
- $this->composeSheetClosed();
- }
- }
-
- /**
- * The sheet's new commit has gone, sent or stashed. The Log's composer shares its draft, so it takes it as the
- * sheet left it.
- */
- protected function composeSheetClosed(): void
- {
- //
}
/**
diff --git a/app/NativeComponents/Log.php b/app/NativeComponents/Log.php
index 294fe7f..43ed179 100644
--- a/app/NativeComponents/Log.php
+++ b/app/NativeComponents/Log.php
@@ -28,25 +28,19 @@
/**
* The log: the web's three of them, Following (you and the people you follow),
- * Latest (everyone, newest first) and Trending (ranked by stars and replies),
- * under a composer you type into where it is, as on the web.
+ * Latest (everyone, newest first) and Trending (ranked by stars and replies).
*
- * EDGE tells PHP nothing about focus, and gives PHP no way to focus a field
- * or scroll a list. So a touch on the field is taken as being in it, and a new
- * key on the list is how it gets back to the top.
+ * EDGE gives PHP no way to scroll a list, so a new key on the list is how it
+ * gets back to the top.
*
- * Nor does it say where a list is scrolled, so the nav bar's compose button
- * can't tell whether the composer is on screen. It always opens the composer
- * in a sheet (see OpensCommitSheet), which leaves the log where you were in
- * it, and the commit goes on top as one from the composer does.
- *
- * The composer's draft is stashed as you type, as on the web (see Stash), so
- * it's there again when the Log opens, marked as restored. The sheet shares
- * it, as the web's dialog does, and the composer takes what the sheet left.
+ * The Log has no composer of its own. The nav bar's compose button opens one
+ * in a sheet (see OpensCommitSheet), which leaves the log where you were in it,
+ * and the commit goes on top. A field in the list was redrawn with the whole
+ * log every time it synced, and fast typing lost and reordered letters.
*
* It opens on the log as it was last time, then asks devnull again in the
* background, and every 30 seconds while it's showing, as the web polls.
- * Commits newer than anything showing wait under the composer in the new
+ * Commits newer than anything showing wait at the top of the list in the new
* commits pill (see HoldsNewCommits), which says how many devnull counted
* since the newest one showing (?since=). Scrolled down, it pins under the tabs.
*
@@ -144,31 +138,8 @@ class Log extends NativeComponent
*/
public ?string $lastVisitAt = null;
- /**
- * The commit you're writing. It stays when the keyboard goes, until it's committed.
- */
- public string $body = '';
-
- /**
- * Whether the composer shows its budget and Commit button: from the moment you're
- * in it until you commit, or leave it empty and the log reloads.
- */
- public bool $composing = false;
-
- /**
- * Mounts a fresh field when it changes, so a committed field starts clean.
- */
- public int $composerKey = 0;
-
- /**
- * Whether the draft in the composer was stashed when it opened, which it marks until you empty it or drop it.
- */
- public bool $draftRestored = false;
-
public function mount(): void
{
- $this->body = app(Stash::class)->get($this->me()?->id, 'commit');
- $this->draftRestored = $this->body !== '';
$this->log = app(ShownTabs::class)->get($this->me()?->id, 'log', self::Logs, 'following');
$this->lastVisitAt = $this->lastVisitSeenAt();
$this->showKept();
@@ -181,7 +152,6 @@ public function mount(): void
public function onResume(): void
{
$this->forgetRefreshes();
- $this->closeEmptyComposer();
$this->forgetDeleted();
$this->forgetHidden();
$this->showKept();
@@ -323,8 +293,6 @@ public function isRanked(): bool
*/
public function load(): void
{
- $this->closeEmptyComposer();
-
if ($this->isRanked()) {
$this->rank();
@@ -432,88 +400,7 @@ public function followSuggested(string $username): void
}
/**
- * A touch on the composer's field puts you in it.
- */
- public function composerFocused(): void
- {
- $this->composing = true;
- }
-
- /**
- * Stashes the draft as it changes. An empty field forgets it, and isn't restored any more.
- */
- public function updatedBody(): void
- {
- app(Stash::class)->put($this->me()?->id, 'commit', $this->body);
-
- if ($this->body === '') {
- $this->draftRestored = false;
- }
- }
-
- /**
- * The web's drop, beside a restored draft: it's thrown away, and a fresh field takes its place.
- */
- public function dropStash(): void
- {
- $this->body = '';
- $this->updatedBody();
- $this->composerKey++;
- }
-
- public function remaining(): int
- {
- return Commit::MaxLength - mb_strlen($this->body);
- }
-
- public function canCommit(): bool
- {
- $length = mb_strlen(trim($this->body));
-
- return $length >= Commit::MinLength && $length <= Commit::MaxLength;
- }
-
- /**
- * Commits the draft and puts it at the top of the log. Pressing Commit has
- * already put the keyboard away. The draft stays if devnull says no.
- */
- public function commit(): void
- {
- if (! $this->canCommit()) {
- return;
- }
-
- $commit = $this->attempt(fn ($devnull) => $devnull->createCommit(trim($this->body)));
-
- if ($commit === null) {
- return;
- }
-
- $this->body = '';
- $this->updatedBody();
- $this->composing = false;
- $this->composerKey++;
- $this->showCommitted($commit);
- }
-
- /**
- * The sheet wrote to the composer's stash, so the composer takes the draft as the sheet left it, in a fresh field.
- */
- protected function composeSheetClosed(): void
- {
- $draft = app(Stash::class)->get($this->me()?->id, 'commit');
-
- if ($draft === $this->body) {
- return;
- }
-
- $this->body = $draft;
- $this->draftRestored = $draft !== '';
- $this->composerKey++;
- }
-
- /**
- * Puts a commit you just made at the top of the log, from the composer or the sheet.
+ * Puts a commit you just made in the compose sheet at the top of the log.
*/
protected function showCommitted(Commit $commit): void
{
@@ -546,20 +433,6 @@ protected function updateShownCommits(Closure $update): void
$this->threads = array_map(fn (array $replies): array => array_map($update, $replies), $this->threads);
}
- /**
- * An empty composer you've left closes again. Nothing says when you leave it,
- * so this waits for the log to reload: on returning to it, or pulling to refresh.
- */
- private function closeEmptyComposer(): void
- {
- if (! $this->composing || $this->body !== '') {
- return;
- }
-
- $this->composing = false;
- $this->composerKey++;
- }
-
/**
* The log as it was kept last time, if there is one. Coming back to it, it's
* laid over what's showing like any fresh page, so a change made elsewhere
@@ -772,9 +645,6 @@ public function render(): View
'myColour' => $this->myColour(),
'viewer' => $viewer,
'pasteis' => app(Account::class)->pasteis(),
- 'composerOpen' => $this->composing || $this->body !== '',
- 'remaining' => $this->remaining(),
- 'canCommit' => $this->canCommit(),
'stashed' => app(Stash::class)->names($viewer?->id),
'newCommitsLabel' => $this->newCommitsLabel(),
'pages' => $pages,
diff --git a/resources/views/native/partials/composer.blade.php b/resources/views/native/partials/composer.blade.php
deleted file mode 100644
index f9f60ae..0000000
--- a/resources/views/native/partials/composer.blade.php
+++ /dev/null
@@ -1,48 +0,0 @@
-{{--
- The top of the log, like the web's composer: your avatar staged on the rail beside "What did you ship?", which you
- type into where it is. Once you're in it, or it holds a draft, Commit sits under it, with the web's counter once
- few of the 4,000 characters are left. The ring is dashed until the commit is long enough to make, then solid.
-
- The field is keyed, so a new $composerKey (or a new list key) mounts a fresh one, clean after a commit. A touch on
- it is how the screen learns you're in it, since a field reports neither focus nor, on iOS, a caret in an empty
- field. The nav bar's compose button opens a composer in the sheet instead (see OpensCommitSheet).
-
- A draft stashed last time comes back in it, with the web's stash@{0} and drop on the left of Commit's row.
---}}
-
-
-
-
-
-
-
-
-
- @if ($composerOpen)
-
- @if ($draftRestored)
- @include('native.partials.stash-label', ['ref' => 'stash', 'drop' => 'dropStash'])
-
- @endif
- @include('native.partials.body-counter', ['body' => $body, 'ref' => 'budget'])
- Commit
-
- @endif
-
-
-
-
-
- @include('native.partials.rail', ['lane' => $me->lane(), 'user' => $me, 'node' => $canCommit ? 'avatar' : 'staged', 'tail' => 'staged', 'tailDashes' => $composerOpen ? 6 : 2])
-
diff --git a/resources/views/native/partials/log-page.blade.php b/resources/views/native/partials/log-page.blade.php
index 92092df..9dcfa7c 100644
--- a/resources/views/native/partials/log-page.blade.php
+++ b/resources/views/native/partials/log-page.blade.php
@@ -19,10 +19,6 @@ class="w-full flex-1 bg-theme-surface"
:on-end-reached="$page['showing'] ? 'loadMore' : null"
:end-reached-buffer="5"
>
- @if ($viewer)
- @include('native.partials.composer', ['me' => $viewer])
- @endif
-
@include('native.partials.new-commits-pill', ['label' => $newCommitsLabel, 'press' => 'showNewCommits', 'shown' => $page['showing'] && $newCommits !== []])
@if (! $page['loaded'] && $page['error'] === null)
diff --git a/tests/Feature/Screens/ComposeSheetTest.php b/tests/Feature/Screens/ComposeSheetTest.php
index 22547ad..f3f2aae 100644
--- a/tests/Feature/Screens/ComposeSheetTest.php
+++ b/tests/Feature/Screens/ComposeSheetTest.php
@@ -50,9 +50,8 @@ function sheetCommitDisabled(TestableComponent $screen): bool
'autofocus' => true,
'value' => '',
])
- // The same list, so it stays scrolled where it was, and the composer at its top stays as it was.
- ->and(nodeByRef($screen, 'log')['id'])->toBe($list)
- ->and($screen->instance()->composing)->toBeFalse();
+ // The same list, so it stays scrolled where it was.
+ ->and(nodeByRef($screen, 'log')['id'])->toBe($list);
});
it('keeps Commit disabled until the commit is long enough, and short enough', function () {
@@ -139,39 +138,6 @@ function sheetCommitDisabled(TestableComponent $screen): bool
Http::assertNotSent(fn (Request $request): bool => $request->method() === 'POST');
});
-it('shares its draft with the composer at the top of the log, as the web dialog does', function () {
- fakeApi([
- 'GET /api/v1/log/following' => [pageJson([])],
- 'POST /api/v1/commits' => [['data' => commitJson(['id' => 'mine', 'body' => 'Half a thought, shipped.'])], 201],
- ]);
-
- $screen = Native::visit('/log', platform: 'ios')
- ->input('body', 'Half a thought')
- ->press('composeInSheet');
-
- expect(nodeByRef($screen, 'sheet-body')['props']['value'])->toBe('Half a thought');
-
- // Closed, the composer takes the draft as the sheet left it.
- $screen->input('sheet-body', 'Half a thought, and more')
- ->dismissSheet('sheet');
-
- expect(nodeByRef($screen, 'body')['props']['value'])->toBe('Half a thought, and more')
- ->and(nodeByRef($screen, 'stash'))->not->toBeEmpty();
-
- // Sent from the sheet, it's gone from both.
- $screen->press('composeInSheet')
- ->input('sheet-body', 'Half a thought, shipped.')
- ->tap('sheet-commit')
- ->dismissSheet('sheet')
- ->assertSet('body', '');
-
- expect(nodeByRef($screen, 'body')['props']['value'])->toBe('');
-
- $screen->press('composeInSheet');
-
- expect(nodeByRef($screen, 'sheet-body')['props']['value'])->toBe('');
-});
-
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'])])])]]);
diff --git a/tests/Feature/Screens/ComposerTest.php b/tests/Feature/Screens/ComposerTest.php
deleted file mode 100644
index c99125e..0000000
--- a/tests/Feature/Screens/ComposerTest.php
+++ /dev/null
@@ -1,224 +0,0 @@
- 'http://devnull.test']);
- signIn();
-});
-
-function composerBudget(): Closure
-{
- return fn (string $text, string $color): Closure => fn (array $node): bool => ($node['ref'] ?? null) === 'budget'
- && ($node['text'] ?? $node['props']['text'] ?? null) === $text
- && ($node['props']['color'] ?? null) === config("native-ui.theme.light.{$color}");
-}
-
-function commitButton(bool $disabled): Closure
-{
- return fn (array $node): bool => ($node['ref'] ?? null) === 'commit' && ($node['props']['disabled'] ?? false) === $disabled;
-}
-
-/**
- * The composer's field, as the native side would mount it.
- *
- * @return array
- */
-function composerField(TestableComponent $screen): array
-{
- return nodeByRef($screen, 'body');
-}
-
-it('is a field at the top of the log, with no budget or Commit until you are in it', function () {
- Http::fake(['devnull.test/*' => Http::response(pageJson([commitJson()]))]);
-
- Native::visit('/log')
- ->assertElement('bare_text_input', fn (array $node): bool => ($node['ref'] ?? null) === 'body'
- && ($node['props']['placeholder'] ?? null) === 'What did you ship?'
- && ($node['props']['max_length'] ?? null) === 4000
- && ($node['props']['multiline'] ?? false)
- && ! isset($node['props']['autofocus']))
- ->assertMissingElement('text', fn (array $node): bool => ($node['ref'] ?? null) === 'budget')
- ->assertMissingElement('button', fn (array $node): bool => ($node['ref'] ?? null) === 'commit');
-});
-
-it('opens where it is when you tap into it, with a disabled Commit, no countdown yet, and no navigation', function () {
- Http::fake(['devnull.test/*' => Http::response(pageJson([commitJson()]))]);
-
- Native::visit('/log')
- ->pressUp('composer-field')
- ->assertNoNavigation()
- ->assertSet('composing', true)
- ->assertMissingElement('text', fn (array $node): bool => ($node['ref'] ?? null) === 'budget')
- ->assertElement('button', commitButton(disabled: true));
-});
-
-it('stays open once it has a draft', function () {
- Http::fake(['devnull.test/*' => Http::response(pageJson([commitJson()]))]);
-
- Native::visit('/log')
- ->input('body', 'Shipped it')
- ->assertElement('button', commitButton(disabled: false));
-});
-
-it('counts down like the web only once 200 characters are left, amber from 20 and red at none', function () {
- Http::fake(['devnull.test/*' => Http::response(pageJson([commitJson()]))]);
-
- Native::visit('/log')
- ->pressUp('composer-field')
- ->input('body', str_repeat('x', 3799))
- ->assertMissingElement('text', fn (array $node): bool => ($node['ref'] ?? null) === 'budget')
- ->input('body', str_repeat('x', 3800))
- ->assertElement('text', composerBudget()('200', 'ink-muted'))
- ->assertElement('text', fn (array $node): bool => ($node['ref'] ?? null) === 'budget' && ($node['props']['font_name'] ?? $node['props']['font'] ?? null) !== null)
- ->input('body', str_repeat('x', 3980))
- ->assertElement('text', composerBudget()('20', 'pending'))
- ->input('body', str_repeat('x', 4000))
- ->assertElement('text', composerBudget()('0', 'destructive'))
- ->assertElement('button', commitButton(disabled: false));
-});
-
-it('shows the fold icon once a commit is long enough for lists to show only its start', function () {
- Http::fake(['devnull.test/*' => Http::response(pageJson([commitJson()]))]);
-
- $fold = fn (array $node): bool => ($node['ref'] ?? null) === 'budget-fold' && ($node['props']['name'] ?? null) === 'custom:fold-vertical';
-
- Native::visit('/log', platform: 'ios')
- ->pressUp('composer-field')
- ->input('body', str_repeat('x', 280))
- ->assertMissingElement('icon', $fold)
- ->input('body', str_repeat('x', 281))
- ->assertElement('icon', $fold)
- ->input('body', implode("\n", range(1, 9)))
- ->assertElement('icon', $fold);
-});
-
-it('enables Commit once the commit is long enough', function () {
- Http::fake(['devnull.test/*' => Http::response(pageJson([commitJson()]))]);
-
- Native::visit('/log')
- ->pressUp('composer-field')
- ->input('body', 'hi')
- ->assertElement('button', commitButton(disabled: true))
- ->input('body', ' hi ')
- ->assertElement('button', commitButton(disabled: true))
- ->input('body', 'hi!')
- ->assertElement('button', commitButton(disabled: false));
-});
-
-it('stages your avatar on the rail in a dashed ring, solid once the commit is long enough', function () {
- Http::fake(['devnull.test/*' => Http::response(pageJson([commitJson(['author' => userJson(['id' => 'u2', 'username' => 'ada', 'lane' => 1])])]))]);
-
- $lane = config('native-ui.theme.light.lane-3');
- $solidRing = fn (array $node): bool => ($node['type'] ?? null) === 'stack' && ($node['layout']['width'] ?? null) === 48.0 && ($node['style']['bg_color'] ?? null) === $lane;
- $stagedRing = fn (array $node): bool => ($node['ref'] ?? null) === 'staged-avatar';
-
- Native::visit('/log')
- ->assertElement('column', $stagedRing)
- ->assertMissingElement('stack', $solidRing)
- ->input('body', 'hi')
- ->assertElement('column', $stagedRing)
- ->input('body', 'hi!')
- ->assertMissingElement('column', $stagedRing)
- ->assertElement('stack', $solidRing);
-});
-
-it('commits without leaving the log, then clears the composer and puts the commit at the top', function () {
- fakeApi([
- 'GET /api/v1/log/following' => [pageJson([commitJson(['id' => 'c1', 'body' => 'An older commit', 'author' => userJson(['id' => 'u2', 'username' => 'ada'])])])],
- 'POST /api/v1/commits' => [['data' => commitJson(['id' => '0199e5c4-1111-7000-8000-00000000beef', 'body' => 'Shipped the mobile app.'])], 201],
- ]);
-
- $screen = Native::visit('/log')->pressUp('composer-field')->input('body', ' Shipped the mobile app. ');
- $draftField = composerField($screen)['id'];
-
- $screen->tap('commit')
- ->assertNoNavigation()
- ->assertNativeNotCalled('Dialog.Toast')
- ->assertSet('sheetOpen', false)
- ->assertSet('body', '')
- ->assertSet('composing', false)
- ->assertSee('Shipped the mobile app.')
- ->assertMissingElement('text', fn (array $node): bool => ($node['ref'] ?? null) === 'budget')
- ->assertMissingElement('button', fn (array $node): bool => ($node['ref'] ?? null) === 'commit');
-
- // A fresh, empty field: the committed one is gone, so the next tap into it opens it again.
- expect(composerField($screen)['id'])->not->toBe($draftField)
- ->and(composerField($screen)['props']['value'] ?? null)->toBe('')
- ->and(array_map(fn ($commit): string => $commit->id, $screen->instance()->commits))->toBe(['0199e5c4-1111-7000-8000-00000000beef', 'c1']);
-
- $json = json_encode($screen->tree());
- expect(strpos($json, 'commit-0199e5c4-1111-7000-8000-00000000beef'))->toBeLessThan(strpos($json, 'commit-c1'));
-
- Http::assertSent(fn (Request $request): bool => $request->method() === 'POST' && $request->body() === '{"body":"Shipped the mobile app."}');
-});
-
-it('marks the new commit as pending on its row', function () {
- fakeApi([
- 'GET /api/v1/log/following' => [pageJson([])],
- 'POST /api/v1/commits' => [['data' => commitJson(['id' => 'abc1234', 'body' => 'Needs a look first', 'status' => 'pending'])], 201],
- ]);
-
- Native::visit('/log')
- ->input('body', 'Needs a look first')
- ->tap('commit')
- ->assertNativeNotCalled('Dialog.Toast')
- ->assertNoNavigation()
- ->assertSee('Needs a look first')
- ->assertSee('pending');
-});
-
-it('keeps the draft and shows the reason when the server says no', function () {
- fakeApi([
- 'GET /api/v1/log/following' => [pageJson([])],
- 'POST /api/v1/commits' => [['message' => 'Too Many Attempts.'], 429],
- ]);
-
- Native::visit('/log')
- ->input('body', 'One more')
- ->tap('commit')
- ->assertSee('Too Many Attempts.')
- ->assertSet('body', 'One more')
- ->assertElement('button', commitButton(disabled: false))
- ->assertNoNavigation();
-});
-
-it('sends nothing shorter than three characters', function () {
- fakeApi(['GET /api/v1/log/following' => [pageJson([])]]);
-
- Native::visit('/log')->input('body', 'ok')->call('commit')->assertSet('body', 'ok');
-
- Http::assertNotSent(fn (Request $request): bool => $request->method() === 'POST');
-});
-
-it('syncs the draft on a short debounce so a burst of typing arrives whole', function () {
- Http::fake(['devnull.test/*' => Http::response(pageJson([]))]);
-
- // In live mode a burst of keystrokes can overflow the native event queue,
- // and the input never resends the dropped tail. Debounce sends one trailing value.
- Native::visit('/log')->assertElement('bare_text_input', fn (array $node): bool => ($node['ref'] ?? null) === 'body'
- && ($node['props']['sync_mode'] ?? null) === 'debounce'
- && ($node['props']['debounce_ms'] ?? null) === 150);
-});
-
-it('closes an empty composer when the log reloads, and keeps one with a draft open', function () {
- Http::fake(['devnull.test/*' => Http::response(pageJson([]))]);
-
- Native::visit('/log')
- ->pressUp('composer-field')
- ->assertSet('composing', true)
- ->call('load')
- ->assertSet('composing', false)
- ->assertMissingElement('button', fn (array $node): bool => ($node['ref'] ?? null) === 'commit');
-
- Native::visit('/log')
- ->pressUp('composer-field')
- ->input('body', 'Still thinking')
- ->call('load')
- ->assertSet('composing', true)
- ->assertSet('body', 'Still thinking')
- ->assertElement('button', commitButton(disabled: false));
-});
diff --git a/tests/Feature/Screens/DraftStashTest.php b/tests/Feature/Screens/DraftStashTest.php
index fc50ea6..53941ce 100644
--- a/tests/Feature/Screens/DraftStashTest.php
+++ b/tests/Feature/Screens/DraftStashTest.php
@@ -72,79 +72,19 @@ function stashLabel(TestableComponent $screen, string $ref): array
// ── The Log's composer ─────────────────────────
-it("stashes the Log's composer as you type, and brings it back marked as restored when the Log opens again", function () {
- serveStashLog();
-
- $screen = Native::visit('/log', platform: 'ios')->input('body', 'Half a thought');
-
- expect(stashed('commit'))->toBe('Half a thought')
- ->and(showsRef($screen, 'stash'))->toBeFalse();
-
- $screen = Native::visit('/log', platform: 'ios');
-
- expect(nodeByRef($screen, 'body')['props']['value'])->toBe('Half a thought')
- ->and(stashLabel($screen, 'stash'))->toBe(['stash@{0}', 'Discard the restored draft'])
- ->and(nodeByRef($screen, 'commit')['props']['disabled'] ?? false)->toBeFalse();
-});
-
-it("forgets the composer's stash once the commit is made", function () {
- serveStashLog();
- stash('commit', 'Sent.');
-
- $screen = Native::visit('/log', platform: 'ios')
- ->tap('commit')
- ->assertSet('body', '');
-
- expect(stashed('commit'))->toBe('')
- ->and(showsRef($screen, 'stash'))->toBeFalse();
-
- expect(nodeByRef(Native::visit('/log', platform: 'ios'), 'body')['props']['value'])->toBe('');
-});
-
-it('forgets the stash when the field is emptied, and stops marking the draft as restored', function () {
- serveStashLog();
- stash('commit', 'Half a thought');
-
- $screen = Native::visit('/log', platform: 'ios')->input('body', '');
-
- expect(stashed('commit'))->toBe('')
- ->and(showsRef($screen, 'stash'))->toBeFalse();
-
- $screen->input('body', 'A new thought');
-
- expect(stashed('commit'))->toBe('A new thought')
- ->and(showsRef($screen, 'stash'))->toBeFalse();
-});
-
-it('drops a restored draft, leaving a fresh empty field', function () {
- serveStashLog();
- stash('commit', 'Half a thought');
-
- $screen = Native::visit('/log', platform: 'ios')
- ->tap('stash-drop')
- ->assertSet('body', '')
- ->assertSet('composerKey', 1);
-
- expect(stashed('commit'))->toBe('')
- ->and(nodeByRef($screen, 'body')['props']['value'])->toBe('')
- ->and(showsRef($screen, 'stash'))->toBeFalse();
-
- Http::assertNotSent(fn (Request $request): bool => $request->method() === 'POST');
-});
-
it('keeps your drafts to you', function () {
serveStashLog();
app(Stash::class)->put('u2', 'commit', "Ada's draft");
- $screen = Native::visit('/log', platform: 'ios');
+ $screen = Native::visit('/log', platform: 'ios')->press('composeInSheet');
- expect(nodeByRef($screen, 'body')['props']['value'])->toBe('')
- ->and(showsRef($screen, 'stash'))->toBeFalse();
+ expect(nodeByRef($screen, 'sheet-body')['props']['value'])->toBe('')
+ ->and(showsRef($screen, 'sheet-stash'))->toBeFalse();
});
// ── The compose sheet ──────────────────────────
-it("stashes the compose sheet as the composer's draft, and forgets it once it's committed", function () {
+it("stashes the compose sheet's draft, and forgets it once it's committed", function () {
serveStashLog();
$screen = Native::visit('/log', platform: 'ios')
@@ -153,21 +93,19 @@ function stashLabel(TestableComponent $screen, string $ref): array
expect(stashed('commit'))->toBe('Written in the sheet');
- $screen->dismissSheet('sheet');
-
- expect(nodeByRef($screen, 'body')['props']['value'])->toBe('Written in the sheet')
- ->and(nodeByRef($screen, 'stash'))->not->toBeEmpty();
-
- $screen->press('composeInSheet');
+ $screen->dismissSheet('sheet')->press('composeInSheet');
expect(nodeByRef($screen, 'sheet-body')['props']['value'])->toBe('Written in the sheet')
->and(stashLabel($screen, 'sheet-stash'))->toBe(['stash@{0}', 'Discard the restored draft']);
$screen->tap('sheet-commit')->dismissSheet('sheet');
- expect(stashed('commit'))->toBe('')
- ->and(nodeByRef($screen, 'body')['props']['value'])->toBe('')
- ->and(showsRef($screen, 'stash'))->toBeFalse();
+ expect(stashed('commit'))->toBe('');
+
+ $screen->press('composeInSheet');
+
+ expect(nodeByRef($screen, 'sheet-body')['props']['value'])->toBe('')
+ ->and(showsRef($screen, 'sheet-stash'))->toBeFalse();
});
it("drops the sheet's restored draft", function () {
@@ -181,10 +119,6 @@ function stashLabel(TestableComponent $screen, string $ref): array
expect(stashed('commit'))->toBe('')
->and(showsRef($screen, 'sheet-stash'))->toBeFalse();
-
- $screen->dismissSheet('sheet');
-
- expect(nodeByRef($screen, 'body')['props']['value'])->toBe('');
});
// ── Quotes ─────────────────────────────────────
diff --git a/tests/Feature/Screens/LaneButtonsTest.php b/tests/Feature/Screens/LaneButtonsTest.php
index d3640f9..e01a4e0 100644
--- a/tests/Feature/Screens/LaneButtonsTest.php
+++ b/tests/Feature/Screens/LaneButtonsTest.php
@@ -53,7 +53,6 @@ function laneLog(): TestableComponent
}
dataset('main buttons', [
- 'Commit, in the composer' => [fn (): TestableComponent => laneLog()->pressUp('composer-field'), 'commit'],
'Commit, in the compose sheet' => [fn (): TestableComponent => laneLog()->press('composeInSheet'), 'sheet-commit'],
'Commit, quoting a commit' => [fn (): TestableComponent => laneLog()->tap('quote-c1'), 'sheet-commit'],
'Send report' => [fn (): TestableComponent => laneLog()->tap('report-c1'), 'send-report'],
@@ -180,8 +179,7 @@ function laneLog(): TestableComponent
expect(buttonProps($report, 'report-done')['size'] ?? null)->toBe('lg');
- expect(buttonProps(laneLog()->pressUp('composer-field'), 'commit'))->not->toHaveKey('size')
- ->and(buttonProps(laneLog()->tap('quote-c1'), 'sheet-commit'))->not->toHaveKey('size');
+ expect(buttonProps(laneLog()->tap('quote-c1'), 'sheet-commit'))->not->toHaveKey('size');
});
it('makes Unlink Apple ID large too', function () {
diff --git a/tests/Feature/Screens/LogCacheTest.php b/tests/Feature/Screens/LogCacheTest.php
index e1e7e13..641417b 100644
--- a/tests/Feature/Screens/LogCacheTest.php
+++ b/tests/Feature/Screens/LogCacheTest.php
@@ -284,7 +284,7 @@ function shownCommitIds(TestableComponent $screen): array
'POST /api/v1/commits' => [['data' => commitJson(['id' => 'c9', 'body' => 'Just shipped'])], 201],
]);
- Native::visit('/log')->pressUp('composer-field')->input('body', 'Just shipped')->tap('commit');
+ Native::visit('/log')->press('composeInSheet')->input('sheet-body', 'Just shipped')->tap('sheet-commit');
holdRefreshes();
@@ -322,7 +322,7 @@ function shownCommitIds(TestableComponent $screen): array
$commit->goBack()->assertDontSee('Delete me')->assertSee('Keep me');
});
-it('puts the pill in the list between the composer and the commits, in a header that pins, there before it shows', function () {
+it('puts the pill at the top of the list, above the commits, in a header that pins, there before it shows', function () {
holdRefreshes();
keep('log/following', pageJson([commitJson(['id' => 'c1'])]));
fakeFreshLog([commitJson(['id' => 'c2', 'body' => 'Newer']), commitJson(['id' => 'c1'])]);
@@ -334,11 +334,11 @@ function shownCommitIds(TestableComponent $screen): array
return array_map(fn (array $node): string => $node['ref'] ?? $node['type'], $parent['children']);
};
- $inTheList = fn (): array => array_slice(array_values(array_filter(childRefs($screen, 'log'), fn (?string $ref): bool => $ref !== null && ! str_starts_with($ref, 'day-'))), 0, 3);
+ $inTheList = fn (): array => array_slice(array_values(array_filter(childRefs($screen, 'log'), fn (?string $ref): bool => $ref !== null && ! str_starts_with($ref, 'day-'))), 0, 2);
// Hidden and empty until there are new commits, but already in its place, so showing it never changes the list's shape.
expect($besideTheList())->toBe(['log'])
- ->and($inTheList())->toBe(['composer', 'new-commits-slot', 'commit-c1'])
+ ->and($inTheList())->toBe(['new-commits-slot', 'commit-c1'])
->and(nodeByRef($screen, 'new-commits-slot')['type'])->toBe('sticky_header')
->and(nodeByRef($screen, 'new-commits-slot')['layout']['display'] ?? null)->toBe(1)
->and(nodeByRef($screen, 'new-commits-slot')['children'] ?? [])->toBe([]);
@@ -346,7 +346,7 @@ function shownCommitIds(TestableComponent $screen): array
finishRefreshes($screen)->assertSee('1 new commit');
expect($besideTheList())->toBe(['log'])
- ->and($inTheList())->toBe(['composer', 'new-commits-slot', 'commit-c1'])
+ ->and($inTheList())->toBe(['new-commits-slot', 'commit-c1'])
->and(nodeByRef($screen, 'new-commits-slot')['layout']['display'] ?? null)->toBeNull()
->and(findNode(nodeByRef($screen, 'new-commits-slot'), fn (array $node): bool => ($node['ref'] ?? null) === 'new-commits'))->not->toBeNull();
});
diff --git a/tests/Feature/Screens/LogTest.php b/tests/Feature/Screens/LogTest.php
index 2fba789..51e78c4 100644
--- a/tests/Feature/Screens/LogTest.php
+++ b/tests/Feature/Screens/LogTest.php
@@ -502,18 +502,6 @@
->assertNoNavigation();
});
-it('starts the log with your avatar staged on the rail, like the web composer', function () {
- Http::fake(['devnull.test/*' => Http::response(pageJson([commitJson()]))]);
-
- $screen = Native::visit('/log')->assertSee('What did you ship?');
-
- // A dashed ring in your lane: sixteen short dashes, each turned to follow the circle.
- $dashes = findNodes(nodeByRef($screen, 'staged-avatar'), fn (array $node): bool => isset($node['props']['rotate']));
-
- expect($dashes)->toHaveCount(16)
- ->and(array_unique(array_map(fn (array $dash): ?string => $dash['style']['bg_color'] ?? null, $dashes)))->toBe([config('native-ui.theme.light.lane-3')]);
-});
-
it('shows an author once per run, and starts a new run at each day', function () {
$this->travelTo(now()->setDate(2026, 10, 15)->setTime(12, 0));
$ada = userJson(['id' => 'u2', 'username' => 'ada', 'name' => 'Ada', 'lane' => 1]);
diff --git a/tests/Feature/Screens/LogsTest.php b/tests/Feature/Screens/LogsTest.php
index 1932011..57f87a3 100644
--- a/tests/Feature/Screens/LogsTest.php
+++ b/tests/Feature/Screens/LogsTest.php
@@ -305,7 +305,7 @@ function ada(): array
$screen = Native::visit('/log');
$row = nodeByRef($screen, 'commit-c1')['id'];
- $screen->pressUp('composer-field')->input('body', 'Fresh from the composer')->tap('commit');
+ $screen->press('composeInSheet')->input('sheet-body', 'Fresh from the composer')->tap('sheet-commit');
expect(shownLogIds($screen))->toBe(['mine', 'c1'])
->and(nodeByRef($screen, 'commit-c1')['id'])->toBe($row);
@@ -527,9 +527,9 @@ function ada(): array
]);
$screen = tapLogTab(Native::visit('/log'), 'trending')
- ->pressUp('composer-field')
- ->input('body', 'Fresh from the composer')
- ->tap('commit');
+ ->press('composeInSheet')
+ ->input('sheet-body', 'Fresh from the composer')
+ ->tap('sheet-commit');
expect(shownLogIds($screen))->toBe(['mine', 'a'])
->and(dayMarkers($screen))->toBe([Timeline::JustNow, Timeline::TopCommits])