Surface/lib/widgets/content/cloud_files.dart
2025-05-22 22:33:32 +08:00

304 lines
8.8 KiB
Dart

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:island/models/file.dart';
import 'package:island/pods/config.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
import 'image.dart';
import 'video.dart';
class CloudFileWidget extends ConsumerWidget {
final SnCloudFile item;
final BoxFit fit;
final String? heroTag;
final bool noBlurhash;
const CloudFileWidget({
super.key,
required this.item,
this.fit = BoxFit.cover,
this.heroTag,
this.noBlurhash = false,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final serverUrl = ref.watch(serverUrlProvider);
final uri = '$serverUrl/files/${item.id}';
final content = switch (item.mimeType?.split('/').firstOrNull) {
"image" => AspectRatio(
aspectRatio: (item.fileMeta?['ratio'] ?? 1).toDouble(),
child: UniversalImage(
uri: uri,
blurHash: noBlurhash ? null : item.fileMeta?['blur'],
),
),
"video" => AspectRatio(
aspectRatio: (item.fileMeta?['ratio'] ?? 16 / 9).toDouble(),
child: UniversalVideo(
uri: uri,
aspectRatio: (item.fileMeta?['ratio'] ?? 16 / 9).toDouble(),
),
),
_ => Text('Unable render for ${item.mimeType}'),
};
if (heroTag != null) {
return Hero(tag: heroTag!, child: content);
}
return content;
}
}
class CloudImageWidget extends ConsumerWidget {
final String fileId;
final BoxFit fit;
final double aspectRatio;
final String? blurHash;
const CloudImageWidget({
super.key,
required this.fileId,
this.aspectRatio = 1,
this.fit = BoxFit.cover,
this.blurHash,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final serverUrl = ref.watch(serverUrlProvider);
final uri = '$serverUrl/files/$fileId';
return AspectRatio(
aspectRatio: aspectRatio,
child: UniversalImage(uri: uri, blurHash: blurHash),
);
}
static ImageProvider provider({
required String fileId,
required String serverUrl,
}) {
final uri = '$serverUrl/files/$fileId';
return CachedNetworkImageProvider(uri);
}
}
class ProfilePictureWidget extends ConsumerWidget {
final String? fileId;
final double radius;
final IconData? fallbackIcon;
final Color? fallbackColor;
const ProfilePictureWidget({
super.key,
required this.fileId,
this.radius = 20,
this.fallbackIcon,
this.fallbackColor,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final serverUrl = ref.watch(serverUrlProvider);
final uri = '$serverUrl/files/$fileId';
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(radius)),
child: Container(
width: radius * 2,
height: radius * 2,
color: Theme.of(context).colorScheme.primaryContainer,
child:
fileId == null
? Icon(
fallbackIcon ?? Symbols.account_circle,
size: radius,
color:
fallbackColor ??
Theme.of(context).colorScheme.onPrimaryContainer,
).center()
: CachedNetworkImage(imageUrl: uri, fit: BoxFit.cover),
),
);
}
}
class SplitAvatarWidget extends ConsumerWidget {
final List<String?> filesId;
final double radius;
final IconData fallbackIcon;
final Color? fallbackColor;
const SplitAvatarWidget({
super.key,
required this.filesId,
this.radius = 20,
this.fallbackIcon = Symbols.account_circle,
this.fallbackColor,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
if (filesId.isEmpty) {
return ProfilePictureWidget(
fileId: null,
radius: radius,
fallbackIcon: fallbackIcon,
fallbackColor: fallbackColor,
);
}
if (filesId.length == 1) {
return ProfilePictureWidget(
fileId: filesId[0],
radius: radius,
fallbackIcon: fallbackIcon,
fallbackColor: fallbackColor,
);
}
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(radius)),
child: Container(
width: radius * 2,
height: radius * 2,
color: Theme.of(context).colorScheme.primaryContainer,
child: Stack(
children: [
if (filesId.length == 2)
Row(
children: [
Expanded(
child: _buildQuadrant(context, filesId[0], ref, radius),
),
Expanded(
child: _buildQuadrant(context, filesId[1], ref, radius),
),
],
)
else if (filesId.length == 3)
Row(
children: [
Column(
children: [
Expanded(
child: _buildQuadrant(context, filesId[0], ref, radius),
),
Expanded(
child: _buildQuadrant(context, filesId[1], ref, radius),
),
],
),
Expanded(
child: _buildQuadrant(context, filesId[2], ref, radius),
),
],
)
else
Column(
children: [
Expanded(
child: Row(
children: [
Expanded(
child: _buildQuadrant(
context,
filesId[0],
ref,
radius,
),
),
Expanded(
child: _buildQuadrant(
context,
filesId[1],
ref,
radius,
),
),
],
),
),
Expanded(
child: Row(
children: [
Expanded(
child: _buildQuadrant(
context,
filesId[2],
ref,
radius,
),
),
Expanded(
child:
filesId.length > 4
? Container(
color:
Theme.of(
context,
).colorScheme.primaryContainer,
child: Center(
child: Text(
'+${filesId.length - 3}',
style: TextStyle(
fontSize: radius * 0.4,
color:
Theme.of(
context,
).colorScheme.onPrimaryContainer,
),
),
),
)
: _buildQuadrant(
context,
filesId[3],
ref,
radius,
),
),
],
),
),
],
),
],
),
),
);
}
Widget _buildQuadrant(
BuildContext context,
String? fileId,
WidgetRef ref,
double radius,
) {
if (fileId == null) {
return Container(
width: radius,
height: radius,
color: Theme.of(context).colorScheme.primaryContainer,
child:
Icon(
fallbackIcon,
size: radius * 0.6,
color:
fallbackColor ??
Theme.of(context).colorScheme.onPrimaryContainer,
).center(),
);
}
final serverUrl = ref.watch(serverUrlProvider);
final uri = '$serverUrl/files/$fileId';
return SizedBox(
width: radius,
height: radius,
child: CachedNetworkImage(imageUrl: uri, fit: BoxFit.cover),
);
}
}