From b39e2e2d64d30ceb04e68b71adb7da682fbbcc33 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Fri, 1 Aug 2025 20:36:39 +0800 Subject: [PATCH] :lipstick: Video player optimized --- lib/screens/about.dart | 2 +- lib/screens/account.dart | 2 +- lib/screens/account/event_calendar.dart | 2 +- lib/screens/account/profile.dart | 614 ++++++++++++---------- lib/screens/auth/create_account.dart | 2 +- lib/screens/auth/login.dart | 2 +- lib/screens/chat/call.dart | 2 +- lib/screens/developers/hub.dart | 2 +- lib/screens/discovery/realms.dart | 2 +- lib/screens/explore.dart | 2 +- lib/screens/posts/compose.dart | 6 +- lib/screens/posts/compose_article.dart | 2 +- lib/screens/posts/post_detail.dart | 2 +- lib/screens/posts/post_search.dart | 2 +- lib/screens/posts/pub_profile.dart | 6 +- lib/screens/realm/realm_detail.dart | 2 +- lib/screens/realm/realms.dart | 4 +- lib/screens/settings.dart | 2 +- lib/widgets/app_scaffold.dart | 6 +- lib/widgets/content/cloud_files.dart | 118 ++++- lib/widgets/content/video.native.dart | 4 +- lib/widgets/publisher/publisher_card.dart | 6 +- lib/widgets/realm/realm_card.dart | 6 +- lib/widgets/web_article_card.dart | 1 + 24 files changed, 500 insertions(+), 299 deletions(-) diff --git a/lib/screens/about.dart b/lib/screens/about.dart index 989bf48..4990043 100644 --- a/lib/screens/about.dart +++ b/lib/screens/about.dart @@ -93,7 +93,7 @@ class _AboutScreenState extends ConsumerState { final theme = Theme.of(context); return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar(title: Text('about'.tr()), elevation: 0), body: _isLoading diff --git a/lib/screens/account.dart b/lib/screens/account.dart index fd0dbc7..74b937a 100644 --- a/lib/screens/account.dart +++ b/lib/screens/account.dart @@ -64,7 +64,7 @@ class AccountScreen extends HookConsumerWidget { } return AppScaffold( - noBackground: isWide, + isNoBackground: isWide, appBar: AppBar(backgroundColor: Colors.transparent, toolbarHeight: 0), body: SingleChildScrollView( padding: getTabbedPadding(context), diff --git a/lib/screens/account/event_calendar.dart b/lib/screens/account/event_calendar.dart index 6cf8bae..44da56b 100644 --- a/lib/screens/account/event_calendar.dart +++ b/lib/screens/account/event_calendar.dart @@ -46,7 +46,7 @@ class EventCalanderScreen extends HookConsumerWidget { } return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( leading: const PageBackButton(), title: Text('eventCalander').tr(), diff --git a/lib/screens/account/profile.dart b/lib/screens/account/profile.dart index a4b6643..a71aa5b 100644 --- a/lib/screens/account/profile.dart +++ b/lib/screens/account/profile.dart @@ -12,6 +12,7 @@ import 'package:island/pods/event_calendar.dart'; import 'package:island/pods/network.dart'; import 'package:island/pods/userinfo.dart'; import 'package:island/services/color.dart'; +import 'package:island/services/responsive.dart'; import 'package:island/services/time.dart'; import 'package:island/services/timezone/native.dart'; import 'package:island/widgets/account/account_name.dart'; @@ -248,294 +249,367 @@ class AccountProfileScreen extends HookConsumerWidget { final user = ref.watch(userInfoProvider); + Widget accountBasicInfo(SnAccount data) => Padding( + padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ProfilePictureWidget(file: data.profile.picture, radius: 32), + const Gap(20), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + children: [ + AccountName(account: data, style: TextStyle(fontSize: 20)), + const Gap(6), + Text('@${data.name}').fontSize(14).opacity(0.85), + ], + ), + AccountStatusWidget(uname: name, padding: EdgeInsets.zero), + ], + ), + ), + ], + ), + ); + + Widget accountProfileDetail(SnAccount data) => Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + spacing: 24, + children: [ + if (buildSubcolumn(data).isNotEmpty) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 2, + children: buildSubcolumn(data), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('bio').tr().bold(), + Text( + data.profile.bio.isEmpty + ? 'descriptionNone'.tr() + : data.profile.bio, + ), + ], + ), + if (data.profile.timeZone.isNotEmpty) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('timeZone').tr().bold(), + Row( + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + spacing: 6, + children: [ + Text(data.profile.timeZone), + Text( + getTzInfo( + data.profile.timeZone, + ).$2.formatCustomGlobal('HH:mm'), + ), + Text( + getTzInfo(data.profile.timeZone).$1.formatOffsetLocal(), + ).fontSize(11), + Text( + 'UTC${getTzInfo(data.profile.timeZone).$1.formatOffset()}', + ).fontSize(11).opacity(0.75), + ], + ), + ], + ), + ], + ).padding(horizontal: 24); + + Widget accountAction(SnAccount data) => Card( + child: Column( + children: [ + Row( + spacing: 8, + children: [ + if (accountRelationship.value == null || + accountRelationship.value!.status > -100) + Expanded( + child: FilledButton.icon( + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + accountRelationship.value == null + ? null + : Theme.of(context).colorScheme.secondary, + ), + foregroundColor: WidgetStatePropertyAll( + accountRelationship.value == null + ? null + : Theme.of(context).colorScheme.onSecondary, + ), + ), + onPressed: relationshipAction, + label: + Text( + accountRelationship.value == null + ? 'addFriendShort' + : 'added', + ).tr(), + icon: + accountRelationship.value == null + ? const Icon(Symbols.person_add) + : const Icon(Symbols.person_check), + ), + ), + if (accountRelationship.value == null || + accountRelationship.value!.status <= -100) + Expanded( + child: FilledButton.icon( + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + accountRelationship.value == null + ? null + : Theme.of(context).colorScheme.secondary, + ), + foregroundColor: WidgetStatePropertyAll( + accountRelationship.value == null + ? null + : Theme.of(context).colorScheme.onSecondary, + ), + ), + onPressed: blockAction, + label: + Text( + accountRelationship.value == null + ? 'blockUser' + : 'unblockUser', + ).tr(), + icon: + accountRelationship.value == null + ? const Icon(Symbols.block) + : const Icon(Symbols.person_cancel), + ), + ), + ], + ).padding(horizontal: 16), + Row( + spacing: 8, + children: [ + Expanded( + child: FilledButton.icon( + onPressed: directMessageAction, + icon: const Icon(Symbols.message), + label: + Text( + accountChat.value == null + ? 'createDirectMessage' + : 'gotoDirectMessage', + maxLines: 1, + ).tr(), + ), + ), + IconButton.filled( + onPressed: () { + showAbuseReportSheet( + context, + resourceIdentifier: 'account/${data.id}', + ); + }, + icon: Icon( + Symbols.flag, + color: Theme.of(context).colorScheme.onError, + ), + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + Theme.of(context).colorScheme.error, + ), + ), + ), + ], + ), + ], + ).padding(horizontal: 16, vertical: 8), + ); + return account.when( data: (data) => AppScaffold( - body: CustomScrollView( - slivers: [ - SliverAppBar( - foregroundColor: appbarColor.value, - expandedHeight: 180, - pinned: true, - leading: PageBackButton( - color: appbarColor.value, - shadows: [appbarShadow], - ), - flexibleSpace: Stack( - children: [ - Positioned.fill( - child: - data.profile.background?.id != null - ? CloudImageWidget( - file: data.profile.background, - ) - : Container( - color: - Theme.of( - context, - ).appBarTheme.backgroundColor, - ), + isNoBackground: false, + appBar: + isWideScreen(context) + ? AppBar( + foregroundColor: appbarColor.value, + leading: PageBackButton( + color: appbarColor.value, + shadows: [appbarShadow], ), - FlexibleSpaceBar( - title: Text( - data.nick, - style: TextStyle( - color: - appbarColor.value ?? - Theme.of(context).appBarTheme.foregroundColor, - shadows: [appbarShadow], + flexibleSpace: Stack( + children: [ + Positioned.fill( + child: + data.profile.background?.id != null + ? CloudImageWidget( + file: data.profile.background, + ) + : Container( + color: + Theme.of( + context, + ).appBarTheme.backgroundColor, + ), + ), + FlexibleSpaceBar( + title: Text( + data.nick, + style: TextStyle( + color: + appbarColor.value ?? + Theme.of( + context, + ).appBarTheme.foregroundColor, + shadows: [appbarShadow], + ), + ), + ), + ], + ), + ) + : null, + body: + isWideScreen(context) + ? Row( + children: [ + Flexible( + child: CustomScrollView( + slivers: [ + SliverToBoxAdapter(child: accountBasicInfo(data)), + if (data.badges.isNotEmpty) + SliverToBoxAdapter( + child: BadgeList( + badges: data.badges, + ).padding(horizontal: 24, bottom: 24), + ), + SliverToBoxAdapter( + child: Column( + spacing: 12, + children: [ + LevelingProgressCard( + level: data.profile.level, + experience: data.profile.experience, + progress: data.profile.levelingProgress, + ), + if (data.profile.verification != null) + VerificationStatusCard( + mark: data.profile.verification!, + ), + ], + ).padding(horizontal: 20), + ), + ], ), ), - ), - ], - ), - ), - SliverToBoxAdapter( - child: Padding( - padding: const EdgeInsets.fromLTRB(24, 24, 24, 8), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ProfilePictureWidget( - file: data.profile.picture, - radius: 32, - ), - const Gap(20), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - children: [ - AccountName( - account: data, - style: TextStyle(fontSize: 20), - ), - const Gap(6), - Text( - '@${data.name}', - ).fontSize(14).opacity(0.85), - ], + Flexible( + child: CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: accountProfileDetail(data), ), - AccountStatusWidget( - uname: name, - padding: EdgeInsets.zero, + + if (user.value != null) + SliverToBoxAdapter(child: accountAction(data)), + SliverToBoxAdapter( + child: Card( + child: FortuneGraphWidget( + events: accountEvents, + eventCalanderUser: data.name, + ), + ).padding(all: 8), ), ], ), ), ], - ), - ), - ), - if (data.badges.isNotEmpty) - SliverToBoxAdapter( - child: BadgeList( - badges: data.badges, - ).padding(horizontal: 24, bottom: 24), - ), - SliverToBoxAdapter( - child: Column( - spacing: 12, - children: [ - LevelingProgressCard( - level: data.profile.level, - experience: data.profile.experience, - progress: data.profile.levelingProgress, - ), - if (data.profile.verification != null) - VerificationStatusCard( - mark: data.profile.verification!, - ), - ], - ).padding(horizontal: 20), - ), - - SliverToBoxAdapter( - child: const Divider(height: 1).padding(vertical: 24), - ), - SliverToBoxAdapter( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - spacing: 24, - children: [ - if (buildSubcolumn(data).isNotEmpty) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 2, - children: buildSubcolumn(data), - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('bio').tr().bold(), - Text( - data.profile.bio.isEmpty - ? 'descriptionNone'.tr() - : data.profile.bio, + ) + : CustomScrollView( + slivers: [ + SliverAppBar( + foregroundColor: appbarColor.value, + expandedHeight: 180, + pinned: true, + leading: PageBackButton( + color: appbarColor.value, + shadows: [appbarShadow], ), - ], - ), - if (data.profile.timeZone.isNotEmpty) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('timeZone').tr().bold(), - Row( - crossAxisAlignment: CrossAxisAlignment.baseline, - textBaseline: TextBaseline.alphabetic, - spacing: 6, - children: [ - Text(data.profile.timeZone), - Text( - getTzInfo( - data.profile.timeZone, - ).$2.formatCustomGlobal('HH:mm'), - ), - Text( - getTzInfo( - data.profile.timeZone, - ).$1.formatOffsetLocal(), - ).fontSize(11), - Text( - 'UTC${getTzInfo(data.profile.timeZone).$1.formatOffset()}', - ).fontSize(11).opacity(0.75), - ], - ), - ], - ), - ], - ).padding(horizontal: 24), - ), - - if (user.value != null) - SliverToBoxAdapter( - child: const Divider( - height: 1, - ).padding(top: 24, bottom: 12), - ), - if (user.value != null) - SliverToBoxAdapter( - child: Row( - spacing: 8, - children: [ - if (accountRelationship.value == null || - accountRelationship.value!.status > -100) - Expanded( - child: FilledButton.icon( - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - accountRelationship.value == null - ? null - : Theme.of(context).colorScheme.secondary, - ), - foregroundColor: WidgetStatePropertyAll( - accountRelationship.value == null - ? null - : Theme.of( - context, - ).colorScheme.onSecondary, + flexibleSpace: Stack( + children: [ + Positioned.fill( + child: + data.profile.background?.id != null + ? CloudImageWidget( + file: data.profile.background, + ) + : Container( + color: + Theme.of( + context, + ).appBarTheme.backgroundColor, + ), + ), + FlexibleSpaceBar( + title: Text( + data.nick, + style: TextStyle( + color: + appbarColor.value ?? + Theme.of( + context, + ).appBarTheme.foregroundColor, + shadows: [appbarShadow], + ), ), ), - onPressed: relationshipAction, - label: - Text( - accountRelationship.value == null - ? 'addFriendShort' - : 'added', - ).tr(), - icon: - accountRelationship.value == null - ? const Icon(Symbols.person_add) - : const Icon(Symbols.person_check), - ), + ], ), - if (accountRelationship.value == null || - accountRelationship.value!.status <= -100) - Expanded( - child: FilledButton.icon( - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - accountRelationship.value == null - ? null - : Theme.of(context).colorScheme.secondary, - ), - foregroundColor: WidgetStatePropertyAll( - accountRelationship.value == null - ? null - : Theme.of( - context, - ).colorScheme.onSecondary, - ), + ), + SliverToBoxAdapter(child: accountBasicInfo(data)), + if (data.badges.isNotEmpty) + SliverToBoxAdapter( + child: BadgeList( + badges: data.badges, + ).padding(horizontal: 24, bottom: 24), + ), + SliverToBoxAdapter( + child: Column( + spacing: 12, + children: [ + LevelingProgressCard( + level: data.profile.level, + experience: data.profile.experience, + progress: data.profile.levelingProgress, ), - onPressed: blockAction, - label: - Text( - accountRelationship.value == null - ? 'blockUser' - : 'unblockUser', - ).tr(), - icon: - accountRelationship.value == null - ? const Icon(Symbols.block) - : const Icon(Symbols.person_cancel), - ), - ), + if (data.profile.verification != null) + VerificationStatusCard( + mark: data.profile.verification!, + ), + ], + ).padding(horizontal: 20), + ), + + SliverToBoxAdapter(child: accountProfileDetail(data)), + + if (user.value != null) + SliverToBoxAdapter(child: accountAction(data)), + SliverToBoxAdapter( + child: Column( + children: [ + FortuneGraphWidget( + events: accountEvents, + eventCalanderUser: data.name, + ), + ], + ).padding(all: 8), + ), ], - ).padding(horizontal: 16), - ), - SliverToBoxAdapter( - child: Row( - spacing: 8, - children: [ - Expanded( - child: FilledButton.icon( - onPressed: directMessageAction, - icon: const Icon(Symbols.message), - label: - Text( - accountChat.value == null - ? 'createDirectMessage' - : 'gotoDirectMessage', - maxLines: 1, - ).tr(), - ), - ), - IconButton.filled( - onPressed: () { - showAbuseReportSheet( - context, - resourceIdentifier: 'account/${data.id}', - ); - }, - icon: Icon( - Symbols.flag, - color: Theme.of(context).colorScheme.onError, - ), - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - Theme.of(context).colorScheme.error, - ), - ), - ), - ], - ).padding(horizontal: 16, top: 4), - ), - SliverToBoxAdapter( - child: const Divider(height: 1).padding(top: 12), - ), - SliverToBoxAdapter( - child: Column( - children: [ - FortuneGraphWidget( - events: accountEvents, - eventCalanderUser: data.name, - ), - ], - ).padding(all: 8), - ), - ], - ), + ), ), error: (error, stackTrace) => AppScaffold( diff --git a/lib/screens/auth/create_account.dart b/lib/screens/auth/create_account.dart index dd2bdd7..4cab0d8 100644 --- a/lib/screens/auth/create_account.dart +++ b/lib/screens/auth/create_account.dart @@ -73,7 +73,7 @@ class CreateAccountScreen extends HookConsumerWidget { } return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( leading: const PageBackButton(), title: Text('createAccount').tr(), diff --git a/lib/screens/auth/login.dart b/lib/screens/auth/login.dart index e6502a1..ded3b7b 100644 --- a/lib/screens/auth/login.dart +++ b/lib/screens/auth/login.dart @@ -55,7 +55,7 @@ class LoginScreen extends HookConsumerWidget { final factorPicked = useState(null); return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( leading: const PageBackButton(), title: Text('login').tr(), diff --git a/lib/screens/chat/call.dart b/lib/screens/chat/call.dart index 7a26027..0f1694f 100644 --- a/lib/screens/chat/call.dart +++ b/lib/screens/chat/call.dart @@ -40,7 +40,7 @@ class CallScreen extends HookConsumerWidget { ); return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( leading: PageBackButton(), title: Column( diff --git a/lib/screens/developers/hub.dart b/lib/screens/developers/hub.dart index 0cba940..0addafb 100644 --- a/lib/screens/developers/hub.dart +++ b/lib/screens/developers/hub.dart @@ -111,7 +111,7 @@ class DeveloperHubScreen extends HookConsumerWidget { ); return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( leading: !isWide ? const PageBackButton() : null, title: Text('developerHub').tr(), diff --git a/lib/screens/discovery/realms.dart b/lib/screens/discovery/realms.dart index 78a816f..ac6110f 100644 --- a/lib/screens/discovery/realms.dart +++ b/lib/screens/discovery/realms.dart @@ -17,7 +17,7 @@ class DiscoveryRealmsScreen extends HookConsumerWidget { final currentQuery = useState(null); return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar(title: Text('discoverRealms'.tr())), body: Stack( children: [ diff --git a/lib/screens/explore.dart b/lib/screens/explore.dart index 6b1f607..d85d66d 100644 --- a/lib/screens/explore.dart +++ b/lib/screens/explore.dart @@ -87,7 +87,7 @@ class ExploreScreen extends HookConsumerWidget { final user = ref.watch(userInfoProvider); return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( toolbarHeight: 0, bottom: PreferredSize( diff --git a/lib/screens/posts/compose.dart b/lib/screens/posts/compose.dart index e1e44ca..60fb41f 100644 --- a/lib/screens/posts/compose.dart +++ b/lib/screens/posts/compose.dart @@ -53,13 +53,13 @@ class PostEditScreen extends HookConsumerWidget { data: (post) => PostComposeScreen(originalPost: post), loading: () => AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar(leading: const PageBackButton()), body: const Center(child: CircularProgressIndicator()), ), error: (e, _) => AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar(leading: const PageBackButton()), body: Text('Error: $e', textAlign: TextAlign.center), ), @@ -287,7 +287,7 @@ class PostComposeScreen extends HookConsumerWidget { } }, child: AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( leading: const PageBackButton(), actions: [ diff --git a/lib/screens/posts/compose_article.dart b/lib/screens/posts/compose_article.dart index b8f7a28..87ff9e5 100644 --- a/lib/screens/posts/compose_article.dart +++ b/lib/screens/posts/compose_article.dart @@ -352,7 +352,7 @@ class ArticleComposeScreen extends HookConsumerWidget { } }, child: AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( leading: const PageBackButton(), title: ValueListenableBuilder( diff --git a/lib/screens/posts/post_detail.dart b/lib/screens/posts/post_detail.dart index e5f953e..a904fd5 100644 --- a/lib/screens/posts/post_detail.dart +++ b/lib/screens/posts/post_detail.dart @@ -54,7 +54,7 @@ class PostDetailScreen extends HookConsumerWidget { final user = ref.watch(userInfoProvider); return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar(title: const Text('Post')), body: postState.when( data: (post) { diff --git a/lib/screens/posts/post_search.dart b/lib/screens/posts/post_search.dart index 43f9098..1ab644b 100644 --- a/lib/screens/posts/post_search.dart +++ b/lib/screens/posts/post_search.dart @@ -110,7 +110,7 @@ class _PostSearchScreenState extends ConsumerState { @override Widget build(BuildContext context) { return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( title: TextField( controller: _searchController, diff --git a/lib/screens/posts/pub_profile.dart b/lib/screens/posts/pub_profile.dart index d0b0fb7..a19f0e2 100644 --- a/lib/screens/posts/pub_profile.dart +++ b/lib/screens/posts/pub_profile.dart @@ -259,7 +259,7 @@ class PublisherProfileScreen extends HookConsumerWidget { return publisher.when( data: (data) => AppScaffold( - noBackground: false, + isNoBackground: false, appBar: isWideScreen(context) ? AppBar( @@ -389,13 +389,13 @@ class PublisherProfileScreen extends HookConsumerWidget { ), error: (error, stackTrace) => AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar(leading: const PageBackButton()), body: Center(child: Text(error.toString())), ), loading: () => AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar(leading: const PageBackButton()), body: Center(child: CircularProgressIndicator()), ), diff --git a/lib/screens/realm/realm_detail.dart b/lib/screens/realm/realm_detail.dart index 08db45e..a02dfc8 100644 --- a/lib/screens/realm/realm_detail.dart +++ b/lib/screens/realm/realm_detail.dart @@ -79,7 +79,7 @@ class RealmDetailScreen extends HookConsumerWidget { ); return AppScaffold( - noBackground: false, + isNoBackground: false, body: realmState.when( loading: () => const Center(child: CircularProgressIndicator()), error: (error, _) => Center(child: Text('Error: $error')), diff --git a/lib/screens/realm/realms.dart b/lib/screens/realm/realms.dart index 3c50f4d..a6d7453 100644 --- a/lib/screens/realm/realms.dart +++ b/lib/screens/realm/realms.dart @@ -41,7 +41,7 @@ class RealmListScreen extends HookConsumerWidget { final realmInvites = ref.watch(realmInvitesProvider); return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( title: const Text('realms').tr(), actions: [ @@ -279,7 +279,7 @@ class EditRealmScreen extends HookConsumerWidget { } return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( title: Text(slug == null ? 'createRealm'.tr() : 'editRealm'.tr()), leading: const PageBackButton(), diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index 289d9db..b862556 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -552,7 +552,7 @@ class SettingsScreen extends HookConsumerWidget { } return AppScaffold( - noBackground: false, + isNoBackground: false, appBar: AppBar( title: Text('settings').tr(), actions: diff --git a/lib/widgets/app_scaffold.dart b/lib/widgets/app_scaffold.dart index 577bfdc..bb5a077 100644 --- a/lib/widgets/app_scaffold.dart +++ b/lib/widgets/app_scaffold.dart @@ -165,7 +165,7 @@ class AppScaffold extends StatelessWidget { final AppBar? appBar; final DrawerCallback? onDrawerChanged; final DrawerCallback? onEndDrawerChanged; - final bool? noBackground; + final bool? isNoBackground; final bool? extendBody; const AppScaffold({ @@ -181,7 +181,7 @@ class AppScaffold extends StatelessWidget { this.endDrawer, this.onDrawerChanged, this.onEndDrawerChanged, - this.noBackground, + this.isNoBackground, this.extendBody, }); @@ -190,7 +190,7 @@ class AppScaffold extends StatelessWidget { final appBarHeight = appBar?.preferredSize.height ?? 0; final safeTop = MediaQuery.of(context).padding.top; - final noBackground = this.noBackground ?? isWideScreen(context); + final noBackground = isNoBackground ?? isWideScreen(context); final content = Column( children: [ diff --git a/lib/widgets/content/cloud_files.dart b/lib/widgets/content/cloud_files.dart index 4557db7..cb26046 100644 --- a/lib/widgets/content/cloud_files.dart +++ b/lib/widgets/content/cloud_files.dart @@ -1,8 +1,11 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/file.dart'; import 'package:island/pods/config.dart'; +import 'package:island/services/time.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:styled_widget/styled_widget.dart'; @@ -45,7 +48,7 @@ class CloudFileWidget extends ConsumerWidget { ), "video" => AspectRatio( aspectRatio: ratio, - child: UniversalVideo(uri: uri, aspectRatio: ratio), + child: CloudVideoWidget(item: item), ), _ => Text('Unable render for ${item.mimeType}'), }; @@ -58,6 +61,119 @@ class CloudFileWidget extends ConsumerWidget { } } +class CloudVideoWidget extends HookConsumerWidget { + final SnCloudFile item; + const CloudVideoWidget({super.key, required this.item}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final open = useState(false); + + final serverUrl = ref.watch(serverUrlProvider); + final uri = '$serverUrl/drive/files/${item.id}'; + + var ratio = + item.fileMeta?['ratio'] is num + ? item.fileMeta!['ratio'].toDouble() + : 1.0; + if (ratio == 0) ratio = 1.0; + + if (open.value) { + return UniversalVideo(uri: uri, aspectRatio: ratio, autoplay: true); + } + + return GestureDetector( + child: Stack( + children: [ + UniversalImage(uri: '$uri?thumbnail=true'), + Positioned.fill( + child: Center( + child: const Icon( + Symbols.play_arrow, + fill: 1, + size: 32, + shadows: [ + BoxShadow( + color: Colors.black54, + offset: Offset(1, 1), + spreadRadius: 8, + blurRadius: 8, + ), + ], + ), + ), + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + spacing: 8, + children: [ + if (item.fileMeta?['duration'] != null) + Text( + Duration( + milliseconds: + ((item.fileMeta?['duration'] as num) * 1000) + .toInt(), + ).formatDuration(), + style: TextStyle( + shadows: [ + BoxShadow( + color: Colors.black54, + offset: Offset(1, 1), + spreadRadius: 8, + blurRadius: 8, + ), + ], + ), + ), + if (item.fileMeta?['bit_rate'] != null) + Text( + '${int.parse(item.fileMeta?['bit_rate'] as String) ~/ 1000} Kbps', + style: TextStyle( + shadows: [ + BoxShadow( + color: Colors.black54, + offset: Offset(1, 1), + spreadRadius: 8, + blurRadius: 8, + ), + ], + ), + ), + ], + ), + Text( + item.name, + style: TextStyle( + fontWeight: FontWeight.bold, + shadows: [ + BoxShadow( + color: Colors.black54, + offset: Offset(1, 1), + spreadRadius: 8, + blurRadius: 8, + ), + ], + ), + ), + ], + ), + ).padding(horizontal: 16, bottom: 12), + ], + ), + onTap: () { + open.value = true; + }, + ); + } +} + class CloudImageWidget extends ConsumerWidget { final String? fileId; final SnCloudFile? file; diff --git a/lib/widgets/content/video.native.dart b/lib/widgets/content/video.native.dart index bba219c..85bfa7e 100644 --- a/lib/widgets/content/video.native.dart +++ b/lib/widgets/content/video.native.dart @@ -11,10 +11,12 @@ import 'package:media_kit_video/media_kit_video.dart'; class UniversalVideo extends ConsumerStatefulWidget { final String uri; final double aspectRatio; + final bool autoplay; const UniversalVideo({ super.key, required this.uri, this.aspectRatio = 16 / 9, + this.autoplay = false, }); @override @@ -47,7 +49,7 @@ class _UniversalVideoState extends ConsumerState { log('[MediaPlayer] Hit cache: $url'); } - _player!.open(Media(uri), play: false); + _player!.open(Media(uri), play: widget.autoplay); } @override diff --git a/lib/widgets/publisher/publisher_card.dart b/lib/widgets/publisher/publisher_card.dart index 59dc9f3..a9abc64 100644 --- a/lib/widgets/publisher/publisher_card.dart +++ b/lib/widgets/publisher/publisher_card.dart @@ -27,9 +27,13 @@ class PublisherCard extends ConsumerWidget { Widget card = Card( clipBehavior: Clip.antiAlias, + margin: EdgeInsets.zero, child: InkWell( onTap: () { - context.pushNamed('publisherProfile', pathParameters: {'name': publisher.name}); + context.pushNamed( + 'publisherProfile', + pathParameters: {'name': publisher.name}, + ); }, child: AspectRatio( aspectRatio: 16 / 7, diff --git a/lib/widgets/realm/realm_card.dart b/lib/widgets/realm/realm_card.dart index adb9582..dba37d2 100644 --- a/lib/widgets/realm/realm_card.dart +++ b/lib/widgets/realm/realm_card.dart @@ -29,9 +29,13 @@ class RealmCard extends ConsumerWidget { Widget card = Card( clipBehavior: Clip.antiAlias, + margin: EdgeInsets.zero, child: InkWell( onTap: () { - context.pushNamed('realmDetail', pathParameters: {'slug': realm.slug}); + context.pushNamed( + 'realmDetail', + pathParameters: {'slug': realm.slug}, + ); }, child: AspectRatio( aspectRatio: 16 / 7, diff --git a/lib/widgets/web_article_card.dart b/lib/widgets/web_article_card.dart index 8a67390..403a5e1 100644 --- a/lib/widgets/web_article_card.dart +++ b/lib/widgets/web_article_card.dart @@ -28,6 +28,7 @@ class WebArticleCard extends StatelessWidget { return ConstrainedBox( constraints: BoxConstraints(maxWidth: maxWidth ?? double.infinity), child: Card( + margin: EdgeInsets.zero, clipBehavior: Clip.antiAlias, child: InkWell( onTap: () => _onTap(context),