Merge branch 'main' into gm_to_channel

This commit is contained in:
harshil Sharma 2023-10-31 07:44:04 +05:30
commit 2a65fa7816
11 changed files with 195 additions and 57 deletions

View file

@ -71,6 +71,7 @@ type MarkdownProps = {
textStyles?: MarkdownTextStyles;
theme: Theme;
value?: string;
onLinkLongPress?: (url?: string) => void;
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
@ -133,7 +134,7 @@ const Markdown = ({
enableInlineLatex, enableLatex, maxNodes,
imagesMetadata, isEdited, isReplyPost, isSearchResult, layoutHeight, layoutWidth,
location, mentionKeys, minimumHashtagLength = 3, onPostPress, postId, searchPatterns,
textStyles = {}, theme, value = '', baseParagraphStyle,
textStyles = {}, theme, value = '', baseParagraphStyle, onLinkLongPress,
}: MarkdownProps) => {
const style = getStyleSheet(theme);
const managedConfig = useManagedConfig<ManagedConfig>();
@ -402,7 +403,10 @@ const Markdown = ({
const renderLink = ({children, href}: {children: ReactElement; href: string}) => {
return (
<MarkdownLink href={href}>
<MarkdownLink
href={href}
onLinkLongPress={onLinkLongPress}
>
{children}
</MarkdownLink>
);

View file

@ -23,6 +23,7 @@ type MarkdownLinkProps = {
experimentalNormalizeMarkdownLinks: string;
href: string;
siteURL: string;
onLinkLongPress?: (url?: string) => void;
}
const style = StyleSheet.create({
@ -44,7 +45,7 @@ const parseLinkLiteral = (literal: string) => {
return parsed.href;
};
const MarkdownLink = ({children, experimentalNormalizeMarkdownLinks, href, siteURL}: MarkdownLinkProps) => {
const MarkdownLink = ({children, experimentalNormalizeMarkdownLinks, href, siteURL, onLinkLongPress}: MarkdownLinkProps) => {
const intl = useIntl();
const {bottom} = useSafeAreaInsets();
const managedConfig = useManagedConfig<ManagedConfig>();
@ -109,6 +110,11 @@ const MarkdownLink = ({children, experimentalNormalizeMarkdownLinks, href, siteU
const handleLongPress = useCallback(() => {
if (managedConfig?.copyAndPasteProtection !== 'true') {
if (onLinkLongPress) {
onLinkLongPress(href);
return;
}
const renderContent = () => {
return (
<View
@ -145,7 +151,7 @@ const MarkdownLink = ({children, experimentalNormalizeMarkdownLinks, href, siteU
theme,
});
}
}, [managedConfig, intl, bottom, theme]);
}, [managedConfig, intl, bottom, theme, onLinkLongPress]);
const renderChildren = experimentalNormalizeMarkdownLinks ? parseChildren() : children;

View file

@ -1,18 +1,29 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {useManagedConfig} from '@mattermost/react-native-emm';
import Clipboard from '@react-native-clipboard/clipboard';
import moment from 'moment';
import React, {useMemo} from 'react';
import {Text, View} from 'react-native';
import React, {useCallback, useMemo} from 'react';
import {useIntl} from 'react-intl';
import {Platform, StyleSheet, Text, View} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import CustomStatusExpiry from '@components/custom_status/custom_status_expiry';
import Emoji from '@components/emoji';
import FormattedDate from '@components/formatted_date';
import FormattedText from '@components/formatted_text';
import Markdown from '@components/markdown';
import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item';
import TouchableWithFeedback from '@components/touchable_with_feedback';
import {Screens} from '@constants';
import {SNACK_BAR_TYPE} from '@constants/snack_bar';
import {ANDROID_33, OS_VERSION} from '@constants/versions';
import {useTheme} from '@context/theme';
import {bottomSheet, dismissBottomSheet} from '@screens/navigation';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {getMarkdownBlockStyles, getMarkdownTextStyles} from '@utils/markdown';
import {showSnackBar} from '@utils/snack_bar';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
@ -66,8 +77,30 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
},
}));
const style = StyleSheet.create({
bottomSheet: {
flex: 1,
},
});
const headerTestId = 'channel_info.extra.header';
const onCopy = async (text: string, isLink?: boolean) => {
Clipboard.setString(text);
await dismissBottomSheet();
if ((Platform.OS === OS_VERSION.ANDROID && Number(Platform.Version) < ANDROID_33) || Platform.OS === OS_VERSION.IOS) {
showSnackBar({
barType: isLink ? SNACK_BAR_TYPE.LINK_COPIED : SNACK_BAR_TYPE.TEXT_COPIED,
});
}
};
const Extra = ({channelId, createdAt, createdBy, customStatus, header, isCustomStatusEnabled}: Props) => {
const intl = useIntl();
const {bottom} = useSafeAreaInsets();
const theme = useTheme();
const managedConfig = useManagedConfig<ManagedConfig>();
const styles = getStyleSheet(theme);
const blockStyles = getMarkdownBlockStyles(theme);
const textStyles = getMarkdownTextStyles(theme);
@ -81,6 +114,70 @@ const Extra = ({channelId, createdAt, createdBy, customStatus, header, isCustomS
),
}), [createdAt, createdBy, theme]);
const handleLongPress = useCallback((url?: string) => {
if (managedConfig?.copyAndPasteProtection !== 'true') {
const renderContent = () => (
<View
testID={`${headerTestId}.bottom_sheet`}
style={style.bottomSheet}
>
<SlideUpPanelItem
leftIcon='content-copy'
onPress={() => {
onCopy(header!);
}}
testID={`${headerTestId}.bottom_sheet.copy_header_text`}
text={intl.formatMessage({
id: 'mobile.markdown.copy_header',
defaultMessage: 'Copy header text',
})}
/>
{Boolean(url) && (
<SlideUpPanelItem
leftIcon='link-variant'
onPress={() => {
onCopy(url!, true);
}}
testID={`${headerTestId}.bottom_sheet.copy_url`}
text={intl.formatMessage({
id: 'mobile.markdown.link.copy_url',
defaultMessage: 'Copy URL',
})}
/>
)}
<SlideUpPanelItem
destructive={true}
leftIcon='cancel'
onPress={() => {
dismissBottomSheet();
}}
testID={`${headerTestId}.bottom_sheet.cancel`}
text={intl.formatMessage({
id: 'mobile.post.cancel',
defaultMessage: 'Cancel',
})}
/>
</View>
);
bottomSheet({
closeButtonId: 'close-markdown-link',
renderContent,
snapPoints: [1, bottomSheetSnapPoint(url ? 3 : 2, ITEM_HEIGHT, bottom)],
title: intl.formatMessage({id: 'post.options.title', defaultMessage: 'Options'}),
theme,
});
}
}, [
header,
bottom,
theme,
intl.formatMessage,
managedConfig?.copyAndPasteProtection,
]);
const touchableHandleLongPress = useCallback(() => handleLongPress(), [handleLongPress]);
return (
<View style={styles.container}>
{isCustomStatusEnabled && Boolean(customStatus) &&
@ -131,25 +228,32 @@ const Extra = ({channelId, createdAt, createdBy, customStatus, header, isCustomS
id='channel_info.header'
defaultMessage='Header:'
style={styles.extraHeading}
testID='channel_info.extra.header'
/>
<Markdown
channelId={channelId}
baseTextStyle={styles.header}
blockStyles={blockStyles}
disableBlockQuote={true}
disableCodeBlock={true}
disableGallery={true}
disableHeading={true}
disableTables={true}
location={Screens.CHANNEL_INFO}
textStyles={textStyles}
layoutHeight={48}
layoutWidth={100}
theme={theme}
imagesMetadata={headerMetadata}
value={header}
testID={headerTestId}
/>
<TouchableWithFeedback
type='opacity'
activeOpacity={0.8}
onLongPress={touchableHandleLongPress}
>
<Markdown
channelId={channelId}
baseTextStyle={styles.header}
blockStyles={blockStyles}
disableBlockQuote={true}
disableCodeBlock={true}
disableGallery={true}
disableHeading={true}
disableTables={true}
location={Screens.CHANNEL_INFO}
textStyles={textStyles}
layoutHeight={48}
layoutWidth={100}
theme={theme}
imagesMetadata={headerMetadata}
value={header}
onLinkLongPress={handleLongPress}
/>
</TouchableWithFeedback>
</View>
}
{Boolean(createdAt && createdBy) &&

View file

@ -3,7 +3,7 @@
import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {StyleSheet} from 'react-native';
import {Dimensions, StyleSheet} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import ServerIcon from '@components/server_icon';
@ -27,7 +27,8 @@ export type ServersRef = {
openServers: () => void;
}
export const SERVER_ITEM_HEIGHT = 72;
export const SERVER_ITEM_HEIGHT = 75;
export const PUSH_ALERT_TEXT_HEIGHT = 42;
const subscriptions: Map<string, UnreadSubscription> = new Map();
const styles = StyleSheet.create({
@ -116,12 +117,18 @@ const Servers = React.forwardRef<ServersRef>((_, ref) => {
<ServerList servers={registeredServers.current!}/>
);
};
const maxScreenHeight = Math.ceil(0.6 * Dimensions.get('window').height);
const maxSnapPoint = Math.min(
maxScreenHeight,
bottomSheetSnapPoint(registeredServers.current.length, SERVER_ITEM_HEIGHT, bottom) + TITLE_HEIGHT + BUTTON_HEIGHT +
(registeredServers.current.filter((s: ServersModel) => s.lastActiveAt).length * PUSH_ALERT_TEXT_HEIGHT),
);
const snapPoints: BottomSheetProps['snapPoints'] = [
1,
bottomSheetSnapPoint(Math.min(2.5, registeredServers.current.length), 72, bottom) + TITLE_HEIGHT + BUTTON_HEIGHT,
maxSnapPoint,
];
if (registeredServers.current.length > 1) {
if (maxSnapPoint === maxScreenHeight) {
snapPoints.push('80%');
}

View file

@ -4,11 +4,12 @@
import {BottomSheetFlatList} from '@gorhom/bottom-sheet';
import React, {useCallback, useMemo} from 'react';
import {useIntl} from 'react-intl';
import {FlatList, type ListRenderItemInfo, StyleSheet, View} from 'react-native';
import {FlatList, StyleSheet, View, type ListRenderItemInfo} from 'react-native';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {BUTTON_HEIGHT} from '@screens/bottom_sheet';
import BottomSheetContent from '@screens/bottom_sheet/content';
import {addNewServer} from '@utils/server';
@ -29,6 +30,9 @@ const styles = StyleSheet.create({
contentContainer: {
marginVertical: 4,
},
serverList: {
marginBottom: BUTTON_HEIGHT,
},
});
const keyExtractor = (item: ServersModel) => item.url;
@ -68,6 +72,7 @@ const ServerList = ({servers}: Props) => {
<View style={[styles.container, {marginTop: isTablet ? 12 : 0}]}>
<List
data={servers}
style={styles.serverList}
renderItem={renderServer}
keyExtractor={keyExtractor}
contentContainerStyle={styles.contentContainer}

View file

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useCallback, useMemo} from 'react';
import React, {useCallback, useMemo, useState, useEffect} from 'react';
import {savePreference} from '@actions/remote/preference';
import SettingContainer from '@components/settings/container';
@ -26,17 +26,22 @@ const DisplayTheme = ({allowedThemeKeys, componentId, currentTeamId, currentUser
const serverUrl = useServerUrl();
const theme = useTheme();
const initialTheme = useMemo(() => theme, [/* dependency array should remain empty */]);
const [newTheme, setNewTheme] = useState<string | undefined>(undefined);
const close = () => popTopScreen(componentId);
const setThemePreference = useCallback((newTheme?: string) => {
const allowedTheme = allowedThemeKeys.find((tk) => tk === newTheme);
useEffect(() => {
const differentTheme = theme.type?.toLowerCase() !== newTheme?.toLowerCase();
if (!differentTheme) {
close();
return;
}
setThemePreference();
}, [newTheme]);
const setThemePreference = useCallback(() => {
const allowedTheme = allowedThemeKeys.find((tk) => tk === newTheme);
const themeJson = Preferences.THEMES[allowedTheme as ThemeKey] || initialTheme;
@ -47,15 +52,20 @@ const DisplayTheme = ({allowedThemeKeys, componentId, currentTeamId, currentUser
value: JSON.stringify(themeJson),
};
savePreference(serverUrl, [pref]);
}, [allowedThemeKeys, currentTeamId, theme.type, serverUrl]);
}, [allowedThemeKeys, currentTeamId, theme.type, serverUrl, newTheme]);
useAndroidHardwareBackHandler(componentId, setThemePreference);
const onAndroidBack = () => {
setThemePreference();
close();
};
useAndroidHardwareBackHandler(componentId, onAndroidBack);
return (
<SettingContainer testID='theme_display_settings'>
<ThemeTiles
allowedThemeKeys={allowedThemeKeys}
onThemeChange={setThemePreference}
onThemeChange={setNewTheme}
selectedTheme={theme.type}
/>
{initialTheme.type === 'custom' && (

View file

@ -613,6 +613,7 @@
"mobile.managed.settings": "Go to settings",
"mobile.markdown.code.copy_code": "Copy Code",
"mobile.markdown.code.plusMoreLines": "+{count, number} more {count, plural, one {line} other {lines}}",
"mobile.markdown.copy_header": "Copy header text",
"mobile.markdown.image.too_large": "Image exceeds max dimensions of {maxWidth} by {maxHeight}:",
"mobile.markdown.link.copy_url": "Copy URL",
"mobile.mention.copy_mention": "Copy Mention",

View file

@ -385,7 +385,7 @@ PODS:
- React-Core
- Starscream (~> 4.0.4)
- SwiftyJSON (~> 5.0)
- react-native-notifications (5.0.0):
- react-native-notifications (5.1.0):
- React-Core
- react-native-paste-input (0.6.4):
- React-Core
@ -946,7 +946,7 @@ SPEC CHECKSUMS:
react-native-in-app-review: db8bb167a5f238e7ceca5c242d6b36ce8c4404a4
react-native-netinfo: fefd4e98d75cbdd6e85fc530f7111a8afdf2b0c5
react-native-network-client: 953ab4d0914fdde6dc40924b5faad8631a587d4e
react-native-notifications: d309f7080aad71206882dbb98d9ed788969f3b6d
react-native-notifications: 4601a5a8db4ced6ae7cfc43b44d35fe437ac50c4
react-native-paste-input: 09f14cbfb646ad9d2ef79cdc6f3d45f337c10ad1
react-native-safe-area-context: 9697629f7b2cda43cf52169bb7e0767d330648c2
react-native-video: c26780b224543c62d5e1b2a7244a5cd1b50e8253
@ -1001,4 +1001,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 25f07cb9e5eed8c84db8e8723000e8470c349058
COCOAPODS: 1.12.1
COCOAPODS: 1.14.2

19
package-lock.json generated
View file

@ -6,7 +6,7 @@
"packages": {
"": {
"name": "mattermost-mobile",
"version": "2.9.0",
"version": "2.10.0",
"hasInstallScript": true,
"license": "Apache 2.0",
"dependencies": {
@ -83,7 +83,7 @@
"react-native-localize": "3.0.2",
"react-native-math-view": "3.9.5",
"react-native-navigation": "7.37.0",
"react-native-notifications": "5.0.0",
"react-native-notifications": "5.1.0",
"react-native-permissions": "3.8.4",
"react-native-reanimated": "3.4.2",
"react-native-safe-area-context": "4.7.1",
@ -19544,9 +19544,9 @@
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
},
"node_modules/react-native-notifications": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-5.0.0.tgz",
"integrity": "sha512-QXtBBmbDtwq9X8WAPLn+OctIeEtnJOQ+RCT6iweaypvFTydt2baLPtawTAbCSXKuWpVDqDAdmZnlQjCcavNzoA==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-5.1.0.tgz",
"integrity": "sha512-laqDSDlCvEASmJR6cXpqaryK855ejQd07vrfYERzhv68YDOoSkKy/URExRP4vAfAOVqHhix80tLbNUcfvZk2VQ==",
"peerDependencies": {
"react": "*",
"react-native": "*"
@ -28574,8 +28574,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@voximplant/react-native-foreground-service/-/react-native-foreground-service-3.0.2.tgz",
"integrity": "sha512-ZIyccOAXPqznA1PAVAYlKZ+GI7kXYCuYgH+gmAkhPouyJbkgrSXaCJJzQ+uBkPr4FBa/PuC/yjzK8vf6tJREQA==",
"requires": {
}
"requires": {}
},
"@webassemblyjs/ast": {
"version": "1.11.1",
@ -37641,9 +37640,9 @@
}
},
"react-native-notifications": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-5.0.0.tgz",
"integrity": "sha512-QXtBBmbDtwq9X8WAPLn+OctIeEtnJOQ+RCT6iweaypvFTydt2baLPtawTAbCSXKuWpVDqDAdmZnlQjCcavNzoA==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-5.1.0.tgz",
"integrity": "sha512-laqDSDlCvEASmJR6cXpqaryK855ejQd07vrfYERzhv68YDOoSkKy/URExRP4vAfAOVqHhix80tLbNUcfvZk2VQ==",
"requires": {}
},
"react-native-permissions": {

View file

@ -84,7 +84,7 @@
"react-native-localize": "3.0.2",
"react-native-math-view": "3.9.5",
"react-native-navigation": "7.37.0",
"react-native-notifications": "5.0.0",
"react-native-notifications": "5.1.0",
"react-native-permissions": "3.8.4",
"react-native-reanimated": "3.4.2",
"react-native-safe-area-context": "4.7.1",

View file

@ -1,5 +1,5 @@
diff --git a/node_modules/react-native-notifications/lib/android/app/build.gradle b/node_modules/react-native-notifications/lib/android/app/build.gradle
index d049e84..9ad7004 100644
index 30bb01c..bba788d 100644
--- a/node_modules/react-native-notifications/lib/android/app/build.gradle
+++ b/node_modules/react-native-notifications/lib/android/app/build.gradle
@@ -96,9 +96,9 @@ android {
@ -7,10 +7,10 @@ index d049e84..9ad7004 100644
testOptions {
unitTests.all { t ->
- reports {
- html.enabled true
- html.required.set true
- }
+ // reports {
+ // html.enabled true
+ // html.required.set true
+ // }
testLogging {
events "PASSED", "SKIPPED", "FAILED", "standardOut", "standardError"
@ -90,10 +90,10 @@ index 5b7f15f..9381794 100644
}
}
diff --git a/node_modules/react-native-notifications/lib/android/app/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java b/node_modules/react-native-notifications/lib/android/app/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java
index 1e7e871..62e5cb8 100644
index 1e7e871..36b96b6 100644
--- a/node_modules/react-native-notifications/lib/android/app/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java
+++ b/node_modules/react-native-notifications/lib/android/app/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java
@@ -14,17 +14,9 @@ public class NotificationIntentAdapter {
@@ -14,17 +14,11 @@ public class NotificationIntentAdapter {
@SuppressLint("UnspecifiedImmutableFlag")
public static PendingIntent createPendingNotificationIntent(Context appContext, PushNotificationProps notification) {
@ -108,9 +108,11 @@ index 1e7e871..62e5cb8 100644
- taskStackBuilder.addNextIntentWithParentStack(mainActivityIntent);
- return taskStackBuilder.getPendingIntent((int) System.currentTimeMillis(), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
- }
+ Intent intent = appContext.getPackageManager().getLaunchIntentForPackage(appContext.getPackageName());
+ intent.putExtra(PUSH_NOTIFICATION_EXTRA_NAME, notification.asBundle());
+ return PendingIntent.getActivity(appContext, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
+ Intent mainActivityIntent = appContext.getPackageManager().getLaunchIntentForPackage(appContext.getPackageName());
+ mainActivityIntent.putExtra(PUSH_NOTIFICATION_EXTRA_NAME, notification.asBundle());
+ TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(appContext);
+ taskStackBuilder.addNextIntentWithParentStack(mainActivityIntent);
+ return taskStackBuilder.getPendingIntent((int) System.currentTimeMillis(), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
}
public static boolean canHandleTrampolineActivity(Context appContext) {