Refactor Open Profile (#8746)
* Refactor Open Profile * Add missing changes * Fix tests * Fix tests * Address feedback
This commit is contained in:
parent
305fbee513
commit
af0a7525c8
89 changed files with 509 additions and 270 deletions
|
|
@ -23,6 +23,7 @@ import {secureGetFromRecord} from '@utils/types';
|
|||
import {displayUsername} from '@utils/user';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Selection = DialogOption | Channel | UserProfile | DialogOption[] | Channel[] | UserProfile[];
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ type AutoCompleteSelectorProps = {
|
|||
teammateNameDisplay: string;
|
||||
isMultiselect?: boolean;
|
||||
testID: string;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
|
|
@ -131,8 +133,22 @@ function getTextAndValueFromSelectedItem(item: Selection, teammateNameDisplay: s
|
|||
}
|
||||
|
||||
function AutoCompleteSelector({
|
||||
dataSource, disabled = false, errorText, getDynamicOptions, helpText, label, onSelected, optional = false,
|
||||
options, placeholder, roundedBorders = true, selected, teammateNameDisplay, isMultiselect = false, testID,
|
||||
dataSource,
|
||||
disabled = false,
|
||||
errorText,
|
||||
getDynamicOptions,
|
||||
helpText,
|
||||
label,
|
||||
onSelected,
|
||||
optional = false,
|
||||
options,
|
||||
placeholder,
|
||||
roundedBorders = true,
|
||||
selected,
|
||||
teammateNameDisplay,
|
||||
isMultiselect = false,
|
||||
testID,
|
||||
location,
|
||||
}: AutoCompleteSelectorProps) {
|
||||
const intl = useIntl();
|
||||
const theme = useTheme();
|
||||
|
|
@ -221,6 +237,7 @@ function AutoCompleteSelector({
|
|||
disabled={disabled}
|
||||
helpText={helpText}
|
||||
errorText={errorText}
|
||||
location={location}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,10 +23,11 @@ import type ChannelModel from '@typings/database/models/servers/channel';
|
|||
import type DraftModel from '@typings/database/models/servers/draft';
|
||||
import type ScheduledPostModel from '@typings/database/models/servers/scheduled_post';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
channel: ChannelModel;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
postReceiverUser?: UserModel;
|
||||
post: DraftModel | ScheduledPostModel;
|
||||
layoutWidth: number;
|
||||
|
|
|
|||
|
|
@ -16,11 +16,12 @@ import {typography} from '@utils/typography';
|
|||
import type DraftModel from '@typings/database/models/servers/draft';
|
||||
import type ScheduledPostModel from '@typings/database/models/servers/scheduled_post';
|
||||
import type {UserMentionKey} from '@typings/global/markdown';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
post: DraftModel | ScheduledPostModel;
|
||||
layoutWidth: number;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
|
||||
const EMPTY_MENTION_KEYS: UserMentionKey[] = [];
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ import DraftAndScheduledPostMessage from './draft_and_scheduled_post_message';
|
|||
|
||||
import type DraftModel from '@typings/database/models/servers/draft';
|
||||
import type ScheduledPostModel from '@typings/database/models/servers/scheduled_post';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
post: DraftModel | ScheduledPostModel;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
layoutWidth: number;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {logWarning} from '@utils/log';
|
|||
import {getMarkdownBlockStyles, getMarkdownTextStyles} from '@utils/markdown';
|
||||
import {concatStyles, changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
import type {PrimitiveType} from 'intl-messageformat';
|
||||
|
||||
type Props = {
|
||||
|
|
@ -21,7 +22,7 @@ type Props = {
|
|||
channelId?: string;
|
||||
defaultMessage: string;
|
||||
id: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
onPostPress?: (e: GestureResponderEvent) => void;
|
||||
style?: StyleProp<TextStyle>;
|
||||
values?: Record<string, PrimitiveType>;
|
||||
|
|
|
|||
|
|
@ -5,28 +5,28 @@ import {useManagedConfig} from '@mattermost/react-native-emm';
|
|||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import React, {useCallback, useEffect, useMemo} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {type GestureResponderEvent, Keyboard, type StyleProp, StyleSheet, Text, type TextStyle, View} from 'react-native';
|
||||
import {type GestureResponderEvent, type StyleProp, StyleSheet, Text, type TextStyle, View} from 'react-native';
|
||||
|
||||
import {fetchUserOrGroupsByMentionsInBatch} from '@actions/remote/user';
|
||||
import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item';
|
||||
import {Screens} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import GroupModel from '@database/models/server/group';
|
||||
import {useMemoMentionedGroup, useMemoMentionedUser} from '@hooks/markdown';
|
||||
import {bottomSheet, dismissBottomSheet, openAsBottomSheet} from '@screens/navigation';
|
||||
import {bottomSheet, dismissBottomSheet, openUserProfileModal} from '@screens/navigation';
|
||||
import {bottomSheetSnapPoint} from '@utils/helpers';
|
||||
import {displayUsername} from '@utils/user';
|
||||
|
||||
import type GroupMembershipModel from '@typings/database/models/servers/group_membership';
|
||||
import type UserModelType from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type AtMentionProps = {
|
||||
channelId?: string;
|
||||
currentUserId: string;
|
||||
disableAtChannelMentionHighlight?: boolean;
|
||||
isSearchResult?: boolean;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
mentionKeys?: Array<{key: string }>;
|
||||
mentionName: string;
|
||||
mentionStyle: StyleProp<TextStyle>;
|
||||
|
|
@ -95,13 +95,11 @@ const AtMention = ({
|
|||
return;
|
||||
}
|
||||
|
||||
const screen = Screens.USER_PROFILE;
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const closeButtonId = 'close-user-profile';
|
||||
const props = {closeButtonId, location, userId: user.id, channelId};
|
||||
|
||||
Keyboard.dismiss();
|
||||
openAsBottomSheet({screen, title, theme, closeButtonId, props});
|
||||
openUserProfileModal(intl, theme, {
|
||||
location,
|
||||
userId: user.id,
|
||||
channelId,
|
||||
});
|
||||
};
|
||||
|
||||
const handleLongPress = useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ describe('Markdown', () => {
|
|||
baseTextStyle: {},
|
||||
enableInlineLatex: true,
|
||||
enableLatex: true,
|
||||
location: 'somewhere?',
|
||||
location: 'Channel',
|
||||
maxNodes: 2000,
|
||||
theme: Preferences.THEMES.denim,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import type {
|
|||
MarkdownAtMentionRenderer, MarkdownBaseRenderer, MarkdownBlockStyles, MarkdownChannelMentionRenderer,
|
||||
MarkdownEmojiRenderer, MarkdownImageRenderer, MarkdownLatexRenderer, MarkdownTextStyles, SearchPattern, UserMentionKey, HighlightWithoutNotificationKey,
|
||||
} from '@typings/global/markdown';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type MarkdownProps = {
|
||||
autolinkedUrlSchemes?: string[];
|
||||
|
|
@ -65,7 +66,7 @@ type MarkdownProps = {
|
|||
isSearchResult?: boolean;
|
||||
layoutHeight?: number;
|
||||
layoutWidth?: number;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
maxNodes: number;
|
||||
mentionKeys?: UserMentionKey[];
|
||||
minimumHashtagLength?: number;
|
||||
|
|
|
|||
|
|
@ -16,9 +16,12 @@ import {popToRoot} from '@screens/navigation';
|
|||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
testID?: string;
|
||||
deactivated?: boolean;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => ({
|
||||
|
|
@ -57,6 +60,7 @@ const edges: Edge[] = ['bottom'];
|
|||
export default function Archived({
|
||||
testID,
|
||||
deactivated,
|
||||
location,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const style = getStyleSheet(theme);
|
||||
|
|
@ -94,7 +98,7 @@ export default function Archived({
|
|||
{...message}
|
||||
style={style.archivedText}
|
||||
baseTextStyle={style.baseTextStyle}
|
||||
location=''
|
||||
location={location}
|
||||
/>
|
||||
<Button
|
||||
buttonStyle={style.closeButton}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import Archived from './archived';
|
|||
import DraftHandler from './draft_handler';
|
||||
import ReadOnly from './read_only';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
const AUTOCOMPLETE_ADJUST = -5;
|
||||
type Props = {
|
||||
testID?: string;
|
||||
|
|
@ -30,6 +32,7 @@ type Props = {
|
|||
containerHeight: number;
|
||||
isChannelScreen: boolean;
|
||||
canShowPostPriority?: boolean;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
|
||||
function PostDraft({
|
||||
|
|
@ -46,6 +49,7 @@ function PostDraft({
|
|||
containerHeight,
|
||||
isChannelScreen,
|
||||
canShowPostPriority,
|
||||
location,
|
||||
}: Props) {
|
||||
const [value, setValue] = useState(message);
|
||||
const [cursorPosition, setCursorPosition] = useState(message.length);
|
||||
|
|
@ -73,6 +77,7 @@ function PostDraft({
|
|||
<Archived
|
||||
testID={archivedTestID}
|
||||
deactivated={deactivatedChannel}
|
||||
location={location}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,13 @@ import {secureGetFromRecord} from '@utils/types';
|
|||
import LastUsers from './last_users';
|
||||
import {postTypeMessages} from './messages';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
canDelete: boolean;
|
||||
currentUserId?: string;
|
||||
currentUsername?: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: Post | null;
|
||||
showJoinLeave: boolean;
|
||||
testID?: string;
|
||||
|
|
|
|||
|
|
@ -14,10 +14,12 @@ import {secureGetFromRecord} from '@utils/types';
|
|||
|
||||
import {postTypeMessages, systemMessages} from './messages';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type LastUsersProps = {
|
||||
actor: string;
|
||||
channelId?: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
postType: string;
|
||||
usernames: string[];
|
||||
theme: Theme;
|
||||
|
|
|
|||
|
|
@ -4,26 +4,27 @@
|
|||
import {Image} from 'expo-image';
|
||||
import React, {type ReactNode} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Keyboard, Platform, StyleSheet, TouchableOpacity, View} from 'react-native';
|
||||
import {Platform, StyleSheet, TouchableOpacity, View} from 'react-native';
|
||||
|
||||
import {buildAbsoluteUrl} from '@actions/remote/file';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import ProfilePicture from '@components/profile_picture';
|
||||
import {Screens, View as ViewConstant} from '@constants';
|
||||
import {View as ViewConstant} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {openAsBottomSheet} from '@screens/navigation';
|
||||
import {openUserProfileModal} from '@screens/navigation';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {ensureString} from '@utils/types';
|
||||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type AvatarProps = {
|
||||
author?: UserModel;
|
||||
enablePostIconOverride?: boolean;
|
||||
isAutoReponse: boolean;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: PostModel;
|
||||
}
|
||||
|
||||
|
|
@ -91,20 +92,13 @@ const Avatar = ({author, enablePostIconOverride, isAutoReponse, location, post}:
|
|||
if (!author) {
|
||||
return;
|
||||
}
|
||||
const screen = Screens.USER_PROFILE;
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const closeButtonId = 'close-user-profile';
|
||||
const props = {
|
||||
closeButtonId,
|
||||
openUserProfileModal(intl, theme, {
|
||||
location,
|
||||
userId: author.id,
|
||||
channelId: post.channelId,
|
||||
location,
|
||||
userIconOverride: propsIconUrl,
|
||||
usernameOverride: propsUsername,
|
||||
};
|
||||
|
||||
Keyboard.dismiss();
|
||||
openAsBottomSheet({screen, title, theme, closeButtonId, props});
|
||||
});
|
||||
});
|
||||
|
||||
let component = (
|
||||
|
|
|
|||
|
|
@ -23,12 +23,13 @@ import {USER_ROW_HEIGHT} from './users_list/user_list_item';
|
|||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
currentUserId: UserModel['id'];
|
||||
currentUserTimezone: UserModel['timezone'];
|
||||
hasReactions: boolean;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: PostModel;
|
||||
theme: Theme;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,17 +3,17 @@
|
|||
|
||||
import React, {useCallback} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Keyboard} from 'react-native';
|
||||
|
||||
import FormattedRelativeTime from '@components/formatted_relative_time';
|
||||
import UserItem from '@components/user_item';
|
||||
import {Screens} from '@constants';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {dismissBottomSheet, openAsBottomSheet} from '@screens/navigation';
|
||||
import {openUserProfileModal as openUserProfileBottomSheet} from '@screens/navigation';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
export const USER_ROW_HEIGHT = 60;
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
user: UserModel;
|
||||
userAcknowledgement: number;
|
||||
timezone?: UserTimezone;
|
||||
|
|
@ -53,14 +53,11 @@ const UserListItem = ({
|
|||
|
||||
const handleUserPress = useCallback(async (userProfile: UserProfile) => {
|
||||
if (userProfile) {
|
||||
await dismissBottomSheet(Screens.BOTTOM_SHEET);
|
||||
const screen = Screens.USER_PROFILE;
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const closeButtonId = 'close-user-profile';
|
||||
const props = {closeButtonId, location, userId: userProfile.id, channelId};
|
||||
|
||||
Keyboard.dismiss();
|
||||
openAsBottomSheet({screen, title, theme, closeButtonId, props});
|
||||
await openUserProfileBottomSheet(intl, theme, {
|
||||
userId: userProfile.id,
|
||||
channelId,
|
||||
location,
|
||||
}, Screens.BOTTOM_SHEET);
|
||||
}
|
||||
}, [channelId, intl, location, theme]);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ import {useIsTablet} from '@hooks/device';
|
|||
import UserListItem from './user_list_item';
|
||||
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
import type {ListRenderItemInfo} from 'react-native';
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
users: UserModel[];
|
||||
userAcknowledgements: Record<string, number>;
|
||||
timezone?: UserTimezone;
|
||||
|
|
|
|||
|
|
@ -17,11 +17,12 @@ import {isStringArray} from '@utils/types';
|
|||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type AddMembersProps = {
|
||||
channelType: string | null;
|
||||
currentUser?: UserModel;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: PostModel;
|
||||
theme: Theme;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@ import {useShowMoreAnimatedStyle} from '@hooks/show_more';
|
|||
import {getMarkdownBlockStyles, getMarkdownTextStyles} from '@utils/markdown';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
theme: Theme;
|
||||
value: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ import {View} from 'react-native';
|
|||
import Markdown from '@components/markdown';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
theme: Theme;
|
||||
value: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ import EmbedTitle from './embed_title';
|
|||
import EmbedSubBindings from './embedded_sub_bindings';
|
||||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
embed: AppBinding;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: PostModel;
|
||||
theme: Theme;
|
||||
}
|
||||
|
|
@ -72,6 +73,7 @@ const EmbeddedBinding = ({embed, location, post, theme}: Props) => {
|
|||
bindings={cleanedBindings}
|
||||
post={post}
|
||||
theme={theme}
|
||||
location={location}
|
||||
/>
|
||||
}
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -7,14 +7,21 @@ import ButtonBinding from './button_binding';
|
|||
import BindingMenu from './menu_binding';
|
||||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
bindings: AppBinding[];
|
||||
post: PostModel;
|
||||
theme: Theme;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
|
||||
const EmbeddedSubBindings = ({bindings, post, theme}: Props) => {
|
||||
const EmbeddedSubBindings = ({
|
||||
bindings,
|
||||
post,
|
||||
theme,
|
||||
location,
|
||||
}: Props) => {
|
||||
const content: React.ReactNode[] = [];
|
||||
|
||||
bindings.forEach((binding) => {
|
||||
|
|
@ -28,6 +35,7 @@ const EmbeddedSubBindings = ({bindings, post, theme}: Props) => {
|
|||
key={binding.location}
|
||||
binding={binding}
|
||||
post={post}
|
||||
location={location}
|
||||
/>,
|
||||
);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import {isArrayOf} from '@utils/types';
|
|||
import EmbeddedBinding from './embedded_binding';
|
||||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: PostModel;
|
||||
theme: Theme;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,15 +15,23 @@ import {logDebug} from '@utils/log';
|
|||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
binding: AppBinding;
|
||||
currentTeamId: string;
|
||||
post: PostModel;
|
||||
teamID?: string;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
|
||||
const MenuBinding = ({binding, currentTeamId, post, teamID}: Props) => {
|
||||
const MenuBinding = ({
|
||||
binding,
|
||||
currentTeamId,
|
||||
post,
|
||||
teamID,
|
||||
location,
|
||||
}: Props) => {
|
||||
const [selected, setSelected] = useState<string>();
|
||||
const serverUrl = useServerUrl();
|
||||
|
||||
|
|
@ -73,6 +81,7 @@ const MenuBinding = ({binding, currentTeamId, post, teamID}: Props) => {
|
|||
selected={selected}
|
||||
onSelected={onSelect}
|
||||
testID={`embedded_binding.${binding.location}`}
|
||||
location={location}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,11 +13,12 @@ import Opengraph from './opengraph';
|
|||
import YouTube from './youtube';
|
||||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type ContentProps = {
|
||||
isReplyPost: boolean;
|
||||
layoutWidth?: number;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: PostModel;
|
||||
theme: Theme;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import AutocompleteSelector from '@components/autocomplete_selector';
|
|||
import {useServerUrl} from '@context/server';
|
||||
import {filterOptions} from '@utils/message_attachment';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
dataSource?: string;
|
||||
defaultOption?: string;
|
||||
|
|
@ -16,9 +18,19 @@ type Props = {
|
|||
name: string;
|
||||
options?: PostActionOption[];
|
||||
postId: string;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
|
||||
const ActionMenu = ({dataSource, defaultOption, disabled, id, name, options, postId}: Props) => {
|
||||
const ActionMenu = ({
|
||||
dataSource,
|
||||
defaultOption,
|
||||
disabled,
|
||||
id,
|
||||
name,
|
||||
options,
|
||||
postId,
|
||||
location,
|
||||
}: Props) => {
|
||||
const serverUrl = useServerUrl();
|
||||
|
||||
const filteredOptions = useMemo(() => {
|
||||
|
|
@ -53,6 +65,7 @@ const ActionMenu = ({dataSource, defaultOption, disabled, id, name, options, pos
|
|||
onSelected={handleSelect}
|
||||
disabled={disabled}
|
||||
testID={`message_attachment.${name}`}
|
||||
location={location}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,12 +6,20 @@ import React from 'react';
|
|||
import ActionButton from './action_button';
|
||||
import ActionMenu from './action_menu';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
actions: PostAction[];
|
||||
postId: string;
|
||||
theme: Theme;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
const AttachmentActions = ({actions, postId, theme}: Props) => {
|
||||
const AttachmentActions = ({
|
||||
actions,
|
||||
postId,
|
||||
theme,
|
||||
location,
|
||||
}: Props) => {
|
||||
const content: React.ReactNode[] = [];
|
||||
|
||||
actions.forEach((action) => {
|
||||
|
|
@ -31,6 +39,7 @@ const AttachmentActions = ({actions, postId, theme}: Props) => {
|
|||
options={action.options}
|
||||
postId={postId}
|
||||
disabled={action.disabled}
|
||||
location={location}
|
||||
/>,
|
||||
);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -8,13 +8,14 @@ import Markdown from '@components/markdown';
|
|||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import type {MarkdownBlockStyles, MarkdownTextStyles} from '@typings/global/markdown';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
baseTextStyle: StyleProp<TextStyle>;
|
||||
blockStyles?: MarkdownBlockStyles;
|
||||
channelId: string;
|
||||
fields: MessageAttachmentField[];
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
metadata?: PostMetadata | null;
|
||||
textStyles?: MarkdownTextStyles;
|
||||
theme: Theme;
|
||||
|
|
|
|||
|
|
@ -7,12 +7,13 @@ import {type StyleProp, StyleSheet, type TextStyle, View} from 'react-native';
|
|||
import Markdown from '@components/markdown';
|
||||
|
||||
import type {MarkdownBlockStyles, MarkdownTextStyles} from '@typings/global/markdown';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
baseTextStyle: StyleProp<TextStyle>;
|
||||
blockStyles?: MarkdownBlockStyles;
|
||||
channelId: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
metadata?: PostMetadata | null;
|
||||
textStyles?: MarkdownTextStyles;
|
||||
theme: Theme;
|
||||
|
|
|
|||
|
|
@ -10,13 +10,14 @@ import ShowMoreButton from '@components/post_list/post/body/message/show_more_bu
|
|||
import {useShowMoreAnimatedStyle} from '@hooks/show_more';
|
||||
|
||||
import type {MarkdownBlockStyles, MarkdownTextStyles} from '@typings/global/markdown';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
baseTextStyle: StyleProp<TextStyle>;
|
||||
blockStyles?: MarkdownBlockStyles;
|
||||
channelId: string;
|
||||
hasThumbnail?: boolean;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
metadata?: PostMetadata | null;
|
||||
textStyles?: MarkdownTextStyles;
|
||||
theme: Theme;
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ import Markdown from '@components/markdown';
|
|||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {tryOpenURL} from '@utils/url';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
link?: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
theme: Theme;
|
||||
value?: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import {StyleSheet, View} from 'react-native';
|
|||
|
||||
import MessageAttachment from './message_attachment';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
attachments: MessageAttachment[];
|
||||
channelId: string;
|
||||
layoutWidth?: number;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
metadata?: PostMetadata | undefined | null;
|
||||
postId: string;
|
||||
theme: Theme;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {render} from '@testing-library/react-native';
|
||||
import React from 'react';
|
||||
import React, {type ComponentProps} from 'react';
|
||||
|
||||
import Preferences from '@constants/preferences';
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ describe('MessageAttachment', () => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
const baseProps = {
|
||||
const baseProps: ComponentProps<typeof MessageAttachment> = {
|
||||
attachment: {
|
||||
id: 1,
|
||||
text: 'This is the text of an attachment',
|
||||
|
|
@ -113,7 +113,7 @@ describe('MessageAttachment', () => {
|
|||
},
|
||||
channelId: 'channel-id',
|
||||
postId: 'post-id',
|
||||
location: 'CENTER',
|
||||
location: 'Channel',
|
||||
theme: Preferences.THEMES.denim,
|
||||
metadata: {
|
||||
images: {
|
||||
|
|
|
|||
|
|
@ -20,11 +20,13 @@ import AttachmentText from './attachment_text';
|
|||
import AttachmentThumbnail from './attachment_thumbnail';
|
||||
import AttachmentTitle from './attachment_title';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
attachment: MessageAttachment;
|
||||
channelId: string;
|
||||
layoutWidth?: number;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
metadata?: PostMetadata | null;
|
||||
postId: string;
|
||||
theme: Theme;
|
||||
|
|
@ -138,6 +140,7 @@ export default function MessageAttachment({attachment, channelId, layoutWidth, l
|
|||
actions={attachment.actions!}
|
||||
postId={postId}
|
||||
theme={theme}
|
||||
location={location}
|
||||
/>
|
||||
}
|
||||
{attachment.image_url && Boolean(metadata?.images?.[attachment.image_url]) &&
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import Reactions from './reactions';
|
|||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type {SearchPattern} from '@typings/global/markdown';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type BodyProps = {
|
||||
appsEnabled: boolean;
|
||||
|
|
@ -36,7 +37,7 @@ type BodyProps = {
|
|||
isPendingOrFailed: boolean;
|
||||
isPostAcknowledgementEnabled?: boolean;
|
||||
isPostAddChannelMember: boolean;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: PostModel;
|
||||
searchPatterns?: SearchPattern[];
|
||||
showAddReaction?: boolean;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import ShowMoreButton from './show_more_button';
|
|||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {HighlightWithoutNotificationKey, SearchPattern, UserMentionKey} from '@typings/global/markdown';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type MessageProps = {
|
||||
currentUser?: UserModel;
|
||||
|
|
@ -27,7 +28,7 @@ type MessageProps = {
|
|||
isPendingOrFailed: boolean;
|
||||
isReplyPost: boolean;
|
||||
layoutWidth?: number;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: PostModel;
|
||||
searchPatterns?: SearchPattern[];
|
||||
theme: Theme;
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@ import {typography} from '@utils/typography';
|
|||
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
participants: UserModel[];
|
||||
teamId?: string;
|
||||
thread: ThreadModel;
|
||||
|
|
|
|||
|
|
@ -3,21 +3,22 @@
|
|||
|
||||
import React, {useCallback} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Keyboard, Text, TouchableOpacity, useWindowDimensions, View} from 'react-native';
|
||||
import {Text, TouchableOpacity, useWindowDimensions, View} from 'react-native';
|
||||
|
||||
import CustomStatusEmoji from '@components/custom_status/custom_status_emoji';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import {Screens} from '@constants';
|
||||
import {openAsBottomSheet} from '@screens/navigation';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {usePreventDoubleTap} from '@hooks/utils';
|
||||
import {openUserProfileModal} from '@screens/navigation';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type HeaderDisplayNameProps = {
|
||||
channelId: string;
|
||||
commentCount: number;
|
||||
displayName?: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
rootPostAuthor?: string;
|
||||
shouldRenderReplyButton?: boolean;
|
||||
theme: Theme;
|
||||
|
|
@ -71,15 +72,15 @@ const HeaderDisplayName = ({
|
|||
const intl = useIntl();
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const onPress = useCallback(preventDoubleTap(() => {
|
||||
const screen = Screens.USER_PROFILE;
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const closeButtonId = 'close-user-profile';
|
||||
const props = {closeButtonId, userId, channelId, location, userIconOverride, usernameOverride};
|
||||
|
||||
Keyboard.dismiss();
|
||||
openAsBottomSheet({screen, title, theme, closeButtonId, props});
|
||||
}), [intl.locale, channelId, userIconOverride, userId, usernameOverride, theme]);
|
||||
const onPress = usePreventDoubleTap(useCallback(() => {
|
||||
openUserProfileModal(intl, theme, {
|
||||
location,
|
||||
userId,
|
||||
channelId,
|
||||
userIconOverride,
|
||||
usernameOverride,
|
||||
});
|
||||
}, [intl, userId, channelId, location, userIconOverride, usernameOverride, theme]));
|
||||
|
||||
const calcNameWidth = () => {
|
||||
const isLandscape = dimensions.width > dimensions.height;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import HeaderTag from './tag';
|
|||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type HeaderProps = {
|
||||
author?: UserModel;
|
||||
|
|
@ -37,7 +38,7 @@ type HeaderProps = {
|
|||
isPendingOrFailed: boolean;
|
||||
isSystemPost: boolean;
|
||||
isWebHook: boolean;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: PostModel;
|
||||
rootPostAuthor?: UserModel;
|
||||
showPostPriority: boolean;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import type PostModel from '@typings/database/models/servers/post';
|
|||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {SearchPattern} from '@typings/global/markdown';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type PostProps = {
|
||||
appsEnabled: boolean;
|
||||
|
|
@ -57,7 +58,7 @@ type PostProps = {
|
|||
isLastReply?: boolean;
|
||||
isPostAddChannelMember: boolean;
|
||||
isPostPriorityEnabled: boolean;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: PostModel;
|
||||
rootId?: string;
|
||||
previousPost?: PostModel;
|
||||
|
|
|
|||
|
|
@ -17,11 +17,12 @@ import {typography} from '@utils/typography';
|
|||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
import type {PrimitiveType} from 'intl-messageformat';
|
||||
|
||||
type SystemMessageProps = {
|
||||
author?: UserModel;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post: PostModel;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import ScrollToEndView from './scroll_to_end_view';
|
|||
|
||||
import type {PostListItem, PostListOtherItem, ViewableItemsChanged, ViewableItemsChangedListenerEvent} from '@typings/components/post_list';
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
appsEnabled: boolean;
|
||||
|
|
@ -40,7 +41,7 @@ type Props = {
|
|||
isCRTEnabled?: boolean;
|
||||
isPostAcknowledgementEnabled?: boolean;
|
||||
lastViewedAt: number;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
nativeID: string;
|
||||
onEndReached?: () => void;
|
||||
posts: PostModel[];
|
||||
|
|
|
|||
|
|
@ -10,13 +10,14 @@ import ChannelInfo from './channel_info';
|
|||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type {SearchPattern} from '@typings/global/markdown';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
appsEnabled: boolean;
|
||||
customEmojiNames: string[];
|
||||
isCRTEnabled: boolean;
|
||||
post: PostModel;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
testID?: string;
|
||||
searchPatterns?: SearchPattern[];
|
||||
skipSavedPostsHighlight?: boolean;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import React, {type ComponentProps} from 'react';
|
||||
|
||||
import {Screens} from '@constants';
|
||||
import {fireEvent, renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
||||
|
|
@ -38,6 +39,7 @@ function getBaseProps(): ComponentProps<typeof SectionNotice> {
|
|||
loading: true,
|
||||
},
|
||||
onDismissClick: jest.fn(),
|
||||
location: Screens.SETTINGS_NOTIFICATION_PUSH,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import Markdown from '../markdown';
|
|||
|
||||
import SectionNoticeButton from './section_notice_button';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
text?: string;
|
||||
|
|
@ -22,6 +24,7 @@ type Props = {
|
|||
type?: 'info' | 'success' | 'danger' | 'welcome' | 'warning' | 'hint';
|
||||
isDismissable?: boolean;
|
||||
onDismissClick?: () => void;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
|
||||
const iconByType = {
|
||||
|
|
@ -150,6 +153,7 @@ const SectionNotice = ({
|
|||
secondaryButton,
|
||||
text,
|
||||
type = 'info',
|
||||
location,
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleFromTheme(theme);
|
||||
|
|
@ -179,7 +183,7 @@ const SectionNotice = ({
|
|||
{text && (
|
||||
<Markdown
|
||||
theme={theme}
|
||||
location=''
|
||||
location={location}
|
||||
baseTextStyle={styles.baseText}
|
||||
value={text}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import {useServerUrl} from '@context/server';
|
|||
import {debounce} from '@helpers/api/general';
|
||||
import {filterProfilesMatchingTerm} from '@utils/user';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
currentUserId: string;
|
||||
tutorialWatched: boolean;
|
||||
|
|
@ -19,6 +21,7 @@ type Props = {
|
|||
searchFunction: (term: string) => Promise<UserProfile[]>;
|
||||
createFilter: (exactMatches: UserProfile[], term: string) => ((p: UserProfile) => boolean);
|
||||
testID: string;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
|
||||
export default function ServerUserList({
|
||||
|
|
@ -31,6 +34,7 @@ export default function ServerUserList({
|
|||
searchFunction,
|
||||
createFilter,
|
||||
testID,
|
||||
location,
|
||||
}: Props) {
|
||||
const serverUrl = useServerUrl();
|
||||
|
||||
|
|
@ -124,6 +128,7 @@ export default function ServerUserList({
|
|||
testID={testID}
|
||||
tutorialWatched={tutorialWatched}
|
||||
includeUserMargin={true}
|
||||
location={location}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
|||
import Footer from './footer';
|
||||
import Label from './label';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
label?: string;
|
||||
value?: boolean;
|
||||
|
|
@ -21,6 +23,7 @@ type Props = {
|
|||
disabled?: boolean;
|
||||
onChange: (value: boolean) => void;
|
||||
testID: string;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
|
|
@ -62,6 +65,7 @@ function BoolSetting({
|
|||
disabled = false,
|
||||
onChange,
|
||||
testID,
|
||||
location,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const style = getStyleSheet(theme);
|
||||
|
|
@ -98,6 +102,7 @@ function BoolSetting({
|
|||
disabledText={disabledText}
|
||||
errorText={errorText}
|
||||
helpText={helpText}
|
||||
location={location}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import {useTheme} from '@context/theme';
|
|||
import {getMarkdownBlockStyles, getMarkdownTextStyles} from '@utils/markdown';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
return {
|
||||
helpTextContainer: {
|
||||
|
|
@ -35,12 +37,14 @@ type Props = {
|
|||
disabledText?: string;
|
||||
helpText?: string;
|
||||
errorText?: string;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
function Footer({
|
||||
disabled,
|
||||
disabledText,
|
||||
helpText,
|
||||
errorText,
|
||||
location,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const style = getStyleSheet(theme);
|
||||
|
|
@ -55,7 +59,7 @@ function Footer({
|
|||
baseTextStyle={style.helpText}
|
||||
textStyles={textStyles}
|
||||
disableAtMentions={true}
|
||||
location=''
|
||||
location={location}
|
||||
blockStyles={blockStyles}
|
||||
value={disabledText}
|
||||
theme={theme}
|
||||
|
|
@ -69,7 +73,7 @@ function Footer({
|
|||
textStyles={textStyles}
|
||||
blockStyles={blockStyles}
|
||||
disableAtMentions={true}
|
||||
location=''
|
||||
location={location}
|
||||
value={helpText}
|
||||
theme={theme}
|
||||
/>
|
||||
|
|
@ -82,7 +86,7 @@ function Footer({
|
|||
textStyles={textStyles}
|
||||
blockStyles={blockStyles}
|
||||
disableAtMentions={true}
|
||||
location=''
|
||||
location={location}
|
||||
value={errorText}
|
||||
theme={theme}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import Label from '../label';
|
|||
|
||||
import RadioEntry from './radio_entry';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
return {
|
||||
items: {
|
||||
|
|
@ -32,6 +34,7 @@ type Props = {
|
|||
errorText?: string;
|
||||
value?: string;
|
||||
testID: string;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
function RadioSetting({
|
||||
label,
|
||||
|
|
@ -41,6 +44,7 @@ function RadioSetting({
|
|||
errorText = '',
|
||||
testID,
|
||||
value,
|
||||
location,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const style = getStyleSheet(theme);
|
||||
|
|
@ -80,6 +84,7 @@ function RadioSetting({
|
|||
disabled={false}
|
||||
errorText={errorText}
|
||||
helpText={helpText}
|
||||
location={location}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import {
|
|||
import Footer from './footer';
|
||||
import Label from './label';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
const input = {
|
||||
color: theme.centerChannelColor,
|
||||
|
|
@ -60,6 +62,7 @@ type Props = {
|
|||
keyboardType: KeyboardTypeOptions;
|
||||
secureTextEntry: boolean;
|
||||
testID: string;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
function TextSetting({
|
||||
label,
|
||||
|
|
@ -76,6 +79,7 @@ function TextSetting({
|
|||
keyboardType,
|
||||
secureTextEntry,
|
||||
testID,
|
||||
location,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const style = getStyleSheet(theme);
|
||||
|
|
@ -123,6 +127,7 @@ function TextSetting({
|
|||
disabledText={disabledText}
|
||||
errorText={errorText}
|
||||
helpText={helpText}
|
||||
location={location}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -20,13 +20,14 @@ import UserAvatar from './user_avatar';
|
|||
import UsersList from './users_list';
|
||||
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
const OVERFLOW_DISPLAY_LIMIT = 99;
|
||||
const USER_ROW_HEIGHT = 40;
|
||||
|
||||
type Props = {
|
||||
channelId?: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
users: UserModel[];
|
||||
breakAt?: number;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
|
|
|
|||
|
|
@ -4,27 +4,28 @@
|
|||
import {BottomSheetFlatList} from '@gorhom/bottom-sheet';
|
||||
import React, {useCallback, useRef} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Keyboard, type ListRenderItemInfo, type NativeScrollEvent, type NativeSyntheticEvent} from 'react-native';
|
||||
import {type ListRenderItemInfo, type NativeScrollEvent, type NativeSyntheticEvent} from 'react-native';
|
||||
import {FlatList} from 'react-native-gesture-handler';
|
||||
|
||||
import UserItem from '@components/user_item';
|
||||
import {Screens} from '@constants';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useBottomSheetListsFix} from '@hooks/bottom_sheet_lists_fix';
|
||||
import {dismissBottomSheet, openAsBottomSheet} from '@screens/navigation';
|
||||
import {openUserProfileModal} from '@screens/navigation';
|
||||
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
channelId?: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
type?: BottomSheetList;
|
||||
users: UserModel[];
|
||||
};
|
||||
|
||||
type ItemProps = {
|
||||
channelId?: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
user: UserModel;
|
||||
}
|
||||
|
||||
|
|
@ -33,14 +34,11 @@ const Item = ({channelId, location, user}: ItemProps) => {
|
|||
const theme = useTheme();
|
||||
|
||||
const openUserProfile = useCallback(async (u: UserModel | UserProfile) => {
|
||||
await dismissBottomSheet(Screens.BOTTOM_SHEET);
|
||||
const screen = Screens.USER_PROFILE;
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const closeButtonId = 'close-user-profile';
|
||||
const props = {closeButtonId, location, userId: u.id, channelId};
|
||||
|
||||
Keyboard.dismiss();
|
||||
openAsBottomSheet({screen, title, theme, closeButtonId, props});
|
||||
openUserProfileModal(intl, theme, {
|
||||
userId: u.id,
|
||||
channelId,
|
||||
location,
|
||||
}, Screens.BOTTOM_SHEET);
|
||||
}, [location, channelId, theme, intl]);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -629,7 +629,7 @@ exports[`components/channel_list_row should show results and tutorial 1`] = `
|
|||
keyboardShouldPersistTaps="always"
|
||||
maxToRenderPerBatch={16}
|
||||
onContentSizeChange={[Function]}
|
||||
onEndReached={[Function]}
|
||||
onEndReached={[MockFunction]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
|
|
@ -949,7 +949,7 @@ exports[`components/channel_list_row should show results no tutorial 1`] = `
|
|||
keyboardShouldPersistTaps="always"
|
||||
maxToRenderPerBatch={16}
|
||||
onContentSizeChange={[Function]}
|
||||
onEndReached={[Function]}
|
||||
onEndReached={[MockFunction]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
|
|
@ -1307,7 +1307,7 @@ exports[`components/channel_list_row should show results no tutorial 2 users 1`]
|
|||
keyboardShouldPersistTaps="always"
|
||||
maxToRenderPerBatch={16}
|
||||
onContentSizeChange={[Function]}
|
||||
onEndReached={[Function]}
|
||||
onEndReached={[MockFunction]}
|
||||
onLayout={[Function]}
|
||||
onMomentumScrollBegin={[Function]}
|
||||
onMomentumScrollEnd={[Function]}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import React, {type ComponentProps} from 'react';
|
||||
import {Image} from 'react-native';
|
||||
|
||||
import {Screens} from '@constants';
|
||||
import {Ringtone} from '@constants/calls';
|
||||
import {renderWithEverything} from '@test/intl-test-helper';
|
||||
import TestHelper from '@test/test_helper';
|
||||
|
|
@ -108,24 +109,26 @@ describe('components/channel_list_row', () => {
|
|||
Image.resolveAssetSource = originalResolveAssetSource;
|
||||
});
|
||||
|
||||
function getBaseProps(): ComponentProps<typeof UserList> {
|
||||
return {
|
||||
profiles: [],
|
||||
testID: 'UserListRow',
|
||||
currentUserId: '1',
|
||||
handleSelectProfile: jest.fn(),
|
||||
fetchMore: jest.fn(),
|
||||
loading: true,
|
||||
selectedIds: {},
|
||||
showNoResults: true,
|
||||
tutorialWatched: true,
|
||||
term: 'some term',
|
||||
location: Screens.CHANNEL,
|
||||
};
|
||||
}
|
||||
|
||||
it('should show no results', () => {
|
||||
const props = getBaseProps();
|
||||
const wrapper = renderWithEverything(
|
||||
<UserList
|
||||
profiles={[]}
|
||||
testID='UserListRow'
|
||||
currentUserId={'1'}
|
||||
handleSelectProfile={() => {
|
||||
// noop
|
||||
}}
|
||||
fetchMore={() => {
|
||||
// noop
|
||||
}}
|
||||
loading={true}
|
||||
selectedIds={{}}
|
||||
term={'some term'}
|
||||
showNoResults={true}
|
||||
tutorialWatched={true}
|
||||
/>,
|
||||
<UserList {...props}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
|
|
@ -133,22 +136,12 @@ describe('components/channel_list_row', () => {
|
|||
});
|
||||
|
||||
it('should show results no tutorial', () => {
|
||||
const props = getBaseProps();
|
||||
props.profiles = [user];
|
||||
props.term = '';
|
||||
|
||||
const wrapper = renderWithEverything(
|
||||
<UserList
|
||||
profiles={[user]}
|
||||
testID='UserListRow'
|
||||
currentUserId={'1'}
|
||||
handleSelectProfile={() => {
|
||||
// noop
|
||||
}}
|
||||
fetchMore={() => {
|
||||
// noop
|
||||
}}
|
||||
loading={true}
|
||||
selectedIds={{}}
|
||||
showNoResults={true}
|
||||
tutorialWatched={true}
|
||||
/>,
|
||||
<UserList {...props}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
|
|
@ -156,22 +149,11 @@ describe('components/channel_list_row', () => {
|
|||
});
|
||||
|
||||
it('should show results no tutorial 2 users', () => {
|
||||
const props = getBaseProps();
|
||||
props.profiles = [user, user2];
|
||||
props.term = '';
|
||||
const wrapper = renderWithEverything(
|
||||
<UserList
|
||||
profiles={[user, user2]}
|
||||
testID='UserListRow'
|
||||
currentUserId={'1'}
|
||||
handleSelectProfile={() => {
|
||||
// noop
|
||||
}}
|
||||
fetchMore={() => {
|
||||
// noop
|
||||
}}
|
||||
loading={true}
|
||||
selectedIds={{}}
|
||||
showNoResults={true}
|
||||
tutorialWatched={true}
|
||||
/>,
|
||||
<UserList {...props}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
|
|
@ -179,22 +161,13 @@ describe('components/channel_list_row', () => {
|
|||
});
|
||||
|
||||
it('should show results and tutorial', () => {
|
||||
const props = getBaseProps();
|
||||
props.profiles = [user];
|
||||
props.showNoResults = false;
|
||||
props.tutorialWatched = false;
|
||||
props.term = '';
|
||||
const wrapper = renderWithEverything(
|
||||
<UserList
|
||||
profiles={[user]}
|
||||
testID='UserListRow'
|
||||
currentUserId={'1'}
|
||||
handleSelectProfile={() => {
|
||||
// noop
|
||||
}}
|
||||
fetchMore={() => {
|
||||
// noop
|
||||
}}
|
||||
loading={true}
|
||||
selectedIds={{}}
|
||||
showNoResults={false}
|
||||
tutorialWatched={false}
|
||||
/>,
|
||||
<UserList {...props}/>,
|
||||
{database},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ import {storeProfile} from '@actions/local/user';
|
|||
import Loading from '@components/loading';
|
||||
import NoResultsWithTerm from '@components/no_results_with_term';
|
||||
import UserListRow from '@components/user_list_row';
|
||||
import {General, Screens} from '@constants';
|
||||
import {General} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useKeyboardHeight} from '@hooks/device';
|
||||
import {openAsBottomSheet} from '@screens/navigation';
|
||||
import {openUserProfileModal} from '@screens/navigation';
|
||||
import {
|
||||
changeOpacity,
|
||||
makeStyleSheetFromTheme,
|
||||
|
|
@ -21,6 +21,7 @@ import {
|
|||
import {typography} from '@utils/typography';
|
||||
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type UserProfileWithChannelAdmin = UserProfile & {scheme_admin?: boolean}
|
||||
type RenderItemType = ListRenderItemInfo<UserProfileWithChannelAdmin> & {section?: SectionListData<UserProfileWithChannelAdmin>}
|
||||
|
|
@ -183,6 +184,7 @@ type Props = {
|
|||
term?: string;
|
||||
tutorialWatched: boolean;
|
||||
includeUserMargin?: boolean;
|
||||
location: AvailableScreens;
|
||||
}
|
||||
|
||||
export default function UserList({
|
||||
|
|
@ -200,6 +202,7 @@ export default function UserList({
|
|||
testID,
|
||||
tutorialWatched,
|
||||
includeUserMargin,
|
||||
location,
|
||||
}: Props) {
|
||||
const intl = useIntl();
|
||||
const theme = useTheme();
|
||||
|
|
@ -235,18 +238,11 @@ export default function UserList({
|
|||
user = profile;
|
||||
}
|
||||
|
||||
const screen = Screens.USER_PROFILE;
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const closeButtonId = 'close-user-profile';
|
||||
const props = {
|
||||
closeButtonId,
|
||||
openUserProfileModal(intl, theme, {
|
||||
userId: user.id,
|
||||
location: Screens.USER_PROFILE,
|
||||
};
|
||||
|
||||
Keyboard.dismiss();
|
||||
openAsBottomSheet({screen, title, theme, closeButtonId, props});
|
||||
}, [intl, serverUrl, theme]);
|
||||
location,
|
||||
});
|
||||
}, [intl, location, serverUrl, theme]);
|
||||
|
||||
const renderItem = useCallback(({item, index, section}: RenderItemType) => {
|
||||
// The list will re-render when the selection changes because it's passed into the list as extraData
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ jest.mock('react-native-navigation', () => ({
|
|||
pop: jest.fn(() => Promise.resolve({
|
||||
catch: jest.fn(),
|
||||
})),
|
||||
setDefaultOptions: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import DatabaseManager from '@database/manager';
|
|||
import NetworkManager from '@managers/network_manager';
|
||||
import {queryAllActiveServers} from '@queries/app/servers';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
import {openAsBottomSheet} from '@screens/navigation';
|
||||
import {openUserProfileModal, openAsBottomSheet} from '@screens/navigation';
|
||||
|
||||
import {useTryCallsFunction, usePermissionsChecker, useCallsAdjustment, useHostControlsAvailable, useHostMenus} from './hooks';
|
||||
|
||||
|
|
@ -74,6 +74,7 @@ jest.mock('@queries/servers/user', () => ({
|
|||
|
||||
jest.mock('@screens/navigation', () => ({
|
||||
openAsBottomSheet: jest.fn(),
|
||||
openUserProfileModal: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('Calls Hooks', () => {
|
||||
|
|
@ -267,9 +268,13 @@ describe('Calls Hooks', () => {
|
|||
await result.current.onPress(mockSession)();
|
||||
});
|
||||
|
||||
expect(openAsBottomSheet).toHaveBeenCalledWith(expect.objectContaining({
|
||||
screen: Screens.USER_PROFILE,
|
||||
}));
|
||||
expect(openUserProfileModal).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
expect.any(Object),
|
||||
expect.objectContaining({
|
||||
userId: mockSession.userId,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import {useAppState} from '@hooks/device';
|
|||
import NetworkManager from '@managers/network_manager';
|
||||
import {queryAllActiveServers} from '@queries/app/servers';
|
||||
import {getCurrentUser} from '@queries/servers/user';
|
||||
import {openAsBottomSheet} from '@screens/navigation';
|
||||
import {openAsBottomSheet, openUserProfileModal} from '@screens/navigation';
|
||||
import {getFullErrorMessage} from '@utils/errors';
|
||||
import {isSystemAdmin} from '@utils/user';
|
||||
|
||||
|
|
@ -211,12 +211,9 @@ export const useHostMenus = () => {
|
|||
}, [intl, theme]);
|
||||
|
||||
const openUserProfile = useCallback(async (session: CallSession) => {
|
||||
const screen = Screens.USER_PROFILE;
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const closeUserProfile = 'close-user-profile';
|
||||
const props = {closeButtonId: closeUserProfile, location: '', userId: session.userId};
|
||||
|
||||
openAsBottomSheet({screen, title, theme, closeButtonId: closeUserProfile, props});
|
||||
openUserProfileModal(intl, theme, {
|
||||
userId: session.userId,
|
||||
});
|
||||
}, [intl, theme]);
|
||||
|
||||
const onPress = useCallback((session: CallSession) => () => {
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ jest.mock('react-native-navigation', () => ({
|
|||
pop: jest.fn(() => Promise.resolve({
|
||||
catch: jest.fn(),
|
||||
})),
|
||||
setDefaultOptions: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {SafeAreaView} from 'react-native-safe-area-context';
|
|||
import {handleGotoLocation} from '@actions/remote/command';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import Markdown from '@components/markdown';
|
||||
import {Screens} from '@constants';
|
||||
import {AppCallResponseTypes} from '@constants/apps';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
|
|
@ -401,7 +402,7 @@ function AppsFormComponent({
|
|||
baseTextStyle={style.errorLabel}
|
||||
textStyles={getMarkdownTextStyles(theme)}
|
||||
blockStyles={getMarkdownBlockStyles(theme)}
|
||||
location=''
|
||||
location={Screens.APPS_FORM}
|
||||
disableAtMentions={true}
|
||||
value={error}
|
||||
theme={theme}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import AutocompleteSelector from '@components/autocomplete_selector';
|
|||
import Markdown from '@components/markdown';
|
||||
import BoolSetting from '@components/settings/bool_setting';
|
||||
import TextSetting from '@components/settings/text_setting';
|
||||
import {View as ViewConstants} from '@constants';
|
||||
import {Screens, View as ViewConstants} from '@constants';
|
||||
import {AppFieldTypes, SelectableAppFieldTypes} from '@constants/apps';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {selectKeyboardType} from '@utils/integrations';
|
||||
|
|
@ -161,6 +161,7 @@ function AppsFormField({
|
|||
secureTextEntry={field.subtype === 'password'}
|
||||
disabled={Boolean(field.readonly)}
|
||||
testID={testID}
|
||||
location={Screens.APPS_FORM}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -185,6 +186,7 @@ function AppsFormField({
|
|||
disabled={field.readonly}
|
||||
isMultiselect={field.multiselect}
|
||||
testID={testID}
|
||||
location={Screens.APPS_FORM}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -200,6 +202,7 @@ function AppsFormField({
|
|||
onChange={handleChange}
|
||||
disabled={field.readonly}
|
||||
testID={testID}
|
||||
location={Screens.APPS_FORM}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -216,7 +219,7 @@ function AppsFormField({
|
|||
value={field.description}
|
||||
mentionKeys={[]}
|
||||
disableAtMentions={true}
|
||||
location=''
|
||||
location={Screens.APPS_FORM}
|
||||
blockStyles={getMarkdownBlockStyles(theme)}
|
||||
textStyles={getMarkdownTextStyles(theme)}
|
||||
baseTextStyle={style.markdownFieldText}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import FloatingCallContainer from '@calls/components/floating_call_container';
|
|||
import FreezeScreen from '@components/freeze_screen';
|
||||
import PostDraft from '@components/post_draft';
|
||||
import ScheduledPostIndicator from '@components/scheduled_post_indicator';
|
||||
import {Screens} from '@constants';
|
||||
import {ExtraKeyboardProvider} from '@context/extra_keyboard';
|
||||
import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
|
||||
import {useChannelSwitch} from '@hooks/channel_switch';
|
||||
|
|
@ -152,6 +153,7 @@ const Channel = ({
|
|||
containerHeight={containerHeight}
|
||||
isChannelScreen={true}
|
||||
canShowPostPriority={true}
|
||||
location={Screens.CHANNEL}
|
||||
/>
|
||||
</ExtraKeyboardProvider>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
|
||||
import React, {useCallback} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Keyboard, type StyleProp, StyleSheet, type ViewStyle} from 'react-native';
|
||||
import {type StyleProp, StyleSheet, type ViewStyle} from 'react-native';
|
||||
|
||||
import ProfilePicture from '@components/profile_picture';
|
||||
import TouchableWithFeedback from '@components/touchable_with_feedback';
|
||||
import {Screens} from '@constants';
|
||||
import {openAsBottomSheet} from '@screens/navigation';
|
||||
import {openUserProfileModal} from '@screens/navigation';
|
||||
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
|
|
@ -32,13 +32,11 @@ const styles = StyleSheet.create({
|
|||
const Member = ({channelId, containerStyle, size = 72, showStatus = true, theme, user}: Props) => {
|
||||
const intl = useIntl();
|
||||
const onPress = useCallback(() => {
|
||||
const screen = Screens.USER_PROFILE;
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const closeButtonId = 'close-user-profile';
|
||||
const props = {closeButtonId, userId: user.id, channelId, location: Screens.CHANNEL};
|
||||
|
||||
Keyboard.dismiss();
|
||||
openAsBottomSheet({screen, title, theme, closeButtonId, props});
|
||||
openUserProfileModal(intl, theme, {
|
||||
userId: user.id,
|
||||
channelId,
|
||||
location: Screens.CHANNEL,
|
||||
});
|
||||
}, [intl, user.id, channelId, theme]);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import Loading from '@components/loading';
|
|||
import Search from '@components/search';
|
||||
import SelectedUsers from '@components/selected_users';
|
||||
import ServerUserList from '@components/server_user_list';
|
||||
import {General} from '@constants';
|
||||
import {General, Screens} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
|
||||
|
|
@ -281,6 +281,7 @@ export default function ChannelAddMembers({
|
|||
fetchFunction={userFetchFunction}
|
||||
searchFunction={userSearchFunction}
|
||||
createFilter={createUserFilter}
|
||||
location={Screens.CHANNEL_ADD_MEMBERS}
|
||||
/>
|
||||
<SelectedUsers
|
||||
keyboardOverlap={keyboardOverlap}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import React, {useCallback, useMemo, useState} from 'react';
|
|||
import AutocompleteSelector from '@components/autocomplete_selector';
|
||||
import BoolSetting from '@components/settings/bool_setting';
|
||||
import TextSetting from '@components/settings/text_setting';
|
||||
import {Screens} from '@constants';
|
||||
|
||||
type HookResult<T> = [
|
||||
{[x: string]: T},
|
||||
|
|
@ -28,6 +29,7 @@ export const useStringProp = (
|
|||
secureTextEntry={false}
|
||||
testID={`${propName}.input`}
|
||||
value={value}
|
||||
location={Screens.COMPONENT_LIBRARY}
|
||||
/>
|
||||
), [value, propName, isTextarea]);
|
||||
const preparedProp = useMemo(() => ({[propName]: value}), [propName, value]);
|
||||
|
|
@ -46,6 +48,7 @@ export const useBooleanProp = (
|
|||
testID={`${propName}.input`}
|
||||
value={value}
|
||||
label={propName}
|
||||
location={Screens.COMPONENT_LIBRARY}
|
||||
/>
|
||||
), [propName, value]);
|
||||
const preparedProp = useMemo(() => ({[propName]: value}), [propName, value]);
|
||||
|
|
@ -99,6 +102,7 @@ export const useDropdownProp = (
|
|||
onSelected={onChange}
|
||||
options={renderedOptions}
|
||||
selected={value}
|
||||
location={Screens.COMPONENT_LIBRARY}
|
||||
/>
|
||||
), [onChange, propName, renderedOptions, value]);
|
||||
const preparedProp = useMemo(() => (value === ALL_OPTION ? undefined : ({[propName]: value})), [propName, value]);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import React, {useCallback, useMemo, useState} from 'react';
|
|||
import {ScrollView, View, type StyleProp, type ViewStyle} from 'react-native';
|
||||
|
||||
import AutocompleteSelector from '@components/autocomplete_selector';
|
||||
import {Preferences} from '@constants';
|
||||
import {Preferences, Screens} from '@constants';
|
||||
import {CustomThemeProvider} from '@context/theme';
|
||||
import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
|
||||
import SecurityManager from '@managers/security_manager';
|
||||
|
|
@ -122,6 +122,7 @@ const ComponentLibrary = ({componentId}: Props) => {
|
|||
onSelected={onSelectComponent}
|
||||
selected={selectedComponent}
|
||||
options={componentOptions}
|
||||
location={Screens.COMPONENT_LIBRARY}
|
||||
/>
|
||||
<AutocompleteSelector
|
||||
testID='selectedTheme'
|
||||
|
|
@ -129,6 +130,7 @@ const ComponentLibrary = ({componentId}: Props) => {
|
|||
onSelected={onSelectTheme}
|
||||
selected={selectedTheme}
|
||||
options={themeOptions}
|
||||
location={Screens.COMPONENT_LIBRARY}
|
||||
/>
|
||||
<AutocompleteSelector
|
||||
testID='selectedBackground'
|
||||
|
|
@ -136,6 +138,7 @@ const ComponentLibrary = ({componentId}: Props) => {
|
|||
onSelected={onSelectBackground}
|
||||
selected={selectedBackground}
|
||||
options={backgroundOptions}
|
||||
location={Screens.COMPONENT_LIBRARY}
|
||||
/>
|
||||
<View style={backgroundStyle}>
|
||||
<CustomThemeProvider theme={Preferences.THEMES[selectedTheme]}>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import Loading from '@components/loading';
|
|||
import Search from '@components/search';
|
||||
import SelectedUsers from '@components/selected_users';
|
||||
import ServerUserList from '@components/server_user_list';
|
||||
import {General} from '@constants';
|
||||
import {General, Screens} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
|
||||
|
|
@ -326,6 +326,7 @@ export default function CreateDirectMessage({
|
|||
fetchFunction={userFetchFunction}
|
||||
searchFunction={userSearchFunction}
|
||||
createFilter={createUserFilter}
|
||||
location={Screens.CREATE_DIRECT_MESSAGE}
|
||||
/>
|
||||
<SelectedUsers
|
||||
keyboardOverlap={keyboardOverlap}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {fireEvent, waitFor} from '@testing-library/react-native';
|
|||
import React from 'react';
|
||||
import {DeviceEventEmitter} from 'react-native';
|
||||
|
||||
import {Events} from '@constants';
|
||||
import {Events, Screens} from '@constants';
|
||||
import {renderWithIntl} from '@test/intl-test-helper';
|
||||
import * as DraftUtils from '@utils/draft';
|
||||
import * as ScheduledPostUtils from '@utils/scheduled_post';
|
||||
|
|
@ -65,7 +65,7 @@ describe('DraftAndScheduledPostSwipeActions', () => {
|
|||
<DraftAndScheduledPostSwipeActions
|
||||
draftType={DRAFT_TYPE_DRAFT}
|
||||
item={mockDraft}
|
||||
location='global_drafts'
|
||||
location={Screens.GLOBAL_DRAFTS}
|
||||
layoutWidth={300}
|
||||
/>,
|
||||
);
|
||||
|
|
@ -79,7 +79,7 @@ describe('DraftAndScheduledPostSwipeActions', () => {
|
|||
<DraftAndScheduledPostSwipeActions
|
||||
draftType={DRAFT_TYPE_SCHEDULED}
|
||||
item={mockScheduledPost}
|
||||
location='global_drafts'
|
||||
location={Screens.GLOBAL_DRAFTS}
|
||||
layoutWidth={300}
|
||||
/>,
|
||||
);
|
||||
|
|
@ -95,7 +95,7 @@ describe('DraftAndScheduledPostSwipeActions', () => {
|
|||
<DraftAndScheduledPostSwipeActions
|
||||
draftType={DRAFT_TYPE_DRAFT}
|
||||
item={mockDraft}
|
||||
location='global_drafts'
|
||||
location={Screens.GLOBAL_DRAFTS}
|
||||
layoutWidth={300}
|
||||
/>,
|
||||
);
|
||||
|
|
@ -110,7 +110,7 @@ describe('DraftAndScheduledPostSwipeActions', () => {
|
|||
<DraftAndScheduledPostSwipeActions
|
||||
draftType={DRAFT_TYPE_DRAFT}
|
||||
item={mockDraft}
|
||||
location='global_drafts'
|
||||
location={Screens.GLOBAL_DRAFTS}
|
||||
layoutWidth={300}
|
||||
/>,
|
||||
);
|
||||
|
|
@ -134,7 +134,7 @@ describe('DraftAndScheduledPostSwipeActions', () => {
|
|||
<DraftAndScheduledPostSwipeActions
|
||||
draftType={DRAFT_TYPE_SCHEDULED}
|
||||
item={mockScheduledPost}
|
||||
location='global_drafts'
|
||||
location={Screens.GLOBAL_DRAFTS}
|
||||
layoutWidth={300}
|
||||
/>,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,11 +22,12 @@ import {typography} from '@utils/typography';
|
|||
|
||||
import type DraftModel from '@typings/database/models/servers/draft';
|
||||
import type ScheduledPostModel from '@typings/database/models/servers/scheduled_post';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
draftType: DraftType;
|
||||
item: DraftModel | ScheduledPostModel;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
layoutWidth: number;
|
||||
firstItem?: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,11 @@ import DraftAndScheduledPostSwipeActions from '../draft_and_scheduled_post_swipe
|
|||
import DraftEmptyComponent from '../draft_empty_component';
|
||||
|
||||
import type DraftModel from '@typings/database/models/servers/draft';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
allDrafts: DraftModel[];
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
tutorialWatched: boolean;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,10 +21,11 @@ import DraftAndScheduledPostSwipeActions from '../draft_and_scheduled_post_swipe
|
|||
import ScheduledPostEmptyComponent from '../scheduled_post_empty_component';
|
||||
|
||||
import type ScheduledPostModel from '@typings/database/models/servers/scheduled_post';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
allScheduledPosts: ScheduledPostModel[];
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
tutorialWatched: boolean;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,11 +28,12 @@ import type ChannelModel from '@typings/database/models/servers/channel';
|
|||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
author?: UserModel;
|
||||
channel?: ChannelModel;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
post?: PostModel;
|
||||
teammateNameDisplay: string;
|
||||
testID: string;
|
||||
|
|
|
|||
|
|
@ -12,11 +12,12 @@ import {typography} from '@utils/typography';
|
|||
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
author?: UserModel;
|
||||
channelId: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
participants: UserModel[];
|
||||
testID: string;
|
||||
thread: ThreadModel;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {fetchProfiles, searchProfiles} from '@actions/remote/user';
|
|||
import FormattedText from '@components/formatted_text';
|
||||
import SearchBar from '@components/search';
|
||||
import ServerUserList from '@components/server_user_list';
|
||||
import {General, View as ViewConstants} from '@constants';
|
||||
import {General, Screens, View as ViewConstants} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {debounce} from '@helpers/api/general';
|
||||
|
|
@ -571,6 +571,7 @@ function IntegrationSelector(
|
|||
searchFunction={userSearchFunction}
|
||||
createFilter={createUserFilter}
|
||||
testID={'integration_selector.user_list'}
|
||||
location={Screens.INTEGRATION_SELECTOR}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import AutocompleteSelector from '@components/autocomplete_selector';
|
|||
import BoolSetting from '@components/settings/bool_setting';
|
||||
import RadioSetting from '@components/settings/radio_setting';
|
||||
import TextSetting from '@components/settings/text_setting';
|
||||
import {Screens} from '@constants';
|
||||
import {selectKeyboardType as selectKB} from '@utils/integrations';
|
||||
import {filterOptions} from '@utils/message_attachment';
|
||||
|
||||
|
|
@ -109,6 +110,7 @@ function DialogElement({
|
|||
secureTextEntry={subtype === 'password'}
|
||||
disabled={false}
|
||||
testID={testID}
|
||||
location={Screens.INTERACTIVE_DIALOG}
|
||||
/>
|
||||
);
|
||||
case 'select':
|
||||
|
|
@ -126,6 +128,7 @@ function DialogElement({
|
|||
selected={getStringValue(value)}
|
||||
roundedBorders={false}
|
||||
testID={testID}
|
||||
location={Screens.INTERACTIVE_DIALOG}
|
||||
/>
|
||||
);
|
||||
case 'radio':
|
||||
|
|
@ -138,6 +141,7 @@ function DialogElement({
|
|||
onChange={handleChange}
|
||||
testID={testID}
|
||||
value={getStringValue(value)}
|
||||
location={Screens.INTERACTIVE_DIALOG}
|
||||
/>
|
||||
);
|
||||
case 'bool':
|
||||
|
|
@ -151,6 +155,7 @@ function DialogElement({
|
|||
optional={optional}
|
||||
onChange={handleChange}
|
||||
testID={testID}
|
||||
location={Screens.INTERACTIVE_DIALOG}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import React from 'react';
|
|||
import {View} from 'react-native';
|
||||
|
||||
import Markdown from '@components/markdown';
|
||||
import {Screens} from '@constants';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {getMarkdownTextStyles, getMarkdownBlockStyles} from '@utils/markdown';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
|
@ -41,7 +42,7 @@ function DialogIntroductionText({value}: Props) {
|
|||
disableHashtags={true}
|
||||
disableAtMentions={true}
|
||||
disableChannelLink={true}
|
||||
location=''
|
||||
location={Screens.INTERACTIVE_DIALOG}
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -323,6 +323,7 @@ export default function ManageChannelMembers({
|
|||
tutorialWatched={tutorialWatched}
|
||||
includeUserMargin={true}
|
||||
fetchMore={handleReachedBottom}
|
||||
location={Screens.MANAGE_CHANNEL_MEMBERS}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
|
|
|
|||
107
app/screens/navigation.test.ts
Normal file
107
app/screens/navigation.test.ts
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import {DeviceEventEmitter, Keyboard, type EmitterSubscription} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import {Events, Preferences, Screens} from '@constants';
|
||||
import NavigationStore from '@store/navigation_store';
|
||||
|
||||
import {openAsBottomSheet, openUserProfileModal} from './navigation';
|
||||
|
||||
import type {FirstArgument} from '@typings/utils/utils';
|
||||
import type {IntlShape} from 'react-intl';
|
||||
|
||||
function expectShowModalCalledWith(screen: string, title: string, props?: Record<string, unknown>) {
|
||||
expect(Navigation.showModal).toHaveBeenCalledWith({
|
||||
stack: {
|
||||
children: [{
|
||||
component: {
|
||||
id: screen,
|
||||
name: screen,
|
||||
passProps: {
|
||||
...props,
|
||||
isModal: true,
|
||||
},
|
||||
options: expect.any(Object),
|
||||
},
|
||||
}],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function expectShowModalOverCurrentContext(screen: string, props?: Record<string, unknown>) {
|
||||
expectShowModalCalledWith(screen, '', props);
|
||||
}
|
||||
|
||||
function expectOpenAsBottomSheetCalledWith(props: FirstArgument<typeof openAsBottomSheet>, isTablet: boolean) {
|
||||
if (isTablet) {
|
||||
expectShowModalCalledWith(props.screen, props.title, {closeButtonId: props.closeButtonId, ...props.props});
|
||||
} else {
|
||||
expectShowModalOverCurrentContext(props.screen, props.props);
|
||||
}
|
||||
}
|
||||
|
||||
function expectDismissBottomSheetCalledWith(screenToDismiss: string, listenerCallback: jest.Mock) {
|
||||
expect(listenerCallback).toHaveBeenCalled();
|
||||
expect(NavigationStore.waitUntilScreensIsRemoved).toHaveBeenCalledWith(screenToDismiss);
|
||||
}
|
||||
|
||||
function expectNotDismissBottomSheetCalledWith(listenerCallback: jest.Mock) {
|
||||
expect(listenerCallback).not.toHaveBeenCalled();
|
||||
expect(NavigationStore.waitUntilScreensIsRemoved).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
describe('openUserProfileModal', () => {
|
||||
const intl = {
|
||||
formatMessage: jest.fn(({defaultMessage}) => defaultMessage),
|
||||
} as unknown as IntlShape;
|
||||
const theme = Preferences.THEMES.denim;
|
||||
const props = {
|
||||
userId: 'user123',
|
||||
};
|
||||
|
||||
let eventSubscription: EmitterSubscription;
|
||||
const listenerCallback = jest.fn();
|
||||
|
||||
beforeAll(() => {
|
||||
eventSubscription = DeviceEventEmitter.addListener(Events.CLOSE_BOTTOM_SHEET, listenerCallback);
|
||||
jest.spyOn(NavigationStore, 'waitUntilScreensIsRemoved').mockImplementation();
|
||||
});
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
eventSubscription.remove();
|
||||
});
|
||||
|
||||
it('should dismiss the keyboard', () => {
|
||||
openUserProfileModal(intl, theme, props);
|
||||
expect(Keyboard.dismiss).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should dismiss the bottom sheet if screenToDismiss is provided', async () => {
|
||||
const screenToDismiss = Screens.BOTTOM_SHEET;
|
||||
await openUserProfileModal(intl, theme, props, screenToDismiss);
|
||||
expectDismissBottomSheetCalledWith(screenToDismiss, listenerCallback);
|
||||
expectOpenAsBottomSheetCalledWith({
|
||||
screen: Screens.USER_PROFILE,
|
||||
title: 'Profile',
|
||||
closeButtonId: 'close-user-profile',
|
||||
theme,
|
||||
props,
|
||||
}, false);
|
||||
});
|
||||
|
||||
it('should not call dismiss if no screenToDismiss is provided', async () => {
|
||||
await openUserProfileModal(intl, theme, props);
|
||||
expectNotDismissBottomSheetCalledWith(listenerCallback);
|
||||
expectOpenAsBottomSheetCalledWith({
|
||||
screen: Screens.USER_PROFILE,
|
||||
title: 'Profile',
|
||||
closeButtonId: 'close-user-profile',
|
||||
theme,
|
||||
props,
|
||||
}, false);
|
||||
});
|
||||
});
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
/* eslint-disable max-lines */
|
||||
|
||||
import merge from 'deepmerge';
|
||||
import {Appearance, DeviceEventEmitter, StatusBar, Platform, Alert, type EmitterSubscription} from 'react-native';
|
||||
import {Appearance, DeviceEventEmitter, StatusBar, Platform, Alert, type EmitterSubscription, Keyboard} from 'react-native';
|
||||
import {type ComponentWillAppearEvent, type ImageResource, type LayoutOrientation, Navigation, type Options, OptionsModalPresentationStyle, type OptionsTopBarButton, type ScreenPoppedEvent, type EventSubscription} from 'react-native-navigation';
|
||||
import tinyColor from 'tinycolor2';
|
||||
|
||||
|
|
@ -20,8 +20,11 @@ import {appearanceControlledScreens, mergeNavigationOptions} from '@utils/naviga
|
|||
import {changeOpacity, setNavigatorStyles} from '@utils/theme';
|
||||
|
||||
import type {BottomSheetFooterProps} from '@gorhom/bottom-sheet';
|
||||
import type {default as UserProfileScreen} from '@screens/user_profile';
|
||||
import type {LaunchProps} from '@typings/launch';
|
||||
import type {AvailableScreens, NavButtons} from '@typings/screens/navigation';
|
||||
import type {ComponentProps} from 'react';
|
||||
import type {IntlShape} from 'react-intl';
|
||||
|
||||
const alpha = {
|
||||
from: 0,
|
||||
|
|
@ -889,3 +892,20 @@ export async function findChannels(title: string, theme: Theme) {
|
|||
options,
|
||||
);
|
||||
}
|
||||
|
||||
export async function openUserProfileModal(
|
||||
intl: IntlShape,
|
||||
theme: Theme,
|
||||
props: Omit<ComponentProps<typeof UserProfileScreen>, 'closeButtonId'>,
|
||||
screenToDismiss?: AvailableScreens,
|
||||
) {
|
||||
if (screenToDismiss) {
|
||||
await dismissBottomSheet(screenToDismiss);
|
||||
}
|
||||
const screen = Screens.USER_PROFILE;
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const closeButtonId = 'close-user-profile';
|
||||
|
||||
Keyboard.dismiss();
|
||||
openAsBottomSheet({screen, title, theme, closeButtonId, props: {...props}});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ import EmojiBar from './emoji_bar';
|
|||
import ReactorsList from './reactors_list';
|
||||
|
||||
import type ReactionModel from '@typings/database/models/servers/reaction';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
initialEmoji: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
reactions?: ReactionModel[];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ import {useBottomSheetListsFix} from '@hooks/bottom_sheet_lists_fix';
|
|||
import Reactor from './reactor';
|
||||
|
||||
import type ReactionModel from '@typings/database/models/servers/reaction';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
reactions: ReactionModel[];
|
||||
type?: BottomSheetList;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,21 +3,21 @@
|
|||
|
||||
import React, {useEffect} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Keyboard} from 'react-native';
|
||||
|
||||
import {fetchUsersByIds} from '@actions/remote/user';
|
||||
import UserItem from '@components/user_item';
|
||||
import {Screens} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {dismissBottomSheet, openAsBottomSheet} from '@screens/navigation';
|
||||
import {openUserProfileModal} from '@screens/navigation';
|
||||
|
||||
import type ReactionModel from '@typings/database/models/servers/reaction';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
type Props = {
|
||||
channelId: string;
|
||||
location: string;
|
||||
location: AvailableScreens;
|
||||
reaction: ReactionModel;
|
||||
user?: UserModel;
|
||||
}
|
||||
|
|
@ -28,14 +28,11 @@ const Reactor = ({channelId, location, reaction, user}: Props) => {
|
|||
const serverUrl = useServerUrl();
|
||||
const openUserProfile = async () => {
|
||||
if (user) {
|
||||
await dismissBottomSheet(Screens.REACTIONS);
|
||||
const screen = Screens.USER_PROFILE;
|
||||
const title = intl.formatMessage({id: 'mobile.routes.user_profile', defaultMessage: 'Profile'});
|
||||
const closeButtonId = 'close-user-profile';
|
||||
const props = {closeButtonId, location, userId: user.id, channelId};
|
||||
|
||||
Keyboard.dismiss();
|
||||
openAsBottomSheet({screen, title, theme, closeButtonId, props});
|
||||
openUserProfileModal(intl, theme, {
|
||||
userId: user.id,
|
||||
channelId,
|
||||
location,
|
||||
}, Screens.REACTIONS);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {View} from 'react-native';
|
|||
|
||||
import {sendTestNotification} from '@actions/remote/notifications';
|
||||
import SectionNotice from '@components/section_notice';
|
||||
import {Screens} from '@constants';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useExternalLink} from '@hooks/use_external_link';
|
||||
import {isMinimumServerVersion} from '@utils/helpers';
|
||||
|
|
@ -131,6 +132,7 @@ const SendTestNotificationNotice = ({
|
|||
primaryButton={primaryButton}
|
||||
secondaryButton={secondaryButton}
|
||||
type='hint'
|
||||
location={Screens.SETTINGS_NOTIFICATION_PUSH}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ jest.mock('react-native-navigation', () => ({
|
|||
registerComponentDidDisappearListener: jest.fn(),
|
||||
}),
|
||||
dismissOverlay: jest.fn(),
|
||||
setDefaultOptions: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ const Thread = ({
|
|||
testID='thread.post_draft'
|
||||
containerHeight={containerHeight}
|
||||
isChannelScreen={false}
|
||||
location={Screens.THREAD}
|
||||
/>
|
||||
</ExtraKeyboardProvider>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,12 @@ import {useTheme} from '@context/theme';
|
|||
import {dismissBottomSheet} from '@screens/navigation';
|
||||
import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles';
|
||||
|
||||
import type {AvailableScreens} from '@typings/screens/navigation';
|
||||
|
||||
export type OptionsType = 'all' | 'message';
|
||||
|
||||
type Props = {
|
||||
location: string;
|
||||
location?: AvailableScreens;
|
||||
type: OptionsType;
|
||||
userId: string;
|
||||
username: string;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ type Props = {
|
|||
isSystemAdmin: boolean;
|
||||
isTeamAdmin: boolean;
|
||||
manageMode?: boolean;
|
||||
location: AvailableScreens;
|
||||
location?: AvailableScreens;
|
||||
teamId: string;
|
||||
teammateDisplayName: string;
|
||||
user: UserModel;
|
||||
|
|
@ -91,7 +91,7 @@ const UserProfile = ({
|
|||
}: Props) => {
|
||||
const {formatMessage, locale} = useIntl();
|
||||
const serverUrl = useServerUrl();
|
||||
const channelContext = channelContextScreens.includes(location);
|
||||
const channelContext = location ? channelContextScreens.includes(location) : false;
|
||||
const showOptions: OptionsType = channelContext && !user.isBot ? 'all' : 'message';
|
||||
const override = Boolean(userIconOverride || usernameOverride);
|
||||
const {bottom} = useSafeAreaInsets();
|
||||
|
|
|
|||
|
|
@ -21,35 +21,6 @@ import {
|
|||
alertSlashCommandFailed,
|
||||
} from './';
|
||||
|
||||
jest.mock('react-native', () => {
|
||||
const RN = jest.requireActual('react-native');
|
||||
return {
|
||||
Platform: RN.Platform,
|
||||
NativeModules: {
|
||||
...RN.NativeModules,
|
||||
RNUtils: {
|
||||
getConstants: () => ({
|
||||
appGroupIdentifier: 'group.mattermost.rnbeta',
|
||||
appGroupSharedDirectory: {
|
||||
sharedDirectory: '',
|
||||
databasePath: '',
|
||||
},
|
||||
}),
|
||||
addListener: jest.fn(),
|
||||
removeListeners: jest.fn(),
|
||||
isRunningInSplitView: jest.fn().mockReturnValue({isSplit: false, isTablet: false}),
|
||||
getDeliveredNotifications: jest.fn().mockResolvedValue([]),
|
||||
removeChannelNotifications: jest.fn().mockImplementation(),
|
||||
removeThreadNotifications: jest.fn().mockImplementation(),
|
||||
removeServerNotifications: jest.fn().mockImplementation(),
|
||||
},
|
||||
},
|
||||
Alert: {
|
||||
alert: jest.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('@i18n', () => ({
|
||||
t: (id: string) => id,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ jest.doMock('react-native', () => {
|
|||
InteractionManager: RNInteractionManager,
|
||||
NativeModules: RNNativeModules,
|
||||
Linking: RNLinking,
|
||||
Keyboard: RNKeyboard,
|
||||
} = ReactNative;
|
||||
|
||||
const Alert = {
|
||||
|
|
@ -250,6 +251,14 @@ jest.doMock('react-native', () => {
|
|||
}),
|
||||
};
|
||||
|
||||
const Keyboard = {
|
||||
...RNKeyboard,
|
||||
dismiss: jest.fn(),
|
||||
addListener: jest.fn(() => ({
|
||||
remove: jest.fn(),
|
||||
})),
|
||||
};
|
||||
|
||||
return Object.setPrototypeOf({
|
||||
Platform: {
|
||||
...Platform,
|
||||
|
|
@ -270,6 +279,7 @@ jest.doMock('react-native', () => {
|
|||
InteractionManager,
|
||||
NativeModules,
|
||||
Linking,
|
||||
Keyboard,
|
||||
Animated: {
|
||||
...ReactNative.Animated,
|
||||
timing: jest.fn(() => ({
|
||||
|
|
@ -393,6 +403,7 @@ jest.mock('react-native-share', () => ({
|
|||
}));
|
||||
|
||||
jest.mock('@screens/navigation', () => ({
|
||||
...jest.requireActual('@screens/navigation'),
|
||||
resetToChannel: jest.fn(),
|
||||
resetToSelectServer: jest.fn(),
|
||||
resetToTeams: jest.fn(),
|
||||
|
|
|
|||
4
types/utils/utils.ts
Normal file
4
types/utils/utils.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export type FirstArgument<T> = T extends (...args: infer P) => any ? P[0] : never;
|
||||
Loading…
Reference in a new issue