diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index 9bb285eb..3884f7ce 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -1597,5 +1597,6 @@ "unsetAsThumbnail": "Unset as thumbnail", "sidebar": "Sidebar", "dropFilesHere": "Drop your files here", - "dragAndDropToAttach": "Drag your files here to attach it" + "dragAndDropToAttach": "Drag your files here to attach it", + "customize": "Customize" } diff --git a/lib/pods/chat/chat_subscribe.g.dart b/lib/pods/chat/chat_subscribe.g.dart index d06ed26e..6f1e7f36 100644 --- a/lib/pods/chat/chat_subscribe.g.dart +++ b/lib/pods/chat/chat_subscribe.g.dart @@ -59,7 +59,7 @@ final class ChatSubscribeNotifierProvider } String _$chatSubscribeNotifierHash() => - r'944cb0c1b1805050470d4b79c60937f622d7b716'; + r'f2f5059a975fc44a41850459d6b7d041ff9d41cb'; final class ChatSubscribeNotifierFamily extends $Family with diff --git a/lib/pods/config.dart b/lib/pods/config.dart index f540bea5..f92dcf94 100644 --- a/lib/pods/config.dart +++ b/lib/pods/config.dart @@ -42,6 +42,7 @@ const kAppAskedReview = 'app_asked_review'; const kAppDashSearchEngine = 'app_dash_search_engine'; const kAppDefaultScreen = 'app_default_screen'; const kAppShowFediverseContent = 'app_show_fediverse_content'; +const kAppDashboardConfig = 'app_dashboard_config'; // Will be overrided by the ProviderScope final sharedPreferencesProvider = Provider((ref) { @@ -68,6 +69,19 @@ sealed class ThemeColors with _$ThemeColors { _$ThemeColorsFromJson(json); } +@freezed +sealed class DashboardConfig with _$DashboardConfig { + factory DashboardConfig({ + required List verticalLayouts, + required List horizontalLayouts, + required bool showSearchBar, + required bool showClockAndCountdown, + }) = _DashboardConfig; + + factory DashboardConfig.fromJson(Map json) => + _$DashboardConfigFromJson(json); +} + @freezed sealed class AppSettings with _$AppSettings { const factory AppSettings({ @@ -94,6 +108,7 @@ sealed class AppSettings with _$AppSettings { required String? dashSearchEngine, required String? defaultScreen, required bool showFediverseContent, + required DashboardConfig? dashboardConfig, }) = _AppSettings; } @@ -126,6 +141,7 @@ class AppSettingsNotifier extends _$AppSettingsNotifier { dashSearchEngine: prefs.getString(kAppDashSearchEngine), defaultScreen: prefs.getString(kAppDefaultScreen), showFediverseContent: prefs.getBool(kAppShowFediverseContent) ?? true, + dashboardConfig: _getDashboardConfigFromPrefs(prefs), ); } @@ -158,6 +174,18 @@ class AppSettingsNotifier extends _$AppSettingsNotifier { } } + DashboardConfig? _getDashboardConfigFromPrefs(SharedPreferences prefs) { + final jsonString = prefs.getString(kAppDashboardConfig); + if (jsonString == null) return null; + + try { + final json = jsonDecode(jsonString); + return DashboardConfig.fromJson(json); + } catch (e) { + return null; + } + } + void setDefaultPoolId(String? value) { final prefs = ref.read(sharedPreferencesProvider); if (value != null) { @@ -324,6 +352,17 @@ class AppSettingsNotifier extends _$AppSettingsNotifier { prefs.setBool(kAppShowFediverseContent, value); state = state.copyWith(showFediverseContent: value); } + + void setDashboardConfig(DashboardConfig? value) { + final prefs = ref.read(sharedPreferencesProvider); + if (value != null) { + final json = jsonEncode(value.toJson()); + prefs.setString(kAppDashboardConfig, json); + } else { + prefs.remove(kAppDashboardConfig); + } + state = state.copyWith(dashboardConfig: value); + } } final updateInfoProvider = @@ -340,4 +379,4 @@ class UpdateInfoNotifier extends Notifier<(String?, String?)> { void setUpdate(String newVersion, String newChangelog) { state = (newVersion, newChangelog); } -} +} \ No newline at end of file diff --git a/lib/pods/config.freezed.dart b/lib/pods/config.freezed.dart index f295cb50..2af84cc0 100644 --- a/lib/pods/config.freezed.dart +++ b/lib/pods/config.freezed.dart @@ -281,6 +281,284 @@ as int?, } +} + + +/// @nodoc +mixin _$DashboardConfig { + + List get verticalLayouts; List get horizontalLayouts; bool get showSearchBar; bool get showClockAndCountdown; +/// Create a copy of DashboardConfig +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$DashboardConfigCopyWith get copyWith => _$DashboardConfigCopyWithImpl(this as DashboardConfig, _$identity); + + /// Serializes this DashboardConfig to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is DashboardConfig&&const DeepCollectionEquality().equals(other.verticalLayouts, verticalLayouts)&&const DeepCollectionEquality().equals(other.horizontalLayouts, horizontalLayouts)&&(identical(other.showSearchBar, showSearchBar) || other.showSearchBar == showSearchBar)&&(identical(other.showClockAndCountdown, showClockAndCountdown) || other.showClockAndCountdown == showClockAndCountdown)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(verticalLayouts),const DeepCollectionEquality().hash(horizontalLayouts),showSearchBar,showClockAndCountdown); + +@override +String toString() { + return 'DashboardConfig(verticalLayouts: $verticalLayouts, horizontalLayouts: $horizontalLayouts, showSearchBar: $showSearchBar, showClockAndCountdown: $showClockAndCountdown)'; +} + + +} + +/// @nodoc +abstract mixin class $DashboardConfigCopyWith<$Res> { + factory $DashboardConfigCopyWith(DashboardConfig value, $Res Function(DashboardConfig) _then) = _$DashboardConfigCopyWithImpl; +@useResult +$Res call({ + List verticalLayouts, List horizontalLayouts, bool showSearchBar, bool showClockAndCountdown +}); + + + + +} +/// @nodoc +class _$DashboardConfigCopyWithImpl<$Res> + implements $DashboardConfigCopyWith<$Res> { + _$DashboardConfigCopyWithImpl(this._self, this._then); + + final DashboardConfig _self; + final $Res Function(DashboardConfig) _then; + +/// Create a copy of DashboardConfig +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? verticalLayouts = null,Object? horizontalLayouts = null,Object? showSearchBar = null,Object? showClockAndCountdown = null,}) { + return _then(_self.copyWith( +verticalLayouts: null == verticalLayouts ? _self.verticalLayouts : verticalLayouts // ignore: cast_nullable_to_non_nullable +as List,horizontalLayouts: null == horizontalLayouts ? _self.horizontalLayouts : horizontalLayouts // ignore: cast_nullable_to_non_nullable +as List,showSearchBar: null == showSearchBar ? _self.showSearchBar : showSearchBar // ignore: cast_nullable_to_non_nullable +as bool,showClockAndCountdown: null == showClockAndCountdown ? _self.showClockAndCountdown : showClockAndCountdown // ignore: cast_nullable_to_non_nullable +as bool, + )); +} + +} + + +/// Adds pattern-matching-related methods to [DashboardConfig]. +extension DashboardConfigPatterns on DashboardConfig { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _DashboardConfig value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _DashboardConfig() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _DashboardConfig value) $default,){ +final _that = this; +switch (_that) { +case _DashboardConfig(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _DashboardConfig value)? $default,){ +final _that = this; +switch (_that) { +case _DashboardConfig() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( List verticalLayouts, List horizontalLayouts, bool showSearchBar, bool showClockAndCountdown)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _DashboardConfig() when $default != null: +return $default(_that.verticalLayouts,_that.horizontalLayouts,_that.showSearchBar,_that.showClockAndCountdown);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// As opposed to `map`, this offers destructuring. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case Subclass2(:final field2): +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult when(TResult Function( List verticalLayouts, List horizontalLayouts, bool showSearchBar, bool showClockAndCountdown) $default,) {final _that = this; +switch (_that) { +case _DashboardConfig(): +return $default(_that.verticalLayouts,_that.horizontalLayouts,_that.showSearchBar,_that.showClockAndCountdown);} +} +/// A variant of `when` that fallback to returning `null` +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? whenOrNull(TResult? Function( List verticalLayouts, List horizontalLayouts, bool showSearchBar, bool showClockAndCountdown)? $default,) {final _that = this; +switch (_that) { +case _DashboardConfig() when $default != null: +return $default(_that.verticalLayouts,_that.horizontalLayouts,_that.showSearchBar,_that.showClockAndCountdown);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _DashboardConfig implements DashboardConfig { + _DashboardConfig({required final List verticalLayouts, required final List horizontalLayouts, required this.showSearchBar, required this.showClockAndCountdown}): _verticalLayouts = verticalLayouts,_horizontalLayouts = horizontalLayouts; + factory _DashboardConfig.fromJson(Map json) => _$DashboardConfigFromJson(json); + + final List _verticalLayouts; +@override List get verticalLayouts { + if (_verticalLayouts is EqualUnmodifiableListView) return _verticalLayouts; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_verticalLayouts); +} + + final List _horizontalLayouts; +@override List get horizontalLayouts { + if (_horizontalLayouts is EqualUnmodifiableListView) return _horizontalLayouts; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_horizontalLayouts); +} + +@override final bool showSearchBar; +@override final bool showClockAndCountdown; + +/// Create a copy of DashboardConfig +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$DashboardConfigCopyWith<_DashboardConfig> get copyWith => __$DashboardConfigCopyWithImpl<_DashboardConfig>(this, _$identity); + +@override +Map toJson() { + return _$DashboardConfigToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _DashboardConfig&&const DeepCollectionEquality().equals(other._verticalLayouts, _verticalLayouts)&&const DeepCollectionEquality().equals(other._horizontalLayouts, _horizontalLayouts)&&(identical(other.showSearchBar, showSearchBar) || other.showSearchBar == showSearchBar)&&(identical(other.showClockAndCountdown, showClockAndCountdown) || other.showClockAndCountdown == showClockAndCountdown)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_verticalLayouts),const DeepCollectionEquality().hash(_horizontalLayouts),showSearchBar,showClockAndCountdown); + +@override +String toString() { + return 'DashboardConfig(verticalLayouts: $verticalLayouts, horizontalLayouts: $horizontalLayouts, showSearchBar: $showSearchBar, showClockAndCountdown: $showClockAndCountdown)'; +} + + +} + +/// @nodoc +abstract mixin class _$DashboardConfigCopyWith<$Res> implements $DashboardConfigCopyWith<$Res> { + factory _$DashboardConfigCopyWith(_DashboardConfig value, $Res Function(_DashboardConfig) _then) = __$DashboardConfigCopyWithImpl; +@override @useResult +$Res call({ + List verticalLayouts, List horizontalLayouts, bool showSearchBar, bool showClockAndCountdown +}); + + + + +} +/// @nodoc +class __$DashboardConfigCopyWithImpl<$Res> + implements _$DashboardConfigCopyWith<$Res> { + __$DashboardConfigCopyWithImpl(this._self, this._then); + + final _DashboardConfig _self; + final $Res Function(_DashboardConfig) _then; + +/// Create a copy of DashboardConfig +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? verticalLayouts = null,Object? horizontalLayouts = null,Object? showSearchBar = null,Object? showClockAndCountdown = null,}) { + return _then(_DashboardConfig( +verticalLayouts: null == verticalLayouts ? _self._verticalLayouts : verticalLayouts // ignore: cast_nullable_to_non_nullable +as List,horizontalLayouts: null == horizontalLayouts ? _self._horizontalLayouts : horizontalLayouts // ignore: cast_nullable_to_non_nullable +as List,showSearchBar: null == showSearchBar ? _self.showSearchBar : showSearchBar // ignore: cast_nullable_to_non_nullable +as bool,showClockAndCountdown: null == showClockAndCountdown ? _self.showClockAndCountdown : showClockAndCountdown // ignore: cast_nullable_to_non_nullable +as bool, + )); +} + + } /// @nodoc @@ -290,7 +568,7 @@ mixin _$AppSettings { ThemeColors? get customColors; Size? get windowSize;// The window size for desktop platforms double get windowOpacity;// The window opacity for desktop platforms double get cardTransparency;// The card background opacity - String? get defaultPoolId; String get messageDisplayStyle; String? get themeMode; bool get disableAnimation; bool get groupedChatList; String? get firstLaunchAt; bool get askedReview; String? get dashSearchEngine; String? get defaultScreen; bool get showFediverseContent; + String? get defaultPoolId; String get messageDisplayStyle; String? get themeMode; bool get disableAnimation; bool get groupedChatList; String? get firstLaunchAt; bool get askedReview; String? get dashSearchEngine; String? get defaultScreen; bool get showFediverseContent; DashboardConfig? get dashboardConfig; /// Create a copy of AppSettings /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -301,16 +579,16 @@ $AppSettingsCopyWith get copyWith => _$AppSettingsCopyWithImpl Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,disableAnimation,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine,defaultScreen,showFediverseContent]); +int get hashCode => Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,disableAnimation,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine,defaultScreen,showFediverseContent,dashboardConfig]); @override String toString() { - return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, disableAnimation: $disableAnimation, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine, defaultScreen: $defaultScreen, showFediverseContent: $showFediverseContent)'; + return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, disableAnimation: $disableAnimation, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine, defaultScreen: $defaultScreen, showFediverseContent: $showFediverseContent, dashboardConfig: $dashboardConfig)'; } @@ -321,11 +599,11 @@ abstract mixin class $AppSettingsCopyWith<$Res> { factory $AppSettingsCopyWith(AppSettings value, $Res Function(AppSettings) _then) = _$AppSettingsCopyWithImpl; @useResult $Res call({ - bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent + bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent, DashboardConfig? dashboardConfig }); -$ThemeColorsCopyWith<$Res>? get customColors; +$ThemeColorsCopyWith<$Res>? get customColors;$DashboardConfigCopyWith<$Res>? get dashboardConfig; } /// @nodoc @@ -338,7 +616,7 @@ class _$AppSettingsCopyWithImpl<$Res> /// Create a copy of AppSettings /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? disableAnimation = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,Object? defaultScreen = freezed,Object? showFediverseContent = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? disableAnimation = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,Object? defaultScreen = freezed,Object? showFediverseContent = null,Object? dashboardConfig = freezed,}) { return _then(_self.copyWith( dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable as bool,soundEffects: null == soundEffects ? _self.soundEffects : soundEffects // ignore: cast_nullable_to_non_nullable @@ -363,7 +641,8 @@ as String?,askedReview: null == askedReview ? _self.askedReview : askedReview // as bool,dashSearchEngine: freezed == dashSearchEngine ? _self.dashSearchEngine : dashSearchEngine // ignore: cast_nullable_to_non_nullable as String?,defaultScreen: freezed == defaultScreen ? _self.defaultScreen : defaultScreen // ignore: cast_nullable_to_non_nullable as String?,showFediverseContent: null == showFediverseContent ? _self.showFediverseContent : showFediverseContent // ignore: cast_nullable_to_non_nullable -as bool, +as bool,dashboardConfig: freezed == dashboardConfig ? _self.dashboardConfig : dashboardConfig // ignore: cast_nullable_to_non_nullable +as DashboardConfig?, )); } /// Create a copy of AppSettings @@ -378,6 +657,18 @@ $ThemeColorsCopyWith<$Res>? get customColors { return $ThemeColorsCopyWith<$Res>(_self.customColors!, (value) { return _then(_self.copyWith(customColors: value)); }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$DashboardConfigCopyWith<$Res>? get dashboardConfig { + if (_self.dashboardConfig == null) { + return null; + } + + return $DashboardConfigCopyWith<$Res>(_self.dashboardConfig!, (value) { + return _then(_self.copyWith(dashboardConfig: value)); + }); } } @@ -457,10 +748,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent, DashboardConfig? dashboardConfig)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _AppSettings() when $default != null: -return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen,_that.showFediverseContent);case _: +return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen,_that.showFediverseContent,_that.dashboardConfig);case _: return orElse(); } @@ -478,10 +769,10 @@ return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_ /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent, DashboardConfig? dashboardConfig) $default,) {final _that = this; switch (_that) { case _AppSettings(): -return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen,_that.showFediverseContent);} +return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen,_that.showFediverseContent,_that.dashboardConfig);} } /// A variant of `when` that fallback to returning `null` /// @@ -495,10 +786,10 @@ return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_ /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent, DashboardConfig? dashboardConfig)? $default,) {final _that = this; switch (_that) { case _AppSettings() when $default != null: -return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen,_that.showFediverseContent);case _: +return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen,_that.showFediverseContent,_that.dashboardConfig);case _: return null; } @@ -510,7 +801,7 @@ return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_ class _AppSettings implements AppSettings { - const _AppSettings({required this.dataSavingMode, required this.soundEffects, required this.festivalFeatures, required this.enterToSend, required this.appBarTransparent, required this.showBackgroundImage, required this.notifyWithHaptic, required this.customFonts, required this.appColorScheme, required this.customColors, required this.windowSize, required this.windowOpacity, required this.cardTransparency, required this.defaultPoolId, required this.messageDisplayStyle, required this.themeMode, required this.disableAnimation, required this.groupedChatList, required this.firstLaunchAt, required this.askedReview, required this.dashSearchEngine, required this.defaultScreen, required this.showFediverseContent}); + const _AppSettings({required this.dataSavingMode, required this.soundEffects, required this.festivalFeatures, required this.enterToSend, required this.appBarTransparent, required this.showBackgroundImage, required this.notifyWithHaptic, required this.customFonts, required this.appColorScheme, required this.customColors, required this.windowSize, required this.windowOpacity, required this.cardTransparency, required this.defaultPoolId, required this.messageDisplayStyle, required this.themeMode, required this.disableAnimation, required this.groupedChatList, required this.firstLaunchAt, required this.askedReview, required this.dashSearchEngine, required this.defaultScreen, required this.showFediverseContent, required this.dashboardConfig}); @override final bool dataSavingMode; @@ -540,6 +831,7 @@ class _AppSettings implements AppSettings { @override final String? dashSearchEngine; @override final String? defaultScreen; @override final bool showFediverseContent; +@override final DashboardConfig? dashboardConfig; /// Create a copy of AppSettings /// with the given fields replaced by the non-null parameter values. @@ -551,16 +843,16 @@ _$AppSettingsCopyWith<_AppSettings> get copyWith => __$AppSettingsCopyWithImpl<_ @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.festivalFeatures, festivalFeatures) || other.festivalFeatures == festivalFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.notifyWithHaptic, notifyWithHaptic) || other.notifyWithHaptic == notifyWithHaptic)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation)&&(identical(other.groupedChatList, groupedChatList) || other.groupedChatList == groupedChatList)&&(identical(other.firstLaunchAt, firstLaunchAt) || other.firstLaunchAt == firstLaunchAt)&&(identical(other.askedReview, askedReview) || other.askedReview == askedReview)&&(identical(other.dashSearchEngine, dashSearchEngine) || other.dashSearchEngine == dashSearchEngine)&&(identical(other.defaultScreen, defaultScreen) || other.defaultScreen == defaultScreen)&&(identical(other.showFediverseContent, showFediverseContent) || other.showFediverseContent == showFediverseContent)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.festivalFeatures, festivalFeatures) || other.festivalFeatures == festivalFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.notifyWithHaptic, notifyWithHaptic) || other.notifyWithHaptic == notifyWithHaptic)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation)&&(identical(other.groupedChatList, groupedChatList) || other.groupedChatList == groupedChatList)&&(identical(other.firstLaunchAt, firstLaunchAt) || other.firstLaunchAt == firstLaunchAt)&&(identical(other.askedReview, askedReview) || other.askedReview == askedReview)&&(identical(other.dashSearchEngine, dashSearchEngine) || other.dashSearchEngine == dashSearchEngine)&&(identical(other.defaultScreen, defaultScreen) || other.defaultScreen == defaultScreen)&&(identical(other.showFediverseContent, showFediverseContent) || other.showFediverseContent == showFediverseContent)&&(identical(other.dashboardConfig, dashboardConfig) || other.dashboardConfig == dashboardConfig)); } @override -int get hashCode => Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,disableAnimation,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine,defaultScreen,showFediverseContent]); +int get hashCode => Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,disableAnimation,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine,defaultScreen,showFediverseContent,dashboardConfig]); @override String toString() { - return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, disableAnimation: $disableAnimation, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine, defaultScreen: $defaultScreen, showFediverseContent: $showFediverseContent)'; + return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, disableAnimation: $disableAnimation, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine, defaultScreen: $defaultScreen, showFediverseContent: $showFediverseContent, dashboardConfig: $dashboardConfig)'; } @@ -571,11 +863,11 @@ abstract mixin class _$AppSettingsCopyWith<$Res> implements $AppSettingsCopyWith factory _$AppSettingsCopyWith(_AppSettings value, $Res Function(_AppSettings) _then) = __$AppSettingsCopyWithImpl; @override @useResult $Res call({ - bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent + bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen, bool showFediverseContent, DashboardConfig? dashboardConfig }); -@override $ThemeColorsCopyWith<$Res>? get customColors; +@override $ThemeColorsCopyWith<$Res>? get customColors;@override $DashboardConfigCopyWith<$Res>? get dashboardConfig; } /// @nodoc @@ -588,7 +880,7 @@ class __$AppSettingsCopyWithImpl<$Res> /// Create a copy of AppSettings /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? disableAnimation = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,Object? defaultScreen = freezed,Object? showFediverseContent = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? disableAnimation = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,Object? defaultScreen = freezed,Object? showFediverseContent = null,Object? dashboardConfig = freezed,}) { return _then(_AppSettings( dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable as bool,soundEffects: null == soundEffects ? _self.soundEffects : soundEffects // ignore: cast_nullable_to_non_nullable @@ -613,7 +905,8 @@ as String?,askedReview: null == askedReview ? _self.askedReview : askedReview // as bool,dashSearchEngine: freezed == dashSearchEngine ? _self.dashSearchEngine : dashSearchEngine // ignore: cast_nullable_to_non_nullable as String?,defaultScreen: freezed == defaultScreen ? _self.defaultScreen : defaultScreen // ignore: cast_nullable_to_non_nullable as String?,showFediverseContent: null == showFediverseContent ? _self.showFediverseContent : showFediverseContent // ignore: cast_nullable_to_non_nullable -as bool, +as bool,dashboardConfig: freezed == dashboardConfig ? _self.dashboardConfig : dashboardConfig // ignore: cast_nullable_to_non_nullable +as DashboardConfig?, )); } @@ -629,6 +922,18 @@ $ThemeColorsCopyWith<$Res>? get customColors { return $ThemeColorsCopyWith<$Res>(_self.customColors!, (value) { return _then(_self.copyWith(customColors: value)); }); +}/// Create a copy of AppSettings +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$DashboardConfigCopyWith<$Res>? get dashboardConfig { + if (_self.dashboardConfig == null) { + return null; + } + + return $DashboardConfigCopyWith<$Res>(_self.dashboardConfig!, (value) { + return _then(_self.copyWith(dashboardConfig: value)); + }); } } diff --git a/lib/pods/config.g.dart b/lib/pods/config.g.dart index 8c52da01..3330be66 100644 --- a/lib/pods/config.g.dart +++ b/lib/pods/config.g.dart @@ -25,6 +25,26 @@ Map _$ThemeColorsToJson(_ThemeColors instance) => 'error': instance.error, }; +_DashboardConfig _$DashboardConfigFromJson(Map json) => + _DashboardConfig( + verticalLayouts: (json['vertical_layouts'] as List) + .map((e) => e as String) + .toList(), + horizontalLayouts: (json['horizontal_layouts'] as List) + .map((e) => e as String) + .toList(), + showSearchBar: json['show_search_bar'] as bool, + showClockAndCountdown: json['show_clock_and_countdown'] as bool, + ); + +Map _$DashboardConfigToJson(_DashboardConfig instance) => + { + 'vertical_layouts': instance.verticalLayouts, + 'horizontal_layouts': instance.horizontalLayouts, + 'show_search_bar': instance.showSearchBar, + 'show_clock_and_countdown': instance.showClockAndCountdown, + }; + // ************************************************************************** // RiverpodGenerator // ************************************************************************** @@ -65,7 +85,7 @@ final class AppSettingsNotifierProvider } String _$appSettingsNotifierHash() => - r'64cf6dfda90fc634336d96bd5313b2a07b19eccb'; + r'0a7f75bd95850b0c564b29c57912ec8fcac53f09'; abstract class _$AppSettingsNotifier extends $Notifier { AppSettings build(); diff --git a/lib/pods/notification.g.dart b/lib/pods/notification.g.dart index 6c82e5e6..479b2b7a 100644 --- a/lib/pods/notification.g.dart +++ b/lib/pods/notification.g.dart @@ -41,7 +41,7 @@ final class NotificationStateProvider } } -String _$notificationStateHash() => r'8625e77d28d71237d86f6d06efab437aa7c09df1'; +String _$notificationStateHash() => r'4597cfc7c75dd0fd05dab65f78265a3ae10d23e7'; abstract class _$NotificationState extends $Notifier> { List build(); diff --git a/lib/screens/dashboard/dash.dart b/lib/screens/dashboard/dash.dart index e19f8232..d1a0fd72 100644 --- a/lib/screens/dashboard/dash.dart +++ b/lib/screens/dashboard/dash.dart @@ -31,6 +31,8 @@ import 'package:island/widgets/share/share_sheet.dart'; import 'dart:async'; import 'package:styled_widget/styled_widget.dart'; +import 'package:island/screens/dashboard/dash_customize.dart'; +import 'package:island/pods/config.dart'; class DashboardScreen extends HookConsumerWidget { const DashboardScreen({super.key}); @@ -44,6 +46,106 @@ class DashboardScreen extends HookConsumerWidget { } } +// Helper functions for dynamic dashboard rendering +class DashboardRenderer { + // Map individual card IDs to widgets + static Widget buildCard(String cardId, WidgetRef ref) { + switch (cardId) { + case 'checkIn': + return CheckInWidget(margin: EdgeInsets.zero); + case 'fortuneGraph': + return Card( + margin: EdgeInsets.zero, + child: FortuneGraphWidget( + events: ref.watch( + eventCalendarProvider( + EventCalendarQuery( + uname: 'me', + year: DateTime.now().year, + month: DateTime.now().month, + ), + ), + ), + ), + ); + case 'fortuneCard': + return FortuneCard(unlimited: true); + case 'postFeatured': + return ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 400), + child: PostFeaturedList(), + ); + case 'friendsOverview': + return FriendsOverviewWidget(); + case 'notifications': + return NotificationsCard(); + case 'chatList': + return ChatListCard(); + default: + return const SizedBox.shrink(); + } + } + + // Map column group IDs to column widgets + static Widget buildColumn(String columnId, WidgetRef ref) { + switch (columnId) { + case 'activityColumn': + return SizedBox( + width: 400, + child: Column( + spacing: 16, + children: [ + CheckInWidget(margin: EdgeInsets.zero), + Card( + margin: EdgeInsets.zero, + child: FortuneGraphWidget( + events: ref.watch( + eventCalendarProvider( + EventCalendarQuery( + uname: 'me', + year: DateTime.now().year, + month: DateTime.now().month, + ), + ), + ), + ), + ), + Expanded(child: FortuneCard()), + ], + ), + ); + case 'postsColumn': + return SizedBox( + width: 400, + child: PostFeaturedList(collapsable: false), + ); + case 'socialColumn': + return SizedBox( + width: 400, + child: Column( + spacing: 16, + children: [ + FriendsOverviewWidget(), + Expanded(child: NotificationsCard()), + ], + ), + ); + case 'chatsColumn': + return SizedBox( + width: 400, + child: Column( + spacing: 16, + children: [Expanded(child: ChatListCard())], + ), + ); + default: + return const SizedBox.shrink(); + } + } +} + + + class DashboardGrid extends HookConsumerWidget { const DashboardGrid({super.key}); @@ -148,6 +250,42 @@ class DashboardGrid extends HookConsumerWidget { ], ), ), + // Customize button + Positioned( + bottom: 16, + right: 16, + child: TextButton.icon( + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + useRootNavigator: true, + builder: (context) => const DashboardCustomizationSheet(), + ); + }, + icon: Icon( + Symbols.tune, + size: 16, + color: Theme.of(context).colorScheme.primary, + ), + label: Text( + 'customize', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ), + style: TextButton.styleFrom( + backgroundColor: Theme.of(context).colorScheme.surface, + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + ), + ), + ), if (dragging.value) Positioned.fill( child: Container( @@ -189,55 +327,26 @@ class _DashboardGridWide extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final userInfo = ref.watch(userInfoProvider); + final appSettings = ref.watch(appSettingsProvider); + + final List children = []; + + // Always include account unactivated card if user is not activated + if (userInfo.value != null && userInfo.value?.activatedAt == null) { + children.add(SizedBox(width: 400, child: AccountUnactivatedCard())); + } + + // Add configured columns in the specified order + final horizontalLayouts = appSettings.dashboardConfig?.horizontalLayouts ?? + ['activityColumn', 'postsColumn', 'socialColumn', 'chatsColumn']; + + for (final columnId in horizontalLayouts) { + children.add(DashboardRenderer.buildColumn(columnId, ref)); + } return Row( spacing: 16, - children: [ - if (userInfo.value != null && userInfo.value?.activatedAt == null) - SizedBox(width: 400, child: AccountUnactivatedCard()), - SizedBox( - width: 400, - child: Column( - spacing: 16, - children: [ - CheckInWidget(margin: EdgeInsets.zero), - Card( - margin: EdgeInsets.zero, - child: FortuneGraphWidget( - events: ref.watch( - eventCalendarProvider( - EventCalendarQuery( - uname: 'me', - year: DateTime.now().year, - month: DateTime.now().month, - ), - ), - ), - ), - ), - Expanded(child: FortuneCard()), - ], - ), - ), - SizedBox(width: 400, child: PostFeaturedList(collapsable: false)), - SizedBox( - width: 400, - child: Column( - spacing: 16, - children: [ - FriendsOverviewWidget(), - Expanded(child: NotificationsCard()), - ], - ), - ), - SizedBox( - width: 400, - child: Column( - spacing: 16, - children: [Expanded(child: ChatListCard())], - ), - ), - ], + children: children, ); } } @@ -248,36 +357,26 @@ class _DashboardGridNarrow extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final userInfo = ref.watch(userInfoProvider); + final appSettings = ref.watch(appSettingsProvider); + + final List children = []; + + // Always include account unactivated card if user is not activated + if (userInfo.value != null && userInfo.value?.activatedAt == null) { + children.add(AccountUnactivatedCard()); + } + + // Add configured cards in the specified order + final verticalLayouts = appSettings.dashboardConfig?.verticalLayouts ?? + ['checkIn', 'fortuneCard', 'postFeatured', 'friendsOverview', 'notifications', 'chatList', 'fortuneGraph']; + + for (final cardId in verticalLayouts) { + children.add(DashboardRenderer.buildCard(cardId, ref)); + } return Column( spacing: 16, - children: [ - if (userInfo.value != null && userInfo.value?.activatedAt == null) - AccountUnactivatedCard(), - CheckInWidget(margin: EdgeInsets.zero), - FortuneCard(unlimited: true), - ConstrainedBox( - constraints: const BoxConstraints(maxHeight: 400), - child: PostFeaturedList(), - ), - FriendsOverviewWidget(), - NotificationsCard(), - ChatListCard(), - Card( - margin: EdgeInsets.zero, - child: FortuneGraphWidget( - events: ref.watch( - eventCalendarProvider( - EventCalendarQuery( - uname: 'me', - year: DateTime.now().year, - month: DateTime.now().month, - ), - ), - ), - ), - ), - ], + children: children, ); } } @@ -685,4 +784,4 @@ class _UnauthorizedCard extends HookConsumerWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/screens/dashboard/dash_customize.dart b/lib/screens/dashboard/dash_customize.dart new file mode 100644 index 00000000..d9743b1f --- /dev/null +++ b/lib/screens/dashboard/dash_customize.dart @@ -0,0 +1,284 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; +import 'package:island/widgets/content/sheet.dart'; +import 'package:island/pods/config.dart'; + +class DashboardCustomizationSheet extends HookConsumerWidget { + const DashboardCustomizationSheet({super.key}); + + static const Map> _cardMetadata = { + // Vertical layout cards + 'checkIn': {'name': 'Check In', 'icon': Symbols.check_circle}, + 'fortuneGraph': {'name': 'Fortune Graph', 'icon': Symbols.show_chart}, + 'fortuneCard': {'name': 'Fortune', 'icon': Symbols.lightbulb}, + 'postFeatured': {'name': 'Featured Posts', 'icon': Symbols.article}, + 'friendsOverview': {'name': 'Friends', 'icon': Symbols.group}, + 'notifications': {'name': 'Notifications', 'icon': Symbols.notifications}, + 'chatList': {'name': 'Chats', 'icon': Symbols.chat}, + // Horizontal layout columns + 'activityColumn': {'name': 'Activity Column', 'icon': Symbols.dashboard, 'description': 'Check In, Fortune Graph & Fortune'}, + 'postsColumn': {'name': 'Posts Column', 'icon': Symbols.article, 'description': 'Featured Posts'}, + 'socialColumn': {'name': 'Social Column', 'icon': Symbols.group, 'description': 'Friends & Notifications'}, + 'chatsColumn': {'name': 'Chats Column', 'icon': Symbols.chat, 'description': 'Recent Chats'}, + }; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final tabController = useTabController(initialLength: 2); + final appSettings = ref.watch(appSettingsProvider); + + // Local state for editing + final verticalLayouts = useState>( + (appSettings.dashboardConfig?.verticalLayouts ?? [ + 'checkIn', + 'fortuneCard', + 'postFeatured', + 'friendsOverview', + 'notifications', + 'chatList', + 'fortuneGraph', + ]).where((id) => id != 'accountUnactivated').toList(), + ); + + final horizontalLayouts = useState>( + _migrateHorizontalLayouts(appSettings.dashboardConfig?.horizontalLayouts), + ); + + final showSearchBar = useState( + appSettings.dashboardConfig?.showSearchBar ?? true, + ); + + final showClockAndCountdown = useState( + appSettings.dashboardConfig?.showClockAndCountdown ?? true, + ); + + void saveConfig() { + final config = DashboardConfig( + verticalLayouts: verticalLayouts.value, + horizontalLayouts: horizontalLayouts.value, + showSearchBar: showSearchBar.value, + showClockAndCountdown: showClockAndCountdown.value, + ); + + ref.read(appSettingsProvider.notifier).setDashboardConfig(config); + Navigator.of(context).pop(); + } + + return SheetScaffold( + titleText: 'Customize Dashboard', + actions: [TextButton(onPressed: saveConfig, child: const Text('Save'))], + child: Column( + children: [ + TabBar( + controller: tabController, + tabs: const [ + Tab(text: 'Vertical'), + Tab(text: 'Horizontal'), + ], + ), + Expanded( + child: TabBarView( + controller: tabController, + children: [ + // Vertical layout + _buildLayoutEditor(context, 'Vertical Layout', verticalLayouts, false), + // Horizontal layout + _buildLayoutEditor( + context, + 'Horizontal Layout', + horizontalLayouts, + true, + ), + ], + ), + ), + const Divider(), + // Settings checkboxes + Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Display Settings', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + CheckboxListTile( + title: const Text('Show Search Bar'), + value: showSearchBar.value, + onChanged: (value) { + if (value != null) { + showSearchBar.value = value; + } + }, + ), + CheckboxListTile( + title: const Text('Show Clock and Countdown'), + value: showClockAndCountdown.value, + onChanged: (value) { + if (value != null) { + showClockAndCountdown.value = value; + } + }, + ), + ], + ), + ), + ], + ), + ); + } + + List _migrateHorizontalLayouts(List? existingLayouts) { + if (existingLayouts == null || existingLayouts.isEmpty) { + // Default horizontal layout using column groups + return ['activityColumn', 'postsColumn', 'socialColumn', 'chatsColumn']; + } + + // If it already contains column groups, use as-is + if (existingLayouts.any((id) => id.contains('Column'))) { + return existingLayouts.where((id) => id != 'accountUnactivated').toList(); + } + + // Migrate from old individual card format to column groups + // This is a simple migration - in a real app you might want more sophisticated logic + return ['activityColumn', 'postsColumn', 'socialColumn', 'chatsColumn']; + } + + Widget _buildLayoutEditor( + BuildContext context, + String title, + ValueNotifier> layouts, + bool isHorizontal, + ) { + // Filter available cards based on layout mode + final relevantCards = isHorizontal + ? _cardMetadata.entries.where((entry) => entry.key.contains('Column')).map((e) => e.key).toList() + : _cardMetadata.entries.where((entry) => !entry.key.contains('Column')).map((e) => e.key).toList(); + + final availableCards = + relevantCards.where((cardId) => !layouts.value.contains(cardId)).toList(); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: Text( + title, + style: Theme.of( + context, + ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), + ), + ), + // Reorderable list for cards + Expanded( + child: ReorderableListView.builder( + buildDefaultDragHandles: false, + itemCount: layouts.value.length, + onReorder: (oldIndex, newIndex) { + if (oldIndex < newIndex) { + newIndex -= 1; + } + final item = layouts.value.removeAt(oldIndex); + layouts.value.insert(newIndex, item); + // Trigger rebuild + layouts.value = List.from(layouts.value); + }, + itemBuilder: (context, index) { + final cardId = layouts.value[index]; + final metadata = + _cardMetadata[cardId] ?? + {'name': cardId, 'icon': Symbols.help}; + + return Card( + key: ValueKey(cardId), + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: ListTile( + leading: Icon( + metadata['icon'] as IconData, + color: Theme.of(context).colorScheme.primary, + ), + title: Text(metadata['name'] as String), + subtitle: isHorizontal && metadata.containsKey('description') + ? Text( + metadata['description'] as String, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ) + : null, + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ReorderableDragStartListener( + index: index, + child: Icon( + Symbols.drag_handle, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + IconButton( + icon: Icon( + Symbols.close, + size: 20, + color: Theme.of(context).colorScheme.error, + ), + onPressed: () { + layouts.value = layouts.value.where((id) => id != cardId).toList(); + }, + ), + ], + ), + ), + ); + }, + ), + ), + // Available cards to add back + if (availableCards.isNotEmpty) + Container( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Available Cards', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: availableCards.map((cardId) { + final metadata = + _cardMetadata[cardId] ?? + {'name': cardId, 'icon': Symbols.help}; + return ActionChip( + avatar: Icon( + metadata['icon'] as IconData, + size: 16, + color: Theme.of(context).colorScheme.primary, + ), + label: Text(metadata['name'] as String), + onPressed: () { + layouts.value = [...layouts.value, cardId]; + }, + ); + }).toList(), + ), + ], + ), + ), + ], + ); + } +} \ No newline at end of file