Files
plezy/lib/services/watch_state_resolver.dart
edde746 352b88109b refactor: extract shared mixins and helpers, drop dead abstractions
Introduces shared seams for paginated views, D-pad reorder, media control
routing, async singletons and the device method channel, then points the
open-coded copies at them.

Also removes unused models and duplicated provider/server plumbing, folds
the twice-implemented artifact store in the server, and factors the
repeated Flutter toolchain prologue in CI into a composite action.
2026-07-26 06:09:48 +02:00

80 lines
2.8 KiB
Dart

import 'package:flutter/foundation.dart';
import '../database/app_database.dart';
import '../media/media_item.dart';
import '../utils/watch_state_notifier.dart';
@immutable
class WatchStateSnapshot {
final bool? isWatched;
final bool hasViewOffsetMs;
final int? viewOffsetMs;
const WatchStateSnapshot({this.isWatched, this.hasViewOffsetMs = false, this.viewOffsetMs});
bool get isEmpty => isWatched == null && !hasViewOffsetMs;
MediaItem apply(MediaItem item) {
var updated = item;
if (isWatched != null) {
updated = updated.withWatchedFlag(isWatched!);
}
if (hasViewOffsetMs) {
updated = updated.copyWith(viewOffsetMs: viewOffsetMs);
}
return updated;
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is WatchStateSnapshot &&
other.isWatched == isWatched &&
other.hasViewOffsetMs == hasViewOffsetMs &&
other.viewOffsetMs == viewOffsetMs;
@override
int get hashCode => Object.hash(isWatched, hasViewOffsetMs, viewOffsetMs);
}
class WatchStateResolver {
const WatchStateResolver._();
static WatchStateSnapshot fromEvent(WatchStateEvent event) {
return switch (event.changeType) {
WatchStateChangeType.watched => const WatchStateSnapshot(isWatched: true, hasViewOffsetMs: true, viewOffsetMs: 0),
WatchStateChangeType.unwatched => const WatchStateSnapshot(
isWatched: false,
hasViewOffsetMs: true,
viewOffsetMs: 0,
),
WatchStateChangeType.progressUpdate =>
event.isNowWatched == true
? const WatchStateSnapshot(isWatched: true, hasViewOffsetMs: true, viewOffsetMs: 0)
: WatchStateSnapshot(hasViewOffsetMs: event.viewOffset != null, viewOffsetMs: event.viewOffset),
WatchStateChangeType.removedFromContinueWatching => const WatchStateSnapshot(),
};
}
static WatchStateSnapshot fromActions(Iterable<OfflineWatchProgressItem> actions) {
OfflineWatchProgressItem? latest;
for (final action in actions) {
if (action.actionType != 'watched' && action.actionType != 'unwatched' && action.actionType != 'progress') {
continue;
}
if (latest == null || action.updatedAt > latest.updatedAt) latest = action;
}
return switch (latest?.actionType) {
'watched' => const WatchStateSnapshot(isWatched: true, hasViewOffsetMs: true, viewOffsetMs: 0),
'unwatched' => const WatchStateSnapshot(isWatched: false, hasViewOffsetMs: true, viewOffsetMs: 0),
'progress' =>
latest!.shouldMarkWatched
? const WatchStateSnapshot(isWatched: true, hasViewOffsetMs: true, viewOffsetMs: 0)
: WatchStateSnapshot(hasViewOffsetMs: latest.viewOffset != null, viewOffsetMs: latest.viewOffset),
_ => const WatchStateSnapshot(),
};
}
}