Compare commits

...

49 Commits

Author SHA1 Message Date
Harrison Healey
0bd742bc50 Bump app build number to 208 (#2979) 2019-07-16 10:31:06 -04:00
Miguel Alatzar
b98812f84f [MM-17016] Ensure Flatlist is mounted prior to calling scrollToIndex (#2977)
* Ensure Flatlist is mounted prior to calling scrollToIndex

* Address review comment
2019-07-16 21:09:33 +08:00
Harrison Healey
71f96fc1cf Update fastlane 2019-07-12 12:30:07 -04:00
Harrison Healey
33f18db34d Temporarily disable Android source maps 2019-07-12 12:00:44 -04:00
Harrison Healey
82e6aac37f Bump app build number to 207 (#2972) 2019-07-12 09:04:21 -04:00
Elias Nahum
38009051bb Fix android crash on Android O & P after upgrading target to 28 (#2968) 2019-07-10 10:31:44 -04:00
Elias Nahum
955cf47921 MM-16857 Fix long posts (#2967) 2019-07-09 11:56:12 -04:00
Saturnino Abril
54530f28a2 fix app chrashing when sending invalid slash command (#2966) 2019-07-09 22:12:31 +08:00
Elias Nahum
18e2da6e3b translations PR 20190708 (#2962) 2019-07-08 18:46:25 -04:00
Elias Nahum
6b69b9fa40 Bump app build number to 206 (#2963)
* Bump app build number to 206

* Set Android targetSdkVersion to API level 28
2019-07-08 18:44:38 -04:00
Elias Nahum
78602977f9 Update device info ref (#2953) 2019-07-08 18:19:04 -04:00
Harrison Healey
85b07cd8b3 Bump app build number to 205 (#2960) 2019-07-08 09:45:59 -04:00
Elias Nahum
c56cd566d2 MM-16823 Fix "Freeze" after deleting cache (#2956) 2019-07-08 09:23:49 -04:00
Harrison Healey
9f85a0a33e MM-16695 Update mattermost-redux to add additional null check (#2951)
* MM-16695 Update mattermost-redux to add additional null check

* Update mattermost-redux
2019-07-08 09:13:39 -04:00
Jesse Hallam
954868f572 MM-16442: count actual reactions before falling back to has_reactions (#2910)
* MM-16442: leverage post hasReactions utils function

This handles backwards compatibility with servers that do not have post
metadata, allowing the client to detect the presence of reactions that
in turn might need to be loaded when the component is mounted.

* temporary mattermost-redux commit for testing

* revert usage of hasReactions, and count directly

We still fallback to has_reactions if set, in case we're connected to a server without post metadata.

* revert mattermost-redux changes
2019-07-06 03:46:51 -04:00
Elias Nahum
e3ffb037e5 Bump app build number to 204 (#2955) 2019-07-05 14:34:02 -04:00
Elias Nahum
ca78c027a0 translations PR 20190701 (#2946) 2019-07-04 11:44:39 -04:00
Elias Nahum
dd28c38196 Place the new message indicator at the top of the screen (#2943)
* Place the new message indicator at the top of the screen

* Add unit tests for channel postVisibility reducer
2019-07-02 12:28:55 -04:00
Elias Nahum
e7a2f4d6b1 Fix AD/LDAP & SAML profile sync properties (#2941) 2019-07-02 10:11:31 -04:00
Harrison Healey
6fc8884ef3 Bump app build number to 203 (#2935) 2019-06-27 12:12:06 -04:00
Elias Nahum
89d159a659 Update redux update to use the release-5.13 branch (#2934) 2019-06-27 11:12:19 -04:00
Miguel Alatzar
a33f26e827 Update jsc to intl version (#2931) 2019-06-27 11:10:13 -04:00
Harrison Healey
8f9a660433 Bump app build number to 202 (#2933) 2019-06-27 09:42:24 -04:00
Miguel Alatzar
749f8a50b3 Check if entities is nil (#2930) 2019-06-26 14:51:28 -04:00
Miguel Alatzar
f338279444 [MM-16456] Ensure Flatlist is mounted prior to calling scrollToOffset (#2925)
* Ensure Flatlist is mounted prior to calling scrollToOffset

* Make linter happy
2019-06-26 17:02:40 +08:00
Elias Nahum
8ad2456d5e MM-16463 Fix replying from push notification to a message in a thread (#2927) 2019-06-26 17:00:50 +08:00
Elias Nahum
442ce13171 translations PR 20190624 (#2919) 2019-06-25 08:46:03 -04:00
Jesse Hallam
611a2c2c3c cherry-pick https://github.com/mattermost/mattermost-mobile/pull/2917 2019-06-24 20:07:30 -03:00
Harrison Healey
2fdb9ce35e MM-16472 Fix renderItem not returning anything (#2909) 2019-06-21 16:12:46 -04:00
Chris Duarte
31a5d46ee0 Ios accessibility fixes (#2842)
* UCHAT-4371 // MM-15771 - iOS - Larger Fonts Cut Off Text in menu and settings screens

revert padding right

* Update snapshot
2019-06-21 16:10:49 -04:00
Elias Nahum
fe0450e4c7 MM-16392 Split view support & hide sidebar on tablets (#2898) 2019-06-21 14:10:59 -04:00
Elias Nahum
cd724ce3c4 Bump app build number to 201 (#2903) 2019-06-19 18:48:07 -04:00
Elias Nahum
587514d868 MM-16432 Fix missing posts when using since API (#2901) 2019-06-19 18:39:09 -04:00
Elias Nahum
2a1258fd8c Fix crash on Android by Updating JSC (#2900) 2019-06-19 16:19:31 -04:00
Elias Nahum
2f25548f47 Bump app build number to 199 (#2896) 2019-06-18 17:43:06 -04:00
Miguel Alatzar
07c9556dfe Return if message is null (#2895) 2019-06-18 17:13:30 -04:00
Elias Nahum
be488178be Bump app version number to 1.21.0 (#2894) 2019-06-18 16:10:14 -04:00
Elias Nahum
8e013a7ede Update gradle to use android-jsc as instructed (#2893) 2019-06-18 13:08:24 -07:00
Elias Nahum
e64e427001 translations PR 20190617 (#2891) 2019-06-18 08:44:14 -04:00
Miguel Alatzar
1734202326 Update react-native-device-info and fix status value: (#2892) 2019-06-17 20:23:49 -07:00
Elias Nahum
81e328864e MM-16239 disable fields according to SAML or LDAP (#2878)
* MM-16239 disable fields according to SAML or LDAP

* fix typo
2019-06-17 15:28:14 -04:00
Elias Nahum
46788fa8f6 Set post list data and timestamp based on the user timezone if available (#2888) 2019-06-17 15:26:03 -04:00
Eli Yukelzon
c84b05217b MM-15486 - Archived channels appear incorrectly on mobile (#2882)
* properly separate archived channels from 'other'

* make sure fetch is performed to display full list

* reverted unneeded change

* corrected display behaviour

* code cleanup

* redux commit reference updated
2019-06-17 10:25:08 -04:00
Miguel Alatzar
630a3db2a6 [MM-15668] Member list items are selectable and enabled only when canManagerUsers is true (#2844)
* Member list items are selectable only when canManagerUsers is true

* Address review comment
2019-06-16 11:16:15 -07:00
Renato Peterman
a565c96fcf [MM-14917] RN Android: Push notification settings are only saved when closing settings page (#2735)
* Add required action and props

* Allow callback param to setModilePush and setMobilePushStats

* Added saveNotificationProps method to save data on alert save action

* Fix import order

* Fix import path

* Fix import order

* Added defaultProps currentUser to NotificationSettingsMobileAndroid

* Added missing trailing comma
2019-06-15 14:29:44 -04:00
Gleb
dddfcbc362 fix ios sso cookie bug when running on top-level domain (#2858) 2019-06-15 14:23:23 -04:00
Woolim Cho
a75c4d0a02 Add key to SlideUpPanel (#2845) 2019-06-15 14:22:19 -04:00
Miguel Alatzar
978c2cdda5 Update react-native-notifications (#2881) 2019-06-15 14:19:07 -04:00
Elias Nahum
b1245f72c9 MM-16095 Android share extension to use keychain for authentication (#2876)
* MM-16095 Android share extension to use keychain for authentication

* Remove unnecessary comment
2019-06-15 14:15:03 -04:00
82 changed files with 931 additions and 388 deletions

View File

@@ -87,7 +87,7 @@ if (System.getenv("SENTRY_ENABLED") == "true") {
flavorAware: false
]
apply from: "../../node_modules/react-native-sentry/sentry.gradle"
//apply from: "../../node_modules/react-native-sentry/sentry.gradle"
}
/**
@@ -114,16 +114,16 @@ android {
}
packagingOptions {
pickFirst 'lib/x86_64/libjsc.so'
pickFirst 'lib/arm64-v8a/libjsc.so'
pickFirst '**/libjsc.so'
pickFirst '**/libc++_shared.so'
}
defaultConfig {
applicationId "com.mattermost.rnbeta"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 198
versionName "1.20.0"
versionCode 208
versionName "1.21.0"
multiDexEnabled = true
ndk {
abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
@@ -193,9 +193,6 @@ repositories {
configurations.all {
resolutionStrategy {
eachDependency { DependencyResolveDetails details ->
if (details.requested.name == 'android-jsc') {
details.useTarget group: details.requested.group, name: 'android-jsc-intl', version: 'r241213'
}
if (details.requested.name == 'play-services-base') {
details.useTarget group: details.requested.group, name: details.requested.name, version: '15.0.1'
}
@@ -213,6 +210,9 @@ configurations.all {
}
dependencies {
// Make sure to put android-jsc at the top
implementation "org.webkit:android-jsc-intl:r241213"
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation 'com.android.support:design:28.0.0'

View File

@@ -26,7 +26,7 @@
],
"services": {
"analytics_service": {
"status": 0
"status": 2
},
"appinvite_service": {
"status": 1,
@@ -57,7 +57,7 @@
],
"services": {
"analytics_service": {
"status": 0
"status": 2
},
"appinvite_service": {
"status": 1,
@@ -88,7 +88,7 @@
],
"services": {
"analytics_service": {
"status": 0
"status": 2
},
"appinvite_service": {
"status": 1,

View File

@@ -44,7 +44,8 @@
android:exported="false" />
<activity
android:name="com.reactnativenavigation.controllers.NavigationActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"/>
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:resizeableActivity="true"/>
<activity
android:name="com.mattermost.share.ShareActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"

View File

@@ -57,7 +57,6 @@ public class InitializationModule extends ReactContextBaseJavaModule {
*
* Miscellaneous:
* MattermostManaged.Config
* replyFromPushNotification
*/
MainApplication app = (MainApplication) mApplication;
@@ -138,8 +137,6 @@ public class InitializationModule extends ReactContextBaseJavaModule {
}
constants.put("managedConfig", config[0]);
constants.put("replyFromPushNotification", app.replyFromPushNotification);
app.replyFromPushNotification = false;
return constants;
}

View File

@@ -60,7 +60,6 @@ import android.util.Log;
public class MainApplication extends NavigationApplication implements INotificationsApplication, INotificationsDrawerApplication {
public NotificationsLifecycleFacade notificationsLifecycleFacade;
public Boolean sharedExtensionIsOpened = false;
public Boolean replyFromPushNotification = false;
public long APP_START_TIME;

View File

@@ -13,6 +13,7 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.WritableMap;
public class MattermostManagedModule extends ReactContextBaseJavaModule {
private static MattermostManagedModule instance;
@@ -73,6 +74,19 @@ public class MattermostManagedModule extends ReactContextBaseJavaModule {
System.exit(0);
}
@ReactMethod
public void isRunningInSplitView(final Promise promise) {
WritableMap result = Arguments.createMap();
Activity current = getCurrentActivity();
if (current != null) {
result.putBoolean("isSplitView", current.isInMultiWindowMode());
} else {
result.putBoolean("isSplitView", false);
}
promise.resolve(result);
}
@ReactMethod
public void quitApp() {
getCurrentActivity().finish();

View File

@@ -36,13 +36,17 @@ public class NotificationReplyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
final CharSequence message = getReplyMessage(intent);
if (message == null) {
return;
}
mContext = context;
bundle = NotificationIntentAdapter.extractPendingNotificationDataFromIntent(intent);
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
final ReactApplicationContext reactApplicationContext = new ReactApplicationContext(context);
final int notificationId = intent.getIntExtra(CustomPushNotification.NOTIFICATION_ID, -1);
final CharSequence message = getReplyMessage(intent);
final KeychainModule keychainModule = new KeychainModule(reactApplicationContext);
keychainModule.getGenericPasswordForOptions(null, new ResolvePromise() {
@@ -75,7 +79,11 @@ public class NotificationReplyBroadcastReceiver extends BroadcastReceiver {
protected void replyToMessage(final String serverUrl, final String token, final int notificationId, final CharSequence message) {
final String channelId = bundle.getString("channel_id");
final String rootId = bundle.getString("post_id");
final String postId = bundle.getString("post_id");
String rootId = bundle.getString("root_id");
if (android.text.TextUtils.isEmpty(rootId)) {
rootId = postId;
}
if (token == null || serverUrl == null) {
onReplyFailed(notificationManager, notificationId, channelId);

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<!-- Trust preinstalled CAs -->
<certificates src="system" />

View File

@@ -5,7 +5,7 @@ buildscript {
buildToolsVersion = "28.0.3"
minSdkVersion = 24
compileSdkVersion = 28
targetSdkVersion = 26
targetSdkVersion = 28
supportLibVersion = "28.0.0"
}
repositories {

View File

@@ -30,7 +30,7 @@ export function executeCommand(message, channelId, rootId) {
const {data, error} = await dispatch(executeCommandService(msg, args));
if (data.trigger_id) {
if (data?.trigger_id) { //eslint-disable-line camelcase
dispatch({type: IntegrationTypes.RECEIVED_DIALOG_TRIGGER_ID, data: data.trigger_id});
}

View File

@@ -7,12 +7,13 @@ import {getSessions} from 'mattermost-redux/actions/users';
import {autoUpdateTimezone} from 'mattermost-redux/actions/timezone';
import {Client4} from 'mattermost-redux/client';
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {ViewTypes} from 'app/constants';
import {app} from 'app/mattermost';
import PushNotifications from 'app/push_notifications';
import {getDeviceTimezone, isTimezoneEnabled} from 'app/utils/timezone';
import {getDeviceTimezone} from 'app/utils/timezone';
import {setCSRFFromCookie} from 'app/utils/security';
export function handleLoginIdChanged(loginId) {

View File

@@ -198,10 +198,11 @@ export default class App {
const username = `${deviceToken}, ${currentUserId}`;
const password = `${token},${url}`;
this.token = token;
this.url = url;
if (this.waitForRehydration) {
this.waitForRehydration = false;
this.token = token;
this.url = url;
}
// Only save to keychain if the url and token are set

View File

@@ -29,11 +29,8 @@ export default class FormattedTime extends React.PureComponent {
} = this.props;
if (timeZone) {
return intl.formatDate(moment.tz(value, timeZone).toDate(), {
hour: 'numeric',
minute: 'numeric',
hour12,
});
const format = hour12 ? 'hh:mm A' : 'HH:mm';
return moment.tz(value, timeZone).format(format);
}
// If no timezone is defined fallback to the previous implementation

View File

@@ -89,6 +89,7 @@ export default class PostAttachmentOpenGraph extends PureComponent {
const bestImage = getNearestPoint(bestDimensions, data.images, 'width', 'height');
const imageUrl = bestImage.secure_url || bestImage.url;
let ogImage;
if (imagesMetadata && imagesMetadata[imageUrl]) {
ogImage = imagesMetadata[imageUrl];
@@ -98,6 +99,12 @@ export default class PostAttachmentOpenGraph extends PureComponent {
ogImage = data.images.find((i) => i.url === imageUrl || i.secure_url === imageUrl);
}
// Fallback when the ogImage does not have dimensions but there is a metaImage defined
const metaImages = imagesMetadata ? Object.values(imagesMetadata) : null;
if ((!ogImage?.width || !ogImage?.height) && metaImages?.length) {
ogImage = metaImages[0];
}
let dimensions = bestDimensions;
if (ogImage?.width && ogImage?.height) {
dimensions = calculateDimensions(ogImage.height, ogImage.width, this.getViewPostWidth());
@@ -139,9 +146,16 @@ export default class PostAttachmentOpenGraph extends PureComponent {
ogImage = openGraphData.images.find((i) => i.url === openGraphImageUrl || i.secure_url === openGraphImageUrl);
}
// Fallback when the ogImage does not have dimensions but there is a metaImage defined
const metaImages = imagesMetadata ? Object.values(imagesMetadata) : null;
if ((!ogImage?.width || !ogImage?.height) && metaImages?.length) {
ogImage = metaImages[0];
}
if (ogImage?.width && ogImage?.height) {
this.setImageSize(imageUrl, ogImage.width, ogImage.height);
} else {
// if we get to this point there can be a scroll pop
Image.getSize(imageUrl, (width, height) => {
this.setImageSize(imageUrl, width, height);
}, () => null);

View File

@@ -10,6 +10,7 @@ import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general
import {getCurrentUserId, getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {getCustomEmojisByName} from 'mattermost-redux/selectors/entities/emojis';
import {makeGetReactionsForPost} from 'mattermost-redux/selectors/entities/posts';
import {memoizeResult} from 'mattermost-redux/utils/helpers';
import {
@@ -30,10 +31,12 @@ const POST_TIMEOUT = 20000;
function makeMapStateToProps() {
const memoizeHasEmojisOnly = memoizeResult((message, customEmojis) => hasEmojisOnly(message, customEmojis));
const getReactionsForPost = makeGetReactionsForPost();
return (state, ownProps) => {
const post = ownProps.post;
const channel = getChannel(state, post.channel_id) || {};
const reactions = getReactionsForPost(state, post.id);
let isFailed = post.failed;
let isPending = post.id === post.pending_post_id;
@@ -83,7 +86,7 @@ function makeMapStateToProps() {
fileIds: post.file_ids,
hasBeenDeleted: post.state === Posts.POST_DELETED,
hasBeenEdited: isEdited(post),
hasReactions: post.has_reactions,
hasReactions: (reactions && Object.keys(reactions).length > 0) || Boolean(post.has_reactions),
isFailed,
isPending,
isPostAddChannelMember,

View File

@@ -179,11 +179,12 @@ export default class PostBodyAdditionalContent extends PureComponent {
};
generateStaticEmbed = (isYouTube, isImage) => {
if (isYouTube || isImage) {
const {isReplyPost, link, metadata, navigator, openGraphData, showLinkPreviews, theme} = this.props;
if (isYouTube || (isImage && !openGraphData)) {
return null;
}
const {isReplyPost, link, metadata, navigator, openGraphData, showLinkPreviews, theme} = this.props;
const attachments = this.getMessageAttachment();
if (attachments) {
return attachments;

View File

@@ -6,6 +6,7 @@ import {connect} from 'react-redux';
import {Preferences} from 'mattermost-redux/constants';
import {makeGetCommentCountForPost} from 'mattermost-redux/selectors/entities/posts';
import {getBool, getTeammateNameDisplaySetting, getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone';
import {getUser, getCurrentUser} from 'mattermost-redux/selectors/entities/users';
import {isPostPendingOrFailed, isSystemMessage} from 'mattermost-redux/utils/post_utils';
import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils';
@@ -13,7 +14,6 @@ import {displayUsername} from 'mattermost-redux/utils/user_utils';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {fromAutoResponder} from 'app/utils/general';
import {isTimezoneEnabled} from 'app/utils/timezone';
import PostHeader from './post_header';

View File

@@ -5,10 +5,9 @@ import {connect} from 'react-redux';
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone';
import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils';
import {isTimezoneEnabled} from 'app/utils/timezone';
import DateHeader from './date_header';
function mapStateToProps(state) {

View File

@@ -281,7 +281,9 @@ export default class PostList extends PureComponent {
scrollToBottom = () => {
setTimeout(() => {
this.flatListRef.current.scrollToOffset({offset: 0, animated: true});
if (this.flatListRef && this.flatListRef.current) {
this.flatListRef.current.scrollToOffset({offset: 0, animated: true});
}
}, 250);
};
@@ -290,13 +292,16 @@ export default class PostList extends PureComponent {
width > 0 &&
height > 0 &&
this.props.initialIndex > 0 &&
!this.hasDoneInitialScroll
!this.hasDoneInitialScroll &&
this.flatListRef?.current
) {
this.flatListRef.current.scrollToIndex({
animated: false,
index: this.props.initialIndex,
viewOffset: 50,
viewPosition: 0.5,
requestAnimationFrame(() => {
this.flatListRef.current.scrollToIndex({
animated: false,
index: this.props.initialIndex,
viewOffset: 0,
viewPosition: 1, // 0 is at bottom
});
});
this.hasDoneInitialScroll = true;
}

View File

@@ -81,14 +81,12 @@ exports[`ChannelItem should match snapshot 1`] = `
style={
Array [
Object {
"alignSelf": "center",
"color": "rgba(255,255,255,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
"color": "#ffffff",
@@ -196,14 +194,12 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
style={
Array [
Object {
"alignSelf": "center",
"color": "rgba(255,255,255,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
"color": "#ffffff",
@@ -311,14 +307,12 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
style={
Array [
Object {
"alignSelf": "center",
"color": "rgba(255,255,255,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
"color": "#ffffff",
@@ -426,14 +420,12 @@ exports[`ChannelItem should match snapshot for deactivated user and is currentCh
style={
Array [
Object {
"alignSelf": "center",
"color": "rgba(255,255,255,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
"color": "#ffffff",
@@ -530,14 +522,12 @@ exports[`ChannelItem should match snapshot for deactivated user and is searchRes
style={
Array [
Object {
"alignSelf": "center",
"color": "rgba(255,255,255,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
"color": "#ffffff",
@@ -640,14 +630,12 @@ exports[`ChannelItem should match snapshot with draft 1`] = `
style={
Array [
Object {
"alignSelf": "center",
"color": "rgba(255,255,255,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
"color": "#ffffff",
@@ -746,14 +734,12 @@ exports[`ChannelItem should match snapshot with mentions and muted 1`] = `
style={
Array [
Object {
"alignSelf": "center",
"color": "rgba(255,255,255,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
"color": "#ffffff",

View File

@@ -242,10 +242,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
fontSize: 14,
fontWeight: '600',
paddingRight: 10,
height: '100%',
flex: 1,
textAlignVertical: 'center',
lineHeight: 44,
alignSelf: 'center',
},
textActive: {
color: theme.sidebarTextActiveColor,

View File

@@ -44,6 +44,7 @@ class FilteredList extends Component {
teammateNameDisplay: PropTypes.string,
onSelectChannel: PropTypes.func.isRequired,
otherChannels: PropTypes.array,
archivedChannels: PropTypes.array,
profiles: PropTypes.object,
teamProfiles: PropTypes.object,
searchOrder: PropTypes.array.isRequired,
@@ -176,6 +177,11 @@ class FilteredList extends Component {
id: t('mobile.channel_list.not_member'),
defaultMessage: 'NOT A MEMBER',
},
archived: {
builder: this.buildArchivedForSearch,
id: t('mobile.channel_list.archived'),
defaultMessage: 'ARCHIVED',
},
});
buildUnreadChannelsForSearch = (props, term) => {
@@ -294,6 +300,19 @@ class FilteredList extends Component {
sort(sortChannelsByDisplayName.bind(null, props.intl.locale));
}
buildArchivedForSearch = (props, term) => {
const {currentChannel, archivedChannels} = props;
return this.filterChannels(archivedChannels.reduce((acc, channel) => {
// when there is no search text, display an archived channel only if we are in it at the moment.
if (term || channel.id === currentChannel.id) {
acc.push({...channel});
}
return acc;
}, []), term);
}
buildOtherMembersForSearch = (props, term) => {
const {otherChannels} = props;

View File

@@ -13,6 +13,7 @@ import {
getChannelsWithUnreadSection,
getCurrentChannel,
getGroupChannels,
getArchivedChannels,
getOtherChannels,
} from 'mattermost-redux/selectors/entities/channels';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
@@ -24,7 +25,7 @@ import Config from 'assets/config';
import FilteredList from './filtered_list';
const DEFAULT_SEARCH_ORDER = ['unreads', 'dms', 'channels', 'members', 'nonmembers'];
const DEFAULT_SEARCH_ORDER = ['unreads', 'dms', 'channels', 'members', 'nonmembers', 'archived'];
const pastDirectMessages = createSelector(
getDirectShowPreferences,
@@ -112,7 +113,8 @@ function mapStateToProps(state) {
currentChannel: getCurrentChannel(state),
currentTeam: getCurrentTeam(state),
currentUserId,
otherChannels: getOtherChannels(state),
otherChannels: getOtherChannels(state, false),
archivedChannels: getArchivedChannels(state),
groupChannelMemberDetails: getGroupChannelMemberDetails(state),
profiles,
teamProfiles,

View File

@@ -5,6 +5,7 @@ import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
BackHandler,
Dimensions,
Keyboard,
StyleSheet,
View,
@@ -17,6 +18,7 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
import SafeAreaView from 'app/components/safe_area_view';
import DrawerLayout, {TABLET_WIDTH} from 'app/components/sidebars/drawer_layout';
import {DeviceTypes} from 'app/constants';
import mattermostManaged from 'app/mattermost_managed';
import tracker from 'app/utils/time_tracker';
import {t} from 'app/utils/i18n';
@@ -69,15 +71,19 @@ export default class ChannelSidebar extends Component {
openDrawerOffset,
drawerOpened: false,
searching: false,
isSplitView: false,
};
}
componentDidMount() {
this.mounted = true;
this.props.actions.getTeams();
this.handleDimensions();
EventEmitter.on('close_channel_drawer', this.closeChannelDrawer);
EventEmitter.on('renderDrawer', this.handleShowDrawerContent);
EventEmitter.on(WebsocketEvents.CHANNEL_UPDATED, this.handleUpdateTitle);
BackHandler.addEventListener('hardwareBackPress', this.handleAndroidBack);
Dimensions.addEventListener('change', this.handleDimensions);
}
componentWillReceiveProps(nextProps) {
@@ -97,7 +103,7 @@ export default class ChannelSidebar extends Component {
shouldComponentUpdate(nextProps, nextState) {
const {currentTeamId, deviceWidth, isLandscape, teamsCount} = this.props;
const {openDrawerOffset, show, searching} = this.state;
const {openDrawerOffset, isSplitView, show, searching} = this.state;
if (nextState.openDrawerOffset !== openDrawerOffset || nextState.show !== show || nextState.searching !== searching) {
return true;
@@ -105,14 +111,17 @@ export default class ChannelSidebar extends Component {
return nextProps.currentTeamId !== currentTeamId ||
nextProps.isLandscape !== isLandscape || nextProps.deviceWidth !== deviceWidth ||
nextProps.teamsCount !== teamsCount;
nextProps.teamsCount !== teamsCount ||
nextState.isSplitView !== isSplitView;
}
componentWillUnmount() {
this.mounted = false;
EventEmitter.off('close_channel_drawer', this.closeChannelDrawer);
EventEmitter.off(WebsocketEvents.CHANNEL_UPDATED, this.handleUpdateTitle);
EventEmitter.off('renderDrawer', this.handleShowDrawerContent);
BackHandler.removeEventListener('hardwareBackPress', this.handleAndroidBack);
Dimensions.addEventListener('change', this.handleDimensions);
}
handleAndroidBack = () => {
@@ -124,6 +133,15 @@ export default class ChannelSidebar extends Component {
return false;
};
handleDimensions = () => {
if (DeviceTypes.IS_TABLET && this.mounted) {
mattermostManaged.isRunningInSplitView().then((result) => {
const isSplitView = Boolean(result.isSplitView);
this.setState({isSplitView});
});
}
};
handleShowDrawerContent = () => {
this.setState({show: true});
};
@@ -380,6 +398,7 @@ export default class ChannelSidebar extends Component {
render() {
const {children, deviceWidth} = this.props;
const {openDrawerOffset} = this.state;
const isTablet = DeviceTypes.IS_TABLET && !this.state.isSplitView;
const drawerWidth = DeviceTypes.IS_TABLET ? TABLET_WIDTH : (deviceWidth - openDrawerOffset);
return (
@@ -390,7 +409,7 @@ export default class ChannelSidebar extends Component {
onDrawerOpen={this.handleDrawerOpen}
drawerWidth={drawerWidth}
useNativeAnimations={true}
isTablet={DeviceTypes.IS_TABLET}
isTablet={isTablet}
>
{children}
</DrawerLayout>

View File

@@ -405,7 +405,7 @@ const launchSelectServer = () => {
});
};
const launchChannel = () => {
const launchChannel = (skipMetrics = false) => {
Navigation.startSingleScreenApp({
screen: {
screen: 'Channel',
@@ -416,6 +416,9 @@ const launchChannel = () => {
screenBackgroundColor: 'transparent',
},
},
passProps: {
skipMetrics,
},
appStyle: {
orientation: 'auto',
},
@@ -508,19 +511,23 @@ const launchEntry = () => {
configurePushNotifications();
const startedSharedExtension = Platform.OS === 'android' && MattermostShare.isOpened;
const fromPushNotification = Platform.OS === 'android' && Initialization.replyFromPushNotification;
if (startedSharedExtension || fromPushNotification) {
if (startedSharedExtension) {
// Hold on launching Entry screen
app.setAppStarted(true);
// Listen for when the user opens the app
new NativeEventsReceiver().appLaunched(() => {
app.setAppStarted(false);
launchEntry();
});
}
if (!app.appStarted) {
launchEntry();
}
new NativeEventsReceiver().appLaunched(() => {
if (startedSharedExtension) {
app.setAppStarted(false);
launchEntry();
} else if (app.token && app.url) {
launchChannel(true);
} else {
launchSelectServer();
}
});

View File

@@ -36,6 +36,7 @@ export default {
},
authenticate: LocalAuth.auth,
blurAppScreen: MattermostManaged.blurAppScreen,
isRunningInSplitView: MattermostManaged.isRunningInSplitView,
getConfig: async () => {
try {
cachedConfig = await MattermostManaged.getConfig();

View File

@@ -52,6 +52,7 @@ export default {
return cachedConfig;
},
hasSafeAreaInsets: MattermostManaged.hasSafeAreaInsets,
isRunningInSplitView: MattermostManaged.isRunningInSplitView,
isDeviceSecure: async () => {
try {
return await LocalAuth.isDeviceSecure();

View File

@@ -1,11 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {AppRegistry, AppState, NativeModules} from 'react-native';
import {AppState, NativeModules} from 'react-native';
import {NotificationsAndroid, PendingNotifications} from 'react-native-notifications';
import Notification from 'react-native-notifications/notification.android';
import {emptyFunction} from 'app/utils/general';
const {NotificationPreferences} = NativeModules;
@@ -37,23 +34,6 @@ class PushNotification {
this.handleNotification(data, true);
}
});
AppRegistry.registerHeadlessTask('notificationReplied', () => async (deviceNotification) => {
const notification = new Notification(deviceNotification);
const data = notification.getData();
const completed = emptyFunction;
if (this.onReply) {
this.onReply(data, data.text, parseInt(data.badge, 10) - parseInt(data.msg_count, 10), completed);
} else {
this.deviceNotification = {
data,
text: data.text,
badge: parseInt(data.badge, 10) - parseInt(data.msg_count, 10),
completed, // used to identify that the notification belongs to a reply
};
}
});
}
handleNotification = (data, userInteraction) => {

View File

@@ -266,7 +266,11 @@ function postVisibility(state = {}, action) {
}
case ViewTypes.INCREASE_POST_VISIBILITY: {
const nextState = {...state};
nextState[action.data] += action.amount;
if (nextState[action.data]) {
nextState[action.data] += action.amount;
} else {
nextState[action.data] = action.amount;
}
return nextState;
}
case ViewTypes.RECEIVED_FOCUSED_POST: {

View File

@@ -0,0 +1,112 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import channelReducer from './channel';
import {ViewTypes} from 'app/constants';
describe('Reducers.channel', () => {
const initialState = {
displayName: '',
drafts: {},
loading: false,
refreshing: false,
postCountInChannel: {},
postVisibility: {},
loadingPosts: {},
lastGetPosts: {},
retryFailed: false,
loadMorePostsVisible: true,
lastChannelViewTime: {},
keepChannelIdAsUnread: null,
};
test('Initial state', () => {
const nextState = channelReducer(
{
displayName: '',
drafts: {},
loading: false,
refreshing: false,
postCountInChannel: {},
postVisibility: {},
loadingPosts: {},
lastGetPosts: {},
retryFailed: false,
loadMorePostsVisible: true,
lastChannelViewTime: {},
keepChannelIdAsUnread: null,
},
{}
);
expect(nextState).toEqual(initialState);
});
test('should set the postVisibility amount for a channel', () => {
const channelId = 'channel_id';
const amount = 15;
const nextState = channelReducer(
{
displayName: '',
drafts: {},
loading: false,
refreshing: false,
postCountInChannel: {},
postVisibility: {},
loadingPosts: {},
lastGetPosts: {},
retryFailed: false,
loadMorePostsVisible: true,
lastChannelViewTime: {},
keepChannelIdAsUnread: null,
},
{
type: ViewTypes.INCREASE_POST_VISIBILITY,
data: channelId,
amount,
}
);
expect(nextState).toEqual({
...initialState,
postVisibility: {
[channelId]: amount,
},
});
});
test('should increase the postVisibility amount for a channel', () => {
const channelId = 'channel_id';
const amount = 15;
const nextState = channelReducer(
{
displayName: '',
drafts: {},
loading: false,
refreshing: false,
postCountInChannel: {},
postVisibility: {
[channelId]: amount,
},
loadingPosts: {},
lastGetPosts: {},
retryFailed: false,
loadMorePostsVisible: true,
lastChannelViewTime: {},
keepChannelIdAsUnread: null,
},
{
type: ViewTypes.INCREASE_POST_VISIBILITY,
data: channelId,
amount,
}
);
expect(nextState).toEqual({
...initialState,
postVisibility: {
[channelId]: 2 * amount,
},
});
});
});

View File

@@ -48,6 +48,7 @@ export default class ChannelBase extends PureComponent {
theme: PropTypes.object.isRequired,
showTermsOfService: PropTypes.bool,
disableTermsModal: PropTypes.bool,
skipMetrics: PropTypes.bool,
};
static contextTypes = {
@@ -88,7 +89,7 @@ export default class ChannelBase extends PureComponent {
}
componentDidMount() {
if (tracker.initialLoad) {
if (tracker.initialLoad && !this.props.skipMetrics) {
this.props.actions.recordLoadTime('Start time', 'initialLoad');
}
@@ -98,7 +99,9 @@ export default class ChannelBase extends PureComponent {
EventEmitter.emit('renderDrawer');
telemetry.end(['start:channel_screen']);
if (!this.props.skipMetrics) {
telemetry.end(['start:channel_screen']);
}
}
componentWillReceiveProps(nextProps) {

View File

@@ -3,9 +3,10 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {Platform, View} from 'react-native';
import {Dimensions, Platform, View} from 'react-native';
import {DeviceTypes, ViewTypes} from 'app/constants';
import mattermostManaged from 'app/mattermost_managed';
import {makeStyleSheetFromTheme} from 'app/utils/theme';
import ChannelDrawerButton from './channel_drawer_button';
@@ -31,6 +32,30 @@ export default class ChannelNavBar extends PureComponent {
theme: PropTypes.object.isRequired,
};
state = {
isSplitView: false,
};
componentDidMount() {
this.mounted = true;
this.handleDimensions();
Dimensions.addEventListener('change', this.handleDimensions);
}
componentWillUnmount() {
this.mounted = false;
Dimensions.removeEventListener('change', this.handleDimensions);
}
handleDimensions = () => {
if (DeviceTypes.IS_TABLET && this.mounted) {
mattermostManaged.isRunningInSplitView().then((result) => {
const isSplitView = Boolean(result.isSplitView);
this.setState({isSplitView});
});
}
};
render() {
const {isLandscape, navigator, onPress, theme} = this.props;
const {openChannelDrawer, openSettingsDrawer} = this.props;
@@ -60,7 +85,7 @@ export default class ChannelNavBar extends PureComponent {
}
let drawerButtonVisible = false;
if (!DeviceTypes.IS_TABLET) {
if (!DeviceTypes.IS_TABLET || this.state.isSplitView) {
drawerButtonVisible = true;
}

View File

@@ -0,0 +1,85 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ChannelMembers should match snapshot 1`] = `
<ForwardRef(forwardConnectRef)>
<Connect(StatusBar) />
<View
style={
Object {
"marginVertical": 5,
}
}
>
<SearchBarIos
autoCapitalize="none"
backgroundColor="transparent"
blurOnSubmit={true}
cancelTitle="Cancel"
inputHeight={33}
inputStyle={
Object {
"backgroundColor": "rgba(61,60,64,0.2)",
"color": "#3d3c40",
"fontSize": 15,
}
}
leftComponent={null}
onBlur={[Function]}
onCancelButtonPress={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
value=""
/>
</View>
<CustomList
data={Array []}
extraData={Object {}}
listType="section"
loading={false}
loadingComponent={null}
noResults={null}
onLoadMore={[Function]}
onRowPress={[Function]}
renderItem={[Function]}
shouldRenderSeparator={true}
showNoResults={true}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
/>
</ForwardRef(forwardConnectRef)>
`;

View File

@@ -225,20 +225,34 @@ export default class ChannelMembers extends PureComponent {
});
};
renderItem = (props) => {
// The list will re-render when the selection changes because it's passed into the list as extraData
const selected = this.state.selectedIds[props.id];
const enabled = props.id !== this.props.currentUserId;
renderItem = (props, selectProps) => {
return (
<UserListRow
key={props.id}
{...props}
selectable={true}
selected={selected}
enabled={enabled}
{...selectProps}
/>
);
}
renderSelectableItem = (props) => {
// The list will re-render when the selection changes because selectedIds is passed into the list as extraData
const selectProps = {
selectable: true,
selected: this.state.selectedIds[props.id],
enabled: props.id !== this.props.currentUserId,
};
return this.renderItem(props, selectProps);
}
renderUnselectableItem = (props) => {
const selectProps = {
selectable: false,
enabled: false,
};
return this.renderItem(props, selectProps);
};
renderLoading = () => {
@@ -292,7 +306,7 @@ export default class ChannelMembers extends PureComponent {
render() {
const {formatMessage} = this.context.intl;
const {theme} = this.props;
const {theme, canManageUsers} = this.props;
const {
removing,
loading,
@@ -374,7 +388,7 @@ export default class ChannelMembers extends PureComponent {
noResults={this.renderNoResults()}
onLoadMore={this.getProfiles}
onRowPress={this.handleSelectProfile}
renderItem={this.renderItem}
renderItem={canManageUsers ? this.renderSelectableItem : this.renderUnselectableItem}
theme={theme}
/>
</KeyboardLayout>

View File

@@ -0,0 +1,60 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import Preferences from 'mattermost-redux/constants/preferences';
import {shallowWithIntl} from 'test/intl-test-helper';
import CustomList from 'app/components/custom_list';
import ChannelMembers from './channel_members';
describe('ChannelMembers', () => {
const navigator = {
setOnNavigatorEvent: jest.fn(),
setButtons: jest.fn(),
};
const baseProps = {
theme: Preferences.THEMES.default,
currentUserId: 'current-user-id',
currentChannelId: 'current-channel-id',
canManageUsers: false,
actions: {
getProfilesInChannel: jest.fn().mockImplementation(() => Promise.resolve()),
handleRemoveChannelMembers: jest.fn(),
searchProfiles: jest.fn(),
},
navigator,
};
test('should match snapshot', () => {
const wrapper = shallowWithIntl(
<ChannelMembers {...baseProps}/>,
);
expect(wrapper.getElement()).toMatchSnapshot();
});
test('should use renderUnselectableItem when canManagerUsers is false', () => {
const props = {...baseProps, canManageUsers: false};
const wrapper = shallowWithIntl(
<ChannelMembers {...props}/>,
);
const renderItem = wrapper.find(CustomList).props().renderItem;
expect(renderItem).toEqual(wrapper.instance().renderUnselectableItem);
});
test('should use renderSelectableItem when canManagerUsers is true', () => {
const props = {...baseProps, canManageUsers: true};
const wrapper = shallowWithIntl(
<ChannelMembers {...props}/>,
);
const renderItem = wrapper.find(CustomList).props().renderItem;
expect(renderItem).toEqual(wrapper.instance().renderSelectableItem);
});
});

View File

@@ -88,9 +88,12 @@ export default class EditProfile extends PureComponent {
removeProfileImage: PropTypes.func.isRequired,
updateUser: PropTypes.func.isRequired,
}).isRequired,
config: PropTypes.object.isRequired,
currentUser: PropTypes.object.isRequired,
firstNameDisabled: PropTypes.bool.isRequired,
lastNameDisabled: PropTypes.bool.isRequired,
navigator: PropTypes.object.isRequired,
nicknameDisabled: PropTypes.bool.isRequired,
positionDisabled: PropTypes.bool.isRequired,
theme: PropTypes.object.isRequired,
};
@@ -318,16 +321,12 @@ export default class EditProfile extends PureComponent {
renderFirstNameSettings = () => {
const {formatMessage} = this.context.intl;
const {config, currentUser, theme} = this.props;
const {firstNameDisabled, theme} = this.props;
const {firstName} = this.state;
const {auth_service: service} = currentUser;
const disabled = (service === 'ldap' && config.LdapFristNameAttributeSet === 'true') ||
(service === 'saml' && config.SamlFirstNameAttributeSet === 'true');
return (
<TextSetting
disabled={disabled}
disabled={firstNameDisabled}
id='firstName'
label={holders.firstName}
disabledText={formatMessage({
@@ -343,17 +342,13 @@ export default class EditProfile extends PureComponent {
renderLastNameSettings = () => {
const {formatMessage} = this.context.intl;
const {config, currentUser, theme} = this.props;
const {lastNameDisabled, theme} = this.props;
const {lastName} = this.state;
const {auth_service: service} = currentUser;
const disabled = (service === 'ldap' && config.LdapLastNameAttributeSet === 'true') ||
(service === 'saml' && config.SamlLastNameAttributeSet === 'true');
return (
<View>
<TextSetting
disabled={disabled}
disabled={lastNameDisabled}
id='lastName'
label={holders.lastName}
disabledText={formatMessage({
@@ -455,16 +450,12 @@ export default class EditProfile extends PureComponent {
renderNicknameSettings = () => {
const {formatMessage} = this.context.intl;
const {config, currentUser, theme} = this.props;
const {nicknameDisabled, theme} = this.props;
const {nickname} = this.state;
const {auth_service: service} = currentUser;
const disabled = (service === 'ldap' && config.LdapNicknameAttributeSet === 'true') ||
(service === 'saml' && config.SamlNicknameAttributeSet === 'true');
return (
<TextSetting
disabled={disabled}
disabled={nicknameDisabled}
id='nickname'
label={holders.nickname}
disabledText={formatMessage({
@@ -481,15 +472,12 @@ export default class EditProfile extends PureComponent {
renderPositionSettings = () => {
const {formatMessage} = this.context.intl;
const {config, currentUser, theme} = this.props;
const {positionDisabled, theme} = this.props;
const {position} = this.state;
const {auth_service: service} = currentUser;
const disabled = (service === 'ldap' || service === 'saml') && config.PositionAttribute === 'true';
return (
<TextSetting
disabled={disabled}
disabled={positionDisabled}
id='position'
label={holders.position}
disabledText={formatMessage({

View File

@@ -32,9 +32,10 @@ describe('edit_profile', () => {
const baseProps = {
actions,
config: {
ShowEmailAddress: true,
},
firstNameDisabled: true,
lastNameDisabled: true,
nicknameDisabled: true,
positionDisabled: true,
theme: Preferences.THEMES.default,
navigator,
currentUser: {

View File

@@ -6,14 +6,39 @@ import {bindActionCreators} from 'redux';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers';
import {setProfileImageUri, removeProfileImage, updateUser} from 'app/actions/views/edit_profile';
import EditProfile from './edit_profile';
function mapStateToProps(state) {
function mapStateToProps(state, ownProps) {
const config = getConfig(state);
const {serverVersion} = state.entities.general;
const {auth_service: service} = ownProps.currentUser;
const firstNameDisabled = (service === 'ldap' && config.LdapFirstNameAttributeSet === 'true') ||
(service === 'saml' && config.SamlFirstNameAttributeSet === 'true');
const lastNameDisabled = (service === 'ldap' && config.LdapLastNameAttributeSet === 'true') ||
(service === 'saml' && config.SamlLastNameAttributeSet === 'true');
const nicknameDisabled = (service === 'ldap' && config.LdapNicknameAttributeSet === 'true') ||
(service === 'saml' && config.SamlNicknameAttributeSet === 'true');
let positionDisabled = false;
if (isMinimumServerVersion(serverVersion, 5, 12)) {
positionDisabled = (service === 'ldap' && config.LdapPositionAttributeSet === 'true') ||
(service === 'saml' && config.SamlPositionAttributeSet === 'true');
} else {
positionDisabled = (service === 'ldap' || service === 'saml') && config.PositionAttribute === 'true';
}
return {
config: getConfig(state),
firstNameDisabled,
lastNameDisabled,
nicknameDisabled,
positionDisabled,
theme: getTheme(state),
};
}

View File

@@ -6,9 +6,10 @@ import {connect} from 'react-redux';
import {setDeviceToken} from 'mattermost-redux/actions/general';
import {autoUpdateTimezone} from 'mattermost-redux/actions/timezone';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone';
import {isLandscape} from 'app/selectors/device';
import {getDeviceTimezone, isTimezoneEnabled} from 'app/utils/timezone';
import {getDeviceTimezone} from 'app/utils/timezone';
const lazyLoadEntry = () => {
return require('./entry').default;

View File

@@ -6,7 +6,7 @@ import {connect} from 'react-redux';
import {selectPost} from 'mattermost-redux/actions/posts';
import {makeGetChannel} from 'mattermost-redux/selectors/entities/channels';
import {getPost} from 'mattermost-redux/selectors/entities/posts';
import {getPost, makeGetReactionsForPost} from 'mattermost-redux/selectors/entities/posts';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {loadThreadIfNecessary} from 'app/actions/views/channel';
@@ -15,14 +15,16 @@ import LongPost from './long_post';
function makeMapStateToProps() {
const getChannel = makeGetChannel();
const getReactionsForPost = makeGetReactionsForPost();
return function mapStateToProps(state, ownProps) {
const post = getPost(state, ownProps.postId);
const channel = post ? getChannel(state, {id: post.channel_id}) : null;
const reactions = getReactionsForPost(state, post.id);
return {
channelName: channel ? channel.display_name : '',
hasReactions: post ? post.has_reactions : false,
hasReactions: (reactions && Object.keys(reactions).length > 0) || Boolean(post.has_reactions),
inThreadView: Boolean(state.entities.posts.selectedPostId),
fileIds: post ? post.file_ids : false,
theme: getTheme(state),

View File

@@ -429,6 +429,7 @@ export default class PostOptions extends PureComponent {
marginFromTop={marginFromTop > 0 ? marginFromTop : 0}
onRequestClose={this.close}
initialPosition={initialPosition}
key={marginFromTop}
>
{options}
</SlideUpPanel>

View File

@@ -9,6 +9,7 @@ import {clearSearch, removeSearchTerms, searchPostsWithParams, getMorePostsForSe
import {getCurrentChannelId, filterPostIds} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone';
import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers';
import {getUserCurrentTimezone} from 'mattermost-redux/utils/timezone_utils';
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
@@ -17,7 +18,7 @@ import {loadChannelsByTeamName, loadThreadIfNecessary} from 'app/actions/views/c
import {isLandscape} from 'app/selectors/device';
import {makePreparePostIdsForSearchPosts} from 'app/selectors/post_list';
import {handleSearchDraftChanged} from 'app/actions/views/search';
import {getDeviceUtcOffset, getUtcOffsetForTimeZone, isTimezoneEnabled} from 'app/utils/timezone';
import {getDeviceUtcOffset, getUtcOffsetForTimeZone} from 'app/utils/timezone';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import Search from './search';

View File

@@ -4,8 +4,8 @@
import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone';
import {isTimezoneEnabled} from 'app/utils/timezone';
import {isThemeSwitchingEnabled} from 'app/utils/theme';
import DisplaySettings from './display_settings';

View File

@@ -1,19 +1,37 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
import {updateMe} from 'mattermost-redux/actions/users';
import NotificationSettingsMobile from './notification_settings_mobile';
function mapStateToProps(state) {
const config = getConfig(state);
const theme = getTheme(state);
const updateMeRequest = state.requests.users.updateMe;
const currentUser = getCurrentUser(state);
return {
config: getConfig(state),
theme: getTheme(state),
config,
theme,
updateMeRequest,
currentUser,
};
}
export default connect(mapStateToProps)(NotificationSettingsMobile);
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
updateMe,
}, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(NotificationSettingsMobile);

View File

@@ -4,6 +4,7 @@
import React from 'react';
import {injectIntl} from 'react-intl';
import {
Alert,
Modal,
Platform,
ScrollView,
@@ -12,6 +13,11 @@ import {
View,
} from 'react-native';
import deepEqual from 'deep-equal';
import PropTypes from 'prop-types';
import {RequestStatus} from 'mattermost-redux/constants';
import FormattedText from 'app/components/formatted_text';
import RadioButtonGroup from 'app/components/radio_button';
import NotificationPreferences from 'app/notification_preferences';
@@ -19,10 +25,20 @@ import PushNotifications from 'app/push_notifications';
import StatusBar from 'app/components/status_bar';
import SectionItem from 'app/screens/settings/section_item';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {getNotificationProps} from 'app/utils/notify_props';
import NotificationSettingsMobileBase from './notification_settings_mobile_base';
class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
static propTypes = {
...NotificationSettingsMobileBase.propTypes,
updateMeRequest: PropTypes.object.isRequired,
}
static defaultProps = {
currentUser: {},
}
cancelMobilePushModal = () => {
this.setState({
newPush: this.state.push,
@@ -354,6 +370,24 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
);
}
componentWillReceiveProps(nextProps) {
super.componentWillReceiveProps(nextProps);
const {updateMeRequest, intl} = nextProps;
if (this.props.updateMeRequest !== updateMeRequest && updateMeRequest.status === RequestStatus.FAILURE) {
Alert.alert(
intl.formatMessage({
id: 'mobile.notification_settings.save_failed_title',
defaultMessage: 'Connection issue',
}),
intl.formatMessage({
id: 'mobile.notification_settings.save_failed_description',
defaultMessage: 'The notification settings failed to save due to a connection issue, please try again.',
})
);
}
}
renderMobilePushSection() {
const {config, theme} = this.props;
@@ -583,12 +617,49 @@ class NotificationSettingsMobileAndroid extends NotificationSettingsMobileBase {
saveMobilePushModal = () => {
this.setState({showMobilePushModal: false});
this.setMobilePush(this.state.newPush);
this.setMobilePush(this.state.newPush, this.saveNotificationProps);
};
saveMobilePushStatusModal = () => {
this.setState({showMobilePushStatusModal: false});
this.setMobilePushStatus(this.state.newPushStatus);
this.setMobilePushStatus(this.state.newPushStatus, this.saveNotificationProps);
};
saveNotificationProps = () => {
const {
channel,
comments,
desktop,
email,
first_name: firstName,
mention_keys: mentionKeys,
push,
push_status: pushStatus,
} = this.state;
const {currentUser} = this.props;
const notifyProps = {
channel,
comments,
desktop,
email,
first_name: firstName,
mention_keys: mentionKeys,
push,
push_status: pushStatus,
user_id: currentUser.id,
};
const {user_id: userId} = notifyProps;
const previousProps = {
...getNotificationProps(currentUser),
user_id: userId,
};
if (!deepEqual(previousProps, notifyProps)) {
this.props.actions.updateMe({notify_props: notifyProps});
}
};
saveMobileSoundsModal = () => {

View File

@@ -11,6 +11,9 @@ import {setNavigatorStyles} from 'app/utils/theme';
export default class NotificationSettingsMobileBase extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
updateMe: PropTypes.func.isRequired,
}),
config: PropTypes.object.isRequired,
currentUser: PropTypes.object.isRequired,
intl: intlShape.isRequired,
@@ -89,12 +92,12 @@ export default class NotificationSettingsMobileBase extends PureComponent {
}
};
setMobilePush = (push) => {
this.setState({push});
setMobilePush = (push, callback) => {
this.setState({push}, callback);
};
setMobilePushStatus = (value) => {
this.setState({push_status: value});
setMobilePushStatus = (value, callback) => {
this.setState({push_status: value}, callback);
};
saveUserNotifyProps = () => {

View File

@@ -40,7 +40,7 @@ export default makeStyleSheetFromTheme((theme) => {
color: theme.centerChannelColor,
flex: 1,
fontSize: 17,
lineHeight: 43,
alignSelf: 'center',
},
arrowContainer: {
justifyContent: 'center',

View File

@@ -7,14 +7,13 @@ import {connect} from 'react-redux';
import {setChannelDisplayName} from 'app/actions/views/channel';
import {makeDirectChannel} from 'app/actions/views/more_dms';
import {getTeammateNameDisplaySetting, getTheme, getBool} from 'mattermost-redux/selectors/entities/preferences';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {getTeammateNameDisplaySetting, getTheme, getBool} from 'mattermost-redux/selectors/entities/preferences';
import {isTimezoneEnabled} from 'mattermost-redux/selectors/entities/timezone';
import Preferences from 'mattermost-redux/constants/preferences';
import {loadBot} from 'mattermost-redux/actions/bots';
import {getBotAccounts} from 'mattermost-redux/selectors/entities/bots';
import {isTimezoneEnabled} from 'app/utils/timezone';
import UserProfile from './user_profile';
function mapStateToProps(state, ownProps) {

View File

@@ -17,7 +17,3 @@ export function getUtcOffsetForTimeZone(timezone) {
return moment.tz(timezone).utcOffset();
}
export function isTimezoneEnabled(state) {
const {config} = state.entities.general;
return config.ExperimentalTimezone === 'true';
}

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Erwähnungen in Antworten",
"mobile.account_notifications.threads_start": "Diskussionen die ich starte",
"mobile.account_notifications.threads_start_participate": "Diskussionen die ich starte oder an denen ich teilnehme",
"mobile.account.settings.cancel": "Abbrechen",
"mobile.account.settings.save": "Speichern",
"mobile.action_menu.select": "Wählen Sie eine Option",
"mobile.advanced_settings.clockDisplay": "Uhrzeit-Format",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "Öffentlicher Kanal",
"mobile.channel_list.alertNo": "Nein",
"mobile.channel_list.alertYes": "Ja",
"mobile.channel_list.archived": "ARCHIVIERT",
"mobile.channel_list.channels": "KANÄLE",
"mobile.channel_list.closeDM": "Direktnachricht schließen",
"mobile.channel_list.closeGM": "Gruppennachricht schließen",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Mentions in threads",
"mobile.account_notifications.threads_start": "Threads that I start",
"mobile.account_notifications.threads_start_participate": "Threads that I start or participate in",
"mobile.account.settings.cancel": "Cancel",
"mobile.account.settings.save": "Save",
"mobile.action_menu.select": "Select an option",
"mobile.advanced_settings.clockDisplay": "Clock display",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "Public Channel",
"mobile.channel_list.alertNo": "No",
"mobile.channel_list.alertYes": "Yes",
"mobile.channel_list.archived": "ARCHIVED",
"mobile.channel_list.channels": "CHANNELS",
"mobile.channel_list.closeDM": "Close Direct Message",
"mobile.channel_list.closeGM": "Close Group Message",
@@ -401,6 +401,7 @@
"mobile.routes.thread": "{channelName} Thread",
"mobile.routes.thread_dm": "Direct Message Thread",
"mobile.routes.user_profile": "Profile",
"mobile.routes.user_profile.edit": "Edit",
"mobile.routes.user_profile.local_time": "LOCAL TIME",
"mobile.routes.user_profile.send_message": "Send Message",
"mobile.search.after_modifier_description": "to find posts after a specific date",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Menciones en hilos",
"mobile.account_notifications.threads_start": "Hilos que yo comience",
"mobile.account_notifications.threads_start_participate": "Hilos que yo comience o participe",
"mobile.account.settings.cancel": "Cancelar",
"mobile.account.settings.save": "Guardar",
"mobile.action_menu.select": "Selecciona una opción",
"mobile.advanced_settings.clockDisplay": "Visualización de la hora",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "Canal Público",
"mobile.channel_list.alertNo": "No",
"mobile.channel_list.alertYes": "Sí",
"mobile.channel_list.archived": "ARCHIVADO",
"mobile.channel_list.channels": "CANALES",
"mobile.channel_list.closeDM": "Cerrar Mensaje Directo",
"mobile.channel_list.closeGM": "Cerrar Mensaje de Grupo",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Mentions dans les fils de discussion",
"mobile.account_notifications.threads_start": "Fils de discussion que je démarre",
"mobile.account_notifications.threads_start_participate": "Fils de discussion que je démarre ou auxquels je participe",
"mobile.account.settings.cancel": "Annuler",
"mobile.account.settings.save": "Enregistrer",
"mobile.action_menu.select": "Sélectionnez une option",
"mobile.advanced_settings.clockDisplay": "Affichage de l'horloge",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "Canal public",
"mobile.channel_list.alertNo": "Non",
"mobile.channel_list.alertYes": "Oui",
"mobile.channel_list.archived": "ARCHIVED",
"mobile.channel_list.channels": "CANAUX",
"mobile.channel_list.closeDM": "Fermer le message personnel",
"mobile.channel_list.closeGM": "Fermer le groupe de message",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Citazioni nelle discussioni",
"mobile.account_notifications.threads_start": "Discussioni avviate da me",
"mobile.account_notifications.threads_start_participate": "Discussioni alle quali ho partecipato",
"mobile.account.settings.cancel": "Annulla",
"mobile.account.settings.save": "Salva",
"mobile.action_menu.select": "Seleziona un'opzione",
"mobile.advanced_settings.clockDisplay": "Visualizza orologio",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "Canale Pubblico",
"mobile.channel_list.alertNo": "No",
"mobile.channel_list.alertYes": "Si",
"mobile.channel_list.archived": "ARCHIVIATO",
"mobile.channel_list.channels": "CANALI",
"mobile.channel_list.closeDM": "Chudi Messaggio Diretto",
"mobile.channel_list.closeGM": "Chiudi Messaggio di Gruppo",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "スレッド内のあなたについての投稿",
"mobile.account_notifications.threads_start": "自分で開始したスレッド",
"mobile.account_notifications.threads_start_participate": "開始もしくは参加したスレッド",
"mobile.account.settings.cancel": "キャンセル",
"mobile.account.settings.save": "保存する",
"mobile.action_menu.select": "オプションを選択してください",
"mobile.advanced_settings.clockDisplay": "時刻表示",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "公開チャンネル",
"mobile.channel_list.alertNo": "いいえ",
"mobile.channel_list.alertYes": "はい",
"mobile.channel_list.archived": "アーカイブ",
"mobile.channel_list.channels": "チャンネル",
"mobile.channel_list.closeDM": "ダイレクトメッセージを閉じる",
"mobile.channel_list.closeGM": "グループメッセージを閉じる",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Mentions in threads",
"mobile.account_notifications.threads_start": "등록한 모든 스레드의 답변에 대해 알림",
"mobile.account_notifications.threads_start_participate": "등록하거나 답변했던 모든 스레드의 답변에 대해 알림",
"mobile.account.settings.cancel": "취소",
"mobile.account.settings.save": "저장",
"mobile.action_menu.select": "Select an option",
"mobile.advanced_settings.clockDisplay": "시간 표시",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "공개 채널",
"mobile.channel_list.alertNo": "아니요",
"mobile.channel_list.alertYes": "네",
"mobile.channel_list.archived": "ARCHIVED",
"mobile.channel_list.channels": "CHANNELS",
"mobile.channel_list.closeDM": "Close Direct Message",
"mobile.channel_list.closeGM": "Close Group Message",
@@ -206,19 +206,19 @@
"mobile.display_settings.theme": "테마",
"mobile.document_preview.failed_description": "문서를 여는 중에 에러가 발생했습니다. {fileType} 뷰어를 설치한 후에 다시 시도해 주십시오.\n",
"mobile.document_preview.failed_title": "Open Document failed",
"mobile.downloader.android_complete": "Download complete",
"mobile.downloader.android_failed": "Download failed",
"mobile.downloader.android_permission": "We need access to the downloads folder to save files.",
"mobile.downloader.android_started": "Download started",
"mobile.downloader.android_success": "download successful",
"mobile.downloader.complete": "Download complete",
"mobile.downloader.disabled_description": "File downloads are disabled on this server. Please contact your System Admin for more details.\n",
"mobile.downloader.disabled_title": "Download disabled",
"mobile.downloader.android_complete": "다운로드 완료",
"mobile.downloader.android_failed": "다운로드 실패",
"mobile.downloader.android_permission": "파일 저장을 위해 다운로드 폴더 접근 권한이 필요합니다.",
"mobile.downloader.android_started": "다운로드 시작",
"mobile.downloader.android_success": "다운로드 성공",
"mobile.downloader.complete": "다운로드 완료",
"mobile.downloader.disabled_description": "이 서버에 파일을 다운로드할 수 없습니다. 자세한 사항은 시스템 관리자에게 문의하세요.\n",
"mobile.downloader.disabled_title": "다운로드 실패",
"mobile.downloader.downloading": "다운로드중...",
"mobile.downloader.failed_description": "파일을 다운로드 하는 중 오류가 발생했습니다. 인터넷 연결 상태를 확인하고 다시 시도해 주세요.\n",
"mobile.downloader.failed_title": "Download failed",
"mobile.downloader.image_saved": "Image Saved",
"mobile.downloader.video_saved": "Video Saved",
"mobile.downloader.failed_title": "다운로드 실패",
"mobile.downloader.image_saved": "이미지 저장 완료",
"mobile.downloader.video_saved": "비디오 저장 완료",
"mobile.drawer.teamsTitle": "서비스 약관",
"mobile.edit_channel": "저장",
"mobile.edit_post.title": "Editing Message",
@@ -379,7 +379,7 @@
"mobile.reset_status.title_ooo": "Disable \"Out Of Office\"?",
"mobile.retry_message": "메세지를 새로 고침 하는데 실패하였습니다. 당겨 올려서 다시 시도해 보세요.",
"mobile.routes.channel_members.action": "팀에서 제거하기",
"mobile.routes.channel_members.action_message": "You must select at least one member to remove from the channel.",
"mobile.routes.channel_members.action_message": "채널에 추가할 최소 한명의 멤버를 선택해야합니다.",
"mobile.routes.channel_members.action_message_confirm": "Are you sure you want to remove the selected members from the channel?",
"mobile.routes.channelInfo": "Info",
"mobile.routes.channelInfo.createdBy": "Created by {creator} on ",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Mentions in threads",
"mobile.account_notifications.threads_start": "Threads that I start",
"mobile.account_notifications.threads_start_participate": "Threads that I start or participate in",
"mobile.account.settings.cancel": "Annuleren",
"mobile.account.settings.save": "Opslaan",
"mobile.action_menu.select": "Select an option",
"mobile.advanced_settings.clockDisplay": "Klok weergave",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "Publieke kanalen",
"mobile.channel_list.alertNo": "Nee",
"mobile.channel_list.alertYes": "Ja",
"mobile.channel_list.archived": "ARCHIVED",
"mobile.channel_list.channels": "CHANNELS",
"mobile.channel_list.closeDM": "Close Direct Message",
"mobile.channel_list.closeGM": "Close Group Message",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Wzmianki w wątkach",
"mobile.account_notifications.threads_start": "Wątki, które rozpocząłem",
"mobile.account_notifications.threads_start_participate": "Wątki, które rozpocząłem lub w których uczestniczę",
"mobile.account.settings.cancel": "Anuluj",
"mobile.account.settings.save": "Zapisz",
"mobile.action_menu.select": "Wybierz opcję",
"mobile.advanced_settings.clockDisplay": "Wyświetlanie czasu",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "Kanał publiczny",
"mobile.channel_list.alertNo": "Nie",
"mobile.channel_list.alertYes": "Tak",
"mobile.channel_list.archived": "ZARCHIWIZOWANY",
"mobile.channel_list.channels": "KANAŁY",
"mobile.channel_list.closeDM": "Zamknij Wiadomość Bezpośrednią",
"mobile.channel_list.closeGM": "Zamknij Wiadomość Grupową",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Menções em tópicos",
"mobile.account_notifications.threads_start": "Tópicos que eu iniciei",
"mobile.account_notifications.threads_start_participate": "Tópicos que eu iniciei ou participo",
"mobile.account.settings.cancel": "Cancelar",
"mobile.account.settings.save": "Salvar",
"mobile.action_menu.select": "Selecione uma opção",
"mobile.advanced_settings.clockDisplay": "Exibição do relógio",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "Canal Público",
"mobile.channel_list.alertNo": "Não",
"mobile.channel_list.alertYes": "Sim",
"mobile.channel_list.archived": "ARQUIVADO",
"mobile.channel_list.channels": "CANAIS",
"mobile.channel_list.closeDM": "Fechar Mensagem Direta",
"mobile.channel_list.closeGM": "Fechar Mensagem em Grupo",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Menționează în fire",
"mobile.account_notifications.threads_start": "Subiecte pe care le încep",
"mobile.account_notifications.threads_start_participate": "Threads la care încep sau particip",
"mobile.account.settings.cancel": "Anulare",
"mobile.account.settings.save": "Salvați",
"mobile.action_menu.select": "Selecteaza o optiune",
"mobile.advanced_settings.clockDisplay": "Afișaj ceas",
@@ -166,7 +165,8 @@
"mobile.channel_info.publicChannel": "Canalul public",
"mobile.channel_list.alertNo": "Nu",
"mobile.channel_list.alertYes": "Da",
"mobile.channel_list.channels": "Canale",
"mobile.channel_list.archived": "ARHIVAT",
"mobile.channel_list.channels": "CANALE",
"mobile.channel_list.closeDM": "Închideți mesajul direct",
"mobile.channel_list.closeGM": "Închideți Mesajul grupului",
"mobile.channel_list.members": "Membri",
@@ -479,7 +479,7 @@
"post_info.auto_responder": "RASPUNS AUTOMAT",
"post_info.bot": "BOT",
"post_info.del": "Șterge",
"post_info.edit": "Editează",
"post_info.edit": "Editați",
"post_info.message.show_less": "Afișați mai puține",
"post_info.message.show_more": "Detalii",
"post_info.system": "Sistem",

View File

@@ -80,10 +80,10 @@
"integrations.add": "Добавить",
"intro_messages.anyMember": " Любой участник может зайти и читать этот канал.",
"intro_messages.beginning": "Начало {name}",
"intro_messages.creator": "{name} - {type}, созданный {creator} {date}",
"intro_messages.creatorPrivate": "{name} - {type}, созданный {creator} {date}",
"intro_messages.creator": "Канал: {name}, создан {creator} {date}",
"intro_messages.creatorPrivate": "Приватный канал: {name}, созданный {creator} {date}",
"intro_messages.group_message": "Начало истории групповых сообщений с участниками. Размещённые здесь сообщения и файлы не видны за пределами этой области.",
"intro_messages.noCreator": "{name} - {type}, созданный {date}",
"intro_messages.noCreator": "Канал: {name}, созданный {date}",
"intro_messages.onlyInvited": " Только приглашенные пользователи могут видеть этот приватный канал.",
"last_users_message.added_to_channel.type": " **добавлены на канал**. Кем: {actor}.",
"last_users_message.added_to_team.type": "были **добавлены в команду** пользователем {actor}.",
@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Если меня упомянули",
"mobile.account_notifications.threads_start": "Если я начал эту ветку",
"mobile.account_notifications.threads_start_participate": "Если я начал эту ветку или участвовал в ней",
"mobile.account.settings.cancel": "Отмена",
"mobile.account.settings.save": "Сохранить",
"mobile.action_menu.select": "Выберите опцию",
"mobile.advanced_settings.clockDisplay": "Отображение времени",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "Публичные каналы",
"mobile.channel_list.alertNo": "Нет",
"mobile.channel_list.alertYes": "Да",
"mobile.channel_list.archived": "ARCHIVED",
"mobile.channel_list.channels": "КАНАЛЫ",
"mobile.channel_list.closeDM": "Закрыть личные сообщения",
"mobile.channel_list.closeGM": "Закрыть сообщения группы",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Konulardaki anmalar",
"mobile.account_notifications.threads_start": "Başlattığım konular",
"mobile.account_notifications.threads_start_participate": "Başlattığım ya da katıldığım konular",
"mobile.account.settings.cancel": "İptal",
"mobile.account.settings.save": "Kaydet",
"mobile.action_menu.select": "Bir seçenek seçin",
"mobile.advanced_settings.clockDisplay": "Saat görünümü",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "Herkese Açık Kanal",
"mobile.channel_list.alertNo": "Hayır",
"mobile.channel_list.alertYes": "Evet",
"mobile.channel_list.archived": "ARŞİVLENMİŞ",
"mobile.channel_list.channels": "KANALLAR",
"mobile.channel_list.closeDM": "Doğrudan İletiyi Kapat",
"mobile.channel_list.closeGM": "Grup İletisini Kapat",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "Згадки в тредах",
"mobile.account_notifications.threads_start": "Гілки розпочаті мною",
"mobile.account_notifications.threads_start_participate": "Якщо я почав цю гілку або приймав участь в ній",
"mobile.account.settings.cancel": "Відміна",
"mobile.account.settings.save": "Зберегти ",
"mobile.action_menu.select": "Виберіть параметр",
"mobile.advanced_settings.clockDisplay": "Дисплей годинника",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "Публічний канал",
"mobile.channel_list.alertNo": "Ні ",
"mobile.channel_list.alertYes": "Так ",
"mobile.channel_list.archived": "ARCHIVED",
"mobile.channel_list.channels": "КАНАЛИ",
"mobile.channel_list.closeDM": "Закрити пряме повідомлення",
"mobile.channel_list.closeGM": "Закрити групове повідомлення",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "串中的提及",
"mobile.account_notifications.threads_start": "我创建的串",
"mobile.account_notifications.threads_start_participate": "我创建的或参与的串",
"mobile.account.settings.cancel": "取消",
"mobile.account.settings.save": "保存",
"mobile.action_menu.select": "选择选项",
"mobile.advanced_settings.clockDisplay": "时钟显示",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "公共频道",
"mobile.channel_list.alertNo": "否",
"mobile.channel_list.alertYes": "是",
"mobile.channel_list.archived": "归档",
"mobile.channel_list.channels": "频道",
"mobile.channel_list.closeDM": "关闭私信",
"mobile.channel_list.closeGM": "关闭组消息",

View File

@@ -127,7 +127,6 @@
"mobile.account_notifications.threads_mentions": "被提及的討論串",
"mobile.account_notifications.threads_start": "我開啟的討論串",
"mobile.account_notifications.threads_start_participate": "我開啟或參與的討論串",
"mobile.account.settings.cancel": "取消",
"mobile.account.settings.save": "儲存",
"mobile.action_menu.select": "選擇選項",
"mobile.advanced_settings.clockDisplay": "顯示時間",
@@ -166,6 +165,7 @@
"mobile.channel_info.publicChannel": "公開頻道",
"mobile.channel_list.alertNo": "否",
"mobile.channel_list.alertYes": "是",
"mobile.channel_list.archived": "已封存",
"mobile.channel_list.channels": "頻道",
"mobile.channel_list.closeDM": "關閉直接傳訊",
"mobile.channel_list.closeGM": "關閉群組訊息",

View File

@@ -30,9 +30,9 @@ GEM
declarative (0.0.10)
declarative-option (0.1.0)
digest-crc (0.4.1)
domain_name (0.5.20180417)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.2)
dotenv (2.7.4)
emoji_regex (1.0.1)
excon (0.64.0)
faraday (0.15.4)
@@ -43,7 +43,7 @@ GEM
faraday_middleware (0.13.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.5)
fastlane (2.125.2)
fastlane (2.127.1)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
@@ -96,7 +96,7 @@ GEM
signet (~> 0.9)
google-cloud-core (1.3.0)
google-cloud-env (~> 1.0)
google-cloud-env (1.1.0)
google-cloud-env (1.2.0)
faraday (~> 0.11)
google-cloud-storage (1.16.0)
digest-crc (~> 0.4)
@@ -163,7 +163,7 @@ GEM
unf_ext (0.0.7.6)
unicode-display_width (1.6.0)
word_wrap (1.0.0)
xcodeproj (1.9.0)
xcodeproj (1.11.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
@@ -187,4 +187,4 @@ DEPENDENCIES
nokogiri
BUNDLED WITH
2.0.1
2.0.2

View File

@@ -80,6 +80,7 @@
7F581D35221ED5C60099E66B /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F581D34221ED5C60099E66B /* NotificationService.swift */; };
7F581D39221ED5C60099E66B /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7F581D32221ED5C60099E66B /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
7F581F78221EEA7C0099E66B /* libUploadAttachments.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FABE04522137F2A00D0F595 /* libUploadAttachments.a */; };
7F5BA34722B99B7B005B05D3 /* Mattermost+RCTUITextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F5BA34622B99B7B005B05D3 /* Mattermost+RCTUITextView.m */; };
7F5CA9A0208FE3B9004F91CE /* libRNDocumentPicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F5CA991208FE38F004F91CE /* libRNDocumentPicker.a */; };
7F642DF02093533300F3165E /* libRNDeviceInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F642DED2093530B00F3165E /* libRNDeviceInfo.a */; };
7F72F2EE2211220500F98FFF /* GenericPreview.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7F72F2ED2211220500F98FFF /* GenericPreview.xib */; };
@@ -819,6 +820,8 @@
7F581D34221ED5C60099E66B /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
7F581D36221ED5C60099E66B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7F581F77221EEA5A0099E66B /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = "<group>"; };
7F5BA34522B99B7B005B05D3 /* Mattermost+RCTUITextView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Mattermost+RCTUITextView.h"; path = "Mattermost/Mattermost+RCTUITextView.h"; sourceTree = "<group>"; };
7F5BA34622B99B7B005B05D3 /* Mattermost+RCTUITextView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "Mattermost+RCTUITextView.m"; path = "Mattermost/Mattermost+RCTUITextView.m"; sourceTree = "<group>"; };
7F5CA956208FE38F004F91CE /* RNDocumentPicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNDocumentPicker.xcodeproj; path = "../node_modules/react-native-document-picker/ios/RNDocumentPicker.xcodeproj"; sourceTree = "<group>"; };
7F63D27B1E6C957C001FAE12 /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = "../node_modules/react-native/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj"; sourceTree = "<group>"; };
7F63D2C21E6DD98A001FAE12 /* Mattermost.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Mattermost.entitlements; path = Mattermost/Mattermost.entitlements; sourceTree = "<group>"; };
@@ -1112,6 +1115,8 @@
7FEB109C1F61019C0039A015 /* UIImage+ImageEffects.m */,
7F151D40221B069200FAD8F3 /* 0155-keys.png */,
7F292AA51E8ABB1100A450A3 /* splash.png */,
7F5BA34522B99B7B005B05D3 /* Mattermost+RCTUITextView.h */,
7F5BA34622B99B7B005B05D3 /* Mattermost+RCTUITextView.m */,
);
name = Mattermost;
sourceTree = "<group>";
@@ -2618,6 +2623,7 @@
7FEB10981F6101710039A015 /* BlurAppScreen.m in Sources */,
7FEB109D1F61019C0039A015 /* MattermostManaged.m in Sources */,
7F240ACD220D460300637665 /* MattermostBucketModule.m in Sources */,
7F5BA34722B99B7B005B05D3 /* Mattermost+RCTUITextView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2762,7 +2768,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 198;
CURRENT_PROJECT_VERSION = 208;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
@@ -2822,7 +2828,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 198;
CURRENT_PROJECT_VERSION = 208;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;

View File

@@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.20.0</string>
<string>1.21.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@@ -34,7 +34,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>198</string>
<string>208</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>

View File

@@ -0,0 +1,17 @@
//
// Mattermost+RCTUITextView.h
// Mattermost
//
// Created by Elias Nahum on 6/18/19.
// Copyright © 2019 Facebook. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Mattermost_RCTUITextView : NSObject
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,36 @@
//
// Mattermost+RCTUITextView.m
// Mattermost
//
// Created by Elias Nahum on 6/18/19.
// Copyright © 2019 Facebook. All rights reserved.
//
#import "Mattermost+RCTUITextView.h"
#import "RCTUITextView.h"
@implementation Mattermost_RCTUITextView
@end
@implementation RCTUITextView (DisableCopyPaste)
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
NSDictionary *response = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"com.apple.configuration.managed"];
if(response) {
NSString *copyPasteProtection = response[@"copyAndPasteProtection"];
BOOL prevent = action == @selector(paste:) ||
action == @selector(copy:) ||
action == @selector(cut:) ||
action == @selector(_share:);
if ([copyPasteProtection isEqual: @"true"] && prevent) {
return NO;
}
}
return [super canPerformAction:action withSender:sender];
}
@end

View File

@@ -6,7 +6,6 @@
// See License.txt for license information.
//
#import "RCTUITextView.h"
#import "MattermostManaged.h"
#import <UploadAttachments/Constants.h>
@@ -152,31 +151,19 @@ RCT_EXPORT_METHOD(getConfig:(RCTPromiseResolveBlock)resolve
}
}
RCT_EXPORT_METHOD(isRunningInSplitView:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
BOOL isRunningInFullScreen = CGRectEqualToRect(
[UIApplication sharedApplication].delegate.window.frame,
[UIApplication sharedApplication].delegate.window.screen.bounds);
resolve(@{
@"isSplitView": @(!isRunningInFullScreen)
});
}
RCT_EXPORT_METHOD(quitApp)
{
exit(0);
}
@end
@implementation RCTUITextView (DisableCopyPaste)
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
NSDictionary *response = [[NSUserDefaults standardUserDefaults] dictionaryForKey:configurationKey];
if(response) {
NSString *copyPasteProtection = response[@"copyAndPasteProtection"];
BOOL prevent = action == @selector(paste:) ||
action == @selector(copy:) ||
action == @selector(cut:) ||
action == @selector(_share:);
if ([copyPasteProtection isEqual: @"true"] && prevent) {
return NO;
}
}
return [super canPerformAction:action withSender:sender];
}
@end

View File

@@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.20.0</string>
<string>1.21.0</string>
<key>CFBundleVersion</key>
<string>198</string>
<string>208</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>

View File

@@ -15,10 +15,10 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.20.0</string>
<string>1.21.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>198</string>
<string>208</string>
</dict>
</plist>

View File

@@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.20.0</string>
<string>1.21.0</string>
<key>CFBundleVersion</key>
<string>198</string>
<string>208</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>

View File

@@ -32,8 +32,13 @@
if(![fileManager fileExistsAtPath:filePath]) {
return nil;
}
NSData *data = [NSData dataWithContentsOfFile:filePath];
return [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
if (data != nil) {
return [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
}
return nil;
}
-(void)removeFile:(NSString *)fileName {

View File

@@ -126,28 +126,30 @@ import os.log
public func notificationReceipt(notificationId: Any?, receivedAt: Int, type: Any?) {
let store = StoreManager.shared() as StoreManager
let _ = store.getEntities(true)
let serverURL = store.getServerUrl()
let sessionToken = store.getToken()
let urlString = "\(serverURL!)/api/v4/notifications/ack"
if (notificationId != nil) {
let jsonObject: [String: Any] = [
"id": notificationId as Any,
"received_at": receivedAt,
"platform": "ios",
"type": type as Any
]
let entities = store.getEntities(true)
if (entities != nil) {
let serverURL = store.getServerUrl()
let sessionToken = store.getToken()
let urlString = "\(serverURL!)/api/v4/notifications/ack"
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()
if (notificationId != nil) {
let jsonObject: [String: Any] = [
"id": notificationId as Any,
"received_at": receivedAt,
"platform": "ios",
"type": type 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()
}
}
}
}

View File

@@ -134,7 +134,8 @@ RCT_EXPORT_METHOD(
[cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> *allCookies) {
NSMutableDictionary *cookies = [NSMutableDictionary dictionary];
for(NSHTTPCookie *currentCookie in allCookies) {
if([currentCookie.domain containsString:topLevelDomain]) {
NSString *domainWithDot = [NSString stringWithFormat:@".%@", currentCookie.domain];
if([currentCookie.domain containsString:topLevelDomain] || [domainWithDot containsString:topLevelDomain]) {
[cookies setObject:currentCookie.value forKey:currentCookie.name];
}
}

187
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "mattermost-mobile",
"version": "1.20.0",
"version": "1.21.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -7664,25 +7664,25 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"optional": true
},
"aproba": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"optional": true
},
"are-we-there-yet": {
"version": "1.1.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"optional": true,
"requires": {
@@ -7692,13 +7692,13 @@
},
"balanced-match": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "",
"resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"optional": true,
"requires": {
@@ -7708,37 +7708,37 @@
},
"chownr": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"optional": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"optional": true
},
"core-util-is": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"optional": true
},
"debug": {
"version": "4.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"optional": true,
"requires": {
@@ -7747,25 +7747,25 @@
},
"deep-extend": {
"version": "0.6.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"optional": true
},
"delegates": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"optional": true
},
"detect-libc": {
"version": "1.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"optional": true
},
"fs-minipass": {
"version": "1.2.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"optional": true,
"requires": {
@@ -7774,13 +7774,13 @@
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"optional": true
},
"gauge": {
"version": "2.7.4",
"resolved": "",
"resolved": false,
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"optional": true,
"requires": {
@@ -7796,7 +7796,7 @@
},
"glob": {
"version": "7.1.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"optional": true,
"requires": {
@@ -7810,13 +7810,13 @@
},
"has-unicode": {
"version": "2.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"optional": true
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "",
"resolved": false,
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"optional": true,
"requires": {
@@ -7825,7 +7825,7 @@
},
"ignore-walk": {
"version": "3.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"optional": true,
"requires": {
@@ -7834,7 +7834,7 @@
},
"inflight": {
"version": "1.0.6",
"resolved": "",
"resolved": false,
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"optional": true,
"requires": {
@@ -7844,19 +7844,19 @@
},
"inherits": {
"version": "2.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"optional": true
},
"ini": {
"version": "1.3.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"optional": true,
"requires": {
@@ -7865,13 +7865,13 @@
},
"isarray": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"optional": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"optional": true,
"requires": {
@@ -7880,13 +7880,13 @@
},
"minimist": {
"version": "0.0.8",
"resolved": "",
"resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"optional": true
},
"minipass": {
"version": "2.3.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"optional": true,
"requires": {
@@ -7896,7 +7896,7 @@
},
"minizlib": {
"version": "1.2.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
"optional": true,
"requires": {
@@ -7905,7 +7905,7 @@
},
"mkdirp": {
"version": "0.5.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"optional": true,
"requires": {
@@ -7914,13 +7914,13 @@
},
"ms": {
"version": "2.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"optional": true
},
"needle": {
"version": "2.3.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==",
"optional": true,
"requires": {
@@ -7931,7 +7931,7 @@
},
"node-pre-gyp": {
"version": "0.12.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
"optional": true,
"requires": {
@@ -7949,7 +7949,7 @@
},
"nopt": {
"version": "4.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"optional": true,
"requires": {
@@ -7959,13 +7959,13 @@
},
"npm-bundled": {
"version": "1.0.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==",
"optional": true
},
"npm-packlist": {
"version": "1.4.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
"optional": true,
"requires": {
@@ -7975,7 +7975,7 @@
},
"npmlog": {
"version": "4.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"optional": true,
"requires": {
@@ -7987,19 +7987,19 @@
},
"number-is-nan": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"optional": true
},
"object-assign": {
"version": "4.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"optional": true
},
"once": {
"version": "1.4.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"optional": true,
"requires": {
@@ -8008,19 +8008,19 @@
},
"os-homedir": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"optional": true
},
"osenv": {
"version": "0.1.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"optional": true,
"requires": {
@@ -8030,19 +8030,19 @@
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"optional": true
},
"process-nextick-args": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"optional": true
},
"rc": {
"version": "1.2.8",
"resolved": "",
"resolved": false,
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"optional": true,
"requires": {
@@ -8054,7 +8054,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"optional": true
}
@@ -8062,7 +8062,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "",
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"optional": true,
"requires": {
@@ -8077,7 +8077,7 @@
},
"rimraf": {
"version": "2.6.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"optional": true,
"requires": {
@@ -8086,43 +8086,43 @@
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"optional": true
},
"sax": {
"version": "1.2.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"optional": true
},
"semver": {
"version": "5.7.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"optional": true
},
"set-blocking": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"optional": true
},
"string-width": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"optional": true,
"requires": {
@@ -8133,7 +8133,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"optional": true,
"requires": {
@@ -8142,7 +8142,7 @@
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"optional": true,
"requires": {
@@ -8151,13 +8151,13 @@
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"optional": true
},
"tar": {
"version": "4.4.8",
"resolved": "",
"resolved": false,
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"optional": true,
"requires": {
@@ -8172,13 +8172,13 @@
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"optional": true
},
"wide-align": {
"version": "1.1.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"optional": true,
"requires": {
@@ -8187,13 +8187,13 @@
},
"wrappy": {
"version": "1.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"optional": true
},
"yallist": {
"version": "3.0.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"optional": true
}
@@ -12579,9 +12579,9 @@
"dev": true
},
"jsc-android": {
"version": "241213.1.0",
"resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-241213.1.0.tgz",
"integrity": "sha512-AH8NYyMNLNhcUEF97QbMxPNLNW+oiSBlvm1rsMNzgJ1d5TQzdh/AOJGsxeeESp3m9YIWGLCgUvGTVoVLs0p68A=="
"version": "241213.2.0",
"resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-241213.2.0.tgz",
"integrity": "sha512-nfddejB9jxFSG+Uewf+zwATFi8F2CZEEgoHLoOj13egiBDoC7zMoxK1c5/Ycf3AGmGuwCgjpn3LWe0f4tKYbjw=="
},
"jsdom": {
"version": "11.12.0",
@@ -13155,8 +13155,8 @@
"integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A=="
},
"mattermost-redux": {
"version": "github:mattermost/mattermost-redux#80407bc07f477eaeb93be4045a686b6bf977697f",
"from": "github:mattermost/mattermost-redux#80407bc07f477eaeb93be4045a686b6bf977697f",
"version": "github:mattermost/mattermost-redux#7f38003e06057d8099949f757d6fbbb6170925e7",
"from": "github:mattermost/mattermost-redux#7f38003e06057d8099949f757d6fbbb6170925e7",
"requires": {
"deep-equal": "1.0.1",
"eslint-plugin-header": "3.0.0",
@@ -13165,6 +13165,7 @@
"harmony-reflect": "1.6.1",
"isomorphic-fetch": "2.2.1",
"mime-db": "1.40.0",
"moment-timezone": "0.5.25",
"redux": "4.0.1",
"redux-action-buffer": "1.2.0",
"redux-batched-actions": "0.4.1",
@@ -14180,7 +14181,7 @@
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
@@ -14192,7 +14193,7 @@
},
"cliui": {
"version": "4.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
"dev": true,
"requires": {
@@ -14221,7 +14222,7 @@
},
"find-up": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
@@ -14236,13 +14237,13 @@
},
"invert-kv": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
"dev": true
},
"lcid": {
"version": "2.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
"dev": true,
"requires": {
@@ -14251,7 +14252,7 @@
},
"locate-path": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
@@ -14278,7 +14279,7 @@
},
"os-locale": {
"version": "3.1.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
"dev": true,
"requires": {
@@ -14298,7 +14299,7 @@
},
"p-locate": {
"version": "3.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
@@ -14319,7 +14320,7 @@
},
"resolve-from": {
"version": "4.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
@@ -14353,7 +14354,7 @@
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
@@ -14362,13 +14363,13 @@
},
"uuid": {
"version": "3.3.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
"dev": true
},
"y18n": {
"version": "4.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
@@ -15474,8 +15475,8 @@
}
},
"react-native-device-info": {
"version": "github:mattermost/react-native-device-info#5be3656f4e0ca5c3544618e5df3e9a498f183483",
"from": "github:mattermost/react-native-device-info#5be3656f4e0ca5c3544618e5df3e9a498f183483"
"version": "github:mattermost/react-native-device-info#66d730b4f675038867ca389be7374714c2db63b2",
"from": "github:mattermost/react-native-device-info#66d730b4f675038867ca389be7374714c2db63b2"
},
"react-native-doc-viewer": {
"version": "2.7.8",
@@ -15556,8 +15557,8 @@
}
},
"react-native-notifications": {
"version": "github:mattermost/react-native-notifications#721fcb41b3c265a5eec208a1df181d4ffcafa477",
"from": "github:mattermost/react-native-notifications#721fcb41b3c265a5eec208a1df181d4ffcafa477",
"version": "github:mattermost/react-native-notifications#1b7ec8513606b42237ab4674de9dacb4d1935e38",
"from": "github:mattermost/react-native-notifications#1b7ec8513606b42237ab4674de9dacb4d1935e38",
"requires": {
"core-js": "^1.0.0",
"uuid": "^2.0.3"
@@ -15567,11 +15568,6 @@
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
},
"uuid": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
"integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho="
}
}
},
@@ -18647,6 +18643,11 @@
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"uuid": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
"integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho="
},
"v8-compile-cache": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "mattermost-mobile",
"version": "1.20.0",
"version": "1.21.0",
"description": "Mattermost Mobile with React Native",
"repository": "git@github.com:mattermost/mattermost-mobile.git",
"author": "Mattermost, Inc.",
@@ -18,8 +18,8 @@
"fuse.js": "3.4.4",
"intl": "1.2.5",
"jail-monkey": "2.2.0",
"jsc-android": "241213.1.0",
"mattermost-redux": "github:mattermost/mattermost-redux#80407bc07f477eaeb93be4045a686b6bf977697f",
"jsc-android": "241213.2.0",
"mattermost-redux": "github:mattermost/mattermost-redux#7f38003e06057d8099949f757d6fbbb6170925e7",
"mime-db": "1.40.0",
"moment-timezone": "0.5.25",
"prop-types": "15.7.2",
@@ -32,7 +32,7 @@
"react-native-calendars": "github:mattermost/react-native-calendars#4937ec5a3bf7e86f9f35fcd85eb4aa6133f45b58",
"react-native-circular-progress": "1.1.0",
"react-native-cookies": "github:joeferraro/react-native-cookies#f11374745deba9f18f7b8a9bb4b0b2573026f522",
"react-native-device-info": "github:mattermost/react-native-device-info#5be3656f4e0ca5c3544618e5df3e9a498f183483",
"react-native-device-info": "github:mattermost/react-native-device-info#66d730b4f675038867ca389be7374714c2db63b2",
"react-native-doc-viewer": "2.7.8",
"react-native-document-picker": "2.3.0",
"react-native-exception-handler": "2.10.7",
@@ -45,7 +45,7 @@
"react-native-linear-gradient": "2.5.4",
"react-native-local-auth": "github:mattermost/react-native-local-auth#cc9ce2f468fbf7b431dfad3191a31aaa9227a6ab",
"react-native-navigation": "github:migbot/react-native-navigation#03c623c373f818827a463ca0fe90f86f56e71b2f",
"react-native-notifications": "github:mattermost/react-native-notifications#721fcb41b3c265a5eec208a1df181d4ffcafa477",
"react-native-notifications": "github:mattermost/react-native-notifications#1b7ec8513606b42237ab4674de9dacb4d1935e38",
"react-native-passcode-status": "1.1.1",
"react-native-permissions": "1.1.1",
"react-native-safe-area": "0.5.1",

View File

@@ -21,7 +21,9 @@ import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import Video from 'react-native-video';
import LocalAuth from 'react-native-local-auth';
import RNFetchBlob from 'rn-fetch-blob';
import {getGenericPassword} from 'react-native-keychain';
import {Client4} from 'mattermost-redux/client';
import {Preferences} from 'mattermost-redux/constants';
import {getFormattedFileSize, lookupMimeType} from 'mattermost-redux/utils/file_utils';
@@ -29,8 +31,10 @@ import Loading from 'app/components/loading';
import PaperPlane from 'app/components/paper_plane';
import {MAX_FILE_COUNT} from 'app/constants/post_textbox';
import mattermostManaged from 'app/mattermost_managed';
import avoidNativeBridge from 'app/utils/avoid_native_bridge';
import {getExtensionFromMime} from 'app/utils/file';
import {emptyFunction} from 'app/utils/general';
import {setCSRFFromCookie} from 'app/utils/security';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
@@ -45,6 +49,7 @@ import {
import ChannelButton from './channel_button';
import TeamButton from './team_button';
const {Initialization} = NativeModules;
const defaultTheme = Preferences.THEMES.default;
const extensionSvg = {
csv: ExcelSvg,
@@ -70,8 +75,6 @@ export default class ExtensionPost extends PureComponent {
maxFileSize: PropTypes.number.isRequired,
navigation: PropTypes.object.isRequired,
teamId: PropTypes.string.isRequired,
token: PropTypes.string,
url: PropTypes.string,
};
static contextTypes = {
@@ -227,6 +230,43 @@ export default class ExtensionPost extends PureComponent {
});
}
getAppCredentials = async () => {
try {
const credentials = await avoidNativeBridge(
() => {
return Initialization.credentialsExist;
},
() => {
return Initialization.credentials;
},
() => {
return getGenericPassword();
}
);
if (credentials) {
const passwordParsed = credentials.password.split(',');
// password == token, url
if (passwordParsed.length === 2) {
const [token, url] = passwordParsed;
if (url && url !== 'undefined' && token && token !== 'undefined') {
this.token = token;
this.url = url;
Client4.setUrl(url);
Client4.setToken(token);
await setCSRFFromCookie(url);
}
}
}
} catch (error) {
return null;
}
return null;
};
getInputRef = (ref) => {
this.input = ref;
};
@@ -294,6 +334,8 @@ export default class ExtensionPost extends PureComponent {
};
initialize = async () => {
await this.getAppCredentials();
const hasPermission = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
let granted;
if (!hasPermission) {
@@ -311,8 +353,8 @@ export default class ExtensionPost extends PureComponent {
};
loadData = async (items) => {
const {actions, maxFileSize, teamId, token, url} = this.props;
if (token && url) {
const {actions, maxFileSize, teamId} = this.props;
if (this.token && this.url) {
const text = [];
const files = [];
let totalSize = 0;
@@ -379,14 +421,14 @@ export default class ExtensionPost extends PureComponent {
onPost = () => {
const {channelId, files, value} = this.state;
const {currentUserId, token, url} = this.props;
const {currentUserId} = this.props;
const data = {
channelId,
currentUserId,
files,
token,
url,
token: this.token,
url: this.url,
value,
};
@@ -542,7 +584,7 @@ export default class ExtensionPost extends PureComponent {
render() {
const {formatMessage} = this.context.intl;
const {maxFileSize, token, url} = this.props;
const {maxFileSize} = this.props;
const {error, hasPermission, files, totalSize, loaded} = this.state;
if (!loaded) {
@@ -555,7 +597,7 @@ export default class ExtensionPost extends PureComponent {
return this.renderErrorMessage(error);
}
if (token && url) {
if (this.token && this.url) {
if (hasPermission === false) {
const storage = formatMessage({
id: 'mobile.extension.permission',

View File

@@ -16,8 +16,7 @@ import ExtensionPost from './extension_post';
function mapStateToProps(state) {
const config = getConfig(state);
const {credentials} = state.entities.general;
const {token, url} = credentials;
let channel = getCurrentChannel(state);
if (channel && channel.delete_at !== 0) {
channel = getDefaultChannel(state);
@@ -29,8 +28,6 @@ function mapStateToProps(state) {
currentUserId: getCurrentUserId(state),
maxFileSize: getAllowedServerMaxFileSize(config),
teamId: getCurrentTeamId(state),
token,
url,
};
}

View File

@@ -5,8 +5,6 @@ import React, {PureComponent} from 'react';
import {Provider} from 'react-redux';
import {IntlProvider} from 'react-intl';
import {Client4} from 'mattermost-redux/client';
import {getTranslations} from 'app/i18n';
import {getCurrentLocale} from 'app/selectors/i18n';
import {store} from 'app/mattermost';
@@ -42,8 +40,6 @@ export default class ShareApp extends PureComponent {
this.unsubscribeFromStore();
}
this.setCredentialsForClient();
dispatch(extensionSelectTeamId(currentTeamId));
if (this.mounted) {
@@ -52,16 +48,6 @@ export default class ShareApp extends PureComponent {
}
};
setCredentialsForClient() {
const state = store.getState();
const {credentials} = state.entities.general;
if (credentials.token && credentials.url) {
Client4.setToken(credentials.token);
Client4.setUrl(credentials.url);
}
}
render() {
if (!this.state.init) {
return null;