✨ Chat channels index
This commit is contained in:
		| @@ -1,6 +1,7 @@ | ||||
| { | ||||
|   "solian": "Solian", | ||||
|   "explore": "Explore", | ||||
|   "chat": "Chat", | ||||
|   "account": "Account", | ||||
|   "signIn": "Sign In", | ||||
|   "signInCaption": "Sign in to create post, start a realm, message your friend and more!", | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| { | ||||
|   "solian": "索链", | ||||
|   "explore": "探索", | ||||
|   "chat": "聊天", | ||||
|   "account": "账号", | ||||
|   "signIn": "登陆", | ||||
|   "signInCaption": "登陆以发表帖子、文章、创建领域、和你的朋友聊天,以及获取更多功能!", | ||||
|   | ||||
							
								
								
									
										63
									
								
								lib/models/channel.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								lib/models/channel.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| class Channel { | ||||
|   int id; | ||||
|   DateTime createdAt; | ||||
|   DateTime updatedAt; | ||||
|   DateTime? deletedAt; | ||||
|   String alias; | ||||
|   String name; | ||||
|   String description; | ||||
|   dynamic members; | ||||
|   dynamic messages; | ||||
|   dynamic calls; | ||||
|   int type; | ||||
|   int accountId; | ||||
|   int realmId; | ||||
|  | ||||
|   Channel({ | ||||
|     required this.id, | ||||
|     required this.createdAt, | ||||
|     required this.updatedAt, | ||||
|     this.deletedAt, | ||||
|     required this.alias, | ||||
|     required this.name, | ||||
|     required this.description, | ||||
|     this.members, | ||||
|     this.messages, | ||||
|     this.calls, | ||||
|     required this.type, | ||||
|     required this.accountId, | ||||
|     required this.realmId, | ||||
|   }); | ||||
|  | ||||
|   factory Channel.fromJson(Map<String, dynamic> json) => Channel( | ||||
|     id: json["id"], | ||||
|     createdAt: DateTime.parse(json["created_at"]), | ||||
|     updatedAt: DateTime.parse(json["updated_at"]), | ||||
|     deletedAt: json["deleted_at"], | ||||
|     alias: json["alias"], | ||||
|     name: json["name"], | ||||
|     description: json["description"], | ||||
|     members: json["members"], | ||||
|     messages: json["messages"], | ||||
|     calls: json["calls"], | ||||
|     type: json["type"], | ||||
|     accountId: json["account_id"], | ||||
|     realmId: json["realm_id"], | ||||
|   ); | ||||
|  | ||||
|   Map<String, dynamic> toJson() => { | ||||
|     "id": id, | ||||
|     "created_at": createdAt.toIso8601String(), | ||||
|     "updated_at": updatedAt.toIso8601String(), | ||||
|     "deleted_at": deletedAt, | ||||
|     "alias": alias, | ||||
|     "name": name, | ||||
|     "description": description, | ||||
|     "members": members, | ||||
|     "messages": messages, | ||||
|     "calls": calls, | ||||
|     "type": type, | ||||
|     "account_id": accountId, | ||||
|     "realm_id": realmId, | ||||
|   }; | ||||
| } | ||||
| @@ -1,6 +1,7 @@ | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:solian/models/post.dart'; | ||||
| import 'package:solian/screens/account.dart'; | ||||
| import 'package:solian/screens/chat/index.dart'; | ||||
| import 'package:solian/screens/explore.dart'; | ||||
| import 'package:solian/screens/posts/comment_editor.dart'; | ||||
| import 'package:solian/screens/posts/moment_editor.dart'; | ||||
| @@ -13,6 +14,11 @@ final router = GoRouter( | ||||
|       name: 'explore', | ||||
|       builder: (context, state) => const ExploreScreen(), | ||||
|     ), | ||||
|     GoRoute( | ||||
|       path: '/chat', | ||||
|       name: 'chat', | ||||
|       builder: (context, state) => const ChatIndexScreen(), | ||||
|     ), | ||||
|     GoRoute( | ||||
|       path: '/account', | ||||
|       name: 'account', | ||||
|   | ||||
							
								
								
									
										74
									
								
								lib/screens/chat/index.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								lib/screens/chat/index.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| 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/utils/service_url.dart'; | ||||
| import 'package:solian/widgets/indent_wrapper.dart'; | ||||
| import 'package:flutter_gen/gen_l10n/app_localizations.dart'; | ||||
|  | ||||
| class ChatIndexScreen extends StatefulWidget { | ||||
|   const ChatIndexScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   State<ChatIndexScreen> createState() => _ChatIndexScreenState(); | ||||
| } | ||||
|  | ||||
| class _ChatIndexScreenState extends State<ChatIndexScreen> { | ||||
|   List<Channel> _channels = List.empty(); | ||||
|  | ||||
|   Future<void> fetchChannels(BuildContext context) async { | ||||
|     final auth = context.read<AuthProvider>(); | ||||
|     if (!await auth.isAuthorized()) return; | ||||
|  | ||||
|     var uri = getRequestUri('messaging', '/api/channels/me/available'); | ||||
|  | ||||
|     var res = await auth.client!.get(uri); | ||||
|     if (res.statusCode == 200) { | ||||
|       final result = jsonDecode(utf8.decode(res.bodyBytes)) as List<dynamic>; | ||||
|       setState(() { | ||||
|         _channels = result.map((x) => Channel.fromJson(x)).toList(); | ||||
|       }); | ||||
|     } else { | ||||
|       var message = utf8.decode(res.bodyBytes); | ||||
|       ScaffoldMessenger.of(context).showSnackBar( | ||||
|         SnackBar(content: Text("Something went wrong... $message")), | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     Future.delayed(Duration.zero, () { | ||||
|       fetchChannels(context); | ||||
|     }); | ||||
|  | ||||
|     super.initState(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return IndentWrapper( | ||||
|       title: AppLocalizations.of(context)!.chat, | ||||
|       child: RefreshIndicator( | ||||
|         onRefresh: () => fetchChannels(context), | ||||
|         child: ListView.builder( | ||||
|           itemCount: _channels.length, | ||||
|           itemBuilder: (context, index) { | ||||
|             final element = _channels[index]; | ||||
|             return ListTile( | ||||
|               leading: const CircleAvatar( | ||||
|                 backgroundColor: Colors.indigo, | ||||
|                 child: Icon(Icons.tag, color: Colors.white), | ||||
|               ), | ||||
|               title: Text(element.name), | ||||
|               subtitle: Text(element.description), | ||||
|               onTap: () {}, | ||||
|             ); | ||||
|           }, | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -19,8 +19,7 @@ class ExploreScreen extends StatefulWidget { | ||||
| } | ||||
|  | ||||
| class _ExploreScreenState extends State<ExploreScreen> { | ||||
|   final PagingController<int, Post> _pagingController = | ||||
|       PagingController(firstPageKey: 0); | ||||
|   final PagingController<int, Post> _pagingController = PagingController(firstPageKey: 0); | ||||
|  | ||||
|   final http.Client _client = http.Client(); | ||||
|  | ||||
| @@ -28,15 +27,12 @@ class _ExploreScreenState extends State<ExploreScreen> { | ||||
|     final offset = pageKey; | ||||
|     const take = 5; | ||||
|  | ||||
|     var uri = | ||||
|         getRequestUri('interactive', '/api/feed?take=$take&offset=$offset'); | ||||
|     var uri = getRequestUri('interactive', '/api/feed?take=$take&offset=$offset'); | ||||
|  | ||||
|     var res = await _client.get(uri); | ||||
|     if (res.statusCode == 200) { | ||||
|       final result = | ||||
|           PaginationResult.fromJson(jsonDecode(utf8.decode(res.bodyBytes))); | ||||
|       final items = | ||||
|           result.data?.map((x) => Post.fromJson(x)).toList() ?? List.empty(); | ||||
|       final result = PaginationResult.fromJson(jsonDecode(utf8.decode(res.bodyBytes))); | ||||
|       final items = result.data?.map((x) => Post.fromJson(x)).toList() ?? List.empty(); | ||||
|       final isLastPage = (result.count - pageKey) < take; | ||||
|       if (isLastPage || result.data == null) { | ||||
|         _pagingController.appendLastPage(items); | ||||
| @@ -77,8 +73,7 @@ class _ExploreScreenState extends State<ExploreScreen> { | ||||
|             constraints: const BoxConstraints(maxWidth: 640), | ||||
|             child: PagedListView<int, Post>.separated( | ||||
|               pagingController: _pagingController, | ||||
|               separatorBuilder: (context, index) => | ||||
|                   const Divider(thickness: 0.3), | ||||
|               separatorBuilder: (context, index) => const Divider(thickness: 0.3), | ||||
|               builderDelegate: PagedChildBuilderDelegate<Post>( | ||||
|                 itemBuilder: (context, item, index) => GestureDetector( | ||||
|                   child: PostItem( | ||||
|   | ||||
| @@ -41,6 +41,13 @@ class _SolianNavigationDrawerState extends State<SolianNavigationDrawer> { | ||||
|         ), | ||||
|         "explore", | ||||
|       ), | ||||
|       ( | ||||
|         NavigationDrawerDestination( | ||||
|           icon: const Icon(Icons.send), | ||||
|           label: Text(AppLocalizations.of(context)!.chat), | ||||
|         ), | ||||
|         "chat", | ||||
|       ), | ||||
|       ( | ||||
|         NavigationDrawerDestination( | ||||
|           icon: const Icon(Icons.account_circle), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user