Mark conversation as unread
Co-authored-by: Sidney Keese <sidney@carbonfive.com>
This commit is contained in:
parent
184f7e1bf3
commit
c408072576
20 changed files with 169 additions and 62 deletions
|
@ -210,11 +210,15 @@
|
|||
"description": "Shown at the top of the archived conversations list in the left pane"
|
||||
},
|
||||
"archiveConversation": {
|
||||
"message": "Archive Conversation",
|
||||
"message": "Archive",
|
||||
"description": "Shown in menu for conversation, and moves conversation out of main conversation list"
|
||||
},
|
||||
"markUnread": {
|
||||
"message": "Mark as unread",
|
||||
"description": "Shown in menu for conversation, and marks conversation as unread"
|
||||
},
|
||||
"moveConversationToInbox": {
|
||||
"message": "Move Conversation to Inbox",
|
||||
"message": "Unarchive",
|
||||
"description": "Undoes Archive Conversation action, and moves archived conversation back to the main conversation list"
|
||||
},
|
||||
"pinConversation": {
|
||||
|
@ -1039,7 +1043,7 @@
|
|||
"description": "Shown on the drop-down menu for an individual message, deletes single message for everyone"
|
||||
},
|
||||
"deleteMessages": {
|
||||
"message": "Delete messages",
|
||||
"message": "Delete",
|
||||
"description": "Menu item for deleting messages, title case."
|
||||
},
|
||||
"deleteConversationConfirmation": {
|
||||
|
@ -2361,6 +2365,10 @@
|
|||
"message": "Conversation returned to inbox",
|
||||
"description": "A toast that shows up when the user unarchives a conversation"
|
||||
},
|
||||
"conversationMarkedUnread": {
|
||||
"message": "Conversation marked unread",
|
||||
"description": "A toast that shows up when user marks a conversation as unread"
|
||||
},
|
||||
"StickerCreator--title": {
|
||||
"message": "Sticker pack creator",
|
||||
"description": "The title of the Sticker Pack Creator window"
|
||||
|
|
|
@ -205,6 +205,8 @@
|
|||
'private'
|
||||
);
|
||||
|
||||
conversation.setMarkedUnread(false);
|
||||
|
||||
const { openConversationExternal } = window.reduxActions.conversations;
|
||||
if (openConversationExternal) {
|
||||
openConversationExternal(id, messageId);
|
||||
|
|
|
@ -73,20 +73,23 @@ message ContactRecord {
|
|||
optional bool blocked = 9;
|
||||
optional bool whitelisted = 10;
|
||||
optional bool archived = 11;
|
||||
optional bool markedUnread = 12;
|
||||
}
|
||||
|
||||
message GroupV1Record {
|
||||
optional bytes id = 1;
|
||||
optional bool blocked = 2;
|
||||
optional bool whitelisted = 3;
|
||||
optional bool archived = 4;
|
||||
optional bytes id = 1;
|
||||
optional bool blocked = 2;
|
||||
optional bool whitelisted = 3;
|
||||
optional bool archived = 4;
|
||||
optional bool markedUnread = 5;
|
||||
}
|
||||
|
||||
message GroupV2Record {
|
||||
optional bytes masterKey = 1;
|
||||
optional bool blocked = 2;
|
||||
optional bool whitelisted = 3;
|
||||
optional bool archived = 4;
|
||||
optional bytes masterKey = 1;
|
||||
optional bool blocked = 2;
|
||||
optional bool whitelisted = 3;
|
||||
optional bool archived = 4;
|
||||
optional bool markedUnread = 5;
|
||||
}
|
||||
|
||||
message AccountRecord {
|
||||
|
@ -112,7 +115,7 @@ message AccountRecord {
|
|||
optional bool sealedSenderIndicators = 7;
|
||||
optional bool typingIndicators = 8;
|
||||
optional bool proxiedLinkPreviews = 9;
|
||||
optional bool noteToSelfUnread = 10;
|
||||
optional bool noteToSelfMarkedUnread = 10;
|
||||
optional bool linkPreviews = 11;
|
||||
repeated PinnedConversation pinnedConversations = 14;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
name: overrideProps.name || 'Some Person',
|
||||
type: overrideProps.type || 'direct',
|
||||
onClick: action('onClick'),
|
||||
markedUnread: boolean('markedUnread', overrideProps.markedUnread || false),
|
||||
lastMessage: overrideProps.lastMessage || {
|
||||
text: text('lastMessage.text', 'Hi there!'),
|
||||
status: select(
|
||||
|
@ -137,18 +138,32 @@ story.add('Message Request', () => {
|
|||
|
||||
story.add('Unread', () => {
|
||||
const counts = [4, 10, 250];
|
||||
const defaultProps = createProps({
|
||||
lastMessage: {
|
||||
text: 'Hey there!',
|
||||
status: 'delivered',
|
||||
},
|
||||
});
|
||||
|
||||
return counts.map(unreadCount => {
|
||||
const props = createProps({
|
||||
lastMessage: {
|
||||
text: 'Hey there!',
|
||||
status: 'delivered',
|
||||
},
|
||||
const items = counts.map(unreadCount => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
unreadCount,
|
||||
});
|
||||
};
|
||||
|
||||
return <ConversationListItem key={unreadCount} {...props} />;
|
||||
});
|
||||
|
||||
const markedUnreadProps = {
|
||||
...defaultProps,
|
||||
markedUnread: true,
|
||||
};
|
||||
|
||||
const markedUnreadItem = [
|
||||
<ConversationListItem key={5} {...markedUnreadProps} />,
|
||||
];
|
||||
|
||||
return [...items, ...markedUnreadItem];
|
||||
});
|
||||
|
||||
story.add('Selected', () => {
|
||||
|
|
|
@ -37,6 +37,7 @@ export type PropsData = {
|
|||
|
||||
lastUpdated: number;
|
||||
unreadCount?: number;
|
||||
markedUnread: boolean;
|
||||
isSelected: boolean;
|
||||
|
||||
acceptedMessageRequest?: boolean;
|
||||
|
@ -93,13 +94,19 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
isUnread(): boolean {
|
||||
const { markedUnread, unreadCount } = this.props;
|
||||
|
||||
return (isNumber(unreadCount) && unreadCount > 0) || markedUnread;
|
||||
}
|
||||
|
||||
public renderUnread(): JSX.Element | null {
|
||||
const { unreadCount } = this.props;
|
||||
|
||||
if (isNumber(unreadCount) && unreadCount > 0) {
|
||||
if (this.isUnread()) {
|
||||
return (
|
||||
<div className="module-conversation-list-item__unread-count">
|
||||
{unreadCount}
|
||||
{unreadCount || ''}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -109,7 +116,6 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
|
||||
public renderHeader(): JSX.Element {
|
||||
const {
|
||||
unreadCount,
|
||||
i18n,
|
||||
isMe,
|
||||
lastUpdated,
|
||||
|
@ -119,14 +125,12 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
title,
|
||||
} = this.props;
|
||||
|
||||
const withUnread = isNumber(unreadCount) && unreadCount > 0;
|
||||
|
||||
return (
|
||||
<div className="module-conversation-list-item__header">
|
||||
<div
|
||||
className={classNames(
|
||||
'module-conversation-list-item__header__name',
|
||||
withUnread
|
||||
this.isUnread()
|
||||
? 'module-conversation-list-item__header__name--with-unread'
|
||||
: null
|
||||
)}
|
||||
|
@ -146,7 +150,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
<div
|
||||
className={classNames(
|
||||
'module-conversation-list-item__header__date',
|
||||
withUnread
|
||||
this.isUnread()
|
||||
? 'module-conversation-list-item__header__date--has-unread'
|
||||
: null
|
||||
)}
|
||||
|
@ -155,7 +159,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
timestamp={lastUpdated}
|
||||
extended={false}
|
||||
module="module-conversation-list-item__header__timestamp"
|
||||
withUnread={withUnread}
|
||||
withUnread={this.isUnread()}
|
||||
i18n={i18n}
|
||||
/>
|
||||
</div>
|
||||
|
@ -172,14 +176,12 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
muteExpiresAt,
|
||||
shouldShowDraft,
|
||||
typingContact,
|
||||
unreadCount,
|
||||
} = this.props;
|
||||
if (!lastMessage && !typingContact) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const messageBody = lastMessage ? lastMessage.text : '';
|
||||
const withUnread = isNumber(unreadCount) && unreadCount > 0;
|
||||
const showingDraft = shouldShowDraft && draftPreview;
|
||||
const deletedForEveryone = Boolean(
|
||||
lastMessage && lastMessage.deletedForEveryone
|
||||
|
@ -192,7 +194,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
dir="auto"
|
||||
className={classNames(
|
||||
'module-conversation-list-item__message__text',
|
||||
withUnread
|
||||
this.isUnread()
|
||||
? 'module-conversation-list-item__message__text--has-unread'
|
||||
: null
|
||||
)}
|
||||
|
@ -249,8 +251,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
/* eslint-enable no-nested-ternary */
|
||||
|
||||
public render(): JSX.Element {
|
||||
const { unreadCount, onClick, id, isSelected, style } = this.props;
|
||||
const withUnread = isNumber(unreadCount) && unreadCount > 0;
|
||||
const { id, isSelected, onClick, style } = this.props;
|
||||
|
||||
return (
|
||||
<button
|
||||
|
@ -263,7 +264,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
|
|||
style={style}
|
||||
className={classNames(
|
||||
'module-conversation-list-item',
|
||||
withUnread ? 'module-conversation-list-item--has-unread' : null,
|
||||
this.isUnread() ? 'module-conversation-list-item--has-unread' : null,
|
||||
isSelected ? 'module-conversation-list-item--is-selected' : null
|
||||
)}
|
||||
data-id={cleanId(id)}
|
||||
|
|
|
@ -18,6 +18,7 @@ const defaultConversations: Array<PropsData> = [
|
|||
id: 'fred-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Fred Willard',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -25,6 +26,7 @@ const defaultConversations: Array<PropsData> = [
|
|||
id: 'marc-convo',
|
||||
isSelected: true,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Marc Barraca',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -35,6 +37,7 @@ const defaultArchivedConversations: Array<PropsData> = [
|
|||
id: 'michelle-archive-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Michelle Mercure',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -46,6 +49,7 @@ const pinnedConversations: Array<PropsData> = [
|
|||
isPinned: true,
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Philip Glass',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -54,6 +58,7 @@ const pinnedConversations: Array<PropsData> = [
|
|||
isPinned: true,
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Robert Moog',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -129,6 +134,7 @@ story.add('Search Results', () => {
|
|||
id: 'fred-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'People Named Fred',
|
||||
type: 'group',
|
||||
},
|
||||
|
@ -147,6 +153,7 @@ story.add('Search Results', () => {
|
|||
id: 'fred-contact',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Fred Willard',
|
||||
type: 'direct',
|
||||
},
|
||||
|
|
|
@ -157,6 +157,7 @@ const conversations = [
|
|||
text: 'The rabbit hopped silently in the night.',
|
||||
status: SENT,
|
||||
},
|
||||
markedUnread: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -177,6 +178,7 @@ const conversations = [
|
|||
text: "What's going on?",
|
||||
status: SENT,
|
||||
},
|
||||
markedUnread: false,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -196,6 +198,7 @@ const contacts = [
|
|||
lastUpdated: Date.now() - 10 * 60 * 1000,
|
||||
unreadCount: 0,
|
||||
isSelected: false,
|
||||
markedUnread: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -211,6 +214,7 @@ const contacts = [
|
|||
lastUpdated: Date.now() - 11 * 60 * 1000,
|
||||
unreadCount: 0,
|
||||
isSelected: false,
|
||||
markedUnread: false,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
@ -44,6 +44,7 @@ const actionProps: PropsActionsType = {
|
|||
onGoBack: action('onGoBack'),
|
||||
|
||||
onArchive: action('onArchive'),
|
||||
onMarkUnread: action('onMarkUnread'),
|
||||
onMoveToInbox: action('onMoveToInbox'),
|
||||
onSetPin: action('onSetPin'),
|
||||
};
|
||||
|
|
|
@ -36,6 +36,7 @@ export interface PropsDataType {
|
|||
isMe?: boolean;
|
||||
isArchived?: boolean;
|
||||
isPinned?: boolean;
|
||||
markedUnread?: boolean;
|
||||
|
||||
disableTimerChanges?: boolean;
|
||||
expirationSettingName?: string;
|
||||
|
@ -60,6 +61,7 @@ export interface PropsActionsType {
|
|||
onGoBack: () => void;
|
||||
|
||||
onArchive: () => void;
|
||||
onMarkUnread: () => void;
|
||||
onMoveToInbox: () => void;
|
||||
}
|
||||
|
||||
|
@ -310,6 +312,7 @@ export class ConversationHeader extends React.Component<PropsType> {
|
|||
isPinned,
|
||||
type,
|
||||
isArchived,
|
||||
markedUnread,
|
||||
muteExpirationLabel,
|
||||
onDeleteMessages,
|
||||
onResetSession,
|
||||
|
@ -319,6 +322,7 @@ export class ConversationHeader extends React.Component<PropsType> {
|
|||
onShowGroupMembers,
|
||||
onShowSafetyNumber,
|
||||
onArchive,
|
||||
onMarkUnread,
|
||||
onSetPin,
|
||||
onMoveToInbox,
|
||||
timerOptions,
|
||||
|
@ -350,28 +354,6 @@ export class ConversationHeader extends React.Component<PropsType> {
|
|||
|
||||
return (
|
||||
<ContextMenu id={triggerId}>
|
||||
<SubMenu title={muteTitle}>
|
||||
{muteOptions.map(item => (
|
||||
<MenuItem
|
||||
key={item.name}
|
||||
disabled={item.disabled}
|
||||
onClick={() => {
|
||||
onSetMuteNotifications(item.value);
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</SubMenu>
|
||||
{isPinned ? (
|
||||
<MenuItem onClick={() => onSetPin(false)}>
|
||||
{i18n('unpinConversation')}
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem onClick={() => onSetPin(true)}>
|
||||
{i18n('pinConversation')}
|
||||
</MenuItem>
|
||||
)}
|
||||
{disableTimerChanges ? null : (
|
||||
<SubMenu title={disappearingTitle}>
|
||||
{(timerOptions || []).map(item => (
|
||||
|
@ -386,21 +368,37 @@ export class ConversationHeader extends React.Component<PropsType> {
|
|||
))}
|
||||
</SubMenu>
|
||||
)}
|
||||
<MenuItem onClick={onShowAllMedia}>{i18n('viewRecentMedia')}</MenuItem>
|
||||
<SubMenu title={muteTitle}>
|
||||
{muteOptions.map(item => (
|
||||
<MenuItem
|
||||
key={item.name}
|
||||
disabled={item.disabled}
|
||||
onClick={() => {
|
||||
onSetMuteNotifications(item.value);
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</SubMenu>
|
||||
{isGroup ? (
|
||||
<MenuItem onClick={onShowGroupMembers}>
|
||||
{i18n('showMembers')}
|
||||
</MenuItem>
|
||||
) : null}
|
||||
<MenuItem onClick={onShowAllMedia}>{i18n('viewRecentMedia')}</MenuItem>
|
||||
{!isGroup && !isMe ? (
|
||||
<MenuItem onClick={onShowSafetyNumber}>
|
||||
{i18n('showSafetyNumber')}
|
||||
</MenuItem>
|
||||
) : null}
|
||||
<MenuItem divider />
|
||||
{!isGroup && acceptedMessageRequest ? (
|
||||
<MenuItem onClick={onResetSession}>{i18n('resetSession')}</MenuItem>
|
||||
) : null}
|
||||
<MenuItem divider />
|
||||
{!markedUnread ? (
|
||||
<MenuItem onClick={onMarkUnread}>{i18n('markUnread')}</MenuItem>
|
||||
) : null}
|
||||
{isArchived ? (
|
||||
<MenuItem onClick={onMoveToInbox}>
|
||||
{i18n('moveConversationToInbox')}
|
||||
|
@ -409,6 +407,15 @@ export class ConversationHeader extends React.Component<PropsType> {
|
|||
<MenuItem onClick={onArchive}>{i18n('archiveConversation')}</MenuItem>
|
||||
)}
|
||||
<MenuItem onClick={onDeleteMessages}>{i18n('deleteMessages')}</MenuItem>
|
||||
{isPinned ? (
|
||||
<MenuItem onClick={() => onSetPin(false)}>
|
||||
{i18n('unpinConversation')}
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem onClick={() => onSetPin(true)}>
|
||||
{i18n('pinConversation')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</ContextMenu>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ storiesOf('Components/Conversation/ProfileChangeNotification', module)
|
|||
title: 'Mr. Fire 🔥',
|
||||
name: 'Mr. Fire 🔥',
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
}}
|
||||
change={{
|
||||
type: 'name',
|
||||
|
@ -37,6 +38,7 @@ storiesOf('Components/Conversation/ProfileChangeNotification', module)
|
|||
type: 'direct',
|
||||
title: 'Mr. Fire 🔥',
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
}}
|
||||
change={{
|
||||
type: 'name',
|
||||
|
|
1
ts/model-types.d.ts
vendored
1
ts/model-types.d.ts
vendored
|
@ -149,6 +149,7 @@ export type ConversationAttributesType = {
|
|||
isPinned: boolean;
|
||||
lastMessageDeletedForEveryone: boolean;
|
||||
lastMessageStatus: LastMessageStatus | null;
|
||||
markedUnread: boolean;
|
||||
messageCount: number;
|
||||
messageCountBeforeMessageRequests: number;
|
||||
messageRequestResponseType: number;
|
||||
|
|
|
@ -1137,7 +1137,7 @@ export class ConversationModel extends window.Backbone.Model<
|
|||
deletedForEveryone: this.get('lastMessageDeletedForEveryone')!,
|
||||
},
|
||||
lastUpdated: this.get('timestamp')!,
|
||||
|
||||
markedUnread: this.get('markedUnread')!,
|
||||
membersCount: this.isPrivate()
|
||||
? undefined
|
||||
: (this.get('membersV2')! || this.get('members')! || []).length,
|
||||
|
@ -3019,6 +3019,12 @@ export class ConversationModel extends window.Backbone.Model<
|
|||
}
|
||||
}
|
||||
|
||||
setMarkedUnread(markedUnread: boolean): void {
|
||||
this.set({ markedUnread });
|
||||
window.Signal.Data.updateConversation(this.attributes);
|
||||
this.captureChange('markedUnread');
|
||||
}
|
||||
|
||||
async updateExpirationTimer(
|
||||
providedExpireTimer: number | undefined,
|
||||
providedSource: unknown,
|
||||
|
@ -3998,6 +4004,7 @@ export class ConversationModel extends window.Backbone.Model<
|
|||
// [X] blocked
|
||||
// [X] whitelisted
|
||||
// [X] archived
|
||||
// [X] markedUnread
|
||||
captureChange(property: string): void {
|
||||
if (!window.Signal.RemoteConfig.isEnabled('desktop.storageWrite')) {
|
||||
window.log.info(
|
||||
|
|
|
@ -113,6 +113,7 @@ export async function toContactRecord(
|
|||
contactRecord.blocked = conversation.isBlocked();
|
||||
contactRecord.whitelisted = Boolean(conversation.get('profileSharing'));
|
||||
contactRecord.archived = Boolean(conversation.get('isArchived'));
|
||||
contactRecord.markedUnread = Boolean(conversation.get('markedUnread'));
|
||||
|
||||
applyUnknownFields(contactRecord, conversation);
|
||||
|
||||
|
@ -137,6 +138,9 @@ export async function toAccountRecord(
|
|||
}
|
||||
accountRecord.avatarUrl = window.storage.get('avatarUrl') || '';
|
||||
accountRecord.noteToSelfArchived = Boolean(conversation.get('isArchived'));
|
||||
accountRecord.noteToSelfMarkedUnread = Boolean(
|
||||
conversation.get('markedUnread')
|
||||
);
|
||||
accountRecord.readReceipts = Boolean(
|
||||
window.storage.get('read-receipt-setting')
|
||||
);
|
||||
|
@ -218,6 +222,7 @@ export async function toGroupV1Record(
|
|||
groupV1Record.blocked = conversation.isBlocked();
|
||||
groupV1Record.whitelisted = Boolean(conversation.get('profileSharing'));
|
||||
groupV1Record.archived = Boolean(conversation.get('isArchived'));
|
||||
groupV1Record.markedUnread = Boolean(conversation.get('markedUnread'));
|
||||
|
||||
applyUnknownFields(groupV1Record, conversation);
|
||||
|
||||
|
@ -236,6 +241,7 @@ export async function toGroupV2Record(
|
|||
groupV2Record.blocked = conversation.isBlocked();
|
||||
groupV2Record.whitelisted = Boolean(conversation.get('profileSharing'));
|
||||
groupV2Record.archived = Boolean(conversation.get('isArchived'));
|
||||
groupV2Record.markedUnread = Boolean(conversation.get('markedUnread'));
|
||||
|
||||
applyUnknownFields(groupV2Record, conversation);
|
||||
|
||||
|
@ -386,6 +392,7 @@ export async function mergeGroupV1Record(
|
|||
|
||||
conversation.set({
|
||||
isArchived: Boolean(groupV1Record.archived),
|
||||
markedUnread: Boolean(groupV1Record.markedUnread),
|
||||
storageID,
|
||||
});
|
||||
|
||||
|
@ -446,6 +453,7 @@ export async function mergeGroupV2Record(
|
|||
|
||||
conversation.set({
|
||||
isArchived: Boolean(groupV2Record.archived),
|
||||
markedUnread: Boolean(groupV2Record.markedUnread),
|
||||
storageID,
|
||||
});
|
||||
|
||||
|
@ -537,6 +545,7 @@ export async function mergeContactRecord(
|
|||
|
||||
conversation.set({
|
||||
isArchived: Boolean(contactRecord.archived),
|
||||
markedUnread: Boolean(contactRecord.markedUnread),
|
||||
storageID,
|
||||
});
|
||||
|
||||
|
@ -559,6 +568,7 @@ export async function mergeAccountRecord(
|
|||
avatarUrl,
|
||||
linkPreviews,
|
||||
noteToSelfArchived,
|
||||
noteToSelfMarkedUnread,
|
||||
pinnedConversations: remotelyPinnedConversationClasses,
|
||||
profileKey,
|
||||
readReceipts,
|
||||
|
@ -734,6 +744,7 @@ export async function mergeAccountRecord(
|
|||
|
||||
conversation.set({
|
||||
isArchived: Boolean(noteToSelfArchived),
|
||||
markedUnread: Boolean(noteToSelfMarkedUnread),
|
||||
storageID,
|
||||
});
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ export type ConversationType = {
|
|||
text: string;
|
||||
deletedForEveryone?: boolean;
|
||||
};
|
||||
markedUnread: boolean;
|
||||
phoneNumber?: string;
|
||||
membersCount?: number;
|
||||
muteExpiresAt?: number;
|
||||
|
|
|
@ -36,6 +36,7 @@ describe('LeftPane', () => {
|
|||
isPinned: true,
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Philip Glass',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -44,6 +45,7 @@ describe('LeftPane', () => {
|
|||
isPinned: true,
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Robert Moog',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -70,6 +72,7 @@ describe('LeftPane', () => {
|
|||
id: 'fred-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Fred Willard',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -78,6 +81,7 @@ describe('LeftPane', () => {
|
|||
isPinned: false,
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Robert Moog',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -104,6 +108,7 @@ describe('LeftPane', () => {
|
|||
id: 'fred-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Fred Willard',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -114,6 +119,7 @@ describe('LeftPane', () => {
|
|||
isPinned: true,
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Philip Glass',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -148,6 +154,7 @@ describe('LeftPane', () => {
|
|||
id: 'jerry-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Jerry Jordan',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -157,6 +164,7 @@ describe('LeftPane', () => {
|
|||
id: 'fred-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Fred Willard',
|
||||
type: 'direct',
|
||||
},
|
||||
|
@ -179,6 +187,7 @@ describe('LeftPane', () => {
|
|||
id: 'fred-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Fred Willard',
|
||||
type: 'direct',
|
||||
},
|
||||
|
|
|
@ -41,6 +41,7 @@ describe('state/selectors/conversations', () => {
|
|||
inboxPosition: 0,
|
||||
phoneNumber: 'notused',
|
||||
isArchived: false,
|
||||
markedUnread: false,
|
||||
|
||||
type: 'direct',
|
||||
isMe: false,
|
||||
|
@ -65,6 +66,7 @@ describe('state/selectors/conversations', () => {
|
|||
inboxPosition: 21,
|
||||
phoneNumber: 'notused',
|
||||
isArchived: false,
|
||||
markedUnread: false,
|
||||
|
||||
type: 'direct',
|
||||
isMe: false,
|
||||
|
@ -89,6 +91,7 @@ describe('state/selectors/conversations', () => {
|
|||
inboxPosition: 22,
|
||||
phoneNumber: 'notused',
|
||||
isArchived: false,
|
||||
markedUnread: false,
|
||||
|
||||
type: 'direct',
|
||||
isMe: false,
|
||||
|
@ -113,6 +116,7 @@ describe('state/selectors/conversations', () => {
|
|||
inboxPosition: 20,
|
||||
phoneNumber: 'notused',
|
||||
isArchived: false,
|
||||
markedUnread: false,
|
||||
|
||||
type: 'direct',
|
||||
isMe: false,
|
||||
|
@ -137,6 +141,7 @@ describe('state/selectors/conversations', () => {
|
|||
inboxPosition: 30,
|
||||
phoneNumber: 'notused',
|
||||
isArchived: false,
|
||||
markedUnread: false,
|
||||
|
||||
type: 'direct',
|
||||
isMe: false,
|
||||
|
@ -185,6 +190,7 @@ describe('state/selectors/conversations', () => {
|
|||
phoneNumber: 'notused',
|
||||
isArchived: false,
|
||||
isPinned: true,
|
||||
markedUnread: false,
|
||||
|
||||
type: 'direct',
|
||||
isMe: false,
|
||||
|
@ -210,6 +216,7 @@ describe('state/selectors/conversations', () => {
|
|||
phoneNumber: 'notused',
|
||||
isArchived: false,
|
||||
isPinned: true,
|
||||
markedUnread: false,
|
||||
|
||||
type: 'direct',
|
||||
isMe: false,
|
||||
|
@ -235,6 +242,7 @@ describe('state/selectors/conversations', () => {
|
|||
phoneNumber: 'notused',
|
||||
isArchived: false,
|
||||
isPinned: true,
|
||||
markedUnread: false,
|
||||
|
||||
type: 'direct',
|
||||
isMe: false,
|
||||
|
|
4
ts/textsecure.d.ts
vendored
4
ts/textsecure.d.ts
vendored
|
@ -929,6 +929,7 @@ export declare class ContactRecordClass {
|
|||
blocked?: boolean | null;
|
||||
whitelisted?: boolean | null;
|
||||
archived?: boolean | null;
|
||||
markedUnread?: boolean;
|
||||
|
||||
__unknownFields?: ArrayBuffer;
|
||||
}
|
||||
|
@ -944,6 +945,7 @@ export declare class GroupV1RecordClass {
|
|||
blocked?: boolean | null;
|
||||
whitelisted?: boolean | null;
|
||||
archived?: boolean | null;
|
||||
markedUnread?: boolean;
|
||||
|
||||
__unknownFields?: ArrayBuffer;
|
||||
}
|
||||
|
@ -959,6 +961,7 @@ export declare class GroupV2RecordClass {
|
|||
blocked?: boolean | null;
|
||||
whitelisted?: boolean | null;
|
||||
archived?: boolean | null;
|
||||
markedUnread?: boolean;
|
||||
|
||||
__unknownFields?: ArrayBuffer;
|
||||
}
|
||||
|
@ -995,6 +998,7 @@ export declare class AccountRecordClass {
|
|||
typingIndicators?: boolean | null;
|
||||
linkPreviews?: boolean | null;
|
||||
pinnedConversations?: PinnedConversationClass[];
|
||||
noteToSelfMarkedUnread?: boolean;
|
||||
|
||||
__unknownFields?: ArrayBuffer;
|
||||
}
|
||||
|
|
|
@ -631,7 +631,7 @@
|
|||
"rule": "jQuery-$(",
|
||||
"path": "js/views/inbox_view.js",
|
||||
"line": " if (e && this.$(e.target).closest('.capture-audio').length > 0) {",
|
||||
"lineNumber": 217,
|
||||
"lineNumber": 219,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-29T18:29:18.234Z",
|
||||
"reasonDetail": "Known DOM elements"
|
||||
|
@ -640,7 +640,7 @@
|
|||
"rule": "jQuery-$(",
|
||||
"path": "js/views/inbox_view.js",
|
||||
"line": " this.$('.conversation:first .recorder').trigger('close');",
|
||||
"lineNumber": 220,
|
||||
"lineNumber": 222,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-29T18:29:18.234Z",
|
||||
"reasonDetail": "Hardcoded selector"
|
||||
|
@ -14703,7 +14703,7 @@
|
|||
"rule": "React-createRef",
|
||||
"path": "ts/components/conversation/ConversationHeader.tsx",
|
||||
"line": " this.menuTriggerRef = React.createRef();",
|
||||
"lineNumber": 84,
|
||||
"lineNumber": 86,
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2020-05-20T20:10:43.540Z",
|
||||
"reasonDetail": "Used to reference popup menu"
|
||||
|
@ -15116,4 +15116,4 @@
|
|||
"reasonCategory": "falseMatch",
|
||||
"updated": "2020-09-08T23:07:22.682Z"
|
||||
}
|
||||
]
|
||||
]
|
|
@ -115,6 +115,12 @@ Whisper.ConversationUnarchivedToast = Whisper.ToastView.extend({
|
|||
},
|
||||
});
|
||||
|
||||
Whisper.ConversationMarkedUnreadToast = Whisper.ToastView.extend({
|
||||
render_attributes() {
|
||||
return { toastMessage: window.i18n('conversationMarkedUnread') };
|
||||
},
|
||||
});
|
||||
|
||||
Whisper.TapToViewExpiredIncomingToast = Whisper.ToastView.extend({
|
||||
render_attributes() {
|
||||
return {
|
||||
|
@ -544,6 +550,14 @@ Whisper.ConversationView = Whisper.View.extend({
|
|||
document.body
|
||||
);
|
||||
},
|
||||
onMarkUnread: () => {
|
||||
this.model.setMarkedUnread(true);
|
||||
|
||||
Whisper.ToastView.show(
|
||||
Whisper.ConversationMarkedUnreadToast,
|
||||
document.body
|
||||
);
|
||||
},
|
||||
onMoveToInbox: () => {
|
||||
this.model.setArchived(false);
|
||||
|
||||
|
@ -3057,6 +3071,7 @@ Whisper.ConversationView = Whisper.View.extend({
|
|||
);
|
||||
|
||||
this.compositionApi.current.reset();
|
||||
this.model.setMarkedUnread(false);
|
||||
this.setQuoteMessage(null);
|
||||
this.resetLinkPreview();
|
||||
this.clearAttachments();
|
||||
|
|
1
ts/window.d.ts
vendored
1
ts/window.d.ts
vendored
|
@ -545,6 +545,7 @@ export type WhisperType = {
|
|||
};
|
||||
ConversationArchivedToast: WhatIsThis;
|
||||
ConversationUnarchivedToast: WhatIsThis;
|
||||
ConversationMarkedUnreadToast: WhatIsThis;
|
||||
AppView: WhatIsThis;
|
||||
WallClockListener: WhatIsThis;
|
||||
MessageRequests: WhatIsThis;
|
||||
|
|
Loading…
Reference in a new issue