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,90 @@ $!${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 ( ) ;
93
+ sortedSubmessages . sort ( ( m1 , m2 ) => m1 . id - m2 . id ) ;
94
+
95
+ const widgetSubmessages : $ReadOnlyArray < {
96
+ sender_id : number ,
97
+ content : SubmessageData ,
98
+ } > = sortedSubmessages
99
+ . filter ( submessage => submessage . msg_type === 'widget' )
100
+ . map ( submessage => ( {
101
+ sender_id : submessage . sender_id ,
102
+ content : JSON . parse ( submessage . content ) ,
103
+ } ) ) ;
104
+
105
+ const pollWidget = widgetSubmessages [ 0 ] ;
106
+ if ( ! pollWidget || ! pollWidget . content || pollWidget . content . widget_type !== 'poll' ) {
107
+ return template `
76
108
$!${ message . content }
77
109
<div class="special-message"
78
110
><p>Interactive message</p
79
111
><p>To use, open on web or desktop</p
80
112
></div>
81
113
` ;
114
+ }
115
+
116
+ if ( pollWidget . content . extra_data == null ) {
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 pollData = new PollData ( {
123
+ message_sender_id : message . sender_id ,
124
+ current_user_id : ownUserId ,
125
+ is_my_poll : message . sender_id === ownUserId ,
126
+ question :
127
+ pollWidget . content . extra_data . question == null ? pollWidget . content . extra_data . question : '' ,
128
+ options :
129
+ pollWidget . content . extra_data . options == null ? pollWidget . content . extra_data . options : [ ] ,
130
+ // TODO: Implement this.
131
+ comma_separated_names : ( ) => '' ,
132
+ report_error_function : ( msg : string ) => {
133
+ logging . error ( msg ) ;
134
+ } ,
135
+ } ) ;
136
+
137
+ for ( const pollEvent of widgetSubmessages ) {
138
+ pollData . handle_event ( pollEvent . sender_id , pollEvent . content ) ;
139
+ }
140
+
141
+ const parsedPollData = pollData . get_widget_data ( ) ;
142
+
143
+ return template `
144
+ <div class="poll-widget">
145
+ <p class="poll-question">${ parsedPollData . question } </p>
146
+ <ul>
147
+ $!${ parsedPollData . options
148
+ . map (
149
+ option =>
150
+ template `
151
+ <li>
152
+ <button
153
+ class="poll-vote"
154
+ data-voted="${ option . current_user_vote } "
155
+ data-key="${ option . key } "
156
+ >${ option . count } </button>
157
+ <span class="poll-option">${ option . option } </span>
158
+ </li>` ,
159
+ )
160
+ . join ( '' ) }
161
+ </ul>
162
+ </div>
163
+ ` ;
164
+ } ;
82
165
83
166
export const flagsStateToStringList = ( flags : FlagsState , id : number ) : string [ ] =>
84
167
Object . keys ( flags ) . filter ( key => flags [ key ] [ id ] ) ;
@@ -112,7 +195,7 @@ export default (
112
195
` ;
113
196
const bodyHtml =
114
197
message . submessages && message . submessages . length > 0
115
- ? widgetBody ( message )
198
+ ? widgetBody ( message , backgroundData . ownUser . user_id )
116
199
: messageBody ( backgroundData , message ) ;
117
200
118
201
if ( isBrief ) {
0 commit comments