diff --git a/.travis.yml b/.travis.yml index 16ccd36bae..4c1a2ae8be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,9 @@ language: node_js node_js: - '6.10' - '7.10' +cache: + directories: + - node_modules deploy: provider: npm on: diff --git a/CHANGELOG.md b/CHANGELOG.md index b9bcfcd351..bf4d923a35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Fix: Updating array of Dates now keeps it's type (was changing to array of ISO strings, issue #590), thanks to [David Riha](https://github.com/rihadavid) * Fix: NaN displayed when filter input is empty or negative number (#749), thanks to [Miguel Serrrano](https://github.com/miguel-s) * Fix: Addresses issue related to displaying iOS alert object containing title and body keys (#539), thanks to [Robert Martin del Campo](https://github.com/repertus) +* Feature: Adds support for localized push notifications if server version is high enough, thanks to [Florent Vilmart](https://github.com/flovilmart) ### 1.1.0 diff --git a/README.md b/README.md index ae168e8a93..2d843a1a9b 100644 --- a/README.md +++ b/README.md @@ -359,6 +359,27 @@ You can give read only access to a user on a per-app basis: With this configuration, user1 will have read only access to `myAppId1` and read/write access to `myAppId2`. +## Configuring Localized Push Notifications + +With the latest version of the [dashboard](https://www.npmjs.com/package/parse-dashboard), it is possible to send localized messages for push notifications. +You can provide a list of locales or languages you want to support for your dashboard users. + +```json +{ + "apps": [ + { + "serverURL": "http://localhost:1337/parse", + "appId": "myAppId", + "masterKey": "myMasterKey", + "appName": "My Parse Server App", + "iconName": "MyAppIcon.png", + "supportedPushLocales": ["en", "ru", "fr"] + } + ], + "iconsFolder": "icons" +} +``` + ## Run with Docker It is easy to use it with Docker. First build the image: diff --git a/src/dashboard/Data/Jobs/Jobs.react.js b/src/dashboard/Data/Jobs/Jobs.react.js index 1ed03a0db5..5fe43e592f 100644 --- a/src/dashboard/Data/Jobs/Jobs.react.js +++ b/src/dashboard/Data/Jobs/Jobs.react.js @@ -202,11 +202,11 @@ export default class Jobs extends TableView { + {

{'On this page you can create JobSchedule objects.'}


-
+ } icon='cloud-happy' /> ); } else { diff --git a/src/dashboard/Push/PushNew.react.js b/src/dashboard/Push/PushNew.react.js index ba797dc681..7a48e2ca0a 100644 --- a/src/dashboard/Push/PushNew.react.js +++ b/src/dashboard/Push/PushNew.react.js @@ -17,7 +17,6 @@ import Field from 'components/Field/Field.react'; import Fieldset from 'components/Fieldset/Fieldset.react'; import FieldStyles from 'components/Field/Field.scss'; import FlowView from 'components/FlowView/FlowView.react'; -import getSiteDomain from 'lib/getSiteDomain'; import history from 'dashboard/history'; import joinWithFinal from 'lib/joinWithFinal'; import Label from 'components/Label/Label.react'; @@ -121,7 +120,6 @@ let LocalizedMessageField = ({ } const XHR_KEY = 'PushNew'; -const TRANSLATE_MORE_INFO_URL = '/docs/android/guide#push-notifications-push-localization'; @subscribeTo('Schema', 'schema') @subscribeTo('PushAudiences', 'pushaudiences') @@ -159,28 +157,18 @@ export default class PushNew extends DashboardView { this.setState({ pushAudiencesFetched :true }); }); - let {xhr, promise} = this.context.currentApp.isLocalizationAvailable(); - this.xhrs.push(xhr); - promise.then(({ available }) => { - if (available) { - this.setState({ isLocalizationAvailable : true }); - let {xhr, promise} = this.context.currentApp.fetchPushLocales(); - this.xhrs.push(xhr); - promise.then(({ options }) => { - let filteredLocales = options.filter((locale) => { - if (locale === '' || locale === undefined) { - return false; - } - return true; - }); - this.setState({ - locales: filteredLocales, - availableLocales: filteredLocales - }); - }).always(() => { - this.setState({ loadingLocale: false }); - }); - } + const available = this.context.currentApp.isLocalizationAvailable(); + if (available) { + const locales = this.context.currentApp.fetchPushLocales(); + const filteredLocales = locales.filter((locale) => !(locale === '' || locale === undefined)); + this.setState({ + isLocalizationAvailable: true, + locales: filteredLocales, + availableLocales: filteredLocales + }); + } + this.setState({ + loadingLocale: false }); } @@ -203,6 +191,18 @@ export default class PushNew extends DashboardView { } const push_time = extractPushTime(changes); + + // Gather the translations, and inject into the payload + const needle = 'translation['; + Object.keys(changes).forEach((key) => { + // translations are stored as `tranlation[lang]` strings as keys, + // this is why we slice it this way + if (key.indexOf(needle) === 0) { + const locale = key.slice(needle.length, key.length - 1); + payload[`alert-${locale}`] = changes[key]; + } + }); + let body = { data: payload, where: changes.target || new Parse.Query(Parse.Installation), @@ -530,16 +530,6 @@ export default class PushNew extends DashboardView { setField('translation_enable', value || null); }} />} /> ); - if (fields.translation_enable) { - translationSegment.push( - -
- In some cases a locale may not be available for a user, either because they are running an earlier version of the SDK or their client has sent up an invalid locale. In those cases, they will receive the default message. - More info. -
-
- ); - } if (fields.translation_enable) { //locales change based on existing selection @@ -759,14 +749,9 @@ export default class PushNew extends DashboardView { // localized message is empty if (changes.translation_enable) { this.state.localizedMessages.forEach((message) => { - if (changes.data_type === 'json') { - if (!isValidJSON(message.value)) { - invalidInputMessages.push(Your message for {message.locale} is not valid JSON.); - } - } else if (!message.value || message.value.trim() === '') { + if (!message.value || message.value.trim() === '') { emptyInputMessages.push(`message for ${message.locale} locale`); } - }); } diff --git a/src/lib/ParseApp.js b/src/lib/ParseApp.js index 2e3edf0c15..661ef829e4 100644 --- a/src/lib/ParseApp.js +++ b/src/lib/ParseApp.js @@ -39,6 +39,7 @@ export default class ParseApp { serverInfo, production, iconName, + supportedPushLocales, }) { this.name = appName; this.createdAt = created_at ? new Date(created_at) : new Date(); @@ -59,6 +60,7 @@ export default class ParseApp { this.serverURL = serverURL; this.serverInfo = serverInfo; this.icon = iconName; + this.supportedPushLocales = supportedPushLocales; this.settings = { fields: {}, @@ -404,13 +406,11 @@ export default class ParseApp { } isLocalizationAvailable() { - let path = '/apps/' + this.slug + '/is_localization_available'; - return AJAX.abortableGet(path); + return !!this.serverInfo.features.push.localization; } fetchPushLocales() { - let path = '/apps/' + this.slug + '/installation_column_options?column=localeIdentifier'; - return AJAX.abortableGet(path); + return this.supportedPushLocales; } fetchPushLocaleDeviceCount(audienceId, where, locales) {