mattermost-mobile/share_extension/utils/index.tsx
Elias Nahum 043b3a0e8e
Refactor Android Share extension (js) (#4893)
* Refactor Android Share extension (js)

* Feedback review
2020-10-19 21:39:59 -03:00

230 lines
No EOL
7.5 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {ReactNode} from 'react';
import {intlShape} from 'react-intl';
import {Alert, NativeModules, PermissionsAndroid} from 'react-native';
import LocalAuth from 'react-native-local-auth';
import RNFetchBlob from 'rn-fetch-blob';
import Loading from '@components/loading';
import {MAX_FILE_COUNT} from '@constants/post_draft';
import {getAppCredentials} from '@init/credentials';
import type {FileInfo} from '@mm-redux/types/files';
import type {Team} from '@mm-redux/types/teams';
import {getFormattedFileSize, lookupMimeType} from '@mm-redux/utils/file_utils';
import {getExtensionFromMime} from '@utils/file';
import mattermostManaged from 'app/mattermost_managed';
import ShareError from '@share/components/error';
const ShareExtension = NativeModules.MattermostShare;
export async function isAuthorized(intl: typeof intlShape): Promise<boolean> {
try {
const config = await mattermostManaged.getConfig();
if (config) {
const authNeeded = config.inAppPinCode && config.inAppPinCode === 'true';
const vendor = config.vendor || 'Mattermost';
if (authNeeded) {
const isSecured = await mattermostManaged.isDeviceSecure();
if (isSecured) {
try {
await LocalAuth.auth({
reason: intl.formatMessage({
id: 'mobile.managed.secured_by',
defaultMessage: 'Secured by {vendor}',
}, {vendor}),
fallbackToPasscode: true,
suppressEnterPassword: false,
});
} catch (err) {
ShareExtension.close(null);
}
} else {
await showNotSecuredAlert(intl, vendor);
ShareExtension.close(null);
}
}
}
} catch {
// do nothing
}
return getCredentials();
}
export async function getSharedItems(items: Array<ShareItem>, intl: typeof intlShape): Promise<ProcessedSharedItems> {
const text = [];
const files: Array<ShareFileInfo> = [];
let totalSize = 0;
let error;
for (let i = 0; i < items.length; i++) {
const item = items[i];
switch (item.type) {
case 'text/plain':
text.push(item.value);
break;
default: {
let fileSize = {size: 0};
const fullPath = item.value;
try {
fileSize = await RNFetchBlob.fs.stat(fullPath); // eslint-disable-line no-await-in-loop
} catch (e) {
error = intl.formatMessage({
id: 'mobile.extension.file_error',
defaultMessage: 'There was an error reading the file to be shared.\nPlease try again.',
});
break;
}
let filename = fullPath.replace(/^.*[\\/]/, '');
let extension = filename.split('.').pop();
if (extension === filename) {
extension = getExtensionFromMime(item.type);
filename = `${filename}.${extension}`;
}
totalSize += fileSize.size;
files.push({
extension,
filename,
fullPath,
mimeType: item.type || lookupMimeType(filename.toLowerCase()),
size: getFormattedFileSize(fileSize as FileInfo),
type: item.type,
});
break;
}
}
}
const value = text.join('\n');
return {error, files, value, totalSize};
}
export async function permissionEnabled(): Promise<boolean> {
const hasPermission = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
let granted;
if (!hasPermission) {
granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
);
}
return (hasPermission || granted === PermissionsAndroid.RESULTS.GRANTED);
}
export function getErrorElement(
state: ShareState,
canUploadFiles: boolean,
maxFileSize: number,
team: Team | undefined | null,
intl: typeof intlShape,
): ReactNode | undefined {
if (state.loading) {
return <Loading/>;
}
if (!state.authorized) {
return <ShareError/>;
}
if (!team?.id) {
const teamRequired = intl.formatMessage({
id: 'mobile.extension.team_required',
defaultMessage: 'You must belong to a team before you can share files.',
});
return <ShareError message={teamRequired}/>;
}
if (!canUploadFiles) {
const uploadsDisabled = intl.formatMessage({
id: 'mobile.file_upload.disabled',
defaultMessage: 'File uploads from mobile are disabled. Please contact your System Admin for more details.',
});
return <ShareError message={uploadsDisabled}/>;
}
if (state.hasPermission === false) {
const storage = intl.formatMessage({
id: 'mobile.extension.permission',
defaultMessage: 'Mattermost needs access to the device storage to share files.',
});
return <ShareError message={storage}/>;
}
if (state.files.length > MAX_FILE_COUNT) {
const fileCount = intl.formatMessage({
id: 'mobile.extension.file_limit',
defaultMessage: 'Sharing is limited to a maximum of 5 files.',
});
return <ShareError message={fileCount}/>;
}
if ((state.totalSize || 0) > maxFileSize) {
const maxSize = intl.formatMessage({
id: 'mobile.extension.max_file_size',
defaultMessage: 'File attachments shared in Mattermost must be less than {size}.',
}, {size: getFormattedFileSize({size: maxFileSize} as FileInfo)});
return <ShareError message={maxSize}/>;
}
return undefined;
}
async function getCredentials(): Promise<boolean> {
try {
const credentials = await getAppCredentials();
return Boolean(credentials);
} catch (error) {
return false;
}
return false;
}
function showNotSecuredAlert(intl: typeof intlShape, vendor: string): Promise<void> {
const {formatMessage} = intl;
return new Promise((resolve) => {
Alert.alert(
formatMessage({
id: 'mobile.managed.blocked_by',
defaultMessage: 'Blocked by {vendor}',
}, {vendor}),
formatMessage({
id: 'mobile.managed.not_secured.android',
defaultMessage: 'This device must be secured with a screen lock to use Mattermost.',
}),
[
{
text: formatMessage({
id: 'mobile.managed.settings',
defaultMessage: 'Go to settings',
}),
onPress: () => {
mattermostManaged.goToSecuritySettings();
},
},
{
text: formatMessage({
id: 'mobile.managed.exit',
defaultMessage: 'Exit',
}),
onPress: () => resolve(),
style: 'cancel',
},
],
{onDismiss: resolve},
);
});
}