forked from Ivasoft/mattermost-mobile
[Gekidou] Markdown and Touchables (#6047)
* Fix markdown formatting and touchable interaction * open gallery as overlay instead of modal * update snapshots * Add missing dependencies to useMemo
This commit is contained in:
@@ -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 <MarkdownLink href={url}>{children}</MarkdownLink>;
|
||||
};
|
||||
|
||||
const renderParagraph = ({children}: {children: ReactElement}) => {
|
||||
return <Text>{children}</Text>;
|
||||
const renderParagraph = ({children, first}: {children: ReactElement; first: boolean}) => {
|
||||
const blockStyle = [styles.block];
|
||||
if (!first) {
|
||||
const blockS = getMarkdownBlockStyles(theme);
|
||||
blockStyle.push(blockS.adjacentParagraph);
|
||||
}
|
||||
|
||||
return (
|
||||
<Text style={blockStyle}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
const renderText = ({context, literal}: {context: string[]; literal: string}) => {
|
||||
|
||||
@@ -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<TextStyle>;
|
||||
touchableStyle?: StyleProp<ViewStyle>;
|
||||
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 (
|
||||
<TouchableOpacity
|
||||
<Text
|
||||
onPress={onPress!}
|
||||
onLongPress={onLongPress}
|
||||
style={touchableStyle}
|
||||
style={styleText}
|
||||
>
|
||||
<Text style={styleText}>
|
||||
<Text style={mentionTextStyle}>
|
||||
{'@' + mention}
|
||||
</Text>
|
||||
{suffixElement}
|
||||
<Text style={mentionTextStyle}>
|
||||
{'@' + mention}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
{suffixElement}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
const withAtMention = withObservables(['mentionName'], ({database, mentionName}: {mentionName: string} & WithDatabaseArgs) => {
|
||||
const config = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG);
|
||||
const license = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.LICENSE);
|
||||
const preferences = database.get<PreferenceModel>(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe();
|
||||
const currentUserId = database.get<SystemModel>(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);
|
||||
52
app/components/markdown/at_mention/index.ts
Normal file
52
app/components/markdown/at_mention/index.ts
Normal file
@@ -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<SystemModel>(SYSTEM).findAndObserve(CONFIG);
|
||||
const license = database.get<SystemModel>(SYSTEM).findAndObserve(LICENSE);
|
||||
const preferences = database.get<PreferenceModel>(PREFERENCE).query(Q.where('category', Preferences.CATEGORY_DISPLAY_SETTINGS)).observe();
|
||||
const currentUserId = database.get<SystemModel>(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));
|
||||
@@ -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<string, {id?: string; display_name: string; name?: string; team_name: string}>;
|
||||
@@ -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 (
|
||||
<TouchableOpacity onPress={handlePress}>
|
||||
<Text style={textStyle}>
|
||||
<Text style={linkStyle}>
|
||||
{`~${channel.display_name}`}
|
||||
</Text>
|
||||
{suffix}
|
||||
<Text style={textStyle}>
|
||||
<Text
|
||||
onPress={handlePress}
|
||||
style={linkStyle}
|
||||
>
|
||||
{`~${channel.display_name}`}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
{suffix}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
const withChannelsForTeam = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const currentTeamId = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID);
|
||||
const currentUserId = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID);
|
||||
const channels = currentTeamId.pipe(
|
||||
switchMap(({value}) => database.get<ChannelModel>(CHANNEL).query(Q.where('team_id', value)).observeWithColumns(['display_name'])),
|
||||
);
|
||||
const team = currentTeamId.pipe(
|
||||
switchMap(({value}) => database.get<TeamModel>(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;
|
||||
40
app/components/markdown/channel_mention/index.ts
Normal file
40
app/components/markdown/channel_mention/index.ts
Normal file
@@ -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<string, {id?: string; display_name: string; name?: string; team_name: string}>;
|
||||
|
||||
const {SERVER: {CHANNEL, SYSTEM, TEAM}} = MM_TABLES;
|
||||
|
||||
const enhance = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const currentTeamId = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_TEAM_ID);
|
||||
const currentUserId = database.get<SystemModel>(SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CURRENT_USER_ID);
|
||||
const channels = currentTeamId.pipe(
|
||||
switchMap(({value}) => database.get<ChannelModel>(CHANNEL).query(Q.where('team_id', value)).observeWithColumns(['display_name'])),
|
||||
);
|
||||
const team = currentTeamId.pipe(
|
||||
switchMap(({value}) => database.get<TeamModel>(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));
|
||||
@@ -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 (
|
||||
<TouchableOpacity onPress={handlePress}>
|
||||
<Text style={linkStyle}>
|
||||
{`#${hashtag}`}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<Text
|
||||
onPress={handlePress}
|
||||
style={linkStyle}
|
||||
>
|
||||
{`#${hashtag}`}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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<MarkdownProps> {
|
||||
static defaultProps = {
|
||||
textStyles: {},
|
||||
@@ -294,7 +322,9 @@ class Markdown extends PureComponent<MarkdownProps> {
|
||||
|
||||
return (
|
||||
<View style={blockStyle}>
|
||||
{children}
|
||||
<Text>
|
||||
{children}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -476,32 +506,4 @@ class Markdown extends PureComponent<MarkdownProps> {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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<TapGestureHandler>();
|
||||
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 = (
|
||||
<TouchableWithoutFeedback
|
||||
disabled={disabled}
|
||||
onLongPress={handleLinkLongPress}
|
||||
onPress={onGestureEvent}
|
||||
>
|
||||
<Animated.View
|
||||
style={[styles, {width, height}, style.container]}
|
||||
testID='markdown_image'
|
||||
>
|
||||
<ProgressiveImage
|
||||
forwardRef={ref}
|
||||
id={fileInfo.id!}
|
||||
defaultSource={{uri: fileInfo.uri!}}
|
||||
onError={handleOnError}
|
||||
resizeMode='contain'
|
||||
style={{width, height}}
|
||||
/>
|
||||
</Animated.View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (image && linkDestination && !disabled) {
|
||||
image = (
|
||||
<TouchableWithFeedback
|
||||
onPress={handleLinkPress}
|
||||
onLongPress={handleLinkLongPress}
|
||||
style={[{width, height}, style.container]}
|
||||
>
|
||||
<ProgressiveImage
|
||||
forwardRef={ref}
|
||||
id={fileInfo.id!}
|
||||
defaultSource={{uri: fileInfo.uri!}}
|
||||
onError={handleOnError}
|
||||
resizeMode='contain'
|
||||
style={{width, height}}
|
||||
/>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (image && linkDestination && !disabled) {
|
||||
return (
|
||||
<TouchableWithFeedback
|
||||
onPress={handleLinkPress}
|
||||
onLongPress={handleLinkLongPress}
|
||||
style={[{width, height}, style.container]}
|
||||
testID='markdown_image_link'
|
||||
>
|
||||
{image}
|
||||
</TouchableWithFeedback>
|
||||
);
|
||||
}
|
||||
@@ -262,23 +276,7 @@ const MarkdownImage = ({
|
||||
return (
|
||||
<GalleryInit galleryIdentifier={galleryIdentifier}>
|
||||
<Animated.View testID='markdown_image'>
|
||||
<LongPressGestureHandler
|
||||
enabled={!disabled}
|
||||
onGestureEvent={handleLinkLongPress}
|
||||
waitFor={tapRef}
|
||||
>
|
||||
<Animated.View style={[styles, {width, height}, style.container]}>
|
||||
<TapGestureHandler
|
||||
enabled={!disabled}
|
||||
onGestureEvent={onGestureEvent}
|
||||
ref={tapRef}
|
||||
>
|
||||
<Animated.View>
|
||||
{image}
|
||||
</Animated.View>
|
||||
</TapGestureHandler>
|
||||
</Animated.View>
|
||||
</LongPressGestureHandler>
|
||||
{image}
|
||||
</Animated.View>
|
||||
</GalleryInit>
|
||||
);
|
||||
|
||||
35
app/components/markdown/markdown_link/index.ts
Normal file
35
app/components/markdown/markdown_link/index.ts
Normal file
@@ -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<SystemModel>(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));
|
||||
@@ -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 (
|
||||
<TouchableOpacity
|
||||
<Text
|
||||
onPress={handlePress}
|
||||
onLongPress={handleLongPress}
|
||||
>
|
||||
<Text>
|
||||
{renderChildren}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
{renderChildren}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
type ConfigValue = {
|
||||
value: ClientConfig;
|
||||
}
|
||||
|
||||
const withConfigValues = withObservables([], ({database}: WithDatabaseArgs) => {
|
||||
const config = database.get<SystemModel>(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;
|
||||
@@ -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<MarkdownTableProps, MarkdownTableState> {
|
||||
private rowsSliced: boolean | undefined;
|
||||
private colsSliced: boolean | undefined;
|
||||
@@ -284,78 +357,4 @@ class MarkdownTable extends PureComponent<MarkdownTableProps, MarkdownTableState
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default injectIntl(MarkdownTable);
|
||||
|
||||
@@ -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 = (
|
||||
<TapGestureHandler
|
||||
enabled={!disabled}
|
||||
onGestureEvent={onGestureEvent}
|
||||
<TouchableWithoutFeedback
|
||||
disabled={disabled}
|
||||
onPress={onGestureEvent}
|
||||
>
|
||||
<Animated.View
|
||||
style={[styles, {width, height}]}
|
||||
@@ -127,7 +126,7 @@ const MarkTableImage = ({disabled, imagesMetadata, location, postId, serverURL,
|
||||
style={{width, height}}
|
||||
/>
|
||||
</Animated.View>
|
||||
</TapGestureHandler>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
<View style={style.previewContainer}>
|
||||
<TapGestureHandler onGestureEvent={onGestureEvent}>
|
||||
<TouchableWithoutFeedback onPress={onGestureEvent}>
|
||||
<Animated.View style={[styles, style.filePreview]}>
|
||||
{filePreviewComponent}
|
||||
</Animated.View>
|
||||
</TapGestureHandler>
|
||||
</TouchableWithoutFeedback>
|
||||
{file.failed &&
|
||||
<UploadRetry
|
||||
onPress={retryFileUpload}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
import React, {useCallback, useEffect} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Keyboard, StyleProp, View, ViewStyle} from 'react-native';
|
||||
import {TouchableOpacity} from 'react-native-gesture-handler';
|
||||
import {Keyboard, StyleProp, TouchableHighlight, View, ViewStyle} from 'react-native';
|
||||
|
||||
import {fetchMissingProfilesByIds, fetchMissingProfilesByUsernames} from '@actions/remote/user';
|
||||
import Markdown from '@components/markdown';
|
||||
@@ -209,10 +208,11 @@ const CombinedUserActivity = ({
|
||||
style={style}
|
||||
testID={testID}
|
||||
>
|
||||
<TouchableOpacity
|
||||
<TouchableHighlight
|
||||
testID={itemTestID}
|
||||
onPress={emptyFunction}
|
||||
onLongPress={onLongPress}
|
||||
underlayColor={changeOpacity(theme.centerChannelColor, 0.1)}
|
||||
>
|
||||
<View style={styles.container}>
|
||||
<SystemAvatar theme={theme}/>
|
||||
@@ -226,7 +226,7 @@ const CombinedUserActivity = ({
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
<Text>{' '}</Text>
|
||||
<TouchableOpacity
|
||||
<Text
|
||||
onPress={onPress}
|
||||
style={style.touchableStyle}
|
||||
style={style.linkText}
|
||||
>
|
||||
<Text style={style.linkText}>
|
||||
<FormattedText
|
||||
id={'last_users_message.others'}
|
||||
defaultMessage={'{numOthers} others '}
|
||||
values={{numOthers}}
|
||||
/>
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<FormattedText
|
||||
id={'last_users_message.others'}
|
||||
defaultMessage={'{numOthers} others '}
|
||||
values={{numOthers}}
|
||||
/>
|
||||
</Text>
|
||||
<FormattedMarkdownText
|
||||
id={systemMessages[postType].id}
|
||||
defaultMessage={systemMessages[postType].defaultMessage}
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
// 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, useRef} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Keyboard, Platform, StyleSheet, View} from 'react-native';
|
||||
import {Keyboard, Platform, StyleSheet, TouchableOpacity, View} from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import {TouchableOpacity} from 'react-native-gesture-handler';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import ProfilePicture from '@components/profile_picture';
|
||||
import SystemAvatar from '@components/system_avatar';
|
||||
import {View as ViewConstant} from '@constants';
|
||||
import {MM_TABLES, SYSTEM_IDENTIFIERS} from '@constants/database';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import NetworkManager from '@init/network_manager';
|
||||
@@ -23,9 +17,7 @@ import {showModal} from '@screens/navigation';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
|
||||
import type {Client} from '@client/rest';
|
||||
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';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {ImageSource} from 'react-native-vector-icons/Icon';
|
||||
|
||||
@@ -149,15 +141,4 @@ const Avatar = ({author, enablePostIconOverride, isAutoReponse, isSystemPost, po
|
||||
return component;
|
||||
};
|
||||
|
||||
const withPost = withObservables(['post'], ({database, post}: {post: PostModel} & WithDatabaseArgs) => {
|
||||
const enablePostIconOverride = database.get<SystemModel>(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;
|
||||
28
app/components/post_list/post/avatar/index.ts
Normal file
28
app/components/post_list/post/avatar/index.ts
Normal file
@@ -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<SystemModel>(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));
|
||||
@@ -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}
|
||||
/>
|
||||
<TouchableOpacity onPress={handleAddChannelMember}>
|
||||
<Text
|
||||
style={textStyles.link}
|
||||
testID='add_channel_member_link'
|
||||
onPress={handleAddChannelMember}
|
||||
>
|
||||
<FormattedText
|
||||
id={linkId}
|
||||
defaultMessage={linkText}
|
||||
style={textStyles.link}
|
||||
testID='add_channel_member_link'
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</Text>
|
||||
<FormattedText
|
||||
id={'post_body.check_for_out_of_channel_mentions.message_last'}
|
||||
defaultMessage={'? They will have access to all message history.'}
|
||||
@@ -222,15 +213,4 @@ const AddMembers = ({channelType, currentUser, post, theme}: AddMembersProps) =>
|
||||
);
|
||||
};
|
||||
|
||||
const withChannelType = withObservables(['post'], ({database, post}: WithDatabaseArgs & {post: PostModel}) => ({
|
||||
currentUser: database.get<SystemModel>(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;
|
||||
31
app/components/post_list/post/body/add_members/index.ts
Normal file
31
app/components/post_list/post/body/add_members/index.ts
Normal file
@@ -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<SystemModel>(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));
|
||||
@@ -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 (
|
||||
<GalleryInit galleryIdentifier={galleryIdentifier}>
|
||||
<Animated.View style={[styles, style.imageContainer, {height: dimensions.height}]}>
|
||||
<TapGestureHandler onGestureEvent={onGestureEvent}>
|
||||
<TouchableWithoutFeedback onPress={onGestureEvent}>
|
||||
<Animated.View testID={`ImagePreview-${fileId}`}>
|
||||
<ProgressiveImage
|
||||
forwardRef={ref}
|
||||
@@ -135,25 +125,10 @@ const ImagePreview = ({expandedLink, isReplyPost, layoutWidth, link, location, m
|
||||
style={[style.image, {width: dimensions.width, height: dimensions.height}]}
|
||||
/>
|
||||
</Animated.View>
|
||||
</TapGestureHandler>
|
||||
</TouchableWithoutFeedback>
|
||||
</Animated.View>
|
||||
</GalleryInit>
|
||||
);
|
||||
};
|
||||
|
||||
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<string, string>)[link]) : of$(undefined)),
|
||||
),
|
||||
),
|
||||
link: of$(link),
|
||||
};
|
||||
});
|
||||
|
||||
export default withDatabase(withExpandedLink(React.memo(ImagePreview)));
|
||||
export default React.memo(ImagePreview);
|
||||
@@ -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<string, string>)[link]) : of$(undefined)),
|
||||
),
|
||||
),
|
||||
link: of$(link),
|
||||
};
|
||||
});
|
||||
|
||||
export default withDatabase(enhance(ImagePreview));
|
||||
@@ -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) &&
|
||||
<TouchableOpacity onPress={openLink}>
|
||||
<Text
|
||||
key='author_name'
|
||||
style={[style.name, Boolean(link) && style.link]}
|
||||
>
|
||||
{name}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<Text
|
||||
key='author_name'
|
||||
onPress={openLink}
|
||||
style={[style.name, Boolean(link) && style.link]}
|
||||
>
|
||||
{name}
|
||||
</Text>
|
||||
}
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -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 (
|
||||
<GalleryInit galleryIdentifier={galleryIdentifier}>
|
||||
<Animated.View style={[styles, style.container, {width}]}>
|
||||
<TapGestureHandler onGestureEvent={onGestureEvent}>
|
||||
<TouchableWithoutFeedback onPress={onGestureEvent}>
|
||||
<Animated.View testID={`attachmentImage-${fileId}`}>
|
||||
<ProgressiveImage
|
||||
forwardRef={ref}
|
||||
@@ -110,7 +109,7 @@ const AttachmentImage = ({imageUrl, imageMetadata, layoutWidth, location, postId
|
||||
style={{height, width}}
|
||||
/>
|
||||
</Animated.View>
|
||||
</TapGestureHandler>
|
||||
</TouchableWithoutFeedback>
|
||||
</Animated.View>
|
||||
</GalleryInit>
|
||||
);
|
||||
|
||||
@@ -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 = (
|
||||
<TouchableOpacity onPress={openLink}>
|
||||
<Text style={[style.title, Boolean(link) && style.link]}>
|
||||
{value}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<Text
|
||||
onPress={openLink}
|
||||
style={[style.title, Boolean(link) && style.link]}
|
||||
>
|
||||
{value}
|
||||
</Text>
|
||||
);
|
||||
} else {
|
||||
title = (
|
||||
|
||||
@@ -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));
|
||||
@@ -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);
|
||||
@@ -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 (
|
||||
<GalleryInit galleryIdentifier={galleryIdentifier}>
|
||||
<Animated.View style={[styles, style.imageContainer, dimensionsStyle]}>
|
||||
<TapGestureHandler onGestureEvent={onGestureEvent}>
|
||||
<TouchableWithoutFeedback onPress={onGestureEvent}>
|
||||
<Animated.View testID={`OpenGraphImage-${fileId}`}>
|
||||
<FastImage
|
||||
style={[style.image, dimensionsStyle]}
|
||||
@@ -130,7 +129,7 @@ const OpengraphImage = ({isReplyPost, layoutWidth, location, metadata, openGraph
|
||||
nativeID={`OpenGraphImage-${fileId}`}
|
||||
/>
|
||||
</Animated.View>
|
||||
</TapGestureHandler>
|
||||
</TouchableWithoutFeedback>
|
||||
</Animated.View>
|
||||
</GalleryInit>
|
||||
);
|
||||
|
||||
24
app/components/post_list/post/body/content/youtube/index.ts
Normal file
24
app/components/post_list/post/body/content/youtube/index.ts
Normal file
@@ -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<SystemModel>(MM_TABLES.SERVER.SYSTEM).findAndObserve(SYSTEM_IDENTIFIERS.CONFIG).pipe(
|
||||
switchMap(({value}: {value: ClientConfig}) => {
|
||||
return of$(value.GoogleDeveloperKey);
|
||||
}),
|
||||
),
|
||||
}));
|
||||
|
||||
export default withDatabase(enhance(YouTube));
|
||||
@@ -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<SystemModel>(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);
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 (
|
||||
<TapGestureHandler
|
||||
onGestureEvent={onGestureEvent}
|
||||
shouldCancelWhenOutside={true}
|
||||
>
|
||||
<TouchableWithoutFeedback onPress={onGestureEvent}>
|
||||
<Animated.View style={[styles]}>
|
||||
<VideoFile
|
||||
file={file}
|
||||
@@ -93,16 +89,13 @@ const File = ({
|
||||
/>
|
||||
}
|
||||
</Animated.View>
|
||||
</TapGestureHandler>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
|
||||
if (isImage(file)) {
|
||||
return (
|
||||
<TapGestureHandler
|
||||
onGestureEvent={onGestureEvent}
|
||||
shouldCancelWhenOutside={true}
|
||||
>
|
||||
<TouchableWithoutFeedback onPress={onGestureEvent}>
|
||||
<Animated.View style={[styles]}>
|
||||
<ImageFile
|
||||
file={file}
|
||||
@@ -119,7 +112,7 @@ const File = ({
|
||||
/>
|
||||
}
|
||||
</Animated.View>
|
||||
</TapGestureHandler>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -3,33 +3,23 @@
|
||||
exports[`renderSystemMessage uses renderer for Channel Display Name update 1`] = `
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
Object {
|
||||
"marginBottom": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<RNGestureHandlerButton
|
||||
collapsable={false}
|
||||
exclusive={true}
|
||||
onGestureEvent={[Function]}
|
||||
onGestureHandlerEvent={[Function]}
|
||||
onGestureHandlerStateChange={[Function]}
|
||||
onHandlerStateChange={[Function]}
|
||||
rippleColor={0}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
style={
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
@@ -52,55 +42,45 @@ exports[`renderSystemMessage uses renderer for Channel Display Name update 1`] =
|
||||
@username
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</RNGestureHandlerButton>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
updated the channel display name from: old displayname to: new displayname
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
updated the channel display name from: old displayname to: new displayname
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`renderSystemMessage uses renderer for Channel Header update 1`] = `
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
Object {
|
||||
"marginBottom": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<RNGestureHandlerButton
|
||||
collapsable={false}
|
||||
exclusive={true}
|
||||
onGestureEvent={[Function]}
|
||||
onGestureHandlerEvent={[Function]}
|
||||
onGestureHandlerStateChange={[Function]}
|
||||
onHandlerStateChange={[Function]}
|
||||
rippleColor={0}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
style={
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
@@ -123,22 +103,22 @@ exports[`renderSystemMessage uses renderer for Channel Header update 1`] = `
|
||||
@username
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</RNGestureHandlerButton>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
updated the channel header from: old header to: new header
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
updated the channel header from: old header to: new header
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
@@ -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`] = `
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
Object {
|
||||
"marginBottom": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<RNGestureHandlerButton
|
||||
collapsable={false}
|
||||
exclusive={true}
|
||||
onGestureEvent={[Function]}
|
||||
onGestureHandlerEvent={[Function]}
|
||||
onGestureHandlerStateChange={[Function]}
|
||||
onHandlerStateChange={[Function]}
|
||||
rippleColor={0}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
style={
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
@@ -210,55 +180,45 @@ exports[`renderSystemMessage uses renderer for Guest added and join to channel 1
|
||||
@username
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</RNGestureHandlerButton>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
joined the channel as a guest.
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
joined the channel as a guest.
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`renderSystemMessage uses renderer for Guest added and join to channel 2`] = `
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
Object {
|
||||
"marginBottom": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<RNGestureHandlerButton
|
||||
collapsable={false}
|
||||
exclusive={true}
|
||||
onGestureEvent={[Function]}
|
||||
onGestureHandlerEvent={[Function]}
|
||||
onGestureHandlerStateChange={[Function]}
|
||||
onHandlerStateChange={[Function]}
|
||||
rippleColor={0}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
style={
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
@@ -281,40 +241,20 @@ exports[`renderSystemMessage uses renderer for Guest added and join to channel 2
|
||||
@other.user
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</RNGestureHandlerButton>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
added to the channel as a guest by
|
||||
</Text>
|
||||
<RNGestureHandlerButton
|
||||
collapsable={false}
|
||||
exclusive={true}
|
||||
onGestureEvent={[Function]}
|
||||
onGestureHandlerEvent={[Function]}
|
||||
onGestureHandlerStateChange={[Function]}
|
||||
onHandlerStateChange={[Function]}
|
||||
rippleColor={0}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
style={
|
||||
Object {
|
||||
"opacity": 1,
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
testID="markdown_text"
|
||||
>
|
||||
added to the channel as a guest by
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
@@ -337,70 +277,70 @@ exports[`renderSystemMessage uses renderer for Guest added and join to channel 2
|
||||
@username.
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</RNGestureHandlerButton>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`renderSystemMessage uses renderer for OLD archived channel without a username 1`] = `
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
Object {
|
||||
"marginBottom": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
archived the channel
|
||||
</Text>
|
||||
<Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
archived the channel
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`renderSystemMessage uses renderer for archived channel 1`] = `
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
Object {
|
||||
"marginBottom": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<RNGestureHandlerButton
|
||||
collapsable={false}
|
||||
exclusive={true}
|
||||
onGestureEvent={[Function]}
|
||||
onGestureHandlerEvent={[Function]}
|
||||
onGestureHandlerStateChange={[Function]}
|
||||
onHandlerStateChange={[Function]}
|
||||
rippleColor={0}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
style={
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
@@ -423,55 +363,45 @@ exports[`renderSystemMessage uses renderer for archived channel 1`] = `
|
||||
@username
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</RNGestureHandlerButton>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
archived the channel
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
archived the channel
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`renderSystemMessage uses renderer for unarchived channel 1`] = `
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
Object {
|
||||
"marginBottom": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<RNGestureHandlerButton
|
||||
collapsable={false}
|
||||
exclusive={true}
|
||||
onGestureEvent={[Function]}
|
||||
onGestureHandlerEvent={[Function]}
|
||||
onGestureHandlerStateChange={[Function]}
|
||||
onHandlerStateChange={[Function]}
|
||||
rippleColor={0}
|
||||
>
|
||||
<View
|
||||
accessible={true}
|
||||
collapsable={false}
|
||||
style={
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"opacity": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
"alignItems": "flex-start",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "wrap",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
@@ -494,21 +424,21 @@ exports[`renderSystemMessage uses renderer for unarchived channel 1`] = `
|
||||
@username
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</RNGestureHandlerButton>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
unarchived the channel
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(63,67,80,0.6)",
|
||||
"fontFamily": "OpenSans",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "400",
|
||||
"lineHeight": 24,
|
||||
}
|
||||
}
|
||||
testID="markdown_text"
|
||||
>
|
||||
unarchived the channel
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
`;
|
||||
|
||||
@@ -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<ViewStyle>;
|
||||
messageStyle: StyleProp<ViewStyle>;
|
||||
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 (
|
||||
<Markdown
|
||||
baseTextStyle={messageStyle}
|
||||
disableGallery={true}
|
||||
textStyles={textStyles}
|
||||
value={intl.formatMessage(localeHolder, values)}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={containerStyle}>
|
||||
<Markdown
|
||||
baseTextStyle={messageStyle}
|
||||
disableGallery={true}
|
||||
textStyles={textStyles}
|
||||
value={intl.formatMessage(localeHolder, values)}
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -609,6 +609,7 @@ export function showOverlay(name: string, passProps = {}, options = {}) {
|
||||
|
||||
Navigation.showOverlay({
|
||||
component: {
|
||||
id: name,
|
||||
name,
|
||||
passProps,
|
||||
options: merge(defaultOptions, options),
|
||||
|
||||
@@ -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<Post | undefined> = 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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user