forward

package
v0.0.19 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package forward is the destination picker raised by the forward action: a filtered list of chats with a live query, then a confirmation naming what is going where.

It is the palette's twin, like the attach picker before it — same 60-cell fixed width, same anchor, same `▌` marker, same key-hint footer, no buttons. The palette collects a command and the attach picker collects a path; this collects a chat.

It knows nothing about Telegram. The app supplies candidates, runs the search behind the query, and does the forwarding; this package owns the query, the filtering, the selection, the confirmation step, and the drawing. That is the same split that keeps the palette from importing the command registry.

Index

Constants

View Source
const Width = 60

Width is the overlay's fixed width in cells, matching the palette and the attach picker. Fixed rather than responsive for the same reason: this is a reading surface, and a 200-column terminal should not stretch a list of chat titles across the whole screen.

Variables

This section is empty.

Functions

This section is empty.

Types

type Action

type Action int

Action is what a keypress asked the app to do.

const (
	// ActionNone means the picker handled the key itself.
	ActionNone Action = iota
	// ActionQueryChanged means the query was edited: the app should run
	// its search and call SetResults.
	ActionQueryChanged
	// ActionForward means the confirmation was accepted. Read Source and
	// Destination.
	ActionForward
	// ActionCancel means the picker closed without forwarding.
	ActionCancel
)

type Chat

type Chat struct {
	ID int64
	// Title is the chat's display name.
	Title string
	// Sigil is the one-cell type mark drawn before the title, so a group
	// and a person are told apart the way they are in the chat list.
	Sigil string
	// Handle is the @username where there is one, shown right-aligned. It
	// is also matched against, which is what lets a stranger be reached by
	// the name they are searchable under.
	Handle string
	// Note is a short right-aligned qualifier for rows that are not from
	// the open dialog list, e.g. "not in your chats".
	Note string
}

Chat is one destination as the picker displays it. The app builds these from its store and from search results; the picker never interprets them beyond matching Title and Handle.

type Model

type Model struct {
	// contains filtered or unexported fields
}

Model is the destination picker overlay.

func New

func New(r theme.Roles) Model

func (*Model) Close

func (m *Model) Close()

Close hides the picker and drops everything it was holding, so reopening never resurrects last time's query, results, or target.

func (Model) Destination

func (m Model) Destination() (Chat, bool)

Destination returns the chat a forward would go to: the frozen choice once the confirmation is open, the highlighted row before that.

func (Model) IsVisible

func (m Model) IsVisible() bool

func (Model) Matches

func (m Model) Matches() []Chat

Matches returns the currently displayed rows, for tests and for callers that want to know whether anything matched.

func (*Model) Open

func (m *Model) Open(src Source, candidates []Chat)

Open shows the picker for one message, with an empty query and the caller's candidate list.

func (Model) Query

func (m Model) Query() string

func (*Model) SetResults

func (m *Model) SetResults(chats []Chat)

SetResults replaces the server-side matches. The app is responsible for dropping results whose query no longer matches Model.Query — a stale answer must never repopulate a list the reader has typed past.

Rows already in the local candidate list are skipped: contacts.search returns your own peers alongside global ones, and a chat listed twice looks like two different destinations.

func (*Model) SetSearchFailed

func (m *Model) SetSearchFailed()

SetSearchFailed notes that the server search did not answer. Local matches stay on screen: a picker that empties itself because the network blinked is worse than one that quietly lists less.

func (*Model) SetSearching

func (m *Model) SetSearching(searching bool)

SetSearching marks a server search in flight, so the status line can say so without the rows changing.

func (Model) Source

func (m Model) Source() Source

Source returns the message the picker was opened on. It cannot change while the picker is open.

func (Model) Step

func (m Model) Step() Step

func (Model) Update

func (m Model) Update(msg tea.KeyPressMsg) (Model, Action)

Update handles a keypress while the picker owns input.

Navigation is arrows only, for divergence 9's reason: this is a text surface, so j and k have to reach the query or a chat called "jack" could not be typed.

func (Model) View

func (m Model) View() string

View renders the overlay. Every line is exactly Width cells, so the caller can place it without the frame shearing.

type Source

type Source struct {
	ChatID    int64
	MessageID int64
	// Preview is a one-line rendering of the message, shown at the
	// confirmation step so the reader can see what they are about to send.
	Preview string
}

Source is the message being forwarded, captured when the picker opens.

It is captured rather than re-read because the picker is modal but the world is not: an update can arrive, the cursor can be moved by a mouse-driven scroll, and a source re-read at confirmation time would be whatever the cursor had drifted to. Forwarding the wrong message to a deliberately chosen destination is the worst failure this surface has.

type Step

type Step int

Step is which of the picker's two screens is showing.

const (
	// StepPick is the searchable destination list.
	StepPick Step = iota
	// StepConfirm names the destination and previews the message.
	StepConfirm
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL