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.
96 lines
3.0 KiB
Dart
96 lines
3.0 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter/material.dart';
|
|
import '../utils/deletion_notifier.dart';
|
|
import 'event_aware.dart';
|
|
import 'watch_state_aware.dart';
|
|
|
|
/// Mixin for screens that need to react to deletion events.
|
|
///
|
|
/// Provides automatic subscription management and filtering based on
|
|
/// which items the screen cares about.
|
|
///
|
|
/// Example usage:
|
|
/// ```dart
|
|
/// class _MyScreenState extends State<MyScreen> with DeletionAware {
|
|
/// List<MediaItem> _items = [];
|
|
///
|
|
/// @override
|
|
/// Set<String>? get deletionIds =>
|
|
/// _items.map((e) => e.id).toSet();
|
|
///
|
|
/// @override
|
|
/// void onDeletionEvent(DeletionEvent event) {
|
|
/// setState(() {
|
|
/// _items.removeWhere((e) => e.id == event.itemId);
|
|
/// });
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
mixin DeletionAware<T extends StatefulWidget> on State<T> {
|
|
StreamSubscription<DeletionEvent>? _deletionSubscription;
|
|
|
|
/// Override to scope events to a specific server.
|
|
///
|
|
/// Return null to receive events from all servers.
|
|
String? get deletionServerId => null;
|
|
|
|
/// Override to specify which global keys this screen cares about.
|
|
///
|
|
/// Use format `serverId:ratingKey`.
|
|
/// Return null to fall back to [deletionIds] matching.
|
|
Set<String>? get deletionGlobalKeys => null;
|
|
|
|
/// Override to specify which item ids this screen cares about.
|
|
///
|
|
/// Return null to receive ALL events (not recommended for performance).
|
|
/// Return an empty set to receive no events.
|
|
///
|
|
/// The set should include:
|
|
/// - Direct items displayed (e.g., episode ids in a season view)
|
|
/// - Parent items that affect display (e.g., show id for seasons)
|
|
Set<String>? get deletionIds;
|
|
|
|
/// Called when a relevant deletion event occurs.
|
|
///
|
|
/// Only called if [deletionIds] is null or contains an affected key.
|
|
void onDeletionEvent(DeletionEvent event);
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_deletionSubscription = subscribeToHierarchicalEvents<DeletionEvent>(
|
|
notifier: DeletionNotifier(),
|
|
mounted: () => mounted,
|
|
serverId: () => deletionServerId,
|
|
globalKeys: () => deletionGlobalKeys,
|
|
itemIds: () => deletionIds,
|
|
onEvent: onDeletionEvent,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_deletionSubscription?.cancel();
|
|
_deletionSubscription = null;
|
|
super.dispose();
|
|
}
|
|
}
|
|
|
|
/// Points [DeletionAware]'s filters at the [WatchStateAware] ones.
|
|
///
|
|
/// The usual case: a screen shows the same rows for both event families, so a
|
|
/// deleted show and a watched show affect exactly the same items. Mix this in
|
|
/// after both aware mixins instead of re-typing the three getters. A screen
|
|
/// that genuinely needs a different scope overrides the getter it cares about
|
|
/// (or skips this mixin entirely).
|
|
mixin DeletionMirrorsWatchState<T extends StatefulWidget> on WatchStateAware<T>, DeletionAware<T> {
|
|
@override
|
|
String? get deletionServerId => watchStateServerId;
|
|
|
|
@override
|
|
Set<String>? get deletionGlobalKeys => watchedGlobalKeys;
|
|
|
|
@override
|
|
Set<String>? get deletionIds => watchedIds;
|
|
}
|