refactored right panel to hook

pull/1783/head
Audric Ackermann 4 years ago
parent 4ca5a4f093
commit 511adcf388
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -200,6 +200,7 @@
.module-message__generic-attachment__icon-container {
position: relative;
cursor: pointer;
}
.module-message__generic-attachment__spinner-container {
padding-inline-start: 4px;

@ -32,14 +32,15 @@
height: 100%;
right: 0vw;
transition: transform 1.5 * $session-transition-duration ease-in-out;
transition: transform 0.3s ease-in-out;
transform: translateX(100%);
will-change: transform;
width: 25vw;
z-index: 1;
&.show {
transform: none;
transition: transform $session-transition-duration ease-in-out;
transition: transform 0.3s ease-in-out;
z-index: 2;
}
}

@ -14,6 +14,8 @@ import { DefaultTheme } from 'styled-components';
import useUnmount from 'react-use/lib/useUnmount';
import { useEncryptedFileFetch } from '../hooks/useEncryptedFileFetch';
import { darkTheme } from '../state/ducks/SessionTheme';
import { useDispatch } from 'react-redux';
import { showLightBox } from '../state/ducks/conversations';
const Colors = {
TEXT_SECONDARY: '#bbb',
@ -29,7 +31,6 @@ const colorSVG = (url: string, color: string) => {
};
type Props = {
close: () => void;
contentType: MIME.MIMEType | undefined;
objectURL: string;
caption?: string;
@ -285,18 +286,19 @@ export const Lightbox = (props: Props) => {
const containerRef = useRef<HTMLDivElement>(null);
// there is no theme in use on the lightbox
const theme = darkTheme;
const dispatch = useDispatch();
const { caption, contentType, objectURL, onNext, onPrevious, onSave } = props;
const onObjectClick = (event: any) => {
event.stopPropagation();
props.close?.();
dispatch(showLightBox(undefined));
};
const onContainerClick = (event: React.MouseEvent<HTMLDivElement>) => {
if (containerRef && event.target !== containerRef.current) {
return;
}
props.close?.();
dispatch(showLightBox(undefined));
};
return (
@ -326,7 +328,7 @@ export const Lightbox = (props: Props) => {
<IconButton
type="close"
onClick={() => {
props.close?.();
dispatch(showLightBox(undefined));
}}
theme={theme}
/>

@ -9,6 +9,8 @@ import { Lightbox } from './Lightbox';
import { AttachmentTypeWithPath } from '../types/Attachment';
// tslint:disable-next-line: no-submodule-imports
import useKey from 'react-use/lib/useKey';
import { showLightBox } from '../state/ducks/conversations';
import { useDispatch } from 'react-redux';
export interface MediaItemType {
objectURL?: string;
@ -22,16 +24,17 @@ export interface MediaItemType {
}
type Props = {
close: () => void;
media: Array<MediaItemType>;
onSave?: (saveData: MediaItemType) => void;
selectedIndex: number;
};
export const LightboxGallery = (props: Props) => {
const { close, media, onSave } = props;
const { media, onSave } = props;
const [currentIndex, setCurrentIndex] = useState(0);
const dispatch = useDispatch();
// just run once, when the component is mounted. It's to show the lightbox on the specified index at start.
useEffect(() => {
setCurrentIndex(props.selectedIndex);
@ -86,12 +89,11 @@ export const LightboxGallery = (props: Props) => {
);
useKey('Escape', () => {
props.close?.();
dispatch(showLightBox(undefined));
});
return (
<Lightbox
close={close}
onPrevious={onPrevious}
onNext={onNext}
onSave={saveCallback}

@ -1,42 +1,35 @@
import React from 'react';
import { DocumentListItem } from './DocumentListItem';
import { ItemClickEvent } from './types/ItemClickEvent';
import { MediaGridItem } from './MediaGridItem';
import { MediaItemType } from '../../LightboxGallery';
import { missingCaseError } from '../../../util/missingCaseError';
import { useSelector } from 'react-redux';
import { getSelectedConversationKey } from '../../../state/selectors/conversations';
interface Props {
type Props = {
type: 'media' | 'documents';
mediaItems: Array<MediaItemType>;
onItemClick?: (event: ItemClickEvent) => void;
}
};
export class AttachmentSection extends React.Component<Props> {
public render() {
const { type } = this.props;
const Items = (props: Props): JSX.Element => {
const { mediaItems, type } = props;
const selectedConversationKey = useSelector(getSelectedConversationKey);
return (
<div className="module-attachment-section">
<div className="module-attachment-section__items">
<div className={`module-attachment-section__items-${type}`}>{this.renderItems()}</div>
</div>
</div>
);
}
private renderItems() {
const { mediaItems, type } = this.props;
return mediaItems.map((mediaItem, position, array) => {
<>
{mediaItems.map((mediaItem, position, array) => {
const shouldShowSeparator = position < array.length - 1;
const { index, attachment, messageTimestamp, messageId } = mediaItem;
const onClick = this.createClickHandler(mediaItem);
switch (type) {
case 'media':
return (
<MediaGridItem key={`${messageId}-${index}`} mediaItem={mediaItem} onClick={onClick} />
<MediaGridItem
key={`${messageId}-${index}`}
mediaItem={mediaItem}
mediaItems={mediaItems}
/>
);
case 'documents':
return (
@ -45,23 +38,29 @@ export class AttachmentSection extends React.Component<Props> {
fileName={attachment.fileName}
fileSize={attachment.size}
shouldShowSeparator={shouldShowSeparator}
onClick={onClick}
timestamp={messageTimestamp}
mediaItem={mediaItem}
conversationId={selectedConversationKey as string}
/>
);
default:
return missingCaseError(type);
}
});
}
private readonly createClickHandler = (mediaItem: MediaItemType) => () => {
const { onItemClick, type } = this.props;
})}
</>
);
};
if (!onItemClick) {
return;
}
export const AttachmentSection = (props: Props) => {
const { type } = props;
onItemClick({ mediaItem, type });
return (
<div className="module-attachment-section">
<div className="module-attachment-section__items">
<div className={`module-attachment-section__items-${type}`}>
<Items {...props} />
</div>
</div>
</div>
);
};
}

@ -4,6 +4,10 @@ import classNames from 'classnames';
import moment from 'moment';
// tslint:disable-next-line:match-default-export-name
import formatFileSize from 'filesize';
import { getDecryptedMediaUrl } from '../../../session/crypto/DecryptedAttachmentsManager';
import { sendDataExtractionNotification } from '../../../session/messages/outgoing/controlMessage/DataExtractionNotificationMessage';
import { AttachmentTypeWithPath, save } from '../../../types/Attachment';
import { MediaItemType } from '../../LightboxGallery';
type Props = {
// Required
@ -12,8 +16,31 @@ type Props = {
// Optional
fileName?: string;
fileSize?: number | null;
onClick?: () => void;
shouldShowSeparator?: boolean;
mediaItem: MediaItemType;
conversationId: string;
};
const saveAttachment = async ({
attachment,
messageTimestamp,
messageSender,
conversationId,
}: {
attachment: AttachmentTypeWithPath;
messageTimestamp: number;
messageSender: string;
conversationId: string;
}) => {
const timestamp = messageTimestamp;
attachment.url = await getDecryptedMediaUrl(attachment.url, attachment.contentType);
save({
attachment,
document,
getAbsolutePath: window.Signal.Migrations.getAbsoluteAttachmentPath,
timestamp,
});
await sendDataExtractionNotification(conversationId, messageSender, timestamp);
};
export const DocumentListItem = (props: Props) => {
@ -28,7 +55,18 @@ export const DocumentListItem = (props: Props) => {
defaultShowSeparator ? 'module-document-list-item--with-separator' : null
)}
>
<div className="module-document-list-item__content" role="button" onClick={props.onClick}>
<div
className="module-document-list-item__content"
role="button"
onClick={() => {
void saveAttachment({
messageSender: props.mediaItem.messageSender,
messageTimestamp: props.mediaItem.messageTimestamp,
attachment: props.mediaItem.attachment,
conversationId: props.conversationId,
});
}}
>
<div className="module-document-list-item__icon" />
<div className="module-document-list-item__metadata">
<span className="module-document-list-item__file-name">{fileName}</span>

@ -1,26 +1,17 @@
import React from 'react';
import React, { useState } from 'react';
import classNames from 'classnames';
import { AttachmentSection } from './AttachmentSection';
import { EmptyState } from './EmptyState';
import { ItemClickEvent } from './types/ItemClickEvent';
import { missingCaseError } from '../../../util/missingCaseError';
import { MediaItemType } from '../../LightboxGallery';
interface Props {
type Props = {
documents: Array<MediaItemType>;
media: Array<MediaItemType>;
onItemClick?: (event: ItemClickEvent) => void;
}
interface State {
selectedTab: 'media' | 'documents';
}
};
interface TabSelectEvent {
type: 'media' | 'documents';
}
type TabType = 'media' | 'documents';
const Tab = ({
isSelected,
@ -30,22 +21,16 @@ const Tab = ({
}: {
isSelected: boolean;
label: string;
onSelect?: (event: TabSelectEvent) => void;
type: 'media' | 'documents';
onSelect: () => void;
type: TabType;
}) => {
const handleClick = onSelect
? () => {
onSelect({ type });
}
: undefined;
return (
<div
className={classNames(
'module-media-gallery__tab',
isSelected ? 'module-media-gallery__tab--active' : null
)}
onClick={handleClick}
onClick={onSelect}
role="tab"
>
{label}
@ -53,42 +38,8 @@ const Tab = ({
);
};
export class MediaGallery extends React.Component<Props, State> {
public state: State = {
selectedTab: 'media',
};
public render() {
const { selectedTab } = this.state;
return (
<div className="module-media-gallery">
<div className="module-media-gallery__tab-container">
<Tab
label={window.i18n('media')}
type="media"
isSelected={selectedTab === 'media'}
onSelect={this.handleTabSelect}
/>
<Tab
label={window.i18n('documents')}
type="documents"
isSelected={selectedTab === 'documents'}
onSelect={this.handleTabSelect}
/>
</div>
<div className="module-media-gallery__content">{this.renderSections()}</div>
</div>
);
}
private readonly handleTabSelect = (event: TabSelectEvent): void => {
this.setState({ selectedTab: event.type });
};
private renderSections() {
const { media, documents, onItemClick } = this.props;
const { selectedTab } = this.state;
const Sections = (props: Props & { selectedTab: TabType }) => {
const { media, documents, selectedTab } = props;
const mediaItems = selectedTab === 'media' ? media : documents;
const type = selectedTab;
@ -112,13 +63,40 @@ export class MediaGallery extends React.Component<Props, State> {
return (
<div className="module-media-gallery__sections">
<AttachmentSection
key="mediaItems"
type={type}
mediaItems={mediaItems}
onItemClick={onItemClick}
<AttachmentSection key="mediaItems" type={type} mediaItems={mediaItems} />
</div>
);
};
export const MediaGallery = (props: Props) => {
const [selectedTab, setSelectedTab] = useState<TabType>('media');
const isDocumentSelected = selectedTab === 'documents';
const isMediaSelected = selectedTab === 'media';
return (
<div className="module-media-gallery">
<div className="module-media-gallery__tab-container">
<Tab
label={window.i18n('media')}
type="media"
isSelected={isMediaSelected}
onSelect={() => {
setSelectedTab('media');
}}
/>
<Tab
label={window.i18n('documents')}
type="documents"
isSelected={isDocumentSelected}
onSelect={() => {
setSelectedTab('documents');
}}
/>
</div>
<div className="module-media-gallery__content">
<Sections {...props} selectedTab={selectedTab} />
</div>
</div>
);
}
}
};

@ -4,10 +4,12 @@ import classNames from 'classnames';
import { isImageTypeSupported, isVideoTypeSupported } from '../../../util/GoogleChrome';
import { MediaItemType } from '../../LightboxGallery';
import { useEncryptedFileFetch } from '../../../hooks/useEncryptedFileFetch';
import { showLightBox } from '../../../state/ducks/conversations';
import { LightBoxOptions } from '../../session/conversation/SessionConversation';
type Props = {
mediaItem: MediaItemType;
onClick?: () => void;
mediaItems: Array<MediaItemType>;
};
const MediaGridItemContent = (props: Props) => {
@ -88,7 +90,18 @@ const MediaGridItemContent = (props: Props) => {
export const MediaGridItem = (props: Props) => {
return (
<div className="module-media-grid-item" role="button" onClick={props.onClick}>
<div
className="module-media-grid-item"
role="button"
onClick={() => {
const lightBoxOptions: LightBoxOptions = {
media: props.mediaItems,
attachment: props.mediaItem.attachment,
};
window.inboxStore?.dispatch(showLightBox(lightBoxOptions));
}}
>
<MediaGridItemContent {...props} />
</div>
);

@ -1,6 +0,0 @@
import { MediaItemType } from '../../../LightboxGallery';
export interface ItemClickEvent {
mediaItem: MediaItemType;
type: 'media' | 'documents';
}

@ -23,6 +23,7 @@ import {
PropsForMessage,
ReduxConversationType,
resetSelectedMessageIds,
showLightBox,
SortedMessageModelProps,
} from '../../../state/ducks/conversations';
import { MessageView } from '../../MainViewController';
@ -47,9 +48,6 @@ interface State {
// quoted message
quotedMessageTimestamp?: number;
quotedMessageProps?: any;
// lightbox options
lightBoxOptions?: LightBoxOptions;
}
export interface LightBoxOptions {
@ -66,6 +64,9 @@ interface Props {
selectedMessages: Array<string>;
showMessageDetails: boolean;
isRightPanelShowing: boolean;
// lightbox options
lightBoxOptions?: LightBoxOptions;
}
export class SessionConversation extends React.Component<Props, State> {
@ -175,13 +176,7 @@ export class SessionConversation extends React.Component<Props, State> {
// ~~~~~~~~~~~~~~ RENDER METHODS ~~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public render() {
const {
showRecordingView,
quotedMessageProps,
lightBoxOptions,
isDraggingFile,
stagedAttachments,
} = this.state;
const { showRecordingView, quotedMessageProps, isDraggingFile, stagedAttachments } = this.state;
const {
selectedConversation,
@ -190,6 +185,7 @@ export class SessionConversation extends React.Component<Props, State> {
showMessageDetails,
selectedMessages,
isRightPanelShowing,
lightBoxOptions,
} = this.props;
if (!selectedConversation || !messagesProps) {
@ -269,7 +265,7 @@ export class SessionConversation extends React.Component<Props, State> {
<div
className={classNames('conversation-item__options-pane', isRightPanelShowing && 'show')}
>
<SessionRightPanelWithDetails {...this.getRightPanelProps()} />
<SessionRightPanelWithDetails />
</div>
</SessionTheme>
);
@ -311,15 +307,6 @@ export class SessionConversation extends React.Component<Props, State> {
};
}
// tslint:disable-next-line: max-func-body-length
public getRightPanelProps() {
return {
onShowLightBox: (lightBoxOptions?: LightBoxOptions) => {
this.setState({ lightBoxOptions });
},
};
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~~~~~~~~~~~ MICROPHONE METHODS ~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -390,7 +377,7 @@ export class SessionConversation extends React.Component<Props, State> {
media: media as any,
attachment,
};
this.setState({ lightBoxOptions });
window.inboxStore?.dispatch(showLightBox(lightBoxOptions));
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -484,14 +471,7 @@ export class SessionConversation extends React.Component<Props, State> {
: 0;
console.warn('renderLightBox', { media, attachment });
return (
<LightboxGallery
media={media}
close={() => {
this.setState({ lightBoxOptions: undefined });
}}
selectedIndex={selectedIndex}
onSave={this.saveAttachment}
/>
<LightboxGallery media={media} selectedIndex={selectedIndex} onSave={this.saveAttachment} />
);
}

@ -4,23 +4,15 @@ import { Avatar, AvatarSize } from '../../Avatar';
import { SessionButton, SessionButtonColor, SessionButtonType } from '../SessionButton';
import { SessionDropdown } from '../SessionDropdown';
import { MediaGallery } from '../../conversation/media-gallery/MediaGallery';
import _, { noop } from 'lodash';
import { TimerOption } from '../../conversation/ConversationHeader';
import _ from 'lodash';
import { Constants } from '../../../session';
import {
ConversationAvatar,
usingClosedConversationDetails,
} from '../usingClosedConversationDetails';
import { AttachmentTypeWithPath, save } from '../../../types/Attachment';
import { DefaultTheme, useTheme, withTheme } from 'styled-components';
import { ConversationAvatar } from '../usingClosedConversationDetails';
import { AttachmentTypeWithPath } from '../../../types/Attachment';
import { useTheme } from 'styled-components';
import {
getMessagesWithFileAttachments,
getMessagesWithVisualMediaAttachments,
} from '../../../data/data';
import { getDecryptedMediaUrl } from '../../../session/crypto/DecryptedAttachmentsManager';
import { LightBoxOptions } from './SessionConversation';
import { UserUtils } from '../../../session/utils';
import { sendDataExtractionNotification } from '../../../session/messages/outgoing/controlMessage/DataExtractionNotificationMessage';
import { SpacerLG } from '../../basic/Text';
import {
deleteMessagesByConvoIdWithConfirmation,
@ -32,30 +24,23 @@ import {
showUpdateGroupMembersByConvoId,
showUpdateGroupNameByConvoId,
} from '../../../interactions/conversationInteractions';
import { ItemClickEvent } from '../../conversation/media-gallery/types/ItemClickEvent';
import { MediaItemType } from '../../LightboxGallery';
// tslint:disable-next-line: no-submodule-imports
import useInterval from 'react-use/lib/useInterval';
import { useDispatch, useSelector } from 'react-redux';
import { getTimerOptions } from '../../../state/selectors/timerOptions';
import { getSelectedConversation, isRightPanelShowing } from '../../../state/selectors/conversations';
import {
getSelectedConversation,
isRightPanelShowing,
} from '../../../state/selectors/conversations';
import { useMembersAvatars } from '../../../hooks/useMembersAvatar';
import { closeRightPanel } from '../../../state/ducks/conversations';
type Props = {
memberAvatars?: Array<ConversationAvatar>; // this is added by usingClosedConversationDetails
onShowLightBox: (lightboxOptions?: LightBoxOptions) => void;
};
async function getMediaGalleryProps(
conversationId: string,
medias: Array<MediaItemType>,
onShowLightBox: (lightboxOptions?: LightBoxOptions) => void
conversationId: string
): Promise<{
documents: Array<MediaItemType>;
media: Array<MediaItemType>;
onItemClick: any;
}> {
// We fetch more documents than media as they dont require to be loaded
// into memory right away. Revisit this once we have infinite scrolling:
@ -118,61 +103,9 @@ async function getMediaGalleryProps(
};
});
const saveAttachment = async ({
attachment,
messageTimestamp,
messageSender,
}: {
attachment: AttachmentTypeWithPath;
messageTimestamp: number;
messageSender: string;
}) => {
const timestamp = messageTimestamp;
attachment.url = await getDecryptedMediaUrl(attachment.url, attachment.contentType);
save({
attachment,
document,
getAbsolutePath: window.Signal.Migrations.getAbsoluteAttachmentPath,
timestamp,
});
await sendDataExtractionNotification(conversationId, messageSender, timestamp);
};
const onItemClick = (event: ItemClickEvent) => {
if (!event) {
console.warn('no event');
return;
}
const { mediaItem, type } = event;
switch (type) {
case 'documents': {
void saveAttachment({
messageSender: mediaItem.messageSender,
messageTimestamp: mediaItem.messageTimestamp,
attachment: mediaItem.attachment,
});
break;
}
case 'media': {
const lightBoxOptions: LightBoxOptions = {
media: medias,
attachment: mediaItem.attachment,
};
onShowLightBox(lightBoxOptions);
break;
}
default:
throw new TypeError(`Unknown attachment type: '${type}'`);
}
};
return {
media,
documents: _.compact(documents), // remove null
onItemClick,
};
}
@ -239,31 +172,30 @@ const HeaderItem = () => {
// tslint:disable: cyclomatic-complexity
// tslint:disable: max-func-body-length
export const SessionRightPanelWithDetails = (props: Props) => {
export const SessionRightPanelWithDetails = () => {
const [documents, setDocuments] = useState<Array<MediaItemType>>([]);
const [media, setMedia] = useState<Array<MediaItemType>>([]);
const [onItemClick, setOnItemClick] = useState<any>(undefined);
const selectedConversation = useSelector(getSelectedConversation);
const isShowing = useSelector(isRightPanelShowing);
console.warn('props', props);
useEffect(() => {
let isRunning = true;
if (isShowing && selectedConversation) {
void getMediaGalleryProps(selectedConversation.id, media, props.onShowLightBox).then(
results => {
void getMediaGalleryProps(selectedConversation.id).then(results => {
console.warn('results2', results);
if (isRunning) {
if (!_.isEqual(documents, results.documents)) {
setDocuments(results.documents);
}
if (!_.isEqual(media, results.media)) {
setMedia(results.media);
setOnItemClick(results.onItemClick);
}
}
);
});
}
return () => {
@ -274,16 +206,10 @@ export const SessionRightPanelWithDetails = (props: Props) => {
useInterval(async () => {
if (isShowing && selectedConversation) {
const results = await getMediaGalleryProps(
selectedConversation.id,
media,
props.onShowLightBox
);
console.warn('results', results);
const results = await getMediaGalleryProps(selectedConversation.id);
if (results.documents.length !== documents.length || results.media.length !== media.length) {
setDocuments(results.documents);
setMedia(results.media);
setOnItemClick(results.onItemClick);
}
}
}, 10000);
@ -319,8 +245,8 @@ export const SessionRightPanelWithDetails = (props: Props) => {
const disappearingMessagesOptions = timerOptions.map(option => {
return {
content: option.name,
onClick: async () => {
await setDisappearingMessagesByConvoId(id, option.value);
onClick: () => {
void setDisappearingMessagesByConvoId(id, option.value);
},
};
});
@ -337,7 +263,6 @@ export const SessionRightPanelWithDetails = (props: Props) => {
: () => {
showLeaveGroupByConvoId(id);
};
console.warn('onItemClick', onItemClick);
return (
<div className="group-settings">
<HeaderItem />
@ -404,7 +329,7 @@ export const SessionRightPanelWithDetails = (props: Props) => {
/>
)}
<MediaGallery documents={documents} media={media} onItemClick={onItemClick} />
<MediaGallery documents={documents} media={media} />
{isGroup && (
// tslint:disable-next-line: use-simple-attributes
<SessionButton

@ -15,6 +15,7 @@ import {
PropsForDataExtractionNotification,
} from '../../models/messageType';
import { NotificationForConvoOption } from '../../components/conversation/ConversationHeader';
import { LightBoxOptions } from '../../components/session/conversation/SessionConversation';
export type MessageModelProps = {
propsForMessage: PropsForMessage;
@ -233,6 +234,7 @@ export type ConversationsStateType = {
messageDetailProps: MessagePropsDetails | undefined;
showRightPanel: boolean;
selectedMessageIds: Array<string>;
lightBox?: LightBoxOptions;
};
async function getMessages(
@ -708,6 +710,13 @@ const conversationsSlice = createSlice({
state.messages = [];
return state;
},
showLightBox(
state: ConversationsStateType,
action: PayloadAction<LightBoxOptions | undefined>
) {
state.lightBox = action.payload;
return state;
},
},
extraReducers: (builder: any) => {
// Add reducers for additional action types here, and handle loading state as needed
@ -752,4 +761,5 @@ export const {
addMessageIdToSelection,
resetSelectedMessageIds,
toggleSelectedMessageId,
showLightBox,
} = actions;

@ -17,6 +17,7 @@ import {
ConversationHeaderProps,
ConversationHeaderTitleProps,
} from '../../components/conversation/ConversationHeader';
import { LightBoxOptions } from '../../components/session/conversation/SessionConversation';
export const getConversations = (state: StateType): ConversationsStateType => state.conversations;
@ -283,3 +284,8 @@ export const getSelectedMessageIds = createSelector(
getConversations,
(state: ConversationsStateType): Array<string> => state.selectedMessageIds
);
export const getLightBoxOptions = createSelector(
getConversations,
(state: ConversationsStateType): LightBoxOptions | undefined => state.lightBox
);

@ -4,6 +4,7 @@ import { SessionConversation } from '../../components/session/conversation/Sessi
import { StateType } from '../reducer';
import { getTheme } from '../selectors/theme';
import {
getLightBoxOptions,
getMessagesOfSelectedConversation,
getSelectedConversation,
getSelectedConversationKey,
@ -23,6 +24,7 @@ const mapStateToProps = (state: StateType) => {
showMessageDetails: isMessageDetailView(state),
isRightPanelShowing: isRightPanelShowing(state),
selectedMessages: getSelectedMessageIds(state),
lightBoxOptions: getLightBoxOptions(state),
};
};

Loading…
Cancel
Save