mirror of
https://github.com/lunaisnotaboy/mastodon.git
synced 2024-12-20 04:07:45 +00:00
[Glitch] Change links in multi-column mode so tabs are open in single-column mode
Port 5fad7bd58a
to glitch-soc
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
parent
0d61985713
commit
9bd012b7cb
23
app/javascript/flavours/glitch/components/router.tsx
Normal file
23
app/javascript/flavours/glitch/components/router.tsx
Normal file
|
@ -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<PropsWithChildren> = ({ children }) => {
|
||||||
|
return <OriginalRouter history={browserHistory}>{children}</OriginalRouter>;
|
||||||
|
};
|
|
@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
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';
|
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 { hydrateStore } from 'flavours/glitch/actions/store';
|
||||||
import { connectUserStream } from 'flavours/glitch/actions/streaming';
|
import { connectUserStream } from 'flavours/glitch/actions/streaming';
|
||||||
import ErrorBoundary from 'flavours/glitch/components/error_boundary';
|
import ErrorBoundary from 'flavours/glitch/components/error_boundary';
|
||||||
|
import { Router } from 'flavours/glitch/components/router';
|
||||||
import UI from 'flavours/glitch/features/ui';
|
import UI from 'flavours/glitch/features/ui';
|
||||||
import initialState, { title as siteTitle } from 'flavours/glitch/initial_state';
|
import initialState, { title as siteTitle } from 'flavours/glitch/initial_state';
|
||||||
import { IntlProvider } from 'flavours/glitch/locales';
|
import { IntlProvider } from 'flavours/glitch/locales';
|
||||||
|
@ -79,11 +80,11 @@ export default class Mastodon extends PureComponent {
|
||||||
<IntlProvider>
|
<IntlProvider>
|
||||||
<ReduxProvider store={store}>
|
<ReduxProvider store={store}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<BrowserRouter>
|
<Router>
|
||||||
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
|
||||||
<Route path='/' component={UI} />
|
<Route path='/' component={UI} />
|
||||||
</ScrollContext>
|
</ScrollContext>
|
||||||
</BrowserRouter>
|
</Router>
|
||||||
|
|
||||||
<Helmet defaultTitle={title} titleTemplate={`%s - ${title}`} />
|
<Helmet defaultTitle={title} titleTemplate={`%s - ${title}`} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
|
||||||
import NavigationPortal from 'flavours/glitch/components/navigation_portal';
|
import NavigationPortal from 'flavours/glitch/components/navigation_portal';
|
||||||
import { timelinePreview, trendsEnabled } from 'flavours/glitch/initial_state';
|
import { timelinePreview, trendsEnabled } from 'flavours/glitch/initial_state';
|
||||||
|
import { transientSingleColumn } from 'flavours/glitch/is_mobile';
|
||||||
import { preferencesLink } from 'flavours/glitch/utils/backend_links';
|
import { preferencesLink } from 'flavours/glitch/utils/backend_links';
|
||||||
|
|
||||||
import ColumnLink from './column_link';
|
import ColumnLink from './column_link';
|
||||||
|
@ -27,6 +28,7 @@ const messages = defineMessages({
|
||||||
followsAndFollowers: { id: 'navigation_bar.follows_and_followers', defaultMessage: 'Follows and followers' },
|
followsAndFollowers: { id: 'navigation_bar.follows_and_followers', defaultMessage: 'Follows and followers' },
|
||||||
about: { id: 'navigation_bar.about', defaultMessage: 'About' },
|
about: { id: 'navigation_bar.about', defaultMessage: 'About' },
|
||||||
search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
|
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' },
|
app_settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -52,6 +54,15 @@ class NavigationPanel extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='navigation-panel'>
|
<div className='navigation-panel'>
|
||||||
|
{transientSingleColumn && (
|
||||||
|
<>
|
||||||
|
<a href={`/deck${location.pathname}`} className='button button--block'>
|
||||||
|
{intl.formatMessage(messages.advancedInterface)}
|
||||||
|
</a>
|
||||||
|
<hr />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{signedIn && (
|
{signedIn && (
|
||||||
<>
|
<>
|
||||||
<ColumnLink transparent to='/home' icon='home' text={intl.formatMessage(messages.home)} />
|
<ColumnLink transparent to='/home' icon='home' text={intl.formatMessage(messages.home)} />
|
||||||
|
|
|
@ -133,11 +133,11 @@ class SwitchingColumnsArea extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
location: PropTypes.object,
|
location: PropTypes.object,
|
||||||
mobile: PropTypes.bool,
|
singleColumn: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
UNSAFE_componentWillMount () {
|
UNSAFE_componentWillMount () {
|
||||||
if (this.props.mobile) {
|
if (this.props.singleColumn) {
|
||||||
document.body.classList.toggle('layout-single-column', true);
|
document.body.classList.toggle('layout-single-column', true);
|
||||||
document.body.classList.toggle('layout-multiple-columns', false);
|
document.body.classList.toggle('layout-multiple-columns', false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -151,9 +151,9 @@ class SwitchingColumnsArea extends PureComponent {
|
||||||
this.node.handleChildrenContentChange();
|
this.node.handleChildrenContentChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevProps.mobile !== this.props.mobile) {
|
if (prevProps.singleColumn !== this.props.singleColumn) {
|
||||||
document.body.classList.toggle('layout-single-column', this.props.mobile);
|
document.body.classList.toggle('layout-single-column', this.props.singleColumn);
|
||||||
document.body.classList.toggle('layout-multiple-columns', !this.props.mobile);
|
document.body.classList.toggle('layout-multiple-columns', !this.props.singleColumn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,16 +164,17 @@ class SwitchingColumnsArea extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { children, mobile } = this.props;
|
const { children, singleColumn } = this.props;
|
||||||
const { signedIn } = this.context.identity;
|
const { signedIn } = this.context.identity;
|
||||||
|
const pathName = this.props.location.pathname;
|
||||||
|
|
||||||
let redirect;
|
let redirect;
|
||||||
|
|
||||||
if (signedIn) {
|
if (signedIn) {
|
||||||
if (mobile) {
|
if (singleColumn) {
|
||||||
redirect = <Redirect from='/' to='/home' exact />;
|
redirect = <Redirect from='/' to='/home' exact />;
|
||||||
} else {
|
} else {
|
||||||
redirect = <Redirect from='/' to='/getting-started' exact />;
|
redirect = <Redirect from='/' to='/deck/getting-started' exact />;
|
||||||
}
|
}
|
||||||
} else if (singleUserMode && owner && initialState?.accounts[owner]) {
|
} else if (singleUserMode && owner && initialState?.accounts[owner]) {
|
||||||
redirect = <Redirect from='/' to={`/@${initialState.accounts[owner].username}`} exact />;
|
redirect = <Redirect from='/' to={`/@${initialState.accounts[owner].username}`} exact />;
|
||||||
|
@ -184,10 +185,13 @@ class SwitchingColumnsArea extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ColumnsAreaContainer ref={this.setRef} singleColumn={mobile}>
|
<ColumnsAreaContainer ref={this.setRef} singleColumn={singleColumn}>
|
||||||
<WrappedSwitch>
|
<WrappedSwitch>
|
||||||
{redirect}
|
{redirect}
|
||||||
|
|
||||||
|
{singleColumn ? <Redirect from='/deck' to='/home' exact /> : null}
|
||||||
|
{singleColumn && pathName.startsWith('/deck/') ? <Redirect from={pathName} to={pathName.slice(5)} /> : null}
|
||||||
|
|
||||||
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
<WrappedRoute path='/getting-started' component={GettingStarted} content={children} />
|
||||||
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
|
||||||
<WrappedRoute path='/about' component={About} content={children} />
|
<WrappedRoute path='/about' component={About} content={children} />
|
||||||
|
@ -652,7 +656,7 @@ class UI extends Component {
|
||||||
|
|
||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
<SwitchingColumnsArea location={location} mobile={layout === 'mobile' || layout === 'single-column'}>
|
<SwitchingColumnsArea location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}>
|
||||||
{children}
|
{children}
|
||||||
</SwitchingColumnsArea>
|
</SwitchingColumnsArea>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as React from 'react';
|
import { Component, PureComponent, cloneElement, Children } from 'react';
|
||||||
|
|
||||||
import { Switch, Route } from 'react-router-dom';
|
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';
|
import BundleContainer from 'flavours/glitch/features/ui/containers/bundle_container';
|
||||||
|
|
||||||
// Small wrapper to pass multiColumn to the route components
|
// 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 () {
|
render () {
|
||||||
const { multiColumn, children } = this.props;
|
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 (
|
return (
|
||||||
<Switch>
|
<Switch location={decklessLocation}>
|
||||||
{React.Children.map(children, child => React.cloneElement(child, { multiColumn }))}
|
{Children.map(children, child => child ? cloneElement(child, { multiColumn }) : null)}
|
||||||
</Switch>
|
</Switch>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -32,7 +40,7 @@ WrappedSwitch.propTypes = {
|
||||||
// Small Wraper to extract the params from the route and pass
|
// Small Wraper to extract the params from the route and pass
|
||||||
// them to the rendered component, together with the content to
|
// them to the rendered component, together with the content to
|
||||||
// be rendered inside (the children)
|
// be rendered inside (the children)
|
||||||
export class WrappedRoute extends React.Component {
|
export class WrappedRoute extends Component {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
component: PropTypes.func.isRequired,
|
component: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -88,6 +88,13 @@
|
||||||
* @property {string} default_content_type
|
* @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
|
* @typedef InitialState
|
||||||
* @property {Record<string, Account>} accounts
|
* @property {Record<string, Account>} accounts
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
|
|
||||||
import { forceSingleColumn } from 'flavours/glitch/initial_state';
|
import { forceSingleColumn, hasMultiColumnPath } from './initial_state';
|
||||||
|
|
||||||
const LAYOUT_BREAKPOINT = 630;
|
const LAYOUT_BREAKPOINT = 630;
|
||||||
|
|
||||||
export const isMobile = (width: number) => width <= LAYOUT_BREAKPOINT;
|
export const isMobile = (width: number) => width <= LAYOUT_BREAKPOINT;
|
||||||
|
|
||||||
|
export const transientSingleColumn = !forceSingleColumn && !hasMultiColumnPath;
|
||||||
|
|
||||||
export type LayoutType = 'mobile' | 'single-column' | 'multi-column';
|
export type LayoutType = 'mobile' | 'single-column' | 'multi-column';
|
||||||
export const layoutFromWindow = (): LayoutType => {
|
export const layoutFromWindow = (): LayoutType => {
|
||||||
if (isMobile(window.innerWidth)) {
|
if (isMobile(window.innerWidth)) {
|
||||||
return 'mobile';
|
return 'mobile';
|
||||||
} else if (forceSingleColumn) {
|
} else if (!forceSingleColumn && !transientSingleColumn) {
|
||||||
return 'single-column';
|
|
||||||
} else {
|
|
||||||
return 'multi-column';
|
return 'multi-column';
|
||||||
|
} else {
|
||||||
|
return 'single-column';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue