diff --git a/src/components/middle/FloatingActionButtons.module.scss b/src/components/middle/FloatingActionButtons.module.scss index 675e2cb84..25f560029 100644 --- a/src/components/middle/FloatingActionButtons.module.scss +++ b/src/components/middle/FloatingActionButtons.module.scss @@ -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; } diff --git a/src/components/middle/FloatingActionButtons.tsx b/src/components/middle/FloatingActionButtons.tsx index 4514f0e95..6009b2569 100644 --- a/src/components/middle/FloatingActionButtons.tsx +++ b/src/components/middle/FloatingActionButtons.tsx @@ -93,24 +93,27 @@ const FloatingActionButtons: FC = ({ return (
- {hasUnreadReactions && ( - - )} - {hasUnreadMentions && ( - - )} + + + = ({ useOnChange(() => { setDropAreaState(DropAreaState.None); - setIsFabShown(undefined); setIsNotchShown(undefined); }, [chatId]); diff --git a/src/components/middle/hooks/useScrollHooks.ts b/src/components/middle/hooks/useScrollHooks.ts index 520701e20..f985f61a7 100644 --- a/src/components/middle/hooks/useScrollHooks.ts +++ b/src/components/middle/hooks/useScrollHooks.ts @@ -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); } diff --git a/src/lib/gramjs/client/MockClient.ts b/src/lib/gramjs/client/MockClient.ts index 3606f5457..391475d43 100644 --- a/src/lib/gramjs/client/MockClient.ts +++ b/src/lib/gramjs/client/MockClient.ts @@ -26,7 +26,7 @@ class TelegramClient { private invokeMiddleware?: (mockClient: TelegramClient, request: Api.Request) => Promise; - 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({ diff --git a/src/lib/gramjs/client/__invokeMiddlewares__/mention.ts b/src/lib/gramjs/client/__invokeMiddlewares__/mention.ts new file mode 100644 index 000000000..fdd95fddd --- /dev/null +++ b/src/lib/gramjs/client/__invokeMiddlewares__/mention.ts @@ -0,0 +1,16 @@ +import Api from '../../tl/api'; +import type TelegramClient from '../MockClient'; +import createMockedMessage from '../mockUtils/createMockedMessage'; + +export default async function(mockClient: TelegramClient, request: Api.Request) { + if (request instanceof Api.messages.GetUnreadMentions) { + return new Api.messages.Messages({ + messages: [ + createMockedMessage('2', 13, mockClient.mockData), + ], + chats: [], + users: [], + }); + } + return 'pass'; +} diff --git a/src/lib/gramjs/client/__mocks__/mention.json b/src/lib/gramjs/client/__mocks__/mention.json new file mode 100644 index 000000000..b9f8b41de --- /dev/null +++ b/src/lib/gramjs/client/__mocks__/mention.json @@ -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": [] +} diff --git a/src/lib/gramjs/client/mockUtils/MockTypes.ts b/src/lib/gramjs/client/mockUtils/MockTypes.ts index 268d43339..f433ef7e7 100644 --- a/src/lib/gramjs/client/mockUtils/MockTypes.ts +++ b/src/lib/gramjs/client/mockUtils/MockTypes.ts @@ -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 & { id: string; }; diff --git a/src/lib/gramjs/client/mockUtils/createMockedDialog.ts b/src/lib/gramjs/client/mockUtils/createMockedDialog.ts index 367a063b9..1cfeeb50d 100644 --- a/src/lib/gramjs/client/mockUtils/createMockedDialog.ts +++ b/src/lib/gramjs/client/mockUtils/createMockedDialog.ts @@ -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({}), }); } diff --git a/src/lib/gramjs/client/mockUtils/createMockedMessage.ts b/src/lib/gramjs/client/mockUtils/createMockedMessage.ts index a66a52e93..613c5b218 100644 --- a/src/lib/gramjs/client/mockUtils/createMockedMessage.ts +++ b/src/lib/gramjs/client/mockUtils/createMockedMessage.ts @@ -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),