(cherry picked from commit f911a29e4c)
Co-authored-by: Daniel Espino García <larkox@gmail.com>
This commit is contained in:
parent
64581b8b2d
commit
68aa016921
24 changed files with 84 additions and 49 deletions
|
|
@ -19,6 +19,7 @@ import {getUserById, observeTeammateNameDisplay} from '@queries/servers/user';
|
|||
import {goToScreen} from '@screens/navigation';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
import {displayUsername} from '@utils/user';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
|
@ -93,7 +94,7 @@ async function getItemName(serverUrl: string, selected: string, teammateNameDisp
|
|||
return '';
|
||||
}
|
||||
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
const database = secureGetFromRecord(DatabaseManager.serverDatabases, serverUrl)?.database;
|
||||
|
||||
switch (dataSource) {
|
||||
case ViewConstants.DATA_SOURCE_USERS: {
|
||||
|
|
|
|||
|
|
@ -46,9 +46,7 @@ const FormattedText = (props: FormattedTextProps) => {
|
|||
// when the `message` is formatted. This allows the formatted
|
||||
// message to then be broken-up into parts with references to the
|
||||
// React Elements inserted back in.
|
||||
Object.keys(values).forEach((name) => {
|
||||
const value = values[name];
|
||||
|
||||
Object.entries(values).forEach(([name, value]) => {
|
||||
if (isValidElement(value)) {
|
||||
const token = generateToken();
|
||||
tokenizedValues[name] = tokenDelimiter + token + tokenDelimiter;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {useServerUrl} from '@context/server';
|
|||
import {t} from '@i18n';
|
||||
import {alertErrorWithFallback} from '@utils/draft';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type TeamModel from '@typings/database/models/servers/team';
|
||||
|
|
@ -40,7 +41,7 @@ function getChannelFromChannelName(name: string, channels: ChannelModel[], chann
|
|||
});
|
||||
|
||||
while (channelName.length > 0) {
|
||||
if (channelsByName[channelName]) {
|
||||
if (secureGetFromRecord(channelsByName, channelName)) {
|
||||
return channelsByName[channelName];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import {bottomSheetSnapPoint} from '@utils/helpers';
|
|||
import {calculateDimensions, getViewPortWidth, isGifTooLarge} from '@utils/images';
|
||||
import {getMarkdownImageSize} from '@utils/markdown';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
import {normalizeProtocol, tryOpenURL} from '@utils/url';
|
||||
|
||||
import type {GalleryItemType} from '@typings/screens/gallery';
|
||||
|
|
@ -80,7 +81,7 @@ const MarkdownImage = ({
|
|||
const style = getStyleSheet(theme);
|
||||
const managedConfig = useManagedConfig<ManagedConfig>();
|
||||
const genericFileId = useRef(generateId('uid')).current;
|
||||
const metadata = imagesMetadata?.[source] || Object.values(imagesMetadata || {})[0];
|
||||
const metadata = secureGetFromRecord(imagesMetadata, source) || Object.values(imagesMetadata || {})[0];
|
||||
const [failed, setFailed] = useState(isGifTooLarge(metadata));
|
||||
const originalSize = getMarkdownImageSize(isReplyPost, isTablet, sourceSize, metadata, layoutWidth, layoutHeight);
|
||||
const serverUrl = useServerUrl();
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {useGalleryItem} from '@hooks/gallery';
|
|||
import {fileToGalleryItem, openGalleryAtIndex} from '@utils/gallery';
|
||||
import {generateId} from '@utils/general';
|
||||
import {calculateDimensions, isGifTooLarge} from '@utils/images';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
|
||||
import type {GalleryItemType} from '@typings/screens/gallery';
|
||||
|
||||
|
|
@ -33,7 +34,7 @@ const style = StyleSheet.create({
|
|||
});
|
||||
|
||||
const MarkTableImage = ({disabled, imagesMetadata, location, postId, serverURL, source}: MarkdownTableImageProps) => {
|
||||
const metadata = imagesMetadata[source];
|
||||
const metadata = secureGetFromRecord(imagesMetadata, source);
|
||||
const fileId = useRef(generateId('uid')).current;
|
||||
const [failed, setFailed] = useState(isGifTooLarge(metadata));
|
||||
const currentServerUrl = useServerUrl();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {bottomSheetModalOptions, showModal, showModalOverCurrentContext} from '@
|
|||
import {emptyFunction} from '@utils/general';
|
||||
import {getMarkdownTextStyles} from '@utils/markdown';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
|
||||
import LastUsers from './last_users';
|
||||
import {postTypeMessages} from './messages';
|
||||
|
|
@ -76,7 +77,7 @@ const CombinedUserActivity = ({
|
|||
const usernamesValues = Object.values(usernamesById);
|
||||
const usernames = userIds.reduce((acc: string[], id: string) => {
|
||||
if (id !== currentUserId && id !== currentUsername) {
|
||||
const name = usernamesById[id] ?? usernamesValues.find((n) => n === id);
|
||||
const name = secureGetFromRecord(usernamesById, id) ?? usernamesValues.find((n) => n === id);
|
||||
acc.push(name ? `@${name}` : someone);
|
||||
}
|
||||
return acc;
|
||||
|
|
@ -112,7 +113,7 @@ const CombinedUserActivity = ({
|
|||
return null;
|
||||
}
|
||||
let actor = '';
|
||||
if (usernamesById[actorId]) {
|
||||
if (secureGetFromRecord(usernamesById, actorId)) {
|
||||
actor = `@${usernamesById[actorId]}`;
|
||||
}
|
||||
|
||||
|
|
@ -141,11 +142,11 @@ const CombinedUserActivity = ({
|
|||
const secondUser = usernames[1];
|
||||
let localeHolder;
|
||||
if (numOthers === 0) {
|
||||
localeHolder = postTypeMessages[postType].one;
|
||||
localeHolder = secureGetFromRecord(postTypeMessages, postType)?.one;
|
||||
|
||||
if (
|
||||
(userIds[0] === currentUserId || userIds[0] === currentUsername) &&
|
||||
postTypeMessages[postType].one_you
|
||||
secureGetFromRecord(postTypeMessages, postType)?.one_you
|
||||
) {
|
||||
localeHolder = postTypeMessages[postType].one_you;
|
||||
}
|
||||
|
|
@ -153,7 +154,8 @@ const CombinedUserActivity = ({
|
|||
localeHolder = postTypeMessages[postType].two;
|
||||
}
|
||||
|
||||
const formattedMessage = intl.formatMessage(localeHolder, {firstUser, secondUser, actor});
|
||||
// We default to empty string, but this should never happen
|
||||
const formattedMessage = localeHolder ? intl.formatMessage(localeHolder, {firstUser, secondUser, actor}) : '';
|
||||
return (
|
||||
<Markdown
|
||||
channelId={post.channel_id}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import FormattedText from '@components/formatted_text';
|
|||
import Markdown from '@components/markdown';
|
||||
import {getMarkdownTextStyles} from '@utils/markdown';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
|
||||
import {postTypeMessages, systemMessages} from './messages';
|
||||
|
||||
|
|
@ -50,12 +51,14 @@ const LastUsers = ({actor, channelId, location, postType, theme, usernames}: Las
|
|||
if (expanded) {
|
||||
const lastIndex = usernames.length - 1;
|
||||
const lastUser = usernames[lastIndex];
|
||||
const expandedMessage = postTypeMessages[postType].many_expanded;
|
||||
const formattedMessage = intl.formatMessage(expandedMessage, {
|
||||
const expandedMessage = secureGetFromRecord(postTypeMessages, postType)?.many_expanded;
|
||||
|
||||
// We default to empty string, but this should never happen
|
||||
const formattedMessage = expandedMessage ? intl.formatMessage(expandedMessage, {
|
||||
users: usernames.slice(0, lastIndex).join(', '),
|
||||
lastUser,
|
||||
actor,
|
||||
});
|
||||
}) : '';
|
||||
|
||||
return (
|
||||
<Markdown
|
||||
|
|
@ -72,6 +75,8 @@ const LastUsers = ({actor, channelId, location, postType, theme, usernames}: Las
|
|||
const firstUser = usernames[0];
|
||||
const numOthers = usernames.length - 1;
|
||||
|
||||
const message = secureGetFromRecord(systemMessages, postType);
|
||||
|
||||
return (
|
||||
<Text>
|
||||
<FormattedMarkdownText
|
||||
|
|
@ -96,8 +101,8 @@ const LastUsers = ({actor, channelId, location, postType, theme, usernames}: Las
|
|||
</Text>
|
||||
<FormattedMarkdownText
|
||||
channelId={channelId}
|
||||
id={systemMessages[postType].id}
|
||||
defaultMessage={systemMessages[postType].defaultMessage}
|
||||
id={message?.id || ''}
|
||||
defaultMessage={message?.defaultMessage || ''}
|
||||
location={location}
|
||||
values={{actor}}
|
||||
baseTextStyle={style.baseText}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {openGalleryAtIndex} from '@utils/gallery';
|
|||
import {generateId} from '@utils/general';
|
||||
import {calculateDimensions, getViewPortWidth, isGifTooLarge} from '@utils/images';
|
||||
import {changeOpacity} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
import {extractFilenameFromUrl, isImageLink, isValidUrl} from '@utils/url';
|
||||
|
||||
import type {GalleryItemType} from '@typings/screens/gallery';
|
||||
|
|
@ -55,7 +56,7 @@ const ImagePreview = ({expandedLink, isReplyPost, layoutWidth, link, location, m
|
|||
const fileId = useRef(generateId('uid')).current;
|
||||
const [imageUrl, setImageUrl] = useState(expandedLink || link);
|
||||
const isTablet = useIsTablet();
|
||||
const imageProps = metadata?.images?.[link];
|
||||
const imageProps = secureGetFromRecord(metadata?.images, link);
|
||||
const dimensions = calculateDimensions(imageProps?.height, imageProps?.width, layoutWidth || getViewPortWidth(isReplyPost, isTablet));
|
||||
|
||||
const onError = useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {useServerUrl} from '@context/server';
|
|||
import {getStatusColors} from '@utils/message_attachment_colors';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
|
||||
import ActionButtonText from './action_button_text';
|
||||
|
||||
|
|
@ -65,7 +66,7 @@ const ActionButton = ({buttonColor, cookie, disabled, id, name, postId, theme}:
|
|||
|
||||
if (buttonColor) {
|
||||
const STATUS_COLORS = getStatusColors(theme);
|
||||
const hexColor = STATUS_COLORS[buttonColor] || theme[buttonColor] || buttonColor;
|
||||
const hexColor = secureGetFromRecord(STATUS_COLORS, buttonColor) || secureGetFromRecord(theme, buttonColor) || buttonColor;
|
||||
customButtonStyle = {borderColor: changeOpacity(hexColor, 0.25), backgroundColor: '#ffffff'};
|
||||
customButtonTextStyle = {color: hexColor};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {View} from 'react-native';
|
|||
import {getMarkdownBlockStyles, getMarkdownTextStyles} from '@utils/markdown';
|
||||
import {getStatusColors} from '@utils/message_attachment_colors';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
import {isValidUrl} from '@utils/url';
|
||||
|
||||
import AttachmentActions from './attachment_actions';
|
||||
|
|
@ -62,7 +63,7 @@ export default function MessageAttachment({attachment, channelId, layoutWidth, l
|
|||
if (attachment.color) {
|
||||
if (attachment.color[0] === '#') {
|
||||
borderStyle = {borderLeftColor: attachment.color};
|
||||
} else if (STATUS_COLORS[attachment.color]) {
|
||||
} else if (secureGetFromRecord(STATUS_COLORS, attachment.color)) {
|
||||
borderStyle = {borderLeftColor: STATUS_COLORS[attachment.color]};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {isTablet} from '@utils/helpers';
|
|||
import {calculateDimensions} from '@utils/images';
|
||||
import {type BestImage, getNearestPoint} from '@utils/opengraph';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
import {extractFilenameFromUrl, isValidUrl} from '@utils/url';
|
||||
|
||||
import type {GalleryItemType} from '@typings/screens/gallery';
|
||||
|
|
@ -71,10 +72,7 @@ const OpengraphImage = ({isReplyPost, layoutWidth, location, metadata, openGraph
|
|||
const imageUrl = (bestImage.secure_url || bestImage.url)!;
|
||||
const imagesMetadata = metadata?.images;
|
||||
|
||||
let ogImage;
|
||||
if (imagesMetadata && imagesMetadata[imageUrl]) {
|
||||
ogImage = imagesMetadata[imageUrl];
|
||||
}
|
||||
let ogImage = secureGetFromRecord(imagesMetadata, imageUrl);
|
||||
|
||||
if (!ogImage) {
|
||||
ogImage = openGraphImages.find((i: BestImage) => i.url === imageUrl || i.secure_url === imageUrl);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {useTheme} from '@context/theme';
|
|||
import {t} from '@i18n';
|
||||
import {getMarkdownTextStyles} from '@utils/markdown';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
|
|
@ -276,7 +277,7 @@ export const SystemMessage = ({post, location, author, hideGuestTags}: SystemMes
|
|||
return renderAddGuestToChannelMessage({post, author, location, styles, intl, theme}, hideGuestTags);
|
||||
}
|
||||
|
||||
const renderer = systemMessageRenderers[post.type];
|
||||
const renderer = secureGetFromRecord(systemMessageRenderers, post.type);
|
||||
if (!renderer) {
|
||||
return (
|
||||
<Markdown
|
||||
|
|
|
|||
|
|
@ -163,15 +163,15 @@ export default function SelectedUsers({
|
|||
|
||||
const users = useMemo(() => {
|
||||
const u = [];
|
||||
for (const id of Object.keys(selectedIds)) {
|
||||
if (!selectedIds[id]) {
|
||||
for (const [id, user] of Object.entries(selectedIds)) {
|
||||
if (!user) {
|
||||
continue;
|
||||
}
|
||||
|
||||
u.push(
|
||||
<SelectedUser
|
||||
key={id}
|
||||
user={selectedIds[id]}
|
||||
user={user}
|
||||
teammateNameDisplay={teammateNameDisplay}
|
||||
onRemove={onRemove}
|
||||
testID={`${testID}.selected_user`}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import ThemeProvider from '@context/theme';
|
|||
import UserLocaleProvider from '@context/user_locale';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {subscribeActiveServers} from '@database/subscription/servers';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
|
||||
import type {Database} from '@nozbe/watermelondb';
|
||||
import type ServersModel from '@typings/database/models/app/servers';
|
||||
|
|
@ -31,7 +32,7 @@ export function withServerDatabase<T extends JSX.IntrinsicAttributes>(Component:
|
|||
|
||||
if (server) {
|
||||
const database =
|
||||
DatabaseManager.serverDatabases[server.url]?.database;
|
||||
secureGetFromRecord(DatabaseManager.serverDatabases, server.url)?.database;
|
||||
|
||||
if (database) {
|
||||
setState({
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles';
|
|||
import {checkDialogElementForError, checkIfErrorsMatchElements} from '@utils/integrations';
|
||||
import {getMarkdownBlockStyles, getMarkdownTextStyles} from '@utils/markdown';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
|
||||
import DialogIntroductionText from '../interactive_dialog/dialog_introduction_text';
|
||||
import {buildNavigationButton, dismissModal, setButtons} from '../navigation';
|
||||
|
|
@ -263,7 +264,7 @@ function AppsFormComponent({
|
|||
elements?.forEach((element) => {
|
||||
const newError = checkDialogElementForError(
|
||||
element,
|
||||
element.name === form.submit_buttons ? button : values[element.name],
|
||||
element.name === form.submit_buttons ? button : secureGetFromRecord(values, element.name),
|
||||
);
|
||||
if (newError) {
|
||||
hasErrors = true;
|
||||
|
|
@ -408,13 +409,17 @@ function AppsFormComponent({
|
|||
/>
|
||||
}
|
||||
{form.fields && form.fields.filter((f) => f.name !== form.submit_buttons).map((field) => {
|
||||
const value = secureGetFromRecord(values, field.name);
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<AppsFormField
|
||||
field={field}
|
||||
key={field.name}
|
||||
name={field.name}
|
||||
errorText={errors[field.name]}
|
||||
value={values[field.name]}
|
||||
errorText={secureGetFromRecord(errors, field.name)}
|
||||
value={value}
|
||||
performLookup={performLookup}
|
||||
onChange={onChange}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -68,8 +68,7 @@ const SkinSelector = ({onSelectSkin, selected, skins}: Props) => {
|
|||
/>
|
||||
</View>
|
||||
<View style={[styles.skins, isTablet && {marginRight: 10}]}>
|
||||
{Object.keys(skins).map((key) => {
|
||||
const name = skins[key];
|
||||
{Object.entries(skins).map(([key, name]) => {
|
||||
return (
|
||||
<View
|
||||
key={name}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
});
|
||||
|
||||
const skins = Object.keys(skinCodes).reduce<Record<string, string>>((result, value) => {
|
||||
const skin = skinCodes[value];
|
||||
const skins = Object.entries(skinCodes).reduce<Record<string, string>>((result, [value, skin]) => {
|
||||
if (value === 'default') {
|
||||
result[value] = 'hand';
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {useIsTablet} from '@hooks/device';
|
|||
import {dismissOverlay} from '@screens/navigation';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {changeOpacity} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
|
||||
import Icon from './icon';
|
||||
import Server from './server';
|
||||
|
|
@ -148,7 +149,7 @@ const InAppNotification = ({componentId, serverName, serverUrl, notification}: I
|
|||
// eslint-disable-next-line new-cap
|
||||
const gesture = Gesture.Pan().activeOffsetY(-20).onStart(() => runOnJS(animateDismissOverlay)());
|
||||
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
const database = secureGetFromRecord(DatabaseManager.serverDatabases, serverUrl)?.database;
|
||||
|
||||
return (
|
||||
<GestureHandlerRootView style={styles.gestureHandler}>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
} from '@screens/navigation';
|
||||
import {filterChannelsMatchingTerm} from '@utils/channel';
|
||||
import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import ChannelListRow from './channel_list_row';
|
||||
|
|
@ -68,7 +69,8 @@ const extractItemKey = (dataSource: string, item: DataType): string => {
|
|||
const toggleFromMap = <T extends DialogOption | Channel | UserProfile>(current: MultiselectSelectedMap, key: string, item: T): MultiselectSelectedMap => {
|
||||
const newMap = {...current};
|
||||
|
||||
if (current[key]) {
|
||||
const hasValue = Boolean(secureGetFromRecord<any>(current, key));
|
||||
if (hasValue) {
|
||||
delete newMap[key];
|
||||
} else {
|
||||
newMap[key] = item;
|
||||
|
|
@ -96,7 +98,7 @@ const filterSearchData = (source: string, searchData: DataTypeList, searchTerm:
|
|||
const handleIdSelection = (dataSource: string, currentIds: {[id: string]: DataType}, item: DataType) => {
|
||||
const newSelectedIds = {...currentIds};
|
||||
const key = extractItemKey(dataSource, item);
|
||||
const wasSelected = currentIds[key];
|
||||
const wasSelected = secureGetFromRecord(currentIds, key);
|
||||
|
||||
if (wasSelected) {
|
||||
Reflect.deleteProperty(newSelectedIds, key);
|
||||
|
|
@ -239,7 +241,9 @@ function IntegrationSelector(
|
|||
} else {
|
||||
setMultiselectSelected((current) => {
|
||||
const multiselectSelectedItems = {...current};
|
||||
delete multiselectSelectedItems[itemKey];
|
||||
if (secureGetFromRecord<any>(multiselectSelectedItems, itemKey) !== undefined) {
|
||||
delete multiselectSelectedItems[itemKey];
|
||||
}
|
||||
return multiselectSelectedItems;
|
||||
});
|
||||
}
|
||||
|
|
@ -470,7 +474,7 @@ function IntegrationSelector(
|
|||
}, [multiselectSelected, theme, isMultiselect]);
|
||||
|
||||
const renderOptionItem = useCallback((itemProps: any) => {
|
||||
const itemSelected = Boolean(multiselectSelected[itemProps.item.value]);
|
||||
const itemSelected = Boolean(secureGetFromRecord<any>(multiselectSelected, itemProps.item.value));
|
||||
return (
|
||||
<OptionListRow
|
||||
key={itemProps.id}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
|
|||
import {buildNavigationButton, dismissModal, setButtons} from '@screens/navigation';
|
||||
import {checkDialogElementForError, checkIfErrorsMatchElements} from '@utils/integrations';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
|
||||
import DialogElement from './dialog_element';
|
||||
import DialogIntroductionText from './dialog_introduction_text';
|
||||
|
|
@ -142,7 +143,7 @@ function InteractiveDialog({
|
|||
let hasErrors = false;
|
||||
if (elements) {
|
||||
elements.forEach((elem) => {
|
||||
const newError = checkDialogElementForError(elem, values[elem.name]);
|
||||
const newError = checkDialogElementForError(elem, secureGetFromRecord(values, elem.name));
|
||||
if (newError) {
|
||||
newErrors[elem.name] = intl.formatMessage({id: newError.id, defaultMessage: newError.defaultMessage}, newError.values);
|
||||
hasErrors = true;
|
||||
|
|
@ -244,6 +245,10 @@ function InteractiveDialog({
|
|||
/>
|
||||
}
|
||||
{Boolean(elements) && elements.map((e) => {
|
||||
const value = secureGetFromRecord(values, e.name);
|
||||
if (value === undefined) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<DialogElement
|
||||
key={'dialogelement' + e.name}
|
||||
|
|
@ -252,13 +257,13 @@ function InteractiveDialog({
|
|||
type={e.type}
|
||||
subtype={e.subtype}
|
||||
helpText={e.help_text}
|
||||
errorText={errors[e.name]}
|
||||
errorText={secureGetFromRecord(errors, e.name)}
|
||||
placeholder={e.placeholder}
|
||||
maxLength={e.max_length}
|
||||
dataSource={e.data_source}
|
||||
optional={e.optional}
|
||||
options={e.options}
|
||||
value={values[e.name]}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {dismissModal, setButtons} from '@screens/navigation';
|
|||
import {isEmail} from '@utils/helpers';
|
||||
import {mergeNavigationOptions} from '@utils/navigation';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
import {isGuest} from '@utils/user';
|
||||
|
||||
import Selection from './selection';
|
||||
|
|
@ -183,7 +184,7 @@ export default function Invite({
|
|||
const id = email ? item : (item as UserProfile).id;
|
||||
const newSelectedIds = Object.assign({}, selectedIds);
|
||||
|
||||
if (!selectedIds[id]) {
|
||||
if (!secureGetFromRecord(selectedIds, id)) {
|
||||
newSelectedIds[id] = item;
|
||||
}
|
||||
|
||||
|
|
@ -301,8 +302,9 @@ export default function Invite({
|
|||
}
|
||||
|
||||
for (const email of emails) {
|
||||
if (membersWithError[email]) {
|
||||
notSent.push({userId: email, reason: membersWithError[email]});
|
||||
const error = secureGetFromRecord(membersWithError, email);
|
||||
if (error) {
|
||||
notSent.push({userId: email, reason: error});
|
||||
} else {
|
||||
sent.push({userId: email, reason: formatMessage({id: 'invite.summary.email_invite', defaultMessage: 'An invitation email has been sent'})});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import CompassIcon from '@components/compass_icon';
|
|||
import UserItem from '@components/user_item';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import TextItem, {TextItemType} from './text_item';
|
||||
|
|
@ -114,7 +115,7 @@ export default function SummaryReport({
|
|||
</Text>
|
||||
</View>
|
||||
{invites.map(({userId, reason}) => {
|
||||
const item = selectedIds[userId];
|
||||
const item = secureGetFromRecord(selectedIds, userId);
|
||||
|
||||
return (
|
||||
<View
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles';
|
|||
import {closePermalink} from '@utils/permalink';
|
||||
import {preventDoubleTap} from '@utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {secureGetFromRecord} from '@utils/types';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import PermalinkError from './permalink_error';
|
||||
|
|
@ -176,7 +177,7 @@ function Permalink({
|
|||
return;
|
||||
}
|
||||
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
const database = secureGetFromRecord(DatabaseManager.serverDatabases, serverUrl)?.database;
|
||||
if (!database) {
|
||||
setError({unreachable: true});
|
||||
setLoading(false);
|
||||
|
|
|
|||
6
app/utils/types.ts
Normal file
6
app/utils/types.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export function secureGetFromRecord<T>(v: Record<string, T> | undefined, key: string) {
|
||||
return typeof v === 'object' && v && Object.prototype.hasOwnProperty.call(v, key) ? v[key] : undefined;
|
||||
}
|
||||
Loading…
Reference in a new issue