diff --git a/.circleci/config.yml b/.circleci/config.yml index d2b61f1fb..dd272f7f6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -91,15 +91,18 @@ commands: steps: - restore_cache: name: Restore npm cache - key: v1-npm-{{ checksum "package.json" }}-{{ arch }} + key: v2-npm-{{ checksum "package.json" }}-{{ arch }} - run: name: Getting JavaScript dependencies - command: NODE_ENV=development npm install + command: NODE_ENV=development npm install --ignore-scripts - save_cache: name: Save npm cache - key: v1-npm-{{ checksum "package.json" }}-{{ arch }} + key: v2-npm-{{ checksum "package.json" }}-{{ arch }} paths: - node_modules + - run: + name: "Run post install scripts" + command: make post-install pods-dependencies: description: "Get cocoapods dependencies" diff --git a/Makefile b/Makefile index 33549d57c..8823431df 100644 --- a/Makefile +++ b/Makefile @@ -74,6 +74,7 @@ clean: ## Cleans dependencies, previous builds and temp files @echo Cleanup finished post-install: + @./node_modules/.bin/patch-package @./node_modules/.bin/jetify @rm -f node_modules/intl/.babelrc @@ -84,8 +85,6 @@ post-install: @sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-relativeformat/package.json @sed -i'' -e 's|"./locale-data/complete.js": false|"./locale-data/complete.js": "./locale-data/complete.js"|g' node_modules/intl/package.json - @./node_modules/.bin/patch-package - start: | pre-run ## Starts the React Native packager server $(call start_packager) diff --git a/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java b/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java index 692dd5696..52573dcfe 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java @@ -20,6 +20,8 @@ import android.net.Uri; import android.os.Bundle; import android.os.Build; import android.provider.Settings.System; +import androidx.annotation.Nullable; +import android.util.Log; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; @@ -36,6 +38,9 @@ import com.wix.reactnativenotifications.core.JsIOHelper; import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME; +import com.mattermost.react_native_interface.ResolvePromise; +import com.facebook.react.bridge.WritableMap; + public class CustomPushNotification extends PushNotification { public static final int MESSAGE_NOTIFICATION_ID = 435345; public static final String GROUP_KEY_MESSAGES = "mm_group_key_messages"; @@ -45,6 +50,7 @@ public class CustomPushNotification extends PushNotification { private static final String PUSH_TYPE_MESSAGE = "message"; private static final String PUSH_TYPE_CLEAR = "clear"; + private static final String PUSH_TYPE_ID_LOADED = "id_loaded"; private static final String PUSH_TYPE_UPDATE_BADGE = "update_badge"; private NotificationChannel mHighImportanceChannel; @@ -96,16 +102,34 @@ public class CustomPushNotification extends PushNotification { @Override public void onReceived() throws InvalidNotificationException { - Bundle data = mNotificationProps.asBundle(); - final String channelId = data.getString("channel_id"); - final String type = data.getString("type"); - final String ackId = data.getString("ack_id"); + final Bundle initialData = mNotificationProps.asBundle(); + final String type = initialData.getString("type"); + final String ackId = initialData.getString("ack_id"); + final String postId = initialData.getString("post_id"); + final String channelId = initialData.getString("channel_id"); int notificationId = MESSAGE_NOTIFICATION_ID; if (ackId != null) { - notificationReceiptDelivery(ackId, type); + notificationReceiptDelivery(ackId, postId, type, new ResolvePromise() { + @Override + public void resolve(@Nullable Object value) { + if (PUSH_TYPE_ID_LOADED.equals(type)) { + Bundle response = (Bundle) value; + mNotificationProps = createProps(response); + } + } + + @Override + public void reject(String code, String message) { + Log.e("ReactNative", code + ": " + message); + } + }); } + // notificationReceiptDelivery can override mNotificationProps + // so we fetch the bundle again + final Bundle data = mNotificationProps.asBundle(); + if (channelId != null) { notificationId = channelId.hashCode(); @@ -501,8 +525,8 @@ public class CustomPushNotification extends PushNotification { return message.replaceFirst(senderName, "").replaceFirst(": ", "").trim(); } - private void notificationReceiptDelivery(String ackId, String type) { - ReceiptDelivery.send(context, ackId, type); + private void notificationReceiptDelivery(String ackId, String postId, String type, ResolvePromise promise) { + ReceiptDelivery.send(context, ackId, postId, type, promise); } private void createNotificationChannels() { diff --git a/android/app/src/main/java/com/mattermost/rnbeta/ReceiptDelivery.java b/android/app/src/main/java/com/mattermost/rnbeta/ReceiptDelivery.java index d63f9de60..e768179f4 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/ReceiptDelivery.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/ReceiptDelivery.java @@ -2,6 +2,7 @@ package com.mattermost.rnbeta; import android.content.Context; import androidx.annotation.Nullable; +import android.os.Bundle; import android.util.Log; import java.lang.System; @@ -18,13 +19,14 @@ import org.json.JSONException; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.Arguments; import com.mattermost.react_native_interface.ResolvePromise; public class ReceiptDelivery { static final String CURRENT_SERVER_URL = "@currentServerUrl"; - public static void send (Context context, final String ackId, final String type) { + public static void send(Context context, final String ackId, final String postId, final String type, ResolvePromise promise) { final ReactApplicationContext reactApplicationContext = new ReactApplicationContext(context); MattermostCredentialsHelper.getCredentialsForCurrentServer(reactApplicationContext, new ResolvePromise() { @@ -47,17 +49,22 @@ public class ReceiptDelivery { } Log.i("ReactNative", String.format("Send receipt delivery ACK=%s TYPE=%s to URL=%s with TOKEN=%s", ackId, type, serverUrl, token)); - execute(serverUrl, token, ackId, type); + execute(serverUrl, postId, token, ackId, type, promise); } } }); } - protected static void execute(String serverUrl, String token, String ackId, String type) { - if (token == null || serverUrl == null) { + protected static void execute(String serverUrl, String postId, String token, String ackId, String type, ResolvePromise promise) { + if (token == null) { + promise.reject("Receipt delivery failure", "Invalid token"); return; } + if (serverUrl == null) { + promise.reject("Receipt delivery failure", "Invalid server URL"); + } + JSONObject json; long receivedAt = System.currentTimeMillis(); @@ -67,8 +74,10 @@ public class ReceiptDelivery { json.put("received_at", receivedAt); json.put("platform", "android"); json.put("type", type); + json.put("post_id", postId); } catch (JSONException e) { Log.e("ReactNative", "Receipt delivery failed to build json payload"); + promise.reject("Receipt delivery failure", e.toString()); return; } @@ -86,9 +95,24 @@ public class ReceiptDelivery { .build(); try { - client.newCall(request).execute(); + Response response = client.newCall(request).execute(); + String responseBody = response.body().toString(); + if (response.code() != 200) { + throw new Exception(responseBody); + } + JSONObject jsonResponse = new JSONObject(responseBody); + Bundle bundle = new Bundle(); + String keys[] = new String[] {"post_id", "category", "message", "team_id", "channel_id", "channel_name", "type", "sender_id", "sender_name", "version"}; + for (int i = 0; i < keys.length; i++) { + String key = keys[i]; + if (jsonResponse.has(key)) { + bundle.putString(key, jsonResponse.getString(key)); + } + } + promise.resolve(bundle); } catch (Exception e) { Log.e("ReactNative", "Receipt delivery failed to send"); + promise.reject("Receipt delivery failure", e.toString()); } } } diff --git a/app/components/reactions/index.js b/app/components/reactions/index.js index 2ea6fe1a5..62dc1914d 100644 --- a/app/components/reactions/index.js +++ b/app/components/reactions/index.js @@ -14,6 +14,7 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; import {getChannel, isChannelReadOnlyById} from 'mattermost-redux/selectors/entities/channels'; import {addReaction} from 'app/actions/views/emoji'; +import {MAX_ALLOWED_REACTIONS} from 'app/constants/emoji'; import Reactions from './reactions'; @@ -27,17 +28,23 @@ function makeMapStateToProps() { const channelIsArchived = channel.delete_at !== 0; const channelIsReadOnly = isChannelReadOnlyById(state, channelId); + const currentUserId = getCurrentUserId(state); + const reactions = getReactionsForPostSelector(state, ownProps.postId); + let canAddReaction = true; let canRemoveReaction = true; + let canAddMoreReactions = true; if (channelIsArchived || channelIsReadOnly) { canAddReaction = false; canRemoveReaction = false; + canAddMoreReactions = false; } else if (hasNewPermissions(state)) { canAddReaction = haveIChannelPermission(state, { team: teamId, channel: channelId, permission: Permissions.ADD_REACTION, }); + canAddMoreReactions = Object.values(reactions).length < MAX_ALLOWED_REACTIONS; canRemoveReaction = haveIChannelPermission(state, { team: teamId, channel: channelId, @@ -45,14 +52,12 @@ function makeMapStateToProps() { }); } - const currentUserId = getCurrentUserId(state); - const reactions = getReactionsForPostSelector(state, ownProps.postId); - return { currentUserId, reactions, theme: getTheme(state), canAddReaction, + canAddMoreReactions, canRemoveReaction, }; }; diff --git a/app/components/reactions/reactions.js b/app/components/reactions/reactions.js index 65edb8ffc..f144bec1a 100644 --- a/app/components/reactions/reactions.js +++ b/app/components/reactions/reactions.js @@ -5,7 +5,7 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import { Image, - ScrollView, + View, } from 'react-native'; import {intlShape} from 'react-intl'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; @@ -26,13 +26,14 @@ export default class Reactions extends PureComponent { getReactionsForPost: PropTypes.func.isRequired, removeReaction: PropTypes.func.isRequired, }).isRequired, + canAddReaction: PropTypes.bool, + canAddMoreReactions: PropTypes.bool, + canRemoveReaction: PropTypes.bool.isRequired, currentUserId: PropTypes.string.isRequired, position: PropTypes.oneOf(['right', 'left']), postId: PropTypes.string.isRequired, reactions: PropTypes.object, theme: PropTypes.object.isRequired, - canAddReaction: PropTypes.bool, - canRemoveReaction: PropTypes.bool.isRequired, }; static defaultProps = { @@ -132,7 +133,7 @@ export default class Reactions extends PureComponent { }; render() { - const {position, reactions, canAddReaction} = this.props; + const {position, reactions, canAddMoreReactions} = this.props; const styles = getStyleSheet(this.props.theme); if (!reactions) { @@ -140,7 +141,7 @@ export default class Reactions extends PureComponent { } let addMoreReactions = null; - if (canAddReaction) { + if (canAddMoreReactions) { addMoreReactions = ( + {reactionElements} - + ); } } @@ -207,5 +203,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { paddingHorizontal: 6, width: 40, }, + reactionsContainer: { + flex: 1, + flexDirection: 'row', + flexWrap: 'wrap', + alignContent: 'flex-start', + }, }; }); diff --git a/app/constants/emoji.js b/app/constants/emoji.js index e3742cfa7..b0526efa9 100644 --- a/app/constants/emoji.js +++ b/app/constants/emoji.js @@ -2,3 +2,4 @@ // See LICENSE.txt for license information. export const ALL_EMOJIS = 'all_emojis'; +export const MAX_ALLOWED_REACTIONS = 40; \ No newline at end of file diff --git a/app/push_notifications/push_notifications.ios.js b/app/push_notifications/push_notifications.ios.js index b04850f0c..8868fd94d 100644 --- a/app/push_notifications/push_notifications.ios.js +++ b/app/push_notifications/push_notifications.ios.js @@ -38,7 +38,7 @@ class PushNotification { this.deviceNotification = { data, foreground, - message: data.message, + message: data.body || data.message, userInfo: data.userInfo, userInteraction, }; @@ -155,9 +155,10 @@ class PushNotification { ephemeralStore.appStartedFromPushNotification = true; } + const data = notification.getData(); const info = { - ...notification.getData(), - message: notification.getMessage(), + ...data, + message: data.body || notification.getMessage(), }; if (!userInteraction) { @@ -166,9 +167,10 @@ class PushNotification { }; onNotificationReceivedForeground = (notification) => { + const data = notification.getData(); const info = { - ...notification.getData(), - message: notification.getMessage(), + ...data, + message: data.body || notification.getMessage(), }; this.handleNotification(info, true, false); }; @@ -177,9 +179,10 @@ class PushNotification { if (action.identifier === REPLY_ACTION) { this.handleReply(notification, action.text, completion); } else { + const data = notification.getData(); const info = { - ...notification.getData(), - message: notification.getMessage(), + ...data, + message: data.body || notification.getMessage(), }; this.handleNotification(info, false, true); completion(); diff --git a/app/screens/post_options/index.js b/app/screens/post_options/index.js index 17d8269a0..fb5de23a8 100644 --- a/app/screens/post_options/index.js +++ b/app/screens/post_options/index.js @@ -14,6 +14,7 @@ import { setUnreadPost, } from 'mattermost-redux/actions/posts'; import {General, Permissions} from 'mattermost-redux/constants'; +import {makeGetReactionsForPost} from 'mattermost-redux/selectors/entities/posts'; import {getChannel, getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels'; import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users'; import {getConfig, getLicense, hasNewPermissions} from 'mattermost-redux/selectors/entities/general'; @@ -22,95 +23,104 @@ import {haveIChannelPermission} from 'mattermost-redux/selectors/entities/roles' import {getCurrentTeamId, getCurrentTeamUrl} from 'mattermost-redux/selectors/entities/teams'; import {canEditPost} from 'mattermost-redux/utils/post_utils'; +import {MAX_ALLOWED_REACTIONS} from 'app/constants/emoji'; import {THREAD} from 'app/constants/screen'; import {addReaction} from 'app/actions/views/emoji'; import {getDimensions, isLandscape} from 'app/selectors/device'; import PostOptions from './post_options'; -export function mapStateToProps(state, ownProps) { - const post = ownProps.post; - const channel = getChannel(state, post.channel_id) || {}; - const config = getConfig(state); - const license = getLicense(state); - const currentUserId = getCurrentUserId(state); - const currentTeamId = getCurrentTeamId(state); - const currentChannelId = getCurrentChannelId(state); +export function makeMapStateToProps() { + const getReactionsForPostSelector = makeGetReactionsForPost(); - const channelIsArchived = channel.delete_at !== 0; + return (state, ownProps) => { + const post = ownProps.post; + const channel = getChannel(state, post.channel_id) || {}; + const config = getConfig(state); + const license = getLicense(state); + const currentUserId = getCurrentUserId(state); + const currentTeamId = getCurrentTeamId(state); + const currentChannelId = getCurrentChannelId(state); + const reactions = getReactionsForPostSelector(state, post.id); + const channelIsArchived = channel.delete_at !== 0; - let canAddReaction = true; - let canReply = true; - let canCopyPermalink = true; - let canCopyText = false; - let canEdit = false; - let canEditUntil = -1; - let {canDelete} = ownProps; - let canFlag = true; - let canPin = true; + let canAddReaction = true; + let canReply = true; + let canCopyPermalink = true; + let canCopyText = false; + let canEdit = false; + let canEditUntil = -1; + let {canDelete} = ownProps; + let canFlag = true; + let canPin = true; - if (hasNewPermissions(state)) { - canAddReaction = haveIChannelPermission(state, { - team: currentTeamId, - channel: post.channel_id, - permission: Permissions.ADD_REACTION, - }); - } - - if (ownProps.location === THREAD) { - canReply = false; - } - - if (channelIsArchived || ownProps.channelIsReadOnly) { - canAddReaction = false; - canReply = false; - canDelete = false; - canPin = false; - } else { - canEdit = canEditPost(state, config, license, currentTeamId, currentChannelId, currentUserId, post); - if (canEdit && license.IsLicensed === 'true' && - (config.AllowEditPost === General.ALLOW_EDIT_POST_TIME_LIMIT || (config.PostEditTimeLimit !== -1 && config.PostEditTimeLimit !== '-1')) - ) { - canEditUntil = post.create_at + (config.PostEditTimeLimit * 1000); + if (hasNewPermissions(state)) { + canAddReaction = haveIChannelPermission(state, { + team: currentTeamId, + channel: post.channel_id, + permission: Permissions.ADD_REACTION, + }); } - } - if (ownProps.isSystemMessage) { - canAddReaction = false; - canReply = false; - canCopyPermalink = false; - canEdit = false; - canPin = false; - canFlag = false; - } - if (ownProps.hasBeenDeleted) { - canDelete = false; - } + if (ownProps.location === THREAD) { + canReply = false; + } - if (!ownProps.showAddReaction) { - canAddReaction = false; - } + if (channelIsArchived || ownProps.channelIsReadOnly) { + canAddReaction = false; + canReply = false; + canDelete = false; + canPin = false; + } else { + canEdit = canEditPost(state, config, license, currentTeamId, currentChannelId, currentUserId, post); + if (canEdit && license.IsLicensed === 'true' && + (config.AllowEditPost === General.ALLOW_EDIT_POST_TIME_LIMIT || (config.PostEditTimeLimit !== -1 && config.PostEditTimeLimit !== '-1')) + ) { + canEditUntil = post.create_at + (config.PostEditTimeLimit * 1000); + } + } - if (!ownProps.isSystemMessage && ownProps.managedConfig?.copyAndPasteProtection !== 'true' && post.message) { - canCopyText = true; - } + if (ownProps.isSystemMessage) { + canAddReaction = false; + canReply = false; + canCopyPermalink = false; + canEdit = false; + canPin = false; + canFlag = false; + } + if (ownProps.hasBeenDeleted) { + canDelete = false; + } - return { - ...getDimensions(state), - canAddReaction, - canReply, - canCopyPermalink, - canCopyText, - canEdit, - canEditUntil, - canDelete, - canFlag, - canPin, - currentTeamUrl: getCurrentTeamUrl(state), - currentUserId: getCurrentUserId(state), - isMyPost: currentUserId === post.user_id, - theme: getTheme(state), - isLandscape: isLandscape(state), + if (!ownProps.showAddReaction) { + canAddReaction = false; + } + + if (!ownProps.isSystemMessage && ownProps.managedConfig?.copyAndPasteProtection !== 'true' && post.message) { + canCopyText = true; + } + + if (reactions && Object.values(reactions).length >= MAX_ALLOWED_REACTIONS) { + canAddReaction = false; + } + + return { + ...getDimensions(state), + canAddReaction, + canReply, + canCopyPermalink, + canCopyText, + canEdit, + canEditUntil, + canDelete, + canFlag, + canPin, + currentTeamUrl: getCurrentTeamUrl(state), + currentUserId, + isMyPost: currentUserId === post.user_id, + theme: getTheme(state), + isLandscape: isLandscape(state), + }; }; } @@ -129,4 +139,4 @@ function mapDispatchToProps(dispatch) { }; } -export default connect(mapStateToProps, mapDispatchToProps)(PostOptions); +export default connect(makeMapStateToProps, mapDispatchToProps)(PostOptions); diff --git a/app/screens/post_options/index.test.js b/app/screens/post_options/index.test.js index b3b8cff04..d07bd1131 100644 --- a/app/screens/post_options/index.test.js +++ b/app/screens/post_options/index.test.js @@ -1,6 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {mapStateToProps} from './index'; +import {makeMapStateToProps} from './index'; import * as channelSelectors from 'mattermost-redux/selectors/entities/channels'; import * as generalSelectors from 'mattermost-redux/selectors/entities/general'; @@ -24,10 +24,24 @@ deviceSelectors.getDimensions = jest.fn(); deviceSelectors.isLandscape = jest.fn(); preferencesSelectors.getTheme = jest.fn(); -describe('mapStateToProps', () => { - const baseState = {}; +describe('makeMapStateToProps', () => { + const baseState = { + entities: { + posts: { + posts: { + post_id: {}, + }, + reactions: { + post_id: {}, + }, + }, + }, + }; + const baseOwnProps = { - post: {}, + post: { + id: 'post_id', + }, }; test('canFlag is false for system messages', () => { @@ -36,6 +50,7 @@ describe('mapStateToProps', () => { isSystemMessage: true, }; + const mapStateToProps = makeMapStateToProps(); const props = mapStateToProps(baseState, ownProps); expect(props.canFlag).toBe(false); }); @@ -46,7 +61,8 @@ describe('mapStateToProps', () => { isSystemMessage: false, }; + const mapStateToProps = makeMapStateToProps(); const props = mapStateToProps(baseState, ownProps); expect(props.canFlag).toBe(true); }); -}); \ No newline at end of file +}); diff --git a/ios/NotificationService/NotificationService.swift b/ios/NotificationService/NotificationService.swift index 371a7f36f..5e686d4c5 100644 --- a/ios/NotificationService/NotificationService.swift +++ b/ios/NotificationService/NotificationService.swift @@ -9,15 +9,37 @@ class NotificationService: UNNotificationServiceExtension { override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) - if let bestAttemptContent = bestAttemptContent { + let ackId = bestAttemptContent.userInfo["ack_id"] + let type = bestAttemptContent.userInfo["type"] + let postId = bestAttemptContent.userInfo["post_id"] UploadSession.shared.notificationReceipt( - notificationId: bestAttemptContent.userInfo["ack_id"], + notificationId: ackId, receivedAt: Date().millisencondsSince1970, - type: bestAttemptContent.userInfo["type"] - ) - - contentHandler(bestAttemptContent) + type: type, + postId: postId + ) { data, error in + if (type as? String == "id_loaded") { + guard let data = data, error == nil else { + return + } + + let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as! Dictionary + bestAttemptContent.title = json!["channel_name"] as! String + bestAttemptContent.body = json!["message"] as! String + + bestAttemptContent.userInfo["channel_name"] = json!["channel_name"] as! String + bestAttemptContent.userInfo["team_id"] = json!["team_id"] as? String + bestAttemptContent.userInfo["sender_id"] = json!["sender_id"] as! String + bestAttemptContent.userInfo["sender_name"] = json!["sender_name"] as! String + bestAttemptContent.userInfo["root_id"] = json!["root_id"] as? String + bestAttemptContent.userInfo["override_username"] = json!["override_username"] as? String + bestAttemptContent.userInfo["override_icon_url"] = json!["override_icon_url"] as? String + bestAttemptContent.userInfo["from_webhook"] = json!["from_webhook"] as? String + } + + contentHandler(bestAttemptContent) + } } } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b90ad2810..2cf1c1e62 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -25,15 +25,6 @@ PODS: - React - KeyboardTrackingView (5.5.0): - React - - libwebp (1.0.3): - - libwebp/demux (= 1.0.3) - - libwebp/mux (= 1.0.3) - - libwebp/webp (= 1.0.3) - - libwebp/demux (1.0.3): - - libwebp/webp - - libwebp/mux (1.0.3): - - libwebp/demux - - libwebp/webp (1.0.3) - RCTRequired (0.61.2) - RCTTypeSafety (0.61.2): - FBLazyVector (= 0.61.2) @@ -268,10 +259,6 @@ PODS: - React - RNDeviceInfo (2.1.2): - React - - RNFastImage (7.0.2): - - React - - SDWebImage (~> 5.0) - - SDWebImageWebPCoder (~> 0.2.3) - RNGestureHandler (1.4.1): - React - RNKeychain (4.0.1): @@ -287,12 +274,6 @@ PODS: - React - RNVectorIcons (6.6.0): - React - - SDWebImage (5.2.2): - - SDWebImage/Core (= 5.2.2) - - SDWebImage/Core (5.2.2) - - SDWebImageWebPCoder (0.2.5): - - libwebp (~> 1.0) - - SDWebImage/Core (~> 5.0) - Sentry (4.4.0): - Sentry/Core (= 4.4.0) - Sentry/Core (4.4.0) @@ -348,7 +329,6 @@ DEPENDENCIES: - rn-fetch-blob (from `../node_modules/rn-fetch-blob`) - "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)" - RNDeviceInfo (from `../node_modules/react-native-device-info`) - - RNFastImage (from `../node_modules/react-native-fast-image`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNKeychain (from `../node_modules/react-native-keychain`) - RNReactNativeDocViewer (from `../node_modules/react-native-doc-viewer`) @@ -363,9 +343,6 @@ DEPENDENCIES: SPEC REPOS: https://github.com/cocoapods/specs.git: - boost-for-react-native - - libwebp - - SDWebImage - - SDWebImageWebPCoder - Sentry - Swime - XCDYouTubeKit @@ -458,8 +435,6 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-community/async-storage" RNDeviceInfo: :path: "../node_modules/react-native-device-info" - RNFastImage: - :path: "../node_modules/react-native-fast-image" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNKeychain: @@ -487,7 +462,6 @@ SPEC CHECKSUMS: glog: 1f3da668190260b06b429bb211bfbee5cd790c28 jail-monkey: 0830c18bb6f085938a4c8529d554f9b29230a5a4 KeyboardTrackingView: d4d7236123b401ed9b1e02869e7943117740c522 - libwebp: 057912d6d0abfb6357d8bb05c0ea470301f5d61e RCTRequired: c639d59ed389cfb1f1203f65c2ea946d8ec586e2 RCTTypeSafety: dc23fb655d6c77667c78e327bf661bc11e3b8aec RCTYouTube: a36b9960e040063877fb8dc61fff19d3bd1a97ff @@ -523,7 +497,6 @@ SPEC CHECKSUMS: rn-fetch-blob: 3258c6483235bb7daf16cf921306ffb18406071f RNCAsyncStorage: 5ae4d57458804e99f73d427214442a6b10a53856 RNDeviceInfo: fd8296de6fca8b743cdc499b896f48e8a9f1faf5 - RNFastImage: 9b0c22643872bb7494c8d87bbbb66cc4c0d9e7a2 RNGestureHandler: 4cb47a93019c1a201df2644413a0a1569a51c8aa RNKeychain: 45dbd50d1ac4bd42c3740f76ffb135abf05746d0 RNReactNativeDocViewer: 571c6ac38483531b8fb521d02a6ba54652ed10a7 @@ -531,8 +504,6 @@ SPEC CHECKSUMS: RNSentry: 2803ba8c8129dcf26b79e9b4d8c80168be6e4390 RNSVG: be27aa7c58819f97399388ae53d7fa0572f32c7f RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4 - SDWebImage: 5fcdb02cc35e05fc35791ec514b191d27189f872 - SDWebImageWebPCoder: 947093edd1349d820c40afbd9f42acb6cdecd987 Sentry: 26650184fe71eb7476dfd2737acb5ea6cc64b4b1 Swime: d7b2c277503b6cea317774aedc2dce05613f8b0b XCDYouTubeKit: 4ca4e3322fa556ec1c32932d378365c15ce1386e diff --git a/ios/UploadAttachments/UploadAttachments/UploadSession.swift b/ios/UploadAttachments/UploadAttachments/UploadSession.swift index d0dcd858e..7cad6f6ee 100644 --- a/ios/UploadAttachments/UploadAttachments/UploadSession.swift +++ b/ios/UploadAttachments/UploadAttachments/UploadSession.swift @@ -126,8 +126,12 @@ import os.log } }) } - + public func notificationReceipt(notificationId: Any?, receivedAt: Int, type: Any?) { + notificationReceipt(notificationId:notificationId, receivedAt:receivedAt, type:type, postId:nil, completion:{_, _ in}) + } + + public func notificationReceipt(notificationId: Any?, receivedAt: Int, type: Any?, postId: Any? = nil, completion: @escaping (Data?, Error?) -> Void) { if (notificationId != nil) { let store = StoreManager.shared() as StoreManager let entities = store.getEntities(true) @@ -142,18 +146,23 @@ import os.log "id": notificationId as Any, "received_at": receivedAt, "platform": "ios", - "type": type as Any + "type": type as Any, + "post_id": postId as Any ] if !JSONSerialization.isValidJSONObject(jsonObject) {return} - + guard let url = URL(string: urlString) else {return} var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("Bearer \(sessionToken!)", forHTTPHeaderField: "Authorization") request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") request.httpBody = try? JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted) - URLSession(configuration: .ephemeral).dataTask(with: request).resume() + + let task = URLSession(configuration: .ephemeral).dataTask(with: request) { data, _, error in + completion(data, error) + } + task.resume() } } } diff --git a/package-lock.json b/package-lock.json index 731fc8b5c..b605400c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4673,7 +4673,7 @@ "dependencies": { "json5": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "dev": true } diff --git a/patches/react-native-notifications+2.0.6.patch b/patches/react-native-notifications+2.0.6.patch index 56857ce1d..041401825 100644 --- a/patches/react-native-notifications+2.0.6.patch +++ b/patches/react-native-notifications+2.0.6.patch @@ -165,7 +165,7 @@ index 0d70024..47b962e 100644 PushNotificationProps asProps(); } diff --git a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java -index 5e4e3d2..871e157 100644 +index 5e4e3d2..ec37f87 100644 --- a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java +++ b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java @@ -1,5 +1,6 @@ @@ -175,16 +175,28 @@ index 5e4e3d2..871e157 100644 import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; -@@ -20,7 +21,9 @@ import com.wix.reactnativenotifications.core.InitialNotificationHolder; +@@ -20,18 +21,20 @@ import com.wix.reactnativenotifications.core.InitialNotificationHolder; import com.wix.reactnativenotifications.core.JsIOHelper; import com.wix.reactnativenotifications.core.NotificationIntentAdapter; import com.wix.reactnativenotifications.core.ProxyService; +import com.wix.reactnativenotifications.core.helpers.ScheduleNotificationHelper; - + +import static com.wix.reactnativenotifications.Defs.LOGTAG; import static com.wix.reactnativenotifications.Defs.NOTIFICATION_OPENED_EVENT_NAME; import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME; import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME; + + public class PushNotification implements IPushNotification { + ++ protected PushNotificationProps mNotificationProps; + final protected Context mContext; + final protected AppLifecycleFacade mAppLifecycleFacade; + final protected AppLaunchHelper mAppLaunchHelper; + final protected JsIOHelper mJsIOHelper; +- final protected PushNotificationProps mNotificationProps; + final protected AppVisibilityListener mAppVisibilityListener = new AppVisibilityListener() { + @Override + public void onAppVisible() { @@ -80,6 +83,41 @@ public class PushNotification implements IPushNotification { return postNotification(notificationId); }