Skip to content

Commit

Permalink
Show "unplayed" dot on outgoing audio messages
Browse files Browse the repository at this point in the history
  • Loading branch information
EvanHahn-Signal committed Jul 27, 2021
1 parent b73c029 commit 14929fb
Show file tree
Hide file tree
Showing 16 changed files with 211 additions and 52 deletions.
3 changes: 2 additions & 1 deletion stylesheets/_modules.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1172,7 +1172,8 @@
@include color-svg('../images/double-check.svg', $color-white-alpha-80);
}
}
.module-message__metadata__status-icon--read {
.module-message__metadata__status-icon--read,
.module-message__metadata__status-icon--viewed {
width: 18px;

@include light-theme {
Expand Down
45 changes: 41 additions & 4 deletions stylesheets/components/MessageAudio.scss
Original file line number Diff line number Diff line change
Expand Up @@ -188,22 +188,59 @@ $audio-attachment-button-margin-small: 4px;
}

.module-message__audio-attachment__countdown {
flex-shrink: 1;
$unplayed-dot-margin: 6px;

@include font-caption;
align-items: center;
display: flex;
flex-shrink: 1;
user-select: none;

@include font-caption;
&:after {
content: '';
display: block;
width: 6px;
height: 6px;
border-radius: 100%;
transition: background 100ms ease-out;
}

&--played:after {
background: transparent;
}

.module-message__audio-attachment--incoming & {
flex-direction: row-reverse;

&:after {
margin-right: $unplayed-dot-margin;
}

@include light-theme {
color: $color-black-alpha-60;
$color: $color-black-alpha-60;
color: $color;
&--unplayed:after {
background: $color;
}
}
@include dark-theme {
color: $color-white-alpha-80;
$color: $color-white-alpha-80;
color: $color;
&--unplayed:after {
background: $color;
}
}
}

.module-message__audio-attachment--outgoing & {
color: $color-white-alpha-80;

&:after {
margin-left: $unplayed-dot-margin;
}

&--unplayed:after {
background: $color-white-alpha-80;
}
}
}
39 changes: 34 additions & 5 deletions ts/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
MessageEvent,
MessageEventData,
ReadEvent,
ViewEvent,
ConfigurationEvent,
ViewOnceOpenSyncEvent,
MessageRequestResponseEvent,
Expand Down Expand Up @@ -2186,6 +2187,10 @@ export async function startApp(): Promise<void> {
'read',
queuedEventListener(onReadReceipt)
);
messageReceiver.addEventListener(
'view',
queuedEventListener(onViewReceipt)
);
messageReceiver.addEventListener(
'verified',
queuedEventListener(onVerified)
Expand Down Expand Up @@ -3711,14 +3716,38 @@ export async function startApp(): Promise<void> {
MessageRequests.getSingleton().onResponse(sync);
}

function onReadReceipt(ev: ReadEvent) {
function onReadReceipt(event: Readonly<ReadEvent>) {
onReadOrViewReceipt({
logTitle: 'read receipt',
event,
type: MessageReceiptType.Read,
});
}

function onViewReceipt(event: Readonly<ViewEvent>): void {
onReadOrViewReceipt({
logTitle: 'view receipt',
event,
type: MessageReceiptType.View,
});
}

function onReadOrViewReceipt({
event,
logTitle,
type,
}: Readonly<{
event: ReadEvent | ViewEvent;
logTitle: string;
type: MessageReceiptType.Read | MessageReceiptType.View;
}>): void {
const {
envelopeTimestamp,
timestamp,
source,
sourceUuid,
sourceDevice,
} = ev.read;
} = event.receipt;
const sourceConversationId = window.ConversationController.ensureContactIds(
{
e164: source,
Expand All @@ -3727,7 +3756,7 @@ export async function startApp(): Promise<void> {
}
);
window.log.info(
'read receipt',
logTitle,
source,
sourceUuid,
sourceDevice,
Expand All @@ -3737,7 +3766,7 @@ export async function startApp(): Promise<void> {
timestamp
);

ev.confirm();
event.confirm();

if (!window.storage.get('read-receipt-setting') || !sourceConversationId) {
return;
Expand All @@ -3748,7 +3777,7 @@ export async function startApp(): Promise<void> {
receiptTimestamp: envelopeTimestamp,
sourceConversationId,
sourceDevice,
type: MessageReceiptType.Read,
type,
});

// Note: We do not wait for completion here
Expand Down
15 changes: 15 additions & 0 deletions ts/components/conversation/Message.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,21 @@ story.add('Audio', () => {
return renderBothDirections(props);
});

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 renderBothDirections(props);
});

story.add('Long Audio', () => {
const props = createProps({
attachments: [
Expand Down
19 changes: 19 additions & 0 deletions ts/components/conversation/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import { ContactType } from '../../types/Contact';

import { getIncrement } from '../../util/timer';
import { isFileDangerous } from '../../util/isFileDangerous';
import { missingCaseError } from '../../util/missingCaseError';
import { BodyRangesType, LocalizerType, ThemeType } from '../../types/Util';
import {
ContactNameColorType,
Expand Down Expand Up @@ -80,6 +81,7 @@ export const MessageStatuses = [
'read',
'sending',
'sent',
'viewed',
] as const;
export type MessageStatusType = typeof MessageStatuses[number];

Expand All @@ -99,6 +101,7 @@ export type AudioAttachmentProps = {
expirationLength?: number;
expirationTimestamp?: number;
id: string;
played: boolean;
showMessageDetail: (id: string) => void;
status?: MessageStatusType;
textPending?: boolean;
Expand Down Expand Up @@ -764,6 +767,21 @@ export class Message extends React.Component<Props, State> {
}
}
if (isAudio(attachments)) {
let played: boolean;
switch (direction) {
case 'outgoing':
played = status === 'viewed';
break;
case 'incoming':
// Not implemented yet. See DESKTOP-1855.
played = true;
break;
default:
window.log.error(missingCaseError(direction));
played = false;
break;
}

return renderAudioAttachment({
i18n,
buttonRef: this.audioButtonRef,
Expand All @@ -777,6 +795,7 @@ export class Message extends React.Component<Props, State> {
expirationLength,
expirationTimestamp,
id,
played,
showMessageDetail,
status,
textPending,
Expand Down
11 changes: 10 additions & 1 deletion ts/components/conversation/MessageAudio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type Props = {
expirationLength?: number;
expirationTimestamp?: number;
id: string;
played: boolean;
showMessageDetail: (id: string) => void;
status?: MessageStatusType;
textPending?: boolean;
Expand Down Expand Up @@ -153,6 +154,7 @@ export const MessageAudio: React.FC<Props> = (props: Props) => {
expirationLength,
expirationTimestamp,
id,
played,
showMessageDetail,
status,
textPending,
Expand Down Expand Up @@ -531,7 +533,14 @@ export const MessageAudio: React.FC<Props> = (props: Props) => {
timestamp={timestamp}
/>
)}
<div className={`${CSS_BASE}__countdown`}>{timeToText(countDown)}</div>
<div
className={classNames(
`${CSS_BASE}__countdown`,
`${CSS_BASE}__countdown--${played ? 'played' : 'unplayed'}`
)}
>
{timeToText(countDown)}
</div>
</div>
);

Expand Down
4 changes: 4 additions & 0 deletions ts/messageModifiers/MessageReceipts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const { deleteSentProtoRecipient } = dataInterface;
export enum MessageReceiptType {
Delivery = 'Delivery',
Read = 'Read',
View = 'View',
}

type MessageReceiptAttributesType = {
Expand Down Expand Up @@ -151,6 +152,9 @@ export class MessageReceipts extends Collection<MessageReceiptModel> {
case MessageReceiptType.Read:
sendActionType = SendActionType.GotReadReceipt;
break;
case MessageReceiptType.View:
sendActionType = SendActionType.GotViewedReceipt;
break;
default:
throw missingCaseError(type);
}
Expand Down
2 changes: 2 additions & 0 deletions ts/messages/MessageSendState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ const STATUS_NUMBERS: Record<SendStatus, number> = {
export const maxStatus = (a: SendStatus, b: SendStatus): SendStatus =>
STATUS_NUMBERS[a] > STATUS_NUMBERS[b] ? a : b;

export const isViewed = (status: SendStatus): boolean =>
status === SendStatus.Viewed;
export const isRead = (status: SendStatus): boolean =>
STATUS_NUMBERS[status] >= STATUS_NUMBERS[SendStatus.Read];
export const isDelivered = (status: SendStatus): boolean =>
Expand Down
3 changes: 2 additions & 1 deletion ts/model-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ export type LastMessageStatus =
| 'sending'
| 'sent'
| 'delivered'
| 'read';
| 'read'
| 'viewed';

type TaskResultType = any;

Expand Down
3 changes: 3 additions & 0 deletions ts/models/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3206,6 +3206,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
case MessageReceiptType.Read:
sendActionType = SendActionType.GotReadReceipt;
break;
case MessageReceiptType.View:
sendActionType = SendActionType.GotViewedReceipt;
break;
default:
throw missingCaseError(receiptType);
}
Expand Down
6 changes: 5 additions & 1 deletion ts/state/selectors/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
isMessageJustForMe,
isRead,
isSent,
isViewed,
maxStatus,
someSendStatus,
} from '../../messages/MessageSendState';
Expand Down Expand Up @@ -914,7 +915,7 @@ export function getMessagePropStatus(
if (hasErrors(message)) {
return sent ? 'partial-sent' : 'error';
}
return sent ? 'read' : 'sending';
return sent ? 'viewed' : 'sending';
}

const sendStates = Object.values(
Expand All @@ -928,6 +929,9 @@ export function getMessagePropStatus(
if (hasErrors(message)) {
return isSent(highestSuccessfulStatus) ? 'partial-sent' : 'error';
}
if (isViewed(highestSuccessfulStatus)) {
return 'viewed';
}
if (isRead(highestSuccessfulStatus)) {
return 'read';
}
Expand Down
1 change: 1 addition & 0 deletions ts/state/smart/MessageAudio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type Props = {
expirationLength?: number;
expirationTimestamp?: number;
id: string;
played: boolean;
showMessageDetail: (id: string) => void;
status?: MessageStatusType;
textPending?: boolean;
Expand Down
15 changes: 15 additions & 0 deletions ts/test-both/messages/MessageSendState_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
isMessageJustForMe,
isRead,
isSent,
isViewed,
maxStatus,
sendStateReducer,
someSendStatus,
Expand Down Expand Up @@ -49,6 +50,20 @@ describe('message send state utilities', () => {
});
});

describe('isViewed', () => {
it('returns true for viewed statuses', () => {
assert.isTrue(isViewed(SendStatus.Viewed));
});

it('returns false for non-viewed statuses', () => {
assert.isFalse(isViewed(SendStatus.Read));
assert.isFalse(isViewed(SendStatus.Delivered));
assert.isFalse(isViewed(SendStatus.Sent));
assert.isFalse(isViewed(SendStatus.Pending));
assert.isFalse(isViewed(SendStatus.Failed));
});
});

describe('isRead', () => {
it('returns true for read and viewed statuses', () => {
assert.isTrue(isRead(SendStatus.Read));
Expand Down

0 comments on commit 14929fb

Please sign in to comment.