forked from Ivasoft/mattermost-mobile
Compare commits
10 Commits
v1.30.0
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41afce3e1d | ||
|
|
87abcce7fa | ||
|
|
8388ae1a30 | ||
|
|
605cc2afd8 | ||
|
|
7f66fe84a7 | ||
|
|
da701268d5 | ||
|
|
318cd13064 | ||
|
|
2fa572bcc7 | ||
|
|
976050ecc2 | ||
|
|
27411cb119 |
@@ -130,8 +130,8 @@ android {
|
||||
applicationId "com.mattermost.rnbeta"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 285
|
||||
versionName "1.30.0"
|
||||
versionCode 287
|
||||
versionName "1.30.1"
|
||||
multiDexEnabled = true
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
|
||||
|
||||
@@ -26,7 +26,6 @@ export function completeLogin(user, deviceToken) {
|
||||
const token = Client4.getToken();
|
||||
const url = Client4.getUrl();
|
||||
|
||||
setCSRFFromCookie(url);
|
||||
setAppCredentials(deviceToken, user.id, token, url);
|
||||
|
||||
// Set timezone
|
||||
@@ -125,6 +124,7 @@ export function login(loginId, password, mfaToken, ldapOnly = false) {
|
||||
|
||||
try {
|
||||
user = await Client4.login(loginId, password, mfaToken, deviceToken, ldapOnly);
|
||||
await setCSRFFromCookie(Client4.getUrl());
|
||||
} catch (error) {
|
||||
return {error};
|
||||
}
|
||||
@@ -142,6 +142,7 @@ export function login(loginId, password, mfaToken, ldapOnly = false) {
|
||||
export function ssoLogin(token) {
|
||||
return async (dispatch) => {
|
||||
Client4.setToken(token);
|
||||
await setCSRFFromCookie(Client4.getUrl());
|
||||
const result = await dispatch(loadMe());
|
||||
|
||||
if (!result.error) {
|
||||
@@ -167,11 +168,8 @@ export function logout(skipServerLogout = false) {
|
||||
}
|
||||
|
||||
export function forceLogoutIfNecessary(error) {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
|
||||
if (currentUserId && error.status_code === HTTP_UNAUTHORIZED && error.url && !error.url.includes('/login')) {
|
||||
return async (dispatch) => {
|
||||
if (error.status_code === HTTP_UNAUTHORIZED && error.url && !error.url.includes('/login')) {
|
||||
dispatch(logout(true));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import PostTextbox from './post_textbox';
|
||||
|
||||
const MAX_MESSAGE_LENGTH = 4000;
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
export function mapStateToProps(state, ownProps) {
|
||||
const currentDraft = ownProps.rootId ? getThreadDraft(state, ownProps.rootId) : getCurrentChannelDraft(state);
|
||||
const config = getConfig(state);
|
||||
|
||||
@@ -50,17 +50,19 @@ function mapStateToProps(state, ownProps) {
|
||||
const currentChannelStats = getCurrentChannelStats(state);
|
||||
const currentChannelMembersCount = currentChannelStats?.member_count || 0; // eslint-disable-line camelcase
|
||||
const isTimezoneEnabled = config?.ExperimentalTimezone === 'true';
|
||||
const canPost = haveIChannelPermission(
|
||||
state,
|
||||
{
|
||||
channel: currentChannel.id,
|
||||
team: currentChannel.team_id,
|
||||
permission: Permissions.CREATE_POST,
|
||||
},
|
||||
);
|
||||
|
||||
let canPost = true;
|
||||
let useChannelMentions = true;
|
||||
if (isMinimumServerVersion(state.entities.general.serverVersion, 5, 22)) {
|
||||
canPost = haveIChannelPermission(
|
||||
state,
|
||||
{
|
||||
channel: currentChannel.id,
|
||||
team: currentChannel.team_id,
|
||||
permission: Permissions.CREATE_POST,
|
||||
},
|
||||
);
|
||||
|
||||
useChannelMentions = haveIChannelPermission(
|
||||
state,
|
||||
{
|
||||
|
||||
100
app/components/post_textbox/index.test.js
Normal file
100
app/components/post_textbox/index.test.js
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Permissions} from 'mattermost-redux/constants';
|
||||
import * as channelSelectors from 'mattermost-redux/selectors/entities/channels';
|
||||
import * as userSelectors from 'mattermost-redux/selectors/entities/users';
|
||||
import * as generalSelectors from 'mattermost-redux/selectors/entities/general';
|
||||
import * as preferenceSelectors from 'mattermost-redux/selectors/entities/preferences';
|
||||
import * as roleSelectors from 'mattermost-redux/selectors/entities/roles';
|
||||
import * as deviceSelectors from 'app/selectors/device';
|
||||
|
||||
import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers';
|
||||
|
||||
import {mapStateToProps} from './index';
|
||||
|
||||
jest.mock('./post_textbox', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
channelSelectors.getCurrentChannel = jest.fn().mockReturnValue({});
|
||||
channelSelectors.isCurrentChannelReadOnly = jest.fn();
|
||||
channelSelectors.getCurrentChannelStats = jest.fn();
|
||||
userSelectors.getStatusForUserId = jest.fn();
|
||||
generalSelectors.canUploadFilesOnMobile = jest.fn();
|
||||
preferenceSelectors.getTheme = jest.fn();
|
||||
roleSelectors.haveIChannelPermission = jest.fn();
|
||||
deviceSelectors.isLandscape = jest.fn();
|
||||
|
||||
describe('mapStateToProps', () => {
|
||||
const baseState = {
|
||||
entities: {
|
||||
general: {
|
||||
config: {},
|
||||
serverVersion: '',
|
||||
},
|
||||
users: {
|
||||
currentUserId: '',
|
||||
},
|
||||
channels: {
|
||||
currentChannelId: '',
|
||||
},
|
||||
preferences: {
|
||||
myPreferences: {},
|
||||
},
|
||||
},
|
||||
views: {
|
||||
channel: {
|
||||
drafts: {},
|
||||
},
|
||||
},
|
||||
requests: {
|
||||
files: {
|
||||
uploadFiles: {
|
||||
status: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const baseOwnProps = {};
|
||||
|
||||
test('haveIChannelPermission is not called when isMinimumServerVersion is not 5.22v', () => {
|
||||
const state = {...baseState};
|
||||
state.entities.general.serverVersion = '5.21';
|
||||
|
||||
mapStateToProps(state, baseOwnProps);
|
||||
expect(isMinimumServerVersion(state.entities.general.serverVersion, 5, 22)).toBe(false);
|
||||
|
||||
expect(roleSelectors.haveIChannelPermission).not.toHaveBeenCalledWith(state, {
|
||||
channel: undefined,
|
||||
team: undefined,
|
||||
permission: Permissions.CREATE_POST,
|
||||
});
|
||||
|
||||
expect(roleSelectors.haveIChannelPermission).not.toHaveBeenCalledWith(state, {
|
||||
channel: undefined,
|
||||
permission: Permissions.USE_CHANNEL_MENTIONS,
|
||||
});
|
||||
});
|
||||
|
||||
test('haveIChannelPermission is called when isMinimumServerVersion is 5.22v', () => {
|
||||
const state = {...baseState};
|
||||
state.entities.general.serverVersion = '5.22';
|
||||
|
||||
mapStateToProps(state, baseOwnProps);
|
||||
expect(isMinimumServerVersion(state.entities.general.serverVersion, 5, 22)).toBe(true);
|
||||
|
||||
expect(roleSelectors.haveIChannelPermission).toHaveBeenCalledWith(state, {
|
||||
channel: undefined,
|
||||
team: undefined,
|
||||
permission: Permissions.CREATE_POST,
|
||||
});
|
||||
|
||||
expect(roleSelectors.haveIChannelPermission).toHaveBeenCalledWith(state, {
|
||||
channel: undefined,
|
||||
permission: Permissions.USE_CHANNEL_MENTIONS,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -280,6 +280,7 @@ class GlobalEventHandler {
|
||||
app: {
|
||||
build: DeviceInfo.getBuildNumber(),
|
||||
version: DeviceInfo.getVersion(),
|
||||
previousVersion: DeviceInfo.getVersion(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@ import {Provider} from 'react-redux';
|
||||
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import {loadMe} from 'app/actions/views/user';
|
||||
import {loadMe, logout} from 'app/actions/views/user';
|
||||
|
||||
import {resetToChannel, resetToSelectServer} from 'app/actions/navigation';
|
||||
import {setDeepLinkURL} from 'app/actions/views/root';
|
||||
@@ -24,6 +24,7 @@ import EphemeralStore from 'app/store/ephemeral_store';
|
||||
import telemetry from 'app/telemetry';
|
||||
import {validatePreviousVersion} from 'app/utils/general';
|
||||
import pushNotificationsUtils from 'app/utils/push_notifications';
|
||||
import {captureJSException} from 'app/utils/sentry';
|
||||
|
||||
const init = async () => {
|
||||
const credentials = await getAppCredentials();
|
||||
@@ -53,10 +54,15 @@ const launchApp = (credentials) => {
|
||||
|
||||
if (credentials) {
|
||||
waitForHydration(store, () => {
|
||||
const valid = validatePreviousVersion(store);
|
||||
const {previousVersion} = store.getState().app;
|
||||
const valid = validatePreviousVersion(previousVersion);
|
||||
if (valid) {
|
||||
store.dispatch(loadMe());
|
||||
resetToChannel({skipMetrics: true});
|
||||
} else {
|
||||
const error = new Error(`Previous app version "${previousVersion}" is invalid.`);
|
||||
captureJSException(error, false, store);
|
||||
store.dispatch(logout());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -36,11 +36,12 @@ export default {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
},
|
||||
authenticate: () => {
|
||||
authenticate: (opts) => {
|
||||
if (!LocalAuth) {
|
||||
LocalAuth = require('react-native-local-auth');
|
||||
}
|
||||
return LocalAuth.auth;
|
||||
|
||||
return LocalAuth.auth(opts);
|
||||
},
|
||||
blurAppScreen: emptyFunction,
|
||||
appGroupIdentifier: null,
|
||||
|
||||
@@ -5,8 +5,10 @@ import {combineReducers} from 'redux';
|
||||
|
||||
import build from './build';
|
||||
import version from './version';
|
||||
import previousVersion from './previousVersion';
|
||||
|
||||
export default combineReducers({
|
||||
build,
|
||||
version,
|
||||
previousVersion,
|
||||
});
|
||||
|
||||
6
app/reducers/app/previousVersion.js
Normal file
6
app/reducers/app/previousVersion.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export default function previousVersion(state = '') {
|
||||
return state;
|
||||
}
|
||||
@@ -56,14 +56,18 @@ export function makeMapStateToProps() {
|
||||
let {canDelete} = ownProps;
|
||||
let canFlag = true;
|
||||
let canPin = true;
|
||||
const canPost = haveIChannelPermission(
|
||||
state,
|
||||
{
|
||||
channel: post.channel_id,
|
||||
team: channel.team_id,
|
||||
permission: Permissions.CREATE_POST,
|
||||
},
|
||||
);
|
||||
|
||||
let canPost = true;
|
||||
if (isMinimumServerVersion(serverVersion, 5, 22)) {
|
||||
canPost = haveIChannelPermission(
|
||||
state,
|
||||
{
|
||||
channel: post.channel_id,
|
||||
team: channel.team_id,
|
||||
permission: Permissions.CREATE_POST,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (hasNewPermissions(state)) {
|
||||
canAddReaction = haveIChannelPermission(state, {
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
// See LICENSE.txt for license information.
|
||||
import {makeMapStateToProps} from './index';
|
||||
|
||||
import {Permissions} from 'mattermost-redux/constants';
|
||||
import * as channelSelectors from 'mattermost-redux/selectors/entities/channels';
|
||||
import * as generalSelectors from 'mattermost-redux/selectors/entities/general';
|
||||
import * as userSelectors from 'mattermost-redux/selectors/entities/users';
|
||||
import * as commonSelectors from 'mattermost-redux/selectors/entities/common';
|
||||
import * as teamSelectors from 'mattermost-redux/selectors/entities/teams';
|
||||
import * as roleSelectors from 'mattermost-redux/selectors/entities/roles';
|
||||
import * as deviceSelectors from 'app/selectors/device';
|
||||
import * as preferencesSelectors from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {isMinimumServerVersion} from 'mattermost-redux/utils/helpers';
|
||||
@@ -26,6 +28,7 @@ teamSelectors.getCurrentTeamUrl = jest.fn();
|
||||
deviceSelectors.getDimensions = jest.fn();
|
||||
deviceSelectors.isLandscape = jest.fn();
|
||||
preferencesSelectors.getTheme = jest.fn();
|
||||
roleSelectors.haveIChannelPermission = jest.fn();
|
||||
|
||||
describe('makeMapStateToProps', () => {
|
||||
const baseState = {
|
||||
@@ -135,4 +138,44 @@ describe('makeMapStateToProps', () => {
|
||||
expect(isMinimumServerVersion(state.entities.general.serverVersion, 5, 18)).toBe(false);
|
||||
expect(props.canMarkAsUnread).toBe(false);
|
||||
});
|
||||
|
||||
test('haveIChannelPermission for canPost is not called when isMinimumServerVersion is not 5.22v', () => {
|
||||
const state = {
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
general: {
|
||||
serverVersion: '5.21',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const mapStateToProps = makeMapStateToProps();
|
||||
mapStateToProps(state, baseOwnProps);
|
||||
expect(isMinimumServerVersion(state.entities.general.serverVersion, 5, 22)).toBe(false);
|
||||
expect(roleSelectors.haveIChannelPermission).not.toHaveBeenCalledWith(state, {
|
||||
channel: undefined,
|
||||
team: undefined,
|
||||
permission: Permissions.CREATE_POST,
|
||||
});
|
||||
});
|
||||
|
||||
test('haveIChannelPermission for canPost is called when isMinimumServerVersion is 5.22v', () => {
|
||||
const state = {
|
||||
entities: {
|
||||
...baseState.entities,
|
||||
general: {
|
||||
serverVersion: '5.22',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const mapStateToProps = makeMapStateToProps();
|
||||
mapStateToProps(state, baseOwnProps);
|
||||
expect(isMinimumServerVersion(state.entities.general.serverVersion, 5, 22)).toBe(true);
|
||||
expect(roleSelectors.haveIChannelPermission).toHaveBeenCalledWith(state, {
|
||||
channel: undefined,
|
||||
team: undefined,
|
||||
permission: Permissions.CREATE_POST,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -67,6 +67,10 @@ export default class SelectServer extends PureComponent {
|
||||
serverUrl: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
allowOtherServers: true,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
@@ -159,11 +163,18 @@ export default class SelectServer extends PureComponent {
|
||||
};
|
||||
|
||||
goToNextScreen = (screen, title, passProps = {}, navOptions = {}) => {
|
||||
const {allowOtherServers} = this.props;
|
||||
let visible = !LocalConfig.AutoSelectServerUrl;
|
||||
|
||||
if (!allowOtherServers) {
|
||||
visible = false;
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
popGesture: !LocalConfig.AutoSelectServerUrl,
|
||||
popGesture: visible,
|
||||
topBar: {
|
||||
visible: !LocalConfig.AutoSelectServerUrl,
|
||||
height: LocalConfig.AutoSelectServerUrl ? 0 : null,
|
||||
visible,
|
||||
height: visible ? null : 0,
|
||||
},
|
||||
};
|
||||
const options = merge(defaultOptions, navOptions);
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import initialState from 'app/initial_state';
|
||||
|
||||
import EphemeralStore from './ephemeral_store';
|
||||
|
||||
import {
|
||||
captureException,
|
||||
@@ -19,23 +16,21 @@ export function messageRetention(store) {
|
||||
const {app} = action.payload;
|
||||
const {entities, views} = action.payload;
|
||||
|
||||
if (!entities || !views) {
|
||||
const version = DeviceInfo.getVersion();
|
||||
EphemeralStore.prevAppVersion = version;
|
||||
action.payload = {
|
||||
...action.payload,
|
||||
app: {
|
||||
build: DeviceInfo.getBuildNumber(),
|
||||
version,
|
||||
},
|
||||
};
|
||||
return next(action);
|
||||
}
|
||||
const build = DeviceInfo.getBuildNumber();
|
||||
const version = DeviceInfo.getVersion();
|
||||
const previousVersion = app?.version;
|
||||
|
||||
EphemeralStore.prevAppVersion = app?.version;
|
||||
if (app?.version !== DeviceInfo.getVersion() || app?.build !== DeviceInfo.getBuildNumber()) {
|
||||
// When a new version of the app has been detected
|
||||
return next(resetStateForNewVersion(action));
|
||||
action.payload = {
|
||||
...action.payload,
|
||||
app: {
|
||||
build,
|
||||
version,
|
||||
previousVersion,
|
||||
},
|
||||
};
|
||||
|
||||
if (!entities || !views) {
|
||||
return next(action);
|
||||
}
|
||||
|
||||
// Keep only the last 60 messages for the last 5 viewed channels in each team
|
||||
@@ -60,140 +55,6 @@ export function messageRetention(store) {
|
||||
};
|
||||
}
|
||||
|
||||
function resetStateForNewVersion(action) {
|
||||
const {payload} = action;
|
||||
const lastChannelForTeam = getLastChannelForTeam(payload);
|
||||
|
||||
let general = initialState.entities.general;
|
||||
if (payload.entities.general) {
|
||||
general = payload.entities.general;
|
||||
}
|
||||
|
||||
let teams = initialState.entities.teams;
|
||||
if (payload.entities.teams) {
|
||||
teams = {
|
||||
currentTeamId: payload.entities.teams.currentTeamId,
|
||||
teams: payload.entities.teams.teams,
|
||||
myMembers: payload.entities.teams.myMembers,
|
||||
};
|
||||
}
|
||||
|
||||
let users = initialState.entities.users;
|
||||
if (payload.entities.users) {
|
||||
const currentUserId = payload.entities.users.currentUserId;
|
||||
if (currentUserId) {
|
||||
users = {
|
||||
currentUserId,
|
||||
profiles: {
|
||||
[currentUserId]: payload.entities.users.profiles[currentUserId],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let preferences = initialState.entities.preferences;
|
||||
if (payload.entities.preferences) {
|
||||
preferences = payload.entities.preferences;
|
||||
}
|
||||
|
||||
let roles = initialState.entities.roles;
|
||||
if (payload.entities.roles) {
|
||||
roles = payload.entities.roles;
|
||||
}
|
||||
|
||||
let search = initialState.entities.search;
|
||||
if (payload.entities.search && payload.entities.search.recent) {
|
||||
search = {
|
||||
recent: payload.entities.search.recent,
|
||||
};
|
||||
}
|
||||
|
||||
let channelDrafts = initialState.views.channel.drafts;
|
||||
if (payload.views.channel && payload.views.channel.drafts) {
|
||||
channelDrafts = payload.views.channel.drafts;
|
||||
}
|
||||
|
||||
let i18n = initialState.views.i18n;
|
||||
if (payload.views.i18n) {
|
||||
i18n = payload.views.i18n;
|
||||
}
|
||||
|
||||
let lastTeamId = initialState.views.team.lastTeamId;
|
||||
if (payload.views.team && payload.views.team.lastTeamId) {
|
||||
lastTeamId = payload.views.team.lastTeamId;
|
||||
}
|
||||
|
||||
const currentChannelId = lastChannelForTeam[lastTeamId] && lastChannelForTeam[lastTeamId].length ? lastChannelForTeam[lastTeamId][0] : '';
|
||||
let channels = initialState.entities.channels;
|
||||
if (payload.entities.channels && currentChannelId) {
|
||||
channels = {
|
||||
currentChannelId,
|
||||
channels: {
|
||||
[currentChannelId]: payload.entities.channels.channels[currentChannelId],
|
||||
},
|
||||
myMembers: {
|
||||
[currentChannelId]: payload.entities.channels.myMembers[currentChannelId],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let threadDrafts = initialState.views.thread.drafts;
|
||||
if (payload.views.thread && payload.views.thread.drafts) {
|
||||
threadDrafts = payload.views.thread.drafts;
|
||||
}
|
||||
|
||||
let selectServer = initialState.views.selectServer;
|
||||
if (payload.views.selectServer) {
|
||||
selectServer = payload.views.selectServer;
|
||||
}
|
||||
|
||||
let recentEmojis = initialState.views.recentEmojis;
|
||||
if (payload.views.recentEmojis) {
|
||||
recentEmojis = payload.views.recentEmojis;
|
||||
}
|
||||
|
||||
const nextState = {
|
||||
app: {
|
||||
build: DeviceInfo.getBuildNumber(),
|
||||
version: DeviceInfo.getVersion(),
|
||||
},
|
||||
entities: {
|
||||
channels,
|
||||
general,
|
||||
teams,
|
||||
users,
|
||||
preferences,
|
||||
search,
|
||||
roles,
|
||||
},
|
||||
views: {
|
||||
channel: {
|
||||
drafts: channelDrafts,
|
||||
},
|
||||
i18n,
|
||||
team: {
|
||||
lastTeamId,
|
||||
lastChannelForTeam,
|
||||
},
|
||||
thread: {
|
||||
drafts: threadDrafts,
|
||||
},
|
||||
selectServer,
|
||||
recentEmojis,
|
||||
},
|
||||
websocket: {
|
||||
lastConnectAt: payload.websocket?.lastConnectAt,
|
||||
lastDisconnectAt: payload.websocket?.lastDisconnectAt,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
type: action.type,
|
||||
payload: nextState,
|
||||
error: action.error,
|
||||
};
|
||||
}
|
||||
|
||||
function getLastChannelForTeam(payload) {
|
||||
const lastChannelForTeam = {...payload.views.team.lastChannelForTeam};
|
||||
const convertLastChannelForTeam = Object.values(lastChannelForTeam).some((value) => !Array.isArray(value));
|
||||
@@ -208,11 +69,14 @@ function getLastChannelForTeam(payload) {
|
||||
}
|
||||
|
||||
export function cleanUpState(action, keepCurrent = false) {
|
||||
const {payload: resetPayload} = resetStateForNewVersion(action);
|
||||
const {payload} = action;
|
||||
const {currentChannelId} = payload.entities.channels;
|
||||
const nextState = Object.assign({}, payload);
|
||||
|
||||
const lastTeamId = payload.views?.team?.lastTeamId;
|
||||
|
||||
const lastChannelForTeam = getLastChannelForTeam(payload);
|
||||
const currentChannelId = lastChannelForTeam[lastTeamId] && lastChannelForTeam[lastTeamId].length ? lastChannelForTeam[lastTeamId][0] : '';
|
||||
|
||||
const {lastChannelForTeam} = resetPayload.views.team;
|
||||
const nextEntities = {
|
||||
posts: {
|
||||
posts: {},
|
||||
@@ -230,9 +94,9 @@ export function cleanUpState(action, keepCurrent = false) {
|
||||
};
|
||||
|
||||
let retentionPeriod = 0;
|
||||
if (resetPayload.entities.general && resetPayload.entities.general.dataRetentionPolicy &&
|
||||
resetPayload.entities.general.dataRetentionPolicy.message_deletion_enabled) {
|
||||
retentionPeriod = resetPayload.entities.general.dataRetentionPolicy.message_retention_cutoff;
|
||||
if (payload.entities.general && payload.entities.general.dataRetentionPolicy &&
|
||||
payload.entities.general.dataRetentionPolicy.message_deletion_enabled) {
|
||||
retentionPeriod = payload.entities.general.dataRetentionPolicy.message_retention_cutoff;
|
||||
}
|
||||
|
||||
const postIdsToKeep = [];
|
||||
@@ -258,6 +122,12 @@ export function cleanUpState(action, keepCurrent = false) {
|
||||
}
|
||||
}
|
||||
|
||||
const nextSearch = {
|
||||
...payload.entities.search,
|
||||
results: searchResults,
|
||||
flagged: flaggedPosts,
|
||||
};
|
||||
|
||||
postIdsToKeep.forEach((postId) => {
|
||||
const post = payload.entities.posts.posts[postId];
|
||||
|
||||
@@ -316,39 +186,12 @@ export function cleanUpState(action, keepCurrent = false) {
|
||||
nextEntities.posts.pendingPostIds = nextPendingPostIds;
|
||||
}
|
||||
|
||||
const nextState = {
|
||||
app: resetPayload.app,
|
||||
entities: {
|
||||
...nextEntities,
|
||||
channels: payload.entities.channels,
|
||||
emojis: payload.entities.emojis,
|
||||
general: resetPayload.entities.general,
|
||||
preferences: resetPayload.entities.preferences,
|
||||
search: {
|
||||
...resetPayload.entities.search,
|
||||
results: searchResults,
|
||||
flagged: flaggedPosts,
|
||||
},
|
||||
teams: resetPayload.entities.teams,
|
||||
users: payload.entities.users,
|
||||
roles: resetPayload.entities.roles,
|
||||
},
|
||||
views: {
|
||||
announcement: payload.views.announcement,
|
||||
...resetPayload.views,
|
||||
channel: {
|
||||
...resetPayload.views.channel,
|
||||
...payload.views.channel,
|
||||
},
|
||||
},
|
||||
websocket: {
|
||||
lastConnectAt: payload.websocket?.lastConnectAt,
|
||||
lastDisconnectAt: payload.websocket?.lastDisconnectAt,
|
||||
},
|
||||
nextState.entities = {
|
||||
...nextState.entities,
|
||||
...nextEntities,
|
||||
search: nextSearch,
|
||||
};
|
||||
|
||||
nextState.errors = payload.errors;
|
||||
|
||||
return {
|
||||
type: action.type,
|
||||
payload: nextState,
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
/* eslint-disable max-nested-callbacks */
|
||||
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
|
||||
import assert from 'assert';
|
||||
|
||||
import {ViewTypes} from 'app/constants';
|
||||
@@ -55,6 +57,122 @@ describe('messageRetention', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('should add build, version, and previousVersion to payload.app on persist/REHYDRATE', () => {
|
||||
const next = (a) => a;
|
||||
const store = {};
|
||||
const build = 'build';
|
||||
const version = 'version';
|
||||
const previousBuild = 'previous-build';
|
||||
const previousVersion = 'previous-version';
|
||||
DeviceInfo.getBuildNumber = jest.fn().mockReturnValue('build');
|
||||
DeviceInfo.getVersion = jest.fn().mockReturnValue('version');
|
||||
const rehydrateAction = {
|
||||
type: 'persist/REHYDRATE',
|
||||
payload: {
|
||||
app: {
|
||||
build: previousBuild,
|
||||
version: previousVersion,
|
||||
},
|
||||
},
|
||||
};
|
||||
const expectedPayloadApp = {
|
||||
build,
|
||||
version,
|
||||
previousVersion,
|
||||
};
|
||||
const entities = {
|
||||
channels: {},
|
||||
posts: {},
|
||||
};
|
||||
const views = {
|
||||
team: {
|
||||
lastChannelForTeam: {},
|
||||
},
|
||||
};
|
||||
|
||||
test('when entities is missing', () => {
|
||||
const action = {...rehydrateAction};
|
||||
|
||||
const nextAction = messageRetention(store)(next)(action);
|
||||
expect(nextAction.payload.app).toStrictEqual(expectedPayloadApp);
|
||||
});
|
||||
|
||||
test('when views is missing', () => {
|
||||
const action = {
|
||||
...rehydrateAction,
|
||||
payload: {
|
||||
...rehydrateAction.payload,
|
||||
entities,
|
||||
},
|
||||
};
|
||||
|
||||
const nextAction = messageRetention(store)(next)(action);
|
||||
expect(nextAction.payload.app).toStrictEqual(expectedPayloadApp);
|
||||
});
|
||||
|
||||
test('when previousVersion !== version', () => {
|
||||
const action = {
|
||||
...rehydrateAction,
|
||||
payload: {
|
||||
...rehydrateAction.payload,
|
||||
entities,
|
||||
views,
|
||||
},
|
||||
};
|
||||
expect(action.payload.app.version).not.toEqual(DeviceInfo.getVersion());
|
||||
|
||||
const nextAction = messageRetention(store)(next)(action);
|
||||
expect(nextAction.payload.app).toStrictEqual(expectedPayloadApp);
|
||||
});
|
||||
|
||||
test('when previousBuild !== build', () => {
|
||||
const action = {
|
||||
...rehydrateAction,
|
||||
payload: {
|
||||
...rehydrateAction.payload,
|
||||
app: {
|
||||
...rehydrateAction.payload.app,
|
||||
version: DeviceInfo.getVersion(),
|
||||
},
|
||||
entities,
|
||||
views,
|
||||
},
|
||||
};
|
||||
expect(action.payload.app.version).toEqual(DeviceInfo.getVersion());
|
||||
expect(action.payload.app.build).not.toEqual(DeviceInfo.getBuildNumber());
|
||||
|
||||
const nextAction = messageRetention(store)(next)(action);
|
||||
expect(nextAction.payload.app).toStrictEqual({
|
||||
...expectedPayloadApp,
|
||||
previousVersion: DeviceInfo.getVersion(),
|
||||
});
|
||||
});
|
||||
|
||||
test('when cleanUpState', () => {
|
||||
const action = {
|
||||
...rehydrateAction,
|
||||
payload: {
|
||||
...rehydrateAction.payload,
|
||||
app: {
|
||||
...rehydrateAction.payload.app,
|
||||
version: DeviceInfo.getVersion(),
|
||||
build: DeviceInfo.getBuildNumber(),
|
||||
},
|
||||
entities,
|
||||
views,
|
||||
},
|
||||
};
|
||||
expect(action.payload.app.version).toEqual(DeviceInfo.getVersion());
|
||||
expect(action.payload.app.build).toEqual(DeviceInfo.getBuildNumber());
|
||||
|
||||
const nextAction = messageRetention(store)(next)(action);
|
||||
expect(nextAction.payload.app).toStrictEqual({
|
||||
...expectedPayloadApp,
|
||||
previousVersion: DeviceInfo.getVersion(),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('cleanUpState', () => {
|
||||
|
||||
@@ -67,6 +67,7 @@ export function waitForHydration(store, callback) {
|
||||
}
|
||||
|
||||
export function getStateForReset(initialState, currentState) {
|
||||
const {app} = currentState;
|
||||
const {currentUserId} = currentState.entities.users;
|
||||
const currentUserProfile = currentState.entities.users.profiles[currentUserId];
|
||||
const {currentTeamId} = currentState.entities.teams;
|
||||
@@ -79,8 +80,8 @@ export function getStateForReset(initialState, currentState) {
|
||||
|
||||
const resetState = merge(initialState, {
|
||||
app: {
|
||||
build: DeviceInfo.getBuildNumber(),
|
||||
version: DeviceInfo.getVersion(),
|
||||
...app,
|
||||
previousVersion: DeviceInfo.getVersion(),
|
||||
},
|
||||
entities: {
|
||||
users: {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
|
||||
import initialState from 'app/initial_state';
|
||||
import {getStateForReset} from 'app/store/utils';
|
||||
|
||||
@@ -20,6 +22,11 @@ describe('getStateForReset', () => {
|
||||
const otherUserId = 'other-user-id';
|
||||
const currentTeamId = 'current-team-id';
|
||||
const currentState = {
|
||||
app: {
|
||||
build: DeviceInfo.getBuildNumber(),
|
||||
version: DeviceInfo.getVersion(),
|
||||
previousVersion: 'previousVersion',
|
||||
},
|
||||
entities: {
|
||||
users: {
|
||||
currentUserId,
|
||||
@@ -76,4 +83,10 @@ describe('getStateForReset', () => {
|
||||
expect(themeKeys.length).not.toEqual(0);
|
||||
expect(themeKeys.length).toEqual(preferenceKeys.length);
|
||||
});
|
||||
|
||||
it('should set previous version as current', () => {
|
||||
const resetState = getStateForReset(initialState, currentState);
|
||||
const {app} = resetState;
|
||||
expect(app.previousVersion).toStrictEqual(currentState.app.version);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,9 +6,6 @@ import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
||||
|
||||
import {Posts} from 'mattermost-redux/constants';
|
||||
|
||||
import {logout} from 'app/actions/views/user';
|
||||
import EphemeralStore from 'app/store/ephemeral_store';
|
||||
|
||||
const INVALID_VERSIONS = ['1.29.0'];
|
||||
|
||||
export function fromAutoResponder(post) {
|
||||
@@ -88,12 +85,9 @@ export function isPendingPost(postId, userId) {
|
||||
return postId.startsWith(userId);
|
||||
}
|
||||
|
||||
export function validatePreviousVersion(store) {
|
||||
const version = EphemeralStore.prevAppVersion;
|
||||
|
||||
if (!version || INVALID_VERSIONS.includes(version)) {
|
||||
console.log('Previous version is no longer valid'); //eslint-disable-line no-console
|
||||
store.dispatch(logout());
|
||||
export function validatePreviousVersion(previousVersion) {
|
||||
if (!previousVersion || INVALID_VERSIONS.includes(previousVersion)) {
|
||||
console.log(`Previous version "${previousVersion}" is no longer valid`); //eslint-disable-line no-console
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1118,7 +1118,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 285;
|
||||
CURRENT_PROJECT_VERSION = 287;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -1156,7 +1156,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 285;
|
||||
CURRENT_PROJECT_VERSION = 287;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.30.0</string>
|
||||
<string>1.30.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@@ -34,7 +34,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>285</string>
|
||||
<string>287</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.30.0</string>
|
||||
<string>1.30.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>285</string>
|
||||
<string>287</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.30.0</string>
|
||||
<string>1.30.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>285</string>
|
||||
<string>287</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
|
||||
@@ -212,6 +212,8 @@ PODS:
|
||||
- React
|
||||
- react-native-image-picker (2.3.1):
|
||||
- React
|
||||
- react-native-local-auth (1.6.0):
|
||||
- React
|
||||
- react-native-netinfo (4.4.0):
|
||||
- React
|
||||
- react-native-notifications (2.0.6):
|
||||
@@ -338,6 +340,7 @@ DEPENDENCIES:
|
||||
- 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`)
|
||||
- react-native-local-auth (from `../node_modules/react-native-local-auth`)
|
||||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||
- react-native-notifications (from `../node_modules/react-native-notifications`)
|
||||
- react-native-passcode-status (from `../node_modules/react-native-passcode-status`)
|
||||
@@ -434,6 +437,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-hw-keyboard-event"
|
||||
react-native-image-picker:
|
||||
:path: "../node_modules/react-native-image-picker"
|
||||
react-native-local-auth:
|
||||
:path: "../node_modules/react-native-local-auth"
|
||||
react-native-netinfo:
|
||||
:path: "../node_modules/@react-native-community/netinfo"
|
||||
react-native-notifications:
|
||||
@@ -526,6 +531,7 @@ SPEC CHECKSUMS:
|
||||
react-native-document-picker: 0573c02d742d4bef38a5d16b5f039754cfa69888
|
||||
react-native-hw-keyboard-event: b517cefb8d5c659a38049c582de85ff43337dc53
|
||||
react-native-image-picker: 668e72d0277dc8c12ae90e835507c1eddd2e4f85
|
||||
react-native-local-auth: 359af242caa1e5c501ac9dfe33b1e238ad8f08c6
|
||||
react-native-netinfo: 892a5130be97ff8bb69c523739c424a2ffc296d1
|
||||
react-native-notifications: d5cb54ef8bf3004dcb56c887650dea08ecbddee7
|
||||
react-native-passcode-status: 88c4f6e074328bc278bd127646b6c694ad5a530a
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mattermost-mobile",
|
||||
"version": "1.30.0",
|
||||
"version": "1.30.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mattermost-mobile",
|
||||
"version": "1.30.0",
|
||||
"version": "1.30.1",
|
||||
"description": "Mattermost Mobile with React Native",
|
||||
"repository": "git@github.com:mattermost/mattermost-mobile.git",
|
||||
"author": "Mattermost, Inc.",
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
diff --git a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java
|
||||
index 48fb5c1..5085663 100644
|
||||
index 48fb5c1..3def244 100644
|
||||
--- a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java
|
||||
+++ b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java
|
||||
@@ -49,6 +49,7 @@ import java.io.InputStream;
|
||||
@@ -3,6 +3,7 @@ package com.imagepicker;
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
+import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
@@ -49,6 +50,7 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
@@ -10,7 +18,7 @@ index 48fb5c1..5085663 100644
|
||||
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
import com.facebook.react.modules.core.PermissionAwareActivity;
|
||||
@@ -69,6 +70,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -69,6 +71,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
public static final int REQUEST_LAUNCH_IMAGE_LIBRARY = 13002;
|
||||
public static final int REQUEST_LAUNCH_VIDEO_LIBRARY = 13003;
|
||||
public static final int REQUEST_LAUNCH_VIDEO_CAPTURE = 13004;
|
||||
@@ -18,7 +26,7 @@ index 48fb5c1..5085663 100644
|
||||
public static final int REQUEST_PERMISSIONS_FOR_CAMERA = 14001;
|
||||
public static final int REQUEST_PERMISSIONS_FOR_LIBRARY = 14002;
|
||||
|
||||
@@ -266,26 +268,24 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -266,26 +269,24 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
cameraIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, videoDurationLimit);
|
||||
}
|
||||
}
|
||||
@@ -58,35 +66,159 @@ index 48fb5c1..5085663 100644
|
||||
}
|
||||
|
||||
if (cameraIntent.resolveActivity(reactContext.getPackageManager()) == null)
|
||||
@@ -444,14 +444,20 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
callback = null;
|
||||
return;
|
||||
@@ -350,16 +351,19 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
libraryIntent = new Intent(Intent.ACTION_PICK);
|
||||
libraryIntent.setType("video/*");
|
||||
}
|
||||
+ else if (pickBoth) {
|
||||
+ libraryIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
+ libraryIntent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
+ libraryIntent.setType("image/*");
|
||||
+ libraryIntent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {"image/*", "video/*"});
|
||||
+ requestCode = REQUEST_LAUNCH_MIXED_CAPTURE;
|
||||
+ }
|
||||
else
|
||||
{
|
||||
requestCode = REQUEST_LAUNCH_IMAGE_LIBRARY;
|
||||
libraryIntent = new Intent(Intent.ACTION_PICK,
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||
-
|
||||
- if (pickBoth)
|
||||
- {
|
||||
- libraryIntent.setType("image/* video/*");
|
||||
- }
|
||||
+ libraryIntent.setType("image/*");
|
||||
}
|
||||
|
||||
+ case REQUEST_LAUNCH_MIXED_CAPTURE:
|
||||
case REQUEST_LAUNCH_VIDEO_CAPTURE:
|
||||
if (libraryIntent.resolveActivity(reactContext.getPackageManager()) == null)
|
||||
@@ -385,75 +389,47 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
}
|
||||
}
|
||||
|
||||
- @Override
|
||||
- public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
- //robustness code
|
||||
- if (passResult(requestCode))
|
||||
- {
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- responseHelper.cleanResponse();
|
||||
-
|
||||
- // user cancel
|
||||
- if (resultCode != Activity.RESULT_OK)
|
||||
- {
|
||||
- removeUselessFiles(requestCode, imageConfig);
|
||||
- responseHelper.invokeCancel(callback);
|
||||
- callback = null;
|
||||
- return;
|
||||
- }
|
||||
+ protected String getMimeType(Activity activity, Uri uri) {
|
||||
+ String mimeType = null;
|
||||
+ if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
|
||||
+ ContentResolver cr = activity.getApplicationContext().getContentResolver();
|
||||
+ mimeType = cr.getType(uri);
|
||||
+ } else {
|
||||
+ String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri
|
||||
+ .toString());
|
||||
+ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
|
||||
+ fileExtension.toLowerCase());
|
||||
+ }
|
||||
+ return mimeType;
|
||||
+ }
|
||||
|
||||
- Uri uri = null;
|
||||
- switch (requestCode)
|
||||
+ protected void extractImageFromResult(Activity activity, Uri uri, int requestCode) {
|
||||
+ String realPath = getRealPathFromURI(uri);
|
||||
+ String mime = getMimeType(activity, uri);
|
||||
+ final boolean isUrl = !TextUtils.isEmpty(realPath) &&
|
||||
+ Patterns.WEB_URL.matcher(realPath).matches();
|
||||
+ if (realPath == null || isUrl)
|
||||
{
|
||||
- case REQUEST_LAUNCH_IMAGE_CAPTURE:
|
||||
- uri = cameraCaptureURI;
|
||||
- break;
|
||||
-
|
||||
- case REQUEST_LAUNCH_IMAGE_LIBRARY:
|
||||
- uri = data.getData();
|
||||
- String realPath = getRealPathFromURI(uri);
|
||||
- final boolean isUrl = !TextUtils.isEmpty(realPath) &&
|
||||
- Patterns.WEB_URL.matcher(realPath).matches();
|
||||
- if (realPath == null || isUrl)
|
||||
- {
|
||||
- try
|
||||
- {
|
||||
- File file = createFileFromURI(uri);
|
||||
- realPath = file.getAbsolutePath();
|
||||
- uri = Uri.fromFile(file);
|
||||
- }
|
||||
- catch (Exception e)
|
||||
- {
|
||||
- // image not in cache
|
||||
- responseHelper.putString("error", "Could not read photo");
|
||||
- responseHelper.putString("uri", uri.toString());
|
||||
- responseHelper.invokeResponse(callback);
|
||||
- callback = null;
|
||||
- return;
|
||||
- }
|
||||
- }
|
||||
- imageConfig = imageConfig.withOriginalFile(new File(realPath));
|
||||
- break;
|
||||
-
|
||||
- case REQUEST_LAUNCH_VIDEO_LIBRARY:
|
||||
- responseHelper.putString("uri", data.getData().toString());
|
||||
- responseHelper.putString("path", getRealPathFromURI(data.getData()));
|
||||
- responseHelper.invokeResponse(callback);
|
||||
- callback = null;
|
||||
- return;
|
||||
-
|
||||
- case REQUEST_LAUNCH_VIDEO_CAPTURE:
|
||||
- final String path = getRealPathFromURI(data.getData());
|
||||
- responseHelper.putString("uri", data.getData().toString());
|
||||
- responseHelper.putString("path", path);
|
||||
- fileScan(reactContext, path);
|
||||
- responseHelper.invokeResponse(callback);
|
||||
- callback = null;
|
||||
- return;
|
||||
+ if (data == null || data.getData() == null) {
|
||||
+ uri = cameraCaptureURI;
|
||||
+ break;
|
||||
+ } else {
|
||||
+ final String path = getRealPathFromURI(data.getData());
|
||||
+ responseHelper.putString("uri", data.getData().toString());
|
||||
+ responseHelper.putString("path", path);
|
||||
+ fileScan(reactContext, path);
|
||||
+ responseHelper.invokeResponse(callback);
|
||||
+ callback = null;
|
||||
+ return;
|
||||
+ }
|
||||
+ try
|
||||
+ {
|
||||
+ File file = createFileFromURI(uri);
|
||||
+ realPath = file.getAbsolutePath();
|
||||
+ uri = Uri.fromFile(file);
|
||||
+ }
|
||||
+ catch (Exception e)
|
||||
+ {
|
||||
+ // image not in cache
|
||||
+ responseHelper.putString("error", "Could not read photo");
|
||||
+ responseHelper.putString("uri", uri.toString());
|
||||
responseHelper.invokeResponse(callback);
|
||||
callback = null;
|
||||
+ this.options = null;
|
||||
return;
|
||||
+ }
|
||||
}
|
||||
|
||||
+ imageConfig = imageConfig.withOriginalFile(new File(realPath));
|
||||
+
|
||||
final ReadExifResult result = readExifInterface(responseHelper, imageConfig);
|
||||
@@ -481,6 +487,13 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
|
||||
if (result.error != null)
|
||||
@@ -461,6 +437,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
removeUselessFiles(requestCode, imageConfig);
|
||||
responseHelper.invokeError(callback, result.error.getMessage());
|
||||
callback = null;
|
||||
+ this.options = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -472,7 +449,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
updatedResultResponse(uri, imageConfig.original.getAbsolutePath());
|
||||
|
||||
// don't create a new file if contraint are respected
|
||||
- if (imageConfig.useOriginal(initialWidth, initialHeight, result.currentRotation))
|
||||
+ if (imageConfig.useOriginal(initialWidth, initialHeight, result.currentRotation) || mime.equals("image/gif"))
|
||||
{
|
||||
responseHelper.putInt("width", initialWidth);
|
||||
responseHelper.putInt("height", initialHeight);
|
||||
@@ -481,6 +458,14 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
else
|
||||
{
|
||||
imageConfig = getResizedImage(reactContext, this.options, imageConfig, initialWidth, initialHeight, requestCode);
|
||||
@@ -94,13 +226,78 @@ index 48fb5c1..5085663 100644
|
||||
+ {
|
||||
+ responseHelper.invokeError(callback, "Could not read image");
|
||||
+ callback = null;
|
||||
+ this.options = null;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (imageConfig.resized == null)
|
||||
{
|
||||
removeUselessFiles(requestCode, imageConfig);
|
||||
@@ -551,7 +564,8 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -523,6 +508,63 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
this.options = null;
|
||||
}
|
||||
|
||||
+ protected void extractVideoFromResult(Uri uri) {
|
||||
+ responseHelper.putString("uri", uri.toString());
|
||||
+ responseHelper.putString("path", getRealPathFromURI(uri));
|
||||
+ responseHelper.invokeResponse(callback);
|
||||
+ callback = null;
|
||||
+ this.options = null;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
+ //robustness code
|
||||
+ if (passResult(requestCode))
|
||||
+ {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ responseHelper.cleanResponse();
|
||||
+
|
||||
+ // user cancel
|
||||
+ if (resultCode != Activity.RESULT_OK)
|
||||
+ {
|
||||
+ removeUselessFiles(requestCode, imageConfig);
|
||||
+ responseHelper.invokeCancel(callback);
|
||||
+ callback = null;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ switch (requestCode)
|
||||
+ {
|
||||
+ case REQUEST_LAUNCH_IMAGE_CAPTURE:
|
||||
+ extractImageFromResult(activity, cameraCaptureURI, requestCode);
|
||||
+ break;
|
||||
+
|
||||
+ case REQUEST_LAUNCH_IMAGE_LIBRARY:
|
||||
+ extractImageFromResult(activity, data.getData(), requestCode);
|
||||
+ break;
|
||||
+
|
||||
+ case REQUEST_LAUNCH_VIDEO_LIBRARY:
|
||||
+ extractVideoFromResult(data.getData());
|
||||
+ break;
|
||||
+
|
||||
+ case REQUEST_LAUNCH_MIXED_CAPTURE:
|
||||
+ case REQUEST_LAUNCH_VIDEO_CAPTURE:
|
||||
+ if (data == null || data.getData() == null) {
|
||||
+ extractImageFromResult(activity, cameraCaptureURI, requestCode);
|
||||
+ } else {
|
||||
+ Uri selectedMediaUri = data.getData();
|
||||
+ if (selectedMediaUri.toString().contains("image")) {
|
||||
+ extractImageFromResult(activity, selectedMediaUri, requestCode);
|
||||
+ } else {
|
||||
+ extractVideoFromResult(selectedMediaUri);
|
||||
+ }
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
public void invokeCustomButton(@NonNull final String action)
|
||||
{
|
||||
responseHelper.invokeCustomButton(this.callback, action);
|
||||
@@ -551,7 +593,8 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
{
|
||||
return callback == null || (cameraCaptureURI == null && requestCode == REQUEST_LAUNCH_IMAGE_CAPTURE)
|
||||
|| (requestCode != REQUEST_LAUNCH_IMAGE_CAPTURE && requestCode != REQUEST_LAUNCH_IMAGE_LIBRARY
|
||||
@@ -110,7 +307,7 @@ index 48fb5c1..5085663 100644
|
||||
}
|
||||
|
||||
private void updatedResultResponse(@Nullable final Uri uri,
|
||||
@@ -571,22 +585,23 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -571,22 +614,23 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@NonNull final Callback callback,
|
||||
@NonNull final int requestCode)
|
||||
{
|
||||
@@ -143,7 +340,7 @@ index 48fb5c1..5085663 100644
|
||||
if (!permissionsGranted)
|
||||
{
|
||||
final Boolean dontAskAgain = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA);
|
||||
@@ -787,4 +802,22 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
@@ -787,4 +831,22 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
||||
videoDurationLimit = options.getInt("durationLimit");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,3 +170,29 @@ index 38b78f1..a47afea 100644
|
||||
} catch (Exception e) {
|
||||
authPromise.reject(E_FAILED_TO_SHOW_AUTH, e);
|
||||
authPromise = null;
|
||||
diff --git a/node_modules/react-native-local-auth/react-native-local-auth.podspec b/node_modules/react-native-local-auth/react-native-local-auth.podspec
|
||||
new file mode 100644
|
||||
index 0000000..5f9a6b4
|
||||
--- /dev/null
|
||||
+++ b/node_modules/react-native-local-auth/react-native-local-auth.podspec
|
||||
@@ -0,0 +1,20 @@
|
||||
+require "json"
|
||||
+package = JSON.parse(File.read(File.join(__dir__, '/package.json')))
|
||||
+
|
||||
+Pod::Spec.new do |s|
|
||||
+ s.name = package['name']
|
||||
+ s.version = package['version']
|
||||
+ s.summary = package['description']
|
||||
+ s.description = package['description']
|
||||
+ s.homepage = package['homepage']
|
||||
+ s.license = package['license']
|
||||
+ s.author = package['author']
|
||||
+ s.source = { :git => 'https://github.com/tradle/react-native-local-auth.git' }
|
||||
+
|
||||
+ s.platform = :ios, '9.0'
|
||||
+ s.ios.deployment_target = '9.0'
|
||||
+
|
||||
+ s.source_files = "*.{h,m}"
|
||||
+
|
||||
+ s.dependency 'React'
|
||||
+end
|
||||
|
||||
Reference in New Issue
Block a user