Refactor the logic on RoomView and apply filters on ShareListView and forward message

This commit is contained in:
Diego Mello 2024-04-19 16:07:46 -03:00
parent 15766e2f64
commit 24464a8859
4 changed files with 99 additions and 46 deletions

View File

@ -501,6 +501,26 @@ class Encryption {
// Decrypt multiple subscriptions // Decrypt multiple subscriptions
decryptSubscriptions = (subscriptions: ISubscription[]) => Promise.all(subscriptions.map(s => this.decryptSubscription(s))); decryptSubscriptions = (subscriptions: ISubscription[]) => Promise.all(subscriptions.map(s => this.decryptSubscription(s)));
// Missing room encryption key
isMissingRoomE2EEKey = ({
encryptionEnabled,
roomEncrypted,
E2EKey
}: {
encryptionEnabled: boolean;
roomEncrypted: TSubscriptionModel['encrypted'];
E2EKey: TSubscriptionModel['E2EKey'];
}) => (encryptionEnabled && roomEncrypted && !E2EKey) ?? false;
// Encrypted room, but user session is not encrypted
isE2EEDisabledEncryptedRoom = ({
encryptionEnabled,
roomEncrypted
}: {
encryptionEnabled: boolean;
roomEncrypted: TSubscriptionModel['encrypted'];
}) => (!encryptionEnabled && roomEncrypted) ?? false;
} }
const encryption = new Encryption(); const encryption = new Encryption();

View File

@ -6,6 +6,7 @@ import { store as reduxStore } from '../store/auxStore';
import { spotlight } from '../services/restApi'; import { spotlight } from '../services/restApi';
import { ISearch, ISearchLocal, IUserMessage, SubscriptionType, TSubscriptionModel } from '../../definitions'; import { ISearch, ISearchLocal, IUserMessage, SubscriptionType, TSubscriptionModel } from '../../definitions';
import { isGroupChat, isReadOnly } from './helpers'; import { isGroupChat, isReadOnly } from './helpers';
import { Encryption } from '../encryption';
export type TSearch = ISearchLocal | IUserMessage | ISearch; export type TSearch = ISearchLocal | IUserMessage | ISearch;
@ -46,10 +47,21 @@ export const localSearchSubscription = async ({
if (filterMessagingAllowed) { if (filterMessagingAllowed) {
const username = reduxStore.getState().login.user.username as string; const username = reduxStore.getState().login.user.username as string;
const encryptionEnabled = reduxStore.getState().encryption.enabled as boolean;
const filteredSubscriptions = await Promise.all( const filteredSubscriptions = await Promise.all(
subscriptions.map(async item => { subscriptions.map(async item => {
const isItemReadOnly = await isReadOnly(item, username); if (await isReadOnly(item, username)) {
return isItemReadOnly ? null : item; return null;
}
if (Encryption.isMissingRoomE2EEKey({ encryptionEnabled, roomEncrypted: item.encrypted, E2EKey: item.E2EKey })) {
return null;
}
if (Encryption.isE2EEDisabledEncryptedRoom({ encryptionEnabled, roomEncrypted: item.encrypted })) {
return null;
}
return item;
}) })
); );
subscriptions = filteredSubscriptions.filter(item => item !== null) as TSubscriptionModel[]; subscriptions = filteredSubscriptions.filter(item => item !== null) as TSubscriptionModel[];

View File

@ -98,6 +98,7 @@ import AudioManager from '../../lib/methods/AudioManager';
import { IListContainerRef, TListRef } from './List/definitions'; import { IListContainerRef, TListRef } from './List/definitions';
import { getMessageById } from '../../lib/database/services/Message'; import { getMessageById } from '../../lib/database/services/Message';
import { getThreadById } from '../../lib/database/services/Thread'; import { getThreadById } from '../../lib/database/services/Thread';
import { Encryption } from '../../lib/encryption';
import { clearInAppFeedback, removeInAppFeedback } from '../../actions/inAppFeedback'; import { clearInAppFeedback, removeInAppFeedback } from '../../actions/inAppFeedback';
import UserPreferences from '../../lib/methods/userPreferences'; import UserPreferences from '../../lib/methods/userPreferences';
import { IRoomViewProps, IRoomViewState } from './definitions'; import { IRoomViewProps, IRoomViewState } from './definitions';
@ -414,17 +415,19 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
return hideSystemMessages ?? []; return hideSystemMessages ?? [];
} }
get missingRoomE2EEKey() { hasE2EEWarning = () => {
const { room } = this.state; const { room } = this.state;
const { encryptionEnabled } = this.props; const { encryptionEnabled } = this.props;
return (encryptionEnabled && 'encrypted' in room && room.encrypted && 'E2EKey' in room && !room.E2EKey) ?? false; if ('encrypted' in room) {
} if (Encryption.isMissingRoomE2EEKey({ encryptionEnabled, roomEncrypted: room.encrypted, E2EKey: room.E2EKey })) {
return true;
get e2eeDisabledEncryptedRoom() { }
const { room } = this.state; if (Encryption.isE2EEDisabledEncryptedRoom({ encryptionEnabled, roomEncrypted: room.encrypted })) {
const { encryptionEnabled } = this.props; return true;
return (!encryptionEnabled && 'encrypted' in room && room.encrypted) ?? false; }
} }
return false;
};
setHeader = () => { setHeader = () => {
const { room, unreadsCount, roomUserId, joined, canForwardGuest, canReturnQueue, canPlaceLivechatOnHold } = this.state; const { room, unreadsCount, roomUserId, joined, canForwardGuest, canReturnQueue, canPlaceLivechatOnHold } = this.state;
@ -513,7 +516,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
onPress={this.goRoomActionsView} onPress={this.goRoomActionsView}
testID={`room-view-title-${title}`} testID={`room-view-title-${title}`}
sourceType={sourceType} sourceType={sourceType}
disabled={this.missingRoomE2EEKey || this.e2eeDisabledEncryptedRoom || !!tmid} disabled={this.hasE2EEWarning() || !!tmid}
/> />
), ),
headerRight: () => ( headerRight: () => (
@ -531,7 +534,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
showActionSheet={this.showActionSheet} showActionSheet={this.showActionSheet}
departmentId={departmentId} departmentId={departmentId}
notificationsDisabled={iSubRoom?.disableNotifications} notificationsDisabled={iSubRoom?.disableNotifications}
disabled={this.missingRoomE2EEKey || this.e2eeDisabledEncryptedRoom} disabled={this.hasE2EEWarning()}
/> />
) )
}); });
@ -1389,8 +1392,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
<Touch <Touch
onPress={this.resumeRoom} onPress={this.resumeRoom}
style={[styles.joinRoomButton, { backgroundColor: themes[theme].actionTintColor }]} style={[styles.joinRoomButton, { backgroundColor: themes[theme].actionTintColor }]}
enabled={!loading} enabled={!loading}>
>
<Text style={[styles.joinRoomText, { color: themes[theme].buttonText }]} testID='room-view-chat-on-hold-button'> <Text style={[styles.joinRoomText, { color: themes[theme].buttonText }]} testID='room-view-chat-on-hold-button'>
{I18n.t('Resume')} {I18n.t('Resume')}
</Text> </Text>
@ -1405,8 +1407,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
<Touch <Touch
onPress={this.joinRoom} onPress={this.joinRoom}
style={[styles.joinRoomButton, { backgroundColor: themes[theme].actionTintColor }]} style={[styles.joinRoomButton, { backgroundColor: themes[theme].actionTintColor }]}
enabled={!loading} enabled={!loading}>
>
<Text style={[styles.joinRoomText, { color: themes[theme].buttonText }]} testID='room-view-join-button'> <Text style={[styles.joinRoomText, { color: themes[theme].buttonText }]} testID='room-view-join-button'>
{I18n.t(this.isOmnichannel ? 'Take_it' : 'Join')} {I18n.t(this.isOmnichannel ? 'Take_it' : 'Join')}
</Text> </Text>
@ -1460,7 +1461,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
render() { render() {
console.count(`${this.constructor.name}.render calls`); console.count(`${this.constructor.name}.render calls`);
const { room, loading, action, selectedMessages } = this.state; const { room, loading, action, selectedMessages } = this.state;
const { user, baseUrl, theme, width, serverVersion, navigation } = this.props; const { user, baseUrl, theme, width, serverVersion, navigation, encryptionEnabled } = this.props;
const { rid, t } = room; const { rid, t } = room;
let bannerClosed; let bannerClosed;
let announcement; let announcement;
@ -1468,14 +1469,16 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
({ bannerClosed, announcement } = room); ({ bannerClosed, announcement } = room);
} }
// Missing room encryption key if ('encrypted' in room) {
if (this.missingRoomE2EEKey) { // Missing room encryption key
return <MissingRoomE2EEKey />; if (Encryption.isMissingRoomE2EEKey({ encryptionEnabled, roomEncrypted: room.encrypted, E2EKey: room.E2EKey })) {
} return <MissingRoomE2EEKey />;
}
// Encrypted room, but user session is not encrypted // Encrypted room, but user session is not encrypted
if (this.e2eeDisabledEncryptedRoom) { if (Encryption.isE2EEDisabledEncryptedRoom({ encryptionEnabled, roomEncrypted: room.encrypted })) {
return <EncryptedRoom navigation={navigation} roomName={getRoomTitle(room)} />; return <EncryptedRoom navigation={navigation} roomName={getRoomTitle(room)} />;
}
} }
return ( return (
@ -1493,8 +1496,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
onSendMessage: this.handleSendMessage, onSendMessage: this.handleSendMessage,
setQuotesAndText: this.setQuotesAndText, setQuotesAndText: this.setQuotesAndText,
getText: this.getText getText: this.getText
}} }}>
>
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }} testID='room-view'> <SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }} testID='room-view'>
<StatusBar /> <StatusBar />
<Banner title={I18n.t('Announcement')} text={announcement} bannerClosed={bannerClosed} closeBanner={this.closeBanner} /> <Banner title={I18n.t('Announcement')} text={announcement} bannerClosed={bannerClosed} closeBanner={this.closeBanner} />

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { Dispatch } from 'redux';
import { StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationProp } from '@react-navigation/stack';
import { BackHandler, FlatList, Keyboard, ScrollView, Text, View } from 'react-native'; import { BackHandler, FlatList, Keyboard, ScrollView, Text, View } from 'react-native';
import ShareExtension from 'rn-extensions-share'; import ShareExtension from 'rn-extensions-share';
@ -20,11 +21,13 @@ import { animateNextTransition } from '../../lib/methods/helpers/layoutAnimation
import { TSupportedThemes, withTheme } from '../../theme'; import { TSupportedThemes, withTheme } from '../../theme';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import { sanitizeLikeString } from '../../lib/database/utils'; import { sanitizeLikeString } from '../../lib/database/utils';
import { Encryption } from '../../lib/encryption';
import styles from './styles'; import styles from './styles';
import ShareListHeader from './Header'; import ShareListHeader from './Header';
import { TServerModel, TSubscriptionModel } from '../../definitions'; import { IApplicationState, TServerModel, TSubscriptionModel } from '../../definitions';
import { ShareInsideStackParamList } from '../../definitions/navigationTypes'; import { ShareInsideStackParamList } from '../../definitions/navigationTypes';
import { getRoomAvatar, isAndroid, isIOS, askAndroidMediaPermissions } from '../../lib/methods/helpers'; import { getRoomAvatar, isAndroid, isIOS, askAndroidMediaPermissions } from '../../lib/methods/helpers';
import { encryptionInit } from '../../actions/encryption';
interface IDataFromShare { interface IDataFromShare {
value: string; value: string;
@ -61,6 +64,8 @@ interface IShareListViewProps extends INavigationOption {
token: string; token: string;
userId: string; userId: string;
theme: TSupportedThemes; theme: TSupportedThemes;
encryptionEnabled: boolean;
dispatch: Dispatch;
} }
const getItemLayout = (data: any, index: number) => ({ length: data.length, offset: ROW_HEIGHT * index, index }); const getItemLayout = (data: any, index: number) => ({ length: data.length, offset: ROW_HEIGHT * index, index });
@ -97,8 +102,9 @@ class ShareListView extends React.Component<IShareListViewProps, IState> {
} }
async componentDidMount() { async componentDidMount() {
const { server } = this.props; const { server, dispatch } = this.props;
try { try {
dispatch(encryptionInit());
const data = (await ShareExtension.data()) as IDataFromShare[]; const data = (await ShareExtension.data()) as IDataFromShare[];
if (isAndroid) { if (isAndroid) {
await this.askForPermission(data); await this.askForPermission(data);
@ -217,6 +223,7 @@ class ShareListView extends React.Component<IShareListViewProps, IState> {
}; };
query = async (text?: string) => { query = async (text?: string) => {
const { encryptionEnabled } = this.props;
const db = database.active; const db = database.active;
const defaultWhereClause = [ const defaultWhereClause = [
Q.where('archived', false), Q.where('archived', false),
@ -234,19 +241,30 @@ class ShareListView extends React.Component<IShareListViewProps, IState> {
.query(...defaultWhereClause) .query(...defaultWhereClause)
.fetch()) as TSubscriptionModel[]; .fetch()) as TSubscriptionModel[];
return data.map(item => ({ return data
rid: item.rid, .map(item => {
t: item.t, if (Encryption.isMissingRoomE2EEKey({ encryptionEnabled, roomEncrypted: item.encrypted, E2EKey: item.E2EKey })) {
name: item.name, return null;
fname: item.fname, }
blocked: item.blocked, if (Encryption.isE2EEDisabledEncryptedRoom({ encryptionEnabled, roomEncrypted: item.encrypted })) {
blocker: item.blocker, return null;
prid: item.prid, }
uids: item.uids,
usernames: item.usernames, return {
topic: item.topic, rid: item.rid,
teamMain: item.teamMain t: item.t,
})); name: item.name,
fname: item.fname,
blocked: item.blocked,
blocker: item.blocker,
prid: item.prid,
uids: item.uids,
usernames: item.usernames,
topic: item.topic,
teamMain: item.teamMain
};
})
.filter(item => !!item);
}; };
getSubscriptions = async (server: string) => { getSubscriptions = async (server: string) => {
@ -465,10 +483,11 @@ class ShareListView extends React.Component<IShareListViewProps, IState> {
}; };
} }
const mapStateToProps = ({ share }: any) => ({ const mapStateToProps = ({ share, encryption }: IApplicationState) => ({
userId: share.user && share.user.id, userId: share.user && (share.user.id as string),
token: share.user && share.user.token, token: share.user && (share.user.token as string),
server: share.server.server server: share.server.server as string,
encryptionEnabled: encryption.enabled
}); });
export default connect(mapStateToProps)(withTheme(ShareListView)); export default connect(mapStateToProps)(withTheme(ShareListView));