Merge branch 'master' into mark-as-unread
This commit is contained in:
commit
b69c6af2b4
15 changed files with 264 additions and 163 deletions
|
|
@ -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"
|
||||
|
|
|
|||
3
Makefile
3
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 = (
|
||||
<TouchableWithFeedback
|
||||
key='addReaction'
|
||||
|
|
@ -173,14 +174,9 @@ export default class Reactions extends PureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
alwaysBounceHorizontal={false}
|
||||
horizontal={true}
|
||||
overScrollMode='never'
|
||||
keyboardShouldPersistTaps={'always'}
|
||||
>
|
||||
<View style={styles.reactionsContainer}>
|
||||
{reactionElements}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -207,5 +203,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
paddingHorizontal: 6,
|
||||
width: 40,
|
||||
},
|
||||
reactionsContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
alignContent: 'flex-start',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
export const ALL_EMOJIS = 'all_emojis';
|
||||
export const MAX_ALLOWED_REACTIONS = 40;
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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<String,Any>
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
package-lock.json
generated
2
package-lock.json
generated
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue