Refactors column component (#40112)

This commit is contained in:
Echo
2026-08-11 14:08:00 +02:00
committed by GitHub
parent e5db3aaa14
commit 6aec81cab0
58 changed files with 284 additions and 482 deletions

View File

@@ -1,60 +0,0 @@
import { forwardRef, useRef, useImperativeHandle } from 'react';
import type { Ref } from 'react';
import classNames from 'classnames';
import { scrollTop } from 'mastodon/scroll';
export interface ColumnRef {
scrollTop: () => void;
node: HTMLDivElement | null;
}
interface ColumnProps {
children?: React.ReactNode;
label?: string;
bindToDocument?: boolean;
className?: string;
}
export const Column = forwardRef<ColumnRef, ColumnProps>(
({ children, label, bindToDocument, className }, ref: Ref<ColumnRef>) => {
const nodeRef = useRef<HTMLDivElement>(null);
useImperativeHandle(ref, () => ({
node: nodeRef.current,
scrollTop() {
let scrollable = null;
if (bindToDocument) {
scrollable = document.scrollingElement;
} else {
scrollable = nodeRef.current?.querySelector('.scrollable');
}
if (!scrollable) {
return;
}
scrollTop(scrollable);
},
}));
return (
<div
role='region'
aria-label={label}
className={classNames('column', className)}
ref={nodeRef}
>
{children}
</div>
);
},
);
Column.displayName = 'Column';
// eslint-disable-next-line import/no-default-export
export default Column;

View File

@@ -7,9 +7,9 @@ import { Icon } from 'mastodon/components/icon';
import { getColumnSkipLinkId } from 'mastodon/features/ui/components/skip_links';
import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
import { useColumnIndexContext } from '../features/ui/components/columns_area';
import { useAppHistory } from '../router';
import { useAppHistory } from './router';
import { useColumnIndexContext } from './context';
type OnClickCallback = () => void;

View File

@@ -0,0 +1,12 @@
import { createContext, useContext } from 'react';
export const ColumnIndexContext = createContext(1);
export const useColumnIndexContext = () => useContext(ColumnIndexContext);
export const ColumnContext = createContext({
scrollTop: () => {
// Implemented in index.tsx
},
});
export const useColumn = () => useContext(ColumnContext);

View File

@@ -16,11 +16,11 @@ import { Icon } from 'mastodon/components/icon';
import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
import { useIdentity } from 'mastodon/identity_context';
import { useColumnIndexContext } from '../features/ui/components/columns_area';
import { getColumnSkipLinkId } from '../features/ui/components/skip_links';
import { getColumnSkipLinkId } from '../../features/ui/components/skip_links';
import { NavigationFocusTarget } from '../navigation_focus_target';
import { useAppHistory } from '../router';
import { NavigationFocusTarget } from './navigation_focus_target';
import { useAppHistory } from './router';
import { useColumn, useColumnIndexContext } from './context';
export const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
@@ -73,7 +73,7 @@ const BackButton: React.FC<{
);
};
export interface Props {
export interface ColumnHeaderProps {
title?: React.ReactNode;
icon?: string;
iconComponent?: IconProp;
@@ -87,12 +87,13 @@ export interface Props {
placeholder?: boolean;
appendContent?: React.ReactNode;
collapseIssues?: boolean;
scrollTopOnClick?: boolean;
onClick?: () => void;
onMove?: (arg0: number) => void;
onPin?: () => void;
}
export const ColumnHeader: React.FC<Props> = ({
export const ColumnHeader: React.FC<ColumnHeaderProps> = ({
title,
icon,
iconComponent,
@@ -106,6 +107,7 @@ export const ColumnHeader: React.FC<Props> = ({
placeholder,
appendContent,
collapseIssues,
scrollTopOnClick,
onClick,
onMove,
onPin,
@@ -125,9 +127,13 @@ export const ColumnHeader: React.FC<Props> = ({
[setCollapsed, setAnimating],
);
const { scrollTop } = useColumn();
const handleTitleClick = useCallback(() => {
onClick?.();
}, [onClick]);
if (scrollTopOnClick) {
scrollTop();
}
}, [onClick, scrollTop, scrollTopOnClick]);
const handleMoveLeft = useCallback(() => {
onMove?.(-1);
@@ -290,7 +296,7 @@ export const ColumnHeader: React.FC<Props> = ({
as='h1'
className='column-header__title-wrapper'
>
{onClick ? (
{onClick || scrollTopOnClick ? (
<button
onClick={handleTitleClick}
className={titleClassNames}
@@ -337,6 +343,3 @@ export const ColumnHeader: React.FC<Props> = ({
return <ButtonInTabsBar>{component}</ButtonInTabsBar>;
}
};
// eslint-disable-next-line import/no-default-export
export default ColumnHeader;

View File

@@ -0,0 +1,80 @@
import { useRef, useMemo } from 'react';
import classNames from 'classnames';
import { useDebouncedCallback } from 'use-debounce';
import { getColumnSkipLinkId } from '@/mastodon/features/ui/components/skip_links';
import { scrollTop } from 'mastodon/scroll';
import { ColumnContext, useColumnIndexContext } from './context';
interface ColumnProps {
children?: React.ReactNode;
label?: string;
bindToDocument?: boolean;
className?: string;
}
const TIMEOUT = 200;
export const Column: React.FC<ColumnProps> = ({
children,
label,
bindToDocument,
className,
}) => {
const nodeRef = useRef<HTMLDivElement>(null);
const idleCallbackId = useRef<number>(null);
const contextValue = useMemo(
() => ({
scrollTop() {
let scrollable = null;
if (bindToDocument) {
scrollable = document.scrollingElement;
} else {
scrollable = nodeRef.current?.querySelector('.scrollable');
}
if (!scrollable) {
return;
}
idleCallbackId.current = scrollTop(scrollable, {
timeout: TIMEOUT,
callback() {
idleCallbackId.current = null;
},
});
},
}),
[bindToDocument],
);
const handleScroll = useDebouncedCallback(() => {
if (typeof idleCallbackId.current === 'number') {
cancelIdleCallback(idleCallbackId.current);
}
}, TIMEOUT);
const columnIndex = useColumnIndexContext();
return (
<div
role='region'
ref={nodeRef}
onScroll={handleScroll}
className={classNames('column', className)}
aria-label={label}
aria-labelledby={
label === undefined ? getColumnSkipLinkId(columnIndex) : undefined
}
>
<ColumnContext.Provider value={contextValue}>
{children}
</ColumnContext.Provider>
</div>
);
};

View File

@@ -13,7 +13,7 @@ import { domain } from 'mastodon/initial_state';
import { injectIntl } from '@/mastodon/components/intl';
import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'mastodon/actions/server';
import { Account } from 'mastodon/components/account';
import Column from 'mastodon/components/column';
import { Column } from '@/mastodon/components/column';
import { NavigationFocusTarget } from 'mastodon/components/navigation_focus_target';
import { ServerHeroImage } from 'mastodon/components/server_hero_image';
import { Skeleton } from 'mastodon/components/skeleton';

View File

@@ -7,7 +7,7 @@ import { Link } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column_header';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import { BundleColumnError } from '@/mastodon/features/ui/components/bundle_column_error';

View File

@@ -9,7 +9,8 @@ import { List as ImmutableList } from 'immutable';
import { fetchEndorsedAccounts } from '@/mastodon/actions/accounts';
import { AccountHeader } from '@/mastodon/components/account_header';
import { AccountListItem } from '@/mastodon/components/account_list_item';
import { ColumnBackButton } from '@/mastodon/components/column_back_button';
import { Column } from '@/mastodon/components/column';
import { ColumnBackButton } from '@/mastodon/components/column/back_button';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import { RemoteHint } from '@/mastodon/components/remote_hint';
import {
@@ -20,7 +21,6 @@ import {
import type { TruncatedListItemInfo } from '@/mastodon/components/truncated_list';
import { TruncatedListItems } from '@/mastodon/components/truncated_list';
import { BundleColumnError } from '@/mastodon/features/ui/components/bundle_column_error';
import Column from '@/mastodon/features/ui/components/column';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { useAccountId } from '@/mastodon/hooks/useAccountId';
import { useAccountVisibility } from '@/mastodon/hooks/useAccountVisibility';

View File

@@ -7,12 +7,12 @@ import { List as ImmutableList, isList } from 'immutable';
import { openModal } from '@/mastodon/actions/modal';
import { expandAccountMediaTimeline } from '@/mastodon/actions/timelines';
import { AccountHeader } from '@/mastodon/components/account_header';
import { ColumnBackButton } from '@/mastodon/components/column_back_button';
import { Column } from '@/mastodon/components/column';
import { ColumnBackButton } from '@/mastodon/components/column/back_button';
import { LimitedAccountHint } from '@/mastodon/components/limited_account_hint';
import { RemoteHint } from '@/mastodon/components/remote_hint';
import ScrollableList from '@/mastodon/components/scrollable_list';
import { BundleColumnError } from '@/mastodon/features/ui/components/bundle_column_error';
import Column from '@/mastodon/features/ui/components/column';
import { useAccountId } from '@/mastodon/hooks/useAccountId';
import { useAccountVisibility } from '@/mastodon/hooks/useAccountVisibility';
import type { MediaAttachment } from '@/mastodon/models/media_attachment';

View File

@@ -14,7 +14,7 @@ import {
} from '@/mastodon/actions/timelines_typed';
import { AccountHeader } from '@/mastodon/components/account_header';
import { Column } from '@/mastodon/components/column';
import { ColumnBackButton } from '@/mastodon/components/column_back_button';
import { ColumnBackButton } from '@/mastodon/components/column/back_button';
import { LimitedAccountHint } from '@/mastodon/components/limited_account_hint';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import { RemoteHint } from '@/mastodon/components/remote_hint';

View File

@@ -10,12 +10,12 @@ import { debounce } from 'lodash';
import { injectIntl } from '@/mastodon/components/intl';
import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react';
import { Account } from 'mastodon/components/account';
import { fetchBlocks, expandBlocks } from '../../actions/blocks';
import { LoadingIndicator } from '../../components/loading_indicator';
import ScrollableList from '../../components/scrollable_list';
import Column from '../ui/components/column';
import { fetchBlocks, expandBlocks } from '@/mastodon/actions/blocks';
import { Account } from '@/mastodon/components/account';
import { Column } from '@/mastodon/components/column';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import ScrollableList from '@/mastodon/components/scrollable_list';
import { ColumnHeader } from '@/mastodon/components/column/header';
const messages = defineMessages({
heading: { id: 'column.blocks', defaultMessage: 'Blocked users' },
@@ -61,7 +61,8 @@ class Blocks extends ImmutablePureComponent {
const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />;
return (
<Column bindToDocument={!multiColumn} icon='ban' iconComponent={BlockIcon} heading={intl.formatMessage(messages.heading)} alwaysShowBackButton>
<Column bindToDocument={!multiColumn}>
<ColumnHeader icon='ban' iconComponent={BlockIcon} title={intl.formatMessage(messages.heading)} showBackButton />
<ScrollableList
scrollKey='blocks'
onLoadMore={this.handleLoadMore}

View File

@@ -1,18 +1,17 @@
import { useEffect, useRef, useCallback } from 'react';
import { useEffect, useCallback } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import BookmarksIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react';
import {
fetchBookmarkedStatuses,
expandBookmarkedStatuses,
} from 'mastodon/actions/bookmarks';
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
import { Column } from 'mastodon/components/column';
import type { ColumnRef } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import StatusList from 'mastodon/components/status_list';
import { getStatusList } from 'mastodon/selectors';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
@@ -27,7 +26,6 @@ const Bookmarks: React.FC<{
}> = ({ columnId, multiColumn }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const columnRef = useRef<ColumnRef>(null);
const statusIds = useAppSelector((state) =>
getStatusList(state, 'bookmarks'),
);
@@ -58,10 +56,6 @@ const Bookmarks: React.FC<{
[dispatch, columnId],
);
const handleHeaderClick = useCallback(() => {
columnRef.current?.scrollTop();
}, []);
const handleLoadMore = useCallback(() => {
dispatch(expandBookmarkedStatuses());
}, [dispatch]);
@@ -78,7 +72,6 @@ const Bookmarks: React.FC<{
return (
<Column
bindToDocument={!multiColumn}
ref={columnRef}
label={intl.formatMessage(messages.heading)}
>
<ColumnHeader
@@ -87,9 +80,9 @@ const Bookmarks: React.FC<{
title={intl.formatMessage(messages.heading)}
onPin={handlePin}
onMove={handleMove}
onClick={handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
scrollTopOnClick
/>
<StatusList

View File

@@ -7,6 +7,8 @@ import { Link } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import HelpIcon from '@/material-icons/400-24px/help.svg?react';
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
import ShareIcon from '@/material-icons/400-24px/share.svg?react';
@@ -18,8 +20,6 @@ import type {
} from 'mastodon/api_types/collections';
import { Badge } from 'mastodon/components/badge';
import { Callout } from 'mastodon/components/callout';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { DisplayName } from 'mastodon/components/display_name';
import { useAccountHandle } from 'mastodon/components/display_name/default';
import { FormattedDateWrapper } from 'mastodon/components/formatted_date';

View File

@@ -13,10 +13,10 @@ import {
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
import { Callout } from 'mastodon/components/callout';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator';
import { useIdentity } from 'mastodon/identity_context';

View File

@@ -4,9 +4,9 @@ import { Route, Switch, useRouteMatch } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { NavigationFocusTarget } from '@/mastodon/components/navigation_focus_target';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { DisplayNameSimple } from 'mastodon/components/display_name/simple';
import { Scrollable } from 'mastodon/components/scrollable_list/components';
import { TabLink, TabList } from 'mastodon/components/tab_list';

View File

@@ -8,6 +8,8 @@ import { Helmet } from '@unhead/react/helmet';
import { connect } from 'react-redux';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { injectIntl } from '@/mastodon/components/intl';
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
@@ -17,8 +19,6 @@ import { canViewFeed } from 'mastodon/permissions';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { connectCommunityStream } from '../../actions/streaming';
import { expandCommunityTimeline } from '../../actions/timelines';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import StatusListContainer from '../ui/containers/status_list_container';
import ColumnSettingsContainer from './containers/column_settings_container';
@@ -70,10 +70,6 @@ class CommunityTimeline extends PureComponent {
dispatch(moveColumn(columnId, dir));
};
handleHeaderClick = () => {
this.column.scrollTop();
};
componentDidMount () {
const { dispatch, onlyMedia } = this.props;
const { signedIn } = this.props.identity;
@@ -110,10 +106,6 @@ class CommunityTimeline extends PureComponent {
}
}
setRef = c => {
this.column = c;
};
handleLoadMore = maxId => {
const { dispatch, onlyMedia } = this.props;
@@ -138,7 +130,7 @@ class CommunityTimeline extends PureComponent {
);
return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon='users'
iconComponent={PeopleIcon}
@@ -146,9 +138,9 @@ class CommunityTimeline extends PureComponent {
title={intl.formatMessage(messages.title)}
onPin={this.handlePin}
onMove={this.handleMove}
onClick={this.handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
scrollTopOnClick
>
<ColumnSettingsContainer columnId={columnId} />
</ColumnHeader>

View File

@@ -9,6 +9,8 @@ import type { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { Helmet } from '@unhead/react/helmet';
import elephantUIPlane from '@/images/elephant_ui_plane.svg';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import EditIcon from '@/material-icons/400-24px/edit_square.svg?react';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
@@ -19,8 +21,6 @@ import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
import { mountCompose, unmountCompose } from 'mastodon/actions/compose';
import { openModal } from 'mastodon/actions/modal';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { Icon } from 'mastodon/components/icon';
import { mascot, reduceMotion } from 'mastodon/initial_state';
import { useAppDispatch, useAppSelector } from 'mastodon/store';

View File

@@ -1,5 +1,5 @@
import PropTypes from 'prop-types';
import { useRef, useCallback, useEffect } from 'react';
import { useCallback, useEffect } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
@@ -11,8 +11,8 @@ import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?re
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
import { mountConversations, unmountConversations, expandConversations } from 'mastodon/actions/conversations';
import { connectDirectStream } from 'mastodon/actions/streaming';
import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { ConversationsList } from './components/conversations_list';
@@ -21,7 +21,6 @@ const messages = defineMessages({
});
const DirectTimeline = ({ columnId, multiColumn }) => {
const columnRef = useRef();
const intl = useIntl();
const dispatch = useDispatch();
const pinned = !!columnId;
@@ -38,10 +37,6 @@ const DirectTimeline = ({ columnId, multiColumn }) => {
dispatch(moveColumn(columnId, dir));
}, [dispatch, columnId]);
const handleHeaderClick = useCallback(() => {
columnRef.current.scrollTop();
}, [columnRef]);
useEffect(() => {
dispatch(mountConversations());
dispatch(expandConversations());
@@ -55,16 +50,16 @@ const DirectTimeline = ({ columnId, multiColumn }) => {
}, [dispatch]);
return (
<Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}>
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon='at'
iconComponent={AlternateEmailIcon}
title={intl.formatMessage(messages.title)}
onPin={handlePin}
onMove={handleMove}
onClick={handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
scrollTopOnClick
/>
<ConversationsList

View File

@@ -1,5 +1,5 @@
import type { ChangeEventHandler } from 'react';
import { useCallback, useEffect, useRef } from 'react';
import { useCallback, useEffect } from 'react';
import { defineMessages, useIntl } from 'react-intl';
@@ -7,6 +7,8 @@ import { List as ImmutableList } from 'immutable';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
import {
addColumn,
@@ -15,9 +17,6 @@ import {
changeColumnParams,
} from 'mastodon/actions/columns';
import { fetchDirectory, expandDirectory } from 'mastodon/actions/directory';
import { Column } from 'mastodon/components/column';
import type { ColumnRef } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { LoadMore } from 'mastodon/components/load_more';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import { RadioButton } from 'mastodon/components/radio_button';
@@ -49,8 +48,6 @@ export const Directory: React.FC<{
const intl = useIntl();
const dispatch = useAppDispatch();
const column = useRef<ColumnRef>(null);
const [orderParam, setOrderParam] = useSearchParam('order');
const [localParam, setLocalParam] = useSearchParam('local');
@@ -98,10 +95,6 @@ export const Directory: React.FC<{
[dispatch, columnId],
);
const handleHeaderClick = useCallback(() => {
column.current?.scrollTop();
}, []);
const handleChangeOrder = useCallback<ChangeEventHandler<HTMLInputElement>>(
(e) => {
if (columnId) {
@@ -194,7 +187,6 @@ export const Directory: React.FC<{
return (
<Column
bindToDocument={!multiColumn}
ref={column}
label={intl.formatMessage(messages.title)}
>
<ColumnHeader
@@ -203,9 +195,9 @@ export const Directory: React.FC<{
title={intl.formatMessage(messages.title)}
onPin={handlePin}
onMove={handleMove}
onClick={handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
scrollTopOnClick
/>
{multiColumn && !pinned ? (

View File

@@ -1,14 +1,13 @@
import { useEffect, useRef, useCallback, useState } from 'react';
import { useEffect, useCallback, useState } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react';
import { apiGetDomainBlocks } from 'mastodon/api/domain_blocks';
import { Column } from 'mastodon/components/column';
import type { ColumnRef } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { Domain } from 'mastodon/components/domain';
import ScrollableList from 'mastodon/components/scrollable_list';
@@ -22,7 +21,6 @@ const Blocks: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
const [loading, setLoading] = useState(true);
const [next, setNext] = useState<string | undefined>();
const hasMore = !!next;
const columnRef = useRef<ColumnRef>(null);
useEffect(() => {
void apiGetDomainBlocks()
@@ -58,10 +56,6 @@ const Blocks: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
});
}, [setLoading, setDomains, setNext, next]);
const handleHeaderClick = useCallback(() => {
columnRef.current?.scrollTop();
}, []);
const handleUnblock = useCallback((domain: string) => {
setDomains((prev) => prev.filter((d) => d !== domain));
}, []);
@@ -76,16 +70,15 @@ const Blocks: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
return (
<Column
bindToDocument={!multiColumn}
ref={columnRef}
label={intl.formatMessage(messages.heading)}
>
<ColumnHeader
icon='ban'
iconComponent={BlockIcon}
title={intl.formatMessage(messages.heading)}
onClick={handleHeaderClick}
multiColumn={multiColumn}
showBackButton
scrollTopOnClick
/>
<ScrollableList

View File

@@ -1,15 +1,12 @@
import { useCallback, useRef } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { NavLink, Switch, Route } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import TrendingUpIcon from '@/material-icons/400-24px/trending_up.svg?react';
import { Column } from 'mastodon/components/column';
import type { ColumnRef } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { SymbolLogo } from 'mastodon/components/logo';
import { Search } from 'mastodon/features/compose/components/search';
import { useBreakpoint } from 'mastodon/features/ui/hooks/useBreakpoint';
@@ -27,25 +24,19 @@ const messages = defineMessages({
const Explore: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
const { signedIn } = useIdentity();
const intl = useIntl();
const columnRef = useRef<ColumnRef>(null);
const logoRequired = useBreakpoint('full');
const handleHeaderClick = useCallback(() => {
columnRef.current?.scrollTop();
}, []);
return (
<Column
bindToDocument={!multiColumn}
ref={columnRef}
label={intl.formatMessage(messages.title)}
>
<ColumnHeader
icon={'explore'}
iconComponent={logoRequired ? SymbolLogo : TrendingUpIcon}
title={intl.formatMessage(messages.title)}
onClick={handleHeaderClick}
multiColumn={multiColumn}
scrollTopOnClick
/>
<div className='explore__search-header'>

View File

@@ -1,18 +1,17 @@
import { useEffect, useRef, useCallback } from 'react';
import { useEffect, useCallback } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
import {
fetchFavouritedStatuses,
expandFavouritedStatuses,
} from 'mastodon/actions/favourites';
import { Column } from 'mastodon/components/column';
import type { ColumnRef } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import StatusList from 'mastodon/components/status_list';
import { getStatusList } from 'mastodon/selectors';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
@@ -27,7 +26,6 @@ const Favourites: React.FC<{ columnId: string; multiColumn: boolean }> = ({
}) => {
const dispatch = useAppDispatch();
const intl = useIntl();
const columnRef = useRef<ColumnRef>(null);
const statusIds = useAppSelector((state) =>
getStatusList(state, 'favourites'),
);
@@ -58,10 +56,6 @@ const Favourites: React.FC<{ columnId: string; multiColumn: boolean }> = ({
[dispatch, columnId],
);
const handleHeaderClick = useCallback(() => {
columnRef.current?.scrollTop();
}, []);
const handleLoadMore = useCallback(() => {
dispatch(expandFavouritedStatuses());
}, [dispatch]);
@@ -78,7 +72,6 @@ const Favourites: React.FC<{ columnId: string; multiColumn: boolean }> = ({
return (
<Column
bindToDocument={!multiColumn}
ref={columnRef}
label={intl.formatMessage(messages.heading)}
>
<ColumnHeader
@@ -87,9 +80,9 @@ const Favourites: React.FC<{ columnId: string; multiColumn: boolean }> = ({
title={intl.formatMessage(messages.heading)}
onPin={handlePin}
onMove={handleMove}
onClick={handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
scrollTopOnClick
/>
<StatusList

View File

@@ -13,12 +13,12 @@ import { debounce } from 'lodash';
import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
import { fetchFavourites, expandFavourites } from 'mastodon/actions/interactions';
import { Account } from 'mastodon/components/account';
import ColumnHeader from 'mastodon/components/column_header';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { Icon } from 'mastodon/components/icon';
import { injectIntl } from '@/mastodon/components/intl';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import ScrollableList from 'mastodon/components/scrollable_list';
import Column from 'mastodon/features/ui/components/column';
const messages = defineMessages({
refresh: { id: 'refresh', defaultMessage: 'Refresh' },

View File

@@ -1,5 +1,5 @@
import PropTypes from 'prop-types';
import { useRef, useCallback, useEffect } from 'react';
import { useCallback, useEffect } from 'react';
import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
@@ -12,13 +12,13 @@ import { addColumn } from 'mastodon/actions/columns';
import { changeSetting } from 'mastodon/actions/settings';
import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { localLiveFeedAccess, remoteLiveFeedAccess, domain } from 'mastodon/initial_state';
import { canViewFeed } from 'mastodon/permissions';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import SettingToggle from '../notifications/components/setting_toggle';
import StatusListContainer from '../ui/containers/status_list_container';
@@ -62,7 +62,6 @@ const Firehose = ({ feedType, multiColumn }) => {
const dispatch = useAppDispatch();
const intl = useIntl();
const { signedIn, permissions } = useIdentity();
const columnRef = useRef(null);
const onlyMedia = useAppSelector((state) => state.getIn(['settings', 'firehose', 'onlyMedia'], false));
const hasUnread = useAppSelector((state) => state.getIn(['timelines', `${feedType}${onlyMedia ? ':media' : ''}`, 'unread'], 0) > 0);
@@ -101,8 +100,6 @@ const Firehose = ({ feedType, multiColumn }) => {
[dispatch, onlyMedia, feedType],
);
const handleHeaderClick = useCallback(() => columnRef.current?.scrollTop(), []);
useEffect(() => {
let disconnect;
@@ -180,15 +177,15 @@ const Firehose = ({ feedType, multiColumn }) => {
}
return (
<Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}>
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon='globe'
iconComponent={PublicIcon}
active={hasUnread}
title={intl.formatMessage(title)}
onPin={handlePin}
onClick={handleHeaderClick}
multiColumn={multiColumn}
scrollTopOnClick
>
<ColumnSettings />
</ColumnHeader>

View File

@@ -11,14 +11,15 @@ import { connect } from 'react-redux';
import { debounce } from 'lodash';
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
import { Column } from '@/mastodon/components/column';
import { injectIntl } from '@/mastodon/components/intl';
import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts';
import ScrollableList from '../../components/scrollable_list';
import { me } from '../../initial_state';
import Column from '../ui/components/column';
import AccountAuthorizeContainer from './containers/account_authorize_container';
import { ColumnHeader } from '@/mastodon/components/column/header';
const messages = defineMessages({
heading: { id: 'column.follow_requests', defaultMessage: 'Follow requests' },
@@ -69,7 +70,8 @@ class FollowRequests extends ImmutablePureComponent {
);
return (
<Column bindToDocument={!multiColumn} icon='user-plus' iconComponent={PersonAddIcon} heading={intl.formatMessage(messages.heading)} alwaysShowBackButton>
<Column bindToDocument={!multiColumn}>
<ColumnHeader icon='user-plus' iconComponent={PersonAddIcon} title={intl.formatMessage(messages.heading)} showBackButton />
<ScrollableList
scrollKey='follow_requests'
onLoadMore={this.handleLoadMore}

View File

@@ -1,4 +1,4 @@
import { useEffect, useCallback, useRef } from 'react';
import { useEffect, useCallback } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
@@ -6,6 +6,8 @@ import { isFulfilled } from '@reduxjs/toolkit';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import TagIcon from '@/material-icons/400-24px/tag.svg?react';
import {
fetchFollowedHashtags,
@@ -13,9 +15,6 @@ import {
} from 'mastodon/actions/tags_typed';
import type { ApiHashtagJSON } from 'mastodon/api_types/tags';
import { Button } from 'mastodon/components/button';
import { Column } from 'mastodon/components/column';
import type { ColumnRef } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { Hashtag } from 'mastodon/components/hashtag';
import ScrollableList from 'mastodon/components/scrollable_list';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
@@ -86,11 +85,6 @@ const FollowedTags: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
[dispatch],
);
const columnRef = useRef<ColumnRef>(null);
const handleHeaderClick = useCallback(() => {
columnRef.current?.scrollTop();
}, []);
const emptyMessage = (
<FormattedMessage
id='empty_column.followed_tags'
@@ -101,16 +95,15 @@ const FollowedTags: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
return (
<Column
bindToDocument={!multiColumn}
ref={columnRef}
label={intl.formatMessage(messages.heading)}
>
<ColumnHeader
icon='hashtag'
iconComponent={TagIcon}
title={intl.formatMessage(messages.heading)}
onClick={handleHeaderClick}
multiColumn={multiColumn}
showBackButton
scrollTopOnClick
/>
<ScrollableList

View File

@@ -3,7 +3,7 @@ import type { FC, ReactNode } from 'react';
import { AccountListItem } from '@/mastodon/components/account_list_item';
import { Column } from '@/mastodon/components/column';
import { ColumnBackButton } from '@/mastodon/components/column_back_button';
import { ColumnBackButton } from '@/mastodon/components/column/back_button';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import ScrollableList from '@/mastodon/components/scrollable_list';
import { BundleColumnError } from '@/mastodon/features/ui/components/bundle_column_error';

View File

@@ -2,7 +2,7 @@ import { useIntl } from 'react-intl';
import { Helmet } from '@unhead/react/helmet';
import { Column } from 'mastodon/components/column';
import { Column } from '@/mastodon/components/column';
import { NavigationPanel } from '../navigation_panel';
import { LinkFooter } from '../ui/components/link_footer';

View File

@@ -13,8 +13,8 @@ import TagIcon from '@/material-icons/400-24px/tag.svg?react';
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
import { connectHashtagStream } from 'mastodon/actions/streaming';
import { expandHashtagTimeline, clearTimeline } from 'mastodon/actions/timelines';
import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { remoteTopicFeedAccess, me, localTopicFeedAccess } from 'mastodon/initial_state';
@@ -90,10 +90,6 @@ class HashtagTimeline extends PureComponent {
dispatch(moveColumn(columnId, dir));
};
handleHeaderClick = () => {
this.column.scrollTop();
};
_subscribe (dispatch, id, tags = {}, local) {
const { signedIn } = this.props.identity;
@@ -156,10 +152,6 @@ class HashtagTimeline extends PureComponent {
this._unsubscribe();
}
setRef = c => {
this.column = c;
};
handleLoadMore = maxId => {
const { dispatch, params, local } = this.props;
const { id, tags } = params;
@@ -173,7 +165,7 @@ class HashtagTimeline extends PureComponent {
const pinned = !!columnId;
return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}>
<Column bindToDocument={!multiColumn} label={`#${id}`}>
<ColumnHeader
icon='hashtag'
iconComponent={TagIcon}
@@ -181,10 +173,10 @@ class HashtagTimeline extends PureComponent {
title={this.title()}
onPin={this.handlePin}
onMove={this.handleMove}
onClick={this.handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
showBackButton
scrollTopOnClick
>
{columnId && <ColumnSettingsContainer columnId={columnId} />}
</ColumnHeader>

View File

@@ -10,6 +10,8 @@ import { connect } from 'react-redux';
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { injectIntl } from '@/mastodon/components/intl';
import { SymbolLogo } from 'mastodon/components/logo';
import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/announcements';
@@ -20,8 +22,6 @@ import { withBreakpoint } from 'mastodon/features/ui/hooks/useBreakpoint';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { expandHomeTimeline } from '../../actions/timelines';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import StatusListContainer from '../ui/containers/status_list_container';
import { ColumnSettings } from './components/column_settings';
@@ -73,14 +73,6 @@ class HomeTimeline extends PureComponent {
dispatch(moveColumn(columnId, dir));
};
handleHeaderClick = () => {
this.column.scrollTop();
};
setRef = c => {
this.column = c;
};
handleLoadMore = maxId => {
this.props.dispatch(expandHomeTimeline({ maxId }));
};
@@ -150,7 +142,7 @@ class HomeTimeline extends PureComponent {
}
return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon='home'
iconComponent={matchesBreakpoint ? SymbolLogo : HomeIcon}
@@ -158,11 +150,11 @@ class HomeTimeline extends PureComponent {
title={intl.formatMessage(messages.title)}
onPin={this.handlePin}
onMove={this.handleMove}
onClick={this.handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
extraButton={announcementsButton}
appendContent={hasAnnouncements && showAnnouncements && <Announcements />}
scrollTopOnClick
>
<ColumnSettings />
</ColumnHeader>

View File

@@ -7,8 +7,8 @@ import { Helmet } from '@unhead/react/helmet';
import ImmutablePureComponent from 'react-immutable-pure-component';
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { injectIntl } from '@/mastodon/components/intl';
const messages = defineMessages({

View File

@@ -1,14 +1,13 @@
import { useRef, useEffect, useCallback } from 'react';
import { useEffect, useCallback } from 'react';
import { useParams } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import TrendingUpIcon from '@/material-icons/400-24px/trending_up.svg?react';
import { expandLinkTimeline } from 'mastodon/actions/timelines';
import { Column } from 'mastodon/components/column';
import type { ColumnRef } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import StatusListContainer from 'mastodon/features/ui/containers/status_list_container';
import type { Card } from 'mastodon/models/status';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
@@ -19,7 +18,6 @@ export const LinkTimeline: React.FC<{
const { url } = useParams<{ url: string }>();
const decodedUrl = url ? decodeURIComponent(url) : undefined;
const dispatch = useAppDispatch();
const columnRef = useRef<ColumnRef>(null);
const firstStatusId = useAppSelector((state) =>
decodedUrl
? (state.timelines.getIn([`link:${decodedUrl}`, 'items', 0]) as string)
@@ -31,10 +29,6 @@ export const LinkTimeline: React.FC<{
: undefined,
);
const handleHeaderClick = useCallback(() => {
columnRef.current?.scrollTop();
}, []);
const handleLoadMore = useCallback(
(maxId: string) => {
void dispatch(expandLinkTimeline(decodedUrl, { maxId }));
@@ -47,14 +41,14 @@ export const LinkTimeline: React.FC<{
}, [dispatch, decodedUrl]);
return (
<Column bindToDocument={!multiColumn} ref={columnRef} label={story?.title}>
<Column bindToDocument={!multiColumn} label={story?.title}>
<ColumnHeader
icon='explore'
iconComponent={TrendingUpIcon}
title={story?.title}
onClick={handleHeaderClick}
multiColumn={multiColumn}
showBackButton
scrollTopOnClick
/>
<StatusListContainer

View File

@@ -17,8 +17,8 @@ import { fetchList } from 'mastodon/actions/lists';
import { openModal } from 'mastodon/actions/modal';
import { connectListStream } from 'mastodon/actions/streaming';
import { expandListTimeline } from 'mastodon/actions/timelines';
import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { Icon } from 'mastodon/components/icon';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
@@ -59,10 +59,6 @@ class ListTimeline extends PureComponent {
dispatch(moveColumn(columnId, dir));
};
handleHeaderClick = () => {
this.column.scrollTop();
};
componentDidMount () {
const { dispatch } = this.props;
const { id } = this.props.params;
@@ -96,10 +92,6 @@ class ListTimeline extends PureComponent {
}
}
setRef = c => {
this.column = c;
};
handleLoadMore = maxId => {
const { id } = this.props.params;
this.props.dispatch(expandListTimeline(id, { maxId }));
@@ -133,7 +125,7 @@ class ListTimeline extends PureComponent {
}
return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={title}>
<Column bindToDocument={!multiColumn} label={title}>
<ColumnHeader
icon='list-ul'
iconComponent={ListAltIcon}
@@ -141,9 +133,9 @@ class ListTimeline extends PureComponent {
title={title}
onPin={this.handlePin}
onMove={this.handleMove}
onClick={this.handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
scrollTopOnClick
>
<div className='column-settings'>
<section className='column-header__links'>

View File

@@ -6,6 +6,8 @@ import { Link } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { NotSignedInIndicator } from '@/mastodon/components/not_signed_in_indicator';
import { useIdentity } from '@/mastodon/identity_context';
import AddIcon from '@/material-icons/400-24px/add.svg?react';
@@ -14,8 +16,6 @@ import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react';
import { fetchLists } from 'mastodon/actions/lists';
import { openModal } from 'mastodon/actions/modal';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { Dropdown } from 'mastodon/components/dropdown_menu';
import { Icon } from 'mastodon/components/icon';
import ScrollableList from 'mastodon/components/scrollable_list';

View File

@@ -6,6 +6,9 @@ import { useParams, Link } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { ColumnSearchHeader } from '@/mastodon/components/column/search_header';
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react';
import { fetchRelationships } from 'mastodon/actions/accounts';
@@ -22,9 +25,6 @@ import {
import { Avatar } from 'mastodon/components/avatar';
import { VerifiedBadge } from 'mastodon/components/badge';
import { Button } from 'mastodon/components/button';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { ColumnSearchHeader } from 'mastodon/components/column_search_header';
import { FollowersCounter } from 'mastodon/components/counters';
import { DisplayName } from 'mastodon/components/display_name';
import ScrollableList from 'mastodon/components/scrollable_list';

View File

@@ -8,6 +8,8 @@ import { isFulfilled } from '@reduxjs/toolkit';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { NotSignedInIndicator } from '@/mastodon/components/not_signed_in_indicator';
import { useIdentity } from '@/mastodon/identity_context';
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
@@ -19,8 +21,6 @@ import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
import type { RepliesPolicyType } from 'mastodon/api_types/lists';
import { Avatar } from 'mastodon/components/avatar';
import { AvatarGroup } from 'mastodon/components/avatar_group';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import {
SelectField,
TextInputField,

View File

@@ -11,13 +11,14 @@ import { connect } from 'react-redux';
import { debounce } from 'lodash';
import VolumeOffIcon from '@/material-icons/400-24px/volume_off.svg?react';
import { Account } from 'mastodon/components/account';
import { fetchMutes, expandMutes } from '@/mastodon/actions/mutes';
import { Account } from '@/mastodon/components/account';
import { Column } from '@/mastodon/components/column';
import { injectIntl } from '@/mastodon/components/intl';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import ScrollableList from '@/mastodon/components/scrollable_list';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { fetchMutes, expandMutes } from '../../actions/mutes';
import { LoadingIndicator } from '../../components/loading_indicator';
import ScrollableList from '../../components/scrollable_list';
import Column from '../ui/components/column';
const messages = defineMessages({
heading: { id: 'column.mutes', defaultMessage: 'Muted users' },
@@ -63,7 +64,8 @@ class Mutes extends ImmutablePureComponent {
const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />;
return (
<Column bindToDocument={!multiColumn} icon='volume-off' iconComponent={VolumeOffIcon} heading={intl.formatMessage(messages.heading)} alwaysShowBackButton>
<Column bindToDocument={!multiColumn}>
<ColumnHeader icon='volume-off' iconComponent={VolumeOffIcon} title={intl.formatMessage(messages.heading)} showBackButton />
<ScrollableList
scrollKey='mutes'
onLoadMore={this.handleLoadMore}

View File

@@ -2,13 +2,13 @@ import { useCallback } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { messages as columnHeaderMessages } from '@/mastodon/components/column/header';
import { useAppDispatch } from '@/mastodon/store';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import UnfoldMoreIcon from '@/material-icons/400-24px/unfold_more.svg?react';
import { requestBrowserPermission } from 'mastodon/actions/notifications';
import { changeSetting } from 'mastodon/actions/settings';
import { Button } from 'mastodon/components/button';
import { messages as columnHeaderMessages } from 'mastodon/components/column_header';
import { Icon } from 'mastodon/components/icon';
import { IconButton } from 'mastodon/components/icon_button';

View File

@@ -1,5 +1,5 @@
import PropTypes from 'prop-types';
import { useRef, useCallback, useEffect } from 'react';
import { useCallback, useEffect } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
@@ -17,8 +17,8 @@ import {
acceptNotificationRequest,
dismissNotificationRequest,
} from 'mastodon/actions/notification_requests';
import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { IconButton } from 'mastodon/components/icon_button';
import ScrollableList from 'mastodon/components/scrollable_list';
import { SensitiveMediaContextProvider } from 'mastodon/features/ui/util/sensitive_media_context';
@@ -32,7 +32,6 @@ const messages = defineMessages({
});
export const NotificationRequest = ({ multiColumn, params: { id } }) => {
const columnRef = useRef();
const intl = useIntl();
const dispatch = useDispatch();
const notificationRequest = useSelector(state => state.notificationRequests.current.item?.id === id ? state.notificationRequests.current.item : null);
@@ -43,10 +42,6 @@ export const NotificationRequest = ({ multiColumn, params: { id } }) => {
const hasMore = useSelector(state => !!state.notificationRequests.current.notifications.next);
const removed = useSelector(state => state.notificationRequests.current.removed);
const handleHeaderClick = useCallback(() => {
columnRef.current?.scrollTop();
}, [columnRef]);
const handleLoadMore = useCallback(() => {
dispatch(expandNotificationsForRequest({ accountId }));
}, [dispatch, accountId]);
@@ -89,14 +84,14 @@ export const NotificationRequest = ({ multiColumn, params: { id } }) => {
}
return (
<Column bindToDocument={!multiColumn} ref={columnRef} label={columnTitle}>
<Column bindToDocument={!multiColumn} label={columnTitle}>
<ColumnHeader
icon='archive'
iconComponent={InventoryIcon}
title={columnTitle}
onClick={handleHeaderClick}
multiColumn={multiColumn}
showBackButton
scrollTopOnClick
extraButton={!removed && (
<>
<IconButton className='column-header__button' iconComponent={DeleteIcon} onClick={handleDismiss} title={intl.formatMessage(messages.dismiss)} />

View File

@@ -1,5 +1,5 @@
import PropTypes from 'prop-types';
import { useRef, useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
@@ -19,8 +19,8 @@ import {
} from 'mastodon/actions/notification_requests';
import { changeSetting } from 'mastodon/actions/settings';
import { CheckBox } from 'mastodon/components/check_box';
import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { Icon } from 'mastodon/components/icon';
import ScrollableList from 'mastodon/components/scrollable_list';
import { Dropdown } from 'mastodon/components/dropdown_menu';
@@ -163,7 +163,6 @@ SelectRow.propTypes = {
};
export const NotificationRequests = ({ multiColumn }) => {
const columnRef = useRef();
const intl = useIntl();
const dispatch = useDispatch();
const isLoading = useSelector(state => state.notificationRequests.isLoading);
@@ -174,10 +173,6 @@ export const NotificationRequests = ({ multiColumn }) => {
const [checkedRequestIds, setCheckedRequestIds] = useState([]);
const [selectAllChecked, setSelectAllChecked] = useState(false);
const handleHeaderClick = useCallback(() => {
columnRef.current?.scrollTop();
}, [columnRef]);
const handleCheck = useCallback(id => {
setCheckedRequestIds(ids => {
const position = ids.indexOf(id);
@@ -213,14 +208,14 @@ export const NotificationRequests = ({ multiColumn }) => {
}, [dispatch]);
return (
<Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}>
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon='archive'
iconComponent={InventoryIcon}
title={intl.formatMessage(messages.title)}
onClick={handleHeaderClick}
multiColumn={multiColumn}
showBackButton
scrollTopOnClick
appendContent={
notificationRequests.length > 0 && (
<SelectRow selectionMode={selectionMode} setSelectionMode={setSelectionMode} selectAllChecked={selectAllChecked} toggleSelectAll={toggleSelectAll} selectedItems={checkedRequestIds} />

View File

@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
@@ -6,6 +6,16 @@ import { Helmet } from '@unhead/react/helmet';
import { isEqual } from 'lodash';
import { useDebouncedCallback } from 'use-debounce';
import {
addColumn,
removeColumn,
moveColumn,
} from '@/mastodon/actions/columns';
import { submitMarkers } from '@/mastodon/actions/markers';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { LoadGap } from '@/mastodon/components/load_gap';
import ScrollableList from '@/mastodon/components/scrollable_list';
import DoneAllIcon from '@/material-icons/400-24px/done_all.svg?react';
import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?react';
import {
@@ -33,13 +43,6 @@ import {
} from 'mastodon/selectors/settings';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { submitMarkers } from '../../actions/markers';
import { Column } from '../../components/column';
import type { ColumnRef } from '../../components/column';
import { ColumnHeader } from '../../components/column_header';
import { LoadGap } from '../../components/load_gap';
import ScrollableList from '../../components/scrollable_list';
import {
FilteredNotificationsBanner,
FilteredNotificationsIconButton,
@@ -96,8 +99,6 @@ export const Notifications: React.FC<{
selectNeedsNotificationPermission,
);
const columnRef = useRef<ColumnRef>(null);
// Keep track of mounted components for unread notification handling
useEffect(() => {
void dispatch(mountNotifications());
@@ -159,10 +160,6 @@ export const Notifications: React.FC<{
[dispatch, columnId],
);
const handleHeaderClick = useCallback(() => {
columnRef.current?.scrollTop();
}, []);
const handleMarkAsRead = useCallback(() => {
dispatch(markNotificationsAsRead());
void dispatch(submitMarkers({ immediate: true }));
@@ -255,7 +252,6 @@ export const Notifications: React.FC<{
return (
<Column
bindToDocument={!multiColumn}
ref={columnRef}
label={intl.formatMessage(messages.title)}
>
<ColumnHeader
@@ -265,10 +261,10 @@ export const Notifications: React.FC<{
title={intl.formatMessage(messages.title)}
onPin={handlePin}
onMove={handleMove}
onClick={handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
extraButton={extraButton}
scrollTopOnClick
>
<ColumnSettingsContainer />
</ColumnHeader>

View File

@@ -7,6 +7,9 @@ import { Link } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { useDebouncedCallback } from 'use-debounce';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { ColumnSearchHeader } from '@/mastodon/components/column/search_header';
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
import { fetchRelationships } from 'mastodon/actions/accounts';
import { importFetchedAccounts } from 'mastodon/actions/importer';
@@ -15,9 +18,6 @@ import { markAsPartial } from 'mastodon/actions/timelines';
import { apiRequest } from 'mastodon/api';
import type { ApiAccountJSON } from 'mastodon/api_types/accounts';
import { Account } from 'mastodon/components/account';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { ColumnSearchHeader } from 'mastodon/components/column_search_header';
import ScrollableList from 'mastodon/components/scrollable_list';
import { useAppSelector, useAppDispatch } from 'mastodon/store';

View File

@@ -7,14 +7,14 @@ import { useHistory } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import AddPhotoAlternateIcon from '@/material-icons/400-24px/add_photo_alternate.svg?react';
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import PersonIcon from '@/material-icons/400-24px/person.svg?react';
import { updateAccount } from 'mastodon/actions/accounts';
import { closeOnboarding } from 'mastodon/actions/onboarding';
import { Button } from 'mastodon/components/button';
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import {
TextAreaField,
TextInputField,

View File

@@ -9,12 +9,13 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
import { fetchPinnedStatuses } from '@/mastodon/actions/pin_statuses';
import { Column } from '@/mastodon/components/column';
import { injectIntl } from '@/mastodon/components/intl';
import { getStatusList } from 'mastodon/selectors';
import StatusList from '@/mastodon/components/status_list';
import { getStatusList } from '@/mastodon/selectors';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { fetchPinnedStatuses } from '../../actions/pin_statuses';
import StatusList from '../../components/status_list';
import Column from '../ui/components/column';
const messages = defineMessages({
heading: { id: 'column.pins', defaultMessage: 'Pinned post' },
@@ -39,19 +40,12 @@ class PinnedStatuses extends ImmutablePureComponent {
this.props.dispatch(fetchPinnedStatuses());
}
handleHeaderClick = () => {
this.column.scrollTop();
};
setRef = c => {
this.column = c;
};
render () {
const { intl, statusIds, hasMore, multiColumn } = this.props;
return (
<Column bindToDocument={!multiColumn} icon='thumb-tack' iconComponent={PushPinIcon} heading={intl.formatMessage(messages.heading)} ref={this.setRef} alwaysShowBackButton>
<Column bindToDocument={!multiColumn}>
<ColumnHeader icon='thumb-tack' iconComponent={PushPinIcon} title={intl.formatMessage(messages.heading)} showBackButton />
<StatusList
statusIds={statusIds}
scrollKey='pinned_statuses'

View File

@@ -4,10 +4,10 @@ import { FormattedMessage, useIntl, defineMessages } from 'react-intl';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { NavigationFocusTarget } from '@/mastodon/components/navigation_focus_target';
import { apiGetPrivacyPolicy } from 'mastodon/api/instance';
import type { ApiPrivacyPolicyJSON } from 'mastodon/api_types/instance';
import { Column } from 'mastodon/components/column';
import { FormattedDateWrapper } from 'mastodon/components/formatted_date';
import { Skeleton } from 'mastodon/components/skeleton';

View File

@@ -8,6 +8,8 @@ import { Helmet } from '@unhead/react/helmet';
import { connect } from 'react-redux';
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { injectIntl } from '@/mastodon/components/intl';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
@@ -17,8 +19,6 @@ import { canViewFeed } from 'mastodon/permissions';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { connectPublicStream } from '../../actions/streaming';
import { expandPublicTimeline } from '../../actions/timelines';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import StatusListContainer from '../ui/containers/status_list_container';
import ColumnSettingsContainer from './containers/column_settings_container';
@@ -73,10 +73,6 @@ class PublicTimeline extends PureComponent {
dispatch(moveColumn(columnId, dir));
};
handleHeaderClick = () => {
this.column.scrollTop();
};
componentDidMount () {
const { dispatch, onlyMedia, onlyRemote } = this.props;
const { signedIn } = this.props.identity;
@@ -113,10 +109,6 @@ class PublicTimeline extends PureComponent {
}
}
setRef = c => {
this.column = c;
};
handleLoadMore = maxId => {
const { dispatch, onlyMedia, onlyRemote } = this.props;
@@ -141,7 +133,7 @@ class PublicTimeline extends PureComponent {
);
return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.title)}>
<ColumnHeader
icon='globe'
iconComponent={PublicIcon}
@@ -149,9 +141,9 @@ class PublicTimeline extends PureComponent {
title={intl.formatMessage(messages.title)}
onPin={this.handlePin}
onMove={this.handleMove}
onClick={this.handleHeaderClick}
pinned={pinned}
multiColumn={multiColumn}
scrollTopOnClick
>
<ColumnSettingsContainer columnId={columnId} />
</ColumnHeader>

View File

@@ -6,17 +6,16 @@ import { List as ImmutableList } from 'immutable';
import { Helmet } from '@unhead/react/helmet';
import { fetchQuotes } from '@/mastodon/actions/interactions_typed';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { Icon } from '@/mastodon/components/icon';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import StatusList from '@/mastodon/components/status_list';
import { useIdentity } from '@/mastodon/identity_context';
import { domain } from '@/mastodon/initial_state';
import { useAppDispatch, useAppSelector } from '@/mastodon/store';
import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
import { fetchQuotes } from 'mastodon/actions/interactions_typed';
import { ColumnHeader } from 'mastodon/components/column_header';
import { Icon } from 'mastodon/components/icon';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import StatusList from 'mastodon/components/status_list';
import { useIdentity } from 'mastodon/identity_context';
import { domain } from 'mastodon/initial_state';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import Column from '../ui/components/column';
const messages = defineMessages({
refresh: { id: 'refresh', defaultMessage: 'Refresh' },

View File

@@ -11,15 +11,14 @@ import { connect } from 'react-redux';
import { debounce } from 'lodash';
import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
import { Account } from 'mastodon/components/account';
import { Icon } from 'mastodon/components/icon';
import { fetchReblogs, expandReblogs } from '@/mastodon/actions/interactions';
import { Account } from '@/mastodon/components/account';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { Icon } from '@/mastodon/components/icon';
import { injectIntl } from '@/mastodon/components/intl';
import { fetchReblogs, expandReblogs } from '../../actions/interactions';
import ColumnHeader from '../../components/column_header';
import { LoadingIndicator } from '../../components/loading_indicator';
import ScrollableList from '../../components/scrollable_list';
import Column from '../ui/components/column';
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
import ScrollableList from '@/mastodon/components/scrollable_list';
const messages = defineMessages({
refresh: { id: 'refresh', defaultMessage: 'Refresh' },

View File

@@ -1,9 +1,11 @@
import { useCallback, useEffect, useRef } from 'react';
import { useCallback, useEffect } from 'react';
import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import CollectionsIcon from '@/material-icons/400-24px/category.svg?react';
import FindInPageIcon from '@/material-icons/400-24px/find_in_page.svg?react';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
@@ -12,9 +14,6 @@ import TagIcon from '@/material-icons/400-24px/tag.svg?react';
import { submitSearch, expandSearch } from 'mastodon/actions/search';
import type { ApiSearchType } from 'mastodon/api_types/search';
import { Account } from 'mastodon/components/account';
import { Column } from 'mastodon/components/column';
import type { ColumnRef } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { CompatibilityHashtag as Hashtag } from 'mastodon/components/hashtag';
import { Icon } from 'mastodon/components/icon';
import ScrollableList from 'mastodon/components/scrollable_list';
@@ -72,7 +71,6 @@ const typeFromParam = (param?: string): SearchType => {
export const SearchResults: React.FC<{ multiColumn: boolean }> = ({
multiColumn,
}) => {
const columnRef = useRef<ColumnRef>(null);
const intl = useIntl();
const [q] = useSearchParam('q');
const [type, setType] = useSearchParam('type');
@@ -93,10 +91,6 @@ export const SearchResults: React.FC<{ multiColumn: boolean }> = ({
}
}, [dispatch, trimmedValue, mappedType]);
const handleHeaderClick = useCallback(() => {
columnRef.current?.scrollTop();
}, []);
const handleSelectAll = useCallback(() => {
setType(null);
}, [setType]);
@@ -243,15 +237,14 @@ export const SearchResults: React.FC<{ multiColumn: boolean }> = ({
return (
<Column
bindToDocument={!multiColumn}
ref={columnRef}
label={intl.formatMessage(messages.title, { q })}
>
<ColumnHeader
icon={'search'}
iconComponent={SearchIcon}
title={intl.formatMessage(messages.title, { q })}
onClick={handleHeaderClick}
multiColumn={multiColumn}
scrollTopOnClick
appendContent={
<>
<div className='explore__search-header'>

View File

@@ -13,6 +13,8 @@ import { connect } from 'react-redux';
import VisibilityIcon from '@/material-icons/400-24px/visibility.svg?react';
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import { Hotkeys } from 'mastodon/components/hotkeys';
import { Icon } from 'mastodon/components/icon';
import { injectIntl } from '@/mastodon/components/intl';
@@ -59,13 +61,11 @@ import {
undoStatusTranslation,
} from '../../actions/statuses';
import { setStatusQuotePolicy } from '../../actions/statuses_typed';
import ColumnHeader from '../../components/column_header';
import { textForScreenReader, defaultMediaVisibility } from '../../components/status';
import { StatusQuoteManager } from '../../components/status_quoted';
import { deleteModal } from '../../initial_state';
import { makeGetStatus, makeGetPictureInPicture } from '../../selectors';
import { getAncestorsIds, getDescendantsIds } from 'mastodon/selectors/contexts';
import Column from '../ui/components/column';
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../ui/util/fullscreen';
import ActionBar from './components/action_bar';

View File

@@ -11,10 +11,10 @@ import { Link, useParams } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { NavigationFocusTarget } from '@/mastodon/components/navigation_focus_target';
import { apiGetTermsOfService } from 'mastodon/api/instance';
import type { ApiTermsOfServiceJSON } from 'mastodon/api_types/instance';
import { Column } from 'mastodon/components/column';
import { BundleColumnError } from 'mastodon/features/ui/components/bundle_column_error';
import { getColumnSkipLinkId } from '../ui/components/skip_links';

View File

@@ -1,33 +0,0 @@
import { render, fireEvent, screen } from '@/testing/rendering';
import Column from '../column';
import { FocusTargetProvider } from '@/mastodon/components/navigation_focus_target';
const fakeIcon = () => <span />;
describe('<Column />', () => {
describe('<ColumnHeader /> click handler', () => {
it('runs the scroll animation if the column contains scrollable content', () => {
const scrollToMock = vi.fn();
const { container } = render(
<FocusTargetProvider>
<Column heading='notifications' icon='notifications' iconComponent={fakeIcon}>
<div className='scrollable' />
</Column>
</FocusTargetProvider>,
);
container.querySelector('.scrollable').scrollTo = scrollToMock;
fireEvent.click(screen.getByText('notifications'));
expect(scrollToMock).toHaveBeenCalledWith({ behavior: 'smooth', top: 0 });
});
it('does not try to scroll if there is no scrollable content', () => {
render(
<FocusTargetProvider>
<Column heading='notifications' icon='notifications' iconComponent={fakeIcon} />
</FocusTargetProvider>
);
fireEvent.click(screen.getByText('notifications'));
});
});
});

View File

@@ -5,10 +5,10 @@ import { Link } from 'react-router-dom';
import { Helmet } from '@unhead/react/helmet';
import { Column } from '@/mastodon/components/column';
import { CopyButton } from '@/mastodon/components/copy_button';
import { EmptyState } from '@/mastodon/components/empty_state';
import { Button } from 'mastodon/components/button';
import { Column } from 'mastodon/components/column';
import classes from './styles.module.scss';

View File

@@ -1,77 +0,0 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { debounce } from 'lodash';
import ColumnHeader from '../../../components/column_header';
import { isMobile } from '../../../is_mobile';
import { scrollTop } from '../../../scroll';
export default class Column extends PureComponent {
static propTypes = {
heading: PropTypes.string,
alwaysShowBackButton: PropTypes.bool,
icon: PropTypes.string,
iconComponent: PropTypes.func,
children: PropTypes.node,
active: PropTypes.bool,
hideHeadingOnMobile: PropTypes.bool,
};
handleHeaderClick = () => {
const scrollable = this.node.querySelector('.scrollable');
if (!scrollable) {
return;
}
this._interruptScrollAnimation = scrollTop(scrollable);
};
scrollTop () {
const scrollable = this.node.querySelector('.scrollable');
if (!scrollable) {
return;
}
this._interruptScrollAnimation = scrollTop(scrollable);
}
handleScroll = debounce(() => {
if (typeof this._interruptScrollAnimation !== 'undefined') {
this._interruptScrollAnimation();
}
}, 200);
setRef = (c) => {
this.node = c;
};
render () {
const { heading, icon, iconComponent, children, active, hideHeadingOnMobile, alwaysShowBackButton } = this.props;
const showHeading = heading && (!hideHeadingOnMobile || (hideHeadingOnMobile && !isMobile(window.innerWidth)));
const columnHeaderId = showHeading && heading.replace(/ /g, '-');
const header = showHeading && (
<ColumnHeader icon={icon} iconComponent={iconComponent} active={active} title={heading} onClick={this.handleHeaderClick} columnHeaderId={columnHeaderId} showBackButton={alwaysShowBackButton} />
);
return (
<div
ref={this.setRef}
role='region'
aria-labelledby={columnHeaderId}
className='column'
onScroll={this.handleScroll}
>
{header}
{children}
</div>
);
}
}

View File

@@ -1,6 +1,6 @@
import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import type { Props as ColumnHeaderProps } from 'mastodon/components/column_header';
import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import type { ColumnHeaderProps } from '@/mastodon/components/column/header';
export const ColumnLoading: React.FC<ColumnHeaderProps> = (otherProps) => (
<Column>

View File

@@ -1,17 +1,16 @@
import {
Children,
cloneElement,
createContext,
forwardRef,
isValidElement,
useCallback,
useContext,
} from 'react';
import classNames from 'classnames';
import type { List, Record } from 'immutable';
import { ColumnIndexContext } from '@/mastodon/components/column/context';
import { useAppSelector } from '@/mastodon/store';
import { Footer } from 'mastodon/features/custom_homepage/components/footer';
import { Header } from 'mastodon/features/custom_homepage/components/header';
@@ -69,9 +68,6 @@ const TabsBarPortal = () => {
return <div id='tabs-bar__portal' ref={setRef} />;
};
export const ColumnIndexContext = createContext(1);
export const useColumnIndexContext = () => useContext(ColumnIndexContext);
interface Column {
uuid: string;
id: keyof typeof componentMap;

View File

@@ -47,11 +47,15 @@ export const scrollRight = (node: Element, position: number) =>
}
});
export const scrollTop = (node: Element) =>
export const scrollTop = (
node: Element,
{ callback, ...rest }: IdleRequestOptions & { callback?: () => void } = {},
) =>
requestIdleCallback(() => {
if (isScrollBehaviorSupported) {
node.scrollTo({ top: 0, behavior: 'smooth' });
} else {
scroll(node, 'scrollTop', 0);
}
});
callback?.();
}, rest);