Add Cross-Signing
This commit is contained in:
parent
44bbb25f6e
commit
4550686829
|
@ -4,9 +4,13 @@
|
||||||
- Chat app bar transparent
|
- Chat app bar transparent
|
||||||
- Implement web file picker
|
- Implement web file picker
|
||||||
- Minor design and UX improvements
|
- Minor design and UX improvements
|
||||||
|
- Implement Cross Signing
|
||||||
|
- Restore keys from online key backup
|
||||||
### Changes:
|
### Changes:
|
||||||
- Show presences of users sharing a direct chat
|
- Show presences of users sharing a direct chat
|
||||||
- Big refactoring
|
- Big refactoring
|
||||||
|
### Fixes:
|
||||||
|
- Various fixes, including e2ee fixes and olm session recovery
|
||||||
|
|
||||||
# Version 0.14.0 - 2020-05-20
|
# Version 0.14.0 - 2020-05-20
|
||||||
### Features:
|
### Features:
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:bubble/bubble.dart';
|
import 'package:bubble/bubble.dart';
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:famedlysdk/encryption.dart';
|
|
||||||
import 'package:fluffychat/components/dialogs/simple_dialogs.dart';
|
import 'package:fluffychat/components/dialogs/simple_dialogs.dart';
|
||||||
import 'package:fluffychat/components/message_content.dart';
|
import 'package:fluffychat/components/message_content.dart';
|
||||||
import 'package:fluffychat/components/reply_content.dart';
|
import 'package:fluffychat/components/reply_content.dart';
|
||||||
|
@ -122,7 +121,7 @@ class Message extends StatelessWidget {
|
||||||
),
|
),
|
||||||
if (event.type == EventTypes.Encrypted &&
|
if (event.type == EventTypes.Encrypted &&
|
||||||
event.messageType == MessageTypes.BadEncrypted &&
|
event.messageType == MessageTypes.BadEncrypted &&
|
||||||
event.content['body'] == DecryptError.UNKNOWN_SESSION)
|
event.content['can_request_session'] == true)
|
||||||
RaisedButton(
|
RaisedButton(
|
||||||
color: color.withAlpha(100),
|
color: color.withAlpha(100),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|
|
@ -14,6 +14,8 @@ import '../l10n/l10n.dart';
|
||||||
import '../utils/beautify_string_extension.dart';
|
import '../utils/beautify_string_extension.dart';
|
||||||
import '../utils/famedlysdk_store.dart';
|
import '../utils/famedlysdk_store.dart';
|
||||||
import 'avatar.dart';
|
import 'avatar.dart';
|
||||||
|
import '../views/key_verification.dart';
|
||||||
|
import '../utils/app_route.dart';
|
||||||
|
|
||||||
class Matrix extends StatefulWidget {
|
class Matrix extends StatefulWidget {
|
||||||
static const String callNamespace = 'chat.fluffy.jitsi_call';
|
static const String callNamespace = 'chat.fluffy.jitsi_call';
|
||||||
|
@ -97,6 +99,7 @@ class MatrixState extends State<Matrix> {
|
||||||
};
|
};
|
||||||
|
|
||||||
StreamSubscription onRoomKeyRequestSub;
|
StreamSubscription onRoomKeyRequestSub;
|
||||||
|
StreamSubscription onKeyVerificationRequestSub;
|
||||||
StreamSubscription onJitsiCallSub;
|
StreamSubscription onJitsiCallSub;
|
||||||
|
|
||||||
void onJitsiCall(EventUpdate eventUpdate) {
|
void onJitsiCall(EventUpdate eventUpdate) {
|
||||||
|
@ -159,7 +162,17 @@ class MatrixState extends State<Matrix> {
|
||||||
store = widget.store ?? Store();
|
store = widget.store ?? Store();
|
||||||
if (widget.client == null) {
|
if (widget.client == null) {
|
||||||
debugPrint('[Matrix] Init matrix client');
|
debugPrint('[Matrix] Init matrix client');
|
||||||
client = Client(widget.clientName, debug: false);
|
final Set verificationMethods = <KeyVerificationMethod>{
|
||||||
|
KeyVerificationMethod.numbers
|
||||||
|
};
|
||||||
|
if (!kIsWeb) {
|
||||||
|
// emojis don't show in web somehow
|
||||||
|
verificationMethods.add(KeyVerificationMethod.emoji);
|
||||||
|
}
|
||||||
|
client = Client(widget.clientName,
|
||||||
|
debug: false,
|
||||||
|
enableE2eeRecovery: true,
|
||||||
|
verificationMethods: verificationMethods);
|
||||||
onJitsiCallSub ??= client.onEvent.stream
|
onJitsiCallSub ??= client.onEvent.stream
|
||||||
.where((e) =>
|
.where((e) =>
|
||||||
e.type == 'timeline' &&
|
e.type == 'timeline' &&
|
||||||
|
@ -184,6 +197,23 @@ class MatrixState extends State<Matrix> {
|
||||||
await request.forwardKey();
|
await request.forwardKey();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
onKeyVerificationRequestSub ??= client.onKeyVerificationRequest.stream
|
||||||
|
.listen((KeyVerification request) async {
|
||||||
|
if (await SimpleDialogs(context).askConfirmation(
|
||||||
|
titleText: L10n.of(context).newVerificationRequest,
|
||||||
|
contentText: L10n.of(context).askVerificationRequest(request.userId),
|
||||||
|
)) {
|
||||||
|
await request.acceptVerification();
|
||||||
|
await Navigator.of(context).push(
|
||||||
|
AppRoute.defaultRoute(
|
||||||
|
context,
|
||||||
|
KeyVerificationView(request: request),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await request.rejectVerification();
|
||||||
|
}
|
||||||
|
});
|
||||||
_initWithStore();
|
_initWithStore();
|
||||||
} else {
|
} else {
|
||||||
client = widget.client;
|
client = widget.client;
|
||||||
|
@ -210,6 +240,7 @@ class MatrixState extends State<Matrix> {
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
onRoomKeyRequestSub?.cancel();
|
onRoomKeyRequestSub?.cancel();
|
||||||
|
onKeyVerificationRequestSub?.cancel();
|
||||||
onJitsiCallSub?.cancel();
|
onJitsiCallSub?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
{
|
{
|
||||||
"@@last_modified": "2020-05-15T15:34:50.065646",
|
"@@last_modified": "2020-06-25T16:02:16.297192",
|
||||||
"About": "About",
|
"About": "About",
|
||||||
"@About": {
|
"@About": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"Accept": "Accept",
|
||||||
|
"@Accept": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"acceptedTheInvitation": "{username} accepted the invitation",
|
"acceptedTheInvitation": "{username} accepted the invitation",
|
||||||
"@acceptedTheInvitation": {
|
"@acceptedTheInvitation": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -74,6 +79,28 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"askSSSSCache": "Please enter your secure store passphrase or recovery key to cache the keys.",
|
||||||
|
"@askSSSSCache": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"askSSSSSign": "To be able to sign the other person, please enter your secure store passphrase or recovery key.",
|
||||||
|
"@askSSSSSign": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"askSSSSVerify": "Please enter your secure store passphrase or recovery key to verify your session.",
|
||||||
|
"@askSSSSVerify": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"askVerificationRequest": "Accept this verification request from {username}?",
|
||||||
|
"@askVerificationRequest": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {
|
||||||
|
"username": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Authentication": "Authentication",
|
"Authentication": "Authentication",
|
||||||
"@Authentication": {
|
"@Authentication": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -102,6 +129,11 @@
|
||||||
"targetName": {}
|
"targetName": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Block Device": "Block Device",
|
||||||
|
"@Block Device": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"byDefaultYouWillBeConnectedTo": "By default you will be connected to {homeserver}",
|
"byDefaultYouWillBeConnectedTo": "By default you will be connected to {homeserver}",
|
||||||
"@byDefaultYouWillBeConnectedTo": {
|
"@byDefaultYouWillBeConnectedTo": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -109,6 +141,11 @@
|
||||||
"homeserver": {}
|
"homeserver": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cachedKeys": "Successfully cached keys!",
|
||||||
|
"@cachedKeys": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"@Cancel": {
|
"@Cancel": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -273,6 +310,16 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"compareEmojiMatch": "Compare and make sure the following emoji match those of the other device:",
|
||||||
|
"@compareEmojiMatch": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"compareNumbersMatch": "Compare and make sure the following numbers match those of the other device:",
|
||||||
|
"@compareNumbersMatch": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Confirm": "Confirm",
|
"Confirm": "Confirm",
|
||||||
"@Confirm": {
|
"@Confirm": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -354,6 +401,16 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"crossSigningDisabled": "Cross-Signing is disabled",
|
||||||
|
"@crossSigningDisabled": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"crossSigningEnabled": "Cross-Signing is enabled",
|
||||||
|
"@crossSigningEnabled": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Currently active": "Currently active",
|
"Currently active": "Currently active",
|
||||||
"@Currently active": {
|
"@Currently active": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -464,6 +521,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"Encryption": "Encryption",
|
||||||
|
"@Encryption": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Encryption algorithm": "Encryption algorithm",
|
"Encryption algorithm": "Encryption algorithm",
|
||||||
"@Encryption algorithm": {
|
"@Encryption algorithm": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -594,6 +656,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"incorrectPassphraseOrKey": "Incorrect passphrase or recovery key",
|
||||||
|
"@incorrectPassphraseOrKey": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Invite contact": "Invite contact",
|
"Invite contact": "Invite contact",
|
||||||
"@Invite contact": {
|
"@Invite contact": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -632,6 +699,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"isDeviceKeyCorrect": "Is the following device key correct?",
|
||||||
|
"@isDeviceKeyCorrect": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"is typing...": "is typing...",
|
"is typing...": "is typing...",
|
||||||
"@is typing...": {
|
"@is typing...": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -649,6 +721,16 @@
|
||||||
"username": {}
|
"username": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"keysCached": "Keys are cached",
|
||||||
|
"@keysCached": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"keysMissing": "Keys are missing",
|
||||||
|
"@keysMissing": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"kicked": "{username} kicked {targetName}",
|
"kicked": "{username} kicked {targetName}",
|
||||||
"@kicked": {
|
"@kicked": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -788,6 +870,21 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"newVerificationRequest": "New verification request!",
|
||||||
|
"@newVerificationRequest": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"noCrossSignBootstrap": "Fluffychat currently does not support enabling Cross-Signing. Please enable it from within Riot.",
|
||||||
|
"@noCrossSignBootstrap": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"noMegolmBootstrap": "Fluffychat currently does not support enabling Online Key Backup. Please enable it from within Riot.",
|
||||||
|
"@noMegolmBootstrap": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"It seems that you have no google services on your phone. That's a good decision for your privacy! To receive push notifications in FluffyChat we recommend using microG: https://microg.org/": "It seems that you have no google services on your phone. That's a good decision for your privacy! To receive push notifications in FluffyChat we recommend using microG: https://microg.org/",
|
"It seems that you have no google services on your phone. That's a good decision for your privacy! To receive push notifications in FluffyChat we recommend using microG: https://microg.org/": "It seems that you have no google services on your phone. That's a good decision for your privacy! To receive push notifications in FluffyChat we recommend using microG: https://microg.org/",
|
||||||
"@It seems that you have no google services on your phone. That's a good decision for your privacy! To receive push notifications in FluffyChat we recommend using microG: https://microg.org/": {
|
"@It seems that you have no google services on your phone. That's a good decision for your privacy! To receive push notifications in FluffyChat we recommend using microG: https://microg.org/": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -830,6 +927,16 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"onlineKeyBackupDisabled": "Online Key Backup is disabled",
|
||||||
|
"@onlineKeyBackupDisabled": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"onlineKeyBackupEnabled": "Online Key Backup is enabled",
|
||||||
|
"@onlineKeyBackupEnabled": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Oops something went wrong...": "Oops something went wrong...",
|
"Oops something went wrong...": "Oops something went wrong...",
|
||||||
"@Oops something went wrong...": {
|
"@Oops something went wrong...": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -855,6 +962,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"passphraseOrKey": "passphrase or recovery key",
|
||||||
|
"@passphraseOrKey": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Password": "Password",
|
"Password": "Password",
|
||||||
"@Password": {
|
"@Password": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -897,6 +1009,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"Reject": "Reject",
|
||||||
|
"@Reject": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Rejoin": "Rejoin",
|
"Rejoin": "Rejoin",
|
||||||
"@Rejoin": {
|
"@Rejoin": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -978,6 +1095,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"Room has been upgraded": "Room has been upgraded",
|
||||||
|
"@Room has been upgraded": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Saturday": "Saturday",
|
"Saturday": "Saturday",
|
||||||
"@Saturday": {
|
"@Saturday": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1083,6 +1205,11 @@
|
||||||
"username": {}
|
"username": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sessionVerified": "Session is verified",
|
||||||
|
"@sessionVerified": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Set a profile picture": "Set a profile picture",
|
"Set a profile picture": "Set a profile picture",
|
||||||
"@Set a profile picture": {
|
"@Set a profile picture": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1113,6 +1240,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"Skip": "Skip",
|
||||||
|
"@Skip": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Change your style": "Change your style",
|
"Change your style": "Change your style",
|
||||||
"@Change your style": {
|
"@Change your style": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1153,6 +1285,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"Submit": "Submit",
|
||||||
|
"@Submit": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Sunday": "Sunday",
|
"Sunday": "Sunday",
|
||||||
"@Sunday": {
|
"@Sunday": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1168,6 +1305,16 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"They Don't Match": "They Don't Match",
|
||||||
|
"@They Don't Match": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"They Match": "They Match",
|
||||||
|
"@They Match": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"This room has been archived.": "This room has been archived.",
|
"This room has been archived.": "This room has been archived.",
|
||||||
"@This room has been archived.": {
|
"@This room has been archived.": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1212,6 +1359,11 @@
|
||||||
"targetName": {}
|
"targetName": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Unblock Device": "Unblock Device",
|
||||||
|
"@Unblock Device": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Unmute chat": "Unmute chat",
|
"Unmute chat": "Unmute chat",
|
||||||
"@Unmute chat": {
|
"@Unmute chat": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1227,6 +1379,11 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"unknownSessionVerify": "Unknown session, please verify",
|
||||||
|
"@unknownSessionVerify": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"unknownEvent": "Unknown event '{type}'",
|
"unknownEvent": "Unknown event '{type}'",
|
||||||
"@unknownEvent": {
|
"@unknownEvent": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1297,6 +1454,36 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"verifyManual": "Verify Manually",
|
||||||
|
"@verifyManual": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"verifiedSession": "Successfully verified session!",
|
||||||
|
"@verifiedSession": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"verifyStart": "Start Verification",
|
||||||
|
"@verifyStart": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"verifySuccess": "You successfully verified!",
|
||||||
|
"@verifySuccess": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"verifyTitle": "Verifying other account",
|
||||||
|
"@verifyTitle": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"Verify User": "Verify User",
|
||||||
|
"@Verify User": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Video call": "Video call",
|
"Video call": "Video call",
|
||||||
"@Video call": {
|
"@Video call": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1322,6 +1509,21 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"waitingPartnerAcceptRequest": "Waiting for partner to accept the request...",
|
||||||
|
"@waitingPartnerAcceptRequest": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"waitingPartnerEmoji": "Waiting for partner to accept the emoji...",
|
||||||
|
"@waitingPartnerEmoji": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"waitingPartnerNumbers": "Waiting for partner to accept the numbers...",
|
||||||
|
"@waitingPartnerNumbers": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders": {}
|
||||||
|
},
|
||||||
"Wallpaper": "Wallpaper",
|
"Wallpaper": "Wallpaper",
|
||||||
"@Wallpaper": {
|
"@Wallpaper": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -1387,4 +1589,4 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -46,6 +46,8 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get about => Intl.message("About");
|
String get about => Intl.message("About");
|
||||||
|
|
||||||
|
String get accept => Intl.message("Accept");
|
||||||
|
|
||||||
String acceptedTheInvitation(String username) => Intl.message(
|
String acceptedTheInvitation(String username) => Intl.message(
|
||||||
"$username accepted the invitation",
|
"$username accepted the invitation",
|
||||||
name: "acceptedTheInvitation",
|
name: "acceptedTheInvitation",
|
||||||
|
@ -81,6 +83,22 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get areYouSure => Intl.message("Are you sure?");
|
String get areYouSure => Intl.message("Are you sure?");
|
||||||
|
|
||||||
|
String get askSSSSCache => Intl.message(
|
||||||
|
"Please enter your secure store passphrase or recovery key to cache the keys.",
|
||||||
|
name: "askSSSSCache");
|
||||||
|
|
||||||
|
String get askSSSSSign => Intl.message(
|
||||||
|
"To be able to sign the other person, please enter your secure store passphrase or recovery key.",
|
||||||
|
name: "askSSSSSign");
|
||||||
|
|
||||||
|
String get askSSSSVerify => Intl.message(
|
||||||
|
"Please enter your secure store passphrase or recovery key to verify your session.",
|
||||||
|
name: "askSSSSVerify");
|
||||||
|
|
||||||
|
String askVerificationRequest(String username) =>
|
||||||
|
Intl.message("Accept this verification request from $username?",
|
||||||
|
name: "askVerificationRequest", args: [username]);
|
||||||
|
|
||||||
String get authentication => Intl.message("Authentication");
|
String get authentication => Intl.message("Authentication");
|
||||||
|
|
||||||
String get avatarHasBeenChanged => Intl.message("Avatar has been changed");
|
String get avatarHasBeenChanged => Intl.message("Avatar has been changed");
|
||||||
|
@ -95,12 +113,17 @@ class L10n extends MatrixLocalizations {
|
||||||
args: [username, targetName],
|
args: [username, targetName],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
String get blockDevice => Intl.message("Block Device");
|
||||||
|
|
||||||
String byDefaultYouWillBeConnectedTo(String homeserver) => Intl.message(
|
String byDefaultYouWillBeConnectedTo(String homeserver) => Intl.message(
|
||||||
'By default you will be connected to $homeserver',
|
'By default you will be connected to $homeserver',
|
||||||
name: 'byDefaultYouWillBeConnectedTo',
|
name: 'byDefaultYouWillBeConnectedTo',
|
||||||
args: [homeserver],
|
args: [homeserver],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
String get cachedKeys =>
|
||||||
|
Intl.message("Successfully cached keys!", name: "cachedKeys");
|
||||||
|
|
||||||
String get cancel => Intl.message("Cancel");
|
String get cancel => Intl.message("Cancel");
|
||||||
|
|
||||||
String changedTheChatAvatar(String username) => Intl.message(
|
String changedTheChatAvatar(String username) => Intl.message(
|
||||||
|
@ -216,6 +239,14 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get close => Intl.message("Close");
|
String get close => Intl.message("Close");
|
||||||
|
|
||||||
|
String get compareEmojiMatch => Intl.message(
|
||||||
|
"Compare and make sure the following emoji match those of the other device:",
|
||||||
|
name: "compareEmojiMatch");
|
||||||
|
|
||||||
|
String get compareNumbersMatch => Intl.message(
|
||||||
|
"Compare and make sure the following numbers match those of the other device:",
|
||||||
|
name: "compareNumbersMatch");
|
||||||
|
|
||||||
String get confirm => Intl.message("Confirm");
|
String get confirm => Intl.message("Confirm");
|
||||||
|
|
||||||
String get connect => Intl.message('Connect');
|
String get connect => Intl.message('Connect');
|
||||||
|
@ -261,6 +292,12 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get createNewGroup => Intl.message("Create new group");
|
String get createNewGroup => Intl.message("Create new group");
|
||||||
|
|
||||||
|
String get crossSigningDisabled =>
|
||||||
|
Intl.message("Cross-Signing is disabled", name: "crossSigningDisabled");
|
||||||
|
|
||||||
|
String get crossSigningEnabled =>
|
||||||
|
Intl.message("Cross-Signing is enabled", name: "crossSigningEnabled");
|
||||||
|
|
||||||
String get currentlyActive => Intl.message('Currently active');
|
String get currentlyActive => Intl.message('Currently active');
|
||||||
|
|
||||||
String dateAndTimeOfDay(String date, String timeOfDay) => Intl.message(
|
String dateAndTimeOfDay(String date, String timeOfDay) => Intl.message(
|
||||||
|
@ -319,6 +356,8 @@ class L10n extends MatrixLocalizations {
|
||||||
String get enableEncryptionWarning => Intl.message(
|
String get enableEncryptionWarning => Intl.message(
|
||||||
"You won't be able to disable the encryption anymore. Are you sure?");
|
"You won't be able to disable the encryption anymore. Are you sure?");
|
||||||
|
|
||||||
|
String get encryption => Intl.message("Encryption");
|
||||||
|
|
||||||
String get encryptionAlgorithm => Intl.message("Encryption algorithm");
|
String get encryptionAlgorithm => Intl.message("Encryption algorithm");
|
||||||
|
|
||||||
String get encryptionNotEnabled => Intl.message("Encryption is not enabled");
|
String get encryptionNotEnabled => Intl.message("Encryption is not enabled");
|
||||||
|
@ -381,6 +420,10 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get identity => Intl.message("Identity");
|
String get identity => Intl.message("Identity");
|
||||||
|
|
||||||
|
String get incorrectPassphraseOrKey =>
|
||||||
|
Intl.message("Incorrect passphrase or recovery key",
|
||||||
|
name: "incorrectPassphraseOrKey");
|
||||||
|
|
||||||
String get inviteContact => Intl.message("Invite contact");
|
String get inviteContact => Intl.message("Invite contact");
|
||||||
|
|
||||||
String inviteContactToGroup(String groupName) => Intl.message(
|
String inviteContactToGroup(String groupName) => Intl.message(
|
||||||
|
@ -405,6 +448,10 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get invitedUsersOnly => Intl.message("Invited users only");
|
String get invitedUsersOnly => Intl.message("Invited users only");
|
||||||
|
|
||||||
|
String get isDeviceKeyCorrect =>
|
||||||
|
Intl.message("Is the following device key correct?",
|
||||||
|
name: "isDeviceKeyCorrect");
|
||||||
|
|
||||||
String get isTyping => Intl.message("is typing...");
|
String get isTyping => Intl.message("is typing...");
|
||||||
|
|
||||||
String get editJitsiInstance => Intl.message('Edit Jitsi instance');
|
String get editJitsiInstance => Intl.message('Edit Jitsi instance');
|
||||||
|
@ -415,6 +462,11 @@ class L10n extends MatrixLocalizations {
|
||||||
args: [username],
|
args: [username],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
String get keysCached => Intl.message("Keys are cached", name: "keysCached");
|
||||||
|
|
||||||
|
String get keysMissing =>
|
||||||
|
Intl.message("Keys are missing", name: "keysMissing");
|
||||||
|
|
||||||
String kicked(String username, String targetName) => Intl.message(
|
String kicked(String username, String targetName) => Intl.message(
|
||||||
"$username kicked $targetName",
|
"$username kicked $targetName",
|
||||||
name: "kicked",
|
name: "kicked",
|
||||||
|
@ -493,6 +545,17 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get newPrivateChat => Intl.message("New private chat");
|
String get newPrivateChat => Intl.message("New private chat");
|
||||||
|
|
||||||
|
String get newVerificationRequest =>
|
||||||
|
Intl.message("New verification request!", name: "newVerificationRequest");
|
||||||
|
|
||||||
|
String get noCrossSignBootstrap => Intl.message(
|
||||||
|
"Fluffychat currently does not support enabling Cross-Signing. Please enable it from within Riot.",
|
||||||
|
name: "noCrossSignBootstrap");
|
||||||
|
|
||||||
|
String get noMegolmBootstrap => Intl.message(
|
||||||
|
"Fluffychat currently does not support enabling Online Key Backup. Please enable it from within Riot.",
|
||||||
|
name: "noMegolmBootstrap");
|
||||||
|
|
||||||
String get noGoogleServicesWarning => Intl.message(
|
String get noGoogleServicesWarning => Intl.message(
|
||||||
"It seems that you have no google services on your phone. That's a good decision for your privacy! To receive push notifications in FluffyChat we recommend using microG: https://microg.org/");
|
"It seems that you have no google services on your phone. That's a good decision for your privacy! To receive push notifications in FluffyChat we recommend using microG: https://microg.org/");
|
||||||
|
|
||||||
|
@ -511,6 +574,14 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get ok => Intl.message('ok');
|
String get ok => Intl.message('ok');
|
||||||
|
|
||||||
|
String get onlineKeyBackupDisabled =>
|
||||||
|
Intl.message("Online Key Backup is disabled",
|
||||||
|
name: "onlineKeyBackupDisabled");
|
||||||
|
|
||||||
|
String get onlineKeyBackupEnabled =>
|
||||||
|
Intl.message("Online Key Backup is enabled",
|
||||||
|
name: "onlineKeyBackupEnabled");
|
||||||
|
|
||||||
String get oopsSomethingWentWrong =>
|
String get oopsSomethingWentWrong =>
|
||||||
Intl.message("Oops something went wrong...");
|
Intl.message("Oops something went wrong...");
|
||||||
|
|
||||||
|
@ -523,6 +594,9 @@ class L10n extends MatrixLocalizations {
|
||||||
String get participatingUserDevices =>
|
String get participatingUserDevices =>
|
||||||
Intl.message("Participating user devices");
|
Intl.message("Participating user devices");
|
||||||
|
|
||||||
|
String get passphraseOrKey =>
|
||||||
|
Intl.message("passphrase or recovery key", name: "passphraseOrKey");
|
||||||
|
|
||||||
String get password => Intl.message("Password");
|
String get password => Intl.message("Password");
|
||||||
|
|
||||||
String get pickImage => Intl.message('Pick image');
|
String get pickImage => Intl.message('Pick image');
|
||||||
|
@ -546,6 +620,8 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get publicRooms => Intl.message("Public Rooms");
|
String get publicRooms => Intl.message("Public Rooms");
|
||||||
|
|
||||||
|
String get reject => Intl.message("Reject");
|
||||||
|
|
||||||
String get rejoin => Intl.message("Rejoin");
|
String get rejoin => Intl.message("Rejoin");
|
||||||
|
|
||||||
String get renderRichContent => Intl.message("Render rich message content");
|
String get renderRichContent => Intl.message("Render rich message content");
|
||||||
|
@ -662,6 +738,9 @@ class L10n extends MatrixLocalizations {
|
||||||
args: [username],
|
args: [username],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
String get sessionVerified =>
|
||||||
|
Intl.message("Session is verified", name: "sessionVerified");
|
||||||
|
|
||||||
String get setAProfilePicture => Intl.message("Set a profile picture");
|
String get setAProfilePicture => Intl.message("Set a profile picture");
|
||||||
|
|
||||||
String get setGroupDescription => Intl.message("Set group description");
|
String get setGroupDescription => Intl.message("Set group description");
|
||||||
|
@ -674,6 +753,8 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get signUp => Intl.message("Sign up");
|
String get signUp => Intl.message("Sign up");
|
||||||
|
|
||||||
|
String get skip => Intl.message("Skip");
|
||||||
|
|
||||||
String get changeTheme => Intl.message("Change your style");
|
String get changeTheme => Intl.message("Change your style");
|
||||||
|
|
||||||
String get systemTheme => Intl.message("System");
|
String get systemTheme => Intl.message("System");
|
||||||
|
@ -690,12 +771,18 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get startYourFirstChat => Intl.message("Start your first chat :-)");
|
String get startYourFirstChat => Intl.message("Start your first chat :-)");
|
||||||
|
|
||||||
|
String get submit => Intl.message("Submit");
|
||||||
|
|
||||||
String get sunday => Intl.message("Sunday");
|
String get sunday => Intl.message("Sunday");
|
||||||
|
|
||||||
String get donate => Intl.message("Donate");
|
String get donate => Intl.message("Donate");
|
||||||
|
|
||||||
String get tapToShowMenu => Intl.message("Tap to show menu");
|
String get tapToShowMenu => Intl.message("Tap to show menu");
|
||||||
|
|
||||||
|
String get theyDontMatch => Intl.message("They Don't Match");
|
||||||
|
|
||||||
|
String get theyMatch => Intl.message("They Match");
|
||||||
|
|
||||||
String get thisRoomHasBeenArchived =>
|
String get thisRoomHasBeenArchived =>
|
||||||
Intl.message("This room has been archived.");
|
Intl.message("This room has been archived.");
|
||||||
|
|
||||||
|
@ -726,6 +813,8 @@ class L10n extends MatrixLocalizations {
|
||||||
args: [username, targetName],
|
args: [username, targetName],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
String get unblockDevice => Intl.message("Unblock Device");
|
||||||
|
|
||||||
String get unmuteChat => Intl.message('Unmute chat');
|
String get unmuteChat => Intl.message('Unmute chat');
|
||||||
|
|
||||||
String get unknownDevice => Intl.message("Unknown device");
|
String get unknownDevice => Intl.message("Unknown device");
|
||||||
|
@ -733,6 +822,10 @@ class L10n extends MatrixLocalizations {
|
||||||
String get unknownEncryptionAlgorithm =>
|
String get unknownEncryptionAlgorithm =>
|
||||||
Intl.message("Unknown encryption algorithm");
|
Intl.message("Unknown encryption algorithm");
|
||||||
|
|
||||||
|
String get unknownSessionVerify =>
|
||||||
|
Intl.message("Unknown session, please verify",
|
||||||
|
name: "unknownSessionVerify");
|
||||||
|
|
||||||
String unknownEvent(String type) => Intl.message(
|
String unknownEvent(String type) => Intl.message(
|
||||||
"Unknown event '$type'",
|
"Unknown event '$type'",
|
||||||
name: "unknownEvent",
|
name: "unknownEvent",
|
||||||
|
@ -787,6 +880,23 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get verify => Intl.message("Verify");
|
String get verify => Intl.message("Verify");
|
||||||
|
|
||||||
|
String get verifyManual =>
|
||||||
|
Intl.message("Verify Manually", name: "verifyManual");
|
||||||
|
|
||||||
|
String get verifiedSession =>
|
||||||
|
Intl.message("Successfully verified session!", name: "verifiedSession");
|
||||||
|
|
||||||
|
String get verifyStart =>
|
||||||
|
Intl.message("Start Verification", name: "verifyStart");
|
||||||
|
|
||||||
|
String get verifySuccess =>
|
||||||
|
Intl.message("You successfully verified!", name: "verifySuccess");
|
||||||
|
|
||||||
|
String get verifyTitle =>
|
||||||
|
Intl.message("Verifying other account", name: "verifyTitle");
|
||||||
|
|
||||||
|
String get verifyUser => Intl.message("Verify User");
|
||||||
|
|
||||||
String get videoCall => Intl.message('Video call');
|
String get videoCall => Intl.message('Video call');
|
||||||
|
|
||||||
String get visibleForAllParticipants =>
|
String get visibleForAllParticipants =>
|
||||||
|
@ -799,6 +909,18 @@ class L10n extends MatrixLocalizations {
|
||||||
|
|
||||||
String get voiceMessage => Intl.message("Voice message");
|
String get voiceMessage => Intl.message("Voice message");
|
||||||
|
|
||||||
|
String get waitingPartnerAcceptRequest =>
|
||||||
|
Intl.message("Waiting for partner to accept the request...",
|
||||||
|
name: "waitingPartnerAcceptRequest");
|
||||||
|
|
||||||
|
String get waitingPartnerEmoji =>
|
||||||
|
Intl.message("Waiting for partner to accept the emoji...",
|
||||||
|
name: "waitingPartnerEmoji");
|
||||||
|
|
||||||
|
String get waitingPartnerNumbers =>
|
||||||
|
Intl.message("Waiting for partner to accept the numbers...",
|
||||||
|
name: "waitingPartnerNumbers");
|
||||||
|
|
||||||
String get wallpaper => Intl.message("Wallpaper");
|
String get wallpaper => Intl.message("Wallpaper");
|
||||||
|
|
||||||
String get warningEncryptionInBeta => Intl.message(
|
String get warningEncryptionInBeta => Intl.message(
|
||||||
|
|
|
@ -23,6 +23,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
|
|
||||||
static m1(username) => "${username} activated end to end encryption";
|
static m1(username) => "${username} activated end to end encryption";
|
||||||
|
|
||||||
|
static m60(username) => "Accept this verification request from ${username}?";
|
||||||
|
|
||||||
static m2(username, targetName) => "${username} banned ${targetName}";
|
static m2(username, targetName) => "${username} banned ${targetName}";
|
||||||
|
|
||||||
static m3(homeserver) => "By default you will be connected to ${homeserver}";
|
static m3(homeserver) => "By default you will be connected to ${homeserver}";
|
||||||
|
@ -157,6 +159,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
"(Optional) Group name":
|
"(Optional) Group name":
|
||||||
MessageLookupByLibrary.simpleMessage("(Optional) Group name"),
|
MessageLookupByLibrary.simpleMessage("(Optional) Group name"),
|
||||||
"About": MessageLookupByLibrary.simpleMessage("About"),
|
"About": MessageLookupByLibrary.simpleMessage("About"),
|
||||||
|
"Accept": MessageLookupByLibrary.simpleMessage("Accept"),
|
||||||
"Account": MessageLookupByLibrary.simpleMessage("Account"),
|
"Account": MessageLookupByLibrary.simpleMessage("Account"),
|
||||||
"Account informations":
|
"Account informations":
|
||||||
MessageLookupByLibrary.simpleMessage("Account informations"),
|
MessageLookupByLibrary.simpleMessage("Account informations"),
|
||||||
|
@ -178,6 +181,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
MessageLookupByLibrary.simpleMessage("Avatar has been changed"),
|
MessageLookupByLibrary.simpleMessage("Avatar has been changed"),
|
||||||
"Ban from chat": MessageLookupByLibrary.simpleMessage("Ban from chat"),
|
"Ban from chat": MessageLookupByLibrary.simpleMessage("Ban from chat"),
|
||||||
"Banned": MessageLookupByLibrary.simpleMessage("Banned"),
|
"Banned": MessageLookupByLibrary.simpleMessage("Banned"),
|
||||||
|
"Block Device": MessageLookupByLibrary.simpleMessage("Block Device"),
|
||||||
"Cancel": MessageLookupByLibrary.simpleMessage("Cancel"),
|
"Cancel": MessageLookupByLibrary.simpleMessage("Cancel"),
|
||||||
"Change the homeserver":
|
"Change the homeserver":
|
||||||
MessageLookupByLibrary.simpleMessage("Change the homeserver"),
|
MessageLookupByLibrary.simpleMessage("Change the homeserver"),
|
||||||
|
@ -242,6 +246,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
"Emote shortcode":
|
"Emote shortcode":
|
||||||
MessageLookupByLibrary.simpleMessage("Emote shortcode"),
|
MessageLookupByLibrary.simpleMessage("Emote shortcode"),
|
||||||
"Empty chat": MessageLookupByLibrary.simpleMessage("Empty chat"),
|
"Empty chat": MessageLookupByLibrary.simpleMessage("Empty chat"),
|
||||||
|
"Encryption": MessageLookupByLibrary.simpleMessage("Encryption"),
|
||||||
"Encryption algorithm":
|
"Encryption algorithm":
|
||||||
MessageLookupByLibrary.simpleMessage("Encryption algorithm"),
|
MessageLookupByLibrary.simpleMessage("Encryption algorithm"),
|
||||||
"Encryption is not enabled":
|
"Encryption is not enabled":
|
||||||
|
@ -351,6 +356,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
MessageLookupByLibrary.simpleMessage("Please enter your username"),
|
MessageLookupByLibrary.simpleMessage("Please enter your username"),
|
||||||
"Public Rooms": MessageLookupByLibrary.simpleMessage("Public Rooms"),
|
"Public Rooms": MessageLookupByLibrary.simpleMessage("Public Rooms"),
|
||||||
"Recording": MessageLookupByLibrary.simpleMessage("Recording"),
|
"Recording": MessageLookupByLibrary.simpleMessage("Recording"),
|
||||||
|
"Reject": MessageLookupByLibrary.simpleMessage("Reject"),
|
||||||
"Rejoin": MessageLookupByLibrary.simpleMessage("Rejoin"),
|
"Rejoin": MessageLookupByLibrary.simpleMessage("Rejoin"),
|
||||||
"Remove": MessageLookupByLibrary.simpleMessage("Remove"),
|
"Remove": MessageLookupByLibrary.simpleMessage("Remove"),
|
||||||
"Remove all other devices":
|
"Remove all other devices":
|
||||||
|
@ -368,6 +374,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
"Request to read older messages"),
|
"Request to read older messages"),
|
||||||
"Revoke all permissions":
|
"Revoke all permissions":
|
||||||
MessageLookupByLibrary.simpleMessage("Revoke all permissions"),
|
MessageLookupByLibrary.simpleMessage("Revoke all permissions"),
|
||||||
|
"Room has been upgraded":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Room has been upgraded"),
|
||||||
"Saturday": MessageLookupByLibrary.simpleMessage("Saturday"),
|
"Saturday": MessageLookupByLibrary.simpleMessage("Saturday"),
|
||||||
"Search for a chat":
|
"Search for a chat":
|
||||||
MessageLookupByLibrary.simpleMessage("Search for a chat"),
|
MessageLookupByLibrary.simpleMessage("Search for a chat"),
|
||||||
|
@ -388,9 +396,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
"Settings": MessageLookupByLibrary.simpleMessage("Settings"),
|
"Settings": MessageLookupByLibrary.simpleMessage("Settings"),
|
||||||
"Share": MessageLookupByLibrary.simpleMessage("Share"),
|
"Share": MessageLookupByLibrary.simpleMessage("Share"),
|
||||||
"Sign up": MessageLookupByLibrary.simpleMessage("Sign up"),
|
"Sign up": MessageLookupByLibrary.simpleMessage("Sign up"),
|
||||||
|
"Skip": MessageLookupByLibrary.simpleMessage("Skip"),
|
||||||
"Source code": MessageLookupByLibrary.simpleMessage("Source code"),
|
"Source code": MessageLookupByLibrary.simpleMessage("Source code"),
|
||||||
"Start your first chat :-)":
|
"Start your first chat :-)":
|
||||||
MessageLookupByLibrary.simpleMessage("Start your first chat :-)"),
|
MessageLookupByLibrary.simpleMessage("Start your first chat :-)"),
|
||||||
|
"Submit": MessageLookupByLibrary.simpleMessage("Submit"),
|
||||||
"Sunday": MessageLookupByLibrary.simpleMessage("Sunday"),
|
"Sunday": MessageLookupByLibrary.simpleMessage("Sunday"),
|
||||||
"System": MessageLookupByLibrary.simpleMessage("System"),
|
"System": MessageLookupByLibrary.simpleMessage("System"),
|
||||||
"Tap to show menu":
|
"Tap to show menu":
|
||||||
|
@ -398,12 +408,17 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
"The encryption has been corrupted":
|
"The encryption has been corrupted":
|
||||||
MessageLookupByLibrary.simpleMessage(
|
MessageLookupByLibrary.simpleMessage(
|
||||||
"The encryption has been corrupted"),
|
"The encryption has been corrupted"),
|
||||||
|
"They Don\'t Match":
|
||||||
|
MessageLookupByLibrary.simpleMessage("They Don\'t Match"),
|
||||||
|
"They Match": MessageLookupByLibrary.simpleMessage("They Match"),
|
||||||
"This room has been archived.": MessageLookupByLibrary.simpleMessage(
|
"This room has been archived.": MessageLookupByLibrary.simpleMessage(
|
||||||
"This room has been archived."),
|
"This room has been archived."),
|
||||||
"Thursday": MessageLookupByLibrary.simpleMessage("Thursday"),
|
"Thursday": MessageLookupByLibrary.simpleMessage("Thursday"),
|
||||||
"Try to send again":
|
"Try to send again":
|
||||||
MessageLookupByLibrary.simpleMessage("Try to send again"),
|
MessageLookupByLibrary.simpleMessage("Try to send again"),
|
||||||
"Tuesday": MessageLookupByLibrary.simpleMessage("Tuesday"),
|
"Tuesday": MessageLookupByLibrary.simpleMessage("Tuesday"),
|
||||||
|
"Unblock Device":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Unblock Device"),
|
||||||
"Unknown device":
|
"Unknown device":
|
||||||
MessageLookupByLibrary.simpleMessage("Unknown device"),
|
MessageLookupByLibrary.simpleMessage("Unknown device"),
|
||||||
"Unknown encryption algorithm": MessageLookupByLibrary.simpleMessage(
|
"Unknown encryption algorithm": MessageLookupByLibrary.simpleMessage(
|
||||||
|
@ -413,6 +428,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
"Use Amoled compatible colors?"),
|
"Use Amoled compatible colors?"),
|
||||||
"Username": MessageLookupByLibrary.simpleMessage("Username"),
|
"Username": MessageLookupByLibrary.simpleMessage("Username"),
|
||||||
"Verify": MessageLookupByLibrary.simpleMessage("Verify"),
|
"Verify": MessageLookupByLibrary.simpleMessage("Verify"),
|
||||||
|
"Verify User": MessageLookupByLibrary.simpleMessage("Verify User"),
|
||||||
"Video call": MessageLookupByLibrary.simpleMessage("Video call"),
|
"Video call": MessageLookupByLibrary.simpleMessage("Video call"),
|
||||||
"Visibility of the chat history": MessageLookupByLibrary.simpleMessage(
|
"Visibility of the chat history": MessageLookupByLibrary.simpleMessage(
|
||||||
"Visibility of the chat history"),
|
"Visibility of the chat history"),
|
||||||
|
@ -451,8 +467,17 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
"acceptedTheInvitation": m0,
|
"acceptedTheInvitation": m0,
|
||||||
"activatedEndToEndEncryption": m1,
|
"activatedEndToEndEncryption": m1,
|
||||||
"alias": MessageLookupByLibrary.simpleMessage("alias"),
|
"alias": MessageLookupByLibrary.simpleMessage("alias"),
|
||||||
|
"askSSSSCache": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Please enter your secure store passphrase or recovery key to cache the keys."),
|
||||||
|
"askSSSSSign": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"To be able to sign the other person, please enter your secure store passphrase or recovery key."),
|
||||||
|
"askSSSSVerify": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Please enter your secure store passphrase or recovery key to verify your session."),
|
||||||
|
"askVerificationRequest": m60,
|
||||||
"bannedUser": m2,
|
"bannedUser": m2,
|
||||||
"byDefaultYouWillBeConnectedTo": m3,
|
"byDefaultYouWillBeConnectedTo": m3,
|
||||||
|
"cachedKeys":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Successfully cached keys!"),
|
||||||
"changedTheChatAvatar": m4,
|
"changedTheChatAvatar": m4,
|
||||||
"changedTheChatDescriptionTo": m5,
|
"changedTheChatDescriptionTo": m5,
|
||||||
"changedTheChatNameTo": m6,
|
"changedTheChatNameTo": m6,
|
||||||
|
@ -467,9 +492,17 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
"changedTheProfileAvatar": m15,
|
"changedTheProfileAvatar": m15,
|
||||||
"changedTheRoomAliases": m16,
|
"changedTheRoomAliases": m16,
|
||||||
"changedTheRoomInvitationLink": m17,
|
"changedTheRoomInvitationLink": m17,
|
||||||
|
"compareEmojiMatch": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Compare and make sure the following emoji match those of the other device:"),
|
||||||
|
"compareNumbersMatch": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Compare and make sure the following numbers match those of the other device:"),
|
||||||
"couldNotDecryptMessage": m18,
|
"couldNotDecryptMessage": m18,
|
||||||
"countParticipants": m19,
|
"countParticipants": m19,
|
||||||
"createdTheChat": m20,
|
"createdTheChat": m20,
|
||||||
|
"crossSigningDisabled":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Cross-Signing is disabled"),
|
||||||
|
"crossSigningEnabled":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Cross-Signing is enabled"),
|
||||||
"dateAndTimeOfDay": m21,
|
"dateAndTimeOfDay": m21,
|
||||||
"dateWithYear": m22,
|
"dateWithYear": m22,
|
||||||
"dateWithoutYear": m23,
|
"dateWithoutYear": m23,
|
||||||
|
@ -481,18 +514,36 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
"You need to pick an emote shortcode and an image!"),
|
"You need to pick an emote shortcode and an image!"),
|
||||||
"groupWith": m24,
|
"groupWith": m24,
|
||||||
"hasWithdrawnTheInvitationFor": m25,
|
"hasWithdrawnTheInvitationFor": m25,
|
||||||
|
"incorrectPassphraseOrKey": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Incorrect passphrase or recovery key"),
|
||||||
"inviteContactToGroup": m26,
|
"inviteContactToGroup": m26,
|
||||||
"inviteText": m27,
|
"inviteText": m27,
|
||||||
"invitedUser": m28,
|
"invitedUser": m28,
|
||||||
"is typing...": MessageLookupByLibrary.simpleMessage("is typing..."),
|
"is typing...": MessageLookupByLibrary.simpleMessage("is typing..."),
|
||||||
|
"isDeviceKeyCorrect": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Is the following device key correct?"),
|
||||||
"joinedTheChat": m29,
|
"joinedTheChat": m29,
|
||||||
|
"keysCached": MessageLookupByLibrary.simpleMessage("Keys are cached"),
|
||||||
|
"keysMissing": MessageLookupByLibrary.simpleMessage("Keys are missing"),
|
||||||
"kicked": m30,
|
"kicked": m30,
|
||||||
"kickedAndBanned": m31,
|
"kickedAndBanned": m31,
|
||||||
"lastActiveAgo": m32,
|
"lastActiveAgo": m32,
|
||||||
"loadCountMoreParticipants": m33,
|
"loadCountMoreParticipants": m33,
|
||||||
"logInTo": m34,
|
"logInTo": m34,
|
||||||
|
"newVerificationRequest":
|
||||||
|
MessageLookupByLibrary.simpleMessage("New verification request!"),
|
||||||
|
"noCrossSignBootstrap": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Fluffychat currently does not support enabling Cross-Signing. Please enable it from within Riot."),
|
||||||
|
"noMegolmBootstrap": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Fluffychat currently does not support enabling Online Key Backup. Please enable it from within Riot."),
|
||||||
"numberSelected": m35,
|
"numberSelected": m35,
|
||||||
"ok": MessageLookupByLibrary.simpleMessage("ok"),
|
"ok": MessageLookupByLibrary.simpleMessage("ok"),
|
||||||
|
"onlineKeyBackupDisabled": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Online Key Backup is disabled"),
|
||||||
|
"onlineKeyBackupEnabled": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Online Key Backup is enabled"),
|
||||||
|
"passphraseOrKey":
|
||||||
|
MessageLookupByLibrary.simpleMessage("passphrase or recovery key"),
|
||||||
"play": m36,
|
"play": m36,
|
||||||
"redactedAnEvent": m37,
|
"redactedAnEvent": m37,
|
||||||
"rejectedTheInvitation": m38,
|
"rejectedTheInvitation": m38,
|
||||||
|
@ -505,11 +556,15 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
"sentASticker": m45,
|
"sentASticker": m45,
|
||||||
"sentAVideo": m46,
|
"sentAVideo": m46,
|
||||||
"sentAnAudio": m47,
|
"sentAnAudio": m47,
|
||||||
|
"sessionVerified":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Session is verified"),
|
||||||
"sharedTheLocation": m48,
|
"sharedTheLocation": m48,
|
||||||
"timeOfDay": m49,
|
"timeOfDay": m49,
|
||||||
"title": MessageLookupByLibrary.simpleMessage("FluffyChat"),
|
"title": MessageLookupByLibrary.simpleMessage("FluffyChat"),
|
||||||
"unbannedUser": m50,
|
"unbannedUser": m50,
|
||||||
"unknownEvent": m51,
|
"unknownEvent": m51,
|
||||||
|
"unknownSessionVerify": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Unknown session, please verify"),
|
||||||
"unreadChats": m52,
|
"unreadChats": m52,
|
||||||
"unreadMessages": m53,
|
"unreadMessages": m53,
|
||||||
"unreadMessagesInChats": m54,
|
"unreadMessagesInChats": m54,
|
||||||
|
@ -517,6 +572,21 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||||
"userAndUserAreTyping": m56,
|
"userAndUserAreTyping": m56,
|
||||||
"userIsTyping": m57,
|
"userIsTyping": m57,
|
||||||
"userLeftTheChat": m58,
|
"userLeftTheChat": m58,
|
||||||
"userSentUnknownEvent": m59
|
"userSentUnknownEvent": m59,
|
||||||
|
"verifiedSession": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Successfully verified session!"),
|
||||||
|
"verifyManual": MessageLookupByLibrary.simpleMessage("Verify Manually"),
|
||||||
|
"verifyStart":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Start Verification"),
|
||||||
|
"verifySuccess":
|
||||||
|
MessageLookupByLibrary.simpleMessage("You successfully verified!"),
|
||||||
|
"verifyTitle":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Verifying other account"),
|
||||||
|
"waitingPartnerAcceptRequest": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Waiting for partner to accept the request..."),
|
||||||
|
"waitingPartnerEmoji": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Waiting for partner to accept the emoji..."),
|
||||||
|
"waitingPartnerNumbers": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Waiting for partner to accept the numbers...")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ Future<void> migrate(String clientName, Database db, Store store) async {
|
||||||
var sess = olm.Session();
|
var sess = olm.Session();
|
||||||
sess.unpickle(credentials['userID'], pickle);
|
sess.unpickle(credentials['userID'], pickle);
|
||||||
await db.storeOlmSession(
|
await db.storeOlmSession(
|
||||||
clientId, identKey, sess.session_id(), pickle);
|
clientId, identKey, sess.session_id(), pickle, null);
|
||||||
sess?.free();
|
sess?.free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,13 @@ extension RoomStatusExtension on Room {
|
||||||
if (directChatPresence.presence.currentlyActive == true) {
|
if (directChatPresence.presence.currentlyActive == true) {
|
||||||
return L10n.of(context).currentlyActive;
|
return L10n.of(context).currentlyActive;
|
||||||
}
|
}
|
||||||
return L10n.of(context).lastActiveAgo(
|
if (directChatPresence.presence.lastActiveAgo == null) {
|
||||||
DateTime.fromMillisecondsSinceEpoch(
|
return L10n.of(context).lastSeenLongTimeAgo;
|
||||||
directChatPresence.presence.lastActiveAgo)
|
}
|
||||||
.localizedTimeShort(context));
|
final time = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
DateTime.now().millisecondsSinceEpoch -
|
||||||
|
directChatPresence.presence.lastActiveAgo);
|
||||||
|
return L10n.of(context).lastActiveAgo(time.localizedTimeShort(context));
|
||||||
}
|
}
|
||||||
return L10n.of(context).lastSeenLongTimeAgo;
|
return L10n.of(context).lastSeenLongTimeAgo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
import 'package:famedlysdk/encryption.dart';
|
||||||
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
||||||
import 'package:fluffychat/components/avatar.dart';
|
import 'package:fluffychat/components/avatar.dart';
|
||||||
import 'package:fluffychat/components/matrix.dart';
|
import 'package:fluffychat/components/matrix.dart';
|
||||||
|
@ -6,6 +7,9 @@ import 'package:fluffychat/utils/beautify_string_extension.dart';
|
||||||
import 'package:fluffychat/l10n/l10n.dart';
|
import 'package:fluffychat/l10n/l10n.dart';
|
||||||
import 'package:fluffychat/views/chat_list.dart';
|
import 'package:fluffychat/views/chat_list.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'key_verification.dart';
|
||||||
|
import '../utils/app_route.dart';
|
||||||
|
import '../components/dialogs/simple_dialogs.dart';
|
||||||
|
|
||||||
class ChatEncryptionSettingsView extends StatelessWidget {
|
class ChatEncryptionSettingsView extends StatelessWidget {
|
||||||
final String id;
|
final String id;
|
||||||
|
@ -33,6 +37,70 @@ class ChatEncryptionSettings extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
||||||
|
Future<void> onSelected(
|
||||||
|
BuildContext context, String action, DeviceKeys key) async {
|
||||||
|
final room = Matrix.of(context).client.getRoomById(widget.id);
|
||||||
|
final unblock = () async {
|
||||||
|
if (key.blocked) {
|
||||||
|
await key.setBlocked(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
switch (action) {
|
||||||
|
case 'verify':
|
||||||
|
await unblock();
|
||||||
|
final req = key.startVerification();
|
||||||
|
req.onUpdate = () {
|
||||||
|
if (req.state == KeyVerificationState.done) {
|
||||||
|
setState(() => null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await Navigator.of(context).push(
|
||||||
|
AppRoute.defaultRoute(
|
||||||
|
context,
|
||||||
|
KeyVerificationView(request: req),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'verify_manual':
|
||||||
|
if (await SimpleDialogs(context).askConfirmation(
|
||||||
|
titleText: L10n.of(context).isDeviceKeyCorrect,
|
||||||
|
contentText: key.ed25519Key.beautified,
|
||||||
|
)) {
|
||||||
|
await unblock();
|
||||||
|
await key.setVerified(true);
|
||||||
|
setState(() => null);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'verify_user':
|
||||||
|
await unblock();
|
||||||
|
final req =
|
||||||
|
await room.client.userDeviceKeys[key.userId].startVerification();
|
||||||
|
req.onUpdate = () {
|
||||||
|
if (req.state == KeyVerificationState.done) {
|
||||||
|
setState(() => null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await Navigator.of(context).push(
|
||||||
|
AppRoute.defaultRoute(
|
||||||
|
context,
|
||||||
|
KeyVerificationView(request: req),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'block':
|
||||||
|
if (key.directVerified) {
|
||||||
|
await key.setVerified(false);
|
||||||
|
}
|
||||||
|
await key.setBlocked(true);
|
||||||
|
setState(() => null);
|
||||||
|
break;
|
||||||
|
case 'unblock':
|
||||||
|
await unblock();
|
||||||
|
setState(() => null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final room = Matrix.of(context).client.getRoomById(widget.id);
|
final room = Matrix.of(context).client.getRoomById(widget.id);
|
||||||
|
@ -68,59 +136,99 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
||||||
if (i == 0 ||
|
if (i == 0 ||
|
||||||
deviceKeys[i].userId != deviceKeys[i - 1].userId)
|
deviceKeys[i].userId != deviceKeys[i - 1].userId)
|
||||||
Material(
|
Material(
|
||||||
child: ListTile(
|
child: PopupMenuButton(
|
||||||
leading: Avatar(
|
onSelected: (action) =>
|
||||||
room
|
onSelected(context, action, deviceKeys[i]),
|
||||||
|
itemBuilder: (c) {
|
||||||
|
var items = <PopupMenuEntry<String>>[];
|
||||||
|
if (room
|
||||||
|
.client
|
||||||
|
.userDeviceKeys[deviceKeys[i].userId]
|
||||||
|
.verified ==
|
||||||
|
UserVerifiedStatus.unknown &&
|
||||||
|
deviceKeys[i].userId != room.client.userID) {
|
||||||
|
items.add(PopupMenuItem(
|
||||||
|
child: Text(L10n.of(context).verifyUser),
|
||||||
|
value: 'verify_user',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
},
|
||||||
|
child: ListTile(
|
||||||
|
leading: Avatar(
|
||||||
|
room
|
||||||
|
.getUserByMXIDSync(deviceKeys[i].userId)
|
||||||
|
.avatarUrl,
|
||||||
|
room
|
||||||
|
.getUserByMXIDSync(deviceKeys[i].userId)
|
||||||
|
.calcDisplayname(),
|
||||||
|
),
|
||||||
|
title: Text(room
|
||||||
.getUserByMXIDSync(deviceKeys[i].userId)
|
.getUserByMXIDSync(deviceKeys[i].userId)
|
||||||
.avatarUrl,
|
.calcDisplayname()),
|
||||||
room
|
subtitle: Text(deviceKeys[i].userId),
|
||||||
.getUserByMXIDSync(deviceKeys[i].userId)
|
|
||||||
.calcDisplayname(),
|
|
||||||
),
|
),
|
||||||
title: Text(room
|
|
||||||
.getUserByMXIDSync(deviceKeys[i].userId)
|
|
||||||
.calcDisplayname()),
|
|
||||||
subtitle: Text(deviceKeys[i].userId),
|
|
||||||
),
|
),
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
),
|
),
|
||||||
CheckboxListTile(
|
PopupMenuButton(
|
||||||
title: Text(
|
onSelected: (action) =>
|
||||||
"${deviceKeys[i].unsigned["device_display_name"] ?? L10n.of(context).unknownDevice} - ${deviceKeys[i].deviceId}",
|
onSelected(context, action, deviceKeys[i]),
|
||||||
style: TextStyle(
|
itemBuilder: (c) {
|
||||||
color: deviceKeys[i].blocked
|
var items = <PopupMenuEntry<String>>[];
|
||||||
? Colors.red
|
if (deviceKeys[i].blocked ||
|
||||||
: deviceKeys[i].verified
|
!deviceKeys[i].verified) {
|
||||||
? Colors.green
|
if (deviceKeys[i].userId == room.client.userID) {
|
||||||
: Colors.orange),
|
items.add(PopupMenuItem(
|
||||||
),
|
child: Text(L10n.of(context).verifyStart),
|
||||||
subtitle: Text(
|
value: 'verify',
|
||||||
deviceKeys[i]
|
));
|
||||||
.keys['ed25519:${deviceKeys[i].deviceId}']
|
items.add(PopupMenuItem(
|
||||||
.beautified,
|
child: Text(L10n.of(context).verifyManual),
|
||||||
style: TextStyle(
|
value: 'verify_manual',
|
||||||
color:
|
));
|
||||||
Theme.of(context).textTheme.bodyText2.color),
|
} else {
|
||||||
),
|
items.add(PopupMenuItem(
|
||||||
value: deviceKeys[i].verified,
|
child: Text(L10n.of(context).verifyUser),
|
||||||
onChanged: (bool newVal) {
|
value: 'verify_user',
|
||||||
if (newVal == true) {
|
));
|
||||||
if (deviceKeys[i].blocked) {
|
|
||||||
deviceKeys[i]
|
|
||||||
.setBlocked(false, Matrix.of(context).client);
|
|
||||||
}
|
}
|
||||||
deviceKeys[i]
|
|
||||||
.setVerified(true, Matrix.of(context).client);
|
|
||||||
} else {
|
|
||||||
if (deviceKeys[i].verified) {
|
|
||||||
deviceKeys[i].setVerified(
|
|
||||||
false, Matrix.of(context).client);
|
|
||||||
}
|
|
||||||
deviceKeys[i]
|
|
||||||
.setBlocked(true, Matrix.of(context).client);
|
|
||||||
}
|
}
|
||||||
setState(() => null);
|
if (deviceKeys[i].blocked) {
|
||||||
|
items.add(PopupMenuItem(
|
||||||
|
child: Text(L10n.of(context).unblockDevice),
|
||||||
|
value: 'unblock',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (!deviceKeys[i].blocked) {
|
||||||
|
items.add(PopupMenuItem(
|
||||||
|
child: Text(L10n.of(context).blockDevice),
|
||||||
|
value: 'block',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return items;
|
||||||
},
|
},
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(
|
||||||
|
"${deviceKeys[i].unsigned["device_display_name"] ?? L10n.of(context).unknownDevice} - ${deviceKeys[i].deviceId}",
|
||||||
|
style: TextStyle(
|
||||||
|
color: deviceKeys[i].blocked
|
||||||
|
? Colors.red
|
||||||
|
: deviceKeys[i].verified
|
||||||
|
? Colors.green
|
||||||
|
: Colors.orange),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
deviceKeys[i]
|
||||||
|
.keys['ed25519:${deviceKeys[i].deviceId}']
|
||||||
|
.beautified,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyText2
|
||||||
|
.color),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
347
lib/views/key_verification.dart
Normal file
347
lib/views/key_verification.dart
Normal file
|
@ -0,0 +1,347 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:famedlysdk/encryption.dart';
|
||||||
|
import 'package:famedlysdk/matrix_api.dart';
|
||||||
|
import 'chat_list.dart';
|
||||||
|
import '../components/adaptive_page_layout.dart';
|
||||||
|
import '../components/avatar.dart';
|
||||||
|
import '../components/dialogs/simple_dialogs.dart';
|
||||||
|
import '../l10n/l10n.dart';
|
||||||
|
|
||||||
|
class KeyVerificationView extends StatelessWidget {
|
||||||
|
final KeyVerification request;
|
||||||
|
|
||||||
|
KeyVerificationView({this.request});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AdaptivePageLayout(
|
||||||
|
primaryPage: FocusPage.SECOND,
|
||||||
|
firstScaffold: ChatList(),
|
||||||
|
secondScaffold: KeyVerificationPage(request: request),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class KeyVerificationPage extends StatefulWidget {
|
||||||
|
final KeyVerification request;
|
||||||
|
|
||||||
|
KeyVerificationPage({this.request});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_KeyVerificationPageState createState() => _KeyVerificationPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _KeyVerificationPageState extends State<KeyVerificationPage> {
|
||||||
|
void Function() originalOnUpdate;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
originalOnUpdate = widget.request.onUpdate;
|
||||||
|
widget.request.onUpdate = () {
|
||||||
|
if (originalOnUpdate != null) {
|
||||||
|
originalOnUpdate();
|
||||||
|
}
|
||||||
|
setState(() => null);
|
||||||
|
};
|
||||||
|
widget.request.client.getProfileFromUserId(widget.request.userId).then((p) {
|
||||||
|
profile = p;
|
||||||
|
setState(() => null);
|
||||||
|
});
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
widget.request.onUpdate =
|
||||||
|
originalOnUpdate; // don't want to get updates anymore
|
||||||
|
if (![KeyVerificationState.error, KeyVerificationState.done]
|
||||||
|
.contains(widget.request.state)) {
|
||||||
|
widget.request.cancel('m.user');
|
||||||
|
}
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Profile profile;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget body;
|
||||||
|
final buttons = <Widget>[];
|
||||||
|
switch (widget.request.state) {
|
||||||
|
case KeyVerificationState.askSSSS:
|
||||||
|
// prompt the user for their ssss passphrase / key
|
||||||
|
final textEditingController = TextEditingController();
|
||||||
|
String input;
|
||||||
|
final checkInput = () async {
|
||||||
|
if (input == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SimpleDialogs(context).showLoadingDialog(context);
|
||||||
|
// make sure the loading spinner shows before we test the keys
|
||||||
|
await Future.delayed(Duration(milliseconds: 100));
|
||||||
|
var valid = false;
|
||||||
|
try {
|
||||||
|
await widget.request.openSSSS(recoveryKey: input);
|
||||||
|
valid = true;
|
||||||
|
} catch (_) {
|
||||||
|
try {
|
||||||
|
await widget.request.openSSSS(passphrase: input);
|
||||||
|
valid = true;
|
||||||
|
} catch (_) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Navigator.of(context)?.pop();
|
||||||
|
if (!valid) {
|
||||||
|
await SimpleDialogs(context).inform(
|
||||||
|
contentText: L10n.of(context).incorrectPassphraseOrKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
body = Container(
|
||||||
|
margin: EdgeInsets.only(left: 8.0, right: 8.0),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Text(L10n.of(context).askSSSSSign,
|
||||||
|
style: TextStyle(fontSize: 20)),
|
||||||
|
Container(height: 10),
|
||||||
|
TextField(
|
||||||
|
controller: textEditingController,
|
||||||
|
autofocus: false,
|
||||||
|
autocorrect: false,
|
||||||
|
onSubmitted: (s) {
|
||||||
|
input = s;
|
||||||
|
checkInput();
|
||||||
|
},
|
||||||
|
minLines: 1,
|
||||||
|
maxLines: 1,
|
||||||
|
obscureText: true,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: L10n.of(context).passphraseOrKey,
|
||||||
|
prefixStyle: TextStyle(color: Theme.of(context).primaryColor),
|
||||||
|
suffixStyle: TextStyle(color: Theme.of(context).primaryColor),
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
buttons.add(RaisedButton(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
elevation: 5,
|
||||||
|
textColor: Colors.white,
|
||||||
|
child: Text(L10n.of(context).submit),
|
||||||
|
onPressed: () {
|
||||||
|
input = textEditingController.text;
|
||||||
|
checkInput();
|
||||||
|
},
|
||||||
|
));
|
||||||
|
buttons.add(RaisedButton(
|
||||||
|
textColor: Theme.of(context).primaryColor,
|
||||||
|
elevation: 5,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Text(L10n.of(context).skip),
|
||||||
|
onPressed: () => widget.request.openSSSS(skip: true),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case KeyVerificationState.askAccept:
|
||||||
|
body = Container(
|
||||||
|
child: Text(
|
||||||
|
L10n.of(context).askVerificationRequest(widget.request.userId),
|
||||||
|
style: TextStyle(fontSize: 20)),
|
||||||
|
margin: EdgeInsets.only(left: 8.0, right: 8.0),
|
||||||
|
);
|
||||||
|
buttons.add(RaisedButton(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
elevation: 5,
|
||||||
|
textColor: Colors.white,
|
||||||
|
child: Text(L10n.of(context).accept),
|
||||||
|
onPressed: () => widget.request.acceptVerification(),
|
||||||
|
));
|
||||||
|
buttons.add(RaisedButton(
|
||||||
|
textColor: Theme.of(context).primaryColor,
|
||||||
|
elevation: 5,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Text(L10n.of(context).reject),
|
||||||
|
onPressed: () {
|
||||||
|
widget.request.rejectVerification().then((_) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case KeyVerificationState.waitingAccept:
|
||||||
|
body = Column(
|
||||||
|
children: <Widget>[
|
||||||
|
CircularProgressIndicator(),
|
||||||
|
Container(height: 10),
|
||||||
|
Text(L10n.of(context).waitingPartnerAcceptRequest),
|
||||||
|
],
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case KeyVerificationState.askSas:
|
||||||
|
var emojiWidgets = <Widget>[];
|
||||||
|
// maybe add a button to switch between the two and only determine default
|
||||||
|
// view for if "emoji" is a present sasType or not?
|
||||||
|
String compareText;
|
||||||
|
if (widget.request.sasTypes.contains('emoji')) {
|
||||||
|
compareText = L10n.of(context).compareEmojiMatch;
|
||||||
|
emojiWidgets =
|
||||||
|
widget.request.sasEmojis.map((e) => _Emoji(e)).toList();
|
||||||
|
} else {
|
||||||
|
compareText = L10n.of(context).compareNumbersMatch;
|
||||||
|
final numbers = widget.request.sasNumbers;
|
||||||
|
emojiWidgets = <Widget>[
|
||||||
|
Text(numbers[0].toString(), style: TextStyle(fontSize: 40)),
|
||||||
|
Text('-', style: TextStyle(fontSize: 40)),
|
||||||
|
Text(numbers[1].toString(), style: TextStyle(fontSize: 40)),
|
||||||
|
Text('-', style: TextStyle(fontSize: 40)),
|
||||||
|
Text(numbers[2].toString(), style: TextStyle(fontSize: 40)),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
body = Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
child: Text(compareText, style: TextStyle(fontSize: 20)),
|
||||||
|
margin: EdgeInsets.only(left: 8.0, right: 8.0),
|
||||||
|
),
|
||||||
|
Container(height: 10),
|
||||||
|
RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children:
|
||||||
|
emojiWidgets.map((w) => WidgetSpan(child: w)).toList(),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
);
|
||||||
|
buttons.add(RaisedButton(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
elevation: 5,
|
||||||
|
textColor: Colors.white,
|
||||||
|
child: Text(L10n.of(context).theyMatch),
|
||||||
|
onPressed: () => widget.request.acceptSas(),
|
||||||
|
));
|
||||||
|
buttons.add(RaisedButton(
|
||||||
|
textColor: Theme.of(context).primaryColor,
|
||||||
|
elevation: 5,
|
||||||
|
color: Colors.white,
|
||||||
|
child: Text(L10n.of(context).theyDontMatch),
|
||||||
|
onPressed: () => widget.request.rejectSas(),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case KeyVerificationState.waitingSas:
|
||||||
|
var acceptText = widget.request.sasTypes.contains('emoji')
|
||||||
|
? L10n.of(context).waitingPartnerEmoji
|
||||||
|
: L10n.of(context).waitingPartnerNumbers;
|
||||||
|
body = Column(
|
||||||
|
children: <Widget>[
|
||||||
|
CircularProgressIndicator(),
|
||||||
|
Container(height: 10),
|
||||||
|
Text(acceptText),
|
||||||
|
],
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case KeyVerificationState.done:
|
||||||
|
body = Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.check_circle, color: Colors.green, size: 200.0),
|
||||||
|
Container(height: 10),
|
||||||
|
Text(L10n.of(context).verifySuccess),
|
||||||
|
],
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
);
|
||||||
|
buttons.add(RaisedButton(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
elevation: 5,
|
||||||
|
textColor: Colors.white,
|
||||||
|
child: Text(L10n.of(context).close),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case KeyVerificationState.error:
|
||||||
|
body = Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.cancel, color: Colors.red, size: 200.0),
|
||||||
|
Container(height: 10),
|
||||||
|
Text(
|
||||||
|
'Error ${widget.request.canceledCode}: ${widget.request.canceledReason}'),
|
||||||
|
],
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
);
|
||||||
|
buttons.add(RaisedButton(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
elevation: 5,
|
||||||
|
textColor: Colors.white,
|
||||||
|
child: Text(L10n.of(context).close),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
body ??= Text('ERROR: Unknown state ' + widget.request.state.toString());
|
||||||
|
final otherName = profile?.displayname ?? widget.request.userId;
|
||||||
|
var bottom;
|
||||||
|
if (widget.request.deviceId != null) {
|
||||||
|
final deviceName = widget
|
||||||
|
.request
|
||||||
|
.client
|
||||||
|
.userDeviceKeys[widget.request.userId]
|
||||||
|
?.deviceKeys[widget.request.deviceId]
|
||||||
|
?.deviceDisplayName ??
|
||||||
|
'';
|
||||||
|
bottom = PreferredSize(
|
||||||
|
child: Text('$deviceName (${widget.request.deviceId})',
|
||||||
|
style: TextStyle(color: Theme.of(context).textTheme.caption.color)),
|
||||||
|
preferredSize: Size(0.0, 20.0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: ListTile(
|
||||||
|
leading: Avatar(profile?.avatarUrl, otherName),
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: Text(L10n.of(context).verifyTitle),
|
||||||
|
isThreeLine: otherName != widget.request.userId,
|
||||||
|
subtitle: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Text(otherName),
|
||||||
|
if (otherName != widget.request.userId)
|
||||||
|
Text(widget.request.userId),
|
||||||
|
],
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
bottom: bottom,
|
||||||
|
),
|
||||||
|
extendBody: true,
|
||||||
|
extendBodyBehindAppBar: true,
|
||||||
|
body: Center(
|
||||||
|
child: body,
|
||||||
|
),
|
||||||
|
persistentFooterButtons: buttons.isEmpty ? null : buttons,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Emoji extends StatelessWidget {
|
||||||
|
final KeyVerificationEmoji emoji;
|
||||||
|
|
||||||
|
_Emoji(this.emoji);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(emoji.emoji, style: TextStyle(fontSize: 50)),
|
||||||
|
Text(emoji.name),
|
||||||
|
Container(height: 10, width: 5),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,10 @@ class Settings extends StatefulWidget {
|
||||||
class _SettingsState extends State<Settings> {
|
class _SettingsState extends State<Settings> {
|
||||||
Future<dynamic> profileFuture;
|
Future<dynamic> profileFuture;
|
||||||
dynamic profile;
|
dynamic profile;
|
||||||
|
Future<bool> crossSigningCachedFuture;
|
||||||
|
bool crossSigningCached;
|
||||||
|
Future<bool> megolmBackupCachedFuture;
|
||||||
|
bool megolmBackupCached;
|
||||||
|
|
||||||
void logoutAction(BuildContext context) async {
|
void logoutAction(BuildContext context) async {
|
||||||
if (await SimpleDialogs(context).askConfirmation() == false) {
|
if (await SimpleDialogs(context).askConfirmation() == false) {
|
||||||
|
@ -123,12 +127,65 @@ class _SettingsState extends State<Settings> {
|
||||||
setState(() => null);
|
setState(() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> requestSSSSCache(BuildContext context) async {
|
||||||
|
final handle = Matrix.of(context).client.encryption.ssss.open();
|
||||||
|
final str = await SimpleDialogs(context).enterText(
|
||||||
|
titleText: L10n.of(context).askSSSSCache,
|
||||||
|
hintText: L10n.of(context).passphraseOrKey,
|
||||||
|
password: true,
|
||||||
|
);
|
||||||
|
if (str != null) {
|
||||||
|
SimpleDialogs(context).showLoadingDialog(context);
|
||||||
|
// make sure the loading spinner shows before we test the keys
|
||||||
|
await Future.delayed(Duration(milliseconds: 100));
|
||||||
|
var valid = false;
|
||||||
|
try {
|
||||||
|
handle.unlock(recoveryKey: str);
|
||||||
|
valid = true;
|
||||||
|
} catch (_) {
|
||||||
|
try {
|
||||||
|
handle.unlock(passphrase: str);
|
||||||
|
valid = true;
|
||||||
|
} catch (_) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Navigator.of(context)?.pop();
|
||||||
|
if (valid) {
|
||||||
|
await handle.maybeCacheAll();
|
||||||
|
await SimpleDialogs(context).inform(
|
||||||
|
contentText: L10n.of(context).cachedKeys,
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
crossSigningCachedFuture = null;
|
||||||
|
crossSigningCached = null;
|
||||||
|
megolmBackupCachedFuture = null;
|
||||||
|
megolmBackupCached = null;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await SimpleDialogs(context).inform(
|
||||||
|
contentText: L10n.of(context).incorrectPassphraseOrKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final client = Matrix.of(context).client;
|
final client = Matrix.of(context).client;
|
||||||
profileFuture ??= client.ownProfile;
|
profileFuture ??= client.ownProfile.then((p) {
|
||||||
profileFuture.then((p) {
|
|
||||||
if (mounted) setState(() => profile = p);
|
if (mounted) setState(() => profile = p);
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
crossSigningCachedFuture ??=
|
||||||
|
client.encryption.crossSigning.isCached().then((c) {
|
||||||
|
if (mounted) setState(() => crossSigningCached = c);
|
||||||
|
return c;
|
||||||
|
});
|
||||||
|
megolmBackupCachedFuture ??=
|
||||||
|
client.encryption.keyManager.isCached().then((c) {
|
||||||
|
if (mounted) setState(() => megolmBackupCached = c);
|
||||||
|
return c;
|
||||||
});
|
});
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: NestedScrollView(
|
body: NestedScrollView(
|
||||||
|
@ -286,6 +343,110 @@ class _SettingsState extends State<Settings> {
|
||||||
onTap: () => logoutAction(context),
|
onTap: () => logoutAction(context),
|
||||||
),
|
),
|
||||||
Divider(thickness: 1),
|
Divider(thickness: 1),
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
L10n.of(context).encryption,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.compare_arrows),
|
||||||
|
title: Text(client.encryption.crossSigning.enabled
|
||||||
|
? L10n.of(context).crossSigningEnabled
|
||||||
|
: L10n.of(context).crossSigningDisabled),
|
||||||
|
subtitle: client.encryption.crossSigning.enabled
|
||||||
|
? Text(client.isUnknownSession
|
||||||
|
? L10n.of(context).unknownSessionVerify
|
||||||
|
: L10n.of(context).sessionVerified +
|
||||||
|
', ' +
|
||||||
|
(crossSigningCached == null
|
||||||
|
? '⌛'
|
||||||
|
: (crossSigningCached
|
||||||
|
? L10n.of(context).keysCached
|
||||||
|
: L10n.of(context).keysMissing)))
|
||||||
|
: null,
|
||||||
|
onTap: () async {
|
||||||
|
if (!client.encryption.crossSigning.enabled) {
|
||||||
|
await SimpleDialogs(context).inform(
|
||||||
|
contentText: L10n.of(context).noCrossSignBootstrap,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (client.isUnknownSession) {
|
||||||
|
final str = await SimpleDialogs(context).enterText(
|
||||||
|
titleText: L10n.of(context).askSSSSVerify,
|
||||||
|
hintText: L10n.of(context).passphraseOrKey,
|
||||||
|
password: true,
|
||||||
|
);
|
||||||
|
if (str != null) {
|
||||||
|
SimpleDialogs(context).showLoadingDialog(context);
|
||||||
|
// make sure the loading spinner shows before we test the keys
|
||||||
|
await Future.delayed(Duration(milliseconds: 100));
|
||||||
|
var valid = false;
|
||||||
|
try {
|
||||||
|
await client.encryption.crossSigning
|
||||||
|
.selfSign(recoveryKey: str);
|
||||||
|
valid = true;
|
||||||
|
} catch (_) {
|
||||||
|
try {
|
||||||
|
await client.encryption.crossSigning
|
||||||
|
.selfSign(passphrase: str);
|
||||||
|
valid = true;
|
||||||
|
} catch (_) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Navigator.of(context)?.pop();
|
||||||
|
if (valid) {
|
||||||
|
await SimpleDialogs(context).inform(
|
||||||
|
contentText: L10n.of(context).verifiedSession,
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
crossSigningCachedFuture = null;
|
||||||
|
crossSigningCached = null;
|
||||||
|
megolmBackupCachedFuture = null;
|
||||||
|
megolmBackupCached = null;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await SimpleDialogs(context).inform(
|
||||||
|
contentText: L10n.of(context).incorrectPassphraseOrKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(await client.encryption.crossSigning.isCached())) {
|
||||||
|
await requestSSSSCache(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
trailing: Icon(Icons.wb_cloudy),
|
||||||
|
title: Text(client.encryption.keyManager.enabled
|
||||||
|
? L10n.of(context).onlineKeyBackupEnabled
|
||||||
|
: L10n.of(context).onlineKeyBackupDisabled),
|
||||||
|
subtitle: client.encryption.keyManager.enabled
|
||||||
|
? Text(megolmBackupCached == null
|
||||||
|
? '⌛'
|
||||||
|
: (megolmBackupCached
|
||||||
|
? L10n.of(context).keysCached
|
||||||
|
: L10n.of(context).keysMissing))
|
||||||
|
: null,
|
||||||
|
onTap: () async {
|
||||||
|
if (!client.encryption.keyManager.enabled) {
|
||||||
|
await SimpleDialogs(context).inform(
|
||||||
|
contentText: L10n.of(context).noMegolmBootstrap,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(await client.encryption.keyManager.isCached())) {
|
||||||
|
await requestSSSSCache(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Divider(thickness: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).about,
|
L10n.of(context).about,
|
||||||
|
|
58
pubspec.lock
58
pubspec.lock
|
@ -29,6 +29,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.0"
|
version: "1.6.0"
|
||||||
|
asn1lib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: asn1lib
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.4"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -36,6 +43,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.1"
|
||||||
|
base58check:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: base58check
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -127,6 +141,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.3"
|
||||||
|
encrypt:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: encrypt
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.2"
|
||||||
encrypted_moor:
|
encrypted_moor:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -136,19 +157,12 @@ 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: b8c6decafc52cbf5c09288c6c6dde62b62ae978f
|
ref: "8e2c8b0d1146e99e80ef5f5bf4b4c8e378772b06"
|
||||||
resolved-ref: b8c6decafc52cbf5c09288c6c6dde62b62ae978f
|
resolved-ref: "8e2c8b0d1146e99e80ef5f5bf4b4c8e378772b06"
|
||||||
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"
|
||||||
|
@ -487,10 +501,10 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "1.x.y"
|
ref: "1.x.y"
|
||||||
resolved-ref: f66975bd1b5cb1865eba5efe6e3a392aa5e396a5
|
resolved-ref: "8e4fcccff7a2d4d0bd5142964db092bf45061905"
|
||||||
url: "https://gitlab.com/famedly/libraries/dart-olm.git"
|
url: "https://gitlab.com/famedly/libraries/dart-olm.git"
|
||||||
source: git
|
source: git
|
||||||
version: "1.1.1"
|
version: "1.2.0"
|
||||||
open_file:
|
open_file:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -512,13 +526,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.10"
|
version: "1.0.10"
|
||||||
|
password_hash:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: password_hash
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.6.4"
|
||||||
path_drawing:
|
path_drawing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -596,6 +617,13 @@ 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:
|
||||||
|
@ -733,21 +761,21 @@ packages:
|
||||||
name: test
|
name: test
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.14.7"
|
version: "1.14.4"
|
||||||
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.16"
|
version: "0.2.15"
|
||||||
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.7"
|
version: "0.3.4"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -27,7 +27,7 @@ dependencies:
|
||||||
famedlysdk:
|
famedlysdk:
|
||||||
git:
|
git:
|
||||||
url: https://gitlab.com/famedly/famedlysdk.git
|
url: https://gitlab.com/famedly/famedlysdk.git
|
||||||
ref: b8c6decafc52cbf5c09288c6c6dde62b62ae978f
|
ref: 8e2c8b0d1146e99e80ef5f5bf4b4c8e378772b06
|
||||||
|
|
||||||
localstorage: ^3.0.1+4
|
localstorage: ^3.0.1+4
|
||||||
bubble: ^1.1.9+1
|
bubble: ^1.1.9+1
|
||||||
|
|
Loading…
Reference in a new issue