Merge branch 'main' into MM-53902-cont

This commit is contained in:
Caleb Roseland 2023-11-02 16:36:28 -05:00
commit a8fe9794d0
38 changed files with 466 additions and 315 deletions

View file

@ -667,6 +667,38 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
## @voximplant/react-native-foreground-service
This product contains a modified version of '@voximplant/react-native-foreground-service' by Voximplant.
A foreground service performs some operation that is noticeable to the user.
* HOMEPAGE:
* https://github.com/voximplant/react-native-foreground-service
* LICENSE: MIT License
Copyright (c) 2019 Zingaya, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---

View file

@ -110,8 +110,8 @@ android {
applicationId "com.mattermost.rnbeta"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 489
versionName "2.9.0"
versionCode 492
versionName "2.10.0"
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}

View file

@ -77,3 +77,11 @@ export const removeLastViewedChannelIdAndServer = async () => {
export const removeLastViewedThreadIdAndServer = async () => {
return storeGlobal(GLOBAL_IDENTIFIERS.LAST_VIEWED_THREAD, null, false);
};
export const storePushDisabledInServerAcknowledged = async (serverUrl: string) => {
return storeGlobal(`${GLOBAL_IDENTIFIERS.PUSH_DISABLED_ACK}${serverUrl}`, 'true', false);
};
export const removePushDisabledInServerAcknowledged = async (serverUrl: string) => {
return storeGlobal(`${GLOBAL_IDENTIFIERS.PUSH_DISABLED_ACK}${serverUrl}`, null, false);
};

View file

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

View file

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

View file

@ -25,7 +25,7 @@ type Props = {
currentUserId?: string;
currentUsername?: string;
location: string;
post: Post;
post: Post | null;
showJoinLeave: boolean;
testID?: string;
theme: Theme;
@ -65,23 +65,11 @@ const CombinedUserActivity = ({
const intl = useIntl();
const isTablet = useIsTablet();
const serverUrl = useServerUrl();
const itemTestID = `${testID}.${post.id}`;
const textStyles = getMarkdownTextStyles(theme);
const {allUserIds, allUsernames, messageData} = post.props.user_activity;
const styles = getStyleSheet(theme);
const content = [];
const removedUserIds: string[] = [];
const loadUserProfiles = () => {
if (allUserIds.length) {
fetchMissingProfilesByIds(serverUrl, allUserIds);
}
if (allUsernames.length) {
fetchMissingProfilesByUsernames(serverUrl, allUsernames);
}
};
const getUsernames = (userIds: string[]) => {
const someone = intl.formatMessage({id: 'channel_loader.someone', defaultMessage: 'Someone'});
const you = intl.formatMessage({id: 'combined_system_message.you', defaultMessage: 'You'});
@ -120,6 +108,9 @@ const CombinedUserActivity = ({
}, [post, canDelete, isTablet, intl, location]);
const renderMessage = (postType: string, userIds: string[], actorId: string) => {
if (!post) {
return null;
}
let actor = '';
if (usernamesById[actorId]) {
actor = `@${usernamesById[actorId]}`;
@ -177,9 +168,26 @@ const CombinedUserActivity = ({
};
useEffect(() => {
loadUserProfiles();
}, [allUserIds, allUsernames]);
if (!post) {
return;
}
const {allUserIds, allUsernames} = post.props.user_activity;
if (allUserIds.length) {
fetchMissingProfilesByIds(serverUrl, allUserIds);
}
if (allUsernames.length) {
fetchMissingProfilesByUsernames(serverUrl, allUsernames);
}
}, [post?.props.user_activity.allUserIds, post?.props.user_activity.allUsernames]);
if (!post) {
return null;
}
const itemTestID = `${testID}.${post.id}`;
const {messageData} = post.props.user_activity;
for (const message of messageData) {
const {postType, actorId} = message;
const userIds = new Set<string>(message.userIds);

View file

@ -28,24 +28,29 @@ const withCombinedPosts = withObservables(['postId'], ({database, postId}: WithD
// Columns observed: `props` is used by `usernamesById`. `message` is used by generateCombinedPost.
const posts = queryPostsById(database, postIds).observeWithColumns(['props', 'message']);
const post = posts.pipe(map((ps) => generateCombinedPost(postId, ps)));
const post = posts.pipe(map((ps) => (ps.length ? generateCombinedPost(postId, ps) : null)));
const canDelete = combineLatest([posts, currentUser]).pipe(
switchMap(([ps, u]) => (ps.length ? observePermissionForPost(database, ps[0], u, Permissions.DELETE_OTHERS_POSTS, false) : of$(false))),
);
const usernamesById = post.pipe(
switchMap(
(p) => queryUsersByIdsOrUsernames(database, p.props.user_activity.allUserIds, p.props.user_activity.allUsernames).observeWithColumns(['username']).
pipe(
// eslint-disable-next-line max-nested-callbacks
switchMap((users) => {
(p) => {
if (!p) {
return of$<Record<string, string>>({});
}
return queryUsersByIdsOrUsernames(database, p.props.user_activity.allUserIds, p.props.user_activity.allUsernames).observeWithColumns(['username']).
pipe(
// eslint-disable-next-line max-nested-callbacks
return of$(users.reduce((acc: Record<string, string>, user) => {
acc[user.id] = user.username;
return acc;
}, {}));
}),
),
switchMap((users) => {
// eslint-disable-next-line max-nested-callbacks
return of$(users.reduce((acc: Record<string, string>, user) => {
acc[user.id] = user.username;
return acc;
}, {}));
}),
);
},
),
);

View file

@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import React, {useCallback} from 'react';
import {Modal, StyleSheet, useWindowDimensions, View} from 'react-native';
import {Modal, useWindowDimensions} from 'react-native';
import HighlightItem from './item';
@ -11,11 +11,10 @@ type Props = {
itemBounds: TutorialItemBounds;
itemBorderRadius?: number;
onDismiss: () => void;
onLayout: () => void;
onShow?: () => void;
}
const TutorialHighlight = ({children, itemBounds, itemBorderRadius, onDismiss, onLayout, onShow}: Props) => {
const TutorialHighlight = ({children, itemBounds, itemBorderRadius, onDismiss, onShow}: Props) => {
const {width, height} = useWindowDimensions();
const handleShowTutorial = useCallback(() => {
@ -26,19 +25,13 @@ const TutorialHighlight = ({children, itemBounds, itemBorderRadius, onDismiss, o
return (
<Modal
visible={true}
visible={itemBounds.endX > 0}
transparent={true}
animationType='fade'
onDismiss={onDismiss}
onRequestClose={onDismiss}
testID='tutorial_highlight'
>
<View
style={StyleSheet.absoluteFill}
pointerEvents='box-none'
onLayout={onLayout}
/>
{itemBounds.endX > 0 &&
<HighlightItem
borderRadius={itemBorderRadius}
itemBounds={itemBounds}
@ -47,7 +40,6 @@ const TutorialHighlight = ({children, itemBounds, itemBorderRadius, onDismiss, o
width={width}
onLayout={handleShowTutorial}
/>
}
{children}
</Modal>
);

View file

@ -32,6 +32,7 @@ type Props = {
rightDecorator?: React.ReactNode;
onUserPress?: (user: UserProfile | UserModel) => void;
onUserLongPress?: (user: UserProfile | UserModel) => void;
onLayout?: () => void;
disabled?: boolean;
viewRef?: React.LegacyRef<View>;
padding?: number;
@ -102,6 +103,7 @@ const UserItem = ({
locale,
teammateNameDisplay,
rightDecorator,
onLayout,
onUserPress,
onUserLongPress,
disabled = false,
@ -159,6 +161,7 @@ const UserItem = ({
onPress={onPress}
onLongPress={onLongPress}
disabled={!(onUserPress || onUserLongPress)}
onLayout={onLayout}
>
<View
ref={viewRef}

View file

@ -138,6 +138,7 @@ exports[`components/channel_list_row should show no results 1`] = `
collapsable={false}
focusable={true}
onClick={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
@ -424,6 +425,7 @@ exports[`components/channel_list_row should show results and tutorial 1`] = `
collapsable={false}
focusable={true}
onClick={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
@ -551,79 +553,8 @@ exports[`components/channel_list_row should show results and tutorial 1`] = `
onRequestClose={[Function]}
testID="tutorial_highlight"
transparent={true}
visible={true}
>
<View
onLayout={[Function]}
pointerEvents="box-none"
style={
{
"bottom": 0,
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
}
}
/>
<View
pointerEvents="none"
style={
[
{
"alignItems": "center",
"bottom": 0,
"justifyContent": "center",
"left": 0,
"position": "absolute",
"right": 0,
"top": 0,
},
undefined,
]
}
testID="tutorial_swipe_left"
>
<View
style={
[
{
"alignItems": "center",
"backgroundColor": "#ffffff",
"borderRadius": 8,
"height": 161,
"padding": 16,
"width": 247,
},
{
"top": -74,
},
]
}
>
<long_press_illustration.svg />
<Text
style={
[
{
"color": "#3f4350",
"fontFamily": "Metropolis-SemiBold",
"fontSize": 16,
"fontWeight": "600",
"lineHeight": 24,
"marginTop": 8,
"paddingHorizontal": 12,
"textAlign": "center",
},
undefined,
]
}
>
Long-press on an item to view a user's profile
</Text>
</View>
</View>
</Modal>
visible={false}
/>
</View>
<View
onFocusCapture={[Function]}
@ -790,6 +721,7 @@ exports[`components/channel_list_row should show results no tutorial 1`] = `
collapsable={false}
focusable={true}
onClick={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
@ -1108,6 +1040,7 @@ exports[`components/channel_list_row should show results no tutorial 2 users 1`]
collapsable={false}
focusable={true}
onClick={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
@ -1299,6 +1232,7 @@ exports[`components/channel_list_row should show results no tutorial 2 users 1`]
collapsable={false}
focusable={true}
onClick={[Function]}
onLayout={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}

View file

@ -155,8 +155,10 @@ function UserListRow({
}, [isChannelAdmin, showManageMode, theme]);
const onLayout = useCallback(() => {
startTutorial();
}, []);
if (showTutorial) {
startTutorial();
}
}, [showTutorial]);
const icon = useMemo(() => {
if (!selectable && !selected) {
@ -190,17 +192,19 @@ function UserListRow({
viewRef={viewRef}
padding={20}
includeMargin={includeMargin}
onLayout={onLayout}
/>
{showTutorial &&
<TutorialHighlight
itemBounds={itemBounds}
onDismiss={handleDismissTutorial}
onLayout={onLayout}
>
{Boolean(itemBounds.endX) &&
<TutorialLongPress
message={formatMessage({id: 'user.tutorial.long_press', defaultMessage: "Long-press on an item to view a user's profile"})}
style={isTablet ? style.tutorialTablet : style.tutorial}
/>
}
</TutorialHighlight>
}
</>

View file

@ -82,6 +82,7 @@ export const GLOBAL_IDENTIFIERS = {
ONBOARDING: 'onboarding',
LAST_VIEWED_CHANNEL: 'lastViewedChannel',
LAST_VIEWED_THREAD: 'lastViewedThread',
PUSH_DISABLED_ACK: 'pushDisabledAck',
};
export enum OperationType {

View file

@ -120,7 +120,7 @@ export default class CategoryModel extends Model implements CategoryInterface {
toCategoryWithChannels = async (): Promise<CategoryWithChannels> => {
const categoryChannels = await this.categoryChannels.fetch();
const orderedChannelIds = categoryChannels.sort((a, b) => {
return b.sortOrder - a.sortOrder;
return a.sortOrder - b.sortOrder;
}).map((cc) => cc.channelId);
return {

View file

@ -5,7 +5,7 @@ import CookieManager, {type Cookie} from '@react-native-cookies/cookies';
import {AppState, type AppStateStatus, DeviceEventEmitter, Platform} from 'react-native';
import FastImage from 'react-native-fast-image';
import {storeOnboardingViewedValue} from '@actions/app/global';
import {removePushDisabledInServerAcknowledged, storeOnboardingViewedValue} from '@actions/app/global';
import {cancelSessionNotification, logout, scheduleSessionNotification} from '@actions/remote/session';
import {Events, Launch} from '@constants';
import DatabaseManager from '@database/manager';
@ -22,6 +22,7 @@ import {getThemeFromState} from '@screens/navigation';
import EphemeralStore from '@store/ephemeral_store';
import {deleteFileCache, deleteFileCacheByDir} from '@utils/file';
import {isMainActivity} from '@utils/helpers';
import {urlSafeBase64Encode} from '@utils/security';
import {addNewServer} from '@utils/server';
import type {LaunchType} from '@typings/launch';
@ -121,6 +122,7 @@ class SessionManager {
WebsocketManager.invalidateClient(serverUrl);
if (removeServer) {
await removePushDisabledInServerAcknowledged(urlSafeBase64Encode(serverUrl));
await DatabaseManager.destroyServerDatabase(serverUrl);
} else {
await DatabaseManager.deleteServerDatabase(serverUrl);

View file

@ -55,6 +55,22 @@ export const getDontAskForReview = async () => {
return Boolean(records?.[0]?.value);
};
export const getPushDisabledInServerAcknowledged = async (serverDomainString: string) => {
const records = await queryGlobalValue(`${GLOBAL_IDENTIFIERS.PUSH_DISABLED_ACK}${serverDomainString}`)?.fetch();
return Boolean(records?.[0]?.value);
};
export const observePushDisabledInServerAcknowledged = (serverDomainString: string) => {
const query = queryGlobalValue(`${GLOBAL_IDENTIFIERS.PUSH_DISABLED_ACK}${serverDomainString}`);
if (!query) {
return of$(false);
}
return query.observe().pipe(
switchMap((result) => (result.length ? result[0].observe() : of$(false))),
switchMap((v) => of$(Boolean(v))),
);
};
export const getFirstLaunch = async () => {
const records = await queryGlobalValue(GLOBAL_IDENTIFIERS.FIRST_LAUNCH)?.fetch();
if (!records?.[0]?.value) {

View file

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

View file

@ -3,7 +3,7 @@
import React from 'react';
import {PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy';
import {PUSH_PROXY_RESPONSE_NOT_AVAILABLE, PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy';
import {renderWithIntl} from '@test/intl-test-helper';
import Header from './header';
@ -22,4 +22,18 @@ describe('components/channel_list/header', () => {
expect(toJSON()).toMatchSnapshot();
});
it('Push notifications disabled show alert icon', () => {
const wrapper = renderWithIntl(
<Header
pushProxyStatus={PUSH_PROXY_RESPONSE_NOT_AVAILABLE}
canCreateChannels={true}
canJoinChannels={true}
canInvitePeople={true}
displayName={'Test!'}
/>,
);
expect(wrapper.getByTestId('channel_list_header.push_alert')).toBeTruthy();
});
});

View file

@ -206,7 +206,7 @@ const ChannelListHeader = ({
>
{serverDisplayName}
</Text>
{(pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED) && (
{pushProxyStatus !== PUSH_PROXY_STATUS_VERIFIED && (
<TouchableWithFeedback
onPress={onPushAlertPress}
testID='channel_list_header.push_alert'

View file

@ -14,26 +14,26 @@ type PlusMenuItemProps = {
const PlusMenuItem = ({pickerAction, onPress}: PlusMenuItemProps) => {
const intl = useIntl();
const menuItems = {
const menuItems: {[key: string]: Omit<React.ComponentProps<typeof SlideUpPanelItem>, 'onPress'>} = {
browseChannels: {
icon: 'globe',
leftIcon: 'globe',
text: intl.formatMessage({id: 'plus_menu.browse_channels.title', defaultMessage: 'Browse Channels'}),
testID: 'plus_menu_item.browse_channels',
},
createNewChannel: {
icon: 'plus',
leftIcon: 'plus',
text: intl.formatMessage({id: 'plus_menu.create_new_channel.title', defaultMessage: 'Create New Channel'}),
testID: 'plus_menu_item.create_new_channel',
},
openDirectMessage: {
icon: 'account-outline',
leftIcon: 'account-outline',
text: intl.formatMessage({id: 'plus_menu.open_direct_message.title', defaultMessage: 'Open a Direct Message'}),
testID: 'plus_menu_item.open_direct_message',
},
invitePeopleToTeam: {
icon: 'account-plus-outline',
leftIcon: 'account-plus-outline',
text: intl.formatMessage({id: 'plus_menu.invite_people_to_team.title', defaultMessage: 'Invite people to the team'}),
testID: 'plus_menu_item.invite_people_to_team',
},

View file

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

View file

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

View file

@ -213,9 +213,11 @@ const ServerItem = ({
};
const onLayout = useCallback(() => {
swipeable.current?.close();
startTutorial();
}, []);
if (showTutorial) {
swipeable.current?.close();
startTutorial();
}
}, [showTutorial]);
const containerStyle = useMemo(() => {
const style: StyleProp<ViewStyle> = [styles.container];
@ -390,6 +392,7 @@ const ServerItem = ({
style={containerStyle}
ref={viewRef}
testID={serverItemTestId}
onLayout={onLayout}
>
<RectButton
onPress={onServerPressed}
@ -474,13 +477,14 @@ const ServerItem = ({
itemBounds={itemBounds}
onDismiss={handleDismissTutorial}
onShow={handleShowTutorial}
onLayout={onLayout}
itemBorderRadius={8}
>
{Boolean(itemBounds.endX) &&
<TutorialSwipeLeft
message={intl.formatMessage({id: 'server.tutorial.swipe', defaultMessage: 'Swipe left on a server to see more actions'})}
style={isTablet ? styles.tutorialTablet : styles.tutorial}
/>
}
</TutorialHighlight>
}
</>

View file

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

View file

@ -180,7 +180,9 @@ const sortChannelsByName = (notifyPropsPerChannel: Record<string, Partial<Channe
export const sortChannels = (sorting: CategorySorting, channelsWithMyChannel: ChannelWithMyChannel[], notifyPropsPerChannel: Record<string, Partial<ChannelNotifyProps>>, locale: string) => {
if (sorting === 'recent') {
return channelsWithMyChannel.sort((cwmA, cwmB) => {
return cwmB.myChannel.lastPostAt - cwmA.myChannel.lastPostAt;
const a = Math.max(cwmA.myChannel.lastPostAt, cwmA.channel.createAt);
const b = Math.max(cwmB.myChannel.lastPostAt, cwmB.channel.createAt);
return b - a;
}).map((cwm) => cwm.channel);
} else if (sorting === 'manual') {
return channelsWithMyChannel.sort((cwmA, cwmB) => {

View file

@ -90,12 +90,27 @@ export const emitNotificationError = (type: 'Team' | 'Channel' | 'Post' | 'Conne
export const scheduleExpiredNotification = (serverUrl: string, session: Session, serverName: string, locale = DEFAULT_LOCALE) => {
const expiresAt = session?.expires_at || 0;
const expiresInDays = Math.ceil(Math.abs(moment.duration(moment().diff(moment(expiresAt))).asDays()));
const expiresInHours = Math.ceil(Math.abs(moment.duration(moment().diff(moment(expiresAt))).asHours()));
const expiresInDays = Math.floor(expiresInHours / 24); // Calculate expiresInDays
const remainingHours = expiresInHours % 24; // Calculate remaining hours
const intl = createIntl({locale, messages: getTranslations(locale)});
const body = intl.formatMessage({
id: 'mobile.session_expired',
defaultMessage: 'Please log in to continue receiving notifications. Sessions for {siteName} are configured to expire every {daysCount, number} {daysCount, plural, one {day} other {days}}.',
}, {siteName: serverName, daysCount: expiresInDays});
let body = '';
if (expiresInDays === 0) {
body = intl.formatMessage({
id: 'mobile.session_expired_hrs',
defaultMessage: 'Please log in to continue receiving notifications. Sessions for {siteName} are configured to expire every {hoursCount, number} {hoursCount, plural, one {hour} other {hours}}.',
}, {siteName: serverName, hoursCount: remainingHours});
} else if (expiresInHours === 0) {
body = intl.formatMessage({
id: 'mobile.session_expired_days',
defaultMessage: 'Please log in to continue receiving notifications. Sessions for {siteName} are configured to expire every {daysCount, number} {daysCount, plural, one {day} other {days}}.',
}, {siteName: serverName, daysCount: expiresInDays});
} else {
body = intl.formatMessage({
id: 'mobile.session_expired_days_hrs',
defaultMessage: 'Please log in to continue receiving notifications. Sessions for {siteName} are configured to expire every {daysCount, number} {daysCount, plural, one {day} other {days}} and {hoursCount, number} {hoursCount, plural, one {hour} other {hours}}.',
}, {siteName: serverName, daysCount: expiresInDays, hoursCount: remainingHours});
}
const title = intl.formatMessage({id: 'mobile.session_expired.title', defaultMessage: 'Session Expired'});
if (expiresAt) {

View file

@ -3,16 +3,29 @@
import {Alert} from 'react-native';
import {storePushDisabledInServerAcknowledged} from '@actions/app/global';
import {PUSH_PROXY_RESPONSE_NOT_AVAILABLE, PUSH_PROXY_RESPONSE_UNKNOWN, PUSH_PROXY_STATUS_NOT_AVAILABLE, PUSH_PROXY_STATUS_UNKNOWN, PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy';
import {getPushDisabledInServerAcknowledged} from '@queries/app/global';
import EphemeralStore from '@store/ephemeral_store';
import {urlSafeBase64Encode} from './security';
import type {IntlShape} from 'react-intl';
export function canReceiveNotifications(serverUrl: string, verification: string, intl: IntlShape) {
export function pushDisabledInServerAck(serverUrl: string) {
const extractedDomain = urlSafeBase64Encode(serverUrl);
return getPushDisabledInServerAcknowledged(extractedDomain);
}
export async function canReceiveNotifications(serverUrl: string, verification: string, intl: IntlShape) {
const hasAckNotification = await pushDisabledInServerAck(serverUrl);
switch (verification) {
case PUSH_PROXY_RESPONSE_NOT_AVAILABLE:
EphemeralStore.setPushProxyVerificationState(serverUrl, PUSH_PROXY_STATUS_NOT_AVAILABLE);
alertPushProxyError(intl);
if (!hasAckNotification) {
alertPushProxyError(intl, serverUrl);
}
break;
case PUSH_PROXY_RESPONSE_UNKNOWN:
EphemeralStore.setPushProxyVerificationState(serverUrl, PUSH_PROXY_STATUS_UNKNOWN);
@ -23,7 +36,14 @@ export function canReceiveNotifications(serverUrl: string, verification: string,
}
}
export function alertPushProxyError(intl: IntlShape) {
const handleAlertResponse = async (buttonIndex: number, serverUrl?: string) => {
if (buttonIndex === 0 && serverUrl) {
// User clicked "Okay" acknowledging that the push notifications are disabled on that server
await storePushDisabledInServerAcknowledged(urlSafeBase64Encode(serverUrl));
}
};
export function alertPushProxyError(intl: IntlShape, serverUrl?: string) {
Alert.alert(
intl.formatMessage({
id: 'alert.push_proxy_error.title',
@ -31,10 +51,11 @@ export function alertPushProxyError(intl: IntlShape) {
}),
intl.formatMessage({
id: 'alert.push_proxy_error.description',
defaultMessage: 'Due to the configuration for this server, notifications cannot be received in the mobile app. Contact your system admin for more information.',
defaultMessage: 'Due to the configuration of this server, notifications cannot be received in the mobile app. Contact your system admin for more information.',
}),
[{
text: intl.formatMessage({id: 'alert.push_proxy.button', defaultMessage: 'Okay'}),
onPress: () => handleAlertResponse(0, serverUrl),
}],
);
}

View file

@ -15,7 +15,7 @@
"account.your_profile": "Your Profile",
"alert.channel_deleted.description": "The channel {displayName} has been archived.",
"alert.channel_deleted.title": "Archived channel",
"alert.push_proxy_error.description": "Due to the configuration for this server, notifications cannot be received in the mobile app. Contact your system admin for more information.",
"alert.push_proxy_error.description": "Due to the configuration of this server, notifications cannot be received in the mobile app. Contact your system admin for more information.",
"alert.push_proxy_error.title": "Notifications cannot be received from this server",
"alert.push_proxy_unknown.description": "This server was unable to receive push notifications for an unknown reason. This will be attempted again next time you connect.",
"alert.push_proxy_unknown.title": "Notifications could not be received from this server",
@ -613,6 +613,7 @@
"mobile.managed.settings": "Go to settings",
"mobile.markdown.code.copy_code": "Copy Code",
"mobile.markdown.code.plusMoreLines": "+{count, number} more {count, plural, one {line} other {lines}}",
"mobile.markdown.copy_header": "Copy header text",
"mobile.markdown.image.too_large": "Image exceeds max dimensions of {maxWidth} by {maxHeight}:",
"mobile.markdown.link.copy_url": "Copy URL",
"mobile.mention.copy_mention": "Copy Mention",
@ -709,7 +710,9 @@
"mobile.server_url.deeplink.emm.denied": "This app is controlled by an EMM and the DeepLink server url does not match the EMM allowed server",
"mobile.server_url.empty": "Please enter a valid server URL",
"mobile.server_url.invalid_format": "URL must start with http:// or https://",
"mobile.session_expired": "Please log in to continue receiving notifications. Sessions for {siteName} are configured to expire every {daysCount, number} {daysCount, plural, one {day} other {days}}.",
"mobile.session_expired_days": "Please log in to continue receiving notifications. Sessions for {siteName} are configured to expire every {daysCount, number} {daysCount, plural, one {day} other {days}}.",
"mobile.session_expired_days_hrs": "Please log in to continue receiving notifications. Sessions for {siteName} are configured to expire every {daysCount, number} {daysCount, plural, one {day} other {days}} and {hoursCount, number} {hoursCount, plural, one {hour} other {hours}}.",
"mobile.session_expired_hrs": "Please log in to continue receiving notifications. Sessions for {siteName} are configured to expire every {hoursCount, number} {hoursCount, plural, one {hour} other {hours}}.",
"mobile.session_expired.title": "Session Expired",
"mobile.set_status.dnd": "Do Not Disturb",
"mobile.storage_permission_denied_description": "Upload files to your server. Open Settings to grant {applicationName} Read and Write access to files on this device.",

View file

@ -686,7 +686,7 @@
"alert.push_proxy.button": "Okay",
"alert.push_proxy_unknown.title": "Notifications could not be received from this server",
"alert.push_proxy_error.title": "Notifications cannot be received from this server",
"alert.push_proxy_error.description": "Due to the configuration for this server, notifications cannot be received in the mobile app. Contact your system admin for more information.",
"alert.push_proxy_error.description": "Due to the configuration of this server, notifications cannot be received in the mobile app. Contact your system admin for more information.",
"alert.channel_deleted.title": "Archived channel",
"alert.channel_deleted.description": "The channel {displayName} has been archived.",
"account.your_profile": "Your Profile",

View file

@ -1 +0,0 @@
{}

View file

@ -1 +0,0 @@
{}

View file

@ -1929,7 +1929,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 489;
CURRENT_PROJECT_VERSION = 492;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
HEADER_SEARCH_PATHS = (
@ -1973,7 +1973,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 489;
CURRENT_PROJECT_VERSION = 492;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
HEADER_SEARCH_PATHS = (
@ -2116,7 +2116,7 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 489;
CURRENT_PROJECT_VERSION = 492;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
GCC_C_LANGUAGE_STANDARD = gnu11;
@ -2165,7 +2165,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 489;
CURRENT_PROJECT_VERSION = 492;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
GCC_C_LANGUAGE_STANDARD = gnu11;

View file

@ -21,7 +21,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.9.0</string>
<string>2.10.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -37,7 +37,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>489</string>
<string>492</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>

View file

@ -19,9 +19,9 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>2.9.0</string>
<string>2.10.0</string>
<key>CFBundleVersion</key>
<string>489</string>
<string>492</string>
<key>UIAppFonts</key>
<array>
<string>OpenSans-Bold.ttf</string>

View file

@ -19,9 +19,9 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>2.9.0</string>
<string>2.10.0</string>
<key>CFBundleVersion</key>
<string>489</string>
<string>492</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>

View file

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

177
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "mattermost-mobile",
"version": "2.9.0",
"version": "2.10.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "mattermost-mobile",
"version": "2.9.0",
"version": "2.10.0",
"hasInstallScript": true,
"license": "Apache 2.0",
"dependencies": {
@ -42,7 +42,7 @@
"@voximplant/react-native-foreground-service": "3.0.2",
"base-64": "1.0.0",
"commonmark": "npm:@mattermost/commonmark@0.30.1-1",
"commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#235bc817bcade503fb81fa51bbbe3c84f958ed12",
"commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#81b5d27509652bae50b4b510ede777dd3bd923cf",
"deep-equal": "2.2.2",
"deepmerge": "4.3.1",
"emoji-regex": "10.2.1",
@ -82,7 +82,7 @@
"react-native-localize": "3.0.2",
"react-native-math-view": "3.9.5",
"react-native-navigation": "7.37.0",
"react-native-notifications": "5.0.0",
"react-native-notifications": "5.1.0",
"react-native-permissions": "3.8.4",
"react-native-reanimated": "3.4.2",
"react-native-safe-area-context": "4.7.1",
@ -9679,8 +9679,8 @@
},
"node_modules/commonmark-react-renderer": {
"version": "4.3.5",
"resolved": "git+ssh://git@github.com/mattermost/commonmark-react-renderer.git#235bc817bcade503fb81fa51bbbe3c84f958ed12",
"integrity": "sha512-GP3+17loU8tNal91FAlqDcDyUHdhahMPpV/XBm++VAyoyvsGKx3wGQZY/cVKXNx+SV2UANXVf4VFx8GpAkqCBQ==",
"resolved": "git+ssh://git@github.com/mattermost/commonmark-react-renderer.git#81b5d27509652bae50b4b510ede777dd3bd923cf",
"integrity": "sha512-2UYjN/Fix93L6udBsmZqCUggE7e5NA8rXHO2R3Dxs3lwHac3MsOMdEYZPST3Z/CCFc6gyKqWqDtCQXofbHffPA==",
"license": "MIT",
"dependencies": {
"lodash.assign": "^4.2.0",
@ -19506,9 +19506,9 @@
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
},
"node_modules/react-native-notifications": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-5.0.0.tgz",
"integrity": "sha512-QXtBBmbDtwq9X8WAPLn+OctIeEtnJOQ+RCT6iweaypvFTydt2baLPtawTAbCSXKuWpVDqDAdmZnlQjCcavNzoA==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-5.1.0.tgz",
"integrity": "sha512-laqDSDlCvEASmJR6cXpqaryK855ejQd07vrfYERzhv68YDOoSkKy/URExRP4vAfAOVqHhix80tLbNUcfvZk2VQ==",
"peerDependencies": {
"react": "*",
"react-native": "*"
@ -23596,8 +23596,7 @@
"version": "7.21.0-placeholder-for-preset-env.2",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
"requires": {
}
"requires": {}
},
"@babel/plugin-syntax-async-generators": {
"version": "7.8.4",
@ -25420,8 +25419,7 @@
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@mattermost/react-native-emm/-/react-native-emm-1.3.5.tgz",
"integrity": "sha512-REdUEsm/RA6lI1Rt4b009jvWn28f7H+e27gd4hlNk6zesIh/dlfiHwYfInW/vwbNFBdSPpvHy7Qi2mdcvrNqhg==",
"requires": {
}
"requires": {}
},
"@mattermost/react-native-network-client": {
"version": "1.4.1",
@ -25444,8 +25442,7 @@
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@mattermost/react-native-turbo-log/-/react-native-turbo-log-0.2.3.tgz",
"integrity": "sha512-usWyD8zVAHzrYqgPH1ne5I14gCOkhS2mefK58g5v4DewZfCm0/Uc0w8MRuPS/9jyOPPq1rUZj8U1AqKgEne9tQ==",
"requires": {
}
"requires": {}
},
"@msgpack/msgpack": {
"version": "2.8.0",
@ -25537,15 +25534,13 @@
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/@react-native-camera-roll/camera-roll/-/camera-roll-5.7.2.tgz",
"integrity": "sha512-s8VAUG1Kvi+tEJkLHObmOJdXAL/uclnXJ/IdnJtx2fCKiWA3Ho0ln9gDQqCYHHHHu+sXk7wovsH/I2/AYy0brg==",
"requires": {
}
"requires": {}
},
"@react-native-clipboard/clipboard": {
"version": "1.11.2",
"resolved": "https://registry.npmjs.org/@react-native-clipboard/clipboard/-/clipboard-1.11.2.tgz",
"integrity": "sha512-bHyZVW62TuleiZsXNHS1Pv16fWc0fh8O9WvBzl4h2fykqZRW9a+Pv/RGTH56E3X2PqzHP38K5go8zmCZUoIsoQ==",
"requires": {
}
"requires": {}
},
"@react-native-community/cli": {
"version": "10.2.4",
@ -27254,8 +27249,7 @@
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-9.4.1.tgz",
"integrity": "sha512-dAbY5mfw+6Kas/GJ6QX9AZyY+K+eq9ad4Su6utoph/nxyH3whp5cMSgRNgE2VhGQVRZ/OG0qq3IaD3+wzoqJXw==",
"requires": {
}
"requires": {}
},
"@react-native-cookies/cookies": {
"version": "6.2.1",
@ -27421,8 +27415,7 @@
"version": "1.3.18",
"resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.18.tgz",
"integrity": "sha512-/0hwnJkrr415yP0Hf4PjUKgGyfshrvNUKFXN85Mrt1gY49hy9IwxZgrrxlh0THXkPeq8q4VWw44eHDfAcQf20Q==",
"requires": {
}
"requires": {}
},
"@react-navigation/native": {
"version": "6.1.7",
@ -27649,72 +27642,63 @@
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/@stream-io/flat-list-mvcp/-/flat-list-mvcp-0.10.3.tgz",
"integrity": "sha512-2ZK8piYlEfKIPZrH8BpZz9uj8HZcUvMCV0X7qSLSAc/vhLOANBfR0SSn0OaWPbqb2mFGAd4FxmLSPp1zKEYuaw==",
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-add-jsx-attribute": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
"integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-remove-jsx-attribute": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz",
"integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-remove-jsx-empty-expression": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz",
"integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-replace-jsx-attribute-value": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz",
"integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-svg-dynamic-title": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz",
"integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-svg-em-dimensions": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz",
"integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-transform-react-native-svg": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.0.0.tgz",
"integrity": "sha512-UKrY3860AQICgH7g+6h2zkoxeVEPLYwX/uAjmqo4PIq2FIHppwhIqZstIyTz0ZtlwreKR41O3W3BzsBBiJV2Aw==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-plugin-transform-svg-component": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz",
"integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==",
"dev": true,
"requires": {
}
"requires": {}
},
"@svgr/babel-preset": {
"version": "8.0.0",
@ -28689,8 +28673,7 @@
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.0.tgz",
"integrity": "sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==",
"dev": true,
"requires": {
}
"requires": {}
},
"@webpack-cli/info": {
"version": "1.4.0",
@ -28706,8 +28689,7 @@
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.0.tgz",
"integrity": "sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==",
"dev": true,
"requires": {
}
"requires": {}
},
"@xtuc/ieee754": {
"version": "1.2.0",
@ -28762,15 +28744,13 @@
"integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
"dev": true,
"peer": true,
"requires": {
}
"requires": {}
},
"acorn-jsx": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"requires": {
}
"requires": {}
},
"agent-base": {
"version": "6.0.2",
@ -28826,8 +28806,7 @@
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true,
"peer": true,
"requires": {
}
"requires": {}
},
"anser": {
"version": "1.4.10",
@ -29105,8 +29084,7 @@
"version": "7.0.0-bridge.0",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
"integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==",
"requires": {
}
"requires": {}
},
"babel-jest": {
"version": "29.6.2",
@ -30055,9 +30033,9 @@
}
},
"commonmark-react-renderer": {
"version": "git+ssh://git@github.com/mattermost/commonmark-react-renderer.git#235bc817bcade503fb81fa51bbbe3c84f958ed12",
"integrity": "sha512-GP3+17loU8tNal91FAlqDcDyUHdhahMPpV/XBm++VAyoyvsGKx3wGQZY/cVKXNx+SV2UANXVf4VFx8GpAkqCBQ==",
"from": "commonmark-react-renderer@github:mattermost/commonmark-react-renderer#235bc817bcade503fb81fa51bbbe3c84f958ed12",
"version": "git+ssh://git@github.com/mattermost/commonmark-react-renderer.git#81b5d27509652bae50b4b510ede777dd3bd923cf",
"integrity": "sha512-2UYjN/Fix93L6udBsmZqCUggE7e5NA8rXHO2R3Dxs3lwHac3MsOMdEYZPST3Z/CCFc6gyKqWqDtCQXofbHffPA==",
"from": "commonmark-react-renderer@github:mattermost/commonmark-react-renderer#81b5d27509652bae50b4b510ede777dd3bd923cf",
"requires": {
"lodash.assign": "^4.2.0",
"lodash.isplainobject": "^4.0.6",
@ -30307,8 +30285,7 @@
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz",
"integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==",
"dev": true,
"requires": {
}
"requires": {}
},
"deep-equal": {
"version": "2.2.2",
@ -31211,8 +31188,7 @@
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz",
"integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==",
"requires": {
}
"requires": {}
},
"eslint-import-resolver-node": {
"version": "0.3.7",
@ -31297,8 +31273,7 @@
"resolved": "https://registry.npmjs.org/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz",
"integrity": "sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg==",
"dev": true,
"requires": {
}
"requires": {}
},
"eslint-plugin-import": {
"version": "2.28.0",
@ -31432,8 +31407,7 @@
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
"requires": {
}
"requires": {}
},
"eslint-plugin-react-native": {
"version": "4.0.0",
@ -33807,8 +33781,7 @@
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
"integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
"dev": true,
"requires": {
}
"requires": {}
},
"jest-regex-util": {
"version": "29.4.3",
@ -37036,8 +37009,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.3.tgz",
"integrity": "sha512-ZnXwLQnGzrDpHBHiC56TXFXvmolPeMjTn1UOm610M4EXGzbEDR7oOIyS2ZiItgbs6eZc4oU/a0hpk8PrcKvv5g==",
"requires": {
}
"requires": {}
},
"react-intl": {
"version": "6.4.4",
@ -37346,8 +37318,7 @@
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/react-native-background-timer/-/react-native-background-timer-2.4.1.tgz",
"integrity": "sha512-TE4Kiy7jUyv+hugxDxitzu38sW1NqjCk4uE5IgU2WevLv7sZacaBc6PZKOShNRPGirLl1NWkaG3LDEkdb9Um5g==",
"requires": {
}
"requires": {}
},
"react-native-button": {
"version": "3.1.0",
@ -37400,15 +37371,13 @@
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/react-native-create-thumbnail/-/react-native-create-thumbnail-1.6.4.tgz",
"integrity": "sha512-JWuKXswDXtqUPfuqh6rjCVMvTSSG3kUtwvSK/YdaNU0i+nZKxeqHmt/CO2+TyI/WSUFynGVmWT1xOHhCZAFsRQ==",
"requires": {
}
"requires": {}
},
"react-native-device-info": {
"version": "10.8.0",
"resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-10.8.0.tgz",
"integrity": "sha512-DE4/X82ZVhdcnR1Y21iTP46WSSJA/rHK3lmeqWfGGq1RKLwXTIdxmfbZZnYwryqJ+esrw2l4ND19qlgxDGby8A==",
"requires": {
}
"requires": {}
},
"react-native-document-picker": {
"version": "9.0.1",
@ -37445,22 +37414,19 @@
"version": "2.10.10",
"resolved": "https://registry.npmjs.org/react-native-exception-handler/-/react-native-exception-handler-2.10.10.tgz",
"integrity": "sha512-otAXGoZDl1689OoUJWN/rXxVbdoZ3xcmyF1uq/CsizdLwwyZqVGd6d+p/vbYvnF996FfEyAEBnHrdFxulTn51w==",
"requires": {
}
"requires": {}
},
"react-native-fast-image": {
"version": "8.6.3",
"resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-8.6.3.tgz",
"integrity": "sha512-Sdw4ESidXCXOmQ9EcYguNY2swyoWmx53kym2zRsvi+VeFCHEdkO+WG1DK+6W81juot40bbfLNhkc63QnWtesNg==",
"requires": {
}
"requires": {}
},
"react-native-file-viewer": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/react-native-file-viewer/-/react-native-file-viewer-2.1.5.tgz",
"integrity": "sha512-MGC6sx9jsqHdefhVQ6o0akdsPGpkXgiIbpygb2Sg4g4bh7v6K1cardLV1NwGB9A6u1yICOSDT/MOC//9Ez6EUg==",
"requires": {
}
"requires": {}
},
"react-native-fs": {
"version": "2.20.0",
@ -37499,22 +37465,19 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-2.0.3.tgz",
"integrity": "sha512-7+qvcxXZts/hA+HOOIFyM1x9m9fn/TJVSTgXaoQ8uT4gLc97IMvqHQ559tDmnlth+hHMzd3HRMpmRLWoKPL0DA==",
"requires": {
}
"requires": {}
},
"react-native-hw-keyboard-event": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/react-native-hw-keyboard-event/-/react-native-hw-keyboard-event-0.0.4.tgz",
"integrity": "sha512-G8qp0nm17PHigLb/axgdF9xg51BKCG2p1AGeq//J/luLp5zNczIcQJh+nm02R1MeEUE3e53wqO4LMe0MV3raZg==",
"requires": {
}
"requires": {}
},
"react-native-image-picker": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-5.6.1.tgz",
"integrity": "sha512-LPPlgJi97EzCDY4NWp7z0oUWmCbagnB6HSoKcLJHJD/DaFYN/dJPrqjqKaqqw8K/5Ze6DIsNg9PZohjNEYQQWQ==",
"requires": {
}
"requires": {}
},
"react-native-in-app-review": {
"version": "4.3.3",
@ -37525,15 +37488,13 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/react-native-incall-manager/-/react-native-incall-manager-4.1.0.tgz",
"integrity": "sha512-v1c+XOGu5VudY5//E3i5xiaRA9v6RvevMzZ4RumLqI+hte+4XslB2z6HSek2FF0EmAnY1rCn4ckiwgkTI1Tmtw==",
"requires": {
}
"requires": {}
},
"react-native-iphone-x-helper": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz",
"integrity": "sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg==",
"requires": {
}
"requires": {}
},
"react-native-keyboard-aware-scroll-view": {
"version": "0.9.5",
@ -37548,8 +37509,7 @@
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/react-native-keyboard-tracking-view/-/react-native-keyboard-tracking-view-5.7.0.tgz",
"integrity": "sha512-MDeEwAbn9LJDOfHq0QLCGaZirVLk2X/tHqkAqz3y6uxryTRdSl9PwleOVar5Jx2oAPEg4J9BXbUD1wwOOi+5Kg==",
"requires": {
}
"requires": {}
},
"react-native-keychain": {
"version": "8.1.2",
@ -37560,15 +37520,13 @@
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.8.2.tgz",
"integrity": "sha512-hgmCsgzd58WNcDCyPtKrvxsaoETjb/jLGxis/dmU3Aqm2u4ICIduj4ECjbil7B7pm9OnuTkmpwXu08XV2mpg8g==",
"requires": {
}
"requires": {}
},
"react-native-localize": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/react-native-localize/-/react-native-localize-3.0.2.tgz",
"integrity": "sha512-/l/oE1LVNgIRRhLbhmfFMHiWV0xhUn0A0iz1ytLVRYywL7FTp8Rx2vkJS/q/RpExDvV7yLw2493XZBYIM1dnLQ==",
"requires": {
}
"requires": {}
},
"react-native-math-view": {
"version": "3.9.5",
@ -37601,11 +37559,10 @@
}
},
"react-native-notifications": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-5.0.0.tgz",
"integrity": "sha512-QXtBBmbDtwq9X8WAPLn+OctIeEtnJOQ+RCT6iweaypvFTydt2baLPtawTAbCSXKuWpVDqDAdmZnlQjCcavNzoA==",
"requires": {
}
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-5.1.0.tgz",
"integrity": "sha512-laqDSDlCvEASmJR6cXpqaryK855ejQd07vrfYERzhv68YDOoSkKy/URExRP4vAfAOVqHhix80tLbNUcfvZk2VQ==",
"requires": {}
},
"react-native-permissions": {
"version": "3.8.4",
@ -37646,8 +37603,7 @@
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.7.1.tgz",
"integrity": "sha512-X2pJG2ttmAbiGlItWedvDkZg1T1ikmEDiz+7HsiIwAIm2UbFqlhqn+B1JF53mSxPzdNaDcCQVHRNPvj8oFu6Yg==",
"requires": {
}
"requires": {}
},
"react-native-screens": {
"version": "3.24.0",
@ -37680,8 +37636,7 @@
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/react-native-size-matters/-/react-native-size-matters-0.3.1.tgz",
"integrity": "sha512-mKOfBLIBFBcs9br1rlZDvxD5+mAl8Gfr5CounwJtxI6Z82rGrMO+Kgl9EIg3RMVf3G855a85YVqHJL2f5EDRlw==",
"requires": {
}
"requires": {}
},
"react-native-svg": {
"version": "13.11.0",
@ -39355,8 +39310,7 @@
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz",
"integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==",
"dev": true,
"requires": {
}
"requires": {}
},
"ts-jest": {
"version": "29.1.1",
@ -39677,15 +39631,13 @@
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.1.6.tgz",
"integrity": "sha512-VO/P91A/PmKH9bcN9a7O3duSuxe6M14ZoYXgA6a8dab8doWNdhiIHzEkX/jFeTTRBsX0Ubk6nG4q2NIjNsj+bg==",
"requires": {
}
"requires": {}
},
"use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"requires": {
}
"requires": {}
},
"utf8": {
"version": "3.0.0",
@ -40018,8 +39970,7 @@
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz",
"integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==",
"requires": {
}
"requires": {}
},
"xdate": {
"version": "0.8.2",

View file

@ -1,6 +1,6 @@
{
"name": "mattermost-mobile",
"version": "2.9.0",
"version": "2.10.0",
"description": "Mattermost Mobile with React Native",
"repository": "git@github.com:mattermost/mattermost-mobile.git",
"author": "Mattermost, Inc.",
@ -43,7 +43,7 @@
"@voximplant/react-native-foreground-service": "3.0.2",
"base-64": "1.0.0",
"commonmark": "npm:@mattermost/commonmark@0.30.1-1",
"commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#235bc817bcade503fb81fa51bbbe3c84f958ed12",
"commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#81b5d27509652bae50b4b510ede777dd3bd923cf",
"deep-equal": "2.2.2",
"deepmerge": "4.3.1",
"emoji-regex": "10.2.1",
@ -83,7 +83,7 @@
"react-native-localize": "3.0.2",
"react-native-math-view": "3.9.5",
"react-native-navigation": "7.37.0",
"react-native-notifications": "5.0.0",
"react-native-notifications": "5.1.0",
"react-native-permissions": "3.8.4",
"react-native-reanimated": "3.4.2",
"react-native-safe-area-context": "4.7.1",

View file

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