Scroll Down Button: Fix flickering mention & reaction buttons (#2344)

This commit is contained in:
Alexander Zinchuk 2023-01-28 02:15:42 +01:00
parent 4f68d23f73
commit e6866aaeaf
10 changed files with 174 additions and 43 deletions

View File

@ -10,6 +10,19 @@
z-index: var(--z-scroll-down-button);
pointer-events: none;
.hidden {
opacity: 0;
pointer-events: none;
}
.reactions {
transition: 0.2s ease opacity, 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) transform;
}
.transform-down {
transform: translateY(4rem);
}
.unread {
pointer-events: none;
}

View File

@ -93,24 +93,27 @@ const FloatingActionButtons: FC<OwnProps & StateProps> = ({
return (
<div ref={elementRef} className={fabClassName}>
{hasUnreadReactions && (
<ScrollDownButton
icon="heart-outline"
ariaLabelLang="AccDescrReactionMentionDown"
onClick={focusNextReaction}
onReadAll={readAllReactions}
unreadCount={reactionsCount}
/>
)}
{hasUnreadMentions && (
<ScrollDownButton
icon="mention"
ariaLabelLang="AccDescrMentionDown"
onClick={focusNextMention}
onReadAll={readAllMentions}
unreadCount={mentionsCount}
/>
)}
<ScrollDownButton
icon="heart-outline"
ariaLabelLang="AccDescrReactionMentionDown"
onClick={focusNextReaction}
onReadAll={readAllReactions}
unreadCount={reactionsCount}
className={buildClassName(
styles.reactions,
!hasUnreadReactions && styles.hidden,
!hasUnreadMentions && styles.transformDown,
)}
/>
<ScrollDownButton
icon="mention"
ariaLabelLang="AccDescrMentionDown"
onClick={focusNextMention}
onReadAll={readAllMentions}
unreadCount={mentionsCount}
className={!hasUnreadMentions && styles.hidden}
/>
<ScrollDownButton
icon="arrow-down"

View File

@ -244,7 +244,6 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
useOnChange(() => {
setDropAreaState(DropAreaState.None);
setIsFabShown(undefined);
setIsNotchShown(undefined);
}, [chatId]);

View File

@ -69,6 +69,8 @@ export default function useScrollHooks(
const isNearBottom = scrollBottom <= FAB_THRESHOLD;
const isAtBottom = scrollBottom <= NOTCH_THRESHOLD;
if (scrollHeight === 0) return;
onFabToggle(isUnread ? !isAtBottom : !isNearBottom);
onNotchToggle(!isAtBottom);
}

View File

@ -26,7 +26,7 @@ class TelegramClient {
private invokeMiddleware?: <A, R>(mockClient: TelegramClient, request: Api.Request<A, R>)
=> Promise<R | undefined | 'pass'>;
private mockData: MockTypes = {
public mockData: MockTypes = {
users: [],
chats: [],
channels: [],
@ -93,7 +93,7 @@ class TelegramClient {
}
getDialogs(type: 'active' | 'archived' = 'active') {
return this.mockData.dialogs[type].map((dialog) => createMockedDialog(dialog, this.mockData));
return this.mockData.dialogs[type].map((dialog) => createMockedDialog(dialog.id, this.mockData));
}
start({

View File

@ -0,0 +1,16 @@
import Api from '../../tl/api';
import type TelegramClient from '../MockClient';
import createMockedMessage from '../mockUtils/createMockedMessage';
export default async function<A, R>(mockClient: TelegramClient, request: Api.Request<A, R>) {
if (request instanceof Api.messages.GetUnreadMentions) {
return new Api.messages.Messages({
messages: [
createMockedMessage('2', 13, mockClient.mockData),
],
chats: [],
users: [],
});
}
return 'pass';
}

View File

@ -0,0 +1,83 @@
{
"users": [
{
"id": "1",
"self": true,
"username": "test"
}
],
"chats": [
],
"channels": [
{
"id": "2",
"title": "Mention",
"creator": true
}
],
"dialogs": {
"active": [
{
"id": "2",
"unreadMentionsCount": 1,
"unreadCount": 1,
"topMessage": 13,
"readInboxMaxId": 12
}
],
"archived": []
},
"messages": {
"2": [
{
"id": 3,
"message": "Hello!"
},
{
"id": 4,
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at nibh accumsan, condimentum nunc at, volutpat dolor. Integer laoreet dui in neque commodo, eu sagittis nibh consequat. Integer eu velit quis odio dictum vulputate auctor nec metus. Nunc vestibulum iaculis rhoncus. Vivamus pretium mollis odio condimentum euismod. Aliquam erat volutpat. 2!"
},
{
"id": 5,
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at nibh accumsan, condimentum nunc at, volutpat dolor. Integer laoreet dui in neque commodo, eu sagittis nibh consequat. Integer eu velit quis odio dictum vulputate auctor nec metus. Nunc vestibulum iaculis rhoncus. Vivamus pretium mollis odio condimentum euismod. Aliquam erat volutpat. 2!"
},
{
"id": 6,
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at nibh accumsan, condimentum nunc at, volutpat dolor. Integer laoreet dui in neque commodo, eu sagittis nibh consequat. Integer eu velit quis odio dictum vulputate auctor nec metus. Nunc vestibulum iaculis rhoncus. Vivamus pretium mollis odio condimentum euismod. Aliquam erat volutpat. 2!"
},
{
"id": 7,
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at nibh accumsan, condimentum nunc at, volutpat dolor. Integer laoreet dui in neque commodo, eu sagittis nibh consequat. Integer eu velit quis odio dictum vulputate auctor nec metus. Nunc vestibulum iaculis rhoncus. Vivamus pretium mollis odio condimentum euismod. Aliquam erat volutpat. 2!"
},
{
"id": 8,
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at nibh accumsan, condimentum nunc at, volutpat dolor. Integer laoreet dui in neque commodo, eu sagittis nibh consequat. Integer eu velit quis odio dictum vulputate auctor nec metus. Nunc vestibulum iaculis rhoncus. Vivamus pretium mollis odio condimentum euismod. Aliquam erat volutpat. 2!"
},
{
"id": 9,
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at nibh accumsan, condimentum nunc at, volutpat dolor. Integer laoreet dui in neque commodo, eu sagittis nibh consequat. Integer eu velit quis odio dictum vulputate auctor nec metus. Nunc vestibulum iaculis rhoncus. Vivamus pretium mollis odio condimentum euismod. Aliquam erat volutpat. 2!"
},
{
"id": 10,
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at nibh accumsan, condimentum nunc at, volutpat dolor. Integer laoreet dui in neque commodo, eu sagittis nibh consequat. Integer eu velit quis odio dictum vulputate auctor nec metus. Nunc vestibulum iaculis rhoncus. Vivamus pretium mollis odio condimentum euismod. Aliquam erat volutpat. 2!"
},
{
"id": 11,
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at nibh accumsan, condimentum nunc at, volutpat dolor. Integer laoreet dui in neque commodo, eu sagittis nibh consequat. Integer eu velit quis odio dictum vulputate auctor nec metus. Nunc vestibulum iaculis rhoncus. Vivamus pretium mollis odio condimentum euismod. Aliquam erat volutpat. 2!"
},
{
"id": 12,
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at nibh accumsan, condimentum nunc at, volutpat dolor. Integer laoreet dui in neque commodo, eu sagittis nibh consequat. Integer eu velit quis odio dictum vulputate auctor nec metus. Nunc vestibulum iaculis rhoncus. Vivamus pretium mollis odio condimentum euismod. Aliquam erat volutpat. 2!"
},
{
"id": 13,
"mentioned": true,
"mediaUnread": true,
"message": "@test"
}
]
},
"availableReactions": [],
"dialogFilters": [],
"documents": []
}

View File

@ -3,7 +3,7 @@ import type Api from '../../tl/api';
import type { ApiAvailableReaction } from '../../../../api/types';
import type { GramJsAppConfig } from '../../../../api/gramjs/apiBuilders/appConfig';
export type MockDialog = {
export type MockDialog = Partial<Api.Dialog> & {
id: string;
};

View File

@ -1,18 +1,30 @@
import Api from "../../tl/api";
import createMockedTypePeer from "./createMockedTypePeer";
import {MockDialog, MockTypes} from "./MockTypes";
import Api from '../../tl/api';
import createMockedTypePeer from './createMockedTypePeer';
import type { MockTypes } from './MockTypes';
export default function createMockedDialog(id: string, mockData: MockTypes): Api.Dialog {
const dialog = mockData.dialogs.active.find((d) => d.id === id)
|| mockData.dialogs.archived.find((d) => d.id === id);
if (!dialog) throw Error('No such dialog');
const {
unreadMentionsCount = 0,
unreadReactionsCount = 0,
readInboxMaxId = 0,
readOutboxMaxId = 0,
unreadCount = 0,
topMessage = 0,
} = dialog;
export default function createMockedDialog({
id,
}: MockDialog, mockData: MockTypes): Api.Dialog {
return new Api.Dialog({
peer: createMockedTypePeer(id, mockData),
topMessage: 0,
readInboxMaxId: 0,
readOutboxMaxId: 0,
unreadCount: 0,
unreadMentionsCount: 0,
unreadReactionsCount: 0,
topMessage,
readInboxMaxId,
readOutboxMaxId,
unreadCount,
unreadMentionsCount,
unreadReactionsCount,
notifySettings: new Api.PeerNotifySettings({}),
});
}

View File

@ -1,24 +1,26 @@
import Api from '../../tl/api';
import {MOCK_STARTING_DATE, MockTypes} from "./MockTypes";
import createMockedTypePeer from "./createMockedTypePeer";
import createMockedMessageMedia from "./createMockedMessageMedia";
import createMockedMessageReactions from "./createMockedMessageReactions";
import createMockedReplies from "./createMockedReplies";
import createMockedReplyTo from "./createMockedReplyTo";
import {omit} from "../../../../util/iteratees";
import type { MockTypes } from './MockTypes';
import { MOCK_STARTING_DATE } from './MockTypes';
import createMockedTypePeer from './createMockedTypePeer';
import createMockedMessageMedia from './createMockedMessageMedia';
import createMockedMessageReactions from './createMockedMessageReactions';
import createMockedReplies from './createMockedReplies';
import createMockedReplyTo from './createMockedReplyTo';
import { omit } from '../../../../util/iteratees';
export default function createMockedMessage(chatId: string, id: number, mockData: MockTypes): Api.Message {
const msg = mockData.messages[chatId].find((message) => message.id === id);
if(!msg) throw Error("No such message " + id);
if (!msg) throw Error(`No such message ${id}`);
const {
date = MOCK_STARTING_DATE + id,
message = "Message",
message = 'Message',
media,
reactions,
replies,
replyTo = createMockedReplyTo(chatId, id, mockData),
entities = [new Api.MessageEntityMention({ offset: 0, length: 5 })],
...rest
} = omit(msg, ['replyToMsgId', 'replyToTopId', 'replyToForumTopic']);
@ -28,6 +30,7 @@ export default function createMockedMessage(chatId: string, id: number, mockData
peerId: createMockedTypePeer(chatId, mockData),
date,
message,
entities,
replyTo,
...(media ? { media: createMockedMessageMedia(media, mockData) } : undefined),
...(reactions ? { reactions: createMockedMessageReactions(chatId, id, mockData) } : undefined),