Skip to content

Update push activity and push details views to query _PushStatus #378

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/components/PushOpenRate/PushOpenRate.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ let PushOpenRate = ({
<div style={customStyles[isWinner ? 'standard' : 'inverse']} className={[styles.title, styles[color]].join(' ')}>{isWinner ? 'WINNER' : ''}</div>
: null}
<div style={customStyles[isWinner ? 'inverse' : 'standard']} className={[styles.percent, styles[color + (isWinner ? '_inv' : '')]].join(' ')}>
<div className={styles.rate}>{rateStr}%</div>
{ /*<div className={styles.rate}>{rateStr}%</div>*/ }
<div className={styles.rate}>N/A</div>
<div className={styles.rate_label}>Open Rate</div>
</div>
<div className={styles.count_wrap} style={{ float: 'left', width: '50%' }}>
<div className={styles.count}>{numOpened}</div>
{ /*<div className={styles.count}>{numOpened}</div>*/ }
<div className={styles.count}>N/A</div>
<div className={styles.count_label}>Push Opens</div>
</div>
<div className={styles.count_wrap} style={{ marginLeft: '50%' }}>
Expand Down
4 changes: 1 addition & 3 deletions src/dashboard/DashboardView.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ export default class DashboardView extends React.Component {
link: '/push/new'
});
}
// The push UI requires immediate and scheduled push (and some ruby endpoints that we will have to remove)
/*

if (features.push && features.push.storedPushData) {
pushSubsections.push({
Expand All @@ -116,7 +114,7 @@ export default class DashboardView extends React.Component {
name: 'Audiences',
link: '/push/audiences'
});
}*/
}

let analyticsSidebarSections = [];

Expand Down
2 changes: 2 additions & 0 deletions src/dashboard/Push/PushConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const INITIAL_PAGE_SIZE = 5;

export const QUERY_FIELD = 'query';

export const SENT_FIELD = 'numSent';

export const DEVICE_MAP = {
ios: 'iOS',
osx: 'OS X',
Expand Down
26 changes: 17 additions & 9 deletions src/dashboard/Push/PushDetails.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ let getSentInfo = (sendTime, expiration) => {
return '';
}

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

componentWillMount() {
this.props.schema.dispatch(SchemaStore.ActionTypes.FETCH);
let { xhr, promise } = this.context.currentApp.fetchPushDetails(this.props.params.pushId);
this.xhrHandles = [xhr];
let promise = this.context.currentApp.fetchPushDetails(this.props.params.pushId);
promise.then((pushDetails) => {
if (!pushDetails) {
return null;
}
this.setState({ pushDetails });
if (pushDetails.statistics && pushDetails.statistics.confidence_interval) {
this.setState({
Expand Down Expand Up @@ -455,6 +457,9 @@ export default class PushDetails extends DashboardView {

renderPushRates(experimentInfo) {
let pushDetails = this.state.pushDetails;
if (!pushDetails.id) {
return null;
}
let launchChoice = pushDetails.launch_choice;
let statistics = pushDetails.statistics;
let isMessageType = pushDetails.exp_type === 'message';
Expand Down Expand Up @@ -511,16 +516,16 @@ export default class PushDetails extends DashboardView {
<div>
<div className={styles.groupHeader}>
<div className={styles.headerTitle}>MESSAGE SENT</div>
<div className={styles.headline}>{getMessage(pushDetails.payload)}</div>
<div className={styles.headline}>{getMessage(pushDetails.get('payload'))}</div>
<div className={styles.subline}>
{getSentInfo(pushDetails.send_time, pushDetails.expiration)}
{getSentInfo(pushDetails.get('pushTime'), pushDetails.get('expiration'))}
</div>
</div>
{prevLaunchGroup}
{experimentInfo}
<PushOpenRate
numOpened={pushDetails.push_opens}
numSent={pushDetails.push_sends}
numOpened={pushDetails.get('numOpened') || 0}
numSent={pushDetails.get('numSent')}
customColor={this.state.standardColor} />
</div>
);
Expand All @@ -547,7 +552,7 @@ export default class PushDetails extends DashboardView {
}

renderTargetTable() {
return getTargetTable(this.state.pushDetails.query, this.props.schema, tableStyles);
return getTargetTable(this.state.pushDetails.get('query'), this.props.schema, tableStyles);
}

renderStatusTable() {
Expand Down Expand Up @@ -679,6 +684,9 @@ export default class PushDetails extends DashboardView {

//TODO: (peterjs) PushPreview Component
renderContent() {
if (this.state.loading) {
return;
}
let { isFlowView, experimentInfo, flowFooterDetails } = this.experimentInfoHelper();
return (
<div className={styles.detailsWrapper}>
Expand Down
100 changes: 54 additions & 46 deletions src/dashboard/Push/PushIndex.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ const PUSH_TYPE_CAMPAIGN = 'campaign';
const PUSH_TYPE_EXPERIMENT = 'experiment';
const PUSH_TYPE_API = 'api';
const PUSH_TYPE_TRANSLATION = 'translation';
const PUSH_DEFAULT_LIMIT = 10;

const PUSH_CATEGORIES = {
all: 'All',
campaign: 'Campaigns',
experiment: 'Experiments',
api: 'sent via API'
// campaign: 'Campaigns',
// experiment: 'Experiments',
// api: 'sent via API'
};

const PUSH_TYPES = {
Expand All @@ -47,12 +48,14 @@ const PUSH_STATUS_COLOR = {
succeeded: 'green',
failed: 'red',
pending: 'blue',
running: 'blue',
};

const PUSH_STATUS_CONTENT = {
succeeded: 'SENT',
failed: 'FAILED',
pending: 'SENDING',
running: 'SENDING',
};

const EXPERIMENT_GROUP = {
Expand Down Expand Up @@ -138,13 +141,28 @@ let getPushName = (pushData) => {
);
} else {
let payload = pushData[PushConstants.PAYLOAD_FIELD];
try {
payload = JSON.parse(payload);
} catch(e) { }
if(payload){
let parsedPayload = JSON.parse(payload);
return parsedPayload.alert ? parsedPayload.alert : payload;
return payload.alert ? payload.alert : payload;
}
}
}

let getPushCount = (pushData) => {
let count = pushData[PushConstants.SENT_FIELD];
if(count != undefined){
return (
<strong>{count}</strong>
);
} else {
return (
<strong>N/A</strong>
);
}
}

let emptyStateContent = {
'all': {
title: 'No pushes to display yet.',
Expand Down Expand Up @@ -190,7 +208,7 @@ let formatStatus = (status) => {
let color = PUSH_STATUS_COLOR[status];
let text = PUSH_STATUS_CONTENT[status];
return (
<StatusIndicator status={color} text={text} />
<StatusIndicator color={color} text={text} />
);
}

Expand All @@ -215,36 +233,33 @@ export default class PushIndex extends DashboardView {
constructor() {
super();
this.section = 'Push';
this.subsection = 'Activity'
this.subsection = 'Past Pushes'
this.action = new SidebarAction('Send a push', this.navigateToNew.bind(this));
this.state = {
pushes: [],
pushCountMap: {},
loading: true,
paginationInfo: undefined,
availableDevices: undefined,
}
this.xhrHandle = null;
}

handleFetch(category, page){
let {xhr, promise} = this.context.currentApp.fetchPushNotifications(category, page);
this.xhrHandle = xhr;
promise.then(({ push_status, push_data, pagination_info }) => {
handleFetch(category, page, limit){
limit = limit || PUSH_DEFAULT_LIMIT;
page = page || 0;
let promise = this.context.currentApp.fetchPushNotifications(category, page, limit);

promise.then((pushes) => {
this.setState({
pushes: this.state.pushes.concat(push_status),
paginationInfo: pagination_info,
paginationInfo: {
has_more: (pushes.length == limit),
page_num: page
},
pushes: this.state.pushes.concat(pushes),
});
if(push_status && push_status.length !== 0){
this.context.currentApp.fetchPushNotificationsCount(push_data).then((objectIdMap) => {
this.setState({
pushCountMap: Object.assign(this.state.pushCountMap, objectIdMap),
});
});
}
}).always(() => {
this.setState({
loading:false,
loading: false,
showMoreLoading: false,
});
});
Expand Down Expand Up @@ -296,7 +311,6 @@ export default class PushIndex extends DashboardView {
handleCategoryClick(category) {
this.setState({
pushes: [],
pushCountMap: {},
loading: true,
});
this.handleFetch(category);
Expand All @@ -312,34 +326,30 @@ export default class PushIndex extends DashboardView {
<CategoryList current={current} linkPrefix={'push/activity/'} categories={[
{ name: PUSH_CATEGORIES[PUSH_TYPE_ALL],
id: PUSH_TYPE_ALL},
{ name: PUSH_CATEGORIES[PUSH_TYPE_CAMPAIGN],
id: PUSH_TYPE_CAMPAIGN},
{ name: PUSH_CATEGORIES[PUSH_TYPE_EXPERIMENT],
id: PUSH_TYPE_EXPERIMENT},
{ name: PUSH_CATEGORIES[PUSH_TYPE_API],
id: PUSH_TYPE_API},
// { name: PUSH_CATEGORIES[PUSH_TYPE_CAMPAIGN],
// id: PUSH_TYPE_CAMPAIGN},
// { name: PUSH_CATEGORIES[PUSH_TYPE_EXPERIMENT],
// id: PUSH_TYPE_EXPERIMENT},
// { name: PUSH_CATEGORIES[PUSH_TYPE_API],
// id: PUSH_TYPE_API},
]} />
);
}

renderRow(push) {
//TODO: special experimentation case for type
return (
<tr onClick={this.navigateToDetails.bind(this, push.objectId)} className={styles.tr}>
<td className={styles.colType}>{getPushStatusType(push.data)}</td>
<tr key={push.id} onClick={this.navigateToDetails.bind(this, push.id)} className={styles.tr}>
<td className={styles.colType}>{getPushStatusType(push.attributes)}</td>
<td className={styles.colTarget}>
{getTranslationInfo(push.data.translation_locale)}
{getExperimentInfo(push.data.experiment)}
{getPushTarget(push.data, this.state.availableDevices)}
</td>
<td className={styles.colPushesSent}>
{typeof(this.state.pushCountMap[push.objectId]) === 'undefined' ?
<LoaderDots /> :
this.state.pushCountMap[push.objectId]}
{getTranslationInfo(push.attributes.translation_locale)}
{getExperimentInfo(push.attributes.experiment)}
{getPushTarget(push.attributes, this.state.availableDevices)}
</td>
<td className={styles.colName}>{getPushName(push.data)}</td>
<td className={styles.colTime}>{getPushTime(push.data.pushTime, push.updatedAt)}</td>
<td className={styles.colStatus}>{formatStatus(push.data.status)}</td>
<td className={styles.colPushesSent}>{getPushCount(push.attributes)}</td>
<td className={styles.colName}>{getPushName(push.attributes)}</td>
<td className={styles.colTime}>{getPushTime(push.attributes.pushTime, push.updatedAt)}</td>
<td className={styles.colStatus}>{formatStatus(push.attributes.status)}</td>
</tr>
);
}
Expand Down Expand Up @@ -370,13 +380,11 @@ export default class PushIndex extends DashboardView {
renderExtras() {
let paginationInfo = this.state.paginationInfo;

if(!paginationInfo){
if (!paginationInfo) {
return null;
}

let maxPage = Math.ceil(paginationInfo.push_status_display_count/paginationInfo.push_status_per_page);

if(paginationInfo.page_num < maxPage && !this.state.loading){
if(paginationInfo.has_more && !this.state.loading){
return (
<div className={styles.showMore}>
<Button progress={this.state.showMoreLoading} color='blue' value='Show more' onClick={this.handleShowMore.bind(this, paginationInfo.page_num + 1)} />
Expand Down
31 changes: 12 additions & 19 deletions src/lib/ParseApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ export default class ParseApp {
validateCollaborator(email) {
let path = '/apps/' + this.slug + '/collaborations/validate?email=' + encodeURIComponent(email);
return AJAX.get(path);
}
}

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

fetchPushNotifications(type, page) {
let path = '/apps/' + this.slug + '/push_notifications/' + `?type=${type}`;
if (page) {
path += `&page=${page}`;
}
return AJAX.abortableGet(path);
}

fetchPushNotificationsCount(pushData) {
let query = '?';
for(let i in pushData){
if(pushData.hasOwnProperty(i)){
query += `pushes[${i}]=${pushData[i]}&`;
}
fetchPushNotifications(type, page, limit) {
let query = new Parse.Query('_PushStatus');
if (type != 'all') {
query.equalTo('source', type || 'rest');
}
let path = '/apps/' + this.slug + '/push_notifications/pushes_sent_batch' + encodeURI(query);
return AJAX.get(path);
query.skip(page*limit);
query.limit(limit);
query.descending('createdAt');
return query.find({ useMasterKey: true });
}

fetchPushAudienceSizeSuggestion() {
Expand All @@ -401,8 +393,9 @@ export default class ParseApp {
}

fetchPushDetails(objectId) {
let path = '/apps/' + this.slug + `/push_notifications/${objectId}/push_details`;
return AJAX.abortableGet(path);
let query = new Parse.Query('_PushStatus');
query.equalTo('objectId', objectId);
return query.first({ useMasterKey: true });
}

isLocalizationAvailable() {
Expand Down
4 changes: 4 additions & 0 deletions src/lib/PushUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,10 @@ let tableInfoBuilderHelper = (styles, key, description, value) => {
}

export function tableInfoBuilder(query, schema, styles = {}) {
try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this check maybe go above the if(!query) { line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that makes sense.
I had experienced some strange behaviour on different Parse Server apps where sometimes query was a JSON string and other times it was already an object. Nevertheless, this should be okay.

query = JSON.parse(query);
} catch(e) {}

if(!query) {
return;
}
Expand Down