diff --git a/lib/src/controllers/app.dart b/lib/src/controllers/app.dart index 6c4fc36..a8b7a43 100644 --- a/lib/src/controllers/app.dart +++ b/lib/src/controllers/app.dart @@ -4,10 +4,13 @@ import 'package:flutter/material.dart'; import 'package:tabnews/src/constants.dart'; import 'package:tabnews/src/models/user.dart'; import 'package:tabnews/src/preferences.dart'; +import 'package:tabnews/src/services/auth.dart'; class AppController { static final AppController _singleton = AppController._internal(); + static final AuthService _authService = AuthService(); + factory AppController() { return _singleton; } @@ -39,4 +42,11 @@ class AppController { return User.fromJson({}); } + + static void updateUser() async { + var userRefresh = await _authService.fetchUser(auth.value); + + Preferences.setString(_userKey, jsonEncode(userRefresh.data)); + user.value = _getLoggedUser(); + } } diff --git a/lib/src/services/content.dart b/lib/src/services/content.dart index 0917d18..5c1dacc 100644 --- a/lib/src/services/content.dart +++ b/lib/src/services/content.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; +import 'package:tabnews/src/controllers/app.dart'; import 'package:tabnews/src/enviroment_vars.dart'; import 'package:tabnews/src/models/comment.dart'; @@ -118,4 +119,20 @@ class ContentService { return HttpResponse(response.statusCode, response.body); } + + Future postTabcoins(String slug, String type) async { + final response = await http.post( + Uri.parse('$apiUrl/$slug/tabcoins'), + headers: { + 'Set-Cookie': 'session_id=${AppController.auth.value}', + 'Cookie': 'session_id=${AppController.auth.value}', + 'Content-Type': 'application/json; charset=UTF-8', + }, + body: jsonEncode({ + 'transaction_type': type, + }), + ); + + return HttpResponse(response.statusCode, response.body); + } } diff --git a/lib/src/ui/layouts/tab.dart b/lib/src/ui/layouts/tab.dart index 8792017..581449f 100644 --- a/lib/src/ui/layouts/tab.dart +++ b/lib/src/ui/layouts/tab.dart @@ -16,7 +16,7 @@ class TabLayout extends StatefulWidget { State createState() => _TabLayoutState(); } -class _TabLayoutState extends State { +class _TabLayoutState extends State with WidgetsBindingObserver { List pagesScrollController = [ ScrollController(), ScrollController(), @@ -51,6 +51,26 @@ class _TabLayoutState extends State { }); } + @override + void initState() { + WidgetsBinding.instance.addObserver(this); + super.initState(); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (AppController.isLoggedIn.value && state == AppLifecycleState.resumed) { + AppController.updateUser(); + } + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/src/ui/pages/content.dart b/lib/src/ui/pages/content.dart index 6522850..05b8a80 100644 --- a/lib/src/ui/pages/content.dart +++ b/lib/src/ui/pages/content.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:tabnews/src/ui/widgets/tabcoins.dart'; import 'package:timeago/timeago.dart' as timeago; import 'package:tabnews/src/controllers/favorites.dart'; @@ -51,6 +52,29 @@ class _ContentPageState extends State { }); } + _tabcoins(String vote) async { + var tabcoinsResp = await _contentService.postTabcoins( + '${widget.username}/${widget.slug}', + vote == 'upvote' ? 'credit' : 'debit', + ); + + if (tabcoinsResp.ok) { + setState(() { + content.tabcoins = tabcoinsResp.data['tabcoins']; + }); + } else { + _onResponse(tabcoinsResp.message); + } + } + + void _onResponse(String message) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + ), + ); + } + @override Widget build(BuildContext context) { timeago.setLocaleMessages('pt-BR', timeago.PtBrMessages()); @@ -89,13 +113,23 @@ class _ContentPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - '${content.ownerUsername} · ${timeago.format(DateTime.parse(content.publishedAt!), locale: "pt-BR")}', - style: const TextStyle().copyWith( - color: context.isDarkMode - ? Colors.grey.shade400 - : Colors.grey.shade700, - ), + Row( + children: [ + Text( + '${content.ownerUsername} · ${timeago.format(DateTime.parse(content.publishedAt!), locale: "pt-BR")}', + style: const TextStyle().copyWith( + color: context.isDarkMode + ? Colors.grey.shade400 + : Colors.grey.shade700, + ), + ), + const Spacer(), + Tabcoins( + upvote: () => _tabcoins('upvote'), + tabcoins: '${content.tabcoins}', + downvote: () => _tabcoins('downvote'), + ), + ], ), const SizedBox(height: 10.0), Text( diff --git a/lib/src/ui/pages/profile.dart b/lib/src/ui/pages/profile.dart index d8e2084..cfaf553 100644 --- a/lib/src/ui/pages/profile.dart +++ b/lib/src/ui/pages/profile.dart @@ -50,16 +50,31 @@ class _ProfilePageState extends State implements ViewAction { ValueListenableBuilder( valueListenable: AppController.user, builder: (context, user, child) { - return Text( - '${user.username}', - style: const TextStyle().copyWith( - fontSize: 28.0, - fontWeight: FontWeight.w700, - ), + return Column( + children: [ + Text( + '${user.username}', + style: const TextStyle().copyWith( + fontSize: 28.0, + fontWeight: FontWeight.w700, + ), + ), + const SizedBox(height: 15.0), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildColorContainer(Colors.blue), + Text('${user.tabcoins ?? 0}'), + const SizedBox(width: 10.0), + _buildColorContainer(Colors.green), + Text('${user.tabcash ?? 0}'), + ], + ), + ], ); }, ), - const SizedBox(height: 80.0), + const SizedBox(height: 60.0), ListTile( onTap: () => Navigation.push(context, const MyContentsPage()), title: const Text('Meu conteúdo'), @@ -87,4 +102,16 @@ class _ProfilePageState extends State implements ViewAction { ), ); } + + Widget _buildColorContainer(Color color) { + return Container( + margin: const EdgeInsets.all(5.0), + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(2.5), + ), + width: 12.0, + height: 12.0, + ); + } } diff --git a/lib/src/ui/widgets/item_comment.dart b/lib/src/ui/widgets/item_comment.dart index 19343cd..e4e6917 100644 --- a/lib/src/ui/widgets/item_comment.dart +++ b/lib/src/ui/widgets/item_comment.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:tabnews/src/services/content.dart'; import 'package:tabnews/src/ui/pages/profile_user.dart'; import 'package:tabnews/src/ui/widgets/comments_children.dart'; +import 'package:tabnews/src/ui/widgets/tabcoins.dart'; import 'package:tabnews/src/utils/navigation.dart'; import 'package:timeago/timeago.dart' as timeago; @@ -8,7 +10,7 @@ import 'package:tabnews/src/extensions/dark_mode.dart'; import 'package:tabnews/src/models/comment.dart'; import 'package:tabnews/src/ui/widgets/markdown.dart'; -class ItemComment extends StatelessWidget { +class ItemComment extends StatefulWidget { final Comment comment; final ScrollController controller; @@ -18,6 +20,39 @@ class ItemComment extends StatelessWidget { required this.controller, }); + @override + State createState() => _ItemCommentState(); +} + +class _ItemCommentState extends State { + Comment get comment => widget.comment; + ScrollController get controller => widget.controller; + + final _contentService = ContentService(); + + _tabcoins(String vote) async { + var tabcoinsResp = await _contentService.postTabcoins( + '${comment.ownerUsername}/${comment.slug}', + vote == 'upvote' ? 'credit' : 'debit', + ); + + if (tabcoinsResp.ok) { + setState(() { + comment.tabcoins = tabcoinsResp.data['tabcoins']; + }); + } else { + _onResponse(tabcoinsResp.message); + } + } + + void _onResponse(String message) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + ), + ); + } + @override Widget build(BuildContext context) { timeago.setLocaleMessages('pt-BR', timeago.PtBrMessages()); @@ -45,6 +80,12 @@ class ItemComment extends StatelessWidget { : Colors.grey.shade700, ), ), + const Spacer(), + Tabcoins( + upvote: () => _tabcoins('upvote'), + tabcoins: '${comment.tabcoins}', + downvote: () => _tabcoins('downvote'), + ), ], ), MarkedownReader( diff --git a/lib/src/ui/widgets/tabcoins.dart b/lib/src/ui/widgets/tabcoins.dart new file mode 100644 index 0000000..9f6472a --- /dev/null +++ b/lib/src/ui/widgets/tabcoins.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +class Tabcoins extends StatefulWidget { + final String tabcoins; + final void Function() upvote; + final void Function() downvote; + + const Tabcoins({ + super.key, + required this.tabcoins, + required this.upvote, + required this.downvote, + }); + + @override + State createState() => _TabcoinsState(); +} + +class _TabcoinsState extends State { + @override + Widget build(BuildContext context) { + return Row( + children: [ + IconButton( + onPressed: widget.upvote, + icon: const Icon(Icons.expand_less), + ), + Text(widget.tabcoins), + IconButton( + onPressed: widget.downvote, + icon: const Icon(Icons.expand_more), + ), + ], + ); + } +}