Merge pull request #2158 from Bilb/click-at-opens-mention

click on @ in conversation item bring to latest mention
This commit is contained in:
Audric Ackermann 2022-02-07 11:50:09 +11:00 committed by GitHub
commit 995bc8dbfa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 123 additions and 30 deletions

View file

@ -74,6 +74,7 @@ module.exports = {
getLastMessagesByConversation,
getOldestMessageInConversation,
getFirstUnreadMessageIdInConversation,
getFirstUnreadMessageWithMention,
hasConversationOutgoingMessage,
trimMessages,
fillWithTestData,
@ -2279,7 +2280,7 @@ const orderByClause = 'ORDER BY COALESCE(serverTimestamp, sent_at, received_at)
const orderByClauseASC = 'ORDER BY COALESCE(serverTimestamp, sent_at, received_at) ASC';
function getMessagesByConversation(conversationId, { messageId = null } = {}) {
const absLimit = 20;
const absLimit = 30;
// If messageId is given it means we are opening the conversation to that specific messageId,
// or that we just scrolled to it by a quote click and needs to load around it.
// If messageId is null, it means we are just opening the convo to the last unread message, or at the bottom
@ -2420,6 +2421,35 @@ function getFirstUnreadMessageIdInConversation(conversationId) {
return rows[0].id;
}
function getFirstUnreadMessageWithMention(conversationId, ourpubkey) {
if (!ourpubkey || !ourpubkey.length) {
throw new Error('getFirstUnreadMessageWithMention needs our pubkey but nothing was given');
}
const likeMatch = `%@${ourpubkey}%`;
const rows = globalInstance
.prepare(
`
SELECT id, json FROM ${MESSAGES_TABLE} WHERE
conversationId = $conversationId AND
unread = $unread AND
body LIKE $likeMatch
ORDER BY serverTimestamp ASC, serverId ASC, sent_at ASC, received_at ASC
LIMIT 1;
`
)
.all({
conversationId,
unread: 1,
likeMatch,
});
if (rows.length === 0) {
return undefined;
}
return rows[0].id;
}
/**
* Deletes all but the 10,000 last received messages.
*/

View file

@ -74,28 +74,3 @@
.module-conversation-list-item--mentioned-us {
border-left: 4px solid $session-color-green !important;
}
.at-symbol {
background-color: var(--color-accent);
color: $color-black;
text-align: center;
margin-top: 0px;
margin-bottom: 0px;
padding-top: 1px;
padding-inline-start: 3px;
padding-inline-end: 3px;
position: static;
margin-inline-start: 5px;
font-weight: 300;
font-size: 11px;
letter-spacing: 0.25px;
height: 16px;
min-width: 16px;
border-radius: 8px;
box-shadow: 0px 0px 0px 1px $color-dark-85;
}

View file

@ -1,8 +1,14 @@
import classNames from 'classnames';
import React, { useContext } from 'react';
import React, { useCallback, useContext } from 'react';
import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { getFirstUnreadMessageWithMention } from '../../../data/data';
import { useConversationPropsById, useIsPinned } from '../../../hooks/useParamSelector';
import { UserUtils } from '../../../session/utils';
import {
openConversationToSpecificMessage,
openConversationWithMessages,
} from '../../../state/ducks/conversations';
import { SectionType } from '../../../state/ducks/section';
import { isSearching } from '../../../state/selectors/search';
import { getFocusedSection } from '../../../state/selectors/section';
@ -74,12 +80,72 @@ const ListItemIcons = () => {
);
};
const MentionAtSymbol = styled.span`
background-color: var(--color-accent);
color: black;
text-align: center;
margin-top: 0px;
margin-bottom: 0px;
padding-top: 1px;
padding-inline-start: 3px;
padding-inline-end: 3px;
position: static;
margin-inline-start: 5px;
font-weight: 300;
font-size: 11px;
letter-spacing: 0.25px;
height: 16px;
min-width: 16px;
border-radius: 8px;
/* transition: filter 0.25s linear; */
cursor: pointer;
:hover {
filter: grayscale(0.7);
}
`;
export const ConversationListItemHeaderItem = () => {
const conversationId = useContext(ContextConversationId);
const isSearchingMode = useSelector(isSearching);
const convoProps = useHeaderItemProps(conversationId);
const openConvoToLastMention = useCallback(
async (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
e.preventDefault();
// mousedown is invoked sooner than onClick, but for both right and left click
if (e.button === 0) {
const oldestMessageUnreadWithMention =
(await getFirstUnreadMessageWithMention(
conversationId,
UserUtils.getOurPubKeyStrFromCache()
)) || null;
if (oldestMessageUnreadWithMention) {
await openConversationToSpecificMessage({
conversationKey: conversationId,
messageIdToNavigateTo: oldestMessageUnreadWithMention,
shouldHighlightMessage: true,
});
} else {
window.log.info('cannot open to latest mention as no unread mention are found');
await openConversationWithMessages({
conversationKey: conversationId,
messageId: null,
});
}
}
},
[conversationId]
);
if (!convoProps) {
return null;
}
@ -88,7 +154,11 @@ export const ConversationListItemHeaderItem = () => {
let atSymbol = null;
let unreadCountDiv = null;
if (unreadCount > 0) {
atSymbol = mentionedUs ? <p className="at-symbol">@</p> : null;
atSymbol = mentionedUs ? (
<MentionAtSymbol title="Open to latest mention" onMouseDown={openConvoToLastMention}>
@
</MentionAtSymbol>
) : null;
unreadCountDiv = <p className="module-conversation-list-item__unread-count">{unreadCount}</p>;
}

View file

@ -130,6 +130,7 @@ const channelsToMake = {
getLastMessagesByConversation,
getOldestMessageInConversation,
getFirstUnreadMessageIdInConversation,
getFirstUnreadMessageWithMention,
hasConversationOutgoingMessage,
getSeenMessagesByHashList,
getLastHashBySnode,
@ -838,6 +839,13 @@ export async function getFirstUnreadMessageIdInConversation(
return channels.getFirstUnreadMessageIdInConversation(conversationId);
}
export async function getFirstUnreadMessageWithMention(
conversationId: string,
ourPubkey: string
): Promise<string | undefined> {
return channels.getFirstUnreadMessageWithMention(conversationId, ourPubkey);
}
export async function hasConversationOutgoingMessage(conversationId: string): Promise<boolean> {
return channels.hasConversationOutgoingMessage(conversationId);
}

View file

@ -19,7 +19,7 @@ import {
} from './messageType';
import autoBind from 'auto-bind';
import { saveMessage } from '../../ts/data/data';
import { getFirstUnreadMessageWithMention, saveMessage } from '../../ts/data/data';
import { ConversationModel, ConversationTypeEnum } from './conversation';
import {
FindAndFormatContactType,
@ -1030,7 +1030,17 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
if (convo) {
const beforeUnread = convo.get('unreadCount');
const unreadCount = await convo.getUnreadCount();
if (beforeUnread !== unreadCount) {
const nextMentionedUs = await getFirstUnreadMessageWithMention(
convo.id,
UserUtils.getOurPubKeyStrFromCache()
);
let mentionedUsChange = false;
if (convo.get('mentionedUs') && !nextMentionedUs) {
convo.set('mentionedUs', false);
mentionedUsChange = true;
}
if (beforeUnread !== unreadCount || mentionedUsChange) {
convo.set({ unreadCount });
await convo.commit();
}