enable back attachments download on context menu

pull/1387/head
Audric Ackermann 5 years ago
parent f166ec814e
commit a7c4ce77a1
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -621,12 +621,6 @@
onDelete: () => this.trigger('delete', this), onDelete: () => this.trigger('delete', this),
onClickLinkPreview: url => this.trigger('navigate-to', url), onClickLinkPreview: url => this.trigger('navigate-to', url),
onDownload: isDangerous =>
this.trigger('download', {
attachment: firstAttachment,
message: this,
isDangerous,
}),
onShowUserDetails: pubkey => onShowUserDetails: pubkey =>
window.Whisper.events.trigger('onShowUserDetails', { window.Whisper.events.trigger('onShowUserDetails', {
userPubKey: pubkey, userPubKey: pubkey,

@ -112,7 +112,7 @@ export interface Props {
onSelectMessage: (messageId: string) => void; onSelectMessage: (messageId: string) => void;
onReply?: (messagId: number) => void; onReply?: (messagId: number) => void;
onRetrySend?: () => void; onRetrySend?: () => void;
onDownload?: (isDangerous: boolean) => void; onDownload?: (attachment: AttachmentType) => void;
onDelete?: () => void; onDelete?: () => void;
onCopyPubKey?: () => void; onCopyPubKey?: () => void;
onBanUser?: () => void; onBanUser?: () => void;
@ -853,7 +853,7 @@ export class Message extends React.PureComponent<Props, State> {
onClick={(e: any) => { onClick={(e: any) => {
e.event.stopPropagation(); e.event.stopPropagation();
if (onDownload) { if (onDownload) {
onDownload(isDangerous); onDownload(attachments[0]);
} }
}} }}
> >

@ -22,7 +22,7 @@ import { SessionConversationMessagesList } from './SessionConversationMessagesLi
import { LightboxGallery, MediaItemType } from '../../LightboxGallery'; import { LightboxGallery, MediaItemType } from '../../LightboxGallery';
import { Message } from '../../conversation/media-gallery/types/Message'; import { Message } from '../../conversation/media-gallery/types/Message';
import { AttachmentType } from '../../../types/Attachment'; import { AttachmentType, save } from '../../../types/Attachment';
import { ToastUtils } from '../../../session/utils'; import { ToastUtils } from '../../../session/utils';
import * as MIME from '../../../types/MIME'; import * as MIME from '../../../types/MIME';
import { SessionFileDropzone } from './SessionFileDropzone'; import { SessionFileDropzone } from './SessionFileDropzone';
@ -133,6 +133,7 @@ export class SessionConversation extends React.Component<Props, State> {
this.replyToMessage = this.replyToMessage.bind(this); this.replyToMessage = this.replyToMessage.bind(this);
this.onClickAttachment = this.onClickAttachment.bind(this); this.onClickAttachment = this.onClickAttachment.bind(this);
this.downloadAttachment = this.downloadAttachment.bind(this);
this.getMessages = this.getMessages.bind(this); this.getMessages = this.getMessages.bind(this);
// Keyboard navigation // Keyboard navigation
@ -435,7 +436,6 @@ export class SessionConversation extends React.Component<Props, State> {
), ),
members, members,
subscriberCount: conversation.get('subscriberCount'), subscriberCount: conversation.get('subscriberCount'),
selectedMessages: this.state.selectedMessages?.length,
isKickedFromGroup: conversation.get('isKickedFromGroup'), isKickedFromGroup: conversation.get('isKickedFromGroup'),
expirationSettingName, expirationSettingName,
showBackButton: Boolean(this.state.infoViewState), showBackButton: Boolean(this.state.infoViewState),
@ -534,6 +534,7 @@ export class SessionConversation extends React.Component<Props, State> {
replyToMessage: this.replyToMessage, replyToMessage: this.replyToMessage,
doneInitialScroll: this.state.doneInitialScroll, doneInitialScroll: this.state.doneInitialScroll,
onClickAttachment: this.onClickAttachment, onClickAttachment: this.onClickAttachment,
onDownloadAttachment: this.downloadAttachment,
messageContainerRef: this.messageContainerRef, messageContainerRef: this.messageContainerRef,
}; };
} }
@ -965,12 +966,12 @@ export class SessionConversation extends React.Component<Props, State> {
index, index,
}: { }: {
attachment: AttachmentType; attachment: AttachmentType;
message: Message; message?: Message;
index: number; index?: number;
}) { }) {
const { getAbsoluteAttachmentPath } = window.Signal.Migrations; const { getAbsoluteAttachmentPath } = window.Signal.Migrations;
window.Signal.Types.Attachment.save({ save({
attachment, attachment,
document, document,
getAbsolutePath: getAbsoluteAttachmentPath, getAbsolutePath: getAbsoluteAttachmentPath,

@ -8,6 +8,8 @@ import { ResetSessionNotification } from '../../conversation/ResetSessionNotific
import { Constants } from '../../../session'; import { Constants } from '../../../session';
import _ from 'lodash'; import _ from 'lodash';
import { ConversationModel } from '../../../../js/models/conversations'; import { ConversationModel } from '../../../../js/models/conversations';
import { contextMenu } from 'react-contexify';
import { AttachmentType } from '../../../types/Attachment';
interface State { interface State {
isScrolledToBottom: boolean; isScrolledToBottom: boolean;
@ -31,6 +33,7 @@ interface Props {
) => Promise<{ previousTopMessage: string }>; ) => Promise<{ previousTopMessage: string }>;
replyToMessage: (messageId: number) => Promise<void>; replyToMessage: (messageId: number) => Promise<void>;
onClickAttachment: (attachment: any, message: any) => void; onClickAttachment: (attachment: any, message: any) => void;
onDownloadAttachment: ({ attachment }: { attachment: any}) => void;
} }
export class SessionConversationMessagesList extends React.Component< export class SessionConversationMessagesList extends React.Component<
@ -122,8 +125,6 @@ export class SessionConversationMessagesList extends React.Component<
<> <>
{messages.map((message: any) => { {messages.map((message: any) => {
const messageProps = message.propsForMessage; const messageProps = message.propsForMessage;
// const quoteProps = messageProps.quote;
// console.warn('propsForQuote', quoteProps);
const timerProps = message.propsForTimerNotification; const timerProps = message.propsForTimerNotification;
const resetSessionProps = message.propsForResetSessionNotification; const resetSessionProps = message.propsForResetSessionNotification;
@ -136,7 +137,6 @@ export class SessionConversationMessagesList extends React.Component<
// in a series of messages from the same user // in a series of messages from the same user
item = messageProps item = messageProps
? this.renderMessage( ? this.renderMessage(
message,
messageProps, messageProps,
message.firstMessageOfSeries, message.firstMessageOfSeries,
multiSelectMode multiSelectMode
@ -149,7 +149,6 @@ export class SessionConversationMessagesList extends React.Component<
) : ( ) : (
item item
); );
// item = attachmentProps ? this.renderMessage(timerProps) : item;
return item; return item;
})} })}
@ -158,7 +157,6 @@ export class SessionConversationMessagesList extends React.Component<
} }
public renderMessage( public renderMessage(
message: any,
messageProps: any, messageProps: any,
firstMessageOfSeries: boolean, firstMessageOfSeries: boolean,
multiSelectMode: boolean multiSelectMode: boolean
@ -182,7 +180,9 @@ export class SessionConversationMessagesList extends React.Component<
messageProps.onClickAttachment = (attachment: any) => { messageProps.onClickAttachment = (attachment: any) => {
this.props.onClickAttachment(attachment, messageProps); this.props.onClickAttachment(attachment, messageProps);
}; };
// messageProps.onDownload = () messageProps.onDownload = (attachment: AttachmentType) => {
this.props.onDownloadAttachment({attachment});
};
return <Message {...messageProps} />; return <Message {...messageProps} />;
} }

@ -15,6 +15,7 @@ import {
ConversationAvatar, ConversationAvatar,
usingClosedConversationDetails, usingClosedConversationDetails,
} from '../usingClosedConversationDetails'; } from '../usingClosedConversationDetails';
import { save } from '../../../types/Attachment';
interface Props { interface Props {
id: string; id: string;
@ -184,7 +185,7 @@ class SessionRightPanel extends React.Component<Props, State> {
const saveAttachment = async ({ attachment, message }: any = {}) => { const saveAttachment = async ({ attachment, message }: any = {}) => {
const timestamp = message.received_at; const timestamp = message.received_at;
window.Signal.Types.Attachment.save({ save({
attachment, attachment,
document, document,
getAbsolutePath: window.Signal.Migrations.getAbsoluteAttachmentPath, getAbsolutePath: window.Signal.Migrations.getAbsoluteAttachmentPath,

@ -7,19 +7,22 @@ import { SignalService } from '../../protobuf';
// @ts-ignore // @ts-ignore
import { stringToArrayBuffer } from '../../../js/modules/string_to_array_buffer'; import { stringToArrayBuffer } from '../../../js/modules/string_to_array_buffer';
// tslint:disable-next-line: max-func-body-length
describe('Attachment', () => { describe('Attachment', () => {
describe('getFileExtension', () => { describe('getFileExtension', () => {
it('should return file extension from content type', () => { it('should return file extension from content type', () => {
const input: Attachment.Attachment = { const input: Attachment.AttachmentType = {
data: stringToArrayBuffer('foo'), fileName: 'funny-cat.mov',
contentType: MIME.IMAGE_GIF, url: 'funny-cat.mov',
contentType: MIME.VIDEO_QUICKTIME,
}; };
assert.strictEqual(Attachment.getFileExtension(input), 'gif'); assert.strictEqual(Attachment.getFileExtension(input), 'gif');
}); });
it('should return file extension for QuickTime videos', () => { it('should return file extension for QuickTime videos', () => {
const input: Attachment.Attachment = { const input: Attachment.AttachmentType = {
data: stringToArrayBuffer('foo'), fileName: 'funny-cat.mov',
url: 'funny-cat.mov',
contentType: MIME.VIDEO_QUICKTIME, contentType: MIME.VIDEO_QUICKTIME,
}; };
assert.strictEqual(Attachment.getFileExtension(input), 'mov'); assert.strictEqual(Attachment.getFileExtension(input), 'mov');
@ -29,9 +32,9 @@ describe('Attachment', () => {
describe('getSuggestedFilename', () => { describe('getSuggestedFilename', () => {
context('for attachment with filename', () => { context('for attachment with filename', () => {
it('should return existing filename if present', () => { it('should return existing filename if present', () => {
const attachment: Attachment.Attachment = { const attachment: Attachment.AttachmentType = {
fileName: 'funny-cat.mov', fileName: 'funny-cat.mov',
data: stringToArrayBuffer('foo'), url: 'funny-cat.mov',
contentType: MIME.VIDEO_QUICKTIME, contentType: MIME.VIDEO_QUICKTIME,
}; };
const actual = Attachment.getSuggestedFilename({ attachment }); const actual = Attachment.getSuggestedFilename({ attachment });
@ -41,9 +44,11 @@ describe('Attachment', () => {
}); });
context('for attachment without filename', () => { context('for attachment without filename', () => {
it('should generate a filename based on timestamp', () => { it('should generate a filename based on timestamp', () => {
const attachment: Attachment.Attachment = { const attachment: Attachment.AttachmentType = {
data: stringToArrayBuffer('foo'),
contentType: MIME.VIDEO_QUICKTIME, contentType: MIME.VIDEO_QUICKTIME,
url: 'funny-cat.mov',
fileName: 'funny-cat.mov',
}; };
const timestamp = moment('2000-01-01').toDate(); const timestamp = moment('2000-01-01').toDate();
const actual = Attachment.getSuggestedFilename({ const actual = Attachment.getSuggestedFilename({
@ -56,10 +61,10 @@ describe('Attachment', () => {
}); });
context('for attachment with index', () => { context('for attachment with index', () => {
it('should generate a filename based on timestamp', () => { it('should generate a filename based on timestamp', () => {
const attachment: Attachment.Attachment = { const attachment: Attachment.AttachmentType = {
data: stringToArrayBuffer('foo'), fileName: 'funny-cat.mov',
contentType: MIME.VIDEO_QUICKTIME, url: 'funny-cat.mov',
}; contentType: MIME.VIDEO_QUICKTIME, };
const timestamp = new Date(new Date(0).getTimezoneOffset() * 60 * 1000); const timestamp = new Date(new Date(0).getTimezoneOffset() * 60 * 1000);
const actual = Attachment.getSuggestedFilename({ const actual = Attachment.getSuggestedFilename({
attachment, attachment,

@ -330,19 +330,14 @@ export const save = ({
getAbsolutePath, getAbsolutePath,
timestamp, timestamp,
}: { }: {
attachment: Attachment; attachment: AttachmentType;
document: Document; document: Document;
index: number; index?: number;
getAbsolutePath: (relativePath: string) => string; getAbsolutePath: (relativePath: string) => string;
timestamp?: number; timestamp?: number;
}): void => { }): void => {
const isObjectURLRequired = is.undefined(attachment.path); const isObjectURLRequired = is.undefined(attachment.fileName);
const url = !is.undefined(attachment.path) const url = getAbsolutePath(attachment.fileName);
? getAbsolutePath(attachment.path)
: arrayBufferToObjectURL({
data: attachment.data,
type: MIME.APPLICATION_OCTET_STREAM,
});
const filename = getSuggestedFilename({ attachment, timestamp, index }); const filename = getSuggestedFilename({ attachment, timestamp, index });
saveURLAsFile({ url, filename, document }); saveURLAsFile({ url, filename, document });
if (isObjectURLRequired) { if (isObjectURLRequired) {
@ -355,7 +350,7 @@ export const getSuggestedFilename = ({
timestamp, timestamp,
index, index,
}: { }: {
attachment: Attachment; attachment: AttachmentType;
timestamp?: number | Date; timestamp?: number | Date;
index?: number; index?: number;
}): string => { }): string => {
@ -375,7 +370,7 @@ export const getSuggestedFilename = ({
}; };
export const getFileExtension = ( export const getFileExtension = (
attachment: Attachment attachment: AttachmentType
): string | undefined => { ): string | undefined => {
if (!attachment.contentType) { if (!attachment.contentType) {
return; return;

Loading…
Cancel
Save