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 ,
@@ -18,6 +22,7 @@ import { shortTime } from '../../utils/date';
18
22
import aggregateReactions from '../../reactions/aggregateReactions' ;
19
23
import { codeToEmojiMap } from '../../emoji/data' ;
20
24
import processAlertWords from './processAlertWords' ;
25
+ import * as logging from '../../utils/logging' ;
21
26
22
27
const messageTagsAsHtml = ( isStarred : boolean , timeEdited : number | void ) : string => {
23
28
const pieces = [ ] ;
@@ -72,13 +77,87 @@ $!${messageReactionListAsHtml(reactions, ownUser.user_id, allImageEmojiById)}
72
77
` ;
73
78
} ;
74
79
75
- const widgetBody = ( message : Message | Outbox ) => template `
80
+ /**
81
+ * Render the body of a message that has submessages.
82
+ *
83
+ * Must not be called on a message without any submessages.
84
+ */
85
+ const widgetBody = ( message : Message , ownUserId : UserId ) => {
86
+ invariant (
87
+ message . submessages !== undefined && message . submessages . length > 0 ,
88
+ 'should have submessages' ,
89
+ ) ;
90
+
91
+ const widgetSubmessages = message . submessages
92
+ . filter ( submessage => submessage . msg_type === 'widget' )
93
+ . map ( submessage => ( {
94
+ ...submessage ,
95
+ content : JSON . parse ( submessage . content ) ,
96
+ } ) ) ;
97
+
98
+ const poll_widgets = widgetSubmessages . filter ( widget => widget . content . widget_type === 'poll' ) ;
99
+ // "events" are submessages other than the main widget submessage - for
100
+ // instance, `vote`, `new_option`, and similar submessages. These are not
101
+ // documented, look at the PollData code if you're interested in what the
102
+ // possibilities are.
103
+ const events = widgetSubmessages . filter ( widget => 'type' in widget . content ) ;
104
+
105
+ if ( poll_widgets . length == 0 ) {
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
+ const poll_widget = poll_widgets [ 0 ] ;
115
+
116
+ if ( ! poll_widget . content || ! poll_widget . content . extra_data ) {
117
+ // We don't expect this to happen in general, but there are some malformed
118
+ // messages lying around that will trigger this.
119
+ return template `$!${ message . content } ` ;
120
+ }
121
+
122
+ const poll_data = new PollData ( {
123
+ current_user_id : ownUserId ,
124
+ is_my_poll : message . sender_id === ownUserId ,
125
+ question : poll_widget . content . extra_data . question ,
126
+ options : poll_widget . content . extra_data . options ,
127
+ comma_separated_names : ( ) => { } ,
128
+ report_error_function : ( msg : string ) => {
129
+ logging . error ( msg ) ;
130
+ } ,
131
+ } ) ;
132
+
133
+ for ( const poll_event of events ) {
134
+ poll_data . handle_event ( poll_event . sender_id , poll_event . content ) ;
135
+ }
136
+
137
+ const parsed_poll_data = poll_data . get_widget_data ( ) ;
138
+
139
+ return template `
140
+ <div class="poll-widget">
141
+ <p class="poll-question">${ parsed_poll_data . question } </p>
142
+ <ul>
143
+ $!${ parsed_poll_data . options
144
+ . map (
145
+ option =>
146
+ template `
147
+ <li>
148
+ <button
149
+ class="poll-vote"
150
+ data-voted="${ option . current_user_vote } "
151
+ data-key="${ option . key } "
152
+ >${ option . count } </button>
153
+ <span class="poll-option">${ option . option } </span>
154
+ </li>` ,
155
+ )
156
+ . join ( '' ) }
157
+ </ul>
158
+ </div>
159
+ ` ;
160
+ } ;
82
161
83
162
export const flagsStateToStringList = ( flags : FlagsState , id : number ) : string [ ] =>
84
163
Object . keys ( flags ) . filter ( key => flags [ key ] [ id ] ) ;
@@ -112,7 +191,7 @@ export default (
112
191
` ;
113
192
const bodyHtml =
114
193
message . submessages && message . submessages . length > 0
115
- ? widgetBody ( message )
194
+ ? widgetBody ( message , backgroundData . ownUser . user_id )
116
195
: messageBody ( backgroundData , message ) ;
117
196
118
197
if ( isBrief ) {
0 commit comments