[Tests] Add login widget tests

[I18n] Make logic wisget test compatible

Took 1 hour 5 minutes
This commit is contained in:
Marcel 2020-02-20 21:55:57 +01:00
parent ff02c581d1
commit 7a3ef82546
35 changed files with 730 additions and 283 deletions

View File

@ -43,15 +43,15 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
widget.room.pushRuleState == PushRuleState.notify widget.room.pushRuleState == PushRuleState.notify
? PopupMenuItem<String>( ? PopupMenuItem<String>(
value: "mute", value: "mute",
child: Text(I18n.of(context).muteChat), child: Text(I18n.tr(context).muteChat),
) )
: PopupMenuItem<String>( : PopupMenuItem<String>(
value: "unmute", value: "unmute",
child: Text(I18n.of(context).unmuteChat), child: Text(I18n.tr(context).unmuteChat),
), ),
PopupMenuItem<String>( PopupMenuItem<String>(
value: "leave", value: "leave",
child: Text(I18n.of(context).leave), child: Text(I18n.tr(context).leave),
), ),
]; ];
if (widget.displayChatDetails) { if (widget.displayChatDetails) {
@ -59,7 +59,7 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
0, 0,
PopupMenuItem<String>( PopupMenuItem<String>(
value: "details", value: "details",
child: Text(I18n.of(context).chatDetails), child: Text(I18n.tr(context).chatDetails),
), ),
); );
} }

View File

@ -22,7 +22,7 @@ class SimpleDialogs {
await showDialog( await showDialog(
context: context, context: context,
builder: (c) => AlertDialog( builder: (c) => AlertDialog(
title: Text(titleText ?? I18n.of(context).enterAUsername), title: Text(titleText ?? I18n.tr(context).enterAUsername),
content: TextField( content: TextField(
controller: controller, controller: controller,
autofocus: true, autofocus: true,
@ -48,14 +48,14 @@ class SimpleDialogs {
FlatButton( FlatButton(
child: Text( child: Text(
cancelText?.toUpperCase() ?? cancelText?.toUpperCase() ??
I18n.of(context).close.toUpperCase(), I18n.tr(context).close.toUpperCase(),
style: TextStyle(color: Colors.blueGrey)), style: TextStyle(color: Colors.blueGrey)),
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
FlatButton( FlatButton(
child: Text( child: Text(
confirmText?.toUpperCase() ?? confirmText?.toUpperCase() ??
I18n.of(context).confirm.toUpperCase(), I18n.tr(context).confirm.toUpperCase(),
), ),
onPressed: () { onPressed: () {
input = controller.text; input = controller.text;
@ -77,19 +77,19 @@ class SimpleDialogs {
await showDialog( await showDialog(
context: context, context: context,
builder: (c) => AlertDialog( builder: (c) => AlertDialog(
title: Text(titleText ?? I18n.of(context).areYouSure), title: Text(titleText ?? I18n.tr(context).areYouSure),
actions: <Widget>[ actions: <Widget>[
FlatButton( FlatButton(
child: Text( child: Text(
cancelText?.toUpperCase() ?? cancelText?.toUpperCase() ??
I18n.of(context).close.toUpperCase(), I18n.tr(context).close.toUpperCase(),
style: TextStyle(color: Colors.blueGrey)), style: TextStyle(color: Colors.blueGrey)),
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
FlatButton( FlatButton(
child: Text( child: Text(
confirmText?.toUpperCase() ?? confirmText?.toUpperCase() ??
I18n.of(context).confirm.toUpperCase(), I18n.tr(context).confirm.toUpperCase(),
), ),
onPressed: () { onPressed: () {
confirmed = true; confirmed = true;

View File

@ -32,7 +32,9 @@ class ChatListItem extends StatelessWidget {
} }
if (room.membership == Membership.ban) { if (room.membership == Membership.ban) {
Toast.show(I18n.of(context).youHaveBeenBannedFromThisChat, context, Toast.show(
I18n.tr(context).youHaveBeenBannedFromThisChat,
context,
duration: 5); duration: 5);
return; return;
} }
@ -41,16 +43,19 @@ class ChatListItem extends StatelessWidget {
await showDialog( await showDialog(
context: context, context: context,
builder: (BuildContext context) => AlertDialog( builder: (BuildContext context) => AlertDialog(
title: Text(I18n.of(context).archivedRoom), title: Text(I18n.tr(context).archivedRoom),
content: Text(I18n.of(context).thisRoomHasBeenArchived), content: Text(
I18n.tr(context).thisRoomHasBeenArchived),
actions: <Widget>[ actions: <Widget>[
FlatButton( FlatButton(
child: Text(I18n.of(context).close.toUpperCase(), child: Text(
I18n.tr(context).close.toUpperCase(),
style: TextStyle(color: Colors.blueGrey)), style: TextStyle(color: Colors.blueGrey)),
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
FlatButton( FlatButton(
child: Text(I18n.of(context).delete.toUpperCase(), child: Text(
I18n.tr(context).delete.toUpperCase(),
style: TextStyle(color: Colors.red)), style: TextStyle(color: Colors.red)),
onPressed: () async { onPressed: () async {
await archiveAction(context); await archiveAction(context);
@ -58,7 +63,8 @@ class ChatListItem extends StatelessWidget {
}, },
), ),
FlatButton( FlatButton(
child: Text(I18n.of(context).rejoin.toUpperCase(), child: Text(
I18n.tr(context).rejoin.toUpperCase(),
style: TextStyle(color: Colors.blue)), style: TextStyle(color: Colors.blue)),
onPressed: () async { onPressed: () async {
await Matrix.of(context) await Matrix.of(context)
@ -115,14 +121,14 @@ class ChatListItem extends StatelessWidget {
secondaryActions: <Widget>[ secondaryActions: <Widget>[
if ([Membership.join, Membership.invite].contains(room.membership)) if ([Membership.join, Membership.invite].contains(room.membership))
IconSlideAction( IconSlideAction(
caption: I18n.of(context).leave, caption: I18n.tr(context).leave,
color: Colors.red, color: Colors.red,
icon: Icons.archive, icon: Icons.archive,
onTap: () => archiveAction(context), onTap: () => archiveAction(context),
), ),
if ([Membership.leave, Membership.ban].contains(room.membership)) if ([Membership.leave, Membership.ban].contains(room.membership))
IconSlideAction( IconSlideAction(
caption: I18n.of(context).delete, caption: I18n.tr(context).delete,
color: Colors.red, color: Colors.red,
icon: Icons.delete_forever, icon: Icons.delete_forever,
onTap: () => archiveAction(context), onTap: () => archiveAction(context),
@ -170,7 +176,8 @@ class ChatListItem extends StatelessWidget {
Expanded( Expanded(
child: room.membership == Membership.invite child: room.membership == Membership.invite
? Text( ? Text(
I18n.of(context).youAreInvitedToThisChat, I18n.tr(context)
.youAreInvitedToThisChat,
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
), ),

View File

@ -79,7 +79,7 @@ class Message extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Text( Text(
ownMessage ownMessage
? I18n.of(context).you ? I18n.tr(context).you
: event.sender.calcDisplayname(), : event.sender.calcDisplayname(),
style: TextStyle( style: TextStyle(
color: ownMessage color: ownMessage

View File

@ -62,19 +62,19 @@ class ParticipantListItem extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
Map<Membership, String> membershipBatch = { Map<Membership, String> membershipBatch = {
Membership.join: "", Membership.join: "",
Membership.ban: I18n.of(context).banned, Membership.ban: I18n.tr(context).banned,
Membership.invite: I18n.of(context).invited, Membership.invite: I18n.tr(context).invited,
Membership.leave: I18n.of(context).leftTheChat, Membership.leave: I18n.tr(context).leftTheChat,
}; };
final String permissionBatch = user.powerLevel == 100 final String permissionBatch = user.powerLevel == 100
? I18n.of(context).admin ? I18n.tr(context).admin
: user.powerLevel >= 50 ? I18n.of(context).moderator : ""; : user.powerLevel >= 50 ? I18n.tr(context).moderator : "";
List<PopupMenuEntry<String>> items = <PopupMenuEntry<String>>[]; List<PopupMenuEntry<String>> items = <PopupMenuEntry<String>>[];
if (user.id != Matrix.of(context).client.userID) { if (user.id != Matrix.of(context).client.userID) {
items.add( items.add(
PopupMenuItem( PopupMenuItem(
child: Text(I18n.of(context).sendAMessage), value: "message"), child: Text(I18n.tr(context).sendAMessage), value: "message"),
); );
} }
if (user.canChangePowerLevel && if (user.canChangePowerLevel &&
@ -82,7 +82,7 @@ class ParticipantListItem extends StatelessWidget {
user.powerLevel != 100) { user.powerLevel != 100) {
items.add( items.add(
PopupMenuItem( PopupMenuItem(
child: Text(I18n.of(context).makeAnAdmin), value: "admin"), child: Text(I18n.tr(context).makeAnAdmin), value: "admin"),
); );
} }
if (user.canChangePowerLevel && if (user.canChangePowerLevel &&
@ -90,29 +90,29 @@ class ParticipantListItem extends StatelessWidget {
user.powerLevel != 50) { user.powerLevel != 50) {
items.add( items.add(
PopupMenuItem( PopupMenuItem(
child: Text(I18n.of(context).makeAModerator), value: "moderator"), child: Text(I18n.tr(context).makeAModerator), value: "moderator"),
); );
} }
if (user.canChangePowerLevel && user.powerLevel != 0) { if (user.canChangePowerLevel && user.powerLevel != 0) {
items.add( items.add(
PopupMenuItem( PopupMenuItem(
child: Text(I18n.of(context).revokeAllPermissions), value: "user"), child: Text(I18n.tr(context).revokeAllPermissions), value: "user"),
); );
} }
if (user.canKick) { if (user.canKick) {
items.add( items.add(
PopupMenuItem( PopupMenuItem(
child: Text(I18n.of(context).kickFromChat), value: "kick"), child: Text(I18n.tr(context).kickFromChat), value: "kick"),
); );
} }
if (user.canBan && user.membership != Membership.ban) { if (user.canBan && user.membership != Membership.ban) {
items.add( items.add(
PopupMenuItem(child: Text(I18n.of(context).banFromChat), value: "ban"), PopupMenuItem(child: Text(I18n.tr(context).banFromChat), value: "ban"),
); );
} else if (user.canBan && user.membership == Membership.ban) { } else if (user.canBan && user.membership == Membership.ban) {
items.add( items.add(
PopupMenuItem( PopupMenuItem(
child: Text(I18n.of(context).removeExile), value: "unban"), child: Text(I18n.tr(context).removeExile), value: "unban"),
); );
} }
return PopupMenuButton( return PopupMenuButton(

View File

@ -104,7 +104,7 @@ class MatrixState extends State<Matrix> {
children: <Widget>[ children: <Widget>[
CircularProgressIndicator(), CircularProgressIndicator(),
SizedBox(width: 16), SizedBox(width: 16),
Text(I18n.of(context).loadingPleaseWait), Text(I18n.tr(context).loadingPleaseWait),
], ],
), ),
), ),
@ -140,7 +140,7 @@ class MatrixState extends State<Matrix> {
final String token = await _firebaseMessaging.getToken(); final String token = await _firebaseMessaging.getToken();
if (token?.isEmpty ?? true) { if (token?.isEmpty ?? true) {
return Toast.show( return Toast.show(
I18n.of(context).noGoogleServicesWarning, I18n.tr(context).noGoogleServicesWarning,
context, context,
duration: 10, duration: 10,
); );
@ -233,9 +233,9 @@ class MatrixState extends State<Matrix> {
// Calculate title // Calculate title
final String title = unread > 1 final String title = unread > 1
? I18n.of(context).unreadMessagesInChats( ? I18n.tr(context).unreadMessagesInChats(
unreadEvents.toString(), unread.toString()) unreadEvents.toString(), unread.toString())
: I18n.of(context).unreadMessages(unreadEvents.toString()); : I18n.tr(context).unreadMessages(unreadEvents.toString());
// Calculate the body // Calculate the body
final String body = event.getLocalizedBody(context, final String body = event.getLocalizedBody(context,
@ -273,7 +273,7 @@ class MatrixState extends State<Matrix> {
), ),
importance: Importance.Max, importance: Importance.Max,
priority: Priority.High, priority: Priority.High,
ticker: I18n.of(context).newMessageInFluffyChat); ticker: I18n.tr(context).newMessageInFluffyChat);
var iOSPlatformChannelSpecifics = IOSNotificationDetails(); var iOSPlatformChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails( var platformChannelSpecifics = NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics); androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);

View File

@ -68,7 +68,7 @@ class MessageContent extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Icon(Icons.play_arrow, color: Colors.white), Icon(Icons.play_arrow, color: Colors.white),
Text( Text(
I18n.of(context).play(event.body), I18n.tr(context).play(event.body),
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
softWrap: false, softWrap: false,
maxLines: 1, maxLines: 1,
@ -93,7 +93,7 @@ class MessageContent extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Icon(Icons.play_arrow, color: Colors.white), Icon(Icons.play_arrow, color: Colors.white),
Text( Text(
I18n.of(context).play(event.body), I18n.tr(context).play(event.body),
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
softWrap: false, softWrap: false,
maxLines: 1, maxLines: 1,
@ -115,7 +115,7 @@ class MessageContent extends StatelessWidget {
child: RaisedButton( child: RaisedButton(
color: Colors.blueGrey, color: Colors.blueGrey,
child: Text( child: Text(
I18n.of(context).download(event.body), I18n.tr(context).download(event.body),
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
softWrap: false, softWrap: false,
maxLines: 1, maxLines: 1,
@ -150,7 +150,7 @@ class MessageContent extends StatelessWidget {
); );
default: default:
return Text( return Text(
I18n.of(context).userSentUnknownEvent( I18n.tr(context).userSentUnknownEvent(
event.sender.calcDisplayname(), event.typeKey), event.sender.calcDisplayname(), event.typeKey),
style: TextStyle( style: TextStyle(
color: textColor, color: textColor,

View File

@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'messages_all.dart'; import 'messages_all.dart';
class AppLocalizationsDelegate extends LocalizationsDelegate<I18n> { class AppLocalizationsDelegate
extends LocalizationsDelegate<FluffychatLocalizations> {
const AppLocalizationsDelegate(); const AppLocalizationsDelegate();
@override @override
@ -11,36 +12,52 @@ class AppLocalizationsDelegate extends LocalizationsDelegate<I18n> {
} }
@override @override
Future<I18n> load(Locale locale) { Future<FluffychatLocalizations> load(Locale locale) {
return I18n.load(locale); return FluffychatLocalizations.load(locale);
} }
@override @override
bool shouldReload(LocalizationsDelegate<I18n> old) { bool shouldReload(LocalizationsDelegate<FluffychatLocalizations> old) {
return false; return false;
} }
} }
class I18n { class I18n {
I18n(this.localeName); static FluffychatLocalizations tr(BuildContext context) {
FluffychatLocalizations fluffychatLocalizations =
FluffychatLocalizations.of(context);
if (fluffychatLocalizations != null) {
return fluffychatLocalizations;
} else {
return FluffychatLocalizations("");
}
}
}
static Future<I18n> load(Locale locale) { class FluffychatLocalizations {
FluffychatLocalizations(this.localeName);
static Future<FluffychatLocalizations> load(Locale locale) {
final String name = final String name =
locale.countryCode == null ? locale.languageCode : locale.toString(); locale.countryCode == null ? locale.languageCode : locale.toString();
final String localeName = Intl.canonicalizedLocale(name); final String localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((bool _) { return initializeMessages(localeName).then((bool _) {
Intl.defaultLocale = localeName; Intl.defaultLocale = localeName;
return I18n(localeName); return FluffychatLocalizations(localeName);
}); });
} }
static I18n of(BuildContext context) { static FluffychatLocalizations of(BuildContext context) {
return Localizations.of<I18n>(context, I18n); return Localizations.of<FluffychatLocalizations>(
context, FluffychatLocalizations);
} }
final String localeName; final String localeName;
// Dummy function to prevent loops
FluffychatLocalizations tr(BuildContext context) => this;
/* <=============> Translations <=============> */ /* <=============> Translations <=============> */
String get about => Intl.message("About"); String get about => Intl.message("About");

View File

@ -1,7 +1,9 @@
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:universal_html/prefer_universal/html.dart' as html;
import 'i18n/i18n.dart'; import 'i18n/i18n.dart';
import 'views/sign_up.dart'; import 'views/sign_up.dart';
@ -28,14 +30,18 @@ class App extends StatelessWidget {
theme: ThemeSwitcherWidget.of(context).themeData, theme: ThemeSwitcherWidget.of(context).themeData,
localizationsDelegates: [ localizationsDelegates: [
AppLocalizationsDelegate(), AppLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
], ],
supportedLocales: [ supportedLocales: [
const Locale('en'), // English const Locale('en'), // English
const Locale('de'), // German const Locale('de'), // German
], ],
locale: kIsWeb
? Locale(html.window.navigator.language.split("-").first)
: null,
home: FutureBuilder<LoginState>( home: FutureBuilder<LoginState>(
future: future:
Matrix.of(context).client.onLoginStateChanged.stream.first, Matrix.of(context).client.onLoginStateChanged.stream.first,

View File

@ -34,7 +34,7 @@ extension DateTimeExtension on DateTime {
/// Returns a simple time String. /// Returns a simple time String.
/// TODO: Add localization /// TODO: Add localization
String localizedTimeOfDay(BuildContext context) { String localizedTimeOfDay(BuildContext context) {
return I18n.of(context).timeOfDay(_z(this.hour % 12), _z(this.hour), return I18n.tr(context).timeOfDay(_z(this.hour % 12), _z(this.hour),
_z(this.minute), this.hour > 11 ? 'pm' : 'am'); _z(this.minute), this.hour > 11 ? 'pm' : 'am');
} }
@ -57,26 +57,26 @@ extension DateTimeExtension on DateTime {
} else if (sameWeek) { } else if (sameWeek) {
switch (this.weekday) { switch (this.weekday) {
case 1: case 1:
return I18n.of(context).monday; return I18n.tr(context).monday;
case 2: case 2:
return I18n.of(context).tuesday; return I18n.tr(context).tuesday;
case 3: case 3:
return I18n.of(context).wednesday; return I18n.tr(context).wednesday;
case 4: case 4:
return I18n.of(context).thursday; return I18n.tr(context).thursday;
case 5: case 5:
return I18n.of(context).friday; return I18n.tr(context).friday;
case 6: case 6:
return I18n.of(context).saturday; return I18n.tr(context).saturday;
case 7: case 7:
return I18n.of(context).sunday; return I18n.tr(context).sunday;
} }
} else if (sameYear) { } else if (sameYear) {
return I18n.of(context).dateWithoutYear( return I18n.tr(context).dateWithoutYear(
this.month.toString().padLeft(2, '0'), this.month.toString().padLeft(2, '0'),
this.day.toString().padLeft(2, '0')); this.day.toString().padLeft(2, '0'));
} }
return I18n.of(context).dateWithYear( return I18n.tr(context).dateWithYear(
this.year.toString(), this.year.toString(),
this.month.toString().padLeft(2, '0'), this.month.toString().padLeft(2, '0'),
this.day.toString().padLeft(2, '0')); this.day.toString().padLeft(2, '0'));
@ -93,7 +93,7 @@ extension DateTimeExtension on DateTime {
bool sameDay = sameYear && now.month == this.month && now.day == this.day; bool sameDay = sameYear && now.month == this.month && now.day == this.day;
if (sameDay) return localizedTimeOfDay(context); if (sameDay) return localizedTimeOfDay(context);
return I18n.of(context).dateAndTimeOfDay( return I18n.tr(context).dateAndTimeOfDay(
localizedTimeShort(context), localizedTimeOfDay(context)); localizedTimeShort(context), localizedTimeOfDay(context));
} }

View File

@ -15,27 +15,27 @@ extension LocalizedBody on Event {
String getLocalizedBody(BuildContext context, String getLocalizedBody(BuildContext context,
{bool withSenderNamePrefix = false, bool hideQuotes = false}) { {bool withSenderNamePrefix = false, bool hideQuotes = false}) {
if (this.redacted) { if (this.redacted) {
return I18n.of(context) return I18n.tr(context)
.removedBy(redactedBecause.sender.calcDisplayname()); .removedBy(redactedBecause.sender.calcDisplayname());
} }
String localizedBody = body; String localizedBody = body;
final String senderName = this.sender.calcDisplayname(); final String senderName = this.sender.calcDisplayname();
switch (this.type) { switch (this.type) {
case EventTypes.Sticker: case EventTypes.Sticker:
localizedBody = I18n.of(context).sentASticker(senderName); localizedBody = I18n.tr(context).sentASticker(senderName);
break; break;
case EventTypes.Redaction: case EventTypes.Redaction:
localizedBody = I18n.of(context).redactedAnEvent(senderName); localizedBody = I18n.tr(context).redactedAnEvent(senderName);
break; break;
case EventTypes.RoomAliases: case EventTypes.RoomAliases:
localizedBody = I18n.of(context).changedTheRoomAliases(senderName); localizedBody = I18n.tr(context).changedTheRoomAliases(senderName);
break; break;
case EventTypes.RoomCanonicalAlias: case EventTypes.RoomCanonicalAlias:
localizedBody = localizedBody =
I18n.of(context).changedTheRoomInvitationLink(senderName); I18n.tr(context).changedTheRoomInvitationLink(senderName);
break; break;
case EventTypes.RoomCreate: case EventTypes.RoomCreate:
localizedBody = I18n.of(context).createdTheChat(senderName); localizedBody = I18n.tr(context).createdTheChat(senderName);
break; break;
case EventTypes.RoomJoinRules: case EventTypes.RoomJoinRules:
JoinRules joinRules = JoinRules.values.firstWhere( JoinRules joinRules = JoinRules.values.firstWhere(
@ -44,9 +44,9 @@ extension LocalizedBody on Event {
content["join_rule"], content["join_rule"],
orElse: () => null); orElse: () => null);
if (joinRules == null) { if (joinRules == null) {
localizedBody = I18n.of(context).changedTheJoinRules(senderName); localizedBody = I18n.tr(context).changedTheJoinRules(senderName);
} else { } else {
localizedBody = I18n.of(context).changedTheJoinRulesTo( localizedBody = I18n.tr(context).changedTheJoinRulesTo(
senderName, joinRules.getLocalizedString(context)); senderName, joinRules.getLocalizedString(context));
} }
break; break;
@ -61,36 +61,36 @@ extension LocalizedBody on Event {
: ""; : "";
if (newMembership != oldMembership) { if (newMembership != oldMembership) {
if (oldMembership == "invite" && newMembership == "join") { if (oldMembership == "invite" && newMembership == "join") {
text = I18n.of(context).acceptedTheInvitation(targetName); text = I18n.tr(context).acceptedTheInvitation(targetName);
} else if (oldMembership == "invite" && newMembership == "leave") { } else if (oldMembership == "invite" && newMembership == "leave") {
if (this.stateKey == this.senderId) { if (this.stateKey == this.senderId) {
text = I18n.of(context).rejectedTheInvitation(targetName); text = I18n.tr(context).rejectedTheInvitation(targetName);
} else { } else {
text = I18n.of(context) text = I18n.tr(context)
.hasWithdrawnTheInvitationFor(senderName, targetName); .hasWithdrawnTheInvitationFor(senderName, targetName);
} }
} else if (oldMembership == "leave" && newMembership == "join") { } else if (oldMembership == "leave" && newMembership == "join") {
text = I18n.of(context).joinedTheChat(targetName); text = I18n.tr(context).joinedTheChat(targetName);
} else if (oldMembership == "join" && newMembership == "ban") { } else if (oldMembership == "join" && newMembership == "ban") {
text = I18n.of(context).kickedAndBanned(senderName, targetName); text = I18n.tr(context).kickedAndBanned(senderName, targetName);
} else if (oldMembership == "join" && } else if (oldMembership == "join" &&
newMembership == "leave" && newMembership == "leave" &&
this.stateKey != this.senderId) { this.stateKey != this.senderId) {
text = I18n.of(context).kicked(senderName, targetName); text = I18n.tr(context).kicked(senderName, targetName);
} else if (oldMembership == "join" && } else if (oldMembership == "join" &&
newMembership == "leave" && newMembership == "leave" &&
this.stateKey == this.senderId) { this.stateKey == this.senderId) {
text = I18n.of(context).userLeftTheChat(targetName); text = I18n.tr(context).userLeftTheChat(targetName);
} else if (oldMembership == "invite" && newMembership == "ban") { } else if (oldMembership == "invite" && newMembership == "ban") {
text = I18n.of(context).bannedUser(senderName, targetName); text = I18n.tr(context).bannedUser(senderName, targetName);
} else if (oldMembership == "leave" && newMembership == "ban") { } else if (oldMembership == "leave" && newMembership == "ban") {
text = I18n.of(context).bannedUser(senderName, targetName); text = I18n.tr(context).bannedUser(senderName, targetName);
} else if (oldMembership == "ban" && newMembership == "leave") { } else if (oldMembership == "ban" && newMembership == "leave") {
text = I18n.of(context).unbannedUser(senderName, targetName); text = I18n.tr(context).unbannedUser(senderName, targetName);
} else if (newMembership == "invite") { } else if (newMembership == "invite") {
text = I18n.of(context).invitedUser(senderName, targetName); text = I18n.tr(context).invitedUser(senderName, targetName);
} else if (newMembership == "join") { } else if (newMembership == "join") {
text = I18n.of(context).joinedTheChat(targetName); text = I18n.tr(context).joinedTheChat(targetName);
} }
} else if (newMembership == "join") { } else if (newMembership == "join") {
final String newAvatar = this.content["avatar_url"] ?? ""; final String newAvatar = this.content["avatar_url"] ?? "";
@ -107,29 +107,29 @@ extension LocalizedBody on Event {
// Has the user avatar changed? // Has the user avatar changed?
if (newAvatar != oldAvatar) { if (newAvatar != oldAvatar) {
text = I18n.of(context).changedTheProfileAvatar(targetName); text = I18n.tr(context).changedTheProfileAvatar(targetName);
} }
// Has the user avatar changed? // Has the user avatar changed?
else if (newDisplayname != oldDisplayname) { else if (newDisplayname != oldDisplayname) {
text = I18n.of(context) text = I18n.tr(context)
.changedTheDisplaynameTo(targetName, newDisplayname); .changedTheDisplaynameTo(targetName, newDisplayname);
} }
} }
localizedBody = text; localizedBody = text;
break; break;
case EventTypes.RoomPowerLevels: case EventTypes.RoomPowerLevels:
localizedBody = I18n.of(context).changedTheChatPermissions(senderName); localizedBody = I18n.tr(context).changedTheChatPermissions(senderName);
break; break;
case EventTypes.RoomName: case EventTypes.RoomName:
localizedBody = localizedBody =
I18n.of(context).changedTheChatNameTo(senderName, content["name"]); I18n.tr(context).changedTheChatNameTo(senderName, content["name"]);
break; break;
case EventTypes.RoomTopic: case EventTypes.RoomTopic:
localizedBody = I18n.of(context) localizedBody = I18n.tr(context)
.changedTheChatDescriptionTo(senderName, content["topic"]); .changedTheChatDescriptionTo(senderName, content["topic"]);
break; break;
case EventTypes.RoomAvatar: case EventTypes.RoomAvatar:
localizedBody = I18n.of(context).changedTheChatAvatar(senderName); localizedBody = I18n.tr(context).changedTheChatAvatar(senderName);
break; break;
case EventTypes.GuestAccess: case EventTypes.GuestAccess:
GuestAccess guestAccess = GuestAccess.values.firstWhere( GuestAccess guestAccess = GuestAccess.values.firstWhere(
@ -139,9 +139,9 @@ extension LocalizedBody on Event {
orElse: () => null); orElse: () => null);
if (guestAccess == null) { if (guestAccess == null) {
localizedBody = localizedBody =
I18n.of(context).changedTheGuestAccessRules(senderName); I18n.tr(context).changedTheGuestAccessRules(senderName);
} else { } else {
localizedBody = I18n.of(context).changedTheGuestAccessRulesTo( localizedBody = I18n.tr(context).changedTheGuestAccessRulesTo(
senderName, guestAccess.getLocalizedString(context)); senderName, guestAccess.getLocalizedString(context));
} }
break; break;
@ -154,48 +154,48 @@ extension LocalizedBody on Event {
orElse: () => null); orElse: () => null);
if (historyVisibility == null) { if (historyVisibility == null) {
localizedBody = localizedBody =
I18n.of(context).changedTheHistoryVisibility(senderName); I18n.tr(context).changedTheHistoryVisibility(senderName);
} else { } else {
localizedBody = I18n.of(context).changedTheHistoryVisibilityTo( localizedBody = I18n.tr(context).changedTheHistoryVisibilityTo(
senderName, historyVisibility.getLocalizedString(context)); senderName, historyVisibility.getLocalizedString(context));
} }
break; break;
case EventTypes.Encryption: case EventTypes.Encryption:
localizedBody = localizedBody =
I18n.of(context).activatedEndToEndEncryption(senderName); I18n.tr(context).activatedEndToEndEncryption(senderName);
if (!room.client.encryptionEnabled) { if (!room.client.encryptionEnabled) {
localizedBody += ". " + I18n.of(context).needPantalaimonWarning; localizedBody += ". " + I18n.tr(context).needPantalaimonWarning;
} }
break; break;
case EventTypes.Encrypted: case EventTypes.Encrypted:
localizedBody = I18n.of(context).couldNotDecryptMessage; localizedBody = I18n.tr(context).couldNotDecryptMessage;
break; break;
case EventTypes.Message: case EventTypes.Message:
switch (this.messageType) { switch (this.messageType) {
case MessageTypes.Image: case MessageTypes.Image:
localizedBody = I18n.of(context).sentAPicture(senderName); localizedBody = I18n.tr(context).sentAPicture(senderName);
break; break;
case MessageTypes.File: case MessageTypes.File:
localizedBody = I18n.of(context).sentAFile(senderName); localizedBody = I18n.tr(context).sentAFile(senderName);
break; break;
case MessageTypes.Audio: case MessageTypes.Audio:
localizedBody = I18n.of(context).sentAnAudio(senderName); localizedBody = I18n.tr(context).sentAnAudio(senderName);
break; break;
case MessageTypes.Video: case MessageTypes.Video:
localizedBody = I18n.of(context).sentAVideo(senderName); localizedBody = I18n.tr(context).sentAVideo(senderName);
break; break;
case MessageTypes.Location: case MessageTypes.Location:
localizedBody = I18n.of(context).sharedTheLocation(senderName); localizedBody = I18n.tr(context).sharedTheLocation(senderName);
break; break;
case MessageTypes.Sticker: case MessageTypes.Sticker:
localizedBody = I18n.of(context).sentASticker(senderName); localizedBody = I18n.tr(context).sentASticker(senderName);
break; break;
case MessageTypes.Emote: case MessageTypes.Emote:
localizedBody = "* $body"; localizedBody = "* $body";
break; break;
case MessageTypes.BadEncrypted: case MessageTypes.BadEncrypted:
localizedBody = localizedBody =
"🔒 " + I18n.of(context).couldNotDecryptMessage + ": " + body; "🔒 " + I18n.tr(context).couldNotDecryptMessage + ": " + body;
break; break;
case MessageTypes.Text: case MessageTypes.Text:
case MessageTypes.Notice: case MessageTypes.Notice:
@ -206,7 +206,7 @@ extension LocalizedBody on Event {
} }
break; break;
default: default:
localizedBody = I18n.of(context).unknownEvent(this.typeKey); localizedBody = I18n.tr(context).unknownEvent(this.typeKey);
} }
// Hide quotes // Hide quotes
@ -221,7 +221,7 @@ extension LocalizedBody on Event {
this.type == EventTypes.Message && this.type == EventTypes.Message &&
textOnlyMessageTypes.contains(this.messageType)) { textOnlyMessageTypes.contains(this.messageType)) {
final String senderNameOrYou = this.senderId == room.client.userID final String senderNameOrYou = this.senderId == room.client.userID
? I18n.of(context).you ? I18n.tr(context).you
: senderName; : senderName;
localizedBody = "$senderNameOrYou: $localizedBody"; localizedBody = "$senderNameOrYou: $localizedBody";
} }

View File

@ -8,13 +8,13 @@ extension LocalizedRoomDisplayname on Room {
(this.canonicalAlias?.isEmpty ?? true) && (this.canonicalAlias?.isEmpty ?? true) &&
!this.isDirectChat && !this.isDirectChat &&
(this.mHeroes != null && this.mHeroes.isNotEmpty)) { (this.mHeroes != null && this.mHeroes.isNotEmpty)) {
return I18n.of(context).groupWith(this.displayname); return I18n.tr(context).groupWith(this.displayname);
} }
if ((this.name?.isEmpty ?? true) && if ((this.name?.isEmpty ?? true) &&
(this.canonicalAlias?.isEmpty ?? true) && (this.canonicalAlias?.isEmpty ?? true) &&
!this.isDirectChat && !this.isDirectChat &&
(this.mHeroes?.isEmpty ?? true)) { (this.mHeroes?.isEmpty ?? true)) {
return I18n.of(context).emptyChat; return I18n.tr(context).emptyChat;
} }
return this.displayname; return this.displayname;
} }

View File

@ -6,13 +6,13 @@ extension HistoryVisibilityDisplayString on HistoryVisibility {
String getLocalizedString(BuildContext context) { String getLocalizedString(BuildContext context) {
switch (this) { switch (this) {
case HistoryVisibility.invited: case HistoryVisibility.invited:
return I18n.of(context).fromTheInvitation; return I18n.tr(context).fromTheInvitation;
case HistoryVisibility.joined: case HistoryVisibility.joined:
return I18n.of(context).fromJoining; return I18n.tr(context).fromJoining;
case HistoryVisibility.shared: case HistoryVisibility.shared:
return I18n.of(context).visibleForAllParticipants; return I18n.tr(context).visibleForAllParticipants;
case HistoryVisibility.world_readable: case HistoryVisibility.world_readable:
return I18n.of(context).visibleForEveryone; return I18n.tr(context).visibleForEveryone;
default: default:
return this.toString().replaceAll("HistoryVisibility.", ""); return this.toString().replaceAll("HistoryVisibility.", "");
} }
@ -23,9 +23,9 @@ extension GuestAccessDisplayString on GuestAccess {
String getLocalizedString(BuildContext context) { String getLocalizedString(BuildContext context) {
switch (this) { switch (this) {
case GuestAccess.can_join: case GuestAccess.can_join:
return I18n.of(context).guestsCanJoin; return I18n.tr(context).guestsCanJoin;
case GuestAccess.forbidden: case GuestAccess.forbidden:
return I18n.of(context).guestsAreForbidden; return I18n.tr(context).guestsAreForbidden;
default: default:
return this.toString().replaceAll("GuestAccess.", ""); return this.toString().replaceAll("GuestAccess.", "");
} }
@ -36,9 +36,9 @@ extension JoinRulesDisplayString on JoinRules {
String getLocalizedString(BuildContext context) { String getLocalizedString(BuildContext context) {
switch (this) { switch (this) {
case JoinRules.public: case JoinRules.public:
return I18n.of(context).anyoneCanJoin; return I18n.tr(context).anyoneCanJoin;
case JoinRules.invite: case JoinRules.invite:
return I18n.of(context).invitedUsersOnly; return I18n.tr(context).invitedUsersOnly;
default: default:
return this.toString().replaceAll("JoinRules.", ""); return this.toString().replaceAll("JoinRules.", "");
} }

View File

@ -29,7 +29,7 @@ class AppInfo extends StatelessWidget {
body: ListView( body: ListView(
children: <Widget>[ children: <Widget>[
ListTile( ListTile(
title: Text(I18n.of(context).yourOwnUsername + ":"), title: Text(I18n.tr(context).yourOwnUsername + ":"),
subtitle: Text(client.userID), subtitle: Text(client.userID),
), ),
ListTile( ListTile(

View File

@ -23,7 +23,7 @@ class _ArchiveState extends State<Archive> {
return AdaptivePageLayout( return AdaptivePageLayout(
firstScaffold: Scaffold( firstScaffold: Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(I18n.of(context).archive), title: Text(I18n.tr(context).archive),
), ),
body: FutureBuilder<List<Room>>( body: FutureBuilder<List<Room>>(
future: getArchive(context), future: getArchive(context),

View File

@ -19,7 +19,7 @@ class AuthWebView extends StatelessWidget {
if (kIsWeb) launch(url); if (kIsWeb) launch(url);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(I18n.of(context).authentication), title: Text(I18n.tr(context).authentication),
leading: IconButton( leading: IconButton(
icon: Icon(Icons.close), icon: Icon(Icons.close),
onPressed: () { onPressed: () {

View File

@ -120,14 +120,14 @@ class _ChatState extends State<_Chat> {
r.user.id == room.client.userID || r.user.id == room.client.userID ||
r.user.id == timeline.events.first.senderId); r.user.id == timeline.events.first.senderId);
if (lastReceipts.length == 1) { if (lastReceipts.length == 1) {
seenByText = I18n.of(context) seenByText = I18n.tr(context)
.seenByUser(lastReceipts.first.user.calcDisplayname()); .seenByUser(lastReceipts.first.user.calcDisplayname());
} else if (lastReceipts.length == 2) { } else if (lastReceipts.length == 2) {
seenByText = seenByText = I18n.of(context).seenByUserAndUser( seenByText = seenByText = I18n.tr(context).seenByUserAndUser(
lastReceipts.first.user.calcDisplayname(), lastReceipts.first.user.calcDisplayname(),
lastReceipts[1].user.calcDisplayname()); lastReceipts[1].user.calcDisplayname());
} else if (lastReceipts.length > 2) { } else if (lastReceipts.length > 2) {
seenByText = I18n.of(context).seenByUserAndCountOthers( seenByText = I18n.tr(context).seenByUserAndCountOthers(
lastReceipts.first.user.calcDisplayname(), lastReceipts.first.user.calcDisplayname(),
(lastReceipts.length - 1).toString()); (lastReceipts.length - 1).toString());
} }
@ -174,7 +174,7 @@ class _ChatState extends State<_Chat> {
void sendFileAction(BuildContext context) async { void sendFileAction(BuildContext context) async {
if (kIsWeb) { if (kIsWeb) {
return Toast.show(I18n.of(context).notSupportedInWeb, context); return Toast.show(I18n.tr(context).notSupportedInWeb, context);
} }
File file = await FilePicker.getFile(); File file = await FilePicker.getFile();
if (file == null) return; if (file == null) return;
@ -187,7 +187,7 @@ class _ChatState extends State<_Chat> {
void sendImageAction(BuildContext context) async { void sendImageAction(BuildContext context) async {
if (kIsWeb) { if (kIsWeb) {
return Toast.show(I18n.of(context).notSupportedInWeb, context); return Toast.show(I18n.tr(context).notSupportedInWeb, context);
} }
File file = await ImagePicker.pickImage( File file = await ImagePicker.pickImage(
source: ImageSource.gallery, source: ImageSource.gallery,
@ -204,7 +204,7 @@ class _ChatState extends State<_Chat> {
void openCameraAction(BuildContext context) async { void openCameraAction(BuildContext context) async {
if (kIsWeb) { if (kIsWeb) {
return Toast.show(I18n.of(context).notSupportedInWeb, context); return Toast.show(I18n.tr(context).notSupportedInWeb, context);
} }
File file = await ImagePicker.pickImage( File file = await ImagePicker.pickImage(
source: ImageSource.camera, source: ImageSource.camera,
@ -235,8 +235,8 @@ class _ChatState extends State<_Chat> {
void redactEventsAction(BuildContext context) async { void redactEventsAction(BuildContext context) async {
bool confirmed = await SimpleDialogs(context).askConfirmation( bool confirmed = await SimpleDialogs(context).askConfirmation(
titleText: I18n.of(context).messageWillBeRemovedWarning, titleText: I18n.tr(context).messageWillBeRemovedWarning,
confirmText: I18n.of(context).remove, confirmText: I18n.tr(context).remove,
); );
if (!confirmed) return; if (!confirmed) return;
for (Event event in selectedEvents) { for (Event event in selectedEvents) {
@ -287,10 +287,10 @@ class _ChatState extends State<_Chat> {
if (room == null) { if (room == null) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(I18n.of(context).oopsSomethingWentWrong), title: Text(I18n.tr(context).oopsSomethingWentWrong),
), ),
body: Center( body: Center(
child: Text(I18n.of(context).youAreNoLongerParticipatingInThisChat), child: Text(I18n.tr(context).youAreNoLongerParticipatingInThisChat),
), ),
); );
} }
@ -305,17 +305,17 @@ class _ChatState extends State<_Chat> {
typingUsers.removeWhere((User u) => u.id == client.userID); typingUsers.removeWhere((User u) => u.id == client.userID);
if (typingUsers.length == 1) { if (typingUsers.length == 1) {
typingText = I18n.of(context).isTyping; typingText = I18n.tr(context).isTyping;
if (typingUsers.first.id != room.directChatMatrixID) { if (typingUsers.first.id != room.directChatMatrixID) {
typingText = typingText =
I18n.of(context).userIsTyping(typingUsers.first.calcDisplayname()); I18n.tr(context).userIsTyping(typingUsers.first.calcDisplayname());
} }
} else if (typingUsers.length == 2) { } else if (typingUsers.length == 2) {
typingText = I18n.of(context).userAndUserAreTyping( typingText = I18n.tr(context).userAndUserAreTyping(
typingUsers.first.calcDisplayname(), typingUsers.first.calcDisplayname(),
typingUsers[1].calcDisplayname()); typingUsers[1].calcDisplayname());
} else if (typingUsers.length > 2) { } else if (typingUsers.length > 2) {
typingText = I18n.of(context).userAndOthersAreTyping( typingText = I18n.tr(context).userAndOthersAreTyping(
typingUsers.first.calcDisplayname(), typingUsers.first.calcDisplayname(),
(typingUsers.length - 1).toString()); (typingUsers.length - 1).toString());
} }
@ -358,7 +358,7 @@ class _ChatState extends State<_Chat> {
), ),
], ],
) )
: Text(I18n.of(context) : Text(I18n.tr(context)
.numberSelected(selectedEvents.length.toString())), .numberSelected(selectedEvents.length.toString())),
actions: selectMode actions: selectMode
? <Widget>[ ? <Widget>[
@ -511,7 +511,7 @@ class _ChatState extends State<_Chat> {
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
Icon(Icons.keyboard_arrow_left), Icon(Icons.keyboard_arrow_left),
Text(I18n.of(context).forward), Text(I18n.tr(context).forward),
], ],
), ),
), ),
@ -524,8 +524,9 @@ class _ChatState extends State<_Chat> {
onPressed: () => replyAction(), onPressed: () => replyAction(),
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
Text( Text(FluffychatLocalizations
I18n.of(context).reply), .of(context)
.reply),
Icon(Icons Icon(Icons
.keyboard_arrow_right), .keyboard_arrow_right),
], ],
@ -539,7 +540,8 @@ class _ChatState extends State<_Chat> {
sendAgainAction(), sendAgainAction(),
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
Text(I18n.of(context) Text(FluffychatLocalizations
.of(context)
.tryToSendAgain), .tryToSendAgain),
SizedBox(width: 4), SizedBox(width: 4),
Icon(Icons.send, size: 16), Icon(Icons.send, size: 16),
@ -575,7 +577,7 @@ class _ChatState extends State<_Chat> {
child: Icon(Icons.attachment), child: Icon(Icons.attachment),
), ),
title: Text( title: Text(
I18n.of(context).sendFile), I18n.tr(context).sendFile),
contentPadding: contentPadding:
EdgeInsets.all(0), EdgeInsets.all(0),
), ),
@ -589,7 +591,7 @@ class _ChatState extends State<_Chat> {
child: Icon(Icons.image), child: Icon(Icons.image),
), ),
title: Text( title: Text(
I18n.of(context).sendImage), I18n.tr(context).sendImage),
contentPadding: contentPadding:
EdgeInsets.all(0), EdgeInsets.all(0),
), ),
@ -603,7 +605,7 @@ class _ChatState extends State<_Chat> {
foregroundColor: Colors.white, foregroundColor: Colors.white,
child: Icon(Icons.camera_alt), child: Icon(Icons.camera_alt),
), ),
title: Text(I18n.of(context) title: Text(I18n.tr(context)
.openCamera), .openCamera),
contentPadding: contentPadding:
EdgeInsets.all(0), EdgeInsets.all(0),
@ -630,7 +632,7 @@ class _ChatState extends State<_Chat> {
controller: sendController, controller: sendController,
decoration: InputDecoration( decoration: InputDecoration(
hintText: hintText:
I18n.of(context).writeAMessage, I18n.tr(context).writeAMessage,
border: InputBorder.none, border: InputBorder.none,
prefixIcon: prefixIcon:
sendController.text.isEmpty sendController.text.isEmpty

View File

@ -34,8 +34,8 @@ class _ChatDetailsState extends State<ChatDetails> {
List<User> members; List<User> members;
void setDisplaynameAction(BuildContext context) async { void setDisplaynameAction(BuildContext context) async {
final String displayname = await SimpleDialogs(context).enterText( final String displayname = await SimpleDialogs(context).enterText(
titleText: I18n.of(context).changeTheNameOfTheGroup, titleText: I18n.tr(context).changeTheNameOfTheGroup,
labelText: I18n.of(context).changeTheNameOfTheGroup, labelText: I18n.tr(context).changeTheNameOfTheGroup,
hintText: widget.room.getLocalizedDisplayname(context), hintText: widget.room.getLocalizedDisplayname(context),
); );
if (displayname == null) return; if (displayname == null) return;
@ -45,7 +45,7 @@ class _ChatDetailsState extends State<ChatDetails> {
); );
if (success != false) { if (success != false) {
Toast.show( Toast.show(
I18n.of(context).displaynameHasBeenChanged, I18n.tr(context).displaynameHasBeenChanged,
context, context,
duration: Toast.LENGTH_LONG, duration: Toast.LENGTH_LONG,
); );
@ -54,9 +54,9 @@ class _ChatDetailsState extends State<ChatDetails> {
void setCanonicalAliasAction(context) async { void setCanonicalAliasAction(context) async {
final String s = await SimpleDialogs(context).enterText( final String s = await SimpleDialogs(context).enterText(
titleText: I18n.of(context).setInvitationLink, titleText: I18n.tr(context).setInvitationLink,
labelText: I18n.of(context).setInvitationLink, labelText: I18n.tr(context).setInvitationLink,
hintText: I18n.of(context).alias.toLowerCase(), hintText: I18n.tr(context).alias.toLowerCase(),
prefixText: "#", prefixText: "#",
suffixText: ":" + widget.room.client.userID.domain, suffixText: ":" + widget.room.client.userID.domain,
); );
@ -96,11 +96,11 @@ class _ChatDetailsState extends State<ChatDetails> {
void setTopicAction(BuildContext context) async { void setTopicAction(BuildContext context) async {
final String displayname = await SimpleDialogs(context).enterText( final String displayname = await SimpleDialogs(context).enterText(
titleText: I18n.of(context).setGroupDescription, titleText: I18n.tr(context).setGroupDescription,
labelText: I18n.of(context).setGroupDescription, labelText: I18n.tr(context).setGroupDescription,
hintText: (widget.room.topic?.isNotEmpty ?? false) hintText: (widget.room.topic?.isNotEmpty ?? false)
? widget.room.topic ? widget.room.topic
: I18n.of(context).addGroupDescription, : I18n.tr(context).addGroupDescription,
multiLine: true, multiLine: true,
); );
if (displayname == null) return; if (displayname == null) return;
@ -110,7 +110,7 @@ class _ChatDetailsState extends State<ChatDetails> {
); );
if (success != false) { if (success != false) {
Toast.show( Toast.show(
I18n.of(context).groupDescriptionHasBeenChanged, I18n.tr(context).groupDescriptionHasBeenChanged,
context, context,
duration: Toast.LENGTH_LONG, duration: Toast.LENGTH_LONG,
); );
@ -135,7 +135,7 @@ class _ChatDetailsState extends State<ChatDetails> {
); );
if (success != false) { if (success != false) {
Toast.show( Toast.show(
I18n.of(context).avatarHasBeenChanged, I18n.tr(context).avatarHasBeenChanged,
context, context,
duration: Toast.LENGTH_LONG, duration: Toast.LENGTH_LONG,
); );
@ -161,10 +161,10 @@ class _ChatDetailsState extends State<ChatDetails> {
if (widget.room == null) { if (widget.room == null) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(I18n.of(context).oopsSomethingWentWrong), title: Text(I18n.tr(context).oopsSomethingWentWrong),
), ),
body: Center( body: Center(
child: Text(I18n.of(context).youAreNoLongerParticipatingInThisChat), child: Text(I18n.tr(context).youAreNoLongerParticipatingInThisChat),
), ),
); );
} }
@ -195,7 +195,7 @@ class _ChatDetailsState extends State<ChatDetails> {
Clipboard.setData( Clipboard.setData(
ClipboardData(text: widget.room.canonicalAlias), ClipboardData(text: widget.room.canonicalAlias),
); );
Toast.show(I18n.of(context).copiedToClipboard, context, Toast.show(I18n.tr(context).copiedToClipboard, context,
duration: 5); duration: 5);
}, },
), ),
@ -229,13 +229,13 @@ class _ChatDetailsState extends State<ChatDetails> {
child: Icon(Icons.edit), child: Icon(Icons.edit),
) )
: null, : null,
title: Text("${I18n.of(context).groupDescription}:", title: Text("${I18n.tr(context).groupDescription}:",
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold)), fontWeight: FontWeight.bold)),
subtitle: LinkText( subtitle: LinkText(
text: widget.room.topic?.isEmpty ?? true text: widget.room.topic?.isEmpty ?? true
? I18n.of(context).addGroupDescription ? I18n.tr(context).addGroupDescription
: widget.room.topic, : widget.room.topic,
linkStyle: TextStyle(color: Colors.blueAccent), linkStyle: TextStyle(color: Colors.blueAccent),
textStyle: TextStyle( textStyle: TextStyle(
@ -250,7 +250,7 @@ class _ChatDetailsState extends State<ChatDetails> {
Divider(thickness: 1), Divider(thickness: 1),
ListTile( ListTile(
title: Text( title: Text(
I18n.of(context).settings, I18n.tr(context).settings,
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -265,7 +265,7 @@ class _ChatDetailsState extends State<ChatDetails> {
foregroundColor: Colors.grey, foregroundColor: Colors.grey,
child: Icon(Icons.people), child: Icon(Icons.people),
), ),
title: Text(I18n.of(context).changeTheNameOfTheGroup), title: Text(I18n.tr(context).changeTheNameOfTheGroup),
subtitle: Text( subtitle: Text(
widget.room.getLocalizedDisplayname(context)), widget.room.getLocalizedDisplayname(context)),
onTap: () => setDisplaynameAction(context), onTap: () => setDisplaynameAction(context),
@ -280,11 +280,11 @@ class _ChatDetailsState extends State<ChatDetails> {
child: Icon(Icons.link), child: Icon(Icons.link),
), ),
onTap: () => setCanonicalAliasAction(context), onTap: () => setCanonicalAliasAction(context),
title: Text(I18n.of(context).setInvitationLink), title: Text(I18n.tr(context).setInvitationLink),
subtitle: Text( subtitle: Text(
(widget.room.canonicalAlias?.isNotEmpty ?? false) (widget.room.canonicalAlias?.isNotEmpty ?? false)
? widget.room.canonicalAlias ? widget.room.canonicalAlias
: I18n.of(context).none), : I18n.tr(context).none),
), ),
PopupMenuButton( PopupMenuButton(
child: ListTile( child: ListTile(
@ -294,7 +294,7 @@ class _ChatDetailsState extends State<ChatDetails> {
foregroundColor: Colors.grey, foregroundColor: Colors.grey,
child: Icon(Icons.public)), child: Icon(Icons.public)),
title: Text( title: Text(
I18n.of(context).whoIsAllowedToJoinThisGroup), I18n.tr(context).whoIsAllowedToJoinThisGroup),
subtitle: Text( subtitle: Text(
widget.room.joinRules.getLocalizedString(context), widget.room.joinRules.getLocalizedString(context),
), ),
@ -328,7 +328,7 @@ class _ChatDetailsState extends State<ChatDetails> {
child: Icon(Icons.visibility), child: Icon(Icons.visibility),
), ),
title: title:
Text(I18n.of(context).visibilityOfTheChatHistory), Text(I18n.tr(context).visibilityOfTheChatHistory),
subtitle: Text( subtitle: Text(
widget.room.historyVisibility widget.room.historyVisibility
.getLocalizedString(context), .getLocalizedString(context),
@ -376,7 +376,7 @@ class _ChatDetailsState extends State<ChatDetails> {
child: Icon(Icons.info_outline), child: Icon(Icons.info_outline),
), ),
title: title:
Text(I18n.of(context).areGuestsAllowedToJoin), Text(I18n.tr(context).areGuestsAllowedToJoin),
subtitle: Text( subtitle: Text(
widget.room.guestAccess widget.room.guestAccess
.getLocalizedString(context), .getLocalizedString(context),
@ -410,9 +410,9 @@ class _ChatDetailsState extends State<ChatDetails> {
ListTile( ListTile(
title: Text( title: Text(
actualMembersCount > 1 actualMembersCount > 1
? I18n.of(context).countParticipants( ? I18n.tr(context).countParticipants(
actualMembersCount.toString()) actualMembersCount.toString())
: I18n.of(context).emptyChat, : I18n.tr(context).emptyChat,
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -422,7 +422,7 @@ class _ChatDetailsState extends State<ChatDetails> {
Divider(height: 1), Divider(height: 1),
widget.room.canInvite widget.room.canInvite
? ListTile( ? ListTile(
title: Text(I18n.of(context).inviteContact), title: Text(I18n.tr(context).inviteContact),
leading: CircleAvatar( leading: CircleAvatar(
child: Icon(Icons.add), child: Icon(Icons.add),
backgroundColor: Theme.of(context).primaryColor, backgroundColor: Theme.of(context).primaryColor,
@ -441,7 +441,7 @@ class _ChatDetailsState extends State<ChatDetails> {
: i < members.length + 1 : i < members.length + 1
? ParticipantListItem(members[i - 1]) ? ParticipantListItem(members[i - 1])
: ListTile( : ListTile(
title: Text(I18n.of(context).loadCountMoreParticipants( title: Text(I18n.tr(context).loadCountMoreParticipants(
(actualMembersCount - members.length).toString())), (actualMembersCount - members.length).toString())),
leading: CircleAvatar( leading: CircleAvatar(
backgroundColor: backgroundColor:

View File

@ -53,25 +53,25 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(I18n.of(context).end2endEncryptionSettings), title: Text(I18n.tr(context).end2endEncryptionSettings),
), ),
body: Column( body: Column(
children: <Widget>[ children: <Widget>[
ListTile( ListTile(
title: Text(I18n.of(context).encryptionAlgorithm), title: Text(I18n.tr(context).encryptionAlgorithm),
subtitle: Text(room.encryptionAlgorithm ?? I18n.of(context).none), subtitle: Text(room.encryptionAlgorithm ?? I18n.tr(context).none),
trailing: Icon(room.encrypted ? Icons.lock : Icons.lock_open, trailing: Icon(room.encrypted ? Icons.lock : Icons.lock_open,
color: room.encrypted ? Colors.green : Colors.red), color: room.encrypted ? Colors.green : Colors.red),
onTap: () async { onTap: () async {
if (room.encrypted) return; if (room.encrypted) return;
if (!room.client.encryptionEnabled) { if (!room.client.encryptionEnabled) {
Toast.show(I18n.of(context).needPantalaimonWarning, context, Toast.show(I18n.tr(context).needPantalaimonWarning, context,
duration: 8); duration: 8);
return; return;
} }
if (await SimpleDialogs(context).askConfirmation( if (await SimpleDialogs(context).askConfirmation(
titleText: I18n.of(context).enableEncryptionWarning, titleText: I18n.tr(context).enableEncryptionWarning,
confirmText: I18n.of(context).yes) == confirmText: I18n.tr(context).yes) ==
true) { true) {
await Matrix.of(context).tryRequestWithLoadingDialog( await Matrix.of(context).tryRequestWithLoadingDialog(
room.enableEncryption(), room.enableEncryption(),
@ -83,15 +83,15 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
trailing: Icon(Icons.info), trailing: Icon(Icons.info),
subtitle: Text( subtitle: Text(
room.client.encryptionEnabled room.client.encryptionEnabled
? I18n.of(context).warningEncryptionInBeta ? I18n.tr(context).warningEncryptionInBeta
: I18n.of(context).needPantalaimonWarning, : I18n.tr(context).needPantalaimonWarning,
), ),
), ),
Divider(height: 1), Divider(height: 1),
if (room.encrypted) if (room.encrypted)
ListTile( ListTile(
title: Text( title: Text(
"${I18n.of(context).participatingUserDevices}:", "${I18n.tr(context).participatingUserDevices}:",
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@ -104,7 +104,7 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
builder: (BuildContext context, snapshot) { builder: (BuildContext context, snapshot) {
if (snapshot.hasError) { if (snapshot.hasError) {
return Center( return Center(
child: Text(I18n.of(context).oopsSomethingWentWrong + child: Text(I18n.tr(context).oopsSomethingWentWrong +
": " + ": " +
snapshot.error.toString()), snapshot.error.toString()),
); );

View File

@ -89,7 +89,9 @@ class _ChatListState extends State<ChatList> {
} }
}, },
onError: (error) => Toast.show( onError: (error) => Toast.show(
I18n.of(context).oopsSomethingWentWrong + " " + error.toString(), I18n.tr(context).oopsSomethingWentWrong +
" " +
error.toString(),
context, context,
duration: 5), duration: 5),
); );
@ -122,13 +124,13 @@ class _ChatListState extends State<ChatList> {
controller: searchController, controller: searchController,
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
hintText: I18n.of(context).searchForAChat, hintText: I18n.tr(context).searchForAChat,
), ),
) )
: Text( : Text(
selectMode == SelectMode.share selectMode == SelectMode.share
? I18n.of(context).share ? I18n.tr(context).share
: I18n.of(context).fluffychat, : I18n.tr(context).fluffychat,
), ),
leading: searchMode leading: searchMode
? IconButton( ? IconButton(
@ -180,11 +182,13 @@ class _ChatListState extends State<ChatList> {
<PopupMenuEntry<String>>[ <PopupMenuEntry<String>>[
PopupMenuItem<String>( PopupMenuItem<String>(
value: "archive", value: "archive",
child: Text(I18n.of(context).archive), child:
Text(I18n.tr(context).archive),
), ),
PopupMenuItem<String>( PopupMenuItem<String>(
value: "settings", value: "settings",
child: Text(I18n.of(context).settings), child:
Text(I18n.tr(context).settings),
), ),
], ],
), ),
@ -200,9 +204,8 @@ class _ChatListState extends State<ChatList> {
child: Icon(Icons.people_outline), child: Icon(Icons.people_outline),
foregroundColor: Colors.white, foregroundColor: Colors.white,
backgroundColor: Colors.blue, backgroundColor: Colors.blue,
label: I18n.of(context).createNewGroup, label: I18n.tr(context).createNewGroup,
labelStyle: labelStyle: TextStyle(fontSize: 18.0, color: Colors.black),
TextStyle(fontSize: 18.0, color: Colors.black),
onTap: () => Navigator.of(context).pushAndRemoveUntil( onTap: () => Navigator.of(context).pushAndRemoveUntil(
AppRoute.defaultRoute(context, NewGroupView()), AppRoute.defaultRoute(context, NewGroupView()),
(r) => r.isFirst), (r) => r.isFirst),
@ -211,10 +214,8 @@ class _ChatListState extends State<ChatList> {
child: Icon(Icons.person_add), child: Icon(Icons.person_add),
foregroundColor: Colors.white, foregroundColor: Colors.white,
backgroundColor: Colors.green, backgroundColor: Colors.green,
label: I18n.of(context).newPrivateChat, label: I18n.tr(context).newPrivateChat,
labelStyle: TextStyle( labelStyle: TextStyle(fontSize: 18.0, color: Colors.black),
fontSize: 18.0,
color: Colors.black),
onTap: () => Navigator.of(context).pushAndRemoveUntil( onTap: () => Navigator.of(context).pushAndRemoveUntil(
AppRoute.defaultRoute(context, NewPrivateChatView()), AppRoute.defaultRoute(context, NewPrivateChatView()),
(r) => r.isFirst), (r) => r.isFirst),
@ -242,8 +243,9 @@ class _ChatListState extends State<ChatList> {
color: Colors.grey, color: Colors.grey,
), ),
Text(searchMode Text(searchMode
? I18n.of(context).noRoomsFound ? I18n.tr(context).noRoomsFound
: I18n.of(context).startYourFirstChat), : I18n.tr(context)
.startYourFirstChat),
], ],
), ),
); );

View File

@ -16,7 +16,7 @@ class ContentWebView extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
I18n.of(context).contentViewer, I18n.tr(context).contentViewer,
), ),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(

View File

@ -53,7 +53,7 @@ class _InvitationSelectionState extends State<InvitationSelection> {
); );
if (success != false) { if (success != false) {
Toast.show( Toast.show(
I18n.of(context).contactHasBeenInvitedToTheGroup, I18n.tr(context).contactHasBeenInvitedToTheGroup,
context, context,
duration: Toast.LENGTH_LONG, duration: Toast.LENGTH_LONG,
); );
@ -114,14 +114,14 @@ class _InvitationSelectionState extends State<InvitationSelection> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String groupName = widget.room.name?.isEmpty ?? false final String groupName = widget.room.name?.isEmpty ?? false
? I18n.of(context).group ? I18n.tr(context).group
: widget.room.name; : widget.room.name;
return AdaptivePageLayout( return AdaptivePageLayout(
primaryPage: FocusPage.SECOND, primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(activeChat: widget.room.id), firstScaffold: ChatList(activeChat: widget.room.id),
secondScaffold: Scaffold( secondScaffold: Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(I18n.of(context).inviteContact), title: Text(I18n.tr(context).inviteContact),
bottom: PreferredSize( bottom: PreferredSize(
preferredSize: Size.fromHeight(68), preferredSize: Size.fromHeight(68),
child: Padding( child: Padding(
@ -137,8 +137,8 @@ class _InvitationSelectionState extends State<InvitationSelection> {
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
prefixText: "@", prefixText: "@",
hintText: I18n.of(context).username, hintText: I18n.tr(context).username,
labelText: I18n.of(context).inviteContactToGroup(groupName), labelText: I18n.tr(context).inviteContactToGroup(groupName),
suffixIcon: loading suffixIcon: loading
? Container( ? Container(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),

View File

@ -28,12 +28,12 @@ class _LoginState extends State<Login> {
void login(BuildContext context) async { void login(BuildContext context) async {
MatrixState matrix = Matrix.of(context); MatrixState matrix = Matrix.of(context);
if (usernameController.text.isEmpty) { if (usernameController.text.isEmpty) {
setState(() => usernameError = I18n.of(context).pleaseEnterYourUsername); setState(() => usernameError = I18n.tr(context).pleaseEnterYourUsername);
} else { } else {
setState(() => usernameError = null); setState(() => usernameError = null);
} }
if (passwordController.text.isEmpty) { if (passwordController.text.isEmpty) {
setState(() => passwordError = I18n.of(context).pleaseEnterYourPassword); setState(() => passwordError = I18n.tr(context).pleaseEnterYourPassword);
} else { } else {
setState(() => passwordError = null); setState(() => passwordError = null);
} }
@ -53,12 +53,12 @@ class _LoginState extends State<Login> {
setState(() => loading = true); setState(() => loading = true);
if (!await matrix.client.checkServer(homeserver)) { if (!await matrix.client.checkServer(homeserver)) {
setState( setState(
() => serverError = I18n.of(context).homeserverIsNotCompatible); () => serverError = I18n.tr(context).homeserverIsNotCompatible);
return setState(() => loading = false); return setState(() => loading = false);
} }
} catch (exception) { } catch (exception) {
setState(() => serverError = I18n.of(context).connectionAttemptFailed); setState(() => serverError = I18n.tr(context).connectionAttemptFailed);
return setState(() => loading = false); return setState(() => loading = false);
} }
try { try {
@ -93,6 +93,7 @@ class _LoginState extends State<Login> {
appBar: AppBar( appBar: AppBar(
leading: loading ? Container() : null, leading: loading ? Container() : null,
title: TextField( title: TextField(
key: Key("serverField"),
autocorrect: false, autocorrect: false,
controller: serverController, controller: serverController,
decoration: InputDecoration( decoration: InputDecoration(
@ -124,14 +125,15 @@ class _LoginState extends State<Login> {
color: Theme.of(context).primaryColor), color: Theme.of(context).primaryColor),
), ),
title: TextField( title: TextField(
key: Key("usernameField"),
readOnly: loading, readOnly: loading,
autocorrect: false, autocorrect: false,
controller: usernameController, controller: usernameController,
decoration: InputDecoration( decoration: InputDecoration(
hintText: hintText:
"@${I18n.of(context).username.toLowerCase()}:domain", "@${I18n.tr(context).username.toLowerCase()}:domain",
errorText: usernameError, errorText: usernameError,
labelText: I18n.of(context).username), labelText: I18n.tr(context).username),
), ),
), ),
ListTile( ListTile(
@ -142,6 +144,7 @@ class _LoginState extends State<Login> {
child: Icon(Icons.lock, color: Theme.of(context).primaryColor), child: Icon(Icons.lock, color: Theme.of(context).primaryColor),
), ),
title: TextField( title: TextField(
key: Key("passwordField"),
readOnly: loading, readOnly: loading,
autocorrect: false, autocorrect: false,
controller: passwordController, controller: passwordController,
@ -156,7 +159,7 @@ class _LoginState extends State<Login> {
onPressed: () => onPressed: () =>
setState(() => showPassword = !showPassword), setState(() => showPassword = !showPassword),
), ),
labelText: I18n.of(context).password), labelText: I18n.tr(context).password),
), ),
), ),
SizedBox(height: 20), SizedBox(height: 20),
@ -164,6 +167,7 @@ class _LoginState extends State<Login> {
height: 50, height: 50,
padding: EdgeInsets.symmetric(horizontal: 12), padding: EdgeInsets.symmetric(horizontal: 12),
child: RaisedButton( child: RaisedButton(
key: Key("loginButton"),
elevation: 7, elevation: 7,
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -172,7 +176,7 @@ class _LoginState extends State<Login> {
child: loading child: loading
? CircularProgressIndicator() ? CircularProgressIndicator()
: Text( : Text(
I18n.of(context).login.toUpperCase(), I18n.tr(context).login.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 16), style: TextStyle(color: Colors.white, fontSize: 16),
), ),
onPressed: () => loading ? null : login(context), onPressed: () => loading ? null : login(context),

View File

@ -69,7 +69,7 @@ class _NewGroupState extends State<_NewGroup> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(I18n.of(context).createNewGroup), title: Text(I18n.tr(context).createNewGroup),
elevation: 0, elevation: 0,
), ),
body: Column( body: Column(
@ -85,13 +85,13 @@ class _NewGroupState extends State<_NewGroup> {
onSubmitted: (s) => submitAction(context), onSubmitted: (s) => submitAction(context),
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
labelText: I18n.of(context).optionalGroupName, labelText: I18n.tr(context).optionalGroupName,
prefixIcon: Icon(Icons.people), prefixIcon: Icon(Icons.people),
hintText: I18n.of(context).enterAGroupName), hintText: I18n.tr(context).enterAGroupName),
), ),
), ),
SwitchListTile( SwitchListTile(
title: Text(I18n.of(context).groupIsPublic), title: Text(I18n.tr(context).groupIsPublic),
value: publicGroup, value: publicGroup,
onChanged: (bool b) => setState(() => publicGroup = b), onChanged: (bool b) => setState(() => publicGroup = b),
), ),

View File

@ -106,7 +106,7 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(I18n.of(context).newPrivateChat), title: Text(I18n.tr(context).newPrivateChat),
elevation: 0, elevation: 0,
), ),
body: Column( body: Column(
@ -125,24 +125,24 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
onFieldSubmitted: (s) => submitAction(context), onFieldSubmitted: (s) => submitAction(context),
validator: (value) { validator: (value) {
if (value.isEmpty) { if (value.isEmpty) {
return I18n.of(context).pleaseEnterAMatrixIdentifier; return I18n.tr(context).pleaseEnterAMatrixIdentifier;
} }
final MatrixState matrix = Matrix.of(context); final MatrixState matrix = Matrix.of(context);
String mxid = "@" + controller.text.trim(); String mxid = "@" + controller.text.trim();
if (mxid == matrix.client.userID) { if (mxid == matrix.client.userID) {
return I18n.of(context).youCannotInviteYourself; return I18n.tr(context).youCannotInviteYourself;
} }
if (!mxid.contains("@")) { if (!mxid.contains("@")) {
return I18n.of(context).makeSureTheIdentifierIsValid; return I18n.tr(context).makeSureTheIdentifierIsValid;
} }
if (!mxid.contains(":")) { if (!mxid.contains(":")) {
return I18n.of(context).makeSureTheIdentifierIsValid; return I18n.tr(context).makeSureTheIdentifierIsValid;
} }
return null; return null;
}, },
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
labelText: I18n.of(context).enterAUsername, labelText: I18n.tr(context).enterAUsername,
prefixIcon: loading prefixIcon: loading
? Container( ? Container(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
@ -162,7 +162,7 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
) )
: Icon(Icons.account_circle), : Icon(Icons.account_circle),
prefixText: "@", prefixText: "@",
hintText: "${I18n.of(context).username.toLowerCase()}", hintText: "${I18n.tr(context).username.toLowerCase()}",
), ),
), ),
), ),
@ -209,11 +209,11 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
Icons.share, Icons.share,
size: 16, size: 16,
), ),
onTap: () => Share.share(I18n.of(context).inviteText( onTap: () => Share.share(I18n.tr(context).inviteText(
Matrix.of(context).client.userID, Matrix.of(context).client.userID,
"https://matrix.to/#/${Matrix.of(context).client.userID}")), "https://matrix.to/#/${Matrix.of(context).client.userID}")),
title: Text( title: Text(
"${I18n.of(context).yourOwnUsername}:", "${I18n.tr(context).yourOwnUsername}:",
style: TextStyle( style: TextStyle(
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
), ),

View File

@ -51,10 +51,10 @@ class _SettingsState extends State<Settings> {
void setDisplaynameAction(BuildContext context) async { void setDisplaynameAction(BuildContext context) async {
final String displayname = await SimpleDialogs(context).enterText( final String displayname = await SimpleDialogs(context).enterText(
titleText: I18n.of(context).editDisplayname, titleText: I18n.tr(context).editDisplayname,
hintText: hintText:
profile?.displayname ?? Matrix.of(context).client.userID.localpart, profile?.displayname ?? Matrix.of(context).client.userID.localpart,
labelText: I18n.of(context).enterAUsername, labelText: I18n.tr(context).enterAUsername,
); );
if (displayname == null) return; if (displayname == null) return;
final MatrixState matrix = Matrix.of(context); final MatrixState matrix = Matrix.of(context);
@ -87,7 +87,7 @@ class _SettingsState extends State<Settings> {
); );
if (success != false) { if (success != false) {
Toast.show( Toast.show(
I18n.of(context).avatarHasBeenChanged, I18n.tr(context).avatarHasBeenChanged,
context, context,
duration: Toast.LENGTH_LONG, duration: Toast.LENGTH_LONG,
); );
@ -116,7 +116,7 @@ class _SettingsState extends State<Settings> {
backgroundColor: Theme.of(context).appBarTheme.color, backgroundColor: Theme.of(context).appBarTheme.color,
flexibleSpace: FlexibleSpaceBar( flexibleSpace: FlexibleSpaceBar(
title: Text( title: Text(
I18n.of(context).settings, I18n.tr(context).settings,
style: TextStyle( style: TextStyle(
color: Theme.of(context).appBarTheme.textTheme.title.color), color: Theme.of(context).appBarTheme.textTheme.title.color),
), ),
@ -134,7 +134,7 @@ class _SettingsState extends State<Settings> {
children: <Widget>[ children: <Widget>[
ListTile( ListTile(
title: Text( title: Text(
I18n.of(context).account, I18n.tr(context).account,
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -143,13 +143,13 @@ class _SettingsState extends State<Settings> {
), ),
ListTile( ListTile(
trailing: Icon(Icons.edit), trailing: Icon(Icons.edit),
title: Text(I18n.of(context).editDisplayname), title: Text(I18n.tr(context).editDisplayname),
subtitle: Text(profile?.displayname ?? client.userID.localpart), subtitle: Text(profile?.displayname ?? client.userID.localpart),
onTap: () => setDisplaynameAction(context), onTap: () => setDisplaynameAction(context),
), ),
ListTile( ListTile(
trailing: Icon(Icons.color_lens), trailing: Icon(Icons.color_lens),
title: Text(I18n.of(context).changeTheme), title: Text(I18n.tr(context).changeTheme),
onTap: () async => await Navigator.of(context).push( onTap: () async => await Navigator.of(context).push(
AppRoute.defaultRoute( AppRoute.defaultRoute(
context, context,
@ -159,7 +159,7 @@ class _SettingsState extends State<Settings> {
), ),
ListTile( ListTile(
trailing: Icon(Icons.devices_other), trailing: Icon(Icons.devices_other),
title: Text(I18n.of(context).devices), title: Text(I18n.tr(context).devices),
onTap: () async => await Navigator.of(context).push( onTap: () async => await Navigator.of(context).push(
AppRoute.defaultRoute( AppRoute.defaultRoute(
context, context,
@ -169,13 +169,13 @@ class _SettingsState extends State<Settings> {
), ),
ListTile( ListTile(
trailing: Icon(Icons.exit_to_app), trailing: Icon(Icons.exit_to_app),
title: Text(I18n.of(context).logout), title: Text(I18n.tr(context).logout),
onTap: () => logoutAction(context), onTap: () => logoutAction(context),
), ),
Divider(thickness: 1), Divider(thickness: 1),
ListTile( ListTile(
title: Text( title: Text(
I18n.of(context).about, I18n.tr(context).about,
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -191,19 +191,19 @@ class _SettingsState extends State<Settings> {
), ),
ListTile( ListTile(
leading: Icon(Icons.donut_large), leading: Icon(Icons.donut_large),
title: Text("Liberapay " + I18n.of(context).donate), title: Text("Liberapay " + I18n.tr(context).donate),
onTap: () => onTap: () =>
launch("https://liberapay.com/KrilleChritzelius/donate"), launch("https://liberapay.com/KrilleChritzelius/donate"),
), ),
ListTile( ListTile(
leading: Icon(Icons.help), leading: Icon(Icons.help),
title: Text(I18n.of(context).help), title: Text(I18n.tr(context).help),
onTap: () => launch( onTap: () => launch(
"https://gitlab.com/ChristianPauly/fluffychat-flutter/issues"), "https://gitlab.com/ChristianPauly/fluffychat-flutter/issues"),
), ),
ListTile( ListTile(
leading: Icon(Icons.account_circle), leading: Icon(Icons.account_circle),
title: Text(I18n.of(context).accountInformations), title: Text(I18n.tr(context).accountInformations),
onTap: () => Navigator.of(context).push( onTap: () => Navigator.of(context).push(
AppRoute.defaultRoute( AppRoute.defaultRoute(
context, context,
@ -213,19 +213,19 @@ class _SettingsState extends State<Settings> {
), ),
ListTile( ListTile(
leading: Icon(Icons.list), leading: Icon(Icons.list),
title: Text(I18n.of(context).changelog), title: Text(I18n.tr(context).changelog),
onTap: () => launch( onTap: () => launch(
"https://gitlab.com/ChristianPauly/fluffychat-flutter/blob/master/CHANGELOG.md"), "https://gitlab.com/ChristianPauly/fluffychat-flutter/blob/master/CHANGELOG.md"),
), ),
ListTile( ListTile(
leading: Icon(Icons.link), leading: Icon(Icons.link),
title: Text(I18n.of(context).license), title: Text(I18n.tr(context).license),
onTap: () => launch( onTap: () => launch(
"https://gitlab.com/ChristianPauly/fluffychat-flutter/raw/master/LICENSE"), "https://gitlab.com/ChristianPauly/fluffychat-flutter/raw/master/LICENSE"),
), ),
ListTile( ListTile(
leading: Icon(Icons.code), leading: Icon(Icons.code),
title: Text(I18n.of(context).sourceCode), title: Text(I18n.tr(context).sourceCode),
onTap: () => launch( onTap: () => launch(
"https://gitlab.com/ChristianPauly/fluffychat-flutter"), "https://gitlab.com/ChristianPauly/fluffychat-flutter"),
), ),

View File

@ -46,8 +46,8 @@ class DevicesSettingsState extends State<DevicesSettings> {
.tryRequestWithLoadingDialog(matrix.client.deleteDevices(deviceIds), .tryRequestWithLoadingDialog(matrix.client.deleteDevices(deviceIds),
onAdditionalAuth: (MatrixException exception) async { onAdditionalAuth: (MatrixException exception) async {
final String password = await SimpleDialogs(context).enterText( final String password = await SimpleDialogs(context).enterText(
titleText: I18n.of(context).pleaseEnterYourPassword, titleText: I18n.tr(context).pleaseEnterYourPassword,
labelText: I18n.of(context).pleaseEnterYourPassword, labelText: I18n.tr(context).pleaseEnterYourPassword,
hintText: "******", hintText: "******",
password: true); password: true);
if (password == null) return; if (password == null) return;
@ -63,7 +63,7 @@ class DevicesSettingsState extends State<DevicesSettings> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text(I18n.of(context).devices)), appBar: AppBar(title: Text(I18n.tr(context).devices)),
body: FutureBuilder<bool>( body: FutureBuilder<bool>(
future: _loadUserDevices(context), future: _loadUserDevices(context),
builder: (BuildContext context, snapshot) { builder: (BuildContext context, snapshot) {
@ -98,7 +98,7 @@ class DevicesSettingsState extends State<DevicesSettings> {
if (devices.isNotEmpty) if (devices.isNotEmpty)
ListTile( ListTile(
title: Text( title: Text(
I18n.of(context).removeAllOtherDevices, I18n.tr(context).removeAllOtherDevices,
style: TextStyle(color: Colors.red), style: TextStyle(color: Colors.red),
), ),
trailing: Icon(Icons.delete_outline), trailing: Icon(Icons.delete_outline),
@ -151,7 +151,7 @@ class UserDeviceListItem extends StatelessWidget {
itemBuilder: (BuildContext context) => [ itemBuilder: (BuildContext context) => [
PopupMenuItem<String>( PopupMenuItem<String>(
value: "remove", value: "remove",
child: Text(I18n.of(context).removeDevice, child: Text(I18n.tr(context).removeDevice,
style: TextStyle(color: Colors.red)), style: TextStyle(color: Colors.red)),
), ),
], ],
@ -161,7 +161,7 @@ class UserDeviceListItem extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Text((userDevice.displayName?.isNotEmpty ?? false) Text((userDevice.displayName?.isNotEmpty ?? false)
? userDevice.displayName ? userDevice.displayName
: I18n.of(context).unknownDevice), : I18n.tr(context).unknownDevice),
Spacer(), Spacer(),
Text(userDevice.lastSeenTs.localizedTimeShort(context)), Text(userDevice.lastSeenTs.localizedTimeShort(context)),
], ],
@ -169,8 +169,8 @@ class UserDeviceListItem extends StatelessWidget {
subtitle: Column( subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text("${I18n.of(context).id}: ${userDevice.deviceId}"), Text("${I18n.tr(context).id}: ${userDevice.deviceId}"),
Text("${I18n.of(context).lastSeenIp}: ${userDevice.lastSeenIp}"), Text("${I18n.tr(context).lastSeenIp}: ${userDevice.lastSeenIp}"),
], ],
), ),
), ),

View File

@ -36,13 +36,13 @@ class ThemesSettingsState extends State<ThemesSettings> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(I18n.of(context).changeTheme), title: Text(I18n.tr(context).changeTheme),
), ),
body: Column( body: Column(
children: <Widget>[ children: <Widget>[
RadioListTile<Themes>( RadioListTile<Themes>(
title: Text( title: Text(
I18n.of(context).systemTheme, I18n.tr(context).systemTheme,
), ),
value: Themes.system, value: Themes.system,
groupValue: _selectedTheme, groupValue: _selectedTheme,
@ -56,7 +56,7 @@ class ThemesSettingsState extends State<ThemesSettings> {
), ),
RadioListTile<Themes>( RadioListTile<Themes>(
title: Text( title: Text(
I18n.of(context).lightTheme, I18n.tr(context).lightTheme,
), ),
value: Themes.light, value: Themes.light,
groupValue: _selectedTheme, groupValue: _selectedTheme,
@ -70,7 +70,7 @@ class ThemesSettingsState extends State<ThemesSettings> {
), ),
RadioListTile<Themes>( RadioListTile<Themes>(
title: Text( title: Text(
I18n.of(context).darkTheme, I18n.tr(context).darkTheme,
), ),
value: Themes.dark, value: Themes.dark,
groupValue: _selectedTheme, groupValue: _selectedTheme,
@ -85,7 +85,7 @@ class ThemesSettingsState extends State<ThemesSettings> {
Divider(thickness: 1), Divider(thickness: 1),
ListTile( ListTile(
title: Text( title: Text(
I18n.of(context).useAmoledTheme, I18n.tr(context).useAmoledTheme,
), ),
trailing: Switch( trailing: Switch(
value: _amoledEnabled, value: _amoledEnabled,

View File

@ -38,7 +38,7 @@ class _SignUpState extends State<SignUp> {
void signUpAction(BuildContext context) async { void signUpAction(BuildContext context) async {
MatrixState matrix = Matrix.of(context); MatrixState matrix = Matrix.of(context);
if (usernameController.text.isEmpty) { if (usernameController.text.isEmpty) {
setState(() => usernameError = I18n.of(context).pleaseChooseAUsername); setState(() => usernameError = I18n.tr(context).pleaseChooseAUsername);
} else { } else {
setState(() => usernameError = null); setState(() => usernameError = null);
} }
@ -61,12 +61,12 @@ class _SignUpState extends State<SignUp> {
setState(() => loading = true); setState(() => loading = true);
if (!await matrix.client.checkServer(homeserver)) { if (!await matrix.client.checkServer(homeserver)) {
setState( setState(
() => serverError = I18n.of(context).homeserverIsNotCompatible); () => serverError = I18n.tr(context).homeserverIsNotCompatible);
return setState(() => loading = false); return setState(() => loading = false);
} }
} catch (exception) { } catch (exception) {
setState(() => serverError = I18n.of(context).connectionAttemptFailed); setState(() => serverError = I18n.tr(context).connectionAttemptFailed);
return setState(() => loading = false); return setState(() => loading = false);
} }
@ -116,7 +116,9 @@ class _SignUpState extends State<SignUp> {
leading: CircleAvatar( leading: CircleAvatar(
backgroundImage: avatar == null ? null : FileImage(avatar), backgroundImage: avatar == null ? null : FileImage(avatar),
backgroundColor: avatar == null backgroundColor: avatar == null
? Theme.of(context).brightness == Brightness.dark ? Color(0xff121212) : Colors.white ? Theme.of(context).brightness == Brightness.dark
? Color(0xff121212)
: Colors.white
: Theme.of(context).secondaryHeaderColor, : Theme.of(context).secondaryHeaderColor,
child: avatar == null child: avatar == null
? Icon(Icons.camera_alt, ? Icon(Icons.camera_alt,
@ -130,15 +132,17 @@ class _SignUpState extends State<SignUp> {
color: Colors.red, color: Colors.red,
), ),
title: Text(avatar == null title: Text(avatar == null
? I18n.of(context).setAProfilePicture ? I18n.tr(context).setAProfilePicture
: I18n.of(context).discardPicture), : I18n.tr(context).discardPicture),
onTap: avatar == null onTap: avatar == null
? setAvatarAction ? setAvatarAction
: () => setState(() => avatar = null), : () => setState(() => avatar = null),
), ),
ListTile( ListTile(
leading: CircleAvatar( leading: CircleAvatar(
backgroundColor: Theme.of(context).brightness == Brightness.dark ? Color(0xff121212) : Colors.white, backgroundColor: Theme.of(context).brightness == Brightness.dark
? Color(0xff121212)
: Colors.white,
child: Icon( child: Icon(
Icons.account_circle, Icons.account_circle,
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
@ -149,9 +153,9 @@ class _SignUpState extends State<SignUp> {
controller: usernameController, controller: usernameController,
onSubmitted: (s) => signUpAction(context), onSubmitted: (s) => signUpAction(context),
decoration: InputDecoration( decoration: InputDecoration(
hintText: I18n.of(context).username, hintText: I18n.tr(context).username,
errorText: usernameError, errorText: usernameError,
labelText: I18n.of(context).chooseAUsername), labelText: I18n.tr(context).chooseAUsername),
), ),
), ),
SizedBox(height: 20), SizedBox(height: 20),
@ -167,7 +171,7 @@ class _SignUpState extends State<SignUp> {
child: loading child: loading
? CircularProgressIndicator() ? CircularProgressIndicator()
: Text( : Text(
I18n.of(context).signUp.toUpperCase(), I18n.tr(context).signUp.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 16), style: TextStyle(color: Colors.white, fontSize: 16),
), ),
onPressed: () => signUpAction(context), onPressed: () => signUpAction(context),
@ -176,7 +180,7 @@ class _SignUpState extends State<SignUp> {
Center( Center(
child: FlatButton( child: FlatButton(
child: Text( child: Text(
I18n.of(context).alreadyHaveAnAccount, I18n.tr(context).alreadyHaveAnAccount,
style: TextStyle( style: TextStyle(
decoration: TextDecoration.underline, decoration: TextDecoration.underline,
color: Colors.blue, color: Colors.blue,

View File

@ -29,7 +29,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
void _signUpAction(BuildContext context, {Map<String, dynamic> auth}) async { void _signUpAction(BuildContext context, {Map<String, dynamic> auth}) async {
MatrixState matrix = Matrix.of(context); MatrixState matrix = Matrix.of(context);
if (passwordController.text.isEmpty) { if (passwordController.text.isEmpty) {
setState(() => passwordError = I18n.of(context).pleaseEnterYourPassword); setState(() => passwordError = I18n.tr(context).pleaseEnterYourPassword);
} else { } else {
setState(() => passwordError = null); setState(() => passwordError = null);
} }
@ -96,7 +96,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
try { try {
await matrix.client.setDisplayname(widget.displayname); await matrix.client.setDisplayname(widget.displayname);
} catch (exception) { } catch (exception) {
Toast.show(I18n.of(context).couldNotSetDisplayname, context, duration: 5); Toast.show(I18n.tr(context).couldNotSetDisplayname, context, duration: 5);
} }
if (widget.avatar != null) { if (widget.avatar != null) {
try { try {
@ -107,7 +107,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
), ),
); );
} catch (exception) { } catch (exception) {
Toast.show(I18n.of(context).couldNotSetAvatar, context, duration: 5); Toast.show(I18n.tr(context).couldNotSetAvatar, context, duration: 5);
} }
} }
await Navigator.of(context).pushAndRemoveUntil( await Navigator.of(context).pushAndRemoveUntil(
@ -119,7 +119,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(I18n.of(context).secureYourAccountWithAPassword), title: Text(I18n.tr(context).secureYourAccountWithAPassword),
), ),
body: ListView( body: ListView(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
@ -156,7 +156,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
onPressed: () => onPressed: () =>
setState(() => showPassword = !showPassword), setState(() => showPassword = !showPassword),
), ),
labelText: I18n.of(context).password), labelText: I18n.tr(context).password),
), ),
), ),
SizedBox(height: 20), SizedBox(height: 20),
@ -172,7 +172,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
child: loading child: loading
? CircularProgressIndicator() ? CircularProgressIndicator()
: Text( : Text(
I18n.of(context).createAccountNow.toUpperCase(), I18n.tr(context).createAccountNow.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 16), style: TextStyle(color: Colors.white, fontSize: 16),
), ),
onPressed: () => loading ? null : _signUpAction(context), onPressed: () => loading ? null : _signUpAction(context),

View File

@ -124,8 +124,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: "6dd3b879b6cfcf1c7da9dedfa62626237afa6d9a" ref: "5a4c484db62234ac8c782bf03f121e8687a07836"
resolved-ref: "6dd3b879b6cfcf1c7da9dedfa62626237afa6d9a" resolved-ref: "5a4c484db62234ac8c782bf03f121e8687a07836"
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"

257
test/fake_matrix_api.dart Normal file
View File

@ -0,0 +1,257 @@
import 'package:http/testing.dart';
import 'dart:convert';
import 'dart:core';
import 'dart:math';
import 'package:http/http.dart';
class FakeMatrixApi extends MockClient {
FakeMatrixApi()
: super((request) async {
// Collect data from Request
final String action = request.url.path.split("/_matrix")[1];
final String method = request.method;
final dynamic data =
method == "GET" ? request.url.queryParameters : request.body;
var res = {};
//print("$method request to $action with Data: $data");
// Sync requests with timeout
if (data is Map<String, dynamic> && data["timeout"] is String) {
await Future.delayed(Duration(seconds: 5));
}
// Call API
if (api.containsKey(method) && api[method].containsKey(action)) {
res = api[method][action](data);
} else if (method == "GET" &&
action.contains("/client/r0/rooms/") &&
action.contains("/state/m.room.member/")) {
res = {"displayname": ""};
return Response(json.encode(res), 200);
} else {
res = {
"errcode": "M_UNRECOGNIZED",
"error": "Unrecognized request"
};
}
return Response(json.encode(res), 100);
});
static final Map<String, Map<String, dynamic>> api = {
"GET": {
"/client/versions": (var req) => {
"versions": ["r0.0.1", "r0.1.0", "r0.2.0", "r0.3.0", "r0.4.0"],
"unstable_features": {"m.lazy_load_members": true},
},
"/client/r0/account/whoami": (var req) =>
{"user_id": "@test:fakeServer.notExisting"},
"/client/r0/login": (var req) => {
"flows": [
{"type": "m.login.password"}
]
},
"/client/r0/sync": (var req) => {
"next_batch": Random().nextDouble().toString(),
"presence": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.presence",
"content": {"presence": "online"}
}
]
},
"account_data": {
"events": [
{
"type": "org.example.custom.config",
"content": {"custom_config_key": "custom_config_value"}
}
]
},
"to_device": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.new_device",
"content": {
"device_id": "XYZABCDE",
"rooms": ["!726s6s6q:example.com"]
}
}
]
},
"rooms": {
"join": {
"!726s6s6q:example.com": {
"unread_notifications": {
"highlight_count": 2,
"notification_count": 2,
},
"state": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.room.member",
"state_key": "@alice:example.com",
"content": {"membership": "join"},
"origin_server_ts": 1417731086795,
"event_id": "66697273743031:example.com"
}
]
},
"timeline": {
"events": [
{
"sender": "@bob:example.com",
"type": "m.room.member",
"state_key": "@bob:example.com",
"content": {"membership": "join"},
"prev_content": {"membership": "invite"},
"origin_server_ts": 1417731086795,
"event_id": "7365636s6r6432:example.com"
},
{
"sender": "@alice:example.com",
"type": "m.room.message",
"txn_id": "1234",
"content": {"body": "I am a fish", "msgtype": "m.text"},
"origin_server_ts": 1417731086797,
"event_id": "74686972643033:example.com"
}
],
"limited": true,
"prev_batch": "t34-23535_0_0"
},
"ephemeral": {
"events": [
{
"type": "m.typing",
"content": {
"user_ids": ["@alice:example.com"]
}
}
]
},
"account_data": {
"events": [
{
"type": "m.tag",
"content": {
"tags": {
"work": {"order": 1}
}
}
},
{
"type": "org.example.custom.room.config",
"content": {"custom_config_key": "custom_config_value"}
}
]
}
}
},
"invite": {
"!696r7674:example.com": {
"invite_state": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.room.name",
"state_key": "",
"content": {"name": "My Room Name"}
},
{
"sender": "@alice:example.com",
"type": "m.room.member",
"state_key": "@bob:example.com",
"content": {"membership": "invite"}
}
]
}
}
},
"leave": {
"!5345234234:example.com": {
"timeline": {"events": []}
},
},
}
},
},
"POST": {
"/client/r0/keys/claim": (var req) => {
"failures": {},
"one_time_keys": {
"@alice:example.com": {
"JLAFKJWSCS": {
"signed_curve25519:AAAAHg": {
"key": "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs",
"signatures": {
"@alice:example.com": {
"ed25519:JLAFKJWSCS":
"FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw"
}
}
}
}
}
}
},
"/client/r0/keys/upload": (var req) => {
"one_time_key_counts": {
"curve25519": 10,
"signed_curve25519": 100,
}
},
"/client/r0/keys/query": (var req) => {
"failures": {},
"device_keys": {
"@test:fakeServer.notExisting": {
"JLAFKJWSCS": {
"user_id": "@test:fakeServer.notExisting",
"device_id": "JLAFKJWSCS",
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"keys": {
"curve25519:JLAFKJWSCS":
"3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI",
"ed25519:JLAFKJWSCS":
"lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"
},
"signatures": {
"@test:fakeServer.notExisting": {
"ed25519:JLAFKJWSCS":
"dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"
}
},
"unsigned": {"device_display_name": "Alice's mobile phone"}
}
}
}
},
"/client/r0/login": (var req) => {
"user_id": "@test:fakeServer.notExisting",
"access_token": "abc123",
"device_id": "GHTYAJCE"
},
"/client/r0/logout": (var reqI) => {},
"/client/r0/logout/all": (var reqI) => {},
},
"PUT": {
"/client/r0/rooms/!request:localhost/state/com.famedly.app.request_state":
(var req) => {"event_id": "1234"},
"/client/r0/user/null/account_data/com.famedly.talk.tasks": (var req) =>
{},
"/client/r0/rooms/1234/typing/@test:fakeServer.notExisting": (var req) =>
{},
},
"DELETE": {
"/unknown/token": (var req) => {"errcode": "M_UNKNOWN_TOKEN"},
},
};
}

35
test/login_test.dart Normal file
View File

@ -0,0 +1,35 @@
import 'package:fluffychat/views/login.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'utils.dart';
void main() {
/// All Tests related to the Login
group("LoginPage", () {
/// Check if all Elements get created
testWidgets('should get created', (WidgetTester tester) async {
await tester.runAsync(() async {
final TestObserver observer = TestObserver()
..onPushed = (Route<dynamic> route, Route<dynamic> previousRoute) {}
..onPopped = (Route<dynamic> route, Route<dynamic> previousRoute) {};
await tester.pumpWidget(
Utils.getWidgetWrapper(
Login(),
observer,
),
);
await tester.pump(Duration.zero);
expect(find.byKey(Key("serverField")), findsOneWidget); // Server field
expect(
find.byKey(Key("usernameField")), findsOneWidget); // Username Input
expect(
find.byKey(Key("passwordField")), findsOneWidget); // Password Input
expect(find.byKey(Key("loginButton")), findsOneWidget); // Login Button
});
});
});
}

128
test/utils.dart Normal file
View File

@ -0,0 +1,128 @@
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/components/theme_switcher.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'fake_matrix_api.dart';
class Utils {
static printWidgets(WidgetTester tester) {
debugPrint(tester.allWidgets.toList().join("\n").toString());
}
static Client get defaultClient {
Client client = Client("testclient", debug: true);
client.httpClient = FakeMatrixApi();
client.onUserEvent.stream.listen(client.handleUserUpdate);
client.connect(
newHomeserver: "https://fakeServer.notExisting",
newDeviceID: "GHTYAJCE",
newToken: "abc123",
newUserID: "@test:fakeServer.notExisting");
return client;
}
static Widget getWidgetWrapper(Widget child, TestObserver routeObserver,
{Client client}) {
return Matrix(
client: client ?? Utils.defaultClient,
child: MaterialApp(
title: "Fluffychat",
theme: lightTheme,
navigatorObservers: <NavigatorObserver>[routeObserver],
home: child,
),
);
}
static double getOpacity(WidgetTester tester, String textValue) {
final FadeTransition opacityWidget = tester.widget<FadeTransition>(find
.ancestor(
of: find.text(textValue),
matching: find.byType(FadeTransition),
)
.first);
return opacityWidget.opacity.value;
}
static Future<Null> tapItem(WidgetTester tester, Key key) async {
/// Tap the button which should open the PopupMenu.
/// By calling tester.pumpAndSettle(), we ensure that all animations
/// have completed before we continue further.
await tester.tap(find.byKey(key));
await tester.pumpAndSettle();
}
}
typedef OnObservation = void Function(
Route<dynamic> route, Route<dynamic> previousRoute);
/// Example Usage:
///
/// ```
/// bool isPushed = false;
// bool isPopped = false;
//
// final TestObserver observer = TestObserver()
// ..onPushed = (Route<dynamic> route, Route<dynamic> previousRoute) {
// // Pushes the initial route.
// expect(route is PageRoute && route.settings.name == '/', isTrue);
// expect(previousRoute, isNull);
// isPushed = true;
// }
// ..onPopped = (Route<dynamic> route, Route<dynamic> previousRoute) {
// isPopped = true;
// };
/// ```
///
class TestObserver extends NavigatorObserver {
OnObservation onPushed;
OnObservation onPopped;
OnObservation onRemoved;
OnObservation onReplaced;
@override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
if (onPushed != null) {
onPushed(route, previousRoute);
}
}
@override
void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
if (onPopped != null) {
onPopped(route, previousRoute);
}
}
@override
void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) {
if (onRemoved != null) onRemoved(route, previousRoute);
}
@override
void didReplace({Route<dynamic> oldRoute, Route<dynamic> newRoute}) {
if (onReplaced != null) onReplaced(newRoute, oldRoute);
}
}
class RouteMatcher extends Matcher {
final String expected;
bool contains = false;
RouteMatcher(this.expected, {this.contains});
@override
Description describe(Description description) {
return null;
}
@override
bool matches(covariant String item, Map matchState) {
if (contains) {
return item.toString().contains(expected);
}
return item == expected;
}
}

View File

@ -1,15 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Test if the app starts', (WidgetTester tester) async {
// Build our app and trigger a frame.
//await tester.pumpWidget(App());
});
}