diff --git a/lib/i18n/app_en.arb b/lib/i18n/app_en.arb index 4f8cffe..7827a82 100644 --- a/lib/i18n/app_en.arb +++ b/lib/i18n/app_en.arb @@ -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!", diff --git a/lib/i18n/app_zh.arb b/lib/i18n/app_zh.arb index 6a80f51..cca4b8c 100644 --- a/lib/i18n/app_zh.arb +++ b/lib/i18n/app_zh.arb @@ -1,6 +1,7 @@ { "solian": "索链", "explore": "探索", + "chat": "聊天", "account": "账号", "signIn": "登陆", "signInCaption": "登陆以发表帖子、文章、创建领域、和你的朋友聊天,以及获取更多功能!", diff --git a/lib/models/channel.dart b/lib/models/channel.dart new file mode 100644 index 0000000..1017307 --- /dev/null +++ b/lib/models/channel.dart @@ -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 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 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, + }; +} \ No newline at end of file diff --git a/lib/router.dart b/lib/router.dart index 93e268d..ce0e0d8 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -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', diff --git a/lib/screens/chat/index.dart b/lib/screens/chat/index.dart new file mode 100644 index 0000000..452a486 --- /dev/null +++ b/lib/screens/chat/index.dart @@ -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 createState() => _ChatIndexScreenState(); +} + +class _ChatIndexScreenState extends State { + List _channels = List.empty(); + + Future fetchChannels(BuildContext context) async { + final auth = context.read(); + 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; + 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: () {}, + ); + }, + ), + ), + ); + } +} diff --git a/lib/screens/explore.dart b/lib/screens/explore.dart index 83c3bb7..5f2bd88 100644 --- a/lib/screens/explore.dart +++ b/lib/screens/explore.dart @@ -19,8 +19,7 @@ class ExploreScreen extends StatefulWidget { } class _ExploreScreenState extends State { - final PagingController _pagingController = - PagingController(firstPageKey: 0); + final PagingController _pagingController = PagingController(firstPageKey: 0); final http.Client _client = http.Client(); @@ -28,15 +27,12 @@ class _ExploreScreenState extends State { 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 { constraints: const BoxConstraints(maxWidth: 640), child: PagedListView.separated( pagingController: _pagingController, - separatorBuilder: (context, index) => - const Divider(thickness: 0.3), + separatorBuilder: (context, index) => const Divider(thickness: 0.3), builderDelegate: PagedChildBuilderDelegate( itemBuilder: (context, item, index) => GestureDetector( child: PostItem( diff --git a/lib/widgets/navigation_drawer.dart b/lib/widgets/navigation_drawer.dart index ff150da..88139e8 100644 --- a/lib/widgets/navigation_drawer.dart +++ b/lib/widgets/navigation_drawer.dart @@ -41,6 +41,13 @@ class _SolianNavigationDrawerState extends State { ), "explore", ), + ( + NavigationDrawerDestination( + icon: const Icon(Icons.send), + label: Text(AppLocalizations.of(context)!.chat), + ), + "chat", + ), ( NavigationDrawerDestination( icon: const Icon(Icons.account_circle),