Forward Picker: Smooth scroll on mobile

This commit is contained in:
Alexander Zinchuk 2022-02-02 22:49:02 +01:00
parent 9c04a2eaa5
commit e7026aa16c
5 changed files with 64 additions and 24 deletions

View File

@ -46,7 +46,7 @@
height: 100%; height: 100%;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
padding: 0 1rem 1rem; padding: 0 0.5rem;
} }
} }
@ -60,6 +60,15 @@
color: var(--color-text-secondary); color: var(--color-text-secondary);
} }
.scroll-container {
position: relative;
}
.ListItem {
position: absolute;
width: 100%;
}
.ListItem.chat-item-clickable { .ListItem.chat-item-clickable {
&:not(.force-rounded-corners) { &:not(.force-rounded-corners) {
@media (max-width: 600px) { @media (max-width: 600px) {

View File

@ -3,6 +3,8 @@ import React, {
FC, memo, useRef, useCallback, FC, memo, useRef, useCallback,
} from '../../lib/teact/teact'; } from '../../lib/teact/teact';
import { CHAT_HEIGHT_PX } from '../../config';
import { IS_ANDROID } from '../../util/environment';
import useInfiniteScroll from '../../hooks/useInfiniteScroll'; import useInfiniteScroll from '../../hooks/useInfiniteScroll';
import useLang from '../../hooks/useLang'; import useLang from '../../hooks/useLang';
import useKeyboardListNavigation from '../../hooks/useKeyboardListNavigation'; import useKeyboardListNavigation from '../../hooks/useKeyboardListNavigation';
@ -27,10 +29,11 @@ export type OwnProps = {
filterRef: RefObject<HTMLInputElement>; filterRef: RefObject<HTMLInputElement>;
filterPlaceholder: string; filterPlaceholder: string;
filter: string; filter: string;
onFilterChange: (filter: string) => void;
loadMore: NoneToVoidFunction; loadMore: NoneToVoidFunction;
onFilterChange: (filter: string) => void;
onSelectChatOrUser: (chatOrUserId: string) => void; onSelectChatOrUser: (chatOrUserId: string) => void;
onClose: NoneToVoidFunction; onClose: NoneToVoidFunction;
onCloseAnimationEnd?: NoneToVoidFunction;
}; };
const ChatOrUserPicker: FC<OwnProps> = ({ const ChatOrUserPicker: FC<OwnProps> = ({
@ -40,10 +43,11 @@ const ChatOrUserPicker: FC<OwnProps> = ({
filterRef, filterRef,
filter, filter,
filterPlaceholder, filterPlaceholder,
onFilterChange,
onClose,
loadMore, loadMore,
onFilterChange,
onSelectChatOrUser, onSelectChatOrUser,
onClose,
onCloseAnimationEnd,
}) => { }) => {
const lang = useLang(); const lang = useLang();
const [viewportIds, getMore] = useInfiniteScroll(loadMore, chatOrUserIds, Boolean(filter)); const [viewportIds, getMore] = useInfiniteScroll(loadMore, chatOrUserIds, Boolean(filter));
@ -85,35 +89,47 @@ const ChatOrUserPicker: FC<OwnProps> = ({
</div> </div>
); );
const viewportOffset = chatOrUserIds!.indexOf(viewportIds![0]);
return ( return (
<Modal <Modal
isOpen={isOpen} isOpen={isOpen}
onClose={onClose}
className="ChatOrUserPicker" className="ChatOrUserPicker"
header={modalHeader} header={modalHeader}
onClose={onClose}
onCloseAnimationEnd={onCloseAnimationEnd}
> >
{viewportIds?.length ? ( {viewportIds?.length ? (
<InfiniteScroll <InfiniteScroll
ref={containerRef}
className="picker-list custom-scroll" className="picker-list custom-scroll"
items={viewportIds} items={viewportIds}
onLoadMore={getMore} onLoadMore={getMore}
noScrollRestore={Boolean(filter)} noFastList
ref={containerRef} noScrollRestore
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
> >
{viewportIds.map((id) => ( <div
<ListItem className="scroll-container"
key={id} // @ts-ignore
className="chat-item-clickable force-rounded-corners" style={IS_ANDROID ? `height: ${chatOrUserIds!.length * CHAT_HEIGHT_PX}px` : undefined}
onClick={() => onSelectChatOrUser(id)} teactFastList
> >
{isUserId(id) ? ( {viewportIds.map((id, i) => (
<PrivateChatInfo status={id === currentUserId ? lang('SavedMessagesInfo') : undefined} userId={id} /> <ListItem
) : ( key={id}
<GroupChatInfo chatId={id} /> className="chat-item-clickable force-rounded-corners"
)} style={`top: ${(viewportOffset + i) * CHAT_HEIGHT_PX}px;`}
</ListItem> onClick={() => onSelectChatOrUser(id)}
))} >
{isUserId(id) ? (
<PrivateChatInfo status={id === currentUserId ? lang('SavedMessagesInfo') : undefined} userId={id} />
) : (
<GroupChatInfo chatId={id} />
)}
</ListItem>
))}
</div>
</InfiniteScroll> </InfiniteScroll>
) : viewportIds && !viewportIds.length ? ( ) : viewportIds && !viewportIds.length ? (
<p className="no-results">{lang('lng_blocked_list_not_found')}</p> <p className="no-results">{lang('lng_blocked_list_not_found')}</p>

View File

@ -1,5 +1,5 @@
import React, { import React, {
FC, useMemo, useState, memo, useRef, useCallback, FC, useMemo, useState, memo, useRef, useCallback, useEffect,
} from '../../lib/teact/teact'; } from '../../lib/teact/teact';
import { getDispatch, getGlobal, withGlobal } from '../../lib/teact/teactn'; import { getDispatch, getGlobal, withGlobal } from '../../lib/teact/teactn';
@ -14,6 +14,7 @@ import {
import { unique } from '../../util/iteratees'; import { unique } from '../../util/iteratees';
import useLang from '../../hooks/useLang'; import useLang from '../../hooks/useLang';
import useCurrentOrPrev from '../../hooks/useCurrentOrPrev'; import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
import useFlag from '../../hooks/useFlag';
import ChatOrUserPicker from '../common/ChatOrUserPicker'; import ChatOrUserPicker from '../common/ChatOrUserPicker';
@ -50,6 +51,13 @@ const ForwardPicker: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const filterRef = useRef<HTMLInputElement>(null); const filterRef = useRef<HTMLInputElement>(null);
const [isShown, markIsShown, unmarkIsShown] = useFlag();
useEffect(() => {
if (isOpen) {
markIsShown();
}
}, [isOpen, markIsShown]);
const chatAndContactIds = useMemo(() => { const chatAndContactIds = useMemo(() => {
if (!isOpen) { if (!isOpen) {
return undefined; return undefined;
@ -82,7 +90,11 @@ const ForwardPicker: FC<OwnProps & StateProps> = ({
setForwardChatId({ id: userId }); setForwardChatId({ id: userId });
}, [setForwardChatId]); }, [setForwardChatId]);
const renderingChatAndContactIds = useCurrentOrPrev(chatAndContactIds)!; const renderingChatAndContactIds = useCurrentOrPrev(chatAndContactIds, true)!;
if (!isOpen && !isShown) {
return undefined;
}
return ( return (
<ChatOrUserPicker <ChatOrUserPicker
@ -96,6 +108,7 @@ const ForwardPicker: FC<OwnProps & StateProps> = ({
loadMore={loadMoreChats} loadMore={loadMoreChats}
onSelectChatOrUser={handleSelectUser} onSelectChatOrUser={handleSelectUser}
onClose={exitForwardMode} onClose={exitForwardMode}
onCloseAnimationEnd={unmarkIsShown}
/> />
); );
}; };

View File

@ -147,8 +147,6 @@
} }
&.chat-item-clickable { &.chat-item-clickable {
margin: 0 -0.5rem;
body.is-ios &, body.is-ios &,
body.is-macos & { body.is-macos & {
--color-text-secondary: var(--color-text-secondary-apple); --color-text-secondary: var(--color-text-secondary-apple);

View File

@ -34,6 +34,10 @@ const useInfiniteScroll = <ListId extends string | number>(
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();
if (isDisabled) {
lastParamsRef.current = {};
}
const prevListIds = usePrevious(listIds); const prevListIds = usePrevious(listIds);
const prevIsDisabled = usePrevious(isDisabled); const prevIsDisabled = usePrevious(isDisabled);
if (listIds && !isDisabled && (listIds !== prevListIds || isDisabled !== prevIsDisabled)) { if (listIds && !isDisabled && (listIds !== prevListIds || isDisabled !== prevIsDisabled)) {