Merge branch 'next-virtuoso-threads' into 'next'
Next: Use ScrollableList in threads See merge request soapbox-pub/soapbox-fe!1251
This commit is contained in:
commit
2704bfe2ef
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
import { Virtuoso, Components } from 'react-virtuoso';
|
||||
|
||||
import PullToRefresh from 'soapbox/components/pull-to-refresh';
|
||||
|
||||
|
@ -10,19 +10,15 @@ type Context = {
|
|||
listClassName?: string,
|
||||
}
|
||||
|
||||
type VComponent = {
|
||||
context?: Context,
|
||||
}
|
||||
|
||||
// NOTE: It's crucial to space lists with **padding** instead of margin!
|
||||
// Pass an `itemClassName` like `pb-3`, NOT a `space-y-3` className
|
||||
// https://virtuoso.dev/troubleshooting#list-does-not-scroll-to-the-bottom--items-jump-around
|
||||
const Item: React.FC<VComponent> = ({ context, ...rest }) => (
|
||||
const Item: Components<Context>['Item'] = ({ context, ...rest }) => (
|
||||
<div className={context?.itemClassName} {...rest} />
|
||||
);
|
||||
|
||||
// Ensure the className winds up here
|
||||
const List = React.forwardRef<HTMLDivElement, VComponent>((props, ref) => {
|
||||
const List: Components<Context>['List'] = React.forwardRef((props, ref) => {
|
||||
const { context, ...rest } = props;
|
||||
return <div ref={ref} className={context?.listClassName} {...rest} />;
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import StickyBox from 'react-sticky-box';
|
||||
|
||||
const Layout: React.FC = ({ children }) => (
|
||||
<div className='sm:py-4 relative pb-36'>
|
||||
<div className='sm:pt-4 relative pb-36'>
|
||||
<div className='max-w-3xl mx-auto sm:px-6 md:max-w-7xl md:px-8 md:grid md:grid-cols-12 md:gap-8'>
|
||||
{children}
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
} from 'soapbox/actions/moderation';
|
||||
import { getSettings } from 'soapbox/actions/settings';
|
||||
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||
import PullToRefresh from 'soapbox/components/pull-to-refresh';
|
||||
import ScrollableList from 'soapbox/components/scrollable_list';
|
||||
import SubNavigation from 'soapbox/components/sub_navigation';
|
||||
import { Column } from 'soapbox/components/ui';
|
||||
import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder_status';
|
||||
|
@ -642,9 +642,11 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
}
|
||||
|
||||
render() {
|
||||
let ancestors, descendants;
|
||||
const { status, ancestorsIds, descendantsIds, intl } = this.props;
|
||||
|
||||
const hasAncestors = ancestorsIds && ancestorsIds.size > 0;
|
||||
const hasDescendants = descendantsIds && descendantsIds.size > 0;
|
||||
|
||||
if (!status && this.state.isLoaded) {
|
||||
// TODO: handle errors other than 404 with `this.state.error?.response?.status`
|
||||
return (
|
||||
|
@ -656,14 +658,6 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
);
|
||||
}
|
||||
|
||||
if (ancestorsIds && ancestorsIds.size > 0) {
|
||||
ancestors = this.renderChildren(ancestorsIds);
|
||||
}
|
||||
|
||||
if (descendantsIds && descendantsIds.size > 0) {
|
||||
descendants = this.renderChildren(descendantsIds);
|
||||
}
|
||||
|
||||
type HotkeyHandlers = { [key: string]: (keyEvent?: KeyboardEvent) => void };
|
||||
|
||||
const handlers: HotkeyHandlers = {
|
||||
|
@ -683,6 +677,76 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
const username = String(status.getIn(['account', 'acct']));
|
||||
const titleMessage = status.visibility === 'direct' ? messages.titleDirect : messages.title;
|
||||
|
||||
const focusedStatus = (
|
||||
<div className={classNames('thread__detailed-status', { 'pb-4': hasDescendants })} key={status.id}>
|
||||
<HotKeys handlers={handlers}>
|
||||
<div
|
||||
ref={this.setStatusRef}
|
||||
className={classNames('detailed-status__wrapper')}
|
||||
tabIndex={0}
|
||||
// FIXME: no "reblogged by" text is added for the screen reader
|
||||
aria-label={textForScreenReader(intl, status)}
|
||||
>
|
||||
{/* @ts-ignore */}
|
||||
<DetailedStatus
|
||||
status={status}
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
onOpenMedia={this.handleOpenMedia}
|
||||
onToggleHidden={this.handleToggleHidden}
|
||||
showMedia={this.state.showMedia}
|
||||
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
||||
/>
|
||||
|
||||
<hr className='mb-2 dark:border-slate-600' />
|
||||
|
||||
<ActionBar
|
||||
status={status}
|
||||
onReply={this.handleReplyClick}
|
||||
onFavourite={this.handleFavouriteClick}
|
||||
onEmojiReact={this.handleEmojiReactClick}
|
||||
onReblog={this.handleReblogClick}
|
||||
onQuote={this.handleQuoteClick}
|
||||
onDelete={this.handleDeleteClick}
|
||||
onDirect={this.handleDirectClick}
|
||||
onChat={this.handleChatClick}
|
||||
onMention={this.handleMentionClick}
|
||||
onMute={this.handleMuteClick}
|
||||
onMuteConversation={this.handleConversationMuteClick}
|
||||
onBlock={this.handleBlockClick}
|
||||
onReport={this.handleReport}
|
||||
onPin={this.handlePin}
|
||||
onBookmark={this.handleBookmark}
|
||||
onEmbed={this.handleEmbed}
|
||||
onDeactivateUser={this.handleDeactivateUser}
|
||||
onDeleteUser={this.handleDeleteUser}
|
||||
onToggleStatusSensitivity={this.handleToggleStatusSensitivity}
|
||||
onDeleteStatus={this.handleDeleteStatus}
|
||||
allowedEmoji={this.props.allowedEmoji}
|
||||
emojiSelectorFocused={this.state.emojiSelectorFocused}
|
||||
handleEmojiSelectorExpand={this.handleEmojiSelectorExpand}
|
||||
handleEmojiSelectorUnfocus={this.handleEmojiSelectorUnfocus}
|
||||
/>
|
||||
</div>
|
||||
</HotKeys>
|
||||
|
||||
{hasDescendants && (
|
||||
<hr className='mt-2 dark:border-slate-600' />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const children: JSX.Element[] = [];
|
||||
|
||||
if (hasAncestors) {
|
||||
children.push(...this.renderChildren(ancestorsIds).toArray());
|
||||
}
|
||||
|
||||
children.push(focusedStatus);
|
||||
|
||||
if (hasDescendants) {
|
||||
children.push(...this.renderChildren(descendantsIds).toArray());
|
||||
}
|
||||
|
||||
return (
|
||||
<Column label={intl.formatMessage(titleMessage, { username })} transparent>
|
||||
<div className='px-4 pt-4 sm:p-0'>
|
||||
|
@ -690,70 +754,9 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
</div>
|
||||
|
||||
<div ref={this.setRef} className='thread'>
|
||||
<PullToRefresh onRefresh={this.handleRefresh}>
|
||||
{ancestors && (
|
||||
<div className='thread__ancestors space-y-4 mb-4'>{ancestors}</div>
|
||||
)}
|
||||
|
||||
<div className='thread__status thread__status--focused'>
|
||||
<HotKeys handlers={handlers}>
|
||||
<div
|
||||
ref={this.setStatusRef}
|
||||
className={classNames('detailed-status__wrapper')}
|
||||
tabIndex={0}
|
||||
// FIXME: no "reblogged by" text is added for the screen reader
|
||||
aria-label={textForScreenReader(intl, status)}
|
||||
>
|
||||
{/* @ts-ignore */}
|
||||
<DetailedStatus
|
||||
status={status}
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
onOpenMedia={this.handleOpenMedia}
|
||||
onToggleHidden={this.handleToggleHidden}
|
||||
showMedia={this.state.showMedia}
|
||||
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
||||
/>
|
||||
|
||||
<hr className='mb-2 dark:border-slate-600' />
|
||||
|
||||
<ActionBar
|
||||
status={status}
|
||||
onReply={this.handleReplyClick}
|
||||
onFavourite={this.handleFavouriteClick}
|
||||
onEmojiReact={this.handleEmojiReactClick}
|
||||
onReblog={this.handleReblogClick}
|
||||
onQuote={this.handleQuoteClick}
|
||||
onDelete={this.handleDeleteClick}
|
||||
onDirect={this.handleDirectClick}
|
||||
onChat={this.handleChatClick}
|
||||
onMention={this.handleMentionClick}
|
||||
onMute={this.handleMuteClick}
|
||||
onMuteConversation={this.handleConversationMuteClick}
|
||||
onBlock={this.handleBlockClick}
|
||||
onReport={this.handleReport}
|
||||
onPin={this.handlePin}
|
||||
onBookmark={this.handleBookmark}
|
||||
onEmbed={this.handleEmbed}
|
||||
onDeactivateUser={this.handleDeactivateUser}
|
||||
onDeleteUser={this.handleDeleteUser}
|
||||
onToggleStatusSensitivity={this.handleToggleStatusSensitivity}
|
||||
onDeleteStatus={this.handleDeleteStatus}
|
||||
allowedEmoji={this.props.allowedEmoji}
|
||||
emojiSelectorFocused={this.state.emojiSelectorFocused}
|
||||
handleEmojiSelectorExpand={this.handleEmojiSelectorExpand}
|
||||
handleEmojiSelectorUnfocus={this.handleEmojiSelectorUnfocus}
|
||||
/>
|
||||
</div>
|
||||
</HotKeys>
|
||||
</div>
|
||||
|
||||
{descendants && (
|
||||
<>
|
||||
<hr className='mt-2 dark:border-slate-600' />
|
||||
<div className='thread__descendants space-y-4'>{descendants}</div>
|
||||
</>
|
||||
)}
|
||||
</PullToRefresh>
|
||||
<ScrollableList onRefresh={this.handleRefresh}>
|
||||
{children}
|
||||
</ScrollableList>
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
|
|
|
@ -170,7 +170,7 @@
|
|||
@apply sm:bg-white sm:dark:bg-slate-800 p-4 sm:shadow-xl sm:p-6 sm:rounded-xl;
|
||||
|
||||
&__status {
|
||||
@apply relative;
|
||||
@apply relative pb-4;
|
||||
|
||||
.status__wrapper {
|
||||
@apply shadow-none p-0;
|
||||
|
@ -182,16 +182,7 @@
|
|||
// }
|
||||
}
|
||||
|
||||
&__descendants {
|
||||
@apply pt-4;
|
||||
}
|
||||
|
||||
&__descendants .thread__status {
|
||||
// @apply py-4;
|
||||
}
|
||||
|
||||
&__descendants .status__content-wrapper,
|
||||
&__ancestors .status__content-wrapper {
|
||||
.status__content-wrapper {
|
||||
padding-left: calc(42px + 12px);
|
||||
}
|
||||
|
||||
|
@ -225,7 +216,7 @@
|
|||
|
||||
&--bottom {
|
||||
@apply block;
|
||||
height: calc(100% - 42px - 8px);
|
||||
height: calc(100% - 42px - 8px - 1rem);
|
||||
top: calc(12px + 42px);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue