💄 Animated image gates
This commit is contained in:
@@ -244,22 +244,20 @@ class CloudFileList extends HookConsumerWidget {
|
|||||||
minWidth: minWidth ?? 0,
|
minWidth: minWidth ?? 0,
|
||||||
maxWidth: files.length == 1 ? maxWidth : double.infinity,
|
maxWidth: files.length == 1 ? maxWidth : double.infinity,
|
||||||
),
|
),
|
||||||
child:
|
child: (ratio == null && isImage)
|
||||||
(ratio == null && isImage)
|
? IntrinsicWidth(child: IntrinsicHeight(child: widgetItem))
|
||||||
? IntrinsicWidth(child: IntrinsicHeight(child: widgetItem))
|
: (ratio == null && isAudio)
|
||||||
: (ratio == null && isAudio)
|
? IntrinsicHeight(child: widgetItem)
|
||||||
? IntrinsicHeight(child: widgetItem)
|
: AspectRatio(
|
||||||
: AspectRatio(
|
aspectRatio: ratio?.toDouble() ?? 1,
|
||||||
aspectRatio: ratio?.toDouble() ?? 1,
|
child: widgetItem,
|
||||||
child: widgetItem,
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final allImages =
|
final allImages = !files.any(
|
||||||
!files.any(
|
(e) => e.mimeType == null || !e.mimeType!.startsWith('image'),
|
||||||
(e) => e.mimeType == null || !e.mimeType!.startsWith('image'),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if (allImages) {
|
if (allImages) {
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
@@ -270,10 +268,9 @@ class CloudFileList extends HookConsumerWidget {
|
|||||||
padding: padding ?? EdgeInsets.zero,
|
padding: padding ?? EdgeInsets.zero,
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
final availableWidth =
|
final availableWidth = constraints.maxWidth.isFinite
|
||||||
constraints.maxWidth.isFinite
|
? constraints.maxWidth
|
||||||
? constraints.maxWidth
|
: MediaQuery.of(context).size.width;
|
||||||
: MediaQuery.of(context).size.width;
|
|
||||||
final itemExtent = math.min(
|
final itemExtent = math.min(
|
||||||
math.min(availableWidth * 0.75, maxWidth * 0.75).toDouble(),
|
math.min(availableWidth * 0.75, maxWidth * 0.75).toDouble(),
|
||||||
640.0,
|
640.0,
|
||||||
@@ -339,10 +336,9 @@ class CloudFileList extends HookConsumerWidget {
|
|||||||
padding: padding,
|
padding: padding,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return AspectRatio(
|
return AspectRatio(
|
||||||
aspectRatio:
|
aspectRatio: files[index].fileMeta?['ratio'] is num
|
||||||
files[index].fileMeta?['ratio'] is num
|
? files[index].fileMeta!['ratio'].toDouble()
|
||||||
? files[index].fileMeta!['ratio'].toDouble()
|
: 1.0,
|
||||||
: 1.0,
|
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
@@ -440,40 +436,68 @@ class _CloudFileListEntry extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final bool fullyUnlocked = !lockedByDS && !lockedByMature;
|
final bool fullyUnlocked = !lockedByDS && !lockedByMature;
|
||||||
Widget fg =
|
Widget fg = fullyUnlocked
|
||||||
fullyUnlocked
|
? (isImage
|
||||||
? (isImage
|
? CloudFileWidget(
|
||||||
? CloudFileWidget(
|
|
||||||
item: file,
|
item: file,
|
||||||
heroTag: heroTag,
|
heroTag: heroTag,
|
||||||
noBlurhash: true,
|
noBlurhash: true,
|
||||||
fit: fit,
|
fit: fit,
|
||||||
useInternalGate: false,
|
useInternalGate: false,
|
||||||
)
|
)
|
||||||
: CloudFileWidget(
|
: CloudFileWidget(
|
||||||
item: file,
|
item: file,
|
||||||
heroTag: heroTag,
|
heroTag: heroTag,
|
||||||
fit: fit,
|
fit: fit,
|
||||||
useInternalGate: false,
|
useInternalGate: false,
|
||||||
))
|
))
|
||||||
: const SizedBox.shrink();
|
: const SizedBox.shrink();
|
||||||
|
|
||||||
Widget overlays;
|
Widget overlays = AnimatedSwitcher(
|
||||||
if (lockedByDS) {
|
duration: const Duration(milliseconds: 300),
|
||||||
overlays = _DataSavingOverlay();
|
child: lockedByDS
|
||||||
} else if (file.sensitiveMarks.isNotEmpty) {
|
? _DataSavingOverlay(key: const ValueKey('ds'))
|
||||||
overlays = _SensitiveOverlay(
|
: (file.sensitiveMarks.isNotEmpty && !showMature.value
|
||||||
file: file,
|
? _SensitiveOverlay(
|
||||||
isRevealed: showMature.value,
|
key: const ValueKey('sensitive-blur'),
|
||||||
onHide: () => showMature.value = false,
|
file: file,
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(key: ValueKey('none'))),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget hideButton = const SizedBox.shrink();
|
||||||
|
if (file.sensitiveMarks.isNotEmpty && showMature.value) {
|
||||||
|
hideButton = Positioned(
|
||||||
|
top: 3,
|
||||||
|
left: 4,
|
||||||
|
child: IconButton(
|
||||||
|
iconSize: 16,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.visibility_off,
|
||||||
|
color: Colors.white,
|
||||||
|
shadows: [
|
||||||
|
Shadow(
|
||||||
|
color: Colors.black,
|
||||||
|
blurRadius: 5.0,
|
||||||
|
offset: Offset(1.0, 1.0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
tooltip: 'Blur content',
|
||||||
|
onPressed: () => showMature.value = false,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
overlays = const SizedBox.shrink();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final content = Stack(
|
final content = Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [if (isImage) Positioned.fill(child: bg), fg, overlays],
|
children: [
|
||||||
|
if (isImage) Positioned.fill(child: bg),
|
||||||
|
fg,
|
||||||
|
overlays,
|
||||||
|
hideButton,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
@@ -494,41 +518,11 @@ class _CloudFileListEntry extends HookConsumerWidget {
|
|||||||
|
|
||||||
class _SensitiveOverlay extends StatelessWidget {
|
class _SensitiveOverlay extends StatelessWidget {
|
||||||
final SnCloudFile file;
|
final SnCloudFile file;
|
||||||
final VoidCallback? onHide;
|
|
||||||
final bool isRevealed;
|
|
||||||
|
|
||||||
const _SensitiveOverlay({
|
const _SensitiveOverlay({required this.file, super.key});
|
||||||
required this.file,
|
|
||||||
this.onHide,
|
|
||||||
this.isRevealed = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (isRevealed) {
|
|
||||||
return Positioned(
|
|
||||||
top: 3,
|
|
||||||
left: 4,
|
|
||||||
child: IconButton(
|
|
||||||
iconSize: 16,
|
|
||||||
constraints: const BoxConstraints(),
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.visibility_off,
|
|
||||||
color: Colors.white,
|
|
||||||
shadows: [
|
|
||||||
Shadow(
|
|
||||||
color: Colors.black,
|
|
||||||
blurRadius: 5.0,
|
|
||||||
offset: Offset(1.0, 1.0),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
tooltip: 'Blur content',
|
|
||||||
onPressed: onHide,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return BackdropFilter(
|
return BackdropFilter(
|
||||||
filter: ImageFilter.blur(sigmaX: 64, sigmaY: 64),
|
filter: ImageFilter.blur(sigmaX: 64, sigmaY: 64),
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -549,6 +543,8 @@ class _SensitiveOverlay extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DataSavingOverlay extends StatelessWidget {
|
class _DataSavingOverlay extends StatelessWidget {
|
||||||
|
const _DataSavingOverlay({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ColoredBox(
|
return ColoredBox(
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/pods/config.dart';
|
import 'package:island/pods/config.dart';
|
||||||
|
|
||||||
typedef WidgetBuilder0 = Widget Function();
|
typedef WidgetBuilder = Widget Function();
|
||||||
|
|
||||||
class DataSavingGate extends ConsumerWidget {
|
class DataSavingGate extends ConsumerWidget {
|
||||||
final bool bypass;
|
final bool bypass;
|
||||||
final WidgetBuilder0 content;
|
final WidgetBuilder content;
|
||||||
final Widget placeholder;
|
final Widget placeholder;
|
||||||
|
|
||||||
const DataSavingGate({
|
const DataSavingGate({
|
||||||
|
|||||||
Reference in New Issue
Block a user