Show "unplayed" dot on incoming audio messages
This commit is contained in:
parent
9fd191ae00
commit
b0750e5f4e
36 changed files with 812 additions and 175 deletions
|
@ -20,6 +20,7 @@ import {
|
|||
VIDEO_MP4,
|
||||
stringToMIMEType,
|
||||
} from '../../types/MIME';
|
||||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||
import { MessageAudio } from './MessageAudio';
|
||||
import { computePeaks } from '../GlobalAudioContext';
|
||||
import { setup as setupI18n } from '../../../js/modules/i18n';
|
||||
|
@ -61,6 +62,7 @@ const MessageAudioContainer: React.FC<AudioAttachmentProps> = props => {
|
|||
audio={audio}
|
||||
computePeaks={computePeaks}
|
||||
setActiveAudioID={(id, context) => setActive({ id, context })}
|
||||
onFirstPlayed={action('onFirstPlayed')}
|
||||
activeAudioID={active.id}
|
||||
activeAudioContext={active.context}
|
||||
/>
|
||||
|
@ -120,12 +122,17 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
isTapToViewExpired: overrideProps.isTapToViewExpired,
|
||||
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||
markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'),
|
||||
markViewed: action('markViewed'),
|
||||
onHeightChange: action('onHeightChange'),
|
||||
openConversation: action('openConversation'),
|
||||
openLink: action('openLink'),
|
||||
previews: overrideProps.previews || [],
|
||||
reactions: overrideProps.reactions,
|
||||
reactToMessage: action('reactToMessage'),
|
||||
readStatus:
|
||||
overrideProps.readStatus === undefined
|
||||
? ReadStatus.Read
|
||||
: overrideProps.readStatus,
|
||||
renderEmojiPicker,
|
||||
renderAudioAttachment,
|
||||
replyToMessage: action('replyToMessage'),
|
||||
|
@ -866,33 +873,48 @@ story.add('Pending GIF', () => {
|
|||
});
|
||||
|
||||
story.add('Audio', () => {
|
||||
const props = createProps({
|
||||
attachments: [
|
||||
{
|
||||
contentType: AUDIO_MP3,
|
||||
fileName: 'incompetech-com-Agnus-Dei-X.mp3',
|
||||
url: '/fixtures/incompetech-com-Agnus-Dei-X.mp3',
|
||||
},
|
||||
],
|
||||
status: 'sent',
|
||||
});
|
||||
const Wrapper = () => {
|
||||
const [isPlayed, setIsPlayed] = React.useState(false);
|
||||
|
||||
return renderBothDirections(props);
|
||||
});
|
||||
const messageProps = createProps({
|
||||
attachments: [
|
||||
{
|
||||
contentType: AUDIO_MP3,
|
||||
fileName: 'incompetech-com-Agnus-Dei-X.mp3',
|
||||
url: '/fixtures/incompetech-com-Agnus-Dei-X.mp3',
|
||||
},
|
||||
],
|
||||
...(isPlayed
|
||||
? {
|
||||
status: 'viewed',
|
||||
readStatus: ReadStatus.Viewed,
|
||||
}
|
||||
: {
|
||||
status: 'read',
|
||||
readStatus: ReadStatus.Read,
|
||||
}),
|
||||
});
|
||||
|
||||
story.add('Audio (played)', () => {
|
||||
const props = createProps({
|
||||
attachments: [
|
||||
{
|
||||
contentType: AUDIO_MP3,
|
||||
fileName: 'incompetech-com-Agnus-Dei-X.mp3',
|
||||
url: '/fixtures/incompetech-com-Agnus-Dei-X.mp3',
|
||||
},
|
||||
],
|
||||
status: 'viewed',
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setIsPlayed(old => !old);
|
||||
}}
|
||||
style={{
|
||||
display: 'block',
|
||||
marginBottom: '2em',
|
||||
}}
|
||||
>
|
||||
Toggle played
|
||||
</button>
|
||||
{renderBothDirections(messageProps)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return renderBothDirections(props);
|
||||
return <Wrapper />;
|
||||
});
|
||||
|
||||
story.add('Long Audio', () => {
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
ConversationTypeType,
|
||||
InteractionModeType,
|
||||
} from '../../state/ducks/conversations';
|
||||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||
import { Avatar } from '../Avatar';
|
||||
import { Spinner } from '../Spinner';
|
||||
import { MessageBody } from './MessageBody';
|
||||
|
@ -110,6 +111,7 @@ export type AudioAttachmentProps = {
|
|||
|
||||
kickOffAttachmentDownload(): void;
|
||||
onCorrupted(): void;
|
||||
onFirstPlayed(): void;
|
||||
};
|
||||
|
||||
export type PropsData = {
|
||||
|
@ -167,6 +169,8 @@ export type PropsData = {
|
|||
isTapToViewExpired?: boolean;
|
||||
isTapToViewError?: boolean;
|
||||
|
||||
readStatus: ReadStatus;
|
||||
|
||||
expirationLength?: number;
|
||||
expirationTimestamp?: number;
|
||||
|
||||
|
@ -225,6 +229,7 @@ export type PropsActions = {
|
|||
attachment: AttachmentType;
|
||||
messageId: string;
|
||||
}) => void;
|
||||
markViewed(messageId: string): void;
|
||||
showVisualAttachment: (options: {
|
||||
attachment: AttachmentType;
|
||||
messageId: string;
|
||||
|
@ -684,7 +689,9 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
isSticker,
|
||||
kickOffAttachmentDownload,
|
||||
markAttachmentAsCorrupted,
|
||||
markViewed,
|
||||
quote,
|
||||
readStatus,
|
||||
reducedMotion,
|
||||
renderAudioAttachment,
|
||||
renderingContext,
|
||||
|
@ -791,8 +798,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
played = status === 'viewed';
|
||||
break;
|
||||
case 'incoming':
|
||||
// Not implemented yet. See DESKTOP-1855.
|
||||
played = true;
|
||||
played = readStatus === ReadStatus.Viewed;
|
||||
break;
|
||||
default:
|
||||
window.log.error(missingCaseError(direction));
|
||||
|
@ -831,6 +837,9 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
messageId: id,
|
||||
});
|
||||
},
|
||||
onFirstPlayed() {
|
||||
markViewed(id);
|
||||
},
|
||||
});
|
||||
}
|
||||
const { pending, fileName, fileSize, contentType } = firstAttachment;
|
||||
|
|
|
@ -37,6 +37,7 @@ export type Props = {
|
|||
buttonRef: React.RefObject<HTMLButtonElement>;
|
||||
kickOffAttachmentDownload(): void;
|
||||
onCorrupted(): void;
|
||||
onFirstPlayed(): void;
|
||||
|
||||
computePeaks(url: string, barCount: number): Promise<ComputePeaksResult>;
|
||||
activeAudioID: string | undefined;
|
||||
|
@ -163,6 +164,7 @@ export const MessageAudio: React.FC<Props> = (props: Props) => {
|
|||
buttonRef,
|
||||
kickOffAttachmentDownload,
|
||||
onCorrupted,
|
||||
onFirstPlayed,
|
||||
|
||||
audio,
|
||||
computePeaks,
|
||||
|
@ -365,6 +367,12 @@ export const MessageAudio: React.FC<Props> = (props: Props) => {
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!played && isPlaying) {
|
||||
onFirstPlayed();
|
||||
}
|
||||
}, [played, isPlaying, onFirstPlayed]);
|
||||
|
||||
// Clicking waveform moves playback head position and starts playback.
|
||||
const onWaveformClick = (event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
|
|
|
@ -10,6 +10,7 @@ import { storiesOf } from '@storybook/react';
|
|||
import { PropsData as MessageDataPropsType } from './Message';
|
||||
import { MessageDetail, Props } from './MessageDetail';
|
||||
import { SendStatus } from '../../messages/MessageSendState';
|
||||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
|
||||
import { setup as setupI18n } from '../../../js/modules/i18n';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
|
@ -35,6 +36,7 @@ const defaultMessage: MessageDataPropsType = {
|
|||
isBlocked: false,
|
||||
isMessageRequestAccepted: true,
|
||||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
status: 'sent',
|
||||
text: 'A message from Max',
|
||||
timestamp: Date.now(),
|
||||
|
@ -71,6 +73,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
doubleCheckMissingQuoteReference: action('doubleCheckMissingQuoteReference'),
|
||||
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||
markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'),
|
||||
markViewed: action('markViewed'),
|
||||
openConversation: action('openConversation'),
|
||||
openLink: action('openLink'),
|
||||
reactToMessage: action('reactToMessage'),
|
||||
|
|
|
@ -69,6 +69,7 @@ export type Props = {
|
|||
| 'interactionMode'
|
||||
| 'kickOffAttachmentDownload'
|
||||
| 'markAttachmentAsCorrupted'
|
||||
| 'markViewed'
|
||||
| 'openConversation'
|
||||
| 'openLink'
|
||||
| 'reactToMessage'
|
||||
|
@ -269,6 +270,7 @@ export class MessageDetail extends React.Component<Props> {
|
|||
interactionMode,
|
||||
kickOffAttachmentDownload,
|
||||
markAttachmentAsCorrupted,
|
||||
markViewed,
|
||||
openConversation,
|
||||
openLink,
|
||||
reactToMessage,
|
||||
|
@ -305,6 +307,7 @@ export class MessageDetail extends React.Component<Props> {
|
|||
interactionMode={interactionMode}
|
||||
kickOffAttachmentDownload={kickOffAttachmentDownload}
|
||||
markAttachmentAsCorrupted={markAttachmentAsCorrupted}
|
||||
markViewed={markViewed}
|
||||
onHeightChange={noop}
|
||||
openConversation={openConversation}
|
||||
openLink={openLink}
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
stringToMIMEType,
|
||||
} from '../../types/MIME';
|
||||
import { Props, Quote } from './Quote';
|
||||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||
import { setup as setupI18n } from '../../../js/modules/i18n';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
|
||||
|
@ -56,11 +57,13 @@ const defaultMessageProps: MessagesProps = {
|
|||
isMessageRequestAccepted: true,
|
||||
kickOffAttachmentDownload: action('default--kickOffAttachmentDownload'),
|
||||
markAttachmentAsCorrupted: action('default--markAttachmentAsCorrupted'),
|
||||
markViewed: action('default--markViewed'),
|
||||
onHeightChange: action('onHeightChange'),
|
||||
openConversation: action('default--openConversation'),
|
||||
openLink: action('default--openLink'),
|
||||
previews: [],
|
||||
reactToMessage: action('default--reactToMessage'),
|
||||
readStatus: ReadStatus.Read,
|
||||
renderEmojiPicker: () => <div />,
|
||||
renderAudioAttachment: () => <div>*AudioAttachment*</div>,
|
||||
replyToMessage: action('default--replyToMessage'),
|
||||
|
|
|
@ -20,6 +20,7 @@ import { LastSeenIndicator } from './LastSeenIndicator';
|
|||
import { TimelineLoadingRow } from './TimelineLoadingRow';
|
||||
import { TypingBubble } from './TypingBubble';
|
||||
import { ContactSpoofingType } from '../../util/contactSpoofing';
|
||||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
|
@ -51,6 +52,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
isBlocked: false,
|
||||
isMessageRequestAccepted: true,
|
||||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
text: '🔥',
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
|
@ -70,6 +72,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
isBlocked: false,
|
||||
isMessageRequestAccepted: true,
|
||||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
text: 'Hello there from the new world! http://somewhere.com',
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
|
@ -102,6 +105,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
isBlocked: false,
|
||||
isMessageRequestAccepted: true,
|
||||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
text: 'Hello there from the new world!',
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
|
@ -200,6 +204,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
isBlocked: false,
|
||||
isMessageRequestAccepted: true,
|
||||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
status: 'sent',
|
||||
text: '🔥',
|
||||
timestamp: Date.now(),
|
||||
|
@ -220,6 +225,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
isBlocked: false,
|
||||
isMessageRequestAccepted: true,
|
||||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
status: 'read',
|
||||
text: 'Hello there from the new world! http://somewhere.com',
|
||||
timestamp: Date.now(),
|
||||
|
@ -240,6 +246,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
isBlocked: false,
|
||||
isMessageRequestAccepted: true,
|
||||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
status: 'sent',
|
||||
text: 'Hello there from the new world! 🔥',
|
||||
timestamp: Date.now(),
|
||||
|
@ -260,6 +267,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
isBlocked: false,
|
||||
isMessageRequestAccepted: true,
|
||||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
status: 'sent',
|
||||
text:
|
||||
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
|
||||
|
@ -281,6 +289,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
isBlocked: false,
|
||||
isMessageRequestAccepted: true,
|
||||
previews: [],
|
||||
readStatus: ReadStatus.Read,
|
||||
status: 'read',
|
||||
text:
|
||||
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
|
||||
|
@ -325,6 +334,7 @@ const actions = () => ({
|
|||
showContactModal: action('showContactModal'),
|
||||
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||
markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'),
|
||||
markViewed: action('markViewed'),
|
||||
showVisualAttachment: action('showVisualAttachment'),
|
||||
downloadAttachment: action('downloadAttachment'),
|
||||
displayTapToViewMessage: action('displayTapToViewMessage'),
|
||||
|
|
|
@ -1496,6 +1496,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
|
|||
'loadNewerMessages',
|
||||
'loadNewestMessages',
|
||||
'markMessageRead',
|
||||
'markViewed',
|
||||
'onBlock',
|
||||
'onBlockAndReportSpam',
|
||||
'onDelete',
|
||||
|
|
|
@ -57,6 +57,7 @@ const getDefaultProps = () => ({
|
|||
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||
learnMoreAboutDeliveryIssue: action('learnMoreAboutDeliveryIssue'),
|
||||
markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'),
|
||||
markViewed: action('markViewed'),
|
||||
showMessageDetail: action('showMessageDetail'),
|
||||
openConversation: action('openConversation'),
|
||||
showContactDetail: action('showContactDetail'),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue