173 lines
5.1 KiB
Dart
173 lines
5.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../../core/contracts/library.dart';
|
|
import '../../../providers/di_providers.dart';
|
|
import '../../../providers/session_provider.dart';
|
|
import '../../../shared/utils/cross_server_search_key.dart';
|
|
import 'media_info_section.dart';
|
|
import 'multi_source_section.dart';
|
|
|
|
|
|
class SourceSelectionSection extends ConsumerWidget {
|
|
|
|
|
|
final CrossServerSearchReq? req;
|
|
|
|
|
|
final List<EmbyRawMediaSource> localSources;
|
|
|
|
|
|
final int selectedSourceIdx;
|
|
|
|
|
|
final CrossServerSourceCard? selectedCrossServerCard;
|
|
|
|
|
|
final String? currentServerName;
|
|
|
|
|
|
final String? excludedServerIdOverride;
|
|
|
|
|
|
final EmbyRawItem? localMediaInfoItem;
|
|
|
|
|
|
final void Function(EmbyRawMediaSource src, int index) onLocalSourceTap;
|
|
|
|
|
|
final void Function(CrossServerSourceCard card) onCrossServerSourceTap;
|
|
|
|
|
|
final String title;
|
|
|
|
|
|
final bool showLoadingWhenEmpty;
|
|
final bool externalLoading;
|
|
final String loadingText;
|
|
|
|
|
|
final Widget? leading;
|
|
|
|
const SourceSelectionSection({
|
|
super.key,
|
|
required this.req,
|
|
required this.localSources,
|
|
required this.selectedSourceIdx,
|
|
required this.selectedCrossServerCard,
|
|
required this.onLocalSourceTap,
|
|
required this.onCrossServerSourceTap,
|
|
this.currentServerName,
|
|
this.excludedServerIdOverride,
|
|
this.localMediaInfoItem,
|
|
this.title = '多源聚合',
|
|
this.showLoadingWhenEmpty = false,
|
|
this.externalLoading = false,
|
|
this.loadingText = '正在查找可用版本…',
|
|
this.leading,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final searchReq = req;
|
|
final card = selectedCrossServerCard;
|
|
|
|
final currentLocalSourceId =
|
|
card == null && selectedSourceIdx < localSources.length
|
|
? localSources[selectedSourceIdx].Id
|
|
: null;
|
|
final selectedCrossServerKey = card != null
|
|
? '${card.serverId}|${card.mediaSourceId}'
|
|
: null;
|
|
|
|
final EmbyRawMediaSource? mediaSource =
|
|
card?.mediaSource ??
|
|
(selectedSourceIdx < localSources.length
|
|
? localSources[selectedSourceIdx]
|
|
: null);
|
|
final EmbyRawItem? mediaItem = card?.item ?? localMediaInfoItem;
|
|
final multiSourceVisibility = searchReq == null
|
|
? _MultiSourceVisibility.hidden
|
|
: _multiSourceVisibility(ref, searchReq);
|
|
final mediaInfo =
|
|
mediaSource != null &&
|
|
mediaItem != null &&
|
|
MediaInfoSection.hasVisibleContent(
|
|
item: mediaItem,
|
|
selectedSource: mediaSource,
|
|
)
|
|
? MediaInfoSection(
|
|
item: mediaItem,
|
|
selectedSource: mediaSource,
|
|
embedded: true,
|
|
)
|
|
: null;
|
|
final bool hasContent =
|
|
multiSourceVisibility != _MultiSourceVisibility.hidden ||
|
|
mediaInfo != null;
|
|
if (!hasContent) return const SizedBox.shrink();
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (leading != null) ...[leading!, const SizedBox(height: 16)],
|
|
if (searchReq != null)
|
|
MultiSourceSection(
|
|
req: searchReq,
|
|
embedded: true,
|
|
localSources: localSources,
|
|
currentLocalSourceId: currentLocalSourceId,
|
|
currentServerName: currentServerName,
|
|
excludedServerIdOverride: excludedServerIdOverride,
|
|
selectedCrossServerKey: selectedCrossServerKey,
|
|
title: title,
|
|
showLoadingWhenEmpty: showLoadingWhenEmpty,
|
|
externalLoading: externalLoading,
|
|
loadingText: loadingText,
|
|
onLocalSourceTap: onLocalSourceTap,
|
|
onSourceTap: (serverId, itemId, {mediaSourceId, card}) async {
|
|
if (card != null) onCrossServerSourceTap(card);
|
|
},
|
|
),
|
|
if (mediaInfo != null) mediaInfo,
|
|
],
|
|
);
|
|
}
|
|
|
|
_MultiSourceVisibility _multiSourceVisibility(
|
|
WidgetRef ref,
|
|
CrossServerSearchReq searchReq,
|
|
) {
|
|
final session = ref.watch(activeSessionProvider);
|
|
if (session == null) return _MultiSourceVisibility.hidden;
|
|
|
|
final key = (
|
|
activeServerId: session.serverId,
|
|
anchorType: searchReq.anchorType,
|
|
providerIdQuery: encodeProviderIdsForKey(searchReq.providerIds),
|
|
seriesProviderIdQuery: encodeProviderIdsForKey(
|
|
searchReq.seriesProviderIds ?? const {},
|
|
),
|
|
parentIndexNumber: searchReq.parentIndexNumber ?? -1,
|
|
indexNumber: searchReq.indexNumber ?? -1,
|
|
itemName: searchReq.itemName,
|
|
excludedServerId: excludedServerIdOverride ?? session.serverId,
|
|
);
|
|
final asyncResult = ref.watch(crossServerSearchDataProvider(key));
|
|
final remoteCards = asyncResult.maybeWhen(
|
|
data: (list) => list,
|
|
orElse: () => asyncResult.value ?? const <CrossServerSourceCard>[],
|
|
);
|
|
if (localSources.isNotEmpty || remoteCards.isNotEmpty) {
|
|
return _MultiSourceVisibility.content;
|
|
}
|
|
|
|
final loading = externalLoading || asyncResult.isLoading;
|
|
if (showLoadingWhenEmpty && loading) return _MultiSourceVisibility.loading;
|
|
|
|
return _MultiSourceVisibility.hidden;
|
|
}
|
|
}
|
|
|
|
enum _MultiSourceVisibility { hidden, loading, content }
|