Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link pgup down on message list #1977

Merged
merged 5 commits into from
Oct 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 26 additions & 12 deletions ts/components/ConversationListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ import {
import _ from 'underscore';
import { useMembersAvatars } from '../hooks/useMembersAvatar';
import { SessionIcon } from './session/icon';
import { useSelector } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { SectionType } from '../state/ducks/section';
import { getFocusedSection } from '../state/selectors/section';
import { ConversationNotificationSettingType } from '../models/conversation';
import { updateUserDetailsModal } from '../state/ducks/modalDialog';

// tslint:disable-next-line: no-empty-interface
export interface ConversationListItemProps extends ReduxConversationType {}
Expand Down Expand Up @@ -131,16 +132,15 @@ const HeaderItem = (props: {
</StyledConversationListItemIconWrapper>
{unreadCountDiv}
{atSymbol}
{
<div
className={classNames(
'module-conversation-list-item__header__date',
unreadCount > 0 ? 'module-conversation-list-item__header__date--has-unread' : null
)}
>
{<Timestamp timestamp={activeAt} extended={false} isConversationListItem={true} />}
</div>
}

<div
className={classNames(
'module-conversation-list-item__header__date',
unreadCount > 0 ? 'module-conversation-list-item__header__date--has-unread' : null
)}
>
<Timestamp timestamp={activeAt} extended={false} isConversationListItem={true} />
</div>
</div>
);
};
Expand Down Expand Up @@ -220,10 +220,12 @@ const AvatarItem = (props: {
memberAvatars?: Array<ConversationAvatar>;
name?: string;
profileName?: string;
isPrivate: boolean;
}) => {
const { avatarPath, name, conversationId, profileName, memberAvatars } = props;
const { avatarPath, name, isPrivate, conversationId, profileName, memberAvatars } = props;

const userName = name || profileName || conversationId;
const dispatch = useDispatch();

return (
<div className="module-conversation-list-item__avatar-container">
Expand All @@ -233,6 +235,17 @@ const AvatarItem = (props: {
size={AvatarSize.S}
memberAvatars={memberAvatars}
pubkey={conversationId}
onAvatarClick={() => {
if (isPrivate) {
dispatch(
updateUserDetailsModal({
conversationId: conversationId,
userName,
authorAvatarPath: avatarPath,
})
);
}
}}
/>
</div>
);
Expand Down Expand Up @@ -309,6 +322,7 @@ const ConversationListItem = (props: Props) => {
memberAvatars={membersAvatar}
profileName={profileName}
name={name}
isPrivate={isPrivate || false}
/>
<div className="module-conversation-list-item__content">
<HeaderItem
Expand Down
12 changes: 12 additions & 0 deletions ts/components/session/conversation/SessionMessagesList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import { useSelector } from 'react-redux';
// tslint:disable-next-line: no-submodule-imports
import useKey from 'react-use/lib/useKey';
import { PropsForDataExtractionNotification, QuoteClickOptions } from '../../../models/messageType';
import {
PropsForExpirationTimer,
Expand All @@ -19,9 +21,19 @@ import { SessionLastSeenIndicator } from './SessionLastSeenIndicator';

export const SessionMessagesList = (props: {
scrollToQuoteMessage: (options: QuoteClickOptions) => Promise<void>;
onPageUpPressed: () => void;
onPageDownPressed: () => void;
}) => {
const messagesProps = useSelector(getSortedMessagesTypesOfSelectedConversation);

useKey('PageUp', () => {
props.onPageUpPressed();
});

useKey('PageDown', () => {
props.onPageDownPressed();
});

return (
<>
{messagesProps.map(messageProps => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
import { SessionMessagesList } from './SessionMessagesList';

export type SessionMessageListProps = {
messageContainerRef: React.RefObject<any>;
messageContainerRef: React.RefObject<HTMLDivElement>;
};

type Props = SessionMessageListProps & {
Expand Down Expand Up @@ -82,7 +82,7 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
const newFirstMesssageId = this.props.messagesProps[0]?.propsForMessage.id;
const messageAddedWasMoreRecentOne = prevFirstMesssageId !== newFirstMesssageId;

if (isSameConvo && snapShot?.realScrollTop && prevMsgLength !== newMsgLength) {
if (isSameConvo && snapShot?.realScrollTop && prevMsgLength !== newMsgLength && currentRef) {
if (messageAddedWasMoreRecentOne) {
if (snapShot.scrollHeight - snapShot.realScrollTop < 50) {
// consider that we were scrolled to bottom
Expand All @@ -105,13 +105,13 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
public getSnapshotBeforeUpdate() {
const messageContainer = this.props.messageContainerRef.current;

const scrollTop = messageContainer.scrollTop;
const scrollHeight = messageContainer.scrollHeight;
const scrollTop = messageContainer?.scrollTop || undefined;
const scrollHeight = messageContainer?.scrollHeight || undefined;

// as we use column-reverse for displaying message list
// the top is < 0
// tslint:disable-next-line: restrict-plus-operands
const realScrollTop = scrollHeight + scrollTop;
const realScrollTop = scrollHeight && scrollTop ? scrollHeight + scrollTop : undefined;
return {
realScrollTop,
fakeScrollTop: scrollTop,
Expand Down Expand Up @@ -147,7 +147,11 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
key="typing-bubble"
/>

<SessionMessagesList scrollToQuoteMessage={this.scrollToQuoteMessage} />
<SessionMessagesList
scrollToQuoteMessage={this.scrollToQuoteMessage}
onPageDownPressed={this.scrollPgDown}
onPageUpPressed={this.scrollPgUp}
/>

<SessionScrollButton onClick={this.scrollToBottom} key="scroll-down-button" />
</div>
Expand Down Expand Up @@ -239,6 +243,30 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
messageContainer.scrollTop = messageContainer.scrollHeight - messageContainer.clientHeight;
}

private scrollPgUp() {
const messageContainer = this.props.messageContainerRef.current;
if (!messageContainer) {
return;
}
messageContainer.scrollBy({
top: Math.floor(-messageContainer.clientHeight * 2) / 3,
behavior: 'smooth',
});
}

private scrollPgDown() {
const messageContainer = this.props.messageContainerRef.current;
if (!messageContainer) {
return;
}

// tslint:disable-next-line: restrict-plus-operands
messageContainer.scrollBy({
top: Math.floor(+messageContainer.clientHeight * 2) / 3,
behavior: 'smooth',
});
}

private async scrollToQuoteMessage(options: QuoteClickOptions) {
const { quoteAuthor, quoteId, referencedMessageNotFound } = options;

Expand Down
2 changes: 1 addition & 1 deletion ts/components/session/menu/ConversationHeaderMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const ConversationHeaderMenu = (props: PropsConversationHeaderMenu) => {
{getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, conversationId)}
{/* TODO: add delete group */}
{getInviteContactMenuItem(isGroup, isPublic, conversationId)}
{getDeleteContactMenuItem(isMe, isGroup, isPublic, left, isKickedFromGroup, conversationId)}
{getDeleteContactMenuItem(isGroup, isPublic, left, isKickedFromGroup, conversationId)}
</Menu>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const ConversationListItemContextMenu = (props: PropsContextConversationItem) =>
{getClearNicknameMenuItem(isMe, hasNickname, isGroup, conversationId)}
{getDeleteMessagesMenuItem(isPublic, conversationId)}
{getInviteContactMenuItem(isGroup, isPublic, conversationId)}
{getDeleteContactMenuItem(isMe, isGroup, isPublic, left, isKickedFromGroup, conversationId)}
{getDeleteContactMenuItem(isGroup, isPublic, left, isKickedFromGroup, conversationId)}
{getLeaveGroupMenuItem(isKickedFromGroup, left, isGroup, isPublic, conversationId)}
</Menu>
);
Expand Down
5 changes: 1 addition & 4 deletions ts/components/session/menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,13 @@ function showCopyId(isPublic: boolean, isGroup: boolean): boolean {
}

function showDeleteContact(
isMe: boolean,
isGroup: boolean,
isPublic: boolean,
isGroupLeft: boolean,
isKickedFromGroup: boolean
): boolean {
// you need to have left a closed group first to be able to delete it completely.
return (!isMe && !isGroup) || (isGroup && (isGroupLeft || isKickedFromGroup || isPublic));
return !isGroup || (isGroup && (isGroupLeft || isKickedFromGroup || isPublic));
}

function showAddModerators(
Expand Down Expand Up @@ -170,7 +169,6 @@ export const getPinConversationMenuItem = (conversationId: string): JSX.Element
};

export function getDeleteContactMenuItem(
isMe: boolean | undefined,
isGroup: boolean | undefined,
isPublic: boolean | undefined,
isLeft: boolean | undefined,
Expand All @@ -181,7 +179,6 @@ export function getDeleteContactMenuItem(

if (
showDeleteContact(
Boolean(isMe),
Boolean(isGroup),
Boolean(isPublic),
Boolean(isLeft),
Expand Down
18 changes: 17 additions & 1 deletion ts/session/onions/onionPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,23 @@ async function buildNewOnionPathsWorker() {
if (allNodes.length <= SnodePool.minSnodePoolCount) {
throw new Error('Too few nodes to build an onion path. Even after fetching from seed.');
}
const otherNodes = _.shuffle(_.differenceBy(allNodes, guardNodes, 'pubkey_ed25519'));

// make sure to not reuse multiple times the same subnet /24
const allNodesGroupedBySubnet24 = _.groupBy(allNodes, e => {
const lastDot = e.ip.lastIndexOf('.');
return e.ip.substr(0, lastDot);
});
const oneNodeForEachSubnet24 = _.map(allNodesGroupedBySubnet24, group => {
return _.sample(group) as Data.Snode;
});
if (oneNodeForEachSubnet24.length <= SnodePool.minSnodePoolCount) {
throw new Error(
'Too few nodes "unique by ip" to build an onion path. Even after fetching from seed.'
);
}
const otherNodes = _.shuffle(
_.differenceBy(oneNodeForEachSubnet24, guardNodes, 'pubkey_ed25519')
);
const guards = _.shuffle(guardNodes);

// Create path for every guard node:
Expand Down
23 changes: 3 additions & 20 deletions ts/test/session/unit/onion/OnionErrors_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import AbortController from 'abort-controller';
import * as Data from '../../../../../ts/data/data';
import { pathFailureCount } from '../../../../session/onions/onionPath';
import { SeedNodeAPI } from '../../../../session/seed_node_api';
import { generateFakeSnodeWithEdKey } from '../../../test-utils/utils';

chai.use(chaiAsPromised as any);
chai.should();
Expand Down Expand Up @@ -63,35 +64,17 @@ describe('OnionPathsErrors', () => {
fakeSwarmForAssociatedWith: Array<string>;

let oldOnionPaths: Array<Array<Data.Snode>>;
const fakeIP = '8.8.8.8';
let fakePortCurrent = 20000;

beforeEach(async () => {
guardPubkeys = TestUtils.generateFakePubKeys(3).map(n => n.key);
otherNodesPubkeys = TestUtils.generateFakePubKeys(20).map(n => n.key);

SNodeAPI.Onions.resetSnodeFailureCount();

guardNodesArray = guardPubkeys.map(ed25519 => {
fakePortCurrent++;
return {
ip: fakeIP,
port: fakePortCurrent,
pubkey_ed25519: ed25519,
pubkey_x25519: ed25519,
};
});
guardNodesArray = guardPubkeys.map(generateFakeSnodeWithEdKey);
guardSnode1 = guardNodesArray[0];

otherNodesArray = otherNodesPubkeys.map(ed25519 => {
fakePortCurrent++;
return {
ip: fakeIP,
port: fakePortCurrent,
pubkey_ed25519: ed25519,
pubkey_x25519: ed25519,
};
});
otherNodesArray = otherNodesPubkeys.map(generateFakeSnodeWithEdKey);

fakeSnodePool = [...guardNodesArray, ...otherNodesArray];

Expand Down
5 changes: 3 additions & 2 deletions ts/test/test-utils/utils/pubkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ export function generateFakePubKeys(amount: number): Array<PubKey> {

export function generateFakeSnode(): Snode {
return {
ip: '136.243.103.171',
// tslint:disable: insecure-random
ip: `136.243.${Math.random() * 255}.${Math.random() * 255}`,
port: 22116,
pubkey_x25519: generateFakePubKeyStr(),
pubkey_ed25519: generateFakePubKeyStr(),
Expand All @@ -46,7 +47,7 @@ export function generateFakeSnode(): Snode {

export function generateFakeSnodeWithEdKey(ed25519Pubkey: string): Snode {
return {
ip: '136.243.103.171',
ip: `136.243.${Math.random() * 255}.${Math.random() * 255}`,
port: 22116,
pubkey_x25519: generateFakePubKeyStr(),
pubkey_ed25519: ed25519Pubkey,
Expand Down