From b5155ebc5f667e15d56f9a08caa4b2433bd2807e Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 3 Apr 2025 00:44:34 +0800 Subject: [PATCH] :recycle: New album --- lib/screens/account.dart | 3 +- lib/screens/album.dart | 204 ++++++++++++-------- lib/screens/post/publisher_page.dart | 1 + lib/widgets/attachment/attachment_zoom.dart | 8 +- 4 files changed, 127 insertions(+), 89 deletions(-) diff --git a/lib/screens/account.dart b/lib/screens/account.dart index 5bc6b67..e37c4cd 100644 --- a/lib/screens/account.dart +++ b/lib/screens/account.dart @@ -15,7 +15,6 @@ import 'package:surface/providers/websocket.dart'; import 'package:surface/types/account.dart'; import 'package:surface/widgets/account/account_image.dart'; import 'package:surface/widgets/account/account_status.dart'; -import 'package:surface/widgets/app_bar_leading.dart'; import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/universal_image.dart'; @@ -112,7 +111,7 @@ class AccountScreen extends StatelessWidget { return AppScaffold( noBackground: ResponsiveScaffold.getIsExpand(context), appBar: AppBar( - leading: AutoAppBarLeading(), + leading: const PageBackButton(), title: Text("screenAccount").tr(), flexibleSpace: ua.user != null && ua.user!.banner.isNotEmpty ? Stack( diff --git a/lib/screens/album.dart b/lib/screens/album.dart index 585fe43..a6aea47 100644 --- a/lib/screens/album.dart +++ b/lib/screens/album.dart @@ -1,19 +1,21 @@ import 'package:dismissible_page/dismissible_page.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:gap/gap.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:material_symbols_icons/symbols.dart'; +import 'package:path/path.dart' show withoutExtension; import 'package:provider/provider.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:surface/providers/sn_network.dart'; +import 'package:surface/providers/userinfo.dart'; import 'package:surface/types/attachment.dart'; -import 'package:surface/widgets/attachment/attachment_zoom.dart'; import 'package:surface/widgets/attachment/attachment_item.dart'; +import 'package:surface/widgets/attachment/attachment_zoom.dart'; import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:uuid/uuid.dart'; +import 'package:very_good_infinite_list/very_good_infinite_list.dart'; class AlbumScreen extends StatefulWidget { const AlbumScreen({super.key}); @@ -48,6 +50,8 @@ class _AlbumScreenState extends State { Future _fetchAttachments() async { setState(() => _isBusy = true); + final ua = context.read(); + const uuid = Uuid(); try { @@ -55,10 +59,11 @@ class _AlbumScreenState extends State { final resp = await sn.client.get('/cgi/uc/attachments', queryParameters: { 'take': 10, 'offset': _attachments.length, + 'author': ua.user?.name, }); final attachments = List.from( resp.data['data']?.map((e) => SnAttachment.fromJson(e)) ?? [], - ).where((e) => e.mimetype.startsWith('image')).toList(); + ); _attachments.addAll(attachments); _heroTags.addAll(_attachments.map((_) => uuid.v4())); @@ -97,94 +102,127 @@ class _AlbumScreenState extends State { @override Widget build(BuildContext context) { return AppScaffold( - body: CustomScrollView( - controller: _scrollController, - slivers: [ - SliverAppBar( - leading: PageBackButton(), - title: Text('screenAlbum').tr(), - ), - SliverToBoxAdapter( - child: Card( - child: Row( - children: [ - SizedBox( - width: 80, - height: 80, - child: CircularProgressIndicator( - value: _billing?.includedRatio ?? 0, - strokeWidth: 8, - backgroundColor: - Theme.of(context).colorScheme.surfaceContainerHigh, + appBar: AppBar( + leading: PageBackButton(), + title: Text('screenAlbum').tr(), + ), + body: Column( + children: [ + Card( + margin: EdgeInsets.zero, + child: Row( + children: [ + SizedBox( + width: 80, + height: 80, + child: CircularProgressIndicator( + value: _billing?.includedRatio ?? 0, + strokeWidth: 8, + backgroundColor: + Theme.of(context).colorScheme.surfaceContainerHigh, + ), + ).padding(all: 12), + const Gap(24), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('attachmentBillingUploaded').tr().bold(), + Text( + (_billing?.currentBytes ?? 0).formatBytes(decimals: 4), + style: GoogleFonts.robotoMono(), + ), + Text('attachmentBillingDiscount').tr().bold(), + Text( + '${(_billing?.discountFileSize ?? 0).formatBytes(decimals: 2)} · ${((_billing?.includedRatio ?? 0) * 100).toStringAsFixed(2)}%', + style: GoogleFonts.robotoMono(), + ), + ], + ), + ), + Tooltip( + message: 'attachmentBillingHint'.tr(), + child: IconButton( + icon: const Icon(Symbols.info), + onPressed: () {}, + ), + ), + ], + ).padding(horizontal: 24, vertical: 8), + ).padding(horizontal: 8, top: 8), + Expanded( + child: InfiniteList( + padding: EdgeInsets.only(top: 8), + itemCount: _attachments.length, + isLoading: _isBusy, + hasReachedMax: + _totalCount != null && _attachments.length >= _totalCount!, + onFetchData: _fetchAttachments, + itemBuilder: (context, index) { + final ele = _attachments[index]; + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + child: AspectRatio( + aspectRatio: (ele.data['ratio'] ?? 1).toDouble(), + child: AttachmentItem( + data: ele, + heroTag: _heroTags[index], + onZoom: () { + context.pushTransparentRoute( + AttachmentZoomView( + data: [ele], + ), + backgroundColor: Colors.black.withOpacity(0.7), + rootNavigator: true, + ); + }, + ), + ), ), - ).padding(all: 12), - const Gap(24), - Expanded( - child: Column( + Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('attachmentBillingUploaded').tr().bold(), - Text( - (_billing?.currentBytes ?? 0) - .formatBytes(decimals: 4), - style: GoogleFonts.robotoMono(), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(ele.name), + if (ele.alt != withoutExtension(ele.name)) + Text(ele.alt), + Text(DateFormat().format(ele.createdAt)), + const Gap(4), + Text(ele.size.formatBytes()).fontSize(12), + ], + ).padding(horizontal: 16, vertical: 12), ), - Text('attachmentBillingDiscount').tr().bold(), - Text( - '${(_billing?.discountFileSize ?? 0).formatBytes(decimals: 2)} · ${((_billing?.includedRatio ?? 0) * 100).toStringAsFixed(2)}%', - style: GoogleFonts.robotoMono(), + Padding( + padding: EdgeInsets.only(left: 12, right: 12, top: 4), + child: IconButton( + padding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + icon: const Icon(Symbols.info), + onPressed: () { + showModalBottomSheet( + context: context, + builder: (context) => AttachmentZoomDetailPopup( + data: ele, + ), + ); + }, + ), ), ], ), - ), - Tooltip( - message: 'attachmentBillingHint'.tr(), - child: IconButton( - icon: const Icon(Symbols.info), - onPressed: () {}, - ), - ), - ], - ).padding(horizontal: 24, vertical: 8), - ), - ), - SliverMasonryGrid.extent( - childCount: _attachments.length, - maxCrossAxisExtent: 320, - mainAxisSpacing: 4, - crossAxisSpacing: 4, - itemBuilder: (context, idx) { - final attachment = _attachments[idx]; - return GestureDetector( - child: ClipRRect( - child: AspectRatio( - aspectRatio: attachment.metadata['ratio']?.toDouble() ?? 1, - child: AttachmentItem( - data: attachment, - heroTag: _heroTags[idx], - ), - ), - ), - onTap: () { - context.pushTransparentRoute( - AttachmentZoomView( - data: [attachment], - heroTags: [_heroTags[idx]], - ), - backgroundColor: Colors.black.withOpacity(0.7), - rootNavigator: true, - ); - }, - ); - }, - ), - if (_isBusy) - SliverToBoxAdapter( - child: Padding( - padding: const EdgeInsets.all(24), - child: const CircularProgressIndicator(), - ).center(), + ], + ); + }, + separatorBuilder: (_, __) => const Gap(8), ), + ) ], ), ); diff --git a/lib/screens/post/publisher_page.dart b/lib/screens/post/publisher_page.dart index 770bce2..3138fe6 100644 --- a/lib/screens/post/publisher_page.dart +++ b/lib/screens/post/publisher_page.dart @@ -303,6 +303,7 @@ class _PostPublisherScreenState extends State ), child: SliverAppBar( expandedHeight: _appBarHeight, + leading: const PageBackButton(), title: _publisher == null ? Text('loading').tr() : RichText( diff --git a/lib/widgets/attachment/attachment_zoom.dart b/lib/widgets/attachment/attachment_zoom.dart index 9682c44..8c13747 100644 --- a/lib/widgets/attachment/attachment_zoom.dart +++ b/lib/widgets/attachment/attachment_zoom.dart @@ -373,7 +373,7 @@ class _AttachmentZoomViewState extends State { _showDetail = true; showModalBottomSheet( context: context, - builder: (context) => _AttachmentZoomDetailPopup( + builder: (context) => AttachmentZoomDetailPopup( data: widget.data.elementAt(_page), ), ).then((_) { @@ -403,7 +403,7 @@ class _AttachmentZoomViewState extends State { _showDetail = true; showModalBottomSheet( context: context, - builder: (context) => _AttachmentZoomDetailPopup( + builder: (context) => AttachmentZoomDetailPopup( data: widget.data.elementAt(_page), ), ).then((_) { @@ -416,10 +416,10 @@ class _AttachmentZoomViewState extends State { } } -class _AttachmentZoomDetailPopup extends StatelessWidget { +class AttachmentZoomDetailPopup extends StatelessWidget { final SnAttachment data; - const _AttachmentZoomDetailPopup({required this.data}); + const AttachmentZoomDetailPopup({required this.data}); @override Widget build(BuildContext context) {