[READ ONLY MIRROR] Open Source TikTok alternative built on AT Protocol github.com/sprksocial/client
flutter atproto video dart
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291import 'dart:ui';
import 'package:material_ui/material_ui.dart';import 'package:spark/src/core/design_system/components/atoms/icons.dart';import 'package:spark/src/core/l10n/app_localizations.dart';import 'package:spark/src/core/moderation/moderation_label_localizations.dart';import 'package:spark/src/core/moderation/moderation_models.dart';
sealed class ModerationPresentation { const ModerationPresentation();
const factory ModerationPresentation.standard({Widget? blurredChild}) = StandardModerationPresentation;
const factory ModerationPresentation.compact({ Widget? blurredChild, VoidCallback? onConcealedTap, }) = CompactModerationPresentation;
Widget render({ required Widget child, required bool concealed, required ModerationUI ui, required List<ModerationCause> causes, required VoidCallback reveal, required VoidCallback showDetails, });}
final class StandardModerationPresentation extends ModerationPresentation { const StandardModerationPresentation({this.blurredChild});
final Widget? blurredChild;
@override Widget render({ required Widget child, required bool concealed, required ModerationUI ui, required List<ModerationCause> causes, required VoidCallback reveal, required VoidCallback showDetails, }) { return Stack( fit: StackFit.passthrough, children: [ if (concealed) IgnorePointer( child: blurredChild ?? ImageFiltered( imageFilter: ImageFilter.blur(sigmaX: 40, sigmaY: 40), child: child, ), ) else child, if (concealed) Positioned.fill( child: _ModerationCover( causes: causes, noOverride: ui.noOverride, onReveal: ui.noOverride ? null : reveal, onDetails: showDetails, ), ) else if (ui.alert || ui.inform) Positioned( left: 12, top: 12, child: _ModerationNotice(causes: causes, onTap: showDetails), ), ], ); }}
final class CompactModerationPresentation extends ModerationPresentation { const CompactModerationPresentation({this.blurredChild, this.onConcealedTap});
final Widget? blurredChild; final VoidCallback? onConcealedTap;
@override Widget render({ required Widget child, required bool concealed, required ModerationUI ui, required List<ModerationCause> causes, required VoidCallback reveal, required VoidCallback showDetails, }) { return Stack( fit: StackFit.passthrough, children: [ if (concealed) blurredChild ?? ImageFiltered( imageFilter: ImageFilter.blur(sigmaX: 18, sigmaY: 18), child: child, ) else child, if (concealed) Positioned.fill( child: Material( color: Colors.black.withValues(alpha: 0.38), child: InkWell( onTap: onConcealedTap ?? (ui.noOverride ? showDetails : reveal), child: const Center( child: AppIcon(AppIconData.hidden, color: Colors.white), ), ), ), ) else if (ui.alert || ui.inform) Positioned( left: 4, top: 4, child: _CompactModerationNotice(onTap: showDetails), ), ], ); }}
class ModerationPending extends StatelessWidget { const ModerationPending({ required this.hasError, required this.onRetry, required this.child, super.key, });
final bool hasError; final VoidCallback onRetry; final Widget child;
@override Widget build(BuildContext context) { return Stack( fit: StackFit.passthrough, children: [ IgnorePointer( child: ExcludeSemantics(child: Opacity(opacity: 0, child: child)), ), Positioned.fill( child: Material( color: Colors.black87, child: hasError ? Semantics( button: true, label: AppLocalizations.of(context).buttonRetry, child: InkWell( onTap: onRetry, child: const Center( child: AppIcon(AppIconData.arrowFlip), ), ), ) : const Center( child: SizedBox.square( dimension: 24, child: CircularProgressIndicator(strokeWidth: 2), ), ), ), ), ], ); }}
class _CompactModerationNotice extends StatelessWidget { const _CompactModerationNotice({required this.onTap});
final VoidCallback onTap;
@override Widget build(BuildContext context) { return Semantics( button: true, label: AppLocalizations.of(context).buttonModerationDetails, child: Material( color: Theme.of(context).colorScheme.surfaceContainerHighest, shape: const CircleBorder(), child: InkResponse( onTap: onTap, radius: 16, child: const Padding( padding: EdgeInsets.all(5), child: AppIcon(AppIconData.warning, size: 16), ), ), ), ); }}
class _ModerationCover extends StatelessWidget { const _ModerationCover({ required this.causes, required this.noOverride, required this.onReveal, required this.onDetails, });
final List<ModerationCause> causes; final bool noOverride; final VoidCallback? onReveal; final VoidCallback onDetails;
@override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final names = causes .map((cause) => cause.localizedStrings(l10n)?.name) .nonNulls .toList(); return ColoredBox( color: Colors.black.withValues(alpha: 0.7), child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ const AppIcon(AppIconData.warning, color: Colors.white, size: 44), const SizedBox(height: 12), Text( l10n.moderationContentWarning, style: const TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), if (names.isNotEmpty) ...[ const SizedBox(height: 8), Text( names.join(', '), style: const TextStyle(color: Colors.white, fontSize: 15), textAlign: TextAlign.center, ), ], const SizedBox(height: 18), if (onReveal != null) FilledButton( onPressed: onReveal, child: Text(l10n.buttonViewContent), ) else if (noOverride) Text( l10n.moderationCannotOverride, style: const TextStyle(color: Colors.white70), textAlign: TextAlign.center, ), TextButton( onPressed: onDetails, child: Text(l10n.buttonModerationDetails), ), ], ), ), ), ); }}
class _ModerationNotice extends StatelessWidget { const _ModerationNotice({required this.causes, required this.onTap});
final List<ModerationCause> causes; final VoidCallback onTap;
@override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final name = causes .map((cause) => cause.localizedStrings(l10n)?.name) .nonNulls .firstOrNull; return ActionChip( avatar: const AppIcon(AppIconData.warning, size: 18), label: Text(name ?? l10n.moderationContentNotice), onPressed: onTap, ); }}