142 lines
4.4 KiB
Dart
142 lines
4.4 KiB
Dart
|
|
import 'dart:io' show Platform;
|
||
|
|
|
||
|
|
import 'package:flutter/material.dart';
|
||
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
|
|
import 'package:go_router/go_router.dart';
|
||
|
|
|
||
|
|
import '../../../core/contracts/library.dart';
|
||
|
|
import '../../../providers/di_providers.dart';
|
||
|
|
import '../../../providers/emby_headers_provider.dart';
|
||
|
|
import '../../../providers/session_provider.dart';
|
||
|
|
import '../../../shared/mappers/media_image_url.dart';
|
||
|
|
import '../../../shared/theme/app_theme.dart';
|
||
|
|
import '../../../shared/widgets/hover_scrollable_row.dart';
|
||
|
|
import '../../../shared/widgets/media_poster_card.dart';
|
||
|
|
import 'section_container.dart';
|
||
|
|
|
||
|
|
|
||
|
|
typedef AdditionalPartTap = void Function(EmbyRawItem item);
|
||
|
|
|
||
|
|
class AdditionalPartsSection extends ConsumerWidget {
|
||
|
|
final String itemId;
|
||
|
|
final bool embedded;
|
||
|
|
|
||
|
|
|
||
|
|
final String? serverId;
|
||
|
|
|
||
|
|
|
||
|
|
final AdditionalPartTap? onItemTap;
|
||
|
|
|
||
|
|
|
||
|
|
final Widget? leading;
|
||
|
|
|
||
|
|
const AdditionalPartsSection({
|
||
|
|
super.key,
|
||
|
|
required this.itemId,
|
||
|
|
this.embedded = false,
|
||
|
|
this.serverId,
|
||
|
|
this.onItemTap,
|
||
|
|
this.leading,
|
||
|
|
});
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
||
|
|
final scopedServerId = serverId;
|
||
|
|
|
||
|
|
final AsyncValue<List<EmbyRawItem>> asyncItems;
|
||
|
|
final String? baseUrl;
|
||
|
|
final String? token;
|
||
|
|
final Map<String, String>? imageHeaders;
|
||
|
|
|
||
|
|
if (scopedServerId != null) {
|
||
|
|
asyncItems = ref.watch(
|
||
|
|
tmdbServerScopedAdditionalPartsProvider((
|
||
|
|
serverId: scopedServerId,
|
||
|
|
itemId: itemId,
|
||
|
|
)),
|
||
|
|
);
|
||
|
|
final auth = ref
|
||
|
|
.watch(tmdbServerScopedAuthProvider(scopedServerId))
|
||
|
|
.value;
|
||
|
|
baseUrl = auth?.baseUrl;
|
||
|
|
token = auth?.token;
|
||
|
|
imageHeaders = auth?.imageHeaders;
|
||
|
|
} else {
|
||
|
|
final session = ref.watch(activeSessionProvider);
|
||
|
|
if (session == null) return const SizedBox.shrink();
|
||
|
|
asyncItems = ref.watch(additionalPartsDataProvider(itemId));
|
||
|
|
baseUrl = session.serverUrl;
|
||
|
|
token = session.token;
|
||
|
|
imageHeaders = ref.watch(embyImageHeadersProvider).value;
|
||
|
|
}
|
||
|
|
|
||
|
|
return asyncItems.when(
|
||
|
|
loading: () => const SizedBox.shrink(),
|
||
|
|
error: (error, stackTrace) => const SizedBox.shrink(),
|
||
|
|
data: (items) {
|
||
|
|
if (items.isEmpty || baseUrl == null || token == null) {
|
||
|
|
return const SizedBox.shrink();
|
||
|
|
}
|
||
|
|
final hPad = SectionContainer.hPadOf(context);
|
||
|
|
final resolvedBaseUrl = baseUrl;
|
||
|
|
final resolvedToken = token;
|
||
|
|
|
||
|
|
final isAndroid = Platform.isAndroid;
|
||
|
|
return SectionContainer(
|
||
|
|
title: '附加片段',
|
||
|
|
embedded: embedded,
|
||
|
|
leadingDivider: embedded,
|
||
|
|
leading: leading,
|
||
|
|
child: SizedBox(
|
||
|
|
height: isAndroid ? 210 : 300,
|
||
|
|
child: Theme(
|
||
|
|
data: AppTheme.dark(),
|
||
|
|
child: HoverScrollableRow(
|
||
|
|
builder: (_, controller) => ListView.builder(
|
||
|
|
controller: controller,
|
||
|
|
scrollDirection: Axis.horizontal,
|
||
|
|
padding: EdgeInsets.symmetric(horizontal: hPad),
|
||
|
|
itemCount: items.length,
|
||
|
|
itemBuilder: (_, i) {
|
||
|
|
final item = items[i];
|
||
|
|
final imageUrl = EmbyImageUrl.primary(
|
||
|
|
baseUrl: resolvedBaseUrl,
|
||
|
|
token: resolvedToken,
|
||
|
|
item: item,
|
||
|
|
maxHeight: 480,
|
||
|
|
);
|
||
|
|
return Padding(
|
||
|
|
padding: EdgeInsets.only(
|
||
|
|
right: i < items.length - 1 ? 12 : 0,
|
||
|
|
),
|
||
|
|
child: MediaPosterCard(
|
||
|
|
item: item,
|
||
|
|
imageUrl: imageUrl,
|
||
|
|
width: isAndroid ? 105 : 160,
|
||
|
|
showHoverPlayIcon: false,
|
||
|
|
enableContextMenu: false,
|
||
|
|
imageHeaders: imageHeaders,
|
||
|
|
onTap: () {
|
||
|
|
final tap = onItemTap;
|
||
|
|
if (tap != null) {
|
||
|
|
tap(item);
|
||
|
|
} else {
|
||
|
|
context.pushReplacement(
|
||
|
|
'/media/${item.Id}',
|
||
|
|
extra: item,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
),
|
||
|
|
);
|
||
|
|
},
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
},
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|