From 9bd012b7cb906e47c85add95d0eab562a45ad341 Mon Sep 17 00:00:00 2001 From: Stanislas Signoud Date: Thu, 13 Jul 2023 17:18:09 +0200 Subject: [PATCH] [Glitch] Change links in multi-column mode so tabs are open in single-column mode Port 5fad7bd58a9c2541938f63e73a6ebdbfd0045903 to glitch-soc Signed-off-by: Claire --- .../flavours/glitch/components/router.tsx | 23 ++++++++++++++++++ .../flavours/glitch/containers/mastodon.jsx | 7 +++--- .../ui/components/navigation_panel.jsx | 11 +++++++++ .../flavours/glitch/features/ui/index.jsx | 24 +++++++++++-------- .../features/ui/util/react_router_helpers.jsx | 18 ++++++++++---- .../flavours/glitch/initial_state.js | 7 ++++++ app/javascript/flavours/glitch/is_mobile.ts | 10 ++++---- 7 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 app/javascript/flavours/glitch/components/router.tsx diff --git a/app/javascript/flavours/glitch/components/router.tsx b/app/javascript/flavours/glitch/components/router.tsx new file mode 100644 index 000000000..018f66e15 --- /dev/null +++ b/app/javascript/flavours/glitch/components/router.tsx @@ -0,0 +1,23 @@ +import type { PropsWithChildren } from 'react'; +import React from 'react'; + +import type { History } from 'history'; +import { createBrowserHistory } from 'history'; +import { Router as OriginalRouter } from 'react-router'; + +import { layoutFromWindow } from 'flavours/glitch/is_mobile'; + +const browserHistory = createBrowserHistory(); +const originalPush = browserHistory.push.bind(browserHistory); + +browserHistory.push = (path: string, state: History.LocationState) => { + if (layoutFromWindow() === 'multi-column' && !path.startsWith('/deck')) { + originalPush(`/deck${path}`, state); + } else { + originalPush(path, state); + } +}; + +export const Router: React.FC = ({ children }) => { + return {children}; +}; diff --git a/app/javascript/flavours/glitch/containers/mastodon.jsx b/app/javascript/flavours/glitch/containers/mastodon.jsx index ae2eb0b60..1ab20d022 100644 --- a/app/javascript/flavours/glitch/containers/mastodon.jsx +++ b/app/javascript/flavours/glitch/containers/mastodon.jsx @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import { PureComponent } from 'react'; import { Helmet } from 'react-helmet'; -import { BrowserRouter, Route } from 'react-router-dom'; +import { Route } from 'react-router-dom'; import { Provider as ReduxProvider } from 'react-redux'; @@ -13,6 +13,7 @@ import { checkDeprecatedLocalSettings } from 'flavours/glitch/actions/local_sett import { hydrateStore } from 'flavours/glitch/actions/store'; import { connectUserStream } from 'flavours/glitch/actions/streaming'; import ErrorBoundary from 'flavours/glitch/components/error_boundary'; +import { Router } from 'flavours/glitch/components/router'; import UI from 'flavours/glitch/features/ui'; import initialState, { title as siteTitle } from 'flavours/glitch/initial_state'; import { IntlProvider } from 'flavours/glitch/locales'; @@ -79,11 +80,11 @@ export default class Mastodon extends PureComponent { - + - + diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx index 4c8c3e4a5..68e456e86 100644 --- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx +++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx @@ -5,6 +5,7 @@ import { defineMessages, injectIntl } from 'react-intl'; import NavigationPortal from 'flavours/glitch/components/navigation_portal'; import { timelinePreview, trendsEnabled } from 'flavours/glitch/initial_state'; +import { transientSingleColumn } from 'flavours/glitch/is_mobile'; import { preferencesLink } from 'flavours/glitch/utils/backend_links'; import ColumnLink from './column_link'; @@ -27,6 +28,7 @@ const messages = defineMessages({ followsAndFollowers: { id: 'navigation_bar.follows_and_followers', defaultMessage: 'Follows and followers' }, about: { id: 'navigation_bar.about', defaultMessage: 'About' }, search: { id: 'navigation_bar.search', defaultMessage: 'Search' }, + advancedInterface: { id: 'navigation_bar.advanced_interface', defaultMessage: 'Open in advanced web interface' }, app_settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' }, }); @@ -52,6 +54,15 @@ class NavigationPanel extends Component { return (
+ {transientSingleColumn && ( + <> + + {intl.formatMessage(messages.advancedInterface)} + +
+ + )} + {signedIn && ( <> diff --git a/app/javascript/flavours/glitch/features/ui/index.jsx b/app/javascript/flavours/glitch/features/ui/index.jsx index 7301b61ae..cbd2637af 100644 --- a/app/javascript/flavours/glitch/features/ui/index.jsx +++ b/app/javascript/flavours/glitch/features/ui/index.jsx @@ -133,11 +133,11 @@ class SwitchingColumnsArea extends PureComponent { static propTypes = { children: PropTypes.node, location: PropTypes.object, - mobile: PropTypes.bool, + singleColumn: PropTypes.bool, }; UNSAFE_componentWillMount () { - if (this.props.mobile) { + if (this.props.singleColumn) { document.body.classList.toggle('layout-single-column', true); document.body.classList.toggle('layout-multiple-columns', false); } else { @@ -151,9 +151,9 @@ class SwitchingColumnsArea extends PureComponent { this.node.handleChildrenContentChange(); } - if (prevProps.mobile !== this.props.mobile) { - document.body.classList.toggle('layout-single-column', this.props.mobile); - document.body.classList.toggle('layout-multiple-columns', !this.props.mobile); + if (prevProps.singleColumn !== this.props.singleColumn) { + document.body.classList.toggle('layout-single-column', this.props.singleColumn); + document.body.classList.toggle('layout-multiple-columns', !this.props.singleColumn); } } @@ -164,16 +164,17 @@ class SwitchingColumnsArea extends PureComponent { }; render () { - const { children, mobile } = this.props; + const { children, singleColumn } = this.props; const { signedIn } = this.context.identity; + const pathName = this.props.location.pathname; let redirect; if (signedIn) { - if (mobile) { + if (singleColumn) { redirect = ; } else { - redirect = ; + redirect = ; } } else if (singleUserMode && owner && initialState?.accounts[owner]) { redirect = ; @@ -184,10 +185,13 @@ class SwitchingColumnsArea extends PureComponent { } return ( - + {redirect} + {singleColumn ? : null} + {singleColumn && pathName.startsWith('/deck/') ? : null} + @@ -652,7 +656,7 @@ class UI extends Component {
- + {children} diff --git a/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.jsx b/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.jsx index 94847cd79..d0d034cbd 100644 --- a/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.jsx +++ b/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.jsx @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import * as React from 'react'; +import { Component, PureComponent, cloneElement, Children } from 'react'; import { Switch, Route } from 'react-router-dom'; @@ -10,14 +10,22 @@ import ColumnLoading from 'flavours/glitch/features/ui/components/column_loading import BundleContainer from 'flavours/glitch/features/ui/containers/bundle_container'; // Small wrapper to pass multiColumn to the route components -export class WrappedSwitch extends React.PureComponent { +export class WrappedSwitch extends PureComponent { + static contextTypes = { + router: PropTypes.object, + }; render () { const { multiColumn, children } = this.props; + const { location } = this.context.router.route; + + const decklessLocation = multiColumn && location.pathname.startsWith('/deck') + ? {...location, pathname: location.pathname.slice(5)} + : location; return ( - - {React.Children.map(children, child => React.cloneElement(child, { multiColumn }))} + + {Children.map(children, child => child ? cloneElement(child, { multiColumn }) : null)} ); } @@ -32,7 +40,7 @@ WrappedSwitch.propTypes = { // Small Wraper to extract the params from the route and pass // them to the rendered component, together with the content to // be rendered inside (the children) -export class WrappedRoute extends React.Component { +export class WrappedRoute extends Component { static propTypes = { component: PropTypes.func.isRequired, diff --git a/app/javascript/flavours/glitch/initial_state.js b/app/javascript/flavours/glitch/initial_state.js index 15ae7a904..f07c9fb71 100644 --- a/app/javascript/flavours/glitch/initial_state.js +++ b/app/javascript/flavours/glitch/initial_state.js @@ -88,6 +88,13 @@ * @property {string} default_content_type */ +/** @type {string} */ +const initialPath = document.querySelector("head meta[name=initialPath]")?.getAttribute("content") ?? ''; +/** @type {boolean} */ +export const hasMultiColumnPath = initialPath === '/' + || initialPath === '/getting-started' + || initialPath.startsWith('/deck'); + /** * @typedef InitialState * @property {Record} accounts diff --git a/app/javascript/flavours/glitch/is_mobile.ts b/app/javascript/flavours/glitch/is_mobile.ts index b3669e1bf..7f339e287 100644 --- a/app/javascript/flavours/glitch/is_mobile.ts +++ b/app/javascript/flavours/glitch/is_mobile.ts @@ -1,19 +1,21 @@ import { supportsPassiveEvents } from 'detect-passive-events'; -import { forceSingleColumn } from 'flavours/glitch/initial_state'; +import { forceSingleColumn, hasMultiColumnPath } from './initial_state'; const LAYOUT_BREAKPOINT = 630; export const isMobile = (width: number) => width <= LAYOUT_BREAKPOINT; +export const transientSingleColumn = !forceSingleColumn && !hasMultiColumnPath; + export type LayoutType = 'mobile' | 'single-column' | 'multi-column'; export const layoutFromWindow = (): LayoutType => { if (isMobile(window.innerWidth)) { return 'mobile'; - } else if (forceSingleColumn) { - return 'single-column'; - } else { + } else if (!forceSingleColumn && !transientSingleColumn) { return 'multi-column'; + } else { + return 'single-column'; } };