Big refactoring

This commit is contained in:
Christian Pauly 2020-06-18 13:39:24 +02:00
parent 35beaa4492
commit 1bd36023e7
11 changed files with 523 additions and 461 deletions

View File

@ -1,3 +1,11 @@
# Version 0.15.0
### Features:
- New room list app bar design
- Chat app bar transparent
### Changes:
- Show presences of users sharing a direct chat
- Big refactoring
# Version 0.14.0 - 2020-05-20 # Version 0.14.0 - 2020-05-20
### Features: ### Features:
- Implement image viewer - Implement image viewer

View File

@ -0,0 +1 @@
6c5611b14df049743797687d0807922a

View File

@ -1,52 +1,48 @@
PODS: PODS:
- file_picker (0.0.1): - file_picker (0.0.1):
- Flutter - Flutter
- Firebase/Core (6.21.0): - Firebase/Core (6.27.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseAnalytics (= 6.4.0) - FirebaseAnalytics (= 6.6.1)
- Firebase/CoreOnly (6.21.0): - Firebase/CoreOnly (6.27.0):
- FirebaseCore (= 6.6.5) - FirebaseCore (= 6.8.0)
- Firebase/Messaging (6.21.0): - Firebase/Messaging (6.27.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseMessaging (~> 4.3.0) - FirebaseMessaging (~> 4.5.0)
- firebase_messaging (0.0.1): - firebase_messaging (0.0.1):
- Firebase/Core - Firebase/Core
- Firebase/Messaging - Firebase/Messaging
- Flutter - Flutter
- FirebaseAnalytics (6.4.0): - FirebaseAnalytics (6.6.1):
- FirebaseCore (~> 6.6) - FirebaseCore (~> 6.8)
- FirebaseInstallations (~> 1.1) - FirebaseInstallations (~> 1.4)
- GoogleAppMeasurement (= 6.4.0) - GoogleAppMeasurement (= 6.6.1)
- GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/AppDelegateSwizzler (~> 6.0)
- GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0)
- GoogleUtilities/Network (~> 6.0) - GoogleUtilities/Network (~> 6.0)
- "GoogleUtilities/NSData+zlib (~> 6.0)" - "GoogleUtilities/NSData+zlib (~> 6.0)"
- nanopb (= 0.3.9011) - nanopb (~> 1.30905.0)
- FirebaseAnalyticsInterop (1.5.0) - FirebaseCore (6.8.0):
- FirebaseCore (6.6.5): - FirebaseCoreDiagnostics (~> 1.3)
- FirebaseCoreDiagnostics (~> 1.2)
- FirebaseCoreDiagnosticsInterop (~> 1.2)
- GoogleUtilities/Environment (~> 6.5) - GoogleUtilities/Environment (~> 6.5)
- GoogleUtilities/Logger (~> 6.5) - GoogleUtilities/Logger (~> 6.5)
- FirebaseCoreDiagnostics (1.2.2): - FirebaseCoreDiagnostics (1.4.0):
- FirebaseCoreDiagnosticsInterop (~> 1.2) - GoogleDataTransportCCTSupport (~> 3.1)
- GoogleDataTransportCCTSupport (~> 2.0)
- GoogleUtilities/Environment (~> 6.5) - GoogleUtilities/Environment (~> 6.5)
- GoogleUtilities/Logger (~> 6.5) - GoogleUtilities/Logger (~> 6.5)
- nanopb (~> 0.3.901) - nanopb (~> 1.30905.0)
- FirebaseCoreDiagnosticsInterop (1.2.0) - FirebaseInstallations (1.4.0):
- FirebaseInstallations (1.1.1): - FirebaseCore (~> 6.8)
- FirebaseCore (~> 6.6) - GoogleUtilities/Environment (~> 6.6)
- GoogleUtilities/UserDefaults (~> 6.5) - GoogleUtilities/UserDefaults (~> 6.6)
- PromisesObjC (~> 1.2) - PromisesObjC (~> 1.2)
- FirebaseInstanceID (4.3.2): - FirebaseInstanceID (4.4.0):
- FirebaseCore (~> 6.6) - FirebaseCore (~> 6.8)
- FirebaseInstallations (~> 1.0) - FirebaseInstallations (~> 1.0)
- GoogleUtilities/Environment (~> 6.5) - GoogleUtilities/Environment (~> 6.5)
- GoogleUtilities/UserDefaults (~> 6.5) - GoogleUtilities/UserDefaults (~> 6.5)
- FirebaseMessaging (4.3.0): - FirebaseMessaging (4.5.0):
- FirebaseAnalyticsInterop (~> 1.5) - FirebaseCore (~> 6.8)
- FirebaseCore (~> 6.6)
- FirebaseInstanceID (~> 4.3) - FirebaseInstanceID (~> 4.3)
- GoogleUtilities/AppDelegateSwizzler (~> 6.5) - GoogleUtilities/AppDelegateSwizzler (~> 6.5)
- GoogleUtilities/Environment (~> 6.5) - GoogleUtilities/Environment (~> 6.5)
@ -54,6 +50,8 @@ PODS:
- GoogleUtilities/UserDefaults (~> 6.5) - GoogleUtilities/UserDefaults (~> 6.5)
- Protobuf (>= 3.9.2, ~> 3.9) - Protobuf (>= 3.9.2, ~> 3.9)
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_keyboard_visibility (0.7.0):
- Flutter
- flutter_local_notifications (0.0.1): - flutter_local_notifications (0.0.1):
- Flutter - Flutter
- flutter_secure_storage (3.3.1): - flutter_secure_storage (3.3.1):
@ -62,42 +60,45 @@ PODS:
- Flutter - Flutter
- FMDB (2.7.5): - FMDB (2.7.5):
- FMDB/standard (= 2.7.5) - FMDB/standard (= 2.7.5)
- FMDB/SQLCipher (2.7.5):
- SQLCipher
- FMDB/standard (2.7.5) - FMDB/standard (2.7.5)
- GoogleAppMeasurement (6.4.0): - GoogleAppMeasurement (6.6.1):
- GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/AppDelegateSwizzler (~> 6.0)
- GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0)
- GoogleUtilities/Network (~> 6.0) - GoogleUtilities/Network (~> 6.0)
- "GoogleUtilities/NSData+zlib (~> 6.0)" - "GoogleUtilities/NSData+zlib (~> 6.0)"
- nanopb (= 0.3.9011) - nanopb (~> 1.30905.0)
- GoogleDataTransport (5.1.0) - GoogleDataTransport (6.2.1)
- GoogleDataTransportCCTSupport (2.0.1): - GoogleDataTransportCCTSupport (3.2.0):
- GoogleDataTransport (~> 5.1) - GoogleDataTransport (~> 6.1)
- nanopb (~> 0.3.901) - nanopb (~> 1.30905.0)
- GoogleUtilities/AppDelegateSwizzler (6.5.2): - GoogleUtilities/AppDelegateSwizzler (6.6.0):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/Network - GoogleUtilities/Network
- GoogleUtilities/Environment (6.5.2) - GoogleUtilities/Environment (6.6.0):
- GoogleUtilities/Logger (6.5.2): - PromisesObjC (~> 1.2)
- GoogleUtilities/Logger (6.6.0):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/MethodSwizzler (6.5.2): - GoogleUtilities/MethodSwizzler (6.6.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/Network (6.5.2): - GoogleUtilities/Network (6.6.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib" - "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability - GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (6.5.2)" - "GoogleUtilities/NSData+zlib (6.6.0)"
- GoogleUtilities/Reachability (6.5.2): - GoogleUtilities/Reachability (6.6.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/UserDefaults (6.5.2): - GoogleUtilities/UserDefaults (6.6.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- image_picker (0.0.1): - image_picker (0.0.1):
- Flutter - Flutter
- nanopb (0.3.9011): - nanopb (1.30905.0):
- nanopb/decode (= 0.3.9011) - nanopb/decode (= 1.30905.0)
- nanopb/encode (= 0.3.9011) - nanopb/encode (= 1.30905.0)
- nanopb/decode (0.3.9011) - nanopb/decode (1.30905.0)
- nanopb/encode (0.3.9011) - nanopb/encode (1.30905.0)
- OLMKit (3.1.0): - OLMKit (3.1.0):
- OLMKit/olmc (= 3.1.0) - OLMKit/olmc (= 3.1.0)
- OLMKit/olmcpp (= 3.1.0) - OLMKit/olmcpp (= 3.1.0)
@ -107,8 +108,8 @@ PODS:
- Flutter - Flutter
- path_provider (0.0.1): - path_provider (0.0.1):
- Flutter - Flutter
- PromisesObjC (1.2.8) - PromisesObjC (1.2.9)
- Protobuf (3.11.4) - Protobuf (3.12.0)
- receive_sharing_intent (0.0.1): - receive_sharing_intent (0.0.1):
- Flutter - Flutter
- share (0.5.2): - share (0.5.2):
@ -116,6 +117,14 @@ PODS:
- sqflite (0.0.1): - sqflite (0.0.1):
- Flutter - Flutter
- FMDB (~> 2.7.2) - FMDB (~> 2.7.2)
- sqflite_sqlcipher (0.0.1):
- Flutter
- FMDB/SQLCipher (~> 2.7.5)
- SQLCipher (4.4.0):
- SQLCipher/standard (= 4.4.0)
- SQLCipher/common (4.4.0)
- SQLCipher/standard (4.4.0):
- SQLCipher/common
- url_launcher (0.0.1): - url_launcher (0.0.1):
- Flutter - Flutter
- url_launcher_macos (0.0.1): - url_launcher_macos (0.0.1):
@ -129,6 +138,7 @@ DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- flutter_sound (from `.symlinks/plugins/flutter_sound/ios`) - flutter_sound (from `.symlinks/plugins/flutter_sound/ios`)
@ -139,6 +149,7 @@ DEPENDENCIES:
- receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`) - receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
- share (from `.symlinks/plugins/share/ios`) - share (from `.symlinks/plugins/share/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`)
- sqflite_sqlcipher (from `.symlinks/plugins/sqflite_sqlcipher/ios`)
- url_launcher (from `.symlinks/plugins/url_launcher/ios`) - url_launcher (from `.symlinks/plugins/url_launcher/ios`)
- url_launcher_macos (from `.symlinks/plugins/url_launcher_macos/ios`) - url_launcher_macos (from `.symlinks/plugins/url_launcher_macos/ios`)
- url_launcher_web (from `.symlinks/plugins/url_launcher_web/ios`) - url_launcher_web (from `.symlinks/plugins/url_launcher_web/ios`)
@ -148,10 +159,8 @@ SPEC REPOS:
trunk: trunk:
- Firebase - Firebase
- FirebaseAnalytics - FirebaseAnalytics
- FirebaseAnalyticsInterop
- FirebaseCore - FirebaseCore
- FirebaseCoreDiagnostics - FirebaseCoreDiagnostics
- FirebaseCoreDiagnosticsInterop
- FirebaseInstallations - FirebaseInstallations
- FirebaseInstanceID - FirebaseInstanceID
- FirebaseMessaging - FirebaseMessaging
@ -164,6 +173,7 @@ SPEC REPOS:
- OLMKit - OLMKit
- PromisesObjC - PromisesObjC
- Protobuf - Protobuf
- SQLCipher
EXTERNAL SOURCES: EXTERNAL SOURCES:
file_picker: file_picker:
@ -172,6 +182,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_messaging/ios" :path: ".symlinks/plugins/firebase_messaging/ios"
Flutter: Flutter:
:path: Flutter :path: Flutter
flutter_keyboard_visibility:
:path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
flutter_local_notifications: flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios" :path: ".symlinks/plugins/flutter_local_notifications/ios"
flutter_secure_storage: flutter_secure_storage:
@ -190,6 +202,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/share/ios" :path: ".symlinks/plugins/share/ios"
sqflite: sqflite:
:path: ".symlinks/plugins/sqflite/ios" :path: ".symlinks/plugins/sqflite/ios"
sqflite_sqlcipher:
:path: ".symlinks/plugins/sqflite_sqlcipher/ios"
url_launcher: url_launcher:
:path: ".symlinks/plugins/url_launcher/ios" :path: ".symlinks/plugins/url_launcher/ios"
url_launcher_macos: url_launcher_macos:
@ -201,35 +215,36 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
file_picker: 408623be2125b79a4539cf703be3d4b3abe5e245 file_picker: 408623be2125b79a4539cf703be3d4b3abe5e245
Firebase: f378c80340dd41c0ad0914af740c021eb282a04b Firebase: fc4cbf6f1592636431821ef9a3c557e4dfd9f268
firebase_messaging: 73b3e7dd7b3b6a7e4bdac10d5295211ca4f87f90 firebase_messaging: cffb57ce40958c6204f03fb0c81713e4cd1e240c
FirebaseAnalytics: a1a0b3327ceb5cd5b4bacffdb293f6c909aa087d FirebaseAnalytics: 0ea640473474f036cabbc2576e20c2d63671c92f
FirebaseAnalyticsInterop: 3f86269c38ae41f47afeb43ebf32a001f58fcdae FirebaseCore: feda061cb1ee6d8ad4824f4a4a8ffbcfe284f595
FirebaseCore: 9f495d3afacb7b558711e6218ebb14b1c51b5802 FirebaseCoreDiagnostics: 4505e4d4009b1d93f605088ee7d7764d5f0d1c84
FirebaseCoreDiagnostics: e9b4cd8ba60dee0f2d13347332e4b7898cca5b61 FirebaseInstallations: 293f567159b6d66d1c990f13bb868066096c94ec
FirebaseCoreDiagnosticsInterop: 296e2c5f5314500a850ad0b83e9e7c10b011a850 FirebaseInstanceID: 3b119bfe90e904851218159c9a4ecb847cc51d18
FirebaseInstallations: acb3216eb9784d3b1d2d2d635ff74fa892cc0c44 FirebaseMessaging: ad9e1a80ea64905e01a0ce1b3eb76a2944544151
FirebaseInstanceID: 7ee0d6777013bb952f377b41965bf132b6a075be
FirebaseMessaging: 4ec33842d36b3319e062e51fb8b35a74f726950d
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
flutter_keyboard_visibility: 6195387fb6d8f46e5cd6dda4a4154e41f800f545
flutter_local_notifications: 9e4738ce2471c5af910d961a6b7eadcf57c50186 flutter_local_notifications: 9e4738ce2471c5af910d961a6b7eadcf57c50186
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
flutter_sound: 0e8163ceac1e00eb6d894e2ae4641ba726a2c479 flutter_sound: 0e8163ceac1e00eb6d894e2ae4641ba726a2c479
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
GoogleAppMeasurement: 6e68a94d0eaeb1d73ef6b0ed4f7334e29d63ae29 GoogleAppMeasurement: 2fd5c5a56c069db635c8e7b92d4809a9591d0a69
GoogleDataTransport: b29a21d813e906014ca16c00897827e40e4a24ab GoogleDataTransport: 9a8a16f79feffc7f42096743de2a7c4815e84020
GoogleDataTransportCCTSupport: 6f15a89b0ca35d6fa523e1f752ef818588885988 GoogleDataTransportCCTSupport: 489c1265d2c85b68187a83a911913d190012158d
GoogleUtilities: ad0f3b691c67909d03a3327cc205222ab8f42e0e GoogleUtilities: 39530bc0ad980530298e9c4af8549e991fd033b1
image_picker: e3eacd46b94694dde7cf2705955cece853aa1a8f image_picker: e3eacd46b94694dde7cf2705955cece853aa1a8f
nanopb: 18003b5e52dab79db540fe93fe9579f399bd1ccd nanopb: c43f40fadfe79e8b8db116583945847910cbabc9
OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639 OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639
open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d
PromisesObjC: c119f3cd559f50b7ae681fa59dc1acd19173b7e6 PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75
Protobuf: 176220c526ad8bd09ab1fb40a978eac3fef665f7 Protobuf: 2793fcd0622a00b546c60e7cbbcc493e043e9bb9
receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1 receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1
share: bae0a282aab4483288913fc4dc0b935d4b491f2e share: bae0a282aab4483288913fc4dc0b935d4b491f2e
sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0 sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0
sqflite_sqlcipher: 45e72be2f26bde6ad196ff8b084123d8634ba921
SQLCipher: e434ed542b24f38ea7b36468a13f9765e1b5c072
url_launcher: a1c0cc845906122c4784c542523d8cacbded5626 url_launcher: a1c0cc845906122c4784c542523d8cacbded5626
url_launcher_macos: fd7894421cd39320dce5f292fc99ea9270b2a313 url_launcher_macos: fd7894421cd39320dce5f292fc99ea9270b2a313
url_launcher_web: e5527357f037c87560776e36436bf2b0288b965c url_launcher_web: e5527357f037c87560776e36436bf2b0288b965c

View File

@ -328,7 +328,6 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = { 249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
@ -446,7 +445,6 @@
}; };
97C147031CF9000F007C117D /* Debug */ = { 97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
@ -503,7 +501,6 @@
}; };
97C147041CF9000F007C117D /* Release */ = { 97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;

View File

@ -167,7 +167,12 @@ class SimpleDialogs {
children: <Widget>[ children: <Widget>[
CircularProgressIndicator(), CircularProgressIndicator(),
SizedBox(width: 16), SizedBox(width: 16),
Text(L10n.of(context).loadingPleaseWait), Expanded(
child: Text(
L10n.of(context).loadingPleaseWait,
overflow: TextOverflow.ellipsis,
maxLines: 1,
)),
], ],
), ),
), ),

View File

@ -1,7 +1,8 @@
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/dialogs/presence_dialog.dart'; import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/views/chat.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../utils/client_presence_extension.dart';
import '../avatar.dart'; import '../avatar.dart';
import '../matrix.dart'; import '../matrix.dart';
@ -10,50 +11,48 @@ class PresenceListItem extends StatelessWidget {
const PresenceListItem(this.presence); const PresenceListItem(this.presence);
static final Map<String, Profile> _presences = {}; void _startChatAction(BuildContext context, String userId) async {
final roomId = await User(userId,
Future<Profile> _requestProfile(BuildContext context) async { room: Room(client: Matrix.of(context).client, id: ''))
_presences[presence.senderId] ??= .startDirectChat();
await Matrix.of(context).client.getProfileFromUserId(presence.senderId); await Navigator.of(context).pushAndRemoveUntil(
return _presences[presence.senderId]; AppRoute.defaultRoute(
context,
ChatView(roomId),
),
(Route r) => r.isFirst);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder<Profile>( return FutureBuilder<Profile>(
future: _requestProfile(context), future:
Matrix.of(context).client.requestProfileCached(presence.senderId),
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData) return Container();
Uri avatarUrl; Uri avatarUrl;
var displayname = presence.senderId.localpart; var displayname = presence.senderId.localpart;
if (snapshot.hasData) { if (snapshot.hasData) {
avatarUrl = snapshot.data.avatarUrl; avatarUrl = snapshot.data.avatarUrl;
displayname = snapshot.data.displayname; displayname =
snapshot.data.displayname ?? presence.senderId.localpart;
} }
return InkWell( return InkWell(
onTap: () => showDialog( onTap: () => _startChatAction(context, presence.senderId),
context: context, child: Container(
builder: (c) => PresenceDialog( width: 80,
presence, child: Column(
avatarUrl: avatarUrl, children: <Widget>[
displayname: displayname, SizedBox(height: 9),
), Avatar(avatarUrl, displayname),
child: Container( Padding(
width: 80, padding: const EdgeInsets.all(6.0),
child: Column( child: Text(
children: <Widget>[ displayname,
SizedBox(height: 9), overflow: TextOverflow.ellipsis,
Avatar(avatarUrl, displayname), maxLines: 1,
Padding(
padding: const EdgeInsets.all(6.0),
child: Text(
displayname,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
), ),
], ),
), ],
), ),
), ),
); );

View File

@ -3,8 +3,18 @@ import 'package:famedlysdk/famedlysdk.dart';
extension ClientPresenceExtension on Client { extension ClientPresenceExtension on Client {
List<Presence> get statusList { List<Presence> get statusList {
final statusList = presences.values.toList().reversed.toList(); final statusList = presences.values.toList().reversed.toList();
statusList.removeWhere((p) => p.presence.statusMsg?.isEmpty ?? true); final directRooms = rooms.where((r) => r.isDirectChat).toList();
statusList.removeWhere((p) =>
directRooms.indexWhere((r) => r.directChatMatrixID == p.senderId) ==
-1);
statusList.reversed.toList(); statusList.reversed.toList();
return statusList; return statusList;
} }
static final Map<String, Profile> presencesCache = {};
Future<Profile> requestProfileCached(String senderId) async {
presencesCache[senderId] ??= await getProfileFromUserId(senderId);
return presencesCache[senderId];
}
} }

View File

@ -358,7 +358,9 @@ class _ChatState extends State<_Chat> {
} }
return Scaffold( return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).appBarTheme.color.withOpacity(0.9),
leading: selectMode leading: selectMode
? IconButton( ? IconButton(
icon: Icon(Icons.close), icon: Icon(Icons.close),
@ -462,337 +464,328 @@ class _ChatState extends State<_Chat> {
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
), ),
SafeArea( Column(
child: Column( children: <Widget>[
children: <Widget>[ if (_loadingHistory) LinearProgressIndicator(),
if (_loadingHistory) LinearProgressIndicator(), Expanded(
Expanded( child: FutureBuilder<bool>(
child: FutureBuilder<bool>( future: getTimeline(),
future: getTimeline(), builder: (BuildContext context, snapshot) {
builder: (BuildContext context, snapshot) { if (!snapshot.hasData) {
if (!snapshot.hasData) { return Center(
return Center( child: CircularProgressIndicator(),
child: CircularProgressIndicator(), );
); }
}
if (room.notificationCount != null && if (room.notificationCount != null &&
room.notificationCount > 0 && room.notificationCount > 0 &&
timeline != null && timeline != null &&
timeline.events.isNotEmpty) { timeline.events.isNotEmpty) {
room.sendReadReceipt(timeline.events.first.eventId); room.sendReadReceipt(timeline.events.first.eventId);
} }
if (timeline.events.isEmpty) return Container(); if (timeline.events.isEmpty) return Container();
return ListView.builder( return ListView.builder(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: max( horizontal: max(
0, 0,
(MediaQuery.of(context).size.width - (MediaQuery.of(context).size.width -
AdaptivePageLayout.defaultMinWidth * AdaptivePageLayout.defaultMinWidth *
3.5) / 3.5) /
2), 2),
), ),
reverse: true, reverse: true,
itemCount: timeline.events.length + 2, itemCount: timeline.events.length + 2,
controller: _scrollController, controller: _scrollController,
itemBuilder: (BuildContext context, int i) { itemBuilder: (BuildContext context, int i) {
return i == timeline.events.length + 1 return i == timeline.events.length + 1
? _canLoadMore && !_loadingHistory ? _canLoadMore && !_loadingHistory
? FlatButton( ? FlatButton(
child: Text( child: Text(
L10n.of(context).loadMore, L10n.of(context).loadMore,
style: TextStyle( style: TextStyle(
color: color: Theme.of(context).primaryColor,
Theme.of(context).primaryColor, fontWeight: FontWeight.bold,
fontWeight: FontWeight.bold, decoration: TextDecoration.underline,
decoration:
TextDecoration.underline,
),
), ),
onPressed: requestHistory, ),
) onPressed: requestHistory,
: Container() )
: i == 0 : Container()
? AnimatedContainer( : i == 0
height: seenByText.isEmpty ? 0 : 24, ? AnimatedContainer(
duration: seenByText.isEmpty height: seenByText.isEmpty ? 0 : 24,
? Duration(milliseconds: 0) duration: seenByText.isEmpty
: Duration(milliseconds: 500), ? Duration(milliseconds: 0)
alignment: : Duration(milliseconds: 500),
timeline.events.first.senderId == alignment:
client.userID timeline.events.first.senderId ==
? Alignment.topRight client.userID
: Alignment.topLeft, ? Alignment.topRight
child: Text( : Alignment.topLeft,
seenByText, child: Text(
maxLines: 1, seenByText,
overflow: TextOverflow.ellipsis, maxLines: 1,
style: TextStyle( overflow: TextOverflow.ellipsis,
color: style: TextStyle(
Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
),
), ),
padding: EdgeInsets.only( ),
left: 8, padding: EdgeInsets.only(
right: 8, left: 8,
bottom: 8, right: 8,
), bottom: 8,
) ),
: Message(timeline.events[i - 1], )
onAvatarTab: (Event event) { : Message(timeline.events[i - 1],
sendController.text += onAvatarTab: (Event event) {
' ${event.senderId}'; sendController.text +=
}, onSelect: (Event event) { ' ${event.senderId}';
if (!event.redacted) { }, onSelect: (Event event) {
if (selectedEvents.contains(event)) { if (!event.redacted) {
setState( if (selectedEvents.contains(event)) {
() => setState(
selectedEvents.remove(event), () => selectedEvents.remove(event),
); );
} else { } else {
setState( setState(
() => selectedEvents.add(event), () => selectedEvents.add(event),
);
}
selectedEvents.sort(
(a, b) => a.originServerTs
.compareTo(b.originServerTs),
); );
} }
}, selectedEvents.sort(
longPressSelect: selectedEvents.isEmpty, (a, b) => a.originServerTs
selected: selectedEvents .compareTo(b.originServerTs),
.contains(timeline.events[i - 1]), );
timeline: timeline, }
nextEvent: i >= 2 },
? timeline.events[i - 2] longPressSelect: selectedEvents.isEmpty,
: null); selected: selectedEvents
}); .contains(timeline.events[i - 1]),
}, timeline: timeline,
), nextEvent: i >= 2
? timeline.events[i - 2]
: null);
});
},
), ),
AnimatedContainer( ),
duration: Duration(milliseconds: 300), AnimatedContainer(
height: replyEvent != null ? 56 : 0, duration: Duration(milliseconds: 300),
child: Material( height: replyEvent != null ? 56 : 0,
color: Theme.of(context).secondaryHeaderColor, child: Material(
child: Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () => setState(() => replyEvent = null),
),
Expanded(
child: ReplyContent(replyEvent),
),
],
),
),
),
Divider(
height: 1,
color: Theme.of(context).secondaryHeaderColor, color: Theme.of(context).secondaryHeaderColor,
thickness: 1, child: Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () => setState(() => replyEvent = null),
),
Expanded(
child: ReplyContent(replyEvent),
),
],
),
), ),
room.canSendDefaultMessages && ),
room.membership == Membership.join Divider(
? Container( height: 1,
decoration: BoxDecoration( color: Theme.of(context).secondaryHeaderColor,
color: Theme.of(context) thickness: 1,
.backgroundColor ),
.withOpacity(0.8), room.canSendDefaultMessages && room.membership == Membership.join
), ? Container(
child: Row( decoration: BoxDecoration(
crossAxisAlignment: CrossAxisAlignment.center, color:
mainAxisAlignment: MainAxisAlignment.spaceBetween, Theme.of(context).backgroundColor.withOpacity(0.8),
children: selectMode ),
? <Widget>[ child: Row(
Container( crossAxisAlignment: CrossAxisAlignment.center,
height: 56, mainAxisAlignment: MainAxisAlignment.spaceBetween,
child: FlatButton( children: selectMode
onPressed: () => ? <Widget>[
forwardEventsAction(context), Container(
child: Row( height: 56,
children: <Widget>[ child: FlatButton(
Icon(Icons.keyboard_arrow_left), onPressed: () =>
Text(L10n.of(context).forward), forwardEventsAction(context),
], child: Row(
), children: <Widget>[
), Icon(Icons.keyboard_arrow_left),
), Text(L10n.of(context).forward),
selectedEvents.length == 1
? selectedEvents.first.status > 0
? Container(
height: 56,
child: FlatButton(
onPressed: () => replyAction(),
child: Row(
children: <Widget>[
Text(
L10n.of(context).reply),
Icon(Icons
.keyboard_arrow_right),
],
),
),
)
: Container(
height: 56,
child: FlatButton(
onPressed: () =>
sendAgainAction(),
child: Row(
children: <Widget>[
Text(L10n.of(context)
.tryToSendAgain),
SizedBox(width: 4),
Icon(Icons.send, size: 16),
],
),
),
)
: Container(),
]
: <Widget>[
if (!kIsWeb && inputText.isEmpty)
PopupMenuButton<String>(
icon: Icon(Icons.add),
onSelected: (String choice) async {
if (choice == 'file') {
sendFileAction(context);
} else if (choice == 'image') {
sendImageAction(context);
}
if (choice == 'camera') {
openCameraAction(context);
}
if (choice == 'voice') {
voiceMessageAction(context);
}
},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: 'file',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
child: Icon(Icons.attachment),
),
title:
Text(L10n.of(context).sendFile),
contentPadding: EdgeInsets.all(0),
),
),
PopupMenuItem<String>(
value: 'image',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
child: Icon(Icons.image),
),
title: Text(
L10n.of(context).sendImage),
contentPadding: EdgeInsets.all(0),
),
),
PopupMenuItem<String>(
value: 'camera',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.purple,
foregroundColor: Colors.white,
child: Icon(Icons.camera_alt),
),
title: Text(
L10n.of(context).openCamera),
contentPadding: EdgeInsets.all(0),
),
),
PopupMenuItem<String>(
value: 'voice',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
child: Icon(Icons.mic),
),
title: Text(
L10n.of(context).voiceMessage),
contentPadding: EdgeInsets.all(0),
),
),
], ],
), ),
EncryptionButton(room), ),
Expanded( ),
child: Padding( selectedEvents.length == 1
padding: const EdgeInsets.symmetric( ? selectedEvents.first.status > 0
vertical: 4.0), ? Container(
child: InputBar( height: 56,
room: room, child: FlatButton(
minLines: 1, onPressed: () => replyAction(),
maxLines: kIsWeb ? 1 : 8, child: Row(
keyboardType: kIsWeb children: <Widget>[
? TextInputType.text Text(L10n.of(context).reply),
: TextInputType.multiline, Icon(Icons
onSubmitted: (String text) { .keyboard_arrow_right),
send(); ],
FocusScope.of(context) ),
.requestFocus(inputFocus); ),
}, )
focusNode: inputFocus, : Container(
controller: sendController, height: 56,
decoration: InputDecoration( child: FlatButton(
hintText: onPressed: () =>
L10n.of(context).writeAMessage, sendAgainAction(),
border: InputBorder.none, child: Row(
children: <Widget>[
Text(L10n.of(context)
.tryToSendAgain),
SizedBox(width: 4),
Icon(Icons.send, size: 16),
],
),
),
)
: Container(),
]
: <Widget>[
if (!kIsWeb && inputText.isEmpty)
PopupMenuButton<String>(
icon: Icon(Icons.add),
onSelected: (String choice) async {
if (choice == 'file') {
sendFileAction(context);
} else if (choice == 'image') {
sendImageAction(context);
}
if (choice == 'camera') {
openCameraAction(context);
}
if (choice == 'voice') {
voiceMessageAction(context);
}
},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: 'file',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
child: Icon(Icons.attachment),
),
title:
Text(L10n.of(context).sendFile),
contentPadding: EdgeInsets.all(0),
), ),
onChanged: (String text) {
typingCoolDown?.cancel();
typingCoolDown =
Timer(Duration(seconds: 2), () {
typingCoolDown = null;
currentlyTyping = false;
room.sendTypingInfo(false);
});
typingTimeout ??=
Timer(Duration(seconds: 30), () {
typingTimeout = null;
currentlyTyping = false;
});
if (!currentlyTyping) {
currentlyTyping = true;
room.sendTypingInfo(true,
timeout: Duration(seconds: 30)
.inMilliseconds);
}
setState(() => inputText = text);
},
), ),
PopupMenuItem<String>(
value: 'image',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
child: Icon(Icons.image),
),
title:
Text(L10n.of(context).sendImage),
contentPadding: EdgeInsets.all(0),
),
),
PopupMenuItem<String>(
value: 'camera',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.purple,
foregroundColor: Colors.white,
child: Icon(Icons.camera_alt),
),
title:
Text(L10n.of(context).openCamera),
contentPadding: EdgeInsets.all(0),
),
),
PopupMenuItem<String>(
value: 'voice',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
child: Icon(Icons.mic),
),
title: Text(
L10n.of(context).voiceMessage),
contentPadding: EdgeInsets.all(0),
),
),
],
),
EncryptionButton(room),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4.0),
child: InputBar(
room: room,
minLines: 1,
maxLines: kIsWeb ? 1 : 8,
keyboardType: kIsWeb
? TextInputType.text
: TextInputType.multiline,
onSubmitted: (String text) {
send();
FocusScope.of(context)
.requestFocus(inputFocus);
},
focusNode: inputFocus,
controller: sendController,
decoration: InputDecoration(
hintText:
L10n.of(context).writeAMessage,
border: InputBorder.none,
),
onChanged: (String text) {
typingCoolDown?.cancel();
typingCoolDown =
Timer(Duration(seconds: 2), () {
typingCoolDown = null;
currentlyTyping = false;
room.sendTypingInfo(false);
});
typingTimeout ??=
Timer(Duration(seconds: 30), () {
typingTimeout = null;
currentlyTyping = false;
});
if (!currentlyTyping) {
currentlyTyping = true;
room.sendTypingInfo(true,
timeout: Duration(seconds: 30)
.inMilliseconds);
}
setState(() => inputText = text);
},
), ),
), ),
if (!kIsWeb && inputText.isEmpty) ),
IconButton( if (!kIsWeb && inputText.isEmpty)
icon: Icon(Icons.mic), IconButton(
onPressed: () => icon: Icon(Icons.mic),
voiceMessageAction(context), onPressed: () =>
), voiceMessageAction(context),
if (kIsWeb || inputText.isNotEmpty) ),
IconButton( if (kIsWeb || inputText.isNotEmpty)
icon: Icon(Icons.send), IconButton(
onPressed: () => send(), icon: Icon(Icons.send),
), onPressed: () => send(),
], ),
), ],
) ),
: Container(), )
], : Container(),
), ],
), ),
], ],
), ),

View File

@ -298,34 +298,61 @@ class _ChatListState extends State<ChatList> {
onPressed: () => onPressed: () =>
Matrix.of(context).shareContent = null, Matrix.of(context).shareContent = null,
), ),
automaticallyImplyLeading: false,
titleSpacing: 0, titleSpacing: 0,
title: selectMode == SelectMode.share title: selectMode == SelectMode.share
? Text(L10n.of(context).share) ? Text(L10n.of(context).share)
: Container( : Container(
padding: EdgeInsets.all(8),
height: 42, height: 42,
margin: EdgeInsets.only(right: 8), margin: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration( child: Material(
color: Theme.of(context).secondaryHeaderColor, elevation: 5,
borderRadius: BorderRadius.circular(90), borderRadius: BorderRadius.circular(7),
), child: Padding(
child: TextField( padding: EdgeInsets.all(8),
autocorrect: false, child: Row(
controller: searchController, children: [
decoration: InputDecoration( Builder(
suffixIcon: loadingPublicRooms builder: (context) => IconButton(
? Container( padding: EdgeInsets.zero,
alignment: Alignment.centerRight, icon: Icon(Icons.menu),
child: Container( onPressed: () =>
width: 20, Scaffold.of(context).openDrawer(),
height: 20, ),
child: CircularProgressIndicator(), ),
Expanded(
child: TextField(
autocorrect: false,
controller: searchController,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(9),
border: InputBorder.none,
hintText:
L10n.of(context).searchForAChat,
), ),
) ),
: Icon(Icons.search), ),
contentPadding: EdgeInsets.all(9), loadingPublicRooms
border: InputBorder.none, ? Container(
hintText: L10n.of(context).searchForAChat, alignment: Alignment.centerRight,
child: Container(
width: 20,
height: 20,
child:
CircularProgressIndicator(),
),
)
: IconButton(
padding: EdgeInsets.zero,
icon: Icon(Icons.account_circle),
onPressed: () =>
Navigator.of(context).push(
AppRoute.defaultRoute(
context,
SettingsView())),
),
],
),
), ),
), ),
), ),
@ -433,9 +460,9 @@ class _ChatListState extends State<ChatList> {
? Container() ? Container()
: PreferredSize( : PreferredSize(
preferredSize: preferredSize:
Size.fromHeight(89), Size.fromHeight(90),
child: Container( child: Container(
height: 81, height: 82,
child: ListView.builder( child: ListView.builder(
scrollDirection: scrollDirection:
Axis.horizontal, Axis.horizontal,

View File

@ -71,6 +71,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.3" version: "1.1.3"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
collection: collection:
dependency: transitive dependency: transitive
description: description:
@ -129,12 +136,19 @@ packages:
url: "https://github.com/simolus3/moor.git" url: "https://github.com/simolus3/moor.git"
source: git source: git
version: "1.0.0" version: "1.0.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
famedlysdk: famedlysdk:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: "857775cf37804440717ce797e0ed63fd39066904" ref: b8c6decafc52cbf5c09288c6c6dde62b62ae978f
resolved-ref: "857775cf37804440717ce797e0ed63fd39066904" resolved-ref: b8c6decafc52cbf5c09288c6c6dde62b62ae978f
url: "https://gitlab.com/famedly/famedlysdk.git" url: "https://gitlab.com/famedly/famedlysdk.git"
source: git source: git
version: "0.0.1" version: "0.0.1"
@ -469,7 +483,7 @@ packages:
name: package_config name: package_config
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.9.3"
package_resolver: package_resolver:
dependency: transitive dependency: transitive
description: description:
@ -483,7 +497,7 @@ packages:
name: path name: path
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.6.4" version: "1.7.0"
path_drawing: path_drawing:
dependency: transitive dependency: transitive
description: description:
@ -561,13 +575,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.4.2" version: "1.4.2"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
random_string: random_string:
dependency: "direct main" dependency: "direct main"
description: description:
@ -628,7 +635,7 @@ packages:
name: source_map_stack_trace name: source_map_stack_trace
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.5" version: "2.0.0"
source_maps: source_maps:
dependency: transitive dependency: transitive
description: description:
@ -705,21 +712,21 @@ packages:
name: test name: test
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.13.0" version: "1.14.7"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.15" version: "0.2.16"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.1" version: "0.3.7"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:

View File

@ -27,7 +27,7 @@ dependencies:
famedlysdk: famedlysdk:
git: git:
url: https://gitlab.com/famedly/famedlysdk.git url: https://gitlab.com/famedly/famedlysdk.git
ref: 857775cf37804440717ce797e0ed63fd39066904 ref: b8c6decafc52cbf5c09288c6c6dde62b62ae978f
localstorage: ^3.0.1+4 localstorage: ^3.0.1+4
bubble: ^1.1.9+1 bubble: ^1.1.9+1