From 5c4d9cc12fae44725886dabc6778233e0916224b Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Sat, 22 Feb 2020 20:05:04 +0100 Subject: [PATCH] Add public room discovery --- README.md | 2 +- .../list_items/public_room_list_item.dart | 48 ++++++++ lib/i18n/i18n.dart | 4 + lib/i18n/intl_de.arb | 10 ++ lib/i18n/intl_messages.arb | 12 +- lib/i18n/messages_de.dart | 2 + lib/i18n/messages_messages.dart | 2 + lib/views/chat_list.dart | 105 +++++++++++++++--- pubspec.lock | 6 +- pubspec.yaml | 2 +- 10 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 lib/components/list_items/public_room_list_item.dart diff --git a/README.md b/README.md index 7b90e22..8340ad4 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ flutter pub run intl_translation:extract_to_arb --output-dir=lib/i18n lib/i18n/i 4. Update the translations with this command: ``` -flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/i18n --no-use-deferred-loading lib/i18n/I18n.dart lib/i18n/intl_*.arb +flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/i18n --no-use-deferred-loading lib/i18n/i18n.dart lib/i18n/intl_*.arb ``` 5. Make sure your language is in `supportedLocales` in `/lib/main.dart`. \ No newline at end of file diff --git a/lib/components/list_items/public_room_list_item.dart b/lib/components/list_items/public_room_list_item.dart new file mode 100644 index 0000000..21c7bce --- /dev/null +++ b/lib/components/list_items/public_room_list_item.dart @@ -0,0 +1,48 @@ +import 'package:famedlysdk/famedlysdk.dart'; +import 'package:flutter/material.dart'; + +import '../../i18n/i18n.dart'; +import '../../utils/app_route.dart'; +import '../../views/chat.dart'; +import '../avatar.dart'; +import '../matrix.dart'; + +class PublicRoomListItem extends StatelessWidget { + final PublicRoomEntry publicRoomEntry; + + const PublicRoomListItem(this.publicRoomEntry, {Key key}) : super(key: key); + + void joinAction(BuildContext context) async { + final success = await Matrix.of(context) + .tryRequestWithLoadingDialog(publicRoomEntry.join()); + if (success != false) { + await Navigator.of(context).push( + AppRoute.defaultRoute( + context, + ChatView(publicRoomEntry.roomId), + ), + ); + } + } + + @override + Widget build(BuildContext context) { + final bool hasTopic = + publicRoomEntry.topic != null && publicRoomEntry.topic.isNotEmpty; + return ListTile( + leading: + Avatar(MxContent(publicRoomEntry.avatarUrl), publicRoomEntry.name), + title: Text(hasTopic + ? "${publicRoomEntry.name} (${publicRoomEntry.numJoinedMembers})" + : publicRoomEntry.name), + subtitle: Text( + hasTopic + ? publicRoomEntry.topic + : I18n.of(context).countParticipants( + publicRoomEntry.numJoinedMembers?.toString() ?? "0"), + maxLines: 1, + ), + onTap: () => joinAction(context), + ); + } +} diff --git a/lib/i18n/i18n.dart b/lib/i18n/i18n.dart index e690901..cd41803 100644 --- a/lib/i18n/i18n.dart +++ b/lib/i18n/i18n.dart @@ -188,6 +188,8 @@ class I18n { String get changeTheNameOfTheGroup => Intl.message("Change the name of the group"); + String get changeTheServer => Intl.message("Change the server"); + String get channelCorruptedDecryptError => Intl.message("The encryption has been corrupted"); @@ -478,6 +480,8 @@ class I18n { String get pleaseEnterYourUsername => Intl.message("Please enter your username"); + String get publicRooms => Intl.message("Public Rooms"); + String get rejoin => Intl.message("Rejoin"); String redactedAnEvent(String username) => Intl.message( diff --git a/lib/i18n/intl_de.arb b/lib/i18n/intl_de.arb index 9d2cc6b..780b6fb 100644 --- a/lib/i18n/intl_de.arb +++ b/lib/i18n/intl_de.arb @@ -216,6 +216,11 @@ "type": "text", "placeholders": {} }, + "Change the server": "Ändere den Server", + "@Change the server": { + "type": "text", + "placeholders": {} + }, "The encryption has been corrupted": "Die Verschlüsselung wurde korrumpiert", "@The encryption has been corrupted": { "type": "text", @@ -761,6 +766,11 @@ "type": "text", "placeholders": {} }, + "Public Rooms": "Öffentliche Räume", + "@Public Rooms": { + "type": "text", + "placeholders": {} + }, "Rejoin": "Wieder beitreten", "@Rejoin": { "type": "text", diff --git a/lib/i18n/intl_messages.arb b/lib/i18n/intl_messages.arb index 0ce6cfb..ade6f90 100644 --- a/lib/i18n/intl_messages.arb +++ b/lib/i18n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2020-02-22T08:18:47.225173", + "@@last_modified": "2020-02-22T20:02:42.757437", "About": "About", "@About": { "type": "text", @@ -216,6 +216,11 @@ "type": "text", "placeholders": {} }, + "Change the server": "Change the server", + "@Change the server": { + "type": "text", + "placeholders": {} + }, "The encryption has been corrupted": "The encryption has been corrupted", "@The encryption has been corrupted": { "type": "text", @@ -761,6 +766,11 @@ "type": "text", "placeholders": {} }, + "Public Rooms": "Public Rooms", + "@Public Rooms": { + "type": "text", + "placeholders": {} + }, "Rejoin": "Rejoin", "@Rejoin": { "type": "text", diff --git a/lib/i18n/messages_de.dart b/lib/i18n/messages_de.dart index 856094c..f3fd13b 100644 --- a/lib/i18n/messages_de.dart +++ b/lib/i18n/messages_de.dart @@ -150,6 +150,7 @@ class MessageLookup extends MessageLookupByLibrary { "Ban from chat" : MessageLookupByLibrary.simpleMessage("Aus dem Chat verbannen"), "Banned" : MessageLookupByLibrary.simpleMessage("Banned"), "Change the name of the group" : MessageLookupByLibrary.simpleMessage("Gruppenname ändern"), + "Change the server" : MessageLookupByLibrary.simpleMessage("Ändere den Server"), "Change your style" : MessageLookupByLibrary.simpleMessage("Change your style"), "Changelog" : MessageLookupByLibrary.simpleMessage("Changelog"), "Chat details" : MessageLookupByLibrary.simpleMessage("Gruppeninfo"), @@ -234,6 +235,7 @@ class MessageLookup extends MessageLookupByLibrary { "Please enter a matrix identifier" : MessageLookupByLibrary.simpleMessage("Bitte eine Matrix ID eingeben"), "Please enter your password" : MessageLookupByLibrary.simpleMessage("Bitte dein Passwort eingeben"), "Please enter your username" : MessageLookupByLibrary.simpleMessage("Bitte deinen Benutzernamen eingeben"), + "Public Rooms" : MessageLookupByLibrary.simpleMessage("Öffentliche Räume"), "Rejoin" : MessageLookupByLibrary.simpleMessage("Wieder beitreten"), "Remove" : MessageLookupByLibrary.simpleMessage("Entfernen"), "Remove all other devices" : MessageLookupByLibrary.simpleMessage("Alle anderen Geräte entfernen"), diff --git a/lib/i18n/messages_messages.dart b/lib/i18n/messages_messages.dart index 4e186ba..31f529c 100644 --- a/lib/i18n/messages_messages.dart +++ b/lib/i18n/messages_messages.dart @@ -150,6 +150,7 @@ class MessageLookup extends MessageLookupByLibrary { "Ban from chat" : MessageLookupByLibrary.simpleMessage("Ban from chat"), "Banned" : MessageLookupByLibrary.simpleMessage("Banned"), "Change the name of the group" : MessageLookupByLibrary.simpleMessage("Change the name of the group"), + "Change the server" : MessageLookupByLibrary.simpleMessage("Change the server"), "Change your style" : MessageLookupByLibrary.simpleMessage("Change your style"), "Changelog" : MessageLookupByLibrary.simpleMessage("Changelog"), "Chat details" : MessageLookupByLibrary.simpleMessage("Chat details"), @@ -234,6 +235,7 @@ class MessageLookup extends MessageLookupByLibrary { "Please enter a matrix identifier" : MessageLookupByLibrary.simpleMessage("Please enter a matrix identifier"), "Please enter your password" : MessageLookupByLibrary.simpleMessage("Please enter your password"), "Please enter your username" : MessageLookupByLibrary.simpleMessage("Please enter your username"), + "Public Rooms" : MessageLookupByLibrary.simpleMessage("Public Rooms"), "Rejoin" : MessageLookupByLibrary.simpleMessage("Rejoin"), "Remove" : MessageLookupByLibrary.simpleMessage("Remove"), "Remove all other devices" : MessageLookupByLibrary.simpleMessage("Remove all other devices"), diff --git a/lib/views/chat_list.dart b/lib/views/chat_list.dart index e523544..ccaaa2a 100644 --- a/lib/views/chat_list.dart +++ b/lib/views/chat_list.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:famedlysdk/famedlysdk.dart'; +import 'package:fluffychat/components/list_items/public_room_list_item.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -8,6 +9,7 @@ import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:toast/toast.dart'; import 'package:uni_links/uni_links.dart'; +import '../components/dialogs/simple_dialogs.dart'; import '../components/theme_switcher.dart'; import '../components/adaptive_page_layout.dart'; import '../components/list_items/chat_list_item.dart'; @@ -51,6 +53,10 @@ class _ChatListState extends State { StreamSubscription sub; final TextEditingController searchController = TextEditingController(); SelectMode selectMode = SelectMode.normal; + Timer coolDown; + PublicRoomsResponse publicRoomsResponse; + bool loadingPublicRooms = false; + String searchServer; Future waitForFirstSync(BuildContext context) async { Client client = Matrix.of(context).client; @@ -64,9 +70,38 @@ class _ChatListState extends State { @override void initState() { - searchController.addListener( - () => setState(() => null), - ); + searchController.addListener(() { + coolDown?.cancel(); + coolDown = Timer(Duration(seconds: 1), () async { + setState(() => loadingPublicRooms = true); + final newPublicRoomsResponse = + await Matrix.of(context).tryRequestWithErrorToast( + Matrix.of(context).client.requestPublicRooms( + limit: 30, + includeAllNetworks: true, + genericSearchTerm: searchController.text, + server: searchServer, + ), + ); + setState(() { + loadingPublicRooms = false; + if (newPublicRoomsResponse != false) { + publicRoomsResponse = newPublicRoomsResponse; + if (searchController.text.isNotEmpty && + searchController.text.isValidMatrixId && + searchController.text.sigil == "#") { + publicRoomsResponse.publicRooms.add( + PublicRoomEntry( + aliases: [searchController.text], + name: searchController.text, + roomId: searchController.text), + ); + } + } + }); + }); + setState(() => null); + }); initUniLinks(); super.initState(); } @@ -133,12 +168,38 @@ class _ChatListState extends State { leading: searchMode ? IconButton( icon: Icon(Icons.arrow_back), - onPressed: () => setState(() => searchMode = false), + onPressed: () => setState(() { + publicRoomsResponse = null; + loadingPublicRooms = false; + searchMode = false; + }), ) : null, automaticallyImplyLeading: false, actions: searchMode - ? null + ? [ + if (loadingPublicRooms) + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Center( + child: CircularProgressIndicator(strokeWidth: 2), + ), + ), + IconButton( + icon: Icon(Icons.domain), + onPressed: () async { + final String newSearchServer = await SimpleDialogs(context) + .enterText( + titleText: I18n.of(context).changeTheServer, + labelText: I18n.of(context).changeTheServer, + hintText: Matrix.of(context).client.userID.domain, + prefixText: "https://"); + if (newSearchServer?.isNotEmpty ?? false) { + searchServer = newSearchServer; + } + }, + ) + ] : [ IconButton( icon: Icon(Icons.search), @@ -201,8 +262,7 @@ class _ChatListState extends State { foregroundColor: Colors.white, backgroundColor: Colors.blue, label: I18n.of(context).createNewGroup, - labelStyle: - TextStyle(fontSize: 18.0, color: Colors.black), + labelStyle: TextStyle(fontSize: 18.0, color: Colors.black), onTap: () => Navigator.of(context).pushAndRemoveUntil( AppRoute.defaultRoute(context, NewGroupView()), (r) => r.isFirst), @@ -212,9 +272,7 @@ class _ChatListState extends State { foregroundColor: Colors.white, backgroundColor: Colors.green, label: I18n.of(context).newPrivateChat, - labelStyle: TextStyle( - fontSize: 18.0, - color: Colors.black), + labelStyle: TextStyle(fontSize: 18.0, color: Colors.black), onTap: () => Navigator.of(context).pushAndRemoveUntil( AppRoute.defaultRoute(context, NewPrivateChatView()), (r) => r.isFirst), @@ -231,7 +289,7 @@ class _ChatListState extends State { !room.displayname .toLowerCase() .contains(searchController.text.toLowerCase() ?? "")); - if (rooms.isEmpty) { + if (rooms.isEmpty && (!searchMode || publicRoomsResponse == null)) { return Center( child: Column( mainAxisSize: MainAxisSize.min, @@ -248,14 +306,27 @@ class _ChatListState extends State { ), ); } + final int publicRoomsCount = + (publicRoomsResponse?.publicRooms?.length ?? 0); + final int totalCount = rooms.length + publicRoomsCount; return ListView.separated( separatorBuilder: (BuildContext context, int i) => - Divider(indent: 70, height: 1), - itemCount: rooms.length, - itemBuilder: (BuildContext context, int i) => ChatListItem( - rooms[i], - activeChat: widget.activeChat == rooms[i].id, - ), + i == totalCount - publicRoomsCount - 1 + ? Material( + elevation: 2, + child: ListTile( + title: Text("Public Rooms:"), + ), + ) + : Divider(indent: 70, height: 1), + itemCount: totalCount, + itemBuilder: (BuildContext context, int i) => i < rooms.length + ? ChatListItem( + rooms[i], + activeChat: widget.activeChat == rooms[i].id, + ) + : PublicRoomListItem( + publicRoomsResponse.publicRooms[i - rooms.length]), ); } else { return Center( diff --git a/pubspec.lock b/pubspec.lock index 5011b53..b5e8351 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -124,8 +124,8 @@ packages: dependency: "direct main" description: path: "." - ref: "0b5e806cdfeccae6ef49c5cfe5dfa19c1a0ff259" - resolved-ref: "0b5e806cdfeccae6ef49c5cfe5dfa19c1a0ff259" + ref: "0b0facb0e058403e3ddf79cb85f318f7acf1e593" + resolved-ref: "0b0facb0e058403e3ddf79cb85f318f7acf1e593" url: "https://gitlab.com/famedly/famedlysdk.git" source: git version: "0.0.1" @@ -274,7 +274,7 @@ packages: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.16.1" + version: "0.16.0" intl_translation: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 879dac2..f615405 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,7 +27,7 @@ dependencies: famedlysdk: git: url: https://gitlab.com/famedly/famedlysdk.git - ref: 0b5e806cdfeccae6ef49c5cfe5dfa19c1a0ff259 + ref: 0b0facb0e058403e3ddf79cb85f318f7acf1e593 localstorage: ^3.0.1+4 bubble: ^1.1.9+1