-
- {renderComposePanel &&
}
+ if (place_tab_bar_at_bottom) {
+ return (
+
+
+
+ {renderComposePanel && }
+
+
+
+
+
+
+ {location.pathname !== '/publish' && }
+
+
+
+
+ );
+ } else {
+ return (
+
+
+
+ {renderComposePanel && }
+
+
+
+
+
+
-
-
-
-
-
- );
+ );
+ }
}
return (
diff --git a/app/javascript/mastodon/features/ui/components/tabs_bar.js b/app/javascript/mastodon/features/ui/components/tabs_bar.js
new file mode 100644
index 000000000..e919a1e25
--- /dev/null
+++ b/app/javascript/mastodon/features/ui/components/tabs_bar.js
@@ -0,0 +1,96 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { NavLink, withRouter } from 'react-router-dom';
+import { FormattedMessage, injectIntl } from 'react-intl';
+import NotificationsCounterIcon from './notifications_counter_icon';
+import { place_tab_bar_at_bottom, show_tab_bar_label } from 'mastodon/initial_state';
+import classNames from 'classnames';
+import { debounce } from 'lodash';
+import { isUserTouching } from '../../../is_mobile';
+import Icon from 'mastodon/components/icon';
+
+export const links = [
+
,
+
,
+
,
+
,
+
,
+
,
+];
+
+export function getIndex (path) {
+ return links.findIndex(link => link.props.to === path);
+}
+
+export function getLink (index) {
+ return links[index].props.to;
+}
+
+export default @injectIntl
+@withRouter
+class TabsBar extends React.PureComponent {
+
+ static propTypes = {
+ intl: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
+ };
+
+ setRef = ref => {
+ this.node = ref;
+ }
+
+ handleClick = (e) => {
+ // Only apply optimization for touch devices, which we assume are slower
+ // We thus avoid the 250ms delay for non-touch devices and the lag for touch devices
+ if (isUserTouching()) {
+ e.preventDefault();
+ e.persist();
+
+ requestAnimationFrame(() => {
+ const tabs = Array(...this.node.querySelectorAll('.tabs-bar__link'));
+ const currentTab = tabs.find(tab => tab.classList.contains('active'));
+ const nextTab = tabs.find(tab => tab.contains(e.target));
+ const { props: { to } } = links[Array(...this.node.childNodes).indexOf(nextTab)];
+
+
+ if (currentTab !== nextTab) {
+ if (currentTab) {
+ currentTab.classList.remove('active');
+ }
+
+ const listener = debounce(() => {
+ nextTab.removeEventListener('transitionend', listener);
+ this.props.history.push(to);
+ }, 50);
+
+ nextTab.addEventListener('transitionend', listener);
+ nextTab.classList.add('active');
+ }
+ });
+ }
+
+ }
+
+ static contextTypes = {
+ router: PropTypes.object.isRequired,
+ identity: PropTypes.object.isRequired,
+ };
+
+ static propTypes = {
+ intl: PropTypes.object.isRequired,
+ };
+
+ render () {
+ const { intl: { formatMessage } } = this.props;
+ return (
+
+
+
+
+
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js
index d04c4a42d..3477913f5 100644
--- a/app/javascript/mastodon/initial_state.js
+++ b/app/javascript/mastodon/initial_state.js
@@ -135,5 +135,7 @@ export const version = getMeta('version');
export const translationEnabled = getMeta('translation_enabled');
export const languages = initialState?.languages;
export const statusPageUrl = getMeta('status_page_url');
+export const place_tab_bar_at_bottom = getMeta('place_tab_bar_at_bottom');
+export const show_tab_bar_label = getMeta('show_tab_bar_label');
export default initialState;
diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss
index 81a040108..e30e42304 100644
--- a/app/javascript/styles/application.scss
+++ b/app/javascript/styles/application.scss
@@ -23,3 +23,5 @@
@import 'mastodon/dashboard';
@import 'mastodon/rtl';
@import 'mastodon/accessibility';
+
+@import 'plugin';
\ No newline at end of file
diff --git a/app/javascript/styles/plugin.scss b/app/javascript/styles/plugin.scss
new file mode 100644
index 000000000..bbeab2555
--- /dev/null
+++ b/app/javascript/styles/plugin.scss
@@ -0,0 +1,34 @@
+// ここから下タブバーの実装
+
+//投稿ボタン
+.columns-area__panels__main .button.bottom_right {
+ position: fixed;
+ right: 18px;
+ bottom: 65px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 64px;
+ height: 64px;
+ font-size: 24px;
+ border-radius: 50%;
+}
+
+.tab-ber-bottom .timeline{
+ width:100%;
+ height: calc(100% - 55px);
+ margin: 0 0 50px 0;
+}
+
+.tab-ber-bottom .navber{
+ width:100%;
+ bottom: 0;
+ position: fixed;
+}
+
+.tab-ber-bottom .navber .tabs-bar__wrapper{
+ bottom: 0;
+ width: 100%;
+}
+
+//ここまで下タブバーの実装
\ No newline at end of file
diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb
index 5fb7655a9..5a919e3d4 100644
--- a/app/lib/user_settings_decorator.rb
+++ b/app/lib/user_settings_decorator.rb
@@ -39,6 +39,7 @@ class UserSettingsDecorator
user.settings['trends'] = trends_preference if change?('setting_trends')
user.settings['crop_images'] = crop_images_preference if change?('setting_crop_images')
user.settings['always_send_emails'] = always_send_emails_preference if change?('setting_always_send_emails')
+ user.settings['place_tab_bar_at_bottom'] = place_tab_bar_at_bottom_preference if change?('setting_place_tab_bar_at_bottom')
end
def merged_notification_emails
@@ -137,6 +138,14 @@ class UserSettingsDecorator
boolean_cast_setting 'setting_always_send_emails'
end
+ def place_tab_bar_at_bottom_preference
+ boolean_cast_setting 'setting_place_tab_bar_at_bottom'
+ end
+
+ def show_tab_bar_label_preference
+ boolean_cast_setting 'setting_show_tab_bar_label'
+ end
+
def boolean_cast_setting(key)
ActiveModel::Type::Boolean.new.cast(settings[key])
end
diff --git a/app/models/user.rb b/app/models/user.rb
index efbe29f44..f03700783 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -136,6 +136,7 @@ class User < ApplicationRecord
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
:advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
:disable_swiping, :always_send_emails,
+ :place_tab_bar_at_bottom,
to: :settings, prefix: :setting, allow_nil: false
delegate :can?, to: :role
diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb
index 24417bca7..0f6ae274b 100644
--- a/app/serializers/initial_state_serializer.rb
+++ b/app/serializers/initial_state_serializer.rb
@@ -51,6 +51,7 @@ class InitialStateSerializer < ActiveModel::Serializer
store[:use_pending_items] = object.current_account.user.setting_use_pending_items
store[:trends] = Setting.trends && object.current_account.user.setting_trends
store[:crop_images] = object.current_account.user.setting_crop_images
+ store[:place_tab_bar_at_bottom] = object.current_account.user.setting_place_tab_bar_at_bottom
else
store[:auto_play_gif] = Setting.auto_play_gif
store[:display_media] = Setting.display_media
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 7b9434d6f..80bf51439 100755
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -2,7 +2,7 @@
%html{ lang: I18n.locale }
%head
%meta{ charset: 'utf-8' }/
- %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }/
+ %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1, viewport-fit=cover'}/
- if cdn_host?
%link{ rel: 'dns-prefetch', href: cdn_host }/
diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml
index 9e3964f21..74306146f 100644
--- a/app/views/settings/preferences/appearance/show.html.haml
+++ b/app/views/settings/preferences/appearance/show.html.haml
@@ -32,6 +32,7 @@
= f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label
= f.input :setting_disable_swiping, as: :boolean, wrapper: :with_label
= f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label
+ = f.input :setting_place_tab_bar_at_bottom, as: :boolean, wrapper: :with_label
%h4= t 'appearance.toot_layout'
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index 96b0131ef..d51557d77 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -56,6 +56,7 @@ en:
setting_display_media_show_all: Always show media
setting_hide_network: Who you follow and who follows you will be hidden on your profile
setting_noindex: Affects your public profile and post pages
+ setting_place_tab_bar_at_bottom: Place the tab bar at the bottom
setting_show_application: The application you use to post will be displayed in the detailed view of your posts
setting_use_blurhash: Gradients are based on the colors of the hidden visuals but obfuscate any details
setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed
diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml
index ae73cefea..0ebd585fa 100644
--- a/config/locales/simple_form.ja.yml
+++ b/config/locales/simple_form.ja.yml
@@ -209,6 +209,7 @@ ja:
setting_expand_spoilers: 閲覧注意としてマークされたトゥートを常に展開する
setting_hide_network: 繋がりを隠す
setting_noindex: 検索エンジンによるインデックスを拒否する
+ setting_place_tab_bar_at_bottom: タブバーを下に配置する
setting_reduce_motion: アニメーションの動きを減らす
setting_show_application: 送信したアプリを開示する
setting_system_font_ui: システムのデフォルトフォントを使う
diff --git a/config/settings.yml b/config/settings.yml
index f0b09dd5c..5976685d7 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -38,6 +38,7 @@ defaults: &defaults
trends_as_landing_page: true
trendable_by_default: false
crop_images: true
+ place_tab_bar_at_bottom: false
notification_emails:
follow: true
reblog: false