diff --git a/NOTICE.txt b/NOTICE.txt
index 645a46a76..336ae5e2a 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -2636,42 +2636,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
----
-
-## react-native-user-agent
-
-This product contains 'react-native-user-agent' by Bebnev Anton.
-
-Library that helps you to get mobile application user agent and web view user agent strings.
-
-* HOMEPAGE:
- * https://github.com/bebnev/react-native-user-agent
-
-* LICENSE: MIT
-
-MIT License
-
-Copyright (c) 2018 Anton Bebnev
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
---
## react-native-vector-icons
diff --git a/android/app/build.gradle b/android/app/build.gradle
index ced9265ed..9a8df50de 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -145,7 +145,7 @@ android {
applicationId "com.mattermost.rnbeta"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 442
+ versionCode 443
versionName "2.0.0"
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
diff --git a/android/app/src/androidTest/java/com/mattermost/rnbeta/DetoxTest.java b/android/app/src/androidTest/java/com/mattermost/rnbeta/DetoxTest.java
index 220be5e4a..dfc645da8 100644
--- a/android/app/src/androidTest/java/com/mattermost/rnbeta/DetoxTest.java
+++ b/android/app/src/androidTest/java/com/mattermost/rnbeta/DetoxTest.java
@@ -1,6 +1,7 @@
package com.mattermost.rnbeta;
import com.wix.detox.Detox;
+import com.wix.detox.config.DetoxConfig;
import org.junit.Rule;
import org.junit.Test;
@@ -19,10 +20,11 @@ public class DetoxTest {
@Test
public void runDetoxTests() {
- Detox.DetoxIdlePolicyConfig idlePolicyConfig = new Detox.DetoxIdlePolicyConfig();
- idlePolicyConfig.masterTimeoutSec = 60;
- idlePolicyConfig.idleResourceTimeoutSec = 30;
+ DetoxConfig detoxConfig = new DetoxConfig();
+ detoxConfig.idlePolicyConfig.masterTimeoutSec = 90;
+ detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60;
+ detoxConfig.rnContextLoadTimeoutSec = (BuildConfig.DEBUG ? 180 : 60);
- Detox.runTests(mActivityRule, idlePolicyConfig);
+ Detox.runTests(mActivityRule, detoxConfig);
}
}
diff --git a/app/components/emoji/emoji.tsx b/app/components/emoji/emoji.tsx
new file mode 100644
index 000000000..8334aea4d
--- /dev/null
+++ b/app/components/emoji/emoji.tsx
@@ -0,0 +1,152 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
+import withObservables from '@nozbe/with-observables';
+import React from 'react';
+import {
+ Platform,
+ StyleSheet,
+ Text,
+} from 'react-native';
+import FastImage from 'react-native-fast-image';
+import {of as of$} from 'rxjs';
+import {switchMap} from 'rxjs/operators';
+
+import {fetchCustomEmojiInBatch} from '@actions/remote/custom_emoji';
+import {useServerUrl} from '@context/server';
+import NetworkManager from '@managers/network_manager';
+import {queryCustomEmojisByName} from '@queries/servers/custom_emoji';
+import {observeConfigBooleanValue} from '@queries/servers/system';
+import {EmojiIndicesByAlias, Emojis} from '@utils/emoji';
+import {isUnicodeEmoji} from '@utils/emoji/helpers';
+
+import type {EmojiProps} from '@typings/components/emoji';
+import type {WithDatabaseArgs} from '@typings/database/database';
+
+const assetImages = new Map([['mattermost.png', require('@assets/images/emojis/mattermost.png')]]);
+
+const Emoji = (props: EmojiProps) => {
+ const {
+ customEmojis,
+ customEmojiStyle,
+ displayTextOnly,
+ emojiName,
+ literal = '',
+ testID,
+ textStyle,
+ } = props;
+ const serverUrl = useServerUrl();
+ let assetImage = '';
+ let unicode;
+ let imageUrl = '';
+ const name = emojiName.trim();
+ if (EmojiIndicesByAlias.has(name)) {
+ const emoji = Emojis[EmojiIndicesByAlias.get(name)!];
+ if (emoji.category === 'custom') {
+ assetImage = emoji.fileName;
+ } else {
+ unicode = emoji.image;
+ }
+ } else {
+ const custom = customEmojis.find((ce) => ce.name === name);
+ if (custom) {
+ try {
+ const client = NetworkManager.getClient(serverUrl);
+ imageUrl = client.getCustomEmojiImageUrl(custom.id);
+ } catch {
+ // do nothing
+ }
+ } else if (name && !isUnicodeEmoji(name)) {
+ fetchCustomEmojiInBatch(serverUrl, name);
+ }
+ }
+
+ let size = props.size;
+ let fontSize = size;
+ if (!size && textStyle) {
+ const flatten = StyleSheet.flatten(textStyle);
+ fontSize = flatten.fontSize;
+ size = fontSize;
+ }
+
+ if (displayTextOnly || (!imageUrl && !assetImage && !unicode)) {
+ return (
+
+ {literal}
+ );
+ }
+
+ const width = size;
+ const height = size;
+
+ if (unicode && !imageUrl) {
+ const codeArray = unicode.split('-');
+ const code = codeArray.reduce((acc: string, c: string) => {
+ return acc + String.fromCodePoint(parseInt(c, 16));
+ }, '');
+
+ return (
+
+ {code}
+
+ );
+ }
+
+ if (assetImage) {
+ const key = Platform.OS === 'android' ? (`${assetImage}-${height}-${width}`) : null;
+
+ const image = assetImages.get(assetImage);
+ if (!image) {
+ return null;
+ }
+ return (
+
+ );
+ }
+
+ if (!imageUrl) {
+ return null;
+ }
+
+ // Android can't change the size of an image after its first render, so
+ // force a new image to be rendered when the size changes
+ const key = Platform.OS === 'android' ? (`${imageUrl}-${height}-${width}`) : null;
+
+ return (
+
+ );
+};
+
+const withCustomEmojis = withObservables(['emojiName'], ({database, emojiName}: WithDatabaseArgs & {emojiName: string}) => {
+ const hasEmojiBuiltIn = EmojiIndicesByAlias.has(emojiName);
+
+ const displayTextOnly = hasEmojiBuiltIn ? of$(false) : observeConfigBooleanValue(database, 'EnableCustomEmoji').pipe(
+ switchMap((value) => of$(!value)),
+ );
+
+ return {
+ displayTextOnly,
+ customEmojis: hasEmojiBuiltIn ? of$([]) : queryCustomEmojisByName(database, [emojiName]).observe(),
+ };
+});
+
+export default withDatabase(withCustomEmojis(Emoji));
diff --git a/app/components/emoji/index.tsx b/app/components/emoji/index.tsx
index 090fe3ad3..918f4bd23 100644
--- a/app/components/emoji/index.tsx
+++ b/app/components/emoji/index.tsx
@@ -1,165 +1,21 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
-import withObservables from '@nozbe/with-observables';
-import React from 'react';
-import {
- Platform,
- StyleProp,
- StyleSheet,
- Text,
- TextStyle,
-} from 'react-native';
-import FastImage, {ImageStyle} from 'react-native-fast-image';
-import {of as of$} from 'rxjs';
-import {switchMap} from 'rxjs/operators';
+import React, {useMemo} from 'react';
-import {fetchCustomEmojiInBatch} from '@actions/remote/custom_emoji';
-import {useServerUrl} from '@context/server';
-import NetworkManager from '@managers/network_manager';
-import {queryCustomEmojisByName} from '@queries/servers/custom_emoji';
-import {observeConfigBooleanValue} from '@queries/servers/system';
-import {EmojiIndicesByAlias, Emojis} from '@utils/emoji';
-import {isUnicodeEmoji} from '@utils/emoji/helpers';
+import {EmojiComponent, EmojiProps} from '@typings/components/emoji';
-import type {WithDatabaseArgs} from '@typings/database/database';
-import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji';
+let emojiComponent: EmojiComponent;
-const assetImages = new Map([['mattermost.png', require('@assets/images/emojis/mattermost.png')]]);
-
-type Props = {
- emojiName: string;
- displayTextOnly?: boolean;
- literal?: string;
- size?: number;
- textStyle?: StyleProp;
- customEmojiStyle?: StyleProp;
- customEmojis: CustomEmojiModel[];
- testID?: string;
-}
-
-const Emoji = (props: Props) => {
- const {
- customEmojis,
- customEmojiStyle,
- displayTextOnly,
- emojiName,
- literal = '',
- testID,
- textStyle,
- } = props;
- const serverUrl = useServerUrl();
- let assetImage = '';
- let unicode;
- let imageUrl = '';
- const name = emojiName.trim();
- if (EmojiIndicesByAlias.has(name)) {
- const emoji = Emojis[EmojiIndicesByAlias.get(name)!];
- if (emoji.category === 'custom') {
- assetImage = emoji.fileName;
- } else {
- unicode = emoji.image;
+const EmojiWrapper = (props: Omit) => {
+ const Emoji = useMemo(() => {
+ if (!emojiComponent) {
+ emojiComponent = require('./emoji').default;
}
- } else {
- const custom = customEmojis.find((ce) => ce.name === name);
- if (custom) {
- try {
- const client = NetworkManager.getClient(serverUrl);
- imageUrl = client.getCustomEmojiImageUrl(custom.id);
- } catch {
- // do nothing
- }
- } else if (name && !isUnicodeEmoji(name)) {
- fetchCustomEmojiInBatch(serverUrl, name);
- }
- }
+ return emojiComponent;
+ }, []);
- let size = props.size;
- let fontSize = size;
- if (!size && textStyle) {
- const flatten = StyleSheet.flatten(textStyle);
- fontSize = flatten.fontSize;
- size = fontSize;
- }
-
- if (displayTextOnly || (!imageUrl && !assetImage && !unicode)) {
- return (
-
- {literal}
- );
- }
-
- const width = size;
- const height = size;
-
- if (unicode && !imageUrl) {
- const codeArray = unicode.split('-');
- const code = codeArray.reduce((acc: string, c: string) => {
- return acc + String.fromCodePoint(parseInt(c, 16));
- }, '');
-
- return (
-
- {code}
-
- );
- }
-
- if (assetImage) {
- const key = Platform.OS === 'android' ? (`${assetImage}-${height}-${width}`) : null;
-
- const image = assetImages.get(assetImage);
- if (!image) {
- return null;
- }
- return (
-
- );
- }
-
- if (!imageUrl) {
- return null;
- }
-
- // Android can't change the size of an image after its first render, so
- // force a new image to be rendered when the size changes
- const key = Platform.OS === 'android' ? (`${imageUrl}-${height}-${width}`) : null;
-
- return (
-
- );
+ return ();
};
-const withCustomEmojis = withObservables(['emojiName'], ({database, emojiName}: WithDatabaseArgs & {emojiName: string}) => {
- const hasEmojiBuiltIn = EmojiIndicesByAlias.has(emojiName);
-
- const displayTextOnly = hasEmojiBuiltIn ? of$(false) : observeConfigBooleanValue(database, 'EnableCustomEmoji').pipe(
- switchMap((value) => of$(!value)),
- );
-
- return {
- displayTextOnly,
- customEmojis: hasEmojiBuiltIn ? of$([]) : queryCustomEmojisByName(database, [emojiName]).observe(),
- };
-});
-
-export default withDatabase(withCustomEmojis(Emoji));
+export default EmojiWrapper;
diff --git a/app/components/markdown/markdown.tsx b/app/components/markdown/markdown.tsx
index 4bdf7a6d5..a7efb3a11 100644
--- a/app/components/markdown/markdown.tsx
+++ b/app/components/markdown/markdown.tsx
@@ -9,13 +9,13 @@ import {Dimensions, GestureResponderEvent, Platform, StyleProp, Text, TextStyle,
import CompassIcon from '@components/compass_icon';
import Emoji from '@components/emoji';
import FormattedText from '@components/formatted_text';
-import Hashtag from '@components/markdown/hashtag';
import {computeTextStyle} from '@utils/markdown';
import {blendColors, changeOpacity, concatStyles, makeStyleSheetFromTheme} from '@utils/theme';
import {getScheme} from '@utils/url';
import AtMention from './at_mention';
import ChannelMention, {ChannelMentions} from './channel_mention';
+import Hashtag from './hashtag';
import MarkdownBlockQuote from './markdown_block_quote';
import MarkdownCodeBlock from './markdown_code_block';
import MarkdownImage from './markdown_image';
diff --git a/app/components/markdown/markdown_code_block/index.tsx b/app/components/markdown/markdown_code_block/index.tsx
index 8e0dad2ed..20549d427 100644
--- a/app/components/markdown/markdown_code_block/index.tsx
+++ b/app/components/markdown/markdown_code_block/index.tsx
@@ -3,14 +3,13 @@
import {useManagedConfig} from '@mattermost/react-native-emm';
import Clipboard from '@react-native-clipboard/clipboard';
-import React, {useCallback} from 'react';
+import React, {useCallback, useMemo} from 'react';
import {useIntl} from 'react-intl';
import {Keyboard, StyleSheet, Text, TextStyle, TouchableOpacity, View} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import FormattedText from '@components/formatted_text';
import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item';
-import SyntaxHighlighter from '@components/syntax_highlight';
import {Screens} from '@constants';
import {useTheme} from '@context/theme';
import {bottomSheet, dismissBottomSheet, goToScreen} from '@screens/navigation';
@@ -19,6 +18,8 @@ import {getHighlightLanguageFromNameOrAlias, getHighlightLanguageName} from '@ut
import {preventDoubleTap} from '@utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
+import type {SyntaxHiglightProps} from '@typings/components/syntax_highlight';
+
type MarkdownCodeBlockProps = {
language: string;
content: string;
@@ -27,6 +28,8 @@ type MarkdownCodeBlockProps = {
const MAX_LINES = 4;
+let syntaxHighlighter: (props: SyntaxHiglightProps) => JSX.Element;
+
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
bottomSheet: {
@@ -70,6 +73,13 @@ const MarkdownCodeBlock = ({language = '', content, textStyle}: MarkdownCodeBloc
const theme = useTheme();
const insets = useSafeAreaInsets();
const style = getStyleSheet(theme);
+ const SyntaxHighlighter = useMemo(() => {
+ if (!syntaxHighlighter) {
+ syntaxHighlighter = require('@components/syntax_highlight').default;
+ }
+
+ return syntaxHighlighter;
+ }, []);
const handlePress = useCallback(preventDoubleTap(() => {
const screen = Screens.CODE;
diff --git a/app/components/profile_picture/status.tsx b/app/components/profile_picture/status.tsx
index ef82320b6..52546266d 100644
--- a/app/components/profile_picture/status.tsx
+++ b/app/components/profile_picture/status.tsx
@@ -38,7 +38,7 @@ const Status = ({author, statusSize, statusStyle, theme}: Props) => {
styles.statusWrapper,
statusStyle,
{borderRadius: statusSize / 2},
- ]), [statusStyle]);
+ ]), [statusStyle, styles]);
const isBot = author && (('isBot' in author) ? author.isBot : author.is_bot);
if (author?.status && !isBot) {
return (
diff --git a/app/components/syntax_highlight/index.tsx b/app/components/syntax_highlight/index.tsx
index 963be4759..a83b2e3e0 100644
--- a/app/components/syntax_highlight/index.tsx
+++ b/app/components/syntax_highlight/index.tsx
@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React, {useCallback, useMemo} from 'react';
-import {StyleSheet, TextStyle, View} from 'react-native';
+import {StyleSheet, View} from 'react-native';
import SyntaxHighlighter from 'react-syntax-highlighter';
import {github, monokai, solarizedDark, solarizedLight} from 'react-syntax-highlighter/dist/cjs/styles/hljs';
@@ -10,12 +10,7 @@ import {useTheme} from '@context/theme';
import CodeHighlightRenderer from './renderer';
-type Props = {
- code: string;
- language: string;
- textStyle: TextStyle;
- selectable?: boolean;
-}
+import type {SyntaxHiglightProps} from '@typings/components/syntax_highlight';
const codeTheme: Record = {
github,
@@ -34,7 +29,7 @@ const styles = StyleSheet.create({
},
});
-const Highlighter = ({code, language, textStyle, selectable = false}: Props) => {
+const Highlighter = ({code, language, textStyle, selectable = false}: SyntaxHiglightProps) => {
const theme = useTheme();
const style = codeTheme[theme.codeTheme] || github;
const preTagStyle = useMemo(() => [
diff --git a/app/hooks/keyboard_tracking.ts b/app/hooks/keyboard_tracking.ts
index 806b21ff0..ad1b8670c 100644
--- a/app/hooks/keyboard_tracking.ts
+++ b/app/hooks/keyboard_tracking.ts
@@ -11,6 +11,18 @@ export const useKeyboardTrackingPaused = (keyboardTrackingRef: RefObject {
+ keyboardTrackingRef.current?.resumeTracking(trackerId);
+ }, []);
+
+ useEffect(() => {
+ const onCommandComplete = () => {
+ const id = NavigationStore.getVisibleScreen();
+ if (screens.includes(id) && isPostDraftPaused.current) {
+ isPostDraftPaused.current = false;
+ keyboardTrackingRef.current?.resumeTracking(trackerId);
+ }
+ };
+
const commandListener = Navigation.events().registerCommandListener(() => {
if (!isPostDraftPaused.current) {
isPostDraftPaused.current = true;
@@ -19,16 +31,17 @@ export const useKeyboardTrackingPaused = (keyboardTrackingRef: RefObject {
- const id = NavigationStore.getVisibleScreen();
- if (screens.includes(id) && isPostDraftPaused.current) {
- isPostDraftPaused.current = false;
- keyboardTrackingRef.current?.resumeTracking(trackerId);
- }
+ onCommandComplete();
+ });
+
+ const popListener = Navigation.events().registerScreenPoppedListener(() => {
+ onCommandComplete();
});
return () => {
commandListener.remove();
commandCompletedListener.remove();
+ popListener.remove();
};
}, [trackerId]);
};
diff --git a/app/managers/network_manager.ts b/app/managers/network_manager.ts
index 7bd5cd04f..d94501eb9 100644
--- a/app/managers/network_manager.ts
+++ b/app/managers/network_manager.ts
@@ -9,7 +9,7 @@ import {
RetryTypes,
} from '@mattermost/react-native-network-client';
import {DeviceEventEmitter} from 'react-native';
-import UserAgent from 'react-native-user-agent';
+import DeviceInfo from 'react-native-device-info';
import LocalConfig from '@assets/config.json';
import {Client} from '@client/rest';
@@ -95,11 +95,11 @@ class NetworkManager {
};
private buildConfig = async () => {
- const userAgent = UserAgent.getUserAgent();
+ const userAgent = `Mattermost Mobile/${DeviceInfo.getVersion()}+${DeviceInfo.getBuildNumber()} (${DeviceInfo.getSystemName()}; ${DeviceInfo.getSystemVersion()}; ${DeviceInfo.getModel()})`;
const managedConfig = ManagedApp.enabled ? Emm.getManagedConfig() : undefined;
const headers: Record = {
- ...this.DEFAULT_CONFIG.headers,
[ClientConstants.HEADER_USER_AGENT]: userAgent,
+ ...this.DEFAULT_CONFIG.headers,
};
const config = {
diff --git a/app/screens/home/search/initial/modifiers/modifier.tsx b/app/screens/home/search/initial/modifiers/modifier.tsx
index e2f303bcf..b58df28bd 100644
--- a/app/screens/home/search/initial/modifiers/modifier.tsx
+++ b/app/screens/home/search/initial/modifiers/modifier.tsx
@@ -46,9 +46,9 @@ const Modifier = ({item, searchRef, searchValue, setSearchValue}: Props) => {
setSearchValue(newValue);
if (item.cursorPosition) {
const position = newValue.length + item.cursorPosition;
- requestAnimationFrame(() => {
+ setTimeout(() => {
searchRef.current?.setNativeProps({selection: {start: position, end: position}});
- });
+ }, 50);
}
});
diff --git a/detox/.detoxrc.json b/detox/.detoxrc.json
index 1987dd715..fb7bc86c1 100644
--- a/detox/.detoxrc.json
+++ b/detox/.detoxrc.json
@@ -13,7 +13,7 @@
"ios.release": {
"type": "ios.app",
"binaryPath": "../ios/Build/Products/Release-iphonesimulator/Mattermost.app",
- "build": "cd ../fastlane && NODE_ENV=production bundle exec fastlane ios simulator && cd ../detox"
+ "build": "cd .. && npm run build:ios-sim && cd detox"
},
"android.debug": {
"type": "android.apk",
@@ -30,7 +30,7 @@
"ios.simulator": {
"type": "ios.simulator",
"device": {
- "type": "iPhone 13"
+ "type": "iPhone 14"
}
},
"android.emulator": {
diff --git a/detox/package.json b/detox/package.json
index d16f9b24c..1e81067ea 100644
--- a/detox/package.json
+++ b/detox/package.json
@@ -52,7 +52,7 @@
"e2e:android-test-release": "detox test -c android.emu.release --record-logs failing --take-screenshots failing",
"e2e:ios-test": "IOS=true detox test -c ios.sim.debug",
"e2e:ios-build-release": "detox build -c ios.sim.release",
- "e2e:ios-test-release": "IOS=true detox test -c ios.sim.release --headless --record-logs failing --take-screenshots failing",
+ "e2e:ios-test-release": "IOS=true detox test -c ios.sim.release --record-logs failing --take-screenshots failing",
"check": "npm run lint && npm run tsc",
"lint": "eslint --ignore-path .gitignore --ignore-pattern node_modules --quiet .",
"start:webhook": "node webhook_server.js",
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 4b479955c..3fad4a6b8 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -355,39 +355,27 @@ platform :ios do
update_identifiers
replace_assets
- sh 'rm -rf ../build-ios/'
- sh 'cd ../ios/ && xcodebuild -workspace Mattermost.xcworkspace/ -scheme Mattermost -sdk iphoneos -configuration Release -parallelizeTargets -resultBundlePath ../build-ios/result -derivedDataPath ../build-ios/ CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO'
- sh 'cd ../build-ios/ && mkdir -p Payload && cp -R ../build-ios/Build/Products/Release-iphoneos/Mattermost.app Payload/ && zip -r Mattermost-unsigned.ipa Payload/'
- sh 'mv ../build-ios/Mattermost-unsigned.ipa ../'
- sh 'rm -rf ../build-ios/'
+ build_ios_unsigned_or_simulator(
+ more_xc_args: "-sdk iphoneos CODE_SIGNING_ALLOWED=NO",
+ rel_build_dir: "Release-iphoneos",
+ output_file: "Mattermost-unsigned.ipa",
+ )
end
lane :simulator do
UI.success('Building iOS app for simulator')
- ENV['APP_NAME'] = 'Mattermost'
- ENV['REPLACE_ASSETS'] = 'true'
- ENV['BUILD_FOR_RELEASE'] = 'true'
- ENV['APP_SCHEME'] = 'mattermost'
+ output_file = "Mattermost-simulator-x86_64.app.zip"
- update_identifiers
- replace_assets
-
- data_path = 'ios'
- if ENV['CIRCLECI'] == 'true'
- data_path = 'build-ios'
- end
-
- sh 'rm -rf ../ios/build/'
- sh 'rm -rf ../build-ios/'
- sh "cd ../ios && xcodebuild -workspace Mattermost.xcworkspace -scheme Mattermost -arch x86_64 -sdk iphonesimulator -configuration Release -parallelizeTargets -derivedDataPath ../#{data_path} ENABLE_BITCODE=NO CODE_SIGN_IDENTITY='' CODE_SIGNING_REQUIRED=NO ENABLE_BITCODE=NO"
- sh "cd ../#{data_path}/Build/Products/Release-iphonesimulator && zip -r Mattermost-simulator-x86_64.app.zip Mattermost.app"
- sh "mv ../#{data_path}/Build/Products/Release-iphonesimulator/Mattermost-simulator-x86_64.app.zip ../"
- sh 'rm -rf ../build-ios/'
+ build_ios_unsigned_or_simulator(
+ more_xc_args: "-sdk iphonesimulator -arch x86_64",
+ rel_build_dir: "Release-iphonesimulator",
+ output_file: output_file,
+ )
upload_file_to_s3({
:os_type => "ios",
- :file => "Mattermost-simulator-x86_64.app.zip"
+ :file => output_file
})
end
@@ -599,6 +587,24 @@ platform :ios do
)
end
+ def build_ios_unsigned_or_simulator(more_xc_args:, rel_build_dir:, output_file:)
+ root_dir = Dir[File.expand_path('..')].first
+ ios_build_dir = "#{root_dir}/ios/Build/Products"
+
+ ENV['APP_NAME'] = 'Mattermost'
+ ENV['REPLACE_ASSETS'] = 'true'
+ ENV['BUILD_FOR_RELEASE'] = 'true'
+ ENV['APP_SCHEME'] = 'mattermost'
+
+ update_identifiers
+ replace_assets
+
+ sh "rm -rf #{ios_build_dir}/"
+ sh "cd #{root_dir}/ios && xcodebuild -workspace Mattermost.xcworkspace -scheme Mattermost -configuration Release -parallelizeTargets CODE_SIGN_IDENTITY='' CODE_SIGNING_REQUIRED=NO SYMROOT='#{ios_build_dir}' #{more_xc_args} "
+ sh "cd #{ios_build_dir}/#{rel_build_dir} && zip -r #{output_file} Mattermost.app"
+ sh "mv #{ios_build_dir}/#{rel_build_dir}/#{output_file} #{root_dir}"
+ end
+
end
platform :android do
diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj
index 7d1df6be7..7dc713ae8 100644
--- a/ios/Mattermost.xcodeproj/project.pbxproj
+++ b/ios/Mattermost.xcodeproj/project.pbxproj
@@ -1095,7 +1095,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- CURRENT_PROJECT_VERSION = 442;
+ CURRENT_PROJECT_VERSION = 443;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
HEADER_SEARCH_PATHS = (
@@ -1139,7 +1139,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- CURRENT_PROJECT_VERSION = 442;
+ CURRENT_PROJECT_VERSION = 443;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
HEADER_SEARCH_PATHS = (
@@ -1282,7 +1282,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 442;
+ CURRENT_PROJECT_VERSION = 443;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
GCC_C_LANGUAGE_STANDARD = gnu11;
@@ -1333,7 +1333,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
- CURRENT_PROJECT_VERSION = 442;
+ CURRENT_PROJECT_VERSION = 443;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
GCC_C_LANGUAGE_STANDARD = gnu11;
diff --git a/ios/Mattermost/Info.plist b/ios/Mattermost/Info.plist
index 5393a3c26..5a74b2bbc 100644
--- a/ios/Mattermost/Info.plist
+++ b/ios/Mattermost/Info.plist
@@ -37,7 +37,7 @@
CFBundleVersion
- 442
+ 443
ITSAppUsesNonExemptEncryption
LSRequiresIPhoneOS
diff --git a/ios/MattermostShare/Info.plist b/ios/MattermostShare/Info.plist
index 904466732..145d5228a 100644
--- a/ios/MattermostShare/Info.plist
+++ b/ios/MattermostShare/Info.plist
@@ -21,7 +21,7 @@
CFBundleShortVersionString
2.0.0
CFBundleVersion
- 442
+ 443
UIAppFonts
OpenSans-Bold.ttf
diff --git a/ios/NotificationService/Info.plist b/ios/NotificationService/Info.plist
index b312569d9..e5f3b06f3 100644
--- a/ios/NotificationService/Info.plist
+++ b/ios/NotificationService/Info.plist
@@ -21,7 +21,7 @@
CFBundleShortVersionString
2.0.0
CFBundleVersion
- 442
+ 443
NSExtension
NSExtensionPointIdentifier
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 5aec0ee03..3a085da90 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -369,8 +369,6 @@ PODS:
- ReactCommon/turbomodule/core
- react-native-turbo-mailer (0.2.0):
- React-Core
- - react-native-user-agent (2.3.1):
- - React
- react-native-video (5.2.1):
- React-Core
- react-native-video/Video (= 5.2.1)
@@ -618,7 +616,6 @@ DEPENDENCIES:
- "react-native-paste-input (from `../node_modules/@mattermost/react-native-paste-input`)"
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- "react-native-turbo-mailer (from `../node_modules/@mattermost/react-native-turbo-mailer`)"
- - react-native-user-agent (from `../node_modules/react-native-user-agent`)
- react-native-video (from `../node_modules/react-native-video`)
- react-native-webrtc (from `../node_modules/react-native-webrtc`)
- react-native-webview (from `../node_modules/react-native-webview`)
@@ -776,8 +773,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-safe-area-context"
react-native-turbo-mailer:
:path: "../node_modules/@mattermost/react-native-turbo-mailer"
- react-native-user-agent:
- :path: "../node_modules/react-native-user-agent"
react-native-video:
:path: "../node_modules/react-native-video"
react-native-webrtc:
@@ -928,7 +923,6 @@ SPEC CHECKSUMS:
react-native-paste-input: 88709b4fd586ea8cc56ba5e2fc4cdfe90597730c
react-native-safe-area-context: 99b24a0c5acd0d5dcac2b1a7f18c49ea317be99a
react-native-turbo-mailer: 226fc3533d16500fb4ad08cf8ab2cfc7bb1ef593
- react-native-user-agent: a90a1e839b99801baad67a73dd6f361a52aa3cf1
react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253
react-native-webrtc: 86d841823e66d68cc1f86712db1c2956056bf0c2
react-native-webview: 91a6bd643c6c298f6dfac309231c15d60b8e9505
diff --git a/package-lock.json b/package-lock.json
index 32a707182..30cd1b8b4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -88,7 +88,6 @@
"react-native-shadow-2": "7.0.6",
"react-native-share": "8.0.0",
"react-native-svg": "13.6.0",
- "react-native-user-agent": "2.3.1",
"react-native-vector-icons": "9.2.0",
"react-native-video": "5.2.1",
"react-native-webrtc": "github:mattermost/react-native-webrtc",
@@ -18733,14 +18732,6 @@
"resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz",
"integrity": "sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw=="
},
- "node_modules/react-native-user-agent": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/react-native-user-agent/-/react-native-user-agent-2.3.1.tgz",
- "integrity": "sha512-AIFr1VgJHwgWmMwCOmIGxuBeAaADlouXKc10UyR4fzWneUbt5uIJIoRu2oExlfCtiT8IyCp106khDD5vx7RUjw==",
- "peerDependencies": {
- "react-native": "*"
- }
- },
"node_modules/react-native-vector-icons": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz",
@@ -36079,12 +36070,6 @@
"resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz",
"integrity": "sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw=="
},
- "react-native-user-agent": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/react-native-user-agent/-/react-native-user-agent-2.3.1.tgz",
- "integrity": "sha512-AIFr1VgJHwgWmMwCOmIGxuBeAaADlouXKc10UyR4fzWneUbt5uIJIoRu2oExlfCtiT8IyCp106khDD5vx7RUjw==",
- "requires": {}
- },
"react-native-vector-icons": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz",
diff --git a/package.json b/package.json
index 215b6d158..01bee6303 100644
--- a/package.json
+++ b/package.json
@@ -85,7 +85,6 @@
"react-native-shadow-2": "7.0.6",
"react-native-share": "8.0.0",
"react-native-svg": "13.6.0",
- "react-native-user-agent": "2.3.1",
"react-native-vector-icons": "9.2.0",
"react-native-video": "5.2.1",
"react-native-webrtc": "github:mattermost/react-native-webrtc",
@@ -175,7 +174,6 @@
},
"scripts": {
"android": "react-native run-android",
- "build-storybook": "build-storybook",
"build:android": "./scripts/build.sh apk",
"build:android-unsigned": "./scripts/build.sh apk unsigned",
"build:ios": "./scripts/build.sh ipa",
@@ -196,9 +194,7 @@
"pod-install": "cd ios && bundle exec pod install",
"postinstall": "patch-package && ./scripts/postinstall.sh",
"preinstall": "npx solidarity",
- "prestorybook": "rnstl",
"start": "react-native start",
- "storybook": "start-storybook -p 7007",
"test": "jest --forceExit --runInBand",
"test:coverage": "jest --coverage",
"test:watch": "npm test -- --watch",
diff --git a/test/setup.ts b/test/setup.ts
index 0ee8bf871..eea80218b 100644
--- a/test/setup.ts
+++ b/test/setup.ts
@@ -236,12 +236,6 @@ jest.mock('react-native-device-info', () => {
};
});
-jest.mock('react-native-user-agent', () => {
- return {
- getUserAgent: () => 'user-agent',
- };
-});
-
jest.mock('react-native-localize', () => ({
getTimeZone: () => 'World/Somewhere',
getLocales: () => ([
diff --git a/types/components/emoji.ts b/types/components/emoji.ts
new file mode 100644
index 000000000..8daf4074f
--- /dev/null
+++ b/types/components/emoji.ts
@@ -0,0 +1,19 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji';
+import type {StyleProp, TextStyle} from 'react-native';
+import type {ImageStyle} from 'react-native-fast-image';
+
+export type EmojiProps = {
+ emojiName: string;
+ displayTextOnly?: boolean;
+ literal?: string;
+ size?: number;
+ textStyle?: StyleProp;
+ customEmojiStyle?: StyleProp;
+ customEmojis: CustomEmojiModel[];
+ testID?: string;
+}
+
+export type EmojiComponent = (props: Omit) => JSX.Element;
diff --git a/types/components/syntax_highlight.ts b/types/components/syntax_highlight.ts
new file mode 100644
index 000000000..d8f959e5f
--- /dev/null
+++ b/types/components/syntax_highlight.ts
@@ -0,0 +1,11 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {TextStyle} from 'react-native';
+
+export type SyntaxHiglightProps = {
+ code: string;
+ language: string;
+ textStyle: TextStyle;
+ selectable?: boolean;
+};