From 4db7ec3feeeadc10cefe1c5316f4e38b4741ced4 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Wed, 30 Sep 2020 21:47:14 +0200 Subject: [PATCH 1/6] remove loading checks and add slivers --- lib/widgets/user_profile.dart | 469 +++++++++++++++++++--------------- 1 file changed, 260 insertions(+), 209 deletions(-) diff --git a/lib/widgets/user_profile.dart b/lib/widgets/user_profile.dart index ddba340..012ea2e 100644 --- a/lib/widgets/user_profile.dart +++ b/lib/widgets/user_profile.dart @@ -10,13 +10,13 @@ import '../util/goto.dart'; import '../util/intl.dart'; import '../util/text_color.dart'; import 'badge.dart'; +import 'fullscreenable_image.dart'; /// Shared widget of UserPage and ProfileTab class UserProfile extends HookWidget { final Future _userView; final String instanceUrl; - // TODO: add `.fromUser` constructor UserProfile({@required int userId, @required this.instanceUrl}) : _userView = LemmyApi(instanceUrl) .v1 @@ -28,191 +28,242 @@ class UserProfile extends HookWidget { : _userView = Future.value(userView), instanceUrl = userView.instanceUrl; + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final userViewSnap = useFuture(_userView, preserveState: false); + + if (!userViewSnap.hasData) { + return const Center(child: CircularProgressIndicator()); + } + + final userView = userViewSnap.data; + + return DefaultTabController( + length: 3, + child: NestedScrollView( + headerSliverBuilder: (_, __) => [ + SliverAppBar( + pinned: true, + expandedHeight: 255, + toolbarHeight: 0, + forceElevated: true, + elevation: 0, + backgroundColor: theme.cardColor, + brightness: theme.brightness, + iconTheme: theme.iconTheme, + flexibleSpace: + FlexibleSpaceBar(background: _UserOverview(userView)), + bottom: TabBar( + labelColor: theme.textTheme.bodyText1.color, + tabs: [ + Tab(text: 'Posts'), + Tab(text: 'Comments'), + Tab(text: 'About'), + ], + ), + ), + ], + body: TabBarView(children: [ + ListView(children: [ + Text( + 'Posts', + style: const TextStyle(fontSize: 36), + ) + ]), + ListView(children: [ + Text( + 'Comments', + style: const TextStyle(fontSize: 36), + ) + ]), + // InfinitePostList( + // fetcher: (page, batchSize, sort) => + // LemmyApi(community.instanceUrl).v1.getPosts( + // type: PostListingType.community, + // sort: sort, + // communityId: community.id, + // page: page, + // limit: batchSize, + // ), + // ), + // InfiniteCommentList( + // fetcher: (page, batchSize, sortType) => + // LemmyApi(community.instanceUrl).v1.getComments( + // communityId: community.id, + // auth: accountsStore + // .defaultTokenFor(community.instanceUrl) + // ?.raw, + // type: CommentListingType.community, + // sort: sortType, + // limit: batchSize, + // page: page, + // )), + _AboutTab(userView), + ]), + ), + ); + } +} + +class _UserOverview extends HookWidget { + final UserView userView; + + const _UserOverview(this.userView); + @override Widget build(BuildContext context) { final theme = Theme.of(context); final colorOnTopOfAccentColor = textColorBasedOnBackground(theme.accentColor); - final userViewSnap = useFuture(_userView, preserveState: false); - - final bio = () { - if (userViewSnap.hasData) { - if (userViewSnap.data.bio != null) { - return Padding( - padding: const EdgeInsets.all(10), - child: Text(userViewSnap.data.bio), - ); - } else { - return Center( - child: Text( - 'no bio', - style: const TextStyle(fontStyle: FontStyle.italic), - ), - ); - } - } else { - return Center(child: CircularProgressIndicator()); - } - }(); - - Widget tabs() => DefaultTabController( - length: 3, - child: Column( - children: [ - TabBar( - labelColor: theme.textTheme.bodyText1.color, - tabs: [ - Tab(text: 'Posts'), - Tab(text: 'Comments'), - Tab(text: 'About'), - ], - ), - Expanded( - child: TabBarView( - children: [ - Center( - child: Text( - 'Posts', - style: const TextStyle(fontSize: 36), - ), - ), - Center( - child: Text( - 'Comments', - style: const TextStyle(fontSize: 36), - ), - ), - bio, - ], - ), - ) - ], - ), - ); - - return Center( - child: Stack( - children: [ - if (userViewSnap.data?.banner != null) - CachedNetworkImage( - imageUrl: userViewSnap.data.banner, + return Stack( + children: [ + if (userView.banner != null) + // TODO: for some reason doesnt react to presses + FullscreenableImage( + url: userView.banner, + child: CachedNetworkImage( + imageUrl: userView.banner, errorWidget: (_, __, ___) => SizedBox.shrink(), - ) - else - Container( + ), + ) + else + Container( + width: double.infinity, + height: double.infinity, + color: theme.primaryColor, + ), + Container( + height: 200, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: FractionalOffset.topCenter, + end: FractionalOffset.bottomCenter, + colors: [ + Colors.black26, + Colors.transparent, + ], + ), + ), + ), + SafeArea( + child: Padding( + padding: const EdgeInsets.only(top: 60), + child: SizedBox( width: double.infinity, height: double.infinity, - color: theme.primaryColor, - ), - Container( - height: 200, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: FractionalOffset.topCenter, - end: FractionalOffset.bottomCenter, - colors: [ - Colors.black26, - Colors.transparent, - ], - ), - ), - ), - SafeArea( - child: Padding( - padding: const EdgeInsets.only(top: 60), - child: SizedBox( - width: double.infinity, - height: double.infinity, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topRight: Radius.circular(40), - topLeft: Radius.circular(40), - ), - color: theme.scaffoldBackgroundColor, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(40), + topLeft: Radius.circular(40), ), + color: theme.scaffoldBackgroundColor, ), ), ), ), - SafeArea( - child: Column( - children: [ - if (userViewSnap.data?.avatar != null) - SizedBox( - width: 80, - height: 80, - child: Container( - // clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - boxShadow: [ - BoxShadow(blurRadius: 6, color: Colors.black54) - ], - borderRadius: BorderRadius.all(Radius.circular(15)), - border: Border.all(color: Colors.white, width: 3), - ), - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(12)), + ), + SafeArea( + child: Column( + children: [ + if (userView.avatar != null) + SizedBox( + width: 80, + height: 80, + child: Container( + // clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + boxShadow: [ + BoxShadow(blurRadius: 6, color: Colors.black54) + ], + borderRadius: BorderRadius.all(Radius.circular(15)), + border: Border.all(color: Colors.white, width: 3), + ), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(12)), + child: FullscreenableImage( + url: userView.avatar, child: CachedNetworkImage( - imageUrl: userViewSnap.data.avatar, + imageUrl: userView.avatar, errorWidget: (_, __, ___) => SizedBox.shrink(), ), ), ), ), - Padding( - padding: userViewSnap.data?.avatar != null - ? const EdgeInsets.only(top: 8.0) - : const EdgeInsets.only(top: 70), - child: Padding( - padding: EdgeInsets.only( - top: userViewSnap.data?.avatar == null ? 10 : 0), - child: Text( - userViewSnap.data?.preferredUsername ?? - userViewSnap.data?.name ?? - '', - style: theme.textTheme.headline6, - ), + ), + Padding( + padding: userView.avatar != null + ? const EdgeInsets.only(top: 8.0) + : const EdgeInsets.only(top: 70), + child: Padding( + padding: + EdgeInsets.only(top: userView.avatar == null ? 10 : 0), + child: Text( + userView.preferredUsername ?? userView.name, + style: theme.textTheme.headline6, ), ), - Padding( - padding: const EdgeInsets.only(top: 4.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '@${userViewSnap.data?.name ?? ''}@', + ), + Padding( + padding: const EdgeInsets.only(top: 4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '@${userView.name}@', + style: theme.textTheme.caption, + ), + InkWell( + onTap: () => goToInstance(context, userView.instanceUrl), + child: Text( + '${userView.instanceUrl}', style: theme.textTheme.caption, ), - InkWell( - onTap: () => goToInstance(context, instanceUrl), - child: Text( - '$instanceUrl', - style: theme.textTheme.caption, - ), - ) - ], - ), + ) + ], ), - Padding( - padding: const EdgeInsets.only(top: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Badge( + ), + Padding( + padding: const EdgeInsets.only(top: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Badge( + child: Row( + children: [ + Icon( + Icons.comment, // TODO: should be article icon + size: 15, + color: colorOnTopOfAccentColor, + ), + Padding( + padding: const EdgeInsets.only(left: 4.0), + child: Text( + '${compactNumber(userView.numberOfPosts)}' + ' Post${pluralS(userView.numberOfPosts)}', + style: TextStyle(color: colorOnTopOfAccentColor), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Badge( child: Row( children: [ Icon( - Icons.comment, // TODO: should be article icon + Icons.comment, size: 15, color: colorOnTopOfAccentColor, ), Padding( padding: const EdgeInsets.only(left: 4.0), child: Text( - ''' -${userViewSnap.hasData ? compactNumber(userViewSnap.data.numberOfPosts) : '-'} Post${pluralS(userViewSnap.data?.numberOfPosts ?? 0)}''', + '${compactNumber(userView.numberOfComments)}' + ' Comment${pluralS(userView.numberOfComments)}', style: TextStyle(color: colorOnTopOfAccentColor), ), @@ -220,68 +271,68 @@ ${userViewSnap.hasData ? compactNumber(userViewSnap.data.numberOfPosts) : '-'} P ], ), ), - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Badge( - child: Row( - children: [ - Icon( - Icons.comment, - size: 15, - color: colorOnTopOfAccentColor, - ), - Padding( - padding: const EdgeInsets.only(left: 4.0), - child: Text( - ''' -${userViewSnap.hasData ? compactNumber(userViewSnap.data.numberOfComments) : '-'} Comment${pluralS(userViewSnap.data?.numberOfComments ?? 0)}''', - style: - TextStyle(color: colorOnTopOfAccentColor), - ), - ), - ], - ), - ), - ), - ], - ), + ), + ], ), - Padding( - padding: const EdgeInsets.only(top: 15), - child: Text( - ''' -Joined ${userViewSnap.hasData ? timeago.format(userViewSnap.data.published) : ''}''', - style: theme.textTheme.bodyText1, - ), + ), + Padding( + padding: const EdgeInsets.only(top: 15), + child: Text( + 'Joined ${timeago.format(userView.published)}', + style: theme.textTheme.bodyText1, ), - Padding( - padding: const EdgeInsets.only(bottom: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.cake, - size: 13, + ), + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.cake, + size: 13, + ), + Padding( + padding: const EdgeInsets.only(left: 4.0), + child: Text( + DateFormat('MMM dd, yyyy').format(userView.published), + style: theme.textTheme.bodyText1, ), - Padding( - padding: const EdgeInsets.only(left: 4.0), - child: Text( - userViewSnap.hasData - ? DateFormat('MMM dd, yyyy') - .format(userViewSnap.data.published) - : '', - style: theme.textTheme.bodyText1, - ), - ), - ], - ), + ), + ], ), - Expanded(child: tabs()) - ], - ), + ), + // Expanded(child: tabs()) + ], ), - ], - ), + ), + ], ); } } + +class _AboutTab extends HookWidget { + final UserView userView; + + const _AboutTab(this.userView); + + @override + Widget build(BuildContext context) { + final bio = () { + if (userView.bio != null) { + return Padding( + padding: const EdgeInsets.all(10), + child: Text(userView.bio), + ); + } else { + return Center( + child: Text( + 'no bio', + style: const TextStyle(fontStyle: FontStyle.italic), + ), + ); + } + }(); + + return ListView(); + } +} From 701b6f8de324b701ad74fbf03f4519d0e86c0f51 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Wed, 30 Sep 2020 23:43:21 +0200 Subject: [PATCH 2/6] render contents of the tabs --- lib/pages/user.dart | 31 +++---- lib/widgets/user_profile.dart | 164 ++++++++++++++++++++-------------- 2 files changed, 112 insertions(+), 83 deletions(-) diff --git a/lib/pages/user.dart b/lib/pages/user.dart index 822153f..b82238e 100644 --- a/lib/pages/user.dart +++ b/lib/pages/user.dart @@ -9,34 +9,29 @@ import '../widgets/user_profile.dart'; class UserPage extends HookWidget { final int userId; final String instanceUrl; - final Future _userView; + final Future _userDetails; UserPage({@required this.userId, @required this.instanceUrl}) : assert(userId != null), assert(instanceUrl != null), - _userView = LemmyApi(instanceUrl) - .v1 - .getUserDetails( - userId: userId, savedOnly: true, sort: SortType.active) - .then((res) => res.user); + _userDetails = LemmyApi(instanceUrl).v1.getUserDetails( + userId: userId, savedOnly: true, sort: SortType.active); + UserPage.fromName({@required this.instanceUrl, @required String username}) : assert(instanceUrl != null), assert(username != null), userId = null, - _userView = LemmyApi(instanceUrl) - .v1 - .getUserDetails( - username: username, savedOnly: true, sort: SortType.active) - .then((res) => res.user); + _userDetails = LemmyApi(instanceUrl).v1.getUserDetails( + username: username, savedOnly: true, sort: SortType.active); @override Widget build(BuildContext context) { - final userViewSnap = useFuture(_userView); + final userDetailsSnap = useFuture(_userDetails); final body = () { - if (userViewSnap.hasData) { - return UserProfile.fromUserView(userViewSnap.data); - } else if (userViewSnap.hasError) { + if (userDetailsSnap.hasData) { + return UserProfile.fromUserDetails(userDetailsSnap.data); + } else if (userDetailsSnap.hasError) { return Center(child: Text('Could not find that user.')); } else { return Center(child: CircularProgressIndicator()); @@ -49,15 +44,15 @@ class UserPage extends HookWidget { backgroundColor: Colors.transparent, shadowColor: Colors.transparent, actions: [ - if (userViewSnap.hasData) ...[ + if (userDetailsSnap.hasData) ...[ IconButton( icon: Icon(Icons.email), onPressed: () {}, // TODO: go to messaging page ), IconButton( icon: Icon(Icons.share), - onPressed: () => Share.text( - 'Share user', userViewSnap.data.actorId, 'text/plain'), + onPressed: () => Share.text('Share user', + userDetailsSnap.data.user.actorId, 'text/plain'), ) ] ], diff --git a/lib/widgets/user_profile.dart b/lib/widgets/user_profile.dart index 012ea2e..34f6ac4 100644 --- a/lib/widgets/user_profile.dart +++ b/lib/widgets/user_profile.dart @@ -11,33 +11,32 @@ import '../util/intl.dart'; import '../util/text_color.dart'; import 'badge.dart'; import 'fullscreenable_image.dart'; +import 'markdown_text.dart'; +import 'sortable_infinite_list.dart'; /// Shared widget of UserPage and ProfileTab class UserProfile extends HookWidget { - final Future _userView; + final Future _userDetails; final String instanceUrl; UserProfile({@required int userId, @required this.instanceUrl}) - : _userView = LemmyApi(instanceUrl) - .v1 - .getUserDetails( - userId: userId, savedOnly: true, sort: SortType.active) - .then((res) => res.user); + : _userDetails = LemmyApi(instanceUrl).v1.getUserDetails( + userId: userId, savedOnly: false, sort: SortType.active); - UserProfile.fromUserView(UserView userView) - : _userView = Future.value(userView), - instanceUrl = userView.instanceUrl; + UserProfile.fromUserDetails(UserDetails userDetails) + : _userDetails = Future.value(userDetails), + instanceUrl = userDetails.user.instanceUrl; @override Widget build(BuildContext context) { final theme = Theme.of(context); - final userViewSnap = useFuture(_userView, preserveState: false); + final userDetailsSnap = useFuture(_userDetails, preserveState: false); - if (!userViewSnap.hasData) { + if (!userDetailsSnap.hasData) { return const Center(child: CircularProgressIndicator()); } - final userView = userViewSnap.data; + final userView = userDetailsSnap.data.user; return DefaultTabController( length: 3, @@ -65,47 +64,40 @@ class UserProfile extends HookWidget { ), ], body: TabBarView(children: [ - ListView(children: [ - Text( - 'Posts', - style: const TextStyle(fontSize: 36), - ) - ]), - ListView(children: [ - Text( - 'Comments', - style: const TextStyle(fontSize: 36), - ) - ]), - // InfinitePostList( - // fetcher: (page, batchSize, sort) => - // LemmyApi(community.instanceUrl).v1.getPosts( - // type: PostListingType.community, - // sort: sort, - // communityId: community.id, - // page: page, - // limit: batchSize, - // ), - // ), - // InfiniteCommentList( - // fetcher: (page, batchSize, sortType) => - // LemmyApi(community.instanceUrl).v1.getComments( - // communityId: community.id, - // auth: accountsStore - // .defaultTokenFor(community.instanceUrl) - // ?.raw, - // type: CommentListingType.community, - // sort: sortType, - // limit: batchSize, - // page: page, - // )), - _AboutTab(userView), + // TODO: first batch is already fetched on render + // TODO: comment and post come from the same endpoint, could be shared + InfinitePostList( + fetcher: (page, batchSize, sort) => LemmyApi(instanceUrl) + .v1 + .getUserDetails( + userId: userView.id, + savedOnly: false, + sort: SortType.active, + page: page, + limit: batchSize, + ) + .then((val) => val.posts), + ), + InfiniteCommentList( + fetcher: (page, batchSize, sort) => LemmyApi(instanceUrl) + .v1 + .getUserDetails( + userId: userView.id, + savedOnly: false, + sort: SortType.active, + page: page, + limit: batchSize, + ) + .then((val) => val.comments), + ), + _AboutTab(userDetailsSnap.data), ]), ), ); } } +/// Content in the sliver flexible space class _UserOverview extends HookWidget { final UserView userView; @@ -311,28 +303,70 @@ class _UserOverview extends HookWidget { } class _AboutTab extends HookWidget { - final UserView userView; + final UserDetails userDetails; - const _AboutTab(this.userView); + const _AboutTab(this.userDetails); @override Widget build(BuildContext context) { - final bio = () { - if (userView.bio != null) { - return Padding( - padding: const EdgeInsets.all(10), - child: Text(userView.bio), - ); - } else { - return Center( - child: Text( - 'no bio', - style: const TextStyle(fontStyle: FontStyle.italic), - ), - ); - } - }(); + final theme = Theme.of(context); + final instanceUrl = userDetails.user.instanceUrl; - return ListView(); + const wallPadding = EdgeInsets.symmetric(horizontal: 15); + + final divider = Padding( + padding: EdgeInsets.symmetric( + horizontal: wallPadding.horizontal / 2, vertical: 10), + child: Divider(), + ); + + return ListView( + padding: EdgeInsets.symmetric(vertical: 20), + children: [ + if (userDetails.user.bio != null) ...[ + Padding( + padding: wallPadding, + child: + MarkdownText(userDetails.user.bio, instanceUrl: instanceUrl)), + divider, + ], + if (userDetails.moderates.isNotEmpty) ...[ + Padding( + padding: wallPadding, + child: Text('Moderates', style: theme.textTheme.subtitle2), + ), + for (final comm in userDetails.moderates) + ListTile( + dense: true, + title: Text('!${comm.communityName}'), + onTap: () => + goToCommunity.byId(context, instanceUrl, comm.communityId), + ), + divider + ], + Padding( + padding: wallPadding, + child: Text('Subscribed', style: theme.textTheme.subtitle2), + ), + if (userDetails.follows.isEmpty) + for (final comm in userDetails.follows) + ListTile( + dense: true, + title: Text('!${comm.communityName}'), + onTap: () => + goToCommunity.byId(context, instanceUrl, comm.communityId), + ) + else + Padding( + padding: const EdgeInsets.only(top: 8), + child: Center( + child: Text( + 'this user does not subscribe to any community', + style: TextStyle(fontStyle: FontStyle.italic), + ), + ), + ) + ], + ); } } From ba3c1d59e13851e9d277e739374ea17747979298 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Wed, 30 Sep 2020 23:53:20 +0200 Subject: [PATCH 3/6] add edit button on owned accounts --- lib/widgets/user_profile.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/widgets/user_profile.dart b/lib/widgets/user_profile.dart index 34f6ac4..9823b84 100644 --- a/lib/widgets/user_profile.dart +++ b/lib/widgets/user_profile.dart @@ -5,6 +5,7 @@ import 'package:intl/intl.dart'; import 'package:lemmy_api_client/lemmy_api_client.dart'; import 'package:timeago/timeago.dart' as timeago; +import '../hooks/stores.dart'; import '../util/extensions/api.dart'; import '../util/goto.dart'; import '../util/intl.dart'; @@ -312,6 +313,11 @@ class _AboutTab extends HookWidget { final theme = Theme.of(context); final instanceUrl = userDetails.user.instanceUrl; + final accStore = useAccountsStore(); + + final isOwnedAccount = accStore.loggedInInstances.contains(instanceUrl) && + accStore.tokens[instanceUrl].containsKey(userDetails.user.name); + const wallPadding = EdgeInsets.symmetric(horizontal: 15); final divider = Padding( @@ -323,6 +329,14 @@ class _AboutTab extends HookWidget { return ListView( padding: EdgeInsets.symmetric(vertical: 20), children: [ + if (isOwnedAccount) + Center( + child: FlatButton.icon( + icon: Icon(Icons.edit), + label: Text('edit profile'), + onPressed: () {}, // TODO: go to account editing + ), + ), if (userDetails.user.bio != null) ...[ Padding( padding: wallPadding, From 4c5df616431f1ce569d7e57790ccb060328249e7 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Thu, 1 Oct 2020 00:06:05 +0200 Subject: [PATCH 4/6] add todo to bad visibility widget --- lib/pages/profile_tab.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pages/profile_tab.dart b/lib/pages/profile_tab.dart index 7b3b8e4..9b522a8 100644 --- a/lib/pages/profile_tab.dart +++ b/lib/pages/profile_tab.dart @@ -43,6 +43,8 @@ class UserProfileTab extends HookWidget { return Scaffold( extendBodyBehindAppBar: true, + // TODO: this is not visible in light mode when the sliver app bar + // in UserProfile is folded appBar: AppBar( backgroundColor: Colors.transparent, shadowColor: Colors.transparent, From f123d8edd51ab56b5f65eb3aabb3904502cf94cc Mon Sep 17 00:00:00 2001 From: shilangyu Date: Thu, 1 Oct 2020 15:55:22 +0200 Subject: [PATCH 5/6] fix placeholder banner --- lib/widgets/user_profile.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/widgets/user_profile.dart b/lib/widgets/user_profile.dart index 9823b84..141e71a 100644 --- a/lib/widgets/user_profile.dart +++ b/lib/widgets/user_profile.dart @@ -124,8 +124,8 @@ class _UserOverview extends HookWidget { else Container( width: double.infinity, - height: double.infinity, - color: theme.primaryColor, + height: 200, + color: theme.accentColor, ), Container( height: 200, From 6604dcb9afff0d1f3554f761cfe3132aa6200085 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Fri, 2 Oct 2020 20:07:13 +0200 Subject: [PATCH 6/6] apply changes requested from CR --- lib/widgets/user_profile.dart | 97 +++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 27 deletions(-) diff --git a/lib/widgets/user_profile.dart b/lib/widgets/user_profile.dart index 141e71a..3a8b06f 100644 --- a/lib/widgets/user_profile.dart +++ b/lib/widgets/user_profile.dart @@ -35,6 +35,16 @@ class UserProfile extends HookWidget { if (!userDetailsSnap.hasData) { return const Center(child: CircularProgressIndicator()); + } else if (userDetailsSnap.hasError) { + return Center( + child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + Icon(Icons.error), + Padding( + padding: const EdgeInsets.all(8), + child: Text('ERROR: ${userDetailsSnap.error}'), + ) + ]), + ); } final userView = userDetailsSnap.data.user; @@ -45,7 +55,7 @@ class UserProfile extends HookWidget { headerSliverBuilder: (_, __) => [ SliverAppBar( pinned: true, - expandedHeight: 255, + expandedHeight: 265, toolbarHeight: 0, forceElevated: true, elevation: 0, @@ -99,6 +109,9 @@ class UserProfile extends HookWidget { } /// Content in the sliver flexible space +/// Renders general info about the given user. +/// Such as his nickname, no. of posts, no. of posts, +/// banner, avatar etc. class _UserOverview extends HookWidget { final UserView userView; @@ -326,16 +339,42 @@ class _AboutTab extends HookWidget { child: Divider(), ); + communityTile(String name, String icon, int id) => ListTile( + dense: true, + onTap: () => goToCommunity.byId(context, instanceUrl, id), + title: Text('!$name'), + leading: icon != null + ? CachedNetworkImage( + height: 40, + width: 40, + imageUrl: icon, + errorWidget: (_, __, ___) => SizedBox(width: 40, height: 40), + imageBuilder: (context, imageProvider) => Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + fit: BoxFit.cover, + image: imageProvider, + ), + ), + )) + : SizedBox(width: 40), + ); + return ListView( padding: EdgeInsets.symmetric(vertical: 20), children: [ if (isOwnedAccount) - Center( - child: FlatButton.icon( - icon: Icon(Icons.edit), - label: Text('edit profile'), - onPressed: () {}, // TODO: go to account editing + ListTile( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.edit), + SizedBox(width: 10), + Text('edit profile'), + ], ), + onTap: () {}, // TODO: go to account editing ), if (userDetails.user.bio != null) ...[ Padding( @@ -345,31 +384,35 @@ class _AboutTab extends HookWidget { divider, ], if (userDetails.moderates.isNotEmpty) ...[ - Padding( - padding: wallPadding, - child: Text('Moderates', style: theme.textTheme.subtitle2), - ), - for (final comm in userDetails.moderates) - ListTile( - dense: true, - title: Text('!${comm.communityName}'), - onTap: () => - goToCommunity.byId(context, instanceUrl, comm.communityId), + ListTile( + title: Center( + child: Text( + 'Moderates:', + style: theme.textTheme.headline6.copyWith(fontSize: 18), + ), ), + ), + for (final comm + in userDetails.moderates + ..sort((a, b) => a.communityName.compareTo(b.communityName))) + communityTile( + comm.communityName, comm.communityIcon, comm.communityId), divider ], - Padding( - padding: wallPadding, - child: Text('Subscribed', style: theme.textTheme.subtitle2), + ListTile( + title: Center( + child: Text( + 'Subscribed:', + style: theme.textTheme.headline6.copyWith(fontSize: 18), + ), + ), ), - if (userDetails.follows.isEmpty) - for (final comm in userDetails.follows) - ListTile( - dense: true, - title: Text('!${comm.communityName}'), - onTap: () => - goToCommunity.byId(context, instanceUrl, comm.communityId), - ) + if (userDetails.follows.isNotEmpty) + for (final comm + in userDetails.follows + ..sort((a, b) => a.communityName.compareTo(b.communityName))) + communityTile( + comm.communityName, comm.communityIcon, comm.communityId) else Padding( padding: const EdgeInsets.only(top: 8),