mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-24 12:19:41 +00:00
Media Gallery improvements
This commit is contained in:
@@ -58,6 +58,10 @@ function createMediaItem(
|
||||
// Unused for now
|
||||
source: undefined,
|
||||
sourceServiceId: undefined,
|
||||
isErased: false,
|
||||
readStatus: undefined,
|
||||
sendStateByConversationId: undefined,
|
||||
errors: undefined,
|
||||
},
|
||||
...overrideProps,
|
||||
};
|
||||
@@ -110,6 +114,10 @@ export function Multimedia(): JSX.Element {
|
||||
// Unused for now
|
||||
source: undefined,
|
||||
sourceServiceId: undefined,
|
||||
isErased: false,
|
||||
readStatus: undefined,
|
||||
sendStateByConversationId: undefined,
|
||||
errors: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -130,6 +138,10 @@ export function Multimedia(): JSX.Element {
|
||||
// Unused for now
|
||||
source: undefined,
|
||||
sourceServiceId: undefined,
|
||||
isErased: false,
|
||||
readStatus: undefined,
|
||||
sendStateByConversationId: undefined,
|
||||
errors: undefined,
|
||||
},
|
||||
},
|
||||
createMediaItem({
|
||||
@@ -170,6 +182,10 @@ export function MissingMedia(): JSX.Element {
|
||||
// Unused for now
|
||||
source: undefined,
|
||||
sourceServiceId: undefined,
|
||||
isErased: false,
|
||||
readStatus: undefined,
|
||||
sendStateByConversationId: undefined,
|
||||
errors: undefined,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -675,6 +675,7 @@ function ReplyOrReactionMessage({
|
||||
id={reply.id}
|
||||
interactionMode="mouse"
|
||||
isSpoilerExpanded={isSpoilerExpanded}
|
||||
isVoiceMessagePlayed={false}
|
||||
messageExpanded={messageExpanded}
|
||||
readStatus={reply.readStatus}
|
||||
renderingContext="StoryViewsNRepliesModal"
|
||||
|
||||
@@ -70,7 +70,6 @@ import {
|
||||
isGIF,
|
||||
isImage,
|
||||
isImageAttachment,
|
||||
isPlayed,
|
||||
isVideo,
|
||||
} from '../../util/Attachment.std.js';
|
||||
import type { EmbeddedContactForUIType } from '../../types/EmbeddedContact.std.js';
|
||||
@@ -250,6 +249,7 @@ export type PropsData = {
|
||||
isSelectMode: boolean;
|
||||
isSMS: boolean;
|
||||
isSpoilerExpanded?: Record<number, boolean>;
|
||||
isVoiceMessagePlayed: boolean;
|
||||
canEndPoll?: boolean;
|
||||
direction: DirectionType;
|
||||
timestamp: number;
|
||||
@@ -1145,11 +1145,11 @@ export class Message extends React.PureComponent<Props, State> {
|
||||
i18n,
|
||||
id,
|
||||
isSticker,
|
||||
isVoiceMessagePlayed,
|
||||
kickOffAttachmentDownload,
|
||||
markAttachmentAsCorrupted,
|
||||
pushPanelForConversation,
|
||||
quote,
|
||||
readStatus,
|
||||
renderAudioAttachment,
|
||||
renderingContext,
|
||||
retryMessageSend,
|
||||
@@ -1282,8 +1282,6 @@ export class Message extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
if (isAttachmentAudio) {
|
||||
const played = isPlayed(direction, status, readStatus);
|
||||
|
||||
return renderAudioAttachment({
|
||||
i18n,
|
||||
buttonRef: this.audioButtonRef,
|
||||
@@ -1299,7 +1297,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||
expirationTimestamp,
|
||||
id,
|
||||
conversationId,
|
||||
played,
|
||||
played: isVoiceMessagePlayed,
|
||||
pushPanelForConversation,
|
||||
status,
|
||||
textPending: textAttachment?.pending,
|
||||
|
||||
@@ -36,6 +36,7 @@ const defaultMessage: MessageDataPropsType = {
|
||||
isSelectMode: false,
|
||||
isSMS: false,
|
||||
isSpoilerExpanded: {},
|
||||
isVoiceMessagePlayed: false,
|
||||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
status: 'sent',
|
||||
|
||||
@@ -108,6 +108,7 @@ const defaultMessageProps: TimelineMessagesProps = {
|
||||
isSelectMode: false,
|
||||
isSMS: false,
|
||||
isSpoilerExpanded: {},
|
||||
isVoiceMessagePlayed: false,
|
||||
toggleSelectMessage: action('toggleSelectMessage'),
|
||||
cancelAttachmentDownload: action('default--cancelAttachmentDownload'),
|
||||
kickOffAttachmentDownload: action('default--kickOffAttachmentDownload'),
|
||||
|
||||
@@ -71,6 +71,7 @@ function mockMessageTimelineItem(
|
||||
isSelectMode: false,
|
||||
isSMS: false,
|
||||
isSpoilerExpanded: {},
|
||||
isVoiceMessagePlayed: false,
|
||||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
canRetryDeleteForEveryone: true,
|
||||
|
||||
@@ -286,6 +286,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||
isTapToView: overrideProps.isTapToView,
|
||||
isTapToViewError: overrideProps.isTapToViewError,
|
||||
isTapToViewExpired: overrideProps.isTapToViewExpired,
|
||||
isVoiceMessagePlayed: false,
|
||||
cancelAttachmentDownload: action('cancelAttachmentDownload'),
|
||||
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||
markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'),
|
||||
|
||||
@@ -27,6 +27,7 @@ export function Multiple(): JSX.Element {
|
||||
i18n={i18n}
|
||||
key={index}
|
||||
mediaItem={mediaItem}
|
||||
isPlayed={Math.random() > 0.5}
|
||||
authorTitle="Alice"
|
||||
onClick={action('onClick')}
|
||||
onShowMessage={action('onShowMessage')}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
import React from 'react';
|
||||
import { noop } from 'lodash';
|
||||
import type { Transition } from 'framer-motion';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
import { tw } from '../../../axo/tw.dom.js';
|
||||
import { formatFileSize } from '../../../util/formatFileSize.std.js';
|
||||
@@ -17,6 +19,13 @@ const BAR_COUNT = 7;
|
||||
const MAX_PEAK_HEIGHT = 22;
|
||||
const MIN_PEAK_HEIGHT = 2;
|
||||
|
||||
const DOT_TRANSITION: Transition = {
|
||||
type: 'spring',
|
||||
mass: 0.5,
|
||||
stiffness: 350,
|
||||
damping: 20,
|
||||
};
|
||||
|
||||
export type DataProps = Readonly<{
|
||||
mediaItem: MediaItemType;
|
||||
onClick: (status: AttachmentStatusType['state']) => void;
|
||||
@@ -29,12 +38,14 @@ export type Props = DataProps &
|
||||
i18n: LocalizerType;
|
||||
theme?: ThemeType;
|
||||
authorTitle: string;
|
||||
isPlayed: boolean;
|
||||
}>;
|
||||
|
||||
export function AudioListItem({
|
||||
i18n,
|
||||
mediaItem,
|
||||
authorTitle,
|
||||
isPlayed,
|
||||
onClick,
|
||||
onShowMessage,
|
||||
}: Props): JSX.Element {
|
||||
@@ -95,13 +106,29 @@ export function AudioListItem({
|
||||
</div>
|
||||
);
|
||||
|
||||
const dot = (
|
||||
<motion.div
|
||||
className={tw('size-1.5 shrink-0 rounded bg-label-secondary')}
|
||||
initial={false}
|
||||
animate={{ scale: isPlayed ? 0 : 1 }}
|
||||
transition={DOT_TRANSITION}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
i18n={i18n}
|
||||
mediaItem={mediaItem}
|
||||
thumbnail={thumbnail}
|
||||
title={fileName == null ? authorTitle : `${fileName} · ${authorTitle}`}
|
||||
subtitle={subtitle.join(' · ')}
|
||||
subtitle={
|
||||
<div className={tw('flex items-center gap-1')}>
|
||||
<div className={tw('truncate overflow-hidden')}>
|
||||
{subtitle.join(' · ')}
|
||||
</div>
|
||||
{dot}
|
||||
</div>
|
||||
}
|
||||
readyLabel={i18n('icu:startDownload')}
|
||||
onClick={onClick}
|
||||
onShowMessage={onShowMessage}
|
||||
|
||||
@@ -27,6 +27,7 @@ export function Multiple(): JSX.Element {
|
||||
i18n={i18n}
|
||||
key={mediaItem.attachment.fileName}
|
||||
mediaItem={mediaItem}
|
||||
authorTitle="Alice"
|
||||
onClick={action('onClick')}
|
||||
onShowMessage={action('onShowMessage')}
|
||||
/>
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ListItem } from './ListItem.dom.js';
|
||||
export type Props = {
|
||||
i18n: LocalizerType;
|
||||
mediaItem: MediaItemType;
|
||||
authorTitle: string;
|
||||
onClick: (status: AttachmentStatusType['state']) => void;
|
||||
onShowMessage: () => void;
|
||||
};
|
||||
@@ -24,6 +25,7 @@ export type Props = {
|
||||
export function DocumentListItem({
|
||||
i18n,
|
||||
mediaItem,
|
||||
authorTitle,
|
||||
onClick,
|
||||
onShowMessage,
|
||||
}: Props): JSX.Element {
|
||||
@@ -50,12 +52,18 @@ export function DocumentListItem({
|
||||
</>
|
||||
);
|
||||
|
||||
const title = new Array<string>();
|
||||
if (fileName) {
|
||||
title.push(fileName);
|
||||
}
|
||||
title.push(authorTitle);
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
i18n={i18n}
|
||||
mediaItem={mediaItem}
|
||||
thumbnail={<FileThumbnail {...attachment} />}
|
||||
title={fileName}
|
||||
title={title.join(' · ')}
|
||||
subtitle={subtitle}
|
||||
readyLabel={i18n('icu:startDownload')}
|
||||
onClick={onClick}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { SpinnerV2 } from '../../SpinnerV2.dom.js';
|
||||
import { tw } from '../../../axo/tw.dom.js';
|
||||
import { AriaClickable } from '../../../axo/AriaClickable.dom.js';
|
||||
import { AxoSymbol } from '../../../axo/AxoSymbol.dom.js';
|
||||
import { UserText } from '../../UserText.dom.js';
|
||||
import {
|
||||
useAttachmentStatus,
|
||||
type AttachmentStatusType,
|
||||
@@ -21,7 +22,7 @@ export type Props = {
|
||||
i18n: LocalizerType;
|
||||
mediaItem: GenericMediaItemType;
|
||||
thumbnail: React.ReactNode;
|
||||
title: React.ReactNode;
|
||||
title: string;
|
||||
subtitle: React.ReactNode;
|
||||
readyLabel: string;
|
||||
onClick: (status: AttachmentStatusType['state']) => void;
|
||||
@@ -129,7 +130,9 @@ export function ListItem({
|
||||
>
|
||||
<div className={tw('shrink-0')}>{thumbnail}</div>
|
||||
<div className={tw('grow overflow-hidden text-start')}>
|
||||
<h3 className={tw('truncate')}>{title}</h3>
|
||||
<h3 className={tw('truncate')}>
|
||||
<UserText text={title} />
|
||||
</h3>
|
||||
<div className={tw('type-body-small leading-4 text-label-secondary')}>
|
||||
{subtitle}
|
||||
</div>
|
||||
|
||||
@@ -65,6 +65,10 @@ const createMediaItem = (
|
||||
// Unused for now
|
||||
source: undefined,
|
||||
sourceServiceId: undefined,
|
||||
readStatus: undefined,
|
||||
isErased: false,
|
||||
errors: undefined,
|
||||
sendStateByConversationId: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -88,6 +88,10 @@ function createRandomMessage(
|
||||
// Unused for now
|
||||
source: undefined,
|
||||
sourceServiceId: undefined,
|
||||
isErased: false,
|
||||
readStatus: undefined,
|
||||
sendStateByConversationId: undefined,
|
||||
errors: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { PropsType } from '../../../../state/smart/MediaItem.preload.js';
|
||||
import { getSafeDomain } from '../../../../types/LinkPreview.std.js';
|
||||
import type { AttachmentStatusType } from '../../../../hooks/useAttachmentStatus.std.js';
|
||||
import { missingCaseError } from '../../../../util/missingCaseError.std.js';
|
||||
import { isVoiceMessagePlayed } from '../../../../util/isVoiceMessagePlayed.std.js';
|
||||
import { LinkPreviewItem } from '../LinkPreviewItem.dom.js';
|
||||
import { MediaGridItem } from '../MediaGridItem.dom.js';
|
||||
import { DocumentListItem } from '../DocumentListItem.dom.js';
|
||||
@@ -31,6 +32,7 @@ export function MediaItem({ mediaItem, onItemClick }: PropsType): JSX.Element {
|
||||
<AudioListItem
|
||||
i18n={i18n}
|
||||
authorTitle="Alice"
|
||||
isPlayed={isVoiceMessagePlayed(mediaItem.message, undefined)}
|
||||
mediaItem={mediaItem}
|
||||
onClick={onClick}
|
||||
onShowMessage={onShowMessage}
|
||||
@@ -44,6 +46,7 @@ export function MediaItem({ mediaItem, onItemClick }: PropsType): JSX.Element {
|
||||
return (
|
||||
<DocumentListItem
|
||||
i18n={i18n}
|
||||
authorTitle="Alice"
|
||||
mediaItem={mediaItem}
|
||||
onClick={onClick}
|
||||
onShowMessage={onShowMessage}
|
||||
|
||||
Reference in New Issue
Block a user