This commit is contained in:
Julian Mondragon 2022-12-16 10:46:15 -05:00
commit 0262b22a02
25 changed files with 280 additions and 283 deletions

View file

@ -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

View file

@ -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'

View file

@ -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);
}
}

View file

@ -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 (
<Text
style={textStyle}
testID={testID}
>
{literal}
</Text>);
}
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 (
<Text
style={[textStyle, {fontSize: size, color: '#000'}]}
testID={testID}
>
{code}
</Text>
);
}
if (assetImage) {
const key = Platform.OS === 'android' ? (`${assetImage}-${height}-${width}`) : null;
const image = assetImages.get(assetImage);
if (!image) {
return null;
}
return (
<FastImage
key={key}
source={image}
style={[customEmojiStyle, {width, height}]}
resizeMode={FastImage.resizeMode.contain}
testID={testID}
/>
);
}
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 (
<FastImage
key={key}
style={[customEmojiStyle, {width, height}]}
source={{uri: imageUrl}}
resizeMode={FastImage.resizeMode.contain}
testID={testID}
/>
);
};
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));

View file

@ -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<TextStyle>;
customEmojiStyle?: StyleProp<ImageStyle>;
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<EmojiProps, 'customEmojis'>) => {
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 (
<Text
style={textStyle}
testID={testID}
>
{literal}
</Text>);
}
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 (
<Text
style={[textStyle, {fontSize: size, color: '#000'}]}
testID={testID}
>
{code}
</Text>
);
}
if (assetImage) {
const key = Platform.OS === 'android' ? (`${assetImage}-${height}-${width}`) : null;
const image = assetImages.get(assetImage);
if (!image) {
return null;
}
return (
<FastImage
key={key}
source={image}
style={[customEmojiStyle, {width, height}]}
resizeMode={FastImage.resizeMode.contain}
testID={testID}
/>
);
}
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 (
<FastImage
key={key}
style={[customEmojiStyle, {width, height}]}
source={{uri: imageUrl}}
resizeMode={FastImage.resizeMode.contain}
testID={testID}
/>
);
return (<Emoji {...props}/>);
};
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;

View file

@ -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';

View file

@ -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;

View file

@ -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 (

View file

@ -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<string, any> = {
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(() => [

View file

@ -11,6 +11,18 @@ export const useKeyboardTrackingPaused = (keyboardTrackingRef: RefObject<Keyboar
const isPostDraftPaused = useRef(false);
useEffect(() => {
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<Keyboar
});
const commandCompletedListener = Navigation.events().registerCommandCompletedListener(() => {
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]);
};

View file

@ -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<ManagedConfig>() : undefined;
const headers: Record<string, string> = {
...this.DEFAULT_CONFIG.headers,
[ClientConstants.HEADER_USER_AGENT]: userAgent,
...this.DEFAULT_CONFIG.headers,
};
const config = {

View file

@ -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);
}
});

View file

@ -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": {

View file

@ -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",

View file

@ -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

View file

@ -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;

View file

@ -37,7 +37,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>442</string>
<string>443</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>

View file

@ -21,7 +21,7 @@
<key>CFBundleShortVersionString</key>
<string>2.0.0</string>
<key>CFBundleVersion</key>
<string>442</string>
<string>443</string>
<key>UIAppFonts</key>
<array>
<string>OpenSans-Bold.ttf</string>

View file

@ -21,7 +21,7 @@
<key>CFBundleShortVersionString</key>
<string>2.0.0</string>
<key>CFBundleVersion</key>
<string>442</string>
<string>443</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>

View file

@ -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

15
package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -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: () => ([

19
types/components/emoji.ts Normal file
View file

@ -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<TextStyle>;
customEmojiStyle?: StyleProp<ImageStyle>;
customEmojis: CustomEmojiModel[];
testID?: string;
}
export type EmojiComponent = (props: Omit<EmojiProps, 'customEmojis'>) => JSX.Element;

View file

@ -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;
};