refactor: migrate models to freezed + json_serializable

This commit is contained in:
edde746
2026-05-11 12:54:26 +02:00
parent 2bbd31d927
commit d0bd919ff4
59 changed files with 9551 additions and 1236 deletions
+9
View File
@@ -38,6 +38,15 @@ jobs:
flutter pub get
- name: Verify generated files committed
run: |
dart run build_runner build --delete-conflicting-outputs
if ! git diff --exit-code lib/; then
echo "::error::Generated files (.g.dart / .freezed.dart) are out of date."
echo "Run 'scripts/codegen.sh' and commit the result."
exit 1
fi
- name: Verify formatting
run: |
# Find all Dart files excluding generated files
+15
View File
@@ -0,0 +1,15 @@
targets:
$default:
builders:
json_serializable:
options:
create_to_json: true
explicit_to_json: true
field_rename: none
include_if_null: true
checked: false
freezed:
options:
copy_with: true
equal: true
to_string: true
+45 -85
View File
@@ -1,114 +1,74 @@
// ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart';
import 'media_kind.dart';
part 'library_query.freezed.dart';
/// Sort order applied to a library query.
enum LibrarySortDirection { ascending, descending }
class LibrarySort {
@freezed
sealed class LibrarySort with _$LibrarySort {
/// Backend-neutral sort field. Common values: `addedAt`, `originallyAvailableAt`,
/// `lastViewedAt`, `title`, `rating`, `viewCount`, `random`.
final String field;
final LibrarySortDirection direction;
const LibrarySort({required this.field, this.direction = LibrarySortDirection.descending});
const factory LibrarySort({
required String field,
@Default(LibrarySortDirection.descending) LibrarySortDirection direction,
}) = _LibrarySort;
}
/// A single filter clause. The semantics of `field` and `value` are
/// backend-translated — the neutral query just carries the intent.
class LibraryFilter {
final String field;
final String op; // "=", "!=", "contains", ">=", etc.
final List<String> values;
const LibraryFilter({required this.field, this.op = '=', required this.values});
@freezed
sealed class LibraryFilter with _$LibraryFilter {
const factory LibraryFilter({required String field, @Default('=') String op, required List<String> values}) =
_LibraryFilter;
}
/// Backend-neutral library content query. Each backend's adapter translates
/// these into its own query DSL (Plex `/library/sections/{id}/all?type=...`
/// or Jellyfin `/Items?ParentId=...&Filters=...`).
class LibraryQuery {
/// Restrict to a single kind (e.g. `MediaKind.movie`). Null = library default.
final MediaKind? kind;
/// Pagination — zero-based offset.
final int offset;
final int limit;
final LibrarySort? sort;
final List<LibraryFilter> filters;
/// Free-text search restricted to this library. Distinct from the global
/// search endpoint.
final String? search;
/// Whether to include items the active user has already watched.
final bool includeWatched;
/// Restrict the result to items whose sort name starts with this string —
/// the alpha-jump bar's filter UX. The literal `#` is a sentinel for
/// "non-alphabetic" and translates to a `NameLessThan=A` query for backends
/// that support it.
final String? nameStartsWith;
/// Genre filter — used by the per-library filter sheet. Backends that
/// take multiple values (Jellyfin) AND/intersect; those that take one
/// (Plex's existing flow) consult `filters` instead.
final List<String>? genres;
final List<String>? officialRatings;
final List<int>? years;
final List<String>? tags;
const LibraryQuery({
this.kind,
this.offset = 0,
this.limit = 50,
this.sort,
this.filters = const [],
this.search,
this.includeWatched = true,
this.nameStartsWith,
this.genres,
this.officialRatings,
this.years,
this.tags,
});
LibraryQuery copyWith({
@freezed
sealed class LibraryQuery with _$LibraryQuery {
const factory LibraryQuery({
/// Restrict to a single kind (e.g. `MediaKind.movie`). Null = library default.
MediaKind? kind,
int? offset,
int? limit,
/// Pagination — zero-based offset.
@Default(0) int offset,
@Default(50) int limit,
LibrarySort? sort,
List<LibraryFilter>? filters,
@Default(<LibraryFilter>[]) List<LibraryFilter> filters,
/// Free-text search restricted to this library. Distinct from the global
/// search endpoint.
String? search,
bool? includeWatched,
/// Whether to include items the active user has already watched.
@Default(true) bool includeWatched,
/// Restrict the result to items whose sort name starts with this string —
/// the alpha-jump bar's filter UX. The literal `#` is a sentinel for
/// "non-alphabetic" and translates to a `NameLessThan=A` query for backends
/// that support it.
String? nameStartsWith,
/// Genre filter — used by the per-library filter sheet. Backends that
/// take multiple values (Jellyfin) AND/intersect; those that take one
/// (Plex's existing flow) consult `filters` instead.
List<String>? genres,
List<String>? officialRatings,
List<int>? years,
List<String>? tags,
}) {
return LibraryQuery(
kind: kind ?? this.kind,
offset: offset ?? this.offset,
limit: limit ?? this.limit,
sort: sort ?? this.sort,
filters: filters ?? this.filters,
search: search ?? this.search,
includeWatched: includeWatched ?? this.includeWatched,
nameStartsWith: nameStartsWith ?? this.nameStartsWith,
genres: genres ?? this.genres,
officialRatings: officialRatings ?? this.officialRatings,
years: years ?? this.years,
tags: tags ?? this.tags,
);
}
}) = _LibraryQuery;
}
/// Page of items returned by [MediaServerClient.getLibraryContent].
/// Carries the total count so the UI can render correct pagination affordances.
class LibraryPage<T> {
final List<T> items;
final int totalCount;
final int offset;
const LibraryPage({required this.items, required this.totalCount, this.offset = 0});
@freezed
sealed class LibraryPage<T> with _$LibraryPage<T> {
const factory LibraryPage({required List<T> items, required int totalCount, @Default(0) int offset}) =
_LibraryPage<T>;
}
File diff suppressed because it is too large Load Diff
+7 -24
View File
@@ -1,20 +1,17 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'media_sort.freezed.dart';
part 'media_sort.g.dart';
@JsonSerializable()
class MediaSort {
final String key;
final String? descKey;
final String title;
final String? defaultDirection;
@freezed
sealed class MediaSort with _$MediaSort {
const MediaSort._();
MediaSort({required this.key, this.descKey, required this.title, this.defaultDirection});
const factory MediaSort({required String key, String? descKey, required String title, String? defaultDirection}) =
_MediaSort;
factory MediaSort.fromJson(Map<String, dynamic> json) => _$MediaSortFromJson(json);
Map<String, dynamic> toJson() => _$MediaSortToJson(this);
String getSortKey({bool descending = false}) {
if (!descending) {
return key;
@@ -26,18 +23,4 @@ class MediaSort {
bool get isDefaultDescending {
return defaultDirection?.toLowerCase() == 'desc';
}
@override
String toString() {
return 'MediaSort(key: $key, title: $title, defaultDirection: $defaultDirection)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is MediaSort && other.key == key;
}
@override
int get hashCode => key.hashCode;
}
+280
View File
@@ -0,0 +1,280 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'media_sort.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$MediaSort {
String get key; String? get descKey; String get title; String? get defaultDirection;
/// Create a copy of MediaSort
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$MediaSortCopyWith<MediaSort> get copyWith => _$MediaSortCopyWithImpl<MediaSort>(this as MediaSort, _$identity);
/// Serializes this MediaSort to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is MediaSort&&(identical(other.key, key) || other.key == key)&&(identical(other.descKey, descKey) || other.descKey == descKey)&&(identical(other.title, title) || other.title == title)&&(identical(other.defaultDirection, defaultDirection) || other.defaultDirection == defaultDirection));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,key,descKey,title,defaultDirection);
@override
String toString() {
return 'MediaSort(key: $key, descKey: $descKey, title: $title, defaultDirection: $defaultDirection)';
}
}
/// @nodoc
abstract mixin class $MediaSortCopyWith<$Res> {
factory $MediaSortCopyWith(MediaSort value, $Res Function(MediaSort) _then) = _$MediaSortCopyWithImpl;
@useResult
$Res call({
String key, String? descKey, String title, String? defaultDirection
});
}
/// @nodoc
class _$MediaSortCopyWithImpl<$Res>
implements $MediaSortCopyWith<$Res> {
_$MediaSortCopyWithImpl(this._self, this._then);
final MediaSort _self;
final $Res Function(MediaSort) _then;
/// Create a copy of MediaSort
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? key = null,Object? descKey = freezed,Object? title = null,Object? defaultDirection = freezed,}) {
return _then(_self.copyWith(
key: null == key ? _self.key : key // ignore: cast_nullable_to_non_nullable
as String,descKey: freezed == descKey ? _self.descKey : descKey // ignore: cast_nullable_to_non_nullable
as String?,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,defaultDirection: freezed == defaultDirection ? _self.defaultDirection : defaultDirection // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// Adds pattern-matching-related methods to [MediaSort].
extension MediaSortPatterns on MediaSort {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _MediaSort value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _MediaSort() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _MediaSort value) $default,){
final _that = this;
switch (_that) {
case _MediaSort():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _MediaSort value)? $default,){
final _that = this;
switch (_that) {
case _MediaSort() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String key, String? descKey, String title, String? defaultDirection)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _MediaSort() when $default != null:
return $default(_that.key,_that.descKey,_that.title,_that.defaultDirection);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String key, String? descKey, String title, String? defaultDirection) $default,) {final _that = this;
switch (_that) {
case _MediaSort():
return $default(_that.key,_that.descKey,_that.title,_that.defaultDirection);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String key, String? descKey, String title, String? defaultDirection)? $default,) {final _that = this;
switch (_that) {
case _MediaSort() when $default != null:
return $default(_that.key,_that.descKey,_that.title,_that.defaultDirection);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _MediaSort extends MediaSort {
const _MediaSort({required this.key, this.descKey, required this.title, this.defaultDirection}): super._();
factory _MediaSort.fromJson(Map<String, dynamic> json) => _$MediaSortFromJson(json);
@override final String key;
@override final String? descKey;
@override final String title;
@override final String? defaultDirection;
/// Create a copy of MediaSort
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$MediaSortCopyWith<_MediaSort> get copyWith => __$MediaSortCopyWithImpl<_MediaSort>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$MediaSortToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MediaSort&&(identical(other.key, key) || other.key == key)&&(identical(other.descKey, descKey) || other.descKey == descKey)&&(identical(other.title, title) || other.title == title)&&(identical(other.defaultDirection, defaultDirection) || other.defaultDirection == defaultDirection));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,key,descKey,title,defaultDirection);
@override
String toString() {
return 'MediaSort(key: $key, descKey: $descKey, title: $title, defaultDirection: $defaultDirection)';
}
}
/// @nodoc
abstract mixin class _$MediaSortCopyWith<$Res> implements $MediaSortCopyWith<$Res> {
factory _$MediaSortCopyWith(_MediaSort value, $Res Function(_MediaSort) _then) = __$MediaSortCopyWithImpl;
@override @useResult
$Res call({
String key, String? descKey, String title, String? defaultDirection
});
}
/// @nodoc
class __$MediaSortCopyWithImpl<$Res>
implements _$MediaSortCopyWith<$Res> {
__$MediaSortCopyWithImpl(this._self, this._then);
final _MediaSort _self;
final $Res Function(_MediaSort) _then;
/// Create a copy of MediaSort
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? key = null,Object? descKey = freezed,Object? title = null,Object? defaultDirection = freezed,}) {
return _then(_MediaSort(
key: null == key ? _self.key : key // ignore: cast_nullable_to_non_nullable
as String,descKey: freezed == descKey ? _self.descKey : descKey // ignore: cast_nullable_to_non_nullable
as String?,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,defaultDirection: freezed == defaultDirection ? _self.defaultDirection : defaultDirection // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
// dart format on
+8 -7
View File
@@ -6,16 +6,17 @@ part of 'media_sort.dart';
// JsonSerializableGenerator
// **************************************************************************
MediaSort _$MediaSortFromJson(Map<String, dynamic> json) => MediaSort(
_MediaSort _$MediaSortFromJson(Map<String, dynamic> json) => _MediaSort(
key: json['key'] as String,
descKey: json['descKey'] as String?,
title: json['title'] as String,
defaultDirection: json['defaultDirection'] as String?,
);
Map<String, dynamic> _$MediaSortToJson(MediaSort instance) => <String, dynamic>{
'key': instance.key,
'descKey': instance.descKey,
'title': instance.title,
'defaultDirection': instance.defaultDirection,
};
Map<String, dynamic> _$MediaSortToJson(_MediaSort instance) =>
<String, dynamic>{
'key': instance.key,
'descKey': instance.descKey,
'title': instance.title,
'defaultDirection': instance.defaultDirection,
};
+45 -106
View File
@@ -1,122 +1,61 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'media_item.dart';
part 'play_queue.freezed.dart';
/// Backend-neutral play queue — a flat ordered list of items with a current
/// cursor. Implementations differ in whether the queue is server-resourced
/// (Plex) or client-only (Jellyfin).
sealed class PlayQueue {
/// Items in playback order.
List<MediaItem> get items;
@freezed
sealed class PlayQueue with _$PlayQueue {
const PlayQueue._();
/// Index of the currently-playing item, or `null` if the queue has not
/// started.
int? get currentIndex;
/// Whether the queue has been shuffled.
bool get shuffled;
/// Backend that minted this queue.
String get backendId;
MediaItem? get current =>
currentIndex != null && currentIndex! >= 0 && currentIndex! < items.length ? items[currentIndex!] : null;
bool get hasNext => currentIndex != null && currentIndex! + 1 < items.length;
bool get hasPrevious => currentIndex != null && currentIndex! > 0;
}
/// Plex play queue — coordinated server-side via `/playQueues` so multiple
/// devices can view/control the same queue.
class PlexServerPlayQueue extends PlayQueue {
/// Plex `playQueueID` — addresses the queue for subsequent fetches.
final int playQueueId;
@override
final List<MediaItem> items;
@override
final int? currentIndex;
@override
final bool shuffled;
/// Plex `playQueueSelectedItemID` of the active item.
final int? selectedItemId;
/// Plex `playQueueVersion` — server-side optimistic concurrency token.
final int? version;
/// Plex `playQueueSourceURI` — used for "Up Next" derivation.
final String? sourceUri;
PlexServerPlayQueue({
required this.playQueueId,
required this.items,
this.currentIndex,
this.shuffled = false,
this.selectedItemId,
this.version,
this.sourceUri,
});
@override
String get backendId => 'plex';
PlexServerPlayQueue copyWith({
int? playQueueId,
List<MediaItem>? items,
/// Plex play queue — coordinated server-side via `/playQueues` so multiple
/// devices can view/control the same queue.
const factory PlayQueue.plex({
/// Plex `playQueueID` — addresses the queue for subsequent fetches.
required int playQueueId,
required List<MediaItem> items,
int? currentIndex,
bool? shuffled,
@Default(false) bool shuffled,
/// Plex `playQueueSelectedItemID` of the active item.
int? selectedItemId,
/// Plex `playQueueVersion` — server-side optimistic concurrency token.
int? version,
/// Plex `playQueueSourceURI` — used for "Up Next" derivation.
String? sourceUri,
}) {
return PlexServerPlayQueue(
playQueueId: playQueueId ?? this.playQueueId,
items: items ?? this.items,
currentIndex: currentIndex ?? this.currentIndex,
shuffled: shuffled ?? this.shuffled,
selectedItemId: selectedItemId ?? this.selectedItemId,
version: version ?? this.version,
sourceUri: sourceUri ?? this.sourceUri,
);
}
}
}) = PlexServerPlayQueue;
/// Client-only play queue used by Jellyfin and any backend without a
/// server-side queue concept. Each [LocalPlayQueue] is anchored by a
/// client-generated UUID so callers can address it like a Plex queue.
class LocalPlayQueue extends PlayQueue {
/// Client-generated UUID identifying this queue for the session.
final String id;
/// Client-only play queue used by Jellyfin and any backend without a
/// server-side queue concept. Each [LocalPlayQueue] is anchored by a
/// client-generated UUID so callers can address it like a Plex queue.
const factory PlayQueue.local({
/// Client-generated UUID identifying this queue for the session.
required String id,
required List<MediaItem> items,
@override
final List<MediaItem> items;
/// Server kind that owns this queue's items (typically `"jellyfin"`).
required String backendId,
int? currentIndex,
@Default(false) bool shuffled,
}) = LocalPlayQueue;
@override
final int? currentIndex;
MediaItem? get current => switch (this) {
PlexServerPlayQueue(:final items, :final currentIndex) || LocalPlayQueue(:final items, :final currentIndex) =>
currentIndex != null && currentIndex >= 0 && currentIndex < items.length ? items[currentIndex] : null,
};
@override
final bool shuffled;
bool get hasNext => switch (this) {
PlexServerPlayQueue(:final items, :final currentIndex) ||
LocalPlayQueue(:final items, :final currentIndex) => currentIndex != null && currentIndex + 1 < items.length,
};
/// Server kind that owns this queue's items (typically `"jellyfin"`).
@override
final String backendId;
LocalPlayQueue({
required this.id,
required this.items,
required this.backendId,
this.currentIndex,
this.shuffled = false,
});
LocalPlayQueue copyWith({String? id, List<MediaItem>? items, int? currentIndex, bool? shuffled, String? backendId}) {
return LocalPlayQueue(
id: id ?? this.id,
items: items ?? this.items,
currentIndex: currentIndex ?? this.currentIndex,
shuffled: shuffled ?? this.shuffled,
backendId: backendId ?? this.backendId,
);
}
bool get hasPrevious => switch (this) {
PlexServerPlayQueue(:final currentIndex) ||
LocalPlayQueue(:final currentIndex) => currentIndex != null && currentIndex > 0,
};
}
+377
View File
@@ -0,0 +1,377 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'play_queue.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$PlayQueue {
List<MediaItem> get items; int? get currentIndex; bool get shuffled;
/// Create a copy of PlayQueue
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PlayQueueCopyWith<PlayQueue> get copyWith => _$PlayQueueCopyWithImpl<PlayQueue>(this as PlayQueue, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PlayQueue&&const DeepCollectionEquality().equals(other.items, items)&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.shuffled, shuffled) || other.shuffled == shuffled));
}
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(items),currentIndex,shuffled);
@override
String toString() {
return 'PlayQueue(items: $items, currentIndex: $currentIndex, shuffled: $shuffled)';
}
}
/// @nodoc
abstract mixin class $PlayQueueCopyWith<$Res> {
factory $PlayQueueCopyWith(PlayQueue value, $Res Function(PlayQueue) _then) = _$PlayQueueCopyWithImpl;
@useResult
$Res call({
List<MediaItem> items, int? currentIndex, bool shuffled
});
}
/// @nodoc
class _$PlayQueueCopyWithImpl<$Res>
implements $PlayQueueCopyWith<$Res> {
_$PlayQueueCopyWithImpl(this._self, this._then);
final PlayQueue _self;
final $Res Function(PlayQueue) _then;
/// Create a copy of PlayQueue
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? items = null,Object? currentIndex = freezed,Object? shuffled = null,}) {
return _then(_self.copyWith(
items: null == items ? _self.items : items // ignore: cast_nullable_to_non_nullable
as List<MediaItem>,currentIndex: freezed == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable
as int?,shuffled: null == shuffled ? _self.shuffled : shuffled // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// Adds pattern-matching-related methods to [PlayQueue].
extension PlayQueuePatterns on PlayQueue {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( PlexServerPlayQueue value)? plex,TResult Function( LocalPlayQueue value)? local,required TResult orElse(),}){
final _that = this;
switch (_that) {
case PlexServerPlayQueue() when plex != null:
return plex(_that);case LocalPlayQueue() when local != null:
return local(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( PlexServerPlayQueue value) plex,required TResult Function( LocalPlayQueue value) local,}){
final _that = this;
switch (_that) {
case PlexServerPlayQueue():
return plex(_that);case LocalPlayQueue():
return local(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( PlexServerPlayQueue value)? plex,TResult? Function( LocalPlayQueue value)? local,}){
final _that = this;
switch (_that) {
case PlexServerPlayQueue() when plex != null:
return plex(_that);case LocalPlayQueue() when local != null:
return local(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function( int playQueueId, List<MediaItem> items, int? currentIndex, bool shuffled, int? selectedItemId, int? version, String? sourceUri)? plex,TResult Function( String id, List<MediaItem> items, String backendId, int? currentIndex, bool shuffled)? local,required TResult orElse(),}) {final _that = this;
switch (_that) {
case PlexServerPlayQueue() when plex != null:
return plex(_that.playQueueId,_that.items,_that.currentIndex,_that.shuffled,_that.selectedItemId,_that.version,_that.sourceUri);case LocalPlayQueue() when local != null:
return local(_that.id,_that.items,_that.backendId,_that.currentIndex,_that.shuffled);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function( int playQueueId, List<MediaItem> items, int? currentIndex, bool shuffled, int? selectedItemId, int? version, String? sourceUri) plex,required TResult Function( String id, List<MediaItem> items, String backendId, int? currentIndex, bool shuffled) local,}) {final _that = this;
switch (_that) {
case PlexServerPlayQueue():
return plex(_that.playQueueId,_that.items,_that.currentIndex,_that.shuffled,_that.selectedItemId,_that.version,_that.sourceUri);case LocalPlayQueue():
return local(_that.id,_that.items,_that.backendId,_that.currentIndex,_that.shuffled);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function( int playQueueId, List<MediaItem> items, int? currentIndex, bool shuffled, int? selectedItemId, int? version, String? sourceUri)? plex,TResult? Function( String id, List<MediaItem> items, String backendId, int? currentIndex, bool shuffled)? local,}) {final _that = this;
switch (_that) {
case PlexServerPlayQueue() when plex != null:
return plex(_that.playQueueId,_that.items,_that.currentIndex,_that.shuffled,_that.selectedItemId,_that.version,_that.sourceUri);case LocalPlayQueue() when local != null:
return local(_that.id,_that.items,_that.backendId,_that.currentIndex,_that.shuffled);case _:
return null;
}
}
}
/// @nodoc
class PlexServerPlayQueue extends PlayQueue {
const PlexServerPlayQueue({required this.playQueueId, required final List<MediaItem> items, this.currentIndex, this.shuffled = false, this.selectedItemId, this.version, this.sourceUri}): _items = items,super._();
/// Plex `playQueueID` — addresses the queue for subsequent fetches.
final int playQueueId;
final List<MediaItem> _items;
@override List<MediaItem> get items {
if (_items is EqualUnmodifiableListView) return _items;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_items);
}
@override final int? currentIndex;
@override@JsonKey() final bool shuffled;
/// Plex `playQueueSelectedItemID` of the active item.
final int? selectedItemId;
/// Plex `playQueueVersion` — server-side optimistic concurrency token.
final int? version;
/// Plex `playQueueSourceURI` — used for "Up Next" derivation.
final String? sourceUri;
/// Create a copy of PlayQueue
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PlexServerPlayQueueCopyWith<PlexServerPlayQueue> get copyWith => _$PlexServerPlayQueueCopyWithImpl<PlexServerPlayQueue>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PlexServerPlayQueue&&(identical(other.playQueueId, playQueueId) || other.playQueueId == playQueueId)&&const DeepCollectionEquality().equals(other._items, _items)&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.shuffled, shuffled) || other.shuffled == shuffled)&&(identical(other.selectedItemId, selectedItemId) || other.selectedItemId == selectedItemId)&&(identical(other.version, version) || other.version == version)&&(identical(other.sourceUri, sourceUri) || other.sourceUri == sourceUri));
}
@override
int get hashCode => Object.hash(runtimeType,playQueueId,const DeepCollectionEquality().hash(_items),currentIndex,shuffled,selectedItemId,version,sourceUri);
@override
String toString() {
return 'PlayQueue.plex(playQueueId: $playQueueId, items: $items, currentIndex: $currentIndex, shuffled: $shuffled, selectedItemId: $selectedItemId, version: $version, sourceUri: $sourceUri)';
}
}
/// @nodoc
abstract mixin class $PlexServerPlayQueueCopyWith<$Res> implements $PlayQueueCopyWith<$Res> {
factory $PlexServerPlayQueueCopyWith(PlexServerPlayQueue value, $Res Function(PlexServerPlayQueue) _then) = _$PlexServerPlayQueueCopyWithImpl;
@override @useResult
$Res call({
int playQueueId, List<MediaItem> items, int? currentIndex, bool shuffled, int? selectedItemId, int? version, String? sourceUri
});
}
/// @nodoc
class _$PlexServerPlayQueueCopyWithImpl<$Res>
implements $PlexServerPlayQueueCopyWith<$Res> {
_$PlexServerPlayQueueCopyWithImpl(this._self, this._then);
final PlexServerPlayQueue _self;
final $Res Function(PlexServerPlayQueue) _then;
/// Create a copy of PlayQueue
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? playQueueId = null,Object? items = null,Object? currentIndex = freezed,Object? shuffled = null,Object? selectedItemId = freezed,Object? version = freezed,Object? sourceUri = freezed,}) {
return _then(PlexServerPlayQueue(
playQueueId: null == playQueueId ? _self.playQueueId : playQueueId // ignore: cast_nullable_to_non_nullable
as int,items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
as List<MediaItem>,currentIndex: freezed == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable
as int?,shuffled: null == shuffled ? _self.shuffled : shuffled // ignore: cast_nullable_to_non_nullable
as bool,selectedItemId: freezed == selectedItemId ? _self.selectedItemId : selectedItemId // ignore: cast_nullable_to_non_nullable
as int?,version: freezed == version ? _self.version : version // ignore: cast_nullable_to_non_nullable
as int?,sourceUri: freezed == sourceUri ? _self.sourceUri : sourceUri // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
class LocalPlayQueue extends PlayQueue {
const LocalPlayQueue({required this.id, required final List<MediaItem> items, required this.backendId, this.currentIndex, this.shuffled = false}): _items = items,super._();
/// Client-generated UUID identifying this queue for the session.
final String id;
final List<MediaItem> _items;
@override List<MediaItem> get items {
if (_items is EqualUnmodifiableListView) return _items;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_items);
}
/// Server kind that owns this queue's items (typically `"jellyfin"`).
final String backendId;
@override final int? currentIndex;
@override@JsonKey() final bool shuffled;
/// Create a copy of PlayQueue
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$LocalPlayQueueCopyWith<LocalPlayQueue> get copyWith => _$LocalPlayQueueCopyWithImpl<LocalPlayQueue>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is LocalPlayQueue&&(identical(other.id, id) || other.id == id)&&const DeepCollectionEquality().equals(other._items, _items)&&(identical(other.backendId, backendId) || other.backendId == backendId)&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex)&&(identical(other.shuffled, shuffled) || other.shuffled == shuffled));
}
@override
int get hashCode => Object.hash(runtimeType,id,const DeepCollectionEquality().hash(_items),backendId,currentIndex,shuffled);
@override
String toString() {
return 'PlayQueue.local(id: $id, items: $items, backendId: $backendId, currentIndex: $currentIndex, shuffled: $shuffled)';
}
}
/// @nodoc
abstract mixin class $LocalPlayQueueCopyWith<$Res> implements $PlayQueueCopyWith<$Res> {
factory $LocalPlayQueueCopyWith(LocalPlayQueue value, $Res Function(LocalPlayQueue) _then) = _$LocalPlayQueueCopyWithImpl;
@override @useResult
$Res call({
String id, List<MediaItem> items, String backendId, int? currentIndex, bool shuffled
});
}
/// @nodoc
class _$LocalPlayQueueCopyWithImpl<$Res>
implements $LocalPlayQueueCopyWith<$Res> {
_$LocalPlayQueueCopyWithImpl(this._self, this._then);
final LocalPlayQueue _self;
final $Res Function(LocalPlayQueue) _then;
/// Create a copy of PlayQueue
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? items = null,Object? backendId = null,Object? currentIndex = freezed,Object? shuffled = null,}) {
return _then(LocalPlayQueue(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
as List<MediaItem>,backendId: null == backendId ? _self.backendId : backendId // ignore: cast_nullable_to_non_nullable
as String,currentIndex: freezed == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable
as int?,shuffled: null == shuffled ? _self.shuffled : shuffled // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
// dart format on
+20 -20
View File
@@ -1,3 +1,11 @@
// ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../utils/json_converters.dart';
part 'remote_command.freezed.dart';
part 'remote_command.g.dart';
enum RemoteCommandType {
dpadUp,
dpadDown,
@@ -46,24 +54,16 @@ enum RemoteCommandType {
syncState,
}
class RemoteCommand {
final RemoteCommandType type;
final Map<String, dynamic>? data;
const RemoteCommand({required this.type, this.data});
factory RemoteCommand.fromJson(Map<String, dynamic> json) {
final index = json['t'] as int;
return RemoteCommand(
type: index < RemoteCommandType.values.length ? RemoteCommandType.values[index] : RemoteCommandType.ping,
data: json['d'] as Map<String, dynamic>?,
);
}
Map<String, dynamic> toJson() {
return {'t': type.index, if (data != null) 'd': data};
}
@override
String toString() => 'RemoteCommand(${type.name}, data: $data)';
class _RemoteCommandTypeConverter extends IndexedEnumConverter<RemoteCommandType> {
const _RemoteCommandTypeConverter() : super(RemoteCommandType.values, RemoteCommandType.ping);
}
@freezed
sealed class RemoteCommand with _$RemoteCommand {
const factory RemoteCommand({
@JsonKey(name: 't') @_RemoteCommandTypeConverter() required RemoteCommandType type,
@JsonKey(name: 'd') Map<String, dynamic>? data,
}) = _RemoteCommand;
factory RemoteCommand.fromJson(Map<String, dynamic> json) => _$RemoteCommandFromJson(json);
}
@@ -0,0 +1,282 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'remote_command.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$RemoteCommand {
@JsonKey(name: 't')@_RemoteCommandTypeConverter() RemoteCommandType get type;@JsonKey(name: 'd') Map<String, dynamic>? get data;
/// Create a copy of RemoteCommand
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$RemoteCommandCopyWith<RemoteCommand> get copyWith => _$RemoteCommandCopyWithImpl<RemoteCommand>(this as RemoteCommand, _$identity);
/// Serializes this RemoteCommand to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is RemoteCommand&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.data, data));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(data));
@override
String toString() {
return 'RemoteCommand(type: $type, data: $data)';
}
}
/// @nodoc
abstract mixin class $RemoteCommandCopyWith<$Res> {
factory $RemoteCommandCopyWith(RemoteCommand value, $Res Function(RemoteCommand) _then) = _$RemoteCommandCopyWithImpl;
@useResult
$Res call({
@JsonKey(name: 't')@_RemoteCommandTypeConverter() RemoteCommandType type,@JsonKey(name: 'd') Map<String, dynamic>? data
});
}
/// @nodoc
class _$RemoteCommandCopyWithImpl<$Res>
implements $RemoteCommandCopyWith<$Res> {
_$RemoteCommandCopyWithImpl(this._self, this._then);
final RemoteCommand _self;
final $Res Function(RemoteCommand) _then;
/// Create a copy of RemoteCommand
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? data = freezed,}) {
return _then(_self.copyWith(
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as RemoteCommandType,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
));
}
}
/// Adds pattern-matching-related methods to [RemoteCommand].
extension RemoteCommandPatterns on RemoteCommand {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _RemoteCommand value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _RemoteCommand() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _RemoteCommand value) $default,){
final _that = this;
switch (_that) {
case _RemoteCommand():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _RemoteCommand value)? $default,){
final _that = this;
switch (_that) {
case _RemoteCommand() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 't')@_RemoteCommandTypeConverter() RemoteCommandType type, @JsonKey(name: 'd') Map<String, dynamic>? data)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _RemoteCommand() when $default != null:
return $default(_that.type,_that.data);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 't')@_RemoteCommandTypeConverter() RemoteCommandType type, @JsonKey(name: 'd') Map<String, dynamic>? data) $default,) {final _that = this;
switch (_that) {
case _RemoteCommand():
return $default(_that.type,_that.data);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 't')@_RemoteCommandTypeConverter() RemoteCommandType type, @JsonKey(name: 'd') Map<String, dynamic>? data)? $default,) {final _that = this;
switch (_that) {
case _RemoteCommand() when $default != null:
return $default(_that.type,_that.data);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _RemoteCommand implements RemoteCommand {
const _RemoteCommand({@JsonKey(name: 't')@_RemoteCommandTypeConverter() required this.type, @JsonKey(name: 'd') final Map<String, dynamic>? data}): _data = data;
factory _RemoteCommand.fromJson(Map<String, dynamic> json) => _$RemoteCommandFromJson(json);
@override@JsonKey(name: 't')@_RemoteCommandTypeConverter() final RemoteCommandType type;
final Map<String, dynamic>? _data;
@override@JsonKey(name: 'd') Map<String, dynamic>? get data {
final value = _data;
if (value == null) return null;
if (_data is EqualUnmodifiableMapView) return _data;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(value);
}
/// Create a copy of RemoteCommand
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$RemoteCommandCopyWith<_RemoteCommand> get copyWith => __$RemoteCommandCopyWithImpl<_RemoteCommand>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$RemoteCommandToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _RemoteCommand&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._data, _data));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(_data));
@override
String toString() {
return 'RemoteCommand(type: $type, data: $data)';
}
}
/// @nodoc
abstract mixin class _$RemoteCommandCopyWith<$Res> implements $RemoteCommandCopyWith<$Res> {
factory _$RemoteCommandCopyWith(_RemoteCommand value, $Res Function(_RemoteCommand) _then) = __$RemoteCommandCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(name: 't')@_RemoteCommandTypeConverter() RemoteCommandType type,@JsonKey(name: 'd') Map<String, dynamic>? data
});
}
/// @nodoc
class __$RemoteCommandCopyWithImpl<$Res>
implements _$RemoteCommandCopyWith<$Res> {
__$RemoteCommandCopyWithImpl(this._self, this._then);
final _RemoteCommand _self;
final $Res Function(_RemoteCommand) _then;
/// Create a copy of RemoteCommand
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? data = freezed,}) {
return _then(_RemoteCommand(
type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as RemoteCommandType,data: freezed == data ? _self._data : data // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
));
}
}
// dart format on
@@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'remote_command.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_RemoteCommand _$RemoteCommandFromJson(Map<String, dynamic> json) =>
_RemoteCommand(
type: const _RemoteCommandTypeConverter().fromJson(
(json['t'] as num).toInt(),
),
data: json['d'] as Map<String, dynamic>?,
);
Map<String, dynamic> _$RemoteCommandToJson(_RemoteCommand instance) =>
<String, dynamic>{
't': const _RemoteCommandTypeConverter().toJson(instance.type),
'd': instance.data,
};
+21 -86
View File
@@ -1,100 +1,35 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'remote_session.g.dart';
part 'remote_session.freezed.dart';
enum RemoteSessionRole { host, remote }
enum RemoteSessionStatus { disconnected, connecting, connected, reconnecting, error }
@JsonSerializable()
class RemoteDevice {
final String id;
final String name;
final String platform;
final DateTime connectedAt;
final Map<String, bool> capabilities;
RemoteDevice({
required this.id,
required this.name,
required this.platform,
DateTime? connectedAt,
Map<String, bool>? capabilities,
}) : connectedAt = connectedAt ?? DateTime.now(),
capabilities = capabilities ?? {};
factory RemoteDevice.fromJson(Map<String, dynamic> json) => _$RemoteDeviceFromJson(json);
Map<String, dynamic> toJson() => _$RemoteDeviceToJson(this);
RemoteDevice copyWith({
String? id,
String? name,
String? platform,
DateTime? connectedAt,
Map<String, bool>? capabilities,
}) {
return RemoteDevice(
id: id ?? this.id,
name: name ?? this.name,
platform: platform ?? this.platform,
connectedAt: connectedAt ?? this.connectedAt,
capabilities: capabilities ?? this.capabilities,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is RemoteDevice && other.id == id;
}
@override
int get hashCode => id.hashCode;
@freezed
sealed class RemoteDevice with _$RemoteDevice {
const factory RemoteDevice({
required String id,
required String name,
required String platform,
required DateTime connectedAt,
@Default(<String, bool>{}) Map<String, bool> capabilities,
}) = _RemoteDevice;
}
@JsonSerializable()
class RemoteSession {
@JsonKey(unknownEnumValue: RemoteSessionRole.remote)
final RemoteSessionRole role;
@JsonKey(unknownEnumValue: RemoteSessionStatus.disconnected)
final RemoteSessionStatus status;
final RemoteDevice? connectedDevice;
final DateTime createdAt;
final String? errorMessage;
@freezed
sealed class RemoteSession with _$RemoteSession {
const RemoteSession._();
RemoteSession({
required this.role,
this.status = RemoteSessionStatus.disconnected,
this.connectedDevice,
DateTime? createdAt,
this.errorMessage,
}) : createdAt = createdAt ?? DateTime.now();
const factory RemoteSession({
required RemoteSessionRole role,
@Default(RemoteSessionStatus.disconnected) RemoteSessionStatus status,
RemoteDevice? connectedDevice,
required DateTime createdAt,
String? errorMessage,
}) = _RemoteSession;
bool get isConnected => status == RemoteSessionStatus.connected;
bool get isHost => role == RemoteSessionRole.host;
bool get isRemote => role == RemoteSessionRole.remote;
factory RemoteSession.fromJson(Map<String, dynamic> json) => _$RemoteSessionFromJson(json);
Map<String, dynamic> toJson() => _$RemoteSessionToJson(this);
RemoteSession copyWith({
RemoteSessionRole? role,
RemoteSessionStatus? status,
RemoteDevice? connectedDevice,
bool clearConnectedDevice = false,
DateTime? createdAt,
String? errorMessage,
bool clearErrorMessage = false,
}) {
return RemoteSession(
role: role ?? this.role,
status: status ?? this.status,
connectedDevice: clearConnectedDevice ? null : (connectedDevice ?? this.connectedDevice),
createdAt: createdAt ?? this.createdAt,
errorMessage: clearErrorMessage ? null : (errorMessage ?? this.errorMessage),
);
}
}
@@ -0,0 +1,570 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'remote_session.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$RemoteDevice {
String get id; String get name; String get platform; DateTime get connectedAt; Map<String, bool> get capabilities;
/// Create a copy of RemoteDevice
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$RemoteDeviceCopyWith<RemoteDevice> get copyWith => _$RemoteDeviceCopyWithImpl<RemoteDevice>(this as RemoteDevice, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is RemoteDevice&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.platform, platform) || other.platform == platform)&&(identical(other.connectedAt, connectedAt) || other.connectedAt == connectedAt)&&const DeepCollectionEquality().equals(other.capabilities, capabilities));
}
@override
int get hashCode => Object.hash(runtimeType,id,name,platform,connectedAt,const DeepCollectionEquality().hash(capabilities));
@override
String toString() {
return 'RemoteDevice(id: $id, name: $name, platform: $platform, connectedAt: $connectedAt, capabilities: $capabilities)';
}
}
/// @nodoc
abstract mixin class $RemoteDeviceCopyWith<$Res> {
factory $RemoteDeviceCopyWith(RemoteDevice value, $Res Function(RemoteDevice) _then) = _$RemoteDeviceCopyWithImpl;
@useResult
$Res call({
String id, String name, String platform, DateTime connectedAt, Map<String, bool> capabilities
});
}
/// @nodoc
class _$RemoteDeviceCopyWithImpl<$Res>
implements $RemoteDeviceCopyWith<$Res> {
_$RemoteDeviceCopyWithImpl(this._self, this._then);
final RemoteDevice _self;
final $Res Function(RemoteDevice) _then;
/// Create a copy of RemoteDevice
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? platform = null,Object? connectedAt = null,Object? capabilities = null,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String,platform: null == platform ? _self.platform : platform // ignore: cast_nullable_to_non_nullable
as String,connectedAt: null == connectedAt ? _self.connectedAt : connectedAt // ignore: cast_nullable_to_non_nullable
as DateTime,capabilities: null == capabilities ? _self.capabilities : capabilities // ignore: cast_nullable_to_non_nullable
as Map<String, bool>,
));
}
}
/// Adds pattern-matching-related methods to [RemoteDevice].
extension RemoteDevicePatterns on RemoteDevice {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _RemoteDevice value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _RemoteDevice() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _RemoteDevice value) $default,){
final _that = this;
switch (_that) {
case _RemoteDevice():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _RemoteDevice value)? $default,){
final _that = this;
switch (_that) {
case _RemoteDevice() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, String platform, DateTime connectedAt, Map<String, bool> capabilities)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _RemoteDevice() when $default != null:
return $default(_that.id,_that.name,_that.platform,_that.connectedAt,_that.capabilities);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String platform, DateTime connectedAt, Map<String, bool> capabilities) $default,) {final _that = this;
switch (_that) {
case _RemoteDevice():
return $default(_that.id,_that.name,_that.platform,_that.connectedAt,_that.capabilities);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String platform, DateTime connectedAt, Map<String, bool> capabilities)? $default,) {final _that = this;
switch (_that) {
case _RemoteDevice() when $default != null:
return $default(_that.id,_that.name,_that.platform,_that.connectedAt,_that.capabilities);case _:
return null;
}
}
}
/// @nodoc
class _RemoteDevice implements RemoteDevice {
const _RemoteDevice({required this.id, required this.name, required this.platform, required this.connectedAt, final Map<String, bool> capabilities = const <String, bool>{}}): _capabilities = capabilities;
@override final String id;
@override final String name;
@override final String platform;
@override final DateTime connectedAt;
final Map<String, bool> _capabilities;
@override@JsonKey() Map<String, bool> get capabilities {
if (_capabilities is EqualUnmodifiableMapView) return _capabilities;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_capabilities);
}
/// Create a copy of RemoteDevice
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$RemoteDeviceCopyWith<_RemoteDevice> get copyWith => __$RemoteDeviceCopyWithImpl<_RemoteDevice>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _RemoteDevice&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.platform, platform) || other.platform == platform)&&(identical(other.connectedAt, connectedAt) || other.connectedAt == connectedAt)&&const DeepCollectionEquality().equals(other._capabilities, _capabilities));
}
@override
int get hashCode => Object.hash(runtimeType,id,name,platform,connectedAt,const DeepCollectionEquality().hash(_capabilities));
@override
String toString() {
return 'RemoteDevice(id: $id, name: $name, platform: $platform, connectedAt: $connectedAt, capabilities: $capabilities)';
}
}
/// @nodoc
abstract mixin class _$RemoteDeviceCopyWith<$Res> implements $RemoteDeviceCopyWith<$Res> {
factory _$RemoteDeviceCopyWith(_RemoteDevice value, $Res Function(_RemoteDevice) _then) = __$RemoteDeviceCopyWithImpl;
@override @useResult
$Res call({
String id, String name, String platform, DateTime connectedAt, Map<String, bool> capabilities
});
}
/// @nodoc
class __$RemoteDeviceCopyWithImpl<$Res>
implements _$RemoteDeviceCopyWith<$Res> {
__$RemoteDeviceCopyWithImpl(this._self, this._then);
final _RemoteDevice _self;
final $Res Function(_RemoteDevice) _then;
/// Create a copy of RemoteDevice
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? platform = null,Object? connectedAt = null,Object? capabilities = null,}) {
return _then(_RemoteDevice(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String,platform: null == platform ? _self.platform : platform // ignore: cast_nullable_to_non_nullable
as String,connectedAt: null == connectedAt ? _self.connectedAt : connectedAt // ignore: cast_nullable_to_non_nullable
as DateTime,capabilities: null == capabilities ? _self._capabilities : capabilities // ignore: cast_nullable_to_non_nullable
as Map<String, bool>,
));
}
}
/// @nodoc
mixin _$RemoteSession {
RemoteSessionRole get role; RemoteSessionStatus get status; RemoteDevice? get connectedDevice; DateTime get createdAt; String? get errorMessage;
/// Create a copy of RemoteSession
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$RemoteSessionCopyWith<RemoteSession> get copyWith => _$RemoteSessionCopyWithImpl<RemoteSession>(this as RemoteSession, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is RemoteSession&&(identical(other.role, role) || other.role == role)&&(identical(other.status, status) || other.status == status)&&(identical(other.connectedDevice, connectedDevice) || other.connectedDevice == connectedDevice)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,role,status,connectedDevice,createdAt,errorMessage);
@override
String toString() {
return 'RemoteSession(role: $role, status: $status, connectedDevice: $connectedDevice, createdAt: $createdAt, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class $RemoteSessionCopyWith<$Res> {
factory $RemoteSessionCopyWith(RemoteSession value, $Res Function(RemoteSession) _then) = _$RemoteSessionCopyWithImpl;
@useResult
$Res call({
RemoteSessionRole role, RemoteSessionStatus status, RemoteDevice? connectedDevice, DateTime createdAt, String? errorMessage
});
$RemoteDeviceCopyWith<$Res>? get connectedDevice;
}
/// @nodoc
class _$RemoteSessionCopyWithImpl<$Res>
implements $RemoteSessionCopyWith<$Res> {
_$RemoteSessionCopyWithImpl(this._self, this._then);
final RemoteSession _self;
final $Res Function(RemoteSession) _then;
/// Create a copy of RemoteSession
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? role = null,Object? status = null,Object? connectedDevice = freezed,Object? createdAt = null,Object? errorMessage = freezed,}) {
return _then(_self.copyWith(
role: null == role ? _self.role : role // ignore: cast_nullable_to_non_nullable
as RemoteSessionRole,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as RemoteSessionStatus,connectedDevice: freezed == connectedDevice ? _self.connectedDevice : connectedDevice // ignore: cast_nullable_to_non_nullable
as RemoteDevice?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,errorMessage: freezed == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
));
}
/// Create a copy of RemoteSession
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$RemoteDeviceCopyWith<$Res>? get connectedDevice {
if (_self.connectedDevice == null) {
return null;
}
return $RemoteDeviceCopyWith<$Res>(_self.connectedDevice!, (value) {
return _then(_self.copyWith(connectedDevice: value));
});
}
}
/// Adds pattern-matching-related methods to [RemoteSession].
extension RemoteSessionPatterns on RemoteSession {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _RemoteSession value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _RemoteSession() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _RemoteSession value) $default,){
final _that = this;
switch (_that) {
case _RemoteSession():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _RemoteSession value)? $default,){
final _that = this;
switch (_that) {
case _RemoteSession() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( RemoteSessionRole role, RemoteSessionStatus status, RemoteDevice? connectedDevice, DateTime createdAt, String? errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _RemoteSession() when $default != null:
return $default(_that.role,_that.status,_that.connectedDevice,_that.createdAt,_that.errorMessage);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( RemoteSessionRole role, RemoteSessionStatus status, RemoteDevice? connectedDevice, DateTime createdAt, String? errorMessage) $default,) {final _that = this;
switch (_that) {
case _RemoteSession():
return $default(_that.role,_that.status,_that.connectedDevice,_that.createdAt,_that.errorMessage);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( RemoteSessionRole role, RemoteSessionStatus status, RemoteDevice? connectedDevice, DateTime createdAt, String? errorMessage)? $default,) {final _that = this;
switch (_that) {
case _RemoteSession() when $default != null:
return $default(_that.role,_that.status,_that.connectedDevice,_that.createdAt,_that.errorMessage);case _:
return null;
}
}
}
/// @nodoc
class _RemoteSession extends RemoteSession {
const _RemoteSession({required this.role, this.status = RemoteSessionStatus.disconnected, this.connectedDevice, required this.createdAt, this.errorMessage}): super._();
@override final RemoteSessionRole role;
@override@JsonKey() final RemoteSessionStatus status;
@override final RemoteDevice? connectedDevice;
@override final DateTime createdAt;
@override final String? errorMessage;
/// Create a copy of RemoteSession
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$RemoteSessionCopyWith<_RemoteSession> get copyWith => __$RemoteSessionCopyWithImpl<_RemoteSession>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _RemoteSession&&(identical(other.role, role) || other.role == role)&&(identical(other.status, status) || other.status == status)&&(identical(other.connectedDevice, connectedDevice) || other.connectedDevice == connectedDevice)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,role,status,connectedDevice,createdAt,errorMessage);
@override
String toString() {
return 'RemoteSession(role: $role, status: $status, connectedDevice: $connectedDevice, createdAt: $createdAt, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class _$RemoteSessionCopyWith<$Res> implements $RemoteSessionCopyWith<$Res> {
factory _$RemoteSessionCopyWith(_RemoteSession value, $Res Function(_RemoteSession) _then) = __$RemoteSessionCopyWithImpl;
@override @useResult
$Res call({
RemoteSessionRole role, RemoteSessionStatus status, RemoteDevice? connectedDevice, DateTime createdAt, String? errorMessage
});
@override $RemoteDeviceCopyWith<$Res>? get connectedDevice;
}
/// @nodoc
class __$RemoteSessionCopyWithImpl<$Res>
implements _$RemoteSessionCopyWith<$Res> {
__$RemoteSessionCopyWithImpl(this._self, this._then);
final _RemoteSession _self;
final $Res Function(_RemoteSession) _then;
/// Create a copy of RemoteSession
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? role = null,Object? status = null,Object? connectedDevice = freezed,Object? createdAt = null,Object? errorMessage = freezed,}) {
return _then(_RemoteSession(
role: null == role ? _self.role : role // ignore: cast_nullable_to_non_nullable
as RemoteSessionRole,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as RemoteSessionStatus,connectedDevice: freezed == connectedDevice ? _self.connectedDevice : connectedDevice // ignore: cast_nullable_to_non_nullable
as RemoteDevice?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,errorMessage: freezed == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String?,
));
}
/// Create a copy of RemoteSession
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$RemoteDeviceCopyWith<$Res>? get connectedDevice {
if (_self.connectedDevice == null) {
return null;
}
return $RemoteDeviceCopyWith<$Res>(_self.connectedDevice!, (value) {
return _then(_self.copyWith(connectedDevice: value));
});
}
}
// dart format on
@@ -1,75 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'remote_session.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
RemoteDevice _$RemoteDeviceFromJson(Map<String, dynamic> json) => RemoteDevice(
id: json['id'] as String,
name: json['name'] as String,
platform: json['platform'] as String,
connectedAt: json['connectedAt'] == null
? null
: DateTime.parse(json['connectedAt'] as String),
capabilities: (json['capabilities'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as bool),
),
);
Map<String, dynamic> _$RemoteDeviceToJson(RemoteDevice instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
'platform': instance.platform,
'connectedAt': instance.connectedAt.toIso8601String(),
'capabilities': instance.capabilities,
};
RemoteSession _$RemoteSessionFromJson(Map<String, dynamic> json) =>
RemoteSession(
role: $enumDecode(
_$RemoteSessionRoleEnumMap,
json['role'],
unknownValue: RemoteSessionRole.remote,
),
status:
$enumDecodeNullable(
_$RemoteSessionStatusEnumMap,
json['status'],
unknownValue: RemoteSessionStatus.disconnected,
) ??
RemoteSessionStatus.disconnected,
connectedDevice: json['connectedDevice'] == null
? null
: RemoteDevice.fromJson(
json['connectedDevice'] as Map<String, dynamic>,
),
createdAt: json['createdAt'] == null
? null
: DateTime.parse(json['createdAt'] as String),
errorMessage: json['errorMessage'] as String?,
);
Map<String, dynamic> _$RemoteSessionToJson(RemoteSession instance) =>
<String, dynamic>{
'role': _$RemoteSessionRoleEnumMap[instance.role]!,
'status': _$RemoteSessionStatusEnumMap[instance.status]!,
'connectedDevice': instance.connectedDevice,
'createdAt': instance.createdAt.toIso8601String(),
'errorMessage': instance.errorMessage,
};
const _$RemoteSessionRoleEnumMap = {
RemoteSessionRole.host: 'host',
RemoteSessionRole.remote: 'remote',
};
const _$RemoteSessionStatusEnumMap = {
RemoteSessionStatus.disconnected: 'disconnected',
RemoteSessionStatus.connecting: 'connecting',
RemoteSessionStatus.connected: 'connected',
RemoteSessionStatus.reconnecting: 'reconnecting',
RemoteSessionStatus.error: 'error',
};
+29 -83
View File
@@ -1,5 +1,10 @@
// ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart';
import '../utils/formatters.dart';
part 'download_models.freezed.dart';
enum DownloadStatus {
queued,
downloading,
@@ -10,30 +15,21 @@ enum DownloadStatus {
partial, // Some episodes downloaded, but not all (for shows/seasons)
}
class DownloadProgress {
final String globalKey;
final DownloadStatus status;
final int progress; // 0-100
final int downloadedBytes;
final int totalBytes;
final double speed; // bytes per second
final String? errorMessage;
final String? currentFile; // What's being downloaded (video, subtitles, artwork)
@freezed
sealed class DownloadProgress with _$DownloadProgress {
const DownloadProgress._();
// Thumbnail path (populated after artwork download completes)
final String? thumbPath;
const DownloadProgress({
required this.globalKey,
required this.status,
this.progress = 0,
this.downloadedBytes = 0,
this.totalBytes = 0,
this.speed = 0,
this.errorMessage,
this.currentFile,
this.thumbPath,
});
const factory DownloadProgress({
required String globalKey,
required DownloadStatus status,
@Default(0) int progress,
@Default(0) int downloadedBytes,
@Default(0) int totalBytes,
@Default(0.0) double speed,
String? errorMessage,
String? currentFile,
String? thumbPath,
}) = _DownloadProgress;
double get progressPercent => progress / 100.0;
@@ -42,73 +38,23 @@ class DownloadProgress {
String get totalFormatted => ByteFormatter.formatBytes(totalBytes);
bool get hasArtworkPaths => thumbPath != null;
DownloadProgress copyWith({
String? globalKey,
DownloadStatus? status,
int? progress,
int? downloadedBytes,
int? totalBytes,
double? speed,
String? errorMessage,
String? currentFile,
String? thumbPath,
}) {
return DownloadProgress(
globalKey: globalKey ?? this.globalKey,
status: status ?? this.status,
progress: progress ?? this.progress,
downloadedBytes: downloadedBytes ?? this.downloadedBytes,
totalBytes: totalBytes ?? this.totalBytes,
speed: speed ?? this.speed,
errorMessage: errorMessage ?? this.errorMessage,
currentFile: currentFile ?? this.currentFile,
thumbPath: thumbPath ?? this.thumbPath,
);
}
}
class DeletionProgress {
final String globalKey;
final String itemTitle;
final int currentItem;
final int totalItems;
final String? currentOperation;
@freezed
sealed class DeletionProgress with _$DeletionProgress {
const DeletionProgress._();
const DeletionProgress({
required this.globalKey,
required this.itemTitle,
required this.currentItem,
required this.totalItems,
this.currentOperation,
});
const factory DeletionProgress({
required String globalKey,
required String itemTitle,
required int currentItem,
required int totalItems,
String? currentOperation,
}) = _DeletionProgress;
double get progressPercent => totalItems > 0 ? (currentItem / totalItems) : 0.0;
int get progressPercentInt => (progressPercent * 100).round();
bool get isComplete => currentItem >= totalItems;
DeletionProgress copyWith({
String? globalKey,
String? itemTitle,
int? currentItem,
int? totalItems,
String? currentOperation,
}) {
return DeletionProgress(
globalKey: globalKey ?? this.globalKey,
itemTitle: itemTitle ?? this.itemTitle,
currentItem: currentItem ?? this.currentItem,
totalItems: totalItems ?? this.totalItems,
currentOperation: currentOperation ?? this.currentOperation,
);
}
@override
String toString() {
return 'DeletionProgress(globalKey: $globalKey, itemTitle: $itemTitle, '
'currentItem: $currentItem, totalItems: $totalItems, '
'progressPercent: $progressPercentInt%)';
}
}
+552
View File
@@ -0,0 +1,552 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'download_models.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$DownloadProgress {
String get globalKey; DownloadStatus get status; int get progress; int get downloadedBytes; int get totalBytes; double get speed; String? get errorMessage; String? get currentFile; String? get thumbPath;
/// Create a copy of DownloadProgress
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$DownloadProgressCopyWith<DownloadProgress> get copyWith => _$DownloadProgressCopyWithImpl<DownloadProgress>(this as DownloadProgress, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DownloadProgress&&(identical(other.globalKey, globalKey) || other.globalKey == globalKey)&&(identical(other.status, status) || other.status == status)&&(identical(other.progress, progress) || other.progress == progress)&&(identical(other.downloadedBytes, downloadedBytes) || other.downloadedBytes == downloadedBytes)&&(identical(other.totalBytes, totalBytes) || other.totalBytes == totalBytes)&&(identical(other.speed, speed) || other.speed == speed)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.currentFile, currentFile) || other.currentFile == currentFile)&&(identical(other.thumbPath, thumbPath) || other.thumbPath == thumbPath));
}
@override
int get hashCode => Object.hash(runtimeType,globalKey,status,progress,downloadedBytes,totalBytes,speed,errorMessage,currentFile,thumbPath);
@override
String toString() {
return 'DownloadProgress(globalKey: $globalKey, status: $status, progress: $progress, downloadedBytes: $downloadedBytes, totalBytes: $totalBytes, speed: $speed, errorMessage: $errorMessage, currentFile: $currentFile, thumbPath: $thumbPath)';
}
}
/// @nodoc
abstract mixin class $DownloadProgressCopyWith<$Res> {
factory $DownloadProgressCopyWith(DownloadProgress value, $Res Function(DownloadProgress) _then) = _$DownloadProgressCopyWithImpl;
@useResult
$Res call({
String globalKey, DownloadStatus status, int progress, int downloadedBytes, int totalBytes, double speed, String? errorMessage, String? currentFile, String? thumbPath
});
}
/// @nodoc
class _$DownloadProgressCopyWithImpl<$Res>
implements $DownloadProgressCopyWith<$Res> {
_$DownloadProgressCopyWithImpl(this._self, this._then);
final DownloadProgress _self;
final $Res Function(DownloadProgress) _then;
/// Create a copy of DownloadProgress
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? globalKey = null,Object? status = null,Object? progress = null,Object? downloadedBytes = null,Object? totalBytes = null,Object? speed = null,Object? errorMessage = freezed,Object? currentFile = freezed,Object? thumbPath = freezed,}) {
return _then(_self.copyWith(
globalKey: null == globalKey ? _self.globalKey : globalKey // ignore: cast_nullable_to_non_nullable
as String,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as DownloadStatus,progress: null == progress ? _self.progress : progress // ignore: cast_nullable_to_non_nullable
as int,downloadedBytes: null == downloadedBytes ? _self.downloadedBytes : downloadedBytes // ignore: cast_nullable_to_non_nullable
as int,totalBytes: null == totalBytes ? _self.totalBytes : totalBytes // ignore: cast_nullable_to_non_nullable
as int,speed: null == speed ? _self.speed : speed // ignore: cast_nullable_to_non_nullable
as double,errorMessage: freezed == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String?,currentFile: freezed == currentFile ? _self.currentFile : currentFile // ignore: cast_nullable_to_non_nullable
as String?,thumbPath: freezed == thumbPath ? _self.thumbPath : thumbPath // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// Adds pattern-matching-related methods to [DownloadProgress].
extension DownloadProgressPatterns on DownloadProgress {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _DownloadProgress value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _DownloadProgress() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _DownloadProgress value) $default,){
final _that = this;
switch (_that) {
case _DownloadProgress():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _DownloadProgress value)? $default,){
final _that = this;
switch (_that) {
case _DownloadProgress() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String globalKey, DownloadStatus status, int progress, int downloadedBytes, int totalBytes, double speed, String? errorMessage, String? currentFile, String? thumbPath)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _DownloadProgress() when $default != null:
return $default(_that.globalKey,_that.status,_that.progress,_that.downloadedBytes,_that.totalBytes,_that.speed,_that.errorMessage,_that.currentFile,_that.thumbPath);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String globalKey, DownloadStatus status, int progress, int downloadedBytes, int totalBytes, double speed, String? errorMessage, String? currentFile, String? thumbPath) $default,) {final _that = this;
switch (_that) {
case _DownloadProgress():
return $default(_that.globalKey,_that.status,_that.progress,_that.downloadedBytes,_that.totalBytes,_that.speed,_that.errorMessage,_that.currentFile,_that.thumbPath);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String globalKey, DownloadStatus status, int progress, int downloadedBytes, int totalBytes, double speed, String? errorMessage, String? currentFile, String? thumbPath)? $default,) {final _that = this;
switch (_that) {
case _DownloadProgress() when $default != null:
return $default(_that.globalKey,_that.status,_that.progress,_that.downloadedBytes,_that.totalBytes,_that.speed,_that.errorMessage,_that.currentFile,_that.thumbPath);case _:
return null;
}
}
}
/// @nodoc
class _DownloadProgress extends DownloadProgress {
const _DownloadProgress({required this.globalKey, required this.status, this.progress = 0, this.downloadedBytes = 0, this.totalBytes = 0, this.speed = 0.0, this.errorMessage, this.currentFile, this.thumbPath}): super._();
@override final String globalKey;
@override final DownloadStatus status;
@override@JsonKey() final int progress;
@override@JsonKey() final int downloadedBytes;
@override@JsonKey() final int totalBytes;
@override@JsonKey() final double speed;
@override final String? errorMessage;
@override final String? currentFile;
@override final String? thumbPath;
/// Create a copy of DownloadProgress
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$DownloadProgressCopyWith<_DownloadProgress> get copyWith => __$DownloadProgressCopyWithImpl<_DownloadProgress>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DownloadProgress&&(identical(other.globalKey, globalKey) || other.globalKey == globalKey)&&(identical(other.status, status) || other.status == status)&&(identical(other.progress, progress) || other.progress == progress)&&(identical(other.downloadedBytes, downloadedBytes) || other.downloadedBytes == downloadedBytes)&&(identical(other.totalBytes, totalBytes) || other.totalBytes == totalBytes)&&(identical(other.speed, speed) || other.speed == speed)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.currentFile, currentFile) || other.currentFile == currentFile)&&(identical(other.thumbPath, thumbPath) || other.thumbPath == thumbPath));
}
@override
int get hashCode => Object.hash(runtimeType,globalKey,status,progress,downloadedBytes,totalBytes,speed,errorMessage,currentFile,thumbPath);
@override
String toString() {
return 'DownloadProgress(globalKey: $globalKey, status: $status, progress: $progress, downloadedBytes: $downloadedBytes, totalBytes: $totalBytes, speed: $speed, errorMessage: $errorMessage, currentFile: $currentFile, thumbPath: $thumbPath)';
}
}
/// @nodoc
abstract mixin class _$DownloadProgressCopyWith<$Res> implements $DownloadProgressCopyWith<$Res> {
factory _$DownloadProgressCopyWith(_DownloadProgress value, $Res Function(_DownloadProgress) _then) = __$DownloadProgressCopyWithImpl;
@override @useResult
$Res call({
String globalKey, DownloadStatus status, int progress, int downloadedBytes, int totalBytes, double speed, String? errorMessage, String? currentFile, String? thumbPath
});
}
/// @nodoc
class __$DownloadProgressCopyWithImpl<$Res>
implements _$DownloadProgressCopyWith<$Res> {
__$DownloadProgressCopyWithImpl(this._self, this._then);
final _DownloadProgress _self;
final $Res Function(_DownloadProgress) _then;
/// Create a copy of DownloadProgress
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? globalKey = null,Object? status = null,Object? progress = null,Object? downloadedBytes = null,Object? totalBytes = null,Object? speed = null,Object? errorMessage = freezed,Object? currentFile = freezed,Object? thumbPath = freezed,}) {
return _then(_DownloadProgress(
globalKey: null == globalKey ? _self.globalKey : globalKey // ignore: cast_nullable_to_non_nullable
as String,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as DownloadStatus,progress: null == progress ? _self.progress : progress // ignore: cast_nullable_to_non_nullable
as int,downloadedBytes: null == downloadedBytes ? _self.downloadedBytes : downloadedBytes // ignore: cast_nullable_to_non_nullable
as int,totalBytes: null == totalBytes ? _self.totalBytes : totalBytes // ignore: cast_nullable_to_non_nullable
as int,speed: null == speed ? _self.speed : speed // ignore: cast_nullable_to_non_nullable
as double,errorMessage: freezed == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String?,currentFile: freezed == currentFile ? _self.currentFile : currentFile // ignore: cast_nullable_to_non_nullable
as String?,thumbPath: freezed == thumbPath ? _self.thumbPath : thumbPath // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
mixin _$DeletionProgress {
String get globalKey; String get itemTitle; int get currentItem; int get totalItems; String? get currentOperation;
/// Create a copy of DeletionProgress
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$DeletionProgressCopyWith<DeletionProgress> get copyWith => _$DeletionProgressCopyWithImpl<DeletionProgress>(this as DeletionProgress, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DeletionProgress&&(identical(other.globalKey, globalKey) || other.globalKey == globalKey)&&(identical(other.itemTitle, itemTitle) || other.itemTitle == itemTitle)&&(identical(other.currentItem, currentItem) || other.currentItem == currentItem)&&(identical(other.totalItems, totalItems) || other.totalItems == totalItems)&&(identical(other.currentOperation, currentOperation) || other.currentOperation == currentOperation));
}
@override
int get hashCode => Object.hash(runtimeType,globalKey,itemTitle,currentItem,totalItems,currentOperation);
@override
String toString() {
return 'DeletionProgress(globalKey: $globalKey, itemTitle: $itemTitle, currentItem: $currentItem, totalItems: $totalItems, currentOperation: $currentOperation)';
}
}
/// @nodoc
abstract mixin class $DeletionProgressCopyWith<$Res> {
factory $DeletionProgressCopyWith(DeletionProgress value, $Res Function(DeletionProgress) _then) = _$DeletionProgressCopyWithImpl;
@useResult
$Res call({
String globalKey, String itemTitle, int currentItem, int totalItems, String? currentOperation
});
}
/// @nodoc
class _$DeletionProgressCopyWithImpl<$Res>
implements $DeletionProgressCopyWith<$Res> {
_$DeletionProgressCopyWithImpl(this._self, this._then);
final DeletionProgress _self;
final $Res Function(DeletionProgress) _then;
/// Create a copy of DeletionProgress
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? globalKey = null,Object? itemTitle = null,Object? currentItem = null,Object? totalItems = null,Object? currentOperation = freezed,}) {
return _then(_self.copyWith(
globalKey: null == globalKey ? _self.globalKey : globalKey // ignore: cast_nullable_to_non_nullable
as String,itemTitle: null == itemTitle ? _self.itemTitle : itemTitle // ignore: cast_nullable_to_non_nullable
as String,currentItem: null == currentItem ? _self.currentItem : currentItem // ignore: cast_nullable_to_non_nullable
as int,totalItems: null == totalItems ? _self.totalItems : totalItems // ignore: cast_nullable_to_non_nullable
as int,currentOperation: freezed == currentOperation ? _self.currentOperation : currentOperation // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// Adds pattern-matching-related methods to [DeletionProgress].
extension DeletionProgressPatterns on DeletionProgress {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _DeletionProgress value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _DeletionProgress() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _DeletionProgress value) $default,){
final _that = this;
switch (_that) {
case _DeletionProgress():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _DeletionProgress value)? $default,){
final _that = this;
switch (_that) {
case _DeletionProgress() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String globalKey, String itemTitle, int currentItem, int totalItems, String? currentOperation)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _DeletionProgress() when $default != null:
return $default(_that.globalKey,_that.itemTitle,_that.currentItem,_that.totalItems,_that.currentOperation);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String globalKey, String itemTitle, int currentItem, int totalItems, String? currentOperation) $default,) {final _that = this;
switch (_that) {
case _DeletionProgress():
return $default(_that.globalKey,_that.itemTitle,_that.currentItem,_that.totalItems,_that.currentOperation);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String globalKey, String itemTitle, int currentItem, int totalItems, String? currentOperation)? $default,) {final _that = this;
switch (_that) {
case _DeletionProgress() when $default != null:
return $default(_that.globalKey,_that.itemTitle,_that.currentItem,_that.totalItems,_that.currentOperation);case _:
return null;
}
}
}
/// @nodoc
class _DeletionProgress extends DeletionProgress {
const _DeletionProgress({required this.globalKey, required this.itemTitle, required this.currentItem, required this.totalItems, this.currentOperation}): super._();
@override final String globalKey;
@override final String itemTitle;
@override final int currentItem;
@override final int totalItems;
@override final String? currentOperation;
/// Create a copy of DeletionProgress
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$DeletionProgressCopyWith<_DeletionProgress> get copyWith => __$DeletionProgressCopyWithImpl<_DeletionProgress>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DeletionProgress&&(identical(other.globalKey, globalKey) || other.globalKey == globalKey)&&(identical(other.itemTitle, itemTitle) || other.itemTitle == itemTitle)&&(identical(other.currentItem, currentItem) || other.currentItem == currentItem)&&(identical(other.totalItems, totalItems) || other.totalItems == totalItems)&&(identical(other.currentOperation, currentOperation) || other.currentOperation == currentOperation));
}
@override
int get hashCode => Object.hash(runtimeType,globalKey,itemTitle,currentItem,totalItems,currentOperation);
@override
String toString() {
return 'DeletionProgress(globalKey: $globalKey, itemTitle: $itemTitle, currentItem: $currentItem, totalItems: $totalItems, currentOperation: $currentOperation)';
}
}
/// @nodoc
abstract mixin class _$DeletionProgressCopyWith<$Res> implements $DeletionProgressCopyWith<$Res> {
factory _$DeletionProgressCopyWith(_DeletionProgress value, $Res Function(_DeletionProgress) _then) = __$DeletionProgressCopyWithImpl;
@override @useResult
$Res call({
String globalKey, String itemTitle, int currentItem, int totalItems, String? currentOperation
});
}
/// @nodoc
class __$DeletionProgressCopyWithImpl<$Res>
implements _$DeletionProgressCopyWith<$Res> {
__$DeletionProgressCopyWithImpl(this._self, this._then);
final _DeletionProgress _self;
final $Res Function(_DeletionProgress) _then;
/// Create a copy of DeletionProgress
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? globalKey = null,Object? itemTitle = null,Object? currentItem = null,Object? totalItems = null,Object? currentOperation = freezed,}) {
return _then(_DeletionProgress(
globalKey: null == globalKey ? _self.globalKey : globalKey // ignore: cast_nullable_to_non_nullable
as String,itemTitle: null == itemTitle ? _self.itemTitle : itemTitle // ignore: cast_nullable_to_non_nullable
as String,currentItem: null == currentItem ? _self.currentItem : currentItem // ignore: cast_nullable_to_non_nullable
as int,totalItems: null == totalItems ? _self.totalItems : totalItems // ignore: cast_nullable_to_non_nullable
as int,currentOperation: freezed == currentOperation ? _self.currentOperation : currentOperation // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
// dart format on
+1 -1
View File
@@ -27,5 +27,5 @@ Map<String, dynamic> _$PlexHomeToJson(PlexHome instance) => <String, dynamic>{
'guestUserUUID': instance.guestUserUUID,
'guestEnabled': instance.guestEnabled,
'subscription': instance.subscription,
'users': instance.users,
'users': instance.users.map((e) => e.toJson()).toList(),
};
+27 -63
View File
@@ -1,3 +1,9 @@
// ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart';
part 'shader_preset.freezed.dart';
part 'shader_preset.g.dart';
enum ShaderPresetType { none, nvscaler, artcnn, anime4k, custom }
/// ArtCNN real-time model sizes.
@@ -51,76 +57,34 @@ enum Anime4KMode {
modeCA,
}
class Anime4KConfig {
final Anime4KQuality quality;
final Anime4KMode mode;
@freezed
sealed class Anime4KConfig with _$Anime4KConfig {
const factory Anime4KConfig({
@JsonKey(unknownEnumValue: Anime4KQuality.fast) required Anime4KQuality quality,
@JsonKey(unknownEnumValue: Anime4KMode.modeA) required Anime4KMode mode,
}) = _Anime4KConfig;
const Anime4KConfig({required this.quality, required this.mode});
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Anime4KConfig && other.quality == quality && other.mode == mode;
}
@override
int get hashCode => quality.hashCode ^ mode.hashCode;
Map<String, dynamic> toJson() => {'quality': quality.name, 'mode': mode.name};
factory Anime4KConfig.fromJson(Map<String, dynamic> json) {
return Anime4KConfig(
quality: Anime4KQuality.values.asNameMap()[json['quality']] ?? Anime4KQuality.fast,
mode: Anime4KMode.values.asNameMap()[json['mode']] ?? Anime4KMode.modeA,
);
}
factory Anime4KConfig.fromJson(Map<String, dynamic> json) => _$Anime4KConfigFromJson(json);
}
class ArtCNNConfig {
final ArtCNNModel model;
final ArtCNNVariant variant;
@freezed
sealed class ArtCNNConfig with _$ArtCNNConfig {
const factory ArtCNNConfig({
@JsonKey(unknownEnumValue: ArtCNNModel.c4f16) required ArtCNNModel model,
@JsonKey(unknownEnumValue: ArtCNNVariant.neutral) required ArtCNNVariant variant,
}) = _ArtCNNConfig;
const ArtCNNConfig({required this.model, required this.variant});
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ArtCNNConfig && other.model == model && other.variant == variant;
}
@override
int get hashCode => model.hashCode ^ variant.hashCode;
Map<String, dynamic> toJson() => {'model': model.name, 'variant': variant.name};
factory ArtCNNConfig.fromJson(Map<String, dynamic> json) {
return ArtCNNConfig(
model: ArtCNNModel.values.asNameMap()[json['model']] ?? ArtCNNModel.c4f16,
variant: ArtCNNVariant.values.asNameMap()[json['variant']] ?? ArtCNNVariant.neutral,
);
}
factory ArtCNNConfig.fromJson(Map<String, dynamic> json) => _$ArtCNNConfigFromJson(json);
}
class NVScalerConfig {
/// Whether to automatically skip NVScaler on HDR content
final bool autoHdrSkip;
@freezed
sealed class NVScalerConfig with _$NVScalerConfig {
const factory NVScalerConfig({
/// Whether to automatically skip NVScaler on HDR content
@Default(true) bool autoHdrSkip,
}) = _NVScalerConfig;
const NVScalerConfig({this.autoHdrSkip = true});
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is NVScalerConfig && other.autoHdrSkip == autoHdrSkip;
}
@override
int get hashCode => autoHdrSkip.hashCode;
Map<String, dynamic> toJson() => {'autoHdrSkip': autoHdrSkip};
factory NVScalerConfig.fromJson(Map<String, dynamic> json) {
return NVScalerConfig(autoHdrSkip: json['autoHdrSkip'] as bool? ?? true);
}
factory NVScalerConfig.fromJson(Map<String, dynamic> json) => _$NVScalerConfigFromJson(json);
}
class ShaderPreset {
+793
View File
@@ -0,0 +1,793 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'shader_preset.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$Anime4KConfig {
@JsonKey(unknownEnumValue: Anime4KQuality.fast) Anime4KQuality get quality;@JsonKey(unknownEnumValue: Anime4KMode.modeA) Anime4KMode get mode;
/// Create a copy of Anime4KConfig
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$Anime4KConfigCopyWith<Anime4KConfig> get copyWith => _$Anime4KConfigCopyWithImpl<Anime4KConfig>(this as Anime4KConfig, _$identity);
/// Serializes this Anime4KConfig to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is Anime4KConfig&&(identical(other.quality, quality) || other.quality == quality)&&(identical(other.mode, mode) || other.mode == mode));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,quality,mode);
@override
String toString() {
return 'Anime4KConfig(quality: $quality, mode: $mode)';
}
}
/// @nodoc
abstract mixin class $Anime4KConfigCopyWith<$Res> {
factory $Anime4KConfigCopyWith(Anime4KConfig value, $Res Function(Anime4KConfig) _then) = _$Anime4KConfigCopyWithImpl;
@useResult
$Res call({
@JsonKey(unknownEnumValue: Anime4KQuality.fast) Anime4KQuality quality,@JsonKey(unknownEnumValue: Anime4KMode.modeA) Anime4KMode mode
});
}
/// @nodoc
class _$Anime4KConfigCopyWithImpl<$Res>
implements $Anime4KConfigCopyWith<$Res> {
_$Anime4KConfigCopyWithImpl(this._self, this._then);
final Anime4KConfig _self;
final $Res Function(Anime4KConfig) _then;
/// Create a copy of Anime4KConfig
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? quality = null,Object? mode = null,}) {
return _then(_self.copyWith(
quality: null == quality ? _self.quality : quality // ignore: cast_nullable_to_non_nullable
as Anime4KQuality,mode: null == mode ? _self.mode : mode // ignore: cast_nullable_to_non_nullable
as Anime4KMode,
));
}
}
/// Adds pattern-matching-related methods to [Anime4KConfig].
extension Anime4KConfigPatterns on Anime4KConfig {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Anime4KConfig value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _Anime4KConfig() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Anime4KConfig value) $default,){
final _that = this;
switch (_that) {
case _Anime4KConfig():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Anime4KConfig value)? $default,){
final _that = this;
switch (_that) {
case _Anime4KConfig() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(unknownEnumValue: Anime4KQuality.fast) Anime4KQuality quality, @JsonKey(unknownEnumValue: Anime4KMode.modeA) Anime4KMode mode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _Anime4KConfig() when $default != null:
return $default(_that.quality,_that.mode);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(unknownEnumValue: Anime4KQuality.fast) Anime4KQuality quality, @JsonKey(unknownEnumValue: Anime4KMode.modeA) Anime4KMode mode) $default,) {final _that = this;
switch (_that) {
case _Anime4KConfig():
return $default(_that.quality,_that.mode);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(unknownEnumValue: Anime4KQuality.fast) Anime4KQuality quality, @JsonKey(unknownEnumValue: Anime4KMode.modeA) Anime4KMode mode)? $default,) {final _that = this;
switch (_that) {
case _Anime4KConfig() when $default != null:
return $default(_that.quality,_that.mode);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _Anime4KConfig implements Anime4KConfig {
const _Anime4KConfig({@JsonKey(unknownEnumValue: Anime4KQuality.fast) required this.quality, @JsonKey(unknownEnumValue: Anime4KMode.modeA) required this.mode});
factory _Anime4KConfig.fromJson(Map<String, dynamic> json) => _$Anime4KConfigFromJson(json);
@override@JsonKey(unknownEnumValue: Anime4KQuality.fast) final Anime4KQuality quality;
@override@JsonKey(unknownEnumValue: Anime4KMode.modeA) final Anime4KMode mode;
/// Create a copy of Anime4KConfig
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$Anime4KConfigCopyWith<_Anime4KConfig> get copyWith => __$Anime4KConfigCopyWithImpl<_Anime4KConfig>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$Anime4KConfigToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Anime4KConfig&&(identical(other.quality, quality) || other.quality == quality)&&(identical(other.mode, mode) || other.mode == mode));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,quality,mode);
@override
String toString() {
return 'Anime4KConfig(quality: $quality, mode: $mode)';
}
}
/// @nodoc
abstract mixin class _$Anime4KConfigCopyWith<$Res> implements $Anime4KConfigCopyWith<$Res> {
factory _$Anime4KConfigCopyWith(_Anime4KConfig value, $Res Function(_Anime4KConfig) _then) = __$Anime4KConfigCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(unknownEnumValue: Anime4KQuality.fast) Anime4KQuality quality,@JsonKey(unknownEnumValue: Anime4KMode.modeA) Anime4KMode mode
});
}
/// @nodoc
class __$Anime4KConfigCopyWithImpl<$Res>
implements _$Anime4KConfigCopyWith<$Res> {
__$Anime4KConfigCopyWithImpl(this._self, this._then);
final _Anime4KConfig _self;
final $Res Function(_Anime4KConfig) _then;
/// Create a copy of Anime4KConfig
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? quality = null,Object? mode = null,}) {
return _then(_Anime4KConfig(
quality: null == quality ? _self.quality : quality // ignore: cast_nullable_to_non_nullable
as Anime4KQuality,mode: null == mode ? _self.mode : mode // ignore: cast_nullable_to_non_nullable
as Anime4KMode,
));
}
}
/// @nodoc
mixin _$ArtCNNConfig {
@JsonKey(unknownEnumValue: ArtCNNModel.c4f16) ArtCNNModel get model;@JsonKey(unknownEnumValue: ArtCNNVariant.neutral) ArtCNNVariant get variant;
/// Create a copy of ArtCNNConfig
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ArtCNNConfigCopyWith<ArtCNNConfig> get copyWith => _$ArtCNNConfigCopyWithImpl<ArtCNNConfig>(this as ArtCNNConfig, _$identity);
/// Serializes this ArtCNNConfig to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ArtCNNConfig&&(identical(other.model, model) || other.model == model)&&(identical(other.variant, variant) || other.variant == variant));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,model,variant);
@override
String toString() {
return 'ArtCNNConfig(model: $model, variant: $variant)';
}
}
/// @nodoc
abstract mixin class $ArtCNNConfigCopyWith<$Res> {
factory $ArtCNNConfigCopyWith(ArtCNNConfig value, $Res Function(ArtCNNConfig) _then) = _$ArtCNNConfigCopyWithImpl;
@useResult
$Res call({
@JsonKey(unknownEnumValue: ArtCNNModel.c4f16) ArtCNNModel model,@JsonKey(unknownEnumValue: ArtCNNVariant.neutral) ArtCNNVariant variant
});
}
/// @nodoc
class _$ArtCNNConfigCopyWithImpl<$Res>
implements $ArtCNNConfigCopyWith<$Res> {
_$ArtCNNConfigCopyWithImpl(this._self, this._then);
final ArtCNNConfig _self;
final $Res Function(ArtCNNConfig) _then;
/// Create a copy of ArtCNNConfig
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? model = null,Object? variant = null,}) {
return _then(_self.copyWith(
model: null == model ? _self.model : model // ignore: cast_nullable_to_non_nullable
as ArtCNNModel,variant: null == variant ? _self.variant : variant // ignore: cast_nullable_to_non_nullable
as ArtCNNVariant,
));
}
}
/// Adds pattern-matching-related methods to [ArtCNNConfig].
extension ArtCNNConfigPatterns on ArtCNNConfig {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ArtCNNConfig value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ArtCNNConfig() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ArtCNNConfig value) $default,){
final _that = this;
switch (_that) {
case _ArtCNNConfig():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ArtCNNConfig value)? $default,){
final _that = this;
switch (_that) {
case _ArtCNNConfig() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(unknownEnumValue: ArtCNNModel.c4f16) ArtCNNModel model, @JsonKey(unknownEnumValue: ArtCNNVariant.neutral) ArtCNNVariant variant)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ArtCNNConfig() when $default != null:
return $default(_that.model,_that.variant);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(unknownEnumValue: ArtCNNModel.c4f16) ArtCNNModel model, @JsonKey(unknownEnumValue: ArtCNNVariant.neutral) ArtCNNVariant variant) $default,) {final _that = this;
switch (_that) {
case _ArtCNNConfig():
return $default(_that.model,_that.variant);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(unknownEnumValue: ArtCNNModel.c4f16) ArtCNNModel model, @JsonKey(unknownEnumValue: ArtCNNVariant.neutral) ArtCNNVariant variant)? $default,) {final _that = this;
switch (_that) {
case _ArtCNNConfig() when $default != null:
return $default(_that.model,_that.variant);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _ArtCNNConfig implements ArtCNNConfig {
const _ArtCNNConfig({@JsonKey(unknownEnumValue: ArtCNNModel.c4f16) required this.model, @JsonKey(unknownEnumValue: ArtCNNVariant.neutral) required this.variant});
factory _ArtCNNConfig.fromJson(Map<String, dynamic> json) => _$ArtCNNConfigFromJson(json);
@override@JsonKey(unknownEnumValue: ArtCNNModel.c4f16) final ArtCNNModel model;
@override@JsonKey(unknownEnumValue: ArtCNNVariant.neutral) final ArtCNNVariant variant;
/// Create a copy of ArtCNNConfig
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ArtCNNConfigCopyWith<_ArtCNNConfig> get copyWith => __$ArtCNNConfigCopyWithImpl<_ArtCNNConfig>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$ArtCNNConfigToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ArtCNNConfig&&(identical(other.model, model) || other.model == model)&&(identical(other.variant, variant) || other.variant == variant));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,model,variant);
@override
String toString() {
return 'ArtCNNConfig(model: $model, variant: $variant)';
}
}
/// @nodoc
abstract mixin class _$ArtCNNConfigCopyWith<$Res> implements $ArtCNNConfigCopyWith<$Res> {
factory _$ArtCNNConfigCopyWith(_ArtCNNConfig value, $Res Function(_ArtCNNConfig) _then) = __$ArtCNNConfigCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(unknownEnumValue: ArtCNNModel.c4f16) ArtCNNModel model,@JsonKey(unknownEnumValue: ArtCNNVariant.neutral) ArtCNNVariant variant
});
}
/// @nodoc
class __$ArtCNNConfigCopyWithImpl<$Res>
implements _$ArtCNNConfigCopyWith<$Res> {
__$ArtCNNConfigCopyWithImpl(this._self, this._then);
final _ArtCNNConfig _self;
final $Res Function(_ArtCNNConfig) _then;
/// Create a copy of ArtCNNConfig
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? model = null,Object? variant = null,}) {
return _then(_ArtCNNConfig(
model: null == model ? _self.model : model // ignore: cast_nullable_to_non_nullable
as ArtCNNModel,variant: null == variant ? _self.variant : variant // ignore: cast_nullable_to_non_nullable
as ArtCNNVariant,
));
}
}
/// @nodoc
mixin _$NVScalerConfig {
/// Whether to automatically skip NVScaler on HDR content
bool get autoHdrSkip;
/// Create a copy of NVScalerConfig
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$NVScalerConfigCopyWith<NVScalerConfig> get copyWith => _$NVScalerConfigCopyWithImpl<NVScalerConfig>(this as NVScalerConfig, _$identity);
/// Serializes this NVScalerConfig to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is NVScalerConfig&&(identical(other.autoHdrSkip, autoHdrSkip) || other.autoHdrSkip == autoHdrSkip));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,autoHdrSkip);
@override
String toString() {
return 'NVScalerConfig(autoHdrSkip: $autoHdrSkip)';
}
}
/// @nodoc
abstract mixin class $NVScalerConfigCopyWith<$Res> {
factory $NVScalerConfigCopyWith(NVScalerConfig value, $Res Function(NVScalerConfig) _then) = _$NVScalerConfigCopyWithImpl;
@useResult
$Res call({
bool autoHdrSkip
});
}
/// @nodoc
class _$NVScalerConfigCopyWithImpl<$Res>
implements $NVScalerConfigCopyWith<$Res> {
_$NVScalerConfigCopyWithImpl(this._self, this._then);
final NVScalerConfig _self;
final $Res Function(NVScalerConfig) _then;
/// Create a copy of NVScalerConfig
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? autoHdrSkip = null,}) {
return _then(_self.copyWith(
autoHdrSkip: null == autoHdrSkip ? _self.autoHdrSkip : autoHdrSkip // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// Adds pattern-matching-related methods to [NVScalerConfig].
extension NVScalerConfigPatterns on NVScalerConfig {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _NVScalerConfig value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _NVScalerConfig() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _NVScalerConfig value) $default,){
final _that = this;
switch (_that) {
case _NVScalerConfig():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _NVScalerConfig value)? $default,){
final _that = this;
switch (_that) {
case _NVScalerConfig() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool autoHdrSkip)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _NVScalerConfig() when $default != null:
return $default(_that.autoHdrSkip);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool autoHdrSkip) $default,) {final _that = this;
switch (_that) {
case _NVScalerConfig():
return $default(_that.autoHdrSkip);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool autoHdrSkip)? $default,) {final _that = this;
switch (_that) {
case _NVScalerConfig() when $default != null:
return $default(_that.autoHdrSkip);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _NVScalerConfig implements NVScalerConfig {
const _NVScalerConfig({this.autoHdrSkip = true});
factory _NVScalerConfig.fromJson(Map<String, dynamic> json) => _$NVScalerConfigFromJson(json);
/// Whether to automatically skip NVScaler on HDR content
@override@JsonKey() final bool autoHdrSkip;
/// Create a copy of NVScalerConfig
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$NVScalerConfigCopyWith<_NVScalerConfig> get copyWith => __$NVScalerConfigCopyWithImpl<_NVScalerConfig>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$NVScalerConfigToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _NVScalerConfig&&(identical(other.autoHdrSkip, autoHdrSkip) || other.autoHdrSkip == autoHdrSkip));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,autoHdrSkip);
@override
String toString() {
return 'NVScalerConfig(autoHdrSkip: $autoHdrSkip)';
}
}
/// @nodoc
abstract mixin class _$NVScalerConfigCopyWith<$Res> implements $NVScalerConfigCopyWith<$Res> {
factory _$NVScalerConfigCopyWith(_NVScalerConfig value, $Res Function(_NVScalerConfig) _then) = __$NVScalerConfigCopyWithImpl;
@override @useResult
$Res call({
bool autoHdrSkip
});
}
/// @nodoc
class __$NVScalerConfigCopyWithImpl<$Res>
implements _$NVScalerConfigCopyWith<$Res> {
__$NVScalerConfigCopyWithImpl(this._self, this._then);
final _NVScalerConfig _self;
final $Res Function(_NVScalerConfig) _then;
/// Create a copy of NVScalerConfig
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? autoHdrSkip = null,}) {
return _then(_NVScalerConfig(
autoHdrSkip: null == autoHdrSkip ? _self.autoHdrSkip : autoHdrSkip // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
// dart format on
+78
View File
@@ -0,0 +1,78 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'shader_preset.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_Anime4KConfig _$Anime4KConfigFromJson(Map<String, dynamic> json) =>
_Anime4KConfig(
quality: $enumDecode(
_$Anime4KQualityEnumMap,
json['quality'],
unknownValue: Anime4KQuality.fast,
),
mode: $enumDecode(
_$Anime4KModeEnumMap,
json['mode'],
unknownValue: Anime4KMode.modeA,
),
);
Map<String, dynamic> _$Anime4KConfigToJson(_Anime4KConfig instance) =>
<String, dynamic>{
'quality': _$Anime4KQualityEnumMap[instance.quality]!,
'mode': _$Anime4KModeEnumMap[instance.mode]!,
};
const _$Anime4KQualityEnumMap = {
Anime4KQuality.fast: 'fast',
Anime4KQuality.hq: 'hq',
};
const _$Anime4KModeEnumMap = {
Anime4KMode.modeA: 'modeA',
Anime4KMode.modeB: 'modeB',
Anime4KMode.modeC: 'modeC',
Anime4KMode.modeAA: 'modeAA',
Anime4KMode.modeBB: 'modeBB',
Anime4KMode.modeCA: 'modeCA',
};
_ArtCNNConfig _$ArtCNNConfigFromJson(Map<String, dynamic> json) =>
_ArtCNNConfig(
model: $enumDecode(
_$ArtCNNModelEnumMap,
json['model'],
unknownValue: ArtCNNModel.c4f16,
),
variant: $enumDecode(
_$ArtCNNVariantEnumMap,
json['variant'],
unknownValue: ArtCNNVariant.neutral,
),
);
Map<String, dynamic> _$ArtCNNConfigToJson(_ArtCNNConfig instance) =>
<String, dynamic>{
'model': _$ArtCNNModelEnumMap[instance.model]!,
'variant': _$ArtCNNVariantEnumMap[instance.variant]!,
};
const _$ArtCNNModelEnumMap = {
ArtCNNModel.c4f16: 'c4f16',
ArtCNNModel.c4f32: 'c4f32',
};
const _$ArtCNNVariantEnumMap = {
ArtCNNVariant.neutral: 'neutral',
ArtCNNVariant.denoise: 'denoise',
ArtCNNVariant.denoiseSharpen: 'denoiseSharpen',
};
_NVScalerConfig _$NVScalerConfigFromJson(Map<String, dynamic> json) =>
_NVScalerConfig(autoHdrSkip: json['autoHdrSkip'] as bool? ?? true);
Map<String, dynamic> _$NVScalerConfigToJson(_NVScalerConfig instance) =>
<String, dynamic>{'autoHdrSkip': instance.autoHdrSkip};
+23 -42
View File
@@ -1,52 +1,33 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'device_code.freezed.dart';
/// Result of requesting a device code from an RFC 8628 authorization server.
///
/// The user enters [userCode] at [verificationUrl]; the app polls the token
/// endpoint with [deviceCode] every [interval] seconds until [expiresIn]
/// seconds elapse.
class DeviceCode {
final String deviceCode;
final String userCode;
final String verificationUrl;
@freezed
sealed class DeviceCode with _$DeviceCode {
const factory DeviceCode({
required String deviceCode,
required String userCode,
required String verificationUrl,
required int expiresIn,
required int interval,
/// URL with the code pre-filled (e.g. `https://trakt.tv/activate/ABC12345`)
/// when the provider supports it. Nullable — Simkl doesn't.
final String? verificationUrlComplete;
final int expiresIn;
final int interval;
const DeviceCode({
required this.deviceCode,
required this.userCode,
required this.verificationUrl,
required this.expiresIn,
required this.interval,
this.verificationUrlComplete,
});
/// URL with the code pre-filled (e.g. `https://trakt.tv/activate/ABC12345`)
/// when the provider supports it. Nullable — Simkl doesn't.
String? verificationUrlComplete,
}) = _DeviceCode;
}
/// Discriminated event emitted by a device-code poll loop.
sealed class DevicePollEvent {
const DevicePollEvent();
}
class DevicePollPending extends DevicePollEvent {
const DevicePollPending();
}
class DevicePollSlowDown extends DevicePollEvent {
const DevicePollSlowDown();
}
class DevicePollDenied extends DevicePollEvent {
const DevicePollDenied();
}
class DevicePollExpired extends DevicePollEvent {
const DevicePollExpired();
}
class DevicePollSuccess extends DevicePollEvent {
final Map<String, dynamic> tokenResponse;
const DevicePollSuccess(this.tokenResponse);
@freezed
sealed class DevicePollEvent with _$DevicePollEvent {
const factory DevicePollEvent.pending() = DevicePollPending;
const factory DevicePollEvent.slowDown() = DevicePollSlowDown;
const factory DevicePollEvent.denied() = DevicePollDenied;
const factory DevicePollEvent.expired() = DevicePollExpired;
const factory DevicePollEvent.success(Map<String, dynamic> tokenResponse) = DevicePollSuccess;
}
@@ -0,0 +1,718 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'device_code.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$DeviceCode {
String get deviceCode; String get userCode; String get verificationUrl; int get expiresIn; int get interval;/// URL with the code pre-filled (e.g. `https://trakt.tv/activate/ABC12345`)
/// when the provider supports it. Nullable — Simkl doesn't.
String? get verificationUrlComplete;
/// Create a copy of DeviceCode
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$DeviceCodeCopyWith<DeviceCode> get copyWith => _$DeviceCodeCopyWithImpl<DeviceCode>(this as DeviceCode, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DeviceCode&&(identical(other.deviceCode, deviceCode) || other.deviceCode == deviceCode)&&(identical(other.userCode, userCode) || other.userCode == userCode)&&(identical(other.verificationUrl, verificationUrl) || other.verificationUrl == verificationUrl)&&(identical(other.expiresIn, expiresIn) || other.expiresIn == expiresIn)&&(identical(other.interval, interval) || other.interval == interval)&&(identical(other.verificationUrlComplete, verificationUrlComplete) || other.verificationUrlComplete == verificationUrlComplete));
}
@override
int get hashCode => Object.hash(runtimeType,deviceCode,userCode,verificationUrl,expiresIn,interval,verificationUrlComplete);
@override
String toString() {
return 'DeviceCode(deviceCode: $deviceCode, userCode: $userCode, verificationUrl: $verificationUrl, expiresIn: $expiresIn, interval: $interval, verificationUrlComplete: $verificationUrlComplete)';
}
}
/// @nodoc
abstract mixin class $DeviceCodeCopyWith<$Res> {
factory $DeviceCodeCopyWith(DeviceCode value, $Res Function(DeviceCode) _then) = _$DeviceCodeCopyWithImpl;
@useResult
$Res call({
String deviceCode, String userCode, String verificationUrl, int expiresIn, int interval, String? verificationUrlComplete
});
}
/// @nodoc
class _$DeviceCodeCopyWithImpl<$Res>
implements $DeviceCodeCopyWith<$Res> {
_$DeviceCodeCopyWithImpl(this._self, this._then);
final DeviceCode _self;
final $Res Function(DeviceCode) _then;
/// Create a copy of DeviceCode
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? deviceCode = null,Object? userCode = null,Object? verificationUrl = null,Object? expiresIn = null,Object? interval = null,Object? verificationUrlComplete = freezed,}) {
return _then(_self.copyWith(
deviceCode: null == deviceCode ? _self.deviceCode : deviceCode // ignore: cast_nullable_to_non_nullable
as String,userCode: null == userCode ? _self.userCode : userCode // ignore: cast_nullable_to_non_nullable
as String,verificationUrl: null == verificationUrl ? _self.verificationUrl : verificationUrl // ignore: cast_nullable_to_non_nullable
as String,expiresIn: null == expiresIn ? _self.expiresIn : expiresIn // ignore: cast_nullable_to_non_nullable
as int,interval: null == interval ? _self.interval : interval // ignore: cast_nullable_to_non_nullable
as int,verificationUrlComplete: freezed == verificationUrlComplete ? _self.verificationUrlComplete : verificationUrlComplete // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// Adds pattern-matching-related methods to [DeviceCode].
extension DeviceCodePatterns on DeviceCode {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _DeviceCode value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _DeviceCode() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _DeviceCode value) $default,){
final _that = this;
switch (_that) {
case _DeviceCode():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _DeviceCode value)? $default,){
final _that = this;
switch (_that) {
case _DeviceCode() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String deviceCode, String userCode, String verificationUrl, int expiresIn, int interval, String? verificationUrlComplete)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _DeviceCode() when $default != null:
return $default(_that.deviceCode,_that.userCode,_that.verificationUrl,_that.expiresIn,_that.interval,_that.verificationUrlComplete);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceCode, String userCode, String verificationUrl, int expiresIn, int interval, String? verificationUrlComplete) $default,) {final _that = this;
switch (_that) {
case _DeviceCode():
return $default(_that.deviceCode,_that.userCode,_that.verificationUrl,_that.expiresIn,_that.interval,_that.verificationUrlComplete);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceCode, String userCode, String verificationUrl, int expiresIn, int interval, String? verificationUrlComplete)? $default,) {final _that = this;
switch (_that) {
case _DeviceCode() when $default != null:
return $default(_that.deviceCode,_that.userCode,_that.verificationUrl,_that.expiresIn,_that.interval,_that.verificationUrlComplete);case _:
return null;
}
}
}
/// @nodoc
class _DeviceCode implements DeviceCode {
const _DeviceCode({required this.deviceCode, required this.userCode, required this.verificationUrl, required this.expiresIn, required this.interval, this.verificationUrlComplete});
@override final String deviceCode;
@override final String userCode;
@override final String verificationUrl;
@override final int expiresIn;
@override final int interval;
/// URL with the code pre-filled (e.g. `https://trakt.tv/activate/ABC12345`)
/// when the provider supports it. Nullable — Simkl doesn't.
@override final String? verificationUrlComplete;
/// Create a copy of DeviceCode
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$DeviceCodeCopyWith<_DeviceCode> get copyWith => __$DeviceCodeCopyWithImpl<_DeviceCode>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DeviceCode&&(identical(other.deviceCode, deviceCode) || other.deviceCode == deviceCode)&&(identical(other.userCode, userCode) || other.userCode == userCode)&&(identical(other.verificationUrl, verificationUrl) || other.verificationUrl == verificationUrl)&&(identical(other.expiresIn, expiresIn) || other.expiresIn == expiresIn)&&(identical(other.interval, interval) || other.interval == interval)&&(identical(other.verificationUrlComplete, verificationUrlComplete) || other.verificationUrlComplete == verificationUrlComplete));
}
@override
int get hashCode => Object.hash(runtimeType,deviceCode,userCode,verificationUrl,expiresIn,interval,verificationUrlComplete);
@override
String toString() {
return 'DeviceCode(deviceCode: $deviceCode, userCode: $userCode, verificationUrl: $verificationUrl, expiresIn: $expiresIn, interval: $interval, verificationUrlComplete: $verificationUrlComplete)';
}
}
/// @nodoc
abstract mixin class _$DeviceCodeCopyWith<$Res> implements $DeviceCodeCopyWith<$Res> {
factory _$DeviceCodeCopyWith(_DeviceCode value, $Res Function(_DeviceCode) _then) = __$DeviceCodeCopyWithImpl;
@override @useResult
$Res call({
String deviceCode, String userCode, String verificationUrl, int expiresIn, int interval, String? verificationUrlComplete
});
}
/// @nodoc
class __$DeviceCodeCopyWithImpl<$Res>
implements _$DeviceCodeCopyWith<$Res> {
__$DeviceCodeCopyWithImpl(this._self, this._then);
final _DeviceCode _self;
final $Res Function(_DeviceCode) _then;
/// Create a copy of DeviceCode
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? deviceCode = null,Object? userCode = null,Object? verificationUrl = null,Object? expiresIn = null,Object? interval = null,Object? verificationUrlComplete = freezed,}) {
return _then(_DeviceCode(
deviceCode: null == deviceCode ? _self.deviceCode : deviceCode // ignore: cast_nullable_to_non_nullable
as String,userCode: null == userCode ? _self.userCode : userCode // ignore: cast_nullable_to_non_nullable
as String,verificationUrl: null == verificationUrl ? _self.verificationUrl : verificationUrl // ignore: cast_nullable_to_non_nullable
as String,expiresIn: null == expiresIn ? _self.expiresIn : expiresIn // ignore: cast_nullable_to_non_nullable
as int,interval: null == interval ? _self.interval : interval // ignore: cast_nullable_to_non_nullable
as int,verificationUrlComplete: freezed == verificationUrlComplete ? _self.verificationUrlComplete : verificationUrlComplete // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
mixin _$DevicePollEvent {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DevicePollEvent);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'DevicePollEvent()';
}
}
/// @nodoc
class $DevicePollEventCopyWith<$Res> {
$DevicePollEventCopyWith(DevicePollEvent _, $Res Function(DevicePollEvent) __);
}
/// Adds pattern-matching-related methods to [DevicePollEvent].
extension DevicePollEventPatterns on DevicePollEvent {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( DevicePollPending value)? pending,TResult Function( DevicePollSlowDown value)? slowDown,TResult Function( DevicePollDenied value)? denied,TResult Function( DevicePollExpired value)? expired,TResult Function( DevicePollSuccess value)? success,required TResult orElse(),}){
final _that = this;
switch (_that) {
case DevicePollPending() when pending != null:
return pending(_that);case DevicePollSlowDown() when slowDown != null:
return slowDown(_that);case DevicePollDenied() when denied != null:
return denied(_that);case DevicePollExpired() when expired != null:
return expired(_that);case DevicePollSuccess() when success != null:
return success(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( DevicePollPending value) pending,required TResult Function( DevicePollSlowDown value) slowDown,required TResult Function( DevicePollDenied value) denied,required TResult Function( DevicePollExpired value) expired,required TResult Function( DevicePollSuccess value) success,}){
final _that = this;
switch (_that) {
case DevicePollPending():
return pending(_that);case DevicePollSlowDown():
return slowDown(_that);case DevicePollDenied():
return denied(_that);case DevicePollExpired():
return expired(_that);case DevicePollSuccess():
return success(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( DevicePollPending value)? pending,TResult? Function( DevicePollSlowDown value)? slowDown,TResult? Function( DevicePollDenied value)? denied,TResult? Function( DevicePollExpired value)? expired,TResult? Function( DevicePollSuccess value)? success,}){
final _that = this;
switch (_that) {
case DevicePollPending() when pending != null:
return pending(_that);case DevicePollSlowDown() when slowDown != null:
return slowDown(_that);case DevicePollDenied() when denied != null:
return denied(_that);case DevicePollExpired() when expired != null:
return expired(_that);case DevicePollSuccess() when success != null:
return success(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function()? pending,TResult Function()? slowDown,TResult Function()? denied,TResult Function()? expired,TResult Function( Map<String, dynamic> tokenResponse)? success,required TResult orElse(),}) {final _that = this;
switch (_that) {
case DevicePollPending() when pending != null:
return pending();case DevicePollSlowDown() when slowDown != null:
return slowDown();case DevicePollDenied() when denied != null:
return denied();case DevicePollExpired() when expired != null:
return expired();case DevicePollSuccess() when success != null:
return success(_that.tokenResponse);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function() pending,required TResult Function() slowDown,required TResult Function() denied,required TResult Function() expired,required TResult Function( Map<String, dynamic> tokenResponse) success,}) {final _that = this;
switch (_that) {
case DevicePollPending():
return pending();case DevicePollSlowDown():
return slowDown();case DevicePollDenied():
return denied();case DevicePollExpired():
return expired();case DevicePollSuccess():
return success(_that.tokenResponse);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function()? pending,TResult? Function()? slowDown,TResult? Function()? denied,TResult? Function()? expired,TResult? Function( Map<String, dynamic> tokenResponse)? success,}) {final _that = this;
switch (_that) {
case DevicePollPending() when pending != null:
return pending();case DevicePollSlowDown() when slowDown != null:
return slowDown();case DevicePollDenied() when denied != null:
return denied();case DevicePollExpired() when expired != null:
return expired();case DevicePollSuccess() when success != null:
return success(_that.tokenResponse);case _:
return null;
}
}
}
/// @nodoc
class DevicePollPending implements DevicePollEvent {
const DevicePollPending();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DevicePollPending);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'DevicePollEvent.pending()';
}
}
/// @nodoc
class $DevicePollPendingCopyWith<$Res> implements $DevicePollEventCopyWith<$Res> {
$DevicePollPendingCopyWith(DevicePollPending _, $Res Function(DevicePollPending) __);
}
/// @nodoc
class _$DevicePollPendingCopyWithImpl<$Res>
implements $DevicePollPendingCopyWith<$Res> {
_$DevicePollPendingCopyWithImpl(this._self, this._then);
final DevicePollPending _self;
final $Res Function(DevicePollPending) _then;
}
/// @nodoc
class DevicePollSlowDown implements DevicePollEvent {
const DevicePollSlowDown();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DevicePollSlowDown);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'DevicePollEvent.slowDown()';
}
}
/// @nodoc
class $DevicePollSlowDownCopyWith<$Res> implements $DevicePollEventCopyWith<$Res> {
$DevicePollSlowDownCopyWith(DevicePollSlowDown _, $Res Function(DevicePollSlowDown) __);
}
/// @nodoc
class _$DevicePollSlowDownCopyWithImpl<$Res>
implements $DevicePollSlowDownCopyWith<$Res> {
_$DevicePollSlowDownCopyWithImpl(this._self, this._then);
final DevicePollSlowDown _self;
final $Res Function(DevicePollSlowDown) _then;
}
/// @nodoc
class DevicePollDenied implements DevicePollEvent {
const DevicePollDenied();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DevicePollDenied);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'DevicePollEvent.denied()';
}
}
/// @nodoc
class $DevicePollDeniedCopyWith<$Res> implements $DevicePollEventCopyWith<$Res> {
$DevicePollDeniedCopyWith(DevicePollDenied _, $Res Function(DevicePollDenied) __);
}
/// @nodoc
class _$DevicePollDeniedCopyWithImpl<$Res>
implements $DevicePollDeniedCopyWith<$Res> {
_$DevicePollDeniedCopyWithImpl(this._self, this._then);
final DevicePollDenied _self;
final $Res Function(DevicePollDenied) _then;
}
/// @nodoc
class DevicePollExpired implements DevicePollEvent {
const DevicePollExpired();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DevicePollExpired);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'DevicePollEvent.expired()';
}
}
/// @nodoc
class $DevicePollExpiredCopyWith<$Res> implements $DevicePollEventCopyWith<$Res> {
$DevicePollExpiredCopyWith(DevicePollExpired _, $Res Function(DevicePollExpired) __);
}
/// @nodoc
class _$DevicePollExpiredCopyWithImpl<$Res>
implements $DevicePollExpiredCopyWith<$Res> {
_$DevicePollExpiredCopyWithImpl(this._self, this._then);
final DevicePollExpired _self;
final $Res Function(DevicePollExpired) _then;
}
/// @nodoc
class DevicePollSuccess implements DevicePollEvent {
const DevicePollSuccess(final Map<String, dynamic> tokenResponse): _tokenResponse = tokenResponse;
final Map<String, dynamic> _tokenResponse;
Map<String, dynamic> get tokenResponse {
if (_tokenResponse is EqualUnmodifiableMapView) return _tokenResponse;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_tokenResponse);
}
/// Create a copy of DevicePollEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$DevicePollSuccessCopyWith<DevicePollSuccess> get copyWith => _$DevicePollSuccessCopyWithImpl<DevicePollSuccess>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DevicePollSuccess&&const DeepCollectionEquality().equals(other._tokenResponse, _tokenResponse));
}
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_tokenResponse));
@override
String toString() {
return 'DevicePollEvent.success(tokenResponse: $tokenResponse)';
}
}
/// @nodoc
abstract mixin class $DevicePollSuccessCopyWith<$Res> implements $DevicePollEventCopyWith<$Res> {
factory $DevicePollSuccessCopyWith(DevicePollSuccess value, $Res Function(DevicePollSuccess) _then) = _$DevicePollSuccessCopyWithImpl;
@useResult
$Res call({
Map<String, dynamic> tokenResponse
});
}
/// @nodoc
class _$DevicePollSuccessCopyWithImpl<$Res>
implements $DevicePollSuccessCopyWith<$Res> {
_$DevicePollSuccessCopyWithImpl(this._self, this._then);
final DevicePollSuccess _self;
final $Res Function(DevicePollSuccess) _then;
/// Create a copy of DevicePollEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? tokenResponse = null,}) {
return _then(DevicePollSuccess(
null == tokenResponse ? _self._tokenResponse : tokenResponse // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
));
}
}
// dart format on
+27 -20
View File
@@ -1,16 +1,42 @@
// ignore_for_file: invalid_annotation_target
import 'package:json_annotation/json_annotation.dart';
import '../../utils/json_utils.dart';
part 'fribb_mapping_row.g.dart';
Object? _readTvdbSeason(Map json, String key) {
final season = json['season'];
return season is Map ? season['tvdb'] : null;
}
Object? _readTmdbSeason(Map json, String key) {
final season = json['season'];
return season is Map ? season['tmdb'] : null;
}
/// One row from `anime-list-mini.json` (Fribb/anime-lists).
@JsonSerializable(createToJson: false)
class FribbMappingRow {
@JsonKey(name: 'anilist_id', fromJson: flexibleInt)
final int? anilistId;
@JsonKey(name: 'imdb_id')
final String? imdbId;
@JsonKey(name: 'mal_id', fromJson: flexibleInt)
final int? malId;
@JsonKey(name: 'simkl_id', fromJson: flexibleInt)
final int? simklId;
@JsonKey(name: 'themoviedb_id', fromJson: flexibleInt)
final int? tmdbId;
@JsonKey(name: 'tvdb_id', fromJson: flexibleInt)
final int? tvdbId;
/// Plex season number this mapping corresponds to. A single show-level
/// external ID can resolve to multiple rows for split-cour anime; the
/// resolver picks by matching the episode's `parentIndex` against these.
@JsonKey(readValue: _readTvdbSeason, fromJson: flexibleInt)
final int? tvdbSeason;
@JsonKey(readValue: _readTmdbSeason, fromJson: flexibleInt)
final int? tmdbSeason;
/// `TV` / `MOVIE` / `OVA` / `ONA` / `SPECIAL` / `UNKNOWN` / `null`.
@@ -30,24 +56,5 @@ class FribbMappingRow {
bool get isMovie => type == 'MOVIE';
factory FribbMappingRow.fromJson(Map<String, dynamic> json) {
final season = json['season'];
int? tvdbSeason;
int? tmdbSeason;
if (season is Map) {
tvdbSeason = (season['tvdb'] as num?)?.toInt();
tmdbSeason = (season['tmdb'] as num?)?.toInt();
}
return FribbMappingRow(
anilistId: (json['anilist_id'] as num?)?.toInt(),
imdbId: json['imdb_id'] as String?,
malId: (json['mal_id'] as num?)?.toInt(),
simklId: (json['simkl_id'] as num?)?.toInt(),
tmdbId: (json['themoviedb_id'] as num?)?.toInt(),
tvdbId: (json['tvdb_id'] as num?)?.toInt(),
tvdbSeason: tvdbSeason,
tmdbSeason: tmdbSeason,
type: json['type'] as String?,
);
}
factory FribbMappingRow.fromJson(Map<String, dynamic> json) => _$FribbMappingRowFromJson(json);
}
@@ -0,0 +1,20 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'fribb_mapping_row.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
FribbMappingRow _$FribbMappingRowFromJson(Map<String, dynamic> json) =>
FribbMappingRow(
anilistId: flexibleInt(json['anilist_id']),
imdbId: json['imdb_id'] as String?,
malId: flexibleInt(json['mal_id']),
simklId: flexibleInt(json['simkl_id']),
tmdbId: flexibleInt(json['themoviedb_id']),
tvdbId: flexibleInt(json['tvdb_id']),
tvdbSeason: flexibleInt(_readTvdbSeason(json, 'tvdbSeason')),
tmdbSeason: flexibleInt(_readTmdbSeason(json, 'tmdbSeason')),
type: json['type'] as String?,
);
+47 -63
View File
@@ -1,110 +1,94 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'trakt_ids.dart';
part 'trakt_scrobble_request.freezed.dart';
/// Body for `POST /scrobble/{start|pause|stop}` and `POST /sync/history`.
///
/// Either movie IDs or show IDs + season/episode are set, never both.
/// [progress] is the percent (0100) for scrobble; ignored for `/sync/history`.
class TraktScrobbleRequest {
final TraktIds? _movieIds;
final TraktIds? _showIds;
final int? _season;
final int? _episode;
final double? progress;
const TraktScrobbleRequest._({TraktIds? movieIds, TraktIds? showIds, int? season, int? episode, this.progress})
: _movieIds = movieIds,
_showIds = showIds,
_season = season,
_episode = episode;
bool get isMovie => _movieIds != null;
bool get isEpisode => _showIds != null;
TraktScrobbleRequest copyWith({double? progress}) => TraktScrobbleRequest._(
movieIds: _movieIds,
showIds: _showIds,
season: _season,
episode: _episode,
progress: progress ?? this.progress,
);
@freezed
sealed class TraktScrobbleRequest with _$TraktScrobbleRequest {
const TraktScrobbleRequest._();
/// Build a movie scrobble payload.
factory TraktScrobbleRequest.movie({required TraktIds ids, double? progress}) {
return TraktScrobbleRequest._(movieIds: ids, progress: progress);
}
const factory TraktScrobbleRequest.movie({required TraktIds ids, double? progress}) = TraktScrobbleMovieRequest;
/// Build an episode scrobble payload using the show's external IDs plus
/// season/episode index. Trakt prefers this shape over an episode-IDs-only
/// payload because it works even when the episode itself isn't in Trakt's
/// catalog yet.
factory TraktScrobbleRequest.episode({
const factory TraktScrobbleRequest.episode({
required TraktIds showIds,
required int season,
required int number,
double? progress,
}) {
return TraktScrobbleRequest._(showIds: showIds, season: season, episode: number, progress: progress);
}
}) = TraktScrobbleEpisodeRequest;
Map<String, dynamic> toJson() => {
if (_movieIds != null) 'movie': {'ids': _movieIds.toJson()},
if (_showIds != null) 'show': {'ids': _showIds.toJson()},
if (_season != null && _episode != null) 'episode': {'season': _season, 'number': _episode},
'progress': ?progress,
bool get isMovie => this is TraktScrobbleMovieRequest;
bool get isEpisode => this is TraktScrobbleEpisodeRequest;
Map<String, dynamic> toJson() => switch (this) {
TraktScrobbleMovieRequest(:final ids, :final progress) => {
'movie': {'ids': ids.toJson()},
'progress': ?progress,
},
TraktScrobbleEpisodeRequest(:final showIds, :final season, :final number, :final progress) => {
'show': {'ids': showIds.toJson()},
'episode': {'season': season, 'number': number},
'progress': ?progress,
},
};
/// Build a `POST /sync/history` body that adds this item to history.
///
/// Optional [watchedAt] (ISO-8601 UTC) lets the server attribute the play
/// to a specific point in time; defaults to "now" on Trakt's side.
Map<String, dynamic> toHistoryAddBody({String? watchedAt}) {
if (isMovie) {
return {
'movies': [
{'watched_at': ?watchedAt, 'ids': _movieIds!.toJson()},
],
};
}
return {
Map<String, dynamic> toHistoryAddBody({String? watchedAt}) => switch (this) {
TraktScrobbleMovieRequest(:final ids) => {
'movies': [
{'watched_at': ?watchedAt, 'ids': ids.toJson()},
],
},
TraktScrobbleEpisodeRequest(:final showIds, :final season, :final number) => {
'shows': [
{
'ids': _showIds!.toJson(),
'ids': showIds.toJson(),
'seasons': [
{
'number': _season,
'number': season,
'episodes': [
{'watched_at': ?watchedAt, 'number': _episode},
{'watched_at': ?watchedAt, 'number': number},
],
},
],
},
],
};
}
},
};
/// Build a `POST /sync/history/remove` body that removes this item from history.
Map<String, dynamic> toHistoryRemoveBody() {
if (isMovie) {
return {
'movies': [
{'ids': _movieIds!.toJson()},
],
};
}
return {
Map<String, dynamic> toHistoryRemoveBody() => switch (this) {
TraktScrobbleMovieRequest(:final ids) => {
'movies': [
{'ids': ids.toJson()},
],
},
TraktScrobbleEpisodeRequest(:final showIds, :final season, :final number) => {
'shows': [
{
'ids': _showIds!.toJson(),
'ids': showIds.toJson(),
'seasons': [
{
'number': _season,
'number': season,
'episodes': [
{'number': _episode},
{'number': number},
],
},
],
},
],
};
}
},
};
}
@@ -0,0 +1,345 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'trakt_scrobble_request.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$TraktScrobbleRequest {
double? get progress;
/// Create a copy of TraktScrobbleRequest
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$TraktScrobbleRequestCopyWith<TraktScrobbleRequest> get copyWith => _$TraktScrobbleRequestCopyWithImpl<TraktScrobbleRequest>(this as TraktScrobbleRequest, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TraktScrobbleRequest&&(identical(other.progress, progress) || other.progress == progress));
}
@override
int get hashCode => Object.hash(runtimeType,progress);
@override
String toString() {
return 'TraktScrobbleRequest(progress: $progress)';
}
}
/// @nodoc
abstract mixin class $TraktScrobbleRequestCopyWith<$Res> {
factory $TraktScrobbleRequestCopyWith(TraktScrobbleRequest value, $Res Function(TraktScrobbleRequest) _then) = _$TraktScrobbleRequestCopyWithImpl;
@useResult
$Res call({
double? progress
});
}
/// @nodoc
class _$TraktScrobbleRequestCopyWithImpl<$Res>
implements $TraktScrobbleRequestCopyWith<$Res> {
_$TraktScrobbleRequestCopyWithImpl(this._self, this._then);
final TraktScrobbleRequest _self;
final $Res Function(TraktScrobbleRequest) _then;
/// Create a copy of TraktScrobbleRequest
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? progress = freezed,}) {
return _then(_self.copyWith(
progress: freezed == progress ? _self.progress : progress // ignore: cast_nullable_to_non_nullable
as double?,
));
}
}
/// Adds pattern-matching-related methods to [TraktScrobbleRequest].
extension TraktScrobbleRequestPatterns on TraktScrobbleRequest {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( TraktScrobbleMovieRequest value)? movie,TResult Function( TraktScrobbleEpisodeRequest value)? episode,required TResult orElse(),}){
final _that = this;
switch (_that) {
case TraktScrobbleMovieRequest() when movie != null:
return movie(_that);case TraktScrobbleEpisodeRequest() when episode != null:
return episode(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( TraktScrobbleMovieRequest value) movie,required TResult Function( TraktScrobbleEpisodeRequest value) episode,}){
final _that = this;
switch (_that) {
case TraktScrobbleMovieRequest():
return movie(_that);case TraktScrobbleEpisodeRequest():
return episode(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( TraktScrobbleMovieRequest value)? movie,TResult? Function( TraktScrobbleEpisodeRequest value)? episode,}){
final _that = this;
switch (_that) {
case TraktScrobbleMovieRequest() when movie != null:
return movie(_that);case TraktScrobbleEpisodeRequest() when episode != null:
return episode(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function( TraktIds ids, double? progress)? movie,TResult Function( TraktIds showIds, int season, int number, double? progress)? episode,required TResult orElse(),}) {final _that = this;
switch (_that) {
case TraktScrobbleMovieRequest() when movie != null:
return movie(_that.ids,_that.progress);case TraktScrobbleEpisodeRequest() when episode != null:
return episode(_that.showIds,_that.season,_that.number,_that.progress);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function( TraktIds ids, double? progress) movie,required TResult Function( TraktIds showIds, int season, int number, double? progress) episode,}) {final _that = this;
switch (_that) {
case TraktScrobbleMovieRequest():
return movie(_that.ids,_that.progress);case TraktScrobbleEpisodeRequest():
return episode(_that.showIds,_that.season,_that.number,_that.progress);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function( TraktIds ids, double? progress)? movie,TResult? Function( TraktIds showIds, int season, int number, double? progress)? episode,}) {final _that = this;
switch (_that) {
case TraktScrobbleMovieRequest() when movie != null:
return movie(_that.ids,_that.progress);case TraktScrobbleEpisodeRequest() when episode != null:
return episode(_that.showIds,_that.season,_that.number,_that.progress);case _:
return null;
}
}
}
/// @nodoc
class TraktScrobbleMovieRequest extends TraktScrobbleRequest {
const TraktScrobbleMovieRequest({required this.ids, this.progress}): super._();
final TraktIds ids;
@override final double? progress;
/// Create a copy of TraktScrobbleRequest
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$TraktScrobbleMovieRequestCopyWith<TraktScrobbleMovieRequest> get copyWith => _$TraktScrobbleMovieRequestCopyWithImpl<TraktScrobbleMovieRequest>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TraktScrobbleMovieRequest&&(identical(other.ids, ids) || other.ids == ids)&&(identical(other.progress, progress) || other.progress == progress));
}
@override
int get hashCode => Object.hash(runtimeType,ids,progress);
@override
String toString() {
return 'TraktScrobbleRequest.movie(ids: $ids, progress: $progress)';
}
}
/// @nodoc
abstract mixin class $TraktScrobbleMovieRequestCopyWith<$Res> implements $TraktScrobbleRequestCopyWith<$Res> {
factory $TraktScrobbleMovieRequestCopyWith(TraktScrobbleMovieRequest value, $Res Function(TraktScrobbleMovieRequest) _then) = _$TraktScrobbleMovieRequestCopyWithImpl;
@override @useResult
$Res call({
TraktIds ids, double? progress
});
}
/// @nodoc
class _$TraktScrobbleMovieRequestCopyWithImpl<$Res>
implements $TraktScrobbleMovieRequestCopyWith<$Res> {
_$TraktScrobbleMovieRequestCopyWithImpl(this._self, this._then);
final TraktScrobbleMovieRequest _self;
final $Res Function(TraktScrobbleMovieRequest) _then;
/// Create a copy of TraktScrobbleRequest
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? ids = null,Object? progress = freezed,}) {
return _then(TraktScrobbleMovieRequest(
ids: null == ids ? _self.ids : ids // ignore: cast_nullable_to_non_nullable
as TraktIds,progress: freezed == progress ? _self.progress : progress // ignore: cast_nullable_to_non_nullable
as double?,
));
}
}
/// @nodoc
class TraktScrobbleEpisodeRequest extends TraktScrobbleRequest {
const TraktScrobbleEpisodeRequest({required this.showIds, required this.season, required this.number, this.progress}): super._();
final TraktIds showIds;
final int season;
final int number;
@override final double? progress;
/// Create a copy of TraktScrobbleRequest
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$TraktScrobbleEpisodeRequestCopyWith<TraktScrobbleEpisodeRequest> get copyWith => _$TraktScrobbleEpisodeRequestCopyWithImpl<TraktScrobbleEpisodeRequest>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TraktScrobbleEpisodeRequest&&(identical(other.showIds, showIds) || other.showIds == showIds)&&(identical(other.season, season) || other.season == season)&&(identical(other.number, number) || other.number == number)&&(identical(other.progress, progress) || other.progress == progress));
}
@override
int get hashCode => Object.hash(runtimeType,showIds,season,number,progress);
@override
String toString() {
return 'TraktScrobbleRequest.episode(showIds: $showIds, season: $season, number: $number, progress: $progress)';
}
}
/// @nodoc
abstract mixin class $TraktScrobbleEpisodeRequestCopyWith<$Res> implements $TraktScrobbleRequestCopyWith<$Res> {
factory $TraktScrobbleEpisodeRequestCopyWith(TraktScrobbleEpisodeRequest value, $Res Function(TraktScrobbleEpisodeRequest) _then) = _$TraktScrobbleEpisodeRequestCopyWithImpl;
@override @useResult
$Res call({
TraktIds showIds, int season, int number, double? progress
});
}
/// @nodoc
class _$TraktScrobbleEpisodeRequestCopyWithImpl<$Res>
implements $TraktScrobbleEpisodeRequestCopyWith<$Res> {
_$TraktScrobbleEpisodeRequestCopyWithImpl(this._self, this._then);
final TraktScrobbleEpisodeRequest _self;
final $Res Function(TraktScrobbleEpisodeRequest) _then;
/// Create a copy of TraktScrobbleRequest
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? showIds = null,Object? season = null,Object? number = null,Object? progress = freezed,}) {
return _then(TraktScrobbleEpisodeRequest(
showIds: null == showIds ? _self.showIds : showIds // ignore: cast_nullable_to_non_nullable
as TraktIds,season: null == season ? _self.season : season // ignore: cast_nullable_to_non_nullable
as int,number: null == number ? _self.number : number // ignore: cast_nullable_to_non_nullable
as int,progress: freezed == progress ? _self.progress : progress // ignore: cast_nullable_to_non_nullable
as double?,
));
}
}
// dart format on
+6 -1
View File
@@ -1,4 +1,9 @@
import 'package:json_annotation/json_annotation.dart';
part 'trakt_user.g.dart';
/// Minimal Trakt user info parsed from `GET /users/settings`.
@JsonSerializable(createToJson: false)
class TraktUser {
final String username;
final String? name;
@@ -10,6 +15,6 @@ class TraktUser {
if (user == null) {
throw const FormatException('Trakt /users/settings response missing "user" field');
}
return TraktUser(username: user['username'] as String, name: user['name'] as String?);
return _$TraktUserFromJson(user);
}
}
+12
View File
@@ -0,0 +1,12 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'trakt_user.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
TraktUser _$TraktUserFromJson(Map<String, dynamic> json) => TraktUser(
username: json['username'] as String,
name: json['name'] as String?,
);
+66 -142
View File
@@ -1,95 +1,77 @@
class BufferRange {
final Duration start;
final Duration end;
const BufferRange({required this.start, required this.end});
// ignore_for_file: invalid_annotation_target
import 'package:freezed_annotation/freezed_annotation.dart';
part 'models.freezed.dart';
@freezed
sealed class BufferRange with _$BufferRange {
const factory BufferRange({required Duration start, required Duration end}) = _BufferRange;
}
/// [cause] is an optional machine-readable tag (e.g. `server-http-500`),
/// letting the UI branch without parsing [message].
class PlayerError {
@Freezed(toStringOverride: false)
sealed class PlayerError with _$PlayerError {
const PlayerError._();
const factory PlayerError(String message, {String? cause}) = _PlayerError;
/// Cause tag for a server-side HTTP 500 — shared-user bandwidth or
/// transcoding limit rejection set by the server owner.
static const String serverHttp500 = 'server-http-500';
final String message;
final String? cause;
const PlayerError(this.message, {this.cause});
@override
String toString() => message;
}
enum PlayerLogLevel { none, fatal, error, warn, info, verbose, debug, trace }
class AudioTrack {
final String id;
final String? title;
final String? language;
final String? codec;
final int? channels;
int? get channelsCount => channels;
final int? sampleRate;
final int? bitrate;
final bool isDefault;
final bool isForced;
@freezed
sealed class AudioTrack with _$AudioTrack {
const AudioTrack._();
const AudioTrack({
required this.id,
this.title,
this.language,
this.codec,
this.channels,
this.sampleRate,
this.bitrate,
this.isDefault = false,
this.isForced = false,
});
const factory AudioTrack({
required String id,
String? title,
String? language,
String? codec,
int? channels,
int? sampleRate,
int? bitrate,
@Default(false) bool isDefault,
@Default(false) bool isForced,
}) = _AudioTrack;
static const auto = AudioTrack(id: 'auto', title: 'Auto');
static const off = AudioTrack(id: 'no', title: 'Off');
int? get channelsCount => channels;
String get displayName {
if (title != null && title!.isNotEmpty) return title!;
if (language != null && language!.isNotEmpty) return language!;
return 'Track $id';
}
@override
String toString() => 'AudioTrack($id, $displayName)';
@override
bool operator ==(Object other) =>
identical(this, other) || other is AudioTrack && runtimeType == other.runtimeType && id == other.id;
@override
int get hashCode => id.hashCode;
}
class SubtitleTrack {
final String id;
final String? title;
final String? language;
final String? codec;
final bool isDefault;
final bool isForced;
final bool isExternal;
final String? uri;
@freezed
sealed class SubtitleTrack with _$SubtitleTrack {
const SubtitleTrack._();
const SubtitleTrack({
required this.id,
this.title,
this.language,
this.codec,
this.isDefault = false,
this.isForced = false,
this.isExternal = false,
this.uri,
});
const factory SubtitleTrack({
required String id,
String? title,
String? language,
String? codec,
@Default(false) bool isDefault,
@Default(false) bool isForced,
@Default(false) bool isExternal,
String? uri,
}) = _SubtitleTrack;
factory SubtitleTrack.uri(String uri, {String? title, String? language}) {
return SubtitleTrack(id: 'external:$uri', title: title, language: language, isExternal: true, uri: uri);
}
factory SubtitleTrack.uri(String uri, {String? title, String? language}) =>
SubtitleTrack(id: 'external:$uri', title: title, language: language, isExternal: true, uri: uri);
static const auto = SubtitleTrack(id: 'auto', title: 'Auto');
@@ -101,103 +83,45 @@ class SubtitleTrack {
if (isExternal) return 'External';
return 'Track $id';
}
@override
String toString() => 'SubtitleTrack($id, $displayName)';
@override
bool operator ==(Object other) =>
identical(this, other) || other is SubtitleTrack && runtimeType == other.runtimeType && id == other.id;
@override
int get hashCode => id.hashCode;
}
class Tracks {
final List<AudioTrack> audio;
final List<SubtitleTrack> subtitle;
@Freezed(toStringOverride: false)
sealed class Tracks with _$Tracks {
const Tracks._();
const Tracks({this.audio = const [], this.subtitle = const []});
Tracks copyWith({List<AudioTrack>? audio, List<SubtitleTrack>? subtitle}) {
return Tracks(audio: audio ?? this.audio, subtitle: subtitle ?? this.subtitle);
}
const factory Tracks({
@Default(<AudioTrack>[]) List<AudioTrack> audio,
@Default(<SubtitleTrack>[]) List<SubtitleTrack> subtitle,
}) = _Tracks;
@override
String toString() => 'Tracks(audio: ${audio.length}, subtitle: ${subtitle.length})';
}
/// Sentinel value used to distinguish "not provided" from "explicitly set to null" in copyWith.
const _sentinel = Object();
class TrackSelection {
final AudioTrack? audio;
final SubtitleTrack? subtitle;
final SubtitleTrack? secondarySubtitle;
const TrackSelection({this.audio, this.subtitle, this.secondarySubtitle});
/// Creates a copy with the given fields replaced.
/// Use [secondarySubtitle] with explicit null to clear the secondary subtitle.
TrackSelection copyWith({AudioTrack? audio, SubtitleTrack? subtitle, Object? secondarySubtitle = _sentinel}) {
return TrackSelection(
audio: audio ?? this.audio,
subtitle: subtitle ?? this.subtitle,
secondarySubtitle: identical(secondarySubtitle, _sentinel)
? this.secondarySubtitle
: secondarySubtitle as SubtitleTrack?,
);
}
@override
String toString() => 'TrackSelection(audio: $audio, subtitle: $subtitle, secondarySubtitle: $secondarySubtitle)';
@freezed
sealed class TrackSelection with _$TrackSelection {
const factory TrackSelection({AudioTrack? audio, SubtitleTrack? subtitle, SubtitleTrack? secondarySubtitle}) =
_TrackSelection;
}
class AudioDevice {
final String name;
final String description;
const AudioDevice({required this.name, this.description = ''});
@freezed
sealed class AudioDevice with _$AudioDevice {
const factory AudioDevice({required String name, @Default('') String description}) = _AudioDevice;
static const auto = AudioDevice(name: 'auto', description: 'Auto');
@override
String toString() => 'AudioDevice($name, $description)';
@override
bool operator ==(Object other) =>
identical(this, other) || other is AudioDevice && runtimeType == other.runtimeType && name == other.name;
@override
int get hashCode => name.hashCode;
}
class PlayerLog {
final PlayerLogLevel level;
final String prefix;
final String text;
@Freezed(toStringOverride: false)
sealed class PlayerLog with _$PlayerLog {
const PlayerLog._();
const PlayerLog({required this.level, required this.prefix, required this.text});
const factory PlayerLog({required PlayerLogLevel level, required String prefix, required String text}) = _PlayerLog;
@override
String toString() => '[$prefix] ${level.name}: $text';
}
class Media {
final String uri;
final Map<String, String>? headers;
final Duration? start;
const Media(this.uri, {this.headers, this.start});
@override
String toString() => 'Media($uri)';
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Media && runtimeType == other.runtimeType && uri == other.uri && start == other.start;
@override
int get hashCode => uri.hashCode ^ start.hashCode;
@freezed
sealed class Media with _$Media {
const factory Media(String uri, {Map<String, String>? headers, Duration? start}) = _Media;
}
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -438,18 +438,18 @@ abstract class PlayerBase with PlayerStreamControllersMixin implements Player {
selectedTrack = _state.tracks.audio.firstWhereOrNull((t) => t.id == id);
}
if (selectedTrack == null) return;
_state = _state.copyWith(track: _state.track.copyWith(audio: selectedTrack));
trackController.add(_state.track);
}
void updateSelectedSubtitleTrack(dynamic trackId) {
final id = trackId?.toString();
SubtitleTrack? selectedTrack;
selectedTrack = (id == null || id == 'no')
final selectedTrack = (id == null || id == 'no')
? SubtitleTrack.off
: _state.tracks.subtitle.firstWhereOrNull((t) => t.id == id);
if (selectedTrack == null) return;
_state = _state.copyWith(track: _state.track.copyWith(subtitle: selectedTrack));
trackController.add(_state.track);
}
+112 -119
View File
@@ -1,148 +1,83 @@
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../models/plex/plex_home_user.dart';
part 'profile.freezed.dart';
/// Top-level profile — the user-facing identity in the app.
///
/// Two kinds:
/// - [ProfileKind.local]: a Plezy-only profile created by the user. May have
/// - [LocalProfile]: a Plezy-only profile created by the user. May have
/// an optional 4-digit PIN.
/// - [ProfileKind.plexHome]: auto-surfaced from a connected Plex account's
/// - [PlexHomeProfile]: auto-surfaced from a connected Plex account's
/// Home users. PIN protection is handled server-side by Plex via the
/// `/home/users/{uuid}/switch` flow — `pinHash` is unused.
///
/// A profile owns 1+ connections via the `profile_connections` join table.
/// The join row carries the per-profile user-level token used to talk to
/// each connection.
class Profile {
final String id;
final ProfileKind kind;
final String displayName;
final String? avatarThumbUrl;
@freezed
sealed class Profile with _$Profile {
const Profile._();
/// Hashed PIN if set — only meaningful for [ProfileKind.local]. The raw
/// PIN is never persisted; see [computePinHash].
final String? pinHash;
const factory Profile.local({
required String id,
required String displayName,
String? avatarThumbUrl,
/// For [ProfileKind.plexHome]: the parent Plex account's connection id.
/// `null` for local profiles.
final String? parentConnectionId;
/// Hashed PIN if set. The raw PIN is never persisted; see [computePinHash].
String? pinHash,
@Default(0) int sortOrder,
required DateTime createdAt,
DateTime? lastUsedAt,
}) = LocalProfile;
/// For [ProfileKind.plexHome]: the Plex Home user UUID. Used by the
/// active-profile binder to call `/home/users/{uuid}/switch`. `null` for
/// local profiles.
final String? plexHomeUserUuid;
const factory Profile.plexHome({
required String id,
required String displayName,
String? avatarThumbUrl,
/// Plex Home flags — only meaningful for [ProfileKind.plexHome].
final bool plexRestricted;
final bool plexAdmin;
/// The parent Plex account's connection id.
String? parentConnectionId,
/// Plex's `protected` flag — true when the home user has a PIN that must
/// be entered before `/home/users/{uuid}/switch` will succeed.
final bool plexProtected;
/// The Plex Home user UUID. Used by the active-profile binder to call
/// `/home/users/{uuid}/switch`.
String? plexHomeUserUuid,
@Default(false) bool plexRestricted,
@Default(false) bool plexAdmin,
final int sortOrder;
final DateTime createdAt;
final DateTime? lastUsedAt;
Profile({
required this.id,
required this.kind,
required this.displayName,
this.avatarThumbUrl,
this.pinHash,
this.parentConnectionId,
this.plexHomeUserUuid,
this.plexRestricted = false,
this.plexAdmin = false,
this.plexProtected = false,
this.sortOrder = 0,
required this.createdAt,
this.lastUsedAt,
});
/// Plex's `protected` flag — true when the home user has a PIN that must
/// be entered before `/home/users/{uuid}/switch` will succeed.
@Default(false) bool plexProtected,
@Default(0) int sortOrder,
required DateTime createdAt,
DateTime? lastUsedAt,
}) = PlexHomeProfile;
/// Construct an in-memory virtual `Profile` for a Plex Home user. These
/// are never persisted — Plex owns the Home user list, so the picker
/// reads them live from [PlexHomeService] and merges them with the local
/// rows from [ProfileRegistry].
/// rows from `ProfileRegistry`.
factory Profile.virtualPlexHome({
required String connectionId,
required PlexHomeUser homeUser,
DateTime? lastUsedAt,
}) {
return Profile(
id: plexHomeProfileId(accountConnectionId: connectionId, homeUserUuid: homeUser.uuid),
kind: ProfileKind.plexHome,
displayName: homeUser.displayName,
avatarThumbUrl: homeUser.thumb.isNotEmpty ? homeUser.thumb : null,
parentConnectionId: connectionId,
plexHomeUserUuid: homeUser.uuid,
plexRestricted: homeUser.restricted,
plexAdmin: homeUser.admin,
plexProtected: homeUser.protected,
sortOrder: homeUser.admin ? 0 : 1,
createdAt: DateTime.fromMillisecondsSinceEpoch(0),
lastUsedAt: lastUsedAt,
);
}
bool get isLocal => kind == ProfileKind.local;
bool get isPlexHome => kind == ProfileKind.plexHome;
/// True when entering this profile requires user-supplied PIN.
///
/// Locals: gated by their own [pinHash].
/// Plex Home: gated by Plex's own protected flag (`plexProtected`).
bool get isPinProtected => isLocal ? (pinHash != null && pinHash!.isNotEmpty) : plexProtected;
Profile copyWith({
String? id,
ProfileKind? kind,
String? displayName,
String? avatarThumbUrl,
bool clearAvatar = false,
String? pinHash,
bool clearPin = false,
String? parentConnectionId,
String? plexHomeUserUuid,
bool? plexRestricted,
bool? plexAdmin,
bool? plexProtected,
int? sortOrder,
DateTime? createdAt,
DateTime? lastUsedAt,
bool clearLastUsedAt = false,
}) {
return Profile(
id: id ?? this.id,
kind: kind ?? this.kind,
displayName: displayName ?? this.displayName,
avatarThumbUrl: clearAvatar ? null : (avatarThumbUrl ?? this.avatarThumbUrl),
pinHash: clearPin ? null : (pinHash ?? this.pinHash),
parentConnectionId: parentConnectionId ?? this.parentConnectionId,
plexHomeUserUuid: plexHomeUserUuid ?? this.plexHomeUserUuid,
plexRestricted: plexRestricted ?? this.plexRestricted,
plexAdmin: plexAdmin ?? this.plexAdmin,
plexProtected: plexProtected ?? this.plexProtected,
sortOrder: sortOrder ?? this.sortOrder,
createdAt: createdAt ?? this.createdAt,
lastUsedAt: clearLastUsedAt ? null : (lastUsedAt ?? this.lastUsedAt),
);
}
Map<String, Object?> toConfigJson() {
return switch (kind) {
ProfileKind.local => {'pinHash': pinHash},
ProfileKind.plexHome => {
'parentConnectionId': parentConnectionId,
'restricted': plexRestricted,
'admin': plexAdmin,
'protected': plexProtected,
},
};
}
}) => Profile.plexHome(
id: plexHomeProfileId(accountConnectionId: connectionId, homeUserUuid: homeUser.uuid),
displayName: homeUser.displayName,
avatarThumbUrl: homeUser.thumb.isNotEmpty ? homeUser.thumb : null,
parentConnectionId: connectionId,
plexHomeUserUuid: homeUser.uuid,
plexRestricted: homeUser.restricted,
plexAdmin: homeUser.admin,
plexProtected: homeUser.protected,
sortOrder: homeUser.admin ? 0 : 1,
createdAt: DateTime.fromMillisecondsSinceEpoch(0),
lastUsedAt: lastUsedAt,
);
factory Profile.fromRow({
required String id,
@@ -156,9 +91,8 @@ class Profile {
}) {
final parsedKind = ProfileKind.fromId(kind);
return switch (parsedKind) {
ProfileKind.local => Profile(
ProfileKind.local => Profile.local(
id: id,
kind: parsedKind,
displayName: displayName,
avatarThumbUrl: avatarThumbUrl,
pinHash: json['pinHash'] as String?,
@@ -166,9 +100,8 @@ class Profile {
createdAt: createdAt,
lastUsedAt: lastUsedAt,
),
ProfileKind.plexHome => Profile(
ProfileKind.plexHome => Profile.plexHome(
id: id,
kind: parsedKind,
displayName: displayName,
avatarThumbUrl: avatarThumbUrl,
parentConnectionId: json['parentConnectionId'] as String?,
@@ -181,6 +114,66 @@ class Profile {
),
};
}
bool get isLocal => this is LocalProfile;
bool get isPlexHome => this is PlexHomeProfile;
ProfileKind get kind => switch (this) {
LocalProfile() => ProfileKind.local,
PlexHomeProfile() => ProfileKind.plexHome,
};
/// True when entering this profile requires user-supplied PIN.
///
/// Locals: gated by their own [pinHash].
/// Plex Home: gated by Plex's own protected flag (`plexProtected`).
bool get isPinProtected => switch (this) {
LocalProfile(:final pinHash) => pinHash != null && pinHash.isNotEmpty,
PlexHomeProfile(:final plexProtected) => plexProtected,
};
/// Hashed PIN, only set for [LocalProfile]. Returns null for plexHome.
String? get pinHash => switch (this) {
LocalProfile(:final pinHash) => pinHash,
PlexHomeProfile() => null,
};
/// Parent Plex account connection id, only set for [PlexHomeProfile].
String? get parentConnectionId => switch (this) {
LocalProfile() => null,
PlexHomeProfile(:final parentConnectionId) => parentConnectionId,
};
/// Plex Home user UUID, only set for [PlexHomeProfile].
String? get plexHomeUserUuid => switch (this) {
LocalProfile() => null,
PlexHomeProfile(:final plexHomeUserUuid) => plexHomeUserUuid,
};
bool get plexRestricted => switch (this) {
LocalProfile() => false,
PlexHomeProfile(:final plexRestricted) => plexRestricted,
};
bool get plexAdmin => switch (this) {
LocalProfile() => false,
PlexHomeProfile(:final plexAdmin) => plexAdmin,
};
bool get plexProtected => switch (this) {
LocalProfile() => false,
PlexHomeProfile(:final plexProtected) => plexProtected,
};
Map<String, Object?> toConfigJson() => switch (this) {
LocalProfile(:final pinHash) => {'pinHash': pinHash},
PlexHomeProfile(:final parentConnectionId, :final plexRestricted, :final plexAdmin, :final plexProtected) => {
'parentConnectionId': parentConnectionId,
'restricted': plexRestricted,
'admin': plexAdmin,
'protected': plexProtected,
},
};
}
enum ProfileKind {
+380
View File
@@ -0,0 +1,380 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'profile.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$Profile {
String get id; String get displayName; String? get avatarThumbUrl; int get sortOrder; DateTime get createdAt; DateTime? get lastUsedAt;
/// Create a copy of Profile
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ProfileCopyWith<Profile> get copyWith => _$ProfileCopyWithImpl<Profile>(this as Profile, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is Profile&&(identical(other.id, id) || other.id == id)&&(identical(other.displayName, displayName) || other.displayName == displayName)&&(identical(other.avatarThumbUrl, avatarThumbUrl) || other.avatarThumbUrl == avatarThumbUrl)&&(identical(other.sortOrder, sortOrder) || other.sortOrder == sortOrder)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.lastUsedAt, lastUsedAt) || other.lastUsedAt == lastUsedAt));
}
@override
int get hashCode => Object.hash(runtimeType,id,displayName,avatarThumbUrl,sortOrder,createdAt,lastUsedAt);
@override
String toString() {
return 'Profile(id: $id, displayName: $displayName, avatarThumbUrl: $avatarThumbUrl, sortOrder: $sortOrder, createdAt: $createdAt, lastUsedAt: $lastUsedAt)';
}
}
/// @nodoc
abstract mixin class $ProfileCopyWith<$Res> {
factory $ProfileCopyWith(Profile value, $Res Function(Profile) _then) = _$ProfileCopyWithImpl;
@useResult
$Res call({
String id, String displayName, String? avatarThumbUrl, int sortOrder, DateTime createdAt, DateTime? lastUsedAt
});
}
/// @nodoc
class _$ProfileCopyWithImpl<$Res>
implements $ProfileCopyWith<$Res> {
_$ProfileCopyWithImpl(this._self, this._then);
final Profile _self;
final $Res Function(Profile) _then;
/// Create a copy of Profile
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? displayName = null,Object? avatarThumbUrl = freezed,Object? sortOrder = null,Object? createdAt = null,Object? lastUsedAt = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,displayName: null == displayName ? _self.displayName : displayName // ignore: cast_nullable_to_non_nullable
as String,avatarThumbUrl: freezed == avatarThumbUrl ? _self.avatarThumbUrl : avatarThumbUrl // ignore: cast_nullable_to_non_nullable
as String?,sortOrder: null == sortOrder ? _self.sortOrder : sortOrder // ignore: cast_nullable_to_non_nullable
as int,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,lastUsedAt: freezed == lastUsedAt ? _self.lastUsedAt : lastUsedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
}
/// Adds pattern-matching-related methods to [Profile].
extension ProfilePatterns on Profile {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( LocalProfile value)? local,TResult Function( PlexHomeProfile value)? plexHome,required TResult orElse(),}){
final _that = this;
switch (_that) {
case LocalProfile() when local != null:
return local(_that);case PlexHomeProfile() when plexHome != null:
return plexHome(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( LocalProfile value) local,required TResult Function( PlexHomeProfile value) plexHome,}){
final _that = this;
switch (_that) {
case LocalProfile():
return local(_that);case PlexHomeProfile():
return plexHome(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( LocalProfile value)? local,TResult? Function( PlexHomeProfile value)? plexHome,}){
final _that = this;
switch (_that) {
case LocalProfile() when local != null:
return local(_that);case PlexHomeProfile() when plexHome != null:
return plexHome(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function( String id, String displayName, String? avatarThumbUrl, String? pinHash, int sortOrder, DateTime createdAt, DateTime? lastUsedAt)? local,TResult Function( String id, String displayName, String? avatarThumbUrl, String? parentConnectionId, String? plexHomeUserUuid, bool plexRestricted, bool plexAdmin, bool plexProtected, int sortOrder, DateTime createdAt, DateTime? lastUsedAt)? plexHome,required TResult orElse(),}) {final _that = this;
switch (_that) {
case LocalProfile() when local != null:
return local(_that.id,_that.displayName,_that.avatarThumbUrl,_that.pinHash,_that.sortOrder,_that.createdAt,_that.lastUsedAt);case PlexHomeProfile() when plexHome != null:
return plexHome(_that.id,_that.displayName,_that.avatarThumbUrl,_that.parentConnectionId,_that.plexHomeUserUuid,_that.plexRestricted,_that.plexAdmin,_that.plexProtected,_that.sortOrder,_that.createdAt,_that.lastUsedAt);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function( String id, String displayName, String? avatarThumbUrl, String? pinHash, int sortOrder, DateTime createdAt, DateTime? lastUsedAt) local,required TResult Function( String id, String displayName, String? avatarThumbUrl, String? parentConnectionId, String? plexHomeUserUuid, bool plexRestricted, bool plexAdmin, bool plexProtected, int sortOrder, DateTime createdAt, DateTime? lastUsedAt) plexHome,}) {final _that = this;
switch (_that) {
case LocalProfile():
return local(_that.id,_that.displayName,_that.avatarThumbUrl,_that.pinHash,_that.sortOrder,_that.createdAt,_that.lastUsedAt);case PlexHomeProfile():
return plexHome(_that.id,_that.displayName,_that.avatarThumbUrl,_that.parentConnectionId,_that.plexHomeUserUuid,_that.plexRestricted,_that.plexAdmin,_that.plexProtected,_that.sortOrder,_that.createdAt,_that.lastUsedAt);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function( String id, String displayName, String? avatarThumbUrl, String? pinHash, int sortOrder, DateTime createdAt, DateTime? lastUsedAt)? local,TResult? Function( String id, String displayName, String? avatarThumbUrl, String? parentConnectionId, String? plexHomeUserUuid, bool plexRestricted, bool plexAdmin, bool plexProtected, int sortOrder, DateTime createdAt, DateTime? lastUsedAt)? plexHome,}) {final _that = this;
switch (_that) {
case LocalProfile() when local != null:
return local(_that.id,_that.displayName,_that.avatarThumbUrl,_that.pinHash,_that.sortOrder,_that.createdAt,_that.lastUsedAt);case PlexHomeProfile() when plexHome != null:
return plexHome(_that.id,_that.displayName,_that.avatarThumbUrl,_that.parentConnectionId,_that.plexHomeUserUuid,_that.plexRestricted,_that.plexAdmin,_that.plexProtected,_that.sortOrder,_that.createdAt,_that.lastUsedAt);case _:
return null;
}
}
}
/// @nodoc
class LocalProfile extends Profile {
const LocalProfile({required this.id, required this.displayName, this.avatarThumbUrl, this.pinHash, this.sortOrder = 0, required this.createdAt, this.lastUsedAt}): super._();
@override final String id;
@override final String displayName;
@override final String? avatarThumbUrl;
/// Hashed PIN if set. The raw PIN is never persisted; see [computePinHash].
final String? pinHash;
@override@JsonKey() final int sortOrder;
@override final DateTime createdAt;
@override final DateTime? lastUsedAt;
/// Create a copy of Profile
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$LocalProfileCopyWith<LocalProfile> get copyWith => _$LocalProfileCopyWithImpl<LocalProfile>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is LocalProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.displayName, displayName) || other.displayName == displayName)&&(identical(other.avatarThumbUrl, avatarThumbUrl) || other.avatarThumbUrl == avatarThumbUrl)&&(identical(other.pinHash, pinHash) || other.pinHash == pinHash)&&(identical(other.sortOrder, sortOrder) || other.sortOrder == sortOrder)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.lastUsedAt, lastUsedAt) || other.lastUsedAt == lastUsedAt));
}
@override
int get hashCode => Object.hash(runtimeType,id,displayName,avatarThumbUrl,pinHash,sortOrder,createdAt,lastUsedAt);
@override
String toString() {
return 'Profile.local(id: $id, displayName: $displayName, avatarThumbUrl: $avatarThumbUrl, pinHash: $pinHash, sortOrder: $sortOrder, createdAt: $createdAt, lastUsedAt: $lastUsedAt)';
}
}
/// @nodoc
abstract mixin class $LocalProfileCopyWith<$Res> implements $ProfileCopyWith<$Res> {
factory $LocalProfileCopyWith(LocalProfile value, $Res Function(LocalProfile) _then) = _$LocalProfileCopyWithImpl;
@override @useResult
$Res call({
String id, String displayName, String? avatarThumbUrl, String? pinHash, int sortOrder, DateTime createdAt, DateTime? lastUsedAt
});
}
/// @nodoc
class _$LocalProfileCopyWithImpl<$Res>
implements $LocalProfileCopyWith<$Res> {
_$LocalProfileCopyWithImpl(this._self, this._then);
final LocalProfile _self;
final $Res Function(LocalProfile) _then;
/// Create a copy of Profile
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? displayName = null,Object? avatarThumbUrl = freezed,Object? pinHash = freezed,Object? sortOrder = null,Object? createdAt = null,Object? lastUsedAt = freezed,}) {
return _then(LocalProfile(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,displayName: null == displayName ? _self.displayName : displayName // ignore: cast_nullable_to_non_nullable
as String,avatarThumbUrl: freezed == avatarThumbUrl ? _self.avatarThumbUrl : avatarThumbUrl // ignore: cast_nullable_to_non_nullable
as String?,pinHash: freezed == pinHash ? _self.pinHash : pinHash // ignore: cast_nullable_to_non_nullable
as String?,sortOrder: null == sortOrder ? _self.sortOrder : sortOrder // ignore: cast_nullable_to_non_nullable
as int,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,lastUsedAt: freezed == lastUsedAt ? _self.lastUsedAt : lastUsedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
}
/// @nodoc
class PlexHomeProfile extends Profile {
const PlexHomeProfile({required this.id, required this.displayName, this.avatarThumbUrl, this.parentConnectionId, this.plexHomeUserUuid, this.plexRestricted = false, this.plexAdmin = false, this.plexProtected = false, this.sortOrder = 0, required this.createdAt, this.lastUsedAt}): super._();
@override final String id;
@override final String displayName;
@override final String? avatarThumbUrl;
/// The parent Plex account's connection id.
final String? parentConnectionId;
/// The Plex Home user UUID. Used by the active-profile binder to call
/// `/home/users/{uuid}/switch`.
final String? plexHomeUserUuid;
@JsonKey() final bool plexRestricted;
@JsonKey() final bool plexAdmin;
/// Plex's `protected` flag — true when the home user has a PIN that must
/// be entered before `/home/users/{uuid}/switch` will succeed.
@JsonKey() final bool plexProtected;
@override@JsonKey() final int sortOrder;
@override final DateTime createdAt;
@override final DateTime? lastUsedAt;
/// Create a copy of Profile
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PlexHomeProfileCopyWith<PlexHomeProfile> get copyWith => _$PlexHomeProfileCopyWithImpl<PlexHomeProfile>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PlexHomeProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.displayName, displayName) || other.displayName == displayName)&&(identical(other.avatarThumbUrl, avatarThumbUrl) || other.avatarThumbUrl == avatarThumbUrl)&&(identical(other.parentConnectionId, parentConnectionId) || other.parentConnectionId == parentConnectionId)&&(identical(other.plexHomeUserUuid, plexHomeUserUuid) || other.plexHomeUserUuid == plexHomeUserUuid)&&(identical(other.plexRestricted, plexRestricted) || other.plexRestricted == plexRestricted)&&(identical(other.plexAdmin, plexAdmin) || other.plexAdmin == plexAdmin)&&(identical(other.plexProtected, plexProtected) || other.plexProtected == plexProtected)&&(identical(other.sortOrder, sortOrder) || other.sortOrder == sortOrder)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.lastUsedAt, lastUsedAt) || other.lastUsedAt == lastUsedAt));
}
@override
int get hashCode => Object.hash(runtimeType,id,displayName,avatarThumbUrl,parentConnectionId,plexHomeUserUuid,plexRestricted,plexAdmin,plexProtected,sortOrder,createdAt,lastUsedAt);
@override
String toString() {
return 'Profile.plexHome(id: $id, displayName: $displayName, avatarThumbUrl: $avatarThumbUrl, parentConnectionId: $parentConnectionId, plexHomeUserUuid: $plexHomeUserUuid, plexRestricted: $plexRestricted, plexAdmin: $plexAdmin, plexProtected: $plexProtected, sortOrder: $sortOrder, createdAt: $createdAt, lastUsedAt: $lastUsedAt)';
}
}
/// @nodoc
abstract mixin class $PlexHomeProfileCopyWith<$Res> implements $ProfileCopyWith<$Res> {
factory $PlexHomeProfileCopyWith(PlexHomeProfile value, $Res Function(PlexHomeProfile) _then) = _$PlexHomeProfileCopyWithImpl;
@override @useResult
$Res call({
String id, String displayName, String? avatarThumbUrl, String? parentConnectionId, String? plexHomeUserUuid, bool plexRestricted, bool plexAdmin, bool plexProtected, int sortOrder, DateTime createdAt, DateTime? lastUsedAt
});
}
/// @nodoc
class _$PlexHomeProfileCopyWithImpl<$Res>
implements $PlexHomeProfileCopyWith<$Res> {
_$PlexHomeProfileCopyWithImpl(this._self, this._then);
final PlexHomeProfile _self;
final $Res Function(PlexHomeProfile) _then;
/// Create a copy of Profile
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? displayName = null,Object? avatarThumbUrl = freezed,Object? parentConnectionId = freezed,Object? plexHomeUserUuid = freezed,Object? plexRestricted = null,Object? plexAdmin = null,Object? plexProtected = null,Object? sortOrder = null,Object? createdAt = null,Object? lastUsedAt = freezed,}) {
return _then(PlexHomeProfile(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,displayName: null == displayName ? _self.displayName : displayName // ignore: cast_nullable_to_non_nullable
as String,avatarThumbUrl: freezed == avatarThumbUrl ? _self.avatarThumbUrl : avatarThumbUrl // ignore: cast_nullable_to_non_nullable
as String?,parentConnectionId: freezed == parentConnectionId ? _self.parentConnectionId : parentConnectionId // ignore: cast_nullable_to_non_nullable
as String?,plexHomeUserUuid: freezed == plexHomeUserUuid ? _self.plexHomeUserUuid : plexHomeUserUuid // ignore: cast_nullable_to_non_nullable
as String?,plexRestricted: null == plexRestricted ? _self.plexRestricted : plexRestricted // ignore: cast_nullable_to_non_nullable
as bool,plexAdmin: null == plexAdmin ? _self.plexAdmin : plexAdmin // ignore: cast_nullable_to_non_nullable
as bool,plexProtected: null == plexProtected ? _self.plexProtected : plexProtected // ignore: cast_nullable_to_non_nullable
as bool,sortOrder: null == sortOrder ? _self.sortOrder : sortOrder // ignore: cast_nullable_to_non_nullable
as int,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,lastUsedAt: freezed == lastUsedAt ? _self.lastUsedAt : lastUsedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
}
// dart format on
+16 -40
View File
@@ -1,3 +1,7 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'profile_connection.freezed.dart';
/// A binding between a [Profile] and a [Connection], carrying the
/// per-profile user-level token used when the profile is active.
///
@@ -9,47 +13,19 @@
///
/// For Jellyfin: [userToken] mirrors the Connection's accessToken (one
/// user per connection); [userIdentifier] is the Jellyfin user id.
class ProfileConnection {
final String profileId;
final String connectionId;
final String? userToken;
final String userIdentifier;
final bool isDefault;
final DateTime? tokenAcquiredAt;
final DateTime? lastUsedAt;
@freezed
sealed class ProfileConnection with _$ProfileConnection {
const ProfileConnection._();
const ProfileConnection({
required this.profileId,
required this.connectionId,
this.userToken,
required this.userIdentifier,
this.isDefault = false,
this.tokenAcquiredAt,
this.lastUsedAt,
});
const factory ProfileConnection({
required String profileId,
required String connectionId,
String? userToken,
required String userIdentifier,
@Default(false) bool isDefault,
DateTime? tokenAcquiredAt,
DateTime? lastUsedAt,
}) = _ProfileConnection;
bool get hasToken => userToken != null && userToken!.isNotEmpty;
ProfileConnection copyWith({
String? profileId,
String? connectionId,
String? userToken,
bool clearUserToken = false,
String? userIdentifier,
bool? isDefault,
DateTime? tokenAcquiredAt,
bool clearTokenAcquiredAt = false,
DateTime? lastUsedAt,
bool clearLastUsedAt = false,
}) {
return ProfileConnection(
profileId: profileId ?? this.profileId,
connectionId: connectionId ?? this.connectionId,
userToken: clearUserToken ? null : (userToken ?? this.userToken),
userIdentifier: userIdentifier ?? this.userIdentifier,
isDefault: isDefault ?? this.isDefault,
tokenAcquiredAt: clearTokenAcquiredAt ? null : (tokenAcquiredAt ?? this.tokenAcquiredAt),
lastUsedAt: clearLastUsedAt ? null : (lastUsedAt ?? this.lastUsedAt),
);
}
}
@@ -0,0 +1,283 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'profile_connection.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ProfileConnection {
String get profileId; String get connectionId; String? get userToken; String get userIdentifier; bool get isDefault; DateTime? get tokenAcquiredAt; DateTime? get lastUsedAt;
/// Create a copy of ProfileConnection
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ProfileConnectionCopyWith<ProfileConnection> get copyWith => _$ProfileConnectionCopyWithImpl<ProfileConnection>(this as ProfileConnection, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ProfileConnection&&(identical(other.profileId, profileId) || other.profileId == profileId)&&(identical(other.connectionId, connectionId) || other.connectionId == connectionId)&&(identical(other.userToken, userToken) || other.userToken == userToken)&&(identical(other.userIdentifier, userIdentifier) || other.userIdentifier == userIdentifier)&&(identical(other.isDefault, isDefault) || other.isDefault == isDefault)&&(identical(other.tokenAcquiredAt, tokenAcquiredAt) || other.tokenAcquiredAt == tokenAcquiredAt)&&(identical(other.lastUsedAt, lastUsedAt) || other.lastUsedAt == lastUsedAt));
}
@override
int get hashCode => Object.hash(runtimeType,profileId,connectionId,userToken,userIdentifier,isDefault,tokenAcquiredAt,lastUsedAt);
@override
String toString() {
return 'ProfileConnection(profileId: $profileId, connectionId: $connectionId, userToken: $userToken, userIdentifier: $userIdentifier, isDefault: $isDefault, tokenAcquiredAt: $tokenAcquiredAt, lastUsedAt: $lastUsedAt)';
}
}
/// @nodoc
abstract mixin class $ProfileConnectionCopyWith<$Res> {
factory $ProfileConnectionCopyWith(ProfileConnection value, $Res Function(ProfileConnection) _then) = _$ProfileConnectionCopyWithImpl;
@useResult
$Res call({
String profileId, String connectionId, String? userToken, String userIdentifier, bool isDefault, DateTime? tokenAcquiredAt, DateTime? lastUsedAt
});
}
/// @nodoc
class _$ProfileConnectionCopyWithImpl<$Res>
implements $ProfileConnectionCopyWith<$Res> {
_$ProfileConnectionCopyWithImpl(this._self, this._then);
final ProfileConnection _self;
final $Res Function(ProfileConnection) _then;
/// Create a copy of ProfileConnection
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? profileId = null,Object? connectionId = null,Object? userToken = freezed,Object? userIdentifier = null,Object? isDefault = null,Object? tokenAcquiredAt = freezed,Object? lastUsedAt = freezed,}) {
return _then(_self.copyWith(
profileId: null == profileId ? _self.profileId : profileId // ignore: cast_nullable_to_non_nullable
as String,connectionId: null == connectionId ? _self.connectionId : connectionId // ignore: cast_nullable_to_non_nullable
as String,userToken: freezed == userToken ? _self.userToken : userToken // ignore: cast_nullable_to_non_nullable
as String?,userIdentifier: null == userIdentifier ? _self.userIdentifier : userIdentifier // ignore: cast_nullable_to_non_nullable
as String,isDefault: null == isDefault ? _self.isDefault : isDefault // ignore: cast_nullable_to_non_nullable
as bool,tokenAcquiredAt: freezed == tokenAcquiredAt ? _self.tokenAcquiredAt : tokenAcquiredAt // ignore: cast_nullable_to_non_nullable
as DateTime?,lastUsedAt: freezed == lastUsedAt ? _self.lastUsedAt : lastUsedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
}
/// Adds pattern-matching-related methods to [ProfileConnection].
extension ProfileConnectionPatterns on ProfileConnection {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProfileConnection value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ProfileConnection() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProfileConnection value) $default,){
final _that = this;
switch (_that) {
case _ProfileConnection():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProfileConnection value)? $default,){
final _that = this;
switch (_that) {
case _ProfileConnection() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String profileId, String connectionId, String? userToken, String userIdentifier, bool isDefault, DateTime? tokenAcquiredAt, DateTime? lastUsedAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ProfileConnection() when $default != null:
return $default(_that.profileId,_that.connectionId,_that.userToken,_that.userIdentifier,_that.isDefault,_that.tokenAcquiredAt,_that.lastUsedAt);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String profileId, String connectionId, String? userToken, String userIdentifier, bool isDefault, DateTime? tokenAcquiredAt, DateTime? lastUsedAt) $default,) {final _that = this;
switch (_that) {
case _ProfileConnection():
return $default(_that.profileId,_that.connectionId,_that.userToken,_that.userIdentifier,_that.isDefault,_that.tokenAcquiredAt,_that.lastUsedAt);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String profileId, String connectionId, String? userToken, String userIdentifier, bool isDefault, DateTime? tokenAcquiredAt, DateTime? lastUsedAt)? $default,) {final _that = this;
switch (_that) {
case _ProfileConnection() when $default != null:
return $default(_that.profileId,_that.connectionId,_that.userToken,_that.userIdentifier,_that.isDefault,_that.tokenAcquiredAt,_that.lastUsedAt);case _:
return null;
}
}
}
/// @nodoc
class _ProfileConnection extends ProfileConnection {
const _ProfileConnection({required this.profileId, required this.connectionId, this.userToken, required this.userIdentifier, this.isDefault = false, this.tokenAcquiredAt, this.lastUsedAt}): super._();
@override final String profileId;
@override final String connectionId;
@override final String? userToken;
@override final String userIdentifier;
@override@JsonKey() final bool isDefault;
@override final DateTime? tokenAcquiredAt;
@override final DateTime? lastUsedAt;
/// Create a copy of ProfileConnection
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ProfileConnectionCopyWith<_ProfileConnection> get copyWith => __$ProfileConnectionCopyWithImpl<_ProfileConnection>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProfileConnection&&(identical(other.profileId, profileId) || other.profileId == profileId)&&(identical(other.connectionId, connectionId) || other.connectionId == connectionId)&&(identical(other.userToken, userToken) || other.userToken == userToken)&&(identical(other.userIdentifier, userIdentifier) || other.userIdentifier == userIdentifier)&&(identical(other.isDefault, isDefault) || other.isDefault == isDefault)&&(identical(other.tokenAcquiredAt, tokenAcquiredAt) || other.tokenAcquiredAt == tokenAcquiredAt)&&(identical(other.lastUsedAt, lastUsedAt) || other.lastUsedAt == lastUsedAt));
}
@override
int get hashCode => Object.hash(runtimeType,profileId,connectionId,userToken,userIdentifier,isDefault,tokenAcquiredAt,lastUsedAt);
@override
String toString() {
return 'ProfileConnection(profileId: $profileId, connectionId: $connectionId, userToken: $userToken, userIdentifier: $userIdentifier, isDefault: $isDefault, tokenAcquiredAt: $tokenAcquiredAt, lastUsedAt: $lastUsedAt)';
}
}
/// @nodoc
abstract mixin class _$ProfileConnectionCopyWith<$Res> implements $ProfileConnectionCopyWith<$Res> {
factory _$ProfileConnectionCopyWith(_ProfileConnection value, $Res Function(_ProfileConnection) _then) = __$ProfileConnectionCopyWithImpl;
@override @useResult
$Res call({
String profileId, String connectionId, String? userToken, String userIdentifier, bool isDefault, DateTime? tokenAcquiredAt, DateTime? lastUsedAt
});
}
/// @nodoc
class __$ProfileConnectionCopyWithImpl<$Res>
implements _$ProfileConnectionCopyWith<$Res> {
__$ProfileConnectionCopyWithImpl(this._self, this._then);
final _ProfileConnection _self;
final $Res Function(_ProfileConnection) _then;
/// Create a copy of ProfileConnection
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? profileId = null,Object? connectionId = null,Object? userToken = freezed,Object? userIdentifier = null,Object? isDefault = null,Object? tokenAcquiredAt = freezed,Object? lastUsedAt = freezed,}) {
return _then(_ProfileConnection(
profileId: null == profileId ? _self.profileId : profileId // ignore: cast_nullable_to_non_nullable
as String,connectionId: null == connectionId ? _self.connectionId : connectionId // ignore: cast_nullable_to_non_nullable
as String,userToken: freezed == userToken ? _self.userToken : userToken // ignore: cast_nullable_to_non_nullable
as String?,userIdentifier: null == userIdentifier ? _self.userIdentifier : userIdentifier // ignore: cast_nullable_to_non_nullable
as String,isDefault: null == isDefault ? _self.isDefault : isDefault // ignore: cast_nullable_to_non_nullable
as bool,tokenAcquiredAt: freezed == tokenAcquiredAt ? _self.tokenAcquiredAt : tokenAcquiredAt // ignore: cast_nullable_to_non_nullable
as DateTime?,lastUsedAt: freezed == lastUsedAt ? _self.lastUsedAt : lastUsedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,
));
}
}
// dart format on
+22 -9
View File
@@ -459,7 +459,11 @@ class CompanionRemoteProvider with ChangeNotifier, DisposableChangeNotifierMixin
final contexts = List<RemoteAuthContext>.unmodifiable(_authContexts);
final result = await _peerService!.createSessionForContexts(_deviceName, _platform, contexts);
_session = RemoteSession(role: RemoteSessionRole.host, status: RemoteSessionStatus.connected);
_session = RemoteSession(
role: RemoteSessionRole.host,
status: RemoteSessionStatus.connected,
createdAt: DateTime.now(),
);
safeNotifyListeners();
// Start LAN discovery broadcasting
@@ -480,6 +484,7 @@ class CompanionRemoteProvider with ChangeNotifier, DisposableChangeNotifierMixin
role: RemoteSessionRole.host,
status: RemoteSessionStatus.error,
errorMessage: e.toString(),
createdAt: DateTime.now(),
);
safeNotifyListeners();
}
@@ -538,7 +543,11 @@ class CompanionRemoteProvider with ChangeNotifier, DisposableChangeNotifierMixin
_peerService = CompanionRemotePeerService();
_setupPeerServiceListeners();
_session = RemoteSession(role: RemoteSessionRole.remote, status: RemoteSessionStatus.connecting);
_session = RemoteSession(
role: RemoteSessionRole.remote,
status: RemoteSessionStatus.connecting,
createdAt: DateTime.now(),
);
safeNotifyListeners();
try {
@@ -582,7 +591,11 @@ class CompanionRemoteProvider with ChangeNotifier, DisposableChangeNotifierMixin
_peerService = CompanionRemotePeerService();
_setupPeerServiceListeners();
_session = RemoteSession(role: RemoteSessionRole.remote, status: RemoteSessionStatus.connecting);
_session = RemoteSession(
role: RemoteSessionRole.remote,
status: RemoteSessionStatus.connecting,
createdAt: DateTime.now(),
);
safeNotifyListeners();
try {
@@ -629,13 +642,13 @@ class CompanionRemoteProvider with ChangeNotifier, DisposableChangeNotifierMixin
_deviceDisconnectedSubscription = _peerService!.onDeviceDisconnected.listen((_) {
appLogger.d('CompanionRemote: Device disconnected (intentional: $_intentionalDisconnect)');
if (_intentionalDisconnect) {
_session = _session?.copyWith(status: RemoteSessionStatus.disconnected, clearConnectedDevice: true);
_session = _session?.copyWith(status: RemoteSessionStatus.disconnected, connectedDevice: null);
safeNotifyListeners();
} else if (isHost) {
_session = _session?.copyWith(
status: RemoteSessionStatus.reconnecting,
clearConnectedDevice: true,
clearErrorMessage: true,
connectedDevice: null,
errorMessage: null,
);
safeNotifyListeners();
appLogger.d('CompanionRemote: Host waiting for client to reconnect');
@@ -668,7 +681,7 @@ class CompanionRemoteProvider with ChangeNotifier, DisposableChangeNotifierMixin
appLogger.d('CompanionRemote: Device info - name: $name, platform: $platform, role: $role');
final device = RemoteDevice(id: id, name: name, platform: platform);
final device = RemoteDevice(id: id, name: name, platform: platform, connectedAt: DateTime.now());
_session = _session?.copyWith(connectedDevice: device);
safeNotifyListeners();
@@ -756,7 +769,7 @@ class CompanionRemoteProvider with ChangeNotifier, DisposableChangeNotifierMixin
_lastAuthContextId = _peerService!.selectedAuthContextId ?? authContextId;
_lastHostClientId = _peerService!.selectedHostClientId ?? _lastHostClientId;
_session = _session?.copyWith(status: RemoteSessionStatus.connected, clearErrorMessage: true);
_session = _session?.copyWith(status: RemoteSessionStatus.connected, errorMessage: null);
_reconnectAttempts = 0;
safeNotifyListeners();
appLogger.d('CompanionRemote: Reconnected successfully');
@@ -777,7 +790,7 @@ class CompanionRemoteProvider with ChangeNotifier, DisposableChangeNotifierMixin
void cancelReconnect() {
_reconnectTimer?.cancel();
_reconnectAttempts = 0;
_session = _session?.copyWith(status: RemoteSessionStatus.disconnected, clearConnectedDevice: true);
_session = _session?.copyWith(status: RemoteSessionStatus.disconnected, connectedDevice: null);
safeNotifyListeners();
}
@@ -64,9 +64,8 @@ class _AddLocalProfileScreenState extends State<AddLocalProfileScreen> with Cont
setState(() => _saving = true);
final registry = context.read<ProfileRegistry>();
final profile = Profile(
final profile = Profile.local(
id: 'local-${const Uuid().v4()}',
kind: ProfileKind.local,
displayName: name,
pinHash: _pinHash,
sortOrder: DateTime.now().millisecondsSinceEpoch,
@@ -87,14 +87,18 @@ class _ProfileDetailScreenState extends State<ProfileDetailScreen> with Controll
onMismatch: (ctx) => showErrorSnackBar(ctx, t.profiles.pinsDontMatch),
);
if (pin == null || !mounted) return;
final updated = _profile.copyWith(pinHash: computePinHash(pin));
final profile = _profile;
if (profile is! LocalProfile) return;
final updated = profile.copyWith(pinHash: computePinHash(pin));
await context.read<ProfileRegistry>().upsert(updated);
if (!mounted) return;
setState(() => _profile = updated);
}
Future<void> _clearPin() async {
final updated = _profile.copyWith(clearPin: true);
final profile = _profile;
if (profile is! LocalProfile) return;
final updated = profile.copyWith(pinHash: null);
await context.read<ProfileRegistry>().upsert(updated);
if (!mounted) return;
setState(() => _profile = updated);
@@ -264,9 +264,8 @@ class _AddJellyfinScreenState extends State<AddJellyfinScreen> with AsyncFormSta
hasProfiles: activeProvider.profiles.isNotEmpty,
)) {
final now = DateTime.now();
final profile = Profile(
final profile = Profile.local(
id: 'local-${const Uuid().v4()}',
kind: ProfileKind.local,
displayName: connection.userName.isNotEmpty ? connection.userName : connection.serverName,
sortOrder: now.millisecondsSinceEpoch,
createdAt: now,
@@ -343,7 +343,12 @@ class CompanionRemotePeerService with KeepaliveMixin {
await _sendEncryptedToSocket(socket, jsonEncode({'type': 'authSuccess'}));
// Notify connection
final device = RemoteDevice(id: 'remote-client', name: deviceName, platform: platform);
final device = RemoteDevice(
id: 'remote-client',
name: deviceName,
platform: platform,
connectedAt: DateTime.now(),
);
_deviceConnectedController.add(device);
_connectionStateController.add(RemoteSessionStatus.connected);
@@ -516,7 +521,12 @@ class CompanionRemotePeerService with KeepaliveMixin {
completer.complete();
}
final device = RemoteDevice(id: 'host', name: 'Desktop', platform: 'desktop');
final device = RemoteDevice(
id: 'host',
name: 'Desktop',
platform: 'desktop',
connectedAt: DateTime.now(),
);
_deviceConnectedController.add(device);
_connectionStateController.add(RemoteSessionStatus.connected);
+21
View File
@@ -0,0 +1,21 @@
import 'package:json_annotation/json_annotation.dart';
/// Maps an enum value to/from its `int` index. Useful for compact wire formats
/// (e.g. companion-remote commands) where the over-the-wire size matters and
/// new enum cases are always appended.
///
/// Out-of-range indices on the wire fall back to [_fallback] instead of
/// throwing — important for forward-compat with newer clients sending
/// commands the host doesn't yet understand.
class IndexedEnumConverter<T extends Enum> implements JsonConverter<T, int> {
const IndexedEnumConverter(this._values, this._fallback);
final List<T> _values;
final T _fallback;
@override
T fromJson(int json) => json >= 0 && json < _values.length ? _values[json] : _fallback;
@override
int toJson(T object) => object.index;
}
+42 -101
View File
@@ -1,98 +1,43 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'watch_session.freezed.dart';
enum SessionRole { host, guest }
enum ControlMode { hostOnly, anyone }
enum SessionState { disconnected, connecting, connected, error }
class Participant {
final String peerId;
final String displayName;
final bool isHost;
final Duration lastKnownPosition;
final bool isBuffering;
const Participant({
required this.peerId,
required this.displayName,
required this.isHost,
this.lastKnownPosition = Duration.zero,
this.isBuffering = false,
});
Participant copyWith({
String? peerId,
String? displayName,
bool? isHost,
Duration? lastKnownPosition,
bool? isBuffering,
}) {
return Participant(
peerId: peerId ?? this.peerId,
displayName: displayName ?? this.displayName,
isHost: isHost ?? this.isHost,
lastKnownPosition: lastKnownPosition ?? this.lastKnownPosition,
isBuffering: isBuffering ?? this.isBuffering,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) || other is Participant && runtimeType == other.runtimeType && peerId == other.peerId;
@override
int get hashCode => peerId.hashCode;
@freezed
sealed class Participant with _$Participant {
const factory Participant({
required String peerId,
required String displayName,
required bool isHost,
@Default(Duration.zero) Duration lastKnownPosition,
@Default(false) bool isBuffering,
}) = _Participant;
}
class WatchSession {
final String sessionId;
final SessionRole role;
final ControlMode controlMode;
final SessionState state;
final String? errorMessage;
final String? mediaRatingKey;
final String? mediaServerId;
final String? mediaTitle;
final String? hostPeerId;
@freezed
sealed class WatchSession with _$WatchSession {
const WatchSession._();
const WatchSession({
required this.sessionId,
required this.role,
required this.controlMode,
required this.state,
this.errorMessage,
this.mediaRatingKey,
this.mediaServerId,
this.mediaTitle,
this.hostPeerId,
});
bool get isHost => role == SessionRole.host;
bool get isConnected => state == SessionState.connected;
WatchSession copyWith({
String? sessionId,
SessionRole? role,
ControlMode? controlMode,
SessionState? state,
const factory WatchSession({
required String sessionId,
required SessionRole role,
required ControlMode controlMode,
required SessionState state,
String? errorMessage,
String? mediaRatingKey,
String? mediaServerId,
String? mediaTitle,
String? hostPeerId,
}) {
return WatchSession(
sessionId: sessionId ?? this.sessionId,
role: role ?? this.role,
controlMode: controlMode ?? this.controlMode,
state: state ?? this.state,
errorMessage: errorMessage ?? this.errorMessage,
mediaRatingKey: mediaRatingKey ?? this.mediaRatingKey,
mediaServerId: mediaServerId ?? this.mediaServerId,
mediaTitle: mediaTitle ?? this.mediaTitle,
hostPeerId: hostPeerId ?? this.hostPeerId,
);
}
}) = _WatchSession;
bool get isHost => role == SessionRole.host;
bool get isConnected => state == SessionState.connected;
/// Create a new session as host
factory WatchSession.createAsHost({
@@ -102,26 +47,22 @@ class WatchSession {
String? mediaRatingKey,
String? mediaServerId,
String? mediaTitle,
}) {
return WatchSession(
sessionId: sessionId,
role: SessionRole.host,
controlMode: controlMode,
state: SessionState.connecting,
hostPeerId: hostPeerId,
mediaRatingKey: mediaRatingKey,
mediaServerId: mediaServerId,
mediaTitle: mediaTitle,
);
}
}) => WatchSession(
sessionId: sessionId,
role: SessionRole.host,
controlMode: controlMode,
state: SessionState.connecting,
hostPeerId: hostPeerId,
mediaRatingKey: mediaRatingKey,
mediaServerId: mediaServerId,
mediaTitle: mediaTitle,
);
/// Create a session as guest (joining)
factory WatchSession.joinAsGuest({required String sessionId}) {
return WatchSession(
sessionId: sessionId,
role: SessionRole.guest,
controlMode: ControlMode.hostOnly, // Will be updated when connected
state: SessionState.connecting,
);
}
factory WatchSession.joinAsGuest({required String sessionId}) => WatchSession(
sessionId: sessionId,
role: SessionRole.guest,
controlMode: ControlMode.hostOnly, // Will be updated when connected
state: SessionState.connecting,
);
}
@@ -0,0 +1,552 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'watch_session.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$Participant {
String get peerId; String get displayName; bool get isHost; Duration get lastKnownPosition; bool get isBuffering;
/// Create a copy of Participant
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ParticipantCopyWith<Participant> get copyWith => _$ParticipantCopyWithImpl<Participant>(this as Participant, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is Participant&&(identical(other.peerId, peerId) || other.peerId == peerId)&&(identical(other.displayName, displayName) || other.displayName == displayName)&&(identical(other.isHost, isHost) || other.isHost == isHost)&&(identical(other.lastKnownPosition, lastKnownPosition) || other.lastKnownPosition == lastKnownPosition)&&(identical(other.isBuffering, isBuffering) || other.isBuffering == isBuffering));
}
@override
int get hashCode => Object.hash(runtimeType,peerId,displayName,isHost,lastKnownPosition,isBuffering);
@override
String toString() {
return 'Participant(peerId: $peerId, displayName: $displayName, isHost: $isHost, lastKnownPosition: $lastKnownPosition, isBuffering: $isBuffering)';
}
}
/// @nodoc
abstract mixin class $ParticipantCopyWith<$Res> {
factory $ParticipantCopyWith(Participant value, $Res Function(Participant) _then) = _$ParticipantCopyWithImpl;
@useResult
$Res call({
String peerId, String displayName, bool isHost, Duration lastKnownPosition, bool isBuffering
});
}
/// @nodoc
class _$ParticipantCopyWithImpl<$Res>
implements $ParticipantCopyWith<$Res> {
_$ParticipantCopyWithImpl(this._self, this._then);
final Participant _self;
final $Res Function(Participant) _then;
/// Create a copy of Participant
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? peerId = null,Object? displayName = null,Object? isHost = null,Object? lastKnownPosition = null,Object? isBuffering = null,}) {
return _then(_self.copyWith(
peerId: null == peerId ? _self.peerId : peerId // ignore: cast_nullable_to_non_nullable
as String,displayName: null == displayName ? _self.displayName : displayName // ignore: cast_nullable_to_non_nullable
as String,isHost: null == isHost ? _self.isHost : isHost // ignore: cast_nullable_to_non_nullable
as bool,lastKnownPosition: null == lastKnownPosition ? _self.lastKnownPosition : lastKnownPosition // ignore: cast_nullable_to_non_nullable
as Duration,isBuffering: null == isBuffering ? _self.isBuffering : isBuffering // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// Adds pattern-matching-related methods to [Participant].
extension ParticipantPatterns on Participant {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Participant value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _Participant() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Participant value) $default,){
final _that = this;
switch (_that) {
case _Participant():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Participant value)? $default,){
final _that = this;
switch (_that) {
case _Participant() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String peerId, String displayName, bool isHost, Duration lastKnownPosition, bool isBuffering)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _Participant() when $default != null:
return $default(_that.peerId,_that.displayName,_that.isHost,_that.lastKnownPosition,_that.isBuffering);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String peerId, String displayName, bool isHost, Duration lastKnownPosition, bool isBuffering) $default,) {final _that = this;
switch (_that) {
case _Participant():
return $default(_that.peerId,_that.displayName,_that.isHost,_that.lastKnownPosition,_that.isBuffering);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String peerId, String displayName, bool isHost, Duration lastKnownPosition, bool isBuffering)? $default,) {final _that = this;
switch (_that) {
case _Participant() when $default != null:
return $default(_that.peerId,_that.displayName,_that.isHost,_that.lastKnownPosition,_that.isBuffering);case _:
return null;
}
}
}
/// @nodoc
class _Participant implements Participant {
const _Participant({required this.peerId, required this.displayName, required this.isHost, this.lastKnownPosition = Duration.zero, this.isBuffering = false});
@override final String peerId;
@override final String displayName;
@override final bool isHost;
@override@JsonKey() final Duration lastKnownPosition;
@override@JsonKey() final bool isBuffering;
/// Create a copy of Participant
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ParticipantCopyWith<_Participant> get copyWith => __$ParticipantCopyWithImpl<_Participant>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Participant&&(identical(other.peerId, peerId) || other.peerId == peerId)&&(identical(other.displayName, displayName) || other.displayName == displayName)&&(identical(other.isHost, isHost) || other.isHost == isHost)&&(identical(other.lastKnownPosition, lastKnownPosition) || other.lastKnownPosition == lastKnownPosition)&&(identical(other.isBuffering, isBuffering) || other.isBuffering == isBuffering));
}
@override
int get hashCode => Object.hash(runtimeType,peerId,displayName,isHost,lastKnownPosition,isBuffering);
@override
String toString() {
return 'Participant(peerId: $peerId, displayName: $displayName, isHost: $isHost, lastKnownPosition: $lastKnownPosition, isBuffering: $isBuffering)';
}
}
/// @nodoc
abstract mixin class _$ParticipantCopyWith<$Res> implements $ParticipantCopyWith<$Res> {
factory _$ParticipantCopyWith(_Participant value, $Res Function(_Participant) _then) = __$ParticipantCopyWithImpl;
@override @useResult
$Res call({
String peerId, String displayName, bool isHost, Duration lastKnownPosition, bool isBuffering
});
}
/// @nodoc
class __$ParticipantCopyWithImpl<$Res>
implements _$ParticipantCopyWith<$Res> {
__$ParticipantCopyWithImpl(this._self, this._then);
final _Participant _self;
final $Res Function(_Participant) _then;
/// Create a copy of Participant
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? peerId = null,Object? displayName = null,Object? isHost = null,Object? lastKnownPosition = null,Object? isBuffering = null,}) {
return _then(_Participant(
peerId: null == peerId ? _self.peerId : peerId // ignore: cast_nullable_to_non_nullable
as String,displayName: null == displayName ? _self.displayName : displayName // ignore: cast_nullable_to_non_nullable
as String,isHost: null == isHost ? _self.isHost : isHost // ignore: cast_nullable_to_non_nullable
as bool,lastKnownPosition: null == lastKnownPosition ? _self.lastKnownPosition : lastKnownPosition // ignore: cast_nullable_to_non_nullable
as Duration,isBuffering: null == isBuffering ? _self.isBuffering : isBuffering // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
mixin _$WatchSession {
String get sessionId; SessionRole get role; ControlMode get controlMode; SessionState get state; String? get errorMessage; String? get mediaRatingKey; String? get mediaServerId; String? get mediaTitle; String? get hostPeerId;
/// Create a copy of WatchSession
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$WatchSessionCopyWith<WatchSession> get copyWith => _$WatchSessionCopyWithImpl<WatchSession>(this as WatchSession, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is WatchSession&&(identical(other.sessionId, sessionId) || other.sessionId == sessionId)&&(identical(other.role, role) || other.role == role)&&(identical(other.controlMode, controlMode) || other.controlMode == controlMode)&&(identical(other.state, state) || other.state == state)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.mediaRatingKey, mediaRatingKey) || other.mediaRatingKey == mediaRatingKey)&&(identical(other.mediaServerId, mediaServerId) || other.mediaServerId == mediaServerId)&&(identical(other.mediaTitle, mediaTitle) || other.mediaTitle == mediaTitle)&&(identical(other.hostPeerId, hostPeerId) || other.hostPeerId == hostPeerId));
}
@override
int get hashCode => Object.hash(runtimeType,sessionId,role,controlMode,state,errorMessage,mediaRatingKey,mediaServerId,mediaTitle,hostPeerId);
@override
String toString() {
return 'WatchSession(sessionId: $sessionId, role: $role, controlMode: $controlMode, state: $state, errorMessage: $errorMessage, mediaRatingKey: $mediaRatingKey, mediaServerId: $mediaServerId, mediaTitle: $mediaTitle, hostPeerId: $hostPeerId)';
}
}
/// @nodoc
abstract mixin class $WatchSessionCopyWith<$Res> {
factory $WatchSessionCopyWith(WatchSession value, $Res Function(WatchSession) _then) = _$WatchSessionCopyWithImpl;
@useResult
$Res call({
String sessionId, SessionRole role, ControlMode controlMode, SessionState state, String? errorMessage, String? mediaRatingKey, String? mediaServerId, String? mediaTitle, String? hostPeerId
});
}
/// @nodoc
class _$WatchSessionCopyWithImpl<$Res>
implements $WatchSessionCopyWith<$Res> {
_$WatchSessionCopyWithImpl(this._self, this._then);
final WatchSession _self;
final $Res Function(WatchSession) _then;
/// Create a copy of WatchSession
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? sessionId = null,Object? role = null,Object? controlMode = null,Object? state = null,Object? errorMessage = freezed,Object? mediaRatingKey = freezed,Object? mediaServerId = freezed,Object? mediaTitle = freezed,Object? hostPeerId = freezed,}) {
return _then(_self.copyWith(
sessionId: null == sessionId ? _self.sessionId : sessionId // ignore: cast_nullable_to_non_nullable
as String,role: null == role ? _self.role : role // ignore: cast_nullable_to_non_nullable
as SessionRole,controlMode: null == controlMode ? _self.controlMode : controlMode // ignore: cast_nullable_to_non_nullable
as ControlMode,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as SessionState,errorMessage: freezed == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String?,mediaRatingKey: freezed == mediaRatingKey ? _self.mediaRatingKey : mediaRatingKey // ignore: cast_nullable_to_non_nullable
as String?,mediaServerId: freezed == mediaServerId ? _self.mediaServerId : mediaServerId // ignore: cast_nullable_to_non_nullable
as String?,mediaTitle: freezed == mediaTitle ? _self.mediaTitle : mediaTitle // ignore: cast_nullable_to_non_nullable
as String?,hostPeerId: freezed == hostPeerId ? _self.hostPeerId : hostPeerId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// Adds pattern-matching-related methods to [WatchSession].
extension WatchSessionPatterns on WatchSession {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _WatchSession value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _WatchSession() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _WatchSession value) $default,){
final _that = this;
switch (_that) {
case _WatchSession():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _WatchSession value)? $default,){
final _that = this;
switch (_that) {
case _WatchSession() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String sessionId, SessionRole role, ControlMode controlMode, SessionState state, String? errorMessage, String? mediaRatingKey, String? mediaServerId, String? mediaTitle, String? hostPeerId)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _WatchSession() when $default != null:
return $default(_that.sessionId,_that.role,_that.controlMode,_that.state,_that.errorMessage,_that.mediaRatingKey,_that.mediaServerId,_that.mediaTitle,_that.hostPeerId);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String sessionId, SessionRole role, ControlMode controlMode, SessionState state, String? errorMessage, String? mediaRatingKey, String? mediaServerId, String? mediaTitle, String? hostPeerId) $default,) {final _that = this;
switch (_that) {
case _WatchSession():
return $default(_that.sessionId,_that.role,_that.controlMode,_that.state,_that.errorMessage,_that.mediaRatingKey,_that.mediaServerId,_that.mediaTitle,_that.hostPeerId);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String sessionId, SessionRole role, ControlMode controlMode, SessionState state, String? errorMessage, String? mediaRatingKey, String? mediaServerId, String? mediaTitle, String? hostPeerId)? $default,) {final _that = this;
switch (_that) {
case _WatchSession() when $default != null:
return $default(_that.sessionId,_that.role,_that.controlMode,_that.state,_that.errorMessage,_that.mediaRatingKey,_that.mediaServerId,_that.mediaTitle,_that.hostPeerId);case _:
return null;
}
}
}
/// @nodoc
class _WatchSession extends WatchSession {
const _WatchSession({required this.sessionId, required this.role, required this.controlMode, required this.state, this.errorMessage, this.mediaRatingKey, this.mediaServerId, this.mediaTitle, this.hostPeerId}): super._();
@override final String sessionId;
@override final SessionRole role;
@override final ControlMode controlMode;
@override final SessionState state;
@override final String? errorMessage;
@override final String? mediaRatingKey;
@override final String? mediaServerId;
@override final String? mediaTitle;
@override final String? hostPeerId;
/// Create a copy of WatchSession
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$WatchSessionCopyWith<_WatchSession> get copyWith => __$WatchSessionCopyWithImpl<_WatchSession>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _WatchSession&&(identical(other.sessionId, sessionId) || other.sessionId == sessionId)&&(identical(other.role, role) || other.role == role)&&(identical(other.controlMode, controlMode) || other.controlMode == controlMode)&&(identical(other.state, state) || other.state == state)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.mediaRatingKey, mediaRatingKey) || other.mediaRatingKey == mediaRatingKey)&&(identical(other.mediaServerId, mediaServerId) || other.mediaServerId == mediaServerId)&&(identical(other.mediaTitle, mediaTitle) || other.mediaTitle == mediaTitle)&&(identical(other.hostPeerId, hostPeerId) || other.hostPeerId == hostPeerId));
}
@override
int get hashCode => Object.hash(runtimeType,sessionId,role,controlMode,state,errorMessage,mediaRatingKey,mediaServerId,mediaTitle,hostPeerId);
@override
String toString() {
return 'WatchSession(sessionId: $sessionId, role: $role, controlMode: $controlMode, state: $state, errorMessage: $errorMessage, mediaRatingKey: $mediaRatingKey, mediaServerId: $mediaServerId, mediaTitle: $mediaTitle, hostPeerId: $hostPeerId)';
}
}
/// @nodoc
abstract mixin class _$WatchSessionCopyWith<$Res> implements $WatchSessionCopyWith<$Res> {
factory _$WatchSessionCopyWith(_WatchSession value, $Res Function(_WatchSession) _then) = __$WatchSessionCopyWithImpl;
@override @useResult
$Res call({
String sessionId, SessionRole role, ControlMode controlMode, SessionState state, String? errorMessage, String? mediaRatingKey, String? mediaServerId, String? mediaTitle, String? hostPeerId
});
}
/// @nodoc
class __$WatchSessionCopyWithImpl<$Res>
implements _$WatchSessionCopyWith<$Res> {
__$WatchSessionCopyWithImpl(this._self, this._then);
final _WatchSession _self;
final $Res Function(_WatchSession) _then;
/// Create a copy of WatchSession
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? sessionId = null,Object? role = null,Object? controlMode = null,Object? state = null,Object? errorMessage = freezed,Object? mediaRatingKey = freezed,Object? mediaServerId = freezed,Object? mediaTitle = freezed,Object? hostPeerId = freezed,}) {
return _then(_WatchSession(
sessionId: null == sessionId ? _self.sessionId : sessionId // ignore: cast_nullable_to_non_nullable
as String,role: null == role ? _self.role : role // ignore: cast_nullable_to_non_nullable
as SessionRole,controlMode: null == controlMode ? _self.controlMode : controlMode // ignore: cast_nullable_to_non_nullable
as ControlMode,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as SessionState,errorMessage: freezed == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String?,mediaRatingKey: freezed == mediaRatingKey ? _self.mediaRatingKey : mediaRatingKey // ignore: cast_nullable_to_non_nullable
as String?,mediaServerId: freezed == mediaServerId ? _self.mediaServerId : mediaServerId // ignore: cast_nullable_to_non_nullable
as String?,mediaTitle: freezed == mediaTitle ? _self.mediaTitle : mediaTitle // ignore: cast_nullable_to_non_nullable
as String?,hostPeerId: freezed == hostPeerId ? _self.hostPeerId : hostPeerId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
// dart format on
@@ -544,7 +544,7 @@ class WatchTogetherProvider with ChangeNotifier {
if (message.peerId != null && message.position != null) {
final index = _participants.indexWhere((p) => p.peerId == message.peerId);
if (index >= 0) {
_participants[index] = _participants[index].copyWith(lastKnownPosition: message.position);
_participants[index] = _participants[index].copyWith(lastKnownPosition: message.position!);
// Don't notify for position updates - too frequent
}
}
@@ -606,7 +606,7 @@ class WatchTogetherProvider with ChangeNotifier {
if (message.controlMode != null) {
appLogger.d('WatchTogether: Received session config, controlMode: ${message.controlMode}');
_session = _session!.copyWith(controlMode: message.controlMode);
_session = _session!.copyWith(controlMode: message.controlMode!);
_syncManager?.updateSession(_session!); // Update sync manager if it exists
notifyListeners();
}
+21 -1
View File
@@ -59,7 +59,27 @@ else
rm -f "$out"
fi
# 2. Native formatting
# 2. Codegen freshness (build_runner outputs newer than their sources)
section "codegen freshness"
stale=()
while IFS= read -r -d '' src; do
for gen in "${src%.dart}.g.dart" "${src%.dart}.freezed.dart"; do
if [ -f "$gen" ] && [ "$src" -nt "$gen" ]; then
stale+=("${src#./}")
break
fi
done
done < <(find lib -name "*.dart" ! -name "*.g.dart" ! -name "*.freezed.dart" -type f -print0 2>/dev/null)
if [ ${#stale[@]} -eq 0 ]; then
ok "no stale generated files"
else
fail "${#stale[@]} dart source(s) newer than their generated .g/.freezed:"
printf ' %s\n' "${stale[@]}"
echo " Run: scripts/codegen.sh"
FAILED=1
fi
# 3. Native formatting
section "native format"
out="$(mktemp)"
if scripts/format_native.sh --check >"$out" 2>&1; then
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/.."
exec dart run build_runner build --delete-conflicting-outputs "$@"
+8 -2
View File
@@ -59,13 +59,19 @@ void main() {
});
group('MediaSort equality & hashCode', () {
test('equality is based on key only (matches current contract)', () {
test('value equality across all fields', () {
final a = MediaSort(key: 'k', descKey: 'k:desc', title: 'A', defaultDirection: 'asc');
final b = MediaSort(key: 'k', descKey: 'other', title: 'B', defaultDirection: 'desc');
final b = MediaSort(key: 'k', descKey: 'k:desc', title: 'A', defaultDirection: 'asc');
expect(a, equals(b));
expect(a.hashCode, b.hashCode);
});
test('differing non-key fields make instances unequal', () {
final a = MediaSort(key: 'k', descKey: 'k:desc', title: 'A', defaultDirection: 'asc');
final b = MediaSort(key: 'k', descKey: 'other', title: 'B', defaultDirection: 'desc');
expect(a, isNot(equals(b)));
});
test('different keys are not equal', () {
final a = MediaSort(key: 'k1', title: 'A');
final b = MediaSort(key: 'k2', title: 'A');
+3 -13
View File
@@ -71,7 +71,7 @@ void main() {
});
Future<Profile> createActiveLocalProfile(String id) async {
final profile = Profile(id: id, kind: ProfileKind.local, displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
final profile = Profile.local(id: id, displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
await profiles.upsert(profile);
await storage.setActiveProfileId(profile.id);
await activeProfile.initialize();
@@ -79,12 +79,7 @@ void main() {
}
test('local profile with no connections binds successfully with empty visibility', () async {
final profile = Profile(
id: 'local-owner',
kind: ProfileKind.local,
displayName: 'Owner',
createdAt: DateTime(2026, 1, 1),
);
final profile = Profile.local(id: 'local-owner', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
await profiles.upsert(profile);
await storage.setActiveProfileId(profile.id);
await activeProfile.initialize();
@@ -97,12 +92,7 @@ void main() {
});
test('started binder does not loop forever after empty local bind', () async {
final profile = Profile(
id: 'local-empty',
kind: ProfileKind.local,
displayName: 'Empty',
createdAt: DateTime(2026, 1, 1),
);
final profile = Profile.local(id: 'local-empty', displayName: 'Empty', createdAt: DateTime(2026, 1, 1));
await profiles.upsert(profile);
await storage.setActiveProfileId(profile.id);
await activeProfile.initialize();
+10 -34
View File
@@ -97,9 +97,7 @@ void main() {
// Fresh state: no auto-fallback to the first profile so the UI can
// force the picker. The binder skips its rebind while active is null,
// which is what avoids the surprise PIN prompt at first sign-in.
await registry.upsert(
Profile(id: 'p1', kind: ProfileKind.local, displayName: 'Owner', createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(Profile.local(id: 'p1', displayName: 'Owner', createdAt: DateTime(2026, 1, 1)));
await provider.initialize();
expect(provider.profiles, hasLength(1));
expect(provider.activeId, isNull);
@@ -127,9 +125,7 @@ void main() {
test('initialize clears storage when stored id is stale', () async {
// A previously-active profile that was deleted should not keep
// storage-scoped settings under the removed profile id.
await registry.upsert(
Profile(id: 'p1', kind: ProfileKind.local, displayName: 'Owner', createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(Profile.local(id: 'p1', displayName: 'Owner', createdAt: DateTime(2026, 1, 1)));
await storage.setActiveProfileId('ghost-id-no-longer-exists');
await provider.initialize();
await Future<void>.delayed(Duration.zero);
@@ -138,24 +134,16 @@ void main() {
});
test('initialize resolves the stored active profile id', () async {
await registry.upsert(
Profile(id: 'p1', kind: ProfileKind.local, displayName: 'Owner', createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(
Profile(id: 'p2', kind: ProfileKind.local, displayName: 'Kids', createdAt: DateTime(2026, 1, 2)),
);
await registry.upsert(Profile.local(id: 'p1', displayName: 'Owner', createdAt: DateTime(2026, 1, 1)));
await registry.upsert(Profile.local(id: 'p2', displayName: 'Kids', createdAt: DateTime(2026, 1, 2)));
await storage.setActiveProfileId('p2');
await provider.initialize();
expect(provider.activeId, 'p2');
});
test('activate without PIN switches a non-protected profile', () async {
await registry.upsert(
Profile(id: 'p1', kind: ProfileKind.local, displayName: 'Owner', createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(
Profile(id: 'p2', kind: ProfileKind.local, displayName: 'Kids', createdAt: DateTime(2026, 1, 2)),
);
await registry.upsert(Profile.local(id: 'p1', displayName: 'Owner', createdAt: DateTime(2026, 1, 1)));
await registry.upsert(Profile.local(id: 'p2', displayName: 'Kids', createdAt: DateTime(2026, 1, 2)));
await provider.initialize();
final p2 = provider.profiles.firstWhere((p) => p.id == 'p2');
final ok = await provider.activate(p2);
@@ -164,9 +152,7 @@ void main() {
});
test('clearActiveProfile clears storage and in-memory active profile', () async {
await registry.upsert(
Profile(id: 'p1', kind: ProfileKind.local, displayName: 'Owner', createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(Profile.local(id: 'p1', displayName: 'Owner', createdAt: DateTime(2026, 1, 1)));
await provider.initialize();
await provider.activate(provider.profiles.single);
@@ -178,13 +164,7 @@ void main() {
test('activate rejects wrong PIN for a protected local profile', () async {
await registry.upsert(
Profile(
id: 'p1',
kind: ProfileKind.local,
displayName: 'Kids',
pinHash: computePinHash('1234'),
createdAt: DateTime(2026, 1, 1),
),
Profile.local(id: 'p1', displayName: 'Kids', pinHash: computePinHash('1234'), createdAt: DateTime(2026, 1, 1)),
);
await provider.initialize();
final p1 = provider.profiles.first;
@@ -193,9 +173,7 @@ void main() {
});
test('hasMultipleProfiles reflects the registry size', () async {
await registry.upsert(
Profile(id: 'p1', kind: ProfileKind.local, displayName: 'Owner', createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(Profile.local(id: 'p1', displayName: 'Owner', createdAt: DateTime(2026, 1, 1)));
await provider.initialize();
expect(provider.hasMultipleProfiles, isFalse);
// Latch onto the next provider notification that flips the flag,
@@ -211,9 +189,7 @@ void main() {
provider.addListener(listener);
addTearDown(() => provider.removeListener(listener));
await registry.upsert(
Profile(id: 'p2', kind: ProfileKind.local, displayName: 'Kids', createdAt: DateTime(2026, 1, 2)),
);
await registry.upsert(Profile.local(id: 'p2', displayName: 'Kids', createdAt: DateTime(2026, 1, 2)));
await flipped.future.timeout(const Duration(seconds: 2));
expect(provider.hasMultipleProfiles, isTrue);
});
+9 -25
View File
@@ -23,9 +23,8 @@ void main() {
});
test('upsert + get round-trips a local profile', () async {
final profile = Profile(
final profile = Profile.local(
id: 'local-1',
kind: ProfileKind.local,
displayName: 'Owner',
pinHash: computePinHash('1234'),
createdAt: DateTime(2026, 1, 1),
@@ -40,9 +39,8 @@ void main() {
});
test('upsert + get round-trips a plex_home profile', () async {
final profile = Profile(
final profile = Profile.plexHome(
id: 'plex-home-acct-uuid',
kind: ProfileKind.plexHome,
displayName: 'Admin',
avatarThumbUrl: 'https://plex.tv/users/abc/avatar?',
parentConnectionId: 'acct',
@@ -62,28 +60,20 @@ void main() {
});
test('list orders by sortOrder then createdAt', () async {
await registry.upsert(
Profile(id: 'a', kind: ProfileKind.local, displayName: 'A', sortOrder: 1, createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(
Profile(id: 'b', kind: ProfileKind.local, displayName: 'B', sortOrder: 0, createdAt: DateTime(2026, 1, 2)),
);
await registry.upsert(Profile.local(id: 'a', displayName: 'A', sortOrder: 1, createdAt: DateTime(2026, 1, 1)));
await registry.upsert(Profile.local(id: 'b', displayName: 'B', sortOrder: 0, createdAt: DateTime(2026, 1, 2)));
final list = await registry.list();
expect(list.map((p) => p.id).toList(), ['b', 'a']);
});
test('remove deletes a profile', () async {
await registry.upsert(
Profile(id: 'p', kind: ProfileKind.local, displayName: 'P', createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(Profile.local(id: 'p', displayName: 'P', createdAt: DateTime(2026, 1, 1)));
await registry.remove('p');
expect(await registry.get('p'), isNull);
});
test('markUsed updates lastUsedAt', () async {
await registry.upsert(
Profile(id: 'p', kind: ProfileKind.local, displayName: 'P', createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(Profile.local(id: 'p', displayName: 'P', createdAt: DateTime(2026, 1, 1)));
final ts = DateTime(2026, 1, 5, 12, 0);
await registry.markUsed('p', ts);
final fetched = await registry.get('p');
@@ -91,12 +81,8 @@ void main() {
});
test('upsert is idempotent (replaces existing row)', () async {
await registry.upsert(
Profile(id: 'p', kind: ProfileKind.local, displayName: 'Original', createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(
Profile(id: 'p', kind: ProfileKind.local, displayName: 'Renamed', createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(Profile.local(id: 'p', displayName: 'Original', createdAt: DateTime(2026, 1, 1)));
await registry.upsert(Profile.local(id: 'p', displayName: 'Renamed', createdAt: DateTime(2026, 1, 1)));
final fetched = await registry.get('p');
expect(fetched!.displayName, 'Renamed');
});
@@ -115,9 +101,7 @@ void main() {
emitsThrough(isEmpty),
]),
);
await registry.upsert(
Profile(id: 'p', kind: ProfileKind.local, displayName: 'P', createdAt: DateTime(2026, 1, 1)),
);
await registry.upsert(Profile.local(id: 'p', displayName: 'P', createdAt: DateTime(2026, 1, 1)));
await registry.remove('p');
await assertion;
});
+5 -9
View File
@@ -4,7 +4,7 @@ import 'package:plezy/profiles/profile.dart';
void main() {
group('Profile', () {
test('local profile defaults', () {
final p = Profile(id: 'local-1', kind: ProfileKind.local, displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
final p = Profile.local(id: 'local-1', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
expect(p.isLocal, isTrue);
expect(p.isPlexHome, isFalse);
expect(p.isPinProtected, isFalse);
@@ -12,9 +12,8 @@ void main() {
});
test('local profile with PIN is pin-protected', () {
final p = Profile(
final p = Profile.local(
id: 'local-1',
kind: ProfileKind.local,
displayName: 'Kids',
pinHash: computePinHash('1234'),
createdAt: DateTime(2026, 1, 1),
@@ -23,9 +22,8 @@ void main() {
});
test('plex_home profile pin protection follows the protected flag', () {
final p = Profile(
final p = Profile.plexHome(
id: 'plex-home-acct1-uuid1',
kind: ProfileKind.plexHome,
displayName: 'Sarah',
parentConnectionId: 'acct1',
plexProtected: true,
@@ -36,9 +34,8 @@ void main() {
});
test('local PIN hash is round-tripped via configJson', () {
final p = Profile(
final p = Profile.local(
id: 'local-1',
kind: ProfileKind.local,
displayName: 'Kids',
pinHash: computePinHash('1234'),
createdAt: DateTime(2026, 1, 1),
@@ -59,9 +56,8 @@ void main() {
});
test('plex_home configJson round-trips with all flags', () {
final p = Profile(
final p = Profile.plexHome(
id: 'plex-home-acct1-uuid1',
kind: ProfileKind.plexHome,
displayName: 'Admin',
parentConnectionId: 'acct1',
plexAdmin: true,
+2 -8
View File
@@ -6,12 +6,7 @@ import 'package:plezy/profiles/profiles_view.dart';
void main() {
group('visibleProfileConnections', () {
test('keeps all local profile connection rows', () {
final profile = Profile(
id: 'local-1',
kind: ProfileKind.local,
displayName: 'Owner',
createdAt: DateTime(2026, 1, 1),
);
final profile = Profile.local(id: 'local-1', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
const rows = [
ProfileConnection(profileId: 'local-1', connectionId: 'plex-1', userIdentifier: 'u1'),
ProfileConnection(profileId: 'local-1', connectionId: 'jellyfin-1', userIdentifier: 'u2'),
@@ -21,9 +16,8 @@ void main() {
});
test('filters Plex Home parent token cache row', () {
final profile = Profile(
final profile = Profile.plexHome(
id: 'plex-home-plex-1-user-1',
kind: ProfileKind.plexHome,
displayName: 'Kid',
parentConnectionId: 'plex-1',
createdAt: DateTime(2026, 1, 1),
@@ -461,7 +461,7 @@ JellyfinConnection _jellyfinConnection(String id) {
}
Profile _localProfile(String id) {
return Profile(id: id, kind: ProfileKind.local, displayName: id, createdAt: DateTime(2026, 1, 1));
return Profile.local(id: id, displayName: id, createdAt: DateTime(2026, 1, 1));
}
PlexHome _home(String adminUuid) {
+2 -12
View File
@@ -80,12 +80,7 @@ void main() {
await db.close();
});
final profile = Profile(
id: 'local-owner',
kind: ProfileKind.local,
displayName: 'Owner',
createdAt: DateTime(2026, 1, 1),
);
final profile = Profile.local(id: 'local-owner', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
final plex = PlexAccountConnection(
id: 'plex-a',
accountToken: 'plex-token',
@@ -217,12 +212,7 @@ void main() {
await db.close();
});
final profile = Profile(
id: 'local-owner',
kind: ProfileKind.local,
displayName: 'Owner',
createdAt: DateTime(2026, 1, 1),
);
final profile = Profile.local(id: 'local-owner', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
final accountA = PlexAccountConnection(
id: 'plex-a',
accountToken: 'wrong-owner-token',
+2 -12
View File
@@ -75,12 +75,7 @@ void main() {
});
test('initial profile selection is skipped when a profile was auto-selected', () {
final profile = Profile(
id: 'local-owner',
kind: ProfileKind.local,
displayName: 'Owner',
createdAt: DateTime(2026, 1, 1),
);
final profile = Profile.local(id: 'local-owner', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
expect(
shouldPromptForInitialProfileSelection(
@@ -94,12 +89,7 @@ void main() {
});
test('initial profile selection is required when the launch setting is enabled', () {
final profile = Profile(
id: 'local-owner',
kind: ProfileKind.local,
displayName: 'Owner',
createdAt: DateTime(2026, 1, 1),
);
final profile = Profile.local(id: 'local-owner', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
expect(
shouldPromptForInitialProfileSelection(
@@ -35,12 +35,7 @@ void main() {
testWidgets('remote back pops the manage profile page', (tester) async {
TvDetectionService.debugSetAppleTVOverride(true);
final db = AppDatabase.forTesting(NativeDatabase.memory());
final profile = Profile(
id: 'local-owner',
kind: ProfileKind.local,
displayName: 'Owner',
createdAt: DateTime(2026, 1, 1),
);
final profile = Profile.local(id: 'local-owner', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
final profiles = ProfileRegistry(db);
final connections = _FakeConnectionRegistry(db);
final profileConnections = _FakeProfileConnectionRegistry(db);
@@ -28,12 +28,7 @@ void main() {
testWidgets('D-pad can focus profile actions and open the manage menu', (tester) async {
final db = AppDatabase.forTesting(NativeDatabase.memory());
final profile = Profile(
id: 'local-owner',
kind: ProfileKind.local,
displayName: 'Owner',
createdAt: DateTime(2026, 1, 1),
);
final profile = Profile.local(id: 'local-owner', displayName: 'Owner', createdAt: DateTime(2026, 1, 1));
final profiles = _FakeProfileRegistry(db, [profile]);
final connections = _FakeConnectionRegistry(db);
final profileConnections = _FakeProfileConnectionRegistry(db);
@@ -5,13 +5,8 @@ import 'package:plezy/profiles/profile.dart';
import 'package:plezy/screens/settings/add_jellyfin_screen.dart';
import 'package:plezy/utils/platform_detector.dart';
Profile _profile(String id) => Profile(
id: id,
kind: ProfileKind.local,
displayName: id,
sortOrder: 0,
createdAt: DateTime.fromMillisecondsSinceEpoch(0),
);
Profile _profile(String id) =>
Profile.local(id: id, displayName: id, sortOrder: 0, createdAt: DateTime.fromMillisecondsSinceEpoch(0));
void main() {
tearDown(() {