feature: Improved banners, made banner settings easier to understand. (#71)

Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
PartyDonut 2024-10-24 23:02:10 +02:00 committed by GitHub
parent 11e0e106d3
commit 476bdc130e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 916 additions and 666 deletions

View file

@ -2,4 +2,3 @@ arb-dir: lib/l10n
template-arb-file: app_en.arb template-arb-file: app_en.arb
output-localization-file: app_localizations.dart output-localization-file: app_localizations.dart
nullable-getter: false nullable-getter: false
untranslated-messages-file: lib/l10n/l10n_errors.txt

View file

@ -116,8 +116,8 @@
"@dashboardContinueReading": {}, "@dashboardContinueReading": {},
"dashboardContinueWatching": "Continue Watching", "dashboardContinueWatching": "Continue Watching",
"@dashboardContinueWatching": {}, "@dashboardContinueWatching": {},
"dashboardNextUp": "Next-up", "nextUp": "Next-up",
"@dashboardNextUp": {}, "@nextUp": {},
"dashboardRecentlyAdded": "Recently added in {name}", "dashboardRecentlyAdded": "Recently added in {name}",
"@dashboardRecentlyAdded": { "@dashboardRecentlyAdded": {
"description": "Recently added on home screen", "description": "Recently added on home screen",
@ -437,8 +437,6 @@
"@navigationSync": {}, "@navigationSync": {},
"never": "Never", "never": "Never",
"@never": {}, "@never": {},
"nextUp": "Next Up",
"@nextUp": {},
"noItemsSynced": "No items synced", "noItemsSynced": "No items synced",
"@noItemsSynced": {}, "@noItemsSynced": {},
"noItemsToShow": "No items to show", "noItemsToShow": "No items to show",
@ -723,14 +721,14 @@
"@settingsContinue": {}, "@settingsContinue": {},
"settingsEnableOsMediaControls": "Enable OS media controls", "settingsEnableOsMediaControls": "Enable OS media controls",
"@settingsEnableOsMediaControls": {}, "@settingsEnableOsMediaControls": {},
"settingsHomeBannerDescription": "Switch between a banner or scrollable carousel", "settingsHomeBannerDescription": "Display as a slideshow, carousel, or hide the banner",
"@settingsHomeBannerDescription": {}, "@settingsHomeBannerDescription": {},
"settingsHomeBannerTitle": "Home banner", "settingsHomeBannerTitle": "Home banner",
"@settingsHomeBannerTitle": {}, "@settingsHomeBannerTitle": {},
"settingsHomeCarouselDesc": "Shows a banner on the dashboard screen", "settingsHomeBannerInformationDesc": "Information to show in home banner",
"@settingsHomeCarouselDesc": {}, "@settingsHomeBannerInformationDesc": {},
"settingsHomeCarouselTitle": "Dashboard banner", "settingsHomeBannerInformationTitle": "Banner information",
"@settingsHomeCarouselTitle": {}, "@settingsHomeBannerInformationTitle": {},
"settingsHomeNextUpDesc": "Type of posters shown in the dashboard screen", "settingsHomeNextUpDesc": "Type of posters shown in the dashboard screen",
"@settingsHomeNextUpDesc": {}, "@settingsHomeNextUpDesc": {},
"settingsHomeNextUpTitle": "Next-up posters", "settingsHomeNextUpTitle": "Next-up posters",

View file

@ -1,93 +0,0 @@
{
"de": [
"homeBannerBanner",
"homeBannerCarousel",
"masonry",
"mediaTypeBoxset",
"settingsHomeBannerDescription",
"settingsHomeBannerTitle",
"settingsHomeNextUpDesc",
"settingsNextUpCutoffDays",
"settingsPlayerNativeLibassAccelDesc",
"settingsPlayerNativeLibassAccelTitle",
"settingsPosterPinch",
"settingsPosterSize",
"settingsShowScaleSlider",
"syncDeleteItemDesc",
"syncRemoveDataDesc",
"timeAndAnnotation",
"totalSize",
"unknown",
"useDefaults",
"videoScalingFillScreenNotif",
"videoScalingFillScreenTitle",
"videoScalingFitHeight",
"videoScalingFitWidth",
"videoScalingScaleDown",
"viewPhotos",
"writer"
],
"es": [
"homeBannerBanner",
"homeBannerCarousel",
"settingsHomeBannerDescription",
"settingsHomeBannerTitle"
],
"fr": [
"homeBannerBanner",
"homeBannerCarousel",
"settingsHomeBannerDescription",
"settingsHomeBannerTitle"
],
"ja": [
"homeBannerBanner",
"homeBannerCarousel",
"settingsHomeBannerDescription",
"settingsHomeBannerTitle"
],
"nb": [
"homeBannerBanner",
"homeBannerCarousel",
"settingsHomeBannerDescription",
"settingsHomeBannerTitle"
],
"nl": [
"homeBannerBanner",
"homeBannerCarousel",
"settingsHomeBannerDescription",
"settingsHomeBannerTitle"
],
"pt": [
"homeBannerBanner",
"homeBannerCarousel",
"settingsHomeBannerDescription",
"settingsHomeBannerTitle"
],
"pt_BR": [
"homeBannerBanner",
"homeBannerCarousel",
"settingsHomeBannerDescription",
"settingsHomeBannerTitle"
],
"uk": [
"homeBannerBanner",
"homeBannerCarousel",
"settingsHomeBannerDescription",
"settingsHomeBannerTitle"
],
"zh": [
"homeBannerBanner",
"homeBannerCarousel",
"settingsHomeBannerDescription",
"settingsHomeBannerTitle"
]
}

View file

@ -36,8 +36,12 @@ mixin _$AccountModel {
ServerConfiguration? get serverConfiguration => ServerConfiguration? get serverConfiguration =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
/// Serializes this AccountModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
/// Create a copy of AccountModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$AccountModelCopyWith<AccountModel> get copyWith => $AccountModelCopyWith<AccountModel> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
@ -74,6 +78,8 @@ class _$AccountModelCopyWithImpl<$Res, $Val extends AccountModel>
// ignore: unused_field // ignore: unused_field
final $Res Function($Val) _then; final $Res Function($Val) _then;
/// Create a copy of AccountModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -175,6 +181,8 @@ class __$$AccountModelImplCopyWithImpl<$Res>
_$AccountModelImpl _value, $Res Function(_$AccountModelImpl) _then) _$AccountModelImpl _value, $Res Function(_$AccountModelImpl) _then)
: super(_value, _then); : super(_value, _then);
/// Create a copy of AccountModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -365,7 +373,7 @@ class _$AccountModelImpl extends _AccountModel with DiagnosticableTreeMixin {
other.serverConfiguration == serverConfiguration)); other.serverConfiguration == serverConfiguration));
} }
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash( int get hashCode => Object.hash(
runtimeType, runtimeType,
@ -382,7 +390,9 @@ class _$AccountModelImpl extends _AccountModel with DiagnosticableTreeMixin {
policy, policy,
serverConfiguration); serverConfiguration);
@JsonKey(ignore: true) /// Create a copy of AccountModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$AccountModelImplCopyWith<_$AccountModelImpl> get copyWith => _$$AccountModelImplCopyWith<_$AccountModelImpl> get copyWith =>
@ -443,8 +453,11 @@ abstract class _AccountModel extends AccountModel {
@override @override
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
ServerConfiguration? get serverConfiguration; ServerConfiguration? get serverConfiguration;
/// Create a copy of AccountModel
/// with the given fields replaced by the non-null parameter values.
@override @override
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
_$$AccountModelImplCopyWith<_$AccountModelImpl> get copyWith => _$$AccountModelImplCopyWith<_$AccountModelImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }

View file

@ -23,8 +23,12 @@ mixin _$IntroOutSkipModel {
IntroSkipModel? get intro => throw _privateConstructorUsedError; IntroSkipModel? get intro => throw _privateConstructorUsedError;
IntroSkipModel? get credits => throw _privateConstructorUsedError; IntroSkipModel? get credits => throw _privateConstructorUsedError;
/// Serializes this IntroOutSkipModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$IntroOutSkipModelCopyWith<IntroOutSkipModel> get copyWith => $IntroOutSkipModelCopyWith<IntroOutSkipModel> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
@ -51,6 +55,8 @@ class _$IntroOutSkipModelCopyWithImpl<$Res, $Val extends IntroOutSkipModel>
// ignore: unused_field // ignore: unused_field
final $Res Function($Val) _then; final $Res Function($Val) _then;
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -69,6 +75,8 @@ class _$IntroOutSkipModelCopyWithImpl<$Res, $Val extends IntroOutSkipModel>
) as $Val); ) as $Val);
} }
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
$IntroSkipModelCopyWith<$Res>? get intro { $IntroSkipModelCopyWith<$Res>? get intro {
@ -81,6 +89,8 @@ class _$IntroOutSkipModelCopyWithImpl<$Res, $Val extends IntroOutSkipModel>
}); });
} }
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
$IntroSkipModelCopyWith<$Res>? get credits { $IntroSkipModelCopyWith<$Res>? get credits {
@ -118,6 +128,8 @@ class __$$IntroOutSkipModelImplCopyWithImpl<$Res>
$Res Function(_$IntroOutSkipModelImpl) _then) $Res Function(_$IntroOutSkipModelImpl) _then)
: super(_value, _then); : super(_value, _then);
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -164,11 +176,13 @@ class _$IntroOutSkipModelImpl extends _IntroOutSkipModel {
(identical(other.credits, credits) || other.credits == credits)); (identical(other.credits, credits) || other.credits == credits));
} }
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType, intro, credits); int get hashCode => Object.hash(runtimeType, intro, credits);
@JsonKey(ignore: true) /// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$IntroOutSkipModelImplCopyWith<_$IntroOutSkipModelImpl> get copyWith => _$$IntroOutSkipModelImplCopyWith<_$IntroOutSkipModelImpl> get copyWith =>
@ -196,8 +210,11 @@ abstract class _IntroOutSkipModel extends IntroOutSkipModel {
IntroSkipModel? get intro; IntroSkipModel? get intro;
@override @override
IntroSkipModel? get credits; IntroSkipModel? get credits;
/// Create a copy of IntroOutSkipModel
/// with the given fields replaced by the non-null parameter values.
@override @override
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
_$$IntroOutSkipModelImplCopyWith<_$IntroOutSkipModelImpl> get copyWith => _$$IntroOutSkipModelImplCopyWith<_$IntroOutSkipModelImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
@ -233,8 +250,12 @@ mixin _$IntroSkipModel {
toJson: _durationToMilliseconds) toJson: _durationToMilliseconds)
Duration get hideTime => throw _privateConstructorUsedError; Duration get hideTime => throw _privateConstructorUsedError;
/// Serializes this IntroSkipModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
/// Create a copy of IntroSkipModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$IntroSkipModelCopyWith<IntroSkipModel> get copyWith => $IntroSkipModelCopyWith<IntroSkipModel> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
@ -280,6 +301,8 @@ class _$IntroSkipModelCopyWithImpl<$Res, $Val extends IntroSkipModel>
// ignore: unused_field // ignore: unused_field
final $Res Function($Val) _then; final $Res Function($Val) _then;
/// Create a copy of IntroSkipModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -360,6 +383,8 @@ class __$$IntroSkipModelImplCopyWithImpl<$Res>
_$IntroSkipModelImpl _value, $Res Function(_$IntroSkipModelImpl) _then) _$IntroSkipModelImpl _value, $Res Function(_$IntroSkipModelImpl) _then)
: super(_value, _then); : super(_value, _then);
/// Create a copy of IntroSkipModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -480,12 +505,14 @@ class _$IntroSkipModelImpl implements _IntroSkipModel {
other.hideTime == hideTime)); other.hideTime == hideTime));
} }
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => int get hashCode =>
Object.hash(runtimeType, id, valid, start, end, showTime, hideTime); Object.hash(runtimeType, id, valid, start, end, showTime, hideTime);
@JsonKey(ignore: true) /// Create a copy of IntroSkipModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$IntroSkipModelImplCopyWith<_$IntroSkipModelImpl> get copyWith => _$$IntroSkipModelImplCopyWith<_$IntroSkipModelImpl> get copyWith =>
@ -558,8 +585,11 @@ abstract class _IntroSkipModel implements IntroSkipModel {
fromJson: _durationFromMilliseconds, fromJson: _durationFromMilliseconds,
toJson: _durationToMilliseconds) toJson: _durationToMilliseconds)
Duration get hideTime; Duration get hideTime;
/// Create a copy of IntroSkipModel
/// with the given fields replaced by the non-null parameter values.
@override @override
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
_$$IntroSkipModelImplCopyWith<_$IntroSkipModelImpl> get copyWith => _$$IntroSkipModelImplCopyWith<_$IntroSkipModelImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }

View file

@ -19,7 +19,9 @@ mixin _$ItemPropertiesModel {
bool get canDelete => throw _privateConstructorUsedError; bool get canDelete => throw _privateConstructorUsedError;
bool get canDownload => throw _privateConstructorUsedError; bool get canDownload => throw _privateConstructorUsedError;
@JsonKey(ignore: true) /// Create a copy of ItemPropertiesModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ItemPropertiesModelCopyWith<ItemPropertiesModel> get copyWith => $ItemPropertiesModelCopyWith<ItemPropertiesModel> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
@ -43,6 +45,8 @@ class _$ItemPropertiesModelCopyWithImpl<$Res, $Val extends ItemPropertiesModel>
// ignore: unused_field // ignore: unused_field
final $Res Function($Val) _then; final $Res Function($Val) _then;
/// Create a copy of ItemPropertiesModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -81,6 +85,8 @@ class __$$ItemPropertiesModelImplCopyWithImpl<$Res>
$Res Function(_$ItemPropertiesModelImpl) _then) $Res Function(_$ItemPropertiesModelImpl) _then)
: super(_value, _then); : super(_value, _then);
/// Create a copy of ItemPropertiesModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -131,7 +137,9 @@ class _$ItemPropertiesModelImpl extends _ItemPropertiesModel {
@override @override
int get hashCode => Object.hash(runtimeType, canDelete, canDownload); int get hashCode => Object.hash(runtimeType, canDelete, canDownload);
@JsonKey(ignore: true) /// Create a copy of ItemPropertiesModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$ItemPropertiesModelImplCopyWith<_$ItemPropertiesModelImpl> get copyWith => _$$ItemPropertiesModelImplCopyWith<_$ItemPropertiesModelImpl> get copyWith =>
@ -149,8 +157,11 @@ abstract class _ItemPropertiesModel extends ItemPropertiesModel {
bool get canDelete; bool get canDelete;
@override @override
bool get canDownload; bool get canDownload;
/// Create a copy of ItemPropertiesModel
/// with the given fields replaced by the non-null parameter values.
@override @override
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
_$$ItemPropertiesModelImplCopyWith<_$ItemPropertiesModelImpl> get copyWith => _$$ItemPropertiesModelImplCopyWith<_$ItemPropertiesModelImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }

View file

@ -28,8 +28,12 @@ mixin _$TrickPlayModel {
Duration get interval => throw _privateConstructorUsedError; Duration get interval => throw _privateConstructorUsedError;
List<String> get images => throw _privateConstructorUsedError; List<String> get images => throw _privateConstructorUsedError;
/// Serializes this TrickPlayModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
/// Create a copy of TrickPlayModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$TrickPlayModelCopyWith<TrickPlayModel> get copyWith => $TrickPlayModelCopyWith<TrickPlayModel> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
@ -60,6 +64,8 @@ class _$TrickPlayModelCopyWithImpl<$Res, $Val extends TrickPlayModel>
// ignore: unused_field // ignore: unused_field
final $Res Function($Val) _then; final $Res Function($Val) _then;
/// Create a copy of TrickPlayModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -130,6 +136,8 @@ class __$$TrickPlayModelImplCopyWithImpl<$Res>
_$TrickPlayModelImpl _value, $Res Function(_$TrickPlayModelImpl) _then) _$TrickPlayModelImpl _value, $Res Function(_$TrickPlayModelImpl) _then)
: super(_value, _then); : super(_value, _then);
/// Create a copy of TrickPlayModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -235,7 +243,7 @@ class _$TrickPlayModelImpl extends _TrickPlayModel {
const DeepCollectionEquality().equals(other._images, _images)); const DeepCollectionEquality().equals(other._images, _images));
} }
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash( int get hashCode => Object.hash(
runtimeType, runtimeType,
@ -247,7 +255,9 @@ class _$TrickPlayModelImpl extends _TrickPlayModel {
interval, interval,
const DeepCollectionEquality().hash(_images)); const DeepCollectionEquality().hash(_images));
@JsonKey(ignore: true) /// Create a copy of TrickPlayModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$TrickPlayModelImplCopyWith<_$TrickPlayModelImpl> get copyWith => _$$TrickPlayModelImplCopyWith<_$TrickPlayModelImpl> get copyWith =>
@ -290,8 +300,11 @@ abstract class _TrickPlayModel extends TrickPlayModel {
Duration get interval; Duration get interval;
@override @override
List<String> get images; List<String> get images;
/// Create a copy of TrickPlayModel
/// with the given fields replaced by the non-null parameter values.
@override @override
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
_$$TrickPlayModelImplCopyWith<_$TrickPlayModelImpl> get copyWith => _$$TrickPlayModelImplCopyWith<_$TrickPlayModelImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }

View file

@ -7,7 +7,6 @@ import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:fladder/util/custom_color_themes.dart'; import 'package:fladder/util/custom_color_themes.dart';
import 'package:fladder/util/localization_helper.dart';
part 'client_settings_model.freezed.dart'; part 'client_settings_model.freezed.dart';
part 'client_settings_model.g.dart'; part 'client_settings_model.g.dart';
@ -23,7 +22,6 @@ class ClientSettingsModel with _$ClientSettingsModel {
Duration? nextUpDateCutoff, Duration? nextUpDateCutoff,
@Default(ThemeMode.system) ThemeMode themeMode, @Default(ThemeMode.system) ThemeMode themeMode,
ColorThemes? themeColor, ColorThemes? themeColor,
@Default(HomeBanner.carousel) HomeBanner homeBanner,
@Default(false) bool amoledBlack, @Default(false) bool amoledBlack,
@Default(false) bool blurPlaceHolders, @Default(false) bool blurPlaceHolders,
@Default(false) bool blurUpcomingEpisodes, @Default(false) bool blurUpcomingEpisodes,
@ -73,18 +71,6 @@ class LocaleConvert implements JsonConverter<Locale?, String?> {
} }
} }
enum HomeBanner {
carousel,
banner;
const HomeBanner();
String label(BuildContext context) => switch (this) {
HomeBanner.carousel => context.localized.homeBannerCarousel,
HomeBanner.banner => context.localized.homeBannerBanner,
};
}
class Vector2 { class Vector2 {
final double x; final double x;
final double y; final double y;

View file

@ -27,7 +27,6 @@ mixin _$ClientSettingsModel {
Duration? get nextUpDateCutoff => throw _privateConstructorUsedError; Duration? get nextUpDateCutoff => throw _privateConstructorUsedError;
ThemeMode get themeMode => throw _privateConstructorUsedError; ThemeMode get themeMode => throw _privateConstructorUsedError;
ColorThemes? get themeColor => throw _privateConstructorUsedError; ColorThemes? get themeColor => throw _privateConstructorUsedError;
HomeBanner get homeBanner => throw _privateConstructorUsedError;
bool get amoledBlack => throw _privateConstructorUsedError; bool get amoledBlack => throw _privateConstructorUsedError;
bool get blurPlaceHolders => throw _privateConstructorUsedError; bool get blurPlaceHolders => throw _privateConstructorUsedError;
bool get blurUpcomingEpisodes => throw _privateConstructorUsedError; bool get blurUpcomingEpisodes => throw _privateConstructorUsedError;
@ -63,7 +62,6 @@ abstract class $ClientSettingsModelCopyWith<$Res> {
Duration? nextUpDateCutoff, Duration? nextUpDateCutoff,
ThemeMode themeMode, ThemeMode themeMode,
ColorThemes? themeColor, ColorThemes? themeColor,
HomeBanner homeBanner,
bool amoledBlack, bool amoledBlack,
bool blurPlaceHolders, bool blurPlaceHolders,
bool blurUpcomingEpisodes, bool blurUpcomingEpisodes,
@ -97,7 +95,6 @@ class _$ClientSettingsModelCopyWithImpl<$Res, $Val extends ClientSettingsModel>
Object? nextUpDateCutoff = freezed, Object? nextUpDateCutoff = freezed,
Object? themeMode = null, Object? themeMode = null,
Object? themeColor = freezed, Object? themeColor = freezed,
Object? homeBanner = null,
Object? amoledBlack = null, Object? amoledBlack = null,
Object? blurPlaceHolders = null, Object? blurPlaceHolders = null,
Object? blurUpcomingEpisodes = null, Object? blurUpcomingEpisodes = null,
@ -137,10 +134,6 @@ class _$ClientSettingsModelCopyWithImpl<$Res, $Val extends ClientSettingsModel>
? _value.themeColor ? _value.themeColor
: themeColor // ignore: cast_nullable_to_non_nullable : themeColor // ignore: cast_nullable_to_non_nullable
as ColorThemes?, as ColorThemes?,
homeBanner: null == homeBanner
? _value.homeBanner
: homeBanner // ignore: cast_nullable_to_non_nullable
as HomeBanner,
amoledBlack: null == amoledBlack amoledBlack: null == amoledBlack
? _value.amoledBlack ? _value.amoledBlack
: amoledBlack // ignore: cast_nullable_to_non_nullable : amoledBlack // ignore: cast_nullable_to_non_nullable
@ -197,7 +190,6 @@ abstract class _$$ClientSettingsModelImplCopyWith<$Res>
Duration? nextUpDateCutoff, Duration? nextUpDateCutoff,
ThemeMode themeMode, ThemeMode themeMode,
ColorThemes? themeColor, ColorThemes? themeColor,
HomeBanner homeBanner,
bool amoledBlack, bool amoledBlack,
bool blurPlaceHolders, bool blurPlaceHolders,
bool blurUpcomingEpisodes, bool blurUpcomingEpisodes,
@ -229,7 +221,6 @@ class __$$ClientSettingsModelImplCopyWithImpl<$Res>
Object? nextUpDateCutoff = freezed, Object? nextUpDateCutoff = freezed,
Object? themeMode = null, Object? themeMode = null,
Object? themeColor = freezed, Object? themeColor = freezed,
Object? homeBanner = null,
Object? amoledBlack = null, Object? amoledBlack = null,
Object? blurPlaceHolders = null, Object? blurPlaceHolders = null,
Object? blurUpcomingEpisodes = null, Object? blurUpcomingEpisodes = null,
@ -269,10 +260,6 @@ class __$$ClientSettingsModelImplCopyWithImpl<$Res>
? _value.themeColor ? _value.themeColor
: themeColor // ignore: cast_nullable_to_non_nullable : themeColor // ignore: cast_nullable_to_non_nullable
as ColorThemes?, as ColorThemes?,
homeBanner: null == homeBanner
? _value.homeBanner
: homeBanner // ignore: cast_nullable_to_non_nullable
as HomeBanner,
amoledBlack: null == amoledBlack amoledBlack: null == amoledBlack
? _value.amoledBlack ? _value.amoledBlack
: amoledBlack // ignore: cast_nullable_to_non_nullable : amoledBlack // ignore: cast_nullable_to_non_nullable
@ -325,7 +312,6 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
this.nextUpDateCutoff, this.nextUpDateCutoff,
this.themeMode = ThemeMode.system, this.themeMode = ThemeMode.system,
this.themeColor, this.themeColor,
this.homeBanner = HomeBanner.carousel,
this.amoledBlack = false, this.amoledBlack = false,
this.blurPlaceHolders = false, this.blurPlaceHolders = false,
this.blurUpcomingEpisodes = false, this.blurUpcomingEpisodes = false,
@ -360,9 +346,6 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
final ColorThemes? themeColor; final ColorThemes? themeColor;
@override @override
@JsonKey() @JsonKey()
final HomeBanner homeBanner;
@override
@JsonKey()
final bool amoledBlack; final bool amoledBlack;
@override @override
@JsonKey() @JsonKey()
@ -390,7 +373,7 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
@override @override
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
return 'ClientSettingsModel(syncPath: $syncPath, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, themeMode: $themeMode, themeColor: $themeColor, homeBanner: $homeBanner, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, libraryPageSize: $libraryPageSize)'; return 'ClientSettingsModel(syncPath: $syncPath, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, themeMode: $themeMode, themeColor: $themeColor, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, libraryPageSize: $libraryPageSize)';
} }
@override @override
@ -405,7 +388,6 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
..add(DiagnosticsProperty('nextUpDateCutoff', nextUpDateCutoff)) ..add(DiagnosticsProperty('nextUpDateCutoff', nextUpDateCutoff))
..add(DiagnosticsProperty('themeMode', themeMode)) ..add(DiagnosticsProperty('themeMode', themeMode))
..add(DiagnosticsProperty('themeColor', themeColor)) ..add(DiagnosticsProperty('themeColor', themeColor))
..add(DiagnosticsProperty('homeBanner', homeBanner))
..add(DiagnosticsProperty('amoledBlack', amoledBlack)) ..add(DiagnosticsProperty('amoledBlack', amoledBlack))
..add(DiagnosticsProperty('blurPlaceHolders', blurPlaceHolders)) ..add(DiagnosticsProperty('blurPlaceHolders', blurPlaceHolders))
..add(DiagnosticsProperty('blurUpcomingEpisodes', blurUpcomingEpisodes)) ..add(DiagnosticsProperty('blurUpcomingEpisodes', blurUpcomingEpisodes))
@ -434,8 +416,6 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
other.themeMode == themeMode) && other.themeMode == themeMode) &&
(identical(other.themeColor, themeColor) || (identical(other.themeColor, themeColor) ||
other.themeColor == themeColor) && other.themeColor == themeColor) &&
(identical(other.homeBanner, homeBanner) ||
other.homeBanner == homeBanner) &&
(identical(other.amoledBlack, amoledBlack) || (identical(other.amoledBlack, amoledBlack) ||
other.amoledBlack == amoledBlack) && other.amoledBlack == amoledBlack) &&
(identical(other.blurPlaceHolders, blurPlaceHolders) || (identical(other.blurPlaceHolders, blurPlaceHolders) ||
@ -467,7 +447,6 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
nextUpDateCutoff, nextUpDateCutoff,
themeMode, themeMode,
themeColor, themeColor,
homeBanner,
amoledBlack, amoledBlack,
blurPlaceHolders, blurPlaceHolders,
blurUpcomingEpisodes, blurUpcomingEpisodes,
@ -504,7 +483,6 @@ abstract class _ClientSettingsModel extends ClientSettingsModel {
final Duration? nextUpDateCutoff, final Duration? nextUpDateCutoff,
final ThemeMode themeMode, final ThemeMode themeMode,
final ColorThemes? themeColor, final ColorThemes? themeColor,
final HomeBanner homeBanner,
final bool amoledBlack, final bool amoledBlack,
final bool blurPlaceHolders, final bool blurPlaceHolders,
final bool blurUpcomingEpisodes, final bool blurUpcomingEpisodes,
@ -534,8 +512,6 @@ abstract class _ClientSettingsModel extends ClientSettingsModel {
@override @override
ColorThemes? get themeColor; ColorThemes? get themeColor;
@override @override
HomeBanner get homeBanner;
@override
bool get amoledBlack; bool get amoledBlack;
@override @override
bool get blurPlaceHolders; bool get blurPlaceHolders;

View file

@ -25,9 +25,6 @@ _$ClientSettingsModelImpl _$$ClientSettingsModelImplFromJson(
themeMode: $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ?? themeMode: $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??
ThemeMode.system, ThemeMode.system,
themeColor: $enumDecodeNullable(_$ColorThemesEnumMap, json['themeColor']), themeColor: $enumDecodeNullable(_$ColorThemesEnumMap, json['themeColor']),
homeBanner:
$enumDecodeNullable(_$HomeBannerEnumMap, json['homeBanner']) ??
HomeBanner.carousel,
amoledBlack: json['amoledBlack'] as bool? ?? false, amoledBlack: json['amoledBlack'] as bool? ?? false,
blurPlaceHolders: json['blurPlaceHolders'] as bool? ?? false, blurPlaceHolders: json['blurPlaceHolders'] as bool? ?? false,
blurUpcomingEpisodes: json['blurUpcomingEpisodes'] as bool? ?? false, blurUpcomingEpisodes: json['blurUpcomingEpisodes'] as bool? ?? false,
@ -50,7 +47,6 @@ Map<String, dynamic> _$$ClientSettingsModelImplToJson(
'nextUpDateCutoff': instance.nextUpDateCutoff?.inMicroseconds, 'nextUpDateCutoff': instance.nextUpDateCutoff?.inMicroseconds,
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!, 'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
'themeColor': _$ColorThemesEnumMap[instance.themeColor], 'themeColor': _$ColorThemesEnumMap[instance.themeColor],
'homeBanner': _$HomeBannerEnumMap[instance.homeBanner]!,
'amoledBlack': instance.amoledBlack, 'amoledBlack': instance.amoledBlack,
'blurPlaceHolders': instance.blurPlaceHolders, 'blurPlaceHolders': instance.blurPlaceHolders,
'blurUpcomingEpisodes': instance.blurUpcomingEpisodes, 'blurUpcomingEpisodes': instance.blurUpcomingEpisodes,
@ -85,8 +81,3 @@ const _$ColorThemesEnumMap = {
ColorThemes.deepPurple: 'deepPurple', ColorThemes.deepPurple: 'deepPurple',
ColorThemes.blueGrey: 'blueGrey', ColorThemes.blueGrey: 'blueGrey',
}; };
const _$HomeBannerEnumMap = {
HomeBanner.carousel: 'carousel',
HomeBanner.banner: 'banner',
};

View file

@ -1,11 +1,38 @@
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:fladder/util/localization_helper.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:fladder/util/localization_helper.dart';
part 'home_settings_model.freezed.dart';
part 'home_settings_model.g.dart';
@freezed
class HomeSettingsModel with _$HomeSettingsModel {
factory HomeSettingsModel({
@Default(HomeBanner.carousel) HomeBanner homeBanner,
@Default(HomeCarouselSettings.combined) HomeCarouselSettings carouselSettings,
@Default(HomeNextUp.separate) HomeNextUp nextUp,
}) = _HomeSettingsModel;
factory HomeSettingsModel.fromJson(Map<String, dynamic> json) => _$HomeSettingsModelFromJson(json);
}
enum HomeBanner {
hide,
carousel,
banner;
const HomeBanner();
String label(BuildContext context) => switch (this) {
HomeBanner.hide => context.localized.hide,
HomeBanner.carousel => context.localized.homeBannerCarousel,
HomeBanner.banner => context.localized.homeBannerBanner,
};
}
enum HomeCarouselSettings { enum HomeCarouselSettings {
off,
nextUp, nextUp,
cont, cont,
combined, combined,
@ -14,20 +41,10 @@ enum HomeCarouselSettings {
const HomeCarouselSettings(); const HomeCarouselSettings();
String label(BuildContext context) => switch (this) { String label(BuildContext context) => switch (this) {
HomeCarouselSettings.off => context.localized.hide,
HomeCarouselSettings.nextUp => context.localized.nextUp, HomeCarouselSettings.nextUp => context.localized.nextUp,
HomeCarouselSettings.cont => context.localized.settingsContinue, HomeCarouselSettings.cont => context.localized.settingsContinue,
HomeCarouselSettings.combined => context.localized.combined, HomeCarouselSettings.combined => context.localized.combined,
}; };
String toMap() {
return toString();
}
static HomeCarouselSettings fromMap(String value) {
return HomeCarouselSettings.values.firstWhereOrNull((element) => element.name == value) ??
HomeCarouselSettings.combined;
}
} }
enum HomeNextUp { enum HomeNextUp {
@ -47,62 +64,4 @@ enum HomeNextUp {
HomeNextUp.combined => context.localized.combined, HomeNextUp.combined => context.localized.combined,
HomeNextUp.separate => context.localized.separate, HomeNextUp.separate => context.localized.separate,
}; };
String toMap() {
return toString();
}
static HomeNextUp fromMap(String value) {
return HomeNextUp.values.firstWhereOrNull((element) => element.name == value) ?? HomeNextUp.separate;
}
}
class HomeSettingsModel {
final HomeCarouselSettings carouselSettings;
final HomeNextUp nextUp;
HomeSettingsModel({
this.carouselSettings = HomeCarouselSettings.combined,
this.nextUp = HomeNextUp.separate,
});
HomeSettingsModel copyWith({
HomeCarouselSettings? carouselSettings,
HomeNextUp? nextUp,
}) {
return HomeSettingsModel(
carouselSettings: carouselSettings ?? this.carouselSettings,
nextUp: nextUp ?? this.nextUp,
);
}
Map<String, dynamic> toMap() {
return {
'carouselSettings': carouselSettings.toMap(),
'nextUp': nextUp.toMap(),
};
}
factory HomeSettingsModel.fromMap(Map<String, dynamic> map) {
return HomeSettingsModel(
carouselSettings: HomeCarouselSettings.fromMap(map['carouselSettings']),
nextUp: HomeNextUp.fromMap(map['nextUp']),
);
}
String toJson() => json.encode(toMap());
factory HomeSettingsModel.fromJson(String source) => HomeSettingsModel.fromMap(json.decode(source));
@override
String toString() => 'HomeSettingsModel(carouselSettings: $carouselSettings, nextUp: $nextUp)';
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is HomeSettingsModel && other.carouselSettings == carouselSettings && other.nextUp == nextUp;
}
@override
int get hashCode => carouselSettings.hashCode ^ nextUp.hashCode;
} }

View file

@ -0,0 +1,216 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'home_settings_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
HomeSettingsModel _$HomeSettingsModelFromJson(Map<String, dynamic> json) {
return _HomeSettingsModel.fromJson(json);
}
/// @nodoc
mixin _$HomeSettingsModel {
HomeBanner get homeBanner => throw _privateConstructorUsedError;
HomeCarouselSettings get carouselSettings =>
throw _privateConstructorUsedError;
HomeNextUp get nextUp => throw _privateConstructorUsedError;
/// Serializes this HomeSettingsModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of HomeSettingsModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$HomeSettingsModelCopyWith<HomeSettingsModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $HomeSettingsModelCopyWith<$Res> {
factory $HomeSettingsModelCopyWith(
HomeSettingsModel value, $Res Function(HomeSettingsModel) then) =
_$HomeSettingsModelCopyWithImpl<$Res, HomeSettingsModel>;
@useResult
$Res call(
{HomeBanner homeBanner,
HomeCarouselSettings carouselSettings,
HomeNextUp nextUp});
}
/// @nodoc
class _$HomeSettingsModelCopyWithImpl<$Res, $Val extends HomeSettingsModel>
implements $HomeSettingsModelCopyWith<$Res> {
_$HomeSettingsModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of HomeSettingsModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? homeBanner = null,
Object? carouselSettings = null,
Object? nextUp = null,
}) {
return _then(_value.copyWith(
homeBanner: null == homeBanner
? _value.homeBanner
: homeBanner // ignore: cast_nullable_to_non_nullable
as HomeBanner,
carouselSettings: null == carouselSettings
? _value.carouselSettings
: carouselSettings // ignore: cast_nullable_to_non_nullable
as HomeCarouselSettings,
nextUp: null == nextUp
? _value.nextUp
: nextUp // ignore: cast_nullable_to_non_nullable
as HomeNextUp,
) as $Val);
}
}
/// @nodoc
abstract class _$$HomeSettingsModelImplCopyWith<$Res>
implements $HomeSettingsModelCopyWith<$Res> {
factory _$$HomeSettingsModelImplCopyWith(_$HomeSettingsModelImpl value,
$Res Function(_$HomeSettingsModelImpl) then) =
__$$HomeSettingsModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{HomeBanner homeBanner,
HomeCarouselSettings carouselSettings,
HomeNextUp nextUp});
}
/// @nodoc
class __$$HomeSettingsModelImplCopyWithImpl<$Res>
extends _$HomeSettingsModelCopyWithImpl<$Res, _$HomeSettingsModelImpl>
implements _$$HomeSettingsModelImplCopyWith<$Res> {
__$$HomeSettingsModelImplCopyWithImpl(_$HomeSettingsModelImpl _value,
$Res Function(_$HomeSettingsModelImpl) _then)
: super(_value, _then);
/// Create a copy of HomeSettingsModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? homeBanner = null,
Object? carouselSettings = null,
Object? nextUp = null,
}) {
return _then(_$HomeSettingsModelImpl(
homeBanner: null == homeBanner
? _value.homeBanner
: homeBanner // ignore: cast_nullable_to_non_nullable
as HomeBanner,
carouselSettings: null == carouselSettings
? _value.carouselSettings
: carouselSettings // ignore: cast_nullable_to_non_nullable
as HomeCarouselSettings,
nextUp: null == nextUp
? _value.nextUp
: nextUp // ignore: cast_nullable_to_non_nullable
as HomeNextUp,
));
}
}
/// @nodoc
@JsonSerializable()
class _$HomeSettingsModelImpl implements _HomeSettingsModel {
_$HomeSettingsModelImpl(
{this.homeBanner = HomeBanner.carousel,
this.carouselSettings = HomeCarouselSettings.combined,
this.nextUp = HomeNextUp.separate});
factory _$HomeSettingsModelImpl.fromJson(Map<String, dynamic> json) =>
_$$HomeSettingsModelImplFromJson(json);
@override
@JsonKey()
final HomeBanner homeBanner;
@override
@JsonKey()
final HomeCarouselSettings carouselSettings;
@override
@JsonKey()
final HomeNextUp nextUp;
@override
String toString() {
return 'HomeSettingsModel(homeBanner: $homeBanner, carouselSettings: $carouselSettings, nextUp: $nextUp)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$HomeSettingsModelImpl &&
(identical(other.homeBanner, homeBanner) ||
other.homeBanner == homeBanner) &&
(identical(other.carouselSettings, carouselSettings) ||
other.carouselSettings == carouselSettings) &&
(identical(other.nextUp, nextUp) || other.nextUp == nextUp));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, homeBanner, carouselSettings, nextUp);
/// Create a copy of HomeSettingsModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$HomeSettingsModelImplCopyWith<_$HomeSettingsModelImpl> get copyWith =>
__$$HomeSettingsModelImplCopyWithImpl<_$HomeSettingsModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$HomeSettingsModelImplToJson(
this,
);
}
}
abstract class _HomeSettingsModel implements HomeSettingsModel {
factory _HomeSettingsModel(
{final HomeBanner homeBanner,
final HomeCarouselSettings carouselSettings,
final HomeNextUp nextUp}) = _$HomeSettingsModelImpl;
factory _HomeSettingsModel.fromJson(Map<String, dynamic> json) =
_$HomeSettingsModelImpl.fromJson;
@override
HomeBanner get homeBanner;
@override
HomeCarouselSettings get carouselSettings;
@override
HomeNextUp get nextUp;
/// Create a copy of HomeSettingsModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$HomeSettingsModelImplCopyWith<_$HomeSettingsModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View file

@ -0,0 +1,49 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'home_settings_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$HomeSettingsModelImpl _$$HomeSettingsModelImplFromJson(
Map<String, dynamic> json) =>
_$HomeSettingsModelImpl(
homeBanner:
$enumDecodeNullable(_$HomeBannerEnumMap, json['homeBanner']) ??
HomeBanner.carousel,
carouselSettings: $enumDecodeNullable(
_$HomeCarouselSettingsEnumMap, json['carouselSettings']) ??
HomeCarouselSettings.combined,
nextUp: $enumDecodeNullable(_$HomeNextUpEnumMap, json['nextUp']) ??
HomeNextUp.separate,
);
Map<String, dynamic> _$$HomeSettingsModelImplToJson(
_$HomeSettingsModelImpl instance) =>
<String, dynamic>{
'homeBanner': _$HomeBannerEnumMap[instance.homeBanner]!,
'carouselSettings':
_$HomeCarouselSettingsEnumMap[instance.carouselSettings]!,
'nextUp': _$HomeNextUpEnumMap[instance.nextUp]!,
};
const _$HomeBannerEnumMap = {
HomeBanner.hide: 'hide',
HomeBanner.carousel: 'carousel',
HomeBanner.banner: 'banner',
};
const _$HomeCarouselSettingsEnumMap = {
HomeCarouselSettings.nextUp: 'nextUp',
HomeCarouselSettings.cont: 'cont',
HomeCarouselSettings.combined: 'combined',
};
const _$HomeNextUpEnumMap = {
HomeNextUp.off: 'off',
HomeNextUp.nextUp: 'nextUp',
HomeNextUp.cont: 'cont',
HomeNextUp.combined: 'combined',
HomeNextUp.separate: 'separate',
};

View file

@ -33,7 +33,9 @@ mixin _$SyncedItem {
@UserDataJsonSerializer() @UserDataJsonSerializer()
UserData? get userData => throw _privateConstructorUsedError; UserData? get userData => throw _privateConstructorUsedError;
@JsonKey(ignore: true) /// Create a copy of SyncedItem
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SyncedItemCopyWith<SyncedItem> get copyWith => $SyncedItemCopyWith<SyncedItem> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
@ -74,6 +76,8 @@ class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
// ignore: unused_field // ignore: unused_field
final $Res Function($Val) _then; final $Res Function($Val) _then;
/// Create a copy of SyncedItem
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -152,6 +156,8 @@ class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
) as $Val); ) as $Val);
} }
/// Create a copy of SyncedItem
/// with the given fields replaced by the non-null parameter values.
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
$IntroOutSkipModelCopyWith<$Res>? get introOutSkipModel { $IntroOutSkipModelCopyWith<$Res>? get introOutSkipModel {
@ -164,6 +170,8 @@ class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
}); });
} }
/// Create a copy of SyncedItem
/// with the given fields replaced by the non-null parameter values.
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
$TrickPlayModelCopyWith<$Res>? get fTrickPlayModel { $TrickPlayModelCopyWith<$Res>? get fTrickPlayModel {
@ -215,6 +223,8 @@ class __$$SyncItemImplCopyWithImpl<$Res>
_$SyncItemImpl _value, $Res Function(_$SyncItemImpl) _then) _$SyncItemImpl _value, $Res Function(_$SyncItemImpl) _then)
: super(_value, _then); : super(_value, _then);
/// Create a copy of SyncedItem
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -415,7 +425,9 @@ class _$SyncItemImpl extends _SyncItem {
const DeepCollectionEquality().hash(_subtitles), const DeepCollectionEquality().hash(_subtitles),
userData); userData);
@JsonKey(ignore: true) /// Create a copy of SyncedItem
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$SyncItemImplCopyWith<_$SyncItemImpl> get copyWith => _$$SyncItemImplCopyWith<_$SyncItemImpl> get copyWith =>
@ -469,8 +481,11 @@ abstract class _SyncItem extends SyncedItem {
@override @override
@UserDataJsonSerializer() @UserDataJsonSerializer()
UserData? get userData; UserData? get userData;
/// Create a copy of SyncedItem
/// with the given fields replaced by the non-null parameter values.
@override @override
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
_$$SyncItemImplCopyWith<_$SyncItemImpl> get copyWith => _$$SyncItemImplCopyWith<_$SyncItemImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }

View file

@ -18,7 +18,9 @@ final _privateConstructorUsedError = UnsupportedError(
mixin _$SyncSettingsModel { mixin _$SyncSettingsModel {
List<SyncedItem> get items => throw _privateConstructorUsedError; List<SyncedItem> get items => throw _privateConstructorUsedError;
@JsonKey(ignore: true) /// Create a copy of SyncSettingsModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SyncSettingsModelCopyWith<SyncSettingsModel> get copyWith => $SyncSettingsModelCopyWith<SyncSettingsModel> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
@ -42,6 +44,8 @@ class _$SyncSettingsModelCopyWithImpl<$Res, $Val extends SyncSettingsModel>
// ignore: unused_field // ignore: unused_field
final $Res Function($Val) _then; final $Res Function($Val) _then;
/// Create a copy of SyncSettingsModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -75,6 +79,8 @@ class __$$SyncSettignsModelImplCopyWithImpl<$Res>
$Res Function(_$SyncSettignsModelImpl) _then) $Res Function(_$SyncSettignsModelImpl) _then)
: super(_value, _then); : super(_value, _then);
/// Create a copy of SyncSettingsModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -122,7 +128,9 @@ class _$SyncSettignsModelImpl extends _SyncSettignsModel {
int get hashCode => int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_items)); Object.hash(runtimeType, const DeepCollectionEquality().hash(_items));
@JsonKey(ignore: true) /// Create a copy of SyncSettingsModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$SyncSettignsModelImplCopyWith<_$SyncSettignsModelImpl> get copyWith => _$$SyncSettignsModelImplCopyWith<_$SyncSettignsModelImpl> get copyWith =>
@ -137,8 +145,11 @@ abstract class _SyncSettignsModel extends SyncSettingsModel {
@override @override
List<SyncedItem> get items; List<SyncedItem> get items;
/// Create a copy of SyncSettingsModel
/// with the given fields replaced by the non-null parameter values.
@override @override
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
_$$SyncSettignsModelImplCopyWith<_$SyncSettignsModelImpl> get copyWith => _$$SyncSettignsModelImplCopyWith<_$SyncSettignsModelImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }

View file

@ -23,8 +23,12 @@ mixin _$SessionInfoModel {
String? get playbackModel => throw _privateConstructorUsedError; String? get playbackModel => throw _privateConstructorUsedError;
TranscodingInfo? get transCodeInfo => throw _privateConstructorUsedError; TranscodingInfo? get transCodeInfo => throw _privateConstructorUsedError;
/// Serializes this SessionInfoModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
/// Create a copy of SessionInfoModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SessionInfoModelCopyWith<SessionInfoModel> get copyWith => $SessionInfoModelCopyWith<SessionInfoModel> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
@ -48,6 +52,8 @@ class _$SessionInfoModelCopyWithImpl<$Res, $Val extends SessionInfoModel>
// ignore: unused_field // ignore: unused_field
final $Res Function($Val) _then; final $Res Function($Val) _then;
/// Create a copy of SessionInfoModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -86,6 +92,8 @@ class __$$SessionInfoModelImplCopyWithImpl<$Res>
$Res Function(_$SessionInfoModelImpl) _then) $Res Function(_$SessionInfoModelImpl) _then)
: super(_value, _then); : super(_value, _then);
/// Create a copy of SessionInfoModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -134,11 +142,13 @@ class _$SessionInfoModelImpl extends _SessionInfoModel {
other.transCodeInfo == transCodeInfo)); other.transCodeInfo == transCodeInfo));
} }
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType, playbackModel, transCodeInfo); int get hashCode => Object.hash(runtimeType, playbackModel, transCodeInfo);
@JsonKey(ignore: true) /// Create a copy of SessionInfoModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$SessionInfoModelImplCopyWith<_$SessionInfoModelImpl> get copyWith => _$$SessionInfoModelImplCopyWith<_$SessionInfoModelImpl> get copyWith =>
@ -166,8 +176,11 @@ abstract class _SessionInfoModel extends SessionInfoModel {
String? get playbackModel; String? get playbackModel;
@override @override
TranscodingInfo? get transCodeInfo; TranscodingInfo? get transCodeInfo;
/// Create a copy of SessionInfoModel
/// with the given fields replaced by the non-null parameter values.
@override @override
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
_$$SessionInfoModelImplCopyWith<_$SessionInfoModelImpl> get copyWith => _$$SessionInfoModelImplCopyWith<_$SessionInfoModelImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }

View file

@ -1,6 +1,7 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/models/settings/home_settings_model.dart'; import 'package:fladder/models/settings/home_settings_model.dart';
import 'package:fladder/providers/shared_provider.dart'; import 'package:fladder/providers/shared_provider.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final homeSettingsProvider = StateNotifierProvider<HomeSettingsNotifier, HomeSettingsModel>((ref) { final homeSettingsProvider = StateNotifierProvider<HomeSettingsNotifier, HomeSettingsModel>((ref) {
return HomeSettingsNotifier(ref); return HomeSettingsNotifier(ref);

View file

@ -1,6 +1,9 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:fladder/models/account_model.dart'; import 'package:fladder/models/account_model.dart';
import 'package:fladder/models/settings/client_settings_model.dart'; import 'package:fladder/models/settings/client_settings_model.dart';
import 'package:fladder/models/settings/home_settings_model.dart'; import 'package:fladder/models/settings/home_settings_model.dart';
@ -15,8 +18,6 @@ import 'package:fladder/providers/settings/photo_view_settings_provider.dart';
import 'package:fladder/providers/settings/subtitle_settings_provider.dart'; import 'package:fladder/providers/settings/subtitle_settings_provider.dart';
import 'package:fladder/providers/settings/video_player_settings_provider.dart'; import 'package:fladder/providers/settings/video_player_settings_provider.dart';
import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/providers/user_provider.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
final sharedPreferencesProvider = Provider<SharedPreferences>((ref) { final sharedPreferencesProvider = Provider<SharedPreferences>((ref) {
throw UnimplementedError(); throw UnimplementedError();
@ -116,14 +117,15 @@ class SharedUtility {
HomeSettingsModel get homeSettings { HomeSettingsModel get homeSettings {
try { try {
return HomeSettingsModel.fromJson(sharedPreferences.getString(_homeSettingsKey) ?? ""); return HomeSettingsModel.fromJson(jsonDecode(sharedPreferences.getString(_homeSettingsKey) ?? ""));
} catch (e) { } catch (e) {
log(e.toString()); log(e.toString());
return HomeSettingsModel(); return HomeSettingsModel();
} }
} }
set homeSettings(HomeSettingsModel settings) => sharedPreferences.setString(_homeSettingsKey, settings.toJson()); set homeSettings(HomeSettingsModel settings) =>
sharedPreferences.setString(_homeSettingsKey, jsonEncode(settings.toJson()));
BookViewerSettingsModel get bookViewSettings { BookViewerSettingsModel get bookViewSettings {
try { try {

View file

@ -27,7 +27,8 @@ import 'package:fladder/screens/settings/security_settings_page.dart' as _i10;
import 'package:fladder/screens/settings/settings_screen.dart' as _i11; import 'package:fladder/screens/settings/settings_screen.dart' as _i11;
import 'package:fladder/screens/splash_screen.dart' as _i12; import 'package:fladder/screens/splash_screen.dart' as _i12;
import 'package:fladder/screens/syncing/synced_screen.dart' as _i13; import 'package:fladder/screens/syncing/synced_screen.dart' as _i13;
import 'package:flutter/material.dart' as _i16; import 'package:flutter/foundation.dart' as _i16;
import 'package:flutter/material.dart' as _i19;
/// generated route for /// generated route for
/// [_i1.ClientSettingsPage] /// [_i1.ClientSettingsPage]
@ -355,7 +356,7 @@ class SettingsRoute extends _i14.PageRouteInfo<void> {
class SplashRoute extends _i14.PageRouteInfo<SplashRouteArgs> { class SplashRoute extends _i14.PageRouteInfo<SplashRouteArgs> {
SplashRoute({ SplashRoute({
dynamic Function(bool)? loggedIn, dynamic Function(bool)? loggedIn,
_i16.Key? key, _i19.Key? key,
List<_i14.PageRouteInfo>? children, List<_i14.PageRouteInfo>? children,
}) : super( }) : super(
SplashRoute.name, SplashRoute.name,
@ -389,7 +390,7 @@ class SplashRouteArgs {
final dynamic Function(bool)? loggedIn; final dynamic Function(bool)? loggedIn;
final _i16.Key? key; final _i19.Key? key;
@override @override
String toString() { String toString() {
@ -401,8 +402,8 @@ class SplashRouteArgs {
/// [_i13.SyncedScreen] /// [_i13.SyncedScreen]
class SyncedRoute extends _i14.PageRouteInfo<SyncedRouteArgs> { class SyncedRoute extends _i14.PageRouteInfo<SyncedRouteArgs> {
SyncedRoute({ SyncedRoute({
_i16.ScrollController? navigationScrollController, _i19.ScrollController? navigationScrollController,
_i16.Key? key, _i19.Key? key,
List<_i14.PageRouteInfo>? children, List<_i14.PageRouteInfo>? children,
}) : super( }) : super(
SyncedRoute.name, SyncedRoute.name,
@ -434,9 +435,9 @@ class SyncedRouteArgs {
this.key, this.key,
}); });
final _i16.ScrollController? navigationScrollController; final _i19.ScrollController? navigationScrollController;
final _i16.Key? key; final _i19.Key? key;
@override @override
String toString() { String toString() {

View file

@ -16,7 +16,7 @@ import 'package:fladder/providers/settings/home_settings_provider.dart';
import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/providers/user_provider.dart';
import 'package:fladder/providers/views_provider.dart'; import 'package:fladder/providers/views_provider.dart';
import 'package:fladder/routes/auto_router.gr.dart'; import 'package:fladder/routes/auto_router.gr.dart';
import 'package:fladder/screens/dashboard/top_posters_row.dart'; import 'package:fladder/screens/dashboard/home_banner_widget.dart';
import 'package:fladder/screens/shared/media/poster_row.dart'; import 'package:fladder/screens/shared/media/poster_row.dart';
import 'package:fladder/screens/shared/nested_scaffold.dart'; import 'package:fladder/screens/shared/nested_scaffold.dart';
import 'package:fladder/screens/shared/nested_sliver_appbar.dart'; import 'package:fladder/screens/shared/nested_sliver_appbar.dart';
@ -69,6 +69,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
final dashboardData = ref.watch(dashboardProvider); final dashboardData = ref.watch(dashboardProvider);
final views = ref.watch(viewsProvider); final views = ref.watch(viewsProvider);
final homeSettings = ref.watch(homeSettingsProvider); final homeSettings = ref.watch(homeSettingsProvider);
final homeBanner = ref.watch(homeSettingsProvider.select((value) => value.homeBanner)) != HomeBanner.hide;
final resumeVideo = dashboardData.resumeVideo; final resumeVideo = dashboardData.resumeVideo;
final resumeAudio = dashboardData.resumeAudio; final resumeAudio = dashboardData.resumeAudio;
final resumeBooks = dashboardData.resumeBooks; final resumeBooks = dashboardData.resumeBooks;
@ -79,7 +80,6 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
HomeCarouselSettings.nextUp => dashboardData.nextUp, HomeCarouselSettings.nextUp => dashboardData.nextUp,
HomeCarouselSettings.combined => [...allResume, ...dashboardData.nextUp], HomeCarouselSettings.combined => [...allResume, ...dashboardData.nextUp],
HomeCarouselSettings.cont => allResume, HomeCarouselSettings.cont => allResume,
_ => [...allResume, ...dashboardData.nextUp],
}; };
return MediaQuery.removeViewInsets( return MediaQuery.removeViewInsets(
@ -100,11 +100,11 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
route: LibrarySearchRoute(), route: LibrarySearchRoute(),
parent: context, parent: context,
), ),
if (homeSettings.carouselSettings != HomeCarouselSettings.off && homeCarouselItems.isNotEmpty) ...{ if (homeBanner && homeCarouselItems.isNotEmpty) ...{
SliverToBoxAdapter( SliverToBoxAdapter(
child: Transform.translate( child: Transform.translate(
offset: Offset(0, AdaptiveLayout.layoutOf(context) == LayoutState.phone ? -14 : 0), offset: Offset(0, AdaptiveLayout.layoutOf(context) == LayoutState.phone ? -14 : 0),
child: TopPostersRow(posters: homeCarouselItems), child: HomeBannerWidget(posters: homeCarouselItems),
), ),
), ),
} else if (AdaptiveLayout.of(context).isDesktop) } else if (AdaptiveLayout.of(context).isDesktop)
@ -147,7 +147,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
(homeSettings.nextUp == HomeNextUp.nextUp || homeSettings.nextUp == HomeNextUp.separate)) (homeSettings.nextUp == HomeNextUp.nextUp || homeSettings.nextUp == HomeNextUp.separate))
SliverToBoxAdapter( SliverToBoxAdapter(
child: PosterRow( child: PosterRow(
label: context.localized.dashboardNextUp, label: context.localized.nextUp,
posters: dashboardData.nextUp, posters: dashboardData.nextUp,
), ),
), ),

View file

@ -3,19 +3,19 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/settings/client_settings_model.dart'; import 'package:fladder/models/settings/home_settings_model.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart'; import 'package:fladder/providers/settings/home_settings_provider.dart';
import 'package:fladder/screens/shared/media/carousel_banner.dart'; import 'package:fladder/screens/shared/media/carousel_banner.dart';
import 'package:fladder/screens/shared/media/media_banner.dart'; import 'package:fladder/screens/shared/media/media_banner.dart';
class TopPostersRow extends ConsumerWidget { class HomeBannerWidget extends ConsumerWidget {
final List<ItemBaseModel> posters; final List<ItemBaseModel> posters;
const TopPostersRow({required this.posters, super.key}); const HomeBannerWidget({required this.posters, super.key});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final bannerType = ref.watch(clientSettingsProvider.select((value) => value.homeBanner)); final bannerType = ref.watch(homeSettingsProvider.select((value) => value.homeBanner));
final maxHeight = (MediaQuery.sizeOf(context).shortestSide * 0.6).clamp(125.0, 350.0); final maxHeight = (MediaQuery.sizeOf(context).shortestSide * 0.6).clamp(125.0, 375.0);
return switch (bannerType) { return switch (bannerType) {
HomeBanner.carousel => Column( HomeBanner.carousel => Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -24,13 +24,14 @@ class TopPostersRow extends ConsumerWidget {
items: posters, items: posters,
maxHeight: maxHeight, maxHeight: maxHeight,
), ),
const SizedBox(height: 8) const SizedBox(height: 24)
], ],
), ),
HomeBanner.banner => MediaBanner( HomeBanner.banner => MediaBanner(
items: posters, items: posters,
maxHeight: maxHeight, maxHeight: maxHeight,
) ),
_ => const SizedBox.shrink(),
}; };
} }
} }

View file

@ -7,7 +7,6 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/models/settings/client_settings_model.dart';
import 'package:fladder/models/settings/home_settings_model.dart'; import 'package:fladder/models/settings/home_settings_model.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart'; import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/providers/settings/home_settings_provider.dart'; import 'package:fladder/providers/settings/home_settings_provider.dart';
@ -164,34 +163,12 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
), ),
const Divider(), const Divider(),
SettingsLabelDivider(label: context.localized.dashboard), SettingsLabelDivider(label: context.localized.dashboard),
SettingsListTile(
label: Text(context.localized.settingsHomeCarouselTitle),
subLabel: Text(context.localized.settingsHomeCarouselDesc),
trailing: EnumBox(
current: ref.watch(
homeSettingsProvider.select(
(value) => value.carouselSettings.label(context),
),
),
itemBuilder: (context) => HomeCarouselSettings.values
.map(
(entry) => PopupMenuItem(
value: entry,
child: Text(entry.label(context)),
onTap: () => ref
.read(homeSettingsProvider.notifier)
.update((context) => context.copyWith(carouselSettings: entry)),
),
)
.toList(),
),
),
SettingsListTile( SettingsListTile(
label: Text(context.localized.settingsHomeBannerTitle), label: Text(context.localized.settingsHomeBannerTitle),
subLabel: Text(context.localized.settingsHomeBannerDescription), subLabel: Text(context.localized.settingsHomeBannerDescription),
trailing: EnumBox( trailing: EnumBox(
current: ref.watch( current: ref.watch(
clientSettingsProvider.select( homeSettingsProvider.select(
(value) => value.homeBanner.label(context), (value) => value.homeBanner.label(context),
), ),
), ),
@ -201,13 +178,34 @@ class _ClientSettingsPageState extends ConsumerState<ClientSettingsPage> {
value: entry, value: entry,
child: Text(entry.label(context)), child: Text(entry.label(context)),
onTap: () => ref onTap: () => ref
.read(clientSettingsProvider.notifier) .read(homeSettingsProvider.notifier)
.update((context) => context.copyWith(homeBanner: entry)), .update((context) => context.copyWith(homeBanner: entry)),
), ),
) )
.toList(), .toList(),
), ),
), ),
if (ref.watch(homeSettingsProvider.select((value) => value.homeBanner)) != HomeBanner.hide)
SettingsListTile(
label: Text(context.localized.settingsHomeBannerInformationTitle),
subLabel: Text(context.localized.settingsHomeBannerInformationDesc),
trailing: EnumBox(
current: ref.watch(
homeSettingsProvider.select((value) => value.carouselSettings.label(context)),
),
itemBuilder: (context) => HomeCarouselSettings.values
.map(
(entry) => PopupMenuItem(
value: entry,
child: Text(entry.label(context)),
onTap: () => ref
.read(homeSettingsProvider.notifier)
.update((context) => context.copyWith(carouselSettings: entry)),
),
)
.toList(),
),
),
SettingsListTile( SettingsListTile(
label: Text(context.localized.settingsHomeNextUpTitle), label: Text(context.localized.settingsHomeNextUpTitle),
subLabel: Text(context.localized.settingsHomeNextUpDesc), subLabel: Text(context.localized.settingsHomeNextUpDesc),

View file

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:ficonsax/ficonsax.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:square_progress_indicator/square_progress_indicator.dart';
import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/util/item_base_model/play_item_helpers.dart';
class BannerPlayButton extends ConsumerWidget {
final ItemBaseModel item;
const BannerPlayButton({
super.key,
required this.item,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Align(
alignment: Alignment.bottomRight,
child: Opacity(
opacity: 0.9,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Stack(
children: [
Positioned.fill(
child: SquareProgressIndicator(
value: item.userData.progress / 100,
borderRadius: 12,
strokeCap: StrokeCap.round,
color: Theme.of(context).colorScheme.primary,
),
),
IconButton(
onPressed: () => item.play(context, ref),
icon: const Icon(
IconsaxBold.play,
size: 30,
),
)
],
),
),
),
),
);
}
}

View file

@ -4,10 +4,12 @@ import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/screens/shared/media/banner_play_button.dart';
import 'package:fladder/util/adaptive_layout.dart'; import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/fladder_image.dart'; import 'package:fladder/util/fladder_image.dart';
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart'; import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
import 'package:fladder/util/list_padding.dart'; import 'package:fladder/util/list_padding.dart';
import 'package:fladder/util/themes_data.dart';
import 'package:fladder/widgets/shared/fladder_carousel.dart'; import 'package:fladder/widgets/shared/fladder_carousel.dart';
import 'package:fladder/widgets/shared/item_actions.dart'; import 'package:fladder/widgets/shared/item_actions.dart';
import 'package:fladder/widgets/shared/modal_bottom_sheet.dart'; import 'package:fladder/widgets/shared/modal_bottom_sheet.dart';
@ -34,107 +36,119 @@ class _CarouselBannerState extends ConsumerState<CarouselBanner> {
constraints: BoxConstraints(maxHeight: widget.maxHeight), constraints: BoxConstraints(maxHeight: widget.maxHeight),
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
final maxExtent = (constraints.maxHeight * 2.1).clamp(250.0, MediaQuery.sizeOf(context).shortestSide * 0.75); final maxExtent = (constraints.maxHeight * 2.1).clamp(
250.0,
(MediaQuery.sizeOf(context).shortestSide * 0.75).clamp(251.0, double.maxFinite),
);
final border = BorderRadius.circular(18); final border = BorderRadius.circular(18);
return FladderCarousel( return FladderCarousel(
elevation: 3,
shrinkExtent: 0,
shape: RoundedRectangleBorder(borderRadius: border), shape: RoundedRectangleBorder(borderRadius: border),
onTap: (index) => widget.items[index].navigateTo(context), itemPadding:
onLongPress: AdaptiveLayout.of(context).inputDevice == InputDevice.pointer const EdgeInsets.symmetric(horizontal: 4).copyWith(top: AdaptiveLayout.of(context).isDesktop ? 6 : 10),
? null padding: const EdgeInsets.symmetric(horizontal: 6),
: (index) { itemExtent: widget.items.length == 1 ? MediaQuery.sizeOf(context).width : maxExtent,
final poster = widget.items[index];
showBottomSheetPill(
context: context,
item: poster,
content: (scrollContext, scrollController) => ListView(
shrinkWrap: true,
controller: scrollController,
children: poster.generateActions(context, ref).listTileItems(scrollContext, useIcons: true),
),
);
},
onSecondaryTap: AdaptiveLayout.of(context).inputDevice == InputDevice.touch
? null
: (details) async {
Offset localPosition = details.$2.globalPosition;
RelativeRect position = RelativeRect.fromLTRB(
localPosition.dx - 320, localPosition.dy, localPosition.dx, localPosition.dy);
final poster = widget.items[details.$1];
await showMenu(
context: context,
position: position,
items: poster.generateActions(context, ref).popupMenuItems(useIcons: true),
);
},
itemExtent: maxExtent,
children: [ children: [
...widget.items.mapIndexed( ...widget.items.mapIndexed(
(index, e) => LayoutBuilder(builder: (context, constraints) { (index, item) => LayoutBuilder(builder: (context, constraints) {
final opacity = (constraints.maxWidth / maxExtent); final opacity = (constraints.maxWidth / maxExtent);
return Stack( return GestureDetector(
clipBehavior: Clip.none, onTap: () => widget.items[index].navigateTo(context),
children: [ onLongPress: AdaptiveLayout.of(context).inputDevice == InputDevice.pointer
FladderImage(image: e.bannerImage), ? null
AnimatedOpacity( : () {
duration: const Duration(milliseconds: 250), final poster = widget.items[index];
opacity: opacity.clamp(0, 1), showBottomSheetPill(
child: Stack( context: context,
children: [ item: poster,
Positioned.fill( content: (scrollContext, scrollController) => ListView(
child: Container( shrinkWrap: true,
decoration: BoxDecoration( controller: scrollController,
gradient: LinearGradient( children:
begin: Alignment.bottomLeft, poster.generateActions(context, ref).listTileItems(scrollContext, useIcons: true),
end: Alignment.topCenter, ),
colors: [ );
Theme.of(context).colorScheme.primaryContainer.withOpacity(0.75), },
Colors.transparent, onSecondaryTapDown: AdaptiveLayout.of(context).inputDevice == InputDevice.touch
], ? null
: (details) async {
Offset localPosition = details.globalPosition;
RelativeRect position = RelativeRect.fromLTRB(
localPosition.dx - 320, localPosition.dy, localPosition.dx, localPosition.dy);
final poster = widget.items[index];
await showMenu(
context: context,
position: position,
items: poster.generateActions(context, ref).popupMenuItems(useIcons: true),
);
},
child: Stack(
clipBehavior: Clip.none,
children: [
FladderImage(image: item.bannerImage),
Opacity(
opacity: opacity.clamp(0, 1),
child: Stack(
children: [
Positioned.fill(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomLeft,
end: Alignment.topCenter,
colors: [
ThemesData.of(context).dark.colorScheme.primaryContainer.withOpacity(0.85),
Colors.transparent,
],
),
), ),
), ),
), ),
), ],
],
),
),
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
e.title,
maxLines: 2,
softWrap: e.title.length > 25,
textWidthBasis: TextWidthBasis.parent,
overflow: TextOverflow.fade,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(color: Colors.white),
),
if (e.label(context) != null)
Text(
e.label(context)!,
maxLines: 2,
softWrap: false,
overflow: TextOverflow.fade,
style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Colors.white),
),
].addInBetween(const SizedBox(height: 4)),
), ),
), ),
), Align(
Container( alignment: Alignment.bottomLeft,
decoration: BoxDecoration( child: Padding(
border: Border.all( padding: const EdgeInsets.all(16.0).copyWith(right: constraints.maxWidth * 0.2),
color: Colors.white.withOpacity(0.1), child: Column(
width: 1.0, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
maxLines: 2,
softWrap: item.title.length > 25,
overflow: TextOverflow.fade,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(color: Colors.white),
),
if (item.label(context) != null || item.subText != null)
Text(
item.label(context) ?? item.subText ?? "",
maxLines: 2,
softWrap: false,
overflow: TextOverflow.fade,
style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Colors.white),
),
].addInBetween(const SizedBox(height: 4)),
), ),
borderRadius: border), ),
), ),
], BannerPlayButton(item: widget.items[index]),
IgnorePointer(
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.white.withOpacity(0.1),
width: 1.0,
),
borderRadius: border),
),
),
],
),
); );
}), }),
) )

View file

@ -1,18 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:async/async.dart'; import 'package:async/async.dart';
import 'package:collection/collection.dart';
import 'package:ficonsax/ficonsax.dart'; import 'package:ficonsax/ficonsax.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/item_base_model.dart';
import 'package:fladder/models/items/movie_model.dart'; import 'package:fladder/screens/shared/media/banner_play_button.dart';
import 'package:fladder/util/adaptive_layout.dart'; import 'package:fladder/util/adaptive_layout.dart';
import 'package:fladder/util/fladder_image.dart'; import 'package:fladder/util/fladder_image.dart';
import 'package:fladder/util/item_base_model/item_base_model_extensions.dart'; import 'package:fladder/util/item_base_model/item_base_model_extensions.dart';
import 'package:fladder/util/list_padding.dart'; import 'package:fladder/util/list_padding.dart';
import 'package:fladder/util/themes_data.dart'; import 'package:fladder/util/themes_data.dart';
import 'package:fladder/widgets/shared/fladder_carousel.dart'; import 'package:fladder/widgets/shared/fladder_carousel.dart';
import 'package:fladder/widgets/shared/fladder_slider.dart';
import 'package:fladder/widgets/shared/item_actions.dart'; import 'package:fladder/widgets/shared/item_actions.dart';
import 'package:fladder/widgets/shared/modal_bottom_sheet.dart'; import 'package:fladder/widgets/shared/modal_bottom_sheet.dart';
@ -84,269 +84,211 @@ class _MediaBannerState extends ConsumerState<MediaBanner> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final overlayColor = ThemesData.of(context).dark.colorScheme.onSecondary; final overlayColor = ThemesData.of(context).dark.colorScheme.primaryContainer;
final shadows = [ final shadows = [
BoxShadow(blurRadius: 12, spreadRadius: 8, color: overlayColor), BoxShadow(blurRadius: 12, spreadRadius: 8, color: overlayColor),
]; ];
final currentItem = widget.items[currentPage.clamp(0, widget.items.length - 1)]; final currentItem = widget.items[currentPage.clamp(0, widget.items.length - 1)];
final actions = currentItem.generateActions(context, ref);
final double dragOpacity = (1 - dragOffset.abs()).clamp(0, 1); final double dragOpacity = (1 - dragOffset.abs()).clamp(0, 1);
return Column( return Padding(
mainAxisSize: MainAxisSize.min, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
children: [ child: Column(
ConstrainedBox( mainAxisSize: MainAxisSize.min,
constraints: BoxConstraints(maxHeight: widget.maxHeight), children: [
child: Card( ConstrainedBox(
elevation: 16, constraints: BoxConstraints(maxHeight: widget.maxHeight),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), child: Card(
surfaceTintColor: overlayColor, elevation: 4,
color: overlayColor, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
child: GestureDetector( surfaceTintColor: overlayColor,
onTap: () => currentItem.navigateTo(context), color: overlayColor,
onLongPress: AdaptiveLayout.of(context).inputDevice == InputDevice.touch
? () async {
interacting = true;
final poster = currentItem;
showBottomSheetPill(
context: context,
item: poster,
content: (scrollContext, scrollController) => ListView(
shrinkWrap: true,
controller: scrollController,
children: poster.generateActions(context, ref).listTileItems(scrollContext, useIcons: true),
),
);
interacting = false;
timer.reset();
}
: null,
child: MouseRegion( child: MouseRegion(
onEnter: (event) => setState(() => showControls = true), cursor: SystemMouseCursors.click,
onHover: (event) => timer.reset(), child: GestureDetector(
onExit: (event) => setState(() => showControls = false), onTap: () => currentItem.navigateTo(context),
child: Stack( onLongPress: AdaptiveLayout.of(context).inputDevice == InputDevice.touch
fit: StackFit.expand, ? () async {
children: [ interacting = true;
Dismissible( final poster = currentItem;
key: const Key("Dismissable"), showBottomSheetPill(
direction: DismissDirection.horizontal, context: context,
onUpdate: (details) { item: poster,
setState(() { content: (scrollContext, scrollController) => ListView(
dragOffset = details.progress * 4; shrinkWrap: true,
}); controller: scrollController,
}, children:
confirmDismiss: (direction) async { poster.generateActions(context, ref).listTileItems(scrollContext, useIcons: true),
if (direction == DismissDirection.startToEnd) { ),
previousSlide(); );
} else { interacting = false;
nextSlide(); timer.reset();
} }
return false; : null,
}, onSecondaryTapDown: AdaptiveLayout.of(context).inputDevice == InputDevice.touch
child: AnimatedOpacity( ? null
duration: const Duration(milliseconds: 125), : (details) async {
opacity: dragOpacity.abs(), Offset localPosition = details.globalPosition;
child: AnimatedSwitcher( RelativeRect position = RelativeRect.fromLTRB(
duration: const Duration(milliseconds: 125), localPosition.dx - 320, localPosition.dy, localPosition.dx, localPosition.dy);
child: Container( final poster = currentItem;
key: Key(currentItem.id),
clipBehavior: Clip.hardEdge, await showMenu(
decoration: BoxDecoration( context: context,
borderRadius: BorderRadius.circular(14), position: position,
), items: poster.generateActions(context, ref).popupMenuItems(useIcons: true),
foregroundDecoration: BoxDecoration( );
borderRadius: BorderRadius.circular(14), },
border: Border.all( child: MouseRegion(
color: Colors.white.withOpacity(0.10), strokeAlign: BorderSide.strokeAlignInside), onEnter: (event) => setState(() => showControls = true),
gradient: LinearGradient( onHover: (event) => timer.reset(),
begin: Alignment.bottomLeft, onExit: (event) => setState(() => showControls = false),
end: Alignment.topCenter, child: Stack(
colors: [ fit: StackFit.expand,
overlayColor.withOpacity(1),
overlayColor.withOpacity(0.75),
overlayColor.withOpacity(0.45),
overlayColor.withOpacity(0.15),
overlayColor.withOpacity(0),
overlayColor.withOpacity(0),
overlayColor.withOpacity(0.1),
],
),
),
child: SizedBox(
width: double.infinity,
height: double.infinity,
child: Padding(
padding: const EdgeInsets.all(1),
child: FladderImage(
fit: BoxFit.cover,
image: currentItem.bannerImage,
),
),
),
),
),
),
),
Row(
children: [ children: [
Expanded( Dismissible(
child: Padding( key: const Key("Dismissable"),
padding: const EdgeInsets.all(16), direction: DismissDirection.horizontal,
child: Column( onUpdate: (details) {
mainAxisAlignment: MainAxisAlignment.end, setState(() {
crossAxisAlignment: CrossAxisAlignment.start, dragOffset = details.progress * 4;
children: [ });
Flexible( },
child: IgnorePointer( confirmDismiss: (direction) async {
child: Column( if (direction == DismissDirection.startToEnd) {
crossAxisAlignment: CrossAxisAlignment.start, previousSlide();
mainAxisSize: MainAxisSize.min, } else {
children: [ nextSlide();
Flexible( }
child: Text( return false;
currentItem.title, },
maxLines: 2, child: AnimatedOpacity(
style: Theme.of(context).textTheme.headlineMedium?.copyWith( duration: const Duration(milliseconds: 125),
shadows: shadows, opacity: dragOpacity.abs(),
color: Colors.white, child: AnimatedSwitcher(
), duration: const Duration(milliseconds: 125),
), child: Container(
), key: Key(currentItem.id),
if (currentItem.label(context) != null && currentItem is! MovieModel) clipBehavior: Clip.hardEdge,
Flexible( decoration: BoxDecoration(
child: Text( borderRadius: BorderRadius.circular(14),
currentItem.label(context)!, ),
maxLines: 2, foregroundDecoration: BoxDecoration(
style: Theme.of(context).textTheme.titleMedium?.copyWith( borderRadius: BorderRadius.circular(14),
shadows: shadows, border: Border.all(
color: Colors.white.withOpacity(0.75), color: Colors.white.withOpacity(0.10), strokeAlign: BorderSide.strokeAlignInside),
), gradient: LinearGradient(
), begin: Alignment.bottomLeft,
), end: Alignment.topCenter,
if (currentItem.overview.summary.isNotEmpty && colors: [
AdaptiveLayout.layoutOf(context) != LayoutState.phone) overlayColor.withOpacity(0.85),
Flexible( Colors.transparent,
child: Text( ],
currentItem.overview.summary, ),
maxLines: 2, ),
style: Theme.of(context).textTheme.titleMedium?.copyWith( child: SizedBox(
shadows: shadows, width: double.infinity,
color: Colors.white.withOpacity(0.75), height: double.infinity,
), child: Padding(
), padding: const EdgeInsets.all(1),
), child: FladderImage(
].addInBetween(const SizedBox(height: 6)), fit: BoxFit.cover,
image: currentItem.bannerImage,
), ),
), ),
), ),
].addInBetween(const SizedBox(height: 16)), ),
), ),
), ),
), ),
Padding( Row(
padding: const EdgeInsets.symmetric(horizontal: 12), children: [
child: AnimatedOpacity( Expanded(
opacity: showControls ? 1 : 0, child: Padding(
duration: const Duration(milliseconds: 250), padding: const EdgeInsets.all(16),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end,
children: [ crossAxisAlignment: CrossAxisAlignment.start,
IconButton.filledTonal( children: [
onPressed: () => nextSlide(), Flexible(
icon: const Icon(IconsaxOutline.arrow_right_3), child: IgnorePointer(
) child: Column(
], crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Text(
currentItem.title,
maxLines: 2,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
shadows: shadows,
color: Colors.white,
),
),
),
if (currentItem.label(context) != null || currentItem.subText != null)
Flexible(
child: Text(
currentItem.label(context) ?? currentItem.subText ?? "",
maxLines: 2,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
shadows: shadows,
color: Colors.white.withOpacity(0.75),
),
),
),
].addInBetween(const SizedBox(height: 6)),
),
),
),
].addInBetween(const SizedBox(height: 16)),
),
),
), ),
), Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: AnimatedOpacity(
opacity: showControls ? 1 : 0,
duration: const Duration(milliseconds: 250),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton.filledTonal(
onPressed: () => nextSlide(),
icon: const Icon(IconsaxOutline.arrow_right_3),
)
],
),
),
),
],
), ),
BannerPlayButton(item: currentItem),
], ],
), ),
if (AdaptiveLayout.of(context).inputDevice == InputDevice.pointer) ),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(16),
child: Card(
child: PopupMenuButton(
onOpened: () => interacting = true,
onCanceled: () {
interacting = false;
timer.reset();
},
itemBuilder: (context) => actions.popupMenuItems(useIcons: true),
),
),
),
),
],
), ),
), ),
), ),
), ),
), if (widget.items.length > 1)
GestureDetector( FractionallySizedBox(
onHorizontalDragUpdate: (details) { widthFactor: 0.35,
final delta = (details.primaryDelta ?? 0) / 20; child: FladderSlider(
slidePosition += delta; value: currentPage.toDouble(),
if (slidePosition > 1) { min: 0,
nextSlide(); animation: const Duration(milliseconds: 250),
slidePosition = 0; thumbWidth: 24,
} else if (slidePosition < -1) { activeTrackColor: Theme.of(context).colorScheme.surfaceDim,
previousSlide(); inactiveTrackColor: Theme.of(context).colorScheme.surfaceDim,
slidePosition = 0; max: widget.items.length.toDouble() - 1,
} onChanged: (value) => setState(() => currentPage = value.toInt()),
},
onHorizontalDragStart: (details) {
slidePosition = 0;
},
child: Container(
color: Colors.black.withOpacity(0),
child: Padding(
padding: const EdgeInsets.only(top: 4),
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
runAlignment: WrapAlignment.center,
children: widget.items.mapIndexed((index, e) {
return Tooltip(
message: '${e.name}\n${e.detailedName}',
child: Card(
elevation: 0,
color: Colors.transparent,
child: InkWell(
onTapUp: currentPage == index
? null
: (details) {
animateToTarget(index);
timer.reset();
},
child: Container(
alignment: Alignment.center,
color: Colors.red.withOpacity(0),
width: 28,
height: 28,
child: AnimatedContainer(
duration: const Duration(milliseconds: 125),
width: currentItem == e ? 22 : 6,
height: currentItem == e ? 10 : 6,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: currentItem == e
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.primary.withOpacity(0.25),
),
),
),
),
),
);
}).toList(),
), ),
), )
), else
) const SizedBox(height: 24)
], ],
),
); );
} }
@ -368,3 +310,28 @@ class _MediaBannerState extends ConsumerState<MediaBanner> {
updateItem(currentPage + step); updateItem(currentPage + step);
} }
} }
class RoundedTrackShape extends RoundedRectSliderTrackShape {
@override
void paint(PaintingContext context, Offset offset,
{required RenderBox parentBox,
required SliderThemeData sliderTheme,
required Animation<double> enableAnimation,
required TextDirection textDirection,
required Offset thumbCenter,
Offset? secondaryOffset,
bool isDiscrete = false,
bool isEnabled = false,
double additionalActiveTrackHeight = 0}) {
super.paint(context, offset,
parentBox: parentBox,
sliderTheme: sliderTheme,
enableAnimation: enableAnimation,
textDirection: textDirection,
thumbCenter: thumbCenter,
secondaryOffset: secondaryOffset,
isDiscrete: isDiscrete,
isEnabled: isEnabled,
additionalActiveTrackHeight: additionalActiveTrackHeight);
}
}

View file

@ -21,7 +21,9 @@ mixin _$ApplicationInfo {
String get buildNumber => throw _privateConstructorUsedError; String get buildNumber => throw _privateConstructorUsedError;
String get os => throw _privateConstructorUsedError; String get os => throw _privateConstructorUsedError;
@JsonKey(ignore: true) /// Create a copy of ApplicationInfo
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ApplicationInfoCopyWith<ApplicationInfo> get copyWith => $ApplicationInfoCopyWith<ApplicationInfo> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
@ -45,6 +47,8 @@ class _$ApplicationInfoCopyWithImpl<$Res, $Val extends ApplicationInfo>
// ignore: unused_field // ignore: unused_field
final $Res Function($Val) _then; final $Res Function($Val) _then;
/// Create a copy of ApplicationInfo
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -93,6 +97,8 @@ class __$$ApplicationInfoImplCopyWithImpl<$Res>
_$ApplicationInfoImpl _value, $Res Function(_$ApplicationInfoImpl) _then) _$ApplicationInfoImpl _value, $Res Function(_$ApplicationInfoImpl) _then)
: super(_value, _then); : super(_value, _then);
/// Create a copy of ApplicationInfo
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
@ -156,7 +162,9 @@ class _$ApplicationInfoImpl extends _ApplicationInfo {
@override @override
int get hashCode => Object.hash(runtimeType, name, version, buildNumber, os); int get hashCode => Object.hash(runtimeType, name, version, buildNumber, os);
@JsonKey(ignore: true) /// Create a copy of ApplicationInfo
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$ApplicationInfoImplCopyWith<_$ApplicationInfoImpl> get copyWith => _$$ApplicationInfoImplCopyWith<_$ApplicationInfoImpl> get copyWith =>
@ -180,8 +188,11 @@ abstract class _ApplicationInfo extends ApplicationInfo {
String get buildNumber; String get buildNumber;
@override @override
String get os; String get os;
/// Create a copy of ApplicationInfo
/// with the given fields replaced by the non-null parameter values.
@override @override
@JsonKey(ignore: true) @JsonKey(includeFromJson: false, includeToJson: false)
_$$ApplicationInfoImplCopyWith<_$ApplicationInfoImpl> get copyWith => _$$ApplicationInfoImplCopyWith<_$ApplicationInfoImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }

View file

@ -1,11 +1,13 @@
import 'package:fladder/models/items/images_models.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/util/adaptive_layout.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_blurhash/flutter_blurhash.dart'; import 'package:flutter_blurhash/flutter_blurhash.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:transparent_image/transparent_image.dart'; import 'package:transparent_image/transparent_image.dart';
import 'package:fladder/models/items/images_models.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/util/adaptive_layout.dart';
class FladderImage extends ConsumerWidget { class FladderImage extends ConsumerWidget {
final ImageData? image; final ImageData? image;
final Widget Function(BuildContext context, Widget child, int? frame, bool wasSynchronouslyLoaded)? frameBuilder; final Widget Function(BuildContext context, Widget child, int? frame, bool wasSynchronouslyLoaded)? frameBuilder;

View file

@ -48,7 +48,8 @@ class FladderCarousel extends StatefulWidget {
/// Creates a Material Design carousel. /// Creates a Material Design carousel.
const FladderCarousel({ const FladderCarousel({
super.key, super.key,
this.padding, this.itemPadding,
this.padding = EdgeInsets.zero,
this.backgroundColor, this.backgroundColor,
this.elevation, this.elevation,
this.shape, this.shape,
@ -68,7 +69,9 @@ class FladderCarousel extends StatefulWidget {
/// The amount of space to surround each carousel item with. /// The amount of space to surround each carousel item with.
/// ///
/// Defaults to [EdgeInsets.all] of 4 pixels. /// Defaults to [EdgeInsets.all] of 4 pixels.
final EdgeInsets? padding; final EdgeInsets? itemPadding;
final EdgeInsets padding;
/// The background color for each carousel item. /// The background color for each carousel item.
/// ///
@ -234,7 +237,7 @@ class _CarouselViewState extends State<FladderCarousel> {
final AxisDirection axisDirection = _getDirection(context); final AxisDirection axisDirection = _getDirection(context);
final ScrollPhysics physics = final ScrollPhysics physics =
widget.itemSnapping ? const CarouselScrollPhysics() : ScrollConfiguration.of(context).getScrollPhysics(context); widget.itemSnapping ? const CarouselScrollPhysics() : ScrollConfiguration.of(context).getScrollPhysics(context);
final EdgeInsets effectivePadding = widget.padding ?? const EdgeInsets.all(4.0); final EdgeInsets effectivePadding = widget.itemPadding ?? const EdgeInsets.all(4.0);
final Color effectiveBackgroundColor = widget.backgroundColor ?? Theme.of(context).colorScheme.surface; final Color effectiveBackgroundColor = widget.backgroundColor ?? Theme.of(context).colorScheme.surface;
final double effectiveElevation = widget.elevation ?? 0.0; final double effectiveElevation = widget.elevation ?? 0.0;
final ShapeBorder effectiveShape = final ShapeBorder effectiveShape =
@ -266,7 +269,10 @@ class _CarouselViewState extends State<FladderCarousel> {
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) { (BuildContext context, int index) {
return Padding( return Padding(
padding: effectivePadding, padding: effectivePadding.add(EdgeInsets.only(
left: index == 0 ? widget.padding.left : 0,
right: index == widget.children.length - 1 ? widget.padding.right : 0,
)),
child: Material( child: Material(
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
color: effectiveBackgroundColor, color: effectiveBackgroundColor,
@ -276,29 +282,31 @@ class _CarouselViewState extends State<FladderCarousel> {
fit: StackFit.expand, fit: StackFit.expand,
children: <Widget>[ children: <Widget>[
widget.children.elementAt(index), widget.children.elementAt(index),
Material( if (widget.onTap != null || widget.onSecondaryTap != null || widget.onLongPress != null)
color: Colors.transparent, Material(
child: InkWell( color: Colors.transparent,
onTap: widget.onTap != null ? () => widget.onTap!.call(index) : null, child: InkWell(
onLongPress: widget.onLongPress != null ? () => widget.onLongPress!.call(index) : null, onTap: widget.onTap != null ? () => widget.onTap!.call(index) : null,
onSecondaryTapDown: widget.onSecondaryTap != null onLongPress:
? (details) => widget.onSecondaryTap!.call((index, details)) widget.onLongPress != null ? () => widget.onLongPress!.call(index) : null,
: null, onSecondaryTapDown: widget.onSecondaryTap != null
overlayColor: widget.overlayColor ?? ? (details) => widget.onSecondaryTap!.call((index, details))
WidgetStateProperty.resolveWith((Set<WidgetState> states) { : null,
if (states.contains(WidgetState.pressed)) { overlayColor: widget.overlayColor ??
return theme.colorScheme.onSurface.withOpacity(0.1); WidgetStateProperty.resolveWith((Set<WidgetState> states) {
} if (states.contains(WidgetState.pressed)) {
if (states.contains(WidgetState.hovered)) { return theme.colorScheme.onSurface.withOpacity(0.1);
return theme.colorScheme.onSurface.withOpacity(0.08); }
} if (states.contains(WidgetState.hovered)) {
if (states.contains(WidgetState.focused)) { return theme.colorScheme.onSurface.withOpacity(0.08);
return theme.colorScheme.onSurface.withOpacity(0.1); }
} if (states.contains(WidgetState.focused)) {
return null; return theme.colorScheme.onSurface.withOpacity(0.1);
}), }
return null;
}),
),
), ),
),
], ],
), ),
), ),

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:fladder/util/num_extension.dart'; import 'package:fladder/util/num_extension.dart';
import 'package:fladder/widgets/gapped_container_shape.dart'; import 'package:fladder/widgets/gapped_container_shape.dart';
import 'package:flutter/material.dart';
double normalize(double min, double max, double value) { double normalize(double min, double max, double value) {
return (value - min) / (max - min); return (value - min) / (max - min);
@ -12,6 +13,8 @@ class FladderSlider extends StatefulWidget {
final double max; final double max;
final int? divisions; final int? divisions;
final double thumbWidth; final double thumbWidth;
final Color? activeTrackColor;
final Color? inactiveTrackColor;
final bool showThumb; final bool showThumb;
final Duration animation; final Duration animation;
final Function(double value)? onChanged; final Function(double value)? onChanged;
@ -25,6 +28,8 @@ class FladderSlider extends StatefulWidget {
this.divisions, this.divisions,
this.onChanged, this.onChanged,
this.thumbWidth = 6.5, this.thumbWidth = 6.5,
this.activeTrackColor,
this.inactiveTrackColor,
this.showThumb = true, this.showThumb = true,
this.animation = const Duration(milliseconds: 100), this.animation = const Duration(milliseconds: 100),
this.onChangeStart, this.onChangeStart,
@ -146,6 +151,8 @@ class FladderSliderState extends State<FladderSlider> with SingleTickerProviderS
height: height, height: height,
width: constraints.maxWidth, width: constraints.maxWidth,
child: GappedContainerShape( child: GappedContainerShape(
activeColor: widget.activeTrackColor,
inActiveColor: widget.inactiveTrackColor,
thumbPosition: relativeValue, thumbPosition: relativeValue,
), ),
), ),