✨ Basis personalize
This commit is contained in:
		| @@ -21,6 +21,10 @@ | ||||
|   "email": "Email Address", | ||||
|   "nickname": "Nickname", | ||||
|   "username": "Username", | ||||
|   "firstName": "First Name", | ||||
|   "lastName": "Last Name", | ||||
|   "description": "Description", | ||||
|   "birthday": "Birthday", | ||||
|   "password": "Password", | ||||
|   "next": "Next", | ||||
|   "edit": "Edit", | ||||
| @@ -28,6 +32,7 @@ | ||||
|   "delete": "Delete", | ||||
|   "exit": "Exit", | ||||
|   "action": "Action", | ||||
|   "reset": "Reset", | ||||
|   "cancel": "Cancel", | ||||
|   "report": "Report", | ||||
|   "reply": "Reply", | ||||
| @@ -45,6 +50,8 @@ | ||||
|   "friendAdd": "Add friend", | ||||
|   "friendAddHint": "Use your their username to send a friend request to your best friend!", | ||||
|   "friendAddDone": "Friend request sent, go reach your friend!", | ||||
|   "personalize": "Personalize", | ||||
|   "personalizeApplied": "Your account information has been updated, some fields may take a while to fully applied.", | ||||
|   "reaction": "Reaction", | ||||
|   "reactVerb": "React", | ||||
|   "post": "Post", | ||||
|   | ||||
| @@ -21,12 +21,17 @@ | ||||
|   "email": "邮箱地址", | ||||
|   "nickname": "显示名", | ||||
|   "username": "用户名", | ||||
|   "firstName": "姓氏", | ||||
|   "lastName": "名字", | ||||
|   "description": "简介", | ||||
|   "birthday": "生日", | ||||
|   "password": "密码", | ||||
|   "next": "下一步", | ||||
|   "edit": "编辑", | ||||
|   "delete": "删除", | ||||
|   "action": "操作", | ||||
|   "apply": "应用", | ||||
|   "reset": "重置", | ||||
|   "cancel": "取消", | ||||
|   "exit": "离开", | ||||
|   "report": "举报", | ||||
| @@ -45,6 +50,8 @@ | ||||
|   "friendAdd": "添加好友", | ||||
|   "friendAddHint": "使用用户名来给你的好朋友发一个好友请求吧!", | ||||
|   "friendAddDone": "好友请求已发送,快告诉你的朋友吧!", | ||||
|   "personalize": "个性化", | ||||
|   "personalizeApplied": "您的账号信息已被更新,部分信息可能需要一段时间来同步。", | ||||
|   "reaction": "反应", | ||||
|   "reactVerb": "作出反应", | ||||
|   "post": "帖子", | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import 'package:solian/models/channel.dart'; | ||||
| import 'package:solian/models/post.dart'; | ||||
| import 'package:solian/screens/account.dart'; | ||||
| import 'package:solian/screens/account/friend.dart'; | ||||
| import 'package:solian/screens/account/personalize.dart'; | ||||
| import 'package:solian/screens/auth/signup.dart'; | ||||
| import 'package:solian/screens/chat/call.dart'; | ||||
| import 'package:solian/screens/chat/chat.dart'; | ||||
| @@ -107,5 +108,10 @@ final router = GoRouter( | ||||
|       name: 'account.friend', | ||||
|       builder: (context, state) => const FriendScreen(), | ||||
|     ), | ||||
|     GoRoute( | ||||
|       path: '/account/personalize', | ||||
|       name: 'account.personalize', | ||||
|       builder: (context, state) => const PersonalizeScreen(), | ||||
|     ), | ||||
|   ], | ||||
| ); | ||||
|   | ||||
| @@ -3,6 +3,7 @@ import 'package:provider/provider.dart'; | ||||
| import 'package:solian/providers/auth.dart'; | ||||
| import 'package:solian/router.dart'; | ||||
| import 'package:solian/screens/account/friend.dart'; | ||||
| import 'package:solian/screens/account/personalize.dart'; | ||||
| import 'package:solian/widgets/account/avatar.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| import 'package:solian/widgets/empty.dart'; | ||||
| @@ -28,6 +29,8 @@ class _AccountScreenState extends State<AccountScreen> { | ||||
|       switch (_selectedTab) { | ||||
|         case 'account.friend': | ||||
|           return const FriendScreenWidget(); | ||||
|         case 'account.personalize': | ||||
|           return const PersonalizeScreenWidget(); | ||||
|         default: | ||||
|           return const SelectionEmptyWidget(); | ||||
|       } | ||||
| @@ -94,9 +97,17 @@ class _AccountScreenWidgetState extends State<AccountScreenWidget> { | ||||
|       return Column( | ||||
|         children: [ | ||||
|           const Padding( | ||||
|             padding: EdgeInsets.symmetric(vertical: 8, horizontal: 24), | ||||
|             padding: EdgeInsets.only(top: 16, bottom: 8, left: 24, right: 24), | ||||
|             child: NameCard(), | ||||
|           ), | ||||
|           ListTile( | ||||
|             contentPadding: const EdgeInsets.symmetric(horizontal: 34), | ||||
|             leading: const Icon(Icons.color_lens), | ||||
|             title: Text(AppLocalizations.of(context)!.personalize), | ||||
|             onTap: () { | ||||
|               widget.onSelect('account.personalize', AppLocalizations.of(context)!.personalize); | ||||
|             }, | ||||
|           ), | ||||
|           ListTile( | ||||
|             contentPadding: const EdgeInsets.symmetric(horizontal: 34), | ||||
|             leading: const Icon(Icons.diversity_1), | ||||
| @@ -167,12 +178,18 @@ class NameCard extends StatelessWidget { | ||||
|       children: [ | ||||
|         Text( | ||||
|           profiles['nick'], | ||||
|           maxLines: 1, | ||||
|           style: const TextStyle( | ||||
|             fontSize: 20, | ||||
|             fontWeight: FontWeight.bold, | ||||
|             overflow: TextOverflow.ellipsis, | ||||
|           ), | ||||
|         ), | ||||
|         Text(profiles['email']) | ||||
|         Text( | ||||
|           profiles['email'], | ||||
|           maxLines: 1, | ||||
|           style: const TextStyle(overflow: TextOverflow.ellipsis), | ||||
|         ) | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| @@ -201,7 +218,7 @@ class NameCard extends StatelessWidget { | ||||
|                 future: renderLabel(context), | ||||
|                 builder: (BuildContext context, AsyncSnapshot<Column> snapshot) { | ||||
|                   if (snapshot.hasData) { | ||||
|                     return snapshot.data!; | ||||
|                     return Expanded(child: snapshot.data!); | ||||
|                   } else { | ||||
|                     return const Column(); | ||||
|                   } | ||||
| @@ -221,7 +238,13 @@ class ActionCard extends StatelessWidget { | ||||
|   final String caption; | ||||
|   final Function onTap; | ||||
|  | ||||
|   const ActionCard({super.key, required this.onTap, required this.title, required this.caption, required this.icon}); | ||||
|   const ActionCard({ | ||||
|     super.key, | ||||
|     required this.onTap, | ||||
|     required this.title, | ||||
|     required this.caption, | ||||
|     required this.icon, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
| @@ -248,9 +271,13 @@ class ActionCard extends StatelessWidget { | ||||
|                 style: const TextStyle( | ||||
|                   fontSize: 18, | ||||
|                   fontWeight: FontWeight.w900, | ||||
|                   overflow: TextOverflow.clip, | ||||
|                 ), | ||||
|               ), | ||||
|               Text(caption), | ||||
|               Text( | ||||
|                 caption, | ||||
|                 style: const TextStyle(overflow: TextOverflow.clip), | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|   | ||||
							
								
								
									
										215
									
								
								lib/screens/account/personalize.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								lib/screens/account/personalize.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,215 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_animate/flutter_animate.dart'; | ||||
| import 'package:intl/intl.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:solian/providers/auth.dart'; | ||||
| import 'package:solian/utils/service_url.dart'; | ||||
| import 'package:solian/widgets/exts.dart'; | ||||
| import 'package:solian/widgets/indent_wrapper.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
|  | ||||
| class PersonalizeScreen extends StatelessWidget { | ||||
|   const PersonalizeScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return IndentWrapper( | ||||
|       title: AppLocalizations.of(context)!.personalize, | ||||
|       hideDrawer: true, | ||||
|       child: const PersonalizeScreenWidget(), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class PersonalizeScreenWidget extends StatefulWidget { | ||||
|   const PersonalizeScreenWidget({super.key}); | ||||
|  | ||||
|   @override | ||||
|   State<PersonalizeScreenWidget> createState() => _PersonalizeScreenWidgetState(); | ||||
| } | ||||
|  | ||||
| class _PersonalizeScreenWidgetState extends State<PersonalizeScreenWidget> { | ||||
|   final _usernameController = TextEditingController(); | ||||
|   final _nicknameController = TextEditingController(); | ||||
|   final _firstNameController = TextEditingController(); | ||||
|   final _lastNameController = TextEditingController(); | ||||
|   final _descriptionController = TextEditingController(); | ||||
|   final _birthdayController = TextEditingController(); | ||||
|  | ||||
|   DateTime? _birthday; | ||||
|  | ||||
|   bool _isSubmitting = false; | ||||
|  | ||||
|   void editBirthday() async { | ||||
|     final DateTime? picked = await showDatePicker( | ||||
|       context: context, | ||||
|       initialDate: _birthday, | ||||
|       firstDate: DateTime(DateTime.now().year - 200), | ||||
|       lastDate: DateTime(DateTime.now().year + 200), | ||||
|     ); | ||||
|     if (picked != null && picked != _birthday) { | ||||
|       setState(() { | ||||
|         _birthday = picked; | ||||
|         _birthdayController.text = DateFormat('yyyy-MM-dd hh:mm').format(_birthday!); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void resetInputs() async { | ||||
|     final auth = context.read<AuthProvider>(); | ||||
|     final prof = await auth.getProfiles(); | ||||
|     _usernameController.text = prof['name']; | ||||
|     _nicknameController.text = prof['nick']; | ||||
|     _descriptionController.text = prof['description']; | ||||
|     _firstNameController.text = prof['profile']['first_name']; | ||||
|     _lastNameController.text = prof['profile']['last_name']; | ||||
|     if (prof['profile']['birthday'] != null) { | ||||
|       _birthday = DateTime.parse(prof['profile']['birthday']); | ||||
|       _birthdayController.text = DateFormat('yyyy-MM-dd hh:mm').format(_birthday!); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void applyChanges() async { | ||||
|     setState(() => _isSubmitting = true); | ||||
|  | ||||
|     final auth = context.read<AuthProvider>(); | ||||
|     if (!await auth.isAuthorized()) { | ||||
|       setState(() => _isSubmitting = false); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     final res = await auth.client!.put( | ||||
|       getRequestUri('passport', '/api/users/me'), | ||||
|       headers: {'Content-Type': 'application/json'}, | ||||
|       body: jsonEncode({ | ||||
|         'name': _usernameController.value.text, | ||||
|         'nick': _nicknameController.value.text, | ||||
|         'description': _descriptionController.value.text, | ||||
|         'first_name': _firstNameController.value.text, | ||||
|         'last_name': _lastNameController.value.text, | ||||
|         'birthday': _birthday?.toIso8601String(), | ||||
|       }), | ||||
|     ); | ||||
|     if (res.statusCode == 200) { | ||||
|       await auth.fetchProfiles(); | ||||
|       resetInputs(); | ||||
|  | ||||
|       ScaffoldMessenger.of(context).showSnackBar(SnackBar( | ||||
|         content: Text(AppLocalizations.of(context)!.personalizeApplied), | ||||
|       )); | ||||
|     } else { | ||||
|       var message = utf8.decode(res.bodyBytes); | ||||
|       context.showErrorDialog(message); | ||||
|     } | ||||
|  | ||||
|     setState(() => _isSubmitting = false); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|  | ||||
|     Future.delayed(Duration.zero, () => resetInputs()); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Container( | ||||
|       padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 32), | ||||
|       child: Column( | ||||
|         children: [ | ||||
|           _isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(), | ||||
|           Row( | ||||
|             children: [ | ||||
|               Flexible( | ||||
|                 flex: 1, | ||||
|                 child: TextField( | ||||
|                   controller: _usernameController, | ||||
|                   decoration: InputDecoration( | ||||
|                     border: const OutlineInputBorder(), | ||||
|                     labelText: AppLocalizations.of(context)!.username, | ||||
|                     prefixText: '@', | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|               const SizedBox(width: 16), | ||||
|               Flexible( | ||||
|                 flex: 1, | ||||
|                 child: TextField( | ||||
|                   controller: _nicknameController, | ||||
|                   decoration: InputDecoration( | ||||
|                     border: const OutlineInputBorder(), | ||||
|                     labelText: AppLocalizations.of(context)!.nickname, | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|           const SizedBox(height: 16), | ||||
|           Row( | ||||
|             children: [ | ||||
|               Flexible( | ||||
|                 flex: 1, | ||||
|                 child: TextField( | ||||
|                   controller: _firstNameController, | ||||
|                   decoration: InputDecoration( | ||||
|                     border: const OutlineInputBorder(), | ||||
|                     labelText: AppLocalizations.of(context)!.firstName, | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|               const SizedBox(width: 16), | ||||
|               Flexible( | ||||
|                 flex: 1, | ||||
|                 child: TextField( | ||||
|                   controller: _lastNameController, | ||||
|                   decoration: InputDecoration( | ||||
|                     border: const OutlineInputBorder(), | ||||
|                     labelText: AppLocalizations.of(context)!.lastName, | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|           const SizedBox(height: 16), | ||||
|           TextField( | ||||
|             controller: _descriptionController, | ||||
|             keyboardType: TextInputType.multiline, | ||||
|             maxLines: null, | ||||
|             minLines: 3, | ||||
|             decoration: InputDecoration( | ||||
|               border: const OutlineInputBorder(), | ||||
|               labelText: AppLocalizations.of(context)!.description, | ||||
|             ), | ||||
|           ), | ||||
|           const SizedBox(height: 16), | ||||
|           TextField( | ||||
|             controller: _birthdayController, | ||||
|             readOnly: true, | ||||
|             decoration: InputDecoration( | ||||
|               border: const OutlineInputBorder(), | ||||
|               labelText: AppLocalizations.of(context)!.birthday, | ||||
|             ), | ||||
|             onTap: editBirthday, | ||||
|           ), | ||||
|           const SizedBox(height: 16), | ||||
|           Row( | ||||
|             mainAxisAlignment: MainAxisAlignment.end, | ||||
|             children: [ | ||||
|               TextButton( | ||||
|                 onPressed: _isSubmitting ? null : () => resetInputs(), | ||||
|                 child: Text(AppLocalizations.of(context)!.reset), | ||||
|               ), | ||||
|               ElevatedButton( | ||||
|                 onPressed: _isSubmitting ? null : () => applyChanges(), | ||||
|                 child: Text(AppLocalizations.of(context)!.apply), | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -53,9 +53,7 @@ class _ChatManageScreenState extends State<ChatManageScreen> { | ||||
|         leading: const Icon(Icons.settings), | ||||
|         title: Text(AppLocalizations.of(context)!.settings), | ||||
|         onTap: () async { | ||||
|           router | ||||
|               .pushNamed('chat.channel.editor', extra: widget.channel) | ||||
|               .then((did) { | ||||
|           router.pushNamed('chat.channel.editor', extra: widget.channel).then((did) { | ||||
|             if (did == true) { | ||||
|               if (router.canPop()) router.pop('refresh'); | ||||
|             } | ||||
| @@ -81,14 +79,10 @@ class _ChatManageScreenState extends State<ChatManageScreen> { | ||||
|                 ), | ||||
|                 const SizedBox(width: 16), | ||||
|                 Expanded( | ||||
|                   child: Column( | ||||
|                       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                       children: [ | ||||
|                         Text(widget.channel.name, | ||||
|                             style: Theme.of(context).textTheme.bodyLarge), | ||||
|                         Text(widget.channel.description, | ||||
|                             style: Theme.of(context).textTheme.bodySmall), | ||||
|                       ]), | ||||
|                   child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ | ||||
|                     Text(widget.channel.name, style: Theme.of(context).textTheme.bodyLarge), | ||||
|                     Text(widget.channel.description, style: Theme.of(context).textTheme.bodySmall), | ||||
|                   ]), | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
| @@ -116,12 +110,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> { | ||||
|                 ...(_isOwned ? authorizedItems : List.empty()), | ||||
|                 const Divider(thickness: 0.3), | ||||
|                 ListTile( | ||||
|                   leading: _isOwned | ||||
|                       ? const Icon(Icons.delete) | ||||
|                       : const Icon(Icons.exit_to_app), | ||||
|                   title: Text(_isOwned | ||||
|                       ? AppLocalizations.of(context)!.delete | ||||
|                       : AppLocalizations.of(context)!.exit), | ||||
|                   leading: _isOwned ? const Icon(Icons.delete) : const Icon(Icons.exit_to_app), | ||||
|                   title: Text(_isOwned ? AppLocalizations.of(context)!.delete : AppLocalizations.of(context)!.exit), | ||||
|                   onTap: () => promptLeaveChannel(), | ||||
|                 ), | ||||
|               ], | ||||
|   | ||||
| @@ -58,8 +58,27 @@ class HttpClient extends http.BaseClient { | ||||
|         throw Exception(utf8.decode(result.bodyBytes)); | ||||
|       } | ||||
|  | ||||
|       request.headers['Authorization'] = 'Bearer $currentToken'; | ||||
|       return await _client.send(request); | ||||
|       http.BaseRequest newRequest; | ||||
|       if (request is http.Request) { | ||||
|         newRequest = http.Request(request.method, request.url) | ||||
|           ..encoding = request.encoding | ||||
|           ..bodyBytes = request.bodyBytes; | ||||
|       } else if (request is http.MultipartRequest) { | ||||
|         newRequest = http.MultipartRequest(request.method, request.url) | ||||
|           ..fields.addAll(request.fields) | ||||
|           ..files.addAll(request.files); | ||||
|       } else { | ||||
|         throw Exception('unsupported request type to auto retry'); | ||||
|       } | ||||
|  | ||||
|       newRequest | ||||
|         ..persistentConnection = request.persistentConnection | ||||
|         ..followRedirects = request.followRedirects | ||||
|         ..maxRedirects = request.maxRedirects | ||||
|         ..headers.addAll(request.headers) | ||||
|         ..headers['Authorization'] = 'Bearer $currentToken'; | ||||
|  | ||||
|       return await _client.send(newRequest); | ||||
|     } | ||||
|  | ||||
|     return res; | ||||
|   | ||||
| @@ -12,6 +12,7 @@ class AttachmentItem extends StatefulWidget { | ||||
|   final String url; | ||||
|   final String? tag; | ||||
|   final String? badge; | ||||
|   final bool noTag; | ||||
|  | ||||
|   const AttachmentItem({ | ||||
|     super.key, | ||||
| @@ -19,6 +20,7 @@ class AttachmentItem extends StatefulWidget { | ||||
|     required this.url, | ||||
|     this.tag, | ||||
|     this.badge, | ||||
|     this.noTag = false, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
| @@ -50,7 +52,7 @@ class _AttachmentItemState extends State<AttachmentItem> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     const borderRadius = Radius.circular(8); | ||||
|     final tag = getTag(); | ||||
|     final tag = widget.noTag ? const Uuid().v4() : getTag(); | ||||
|  | ||||
|     Widget content; | ||||
|  | ||||
| @@ -128,12 +130,16 @@ class _AttachmentItemState extends State<AttachmentItem> { | ||||
| class AttachmentList extends StatelessWidget { | ||||
|   final List<Attachment> items; | ||||
|   final String provider; | ||||
|   final bool noTag; | ||||
|  | ||||
|   const AttachmentList( | ||||
|       {super.key, required this.items, required this.provider}); | ||||
|   const AttachmentList({ | ||||
|     super.key, | ||||
|     required this.items, | ||||
|     required this.provider, | ||||
|     this.noTag = false, | ||||
|   }); | ||||
|  | ||||
|   Uri getFileUri(String fileId) => | ||||
|       getRequestUri(provider, '/api/attachments/o/$fileId'); | ||||
|   Uri getFileUri(String fileId) => getRequestUri(provider, '/api/attachments/o/$fileId'); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
| @@ -156,6 +162,7 @@ class AttachmentList extends StatelessWidget { | ||||
|                 tag: item.fileId, | ||||
|                 url: getFileUri(item.fileId).toString(), | ||||
|                 badge: items.length <= 1 ? null : badge, | ||||
|                 noTag: noTag, | ||||
|               ), | ||||
|             ); | ||||
|           }, | ||||
|   | ||||
| @@ -47,8 +47,7 @@ class _PostItemState extends State<PostItem> { | ||||
|   } | ||||
|  | ||||
|   void viewComments() { | ||||
|     final PagingController<int, Post> commentPaging = | ||||
|         PagingController(firstPageKey: 0); | ||||
|     final PagingController<int, Post> commentPaging = PagingController(firstPageKey: 0); | ||||
|  | ||||
|     showModalBottomSheet( | ||||
|       context: context, | ||||
| @@ -88,12 +87,17 @@ class _PostItemState extends State<PostItem> { | ||||
|   Widget renderAttachments() { | ||||
|     if (widget.item.modelType == 'article') return Container(); | ||||
|  | ||||
|     if (widget.item.attachments != null && | ||||
|         widget.item.attachments!.isNotEmpty) { | ||||
|     final screenWidth = MediaQuery.of(context).size.width; | ||||
|     final isLargeScreen = screenWidth >= 600; | ||||
|  | ||||
|     if (widget.item.attachments != null && widget.item.attachments!.isNotEmpty) { | ||||
|       return Padding( | ||||
|         padding: const EdgeInsets.only(top: 8), | ||||
|         child: AttachmentList( | ||||
|             items: widget.item.attachments!, provider: 'interactive'), | ||||
|           items: widget.item.attachments!, | ||||
|           provider: 'interactive', | ||||
|           noTag: isLargeScreen && widget.brief, | ||||
|         ), | ||||
|       ); | ||||
|     } else { | ||||
|       return Container(); | ||||
| @@ -133,9 +137,8 @@ class _PostItemState extends State<PostItem> { | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   String getAuthorDescribe() => widget.item.author.description.isNotEmpty | ||||
|       ? widget.item.author.description | ||||
|       : 'No description yet.'; | ||||
|   String getAuthorDescribe() => | ||||
|       widget.item.author.description.isNotEmpty ? widget.item.author.description : 'No description yet.'; | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
| @@ -181,8 +184,7 @@ class _PostItemState extends State<PostItem> { | ||||
|                     children: [ | ||||
|                       ...headingParts, | ||||
|                       Padding( | ||||
|                         padding: | ||||
|                             const EdgeInsets.only(left: 12, right: 12, top: 4), | ||||
|                         padding: const EdgeInsets.only(left: 12, right: 12, top: 4), | ||||
|                         child: renderContent(), | ||||
|                       ), | ||||
|                       renderAttachments(), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user