add first step to enable back MessageDetails.

Still not much the react way to do it for now
This commit is contained in:
Audric Ackermann 2020-11-25 11:52:20 +11:00
parent 71e1f1e143
commit 5c55a9411f
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
8 changed files with 144 additions and 63 deletions

View File

@ -55,6 +55,9 @@ export interface MessageModel extends Backbone.Model<MessageAttributes> {
sendSyncMessageOnly: (message: any) => void;
isUnread: () => boolean;
commit: () => Promise<number>;
getPropsForMessageDetail: (
onSendAnyway: any /*, onShowSafetyNumber: any*/
) => any; // FIXME enable this back. Be sure to handle case where it comes from a medium group
propsForMessage?: any;
propsForTimerNotification?: any;

View File

@ -617,7 +617,6 @@
onCopyPubKey: () => this.copyPubKey(),
onBanUser: () => this.banUser(),
onRetrySend: () => this.retrySend(),
onShowDetail: () => this.trigger('show-message-detail', this),
markRead: readAt => this.markRead(readAt),
onShowUserDetails: pubkey =>
@ -804,7 +803,7 @@
return Boolean(lookup[contactId]);
},
async getPropsForMessageDetail() {
async getPropsForMessageDetail(onSendAnyway, onShowSafetyNumber) {
const newIdentity = i18n('newIdentity');
const OUTGOING_KEY_ERROR = 'OutgoingIdentityKeyError';
@ -867,13 +866,15 @@
isUnidentifiedDelivery,
isPrimaryDevice,
profileName,
onSendAnyway: () =>
this.trigger('force-send', {
onSendAnyway: () => {
onSendAnyway({
contact: this.findContact(id),
message: this,
}),
onShowSafetyNumber: () =>
this.trigger('show-identity', this.findContact(id)),
});
},
onShowSafetyNumber: () => {
onShowSafetyNumber(this.findContact(id));
},
};
})
);
@ -890,7 +891,7 @@
sentAt: this.get('sent_at'),
receivedAt: this.get('received_at'),
message: {
...this.getPropsForMessage({ noClick: true }),
...this.propsForMessage,
disableMenu: true,
// To ensure that group avatar doesn't show up
conversationType: 'direct',

View File

@ -2,6 +2,7 @@
height: calc(100% - 48px);
width: 100%;
overflow-y: auto;
z-index: 2;
}
.message-container {

View File

@ -1203,6 +1203,7 @@
.module-message-detail__contact__text {
margin-inline-start: 10px;
flex-grow: 1;
min-width: 0;
}
.module-message-detail__contact__error {

View File

@ -145,54 +145,56 @@ export class MessageDetail extends React.Component<Props> {
const { errors, message, receivedAt, sentAt } = this.props;
return (
<div className="module-message-detail">
<div className="module-message-detail__message-container">
<Message {...message} />
</div>
<table className="module-message-detail__info">
<tbody>
{(errors || []).map((error, index) => (
<tr key={index}>
<td className="module-message-detail__label">
{i18n('error')}
</td>
<td>
{' '}
<span className="error-message">{error.message}</span>{' '}
</td>
</tr>
))}
<tr>
<td className="module-message-detail__label">{i18n('sent')}</td>
<td>
{moment(sentAt).format('LLLL')}{' '}
<span className="module-message-detail__unix-timestamp">
({sentAt})
</span>
</td>
</tr>
{receivedAt ? (
<div className="message-detail-wrapper">
<div className="module-message-detail">
<div className="module-message-detail__message-container">
<Message {...message} />
</div>
<table className="module-message-detail__info">
<tbody>
{(errors || []).map((error, index) => (
<tr key={index}>
<td className="module-message-detail__label">
{i18n('error')}
</td>
<td>
{' '}
<span className="error-message">{error.message}</span>{' '}
</td>
</tr>
))}
<tr>
<td className="module-message-detail__label">
{i18n('received')}
</td>
<td className="module-message-detail__label">{i18n('sent')}</td>
<td>
{moment(receivedAt).format('LLLL')}{' '}
{moment(sentAt).format('LLLL')}{' '}
<span className="module-message-detail__unix-timestamp">
({receivedAt})
({sentAt})
</span>
</td>
</tr>
) : null}
<tr>
<td className="module-message-detail__label">
{message.direction === 'incoming' ? i18n('from') : i18n('to')}
</td>
</tr>
</tbody>
</table>
{this.renderContacts()}
{this.renderDeleteButton()}
{receivedAt ? (
<tr>
<td className="module-message-detail__label">
{i18n('received')}
</td>
<td>
{moment(receivedAt).format('LLLL')}{' '}
<span className="module-message-detail__unix-timestamp">
({receivedAt})
</span>
</td>
</tr>
) : null}
<tr>
<td className="module-message-detail__label">
{message.direction === 'incoming' ? i18n('from') : i18n('to')}
</td>
</tr>
</tbody>
</table>
{this.renderContacts()}
{this.renderDeleteButton()}
</div>
</div>
);
}

View File

@ -30,6 +30,7 @@ import { ConversationType } from '../../../state/ducks/conversations';
import { MessageView } from '../../MainViewController';
import { getMessageById, removeMessage } from '../../../../js/modules/data';
import { pushUnblockToSend } from '../../../session/utils/Toast';
import { MessageDetail } from '../../conversation/MessageDetail';
interface State {
// Message sending progress
@ -50,8 +51,11 @@ interface State {
showRecordingView: boolean;
showOptionsPane: boolean;
// For displaying `More Info` on messages, and `Safety Number`, etc.
infoViewState?: 'safetyNumber' | 'messageDetails';
// For displaying `Safety Number`, etc.
infoViewState?: 'safetyNumber';
// if set, the `More Info` of a message screen is shown on top of the conversation.
messageDetailShowProps?: any; // FIXME set the type for this
stagedAttachments: Array<StagedAttachmentType>;
isDraggingFile: boolean;
@ -127,6 +131,7 @@ export class SessionConversation extends React.Component<Props, State> {
this.deleteSelectedMessages = this.deleteSelectedMessages.bind(this);
this.replyToMessage = this.replyToMessage.bind(this);
this.showMessageDetails = this.showMessageDetails.bind(this);
this.deleteMessage = this.deleteMessage.bind(this);
this.onClickAttachment = this.onClickAttachment.bind(this);
this.downloadAttachment = this.downloadAttachment.bind(this);
@ -210,6 +215,7 @@ export class SessionConversation extends React.Component<Props, State> {
infoViewState: undefined,
stagedAttachments: [],
isDraggingFile: false,
messageDetailShowProps: undefined,
});
}
}
@ -239,6 +245,8 @@ export class SessionConversation extends React.Component<Props, State> {
selectedMessages,
isDraggingFile,
stagedAttachments,
infoViewState,
messageDetailShowProps,
} = this.state;
const selectionMode = !!selectedMessages.length;
@ -273,7 +281,7 @@ export class SessionConversation extends React.Component<Props, State> {
);
if (this.messageContainerRef.current) {
// force scrolling to bottom on message sent
// this will mark all messages as read
// this will mark all messages as read, and reset the conversation unreadCount
(this.messageContainerRef
.current as any).scrollTop = this.messageContainerRef.current?.scrollHeight;
}
@ -281,8 +289,8 @@ export class SessionConversation extends React.Component<Props, State> {
const shouldRenderRightPanel = !conversationModel.isRss();
const showSafetyNumber = this.state.infoViewState === 'safetyNumber';
const showMessageDetails = this.state.infoViewState === 'messageDetails';
const showSafetyNumber = infoViewState === 'safetyNumber';
const showMessageDetails = !!messageDetailShowProps;
return (
<SessionTheme theme={this.props.theme}>
@ -308,13 +316,15 @@ export class SessionConversation extends React.Component<Props, State> {
<div
className={classNames(
'conversation-info-panel',
this.state.infoViewState && 'show'
(infoViewState || showMessageDetails) && 'show'
)}
>
{showSafetyNumber && (
<SessionKeyVerification conversation={conversationModel} />
)}
{showMessageDetails && <>&nbsp</>}
{showMessageDetails && (
<MessageDetail {...messageDetailShowProps} />
)}
</div>
{lightBoxOptions?.media && this.renderLightBox(lightBoxOptions)}
@ -400,7 +410,11 @@ export class SessionConversation extends React.Component<Props, State> {
public getHeaderProps() {
const { conversationKey } = this.props;
const { selectedMessages, infoViewState } = this.state;
const {
selectedMessages,
infoViewState,
messageDetailShowProps,
} = this.state;
const conversation = window.ConversationController.getOrThrow(
conversationKey
);
@ -433,7 +447,7 @@ export class SessionConversation extends React.Component<Props, State> {
subscriberCount: conversation.get('subscriberCount'),
isKickedFromGroup: conversation.get('isKickedFromGroup'),
expirationSettingName,
showBackButton: Boolean(infoViewState),
showBackButton: Boolean(infoViewState || messageDetailShowProps),
timerOptions: window.Whisper.ExpirationTimerOptions.map((item: any) => ({
name: item.getName(),
value: item.get('seconds'),
@ -458,7 +472,10 @@ export class SessionConversation extends React.Component<Props, State> {
},
onGoBack: () => {
this.setState({ infoViewState: undefined });
this.setState({
infoViewState: undefined,
messageDetailShowProps: undefined,
});
},
onUpdateGroupName: () => {
@ -514,6 +531,7 @@ export class SessionConversation extends React.Component<Props, State> {
deleteMessage: this.deleteMessage,
fetchMessagesForConversation: actions.fetchMessagesForConversation,
replyToMessage: this.replyToMessage,
showMessageDetails: this.showMessageDetails,
onClickAttachment: this.onClickAttachment,
onDownloadAttachment: this.downloadAttachment,
messageContainerRef: this.messageContainerRef,
@ -812,6 +830,13 @@ export class SessionConversation extends React.Component<Props, State> {
}
}
private async showMessageDetails(messageProps: any) {
messageProps.onDeleteMessage = (id: string) => {
this.deleteMessage(id);
};
this.setState({ messageDetailShowProps: messageProps });
}
private onClickAttachment(attachment: any, message: any) {
const lightBoxOptions = {
media: [

View File

@ -37,6 +37,7 @@ interface Props {
count: number;
}) => void;
replyToMessage: (messageId: number) => Promise<void>;
showMessageDetails: (messageProps: any) => Promise<void>;
onClickAttachment: (attachment: any, message: any) => void;
onDownloadAttachment: ({ attachment }: { attachment: any }) => void;
onDeleteSelectedMessages: () => Promise<void>;
@ -61,6 +62,8 @@ export class SessionMessagesList extends React.Component<Props, State> {
this.getScrollOffsetBottomPx = this.getScrollOffsetBottomPx.bind(this);
this.displayUnreadBannerIndex = this.displayUnreadBannerIndex.bind(this);
this.onSendAnyway = this.onSendAnyway.bind(this);
this.messageContainerRef = this.props.messageContainerRef;
this.ignoreScrollEvents = true;
}
@ -272,7 +275,8 @@ export class SessionMessagesList extends React.Component<Props, State> {
{this.renderMessage(
messageProps,
message.firstMessageOfSeries,
multiSelectMode
multiSelectMode,
message
)}
{unreadIndicator}
</>
@ -285,7 +289,8 @@ export class SessionMessagesList extends React.Component<Props, State> {
private renderMessage(
messageProps: any,
firstMessageOfSeries: boolean,
multiSelectMode: boolean
multiSelectMode: boolean,
message: MessageModel
) {
const selected =
!!messageProps?.id &&
@ -298,6 +303,13 @@ export class SessionMessagesList extends React.Component<Props, State> {
messageProps.onSelectMessage = this.props.selectMessage;
messageProps.onDeleteMessage = this.props.deleteMessage;
messageProps.onReply = this.props.replyToMessage;
messageProps.onShowDetail = async () => {
void this.props.showMessageDetails(
await message.getPropsForMessageDetail(
this.onSendAnyway /*, this.props.onShowSafetyNumber*/
)
);
};
messageProps.onClickAttachment = (attachment: any) => {
this.props.onClickAttachment(attachment, messageProps);
@ -534,4 +546,30 @@ export class SessionMessagesList extends React.Component<Props, State> {
const clientHeight = messageContainer.clientHeight;
return scrollHeight - scrollTop - clientHeight;
}
private async onSendAnyway({ contact, message }: any) {
const { i18n } = window;
window.confirmationDialog({
message: i18n('identityKeyErrorOnSend', [
contact.getTitle(),
contact.getTitle(),
]),
messageSub: i18n('youMayWishToVerifyContact'),
okText: i18n('sendAnyway'),
resolve: async () => {
await contact.updateVerified();
if (contact.isUnverified()) {
await contact.setVerifiedDefault();
}
const untrusted = await contact.isUntrusted();
if (untrusted) {
await contact.setApproved();
}
message.resend(contact.id);
},
});
}
}

View File

@ -33,7 +33,7 @@ export type MessageType = {
isSelected?: boolean;
};
export type MessageTypeInConvo = {
type MessageTypeInConvo = {
id: string;
conversationId: string;
attributes: any;
@ -46,6 +46,7 @@ export type MessageTypeInConvo = {
propsForGroupNotification: Object;
firstMessageOfSeries: boolean;
receivedAt: number;
getPropsForMessageDetail(): Promise<any>;
};
export type ConversationType = {
@ -397,6 +398,15 @@ const toPickFromMessageModel = [
'propsForVerificationNotification',
'propsForResetSessionNotification',
'propsForGroupNotification',
// FIXME below are what is needed to fetch on the fly messageDetails. This is not the react way
'getPropsForMessageDetail',
'get',
'getConversation',
'isIncoming',
'findAndFormatContact',
'findContact',
'isUnidentifiedDelivery',
'getStatus',
];
function getEmptyState(): ConversationsStateType {