Skip to content

Commit 9b5dfb0

Browse files
JeremyPleasedrew-gross
authored andcommitted
Update push activity and push details views to query _PushStatus (#378)
* Update push activity and push details views to query _PushStatus _PushStatus class in Parse Server can be queried directly when using the master key. This allows us some basic functionality of viewing push history and statuses. * Remove some rogue console.log's * Rely on Parse Server setting features.push.storedPushData to true for enabling push activity * In PushUtils tableInfoBuilder attempt to parse query json and then check if query is set
1 parent 95557be commit 9b5dfb0

File tree

7 files changed

+94
-79
lines changed

7 files changed

+94
-79
lines changed

src/components/PushOpenRate/PushOpenRate.react.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,13 @@ let PushOpenRate = ({
5757
<div style={customStyles[isWinner ? 'standard' : 'inverse']} className={[styles.title, styles[color]].join(' ')}>{isWinner ? 'WINNER' : ''}</div>
5858
: null}
5959
<div style={customStyles[isWinner ? 'inverse' : 'standard']} className={[styles.percent, styles[color + (isWinner ? '_inv' : '')]].join(' ')}>
60-
<div className={styles.rate}>{rateStr}%</div>
60+
{ /*<div className={styles.rate}>{rateStr}%</div>*/ }
61+
<div className={styles.rate}>N/A</div>
6162
<div className={styles.rate_label}>Open Rate</div>
6263
</div>
6364
<div className={styles.count_wrap} style={{ float: 'left', width: '50%' }}>
64-
<div className={styles.count}>{numOpened}</div>
65+
{ /*<div className={styles.count}>{numOpened}</div>*/ }
66+
<div className={styles.count}>N/A</div>
6567
<div className={styles.count_label}>Push Opens</div>
6668
</div>
6769
<div className={styles.count_wrap} style={{ marginLeft: '50%' }}>

src/dashboard/DashboardView.react.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,6 @@ export default class DashboardView extends React.Component {
101101
link: '/push/new'
102102
});
103103
}
104-
// The push UI requires immediate and scheduled push (and some ruby endpoints that we will have to remove)
105-
/*
106104

107105
if (features.push && features.push.storedPushData) {
108106
pushSubsections.push({
@@ -116,7 +114,7 @@ export default class DashboardView extends React.Component {
116114
name: 'Audiences',
117115
link: '/push/audiences'
118116
});
119-
}*/
117+
}
120118

121119
let analyticsSidebarSections = [];
122120

src/dashboard/Push/PushConstants.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export const INITIAL_PAGE_SIZE = 5;
2121

2222
export const QUERY_FIELD = 'query';
2323

24+
export const SENT_FIELD = 'numSent';
25+
2426
export const DEVICE_MAP = {
2527
ios: 'iOS',
2628
osx: 'OS X',

src/dashboard/Push/PushDetails.react.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ let getSentInfo = (sendTime, expiration) => {
5959
return '';
6060
}
6161

62-
let fmtSendTime = getFormattedTime(sendTime);
62+
let fmtSendTime = getFormattedTime({time: sendTime});
6363
let fmtExpiration = expiration ? getFormattedTime({time: expiration * 1000}) : null;
6464
if (expiration){
6565
return `Sent ${fmtSendTime} and expires ${fmtExpiration}`;
@@ -118,7 +118,7 @@ let getStatusTable = (pushDetails, deferDeliveries) => {
118118
<div className={styles.deliveryName}>Successful Deliveries</div>
119119
<div className={styles.deliveryMessage}>Give your test a memorable name so you remember what you were testing when you see the results.</div>
120120
</td>
121-
<td className={tableStyles.td} width={'35%'}>{pushDetails.push_sends}</td>
121+
<td className={tableStyles.td} width={'35%'}>{pushDetails.get('numSent')}</td>
122122
</tr> :
123123
null
124124
}
@@ -219,9 +219,11 @@ export default class PushDetails extends DashboardView {
219219

220220
componentWillMount() {
221221
this.props.schema.dispatch(SchemaStore.ActionTypes.FETCH);
222-
let { xhr, promise } = this.context.currentApp.fetchPushDetails(this.props.params.pushId);
223-
this.xhrHandles = [xhr];
222+
let promise = this.context.currentApp.fetchPushDetails(this.props.params.pushId);
224223
promise.then((pushDetails) => {
224+
if (!pushDetails) {
225+
return null;
226+
}
225227
this.setState({ pushDetails });
226228
if (pushDetails.statistics && pushDetails.statistics.confidence_interval) {
227229
this.setState({
@@ -455,6 +457,9 @@ export default class PushDetails extends DashboardView {
455457

456458
renderPushRates(experimentInfo) {
457459
let pushDetails = this.state.pushDetails;
460+
if (!pushDetails.id) {
461+
return null;
462+
}
458463
let launchChoice = pushDetails.launch_choice;
459464
let statistics = pushDetails.statistics;
460465
let isMessageType = pushDetails.exp_type === 'message';
@@ -511,16 +516,16 @@ export default class PushDetails extends DashboardView {
511516
<div>
512517
<div className={styles.groupHeader}>
513518
<div className={styles.headerTitle}>MESSAGE SENT</div>
514-
<div className={styles.headline}>{getMessage(pushDetails.payload)}</div>
519+
<div className={styles.headline}>{getMessage(pushDetails.get('payload'))}</div>
515520
<div className={styles.subline}>
516-
{getSentInfo(pushDetails.send_time, pushDetails.expiration)}
521+
{getSentInfo(pushDetails.get('pushTime'), pushDetails.get('expiration'))}
517522
</div>
518523
</div>
519524
{prevLaunchGroup}
520525
{experimentInfo}
521526
<PushOpenRate
522-
numOpened={pushDetails.push_opens}
523-
numSent={pushDetails.push_sends}
527+
numOpened={pushDetails.get('numOpened') || 0}
528+
numSent={pushDetails.get('numSent')}
524529
customColor={this.state.standardColor} />
525530
</div>
526531
);
@@ -547,7 +552,7 @@ export default class PushDetails extends DashboardView {
547552
}
548553

549554
renderTargetTable() {
550-
return getTargetTable(this.state.pushDetails.query, this.props.schema, tableStyles);
555+
return getTargetTable(this.state.pushDetails.get('query'), this.props.schema, tableStyles);
551556
}
552557

553558
renderStatusTable() {
@@ -679,6 +684,9 @@ export default class PushDetails extends DashboardView {
679684

680685
//TODO: (peterjs) PushPreview Component
681686
renderContent() {
687+
if (this.state.loading) {
688+
return;
689+
}
682690
let { isFlowView, experimentInfo, flowFooterDetails } = this.experimentInfoHelper();
683691
return (
684692
<div className={styles.detailsWrapper}>

src/dashboard/Push/PushIndex.react.js

Lines changed: 54 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ const PUSH_TYPE_CAMPAIGN = 'campaign';
2929
const PUSH_TYPE_EXPERIMENT = 'experiment';
3030
const PUSH_TYPE_API = 'api';
3131
const PUSH_TYPE_TRANSLATION = 'translation';
32+
const PUSH_DEFAULT_LIMIT = 10;
3233

3334
const PUSH_CATEGORIES = {
3435
all: 'All',
35-
campaign: 'Campaigns',
36-
experiment: 'Experiments',
37-
api: 'sent via API'
36+
// campaign: 'Campaigns',
37+
// experiment: 'Experiments',
38+
// api: 'sent via API'
3839
};
3940

4041
const PUSH_TYPES = {
@@ -47,12 +48,14 @@ const PUSH_STATUS_COLOR = {
4748
succeeded: 'green',
4849
failed: 'red',
4950
pending: 'blue',
51+
running: 'blue',
5052
};
5153

5254
const PUSH_STATUS_CONTENT = {
5355
succeeded: 'SENT',
5456
failed: 'FAILED',
5557
pending: 'SENDING',
58+
running: 'SENDING',
5659
};
5760

5861
const EXPERIMENT_GROUP = {
@@ -138,13 +141,28 @@ let getPushName = (pushData) => {
138141
);
139142
} else {
140143
let payload = pushData[PushConstants.PAYLOAD_FIELD];
144+
try {
145+
payload = JSON.parse(payload);
146+
} catch(e) { }
141147
if(payload){
142-
let parsedPayload = JSON.parse(payload);
143-
return parsedPayload.alert ? parsedPayload.alert : payload;
148+
return payload.alert ? payload.alert : payload;
144149
}
145150
}
146151
}
147152

153+
let getPushCount = (pushData) => {
154+
let count = pushData[PushConstants.SENT_FIELD];
155+
if(count != undefined){
156+
return (
157+
<strong>{count}</strong>
158+
);
159+
} else {
160+
return (
161+
<strong>N/A</strong>
162+
);
163+
}
164+
}
165+
148166
let emptyStateContent = {
149167
'all': {
150168
title: 'No pushes to display yet.',
@@ -190,7 +208,7 @@ let formatStatus = (status) => {
190208
let color = PUSH_STATUS_COLOR[status];
191209
let text = PUSH_STATUS_CONTENT[status];
192210
return (
193-
<StatusIndicator status={color} text={text} />
211+
<StatusIndicator color={color} text={text} />
194212
);
195213
}
196214

@@ -215,36 +233,33 @@ export default class PushIndex extends DashboardView {
215233
constructor() {
216234
super();
217235
this.section = 'Push';
218-
this.subsection = 'Activity'
236+
this.subsection = 'Past Pushes'
219237
this.action = new SidebarAction('Send a push', this.navigateToNew.bind(this));
220238
this.state = {
221239
pushes: [],
222-
pushCountMap: {},
223240
loading: true,
224241
paginationInfo: undefined,
225242
availableDevices: undefined,
226243
}
227244
this.xhrHandle = null;
228245
}
229246

230-
handleFetch(category, page){
231-
let {xhr, promise} = this.context.currentApp.fetchPushNotifications(category, page);
232-
this.xhrHandle = xhr;
233-
promise.then(({ push_status, push_data, pagination_info }) => {
247+
handleFetch(category, page, limit){
248+
limit = limit || PUSH_DEFAULT_LIMIT;
249+
page = page || 0;
250+
let promise = this.context.currentApp.fetchPushNotifications(category, page, limit);
251+
252+
promise.then((pushes) => {
234253
this.setState({
235-
pushes: this.state.pushes.concat(push_status),
236-
paginationInfo: pagination_info,
254+
paginationInfo: {
255+
has_more: (pushes.length == limit),
256+
page_num: page
257+
},
258+
pushes: this.state.pushes.concat(pushes),
237259
});
238-
if(push_status && push_status.length !== 0){
239-
this.context.currentApp.fetchPushNotificationsCount(push_data).then((objectIdMap) => {
240-
this.setState({
241-
pushCountMap: Object.assign(this.state.pushCountMap, objectIdMap),
242-
});
243-
});
244-
}
245260
}).always(() => {
246261
this.setState({
247-
loading:false,
262+
loading: false,
248263
showMoreLoading: false,
249264
});
250265
});
@@ -296,7 +311,6 @@ export default class PushIndex extends DashboardView {
296311
handleCategoryClick(category) {
297312
this.setState({
298313
pushes: [],
299-
pushCountMap: {},
300314
loading: true,
301315
});
302316
this.handleFetch(category);
@@ -312,34 +326,30 @@ export default class PushIndex extends DashboardView {
312326
<CategoryList current={current} linkPrefix={'push/activity/'} categories={[
313327
{ name: PUSH_CATEGORIES[PUSH_TYPE_ALL],
314328
id: PUSH_TYPE_ALL},
315-
{ name: PUSH_CATEGORIES[PUSH_TYPE_CAMPAIGN],
316-
id: PUSH_TYPE_CAMPAIGN},
317-
{ name: PUSH_CATEGORIES[PUSH_TYPE_EXPERIMENT],
318-
id: PUSH_TYPE_EXPERIMENT},
319-
{ name: PUSH_CATEGORIES[PUSH_TYPE_API],
320-
id: PUSH_TYPE_API},
329+
// { name: PUSH_CATEGORIES[PUSH_TYPE_CAMPAIGN],
330+
// id: PUSH_TYPE_CAMPAIGN},
331+
// { name: PUSH_CATEGORIES[PUSH_TYPE_EXPERIMENT],
332+
// id: PUSH_TYPE_EXPERIMENT},
333+
// { name: PUSH_CATEGORIES[PUSH_TYPE_API],
334+
// id: PUSH_TYPE_API},
321335
]} />
322336
);
323337
}
324338

325339
renderRow(push) {
326340
//TODO: special experimentation case for type
327341
return (
328-
<tr onClick={this.navigateToDetails.bind(this, push.objectId)} className={styles.tr}>
329-
<td className={styles.colType}>{getPushStatusType(push.data)}</td>
342+
<tr key={push.id} onClick={this.navigateToDetails.bind(this, push.id)} className={styles.tr}>
343+
<td className={styles.colType}>{getPushStatusType(push.attributes)}</td>
330344
<td className={styles.colTarget}>
331-
{getTranslationInfo(push.data.translation_locale)}
332-
{getExperimentInfo(push.data.experiment)}
333-
{getPushTarget(push.data, this.state.availableDevices)}
334-
</td>
335-
<td className={styles.colPushesSent}>
336-
{typeof(this.state.pushCountMap[push.objectId]) === 'undefined' ?
337-
<LoaderDots /> :
338-
this.state.pushCountMap[push.objectId]}
345+
{getTranslationInfo(push.attributes.translation_locale)}
346+
{getExperimentInfo(push.attributes.experiment)}
347+
{getPushTarget(push.attributes, this.state.availableDevices)}
339348
</td>
340-
<td className={styles.colName}>{getPushName(push.data)}</td>
341-
<td className={styles.colTime}>{getPushTime(push.data.pushTime, push.updatedAt)}</td>
342-
<td className={styles.colStatus}>{formatStatus(push.data.status)}</td>
349+
<td className={styles.colPushesSent}>{getPushCount(push.attributes)}</td>
350+
<td className={styles.colName}>{getPushName(push.attributes)}</td>
351+
<td className={styles.colTime}>{getPushTime(push.attributes.pushTime, push.updatedAt)}</td>
352+
<td className={styles.colStatus}>{formatStatus(push.attributes.status)}</td>
343353
</tr>
344354
);
345355
}
@@ -370,13 +380,11 @@ export default class PushIndex extends DashboardView {
370380
renderExtras() {
371381
let paginationInfo = this.state.paginationInfo;
372382

373-
if(!paginationInfo){
383+
if (!paginationInfo) {
374384
return null;
375385
}
376386

377-
let maxPage = Math.ceil(paginationInfo.push_status_display_count/paginationInfo.push_status_per_page);
378-
379-
if(paginationInfo.page_num < maxPage && !this.state.loading){
387+
if(paginationInfo.has_more && !this.state.loading){
380388
return (
381389
<div className={styles.showMore}>
382390
<Button progress={this.state.showMoreLoading} color='blue' value='Show more' onClick={this.handleShowMore.bind(this, paginationInfo.page_num + 1)} />

src/lib/ParseApp.js

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ export default class ParseApp {
364364
validateCollaborator(email) {
365365
let path = '/apps/' + this.slug + '/collaborations/validate?email=' + encodeURIComponent(email);
366366
return AJAX.get(path);
367-
}
367+
}
368368

369369
fetchPushSubscriberCount(audienceId, query) {
370370
let path = '/apps/' + this.slug + '/dashboard_ajax/push_subscriber_count';
@@ -376,23 +376,15 @@ export default class ParseApp {
376376
return AJAX.abortableGet(audienceId ? `${path}${urlsSeparator}audienceId=${audienceId}` : path);
377377
}
378378

379-
fetchPushNotifications(type, page) {
380-
let path = '/apps/' + this.slug + '/push_notifications/' + `?type=${type}`;
381-
if (page) {
382-
path += `&page=${page}`;
383-
}
384-
return AJAX.abortableGet(path);
385-
}
386-
387-
fetchPushNotificationsCount(pushData) {
388-
let query = '?';
389-
for(let i in pushData){
390-
if(pushData.hasOwnProperty(i)){
391-
query += `pushes[${i}]=${pushData[i]}&`;
392-
}
379+
fetchPushNotifications(type, page, limit) {
380+
let query = new Parse.Query('_PushStatus');
381+
if (type != 'all') {
382+
query.equalTo('source', type || 'rest');
393383
}
394-
let path = '/apps/' + this.slug + '/push_notifications/pushes_sent_batch' + encodeURI(query);
395-
return AJAX.get(path);
384+
query.skip(page*limit);
385+
query.limit(limit);
386+
query.descending('createdAt');
387+
return query.find({ useMasterKey: true });
396388
}
397389

398390
fetchPushAudienceSizeSuggestion() {
@@ -401,8 +393,9 @@ export default class ParseApp {
401393
}
402394

403395
fetchPushDetails(objectId) {
404-
let path = '/apps/' + this.slug + `/push_notifications/${objectId}/push_details`;
405-
return AJAX.abortableGet(path);
396+
let query = new Parse.Query('_PushStatus');
397+
query.equalTo('objectId', objectId);
398+
return query.first({ useMasterKey: true });
406399
}
407400

408401
isLocalizationAvailable() {

src/lib/PushUtils.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,10 @@ let tableInfoBuilderHelper = (styles, key, description, value) => {
303303
}
304304

305305
export function tableInfoBuilder(query, schema, styles = {}) {
306+
try {
307+
query = JSON.parse(query);
308+
} catch(e) {}
309+
306310
if(!query) {
307311
return;
308312
}

0 commit comments

Comments
 (0)