From 1997dd11441871c1f571f81fbfe08f64d66bcd8e Mon Sep 17 00:00:00 2001 From: Marcelo Pecin Date: Sun, 27 Nov 2022 00:08:48 +0100 Subject: [PATCH] =?UTF-8?q?feat(profile):=20Visualizar=20perfis=20de=20usu?= =?UTF-8?q?=C3=A1rios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- lib/src/services/user.dart | 20 +++++ lib/src/ui/pages/profile_user.dart | 116 +++++++++++++++++++++++++++ lib/src/ui/widgets/item_comment.dart | 28 +++++-- lib/src/ui/widgets/item_content.dart | 19 ++++- 5 files changed, 174 insertions(+), 11 deletions(-) create mode 100644 lib/src/services/user.dart create mode 100644 lib/src/ui/pages/profile_user.dart diff --git a/README.md b/README.md index 2420ccc..c5968e9 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Isto irá rodar o projeto no seu emulador/simulador ou dispositivo real conectad - [x] Criação de conta pelo App - [ ] Resposta dos conteúdos - [x] Postagens de conteúdos -- [ ] Visualização do perfil de outros usuários +- [x] Visualização do perfil de outros usuários - [ ] Favoritos (local database) - [ ] Opção ler mais tarde (local database) - [ ] Buscar conteúdos (?) diff --git a/lib/src/services/user.dart b/lib/src/services/user.dart new file mode 100644 index 0000000..300b389 --- /dev/null +++ b/lib/src/services/user.dart @@ -0,0 +1,20 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; + +import 'package:tabnews/src/models/user.dart'; + +class UserService { + final apiUrl = 'https://www.tabnews.com.br/api/v1'; + + Future fetchUser(String username) async { + final response = await http.get( + Uri.parse('$apiUrl/users/$username'), + ); + + if (response.statusCode == 200) { + return User.fromJson(jsonDecode(response.body)); + } else { + throw Exception('Failed to get public user'); + } + } +} diff --git a/lib/src/ui/pages/profile_user.dart b/lib/src/ui/pages/profile_user.dart new file mode 100644 index 0000000..69af00e --- /dev/null +++ b/lib/src/ui/pages/profile_user.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; +import 'package:tabnews/src/models/content.dart'; +import 'package:tabnews/src/models/user.dart'; +import 'package:tabnews/src/services/api.dart'; +import 'package:tabnews/src/services/user.dart'; +import 'package:tabnews/src/ui/layouts/page.dart'; +import 'package:tabnews/src/ui/widgets/item_content.dart'; +import 'package:tabnews/src/ui/widgets/progress_indicator.dart'; + +class ProfileUserPage extends StatefulWidget { + final String username; + + const ProfileUserPage({super.key, required this.username}); + + @override + State createState() => _ProfileUserPageState(); +} + +class _ProfileUserPageState extends State { + final userService = UserService(); + final contentService = Api(); + bool _isLoading = true; + late User _user; + + static const _perPage = 30; + final PagingController _pagingController = + PagingController(firstPageKey: 1); + + final ScrollController _controller = ScrollController(); + + @override + void initState() { + super.initState(); + + _getUser(); + _pagingController.addPageRequestListener((pageKey) { + _getContents(pageKey); + }); + } + + @override + void dispose() { + _controller.dispose(); + _pagingController.dispose(); + + super.dispose(); + } + + Future _getContents(int page) async { + final content = await contentService.fetchMyContents( + page: page, + user: widget.username, + ); + + final isLastPage = content.length < _perPage; + + if (isLastPage) { + _pagingController.appendLastPage(content); + } else { + final nextPage = page + 1; + + _pagingController.appendPage(content, nextPage); + } + } + + _getUser() async { + var userRet = await userService.fetchUser(widget.username); + + setState(() { + _user = userRet; + _isLoading = false; + }); + } + + @override + Widget build(BuildContext context) { + return PageLayout( + onRefresh: () async {}, + body: _isLoading + ? const AppProgressIndicator() + : SingleChildScrollView( + controller: _controller, + padding: const EdgeInsets.all(15.0), + child: Column( + children: [ + Text( + '${_user.username}', + style: const TextStyle().copyWith( + fontSize: 24.0, + fontWeight: FontWeight.w700, + ), + ), + const SizedBox(height: 15.0), + const Divider(), + const SizedBox(height: 15.0), + PagedListView( + shrinkWrap: true, + scrollController: _controller, + pagingController: _pagingController, + builderDelegate: PagedChildBuilderDelegate( + itemBuilder: (context, item, index) { + return ItemContent(index: index, content: item); + }, + firstPageProgressIndicatorBuilder: (_) => + const AppProgressIndicator(), + newPageProgressIndicatorBuilder: (_) => + const AppProgressIndicator(), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/ui/widgets/item_comment.dart b/lib/src/ui/widgets/item_comment.dart index c748900..19343cd 100644 --- a/lib/src/ui/widgets/item_comment.dart +++ b/lib/src/ui/widgets/item_comment.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:tabnews/src/ui/pages/profile_user.dart'; import 'package:tabnews/src/ui/widgets/comments_children.dart'; +import 'package:tabnews/src/utils/navigation.dart'; import 'package:timeago/timeago.dart' as timeago; import 'package:tabnews/src/extensions/dark_mode.dart'; @@ -25,13 +27,25 @@ class ItemComment extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Text( - '${comment.ownerUsername} · ${timeago.format(DateTime.parse(comment.publishedAt!), locale: "pt-BR")}', - style: const TextStyle().copyWith( - color: context.isDarkMode - ? Colors.grey.shade400 - : Colors.grey.shade700, - ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + InkWell( + onTap: () => Navigation.push( + context, + ProfileUserPage(username: '${comment.ownerUsername}'), + ), + child: Text('${comment.ownerUsername}'), + ), + Text( + ' · ${timeago.format(DateTime.parse(comment.publishedAt!), locale: "pt-BR")}', + style: const TextStyle().copyWith( + color: context.isDarkMode + ? Colors.grey.shade400 + : Colors.grey.shade700, + ), + ), + ], ), MarkedownReader( body: comment.body!, diff --git a/lib/src/ui/widgets/item_content.dart b/lib/src/ui/widgets/item_content.dart index ed954cb..796dc1d 100644 --- a/lib/src/ui/widgets/item_content.dart +++ b/lib/src/ui/widgets/item_content.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:timeago/timeago.dart' as timeago; import 'package:tabnews/src/ui/pages/content.dart'; +import 'package:tabnews/src/ui/pages/profile_user.dart'; import 'package:tabnews/src/utils/navigation.dart'; import 'package:tabnews/src/extensions/dark_mode.dart'; import 'package:tabnews/src/models/content.dart'; @@ -43,6 +44,8 @@ class ItemContent extends StatelessWidget { borderRadius: BorderRadius.circular(10.0), ), child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -67,11 +70,21 @@ class ItemContent extends StatelessWidget { ], ), const SizedBox(height: 15.0), - Row( - mainAxisAlignment: MainAxisAlignment.end, + Column( + crossAxisAlignment: CrossAxisAlignment.end, children: [ + InkWell( + onTap: () => Navigation.push( + context, + ProfileUserPage( + username: '${content.ownerUsername}', + ), + ), + child: Text('${content.ownerUsername}'), + ), + const SizedBox(height: 5.0), Text( - '${content.tabcoins} tabcoins · ${content.ownerUsername} · ${timeago.format(date, locale: "pt-BR")}', + '${content.tabcoins} tabcoins · ${content.childrenDeepCount} comentários · ${timeago.format(date, locale: "pt-BR")}', style: TextStyle( color: context.isDarkMode ? Colors.grey.shade400