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
5 changes: 5 additions & 0 deletions src/toad/transcript_preparation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field, replace
from itertools import islice
from pathlib import Path
from typing import TYPE_CHECKING, ClassVar
from weakref import ReferenceType, ref

Expand Down Expand Up @@ -507,6 +508,10 @@ class ReplaceablePageSource(PreparedPageSource):
@abstractmethod
def replacement(self, through) -> ReplaceablePageSource: ...

def reads_from(self, root: str | Path) -> bool:
"""Whether this reader reads a conversation from ``root``; a closed one reads none."""
return False


class SavedHistory(ReplaceablePageSource):
"""Prepared pages for a snapshot; storage and work limits belong to the runtime.
Expand Down
13 changes: 8 additions & 5 deletions src/toad/wire_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,15 +439,15 @@ async def execute(self, runtime, key):

@dataclass(frozen=True)
class ChannelDestination(BusDestination):
"""A channel or inbox page: shown while its view still reads this conversation from this source."""
"""A channel or inbox page: shown while its view still reads this conversation from this
root. A page reader the view replaces (a refresh, the next page) still shows it."""

source: WirePageBuffer
view: CommsChatView

def shown(self) -> bool:
view = self.view
return view.is_attached and self.source is view.message_history.page_source and (
self.conversation == view.conversation)
return view.is_attached and self.conversation == view.conversation and (
view.message_history.page_source.reads_from(self.root))

async def presented(self, sent: MessageReference) -> None:
await self.view.show_sent(sent)
Expand All @@ -467,6 +467,9 @@ def __init__(self, *args, revision: ConversationObservation, **kwargs):
def replacement(self, through) -> WirePageBuffer:
return type(self)(self.loader, through, self.runtime, revision=self.revision)

def reads_from(self, root: str | Path) -> bool:
return self.loader.root == root

def uses_observation(self, observation: Observation) -> bool:
return observation.observes(self.loader.root)

Expand Down Expand Up @@ -502,7 +505,7 @@ def begin_submission(
self, view: CommsChatView, owner: SubmissionOwner, submission: SubmittedText
) -> SubmissionStart:
"""A channel or inbox message queues behind the view's earlier ones; compose stays open."""
return owner.start_bus(submission, ChannelDestination(self.loader.root, self.loader.conversation, self, view))
return owner.start_bus(submission, ChannelDestination(self.loader.root, self.loader.conversation, view))

def schedule_refresh(self, view: CommsChatView):
history = view.message_history
Expand Down
3 changes: 1 addition & 2 deletions tests/textual/test_stale_route_writes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ async def test_a_view_that_changed_before_its_send_left_keeps_the_draft_editable
messaging = workspace.comms.messaging
before = messaging.bus.bus.revision()
# Built for a conversation the view no longer shows: the view moved on before the send left.
stale = ChannelDestination(workspace.roots.wire, ChannelConversation(ChannelTarget("#elsewhere")),
view.message_history.page_source, view)
stale = ChannelDestination(workspace.roots.wire, ChannelConversation(ChannelTarget("#elsewhere")), view)
await view.submissions.start_bus(OrdinaryInputSubmission("STALE_DRAFT"), stale).prepare()
await ui.until(lambda: view.prompt.text == "STALE_DRAFT", why="the refused draft back in the composer")
assert not view.submissions.delivering
Expand Down
Loading