Skip to content

Commit b29bc6a

Browse files
committed
Timeline UX: reverse sort, search, right-justified timestamps
- r key reverses chronological order (newest/oldest first) with breadcrumb indicator - / key opens search in timeline view - Timestamps right-justified on the header line (sender left, time right) - ListConversationMessages now respects TextFilter.SortDirection - Updated footer keybindings to show new keys
1 parent 3ce0795 commit b29bc6a

4 files changed

Lines changed: 43 additions & 8 deletions

File tree

internal/query/sqlite_text.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ func textMsgTypeFilterAlias(alias string) string {
5151
return alias + ".message_type IN ('whatsapp','imessage','sms','google_voice_text')"
5252
}
5353

54+
func sqliteDirection(d SortDirection) string {
55+
if d == SortAsc {
56+
return "ASC"
57+
}
58+
return "DESC"
59+
}
60+
5461
// buildSQLiteTextFilterConditions builds WHERE conditions from a TextFilter.
5562
// All conditions use the m. prefix for the messages table.
5663
func buildSQLiteTextFilterConditions(filter TextFilter) (string, []interface{}) {
@@ -387,9 +394,9 @@ func (e *SQLiteEngine) ListConversationMessages(
387394
LEFT JOIN conversations c ON c.id = m.conversation_id
388395
LEFT JOIN message_bodies mb ON mb.message_id = m.id
389396
WHERE %s
390-
ORDER BY m.sent_at ASC
397+
ORDER BY m.sent_at %s
391398
LIMIT ? OFFSET ?
392-
`, where)
399+
`, where, sqliteDirection(filter.SortDirection))
393400

394401
args = append(args, limit, filter.Pagination.Offset)
395402

internal/textimport/integration_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,9 @@ func TestIntegration(t *testing.T) {
330330
}
331331

332332
// ListConversationMessages — returns messages for conv1 in chronological order.
333-
messages, err := te.ListConversationMessages(ctx, conv1ID, query.TextFilter{})
333+
messages, err := te.ListConversationMessages(ctx, conv1ID, query.TextFilter{
334+
SortDirection: query.SortAsc,
335+
})
334336
if err != nil {
335337
t.Fatalf("ListConversationMessages(conv1): %v", err)
336338
}

internal/tui/text_keys.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,24 @@ func (m Model) handleTextTimelineKeys(
112112
msg tea.KeyMsg,
113113
) (tea.Model, tea.Cmd) {
114114
switch msg.String() {
115+
case "r":
116+
// Reverse chronological order
117+
if m.textState.filter.SortDirection == query.SortAsc {
118+
m.textState.filter.SortDirection = query.SortDesc
119+
} else {
120+
m.textState.filter.SortDirection = query.SortAsc
121+
}
122+
m.textState.cursor = 0
123+
m.textState.scrollOffset = 0
124+
m.loading = true
125+
return m, m.loadTextMessages()
126+
127+
case "/":
128+
m.inlineSearchActive = true
129+
m.searchInput.Reset()
130+
m.searchInput.Focus()
131+
return m, nil
132+
115133
case "esc", "backspace":
116134
return m.textGoBack()
117135

internal/tui/text_view.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,11 @@ func (m Model) textBreadcrumb() string {
8585
textDrillKey(m),
8686
)
8787
case textLevelTimeline:
88-
return fmt.Sprintf(
89-
"Timeline (conv %d)", m.textState.selectedConvID,
90-
)
88+
order := "\u2191 oldest first"
89+
if m.textState.filter.SortDirection == query.SortDesc {
90+
order = "\u2193 newest first"
91+
}
92+
return fmt.Sprintf("Timeline %s", order)
9193
}
9294
return ""
9395
}
@@ -461,7 +463,12 @@ func (m Model) textTimelineView() string {
461463
from = "Unknown"
462464
}
463465
timeStr := msg.SentAt.Format("2006-01-02 15:04")
464-
headerLine := fmt.Sprintf("%s %s", from, timeStr)
466+
// Right-justify timestamp: sender on left, time on right
467+
gap := bodyWidth - len(from) - len(timeStr)
468+
if gap < 2 {
469+
gap = 2
470+
}
471+
headerLine := from + strings.Repeat(" ", gap) + timeStr
465472

466473
allLines = append(allLines, chatLine{
467474
text: headerLine, msgIdx: i, isFirst: true,
@@ -615,7 +622,8 @@ func (m Model) textFooterView() string {
615622

616623
case textLevelTimeline:
617624
keys = []string{
618-
"\u2191/\u2193 navigate", "Esc back",
625+
"\u2191/\u2193 navigate", "r reverse",
626+
"/ search", "Esc back",
619627
"m email", "? help",
620628
}
621629
n := len(m.textState.messages)

0 commit comments

Comments
 (0)