diff --git a/lib/screens/post/post_publisher.dart b/lib/screens/post/post_publisher.dart index a25ef1f..745e044 100644 --- a/lib/screens/post/post_publisher.dart +++ b/lib/screens/post/post_publisher.dart @@ -6,6 +6,7 @@ import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:provider/provider.dart'; +import 'package:sliver_tools/sliver_tools.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:surface/providers/post.dart'; import 'package:surface/providers/sn_network.dart'; @@ -111,161 +112,310 @@ class _PostPublisherScreenState extends State { Widget build(BuildContext context) { final imageHeight = _appBarHeight + kToolbarHeight + 8; + const labelShadows = [ + Shadow( + offset: Offset(1, 1), + blurRadius: 5.0, + color: Color.fromARGB(255, 0, 0, 0), + ), + ]; + final sn = context.read(); - return Scaffold( - body: CustomScrollView( - controller: _scrollController, - slivers: [ - SliverAppBar( - expandedHeight: _appBarHeight, - title: Text(_publisher?.nick ?? 'loading'.tr()), - pinned: true, - flexibleSpace: _publisher != null - ? Stack( - fit: StackFit.expand, - children: [ - UniversalImage( - sn.getAttachmentUrl(_publisher!.banner), - fit: BoxFit.cover, - height: imageHeight, - width: _appBarWidth, - cacheHeight: imageHeight, - cacheWidth: _appBarWidth, - ), - Positioned( - top: 0, - left: 0, - right: 0, - height: 56 + MediaQuery.of(context).padding.top, - child: ClipRect( - child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: _appBarBlur, - sigmaY: _appBarBlur, - ), - child: Container( - color: Colors.black.withOpacity( - clampDouble(_appBarBlur * 0.1, 0, 0.5), - ), - ), - ), - ), - ), - ], - ) - : null, - ), - if (_publisher != null) - SliverToBoxAdapter( - child: Container( - constraints: const BoxConstraints(maxWidth: 640), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + + // TODO fix loading on different type + return DefaultTabController( + length: 3, + child: Scaffold( + body: NestedScrollView( + controller: _scrollController, + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { + return [ + SliverOverlapAbsorber( + handle: + NestedScrollView.sliverOverlapAbsorberHandleFor(context), + sliver: MultiSliver( children: [ - Row( - children: [ - AccountImage( - content: _publisher!.avatar, - radius: 28, - ), - const Gap(16), - Expanded( + SliverAppBar( + expandedHeight: _appBarHeight, + title: _publisher == null + ? Text('loading').tr() + : RichText( + textAlign: TextAlign.center, + text: TextSpan(children: [ + TextSpan( + text: _publisher!.nick, + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith( + color: Colors.white, + shadows: labelShadows, + ), + ), + const TextSpan(text: '\n'), + TextSpan( + text: '@${_publisher!.name}', + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( + color: Colors.white, + shadows: labelShadows, + ), + ), + ]), + ), + pinned: true, + flexibleSpace: _publisher != null + ? Stack( + fit: StackFit.expand, + children: [ + UniversalImage( + sn.getAttachmentUrl(_publisher!.banner), + fit: BoxFit.cover, + height: imageHeight, + width: _appBarWidth, + cacheHeight: imageHeight, + cacheWidth: _appBarWidth, + ), + Positioned( + top: 0, + left: 0, + right: 0, + height: + 56 + MediaQuery.of(context).padding.top, + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: _appBarBlur, + sigmaY: _appBarBlur, + ), + child: Container( + color: Colors.black.withOpacity( + clampDouble( + _appBarBlur * 0.1, 0, 0.5), + ), + ), + ), + ), + ), + ], + ) + : null, + ), + if (_publisher != null) + SliverToBoxAdapter( + child: Container( + constraints: const BoxConstraints(maxWidth: 640), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - _publisher!.nick, - style: Theme.of(context).textTheme.titleMedium, - ).bold(), - Text('@${_publisher!.name}').fontSize(13), + Row( + children: [ + AccountImage( + content: _publisher!.avatar, + radius: 28, + ), + const Gap(16), + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + _publisher!.nick, + style: Theme.of(context) + .textTheme + .titleMedium, + ).bold(), + Text('@${_publisher!.name}') + .fontSize(13), + ], + ), + ), + ElevatedButton( + style: ButtonStyle( + elevation: WidgetStatePropertyAll(0)), + onPressed: () {}, + child: Text('subscribe').tr(), + ), + ], + ).padding(right: 8), + const Gap(12), + Text(_publisher!.description) + .padding(horizontal: 8), + const Gap(12), + Column( + children: [ + Row( + children: [ + const Icon(Symbols.calendar_add_on), + const Gap(8), + Text('publisherJoinedAt').tr(args: [ + DateFormat('y/M/d') + .format(_publisher!.createdAt) + ]), + ], + ), + Row( + children: [ + const Icon(Symbols.trending_up), + const Gap(8), + Text('publisherSocialPointTotal').plural( + _publisher!.totalUpvote - + _publisher!.totalDownvote, + ), + ], + ), + Row( + children: [ + const Icon(Symbols.tools_wrench), + const Gap(8), + InkWell( + child: Text('publisherRunBy').tr(args: [ + '@${_account?.name ?? 'unknown'}', + ]), + onTap: () {}, + ), + const Gap(8), + AccountImage( + content: _account?.avatar, radius: 8), + ], + ), + ], + ).padding(horizontal: 8), ], + ).padding(all: 16), + ).center(), + ), + SliverToBoxAdapter(child: const Divider(height: 1)), + TabBar( + tabs: [ + Tab( + icon: Icon( + Symbols.pages, + color: Theme.of(context).colorScheme.onSurface, ), ), - ElevatedButton( - style: - ButtonStyle(elevation: WidgetStatePropertyAll(0)), - onPressed: () {}, - child: Text('subscribe').tr(), + Tab( + icon: Icon( + Symbols.sticky_note_2, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + Tab( + icon: Icon( + Symbols.article, + color: Theme.of(context).colorScheme.onSurface, + ), ), ], - ).padding(right: 8), - const Gap(12), - Text(_publisher!.description).padding(horizontal: 8), - const Gap(12), - Column( - children: [ - Row( - children: [ - const Icon(Symbols.calendar_add_on), - const Gap(8), - Text('publisherJoinedAt').tr(args: [ - DateFormat('y/M/d').format(_publisher!.createdAt) - ]), - ], - ), - Row( - children: [ - const Icon(Symbols.trending_up), - const Gap(8), - Text('publisherSocialPointTotal').plural( - _publisher!.totalUpvote - - _publisher!.totalDownvote, - ), - ], - ), - Row( - children: [ - const Icon(Symbols.tools_wrench), - const Gap(8), - InkWell( - child: Text('publisherRunBy').tr(args: [ - '@${_account?.name ?? 'unknown'}', - ]), - onTap: () {}, - ), - const Gap(8), - AccountImage(content: _account?.avatar, radius: 8), - ], - ), - ], - ).padding(horizontal: 8), + ), + SliverToBoxAdapter(child: const Divider(height: 1)), + Gap(MediaQuery.of(context).padding.top), ], - ).padding(all: 16), - ).center(), - ), - SliverToBoxAdapter( - child: const Divider(height: 1), - ), - SliverInfiniteList( - itemCount: _posts.length, - isLoading: _isBusy, - hasReachedMax: _postCount != null && _posts.length >= _postCount!, - onFetchData: _fetchPosts, - itemBuilder: (context, idx) { - return GestureDetector( - child: PostItem( - data: _posts[idx], - maxWidth: 640, - onChanged: (data) { - setState(() => _posts[idx] = data); - }, - onDeleted: () { - _posts.clear(); - _fetchPosts(); - }, ), - onTap: () { - GoRouter.of(context).pushNamed( - 'postDetail', - pathParameters: {'slug': _posts[idx].id.toString()}, - extra: _posts[idx], + ), + ]; + }, + body: TabBarView( + children: [ + InfiniteList( + itemCount: _posts.length, + isLoading: _isBusy, + hasReachedMax: + _postCount != null && _posts.length >= _postCount!, + onFetchData: _fetchPosts, + itemBuilder: (context, idx) { + return GestureDetector( + child: PostItem( + data: _posts[idx], + maxWidth: 640, + onChanged: (data) { + setState(() => _posts[idx] = data); + }, + onDeleted: () { + _posts.clear(); + _fetchPosts(); + }, + ), + onTap: () { + GoRouter.of(context).pushNamed( + 'postDetail', + pathParameters: {'slug': _posts[idx].id.toString()}, + extra: _posts[idx], + ); + }, ); }, - ); - }, - separatorBuilder: (context, index) => const Divider(height: 1), + separatorBuilder: (context, index) => const Divider(height: 1), + ), + InfiniteList( + itemCount: _posts.where((e) => e.type == 'story').length, + isLoading: _isBusy, + hasReachedMax: + _postCount != null && _posts.length >= _postCount!, + onFetchData: _fetchPosts, + itemBuilder: (context, idx) { + return GestureDetector( + child: PostItem( + data: + _posts.where((e) => e.type == 'story').elementAt(idx), + maxWidth: 640, + onChanged: (data) { + setState(() => _posts[idx] = data); + }, + onDeleted: () { + _posts.clear(); + _fetchPosts(); + }, + ), + onTap: () { + GoRouter.of(context).pushNamed( + 'postDetail', + pathParameters: {'slug': _posts[idx].id.toString()}, + extra: _posts[idx], + ); + }, + ); + }, + separatorBuilder: (context, index) => const Divider(height: 1), + ), + InfiniteList( + itemCount: _posts.where((e) => e.type == 'article').length, + isLoading: _isBusy, + hasReachedMax: + _postCount != null && _posts.length >= _postCount!, + onFetchData: _fetchPosts, + itemBuilder: (context, idx) { + return GestureDetector( + child: PostItem( + data: _posts + .where((e) => e.type == 'article') + .elementAt(idx), + maxWidth: 640, + onChanged: (data) { + setState(() => _posts[idx] = data); + }, + onDeleted: () { + _posts.clear(); + _fetchPosts(); + }, + ), + onTap: () { + GoRouter.of(context).pushNamed( + 'postDetail', + pathParameters: {'slug': _posts[idx].id.toString()}, + extra: _posts[idx], + ); + }, + ); + }, + separatorBuilder: (context, index) => const Divider(height: 1), + ), + ], ), - ], + ), ), ); } diff --git a/pubspec.lock b/pubspec.lock index 81a220d..4ad848f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1551,6 +1551,14 @@ packages: description: flutter source: sdk version: "0.0.99" + sliver_tools: + dependency: "direct main" + description: + name: sliver_tools + sha256: eae28220badfb9d0559207badcbbc9ad5331aac829a88cb0964d330d2a4636a6 + url: "https://pub.dev" + source: hosted + version: "0.2.12" source_gen: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 51dfa00..8bb0e5d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -94,6 +94,7 @@ dependencies: permission_handler: ^11.3.1 flutter_staggered_grid_view: ^0.7.0 popover: ^0.3.1 + sliver_tools: ^0.2.12 dev_dependencies: flutter_test: