diff --git a/app/components/formatted_markdown_text/index.tsx b/app/components/formatted_markdown_text/index.tsx index ddb0203999..dbee2feb33 100644 --- a/app/components/formatted_markdown_text/index.tsx +++ b/app/components/formatted_markdown_text/index.tsx @@ -5,12 +5,12 @@ import {Parser} from 'commonmark'; import Renderer from 'commonmark-react-renderer'; import React, {ReactElement, useRef} from 'react'; import {useIntl} from 'react-intl'; -import {GestureResponderEvent, Platform, StyleProp, Text, TextStyle} from 'react-native'; +import {GestureResponderEvent, StyleProp, Text, TextStyle} from 'react-native'; import AtMention from '@components/markdown/at_mention'; import MarkdownLink from '@components/markdown/markdown_link'; import {useTheme} from '@context/theme'; -import {getMarkdownTextStyles} from '@utils/markdown'; +import {getMarkdownBlockStyles, getMarkdownTextStyles} from '@utils/markdown'; import {concatStyles, changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import type {PrimitiveType} from 'intl-messageformat'; @@ -31,6 +31,11 @@ const TARGET_BLANK_URL_PREFIX = '!'; const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { return { + block: { + alignItems: 'flex-start', + flexDirection: 'row', + flexWrap: 'wrap', + }, message: { color: changeOpacity(theme.centerChannelColor, 0.8), fontSize: 16, @@ -39,9 +44,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { atMentionOpacity: { opacity: 1, }, - touchableStyle: { - top: Platform.select({ios: 2, default: 4}), - }, }; }); @@ -86,7 +88,6 @@ const FormattedMarkdownText = ({baseTextStyle, defaultMessage, id, onPostPress, mentionName={mentionName} onPostPress={onPostPress} textStyle={[computeTextStyle(baseTextStyle, context), styles.atMentionOpacity]} - touchableStyle={styles.touchableStyle} /> ); }; @@ -110,8 +111,18 @@ const FormattedMarkdownText = ({baseTextStyle, defaultMessage, id, onPostPress, return {children}; }; - const renderParagraph = ({children}: {children: ReactElement}) => { - return {children}; + const renderParagraph = ({children, first}: {children: ReactElement; first: boolean}) => { + const blockStyle = [styles.block]; + if (!first) { + const blockS = getMarkdownBlockStyles(theme); + blockStyle.push(blockS.adjacentParagraph); + } + + return ( + + {children} + + ); }; const renderText = ({context, literal}: {context: string[]; literal: string}) => { diff --git a/app/components/markdown/at_mention/index.tsx b/app/components/markdown/at_mention/at_mention.tsx similarity index 74% rename from app/components/markdown/at_mention/index.tsx rename to app/components/markdown/at_mention/at_mention.tsx index e1c3f442d6..a1923978ba 100644 --- a/app/components/markdown/at_mention/index.tsx +++ b/app/components/markdown/at_mention/at_mention.tsx @@ -2,30 +2,20 @@ // See LICENSE.txt for license information. import {useManagedConfig} from '@mattermost/react-native-emm'; -import {Database, Q} from '@nozbe/watermelondb'; -import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; -import withObservables from '@nozbe/with-observables'; +import {Database} from '@nozbe/watermelondb'; import Clipboard from '@react-native-community/clipboard'; import React, {useCallback, useMemo} from 'react'; import {useIntl} from 'react-intl'; -import {GestureResponderEvent, StyleProp, StyleSheet, Text, TextStyle, View, ViewStyle} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; -import {combineLatest, of as of$} from 'rxjs'; -import {map, switchMap} from 'rxjs/operators'; +import {GestureResponderEvent, StyleProp, StyleSheet, Text, TextStyle, View} from 'react-native'; import CompassIcon from '@components/compass_icon'; import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item'; -import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {MM_TABLES} from '@constants/database'; import {useTheme} from '@context/theme'; import UserModel from '@database/models/server/user'; -import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; import {bottomSheet, dismissBottomSheet, showModal} from '@screens/navigation'; import {displayUsername, getUsersByUsername} from '@utils/user'; -import type {WithDatabaseArgs} from '@typings/database/database'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModelType from '@typings/database/models/servers/user'; type AtMentionProps = { @@ -39,16 +29,13 @@ type AtMentionProps = { onPostPress?: (e: GestureResponderEvent) => void; teammateNameDisplay: string; textStyle?: StyleProp; - touchableStyle?: StyleProp; users: UserModelType[]; } -const {SERVER: {PREFERENCE, SYSTEM, USER}} = MM_TABLES; +const {SERVER: {USER}} = MM_TABLES; const style = StyleSheet.create({ - bottomSheet: { - flex: 1, - }, + bottomSheet: {flex: 1}, }); const AtMention = ({ @@ -62,7 +49,6 @@ const AtMention = ({ onPostPress, teammateNameDisplay, textStyle, - touchableStyle, users, }: AtMentionProps) => { const intl = useIntl(); @@ -228,48 +214,17 @@ const AtMention = ({ } return ( - - - - {'@' + mention} - - {suffixElement} + + {'@' + mention} - + {suffixElement} + ); }; -const withAtMention = withObservables(['mentionName'], ({database, mentionName}: {mentionName: string} & WithDatabaseArgs) => { - const config = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); - const license = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE); - const preferences = database.get(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe(); - const currentUserId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => of$(value)), - ); - const teammateNameDisplay = combineLatest([config, license, preferences]).pipe( - map( - ([{value: cfg}, {value: lcs}, prefs]) => getTeammateNameDisplaySetting(prefs, cfg, lcs), - ), - ); - - let mn = mentionName.toLowerCase(); - if ((/[._-]$/).test(mn)) { - mn = mn.substring(0, mn.length - 1); - } - - return { - currentUserId, - teammateNameDisplay, - users: database.get(USER).query( - Q.where('username', Q.like( - `%${Q.sanitizeLikeString(mn)}%`, - )), - ).observeWithColumns(['username']), - }; -}); - -export default withDatabase(withAtMention(React.memo(AtMention))); +export default React.memo(AtMention); diff --git a/app/components/markdown/at_mention/index.ts b/app/components/markdown/at_mention/index.ts new file mode 100644 index 0000000000..c2fb74520d --- /dev/null +++ b/app/components/markdown/at_mention/index.ts @@ -0,0 +1,52 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Q} from '@nozbe/watermelondb'; +import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; +import withObservables from '@nozbe/with-observables'; +import {combineLatest, of as of$} from 'rxjs'; +import {map, switchMap} from 'rxjs/operators'; + +import {Preferences} from '@constants'; +import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {getTeammateNameDisplaySetting} from '@helpers/api/preference'; + +import AtMention from './at_mention'; + +import type {WithDatabaseArgs} from '@typings/database/database'; +import type PreferenceModel from '@typings/database/models/servers/preference'; +import type SystemModel from '@typings/database/models/servers/system'; + +const {SERVER: {PREFERENCE, SYSTEM, USER}} = MM_TABLES; +const {CONFIG, CURRENT_USER_ID, LICENSE} = SYSTEM_IDENTIFIERS; + +const enhance = withObservables(['mentionName'], ({database, mentionName}: {mentionName: string} & WithDatabaseArgs) => { + const config = database.get(SYSTEM).findAndObserve(CONFIG); + const license = database.get(SYSTEM).findAndObserve(LICENSE); + const preferences = database.get(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe(); + const currentUserId = database.get(SYSTEM).findAndObserve(CURRENT_USER_ID).pipe( + switchMap(({value}) => of$(value)), + ); + const teammateNameDisplay = combineLatest([config, license, preferences]).pipe( + map( + ([{value: cfg}, {value: lcs}, prefs]) => getTeammateNameDisplaySetting(prefs, cfg, lcs), + ), + ); + + let mn = mentionName.toLowerCase(); + if ((/[._-]$/).test(mn)) { + mn = mn.substring(0, mn.length - 1); + } + + return { + currentUserId, + teammateNameDisplay, + users: database.get(USER).query( + Q.where('username', Q.like( + `%${Q.sanitizeLikeString(mn)}%`, + )), + ).observeWithColumns(['username']), + }; +}); + +export default withDatabase(enhance(AtMention)); diff --git a/app/components/markdown/channel_mention/index.tsx b/app/components/markdown/channel_mention/channel_mention.tsx similarity index 68% rename from app/components/markdown/channel_mention/index.tsx rename to app/components/markdown/channel_mention/channel_mention.tsx index 3ddc43061a..df4b033324 100644 --- a/app/components/markdown/channel_mention/index.tsx +++ b/app/components/markdown/channel_mention/channel_mention.tsx @@ -1,26 +1,18 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; -import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; -import withObservables from '@nozbe/with-observables'; import React, {useCallback} from 'react'; import {useIntl} from 'react-intl'; import {StyleProp, Text, TextStyle} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; -import {map, switchMap} from 'rxjs/operators'; import {joinChannel, switchToChannelById} from '@actions/remote/channel'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {useServerUrl} from '@context/server'; import {t} from '@i18n'; import {dismissAllModals, popToRoot} from '@screens/navigation'; import {alertErrorWithFallback} from '@utils/draft'; import {preventDoubleTap} from '@utils/tap'; -import type {WithDatabaseArgs} from '@typings/database/database'; import type ChannelModel from '@typings/database/models/servers/channel'; -import type SystemModel from '@typings/database/models/servers/system'; import type TeamModel from '@typings/database/models/servers/team'; export type ChannelMentions = Record; @@ -65,8 +57,6 @@ function getChannelFromChannelName(name: string, channels: ChannelModel[], chann return null; } -const {SERVER: {CHANNEL, SYSTEM, TEAM}} = MM_TABLES; - const ChannelMention = ({ channelMentions, channelName, channels, currentTeamId, currentUserId, linkStyle, team, textStyle, @@ -112,33 +102,16 @@ const ChannelMention = ({ } return ( - - - - {`~${channel.display_name}`} - - {suffix} + + + {`~${channel.display_name}`} - + {suffix} + ); }; -const withChannelsForTeam = withObservables([], ({database}: WithDatabaseArgs) => { - const currentTeamId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID); - const currentUserId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID); - const channels = currentTeamId.pipe( - switchMap(({value}) => database.get(CHANNEL).query(Q.where('team_id', value)).observeWithColumns(['display_name'])), - ); - const team = currentTeamId.pipe( - switchMap(({value}) => database.get(TEAM).findAndObserve(value)), - ); - - return { - channels, - currentTeamId: currentTeamId.pipe(map((ct) => ct.value)), - currentUserId: currentUserId.pipe(map((cu) => cu.value)), - team, - }; -}); - -export default withDatabase(withChannelsForTeam(ChannelMention)); +export default ChannelMention; diff --git a/app/components/markdown/channel_mention/index.ts b/app/components/markdown/channel_mention/index.ts new file mode 100644 index 0000000000..ba35a484b8 --- /dev/null +++ b/app/components/markdown/channel_mention/index.ts @@ -0,0 +1,40 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Q} from '@nozbe/watermelondb'; +import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; +import withObservables from '@nozbe/with-observables'; +import {map, switchMap} from 'rxjs/operators'; + +import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; + +import ChannelMention from './channel_mention'; + +import type {WithDatabaseArgs} from '@typings/database/database'; +import type ChannelModel from '@typings/database/models/servers/channel'; +import type SystemModel from '@typings/database/models/servers/system'; +import type TeamModel from '@typings/database/models/servers/team'; + +export type ChannelMentions = Record; + +const {SERVER: {CHANNEL, SYSTEM, TEAM}} = MM_TABLES; + +const enhance = withObservables([], ({database}: WithDatabaseArgs) => { + const currentTeamId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID); + const currentUserId = database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID); + const channels = currentTeamId.pipe( + switchMap(({value}) => database.get(CHANNEL).query(Q.where('team_id', value)).observeWithColumns(['display_name'])), + ); + const team = currentTeamId.pipe( + switchMap(({value}) => database.get(TEAM).findAndObserve(value)), + ); + + return { + channels, + currentTeamId: currentTeamId.pipe(map((ct) => ct.value)), + currentUserId: currentUserId.pipe(map((cu) => cu.value)), + team, + }; +}); + +export default withDatabase(enhance(ChannelMention)); diff --git a/app/components/markdown/hashtag/index.tsx b/app/components/markdown/hashtag/index.tsx index c0fd98498d..e7327fd4f3 100644 --- a/app/components/markdown/hashtag/index.tsx +++ b/app/components/markdown/hashtag/index.tsx @@ -3,7 +3,6 @@ import React from 'react'; import {Text, TextStyle} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; import {popToRoot, showSearchModal, dismissAllModals} from '@screens/navigation'; @@ -22,11 +21,12 @@ const Hashtag = ({hashtag, linkStyle}: HashtagProps) => { }; return ( - - - {`#${hashtag}`} - - + + {`#${hashtag}`} + ); }; diff --git a/app/components/markdown/index.tsx b/app/components/markdown/index.tsx index 6051ee0e28..4487a8dce8 100644 --- a/app/components/markdown/index.tsx +++ b/app/components/markdown/index.tsx @@ -56,6 +56,34 @@ type MarkdownProps = { value: string | number; } +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + // Android has trouble giving text transparency depending on how it's nested, + // so we calculate the resulting colour manually + const editedOpacity = Platform.select({ + ios: 0.3, + android: 1.0, + }); + const editedColor = Platform.select({ + ios: theme.centerChannelColor, + android: blendColors(theme.centerChannelBg, theme.centerChannelColor, 0.3), + }); + + return { + block: { + alignItems: 'flex-start', + flexDirection: 'row', + flexWrap: 'wrap', + }, + editedIndicatorText: { + color: editedColor, + opacity: editedOpacity, + }, + atMentionOpacity: { + opacity: 1, + }, + }; +}); + class Markdown extends PureComponent { static defaultProps = { textStyles: {}, @@ -294,7 +322,9 @@ class Markdown extends PureComponent { return ( - {children} + + {children} + ); }; @@ -476,32 +506,4 @@ class Markdown extends PureComponent { } } -const getStyleSheet = makeStyleSheetFromTheme((theme) => { - // Android has trouble giving text transparency depending on how it's nested, - // so we calculate the resulting colour manually - const editedOpacity = Platform.select({ - ios: 0.3, - android: 1.0, - }); - const editedColor = Platform.select({ - ios: theme.centerChannelColor, - android: blendColors(theme.centerChannelBg, theme.centerChannelColor, 0.3), - }); - - return { - block: { - alignItems: 'flex-start', - flexDirection: 'row', - flexWrap: 'wrap', - }, - editedIndicatorText: { - color: editedColor, - opacity: editedOpacity, - }, - atMentionOpacity: { - opacity: 1, - }, - }; -}); - export default Markdown; diff --git a/app/components/markdown/markdown_code_block/index.tsx b/app/components/markdown/markdown_code_block/index.tsx index 8a4fff0682..70eb2ec22b 100644 --- a/app/components/markdown/markdown_code_block/index.tsx +++ b/app/components/markdown/markdown_code_block/index.tsx @@ -5,8 +5,7 @@ import {useManagedConfig} from '@mattermost/react-native-emm'; import Clipboard from '@react-native-community/clipboard'; import React, {useCallback} from 'react'; import {useIntl} from 'react-intl'; -import {Keyboard, StyleSheet, Text, TextStyle, View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; +import {Keyboard, StyleSheet, Text, TextStyle, TouchableOpacity, View} from 'react-native'; import FormattedText from '@components/formatted_text'; import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item'; diff --git a/app/components/markdown/markdown_image/index.tsx b/app/components/markdown/markdown_image/index.tsx index ef26de640d..8abc65e7d6 100644 --- a/app/components/markdown/markdown_image/index.tsx +++ b/app/components/markdown/markdown_image/index.tsx @@ -5,8 +5,7 @@ import {useManagedConfig} from '@mattermost/react-native-emm'; import Clipboard from '@react-native-community/clipboard'; import React, {useCallback, useMemo, useRef, useState} from 'react'; import {useIntl} from 'react-intl'; -import {Alert, Platform, StyleProp, Text, TextStyle, View} from 'react-native'; -import {LongPressGestureHandler, TapGestureHandler} from 'react-native-gesture-handler'; +import {Alert, Platform, StyleProp, Text, TextStyle, TouchableWithoutFeedback, View} from 'react-native'; import Animated from 'react-native-reanimated'; import {SvgUri} from 'react-native-svg'; import parseUrl from 'url-parse'; @@ -74,8 +73,7 @@ const MarkdownImage = ({ const style = getStyleSheet(theme); const managedConfig = useManagedConfig(); const genericFileId = useRef(generateId('uid')).current; - const tapRef = useRef(); - const metadata = imagesMetadata?.[source] || Object.values(imagesMetadata || {})?.[0]; + const metadata = imagesMetadata?.[source] || Object.values(imagesMetadata || {})[0]; const [failed, setFailed] = useState(isGifTooLarge(metadata)); const originalSize = getMarkdownImageSize(isReplyPost, isTablet, sourceSize, metadata, layoutWidth); const serverUrl = useServerUrl(); @@ -109,11 +107,12 @@ const MarkdownImage = ({ width: originalSize.width, height: originalSize.height, } as FileInfo; - }, [uri, originalSize, metadata, isReplyPost, isTablet]); + }, [originalSize, metadata]); const handlePreviewImage = useCallback(() => { const item: GalleryItemType = { ...fileToGalleryItem(fileInfo), + mime_type: lookupMimeType(fileInfo.name), type: 'image', }; openGalleryAtIndex(galleryIdentifier, 0, [item]); @@ -233,28 +232,43 @@ const MarkdownImage = ({ ); } else { image = ( + + + + + + ); + } + } + + if (image && linkDestination && !disabled) { + image = ( + - - ); - } - } - - if (image && linkDestination && !disabled) { - return ( - - {image} ); } @@ -262,23 +276,7 @@ const MarkdownImage = ({ return ( - - - - - {image} - - - - + {image} ); diff --git a/app/components/markdown/markdown_link/index.ts b/app/components/markdown/markdown_link/index.ts new file mode 100644 index 0000000000..7df6fdf40c --- /dev/null +++ b/app/components/markdown/markdown_link/index.ts @@ -0,0 +1,35 @@ +// 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 {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; + +import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; + +import MarkdownLink from './markdown_link'; + +import type {WithDatabaseArgs} from '@typings/database/database'; +import type SystemModel from '@typings/database/models/servers/system'; + +type ConfigValue = { + value: ClientConfig; +} + +const enhance = withObservables([], ({database}: WithDatabaseArgs) => { + const config = database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); + const experimentalNormalizeMarkdownLinks = config.pipe( + switchMap(({value}: ConfigValue) => of$(value.ExperimentalNormalizeMarkdownLinks)), + ); + const siteURL = config.pipe( + switchMap(({value}: ConfigValue) => of$(value.SiteURL)), + ); + + return { + experimentalNormalizeMarkdownLinks, + siteURL, + }; +}); + +export default withDatabase(enhance(MarkdownLink)); diff --git a/app/components/markdown/markdown_link/index.tsx b/app/components/markdown/markdown_link/markdown_link.tsx similarity index 82% rename from app/components/markdown/markdown_link/index.tsx rename to app/components/markdown/markdown_link/markdown_link.tsx index a1b315046e..abd7e495b7 100644 --- a/app/components/markdown/markdown_link/index.tsx +++ b/app/components/markdown/markdown_link/markdown_link.tsx @@ -2,21 +2,15 @@ // See LICENSE.txt for license information. import {useManagedConfig} from '@mattermost/react-native-emm'; -import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; -import withObservables from '@nozbe/with-observables'; import Clipboard from '@react-native-community/clipboard'; import React, {Children, ReactElement, useCallback} from 'react'; import {useIntl} from 'react-intl'; import {Alert, StyleSheet, Text, View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; import urlParse from 'url-parse'; import {showPermalink} from '@actions/local/permalink'; import {switchToChannelByName} from '@actions/remote/channel'; import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import DeepLinkTypes from '@constants/deep_linking'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; @@ -25,8 +19,6 @@ import {errorBadChannel} from '@utils/draft'; import {preventDoubleTap} from '@utils/tap'; import {matchDeepLink, normalizeProtocol, tryOpenURL} from '@utils/url'; -import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; import type {DeepLinkChannel, DeepLinkPermalink, DeepLinkWithData} from '@typings/launch'; type MarkdownLinkProps = { @@ -165,34 +157,13 @@ const MarkdownLink = ({children, experimentalNormalizeMarkdownLinks, href, siteU const renderChildren = experimentalNormalizeMarkdownLinks ? parseChildren() : children; return ( - - - {renderChildren} - - + {renderChildren} + ); }; -type ConfigValue = { - value: ClientConfig; -} - -const withConfigValues = withObservables([], ({database}: WithDatabaseArgs) => { - const config = database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG); - const experimentalNormalizeMarkdownLinks = config.pipe( - switchMap(({value}: ConfigValue) => of$(value.ExperimentalNormalizeMarkdownLinks)), - ); - const siteURL = config.pipe( - switchMap(({value}: ConfigValue) => of$(value.SiteURL)), - ); - - return { - experimentalNormalizeMarkdownLinks, - siteURL, - }; -}); - -export default withDatabase(withConfigValues(MarkdownLink)); +export default MarkdownLink; diff --git a/app/components/markdown/markdown_table/index.tsx b/app/components/markdown/markdown_table/index.tsx index fd6b82b2ff..d733e73d99 100644 --- a/app/components/markdown/markdown_table/index.tsx +++ b/app/components/markdown/markdown_table/index.tsx @@ -3,8 +3,7 @@ import React, {PureComponent, ReactNode} from 'react'; import {injectIntl, IntlShape} from 'react-intl'; -import {Dimensions, EventSubscription, LayoutChangeEvent, Platform, ScaledSize, ScrollView, View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; +import {Dimensions, EventSubscription, LayoutChangeEvent, Platform, ScaledSize, ScrollView, TouchableOpacity, View} from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import CompassIcon from '@components/compass_icon'; @@ -34,6 +33,80 @@ type MarkdownTableProps = MarkdownTableInputProps & { theme: Theme; } +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return { + container: { + maxHeight: MAX_HEIGHT, + }, + expandButton: { + height: 34, + width: 34, + }, + iconContainer: { + maxWidth: '100%', + alignItems: 'flex-end', + paddingTop: 8, + paddingBottom: 4, + ...Platform.select({ + ios: { + paddingRight: 14, + }, + }), + }, + iconButton: { + backgroundColor: theme.centerChannelBg, + marginTop: -32, + marginRight: -6, + borderWidth: 1, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + borderRadius: 50, + borderColor: changeOpacity(theme.centerChannelColor, 0.2), + width: 34, + height: 34, + }, + icon: { + fontSize: 14, + color: theme.linkColor, + ...Platform.select({ + ios: { + fontSize: 13, + }, + }), + }, + displayFlex: { + flex: 1, + }, + table: { + width: '100%', + borderColor: changeOpacity(theme.centerChannelColor, 0.2), + borderWidth: 1, + }, + tablePadding: { + paddingRight: 10, + }, + moreBelow: { + bottom: Platform.select({ + ios: 34, + android: 33.75, + }), + height: 20, + position: 'absolute', + left: 0, + borderColor: changeOpacity(theme.centerChannelColor, 0.2), + }, + moreRight: { + maxHeight: MAX_HEIGHT, + position: 'absolute', + top: 0, + width: 20, + borderColor: changeOpacity(theme.centerChannelColor, 0.2), + borderRightWidth: 1, + }, + }; +}); + class MarkdownTable extends PureComponent { private rowsSliced: boolean | undefined; private colsSliced: boolean | undefined; @@ -284,78 +357,4 @@ class MarkdownTable extends PureComponent { - return { - container: { - maxHeight: MAX_HEIGHT, - }, - expandButton: { - height: 34, - width: 34, - }, - iconContainer: { - maxWidth: '100%', - alignItems: 'flex-end', - paddingTop: 8, - paddingBottom: 4, - ...Platform.select({ - ios: { - paddingRight: 14, - }, - }), - }, - iconButton: { - backgroundColor: theme.centerChannelBg, - marginTop: -32, - marginRight: -6, - borderWidth: 1, - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - borderRadius: 50, - borderColor: changeOpacity(theme.centerChannelColor, 0.2), - width: 34, - height: 34, - }, - icon: { - fontSize: 14, - color: theme.linkColor, - ...Platform.select({ - ios: { - fontSize: 13, - }, - }), - }, - displayFlex: { - flex: 1, - }, - table: { - width: '100%', - borderColor: changeOpacity(theme.centerChannelColor, 0.2), - borderWidth: 1, - }, - tablePadding: { - paddingRight: 10, - }, - moreBelow: { - bottom: Platform.select({ - ios: 34, - android: 33.75, - }), - height: 20, - position: 'absolute', - left: 0, - borderColor: changeOpacity(theme.centerChannelColor, 0.2), - }, - moreRight: { - maxHeight: MAX_HEIGHT, - position: 'absolute', - top: 0, - width: 20, - borderColor: changeOpacity(theme.centerChannelColor, 0.2), - borderRightWidth: 1, - }, - }; -}); - export default injectIntl(MarkdownTable); diff --git a/app/components/markdown/markdown_table_image/index.tsx b/app/components/markdown/markdown_table_image/index.tsx index d6626e8cce..505d82eeee 100644 --- a/app/components/markdown/markdown_table_image/index.tsx +++ b/app/components/markdown/markdown_table_image/index.tsx @@ -2,8 +2,7 @@ // See LICENSE.txt for license information. import React, {memo, useCallback, useRef, useState} from 'react'; -import {StyleSheet, View} from 'react-native'; -import {TapGestureHandler} from 'react-native-gesture-handler'; +import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native'; import Animated from 'react-native-reanimated'; import parseUrl from 'url-parse'; @@ -110,9 +109,9 @@ const MarkTableImage = ({disabled, imagesMetadata, location, postId, serverURL, } else { const {height, width} = calculateDimensions(metadata.height, metadata.width, 100, 100); image = ( - - + ); } diff --git a/app/components/post_draft/uploads/upload_item/index.tsx b/app/components/post_draft/uploads/upload_item/index.tsx index 0d98b2f4d1..34041a0751 100644 --- a/app/components/post_draft/uploads/upload_item/index.tsx +++ b/app/components/post_draft/uploads/upload_item/index.tsx @@ -2,8 +2,7 @@ // See LICENSE.txt for license information. import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import {StyleSheet, View} from 'react-native'; -import {TapGestureHandler} from 'react-native-gesture-handler'; +import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native'; import Animated from 'react-native-reanimated'; import {updateDraftFile} from '@actions/local/draft'; @@ -131,11 +130,11 @@ export default function UploadItem({ style={style.preview} > - + {filePreviewComponent} - + {file.failed && - @@ -226,7 +226,7 @@ const CombinedUserActivity = ({ - + ); }; diff --git a/app/components/post_list/combined_user_activity/last_users.tsx b/app/components/post_list/combined_user_activity/last_users.tsx index 39b0c35455..65c0bdbd32 100644 --- a/app/components/post_list/combined_user_activity/last_users.tsx +++ b/app/components/post_list/combined_user_activity/last_users.tsx @@ -3,8 +3,7 @@ import React, {useState} from 'react'; import {useIntl} from 'react-intl'; -import {Platform, Text} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; +import {Text} from 'react-native'; import FormattedMarkdownText from '@components/formatted_markdown_text'; import FormattedText from '@components/formatted_text'; @@ -33,9 +32,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { fontSize: 16, lineHeight: 20, }, - touchableStyle: { - top: Platform.select({ios: 3, default: 5}), - }, }; }); @@ -83,18 +79,16 @@ const LastUsers = ({actor, postType, theme, usernames}: LastUsersProps) => { textStyles={textStyles} /> {' '} - - - - - + + { - const enablePostIconOverride = database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap((cfg) => of$(cfg.value.EnablePostIconOverride === 'true')), - ); - - return { - author: post.author.observe(), - enablePostIconOverride, - }; -}); - -export default withDatabase(withPost(Avatar)); +export default Avatar; diff --git a/app/components/post_list/post/avatar/index.ts b/app/components/post_list/post/avatar/index.ts new file mode 100644 index 0000000000..777d28d666 --- /dev/null +++ b/app/components/post_list/post/avatar/index.ts @@ -0,0 +1,28 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; +import enhance from '@nozbe/with-observables'; +import {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; + +import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; + +import Avatar from './avatar'; + +import type {WithDatabaseArgs} from '@typings/database/database'; +import type PostModel from '@typings/database/models/servers/post'; +import type SystemModel from '@typings/database/models/servers/system'; + +const withPost = enhance(['post'], ({database, post}: {post: PostModel} & WithDatabaseArgs) => { + const enablePostIconOverride = database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( + switchMap((cfg) => of$(cfg.value.EnablePostIconOverride === 'true')), + ); + + return { + author: post.author.observe(), + enablePostIconOverride, + }; +}); + +export default withDatabase(withPost(Avatar)); diff --git a/app/components/post_list/post/body/add_members/index.tsx b/app/components/post_list/post/body/add_members/add_members.tsx similarity index 85% rename from app/components/post_list/post/body/add_members/index.tsx rename to app/components/post_list/post/body/add_members/add_members.tsx index 06e4580b3c..67d8c81062 100644 --- a/app/components/post_list/post/body/add_members/index.tsx +++ b/app/components/post_list/post/body/add_members/add_members.tsx @@ -1,30 +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, {ReactNode} from 'react'; import {useIntl} from 'react-intl'; import {Text} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; import {removePost, sendAddToChannelEphemeralPost} from '@actions/local/post'; import {addMembersToChannel} from '@actions/remote/channel'; import FormattedText from '@components/formatted_text'; import AtMention from '@components/markdown/at_mention'; import {General} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {useServerUrl} from '@context/server'; import {t} from '@i18n'; import {getMarkdownTextStyles} from '@utils/markdown'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; -import type {WithDatabaseArgs} from '@typings/database/database'; -import type ChannelModel from '@typings/database/models/servers/channel'; import type PostModel from '@typings/database/models/servers/post'; -import type SystemModel from '@typings/database/models/servers/system'; import type UserModel from '@typings/database/models/servers/user'; type AddMembersProps = { @@ -34,8 +25,6 @@ type AddMembersProps = { theme: Theme; } -const {SERVER: {SYSTEM, USER}} = MM_TABLES; - const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { return { message: { @@ -182,14 +171,16 @@ const AddMembers = ({channelType, currentUser, post, theme}: AddMembersProps) => defaultMessage={outOfChannelMessageText} style={styles.message} /> - + - + ); }; -const withChannelType = withObservables(['post'], ({database, post}: WithDatabaseArgs & {post: PostModel}) => ({ - currentUser: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( - switchMap(({value}) => database.get(USER).findAndObserve(value)), - ), - channelType: post.channel.observe().pipe( - switchMap( - (channel: ChannelModel) => (channel ? of$(channel.type) : of$(null)), - ), - ), -})); - -export default withDatabase(withChannelType(AddMembers)); +export default AddMembers; diff --git a/app/components/post_list/post/body/add_members/index.ts b/app/components/post_list/post/body/add_members/index.ts new file mode 100644 index 0000000000..cf3d0d026d --- /dev/null +++ b/app/components/post_list/post/body/add_members/index.ts @@ -0,0 +1,31 @@ +// 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 {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; + +import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; + +import AddMembers from './add_members'; + +import type {WithDatabaseArgs} from '@typings/database/database'; +import type ChannelModel from '@typings/database/models/servers/channel'; +import type PostModel from '@typings/database/models/servers/post'; +import type SystemModel from '@typings/database/models/servers/system'; + +const {SERVER: {SYSTEM, USER}} = MM_TABLES; + +const enhance = withObservables(['post'], ({database, post}: WithDatabaseArgs & {post: PostModel}) => ({ + currentUser: database.get(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID).pipe( + switchMap(({value}) => database.get(USER).findAndObserve(value)), + ), + channelType: post.channel.observe().pipe( + switchMap( + (channel: ChannelModel) => (channel ? of$(channel.type) : of$(null)), + ), + ), +})); + +export default withDatabase(enhance(AddMembers)); diff --git a/app/components/post_list/post/body/content/image_preview/index.tsx b/app/components/post_list/post/body/content/image_preview/image_preview.tsx similarity index 77% rename from app/components/post_list/post/body/content/image_preview/index.tsx rename to app/components/post_list/post/body/content/image_preview/image_preview.tsx index 598f518a91..0092e89e66 100644 --- a/app/components/post_list/post/body/content/image_preview/index.tsx +++ b/app/components/post_list/post/body/content/image_preview/image_preview.tsx @@ -1,19 +1,12 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; -import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; -import withObservables from '@nozbe/with-observables'; import React, {useCallback, useEffect, useRef, useState} from 'react'; -import {Animated, StyleSheet, View} from 'react-native'; -import {TapGestureHandler} from 'react-native-gesture-handler'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; +import {Animated, StyleSheet, TouchableWithoutFeedback, View} from 'react-native'; import {getRedirectLocation} from '@actions/remote/general'; import FileIcon from '@components/post_list/post/body/files/file_icon'; import ProgressiveImage from '@components/progressive_image'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {GalleryInit} from '@context/gallery'; import {useServerUrl} from '@context/server'; import {useIsTablet} from '@hooks/device'; @@ -26,9 +19,6 @@ import {calculateDimensions, getViewPortWidth, isGifTooLarge} from '@utils/image import {changeOpacity} from '@utils/theme'; import {extractFilenameFromUrl, isImageLink, isValidUrl} from '@utils/url'; -import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; - type ImagePreviewProps = { expandedLink?: string; isReplyPost: boolean; @@ -124,7 +114,7 @@ const ImagePreview = ({expandedLink, isReplyPost, layoutWidth, link, location, m return ( - + - + ); }; -const withExpandedLink = withObservables(['metadata'], ({database, metadata}: WithDatabaseArgs & {metadata: PostMetadata}) => { - const link = metadata.embeds?.[0].url; - - return { - expandedLink: database.get(MM_TABLES.SERVER.SYSTEM).query( - Q.where('id', SYSTEM_IDENTIFIERS.EXPANDED_LINKS), - ).observe().pipe( - switchMap((values: SystemModel[]) => ( - (link && values.length) ? of$((values[0].value as Record)[link]) : of$(undefined)), - ), - ), - link: of$(link), - }; -}); - -export default withDatabase(withExpandedLink(React.memo(ImagePreview))); +export default React.memo(ImagePreview); diff --git a/app/components/post_list/post/body/content/image_preview/index.ts b/app/components/post_list/post/body/content/image_preview/index.ts new file mode 100644 index 0000000000..b4729240a1 --- /dev/null +++ b/app/components/post_list/post/body/content/image_preview/index.ts @@ -0,0 +1,32 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Q} from '@nozbe/watermelondb'; +import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; +import withObservables from '@nozbe/with-observables'; +import {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; + +import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; + +import ImagePreview from './image_preview'; + +import type {WithDatabaseArgs} from '@typings/database/database'; +import type SystemModel from '@typings/database/models/servers/system'; + +const enhance = withObservables(['metadata'], ({database, metadata}: WithDatabaseArgs & {metadata: PostMetadata}) => { + const link = metadata.embeds?.[0].url; + + return { + expandedLink: database.get(MM_TABLES.SERVER.SYSTEM).query( + Q.where('id', SYSTEM_IDENTIFIERS.EXPANDED_LINKS), + ).observe().pipe( + switchMap((values: SystemModel[]) => ( + (link && values.length) ? of$((values[0].value as Record)[link]) : of$(undefined)), + ), + ), + link: of$(link), + }; +}); + +export default withDatabase(enhance(ImagePreview)); diff --git a/app/components/post_list/post/body/content/message_attachments/attachment_author.tsx b/app/components/post_list/post/body/content/message_attachments/attachment_author.tsx index f80da28ee8..8191e3352e 100644 --- a/app/components/post_list/post/body/content/message_attachments/attachment_author.tsx +++ b/app/components/post_list/post/body/content/message_attachments/attachment_author.tsx @@ -5,7 +5,6 @@ import React from 'react'; import {useIntl} from 'react-intl'; import {Alert, Text, View} from 'react-native'; import FastImage from 'react-native-fast-image'; -import {TouchableOpacity} from 'react-native-gesture-handler'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {tryOpenURL} from '@utils/url'; @@ -69,14 +68,13 @@ const AttachmentAuthor = ({icon, link, name, theme}: Props) => { /> } {Boolean(name) && - - - {name} - - + + {name} + } ); diff --git a/app/components/post_list/post/body/content/message_attachments/attachment_image/index.tsx b/app/components/post_list/post/body/content/message_attachments/attachment_image/index.tsx index 85bb9be705..e4c0f1d9b1 100644 --- a/app/components/post_list/post/body/content/message_attachments/attachment_image/index.tsx +++ b/app/components/post_list/post/body/content/message_attachments/attachment_image/index.tsx @@ -2,8 +2,7 @@ // See LICENSE.txt for license information. import React, {useCallback, useRef, useState} from 'react'; -import {View} from 'react-native'; -import {TapGestureHandler} from 'react-native-gesture-handler'; +import {TouchableWithoutFeedback, View} from 'react-native'; import Animated from 'react-native-reanimated'; import FileIcon from '@components/post_list/post/body/files/file_icon'; @@ -98,7 +97,7 @@ const AttachmentImage = ({imageUrl, imageMetadata, layoutWidth, location, postId return ( - + - + ); diff --git a/app/components/post_list/post/body/content/message_attachments/attachment_title.tsx b/app/components/post_list/post/body/content/message_attachments/attachment_title.tsx index 723fdff9a5..85e7bf225c 100644 --- a/app/components/post_list/post/body/content/message_attachments/attachment_title.tsx +++ b/app/components/post_list/post/body/content/message_attachments/attachment_title.tsx @@ -4,7 +4,6 @@ import React from 'react'; import {useIntl} from 'react-intl'; import {Alert, Text, View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; import Markdown from '@components/markdown'; import {makeStyleSheetFromTheme} from '@utils/theme'; @@ -60,11 +59,12 @@ const AttachmentTitle = ({link, theme, value}: Props) => { let title; if (link) { title = ( - - - {value} - - + + {value} + ); } else { title = ( diff --git a/app/components/post_list/post/body/content/opengraph/index.ts b/app/components/post_list/post/body/content/opengraph/index.ts new file mode 100644 index 0000000000..b38d0c9f0c --- /dev/null +++ b/app/components/post_list/post/body/content/opengraph/index.ts @@ -0,0 +1,47 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Q} from '@nozbe/watermelondb'; +import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; +import withObservables from '@nozbe/with-observables'; +import {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; + +import {Preferences} from '@constants'; +import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; +import {getPreferenceAsBool} from '@helpers/api/preference'; + +import Opengraph from './opengraph'; + +import type {WithDatabaseArgs} from '@typings/database/database'; +import type PreferenceModel from '@typings/database/models/servers/preference'; +import type SystemModel from '@typings/database/models/servers/system'; + +const enhance = withObservables( + ['removeLinkPreview'], + ({database, removeLinkPreview}: WithDatabaseArgs & {removeLinkPreview: boolean}) => { + if (removeLinkPreview) { + return {showLinkPreviews: of$(false)}; + } + + const showLinkPreviews = database.get(MM_TABLES.SERVER.PREFERENCE).query( + Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS), + Q.where('name', Preferences.LINK_PREVIEW_DISPLAY), + ).observe().pipe( + switchMap( + (preferences: PreferenceModel[]) => database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( + // eslint-disable-next-line max-nested-callbacks + switchMap((config: SystemModel) => { + const cfg: ClientConfig = config.value; + const previewsEnabled = getPreferenceAsBool(preferences, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.LINK_PREVIEW_DISPLAY, true); + return of$(previewsEnabled && cfg.EnableLinkPreviews === 'true'); + }), + ), + ), + ); + + return {showLinkPreviews}; + }, +); + +export default withDatabase(enhance(Opengraph)); diff --git a/app/components/post_list/post/body/content/opengraph/index.tsx b/app/components/post_list/post/body/content/opengraph/opengraph.tsx similarity index 69% rename from app/components/post_list/post/body/content/opengraph/index.tsx rename to app/components/post_list/post/body/content/opengraph/opengraph.tsx index 3eeb887683..d977b77314 100644 --- a/app/components/post_list/post/body/content/opengraph/index.tsx +++ b/app/components/post_list/post/body/content/opengraph/opengraph.tsx @@ -1,28 +1,15 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Q} from '@nozbe/watermelondb'; -import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider'; -import withObservables from '@nozbe/with-observables'; import React from 'react'; import {useIntl} from 'react-intl'; -import {Alert, Text, View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; +import {Alert, Text, TouchableOpacity, View} from 'react-native'; -import {Preferences} from '@constants'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; -import {getPreferenceAsBool} from '@helpers/api/preference'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {tryOpenURL} from '@utils/url'; import OpengraphImage from './opengraph_image'; -import type {WithDatabaseArgs} from '@typings/database/database'; -import type PreferenceModel from '@typings/database/models/servers/preference'; -import type SystemModel from '@typings/database/models/servers/system'; - type OpengraphProps = { isReplyPost: boolean; layoutWidth?: number; @@ -176,29 +163,4 @@ const Opengraph = ({isReplyPost, layoutWidth, location, metadata, postId, showLi ); }; -const enhanced = withObservables( - ['removeLinkPreview'], ({database, removeLinkPreview}: WithDatabaseArgs & {removeLinkPreview: boolean}) => { - if (removeLinkPreview) { - return {showLinkPreviews: of$(false)}; - } - - const showLinkPreviews = database.get(MM_TABLES.SERVER.PREFERENCE).query( - Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS), - Q.where('name', Preferences.LINK_PREVIEW_DISPLAY), - ).observe().pipe( - switchMap( - (preferences: PreferenceModel[]) => database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - // eslint-disable-next-line max-nested-callbacks - switchMap((config: SystemModel) => { - const cfg: ClientConfig = config.value; - const previewsEnabled = getPreferenceAsBool(preferences, Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.LINK_PREVIEW_DISPLAY, true); - return of$(previewsEnabled && cfg.EnableLinkPreviews === 'true'); - }), - ), - ), - ); - - return {showLinkPreviews}; - }); - -export default withDatabase(enhanced(React.memo(Opengraph))); +export default React.memo(Opengraph); diff --git a/app/components/post_list/post/body/content/opengraph/opengraph_image/index.tsx b/app/components/post_list/post/body/content/opengraph/opengraph_image/index.tsx index c3e55bf9b7..01a0779db9 100644 --- a/app/components/post_list/post/body/content/opengraph/opengraph_image/index.tsx +++ b/app/components/post_list/post/body/content/opengraph/opengraph_image/index.tsx @@ -2,9 +2,8 @@ // See LICENSE.txt for license information. import React, {useMemo, useRef} from 'react'; -import {useWindowDimensions} from 'react-native'; +import {TouchableWithoutFeedback, useWindowDimensions} from 'react-native'; import FastImage, {Source} from 'react-native-fast-image'; -import {TapGestureHandler} from 'react-native-gesture-handler'; import Animated from 'react-native-reanimated'; import {Device as DeviceConstant, View as ViewConstants} from '@constants'; @@ -118,7 +117,7 @@ const OpengraphImage = ({isReplyPost, layoutWidth, location, metadata, openGraph return ( - + - + ); diff --git a/app/components/post_list/post/body/content/youtube/index.ts b/app/components/post_list/post/body/content/youtube/index.ts new file mode 100644 index 0000000000..99111d1ad6 --- /dev/null +++ b/app/components/post_list/post/body/content/youtube/index.ts @@ -0,0 +1,24 @@ +// 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 {of as of$} from 'rxjs'; +import {switchMap} from 'rxjs/operators'; + +import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; + +import YouTube from './youtube'; + +import type {WithDatabaseArgs} from '@typings/database/database'; +import type SystemModel from '@typings/database/models/servers/system'; + +const enhance = withObservables([], ({database}: WithDatabaseArgs) => ({ + googleDeveloperKey: database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( + switchMap(({value}: {value: ClientConfig}) => { + return of$(value.GoogleDeveloperKey); + }), + ), +})); + +export default withDatabase(enhance(YouTube)); diff --git a/app/components/post_list/post/body/content/youtube/index.tsx b/app/components/post_list/post/body/content/youtube/youtube.tsx similarity index 84% rename from app/components/post_list/post/body/content/youtube/index.tsx rename to app/components/post_list/post/body/content/youtube/youtube.tsx index fa2072e475..7c9b2095af 100644 --- a/app/components/post_list/post/body/content/youtube/index.tsx +++ b/app/components/post_list/post/body/content/youtube/youtube.tsx @@ -1,26 +1,17 @@ // 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, {useCallback} from 'react'; import {useIntl} from 'react-intl'; -import {Alert, Image, Platform, StatusBar, StyleSheet, View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; +import {Alert, Image, Platform, StatusBar, StyleSheet, TouchableOpacity, View} from 'react-native'; import {YouTubeStandaloneAndroid, YouTubeStandaloneIOS} from 'react-native-youtube'; -import {of as of$} from 'rxjs'; -import {switchMap} from 'rxjs/operators'; import ProgressiveImage from '@components/progressive_image'; -import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {useIsTablet} from '@hooks/device'; import {emptyFunction} from '@utils/general'; import {calculateDimensions, getViewPortWidth} from '@utils/images'; import {getYouTubeVideoId, tryOpenURL} from '@utils/url'; -import type {WithDatabaseArgs} from '@typings/database/database'; -import type SystemModel from '@typings/database/models/servers/system'; - type YouTubeProps = { googleDeveloperKey?: string; isReplyPost: boolean; @@ -178,12 +169,4 @@ const YouTube = ({googleDeveloperKey, isReplyPost, layoutWidth, metadata}: YouTu ); }; -const withGoogleKey = withObservables([], ({database}: WithDatabaseArgs) => ({ - googleDeveloperKey: database.get(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe( - switchMap(({value}: {value: ClientConfig}) => { - return of$(value.GoogleDeveloperKey); - }), - ), -})); - -export default withDatabase(withGoogleKey(React.memo(YouTube))); +export default React.memo(YouTube); diff --git a/app/components/post_list/post/body/failed/index.tsx b/app/components/post_list/post/body/failed/index.tsx index 3a6f897904..82aa1194da 100644 --- a/app/components/post_list/post/body/failed/index.tsx +++ b/app/components/post_list/post/body/failed/index.tsx @@ -3,8 +3,7 @@ import React, {useCallback} from 'react'; import {useIntl} from 'react-intl'; -import {StyleSheet, View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; +import {StyleSheet, TouchableOpacity, View} from 'react-native'; import {removePost} from '@actions/local/post'; import CompassIcon from '@components/compass_icon'; diff --git a/app/components/post_list/post/body/files/document_file.tsx b/app/components/post_list/post/body/files/document_file.tsx index 082c541295..e6368f1483 100644 --- a/app/components/post_list/post/body/files/document_file.tsx +++ b/app/components/post_list/post/body/files/document_file.tsx @@ -5,9 +5,8 @@ import {ClientResponse, ProgressPromise} from '@mattermost/react-native-network- import * as FileSystem from 'expo-file-system'; import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react'; import {useIntl} from 'react-intl'; -import {Platform, StatusBar, StatusBarStyle, StyleSheet, View} from 'react-native'; +import {Platform, StatusBar, StatusBarStyle, StyleSheet, TouchableOpacity, View} from 'react-native'; import FileViewer from 'react-native-file-viewer'; -import {TouchableOpacity} from 'react-native-gesture-handler'; import tinyColor from 'tinycolor2'; import ProgressBar from '@components/progress_bar'; diff --git a/app/components/post_list/post/body/files/file.tsx b/app/components/post_list/post/body/files/file.tsx index 5d54974845..848cd51646 100644 --- a/app/components/post_list/post/body/files/file.tsx +++ b/app/components/post_list/post/body/files/file.tsx @@ -2,8 +2,7 @@ // See LICENSE.txt for license information. import React, {useCallback, useRef} from 'react'; -import {View} from 'react-native'; -import {TapGestureHandler} from 'react-native-gesture-handler'; +import {View, TouchableWithoutFeedback} from 'react-native'; import Animated from 'react-native-reanimated'; import TouchableWithFeedback from '@components/touchable_with_feedback'; @@ -71,10 +70,7 @@ const File = ({ if (isVideo(file) && publicLinkEnabled) { return ( - + } - + ); } if (isImage(file)) { return ( - + } - + ); } diff --git a/app/components/post_list/post/body/files/file_info.tsx b/app/components/post_list/post/body/files/file_info.tsx index 240b88ce65..10ce698650 100644 --- a/app/components/post_list/post/body/files/file_info.tsx +++ b/app/components/post_list/post/body/files/file_info.tsx @@ -2,8 +2,7 @@ // See LICENSE.txt for license information. import React from 'react'; -import {Text, View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; +import {Text, TouchableOpacity, View} from 'react-native'; import {getFormattedFileSize} from '@utils/file'; import {makeStyleSheetFromTheme} from '@utils/theme'; diff --git a/app/components/post_list/post/body/message/show_more_button.tsx b/app/components/post_list/post/body/message/show_more_button.tsx index 91e93d82cc..439ed3f4bc 100644 --- a/app/components/post_list/post/body/message/show_more_button.tsx +++ b/app/components/post_list/post/body/message/show_more_button.tsx @@ -2,8 +2,7 @@ // See LICENSE.txt for license information. import React from 'react'; -import {View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; +import {TouchableOpacity, View} from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import CompassIcon from '@components/compass_icon'; diff --git a/app/components/post_list/post/body/reactions/reaction.tsx b/app/components/post_list/post/body/reactions/reaction.tsx index cf3dac1fee..1be845a4dd 100644 --- a/app/components/post_list/post/body/reactions/reaction.tsx +++ b/app/components/post_list/post/body/reactions/reaction.tsx @@ -2,9 +2,8 @@ // See LICENSE.txt for license information. import React, {useCallback} from 'react'; -import {View} from 'react-native'; +import {TouchableOpacity, View} from 'react-native'; import AnimatedNumbers from 'react-native-animated-numbers'; -import {TouchableOpacity} from 'react-native-gesture-handler'; import Emoji from '@components/emoji'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; diff --git a/app/components/post_list/post/body/reactions/reactions.tsx b/app/components/post_list/post/body/reactions/reactions.tsx index 134d409fe8..f12619100b 100644 --- a/app/components/post_list/post/body/reactions/reactions.tsx +++ b/app/components/post_list/post/body/reactions/reactions.tsx @@ -3,8 +3,7 @@ import React, {useCallback, useEffect, useRef, useState} from 'react'; import {useIntl} from 'react-intl'; -import {View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; +import {TouchableOpacity, View} from 'react-native'; import {addReaction, removeReaction} from '@actions/remote/reactions'; import CompassIcon from '@components/compass_icon'; diff --git a/app/components/post_list/post/header/display_name/index.tsx b/app/components/post_list/post/header/display_name/index.tsx index 96b72a8546..b52ee8a038 100644 --- a/app/components/post_list/post/header/display_name/index.tsx +++ b/app/components/post_list/post/header/display_name/index.tsx @@ -3,8 +3,7 @@ import React, {useCallback, useRef} from 'react'; import {useIntl} from 'react-intl'; -import {Keyboard, Text, useWindowDimensions, View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; +import {Keyboard, Text, TouchableOpacity, useWindowDimensions, View} from 'react-native'; import CompassIcon from '@components/compass_icon'; import FormattedText from '@components/formatted_text'; diff --git a/app/components/post_list/post/header/reply/index.tsx b/app/components/post_list/post/header/reply/index.tsx index a10df239fd..ee74d16fce 100644 --- a/app/components/post_list/post/header/reply/index.tsx +++ b/app/components/post_list/post/header/reply/index.tsx @@ -2,8 +2,7 @@ // See LICENSE.txt for license information. import React, {useCallback} from 'react'; -import {Text, View} from 'react-native'; -import {TouchableOpacity} from 'react-native-gesture-handler'; +import {Text, TouchableOpacity, View} from 'react-native'; import {fetchAndSwitchToThread} from '@actions/remote/thread'; import CompassIcon from '@components/compass_icon'; diff --git a/app/components/post_list/post/post.tsx b/app/components/post_list/post/post.tsx index 026e701b1b..f0ea92c9a8 100644 --- a/app/components/post_list/post/post.tsx +++ b/app/components/post_list/post/post.tsx @@ -3,8 +3,7 @@ import React, {ReactNode, useMemo, useRef} from 'react'; import {useIntl} from 'react-intl'; -import {Keyboard, Platform, StyleProp, View, ViewStyle} from 'react-native'; -import {TouchableHighlight} from 'react-native-gesture-handler'; +import {Keyboard, Platform, StyleProp, View, ViewStyle, TouchableHighlight} from 'react-native'; import {showPermalink} from '@actions/local/permalink'; import {removePost} from '@actions/local/post'; diff --git a/app/components/post_list/post/system_message/__snapshots__/system_message_helpers.test.js.snap b/app/components/post_list/post/system_message/__snapshots__/system_message_helpers.test.js.snap index 604a2c23d2..a8d93ce4fc 100644 --- a/app/components/post_list/post/system_message/__snapshots__/system_message_helpers.test.js.snap +++ b/app/components/post_list/post/system_message/__snapshots__/system_message_helpers.test.js.snap @@ -3,33 +3,23 @@ exports[`renderSystemMessage uses renderer for Channel Display Name update 1`] = ` - - + "alignItems": "flex-start", + "flexDirection": "row", + "flexWrap": "wrap", + }, + ] + } + > + - - - - updated the channel display name from: old displayname to: new displayname - + + updated the channel display name from: old displayname to: new displayname + + + `; exports[`renderSystemMessage uses renderer for Channel Header update 1`] = ` - - + "alignItems": "flex-start", + "flexDirection": "row", + "flexWrap": "wrap", + }, + ] + } + > + - - - - updated the channel header from: old header to: new header - + + updated the channel header from: old header to: new header + + + `; @@ -161,33 +141,23 @@ exports[`renderSystemMessage uses renderer for Channel Purpose update 1`] = ` exports[`renderSystemMessage uses renderer for Guest added and join to channel 1`] = ` - - + "alignItems": "flex-start", + "flexDirection": "row", + "flexWrap": "wrap", + }, + ] + } + > + - - - - joined the channel as a guest. - + + joined the channel as a guest. + + + `; exports[`renderSystemMessage uses renderer for Guest added and join to channel 2`] = ` - - + "alignItems": "flex-start", + "flexDirection": "row", + "flexWrap": "wrap", + }, + ] + } + > + - - - - added to the channel as a guest by - - - + testID="markdown_text" + > + added to the channel as a guest by + - - + + `; exports[`renderSystemMessage uses renderer for OLD archived channel without a username 1`] = ` - - archived the channel - + + + archived the channel + + + `; exports[`renderSystemMessage uses renderer for archived channel 1`] = ` - - + "alignItems": "flex-start", + "flexDirection": "row", + "flexWrap": "wrap", + }, + ] + } + > + - - - - archived the channel - + + archived the channel + + + `; exports[`renderSystemMessage uses renderer for unarchived channel 1`] = ` - - + "alignItems": "flex-start", + "flexDirection": "row", + "flexWrap": "wrap", + }, + ] + } + > + - - - - unarchived the channel - + + unarchived the channel + + + `; diff --git a/app/components/post_list/post/system_message/system_message.tsx b/app/components/post_list/post/system_message/system_message.tsx index ad8d5b5e2b..4e30b7c71d 100644 --- a/app/components/post_list/post/system_message/system_message.tsx +++ b/app/components/post_list/post/system_message/system_message.tsx @@ -3,7 +3,7 @@ import React from 'react'; import {IntlShape, useIntl} from 'react-intl'; -import {StyleProp, Text, TextStyle, ViewStyle} from 'react-native'; +import {StyleProp, Text, TextStyle, View, ViewStyle} from 'react-native'; import Markdown from '@components/markdown'; import {Post} from '@constants'; @@ -25,6 +25,7 @@ type SystemMessageProps = { type RenderersProps = SystemMessageProps & { intl: IntlShape; styles: { + containerStyle: StyleProp; messageStyle: StyleProp; textStyles: { [key: string]: TextStyle; @@ -45,6 +46,9 @@ type RenderMessageProps = RenderersProps & { const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { return { + container: { + marginBottom: 5, + }, systemMessage: { color: changeOpacity(theme.centerChannelColor, 0.6), ...typography('Body', 200, 'Regular'), @@ -61,7 +65,7 @@ const renderUsername = (value = '') => { }; const renderMessage = ({styles, intl, localeHolder, theme, values, skipMarkdown = false}: RenderMessageProps) => { - const {messageStyle, textStyles} = styles; + const {containerStyle, messageStyle, textStyles} = styles; if (skipMarkdown) { return ( @@ -72,13 +76,15 @@ const renderMessage = ({styles, intl, localeHolder, theme, values, skipMarkdown } return ( - + + + ); }; @@ -259,7 +265,7 @@ export const SystemMessage = ({post, author}: SystemMessageProps) => { const theme = useTheme(); const style = getStyleSheet(theme); const textStyles = getMarkdownTextStyles(theme); - const styles = {messageStyle: style.systemMessage, textStyles}; + const styles = {messageStyle: style.systemMessage, textStyles, containerStyle: style.container}; const renderer = systemMessageRenderers[post.type]; if (!renderer) { @@ -272,7 +278,6 @@ export const SystemMessage = ({post, author}: SystemMessageProps) => { theme={theme} /> ); - return null; } return renderer({post, author, styles, intl, theme}); diff --git a/app/hooks/gallery.ts b/app/hooks/gallery.ts index a0a69ffc7f..2d9c557831 100644 --- a/app/hooks/gallery.ts +++ b/app/hooks/gallery.ts @@ -10,7 +10,6 @@ import { } from 'react-native-reanimated'; import {useGallery} from '@context/gallery'; -import {measureItem} from '@utils/gallery'; import type {GestureHandlerGestureEvent} from 'react-native-gesture-handler'; @@ -236,20 +235,13 @@ export function useGalleryItem( gallery.registerItem(index, ref); }, []); - const onGestureEvent = useAnimatedGestureHandler({ - onFinish: (_evt, _ctx, isCanceledOrFailed) => { - if (isCanceledOrFailed) { - return; - } + const onGestureEvent = () => { + 'worklet'; - activeIndex.value = index; + activeIndex.value = index; - // measure the images - // width/height and position to animate from it to the full screen one - measureItem(ref, gallery.sharedValues); - runOnJS(onPress)(identifier, index); - }, - }); + runOnJS(onPress)(identifier, index); + }; return { ref, diff --git a/app/screens/gallery/index.tsx b/app/screens/gallery/index.tsx index 7aeb56dffa..923ef48d9a 100644 --- a/app/screens/gallery/index.tsx +++ b/app/screens/gallery/index.tsx @@ -6,10 +6,9 @@ import {NativeModules, useWindowDimensions, Platform} from 'react-native'; import {Navigation} from 'react-native-navigation'; import {Screens} from '@constants'; -import {useTheme} from '@context/theme'; import {useIsTablet} from '@hooks/device'; import {useGalleryControls} from '@hooks/gallery'; -import {dismissModal} from '@screens/navigation'; +import {dismissOverlay} from '@screens/navigation'; import {freezeOtherScreens} from '@utils/gallery'; import Footer from './footer'; @@ -26,7 +25,6 @@ type Props = { const GalleryScreen = ({galleryIdentifier, hideActions, initialIndex, items}: Props) => { const dim = useWindowDimensions(); const isTablet = useIsTablet(); - const theme = useTheme(); const [localIndex, setLocalIndex] = useState(initialIndex); const {setControlsHidden, headerStyles, footerStyles} = useGalleryControls(); const dimensions = useMemo(() => ({width: dim.width, height: dim.height}), [dim.width]); @@ -55,16 +53,7 @@ const GalleryScreen = ({galleryIdentifier, hideActions, initialIndex, items}: Pr } freezeOtherScreens(false); requestAnimationFrame(async () => { - dismissModal({ - componentId: Screens.GALLERY, - layout: { - orientation: isTablet ? undefined : ['portrait'], - }, - statusBar: { - visible: true, - backgroundColor: theme.sidebarBg, - }, - }); + dismissOverlay(Screens.GALLERY); }); }, [isTablet]); diff --git a/app/screens/navigation.ts b/app/screens/navigation.ts index 47aa895782..e69fccc59e 100644 --- a/app/screens/navigation.ts +++ b/app/screens/navigation.ts @@ -609,6 +609,7 @@ export function showOverlay(name: string, passProps = {}, options = {}) { Navigation.showOverlay({ component: { + id: name, name, passProps, options: merge(defaultOptions, options), diff --git a/app/screens/post_options/index.ts b/app/screens/post_options/index.ts index 74999ce95c..0e7c2fe13c 100644 --- a/app/screens/post_options/index.ts +++ b/app/screens/post_options/index.ts @@ -12,6 +12,7 @@ import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database'; import {MAX_ALLOWED_REACTIONS} from '@constants/emoji'; import {isMinimumServerVersion} from '@utils/helpers'; import {isSystemMessage} from '@utils/post'; +import {getPostIdsForCombinedUserActivityPost} from '@utils/post_list'; import {hasPermissionForChannel, hasPermissionForPost} from '@utils/role'; import {isSystemAdmin} from '@utils/user'; @@ -62,7 +63,7 @@ const withPost = withObservables([], ({post, database}: {post: Post | PostModel} let id: string | undefined; let combinedPost: Observable = of$(undefined); if (post.type === Post.POST_TYPES.COMBINED_USER_ACTIVITY && post.props?.system_post_ids) { - const systemPostIds = post.props.system_post_ids as string[]; + const systemPostIds = getPostIdsForCombinedUserActivityPost(post.id); id = systemPostIds?.pop(); combinedPost = of$(post as Post); } diff --git a/app/utils/gallery/index.ts b/app/utils/gallery/index.ts index eb80d1ae7c..eae03c5e43 100644 --- a/app/utils/gallery/index.ts +++ b/app/utils/gallery/index.ts @@ -7,7 +7,7 @@ import {Navigation, Options, OptionsLayout} from 'react-native-navigation'; import {measure} from 'react-native-reanimated'; import {Events, Screens} from '@constants'; -import {showModalOverCurrentContext} from '@screens/navigation'; +import {showOverlay} from '@screens/navigation'; import {isImage, isVideo} from '@utils/file'; import {generateId} from '@utils/general'; @@ -154,7 +154,7 @@ export function openGalleryAtIndex(galleryIdentifier: string, initialIndex: numb Navigation.setDefaultOptions({layout}); NativeModules.MattermostManaged.unlockOrientation(); } - showModalOverCurrentContext(Screens.GALLERY, props, options); + showOverlay(Screens.GALLERY, props, options); setTimeout(() => { freezeOtherScreens(true); diff --git a/app/utils/markdown/index.ts b/app/utils/markdown/index.ts index 76c375f444..a57e35502a 100644 --- a/app/utils/markdown/index.ts +++ b/app/utils/markdown/index.ts @@ -34,7 +34,7 @@ export const getMarkdownTextStyles = makeStyleSheetFromTheme((theme: Theme) => { lineHeight: 25, }, heading1Text: { - paddingBottom: 8, + paddingVertical: 8, }, heading2: { fontFamily: 'OpenSans-Bold', @@ -42,7 +42,7 @@ export const getMarkdownTextStyles = makeStyleSheetFromTheme((theme: Theme) => { lineHeight: 25, }, heading2Text: { - paddingBottom: 8, + paddingVertical: 6, }, heading3: { fontFamily: 'OpenSans-Bold', @@ -50,7 +50,7 @@ export const getMarkdownTextStyles = makeStyleSheetFromTheme((theme: Theme) => { lineHeight: 25, }, heading3Text: { - paddingBottom: 8, + paddingVertical: 6, }, heading4: { fontFamily: 'OpenSans-Bold', @@ -58,7 +58,7 @@ export const getMarkdownTextStyles = makeStyleSheetFromTheme((theme: Theme) => { lineHeight: 25, }, heading4Text: { - paddingBottom: 8, + paddingVertical: 5, }, heading5: { fontFamily: 'OpenSans-Bold', @@ -66,7 +66,7 @@ export const getMarkdownTextStyles = makeStyleSheetFromTheme((theme: Theme) => { lineHeight: 25, }, heading5Text: { - paddingBottom: 8, + paddingVertical: 5, }, heading6: { fontFamily: 'OpenSans-Bold', @@ -74,7 +74,7 @@ export const getMarkdownTextStyles = makeStyleSheetFromTheme((theme: Theme) => { lineHeight: 25, }, heading6Text: { - paddingBottom: 8, + paddingVertical: 4, }, code: { alignSelf: 'center',