Initial commit
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
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 }
|
||||
Reference in New Issue
Block a user