-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
fix(push): Resolve error when creating audience by adding PushAudienceFilter component (#2626) #2983
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
fix(push): Resolve error when creating audience by adding PushAudienceFilter component (#2626) #2983
Changes from all commits
828b64a
e4f2d17
460208d
02b3837
ff371de
2c89db1
20d4864
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,163 @@ | ||||||||||||||||||||||||||||||||||||||
/* | ||||||||||||||||||||||||||||||||||||||
* Copyright (c) 2016-present, Parse, LLC | ||||||||||||||||||||||||||||||||||||||
* All rights reserved. | ||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||
* This source code is licensed under the license found in the LICENSE file in | ||||||||||||||||||||||||||||||||||||||
* the root directory of this source tree. | ||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||
import * as Filters from 'lib/Filters'; | ||||||||||||||||||||||||||||||||||||||
import { List, Map } from 'immutable'; | ||||||||||||||||||||||||||||||||||||||
import PropTypes from 'lib/PropTypes'; | ||||||||||||||||||||||||||||||||||||||
import React from 'react'; | ||||||||||||||||||||||||||||||||||||||
import stringCompare from 'lib/stringCompare'; | ||||||||||||||||||||||||||||||||||||||
import { CurrentApp } from 'context/currentApp'; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
function changeField(schema, filters, index, newField) { | ||||||||||||||||||||||||||||||||||||||
const allowedConstraints = Filters.FieldConstraints[schema[newField].type]; | ||||||||||||||||||||||||||||||||||||||
const current = filters.get(index); | ||||||||||||||||||||||||||||||||||||||
const constraint = current.get('constraint'); | ||||||||||||||||||||||||||||||||||||||
const compare = current.get('compareTo'); | ||||||||||||||||||||||||||||||||||||||
const defaultCompare = Filters.DefaultComparisons[schema[newField].type]; | ||||||||||||||||||||||||||||||||||||||
const useExisting = allowedConstraints.includes(constraint); | ||||||||||||||||||||||||||||||||||||||
const newFilter = new Map({ | ||||||||||||||||||||||||||||||||||||||
field: newField, | ||||||||||||||||||||||||||||||||||||||
constraint: useExisting ? constraint : Filters.FieldConstraints[schema[newField].type][0], | ||||||||||||||||||||||||||||||||||||||
compareTo: useExisting && typeof defaultCompare === typeof compare ? compare : defaultCompare, | ||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||
return filters.set(index, newFilter); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
function changeConstraint(schema, filters, index, newConstraint, prevCompareTo) { | ||||||||||||||||||||||||||||||||||||||
const field = filters.get(index).get('field'); | ||||||||||||||||||||||||||||||||||||||
let compareType = schema[field].type; | ||||||||||||||||||||||||||||||||||||||
if (Object.prototype.hasOwnProperty.call(Filters.Constraints[newConstraint], 'field')) { | ||||||||||||||||||||||||||||||||||||||
compareType = Filters.Constraints[newConstraint].field; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
const newFilter = new Map({ | ||||||||||||||||||||||||||||||||||||||
field: field, | ||||||||||||||||||||||||||||||||||||||
constraint: newConstraint, | ||||||||||||||||||||||||||||||||||||||
compareTo: prevCompareTo ?? Filters.DefaultComparisons[compareType], | ||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||
return filters.set(index, newFilter); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
function changeCompareTo(schema, filters, index, type, newCompare) { | ||||||||||||||||||||||||||||||||||||||
const newValue = newCompare; | ||||||||||||||||||||||||||||||||||||||
return filters.set(index, filters.get(index).set('compareTo', newValue)); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
function deleteRow(filters, index) { | ||||||||||||||||||||||||||||||||||||||
return filters.delete(index); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
const PushAudienceFilter = ({ | ||||||||||||||||||||||||||||||||||||||
schema, | ||||||||||||||||||||||||||||||||||||||
filters, | ||||||||||||||||||||||||||||||||||||||
renderRow, | ||||||||||||||||||||||||||||||||||||||
onChange, | ||||||||||||||||||||||||||||||||||||||
onSearch, | ||||||||||||||||||||||||||||||||||||||
blacklist, | ||||||||||||||||||||||||||||||||||||||
className, | ||||||||||||||||||||||||||||||||||||||
onDeleteRow, | ||||||||||||||||||||||||||||||||||||||
}) => { | ||||||||||||||||||||||||||||||||||||||
const currentApp = React.useContext(CurrentApp); | ||||||||||||||||||||||||||||||||||||||
blacklist = blacklist || []; | ||||||||||||||||||||||||||||||||||||||
const available = Filters.availableFilters(schema, filters); | ||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||
<div> | ||||||||||||||||||||||||||||||||||||||
{filters.toArray().map((filter, i) => { | ||||||||||||||||||||||||||||||||||||||
const field = filter.get('field'); | ||||||||||||||||||||||||||||||||||||||
const constraint = filter.get('constraint'); | ||||||||||||||||||||||||||||||||||||||
const compareTo = filter.get('compareTo'); | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
const fields = Object.keys(available).concat([]); | ||||||||||||||||||||||||||||||||||||||
if (fields.indexOf(field) < 0) { | ||||||||||||||||||||||||||||||||||||||
fields.push(field); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
// Get the column preference of the current class. | ||||||||||||||||||||||||||||||||||||||
const currentColumnPreference = currentApp.columnPreference | ||||||||||||||||||||||||||||||||||||||
? currentApp.columnPreference[className] | ||||||||||||||||||||||||||||||||||||||
: null; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
// Check if the preference exists. | ||||||||||||||||||||||||||||||||||||||
if (currentColumnPreference) { | ||||||||||||||||||||||||||||||||||||||
const fieldsToSortToTop = currentColumnPreference | ||||||||||||||||||||||||||||||||||||||
.filter(item => item.filterSortToTop) | ||||||||||||||||||||||||||||||||||||||
.map(item => item.name); | ||||||||||||||||||||||||||||||||||||||
// Sort the fields. | ||||||||||||||||||||||||||||||||||||||
fields.sort((a, b) => { | ||||||||||||||||||||||||||||||||||||||
// Only "a" should sorted to the top. | ||||||||||||||||||||||||||||||||||||||
if (fieldsToSortToTop.includes(a) && !fieldsToSortToTop.includes(b)) { | ||||||||||||||||||||||||||||||||||||||
return -1; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
// Only "b" should sorted to the top. | ||||||||||||||||||||||||||||||||||||||
if (!fieldsToSortToTop.includes(a) && fieldsToSortToTop.includes(b)) { | ||||||||||||||||||||||||||||||||||||||
return 1; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
// Both should sorted to the top -> they should be sorted to the same order as in the "fieldsToSortToTop" array. | ||||||||||||||||||||||||||||||||||||||
if (fieldsToSortToTop.includes(a) && fieldsToSortToTop.includes(b)) { | ||||||||||||||||||||||||||||||||||||||
return fieldsToSortToTop.indexOf(a) - fieldsToSortToTop.indexOf(b); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
return stringCompare(a, b); | ||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
// If there's no preference: Use the default sort function. | ||||||||||||||||||||||||||||||||||||||
else { | ||||||||||||||||||||||||||||||||||||||
fields.sort(); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
const constraints = Filters.FieldConstraints[schema[field].type].filter( | ||||||||||||||||||||||||||||||||||||||
c => blacklist.indexOf(c) < 0 | ||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||
let compareType = schema[field].type; | ||||||||||||||||||||||||||||||||||||||
if (Object.prototype.hasOwnProperty.call(Filters.Constraints[constraint], 'field')) { | ||||||||||||||||||||||||||||||||||||||
compareType = Filters.Constraints[constraint].field; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
Comment on lines
+110
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard against missing schema entries to avoid crashes. If a saved filter references a removed field, Apply minimal guard: -const constraints = Filters.FieldConstraints[schema[field].type].filter(
+if (!schema[field]) {
+ // Field no longer exists; skip rendering this row safely.
+ return null;
+}
+const constraints = Filters.FieldConstraints[schema[field].type].filter(
c => blacklist.indexOf(c) < 0
); Optionally, surface a UI hint instead of skipping; happy to provide. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||
return renderRow({ | ||||||||||||||||||||||||||||||||||||||
fields, | ||||||||||||||||||||||||||||||||||||||
constraints, | ||||||||||||||||||||||||||||||||||||||
compareInfo: { | ||||||||||||||||||||||||||||||||||||||
type: compareType, | ||||||||||||||||||||||||||||||||||||||
targetClass: schema[field].targetClass, | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
currentField: field, | ||||||||||||||||||||||||||||||||||||||
currentConstraint: constraint, | ||||||||||||||||||||||||||||||||||||||
compareTo, | ||||||||||||||||||||||||||||||||||||||
key: field + '-' + constraint + '-' + i, | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
onChangeField: newField => { | ||||||||||||||||||||||||||||||||||||||
onChange(changeField(schema, filters, i, newField)); | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
onChangeConstraint: (newConstraint, prevCompareTo) => { | ||||||||||||||||||||||||||||||||||||||
onChange(changeConstraint(schema, filters, i, newConstraint, prevCompareTo)); | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
onChangeCompareTo: newCompare => { | ||||||||||||||||||||||||||||||||||||||
onChange(changeCompareTo(schema, filters, i, compareType, newCompare)); | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
onKeyDown: ({ key }) => { | ||||||||||||||||||||||||||||||||||||||
if (key === 'Enter') { | ||||||||||||||||||||||||||||||||||||||
onSearch(); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
Comment on lines
+138
to
+142
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enter key handler can throw when onSearch is undefined. Use optional chaining to make it safe. Apply: onKeyDown: ({ key }) => {
if (key === 'Enter') {
- onSearch();
+ onSearch?.();
}
}, 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||
onDeleteRow: () => { | ||||||||||||||||||||||||||||||||||||||
onDeleteRow?.(); | ||||||||||||||||||||||||||||||||||||||
onChange(deleteRow(filters, i)); | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||
})} | ||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
export default PushAudienceFilter; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
PushAudienceFilter.propTypes = { | ||||||||||||||||||||||||||||||||||||||
schema: PropTypes.object.isRequired.describe( | ||||||||||||||||||||||||||||||||||||||
'A class schema, mapping field names to their Type strings' | ||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||
filters: PropTypes.instanceOf(List).isRequired.describe( | ||||||||||||||||||||||||||||||||||||||
'An array of filter objects. Each filter contains "field", "comparator", and "compareTo" fields.' | ||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||
renderRow: PropTypes.func.isRequired.describe('A function for rendering a row of a filter.'), | ||||||||||||||||||||||||||||||||||||||
}; |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,7 +5,8 @@ | |||||||||||||||||||||||||||||||
* This source code is licensed under the license found in the LICENSE file in | ||||||||||||||||||||||||||||||||
* the root directory of this source tree. | ||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||
import CodeSnippet from 'components/CodeSnippet/CodeSnippet.react'; | ||||||||||||||||||||||||||||||||
import CodeEditor from 'components/CodeEditor/CodeEditor.react'; | ||||||||||||||||||||||||||||||||
import DashboardView from 'dashboard/DashboardView.react'; | ||||||||||||||||||||||||||||||||
import EmptyState from 'components/EmptyState/EmptyState.react'; | ||||||||||||||||||||||||||||||||
import FileTree from 'components/FileTree/FileTree.react'; | ||||||||||||||||||||||||||||||||
|
@@ -14,11 +15,13 @@ | |||||||||||||||||||||||||||||||
import Toolbar from 'components/Toolbar/Toolbar.react'; | ||||||||||||||||||||||||||||||||
import generatePath from 'lib/generatePath'; | ||||||||||||||||||||||||||||||||
import { withRouter } from 'lib/withRouter'; | ||||||||||||||||||||||||||||||||
import SaveButton from 'components/SaveButton/SaveButton.react'; | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
function getPath(params) { | ||||||||||||||||||||||||||||||||
return params.splat; | ||||||||||||||||||||||||||||||||
return params['*']; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
@withRouter | ||||||||||||||||||||||||||||||||
class CloudCode extends DashboardView { | ||||||||||||||||||||||||||||||||
constructor() { | ||||||||||||||||||||||||||||||||
|
@@ -29,6 +32,8 @@ | |||||||||||||||||||||||||||||||
this.state = { | ||||||||||||||||||||||||||||||||
files: undefined, | ||||||||||||||||||||||||||||||||
source: undefined, | ||||||||||||||||||||||||||||||||
saveState: SaveButton.States.WAITING, | ||||||||||||||||||||||||||||||||
saveError: '', | ||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
|
@@ -37,7 +42,7 @@ | |||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
componentWillReceiveProps(nextProps, nextContext) { | ||||||||||||||||||||||||||||||||
if (this.context !== nextContext) { | ||||||||||||||||||||||||||||||||
if (this.context !== nextContext || getPath(nextProps.params) !== getPath(this.props.params)) { | ||||||||||||||||||||||||||||||||
this.fetchSource(nextContext, getPath(nextProps.params)); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
@@ -54,9 +59,13 @@ | |||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
if (!fileName || release.files[fileName] === undefined) { | ||||||||||||||||||||||||||||||||
// Means we're still in /cloud_code/. Let's redirect to /cloud_code/main.js | ||||||||||||||||||||||||||||||||
this.props.navigate(generatePath(this.context, 'cloud_code/main.js'), { replace: true }); | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
this.props.navigate( | ||||||||||||||||||||||||||||||||
generatePath(this.context, `cloud_code/${Object.keys(release.files)[0]}`) | ||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||
// Means we can load /cloud_code/<fileName> | ||||||||||||||||||||||||||||||||
this.setState({ source: undefined }); | ||||||||||||||||||||||||||||||||
app.getSource(fileName).then( | ||||||||||||||||||||||||||||||||
source => this.setState({ source: source }), | ||||||||||||||||||||||||||||||||
() => this.setState({ source: undefined }) | ||||||||||||||||||||||||||||||||
|
@@ -90,6 +99,24 @@ | |||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
async getCode() { | ||||||||||||||||||||||||||||||||
if (!this.editor) { | ||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
this.setState({ saveState: SaveButton.States.SAVING }); | ||||||||||||||||||||||||||||||||
let fileName = getPath(this.props.params); | ||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||
await this.context.saveSource(fileName,this.editor.value); | ||||||||||||||||||||||||||||||||
this.setState({ saveState: SaveButton.States.SUCCEEDED }); | ||||||||||||||||||||||||||||||||
setTimeout(()=> { | ||||||||||||||||||||||||||||||||
this.setState({ saveState: SaveButton.States.WAITING }); | ||||||||||||||||||||||||||||||||
},2000); | ||||||||||||||||||||||||||||||||
} catch (e) { | ||||||||||||||||||||||||||||||||
this.setState({ saveState: SaveButton.States.FAILED }); | ||||||||||||||||||||||||||||||||
this.setState({ saveError: e.message || e }); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
Comment on lines
+114
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let the save button recover after a failure. Once You can mirror the success path timing and clear the failure state shortly after logging the error: } catch (e) {
- this.setState({ saveState: SaveButton.States.FAILED });
- this.setState({ saveError: e.message || e });
+ this.setState({
+ saveState: SaveButton.States.FAILED,
+ saveError: e.message || e,
+ });
+ setTimeout(() => {
+ this.setState({ saveState: SaveButton.States.WAITING });
+ }, 2000);
} 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
renderContent() { | ||||||||||||||||||||||||||||||||
let toolbar = null; | ||||||||||||||||||||||||||||||||
let content = null; | ||||||||||||||||||||||||||||||||
|
@@ -113,11 +140,23 @@ | |||||||||||||||||||||||||||||||
if (fileName) { | ||||||||||||||||||||||||||||||||
toolbar = <Toolbar section="Cloud Code" subsection={fileName} />; | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
const source = this.state.files[fileName]; | ||||||||||||||||||||||||||||||||
if (source && source.source) { | ||||||||||||||||||||||||||||||||
const source = this.state.source; | ||||||||||||||||||||||||||||||||
if (source) { | ||||||||||||||||||||||||||||||||
content = ( | ||||||||||||||||||||||||||||||||
<div className={styles.content}> | ||||||||||||||||||||||||||||||||
<CodeSnippet source={source.source} language="javascript" /> | ||||||||||||||||||||||||||||||||
{/* <CodeSnippet source={source.source} language="javascript" /> */} | ||||||||||||||||||||||||||||||||
<CodeEditor | ||||||||||||||||||||||||||||||||
defaultValue={source} | ||||||||||||||||||||||||||||||||
ref={editor => (this.editor = editor)} | ||||||||||||||||||||||||||||||||
fontSize={14} | ||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||
<div style={{ padding: 10, alignContent: 'center', display: 'flex', justifyContent: 'center' }}> | ||||||||||||||||||||||||||||||||
<SaveButton | ||||||||||||||||||||||||||||||||
state={this.state.saveState} | ||||||||||||||||||||||||||||||||
failedText={this.state.saveError} | ||||||||||||||||||||||||||||||||
onClick={() => this.getCode(this)} | ||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -154,6 +154,15 @@ export default class ParseApp { | |||||||||||||||||||||||||
return this.apiRequest('GET', path, {}, { useMasterKey: true }); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||
* Saves source of a Cloud Code hosted file from api.parse.com | ||||||||||||||||||||||||||
* fileName - the name of the file to be fetched | ||||||||||||||||||||||||||
* data - the text to save to the cloud file | ||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||
saveSource(fileName, data) { | ||||||||||||||||||||||||||
return this.apiRequest('POST', `scripts/${fileName}`, { data }, { useMasterKey: true }); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
Comment on lines
+162
to
+164
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep the Cloud Code cache in sync after save.
Apply this diff to update the cache eagerly: - saveSource(fileName, data) {
- return this.apiRequest('POST', `scripts/${fileName}`, { data }, { useMasterKey: true });
- }
+ saveSource(fileName, data) {
+ return this.apiRequest('POST', `scripts/${fileName}`, { data }, { useMasterKey: true }).then(
+ result => {
+ if (this.latestRelease.files && this.latestRelease.files[fileName]) {
+ this.latestRelease.files[fileName].source = data;
+ }
+ return result;
+ }
+ );
+ } 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||
* Fetches source of a Cloud Code hosted file from api.parse.com | ||||||||||||||||||||||||||
* fileName - the name of the file to be fetched | ||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pressing Enter in a row may crash: onSearch isn’t provided to child.
PushAudienceFilter
callsonSearch()
on Enter without optional chaining. Since this dialog doesn’t passonSearch
, users pressing Enter can hit a TypeError. Pass a no‑op or wire it to refresh the audience size.Apply:
📝 Committable suggestion
🤖 Prompt for AI Agents