From 25467f42b831fce40ff4ad8c744c99b174687ede Mon Sep 17 00:00:00 2001 From: poppingmoon <63451158+poppingmoon@users.noreply.github.com> Date: Tue, 29 Oct 2024 00:08:59 +0900 Subject: [PATCH] feat: make buttons in timelines page customizable --- lib/i18n/aria/aria.i18n.yaml | 9 +- lib/i18n/strings.g.dart | 2 +- lib/i18n/strings_en_US.g.dart | 9 +- lib/model/general_settings.dart | 40 +- lib/model/general_settings.freezed.dart | 121 ++--- lib/model/general_settings.g.dart | 48 +- .../general_settings_notifier_provider.dart | 22 +- .../general_settings_notifier_provider.g.dart | 2 +- lib/view/page/settings/appearance_page.dart | 212 ++++++++- lib/view/page/timelines_page.dart | 422 +++++++++++------- 10 files changed, 631 insertions(+), 256 deletions(-) diff --git a/lib/i18n/aria/aria.i18n.yaml b/lib/i18n/aria/aria.i18n.yaml index aa815bb4..9941d1f4 100644 --- a/lib/i18n/aria/aria.i18n.yaml +++ b/lib/i18n/aria/aria.i18n.yaml @@ -11,6 +11,7 @@ authenticate: "Authenticate" authenticated: "Authenticated" avatarSize: "Avatar size" background: "Background" +buttonTypes: "Button types" confirmBeforeFollow: "Confirm before follow" confirmBeforePost: "Confirm before post" confirmBeforeReact: "Confirm before react" @@ -100,6 +101,7 @@ playAudio: "Play audio" playVideo: "Play video" pleaseCopyResponse: "Please copy this response" postConfirm: "Are you sure you want to post this note?" +postForm: "Posting form" reactionConfirm: "Are you sure you want to add a reaction?" recentlyUsedEmojis: "Recently used emojis" renoteConfirm: "Are you sure you want to renote this note?" @@ -116,19 +118,17 @@ showAvatarsInNote: "Show avatars in notes" showAvatarsInSubNote: "Show avatars in sub notes" showEntireImage: "Show entire image" showExpandedImage: "Show expanded image" -showHomeFAB: "Show home floating action button" showImage: "Show image" showLikeButtonInNoteFooter: "Add \"Like\" to note action menu" showMenuButtonInTabBar: "Show menu button in tab bar" showNoteCreatedAt: "Show creation date of notes" showNoteFooter: "Show action buttons in notes" showNoteReactionsViewer: "Show reactions viewers in notes" -showNotificationsFAB: "Show notifications floating action button" showPopupOnNewNote: "Show popup on new note" -showPostForm: "Show post form" showQuoteButtonInNoteFooter: "Add \"Quote\" to note action menu" showSelfRenotes: "Show self-renotes" -showShowPostFormFAB: "Show mini post form floating action button" +showSmallButtons: "Show small buttons" +showSquaredButtons: "Show squared buttons" showStackTrace: "Show stack trace" showSubNoteFooter: "Show action buttons in subnotes" showSubNoteReactionsViewer: "Show reactions viewers in subnotes" @@ -144,6 +144,7 @@ tabType: "Tab type" tabs: "Tabs" tapToShow: "Tap to show" timeMachine: "Time machine" +timelinesPageButtons: "Timelines page buttons" unfavorited: "Removed from favorites" unfollowConfirm(rich): "Are you sure you want to unfollow {name}?" untilDate: "Until" diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 967a1fef..8f0e90a1 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -4,7 +4,7 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 31 -/// Strings: 54621 (1761 per locale) +/// Strings: 54622 (1762 per locale) // coverage:ignore-file // ignore_for_file: type=lint, unused_import diff --git a/lib/i18n/strings_en_US.g.dart b/lib/i18n/strings_en_US.g.dart index a552ed55..dc0b10ba 100644 --- a/lib/i18n/strings_en_US.g.dart +++ b/lib/i18n/strings_en_US.g.dart @@ -63,6 +63,7 @@ class TranslationsAriaEnUs { String get authenticated => 'Authenticated'; String get avatarSize => 'Avatar size'; String get background => 'Background'; + String get buttonTypes => 'Button types'; String get confirmBeforeFollow => 'Confirm before follow'; String get confirmBeforePost => 'Confirm before post'; String get confirmBeforeReact => 'Confirm before react'; @@ -180,6 +181,7 @@ class TranslationsAriaEnUs { String get playVideo => 'Play video'; String get pleaseCopyResponse => 'Please copy this response'; String get postConfirm => 'Are you sure you want to post this note?'; + String get postForm => 'Posting form'; String get reactionConfirm => 'Are you sure you want to add a reaction?'; String get recentlyUsedEmojis => 'Recently used emojis'; String get renoteConfirm => 'Are you sure you want to renote this note?'; @@ -205,19 +207,17 @@ class TranslationsAriaEnUs { String get showAvatarsInSubNote => 'Show avatars in sub notes'; String get showEntireImage => 'Show entire image'; String get showExpandedImage => 'Show expanded image'; - String get showHomeFAB => 'Show home floating action button'; String get showImage => 'Show image'; String get showLikeButtonInNoteFooter => 'Add "Like" to note action menu'; String get showMenuButtonInTabBar => 'Show menu button in tab bar'; String get showNoteCreatedAt => 'Show creation date of notes'; String get showNoteFooter => 'Show action buttons in notes'; String get showNoteReactionsViewer => 'Show reactions viewers in notes'; - String get showNotificationsFAB => 'Show notifications floating action button'; String get showPopupOnNewNote => 'Show popup on new note'; - String get showPostForm => 'Show post form'; String get showQuoteButtonInNoteFooter => 'Add "Quote" to note action menu'; String get showSelfRenotes => 'Show self-renotes'; - String get showShowPostFormFAB => 'Show mini post form floating action button'; + String get showSmallButtons => 'Show small buttons'; + String get showSquaredButtons => 'Show squared buttons'; String get showStackTrace => 'Show stack trace'; String get showSubNoteFooter => 'Show action buttons in subnotes'; String get showSubNoteReactionsViewer => 'Show reactions viewers in subnotes'; @@ -233,6 +233,7 @@ class TranslationsAriaEnUs { String get tabs => 'Tabs'; String get tapToShow => 'Tap to show'; String get timeMachine => 'Time machine'; + String get timelinesPageButtons => 'Timelines page buttons'; String get unfavorited => 'Removed from favorites'; TextSpan unfollowConfirm({required InlineSpan name}) => TextSpan(children: [ const TextSpan(text: 'Are you sure you want to unfollow '), diff --git a/lib/model/general_settings.dart b/lib/model/general_settings.dart index 1b2f5f35..98fe347e 100644 --- a/lib/model/general_settings.dart +++ b/lib/model/general_settings.dart @@ -27,6 +27,13 @@ const defaultNoteHorizontalPadding = 12.0; const maxNoteHorizontalPadding = 36.0; const minEmojiPickerScale = 0.1; const maxEmojiPickerScale = 3.0; +const defaultTimelinesPageButtonTypes = [ + TimelinesPageButtonType.menu, + TimelinesPageButtonType.home, + TimelinesPageButtonType.notifications, + TimelinesPageButtonType.postForm, + TimelinesPageButtonType.note, +]; @freezed class GeneralSettings with _$GeneralSettings { @@ -98,9 +105,10 @@ class GeneralSettings with _$GeneralSettings { @Default(false) bool useGroupedNotifications, @Default(false) bool showTimelineTabBarAtBottom, @Default(false) bool showMenuButtonInTabBar, - @Default(true) bool showHomeFAB, - @Default(true) bool showNotificationsFAB, - @Default(true) bool showShowPostFormFAB, + @Default(defaultTimelinesPageButtonTypes) + List timelinesPageButtonTypes, + @Default(false) bool showSmallTimelinesPageButtons, + @Default(false) bool showSquaredTimelinesPageButtons, @Default(true) bool showTabHeaderInOneLine, @Default(false) bool alwaysShowTabHeader, @Default(true) bool showTimelineLastViewedAt, @@ -176,3 +184,29 @@ class ColorConverter extends JsonConverter { return color.value; } } + +enum TimelinesPageButtonType { + announcements, + antennas, + channels, + clips, + drive, + explore, + favorites, + gallery, + games, + home, + instanceInfo, + lists, + lookup, + menu, + note, + notifications, + pages, + play, + postForm, + profile, + reload, + search, + settings, +} diff --git a/lib/model/general_settings.freezed.dart b/lib/model/general_settings.freezed.dart index 0dbc9a34..0c97ddbf 100644 --- a/lib/model/general_settings.freezed.dart +++ b/lib/model/general_settings.freezed.dart @@ -89,9 +89,11 @@ mixin _$GeneralSettings { bool get useGroupedNotifications => throw _privateConstructorUsedError; bool get showTimelineTabBarAtBottom => throw _privateConstructorUsedError; bool get showMenuButtonInTabBar => throw _privateConstructorUsedError; - bool get showHomeFAB => throw _privateConstructorUsedError; - bool get showNotificationsFAB => throw _privateConstructorUsedError; - bool get showShowPostFormFAB => throw _privateConstructorUsedError; + List get timelinesPageButtonTypes => + throw _privateConstructorUsedError; + bool get showSmallTimelinesPageButtons => throw _privateConstructorUsedError; + bool get showSquaredTimelinesPageButtons => + throw _privateConstructorUsedError; bool get showTabHeaderInOneLine => throw _privateConstructorUsedError; bool get alwaysShowTabHeader => throw _privateConstructorUsedError; bool get showTimelineLastViewedAt => throw _privateConstructorUsedError; @@ -193,9 +195,9 @@ abstract class $GeneralSettingsCopyWith<$Res> { bool useGroupedNotifications, bool showTimelineTabBarAtBottom, bool showMenuButtonInTabBar, - bool showHomeFAB, - bool showNotificationsFAB, - bool showShowPostFormFAB, + List timelinesPageButtonTypes, + bool showSmallTimelinesPageButtons, + bool showSquaredTimelinesPageButtons, bool showTabHeaderInOneLine, bool alwaysShowTabHeader, bool showTimelineLastViewedAt, @@ -293,9 +295,9 @@ class _$GeneralSettingsCopyWithImpl<$Res, $Val extends GeneralSettings> Object? useGroupedNotifications = null, Object? showTimelineTabBarAtBottom = null, Object? showMenuButtonInTabBar = null, - Object? showHomeFAB = null, - Object? showNotificationsFAB = null, - Object? showShowPostFormFAB = null, + Object? timelinesPageButtonTypes = null, + Object? showSmallTimelinesPageButtons = null, + Object? showSquaredTimelinesPageButtons = null, Object? showTabHeaderInOneLine = null, Object? alwaysShowTabHeader = null, Object? showTimelineLastViewedAt = null, @@ -553,17 +555,17 @@ class _$GeneralSettingsCopyWithImpl<$Res, $Val extends GeneralSettings> ? _value.showMenuButtonInTabBar : showMenuButtonInTabBar // ignore: cast_nullable_to_non_nullable as bool, - showHomeFAB: null == showHomeFAB - ? _value.showHomeFAB - : showHomeFAB // ignore: cast_nullable_to_non_nullable - as bool, - showNotificationsFAB: null == showNotificationsFAB - ? _value.showNotificationsFAB - : showNotificationsFAB // ignore: cast_nullable_to_non_nullable + timelinesPageButtonTypes: null == timelinesPageButtonTypes + ? _value.timelinesPageButtonTypes + : timelinesPageButtonTypes // ignore: cast_nullable_to_non_nullable + as List, + showSmallTimelinesPageButtons: null == showSmallTimelinesPageButtons + ? _value.showSmallTimelinesPageButtons + : showSmallTimelinesPageButtons // ignore: cast_nullable_to_non_nullable as bool, - showShowPostFormFAB: null == showShowPostFormFAB - ? _value.showShowPostFormFAB - : showShowPostFormFAB // ignore: cast_nullable_to_non_nullable + showSquaredTimelinesPageButtons: null == showSquaredTimelinesPageButtons + ? _value.showSquaredTimelinesPageButtons + : showSquaredTimelinesPageButtons // ignore: cast_nullable_to_non_nullable as bool, showTabHeaderInOneLine: null == showTabHeaderInOneLine ? _value.showTabHeaderInOneLine @@ -726,9 +728,9 @@ abstract class _$$GeneralSettingsImplCopyWith<$Res> bool useGroupedNotifications, bool showTimelineTabBarAtBottom, bool showMenuButtonInTabBar, - bool showHomeFAB, - bool showNotificationsFAB, - bool showShowPostFormFAB, + List timelinesPageButtonTypes, + bool showSmallTimelinesPageButtons, + bool showSquaredTimelinesPageButtons, bool showTabHeaderInOneLine, bool alwaysShowTabHeader, bool showTimelineLastViewedAt, @@ -824,9 +826,9 @@ class __$$GeneralSettingsImplCopyWithImpl<$Res> Object? useGroupedNotifications = null, Object? showTimelineTabBarAtBottom = null, Object? showMenuButtonInTabBar = null, - Object? showHomeFAB = null, - Object? showNotificationsFAB = null, - Object? showShowPostFormFAB = null, + Object? timelinesPageButtonTypes = null, + Object? showSmallTimelinesPageButtons = null, + Object? showSquaredTimelinesPageButtons = null, Object? showTabHeaderInOneLine = null, Object? alwaysShowTabHeader = null, Object? showTimelineLastViewedAt = null, @@ -1084,17 +1086,17 @@ class __$$GeneralSettingsImplCopyWithImpl<$Res> ? _value.showMenuButtonInTabBar : showMenuButtonInTabBar // ignore: cast_nullable_to_non_nullable as bool, - showHomeFAB: null == showHomeFAB - ? _value.showHomeFAB - : showHomeFAB // ignore: cast_nullable_to_non_nullable + timelinesPageButtonTypes: null == timelinesPageButtonTypes + ? _value._timelinesPageButtonTypes + : timelinesPageButtonTypes // ignore: cast_nullable_to_non_nullable + as List, + showSmallTimelinesPageButtons: null == showSmallTimelinesPageButtons + ? _value.showSmallTimelinesPageButtons + : showSmallTimelinesPageButtons // ignore: cast_nullable_to_non_nullable as bool, - showNotificationsFAB: null == showNotificationsFAB - ? _value.showNotificationsFAB - : showNotificationsFAB // ignore: cast_nullable_to_non_nullable - as bool, - showShowPostFormFAB: null == showShowPostFormFAB - ? _value.showShowPostFormFAB - : showShowPostFormFAB // ignore: cast_nullable_to_non_nullable + showSquaredTimelinesPageButtons: null == showSquaredTimelinesPageButtons + ? _value.showSquaredTimelinesPageButtons + : showSquaredTimelinesPageButtons // ignore: cast_nullable_to_non_nullable as bool, showTabHeaderInOneLine: null == showTabHeaderInOneLine ? _value.showTabHeaderInOneLine @@ -1252,9 +1254,10 @@ class _$GeneralSettingsImpl implements _GeneralSettings { this.useGroupedNotifications = false, this.showTimelineTabBarAtBottom = false, this.showMenuButtonInTabBar = false, - this.showHomeFAB = true, - this.showNotificationsFAB = true, - this.showShowPostFormFAB = true, + final List timelinesPageButtonTypes = + defaultTimelinesPageButtonTypes, + this.showSmallTimelinesPageButtons = false, + this.showSquaredTimelinesPageButtons = false, this.showTabHeaderInOneLine = true, this.alwaysShowTabHeader = false, this.showTimelineLastViewedAt = true, @@ -1276,7 +1279,8 @@ class _$GeneralSettingsImpl implements _GeneralSettings { this.enablePredictiveBack = false, this.themeMode = ThemeMode.system, this.lightThemeId = 'a58a0abb-ff8c-476a-8dec-0ad7837e7e96', - this.darkThemeId = '66e7e5a9-cd43-42cd-837d-12f47841fa34'}); + this.darkThemeId = '66e7e5a9-cd43-42cd-837d-12f47841fa34'}) + : _timelinesPageButtonTypes = timelinesPageButtonTypes; factory _$GeneralSettingsImpl.fromJson(Map json) => _$$GeneralSettingsImplFromJson(json); @@ -1458,15 +1462,22 @@ class _$GeneralSettingsImpl implements _GeneralSettings { @override @JsonKey() final bool showMenuButtonInTabBar; + final List _timelinesPageButtonTypes; @override @JsonKey() - final bool showHomeFAB; + List get timelinesPageButtonTypes { + if (_timelinesPageButtonTypes is EqualUnmodifiableListView) + return _timelinesPageButtonTypes; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_timelinesPageButtonTypes); + } + @override @JsonKey() - final bool showNotificationsFAB; + final bool showSmallTimelinesPageButtons; @override @JsonKey() - final bool showShowPostFormFAB; + final bool showSquaredTimelinesPageButtons; @override @JsonKey() final bool showTabHeaderInOneLine; @@ -1538,7 +1549,7 @@ class _$GeneralSettingsImpl implements _GeneralSettings { @override String toString() { - return 'GeneralSettings(locale: $locale, collapseRenotes: $collapseRenotes, sensitive: $sensitive, highlightSensitiveMedia: $highlightSensitiveMedia, animatedMfm: $animatedMfm, advancedMfm: $advancedMfm, showReactionsCount: $showReactionsCount, loadRawImages: $loadRawImages, instanceTicker: $instanceTicker, showNoteCreatedAt: $showNoteCreatedAt, showAvatarsInNote: $showAvatarsInNote, showAvatarsInSubNote: $showAvatarsInSubNote, squareAvatars: $squareAvatars, showAvatarDecorations: $showAvatarDecorations, showQuoteButtonInNoteFooter: $showQuoteButtonInNoteFooter, showLikeButtonInNoteFooter: $showLikeButtonInNoteFooter, showClipButtonInNoteFooter: $showClipButtonInNoteFooter, showTranslateButtonInNoteFooter: $showTranslateButtonInNoteFooter, showNoteReactionsViewer: $showNoteReactionsViewer, showSubNoteReactionsViewer: $showSubNoteReactionsViewer, showNoteFooter: $showNoteFooter, showSubNoteFooter: $showSubNoteFooter, alwaysExpandCw: $alwaysExpandCw, alwaysExpandLongNote: $alwaysExpandLongNote, alwaysExpandMediaInSubNote: $alwaysExpandMediaInSubNote, mergeReactionsByName: $mergeReactionsByName, alwaysShowAllReactions: $alwaysShowAllReactions, mediaListWithOneImageAppearance: $mediaListWithOneImageAppearance, thumbnailBoxFit: $thumbnailBoxFit, emojiStyle: $emojiStyle, fontFamily: $fontFamily, fontSize: $fontSize, lineHeight: $lineHeight, avatarScale: $avatarScale, reactionsDisplayScale: $reactionsDisplayScale, limitWidthOfReaction: $limitWidthOfReaction, noteFooterScale: $noteFooterScale, noteVerticalPadding: $noteVerticalPadding, noteHorizontalPadding: $noteHorizontalPadding, publicNoteBackgroundColor: $publicNoteBackgroundColor, homeNoteBackgroundColor: $homeNoteBackgroundColor, followersNoteBackgroundColor: $followersNoteBackgroundColor, specifiedNoteBackgroundColor: $specifiedNoteBackgroundColor, emojiPickerUseDialog: $emojiPickerUseDialog, emojiPickerScale: $emojiPickerScale, emojiPickerAutofocus: $emojiPickerAutofocus, emojiPickerKeepOpen: $emojiPickerKeepOpen, dataSaverMedia: $dataSaverMedia, dataSaverAvatar: $dataSaverAvatar, dataSaverUrlPreview: $dataSaverUrlPreview, disableDataSaverWhenOnWifi: $disableDataSaverWhenOnWifi, reduceAnimation: $reduceAnimation, disableShowingAnimatedImages: $disableShowingAnimatedImages, enableEmojiFadeIn: $enableEmojiFadeIn, forceShowAds: $forceShowAds, useGroupedNotifications: $useGroupedNotifications, showTimelineTabBarAtBottom: $showTimelineTabBarAtBottom, showMenuButtonInTabBar: $showMenuButtonInTabBar, showHomeFAB: $showHomeFAB, showNotificationsFAB: $showNotificationsFAB, showShowPostFormFAB: $showShowPostFormFAB, showTabHeaderInOneLine: $showTabHeaderInOneLine, alwaysShowTabHeader: $alwaysShowTabHeader, showTimelineLastViewedAt: $showTimelineLastViewedAt, showPopupOnNewNote: $showPopupOnNewNote, vibrateNote: $vibrateNote, vibrateNotification: $vibrateNotification, enableInfiniteScroll: $enableInfiniteScroll, keepScreenOn: $keepScreenOn, enableHorizontalSwipe: $enableHorizontalSwipe, openSensitiveMediaOnDoubleTap: $openSensitiveMediaOnDoubleTap, noteTapAction: $noteTapAction, noteDoubleTapAction: $noteDoubleTapAction, noteLongPressAction: $noteLongPressAction, confirmBeforePost: $confirmBeforePost, confirmBeforeReact: $confirmBeforeReact, confirmBeforeFollow: $confirmBeforeFollow, confirmWhenRevealingSensitiveMedia: $confirmWhenRevealingSensitiveMedia, launchMode: $launchMode, enablePredictiveBack: $enablePredictiveBack, themeMode: $themeMode, lightThemeId: $lightThemeId, darkThemeId: $darkThemeId)'; + return 'GeneralSettings(locale: $locale, collapseRenotes: $collapseRenotes, sensitive: $sensitive, highlightSensitiveMedia: $highlightSensitiveMedia, animatedMfm: $animatedMfm, advancedMfm: $advancedMfm, showReactionsCount: $showReactionsCount, loadRawImages: $loadRawImages, instanceTicker: $instanceTicker, showNoteCreatedAt: $showNoteCreatedAt, showAvatarsInNote: $showAvatarsInNote, showAvatarsInSubNote: $showAvatarsInSubNote, squareAvatars: $squareAvatars, showAvatarDecorations: $showAvatarDecorations, showQuoteButtonInNoteFooter: $showQuoteButtonInNoteFooter, showLikeButtonInNoteFooter: $showLikeButtonInNoteFooter, showClipButtonInNoteFooter: $showClipButtonInNoteFooter, showTranslateButtonInNoteFooter: $showTranslateButtonInNoteFooter, showNoteReactionsViewer: $showNoteReactionsViewer, showSubNoteReactionsViewer: $showSubNoteReactionsViewer, showNoteFooter: $showNoteFooter, showSubNoteFooter: $showSubNoteFooter, alwaysExpandCw: $alwaysExpandCw, alwaysExpandLongNote: $alwaysExpandLongNote, alwaysExpandMediaInSubNote: $alwaysExpandMediaInSubNote, mergeReactionsByName: $mergeReactionsByName, alwaysShowAllReactions: $alwaysShowAllReactions, mediaListWithOneImageAppearance: $mediaListWithOneImageAppearance, thumbnailBoxFit: $thumbnailBoxFit, emojiStyle: $emojiStyle, fontFamily: $fontFamily, fontSize: $fontSize, lineHeight: $lineHeight, avatarScale: $avatarScale, reactionsDisplayScale: $reactionsDisplayScale, limitWidthOfReaction: $limitWidthOfReaction, noteFooterScale: $noteFooterScale, noteVerticalPadding: $noteVerticalPadding, noteHorizontalPadding: $noteHorizontalPadding, publicNoteBackgroundColor: $publicNoteBackgroundColor, homeNoteBackgroundColor: $homeNoteBackgroundColor, followersNoteBackgroundColor: $followersNoteBackgroundColor, specifiedNoteBackgroundColor: $specifiedNoteBackgroundColor, emojiPickerUseDialog: $emojiPickerUseDialog, emojiPickerScale: $emojiPickerScale, emojiPickerAutofocus: $emojiPickerAutofocus, emojiPickerKeepOpen: $emojiPickerKeepOpen, dataSaverMedia: $dataSaverMedia, dataSaverAvatar: $dataSaverAvatar, dataSaverUrlPreview: $dataSaverUrlPreview, disableDataSaverWhenOnWifi: $disableDataSaverWhenOnWifi, reduceAnimation: $reduceAnimation, disableShowingAnimatedImages: $disableShowingAnimatedImages, enableEmojiFadeIn: $enableEmojiFadeIn, forceShowAds: $forceShowAds, useGroupedNotifications: $useGroupedNotifications, showTimelineTabBarAtBottom: $showTimelineTabBarAtBottom, showMenuButtonInTabBar: $showMenuButtonInTabBar, timelinesPageButtonTypes: $timelinesPageButtonTypes, showSmallTimelinesPageButtons: $showSmallTimelinesPageButtons, showSquaredTimelinesPageButtons: $showSquaredTimelinesPageButtons, showTabHeaderInOneLine: $showTabHeaderInOneLine, alwaysShowTabHeader: $alwaysShowTabHeader, showTimelineLastViewedAt: $showTimelineLastViewedAt, showPopupOnNewNote: $showPopupOnNewNote, vibrateNote: $vibrateNote, vibrateNotification: $vibrateNotification, enableInfiniteScroll: $enableInfiniteScroll, keepScreenOn: $keepScreenOn, enableHorizontalSwipe: $enableHorizontalSwipe, openSensitiveMediaOnDoubleTap: $openSensitiveMediaOnDoubleTap, noteTapAction: $noteTapAction, noteDoubleTapAction: $noteDoubleTapAction, noteLongPressAction: $noteLongPressAction, confirmBeforePost: $confirmBeforePost, confirmBeforeReact: $confirmBeforeReact, confirmBeforeFollow: $confirmBeforeFollow, confirmWhenRevealingSensitiveMedia: $confirmWhenRevealingSensitiveMedia, launchMode: $launchMode, enablePredictiveBack: $enablePredictiveBack, themeMode: $themeMode, lightThemeId: $lightThemeId, darkThemeId: $darkThemeId)'; } @override @@ -1634,9 +1645,9 @@ class _$GeneralSettingsImpl implements _GeneralSettings { (identical(other.useGroupedNotifications, useGroupedNotifications) || other.useGroupedNotifications == useGroupedNotifications) && (identical(other.showTimelineTabBarAtBottom, showTimelineTabBarAtBottom) || other.showTimelineTabBarAtBottom == showTimelineTabBarAtBottom) && (identical(other.showMenuButtonInTabBar, showMenuButtonInTabBar) || other.showMenuButtonInTabBar == showMenuButtonInTabBar) && - (identical(other.showHomeFAB, showHomeFAB) || other.showHomeFAB == showHomeFAB) && - (identical(other.showNotificationsFAB, showNotificationsFAB) || other.showNotificationsFAB == showNotificationsFAB) && - (identical(other.showShowPostFormFAB, showShowPostFormFAB) || other.showShowPostFormFAB == showShowPostFormFAB) && + const DeepCollectionEquality().equals(other._timelinesPageButtonTypes, _timelinesPageButtonTypes) && + (identical(other.showSmallTimelinesPageButtons, showSmallTimelinesPageButtons) || other.showSmallTimelinesPageButtons == showSmallTimelinesPageButtons) && + (identical(other.showSquaredTimelinesPageButtons, showSquaredTimelinesPageButtons) || other.showSquaredTimelinesPageButtons == showSquaredTimelinesPageButtons) && (identical(other.showTabHeaderInOneLine, showTabHeaderInOneLine) || other.showTabHeaderInOneLine == showTabHeaderInOneLine) && (identical(other.alwaysShowTabHeader, alwaysShowTabHeader) || other.alwaysShowTabHeader == alwaysShowTabHeader) && (identical(other.showTimelineLastViewedAt, showTimelineLastViewedAt) || other.showTimelineLastViewedAt == showTimelineLastViewedAt) && @@ -1723,9 +1734,9 @@ class _$GeneralSettingsImpl implements _GeneralSettings { useGroupedNotifications, showTimelineTabBarAtBottom, showMenuButtonInTabBar, - showHomeFAB, - showNotificationsFAB, - showShowPostFormFAB, + const DeepCollectionEquality().hash(_timelinesPageButtonTypes), + showSmallTimelinesPageButtons, + showSquaredTimelinesPageButtons, showTabHeaderInOneLine, alwaysShowTabHeader, showTimelineLastViewedAt, @@ -1828,9 +1839,9 @@ abstract class _GeneralSettings implements GeneralSettings { final bool useGroupedNotifications, final bool showTimelineTabBarAtBottom, final bool showMenuButtonInTabBar, - final bool showHomeFAB, - final bool showNotificationsFAB, - final bool showShowPostFormFAB, + final List timelinesPageButtonTypes, + final bool showSmallTimelinesPageButtons, + final bool showSquaredTimelinesPageButtons, final bool showTabHeaderInOneLine, final bool alwaysShowTabHeader, final bool showTimelineLastViewedAt, @@ -1981,11 +1992,11 @@ abstract class _GeneralSettings implements GeneralSettings { @override bool get showMenuButtonInTabBar; @override - bool get showHomeFAB; + List get timelinesPageButtonTypes; @override - bool get showNotificationsFAB; + bool get showSmallTimelinesPageButtons; @override - bool get showShowPostFormFAB; + bool get showSquaredTimelinesPageButtons; @override bool get showTabHeaderInOneLine; @override diff --git a/lib/model/general_settings.g.dart b/lib/model/general_settings.g.dart index 33cc1791..7f661d15 100644 --- a/lib/model/general_settings.g.dart +++ b/lib/model/general_settings.g.dart @@ -100,9 +100,16 @@ _$GeneralSettingsImpl _$$GeneralSettingsImplFromJson( showTimelineTabBarAtBottom: json['showTimelineTabBarAtBottom'] as bool? ?? false, showMenuButtonInTabBar: json['showMenuButtonInTabBar'] as bool? ?? false, - showHomeFAB: json['showHomeFAB'] as bool? ?? true, - showNotificationsFAB: json['showNotificationsFAB'] as bool? ?? true, - showShowPostFormFAB: json['showShowPostFormFAB'] as bool? ?? true, + timelinesPageButtonTypes: + (json['timelinesPageButtonTypes'] as List?) + ?.map((e) => + $enumDecodeNullable(_$TimelinesPageButtonTypeEnumMap, e)) + .toList() ?? + defaultTimelinesPageButtonTypes, + showSmallTimelinesPageButtons: + json['showSmallTimelinesPageButtons'] as bool? ?? false, + showSquaredTimelinesPageButtons: + json['showSquaredTimelinesPageButtons'] as bool? ?? false, showTabHeaderInOneLine: json['showTabHeaderInOneLine'] as bool? ?? true, alwaysShowTabHeader: json['alwaysShowTabHeader'] as bool? ?? false, showTimelineLastViewedAt: @@ -225,9 +232,12 @@ Map _$$GeneralSettingsImplToJson( val['useGroupedNotifications'] = instance.useGroupedNotifications; val['showTimelineTabBarAtBottom'] = instance.showTimelineTabBarAtBottom; val['showMenuButtonInTabBar'] = instance.showMenuButtonInTabBar; - val['showHomeFAB'] = instance.showHomeFAB; - val['showNotificationsFAB'] = instance.showNotificationsFAB; - val['showShowPostFormFAB'] = instance.showShowPostFormFAB; + val['timelinesPageButtonTypes'] = instance.timelinesPageButtonTypes + .map((e) => _$TimelinesPageButtonTypeEnumMap[e]) + .toList(); + val['showSmallTimelinesPageButtons'] = instance.showSmallTimelinesPageButtons; + val['showSquaredTimelinesPageButtons'] = + instance.showSquaredTimelinesPageButtons; val['showTabHeaderInOneLine'] = instance.showTabHeaderInOneLine; val['alwaysShowTabHeader'] = instance.alwaysShowTabHeader; val['showTimelineLastViewedAt'] = instance.showTimelineLastViewedAt; @@ -329,6 +339,32 @@ Value? _$JsonConverterFromJson( ) => json == null ? null : fromJson(json as Json); +const _$TimelinesPageButtonTypeEnumMap = { + TimelinesPageButtonType.announcements: 'announcements', + TimelinesPageButtonType.antennas: 'antennas', + TimelinesPageButtonType.channels: 'channels', + TimelinesPageButtonType.clips: 'clips', + TimelinesPageButtonType.drive: 'drive', + TimelinesPageButtonType.explore: 'explore', + TimelinesPageButtonType.favorites: 'favorites', + TimelinesPageButtonType.gallery: 'gallery', + TimelinesPageButtonType.games: 'games', + TimelinesPageButtonType.home: 'home', + TimelinesPageButtonType.instanceInfo: 'instanceInfo', + TimelinesPageButtonType.lists: 'lists', + TimelinesPageButtonType.lookup: 'lookup', + TimelinesPageButtonType.menu: 'menu', + TimelinesPageButtonType.note: 'note', + TimelinesPageButtonType.notifications: 'notifications', + TimelinesPageButtonType.pages: 'pages', + TimelinesPageButtonType.play: 'play', + TimelinesPageButtonType.postForm: 'postForm', + TimelinesPageButtonType.profile: 'profile', + TimelinesPageButtonType.reload: 'reload', + TimelinesPageButtonType.search: 'search', + TimelinesPageButtonType.settings: 'settings', +}; + const _$NoteActionTypeEnumMap = { NoteActionType.none: 'none', NoteActionType.expand: 'expand', diff --git a/lib/provider/general_settings_notifier_provider.dart b/lib/provider/general_settings_notifier_provider.dart index c2d7d9f2..56419046 100644 --- a/lib/provider/general_settings_notifier_provider.dart +++ b/lib/provider/general_settings_notifier_provider.dart @@ -376,18 +376,28 @@ class GeneralSettingsNotifier extends _$GeneralSettingsNotifier { await _save(); } - Future setShowHomeFAB(bool showHomeFAB) async { - state = state.copyWith(showHomeFAB: showHomeFAB); + Future setTimelinesPageButtonTypes( + List timelinesPageButtonTypes, + ) async { + state = state.copyWith(timelinesPageButtonTypes: timelinesPageButtonTypes); await _save(); } - Future setShowNotificationsFAB(bool showNotificationsFAB) async { - state = state.copyWith(showNotificationsFAB: showNotificationsFAB); + Future setShowSmallTimelinesPageButtons( + bool showSmallTimelinesPageButtons, + ) async { + state = state.copyWith( + showSmallTimelinesPageButtons: showSmallTimelinesPageButtons, + ); await _save(); } - Future setShowShowPostFormFAB(bool showShowPostFormFAB) async { - state = state.copyWith(showShowPostFormFAB: showShowPostFormFAB); + Future setShowSquareTimelinesPageButtons( + bool showSquareTimelinesPageButtons, + ) async { + state = state.copyWith( + showSquaredTimelinesPageButtons: showSquareTimelinesPageButtons, + ); await _save(); } diff --git a/lib/provider/general_settings_notifier_provider.g.dart b/lib/provider/general_settings_notifier_provider.g.dart index 8abc00cc..255446d4 100644 --- a/lib/provider/general_settings_notifier_provider.g.dart +++ b/lib/provider/general_settings_notifier_provider.g.dart @@ -7,7 +7,7 @@ part of 'general_settings_notifier_provider.dart'; // ************************************************************************** String _$generalSettingsNotifierHash() => - r'a2d10ce1e4992efed6f3c23e2ba9b08c10e0b546'; + r'652567e38c6ed7f5e4e1f31bd2b8eb5b5e13c7e5'; /// See also [GeneralSettingsNotifier]. @ProviderFor(GeneralSettingsNotifier) diff --git a/lib/view/page/settings/appearance_page.dart b/lib/view/page/settings/appearance_page.dart index 3eb481af..13b5251a 100644 --- a/lib/view/page/settings/appearance_page.dart +++ b/lib/view/page/settings/appearance_page.dart @@ -1,8 +1,11 @@ +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../../../i18n/strings.g.dart'; +import '../../../model/general_settings.dart'; import '../../../provider/general_settings_notifier_provider.dart'; +import '../../dialog/radio_dialog.dart'; import '../../widget/general_settings_scaffold.dart'; class AppearancePage extends HookConsumerWidget { @@ -16,8 +19,9 @@ class AppearancePage extends HookConsumerWidget { appBar: AppBar(title: Text(t.misskey.appearance)), body: ListView( children: [ + const SizedBox(height: 8.0), Padding( - padding: const EdgeInsets.only(left: 16.0), + padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Text( t.misskey.dataSaver, style: TextStyle( @@ -106,27 +110,6 @@ class AppearancePage extends HookConsumerWidget { .read(generalSettingsNotifierProvider.notifier) .setShowMenuButtonInTabBar(value), ), - SwitchListTile( - title: Text(t.aria.showHomeFAB), - value: settings.showHomeFAB, - onChanged: (value) => ref - .read(generalSettingsNotifierProvider.notifier) - .setShowHomeFAB(value), - ), - SwitchListTile( - title: Text(t.aria.showNotificationsFAB), - value: settings.showNotificationsFAB, - onChanged: (value) => ref - .read(generalSettingsNotifierProvider.notifier) - .setShowNotificationsFAB(value), - ), - SwitchListTile( - title: Text(t.aria.showShowPostFormFAB), - value: settings.showShowPostFormFAB, - onChanged: (value) => ref - .read(generalSettingsNotifierProvider.notifier) - .setShowShowPostFormFAB(value), - ), SwitchListTile( title: Text(t.aria.showTabHeaderInOneLine), value: settings.showTabHeaderInOneLine, @@ -156,6 +139,153 @@ class AppearancePage extends HookConsumerWidget { .setShowPopupOnNewNote(value), ), const Divider(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + t.aria.timelinesPageButtons, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.8), + ), + ), + ), + ExpansionTile( + title: Text(t.aria.buttonTypes), + childrenPadding: const EdgeInsets.symmetric(horizontal: 8.0), + children: [ + ...settings.timelinesPageButtonTypes.mapIndexed( + (index, type) => ListTile( + leading: type != null + ? Icon( + switch (type) { + TimelinesPageButtonType.announcements => + Icons.campaign, + TimelinesPageButtonType.antennas => + Icons.settings_input_antenna, + TimelinesPageButtonType.channels => Icons.tv, + TimelinesPageButtonType.clips => Icons.attach_file, + TimelinesPageButtonType.drive => Icons.cloud, + TimelinesPageButtonType.explore => Icons.tag, + TimelinesPageButtonType.favorites => + Icons.star_rounded, + TimelinesPageButtonType.gallery => + Icons.collections, + TimelinesPageButtonType.games => Icons.games, + TimelinesPageButtonType.home => Icons.home, + TimelinesPageButtonType.instanceInfo => Icons.dns, + TimelinesPageButtonType.lists => Icons.list, + TimelinesPageButtonType.lookup => + Icons.travel_explore, + TimelinesPageButtonType.menu => Icons.menu, + TimelinesPageButtonType.note => Icons.edit, + TimelinesPageButtonType.notifications => + Icons.notifications, + TimelinesPageButtonType.pages => Icons.article, + TimelinesPageButtonType.play => Icons.play_arrow, + TimelinesPageButtonType.postForm => Icons.keyboard, + TimelinesPageButtonType.profile => Icons.person, + TimelinesPageButtonType.reload => Icons.refresh, + TimelinesPageButtonType.search => Icons.search, + TimelinesPageButtonType.settings => Icons.settings, + }, + ) + : const SizedBox.shrink(), + title: _TimelinesPageButtonTypeNameWidget(buttonType: type), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (type != null) + IconButton( + onPressed: () => ref + .read(generalSettingsNotifierProvider.notifier) + .setTimelinesPageButtonTypes( + settings.timelinesPageButtonTypes + .mapIndexed( + (i, type) => i != index ? type : null, + ) + .toList(), + ), + icon: const Icon(Icons.close), + ), + IconButton( + onPressed: () => ref + .read(generalSettingsNotifierProvider.notifier) + .setTimelinesPageButtonTypes( + settings.timelinesPageButtonTypes + .whereIndexed((i, _) => i != index) + .toList(), + ), + icon: const Icon(Icons.delete), + ), + ], + ), + onTap: () async { + final result = await showRadioDialog( + context, + values: TimelinesPageButtonType.values, + initialValue: type, + title: Text(t.aria.buttonTypes), + itemBuilder: (context, type) => + _TimelinesPageButtonTypeNameWidget(buttonType: type), + ); + if (result != null && result != type) { + await ref + .read(generalSettingsNotifierProvider.notifier) + .setTimelinesPageButtonTypes( + settings.timelinesPageButtonTypes + .mapIndexed( + (i, type) => i != index ? type : result, + ) + .toList(), + ); + } + }, + ), + ), + Container( + padding: const EdgeInsets.all(8.0), + width: double.infinity, + child: Wrap( + spacing: 8.0, + runSpacing: 8.0, + children: [ + OutlinedButton.icon( + onPressed: () => ref + .read(generalSettingsNotifierProvider.notifier) + .setTimelinesPageButtonTypes( + [...settings.timelinesPageButtonTypes, null], + ), + icon: const Icon(Icons.add), + label: Text(t.misskey.add), + ), + OutlinedButton.icon( + onPressed: () => ref + .read(generalSettingsNotifierProvider.notifier) + .setTimelinesPageButtonTypes( + defaultTimelinesPageButtonTypes, + ), + icon: const Icon(Icons.refresh), + label: Text(t.misskey.default_), + ), + ], + ), + ), + ], + ), + SwitchListTile( + title: Text(t.aria.showSmallButtons), + value: settings.showSmallTimelinesPageButtons, + onChanged: (value) => ref + .read(generalSettingsNotifierProvider.notifier) + .setShowSmallTimelinesPageButtons(value), + ), + SwitchListTile( + title: Text(t.aria.showSquaredButtons), + value: settings.showSquaredTimelinesPageButtons, + onChanged: (value) => ref + .read(generalSettingsNotifierProvider.notifier) + .setShowSquareTimelinesPageButtons(value), + ), + const Divider(), SwitchListTile( title: Text(t.aria.vibrateNote), value: settings.vibrateNote, @@ -176,3 +306,41 @@ class AppearancePage extends HookConsumerWidget { ); } } + +class _TimelinesPageButtonTypeNameWidget extends StatelessWidget { + const _TimelinesPageButtonTypeNameWidget({this.buttonType}); + + final TimelinesPageButtonType? buttonType; + + @override + Widget build(BuildContext context) { + return Text( + switch (buttonType) { + TimelinesPageButtonType.announcements => t.misskey.announcements, + TimelinesPageButtonType.antennas => t.misskey.antennas, + TimelinesPageButtonType.channels => t.misskey.channel, + TimelinesPageButtonType.clips => t.misskey.clips, + TimelinesPageButtonType.drive => t.misskey.drive, + TimelinesPageButtonType.explore => t.misskey.explore, + TimelinesPageButtonType.favorites => t.misskey.favorites, + TimelinesPageButtonType.gallery => t.misskey.gallery, + TimelinesPageButtonType.games => 'Misskey Games', + TimelinesPageButtonType.home => t.misskey.home, + TimelinesPageButtonType.instanceInfo => t.misskey.instanceInfo, + TimelinesPageButtonType.lists => t.misskey.lists, + TimelinesPageButtonType.lookup => t.misskey.lookup, + TimelinesPageButtonType.menu => t.misskey.menu, + TimelinesPageButtonType.note => t.misskey.note, + TimelinesPageButtonType.notifications => t.misskey.notifications, + TimelinesPageButtonType.pages => t.misskey.pages, + TimelinesPageButtonType.play => 'Play', + TimelinesPageButtonType.postForm => t.aria.postForm, + TimelinesPageButtonType.profile => t.misskey.profile, + TimelinesPageButtonType.reload => t.misskey.reload, + TimelinesPageButtonType.search => t.misskey.search, + TimelinesPageButtonType.settings => t.misskey.settings, + null => t.misskey.notSet, + }, + ); + } +} diff --git a/lib/view/page/timelines_page.dart b/lib/view/page/timelines_page.dart index f660c8ce..5453250e 100644 --- a/lib/view/page/timelines_page.dart +++ b/lib/view/page/timelines_page.dart @@ -1,5 +1,6 @@ import 'dart:ui'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; @@ -8,6 +9,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../../constant/shortcuts.dart'; import '../../extension/scroll_controller_extension.dart'; import '../../i18n/strings.g.dart'; +import '../../model/general_settings.dart'; +import '../../model/tab_settings.dart'; import '../../model/tab_type.dart'; import '../../provider/api/i_notifier_provider.dart'; import '../../provider/emojis_notifier_provider.dart'; @@ -19,10 +22,14 @@ import '../../provider/timeline_scroll_controller_provider.dart'; import '../../provider/timeline_tab_index_notifier_provider.dart'; import '../../provider/timeline_tab_settings_provider.dart'; import '../../provider/timeline_tabs_notifier_provider.dart'; +import '../../util/lookup.dart'; +import '../../util/reload_timeline.dart'; +import '../dialog/text_field_dialog.dart'; import '../widget/post_form.dart'; import '../widget/timeline_drawer.dart'; import '../widget/timeline_tab_bar.dart'; import '../widget/timeline_widget.dart'; +import '../widget/user_avatar.dart'; class TimelinesPage extends HookConsumerWidget { const TimelinesPage({super.key}); @@ -38,20 +45,25 @@ class TimelinesPage extends HookConsumerWidget { .select((settings) => settings.showTimelineTabBarAtBottom), ); final showMenuButtonInTabBar = ref.watch( - generalSettingsNotifierProvider - .select((settings) => settings.showMenuButtonInTabBar), + generalSettingsNotifierProvider.select( + (settings) => + settings.showMenuButtonInTabBar || + !settings.timelinesPageButtonTypes + .sublist(0, 5) + .contains(TimelinesPageButtonType.menu), + ), ); - final showHomeFAB = ref.watch( + final buttonTypes = ref.watch( generalSettingsNotifierProvider - .select((settings) => settings.showHomeFAB), + .select((settings) => settings.timelinesPageButtonTypes), ); - final showNotificationsFAB = ref.watch( + final mini = ref.watch( generalSettingsNotifierProvider - .select((settings) => settings.showNotificationsFAB), + .select((settings) => settings.showSmallTimelinesPageButtons), ); - final showShowPostFormFAB = ref.watch( + final square = ref.watch( generalSettingsNotifierProvider - .select((settings) => settings.showShowPostFormFAB), + .select((settings) => settings.showSquaredTimelinesPageButtons), ); final enableHorizontalSwipe = ref.watch( generalSettingsNotifierProvider @@ -62,9 +74,6 @@ class TimelinesPage extends HookConsumerWidget { initialIndex: useMemoized(() => tabIndex, [numTabs]), keys: [numTabs], ); - final i = tabSettings != null - ? ref.watch(iNotifierProvider(tabSettings.account)).valueOrNull - : null; final showPostForm = useState(false); useEffect( () { @@ -138,7 +147,6 @@ class TimelinesPage extends HookConsumerWidget { [tabs], ); final isLargeScreen = MediaQuery.sizeOf(context).width > 1200.0; - final scaffoldKey = useMemoized(() => GlobalKey()); final rootFocusNode = useFocusNode(); final postFormFocusNode = useFocusNode(); final colors = @@ -176,11 +184,11 @@ class TimelinesPage extends HookConsumerWidget { ), Expanded( child: Scaffold( - key: scaffoldKey, appBar: showTimelineTabBarAtBottom ? null : AppBar( - automaticallyImplyLeading: showMenuButtonInTabBar, + automaticallyImplyLeading: + !isLargeScreen && showMenuButtonInTabBar, title: TimelineTabBar(controller: controller), centerTitle: true, ), @@ -266,7 +274,7 @@ class TimelinesPage extends HookConsumerWidget { elevation: 0.0, child: Row( children: [ - if (showMenuButtonInTabBar) + if (!isLargeScreen && showMenuButtonInTabBar) const Padding( padding: EdgeInsets.all(8.0), child: DrawerButton(), @@ -281,148 +289,33 @@ class TimelinesPage extends HookConsumerWidget { floatingActionButton: tabSettings == null || !showPostForm.value ? Row( mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - if (!isLargeScreen) - FloatingActionButton( - heroTag: const ValueKey(0), - tooltip: t.misskey.menu, - foregroundColor: colors.fg, - backgroundColor: colors.panel, - shape: const CircleBorder(), - onPressed: () => - scaffoldKey.currentState?.openDrawer(), - child: const Icon(Icons.menu), - ), - if (showHomeFAB) - FloatingActionButton( - heroTag: const ValueKey(1), - tooltip: t.misskey.home, - foregroundColor: colors.fg, - backgroundColor: colors.panel, - shape: const CircleBorder(), - onPressed: tabSettings != null - ? () => ref - .read( - timelineScrollControllerProvider( - tabSettings, - ), - ) - .scrollToTop() - : null, - child: const Icon(Icons.home), - ), - if (showNotificationsFAB) - FloatingActionButton( - heroTag: const ValueKey(2), - tooltip: t.misskey.notifications, - foregroundColor: colors.fg.withOpacity( - tabSettings != null && - !tabSettings.account.isGuest - ? 1.0 - : 0.5, - ), - backgroundColor: colors.panel.withOpacity( - tabSettings != null && - !tabSettings.account.isGuest - ? 1.0 - : 0.5, - ), - disabledElevation: 0.0, - shape: const CircleBorder(), - onPressed: tabSettings != null && - !tabSettings.account.isGuest - ? () => context.push( - '/${tabSettings.account}/notifications', - ) - : null, - child: Stack( - children: [ - const Icon(Icons.notifications), - if (i?.hasUnreadNotification ?? false) - DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: colors.accent, - ), - child: const SizedBox( - height: 12.0, - width: 12.0, - ), - ), - ], - ), - ), - if (showShowPostFormFAB) - FloatingActionButton( - heroTag: const ValueKey(3), - tooltip: t.aria.showPostForm, - foregroundColor: colors.fg.withOpacity( - tabSettings != null && - !tabSettings.account.isGuest - ? 1.0 - : 0.5, - ), - backgroundColor: colors.panel.withOpacity( - tabSettings != null && - !tabSettings.account.isGuest - ? 1.0 - : 0.5, - ), - disabledElevation: 0.0, - shape: const CircleBorder(), - onPressed: tabSettings != null && - !tabSettings.account.isGuest - ? () => showPostForm.value = !showPostForm.value - : null, - child: const Icon(Icons.keyboard), - ), - FloatingActionButton( - heroTag: const ValueKey(4), - tooltip: t.misskey.note, - onPressed: tabSettings != null && - !tabSettings.account.isGuest - ? () => - context.push('/${tabSettings.account}/post') - : null, - foregroundColor: colors.fgOnAccent.withOpacity( - tabSettings != null && !tabSettings.account.isGuest - ? 1.0 - : 0.5, - ), - backgroundColor: Colors.transparent, - shape: const CircleBorder(), - disabledElevation: 0.0, - child: Ink( - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - colors.buttonGradateA.withOpacity( - tabSettings != null && - !tabSettings.account.isGuest - ? 1.0 - : 0.5, + children: buttonTypes + .where( + (type) => + !isLargeScreen || + type != TimelinesPageButtonType.menu, + ) + .mapIndexed( + (index, type) => type != null + ? _TimelinesPageButton( + tabSettings: tabSettings, + buttonType: type, + index: index, + mini: mini, + square: square, + showPostForm: () => + showPostForm.value = true, + ) + : SizedBox.square( + dimension: mini ? 40.0 : 56.0, ), - colors.buttonGradateB.withOpacity( - tabSettings != null && - !tabSettings.account.isGuest - ? 1.0 - : 0.5, - ), - ], - ), - borderRadius: BorderRadius.circular(32.0), - ), - child: const Padding( - padding: EdgeInsets.all(16.0), - child: Icon(Icons.edit), - ), - ), - ), - ], + ) + .toList(), ) : const SizedBox.shrink(), - floatingActionButtonLocation: - FloatingActionButtonLocation.centerFloat, + floatingActionButtonLocation: mini + ? FloatingActionButtonLocation.miniCenterFloat + : FloatingActionButtonLocation.centerFloat, ), ), ], @@ -430,3 +323,224 @@ class TimelinesPage extends HookConsumerWidget { ); } } + +class _TimelinesPageButton extends ConsumerWidget { + const _TimelinesPageButton({ + required this.tabSettings, + required this.buttonType, + this.index, + this.mini = false, + this.square = false, + this.showPostForm, + }); + + final TabSettings? tabSettings; + final TimelinesPageButtonType buttonType; + final int? index; + final bool mini; + final bool square; + final void Function()? showPostForm; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final account = tabSettings?.account; + final i = account != null + ? ref.watch(iNotifierProvider(account)).valueOrNull + : null; + final colors = + ref.watch(misskeyColorsProvider(Theme.of(context).brightness)); + + final requireTabSettings = switch (buttonType) { + TimelinesPageButtonType.menu || TimelinesPageButtonType.settings => false, + _ => true, + }; + final requireCredencial = switch (buttonType) { + TimelinesPageButtonType.announcements => false, + TimelinesPageButtonType.antennas => true, + TimelinesPageButtonType.channels => false, + TimelinesPageButtonType.clips => true, + TimelinesPageButtonType.drive => true, + TimelinesPageButtonType.explore => false, + TimelinesPageButtonType.favorites => true, + TimelinesPageButtonType.gallery => false, + TimelinesPageButtonType.games => false, + TimelinesPageButtonType.home => false, + TimelinesPageButtonType.instanceInfo => false, + TimelinesPageButtonType.lists => true, + TimelinesPageButtonType.lookup => true, + TimelinesPageButtonType.menu => false, + TimelinesPageButtonType.notifications => true, + TimelinesPageButtonType.pages => false, + TimelinesPageButtonType.play => false, + TimelinesPageButtonType.postForm => true, + TimelinesPageButtonType.note => true, + TimelinesPageButtonType.profile => true, + TimelinesPageButtonType.reload => false, + TimelinesPageButtonType.search => false, + TimelinesPageButtonType.settings => false, + }; + final disabled = (requireTabSettings && tabSettings == null) || + (requireCredencial && (tabSettings?.account.isGuest ?? true)); + final primary = buttonType == TimelinesPageButtonType.note; + + final tooltip = switch (buttonType) { + TimelinesPageButtonType.announcements => t.misskey.announcements, + TimelinesPageButtonType.antennas => t.misskey.antennas, + TimelinesPageButtonType.channels => t.misskey.channel, + TimelinesPageButtonType.clips => t.misskey.clips, + TimelinesPageButtonType.drive => t.misskey.drive, + TimelinesPageButtonType.explore => t.misskey.explore, + TimelinesPageButtonType.favorites => t.misskey.favorites, + TimelinesPageButtonType.gallery => t.misskey.gallery, + TimelinesPageButtonType.games => 'Misskey Games', + TimelinesPageButtonType.home => t.misskey.home, + TimelinesPageButtonType.instanceInfo => t.misskey.instanceInfo, + TimelinesPageButtonType.lists => t.misskey.lists, + TimelinesPageButtonType.lookup => t.misskey.lookup, + TimelinesPageButtonType.menu => t.misskey.menu, + TimelinesPageButtonType.note => t.misskey.note, + TimelinesPageButtonType.notifications => t.misskey.notifications, + TimelinesPageButtonType.pages => t.misskey.pages, + TimelinesPageButtonType.play => 'Play', + TimelinesPageButtonType.postForm => t.aria.postForm, + TimelinesPageButtonType.profile => t.misskey.profile, + TimelinesPageButtonType.reload => t.misskey.reload, + TimelinesPageButtonType.search => t.misskey.search, + TimelinesPageButtonType.settings => t.misskey.settings, + }; + final onPressed = switch (buttonType) { + TimelinesPageButtonType.announcements => () => + context.push('/$account/announcements'), + TimelinesPageButtonType.antennas => () => + context.push('/$account/antennas'), + TimelinesPageButtonType.channels => () => + context.push('/$account/channels'), + TimelinesPageButtonType.clips => () => context.push('/$account/clips'), + TimelinesPageButtonType.drive => () => context.push('/$account/drive'), + TimelinesPageButtonType.explore => () => + context.push('/$account/explore'), + TimelinesPageButtonType.favorites => () => + context.push('/$account/favorites'), + TimelinesPageButtonType.gallery => () => + context.push('/$account/gallery'), + TimelinesPageButtonType.games => () => context.push('/$account/games'), + TimelinesPageButtonType.home => () => ref + .read(timelineScrollControllerProvider(tabSettings!)) + .scrollToTop(), + TimelinesPageButtonType.instanceInfo => () => + context.push('/$account/servers/${account!.host}'), + TimelinesPageButtonType.lists => () => context.push('/$account/lists'), + TimelinesPageButtonType.lookup => () async { + final result = await showTextFieldDialog( + context, + title: Text(t.misskey.lookup), + ); + if (!context.mounted) return; + if (result == null) return; + await lookup(ref, account!, result.trim()); + }, + TimelinesPageButtonType.menu => () => Scaffold.of(context).openDrawer(), + TimelinesPageButtonType.note => () => context.push('/$account/post'), + TimelinesPageButtonType.notifications => () => + context.push('/$account/notifications'), + TimelinesPageButtonType.pages => () => context.push('/$account/pages'), + TimelinesPageButtonType.play => () => context.push('/$account/play'), + TimelinesPageButtonType.postForm => showPostForm, + TimelinesPageButtonType.profile => () => + context.push('/$account/@${account!.username}'), + TimelinesPageButtonType.reload => () => reloadTimeline(ref, tabSettings!), + TimelinesPageButtonType.search => () => context.push('/$account/search'), + TimelinesPageButtonType.settings => () => context.push('/settings'), + }; + final child = switch (buttonType) { + TimelinesPageButtonType.announcements => Stack( + children: [ + const Icon(Icons.campaign), + if (i?.hasUnreadAnnouncement ?? false) + DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).colorScheme.primary, + ), + child: const SizedBox( + height: 12.0, + width: 12.0, + ), + ), + ], + ), + TimelinesPageButtonType.antennas => + const Icon(Icons.settings_input_antenna), + TimelinesPageButtonType.channels => const Icon(Icons.tv), + TimelinesPageButtonType.clips => const Icon(Icons.attach_file), + TimelinesPageButtonType.drive => const Icon(Icons.cloud), + TimelinesPageButtonType.explore => const Icon(Icons.tag), + TimelinesPageButtonType.favorites => const Icon(Icons.star_rounded), + TimelinesPageButtonType.gallery => const Icon(Icons.collections), + TimelinesPageButtonType.games => const Icon(Icons.games), + TimelinesPageButtonType.home => const Icon(Icons.home), + TimelinesPageButtonType.instanceInfo => const Icon(Icons.dns), + TimelinesPageButtonType.lists => const Icon(Icons.list), + TimelinesPageButtonType.lookup => const Icon(Icons.travel_explore), + TimelinesPageButtonType.menu => const Icon(Icons.menu), + TimelinesPageButtonType.note => Ink( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + colors.buttonGradateA.withOpacity(!disabled ? 1.0 : 0.5), + colors.buttonGradateB.withOpacity(!disabled ? 1.0 : 0.5), + ], + ), + borderRadius: BorderRadius.circular( + square + ? mini + ? 12.0 + : 16.0 + : 32.0, + ), + ), + child: Padding( + padding: EdgeInsets.all(mini ? 8.0 : 16.0), + child: const Icon(Icons.edit), + ), + ), + TimelinesPageButtonType.notifications => Stack( + children: [ + const Icon(Icons.notifications), + if (i?.hasUnreadNotification ?? false) + DecoratedBox( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: colors.accent, + ), + child: const SizedBox(height: 12.0, width: 12.0), + ), + ], + ), + TimelinesPageButtonType.pages => const Icon(Icons.article), + TimelinesPageButtonType.play => const Icon(Icons.play_arrow), + TimelinesPageButtonType.postForm => const Icon(Icons.keyboard), + TimelinesPageButtonType.profile => account != null && i != null + ? UserAvatar(account: account, user: i, size: 28.0) + : const Icon(Icons.person), + TimelinesPageButtonType.reload => const Icon(Icons.refresh), + TimelinesPageButtonType.search => const Icon(Icons.search), + TimelinesPageButtonType.settings => const Icon(Icons.settings), + }; + + return FloatingActionButton( + heroTag: ValueKey('<_TimelinesPageButton tag $index>'), + tooltip: tooltip, + foregroundColor: (primary ? colors.fgOnAccent : colors.fg) + .withOpacity(!disabled ? 1.0 : 0.5), + backgroundColor: primary + ? Colors.transparent + : colors.panel.withOpacity(!disabled ? 1.0 : 0.5), + disabledElevation: 0.0, + shape: square ? null : const CircleBorder(), + mini: mini, + onPressed: !disabled ? onPressed : null, + child: child, + ); + } +}