Merge the deduplication and dead-code removal pass

Consolidates duplicated logic behind shared implementations — paginated
grid tabs, focus chrome, cached remote stores, sheet selection columns,
the server artifact store and a test fixture layer — and removes code
that had become unreachable. Net reduction of about 5,500 lines with no
behaviour change.

Where a fix had landed separately in code that moved into a shared
helper, the fix was re-applied inside the helper rather than left behind
in the copy that went away.
This commit is contained in:
edde746
2026-07-26 19:41:23 +02:00
425 changed files with 12045 additions and 17543 deletions
+71 -95
View File
@@ -268,7 +268,13 @@ PageRoute<bool> mediaDetailRoute({
}
class _MediaDetailScreenState extends State<MediaDetailScreen>
with WatchStateAware, DeletionAware, MountedSetStateMixin, ServerBoundMediaMixin, RouteAware {
with
WatchStateAware,
DeletionAware,
DeletionMirrorsWatchState,
MountedSetStateMixin,
ServerBoundMediaMixin,
RouteAware {
/// Public input alias — used as the live source of truth until the detail
/// fetch returns. Holds backend-neutral [MediaItem] data.
MediaItem get _metadata => _fullMetadata ?? widget.metadata;
@@ -394,7 +400,9 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
@override
bool get isServerBoundOffline => widget.isOffline;
// WatchStateAware: watch the show/movie and all season/episode ratingKeys
// WatchStateAware: watch the show/movie and all season/episode ratingKeys.
// DeletionMirrorsWatchState reuses these three getters for deletion events —
// the same items are on screen either way.
@override
Set<String>? get watchedIds {
final keys = <String>{_metadata.id};
@@ -534,36 +542,6 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
}
}
@override
Set<String>? get deletionIds {
final keys = <String>{_metadata.id};
for (final season in _seasons) {
keys.add(season.id);
}
for (final ep in _episodes) {
keys.add(ep.id);
}
return keys;
}
@override
String? get deletionServerId => serverBoundServerId;
@override
Set<String>? get deletionGlobalKeys {
final serverId = serverBoundServerId;
if (serverId == null) return null;
final keys = <String>{toServerBoundGlobalKey(_metadata.id, serverId: ServerId(serverId))};
for (final season in _seasons) {
keys.add(toServerBoundGlobalKey(season.id, serverId: ServerId(season.serverId ?? serverId)));
}
for (final ep in _episodes) {
keys.add(toServerBoundGlobalKey(ep.id, serverId: ServerId(ep.serverId ?? serverId)));
}
return keys;
}
@override
void onDeletionEvent(DeletionEvent event) {
// Download-only deletions should only remove items when viewing offline content
@@ -3592,6 +3570,42 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
);
}
/// The ordered metadata fields the TV detail line renders and its announcement reads,
/// built through [text] for plain fields and [rating] for the rating slot.
List<T> _tvDetailMetadataParts<T extends Object>(
MediaItem metadata, {
required T Function(String value) text,
required T? Function(MediaItem item) rating,
}) {
final lineMetadata = _tvDetailFocusedEpisode.value ?? metadata;
final parts = <T>[];
void add(T? part) {
if (part != null) parts.add(part);
}
final episodeLabel = formatSeasonEpisodeLabel(lineMetadata.parentIndex, lineMetadata.index);
if (lineMetadata.isEpisode && episodeLabel != null) add(text(episodeLabel));
if (lineMetadata.isMovie) {
add(text(t.discover.movie));
} else if (lineMetadata.isShow) {
add(text(t.discover.tvShow));
}
add(rating(lineMetadata));
if (lineMetadata.contentRating != null) add(text(formatContentRating(lineMetadata.contentRating!)));
if (lineMetadata.durationMs != null) add(text(formatDurationTextual(lineMetadata.durationMs!)));
if (lineMetadata.isEpisode && lineMetadata.originallyAvailableAt != null) {
add(text(formatAbbreviatedDate(lineMetadata.originallyAvailableAt!)));
} else if (lineMetadata.year != null) {
add(text(lineMetadata.year.toString()));
}
for (final label in buildMediaQualityLabels(lineMetadata)) {
add(text(label));
}
return parts;
}
String _tvDetailInformationSemanticLabel(
MediaItem metadata, {
required String? description,
@@ -3608,23 +3622,13 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
add(metadata.displayTitle);
if (!identical(lineMetadata, metadata)) add(lineMetadata.displayTitle);
final episodeLabel = formatSeasonEpisodeLabel(lineMetadata.parentIndex, lineMetadata.index);
if (lineMetadata.isEpisode) add(episodeLabel);
if (lineMetadata.isMovie) {
add(t.discover.movie);
} else if (lineMetadata.isShow) {
add(t.discover.tvShow);
}
add(MediaRatingBadge.semanticLabelForMedia(lineMetadata, fallbackItem: metadata));
if (lineMetadata.contentRating != null) add(formatContentRating(lineMetadata.contentRating!));
if (lineMetadata.durationMs != null) add(formatDurationTextual(lineMetadata.durationMs!));
if (lineMetadata.isEpisode && lineMetadata.originallyAvailableAt != null) {
add(formatAbbreviatedDate(lineMetadata.originallyAvailableAt!));
} else if (lineMetadata.year != null) {
add(lineMetadata.year.toString());
}
for (final label in buildMediaQualityLabels(lineMetadata)) {
add(label);
final fields = _tvDetailMetadataParts<String>(
metadata,
text: (value) => value,
rating: (item) => MediaRatingBadge.semanticLabelForMedia(item, fallbackItem: metadata),
);
for (final field in fields) {
add(field);
}
if (genres.isNotEmpty) add(genres.join(', '));
add(description);
@@ -3681,60 +3685,32 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
}
Widget _buildTvDetailMetadataLine(BuildContext context, MediaItem metadata, double scale) {
final lineMetadata = _tvDetailFocusedEpisode.value ?? metadata;
final episodeLabel = formatSeasonEpisodeLabel(lineMetadata.parentIndex, lineMetadata.index);
final qualityLabels = buildMediaQualityLabels(lineMetadata);
final textStyle = TextStyle(
color: _tvDetailForegroundColor(context),
fontSize: 18 * scale,
fontWeight: .w700,
letterSpacing: 0.1,
);
final children = <Widget>[];
void addSeparator() {
if (children.isNotEmpty) children.add(Text('', maxLines: 1, style: textStyle));
}
void addTextPart(String text) {
addSeparator();
children.add(Text(text, maxLines: 1, style: textStyle));
}
void addWidgetPart(Widget widget) {
addSeparator();
children.add(widget);
}
if (lineMetadata.isEpisode && episodeLabel != null) addTextPart(episodeLabel);
if (lineMetadata.isMovie) {
addTextPart(t.discover.movie);
} else if (lineMetadata.isShow) {
addTextPart(t.discover.tvShow);
}
final ratingBadge = MediaRatingBadge.inlineForMedia(
item: lineMetadata,
fallbackItem: metadata,
foregroundColor: textStyle.color,
iconSize: textStyle.fontSize,
spacing: 4 * scale,
textStyle: textStyle,
final fields = _tvDetailMetadataParts<Widget>(
metadata,
text: (value) => Text(value, maxLines: 1, style: textStyle),
rating: (item) => MediaRatingBadge.inlineForMedia(
item: item,
fallbackItem: metadata,
foregroundColor: textStyle.color,
iconSize: textStyle.fontSize,
spacing: 4 * scale,
textStyle: textStyle,
),
);
if (ratingBadge != null) {
addWidgetPart(ratingBadge);
}
if (lineMetadata.contentRating != null) addTextPart(formatContentRating(lineMetadata.contentRating!));
if (lineMetadata.durationMs != null) addTextPart(formatDurationTextual(lineMetadata.durationMs!));
if (lineMetadata.isEpisode && lineMetadata.originallyAvailableAt != null) {
addTextPart(formatAbbreviatedDate(lineMetadata.originallyAvailableAt!));
} else if (lineMetadata.year != null) {
addTextPart(lineMetadata.year.toString());
}
for (final label in qualityLabels) {
addTextPart(label);
}
if (children.isEmpty) return const SizedBox.shrink();
if (fields.isEmpty) return const SizedBox.shrink();
final children = <Widget>[];
for (final field in fields) {
if (children.isNotEmpty) children.add(Text('', maxLines: 1, style: textStyle));
children.add(field);
}
return SingleChildScrollView(
scrollDirection: Axis.horizontal,