💄 Improve UX
This commit is contained in:
		| @@ -53,6 +53,9 @@ | ||||
|   "chatNew": "New Chat", | ||||
|   "chatNewCreate": "Create a channel", | ||||
|   "chatNewJoin": "Join a exists channel", | ||||
|   "chatManage": "Manage Chat", | ||||
|   "chatMember": "Member", | ||||
|   "chatNotifySetting": "Notify Settings", | ||||
|   "chatChannelUsage": "Channel", | ||||
|   "chatChannelUsageCaption": "Channel is place to talk with people, one or a lot.", | ||||
|   "chatChannelOrganize": "Organize a channel", | ||||
|   | ||||
| @@ -51,6 +51,9 @@ | ||||
|   "reactionAdded": "你的反应已被添加。", | ||||
|   "reactionRemoved": "你的反应已被移除。", | ||||
|   "chatNew": "新聊天", | ||||
|   "chatManage": "管理聊天", | ||||
|   "chatMember": "成员", | ||||
|   "chatNotifySetting": "通知设定", | ||||
|   "chatNewCreate": "新建频道", | ||||
|   "chatNewJoin": "加入已有频道", | ||||
|   "chatChannelUsage": "频道", | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import 'package:solian/models/account.dart'; | ||||
|  | ||||
| class Channel { | ||||
|   int id; | ||||
|   DateTime createdAt; | ||||
| @@ -7,9 +9,9 @@ class Channel { | ||||
|   String name; | ||||
|   String description; | ||||
|   dynamic members; | ||||
|   dynamic messages; | ||||
|   dynamic calls; | ||||
|   int type; | ||||
|   Account account; | ||||
|   int accountId; | ||||
|   int realmId; | ||||
|  | ||||
| @@ -22,9 +24,9 @@ class Channel { | ||||
|     required this.name, | ||||
|     required this.description, | ||||
|     this.members, | ||||
|     this.messages, | ||||
|     this.calls, | ||||
|     required this.type, | ||||
|     required this.account, | ||||
|     required this.accountId, | ||||
|     required this.realmId, | ||||
|   }); | ||||
| @@ -38,9 +40,9 @@ class Channel { | ||||
|     name: json["name"], | ||||
|     description: json["description"], | ||||
|     members: json["members"], | ||||
|     messages: json["messages"], | ||||
|     calls: json["calls"], | ||||
|     type: json["type"], | ||||
|     account: Account.fromJson(json["account"]), | ||||
|     accountId: json["account_id"], | ||||
|     realmId: json["realm_id"], | ||||
|   ); | ||||
| @@ -54,9 +56,9 @@ class Channel { | ||||
|     "name": name, | ||||
|     "description": description, | ||||
|     "members": members, | ||||
|     "messages": messages, | ||||
|     "calls": calls, | ||||
|     "type": type, | ||||
|     "account": account, | ||||
|     "account_id": accountId, | ||||
|     "realm_id": realmId, | ||||
|   }; | ||||
|   | ||||
| @@ -13,7 +13,7 @@ class ChatProvider { | ||||
|  | ||||
|     await auth.refreshToken(); | ||||
|  | ||||
|     var ori = getRequestUri('messaging', '/api/unified'); | ||||
|     var ori = getRequestUri('messaging', '/api/ws'); | ||||
|     var uri = Uri( | ||||
|       scheme: ori.scheme.replaceFirst('http', 'ws'), | ||||
|       host: ori.host, | ||||
|   | ||||
| @@ -4,13 +4,14 @@ import 'package:solian/models/post.dart'; | ||||
| import 'package:solian/screens/account.dart'; | ||||
| import 'package:solian/screens/chat/chat.dart'; | ||||
| import 'package:solian/screens/chat/index.dart'; | ||||
| import 'package:solian/screens/chat/manage.dart'; | ||||
| import 'package:solian/screens/chat/channel/channel_editor.dart'; | ||||
| import 'package:solian/screens/explore.dart'; | ||||
| import 'package:solian/screens/notification.dart'; | ||||
| import 'package:solian/screens/posts/comment_editor.dart'; | ||||
| import 'package:solian/screens/posts/moment_editor.dart'; | ||||
| import 'package:solian/screens/posts/screen.dart'; | ||||
| import 'package:solian/screens/signin.dart'; | ||||
| import 'package:solian/widgets/chat/channel_editor.dart'; | ||||
|  | ||||
| final router = GoRouter( | ||||
|   routes: [ | ||||
| @@ -27,13 +28,18 @@ final router = GoRouter( | ||||
|     GoRoute( | ||||
|       path: '/chat/create', | ||||
|       name: 'chat.channel.editor', | ||||
|       builder: (context, state) => ChannelEditor(editing: state.extra as Channel?), | ||||
|       builder: (context, state) => ChannelEditorScreen(editing: state.extra as Channel?), | ||||
|     ), | ||||
|     GoRoute( | ||||
|       path: '/chat/c/:channel', | ||||
|       name: 'chat.channel', | ||||
|       builder: (context, state) => ChatScreen(alias: state.pathParameters['channel'] as String), | ||||
|     ), | ||||
|     GoRoute( | ||||
|       path: '/chat/c/:channel/manage', | ||||
|       name: 'chat.channel.manage', | ||||
|       builder: (context, state) => ChatManageScreen(channel: state.extra as Channel), | ||||
|     ), | ||||
|     GoRoute( | ||||
|       path: '/account', | ||||
|       name: 'account', | ||||
|   | ||||
| @@ -12,16 +12,16 @@ import 'package:solian/widgets/indent_wrapper.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| import 'package:uuid/uuid.dart'; | ||||
| 
 | ||||
| class ChannelEditor extends StatefulWidget { | ||||
| class ChannelEditorScreen extends StatefulWidget { | ||||
|   final Channel? editing; | ||||
| 
 | ||||
|   const ChannelEditor({super.key, this.editing}); | ||||
|   const ChannelEditorScreen({super.key, this.editing}); | ||||
| 
 | ||||
|   @override | ||||
|   State<ChannelEditor> createState() => _ChannelEditorState(); | ||||
|   State<ChannelEditorScreen> createState() => _ChannelEditorScreenState(); | ||||
| } | ||||
| 
 | ||||
| class _ChannelEditorState extends State<ChannelEditor> { | ||||
| class _ChannelEditorScreenState extends State<ChannelEditorScreen> { | ||||
|   final _aliasController = TextEditingController(); | ||||
|   final _nameController = TextEditingController(); | ||||
|   final _descriptionController = TextEditingController(); | ||||
| @@ -9,6 +9,7 @@ import 'package:solian/utils/service_url.dart'; | ||||
| import 'package:solian/widgets/chat/chat_new.dart'; | ||||
| import 'package:solian/widgets/indent_wrapper.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| import 'package:solian/widgets/notification_notifier.dart'; | ||||
| import 'package:solian/widgets/signin_required.dart'; | ||||
|  | ||||
| class ChatIndexScreen extends StatefulWidget { | ||||
| @@ -63,6 +64,7 @@ class _ChatIndexScreenState extends State<ChatIndexScreen> { | ||||
|  | ||||
|     return IndentWrapper( | ||||
|       title: AppLocalizations.of(context)!.chat, | ||||
|       appBarActions: const [NotificationButton()], | ||||
|       floatingActionButton: FutureBuilder( | ||||
|         future: auth.isAuthorized(), | ||||
|         builder: (context, snapshot) { | ||||
|   | ||||
							
								
								
									
										96
									
								
								lib/screens/chat/manage.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								lib/screens/chat/manage.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:solian/models/channel.dart'; | ||||
| import 'package:solian/providers/auth.dart'; | ||||
| import 'package:solian/router.dart'; | ||||
| import 'package:solian/widgets/indent_wrapper.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
|  | ||||
| class ChatManageScreen extends StatefulWidget { | ||||
|   final Channel channel; | ||||
|  | ||||
|   const ChatManageScreen({super.key, required this.channel}); | ||||
|  | ||||
|   @override | ||||
|   State<ChatManageScreen> createState() => _ChatManageScreenState(); | ||||
| } | ||||
|  | ||||
| class _ChatManageScreenState extends State<ChatManageScreen> { | ||||
|   bool isOwned = false; | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|  | ||||
|     Future.delayed(Duration.zero, () async { | ||||
|       final auth = context.read<AuthProvider>(); | ||||
|       final prof = await auth.getProfiles(); | ||||
|  | ||||
|       setState(() { | ||||
|         isOwned = prof['id'] == widget.channel.account.externalId; | ||||
|       }); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final authorizedItems = [ | ||||
|       ListTile( | ||||
|         leading: const Icon(Icons.settings), | ||||
|         title: Text(AppLocalizations.of(context)!.settings), | ||||
|         onTap: () async { | ||||
|           router.pushNamed('chat.channel.editor', extra: widget.channel).then((did) { | ||||
|             if (did == true) { | ||||
|               if (router.canPop()) router.pop(true); | ||||
|             } | ||||
|           }); | ||||
|         }, | ||||
|       ), | ||||
|     ]; | ||||
|  | ||||
|     return IndentWrapper( | ||||
|       title: AppLocalizations.of(context)!.chatManage, | ||||
|       hideDrawer: true, | ||||
|       noSafeArea: true, | ||||
|       child: Column( | ||||
|         children: [ | ||||
|           Padding( | ||||
|             padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), | ||||
|             child: Row( | ||||
|               children: [ | ||||
|                 const CircleAvatar( | ||||
|                   radius: 24, | ||||
|                   backgroundColor: Colors.teal, | ||||
|                   child: Icon(Icons.tag, color: Colors.white), | ||||
|                 ), | ||||
|                 const SizedBox(width: 16), | ||||
|                 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), | ||||
|                 ]) | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|           const Divider(thickness: 0.3), | ||||
|           Expanded( | ||||
|             child: ListView( | ||||
|               children: [ | ||||
|                 ListTile( | ||||
|                   leading: const Icon(Icons.edit_notifications), | ||||
|                   title: Text(AppLocalizations.of(context)!.chatNotifySetting), | ||||
|                   onTap: () {}, | ||||
|                 ), | ||||
|                 ListTile( | ||||
|                   leading: const Icon(Icons.supervisor_account), | ||||
|                   title: Text(AppLocalizations.of(context)!.chatMember), | ||||
|                   onTap: () {}, | ||||
|                 ), | ||||
|                 ...(isOwned ? authorizedItems : List.empty()), | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -23,6 +23,7 @@ class _NotificationScreenState extends State<NotificationScreen> { | ||||
|  | ||||
|     return IndentWrapper( | ||||
|       noSafeArea: true, | ||||
|       hideDrawer: true, | ||||
|       title: AppLocalizations.of(context)!.notification, | ||||
|       child: RefreshIndicator( | ||||
|         onRefresh: () => nty.fetch(auth), | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
| import 'package:solian/models/channel.dart'; | ||||
| import 'package:solian/router.dart'; | ||||
|  | ||||
| @@ -13,41 +12,17 @@ class ChannelAction extends StatelessWidget { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Column( | ||||
|       children: [ | ||||
|         MenuAnchor( | ||||
|           menuChildren: [ | ||||
|             MenuItemButton( | ||||
|               child: Row( | ||||
|                 children: [ | ||||
|                   const Icon(Icons.settings), | ||||
|                   const SizedBox(width: 12), | ||||
|                   Text(AppLocalizations.of(context)!.settings), | ||||
|                 ], | ||||
|               ), | ||||
|               onPressed: () { | ||||
|                 router.pushNamed('chat.channel.editor', extra: channel).then((did) { | ||||
|                   if(did == true) onUpdate(); | ||||
|                 }); | ||||
|               }, | ||||
|             ), | ||||
|           ], | ||||
|           builder: (BuildContext context, MenuController controller, Widget? child) { | ||||
|             return IconButton( | ||||
|               onPressed: () { | ||||
|                 if (controller.isOpen) { | ||||
|                   controller.close(); | ||||
|                 } else { | ||||
|                   controller.open(); | ||||
|                 } | ||||
|               }, | ||||
|               focusNode: _focusNode, | ||||
|               style: TextButton.styleFrom(shape: const CircleBorder()), | ||||
|               icon: const Icon(Icons.more_horiz), | ||||
|             ); | ||||
|           }, | ||||
|         ), | ||||
|       ], | ||||
|     return IconButton( | ||||
|       onPressed: () { | ||||
|         router.pushNamed( | ||||
|           'chat.channel.manage', | ||||
|           extra: channel, | ||||
|           pathParameters: {'channel': channel.alias}, | ||||
|         ); | ||||
|       }, | ||||
|       focusNode: _focusNode, | ||||
|       style: TextButton.styleFrom(shape: const CircleBorder()), | ||||
|       icon: const Icon(Icons.more_horiz), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -28,6 +28,8 @@ class ChatMaintainer extends StatefulWidget { | ||||
|  | ||||
| class _ChatMaintainerState extends State<ChatMaintainer> { | ||||
|   void connect() { | ||||
|     ScaffoldMessenger.of(context).clearSnackBars(); | ||||
|  | ||||
|     final notify = ScaffoldMessenger.of(context).showSnackBar( | ||||
|       SnackBar( | ||||
|         content: Text(AppLocalizations.of(context)!.connectingServer), | ||||
| @@ -73,8 +75,6 @@ class _ChatMaintainerState extends State<ChatMaintainer> { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     ScaffoldMessenger.of(context).clearSnackBars(); | ||||
|  | ||||
|     return widget.child; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_animate/flutter_animate.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:solian/models/reaction.dart'; | ||||
| import 'package:solian/providers/auth.dart'; | ||||
| @@ -106,7 +107,7 @@ class _ReactionActionPopupState extends State<ReactionActionPopup> { | ||||
|       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|       children: [ | ||||
|         Container( | ||||
|           padding: const EdgeInsets.only(left: 8, right: 8, top: 20), | ||||
|           padding: const EdgeInsets.only(left: 8, right: 8, top: 20, bottom: 12), | ||||
|           child: Padding( | ||||
|             padding: const EdgeInsets.symmetric( | ||||
|               horizontal: 8, | ||||
| @@ -118,7 +119,7 @@ class _ReactionActionPopupState extends State<ReactionActionPopup> { | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|         _isSubmitting ? const LinearProgressIndicator() : Container(), | ||||
|         _isSubmitting ? const LinearProgressIndicator().animate().scaleX() : Container(), | ||||
|         Expanded( | ||||
|           child: ListView.builder( | ||||
|             itemCount: reactions.length, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user