1
1
use std:: sync:: Arc ;
2
2
3
3
use imbl:: Vector ;
4
- use matrix_sdk:: ruma:: events:: room:: message:: MessageType ;
4
+ use matrix_sdk:: ruma:: { events:: room:: message:: MessageType , UserId } ;
5
5
use matrix_sdk_ui:: timeline:: {
6
- MembershipChange , MsgLikeContent , MsgLikeKind , TimelineDetails , TimelineItem ,
7
- TimelineItemContent , TimelineItemKind , VirtualTimelineItem ,
6
+ MembershipChange , Message , MsgLikeContent , MsgLikeKind , RoomMembershipChange , ThreadSummary ,
7
+ TimelineDetails , TimelineItem , TimelineItemContent , TimelineItemKind , VirtualTimelineItem ,
8
8
} ;
9
9
use ratatui:: { prelude:: * , widgets:: * } ;
10
10
@@ -27,147 +27,10 @@ impl StatefulWidget for &mut TimelineView<'_> {
27
27
where
28
28
Self : Sized ,
29
29
{
30
- let mut content: Vec < ListItem < ' _ > > = Vec :: new ( ) ;
31
-
32
- for item in self . items . iter ( ) {
33
- match item. kind ( ) {
34
- TimelineItemKind :: Event ( ev) => {
35
- let sender = ev. sender ( ) ;
36
-
37
- match ev. content ( ) {
38
- TimelineItemContent :: MsgLike ( MsgLikeContent {
39
- kind : MsgLikeKind :: Message ( message) ,
40
- ..
41
- } ) => {
42
- if ev. content ( ) . thread_root ( ) . is_some ( ) {
43
- continue ;
44
- }
45
-
46
- if let MessageType :: Text ( text) = message. msgtype ( ) {
47
- let mut lines = Vec :: new ( ) ;
48
- let first_line = Line :: from ( format ! ( "{}: {}" , sender, text. body) ) ;
49
-
50
- lines. push ( first_line) ;
51
-
52
- if let Some ( thread_summary) = ev. content ( ) . thread_summary ( ) {
53
- match thread_summary. latest_event {
54
- TimelineDetails :: Unavailable => { }
55
- TimelineDetails :: Pending => { }
56
- TimelineDetails :: Ready ( e) => {
57
- let sender = e. sender ;
58
- let content =
59
- e. content . as_message ( ) . map ( |m| m. msgtype ( ) ) ;
60
-
61
- if let Some ( MessageType :: Text ( text) ) = content {
62
- let replies = if thread_summary. num_replies == 1 {
63
- "1 reply" . to_owned ( )
64
- } else {
65
- format ! ( "{} replies" , {
66
- thread_summary. num_replies
67
- } )
68
- } ;
69
- let thread_line = Line :: from ( format ! (
70
- " 💬 {replies} {sender}: {}" ,
71
- text. body
72
- ) ) ;
73
-
74
- lines. push ( thread_line) ;
75
- }
76
- }
77
- TimelineDetails :: Error ( _) => { }
78
- }
79
- }
80
-
81
- content. push ( ListItem :: from ( lines) ) ;
82
- }
83
- }
84
-
85
- TimelineItemContent :: MsgLike ( MsgLikeContent {
86
- kind : MsgLikeKind :: Redacted ,
87
- ..
88
- } ) => content. push ( format ! ( "{sender}: -- redacted --" ) . into ( ) ) ,
89
-
90
- TimelineItemContent :: MsgLike ( MsgLikeContent {
91
- kind : MsgLikeKind :: UnableToDecrypt ( _) ,
92
- ..
93
- } ) => content. push ( format ! ( "{sender}: (UTD)" ) . into ( ) ) ,
94
-
95
- TimelineItemContent :: MembershipChange ( m) => {
96
- if let Some ( change) = m. change ( ) {
97
- let display_name =
98
- m. display_name ( ) . unwrap_or_else ( || m. user_id ( ) . to_string ( ) ) ;
99
-
100
- let change = match change {
101
- MembershipChange :: Joined => "has joined the room" ,
102
- MembershipChange :: Left => "has left the room" ,
103
- MembershipChange :: Banned => "has been banned" ,
104
- MembershipChange :: Unbanned => "has been unbanned" ,
105
- MembershipChange :: Kicked => "has been kicked from the room" ,
106
- MembershipChange :: Invited => "has been invited to the room" ,
107
- MembershipChange :: KickedAndBanned => {
108
- "has been kicked and banned from the room"
109
- }
110
- MembershipChange :: InvitationAccepted => {
111
- "has accepted the invitation to the room"
112
- }
113
- MembershipChange :: InvitationRejected => {
114
- "has rejected the invitation to the room"
115
- }
116
- MembershipChange :: Knocked => "knocked on the room" ,
117
- MembershipChange :: KnockAccepted => {
118
- "has accepted a knock on the room"
119
- }
120
- MembershipChange :: KnockRetracted => {
121
- "has retracted a knock on the room"
122
- }
123
- MembershipChange :: KnockDenied => "has denied a knock" ,
124
- MembershipChange :: None
125
- | MembershipChange :: Error
126
- | MembershipChange :: InvitationRevoked
127
- | MembershipChange :: NotImplemented => {
128
- "has changed it's membership status"
129
- }
130
- } ;
131
-
132
- content. push ( format ! ( "{display_name} {change}" ) . into ( ) ) ;
133
- }
134
- }
135
-
136
- TimelineItemContent :: MsgLike ( MsgLikeContent {
137
- kind : MsgLikeKind :: Sticker ( _) ,
138
- ..
139
- } )
140
- | TimelineItemContent :: ProfileChange ( _)
141
- | TimelineItemContent :: OtherState ( _)
142
- | TimelineItemContent :: FailedToParseMessageLike { .. }
143
- | TimelineItemContent :: FailedToParseState { .. }
144
- | TimelineItemContent :: MsgLike ( MsgLikeContent {
145
- kind : MsgLikeKind :: Poll ( _) ,
146
- ..
147
- } )
148
- | TimelineItemContent :: CallInvite
149
- | TimelineItemContent :: CallNotify => {
150
- continue ;
151
- }
152
- }
153
- }
154
-
155
- TimelineItemKind :: Virtual ( virt) => match virt {
156
- VirtualTimelineItem :: DateDivider ( unix_ts) => {
157
- content. push ( format ! ( "Date: {unix_ts:?}" ) . into ( ) ) ;
158
- }
159
- VirtualTimelineItem :: ReadMarker => {
160
- content. push ( "Read marker" . to_owned ( ) . into ( ) ) ;
161
- }
162
- VirtualTimelineItem :: TimelineStart => {
163
- content. push ( "🥳 Timeline start! 🥳" . to_owned ( ) . into ( ) ) ;
164
- }
165
- } ,
166
- }
167
- }
30
+ let content = self . items . iter ( ) . map ( format_timeline_item) ;
168
31
169
32
let list_items = content
170
- . into_iter ( )
33
+ . flatten ( )
171
34
. enumerate ( )
172
35
. map ( |( i, line) | {
173
36
let bg_color = match i % 2 {
@@ -187,3 +50,130 @@ impl StatefulWidget for &mut TimelineView<'_> {
187
50
StatefulWidget :: render ( list, area, buf, state) ;
188
51
}
189
52
}
53
+
54
+ fn format_timeline_item ( item : & Arc < TimelineItem > ) -> Option < ListItem < ' _ > > {
55
+ let item = match item. kind ( ) {
56
+ TimelineItemKind :: Event ( ev) => {
57
+ if ev. content ( ) . thread_root ( ) . is_some ( ) {
58
+ return None ;
59
+ }
60
+
61
+ let sender = ev. sender ( ) ;
62
+
63
+ match ev. content ( ) {
64
+ TimelineItemContent :: MsgLike ( MsgLikeContent {
65
+ kind : MsgLikeKind :: Message ( message) ,
66
+ ..
67
+ } ) => format_text_message ( sender, message, ev. content ( ) . thread_summary ( ) ) ?,
68
+
69
+ TimelineItemContent :: MsgLike ( MsgLikeContent {
70
+ kind : MsgLikeKind :: Redacted ,
71
+ ..
72
+ } ) => format ! ( "{sender}: -- redacted --" ) . into ( ) ,
73
+
74
+ TimelineItemContent :: MsgLike ( MsgLikeContent {
75
+ kind : MsgLikeKind :: UnableToDecrypt ( _) ,
76
+ ..
77
+ } ) => format ! ( "{sender}: (UTD)" ) . into ( ) ,
78
+
79
+ TimelineItemContent :: MembershipChange ( m) => format_membership_change ( m) ?,
80
+
81
+ TimelineItemContent :: MsgLike ( MsgLikeContent {
82
+ kind : MsgLikeKind :: Sticker ( _) ,
83
+ ..
84
+ } )
85
+ | TimelineItemContent :: ProfileChange ( _)
86
+ | TimelineItemContent :: OtherState ( _)
87
+ | TimelineItemContent :: FailedToParseMessageLike { .. }
88
+ | TimelineItemContent :: FailedToParseState { .. }
89
+ | TimelineItemContent :: MsgLike ( MsgLikeContent {
90
+ kind : MsgLikeKind :: Poll ( _) , ..
91
+ } )
92
+ | TimelineItemContent :: CallInvite
93
+ | TimelineItemContent :: CallNotify => {
94
+ return None ;
95
+ }
96
+ }
97
+ }
98
+
99
+ TimelineItemKind :: Virtual ( virt) => match virt {
100
+ VirtualTimelineItem :: DateDivider ( unix_ts) => format ! ( "Date: {unix_ts:?}" ) . into ( ) ,
101
+ VirtualTimelineItem :: ReadMarker => "Read marker" . to_owned ( ) . into ( ) ,
102
+ VirtualTimelineItem :: TimelineStart => "🥳 Timeline start! 🥳" . to_owned ( ) . into ( ) ,
103
+ } ,
104
+ } ;
105
+
106
+ Some ( item)
107
+ }
108
+
109
+ fn format_text_message (
110
+ sender : & UserId ,
111
+ message : & Message ,
112
+ thread_summary : Option < ThreadSummary > ,
113
+ ) -> Option < ListItem < ' static > > {
114
+ if let MessageType :: Text ( text) = message. msgtype ( ) {
115
+ let mut lines = Vec :: new ( ) ;
116
+ let first_line = Line :: from ( format ! ( "{}: {}" , sender, text. body) ) ;
117
+
118
+ lines. push ( first_line) ;
119
+
120
+ if let Some ( thread_summary) = thread_summary {
121
+ match thread_summary. latest_event {
122
+ TimelineDetails :: Unavailable => { }
123
+ TimelineDetails :: Pending => { }
124
+ TimelineDetails :: Ready ( e) => {
125
+ let sender = e. sender ;
126
+ let content = e. content . as_message ( ) . map ( |m| m. msgtype ( ) ) ;
127
+
128
+ if let Some ( MessageType :: Text ( text) ) = content {
129
+ let replies = if thread_summary. num_replies == 1 {
130
+ "1 reply" . to_owned ( )
131
+ } else {
132
+ format ! ( "{} replies" , { thread_summary. num_replies } )
133
+ } ;
134
+ let thread_line =
135
+ Line :: from ( format ! ( " 💬 {replies} {sender}: {}" , text. body) ) ;
136
+
137
+ lines. push ( thread_line) ;
138
+ }
139
+ }
140
+ TimelineDetails :: Error ( _) => { }
141
+ }
142
+ }
143
+
144
+ Some ( ListItem :: from ( lines) )
145
+ } else {
146
+ None
147
+ }
148
+ }
149
+
150
+ fn format_membership_change ( membership : & RoomMembershipChange ) -> Option < ListItem < ' static > > {
151
+ if let Some ( change) = membership. change ( ) {
152
+ let display_name =
153
+ membership. display_name ( ) . unwrap_or_else ( || membership. user_id ( ) . to_string ( ) ) ;
154
+
155
+ let change = match change {
156
+ MembershipChange :: Joined => "has joined the room" ,
157
+ MembershipChange :: Left => "has left the room" ,
158
+ MembershipChange :: Banned => "has been banned" ,
159
+ MembershipChange :: Unbanned => "has been unbanned" ,
160
+ MembershipChange :: Kicked => "has been kicked from the room" ,
161
+ MembershipChange :: Invited => "has been invited to the room" ,
162
+ MembershipChange :: KickedAndBanned => "has been kicked and banned from the room" ,
163
+ MembershipChange :: InvitationAccepted => "has accepted the invitation to the room" ,
164
+ MembershipChange :: InvitationRejected => "has rejected the invitation to the room" ,
165
+ MembershipChange :: Knocked => "knocked on the room" ,
166
+ MembershipChange :: KnockAccepted => "has accepted a knock on the room" ,
167
+ MembershipChange :: KnockRetracted => "has retracted a knock on the room" ,
168
+ MembershipChange :: KnockDenied => "has denied a knock" ,
169
+ MembershipChange :: None
170
+ | MembershipChange :: Error
171
+ | MembershipChange :: InvitationRevoked
172
+ | MembershipChange :: NotImplemented => "has changed it's membership status" ,
173
+ } ;
174
+
175
+ Some ( format ! ( "{display_name} {change}" ) . into ( ) )
176
+ } else {
177
+ None
178
+ }
179
+ }
0 commit comments