forked from Ivasoft/mattermost-mobile
Compare commits
12 Commits
release-1.
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41848b2634 | ||
|
|
8d81d946c5 | ||
|
|
10d27ee5ba | ||
|
|
647def15be | ||
|
|
da440e50fb | ||
|
|
793ac98d74 | ||
|
|
fab353b494 | ||
|
|
8853b5dd45 | ||
|
|
464d93df8d | ||
|
|
47f126b71f | ||
|
|
4b2a0c7aea | ||
|
|
f6bedbb7d6 |
@@ -133,8 +133,8 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60"
|
||||
versionCode 302
|
||||
versionName "1.32.0"
|
||||
versionCode 307
|
||||
versionName "1.32.2"
|
||||
multiDexEnabled = true
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
|
||||
|
||||
@@ -36,12 +36,7 @@ public class MattermostCredentialsHelper {
|
||||
|
||||
HashMap<String, String> asyncStorageResults = asyncStorage.multiGet(asyncStorageKeys);
|
||||
String serverUrl = asyncStorageResults.get(CURRENT_SERVER_URL);
|
||||
final WritableMap options = Arguments.createMap();
|
||||
final WritableMap authPrompt = Arguments.createMap();
|
||||
authPrompt.putString("title", "Authenticate to retrieve secret");
|
||||
authPrompt.putString("cancel", "Cancel");
|
||||
options.putMap("authenticationPrompt", authPrompt);
|
||||
|
||||
keychainModule.getInternetCredentialsForServer(serverUrl, options, promise);
|
||||
keychainModule.getGenericPasswordForOptions(serverUrl, promise);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ export function getEmojisInPosts(posts) {
|
||||
const emojisToLoad = getNeededCustomEmojis(state, posts);
|
||||
|
||||
if (emojisToLoad?.size > 0) {
|
||||
const promises = emojisToLoad.map((name) => getCustomEmojiByName(name));
|
||||
const promises = Array.from(emojisToLoad).map((name) => getCustomEmojiByName(name));
|
||||
const result = await Promise.all(promises);
|
||||
const actions = [];
|
||||
const data = [];
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import {Alert, AppState, Dimensions, Linking, NativeModules, Platform} from 'react-native';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import CookieManager from '@react-native-community/cookies';
|
||||
import CookieManager from 'react-native-cookies';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import {getLocales} from 'react-native-localize';
|
||||
import RNFetchBlob from 'rn-fetch-blob';
|
||||
|
||||
@@ -885,7 +885,7 @@ export const getDefaultChannelForTeams: (a: GlobalState) => RelationOneToOne<Tea
|
||||
});
|
||||
|
||||
export const getMyFirstChannelForTeams: (a: GlobalState) => RelationOneToOne<Team, Channel> = createSelector(getAllChannels, getMyChannelMemberships, getMyTeams, getCurrentUser, (allChannels: IDMappedObjects<Channel>, myChannelMemberships: RelationOneToOne<Channel, ChannelMembership>, myTeams: Array<Team>, currentUser: UserProfile): RelationOneToOne<Team, Channel> => {
|
||||
const locale = currentUser.locale || General.DEFAULT_LOCALE;
|
||||
const locale = currentUser?.locale || General.DEFAULT_LOCALE;
|
||||
const result: RelationOneToOne<Team, Channel> = {};
|
||||
|
||||
for (const team of myTeams) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import {WebView} from 'react-native-webview';
|
||||
import CookieManager from '@react-native-community/cookies';
|
||||
import CookieManager from 'react-native-cookies';
|
||||
import urlParse from 'url-parse';
|
||||
|
||||
import {Client4} from '@mm-redux/client';
|
||||
@@ -27,7 +27,7 @@ const HEADERS = {
|
||||
'X-Mobile-App': 'mattermost',
|
||||
};
|
||||
|
||||
const postMessageJS = 'window.ReactNativeWebView.postMessage(document.body.innerText);';
|
||||
const postMessageJS = "window.postMessage(document.body.innerText, '*');";
|
||||
|
||||
// Used to make sure that OneLogin forms scale appropriately on both platforms.
|
||||
const oneLoginFormScalingJS = `
|
||||
@@ -109,7 +109,15 @@ class SSO extends PureComponent {
|
||||
}
|
||||
|
||||
extractCookie = (parsedUrl) => {
|
||||
CookieManager.get(parsedUrl.origin, true).then((res) => {
|
||||
const original = urlParse(this.props.serverUrl);
|
||||
|
||||
// Check whether we need to set a sub-path
|
||||
parsedUrl.set('pathname', original.pathname || '');
|
||||
|
||||
parsedUrl.set('query', '');
|
||||
Client4.setUrl(parsedUrl.href);
|
||||
|
||||
CookieManager.get(parsedUrl.href, true).then((res) => {
|
||||
const mmtoken = res.MMAUTHTOKEN;
|
||||
const token = typeof mmtoken === 'object' ? mmtoken.value : mmtoken;
|
||||
|
||||
@@ -121,13 +129,6 @@ class SSO extends PureComponent {
|
||||
} = this.props.actions;
|
||||
|
||||
Client4.setToken(token);
|
||||
if (this.props.serverUrl !== parsedUrl.origin) {
|
||||
const original = urlParse(this.props.serverUrl);
|
||||
|
||||
// Check whether we need to set a sub-path
|
||||
parsedUrl.set('pathname', original.pathname || '');
|
||||
Client4.setUrl(parsedUrl.href);
|
||||
}
|
||||
ssoLogin(token).then((result) => {
|
||||
if (result.error) {
|
||||
this.onLoadEndError(result.error);
|
||||
@@ -239,17 +240,16 @@ class SSO extends PureComponent {
|
||||
<WebView
|
||||
ref={this.webViewRef}
|
||||
source={{uri: this.loginUrl, headers: HEADERS}}
|
||||
javaScriptEnabled={true}
|
||||
javaScriptEnabledAndroid={true}
|
||||
automaticallyAdjustContentInsets={false}
|
||||
startInLoadingState={true}
|
||||
onNavigationStateChange={this.onNavigationStateChange}
|
||||
onShouldStartLoadWithRequest={() => true}
|
||||
injectedJavaScript={jsCode}
|
||||
onLoadEnd={this.onLoadEnd}
|
||||
onMessage={(messagingEnabled || Platform.OS === 'android') ? this.onMessage : null}
|
||||
sharedCookiesEnabled={Platform.OS === 'android'}
|
||||
onMessage={messagingEnabled ? this.onMessage : null}
|
||||
useSharedProcessPool={true}
|
||||
cacheEnabled={false}
|
||||
useSharedProcessPool={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -187,7 +187,9 @@ export default function configureStore(storage: any, preloadedState: any = {}, o
|
||||
const rootReducer: any = (state: GlobalState, action: GenericAction) => {
|
||||
if (action.type === General.OFFLINE_STORE_PURGE) {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
delete action.data._persist;
|
||||
if (action.data?._persist) {
|
||||
delete action?.data?._persist;
|
||||
}
|
||||
return baseReducer(action.data, action as any);
|
||||
}
|
||||
return baseReducer(state as any, action as any);
|
||||
@@ -230,7 +232,7 @@ export default function configureStore(storage: any, preloadedState: any = {}, o
|
||||
|
||||
store.dispatch({
|
||||
type: General.OFFLINE_STORE_PURGE,
|
||||
state,
|
||||
data: state,
|
||||
});
|
||||
|
||||
console.log('HYDRATED FROM v4', storeKeys); // eslint-disable-line no-console
|
||||
@@ -240,6 +242,8 @@ export default function configureStore(storage: any, preloadedState: any = {}, o
|
||||
});
|
||||
store.dispatch({type: General.REHYDRATED});
|
||||
AsyncStorage.multiRemove(storeKeys);
|
||||
} else if (store.getState()._persist?.rehydrated) { // eslint-disable-line no-underscore-dangle
|
||||
store.dispatch({type: General.REHYDRATED});
|
||||
} else {
|
||||
let executed = false;
|
||||
const unsubscribe = store.subscribe(() => {
|
||||
|
||||
@@ -31,9 +31,9 @@ export function cleanUpState(payload, keepCurrent = false) {
|
||||
postsInChannel: {},
|
||||
postsInThread: {},
|
||||
reactions: {},
|
||||
openGraph: payload.entities.posts.openGraph,
|
||||
selectedPostId: payload.entities.posts.selectedPostId,
|
||||
currentFocusedPostId: payload.entities.posts.currentFocusedPostId,
|
||||
openGraph: payload.entities?.posts?.openGraph,
|
||||
selectedPostId: payload.entities?.posts?.selectedPostId,
|
||||
currentFocusedPostId: payload.entities?.posts?.currentFocusedPostId,
|
||||
},
|
||||
files: {
|
||||
files: {},
|
||||
@@ -42,21 +42,20 @@ export function cleanUpState(payload, keepCurrent = false) {
|
||||
};
|
||||
|
||||
let retentionPeriod = 0;
|
||||
if (payload.entities.general && payload.entities.general.dataRetentionPolicy &&
|
||||
payload.entities.general.dataRetentionPolicy.message_deletion_enabled) {
|
||||
if (payload.entities?.general?.dataRetentionPolicy?.message_deletion_enabled) {
|
||||
retentionPeriod = payload.entities.general.dataRetentionPolicy.message_retention_cutoff;
|
||||
}
|
||||
|
||||
const postIdsToKeep = [];
|
||||
|
||||
// Keep the last 60 posts in each recently viewed channel
|
||||
nextEntities.posts.postsInChannel = cleanUpPostsInChannel(payload.entities.posts.postsInChannel, lastChannelForTeam, keepCurrent ? currentChannelId : '');
|
||||
nextEntities.posts.postsInChannel = cleanUpPostsInChannel(payload.entities.posts?.postsInChannel, lastChannelForTeam, keepCurrent ? currentChannelId : '');
|
||||
postIdsToKeep.push(...getAllFromPostsInChannel(nextEntities.posts.postsInChannel));
|
||||
|
||||
// Keep any posts that appear in search results
|
||||
let searchResults = [];
|
||||
let flaggedPosts = [];
|
||||
if (payload.entities.search) {
|
||||
if (payload.entities?.search) {
|
||||
if (payload.entities.search.results?.length) {
|
||||
const {results} = payload.entities.search;
|
||||
searchResults = results;
|
||||
@@ -71,50 +70,57 @@ export function cleanUpState(payload, keepCurrent = false) {
|
||||
}
|
||||
|
||||
const nextSearch = {
|
||||
...payload.entities.search,
|
||||
...(payload.entities.search || {}),
|
||||
results: searchResults,
|
||||
flagged: flaggedPosts,
|
||||
};
|
||||
|
||||
postIdsToKeep.forEach((postId) => {
|
||||
const post = payload.entities.posts.posts[postId];
|
||||
if (payload.entities.posts?.posts) {
|
||||
const reactions = payload.entities.posts.reactions || {};
|
||||
const fileIdsByPostId = payload.entities.files?.fileIdsByPostId || {};
|
||||
const files = payload.entities.files?.files || {};
|
||||
const postsInThread = payload.entities.posts.postsInThread || {};
|
||||
|
||||
if (post) {
|
||||
if (retentionPeriod && post.create_at < retentionPeriod) {
|
||||
// This post has been removed by data retention, so don't keep it
|
||||
removeFromPostsInChannel(nextEntities.posts.postsInChannel, post.channel_id, postId);
|
||||
postIdsToKeep.forEach((postId) => {
|
||||
const post = payload.entities.posts.posts[postId];
|
||||
|
||||
return;
|
||||
if (post) {
|
||||
if (retentionPeriod && post.create_at < retentionPeriod) {
|
||||
// This post has been removed by data retention, so don't keep it
|
||||
removeFromPostsInChannel(nextEntities.posts.postsInChannel, post.channel_id, postId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep the post
|
||||
nextEntities.posts.posts[postId] = post;
|
||||
|
||||
// And its reactions
|
||||
const reaction = reactions[postId];
|
||||
if (reaction) {
|
||||
nextEntities.posts.reactions[postId] = reaction;
|
||||
}
|
||||
|
||||
// And its files
|
||||
const fileIds = fileIdsByPostId[postId];
|
||||
if (fileIds) {
|
||||
nextEntities.files.fileIdsByPostId[postId] = fileIds;
|
||||
fileIds.forEach((fileId) => {
|
||||
nextEntities.files.files[fileId] = files[fileId];
|
||||
});
|
||||
}
|
||||
|
||||
// And its comments
|
||||
const threadPosts = postsInThread[postId];
|
||||
if (threadPosts) {
|
||||
nextEntities.posts.postsInThread[postId] = threadPosts;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the post
|
||||
nextEntities.posts.posts[postId] = post;
|
||||
|
||||
// And its reactions
|
||||
const reaction = payload.entities.posts.reactions[postId];
|
||||
if (reaction) {
|
||||
nextEntities.posts.reactions[postId] = reaction;
|
||||
}
|
||||
|
||||
// And its files
|
||||
const fileIds = payload.entities.files.fileIdsByPostId[postId];
|
||||
if (fileIds) {
|
||||
nextEntities.files.fileIdsByPostId[postId] = fileIds;
|
||||
fileIds.forEach((fileId) => {
|
||||
nextEntities.files.files[fileId] = payload.entities.files.files[fileId];
|
||||
});
|
||||
}
|
||||
|
||||
// And its comments
|
||||
const postsInThread = payload.entities.posts.postsInThread[postId];
|
||||
if (postsInThread) {
|
||||
nextEntities.posts.postsInThread[postId] = postsInThread;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Remove any pending posts that haven't failed
|
||||
if (payload.entities.posts && payload.entities.posts.pendingPostIds && payload.entities.posts.pendingPostIds.length) {
|
||||
if (payload.entities.posts?.pendingPostIds?.length) {
|
||||
const nextPendingPostIds = [...payload.entities.posts.pendingPostIds];
|
||||
payload.entities.posts.pendingPostIds.forEach((id) => {
|
||||
const posts = nextEntities.posts.posts;
|
||||
@@ -135,9 +141,9 @@ export function cleanUpState(payload, keepCurrent = false) {
|
||||
}
|
||||
|
||||
nextState.views = {
|
||||
...nextState.views,
|
||||
...(nextState.views || {}),
|
||||
root: {
|
||||
...nextState.views?.root,
|
||||
...(nextState.views?.root || {}),
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
hydrationComplete: nextState.views?.root?.hydrationComplete || !nextState._persist,
|
||||
},
|
||||
@@ -161,41 +167,43 @@ export function cleanUpState(payload, keepCurrent = false) {
|
||||
export function cleanUpPostsInChannel(postsInChannel, lastChannelForTeam, currentChannelId, recentPostCount = 60) {
|
||||
const nextPostsInChannel = {};
|
||||
|
||||
for (const channelIds of Object.values(lastChannelForTeam)) {
|
||||
for (const channelId of channelIds) {
|
||||
if (nextPostsInChannel[channelId]) {
|
||||
// This is a DM or GM channel that we've already seen on another team
|
||||
continue;
|
||||
}
|
||||
|
||||
const postsForChannel = postsInChannel[channelId];
|
||||
|
||||
if (!postsForChannel) {
|
||||
// We don't have anything to keep for this channel
|
||||
continue;
|
||||
}
|
||||
|
||||
let nextPostsForChannel;
|
||||
|
||||
if (channelId === currentChannelId) {
|
||||
// Keep all of the posts for this channel
|
||||
nextPostsForChannel = postsForChannel;
|
||||
} else {
|
||||
// Only keep the most recent posts for this channel
|
||||
const recentBlock = postsForChannel.find((block) => block.recent);
|
||||
|
||||
if (!recentBlock) {
|
||||
// We don't have recent posts for this channel
|
||||
if (postsInChannel && lastChannelForTeam) {
|
||||
for (const channelIds of Object.values(lastChannelForTeam)) {
|
||||
for (const channelId of channelIds) {
|
||||
if (nextPostsInChannel[channelId]) {
|
||||
// This is a DM or GM channel that we've already seen on another team
|
||||
continue;
|
||||
}
|
||||
|
||||
nextPostsForChannel = [{
|
||||
...recentBlock,
|
||||
order: recentBlock.order.slice(0, recentPostCount),
|
||||
}];
|
||||
}
|
||||
const postsForChannel = postsInChannel[channelId];
|
||||
|
||||
nextPostsInChannel[channelId] = nextPostsForChannel;
|
||||
if (!postsForChannel) {
|
||||
// We don't have anything to keep for this channel
|
||||
continue;
|
||||
}
|
||||
|
||||
let nextPostsForChannel;
|
||||
|
||||
if (channelId === currentChannelId) {
|
||||
// Keep all of the posts for this channel
|
||||
nextPostsForChannel = postsForChannel;
|
||||
} else {
|
||||
// Only keep the most recent posts for this channel
|
||||
const recentBlock = postsForChannel.find((block) => block.recent);
|
||||
|
||||
if (!recentBlock) {
|
||||
// We don't have recent posts for this channel
|
||||
continue;
|
||||
}
|
||||
|
||||
nextPostsForChannel = [{
|
||||
...recentBlock,
|
||||
order: recentBlock.order.slice(0, recentPostCount),
|
||||
}];
|
||||
}
|
||||
|
||||
nextPostsInChannel[channelId] = nextPostsForChannel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,9 +214,11 @@ export function cleanUpPostsInChannel(postsInChannel, lastChannelForTeam, curren
|
||||
export function getAllFromPostsInChannel(postsInChannel) {
|
||||
const postIds = [];
|
||||
|
||||
for (const postsForChannel of Object.values(postsInChannel)) {
|
||||
for (const block of postsForChannel) {
|
||||
postIds.push(...block.order);
|
||||
if (postsInChannel) {
|
||||
for (const postsForChannel of Object.values(postsInChannel)) {
|
||||
for (const block of postsForChannel) {
|
||||
postIds.push(...block.order);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ export default async function getStorage(identifier = 'default') {
|
||||
const MMKV = await new MMKVStorage.Loader().
|
||||
withInstanceID(identifier).
|
||||
setProcessingMode(MMKVStorage.MODES.MULTI_PROCESS).
|
||||
withEncryption().
|
||||
initialize();
|
||||
|
||||
return {
|
||||
|
||||
@@ -55,7 +55,7 @@ class JavascriptAndNativeErrorHandler {
|
||||
|
||||
Alert.alert(
|
||||
translations[t('mobile.error_handler.title')],
|
||||
translations[t('mobile.error_handler.description')],
|
||||
translations[t('mobile.error_handler.description')] + `\n\n${e.message}\n\n${e.stack}`,
|
||||
[{
|
||||
text: translations[t('mobile.error_handler.button')],
|
||||
onPress: async () => {
|
||||
|
||||
@@ -90,7 +90,7 @@ export function validatePreviousVersion(previousVersion) {
|
||||
INVALID_VERSIONS.push('1.31.0', '1.31.1');
|
||||
}
|
||||
|
||||
if (!previousVersion || INVALID_VERSIONS.includes(previousVersion)) {
|
||||
if (INVALID_VERSIONS.includes(previousVersion)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Client4} from '@mm-redux/client';
|
||||
import CookieManager from '@react-native-community/cookies';
|
||||
import CookieManager from 'react-native-cookies';
|
||||
|
||||
export function setCSRFFromCookie(url) {
|
||||
return new Promise((resolve) => {
|
||||
|
||||
@@ -936,7 +936,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 302;
|
||||
CURRENT_PROJECT_VERSION = 307;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -945,7 +945,7 @@
|
||||
"$(SRCROOT)/UploadAttachments/UploadAttachments",
|
||||
);
|
||||
INFOPLIST_FILE = Mattermost/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_CFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -978,7 +978,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 302;
|
||||
CURRENT_PROJECT_VERSION = 307;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -987,7 +987,7 @@
|
||||
"$(SRCROOT)/UploadAttachments/UploadAttachments",
|
||||
);
|
||||
INFOPLIST_FILE = Mattermost/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_CFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1035,7 +1035,7 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)/UploadAttachments/UploadAttachments";
|
||||
INFOPLIST_FILE = MattermostShare/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
@@ -1081,7 +1081,7 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)/UploadAttachments/UploadAttachments";
|
||||
INFOPLIST_FILE = MattermostShare/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_CFLAGS = (
|
||||
@@ -1123,7 +1123,7 @@
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
@@ -1167,7 +1167,7 @@
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_CFLAGS = (
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.32.0</string>
|
||||
<string>1.32.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@@ -34,7 +34,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>302</string>
|
||||
<string>307</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.32.0</string>
|
||||
<string>1.32.2</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>302</string>
|
||||
<string>307</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.32.0</string>
|
||||
<string>1.32.2</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>302</string>
|
||||
<string>307</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
|
||||
@@ -207,7 +207,7 @@ PODS:
|
||||
- React-jsinspector (0.62.2)
|
||||
- react-native-cameraroll (1.6.2):
|
||||
- React
|
||||
- react-native-cookies (3.0.0):
|
||||
- react-native-cookies (3.2.0):
|
||||
- React
|
||||
- react-native-document-picker (3.4.0):
|
||||
- React
|
||||
@@ -235,7 +235,7 @@ PODS:
|
||||
- react-native-video/Video (= 5.0.2)
|
||||
- react-native-video/Video (5.0.2):
|
||||
- React
|
||||
- react-native-webview (9.4.0):
|
||||
- react-native-webview (7.0.1):
|
||||
- React
|
||||
- React-RCTActionSheet (0.62.2):
|
||||
- React-Core/RCTActionSheetHeaders (= 0.62.2)
|
||||
@@ -320,7 +320,7 @@ PODS:
|
||||
- React
|
||||
- RNGestureHandler (1.6.1):
|
||||
- React
|
||||
- RNKeychain (6.0.0):
|
||||
- RNKeychain (4.0.5):
|
||||
- React
|
||||
- RNLocalize (1.4.0):
|
||||
- React
|
||||
@@ -380,7 +380,7 @@ DEPENDENCIES:
|
||||
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
|
||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
||||
- "react-native-cameraroll (from `../node_modules/@react-native-community/cameraroll`)"
|
||||
- "react-native-cookies (from `../node_modules/@react-native-community/cookies`)"
|
||||
- react-native-cookies (from `../node_modules/react-native-cookies/ios`)
|
||||
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
|
||||
- react-native-hw-keyboard-event (from `../node_modules/react-native-hw-keyboard-event`)
|
||||
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
|
||||
@@ -484,7 +484,7 @@ EXTERNAL SOURCES:
|
||||
react-native-cameraroll:
|
||||
:path: "../node_modules/@react-native-community/cameraroll"
|
||||
react-native-cookies:
|
||||
:path: "../node_modules/@react-native-community/cookies"
|
||||
:path: "../node_modules/react-native-cookies/ios"
|
||||
react-native-document-picker:
|
||||
:path: "../node_modules/react-native-document-picker"
|
||||
react-native-hw-keyboard-event:
|
||||
@@ -597,7 +597,7 @@ SPEC CHECKSUMS:
|
||||
React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da
|
||||
React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493
|
||||
react-native-cameraroll: 94bec91c68b94ac946c61b497b594bb38692c41b
|
||||
react-native-cookies: 6fe6a70d3b2fd8e81ddcd94d60a87727010d9b2e
|
||||
react-native-cookies: 854d59c4135c70b92a02ca4930e68e4e2eb58150
|
||||
react-native-document-picker: d694111879537cec2c258a1dcd2243d9df746824
|
||||
react-native-hw-keyboard-event: b517cefb8d5c659a38049c582de85ff43337dc53
|
||||
react-native-image-picker: 668e72d0277dc8c12ae90e835507c1eddd2e4f85
|
||||
@@ -609,7 +609,7 @@ SPEC CHECKSUMS:
|
||||
react-native-safe-area: e8230b0017d76c00de6b01e2412dcf86b127c6a3
|
||||
react-native-safe-area-context: 5f3e081f937ab7c44c179d551f332fa494cfcf8f
|
||||
react-native-video: 961749da457e73bf0b5565edfbaffc25abfb8974
|
||||
react-native-webview: 0e2881d2e3e72ad52a82445c1a116c43205fd471
|
||||
react-native-webview: 0d1c2b4e7ffb0543a74fa0512f2f8dc5fb0e49e2
|
||||
React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c
|
||||
React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0
|
||||
React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71
|
||||
@@ -630,7 +630,7 @@ SPEC CHECKSUMS:
|
||||
RNFastImage: 35ae972d6727c84ee3f5c6897e07f84d0a3445e9
|
||||
RNFileViewer: 3600e85d326dadfd0fd367eb38fcb4300b699b49
|
||||
RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38
|
||||
RNKeychain: bf2d7e9a0ae7a073c07770dd2aa6d11c67581733
|
||||
RNKeychain: 840f8e6f13be0576202aefcdffd26a4f54bfe7b5
|
||||
RNLocalize: fc27ee5878ce5a3af73873fb2d8e866e0d1e6d84
|
||||
RNPermissions: 8ca17fd6c822eea589fe84709d9426e05cc39c39
|
||||
RNReactNativeHapticFeedback: 22c5ecf474428766c6b148f96f2ff6155cd7225e
|
||||
|
||||
10230
package-lock.json
generated
10230
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mattermost-mobile",
|
||||
"version": "1.32.0",
|
||||
"version": "1.32.2",
|
||||
"description": "Mattermost Mobile with React Native",
|
||||
"repository": "git@github.com:mattermost/mattermost-mobile.git",
|
||||
"author": "Mattermost, Inc.",
|
||||
@@ -10,7 +10,6 @@
|
||||
"@babel/runtime": "7.9.6",
|
||||
"@react-native-community/async-storage": "1.10.1",
|
||||
"@react-native-community/cameraroll": "1.6.2",
|
||||
"@react-native-community/cookies": "3.0.0",
|
||||
"@react-native-community/masked-view": "0.1.10",
|
||||
"@react-native-community/netinfo": "5.8.1",
|
||||
"@react-navigation/native": "5.3.2",
|
||||
@@ -39,6 +38,7 @@
|
||||
"react-native-button": "3.0.1",
|
||||
"react-native-calendars": "1.265.0",
|
||||
"react-native-circular-progress": "1.3.6",
|
||||
"react-native-cookies": "github:mattermost/react-native-cookies#b35bafc388ae09c83bd875e887daf6a0755e0b40",
|
||||
"react-native-device-info": "5.5.7",
|
||||
"react-native-document-picker": "3.4.0",
|
||||
"react-native-elements": "2.0.0",
|
||||
@@ -52,7 +52,7 @@
|
||||
"react-native-image-picker": "2.3.1",
|
||||
"react-native-keyboard-aware-scroll-view": "0.9.1",
|
||||
"react-native-keyboard-tracking-view": "5.7.0",
|
||||
"react-native-keychain": "6.0.0",
|
||||
"react-native-keychain": "4.0.5",
|
||||
"react-native-linear-gradient": "2.5.6",
|
||||
"react-native-local-auth": "1.6.0",
|
||||
"react-native-localize": "1.4.0",
|
||||
@@ -72,7 +72,7 @@
|
||||
"react-native-v8": "0.62.2-patch.1",
|
||||
"react-native-vector-icons": "6.6.0",
|
||||
"react-native-video": "5.0.2",
|
||||
"react-native-webview": "9.4.0",
|
||||
"react-native-webview": "github:mattermost/react-native-webview#b5e22940a613869d3999feac9451ee65352f4fbe",
|
||||
"react-native-youtube": "2.0.1",
|
||||
"react-redux": "7.2.0",
|
||||
"redux": "4.0.5",
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
|
||||
index 1995d87..33bd83c 100644
|
||||
--- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
|
||||
+++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
|
||||
@@ -2,8 +2,12 @@ package com.reactnativecommunity.webview;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
+import android.app.Activity;
|
||||
+import android.app.AlertDialog;
|
||||
import android.app.DownloadManager;
|
||||
+import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
+import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -15,6 +19,7 @@ import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.content.ContextCompat;
|
||||
+import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
@@ -25,7 +30,9 @@ import android.webkit.ConsoleMessage;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.DownloadListener;
|
||||
import android.webkit.GeolocationPermissions;
|
||||
+import android.webkit.HttpAuthHandler;
|
||||
import android.webkit.JavascriptInterface;
|
||||
+import android.widget.LinearLayout;
|
||||
import android.webkit.PermissionRequest;
|
||||
import android.webkit.URLUtil;
|
||||
import android.webkit.ValueCallback;
|
||||
@@ -35,6 +42,7 @@ import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
+import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.facebook.react.views.scroll.ScrollEvent;
|
||||
@@ -720,6 +728,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
||||
|
||||
protected static class RNCWebViewClient extends WebViewClient {
|
||||
|
||||
+ protected Activity mCurrentActivity;
|
||||
protected boolean mLastLoadFailed = false;
|
||||
protected @Nullable
|
||||
ReadableArray mUrlPrefixesForDefaultIntent;
|
||||
@@ -729,6 +738,10 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
||||
ignoreErrFailedForThisURL = url;
|
||||
}
|
||||
|
||||
+ public void setCurrentActivity(Activity mCurrentActivity) {
|
||||
+ this.mCurrentActivity = mCurrentActivity;
|
||||
+ }
|
||||
+
|
||||
@Override
|
||||
public void onPageFinished(WebView webView, String url) {
|
||||
super.onPageFinished(webView, url);
|
||||
@@ -810,6 +823,49 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
||||
new TopLoadingErrorEvent(webView.getId(), eventData));
|
||||
}
|
||||
|
||||
+ @Override
|
||||
+ public void onReceivedHttpAuthRequest(WebView view,
|
||||
+ final HttpAuthHandler handler, String host, String realm)
|
||||
+ {
|
||||
+ if (this.mCurrentActivity != null) {
|
||||
+ final EditText usernameInput = new EditText(this.mCurrentActivity);
|
||||
+ usernameInput.setHint("Username");
|
||||
+
|
||||
+ final EditText passwordInput = new EditText(this.mCurrentActivity);
|
||||
+ passwordInput.setHint("Password");
|
||||
+ passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
+
|
||||
+ LinearLayout layout = new LinearLayout(this.mCurrentActivity);
|
||||
+ layout.setOrientation(LinearLayout.VERTICAL);
|
||||
+ layout.addView(usernameInput);
|
||||
+ layout.addView(passwordInput);
|
||||
+
|
||||
+ AlertDialog.Builder authDialog = new AlertDialog.Builder(this.mCurrentActivity)
|
||||
+ .setTitle("Authentication Challenge")
|
||||
+ .setMessage(host + " requires user name and password ")
|
||||
+ .setView(layout)
|
||||
+ .setCancelable(false)
|
||||
+ .setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
||||
+ public void onClick(DialogInterface dialogInterface, int i) {
|
||||
+ handler.proceed(usernameInput.getText().toString(), passwordInput.getText().toString());
|
||||
+ dialogInterface.dismiss();
|
||||
+ }
|
||||
+ })
|
||||
+ .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||
+ public void onClick(DialogInterface dialogInterface, int i) {
|
||||
+ dialogInterface.dismiss();
|
||||
+ handler.cancel();
|
||||
+ }
|
||||
+ });
|
||||
+
|
||||
+ if (view != null) {
|
||||
+ authDialog.show();
|
||||
+ }
|
||||
+ } else {
|
||||
+ handler.cancel();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@Override
|
||||
public void onReceivedHttpError(
|
||||
@@ -1010,6 +1066,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
||||
protected boolean sendContentSizeChangeEvents = false;
|
||||
private OnScrollDispatchHelper mOnScrollDispatchHelper;
|
||||
protected boolean hasScrollEvent = false;
|
||||
+ protected ReactContext reactContext;
|
||||
|
||||
/**
|
||||
* WebView must be created with an context of the current activity
|
||||
@@ -1019,6 +1076,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
||||
*/
|
||||
public RNCWebView(ThemedReactContext reactContext) {
|
||||
super(reactContext);
|
||||
+ this.reactContext = reactContext;
|
||||
}
|
||||
|
||||
public void setIgnoreErrFailedForThisURL(String url) {
|
||||
@@ -1069,6 +1127,9 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
||||
super.setWebViewClient(client);
|
||||
if (client instanceof RNCWebViewClient) {
|
||||
mRNCWebViewClient = (RNCWebViewClient) client;
|
||||
+ if (this.reactContext != null && this.reactContext.getCurrentActivity() != null && mRNCWebViewClient != null) {
|
||||
+ mRNCWebViewClient.setCurrentActivity(this.reactContext.getCurrentActivity());
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/node_modules/react-native-webview/apple/RNCWebView.m b/node_modules/react-native-webview/apple/RNCWebView.m
|
||||
index d2f9956..4b8d0fe 100644
|
||||
--- a/node_modules/react-native-webview/apple/RNCWebView.m
|
||||
+++ b/node_modules/react-native-webview/apple/RNCWebView.m
|
||||
@@ -737,7 +737,44 @@ - (void) webView:(WKWebView *)webView
|
||||
if (webView.URL != nil) {
|
||||
host = webView.URL.host;
|
||||
}
|
||||
- if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
|
||||
+
|
||||
+ NSString *authenticationMethod = [[challenge protectionSpace] authenticationMethod];
|
||||
+
|
||||
+ if (authenticationMethod == NSURLAuthenticationMethodNTLM || authenticationMethod == NSURLAuthenticationMethodNegotiate) {
|
||||
+ NSString *title = @"Authentication Challenge";
|
||||
+ NSString *message = [NSString stringWithFormat:@"%@ requires user name and password", host];
|
||||
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
|
||||
+ [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
+ textField.placeholder = @"User";
|
||||
+ }];
|
||||
+
|
||||
+ [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
+ textField.placeholder = @"Password";
|
||||
+ textField.secureTextEntry = YES;
|
||||
+ }];
|
||||
+
|
||||
+ [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
+ NSString *userName = ((UITextField *)alertController.textFields[0]).text;
|
||||
+ NSString *password = ((UITextField *)alertController.textFields[1]).text;
|
||||
+ NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:userName password:password persistence:NSURLCredentialPersistenceNone];
|
||||
+
|
||||
+ completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
|
||||
+ }]];
|
||||
+
|
||||
+ [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
|
||||
+ completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
|
||||
+ }]];
|
||||
+
|
||||
+ dispatch_async(dispatch_get_main_queue(), ^{
|
||||
+ UIViewController *rootVC = UIApplication.sharedApplication.delegate.window.rootViewController;
|
||||
+
|
||||
+ while (rootVC.presentedViewController != nil) {
|
||||
+ rootVC = rootVC.presentedViewController;
|
||||
+ }
|
||||
+ [rootVC presentViewController:alertController animated:YES completion:^{}];
|
||||
+ });
|
||||
+ return;
|
||||
+ } else if (authenticationMethod == NSURLAuthenticationMethodClientCertificate) {
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, clientAuthenticationCredential);
|
||||
return;
|
||||
}
|
||||
@@ -154,7 +154,7 @@ jest.mock('react-native-localize', () => ({
|
||||
]),
|
||||
}));
|
||||
|
||||
jest.mock('@react-native-community/cookies', () => ({
|
||||
jest.mock('react-native-cookies', () => ({
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
openURL: jest.fn(),
|
||||
|
||||
Reference in New Issue
Block a user