* start upgrade to RN 74 * migrate react-native-fs to expo-file-system * exclude expo-asset module * fix database manager remove directory * fix: android network helper * include expo on android * temporarily disable android dep lock * replace react-native-create-thumbnail with expo-video-thumnails * update patches file version * fix android build on 74 * create local library to replace MattermostManaged, Notifications and SplitView modules with new arch support * migrate app to use new mattermost-rnutils library * remove unused flipper class for android unsigned * fix mattermost-rnutils android foldedObserver lifecycle * use mattermost-rnutils on Android * use mattermost-rnutils on iOS * path react-native-navigation to not crash when activity is not NavigationActivity * create local library for android share extension with new arch support * Replace ShareModule with @mattermost/rnshare library * remove ShareModule from android native code * update react-intl * update nodejs to 20.x.x npm to 10.x.x and dev deps * update @gorhom/bottom-sheet * use MattermostShare conditionally based on the platform * update @react-native-camera-roll/camera-roll * remove unused react-native-calendars * fix metro config * fix terminate session race condition * remove unused analytics * replace react-native-device-info with expo-application and expo-device * update @react-native-clipboard/clipboard * update @react-native-community/datetimepicker * update @react-native-community/netinfo * update @sentry/react-native * update react-native-document-picker * update react-native-gesture-handler * update react-native-share * update react-native-svg and react-native-svg-transformer * update react-native-vector-icons * update babel * update react-native-shadow-2 * update semver * remove react-native-svg-transformer and convert svg files to svg components * fix @mattermost/rnshare new-arch build on android * remove react-native-create-thumbnail resolution in build.gradle * create @mattermost/hardware-keyboard library to replace hw-keyboard-events * fix hardware-keyboard library * fix rnutils library * create @mattermost/keyboard-tracker library * replace react-native-keyboard-tracking-view with @mattermost/keyboard-tracker * fix: rnutils to not crash on lateinit context * fix: rnutils delete database * revert changes to session_manager * Removed react-native-webview and added expo-web-browser instead With expo-web-browser we no longer need the webview for SSO login the SSO login is now done by using "custom Chrome tabs" on Android and ASWebAuthenticationSession on iOS * remove patch for react-native-keyboard-tracking-view * replace react-native-linear-gradient with expo-linear-gradient * replace react-native-in-app-review with expo-store-review * fix: shared group database directory on ios * replace react-native-fast-image with expo-image * remove unusued type def for react-native-keyboard-tracking-view * replace react-native-elements and react-native-button with @rneui * node version * update sizzling methods * fix tests using jest-expo * replace jail-monkey with expo-device * update babel deps * update typescript eslint * update rn and expo * react-native-document-picker @react-native-camera-roll/camera-roll @react-native-community/datetimepicker react-native-reanimated react-native-safe-area-context * update patches * update @sentry/react-native * upgrade react-native-navigation * update expo & expo-image * upgrade to working version of @sentry/react-native * update node, cocoapods, bundler, fastlane versions * @testing-library/react-native and eslint-plugin-jest * fix: FloatingTextInput causing a crash with reanimated * update sentry, localize, @types/lodash and uuid * fix floating text input label * update react-native-video * fix: cannot calculate shadow efficiently on some components * fix: reduce motion warning for bottomSheet * fix: shadow on YouTube component * update react-native-webrtc expo and @typescript-eslint * audit fix * fix swizzling bad merge * temp use of github dependency for @mattermost libraries * feedback review * feedback review * npm audit fix * update bundle deps * update @mattermost/react-native-turbo-log * update deps
178 lines
5.3 KiB
TypeScript
178 lines
5.3 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import RNUtils, {type SplitViewResult} from '@mattermost/rnutils';
|
|
import moment, {type Moment} from 'moment-timezone';
|
|
import {Platform} from 'react-native';
|
|
|
|
import {CUSTOM_STATUS_TIME_PICKER_INTERVALS_IN_MINUTES} from '@constants/custom_status';
|
|
import {STATUS_BAR_HEIGHT} from '@constants/view';
|
|
|
|
const {isRunningInSplitView} = RNUtils;
|
|
const MattermostShare = Platform.select({
|
|
default: null,
|
|
android: require('@mattermost/rnshare').default,
|
|
});
|
|
|
|
// isMinimumServerVersion will return true if currentVersion is equal to higher or than
|
|
// the provided minimum version. A non-equal major version will ignore minor and dot
|
|
// versions, and a non-equal minor version will ignore dot version.
|
|
// currentVersion is a string, e.g '4.6.0'
|
|
// minMajorVersion, minMinorVersion, minDotVersion are integers
|
|
export const isMinimumServerVersion = (currentVersion = '', minMajorVersion = 0, minMinorVersion = 0, minDotVersion = 0): boolean => {
|
|
if (!currentVersion || typeof currentVersion !== 'string') {
|
|
return false;
|
|
}
|
|
|
|
const split = currentVersion.split('.');
|
|
|
|
const major = parseInt(split[0], 10);
|
|
const minor = parseInt(split[1] || '0', 10);
|
|
const dot = parseInt(split[2] || '0', 10);
|
|
|
|
if (major > minMajorVersion) {
|
|
return true;
|
|
}
|
|
if (major < minMajorVersion) {
|
|
return false;
|
|
}
|
|
|
|
// Major version is equal, check minor
|
|
if (minor > minMinorVersion) {
|
|
return true;
|
|
}
|
|
if (minor < minMinorVersion) {
|
|
return false;
|
|
}
|
|
|
|
// Minor version is equal, check dot
|
|
if (dot > minDotVersion) {
|
|
return true;
|
|
}
|
|
if (dot < minDotVersion) {
|
|
return false;
|
|
}
|
|
|
|
// Dot version is equal
|
|
return true;
|
|
};
|
|
|
|
export function buildQueryString(parameters: Dictionary<any>): string {
|
|
const keys = Object.keys(parameters);
|
|
if (keys.length === 0) {
|
|
return '';
|
|
}
|
|
|
|
let query = '?';
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const key = keys[i];
|
|
if (parameters[key] == null) {
|
|
continue;
|
|
}
|
|
query += key + '=' + encodeURIComponent(parameters[key]);
|
|
|
|
if (i < keys.length - 1) {
|
|
query += '&';
|
|
}
|
|
}
|
|
|
|
return query;
|
|
}
|
|
|
|
export function isEmail(email: string): boolean {
|
|
// writing a regex to match all valid email addresses is really, really hard. (see http://stackoverflow.com/a/201378)
|
|
// this regex ensures:
|
|
// - at least one character that is not a space, comma, or @ symbol
|
|
// - followed by a single @ symbol
|
|
// - followed by at least one character that is not a space, comma, or @ symbol
|
|
// this prevents <Outlook Style> outlook.style@domain.com addresses and multiple comma-separated addresses from being accepted
|
|
return (/^[^ ,@]+@[^ ,@]+$/).test(email);
|
|
}
|
|
|
|
export function identity<T>(arg: T): T {
|
|
return arg;
|
|
}
|
|
|
|
export function safeParseJSON(rawJson: string | Record<string, unknown> | unknown[]) {
|
|
let data = rawJson;
|
|
try {
|
|
if (typeof rawJson == 'string') {
|
|
data = JSON.parse(rawJson);
|
|
}
|
|
} catch {
|
|
// Do nothing
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
export function getCurrentMomentForTimezone(timezone: string | null) {
|
|
return timezone ? moment.tz(timezone) : moment();
|
|
}
|
|
|
|
export function getUtcOffsetForTimeZone(timezone: string) {
|
|
return moment.tz(timezone).utcOffset();
|
|
}
|
|
|
|
export function toTitleCase(str: string) {
|
|
function doTitleCase(txt: string) {
|
|
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
|
}
|
|
return str.replace(/\w\S*/g, doTitleCase);
|
|
}
|
|
|
|
export function getRoundedTime(value: Moment) {
|
|
const roundedTo = CUSTOM_STATUS_TIME_PICKER_INTERVALS_IN_MINUTES;
|
|
const start = moment(value);
|
|
const diff = start.minute() % roundedTo;
|
|
if (diff === 0) {
|
|
return value;
|
|
}
|
|
const remainder = roundedTo - diff;
|
|
return start.add(remainder, 'm').seconds(0).milliseconds(0);
|
|
}
|
|
|
|
export function isTablet() {
|
|
const result: SplitViewResult = isRunningInSplitView();
|
|
return result.isTablet && !result.isSplit;
|
|
}
|
|
|
|
export const pluckUnique = (key: string) => (array: Array<{[key: string]: unknown}>) => Array.from(new Set(array.map((obj) => obj[key])));
|
|
|
|
export function bottomSheetSnapPoint(itemsCount: number, itemHeight: number, bottomInset: number) {
|
|
const bottom = Platform.select({ios: bottomInset, default: 0}) + STATUS_BAR_HEIGHT;
|
|
return (itemsCount * itemHeight) + bottom;
|
|
}
|
|
|
|
export function hasTrailingSpaces(term: string) {
|
|
return term.length !== term.trimEnd().length;
|
|
}
|
|
|
|
/**
|
|
* isMainActivity returns true if the current activity on Android is the MainActivity otherwise it returns false,
|
|
* on iOS the result is always true
|
|
*
|
|
* @returns boolean
|
|
*/
|
|
export function isMainActivity() {
|
|
return Platform.select({
|
|
default: true,
|
|
android: MattermostShare?.getCurrentActivityName() === 'MainActivity',
|
|
});
|
|
}
|
|
|
|
export function areBothStringArraysEqual(a: string[], b: string[]) {
|
|
if (a.length !== b.length) {
|
|
return false;
|
|
}
|
|
|
|
if (a.length === 0 && b.length === 0) {
|
|
return false;
|
|
}
|
|
|
|
const aSorted = a.sort();
|
|
const bSorted = b.sort();
|
|
const areBothEqual = aSorted.every((value, index) => value === bSorted[index]);
|
|
|
|
return areBothEqual;
|
|
}
|