Add the ability to close the last column

This commit is contained in:
noellabo 2022-05-19 19:22:49 +09:00
parent 79dfb04f64
commit 2d68765088
19 changed files with 83 additions and 10 deletions

View file

@ -85,6 +85,7 @@ class Settings::PreferencesController < Settings::BaseController
:setting_post_reference_modal, :setting_post_reference_modal,
:setting_add_reference_modal, :setting_add_reference_modal,
:setting_unselect_reference_modal, :setting_unselect_reference_modal,
:setting_enable_empty_column,
notification_emails: %i(follow follow_request reblog favourite emoji_reaction status_reference mention digest report pending_account trending_tag), notification_emails: %i(follow follow_request reblog favourite emoji_reaction status_reference mention digest report pending_account trending_tag),
interactions: %i(must_be_follower must_be_following must_be_following_dm) interactions: %i(must_be_follower must_be_following must_be_following_dm)
) )

View file

@ -3,6 +3,7 @@ import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { enableEmptyColumn } from 'mastodon/initial_state';
export default class ColumnBackButton extends React.PureComponent { export default class ColumnBackButton extends React.PureComponent {
@ -22,6 +23,10 @@ export default class ColumnBackButton extends React.PureComponent {
} }
} }
handleCloseClick = () => {
this.context.router.history.push('/empty');
}
render () { render () {
const { multiColumn } = this.props; const { multiColumn } = this.props;
@ -32,8 +37,15 @@ export default class ColumnBackButton extends React.PureComponent {
</button> </button>
); );
const backButton = enableEmptyColumn ? (
<button onClick={this.handleCloseClick} className='column-back-button'>
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
<FormattedMessage id='column_close_button.label' defaultMessage='Close' />
</button>
) : component;
if (multiColumn) { if (multiColumn) {
return component; return backButton;
} else { } else {
// The portal container and the component may be rendered to the DOM in // The portal container and the component may be rendered to the DOM in
// the same React render pass, so the container might not be available at // the same React render pass, so the container might not be available at

View file

@ -2,16 +2,26 @@ import React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import ColumnBackButton from './column_back_button'; import ColumnBackButton from './column_back_button';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import { enableEmptyColumn } from 'mastodon/initial_state';
export default class ColumnBackButtonSlim extends ColumnBackButton { export default class ColumnBackButtonSlim extends ColumnBackButton {
render () { render () {
const { multiColumn } = this.props;
return ( return (
<div className='column-back-button--slim'> <div className='column-back-button--slim'>
{ multiColumn && enableEmptyColumn ? (
<div role='button' tabIndex='0' onClick={this.handleCloseClick} className='column-back-button column-back-button--slim-button'>
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
<FormattedMessage id='column_close_button.label' defaultMessage='Back' />
</div>
) : (
<div role='button' tabIndex='0' onClick={this.handleClick} className='column-back-button column-back-button--slim-button'> <div role='button' tabIndex='0' onClick={this.handleClick} className='column-back-button column-back-button--slim-button'>
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth /> <Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
<FormattedMessage id='column_back_button.label' defaultMessage='Back' /> <FormattedMessage id='column_back_button.label' defaultMessage='Back' />
</div> </div>
)}
</div> </div>
); );
} }

View file

@ -4,6 +4,7 @@ import { createPortal } from 'react-dom';
import classNames from 'classnames'; import classNames from 'classnames';
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import { enableEmptyColumn } from 'mastodon/initial_state';
const messages = defineMessages({ const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' }, show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
@ -71,6 +72,10 @@ class ColumnHeader extends React.PureComponent {
this.historyBack(); this.historyBack();
} }
handleCloseClick = () => {
this.context.router.history.push('/empty');
}
handleTransitionEnd = () => { handleTransitionEnd = () => {
this.setState({ animating: false }); this.setState({ animating: false });
} }
@ -128,7 +133,12 @@ class ColumnHeader extends React.PureComponent {
} }
if (!pinned && (multiColumn || showBackButton)) { if (!pinned && (multiColumn || showBackButton)) {
backButton = ( backButton = multiColumn && enableEmptyColumn ? (
<button onClick={this.handleCloseClick} className='column-header__back-button'>
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
<FormattedMessage id='column_close_button.label' defaultMessage='Close' />
</button>
) : (
<button onClick={this.handleBackClick} className='column-header__back-button'> <button onClick={this.handleBackClick} className='column-header__back-button'>
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth /> <Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
<FormattedMessage id='column_back_button.label' defaultMessage='Back' /> <FormattedMessage id='column_back_button.label' defaultMessage='Back' />

View file

@ -0,0 +1,10 @@
import React, { Fragment } from 'react';
import ImmutablePureComponent from 'react-immutable-pure-component';
export default class EmptyColumn extends ImmutablePureComponent {
render () {
return <Fragment />;
}
}

View file

@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { me, profile_directory, showTrends, enable_limited_timeline } from '../../initial_state'; import { me, profile_directory, showTrends, enable_limited_timeline, enableEmptyColumn } from '../../initial_state';
import { fetchFollowRequests } from 'mastodon/actions/accounts'; import { fetchFollowRequests } from 'mastodon/actions/accounts';
import { fetchFavouriteDomains } from 'mastodon/actions/favourite_domains'; import { fetchFavouriteDomains } from 'mastodon/actions/favourite_domains';
import { fetchFavouriteTags } from 'mastodon/actions/favourite_tags'; import { fetchFavouriteTags } from 'mastodon/actions/favourite_tags';
@ -19,6 +19,7 @@ import { getOrderedLists } from 'mastodon/features/ui/components/list_panel';
import { getOrderedDomains } from 'mastodon/features/ui/components/favourite_domain_panel'; import { getOrderedDomains } from 'mastodon/features/ui/components/favourite_domain_panel';
import { getOrderedTags } from 'mastodon/features/ui/components/favourite_tag_panel'; import { getOrderedTags } from 'mastodon/features/ui/components/favourite_tag_panel';
import TrendsContainer from './containers/trends_container'; import TrendsContainer from './containers/trends_container';
import ColumnBackButton from 'mastodon/components/column_back_button';
const messages = defineMessages({ const messages = defineMessages({
home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' }, home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
@ -270,6 +271,7 @@ class GettingStarted extends ImmutablePureComponent {
<Icon id='bars' className='column-header__icon' fixedWidth /> <Icon id='bars' className='column-header__icon' fixedWidth />
<FormattedMessage id='getting_started.heading' defaultMessage='Getting started' /> <FormattedMessage id='getting_started.heading' defaultMessage='Getting started' />
</button> </button>
{enableEmptyColumn && <div className='column-header__buttons'><ColumnBackButton multiColumn={multiColumn} /></div>}
</h1> </h1>
</div>} </div>}

View file

@ -21,6 +21,10 @@ export default class Header extends ImmutablePureComponent {
this.context.router.history.push(`/statuses/${this.props.status.get('id')}`); this.context.router.history.push(`/statuses/${this.props.status.get('id')}`);
}; };
handleStatusCloseClick = () => {
this.context.router.history.push('/empty');
}
render () { render () {
const { status, hideTabs } = this.props; const { status, hideTabs } = this.props;

View file

@ -65,8 +65,9 @@ import {
FollowRecommendations, FollowRecommendations,
Trends, Trends,
Suggestions, Suggestions,
EmptyColumn,
} from './util/async-components'; } from './util/async-components';
import { me } from '../../initial_state'; import { me, enableEmptyColumn } from '../../initial_state';
import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding'; import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
// Dummy import, to make sure that <Status /> ends up in the application bundle. // Dummy import, to make sure that <Status /> ends up in the application bundle.
@ -158,7 +159,7 @@ class SwitchingColumnsArea extends React.PureComponent {
render () { render () {
const { children, mobile } = this.props; const { children, mobile } = this.props;
const redirect = mobile ? <Redirect from='/' to='/timelines/home' exact /> : <Redirect from='/' to='/getting-started' exact />; const redirect = mobile ? <Redirect from='/' to='/timelines/home' exact /> : enableEmptyColumn ? <Redirect from='/' to='/empty' exact /> : <Redirect from='/' to='/getting-started' exact />;
return ( return (
<ColumnsAreaContainer ref={this.setRef} singleColumn={mobile}> <ColumnsAreaContainer ref={this.setRef} singleColumn={mobile}>
@ -211,6 +212,8 @@ class SwitchingColumnsArea extends React.PureComponent {
<WrappedRoute path='/lists' component={Lists} content={children} /> <WrappedRoute path='/lists' component={Lists} content={children} />
<WrappedRoute path='/circles' component={Circles} content={children} /> <WrappedRoute path='/circles' component={Circles} content={children} />
<WrappedRoute path='/empty' component={EmptyColumn} content={children} />
<WrappedRoute component={GenericNotFound} content={children} /> <WrappedRoute component={GenericNotFound} content={children} />
</WrappedSwitch> </WrappedSwitch>
</ColumnsAreaContainer> </ColumnsAreaContainer>

View file

@ -217,3 +217,7 @@ export function Trends () {
export function Suggestions () { export function Suggestions () {
return import(/* webpackChunkName: "features/suggestions" */'../../suggestions'); return import(/* webpackChunkName: "features/suggestions" */'../../suggestions');
} }
export function EmptyColumn () {
return import(/* webpackChunkName: "features/empty" */'../../empty');
}

View file

@ -49,5 +49,6 @@ export const new_features_policy = getMeta('new_features_policy');
export const enableStatusReference = getMeta('enable_status_reference'); export const enableStatusReference = getMeta('enable_status_reference');
export const maxReferences = initialState?.status_references?.max_references; export const maxReferences = initialState?.status_references?.max_references;
export const matchVisibilityOfReferences = getMeta('match_visibility_of_references'); export const matchVisibilityOfReferences = getMeta('match_visibility_of_references');
export const enableEmptyColumn = getMeta('enable_empty_column');
export default initialState; export default initialState;

View file

@ -108,6 +108,7 @@
"column.public": "Federated timeline", "column.public": "Federated timeline",
"column_back_button.label": "Back", "column_back_button.label": "Back",
"column_back_button_to_pots.label": "Back to post detail", "column_back_button_to_pots.label": "Back to post detail",
"column_close_button.label": "Close",
"column_header.hide_settings": "Hide settings", "column_header.hide_settings": "Hide settings",
"column_header.moveLeft_settings": "Move column to the left", "column_header.moveLeft_settings": "Move column to the left",
"column_header.moveRight_settings": "Move column to the right", "column_header.moveRight_settings": "Move column to the right",

View file

@ -108,6 +108,7 @@
"column.public": "連合タイムライン", "column.public": "連合タイムライン",
"column_back_button.label": "戻る", "column_back_button.label": "戻る",
"column_back_button_to_pots.label": "投稿の詳細に戻る", "column_back_button_to_pots.label": "投稿の詳細に戻る",
"column_close_button.label": "閉じる",
"column_header.hide_settings": "設定を隠す", "column_header.hide_settings": "設定を隠す",
"column_header.moveLeft_settings": "カラムを左に移動する", "column_header.moveLeft_settings": "カラムを左に移動する",
"column_header.moveRight_settings": "カラムを右に移動する", "column_header.moveRight_settings": "カラムを右に移動する",

View file

@ -79,6 +79,7 @@ class UserSettingsDecorator
user.settings['enable_status_reference'] = enable_status_reference_preference if change?('setting_enable_status_reference') user.settings['enable_status_reference'] = enable_status_reference_preference if change?('setting_enable_status_reference')
user.settings['match_visibility_of_references'] = match_visibility_of_references_preference if change?('setting_match_visibility_of_references') user.settings['match_visibility_of_references'] = match_visibility_of_references_preference if change?('setting_match_visibility_of_references')
user.settings['hexagon_avatar'] = hexagon_avatar_preference if change?('setting_hexagon_avatar') user.settings['hexagon_avatar'] = hexagon_avatar_preference if change?('setting_hexagon_avatar')
user.settings['enable_empty_column'] = enable_empty_column_preference if change?('setting_enable_empty_column')
end end
def merged_notification_emails def merged_notification_emails
@ -281,6 +282,10 @@ end
boolean_cast_setting 'setting_match_visibility_of_references' boolean_cast_setting 'setting_match_visibility_of_references'
end end
def enable_empty_column_preference
boolean_cast_setting 'setting_enable_empty_column'
end
def boolean_cast_setting(key) def boolean_cast_setting(key)
ActiveModel::Type::Boolean.new.cast(settings[key]) ActiveModel::Type::Boolean.new.cast(settings[key])
end end

View file

@ -136,7 +136,7 @@ class User < ApplicationRecord
:theme_instance_ticker, :theme_instance_ticker,
:enable_status_reference, :match_visibility_of_references, :enable_status_reference, :match_visibility_of_references,
:post_reference_modal, :add_reference_modal, :unselect_reference_modal, :post_reference_modal, :add_reference_modal, :unselect_reference_modal,
:hexagon_avatar, :hexagon_avatar, :enable_empty_column,
to: :settings, prefix: :setting, allow_nil: false to: :settings, prefix: :setting, allow_nil: false

View file

@ -63,6 +63,7 @@ class InitialStateSerializer < ActiveModel::Serializer
store[:post_reference_modal] = object.current_account.user.setting_post_reference_modal store[:post_reference_modal] = object.current_account.user.setting_post_reference_modal
store[:add_reference_modal] = object.current_account.user.setting_add_reference_modal store[:add_reference_modal] = object.current_account.user.setting_add_reference_modal
store[:unselect_reference_modal] = object.current_account.user.setting_unselect_reference_modal store[:unselect_reference_modal] = object.current_account.user.setting_unselect_reference_modal
store[:enable_empty_column] = object.current_account.user.setting_enable_empty_column
else else
store[:auto_play_gif] = Setting.auto_play_gif store[:auto_play_gif] = Setting.auto_play_gif

View file

@ -31,6 +31,9 @@
.fields-group .fields-group
= f.input :setting_advanced_layout, as: :boolean, wrapper: :with_label, hint: false = f.input :setting_advanced_layout, as: :boolean, wrapper: :with_label, hint: false
.fields-group
= f.input :setting_enable_empty_column, as: :boolean, wrapper: :with_label, fedibird_features: true
%h4= t 'appearance.animations_and_accessibility' %h4= t 'appearance.animations_and_accessibility'
.fields-group .fields-group

View file

@ -59,6 +59,7 @@ en:
setting_display_media_default: Hide media marked as sensitive setting_display_media_default: Hide media marked as sensitive
setting_display_media_hide_all: Always hide media setting_display_media_hide_all: Always hide media
setting_display_media_show_all: Always show media setting_display_media_show_all: Always show media
setting_enable_empty_column: The last column is initially hidden and can be closed at will
setting_enable_limited_timeline: Enable a limited home to display private and circle and direct message setting_enable_limited_timeline: Enable a limited home to display private and circle and direct message
setting_enable_reaction: Enable the reaction display on the timeline and display the reaction button setting_enable_reaction: Enable the reaction display on the timeline and display the reaction button
setting_enable_status_reference: Enable the feature where a post references another post setting_enable_status_reference: Enable the feature where a post references another post
@ -216,6 +217,7 @@ en:
setting_display_media_default: Default setting_display_media_default: Default
setting_display_media_hide_all: Hide all setting_display_media_hide_all: Hide all
setting_display_media_show_all: Show all setting_display_media_show_all: Show all
setting_enable_empty_column: Enable last column to be closed
setting_enable_limited_timeline: Enable limited timeline setting_enable_limited_timeline: Enable limited timeline
setting_enable_reaction: Enable reaction setting_enable_reaction: Enable reaction
setting_enable_status_reference: Enable reference setting_enable_status_reference: Enable reference

View file

@ -59,6 +59,7 @@ ja:
setting_display_media_default: 閲覧注意としてマークされたメディアは隠す setting_display_media_default: 閲覧注意としてマークされたメディアは隠す
setting_display_media_hide_all: メディアを常に隠す setting_display_media_hide_all: メディアを常に隠す
setting_display_media_show_all: メディアを常に表示する setting_display_media_show_all: メディアを常に表示する
setting_enable_empty_column: 最後のカラムを初期状態で非表示にし、任意に閉じられるようにします
setting_enable_limited_timeline: フォロワー限定・サークル・ダイレクトメッセージを表示する限定ホームを有効にします setting_enable_limited_timeline: フォロワー限定・サークル・ダイレクトメッセージを表示する限定ホームを有効にします
setting_enable_reaction: タイムラインでリアクションの表示を有効にし、リアクションボタンを表示する setting_enable_reaction: タイムラインでリアクションの表示を有効にし、リアクションボタンを表示する
setting_enable_status_reference: 投稿が別の投稿を参照する機能を有効にします setting_enable_status_reference: 投稿が別の投稿を参照する機能を有効にします
@ -216,6 +217,7 @@ ja:
setting_display_media_default: 標準 setting_display_media_default: 標準
setting_display_media_hide_all: 非表示 setting_display_media_hide_all: 非表示
setting_display_media_show_all: 表示 setting_display_media_show_all: 表示
setting_enable_empty_column: 最終カラムを閉じられるようにする
setting_enable_limited_timeline: 限定ホームを有効にする setting_enable_limited_timeline: 限定ホームを有効にする
setting_enable_reaction: リアクションを有効にする setting_enable_reaction: リアクションを有効にする
setting_enable_status_reference: 参照を有効にする setting_enable_status_reference: 参照を有効にする

View file

@ -97,6 +97,7 @@ defaults: &defaults
add_reference_modal: true add_reference_modal: true
unselect_reference_modal: false unselect_reference_modal: false
hexagon_avatar: false hexagon_avatar: false
enable_empty_column: false
development: development:
<<: *defaults <<: *defaults