diff --git a/lib/model/general_settings.dart b/lib/model/general_settings.dart index 869fb9655..f57bddc2d 100644 --- a/lib/model/general_settings.dart +++ b/lib/model/general_settings.dart @@ -33,6 +33,15 @@ enum AutomaticPush { const AutomaticPush(this.displayName); } +enum TabPosition { + top("上"), + bottom("下"); + + final String displayName; + + const TabPosition(this.displayName); +} + @freezed class GeneralSettings with _$GeneralSettings { const factory GeneralSettings({ @@ -57,6 +66,9 @@ class GeneralSettings with _$GeneralSettings { /// リアクション済みノートを短くする @Default(true) bool enableFavoritedRenoteElipsed, + + /// タブの位置 + @Default(TabPosition.top) TabPosition tabPosition, }) = _GeneralSettings; factory GeneralSettings.fromJson(Map json) => diff --git a/lib/model/general_settings.freezed.dart b/lib/model/general_settings.freezed.dart index 9190e9c88..491db9bda 100644 --- a/lib/model/general_settings.freezed.dart +++ b/lib/model/general_settings.freezed.dart @@ -42,6 +42,9 @@ mixin _$GeneralSettings { /// リアクション済みノートを短くする bool get enableFavoritedRenoteElipsed => throw _privateConstructorUsedError; + /// タブの位置 + TabPosition get tabPosition => throw _privateConstructorUsedError; + Map toJson() => throw _privateConstructorUsedError; @JsonKey(ignore: true) $GeneralSettingsCopyWith get copyWith => @@ -63,7 +66,8 @@ abstract class $GeneralSettingsCopyWith<$Res> { AutomaticPush automaticPush, bool enableAnimatedMFM, bool enableLongTextElipsed, - bool enableFavoritedRenoteElipsed}); + bool enableFavoritedRenoteElipsed, + TabPosition tabPosition}); } /// @nodoc @@ -88,6 +92,7 @@ class _$GeneralSettingsCopyWithImpl<$Res, $Val extends GeneralSettings> Object? enableAnimatedMFM = null, Object? enableLongTextElipsed = null, Object? enableFavoritedRenoteElipsed = null, + Object? tabPosition = null, }) { return _then(_value.copyWith( lightColorThemeId: null == lightColorThemeId @@ -126,6 +131,10 @@ class _$GeneralSettingsCopyWithImpl<$Res, $Val extends GeneralSettings> ? _value.enableFavoritedRenoteElipsed : enableFavoritedRenoteElipsed // ignore: cast_nullable_to_non_nullable as bool, + tabPosition: null == tabPosition + ? _value.tabPosition + : tabPosition // ignore: cast_nullable_to_non_nullable + as TabPosition, ) as $Val); } } @@ -147,7 +156,8 @@ abstract class _$$_GeneralSettingsCopyWith<$Res> AutomaticPush automaticPush, bool enableAnimatedMFM, bool enableLongTextElipsed, - bool enableFavoritedRenoteElipsed}); + bool enableFavoritedRenoteElipsed, + TabPosition tabPosition}); } /// @nodoc @@ -170,6 +180,7 @@ class __$$_GeneralSettingsCopyWithImpl<$Res> Object? enableAnimatedMFM = null, Object? enableLongTextElipsed = null, Object? enableFavoritedRenoteElipsed = null, + Object? tabPosition = null, }) { return _then(_$_GeneralSettings( lightColorThemeId: null == lightColorThemeId @@ -208,6 +219,10 @@ class __$$_GeneralSettingsCopyWithImpl<$Res> ? _value.enableFavoritedRenoteElipsed : enableFavoritedRenoteElipsed // ignore: cast_nullable_to_non_nullable as bool, + tabPosition: null == tabPosition + ? _value.tabPosition + : tabPosition // ignore: cast_nullable_to_non_nullable + as TabPosition, )); } } @@ -224,7 +239,8 @@ class _$_GeneralSettings implements _GeneralSettings { this.automaticPush = AutomaticPush.none, this.enableAnimatedMFM = true, this.enableLongTextElipsed = false, - this.enableFavoritedRenoteElipsed = true}); + this.enableFavoritedRenoteElipsed = true, + this.tabPosition = TabPosition.top}); factory _$_GeneralSettings.fromJson(Map json) => _$$_GeneralSettingsFromJson(json); @@ -269,9 +285,14 @@ class _$_GeneralSettings implements _GeneralSettings { @JsonKey() final bool enableFavoritedRenoteElipsed; + /// タブの位置 + @override + @JsonKey() + final TabPosition tabPosition; + @override String toString() { - return 'GeneralSettings(lightColorThemeId: $lightColorThemeId, darkColorThemeId: $darkColorThemeId, themeColorSystem: $themeColorSystem, nsfwInherit: $nsfwInherit, enableDirectReaction: $enableDirectReaction, automaticPush: $automaticPush, enableAnimatedMFM: $enableAnimatedMFM, enableLongTextElipsed: $enableLongTextElipsed, enableFavoritedRenoteElipsed: $enableFavoritedRenoteElipsed)'; + return 'GeneralSettings(lightColorThemeId: $lightColorThemeId, darkColorThemeId: $darkColorThemeId, themeColorSystem: $themeColorSystem, nsfwInherit: $nsfwInherit, enableDirectReaction: $enableDirectReaction, automaticPush: $automaticPush, enableAnimatedMFM: $enableAnimatedMFM, enableLongTextElipsed: $enableLongTextElipsed, enableFavoritedRenoteElipsed: $enableFavoritedRenoteElipsed, tabPosition: $tabPosition)'; } @override @@ -298,7 +319,9 @@ class _$_GeneralSettings implements _GeneralSettings { (identical(other.enableFavoritedRenoteElipsed, enableFavoritedRenoteElipsed) || other.enableFavoritedRenoteElipsed == - enableFavoritedRenoteElipsed)); + enableFavoritedRenoteElipsed) && + (identical(other.tabPosition, tabPosition) || + other.tabPosition == tabPosition)); } @JsonKey(ignore: true) @@ -313,7 +336,8 @@ class _$_GeneralSettings implements _GeneralSettings { automaticPush, enableAnimatedMFM, enableLongTextElipsed, - enableFavoritedRenoteElipsed); + enableFavoritedRenoteElipsed, + tabPosition); @JsonKey(ignore: true) @override @@ -339,7 +363,8 @@ abstract class _GeneralSettings implements GeneralSettings { final AutomaticPush automaticPush, final bool enableAnimatedMFM, final bool enableLongTextElipsed, - final bool enableFavoritedRenoteElipsed}) = _$_GeneralSettings; + final bool enableFavoritedRenoteElipsed, + final TabPosition tabPosition}) = _$_GeneralSettings; factory _GeneralSettings.fromJson(Map json) = _$_GeneralSettings.fromJson; @@ -375,6 +400,10 @@ abstract class _GeneralSettings implements GeneralSettings { /// リアクション済みノートを短くする bool get enableFavoritedRenoteElipsed; @override + + /// タブの位置 + TabPosition get tabPosition; + @override @JsonKey(ignore: true) _$$_GeneralSettingsCopyWith<_$_GeneralSettings> get copyWith => throw _privateConstructorUsedError; diff --git a/lib/model/general_settings.g.dart b/lib/model/general_settings.g.dart index 6c5c698cf..56b75b2af 100644 --- a/lib/model/general_settings.g.dart +++ b/lib/model/general_settings.g.dart @@ -24,6 +24,9 @@ _$_GeneralSettings _$$_GeneralSettingsFromJson(Map json) => enableLongTextElipsed: json['enableLongTextElipsed'] as bool? ?? false, enableFavoritedRenoteElipsed: json['enableFavoritedRenoteElipsed'] as bool? ?? true, + tabPosition: + $enumDecodeNullable(_$TabPositionEnumMap, json['tabPosition']) ?? + TabPosition.top, ); Map _$$_GeneralSettingsToJson(_$_GeneralSettings instance) => @@ -37,6 +40,7 @@ Map _$$_GeneralSettingsToJson(_$_GeneralSettings instance) => 'enableAnimatedMFM': instance.enableAnimatedMFM, 'enableLongTextElipsed': instance.enableLongTextElipsed, 'enableFavoritedRenoteElipsed': instance.enableFavoritedRenoteElipsed, + 'tabPosition': _$TabPositionEnumMap[instance.tabPosition]!, }; const _$ThemeColorSystemEnumMap = { @@ -56,3 +60,8 @@ const _$AutomaticPushEnumMap = { AutomaticPush.automatic: 'automatic', AutomaticPush.none: 'none', }; + +const _$TabPositionEnumMap = { + TabPosition.top: 'top', + TabPosition.bottom: 'bottom', +}; diff --git a/lib/view/settings_page/general_settings_page/general_settings_page.dart b/lib/view/settings_page/general_settings_page/general_settings_page.dart index 8750a0340..8014fca14 100644 --- a/lib/view/settings_page/general_settings_page/general_settings_page.dart +++ b/lib/view/settings_page/general_settings_page/general_settings_page.dart @@ -24,6 +24,7 @@ class GeneralSettingsPageState extends ConsumerState { bool enableAnimatedMFM = true; bool enableLongTextElipsed = false; bool enableFavoritedRenoteElipsed = true; + TabPosition tabPosition = TabPosition.top; @override void initState() { @@ -54,6 +55,7 @@ class GeneralSettingsPageState extends ConsumerState { enableAnimatedMFM = settings.enableAnimatedMFM; enableLongTextElipsed = settings.enableLongTextElipsed; enableFavoritedRenoteElipsed = settings.enableFavoritedRenoteElipsed; + tabPosition = settings.tabPosition; }); } @@ -68,7 +70,8 @@ class GeneralSettingsPageState extends ConsumerState { automaticPush: automaticPush, enableAnimatedMFM: enableAnimatedMFM, enableFavoritedRenoteElipsed: enableFavoritedRenoteElipsed, - enableLongTextElipsed: enableLongTextElipsed), + enableLongTextElipsed: enableLongTextElipsed, + tabPosition: tabPosition), ); } @@ -96,6 +99,7 @@ class GeneralSettingsPageState extends ConsumerState { const Padding(padding: EdgeInsets.only(top: 10)), const Text("閲覧注意のついたノートの表示"), DropdownButton( + isExpanded: true, items: [ for (final element in NSFWInherit.values) DropdownMenuItem( @@ -114,6 +118,7 @@ class GeneralSettingsPageState extends ConsumerState { const Padding(padding: EdgeInsets.only(top: 10)), const Text("一覧の自動更新"), DropdownButton( + isExpanded: true, items: [ for (final element in AutomaticPush.values) DropdownMenuItem( @@ -157,6 +162,25 @@ class GeneralSettingsPageState extends ConsumerState { }), title: const Text("長いノートを省略します。"), ), + const Padding(padding: EdgeInsets.only(top: 10)), + const Text("タブの位置"), + DropdownButton( + isExpanded: true, + items: [ + for (final element in TabPosition.values) + DropdownMenuItem( + value: element, + child: Text("${element.displayName}に表示する"), + ) + ], + value: tabPosition, + onChanged: (value) => setState( + () { + tabPosition = value ?? TabPosition.top; + save(); + }, + ), + ), ], ), ), diff --git a/lib/view/time_line_page/time_line_page.dart b/lib/view/time_line_page/time_line_page.dart index 9110c195c..2616b09b0 100644 --- a/lib/view/time_line_page/time_line_page.dart +++ b/lib/view/time_line_page/time_line_page.dart @@ -1,6 +1,7 @@ import 'package:auto_route/auto_route.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; +import 'package:miria/model/general_settings.dart'; import 'package:miria/model/tab_setting.dart'; import 'package:miria/model/tab_type.dart'; import 'package:miria/providers.dart'; @@ -39,6 +40,8 @@ class TimeLinePageState extends ConsumerState { late final PageController pageController; late final List scrollControllers; + final GlobalKey scaffoldKey = GlobalKey(); + @override void initState() { tabSettings = ref.read( @@ -146,6 +149,48 @@ class TimeLinePageState extends ConsumerState { )); } + Widget buildAppbar() { + return AppBar( + title: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: tabSettings + .mapIndexed( + (index, tabSetting) => Ink( + color: tabSetting == currentTabSetting + ? AppTheme.of(context).currentDisplayTabColor + : Colors.transparent, + child: AccountScope( + account: tabSetting.account, + child: IconButton( + icon: TabIconView( + icon: tabSetting.icon, + color: tabSetting == currentTabSetting + ? Theme.of(context).primaryColor + : Colors.white, + ), + onPressed: () => tabSetting == currentTabSetting + ? reload() + : pageController.jumpToPage(index), + ), + ), + ), + ) + .toList(), + ), + ), + actions: [ + AccountScope( + account: currentTabSetting.account, + child: const NotificationIcon(), + ), + ], + leading: IconButton( + onPressed: () => scaffoldKey.currentState?.openDrawer(), + icon: const Icon(Icons.menu)), + ); + } + @override Widget build(BuildContext context) { final socketTimelineBase = ref.watch(currentTabSetting.timelineProvider); @@ -154,45 +199,21 @@ class TimeLinePageState extends ConsumerState { : null; return Scaffold( - appBar: AppBar( - title: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: tabSettings - .mapIndexed( - (index, tabSetting) => Ink( - color: tabSetting == currentTabSetting - ? AppTheme.of(context).currentDisplayTabColor - : Colors.transparent, - child: AccountScope( - account: tabSetting.account, - child: IconButton( - icon: TabIconView( - icon: tabSetting.icon, - color: tabSetting == currentTabSetting - ? Theme.of(context).primaryColor - : Colors.white, - ), - onPressed: () => tabSetting == currentTabSetting - ? reload() - : pageController.jumpToPage(index), - ), - ), - ), - ) - .toList(), - ), - ), - actions: [ - AccountScope( - account: currentTabSetting.account, - child: const NotificationIcon(), - ), - ], - ), + key: scaffoldKey, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(0), + child: AppBar( + automaticallyImplyLeading: false, + )), body: SafeArea( child: Column( children: [ + if (ref + .read(generalSettingsRepositoryProvider) + .settings + .tabPosition == + TabPosition.top) + buildAppbar(), Container( decoration: BoxDecoration( border: Border( @@ -322,10 +343,18 @@ class TimeLinePageState extends ConsumerState { ], ), ), + if (ref + .read(generalSettingsRepositoryProvider) + .settings + .tabPosition == + TabPosition.bottom && + !ref.watch(timelineFocusNode).hasFocus) + buildAppbar(), ], ), ), resizeToAvoidBottomInset: true, + drawerEnableOpenDragGesture: true, drawer: CommonDrawer( initialOpenAccount: currentTabSetting.account, ),