Repository navigation
Expand file tree
/
Copy pathmessages_ui.go
More file actions
148 lines (127 loc) · 4.4 KB
/
Copy pathmessages_ui.go
File metadata and controls
148 lines (127 loc) · 4.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package main
import (
"context"
"errors"
"fmt"
"hash/fnv"
"net/http"
"strings"
"time"
"github.com/fossteams/teams-api/pkg/csa"
"github.com/rivo/tview"
)
func conversationPaneTitle(target ConversationTarget, syncedAt time.Time, status string) string {
if status == "" {
if syncedAt.IsZero() {
return target.Title
}
status = "live " + syncedAt.Format("15:04:05")
}
if target.Title == "" {
return status
}
return target.Title + " | " + status
}
func messagesSignature(messages []csa.ChatMessage) string {
hasher := fnv.New64a()
for _, message := range messages {
_, _ = hasher.Write([]byte(message.Id))
_, _ = hasher.Write([]byte{0})
_, _ = hasher.Write([]byte(message.Version))
_, _ = hasher.Write([]byte{0})
_, _ = hasher.Write([]byte(message.ImDisplayName))
_, _ = hasher.Write([]byte{0})
_, _ = hasher.Write([]byte(message.Content))
_, _ = hasher.Write([]byte{0})
_, _ = hasher.Write([]byte(time.Time(message.ComposeTime).Format(time.RFC3339Nano)))
_, _ = hasher.Write([]byte{'\n'})
}
return fmt.Sprintf("%x", hasher.Sum64())
}
func (s *AppState) renderConversationMessages(target ConversationTarget, messages []csa.ChatMessage, syncedAt time.Time, preserveSelection bool) {
chatList := s.components[ViChat].(*tview.List)
previousCount := chatList.GetItemCount()
previousIndex := chatList.GetCurrentItem()
followTail := previousCount > 0 && previousIndex >= previousCount-1
status := ""
if !syncedAt.IsZero() {
status = s.conversationSyncStatus(syncedAt)
}
chatList.Clear()
chatList.SetTitle(conversationPaneTitle(target, time.Time{}, status)).
SetBorder(true).
SetTitleAlign(tview.AlignCenter)
s.chatPaneFocusable = true
for _, message := range messages {
if message.ImDisplayName == "" {
continue
}
chatList.AddItem(textMessage(message.Content), message.ImDisplayName, 0, nil)
}
if chatList.GetItemCount() == 0 {
chatList.AddItem("No recent messages found", fmt.Sprintf("Loaded the latest %d messages for this conversation.", s.messageLimit), 0, nil)
}
if !preserveSelection || chatList.GetItemCount() == 0 {
return
}
switch {
case followTail:
chatList.SetCurrentItem(chatList.GetItemCount() - 1)
case previousIndex < chatList.GetItemCount():
chatList.SetCurrentItem(previousIndex)
default:
chatList.SetCurrentItem(chatList.GetItemCount() - 1)
}
}
func (s *AppState) updateConversationPaneTitle(target ConversationTarget, syncedAt time.Time, status string) {
chatList, ok := s.components[ViChat].(*tview.List)
if !ok {
return
}
if status == "" && !syncedAt.IsZero() {
status = s.conversationSyncStatus(syncedAt)
}
chatList.SetTitle(conversationPaneTitle(target, time.Time{}, status)).
SetBorder(true).
SetTitleAlign(tview.AlignCenter)
}
func (s *AppState) renderConversationLoadError(target ConversationTarget, err error) {
chatList, ok := s.components[ViChat].(*tview.List)
if !ok {
return
}
mainText, secondaryText := conversationLoadErrorText(err)
chatList.Clear()
chatList.SetTitle(conversationPaneTitle(target, time.Time{}, "load failed")).
SetBorder(true).
SetTitleAlign(tview.AlignCenter)
chatList.AddItem(mainText, secondaryText, 0, nil)
chatList.AddItem("Retry", "Press Right or l to retry this conversation. Esc returns to the list.", 0, nil)
s.chatPaneFocusable = true
}
func conversationLoadErrorText(err error) (string, string) {
switch {
case errors.Is(err, context.DeadlineExceeded):
return "Timed out while loading recent messages", "Teams did not respond before the request timeout. Press Right or l to retry."
case errors.Is(err, context.Canceled):
return "Loading was cancelled", "Move to another conversation or press Right or l to retry."
}
var statusErr *messageFetchStatusError
if errors.As(err, &statusErr) {
switch statusErr.StatusCode {
case http.StatusUnauthorized, http.StatusForbidden:
return "Teams rejected the request", "Your Teams tokens may have expired. Refresh them and try again."
case http.StatusTooManyRequests:
return "Teams is rate limiting requests", "Wait a few seconds, then press Right or l to retry."
default:
if statusErr.StatusCode >= http.StatusInternalServerError {
return "Teams returned a server error", "The service is temporarily unavailable. Press Right or l to retry."
}
}
}
message := strings.TrimSpace(err.Error())
if message == "" {
message = "An unexpected error occurred while loading recent messages."
}
return "Unable to load recent messages", message
}