diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx b/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx
deleted file mode 100644
index b619ad66b2..0000000000
--- a/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx
+++ /dev/null
@@ -1,170 +0,0 @@
-import PropTypes from 'prop-types';
-import { Children, cloneElement, createContext, useContext, useCallback } from 'react';
-
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
-
-import { scrollRight } from '../../../scroll';
-import {
- Compose,
- Notifications,
- HomeTimeline,
- CommunityTimeline,
- PublicTimeline,
- HashtagTimeline,
- DirectTimeline,
- FavouritedStatuses,
- BookmarkedStatuses,
- ListTimeline,
- Directory,
-} from '../util/async-components';
-import { useColumnsContext } from '../util/columns_context';
-
-import Bundle from './bundle';
-import BundleColumnError from './bundle_column_error';
-import { ColumnLoading } from './column_loading';
-import { ComposePanel, RedirectToMobileComposeIfNeeded } from './compose_panel';
-import DrawerLoading from './drawer_loading';
-import { CollapsibleNavigationPanel } from 'flavours/glitch/features/navigation_panel';
-
-const componentMap = {
- 'COMPOSE': Compose,
- 'HOME': HomeTimeline,
- 'NOTIFICATIONS': Notifications,
- 'PUBLIC': PublicTimeline,
- 'REMOTE': PublicTimeline,
- 'COMMUNITY': CommunityTimeline,
- 'HASHTAG': HashtagTimeline,
- 'DIRECT': DirectTimeline,
- 'FAVOURITES': FavouritedStatuses,
- 'BOOKMARKS': BookmarkedStatuses,
- 'LIST': ListTimeline,
- 'DIRECTORY': Directory,
-};
-
-const TabsBarPortal = () => {
- const {setTabsBarElement} = useColumnsContext();
-
- const setRef = useCallback((element) => {
- if(element)
- setTabsBarElement(element);
- }, [setTabsBarElement]);
-
- return
;
-};
-
-// Simple context to allow column children to know which column they're in
-export const ColumnIndexContext = createContext(1);
-/**
- * @returns {number}
- */
-export const useColumnIndexContext = () => useContext(ColumnIndexContext);
-
-export default class ColumnsArea extends ImmutablePureComponent {
- static propTypes = {
- columns: ImmutablePropTypes.list.isRequired,
- isModalOpen: PropTypes.bool.isRequired,
- singleColumn: PropTypes.bool,
- children: PropTypes.node,
- };
-
- // Corresponds to (max-width: $no-gap-breakpoint - 1px) in SCSS
- mediaQuery = 'matchMedia' in window && window.matchMedia('(max-width: 1174px)');
-
- state = {
- renderComposePanel: !(this.mediaQuery && this.mediaQuery.matches),
- };
-
- componentDidMount() {
- if (this.mediaQuery) {
- if (this.mediaQuery.addEventListener) {
- this.mediaQuery.addEventListener('change', this.handleLayoutChange);
- } else {
- this.mediaQuery.addListener(this.handleLayoutChange);
- }
- this.setState({ renderComposePanel: !this.mediaQuery.matches });
- }
-
- this.isRtlLayout = document.getElementsByTagName('body')[0].classList.contains('rtl');
- }
-
- componentWillUnmount () {
- if (this.mediaQuery) {
- if (this.mediaQuery.removeEventListener) {
- this.mediaQuery.removeEventListener('change', this.handleLayoutChange);
- } else {
- this.mediaQuery.removeListener(this.handleLayoutChange);
- }
- }
- }
-
- handleChildrenContentChange() {
- if (!this.props.singleColumn) {
- const modifier = this.isRtlLayout ? -1 : 1;
- scrollRight(this.node, (this.node.scrollWidth - window.innerWidth) * modifier);
- }
- }
-
- handleLayoutChange = (e) => {
- this.setState({ renderComposePanel: !e.matches });
- };
-
- setRef = (node) => {
- this.node = node;
- };
-
- renderLoading = columnId => () => {
- return columnId === 'COMPOSE' ? : ;
- };
-
- renderError = (props) => {
- return ;
- };
-
- render () {
- const { columns, children, singleColumn, isModalOpen } = this.props;
- const { renderComposePanel } = this.state;
-
- if (singleColumn) {
- return (
-
-
-
- {renderComposePanel && }
-
-
-
-
-
-
-
-
- );
- }
-
- return (
-
- {columns.map((column, index) => {
- const params = column.get('params', null) === null ? null : column.get('params').toJS();
- const other = params && params.other ? params.other : {};
-
- return (
-
-
- {SpecificComponent => }
-
-
- );
- })}
-
-
- {Children.map(children, child => cloneElement(child, { multiColumn: true }))}
-
-
- );
- }
-
-}
diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.tsx b/app/javascript/flavours/glitch/features/ui/components/columns_area.tsx
new file mode 100644
index 0000000000..818fef2e02
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.tsx
@@ -0,0 +1,177 @@
+import {
+ Children,
+ cloneElement,
+ createContext,
+ forwardRef,
+ useCallback,
+ useContext,
+} from 'react';
+
+import classNames from 'classnames';
+
+import type { List, Record } from 'immutable';
+
+import { useAppSelector } from '@/flavours/glitch/store';
+import { CollapsibleNavigationPanel } from 'flavours/glitch/features/navigation_panel';
+
+import { useBreakpoint } from '../hooks/useBreakpoint';
+import {
+ Compose,
+ Notifications,
+ HomeTimeline,
+ CommunityTimeline,
+ PublicTimeline,
+ HashtagTimeline,
+ DirectTimeline,
+ FavouritedStatuses,
+ BookmarkedStatuses,
+ ListTimeline,
+ Directory,
+} from '../util/async-components';
+import { useColumnsContext } from '../util/columns_context';
+
+import Bundle from './bundle';
+import BundleColumnError from './bundle_column_error';
+import { ColumnLoading } from './column_loading';
+import { ComposePanel, RedirectToMobileComposeIfNeeded } from './compose_panel';
+import DrawerLoading from './drawer_loading';
+
+const componentMap = {
+ COMPOSE: Compose,
+ HOME: HomeTimeline,
+ NOTIFICATIONS: Notifications,
+ PUBLIC: PublicTimeline,
+ REMOTE: PublicTimeline,
+ COMMUNITY: CommunityTimeline,
+ HASHTAG: HashtagTimeline,
+ DIRECT: DirectTimeline,
+ FAVOURITES: FavouritedStatuses,
+ BOOKMARKS: BookmarkedStatuses,
+ LIST: ListTimeline,
+ DIRECTORY: Directory,
+} as const;
+
+const TabsBarPortal = () => {
+ const { setTabsBarElement } = useColumnsContext();
+
+ const setRef = useCallback(
+ (element: HTMLDivElement | null) => {
+ if (element) {
+ setTabsBarElement(element);
+ }
+ },
+ [setTabsBarElement],
+ );
+
+ return ;
+};
+
+export const ColumnIndexContext = createContext(1);
+export const useColumnIndexContext = () => useContext(ColumnIndexContext);
+
+interface Column {
+ uuid: string;
+ id: keyof typeof componentMap;
+ params?: null | Record<{ other?: unknown }>;
+}
+
+type FetchedComponent = React.FC<{
+ columnId?: string;
+ multiColumn?: boolean;
+ params: unknown;
+}>;
+
+export const ColumnsArea = forwardRef<
+ HTMLDivElement,
+ {
+ singleColumn?: boolean;
+ children: React.ReactElement | React.ReactElement[];
+ }
+>(({ children, singleColumn }, ref) => {
+ const renderComposePanel = !useBreakpoint('full');
+ const columns = useAppSelector((state) =>
+ (state.settings as Record<{ columns: List> }>).get(
+ 'columns',
+ ),
+ );
+ const isModalOpen = useAppSelector(
+ (state) => !state.modal.get('stack').isEmpty(),
+ );
+
+ if (singleColumn) {
+ return (
+
+
+
+ {renderComposePanel && }
+
+
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+ {columns.map((column, index) => {
+ const params = column.get('params')
+ ? column.get('params')?.toJS()
+ : null;
+ const other = params?.other ?? {};
+ const uuid = column.get('uuid');
+ const id = column.get('id');
+
+ return (
+
+
+ {(SpecificComponent: FetchedComponent) => (
+
+ )}
+
+
+ );
+ })}
+
+
+ {Children.map(children, (child) =>
+ cloneElement(child, { multiColumn: true }),
+ )}
+
+
+ );
+});
+
+ColumnsArea.displayName = 'ColumnsArea';
+
+const ErrorComponent = (props: { onRetry: () => void }) => {
+ return ;
+};
+
+const renderLoading = (columnId: string) => {
+ const LoadingComponent =
+ columnId === 'COMPOSE' ? : ;
+ return () => LoadingComponent;
+};
diff --git a/app/javascript/flavours/glitch/features/ui/containers/columns_area_container.js b/app/javascript/flavours/glitch/features/ui/containers/columns_area_container.js
deleted file mode 100644
index b11bcade5c..0000000000
--- a/app/javascript/flavours/glitch/features/ui/containers/columns_area_container.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { connect } from 'react-redux';
-
-import { openModal } from 'flavours/glitch/actions/modal';
-
-import ColumnsArea from '../components/columns_area';
-
-const mapStateToProps = state => ({
- columns: state.getIn(['settings', 'columns']),
- isModalOpen: !!state.get('modal').modalType,
-});
-
-const mapDispatchToProps = dispatch => ({
- openSettings (e) {
- e.preventDefault();
- e.stopPropagation();
- dispatch(openModal({
- modalType: 'SETTINGS',
- modalProps: {},
- }));
- },
-});
-
-export default connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })(ColumnsArea);
diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx
index 6668ecb492..f058f81a83 100644
--- a/app/javascript/flavours/glitch/features/ui/index.jsx
+++ b/app/javascript/flavours/glitch/features/ui/index.jsx
@@ -11,6 +11,7 @@ import { connect } from 'react-redux';
import Favico from 'favico.js';
import { debounce } from 'lodash';
+import { scrollRight } from '../../scroll';
import { focusApp, unfocusApp, changeLayout } from 'flavours/glitch/actions/app';
import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'flavours/glitch/actions/markers';
import { fetchNotifications } from 'flavours/glitch/actions/notification_groups';
@@ -37,7 +38,7 @@ import BundleColumnError from './components/bundle_column_error';
import { NavigationBar } from './components/navigation_bar';
import { UploadArea } from './components/upload_area';
import { HashtagMenuController } from './components/hashtag_menu_controller';
-import ColumnsAreaContainer from './containers/columns_area_container';
+import { ColumnsArea } from './components/columns_area';
import LoadingBarContainer from './containers/loading_bar_container';
import ModalContainer from './containers/modal_container';
import {
@@ -133,7 +134,7 @@ class SwitchingColumnsArea extends PureComponent {
componentDidUpdate (prevProps) {
if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) {
- this.node.handleChildrenContentChange();
+ this.handleChildrenContentChange();
}
if (prevProps.singleColumn !== this.props.singleColumn) {
@@ -142,6 +143,16 @@ class SwitchingColumnsArea extends PureComponent {
}
}
+ handleChildrenContentChange() {
+ if (!this.props.singleColumn) {
+ const isRtlLayout = document.getElementsByTagName('body')[0]
+ ?.classList.contains('rtl');
+ const modifier = isRtlLayout ? -1 : 1;
+
+ scrollRight(this.node, (this.node.scrollWidth - window.innerWidth) * modifier);
+ }
+ }
+
setRef = c => {
if (c) {
this.node = c;
@@ -189,7 +200,7 @@ class SwitchingColumnsArea extends PureComponent {
return (
-
+
{redirect}
@@ -269,7 +280,7 @@ class SwitchingColumnsArea extends PureComponent {
}
-
+
);
}