1
1
/* @flow strict-local */
2
2
import { PixelRatio } from 'react-native' ;
3
+ import invariant from 'invariant' ;
3
4
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now' ;
5
+ // $FlowFixMe[untyped-import]
6
+ import { PollData } from '@zulip/shared/js/poll_data' ;
7
+
4
8
import template from './template' ;
5
9
import type {
6
10
AggregatedReaction ,
@@ -10,6 +14,7 @@ import type {
10
14
MessageLike ,
11
15
Outbox ,
12
16
Reaction ,
17
+ SubmessageData ,
13
18
ImageEmojiType ,
14
19
UserId ,
15
20
} from '../../types' ;
@@ -18,6 +23,7 @@ import { shortTime } from '../../utils/date';
18
23
import aggregateReactions from '../../reactions/aggregateReactions' ;
19
24
import { codeToEmojiMap } from '../../emoji/data' ;
20
25
import processAlertWords from './processAlertWords' ;
26
+ import * as logging from '../../utils/logging' ;
21
27
22
28
const messageTagsAsHtml = ( isStarred : boolean , timeEdited : number | void ) : string => {
23
29
const pieces = [ ] ;
@@ -72,13 +78,89 @@ $!${messageReactionListAsHtml(reactions, ownUser.user_id, allImageEmojiById)}
72
78
` ;
73
79
} ;
74
80
75
- const widgetBody = ( message : Message | Outbox ) => template `
81
+ /**
82
+ * Render the body of a message that has submessages.
83
+ *
84
+ * Must not be called on a message without any submessages.
85
+ */
86
+ const widgetBody = ( message : Message , ownUserId : UserId ) => {
87
+ invariant (
88
+ message . submessages !== undefined && message . submessages . length > 0 ,
89
+ 'should have submessages' ,
90
+ ) ;
91
+
92
+ const sortedSubmessages = message . submessages . concat ( ) . sort ( ( m1 , m2 ) => m1 . id - m2 . id ) ;
93
+
94
+ const widgetSubmessages : Array < {
95
+ sender_id : number ,
96
+ content : SubmessageData ,
97
+ } > = sortedSubmessages
98
+ . filter ( submessage => submessage . msg_type === 'widget' )
99
+ . map ( submessage => ( {
100
+ sender_id : submessage . sender_id ,
101
+ content : JSON . parse ( submessage . content ) ,
102
+ } ) ) ;
103
+
104
+ const pollWidget = widgetSubmessages . shift ( ) ;
105
+ if ( ! pollWidget || ! pollWidget . content || pollWidget . content . widget_type !== 'poll' ) {
106
+ return template `
76
107
$!${ message . content }
77
108
<div class="special-message"
78
109
><p>Interactive message</p
79
110
><p>To use, open on web or desktop</p
80
111
></div>
81
112
` ;
113
+ }
114
+
115
+ if ( pollWidget . content . extra_data == null ) {
116
+ // We don't expect this to happen in general, but there are some malformed
117
+ // messages lying around that will trigger this.
118
+ return template `$!${ message . content } ` ;
119
+ }
120
+
121
+ const pollData = new PollData ( {
122
+ message_sender_id : message . sender_id ,
123
+ current_user_id : ownUserId ,
124
+ is_my_poll : message . sender_id === ownUserId ,
125
+ question :
126
+ pollWidget . content . extra_data . question == null ? '' : pollWidget . content . extra_data . question ,
127
+ options :
128
+ pollWidget . content . extra_data . options == null ? [ ] : pollWidget . content . extra_data . options ,
129
+ // TODO: Implement this.
130
+ comma_separated_names : ( ) => '' ,
131
+ report_error_function : ( msg : string ) => {
132
+ logging . error ( msg ) ;
133
+ } ,
134
+ } ) ;
135
+
136
+ for ( const pollEvent of widgetSubmessages ) {
137
+ pollData . handle_event ( pollEvent . sender_id , pollEvent . content ) ;
138
+ }
139
+
140
+ const parsedPollData = pollData . get_widget_data ( ) ;
141
+
142
+ return template `
143
+ <div class="poll-widget">
144
+ <p class="poll-question">${ parsedPollData . question } </p>
145
+ <ul>
146
+ $!${ parsedPollData . options
147
+ . map (
148
+ option =>
149
+ template `
150
+ <li>
151
+ <button
152
+ class="poll-vote"
153
+ data-voted="${ option . current_user_vote } "
154
+ data-key="${ option . key } "
155
+ >${ option . count } </button>
156
+ <span class="poll-option">${ option . option } </span>
157
+ </li>` ,
158
+ )
159
+ . join ( '' ) }
160
+ </ul>
161
+ </div>
162
+ ` ;
163
+ } ;
82
164
83
165
export const flagsStateToStringList = ( flags : FlagsState , id : number ) : string [ ] =>
84
166
Object . keys ( flags ) . filter ( key => flags [ key ] [ id ] ) ;
@@ -112,7 +194,7 @@ export default (
112
194
` ;
113
195
const bodyHtml =
114
196
message . submessages && message . submessages . length > 0
115
- ? widgetBody ( message )
197
+ ? widgetBody ( message , backgroundData . ownUser . user_id )
116
198
: messageBody ( backgroundData , message ) ;
117
199
118
200
if ( isBrief ) {
0 commit comments