Merge branch 'krille/apl' into 'main'

refactor: Use APL

See merge request famedly/fluffychat!338
This commit is contained in:
Krille Fear 2021-01-16 11:46:38 +00:00
commit 4b0f9fc8de
40 changed files with 1012 additions and 1283 deletions

View File

@ -1,52 +0,0 @@
import 'package:flutter/material.dart';
enum FocusPage { FIRST, SECOND }
class AdaptivePageLayout extends StatelessWidget {
final Widget firstScaffold;
final Widget secondScaffold;
final FocusPage primaryPage;
final double minWidth;
static const double defaultMinWidth = 400;
static bool columnMode(BuildContext context) =>
MediaQuery.of(context).size.width > 2 * defaultMinWidth;
AdaptivePageLayout(
{this.firstScaffold,
this.secondScaffold,
this.primaryPage = FocusPage.FIRST,
this.minWidth = defaultMinWidth,
Key key})
: super(key: key);
@override
Widget build(BuildContext context) {
return OrientationBuilder(builder: (context, orientation) {
if (orientation == Orientation.portrait || !columnMode(context)) {
if (primaryPage == FocusPage.FIRST) {
return firstScaffold;
} else {
return secondScaffold;
}
}
return Row(
children: <Widget>[
Container(
width: minWidth,
child: firstScaffold,
),
Container(
width: 1,
color: Theme.of(context).secondaryHeaderColor, //Color(0xFFE8E8E8),
),
Expanded(
child: Container(
child: secondScaffold,
),
)
],
);
});
}
}

View File

@ -1,10 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/views/chat_details.dart';
import 'package:fluffychat/views/chat_list.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -95,9 +93,8 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
final success = await showFutureLoadingDialog( final success = await showFutureLoadingDialog(
context: context, future: () => widget.room.leave()); context: context, future: () => widget.room.leave());
if (success.error == null) { if (success.error == null) {
await Navigator.of(context).pushAndRemoveUntil( await AdaptivePageLayout.of(context)
AppRoute.defaultRoute(context, ChatListView()), .pushNamedAndRemoveAllOthers('/');
(Route r) => false);
} }
} }
break; break;
@ -117,12 +114,9 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
startCallAction(context); startCallAction(context);
break; break;
case 'details': case 'details':
await Navigator.of(context).push( await AdaptivePageLayout.of(context).pushNamedAndRemoveAllOthers(
AppRoute.defaultRoute( '/rooms/${widget.room.id}/details');
context,
ChatDetails(widget.room),
),
);
break; break;
} }
}, },

View File

@ -1,11 +1,6 @@
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/views/archive.dart';
import 'package:fluffychat/views/discover_view.dart';
import 'package:fluffychat/views/new_group.dart';
import 'package:fluffychat/views/new_private_chat.dart';
import 'package:fluffychat/views/settings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -13,15 +8,9 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'matrix.dart'; import 'matrix.dart';
class DefaultDrawer extends StatelessWidget { class DefaultDrawer extends StatelessWidget {
void _drawerTapAction(BuildContext context, Widget view) { void _drawerTapAction(BuildContext context, String route) {
Navigator.of(context).pop(); Navigator.of(context).pop();
Navigator.of(context).pushAndRemoveUntil( AdaptivePageLayout.of(context).pushNamedAndRemoveUntilIsFirst(route);
AppRoute.defaultRoute(
context,
view,
),
(r) => r.isFirst,
);
} }
void _setStatus(BuildContext context) async { void _setStatus(BuildContext context) async {
@ -64,12 +53,12 @@ class DefaultDrawer extends StatelessWidget {
ListTile( ListTile(
leading: Icon(Icons.people_outline), leading: Icon(Icons.people_outline),
title: Text(L10n.of(context).createNewGroup), title: Text(L10n.of(context).createNewGroup),
onTap: () => _drawerTapAction(context, NewGroupView()), onTap: () => _drawerTapAction(context, '/newgroup'),
), ),
ListTile( ListTile(
leading: Icon(Icons.person_add_outlined), leading: Icon(Icons.person_add_outlined),
title: Text(L10n.of(context).newPrivateChat), title: Text(L10n.of(context).newPrivateChat),
onTap: () => _drawerTapAction(context, NewPrivateChatView()), onTap: () => _drawerTapAction(context, '/newprivatechat'),
), ),
Divider(height: 1), Divider(height: 1),
ListTile( ListTile(
@ -77,7 +66,7 @@ class DefaultDrawer extends StatelessWidget {
title: Text(L10n.of(context).archive), title: Text(L10n.of(context).archive),
onTap: () => _drawerTapAction( onTap: () => _drawerTapAction(
context, context,
Archive(), '/archive',
), ),
), ),
ListTile( ListTile(
@ -85,7 +74,7 @@ class DefaultDrawer extends StatelessWidget {
title: Text(L10n.of(context).discoverGroups), title: Text(L10n.of(context).discoverGroups),
onTap: () => _drawerTapAction( onTap: () => _drawerTapAction(
context, context,
DiscoverView(), '/discover',
), ),
), ),
Divider(height: 1), Divider(height: 1),
@ -94,7 +83,7 @@ class DefaultDrawer extends StatelessWidget {
title: Text(L10n.of(context).settings), title: Text(L10n.of(context).settings),
onTap: () => _drawerTapAction( onTap: () => _drawerTapAction(
context, context,
SettingsView(), '/settings',
), ),
), ),
], ],

View File

@ -1,10 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:flushbar/flushbar_helper.dart'; import 'package:flushbar/flushbar_helper.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/views/chat_encryption_settings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -23,12 +22,8 @@ class _EncryptionButtonState extends State<EncryptionButton> {
void _enableEncryptionAction() async { void _enableEncryptionAction() async {
if (widget.room.encrypted) { if (widget.room.encrypted) {
await Navigator.of(context).push( await AdaptivePageLayout.of(context)
AppRoute.defaultRoute( .pushNamed('/rooms/${widget.room.id}/encryption');
context,
ChatEncryptionSettingsView(widget.room.id),
),
);
return; return;
} }
if (!widget.room.client.encryptionEnabled) { if (!widget.room.client.encryptionEnabled) {

View File

@ -1,19 +1,17 @@
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:circular_check_box/circular_check_box.dart'; import 'package:circular_check_box/circular_check_box.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/utils/event_extension.dart'; import 'package:fluffychat/utils/event_extension.dart';
import 'package:fluffychat/utils/matrix_locals.dart'; import 'package:fluffychat/utils/matrix_locals.dart';
import 'package:fluffychat/utils/room_status_extension.dart'; import 'package:fluffychat/utils/room_status_extension.dart';
import 'package:fluffychat/views/chat.dart';
import 'package:flushbar/flushbar_helper.dart'; import 'package:flushbar/flushbar_helper.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:pedantic/pedantic.dart'; import 'package:pedantic/pedantic.dart';
import '../../utils/app_route.dart';
import '../../utils/date_time_extension.dart'; import '../../utils/date_time_extension.dart';
import '../../views/chat.dart';
import '../avatar.dart'; import '../avatar.dart';
import '../dialogs/send_file_dialog.dart'; import '../dialogs/send_file_dialog.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
@ -102,11 +100,8 @@ class ChatListItem extends StatelessWidget {
} }
Matrix.of(context).shareContent = null; Matrix.of(context).shareContent = null;
} }
await Navigator.pushAndRemoveUntil( await AdaptivePageLayout.of(context)
context, .pushNamedAndRemoveUntilIsFirst('/rooms/${room.id}');
AppRoute.defaultRoute(context, ChatView(room.id)),
(r) => r.isFirst,
);
} }
} }
} }

View File

@ -1,12 +1,12 @@
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/message_content.dart'; import 'package:fluffychat/components/message_content.dart';
import 'package:fluffychat/components/reply_content.dart'; import 'package:fluffychat/components/reply_content.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/event_extension.dart'; import 'package:fluffychat/utils/event_extension.dart';
import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/utils/string_color.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../adaptive_page_layout.dart';
import '../avatar.dart'; import '../avatar.dart';
import '../matrix.dart'; import '../matrix.dart';
import '../message_reactions.dart'; import '../message_reactions.dart';
@ -88,8 +88,7 @@ class Message extends StatelessWidget {
color: color, color: color,
borderRadius: BorderRadius.circular(radius), borderRadius: BorderRadius.circular(radius),
), ),
constraints: constraints: BoxConstraints(maxWidth: FluffyThemes.columnWidth),
BoxConstraints(maxWidth: AdaptivePageLayout.defaultMinWidth),
child: Stack( child: Stack(
children: <Widget>[ children: <Widget>[
Column( Column(

View File

@ -1,10 +1,9 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../../utils/app_route.dart';
import '../../views/chat.dart';
import '../avatar.dart'; import '../avatar.dart';
import '../matrix.dart'; import '../matrix.dart';
@ -19,12 +18,8 @@ class PublicRoomListItem extends StatelessWidget {
future: () => _joinRoomAndWait(context), future: () => _joinRoomAndWait(context),
); );
if (success.error == null) { if (success.error == null) {
await Navigator.of(context).push( await AdaptivePageLayout.of(context)
AppRoute.defaultRoute( .pushNamed('/rooms/${success.result}');
context,
ChatView(success.result),
),
);
} }
} }

View File

@ -3,14 +3,13 @@ import 'dart:io';
import 'dart:convert'; import 'dart:convert';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/encryption.dart'; import 'package:famedlysdk/encryption.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/utils/firebase_controller.dart'; import 'package:fluffychat/utils/firebase_controller.dart';
import 'package:fluffychat/utils/matrix_locals.dart'; import 'package:fluffychat/utils/matrix_locals.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/sentry_controller.dart'; import 'package:fluffychat/utils/sentry_controller.dart';
import 'package:fluffychat/views/settings_3pid.dart';
import 'package:flushbar/flushbar.dart'; import 'package:flushbar/flushbar.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -38,7 +37,16 @@ class Matrix extends StatefulWidget {
final Widget child; final Widget child;
Matrix({this.child, Key key}) : super(key: key); final GlobalKey<AdaptivePageLayoutState> apl;
final BuildContext context;
Matrix({
this.child,
@required this.apl,
@required this.context,
Key key,
}) : super(key: key);
@override @override
MatrixState createState() => MatrixState(); MatrixState createState() => MatrixState();
@ -52,7 +60,7 @@ class MatrixState extends State<Matrix> {
Client client; Client client;
Store store = Store(); Store store = Store();
@override @override
BuildContext context; BuildContext get context => widget.context;
Map<String, dynamic> get shareContent => _shareContent; Map<String, dynamic> get shareContent => _shareContent;
set shareContent(Map<String, dynamic> content) { set shareContent(Map<String, dynamic> content) {
@ -76,29 +84,16 @@ class MatrixState extends State<Matrix> {
} }
void _initWithStore() async { void _initWithStore() async {
var initLoginState = client.onLoginStateChanged.stream.first;
try { try {
client.init(); client.init();
final firstLoginState = await initLoginState;
if (firstLoginState == LoginState.logged) {
if (PlatformInfos.isMobile) {
await FirebaseController.setupFirebase(
this,
clientName,
);
}
}
final storeItem = await store.getItem(SettingKeys.showNoPid); final storeItem = await store.getItem(SettingKeys.showNoPid);
final configOptionMissing = storeItem == null || storeItem.isEmpty; final configOptionMissing = storeItem == null || storeItem.isEmpty;
if (configOptionMissing || (!configOptionMissing && storeItem == '1')) { if (configOptionMissing || (!configOptionMissing && storeItem == '1')) {
if (configOptionMissing) { if (configOptionMissing) {
await store.setItem(SettingKeys.showNoPid, '0'); await store.setItem(SettingKeys.showNoPid, '0');
} }
await Matrix.of(context) await client.requestThirdPartyIdentifiers().then((l) {
.client
.requestThirdPartyIdentifiers()
.then((l) {
if (l.isEmpty) { if (l.isEmpty) {
Flushbar( Flushbar(
title: L10n.of(context).warning, title: L10n.of(context).warning,
@ -110,12 +105,8 @@ class MatrixState extends State<Matrix> {
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),
), ),
child: Text(L10n.of(context).edit), child: Text(L10n.of(context).edit),
onPressed: () => Navigator.of(context).push( onPressed: () =>
AppRoute.defaultRoute( AdaptivePageLayout.of(context).pushNamed('/settings/3pid'),
context,
Settings3PidView(),
),
),
), ),
flushbarStyle: FlushbarStyle.FLOATING, flushbarStyle: FlushbarStyle.FLOATING,
).show(context); ).show(context);
@ -133,6 +124,7 @@ class MatrixState extends State<Matrix> {
StreamSubscription onKeyVerificationRequestSub; StreamSubscription onKeyVerificationRequestSub;
StreamSubscription onJitsiCallSub; StreamSubscription onJitsiCallSub;
StreamSubscription onNotification; StreamSubscription onNotification;
StreamSubscription<LoginState> onLoginStateChanged;
StreamSubscription<UiaRequest> onUiaRequest; StreamSubscription<UiaRequest> onUiaRequest;
StreamSubscription<html.Event> onFocusSub; StreamSubscription<html.Event> onFocusSub;
StreamSubscription<html.Event> onBlurSub; StreamSubscription<html.Event> onBlurSub;
@ -301,6 +293,8 @@ class MatrixState extends State<Matrix> {
} }
} }
LoginState loginState;
void initMatrix() { void initMatrix() {
clientName = clientName =
'${AppConfig.applicationName} ${kIsWeb ? 'Web' : Platform.operatingSystem}'; '${AppConfig.applicationName} ${kIsWeb ? 'Web' : Platform.operatingSystem}';
@ -380,6 +374,19 @@ class MatrixState extends State<Matrix> {
onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true); onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true);
onBlurSub = html.window.onBlur.listen((_) => webHasFocus = false); onBlurSub = html.window.onBlur.listen((_) => webHasFocus = false);
} }
onLoginStateChanged ??= client.onLoginStateChanged.stream.listen((state) {
if (loginState != state) {
loginState = state;
widget.apl.currentState.pushNamedAndRemoveAllOthers('/');
if (loginState == LoginState.logged) {
FirebaseController.context = context;
FirebaseController.setupFirebase(
this,
clientName,
).catchError(SentryController.captureException);
}
}
});
onUiaRequest ??= client.onUiaRequest.stream.listen(_onUiaRequest); onUiaRequest ??= client.onUiaRequest.stream.listen(_onUiaRequest);
if (kIsWeb || Platform.isLinux) { if (kIsWeb || Platform.isLinux) {
client.onSync.stream.first.then((s) { client.onSync.stream.first.then((s) {

View File

@ -1,12 +1,11 @@
import 'dart:math'; import 'dart:math';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/dialogs/permission_slider_dialog.dart'; import 'package:fluffychat/components/dialogs/permission_slider_dialog.dart';
import 'package:fluffychat/utils/app_route.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/views/chat.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'content_banner.dart'; import 'content_banner.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -15,7 +14,6 @@ import '../utils/presence_extension.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'matrix.dart'; import 'matrix.dart';
import 'dialogs/key_verification_dialog.dart'; import 'dialogs/key_verification_dialog.dart';
import '../utils/app_route.dart';
class UserBottomSheet extends StatelessWidget { class UserBottomSheet extends StatelessWidget {
final User user; final User user;
@ -76,12 +74,8 @@ class UserBottomSheet extends StatelessWidget {
break; break;
case 'message': case 'message':
final roomId = await user.startDirectChat(); final roomId = await user.startDirectChat();
await Navigator.of(context).pushAndRemoveUntil( await AdaptivePageLayout.of(context)
AppRoute.defaultRoute( .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
context,
ChatView(roomId),
),
(Route r) => r.isFirst);
break; break;
} }
} }
@ -161,8 +155,8 @@ class UserBottomSheet extends StatelessWidget {
} }
return Center( return Center(
child: Container( child: Container(
width: min(MediaQuery.of(context).size.width, width: min(
AdaptivePageLayout.defaultMinWidth * 1.5), MediaQuery.of(context).size.width, FluffyThemes.columnWidth * 1.5),
child: SafeArea( child: SafeArea(
child: Material( child: Material(
elevation: 4, elevation: 4,

228
lib/config/routes.dart Normal file
View File

@ -0,0 +1,228 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/views/archive.dart';
import 'package:fluffychat/views/auth_web_view.dart';
import 'package:fluffychat/views/chat.dart';
import 'package:fluffychat/views/chat_details.dart';
import 'package:fluffychat/views/chat_encryption_settings.dart';
import 'package:fluffychat/views/chat_list.dart';
import 'package:fluffychat/views/chat_permissions_settings.dart';
import 'package:fluffychat/views/discover_view.dart';
import 'package:fluffychat/views/empty_page.dart';
import 'package:fluffychat/views/homeserver_picker.dart';
import 'package:fluffychat/views/invitation_selection.dart';
import 'package:fluffychat/views/loading_view.dart';
import 'package:fluffychat/views/log_view.dart';
import 'package:fluffychat/views/login.dart';
import 'package:fluffychat/views/new_group.dart';
import 'package:fluffychat/views/new_private_chat.dart';
import 'package:fluffychat/views/settings.dart';
import 'package:fluffychat/views/settings_3pid.dart';
import 'package:fluffychat/views/settings_devices.dart';
import 'package:fluffychat/views/settings_emotes.dart';
import 'package:fluffychat/views/settings_ignore_list.dart';
import 'package:fluffychat/views/settings_multiple_emotes.dart';
import 'package:fluffychat/views/settings_notifications.dart';
import 'package:fluffychat/views/settings_style.dart';
import 'package:fluffychat/views/sign_up.dart';
import 'package:fluffychat/views/sign_up_password.dart';
import 'package:flutter/material.dart';
class FluffyRoutes {
final BuildContext context;
const FluffyRoutes(this.context);
ViewData onGenerateRoute(RouteSettings settings) {
final parts = settings.name.split('/');
Logs().v(settings.name);
// Routes if the app is loading
if (Matrix.of(context).loginState == null) {
return ViewData(mainView: (_) => LoadingView());
// Routes if user is NOT logged in
} else if (Matrix.of(context).loginState == LoginState.loggedOut) {
switch (parts[1]) {
case '':
return ViewData(mainView: (_) => HomeserverPicker());
case 'login':
return ViewData(mainView: (_) => Login());
case 'signup':
if (parts.length == 5 && parts[2] == 'password') {
return ViewData(
mainView: (_) => SignUpPassword(
parts[3],
displayname: parts[4],
avatar: settings.arguments,
),
);
}
return ViewData(mainView: (_) => SignUp());
case 'authwebview':
if (parts.length == 4) {
return ViewData(
mainView: (_) => AuthWebView(
parts[2],
Uri.decodeComponent(parts[3]),
settings.arguments,
),
);
}
}
}
// Routes IF user is logged in
else {
switch (parts[1]) {
case '':
return ViewData(
mainView: (_) => ChatList(),
emptyView: (_) => EmptyPage(),
);
case 'rooms':
if (parts.length == 3) {
return ViewData(
leftView: (_) => ChatList(),
mainView: (_) => Chat(parts[2]),
);
} else if (parts.length == 4) {
final roomId = parts[2];
final action = parts[3];
switch (action) {
case 'details':
return ViewData(
leftView: (_) => ChatList(),
mainView: (_) => Chat(parts[2]),
rightView: (_) => ChatDetails(roomId),
);
case 'encryption':
return ViewData(
leftView: (_) => ChatList(),
mainView: (_) => Chat(parts[2]),
rightView: (_) => ChatEncryptionSettings(roomId),
);
case 'permissions':
return ViewData(
leftView: (_) => ChatList(),
mainView: (_) => Chat(parts[2]),
rightView: (_) => ChatPermissionsSettings(roomId),
);
case 'invite':
return ViewData(
leftView: (_) => ChatList(),
mainView: (_) => Chat(parts[2]),
rightView: (_) => InvitationSelection(roomId),
);
case 'emotes':
return ViewData(
leftView: (_) => ChatList(),
mainView: (_) => Chat(parts[2]),
rightView: (_) => MultipleEmotesSettings(roomId),
);
}
}
return ViewData(
mainView: (_) => ChatList(),
emptyView: (_) => EmptyPage(),
);
case 'archive':
return ViewData(
mainView: (_) => Archive(),
emptyView: (_) => Chat(parts[2]),
);
case 'discover':
return ViewData(
mainView: (_) =>
DiscoverPage(alias: parts.length == 3 ? parts[2] : null),
emptyView: (_) => EmptyPage(),
);
case 'logs':
return ViewData(
mainView: (_) => LogViewer(),
);
case 'newgroup':
return ViewData(
leftView: (_) => ChatList(),
mainView: (_) => NewGroup(),
);
case 'newprivatechat':
return ViewData(
leftView: (_) => ChatList(),
mainView: (_) => NewPrivateChat(),
);
case 'settings':
if (parts.length == 3) {
final action = parts[2];
switch (action) {
case '3pid':
return ViewData(
leftView: (_) => Settings(),
mainView: (_) => Settings3Pid(),
);
case 'devices':
return ViewData(
leftView: (_) => Settings(),
mainView: (_) => DevicesSettings(),
);
case 'emotes':
return ViewData(
leftView: (_) => Settings(),
mainView: (_) => EmotesSettings(room: settings.arguments),
);
case 'ignore':
return ViewData(
leftView: (_) => Settings(),
mainView: (_) => SettingsIgnoreList(),
);
case 'notifications':
return ViewData(
leftView: (_) => Settings(),
mainView: (_) => SettingsNotifications(),
);
case 'style':
return ViewData(
leftView: (_) => Settings(),
mainView: (_) => SettingsStyle(),
);
}
}
return ViewData(
mainView: (_) => Settings(),
emptyView: (_) => EmptyPage(),
);
}
}
// If route cant be found:
return ViewData(
mainView: (_) => Center(
child: Text('Route "${settings.name}" not found...'),
),
);
}
}
class SettingsDevices {}
class FadeRoute extends PageRouteBuilder {
final Widget page;
FadeRoute({this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>
page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
FadeTransition(
opacity: animation,
child: child,
),
);
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
abstract class FluffyThemes { abstract class FluffyThemes {
static const double columnWidth = 360.0;
static ThemeData light = ThemeData( static ThemeData light = ThemeData(
primaryColorDark: Colors.white, primaryColorDark: Colors.white,
primaryColorLight: Color(0xff121212), primaryColorLight: Color(0xff121212),

View File

@ -2,22 +2,19 @@
import 'dart:async'; import 'dart:async';
import 'package:adaptive_theme/adaptive_theme.dart'; import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:fluffychat/config/routes.dart';
import 'package:fluffychat/utils/sentry_controller.dart'; import 'package:fluffychat/utils/sentry_controller.dart';
import 'package:fluffychat/views/homeserver_picker.dart'; import 'package:flutter/cupertino.dart';
import 'package:flushbar/flushbar_helper.dart';
import 'package:flutter/foundation.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_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:universal_html/prefer_universal/html.dart' as html; import 'package:universal_html/prefer_universal/html.dart' as html;
import 'components/matrix.dart'; import 'components/matrix.dart';
import 'config/themes.dart'; import 'config/themes.dart';
import 'utils/localized_exception_extension.dart';
import 'app_config.dart'; import 'app_config.dart';
import 'views/chat_list.dart';
void main() async { void main() async {
SystemChrome.setSystemUIOverlayStyle( SystemChrome.setSystemUIOverlayStyle(
@ -31,51 +28,38 @@ void main() async {
} }
class App extends StatelessWidget { class App extends StatelessWidget {
final GlobalKey<AdaptivePageLayoutState> _apl =
GlobalKey<AdaptivePageLayoutState>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Matrix( return AdaptiveTheme(
child: Builder( light: FluffyThemes.light,
builder: (BuildContext context) => AdaptiveTheme( dark: FluffyThemes.dark,
light: FluffyThemes.light, initial: AdaptiveThemeMode.system,
dark: FluffyThemes.dark, builder: (theme, darkTheme) => MaterialApp(
initial: AdaptiveThemeMode.system, title: '${AppConfig.applicationName}',
builder: (theme, darkTheme) => MaterialApp( theme: theme,
title: '${AppConfig.applicationName}', darkTheme: darkTheme,
theme: theme, localizationsDelegates: L10n.localizationsDelegates,
darkTheme: darkTheme, supportedLocales: L10n.supportedLocales,
localizationsDelegates: L10n.localizationsDelegates, locale: kIsWeb
supportedLocales: L10n.supportedLocales, ? Locale(html.window.navigator.language.split('-').first)
locale: kIsWeb : null,
? Locale(html.window.navigator.language.split('-').first) home: Builder(
: null, builder: (context) => Matrix(
home: FutureBuilder<LoginState>( context: context,
future: apl: _apl,
Matrix.of(context).client.onLoginStateChanged.stream.first, child: Builder(
builder: (context, snapshot) { builder: (context) => AdaptivePageLayout(
LoadingDialog.defaultTitle = L10n.of(context).loadingPleaseWait; key: _apl,
LoadingDialog.defaultBackLabel = L10n.of(context).close; onGenerateRoute: FluffyRoutes(context).onGenerateRoute,
LoadingDialog.defaultOnError = dividerColor: Theme.of(context).dividerColor,
(Object e) => e.toLocalizedString(context); columnWidth: FluffyThemes.columnWidth,
if (snapshot.hasError) { routeBuilder: (builder, settings) =>
WidgetsBinding.instance _apl.currentState.columnMode(context)
.addPostFrameCallback((_) => FlushbarHelper.createError( ? FadeRoute(page: builder(context))
title: L10n.of(context).oopsSomethingWentWrong, : CupertinoPageRoute(builder: builder),
message: snapshot.error.toString(), ),
).show(context));
return HomeserverPicker();
}
if (!snapshot.hasData) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
if (Matrix.of(context).client.isLogged()) {
return ChatListView();
}
return HomeserverPicker();
},
), ),
), ),
), ),

View File

@ -1,10 +1,11 @@
import 'package:fluffychat/components/adaptive_page_layout.dart'; import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class AppRoute extends PageRouteBuilder { class AppRoute extends PageRouteBuilder {
static Route defaultRoute(BuildContext context, Widget page) { static Route defaultRoute(BuildContext context, Widget page) {
return context != null && !AdaptivePageLayout.columnMode(context) return context != null &&
!AdaptivePageLayout.of(context).columnMode(context)
? CupertinoPageRoute( ? CupertinoPageRoute(
builder: (BuildContext context) => page, builder: (BuildContext context) => page,
) )

View File

@ -7,8 +7,6 @@ import 'package:flushbar/flushbar_helper.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/views/chat.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -112,12 +110,8 @@ abstract class FirebaseController {
roomId = (message['data'] ?? message)['room_id']; roomId = (message['data'] ?? message)['room_id'];
} }
if (roomId?.isEmpty ?? true) throw ('Bad roomId'); if (roomId?.isEmpty ?? true) throw ('Bad roomId');
await Navigator.of(context).pushAndRemoveUntil( await matrix.widget.apl.currentState
AppRoute.defaultRoute( .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
context,
ChatView(roomId),
),
(r) => r.isFirst);
} catch (_) { } catch (_) {
await FlushbarHelper.createError(message: 'Failed to open chat...') await FlushbarHelper.createError(message: 'Failed to open chat...')
.show(context); .show(context);

View File

@ -1,11 +1,9 @@
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/app_config.dart'; import 'package:fluffychat/app_config.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/views/chat.dart';
import 'package:fluffychat/views/discover_view.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -74,12 +72,8 @@ class UrlLauncher {
} }
if (room != null) { if (room != null) {
// we have the room, so....just open it! // we have the room, so....just open it!
await Navigator.pushAndRemoveUntil( await AdaptivePageLayout.of(context)
context, .pushNamedAndRemoveUntilIsFirst('/rooms/${room.id}/$event');
AppRoute.defaultRoute(
context, ChatView(room.id, scrollToEventId: event)),
(r) => r.isFirst,
);
return; return;
} }
if (roomIdOrAlias.sigil == '!') { if (roomIdOrAlias.sigil == '!') {
@ -101,21 +95,12 @@ class UrlLauncher {
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
future: () => Future.delayed(const Duration(seconds: 2))); future: () => Future.delayed(const Duration(seconds: 2)));
await Navigator.pushAndRemoveUntil( await AdaptivePageLayout.of(context).pushNamedAndRemoveUntilIsFirst(
context, '/rooms/${response.result}/$event');
AppRoute.defaultRoute(
context, ChatView(response.result, scrollToEventId: event)),
(r) => r.isFirst,
);
} }
} else { } else {
await Navigator.of(context).pushAndRemoveUntil( await AdaptivePageLayout.of(context)
AppRoute.defaultRoute( .pushNamedAndRemoveUntilIsFirst('/discover/${roomIdOrAlias}');
context,
DiscoverView(alias: roomIdOrAlias),
),
(r) => r.isFirst,
);
} }
} else if (identityParts.primaryIdentifier.sigil == '@') { } else if (identityParts.primaryIdentifier.sigil == '@') {
final user = User( final user = User(
@ -124,11 +109,9 @@ class UrlLauncher {
); );
var roomId = matrix.client.getDirectChatFromUserId(user.id); var roomId = matrix.client.getDirectChatFromUserId(user.id);
if (roomId != null) { if (roomId != null) {
await Navigator.pushAndRemoveUntil( await AdaptivePageLayout.of(context)
context, .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
AppRoute.defaultRoute(context, ChatView(roomId)),
(r) => r.isFirst,
);
return; return;
} }
@ -142,14 +125,10 @@ class UrlLauncher {
future: () => user.startDirectChat(), future: () => user.startDirectChat(),
)) ))
.result; .result;
Navigator.of(context).pop();
if (roomId != null) { if (roomId != null) {
await Navigator.pushAndRemoveUntil( await AdaptivePageLayout.of(context)
context, .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
AppRoute.defaultRoute(context, ChatView(roomId)),
(r) => r.isFirst,
);
} }
} }
} }

View File

@ -1,5 +1,4 @@
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/list_items/chat_list_item.dart'; import 'package:fluffychat/components/list_items/chat_list_item.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -35,36 +34,28 @@ class _ArchiveState extends State<Archive> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AdaptivePageLayout( return Scaffold(
firstScaffold: Scaffold( appBar: AppBar(
appBar: AppBar( title: Text(L10n.of(context).archive),
title: Text(L10n.of(context).archive), elevation: _scrolledToTop ? 0 : null,
elevation: _scrolledToTop ? 0 : null,
),
body: FutureBuilder<List<Room>>(
future: getArchive(context),
builder: (BuildContext context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
archive = snapshot.data;
return ListView.builder(
controller: _scrollController,
itemCount: archive.length,
itemBuilder: (BuildContext context, int i) => ChatListItem(
archive[i],
onForget: () => setState(() => archive.removeAt(i))),
);
}
},
),
), ),
secondScaffold: Scaffold( body: FutureBuilder<List<Room>>(
body: Center( future: getArchive(context),
child: Image.asset('assets/logo.png', width: 100, height: 100), builder: (BuildContext context, snapshot) {
), if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
archive = snapshot.data;
return ListView.builder(
controller: _scrollController,
itemCount: archive.length,
itemBuilder: (BuildContext context, int i) => ChatListItem(
archive[i],
onForget: () => setState(() => archive.removeAt(i))),
);
}
},
), ),
primaryPage: FocusPage.FIRST,
); );
} }
} }

View File

@ -1,3 +1,4 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -23,7 +24,7 @@ class AuthWebView extends StatelessWidget {
leading: IconButton( leading: IconButton(
icon: Icon(Icons.close), icon: Icon(Icons.close),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); AdaptivePageLayout.of(context).pop();
onAuthDone(); onAuthDone();
}, },
), ),

View File

@ -3,14 +3,15 @@ import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:emoji_picker/emoji_picker.dart'; import 'package:emoji_picker/emoji_picker.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:file_picker_cross/file_picker_cross.dart'; import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/avatar.dart'; import 'package:fluffychat/components/avatar.dart';
import 'package:fluffychat/components/chat_settings_popup_menu.dart'; import 'package:fluffychat/components/chat_settings_popup_menu.dart';
import 'package:fluffychat/components/connection_status_header.dart'; import 'package:fluffychat/components/connection_status_header.dart';
import 'package:fluffychat/components/dialogs/recording_dialog.dart'; import 'package:fluffychat/components/dialogs/recording_dialog.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:flushbar/flushbar_helper.dart'; import 'package:flushbar/flushbar_helper.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/encryption_button.dart'; import 'package:fluffychat/components/encryption_button.dart';
@ -19,7 +20,6 @@ import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/components/reply_content.dart'; import 'package:fluffychat/components/reply_content.dart';
import 'package:fluffychat/components/user_bottom_sheet.dart'; import 'package:fluffychat/components/user_bottom_sheet.dart';
import 'package:fluffychat/config/app_emojis.dart'; import 'package:fluffychat/config/app_emojis.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/utils/matrix_locals.dart'; import 'package:fluffychat/utils/matrix_locals.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/room_status_extension.dart'; import 'package:fluffychat/utils/room_status_extension.dart';
@ -38,39 +38,19 @@ import '../components/dialogs/send_file_dialog.dart';
import '../components/input_bar.dart'; import '../components/input_bar.dart';
import '../utils/filtered_timeline_extension.dart'; import '../utils/filtered_timeline_extension.dart';
import '../utils/matrix_file_extension.dart'; import '../utils/matrix_file_extension.dart';
import 'chat_details.dart';
import 'chat_list.dart';
class ChatView extends StatelessWidget { class Chat extends StatefulWidget {
final String id; final String id;
final String scrollToEventId; final String scrollToEventId;
const ChatView(this.id, {Key key, this.scrollToEventId}) : super(key: key); Chat(this.id, {Key key, this.scrollToEventId})
: super(key: key ?? Key('chatroom-$id'));
@override
Widget build(BuildContext context) {
// TODO: implement build
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(
activeChat: id,
),
secondScaffold: _Chat(id, scrollToEventId: scrollToEventId),
);
}
}
class _Chat extends StatefulWidget {
final String id;
final String scrollToEventId;
const _Chat(this.id, {Key key, this.scrollToEventId}) : super(key: key);
@override @override
_ChatState createState() => _ChatState(); _ChatState createState() => _ChatState();
} }
class _ChatState extends State<_Chat> { class _ChatState extends State<Chat> {
Room room; Room room;
Timeline timeline; Timeline timeline;
@ -343,7 +323,7 @@ class _ChatState extends State<_Chat> {
}; };
} }
setState(() => selectedEvents.clear()); setState(() => selectedEvents.clear());
Navigator.of(context).popUntil((r) => r.isFirst); AdaptivePageLayout.of(context).popUntilIsFirst();
} }
void sendAgainAction(Timeline timeline) { void sendAgainAction(Timeline timeline) {
@ -509,12 +489,8 @@ class _ChatState extends State<_Chat> {
'${room.directChatMatrixID} ', '${room.directChatMatrixID} ',
), ),
) )
: () => Navigator.of(context).push( : () => AdaptivePageLayout.of(context)
AppRoute.defaultRoute( .pushNamed('/rooms/${room.id}/details'),
context,
ChatDetails(room),
),
),
title: Text( title: Text(
room.getLocalizedDisplayname( room.getLocalizedDisplayname(
MatrixLocals(L10n.of(context))), MatrixLocals(L10n.of(context))),
@ -643,8 +619,7 @@ class _ChatState extends State<_Chat> {
horizontal: max( horizontal: max(
0, 0,
(MediaQuery.of(context).size.width - (MediaQuery.of(context).size.width -
AdaptivePageLayout.defaultMinWidth * FluffyThemes.columnWidth * 3.5) /
3.5) /
2), 2),
), ),
reverse: true, reverse: true,

View File

@ -1,40 +1,36 @@
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:fluffychat/app_config.dart'; import 'package:fluffychat/app_config.dart';
import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/views/chat_permissions_settings.dart';
import 'package:flushbar/flushbar_helper.dart'; import 'package:flushbar/flushbar_helper.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:file_picker_cross/file_picker_cross.dart'; import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/chat_settings_popup_menu.dart'; import 'package:fluffychat/components/chat_settings_popup_menu.dart';
import 'package:fluffychat/components/content_banner.dart'; import 'package:fluffychat/components/content_banner.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/list_items/participant_list_item.dart'; import 'package:fluffychat/components/list_items/participant_list_item.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/utils/matrix_locals.dart'; import 'package:fluffychat/utils/matrix_locals.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/views/chat_list.dart';
import 'package:fluffychat/views/invitation_selection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:matrix_link_text/link_text.dart'; import 'package:matrix_link_text/link_text.dart';
import './settings_emotes.dart';
import './settings_multiple_emotes.dart';
import '../utils/url_launcher.dart'; import '../utils/url_launcher.dart';
class ChatDetails extends StatefulWidget { class ChatDetails extends StatefulWidget {
final Room room; final String roomId;
const ChatDetails(this.room); const ChatDetails(this.roomId);
@override @override
_ChatDetailsState createState() => _ChatDetailsState(); _ChatDetailsState createState() => _ChatDetailsState();
} }
class _ChatDetailsState extends State<ChatDetails> { class _ChatDetailsState extends State<ChatDetails> {
Room room;
List<User> members; List<User> members;
void setDisplaynameAction(BuildContext context) async { void setDisplaynameAction(BuildContext context) async {
final input = await showTextInputDialog( final input = await showTextInputDialog(
@ -42,7 +38,7 @@ class _ChatDetailsState extends State<ChatDetails> {
title: L10n.of(context).changeTheNameOfTheGroup, title: L10n.of(context).changeTheNameOfTheGroup,
textFields: [ textFields: [
DialogTextField( DialogTextField(
initialText: widget.room.getLocalizedDisplayname( initialText: room.getLocalizedDisplayname(
MatrixLocals( MatrixLocals(
L10n.of(context), L10n.of(context),
), ),
@ -53,7 +49,7 @@ class _ChatDetailsState extends State<ChatDetails> {
if (input == null) return; if (input == null) return;
final success = await showFutureLoadingDialog( final success = await showFutureLoadingDialog(
context: context, context: context,
future: () => widget.room.setName(input.single), future: () => room.setName(input.single),
); );
if (success.error == null) { if (success.error == null) {
await FlushbarHelper.createSuccess( await FlushbarHelper.createSuccess(
@ -74,9 +70,9 @@ class _ChatDetailsState extends State<ChatDetails> {
], ],
); );
if (input == null) return; if (input == null) return;
final domain = widget.room.client.userID.domain; final domain = room.client.userID.domain;
final canonicalAlias = '%23' + input.single + '%3A' + domain; final canonicalAlias = '%23' + input.single + '%3A' + domain;
final aliasEvent = widget.room.getState('m.room.aliases', domain); final aliasEvent = room.getState('m.room.aliases', domain);
final aliases = final aliases =
aliasEvent != null ? aliasEvent.content['aliases'] ?? [] : []; aliasEvent != null ? aliasEvent.content['aliases'] ?? [] : [];
if (aliases.indexWhere((s) => s == canonicalAlias) == -1) { if (aliases.indexWhere((s) => s == canonicalAlias) == -1) {
@ -84,22 +80,19 @@ class _ChatDetailsState extends State<ChatDetails> {
newAliases.add(canonicalAlias); newAliases.add(canonicalAlias);
final response = await showFutureLoadingDialog( final response = await showFutureLoadingDialog(
context: context, context: context,
future: () => future: () => room.client.requestRoomAliasInformations(canonicalAlias),
widget.room.client.requestRoomAliasInformations(canonicalAlias),
); );
if (response.error != null) { if (response.error != null) {
final success = await showFutureLoadingDialog( final success = await showFutureLoadingDialog(
context: context, context: context,
future: () => widget.room.client future: () => room.client.createRoomAlias(canonicalAlias, room.id),
.createRoomAlias(canonicalAlias, widget.room.id),
); );
if (success.error != null) return; if (success.error != null) return;
} }
} }
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
future: () => widget.room.client future: () => room.client.sendState(room.id, 'm.room.canonical_alias', {
.sendState(widget.room.id, 'm.room.canonical_alias', {
'alias': input.single, 'alias': input.single,
}), }),
); );
@ -112,7 +105,7 @@ class _ChatDetailsState extends State<ChatDetails> {
textFields: [ textFields: [
DialogTextField( DialogTextField(
hintText: L10n.of(context).setGroupDescription, hintText: L10n.of(context).setGroupDescription,
initialText: widget.room.topic, initialText: room.topic,
minLines: 1, minLines: 1,
maxLines: 4, maxLines: 4,
) )
@ -121,7 +114,7 @@ class _ChatDetailsState extends State<ChatDetails> {
if (input == null) return; if (input == null) return;
final success = await showFutureLoadingDialog( final success = await showFutureLoadingDialog(
context: context, context: context,
future: () => widget.room.setDescription(input.single), future: () => room.setDescription(input.single),
); );
if (success.error == null) { if (success.error == null) {
await FlushbarHelper.createSuccess( await FlushbarHelper.createSuccess(
@ -156,7 +149,7 @@ class _ChatDetailsState extends State<ChatDetails> {
final success = await showFutureLoadingDialog( final success = await showFutureLoadingDialog(
context: context, context: context,
future: () => widget.room.setAvatar(file), future: () => room.setAvatar(file),
); );
if (success.error == null) { if (success.error == null) {
await FlushbarHelper.createSuccess( await FlushbarHelper.createSuccess(
@ -167,7 +160,7 @@ class _ChatDetailsState extends State<ChatDetails> {
void requestMoreMembersAction(BuildContext context) async { void requestMoreMembersAction(BuildContext context) async {
final participants = await showFutureLoadingDialog( final participants = await showFutureLoadingDialog(
context: context, future: () => widget.room.requestParticipants()); context: context, future: () => room.requestParticipants());
if (participants.error == null) { if (participants.error == null) {
setState(() => members = participants.result); setState(() => members = participants.result);
} }
@ -175,7 +168,8 @@ class _ChatDetailsState extends State<ChatDetails> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (widget.room == null) { room ??= Matrix.of(context).client.getRoomById(widget.roomId);
if (room == null) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(L10n.of(context).oopsSomethingWentWrong), title: Text(L10n.of(context).oopsSomethingWentWrong),
@ -185,381 +179,344 @@ class _ChatDetailsState extends State<ChatDetails> {
), ),
); );
} }
members ??= widget.room.getParticipants(); members ??= room.getParticipants();
members.removeWhere((u) => u.membership == Membership.leave); members.removeWhere((u) => u.membership == Membership.leave);
final actualMembersCount = final actualMembersCount =
widget.room.mInvitedMemberCount + widget.room.mJoinedMemberCount; room.mInvitedMemberCount + room.mJoinedMemberCount;
final canRequestMoreMembers = members.length < actualMembersCount; final canRequestMoreMembers = members.length < actualMembersCount;
return AdaptivePageLayout( return StreamBuilder(
primaryPage: FocusPage.SECOND, stream: room.onUpdate.stream,
firstScaffold: ChatList( builder: (context, snapshot) {
activeChat: widget.room.id, return Scaffold(
), body: NestedScrollView(
secondScaffold: StreamBuilder( headerSliverBuilder:
stream: widget.room.onUpdate.stream, (BuildContext context, bool innerBoxIsScrolled) => <Widget>[
builder: (context, snapshot) { SliverAppBar(
return Scaffold( expandedHeight: 300.0,
body: NestedScrollView( floating: true,
headerSliverBuilder: pinned: true,
(BuildContext context, bool innerBoxIsScrolled) => <Widget>[ actions: <Widget>[
SliverAppBar( if (room.canonicalAlias?.isNotEmpty ?? false)
expandedHeight: 300.0, IconButton(
floating: true, icon: Icon(Icons.share_outlined),
pinned: true, onPressed: () => FluffyShare.share(
actions: <Widget>[ AppConfig.inviteLinkPrefix + room.canonicalAlias,
if (widget.room.canonicalAlias?.isNotEmpty ?? false) context),
IconButton( ),
icon: Icon(Icons.share_outlined), ChatSettingsPopupMenu(room, false)
onPressed: () => FluffyShare.share( ],
AppConfig.inviteLinkPrefix + title: Text(
widget.room.canonicalAlias, room.getLocalizedDisplayname(
context), MatrixLocals(L10n.of(context))),
), style: TextStyle(
ChatSettingsPopupMenu(widget.room, false) color: Theme.of(context)
], .appBarTheme
title: Text( .textTheme
widget.room.getLocalizedDisplayname( .headline6
MatrixLocals(L10n.of(context))), .color)),
style: TextStyle( backgroundColor: Theme.of(context).appBarTheme.color,
color: Theme.of(context) flexibleSpace: FlexibleSpaceBar(
.appBarTheme background: ContentBanner(room.avatar,
.textTheme onEdit: room.canSendEvent('m.room.avatar')
.headline6 ? () => setAvatarAction(context)
.color)), : null),
backgroundColor: Theme.of(context).appBarTheme.color,
flexibleSpace: FlexibleSpaceBar(
background: ContentBanner(widget.room.avatar,
onEdit: widget.room.canSendEvent('m.room.avatar')
? () => setAvatarAction(context)
: null),
),
), ),
], ),
body: ListView.builder( ],
itemCount: body: ListView.builder(
members.length + 1 + (canRequestMoreMembers ? 1 : 0), itemCount: members.length + 1 + (canRequestMoreMembers ? 1 : 0),
itemBuilder: (BuildContext context, int i) => i == 0 itemBuilder: (BuildContext context, int i) => i == 0
? Column( ? Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[ children: <Widget>[
ListTile( ListTile(
leading: widget.room.canSendEvent('m.room.topic') leading: room.canSendEvent('m.room.topic')
? CircleAvatar( ? CircleAvatar(
backgroundColor: Theme.of(context) backgroundColor: Theme.of(context)
.scaffoldBackgroundColor, .scaffoldBackgroundColor,
foregroundColor: Colors.grey, foregroundColor: Colors.grey,
child: Icon(Icons.edit_outlined), child: Icon(Icons.edit_outlined),
) )
: null, : null,
title: Text( title: Text('${L10n.of(context).groupDescription}:',
'${L10n.of(context).groupDescription}:',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold)),
subtitle: LinkText(
text: widget.room.topic?.isEmpty ?? true
? L10n.of(context).addGroupDescription
: widget.room.topic,
linkStyle: TextStyle(color: Colors.blueAccent),
textStyle: TextStyle(
fontSize: 14,
color: Theme.of(context)
.textTheme
.bodyText2
.color,
),
onLinkTap: (url) =>
UrlLauncher(context, url).launchUrl(),
),
onTap: widget.room.canSendEvent('m.room.topic')
? () => setTopicAction(context)
: null,
),
Divider(thickness: 1),
ListTile(
title: Text(
L10n.of(context).settings,
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold)),
), subtitle: LinkText(
text: room.topic?.isEmpty ?? true
? L10n.of(context).addGroupDescription
: room.topic,
linkStyle: TextStyle(color: Colors.blueAccent),
textStyle: TextStyle(
fontSize: 14,
color:
Theme.of(context).textTheme.bodyText2.color,
),
onLinkTap: (url) =>
UrlLauncher(context, url).launchUrl(),
),
onTap: room.canSendEvent('m.room.topic')
? () => setTopicAction(context)
: null,
),
Divider(thickness: 1),
ListTile(
title: Text(
L10n.of(context).settings,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
), ),
), ),
if (widget.room.canSendEvent('m.room.name')) ),
ListTile( if (room.canSendEvent('m.room.name'))
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.people_outlined),
),
title: Text(
L10n.of(context).changeTheNameOfTheGroup),
subtitle: Text(widget.room
.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)))),
onTap: () => setDisplaynameAction(context),
),
if (widget.room
.canSendEvent('m.room.canonical_alias') &&
widget.room.joinRules == JoinRules.public)
ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.link_outlined),
),
onTap: () => setCanonicalAliasAction(context),
title: Text(L10n.of(context).setInvitationLink),
subtitle: Text(
(widget.room.canonicalAlias?.isNotEmpty ??
false)
? widget.room.canonicalAlias
: L10n.of(context).none),
),
ListTile( ListTile(
leading: CircleAvatar( leading: CircleAvatar(
backgroundColor: backgroundColor:
Theme.of(context).scaffoldBackgroundColor, Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey, foregroundColor: Colors.grey,
child: Icon(Icons.insert_emoticon_outlined), child: Icon(Icons.people_outlined),
), ),
title: Text(L10n.of(context).emoteSettings), title: Text(
subtitle: Text(L10n.of(context).setCustomEmotes), L10n.of(context).changeTheNameOfTheGroup),
onTap: () async { subtitle: Text(room.getLocalizedDisplayname(
// okay, we need to test if there are any emote state events other than the default one MatrixLocals(L10n.of(context)))),
// if so, we need to be directed to a selection screen for which pack we want to look at onTap: () => setDisplaynameAction(context),
// otherwise, we just open the normal one.
if ((widget.room.states
.states['im.ponies.room_emotes'] ??
<String, Event>{})
.keys
.any((String s) => s.isNotEmpty)) {
await Navigator.of(context).push(
AppRoute.defaultRoute(
context,
MultipleEmotesSettingsView(
room: widget.room),
),
);
} else {
await Navigator.of(context).push(
AppRoute.defaultRoute(
context,
EmotesSettingsView(room: widget.room),
),
);
}
},
), ),
if (room.canSendEvent('m.room.canonical_alias') &&
room.joinRules == JoinRules.public)
ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.link_outlined),
),
onTap: () => setCanonicalAliasAction(context),
title: Text(L10n.of(context).setInvitationLink),
subtitle: Text(
(room.canonicalAlias?.isNotEmpty ?? false)
? room.canonicalAlias
: L10n.of(context).none),
),
ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.insert_emoticon_outlined),
),
title: Text(L10n.of(context).emoteSettings),
subtitle: Text(L10n.of(context).setCustomEmotes),
onTap: () async {
// okay, we need to test if there are any emote state events other than the default one
// if so, we need to be directed to a selection screen for which pack we want to look at
// otherwise, we just open the normal one.
if ((room.states
.states['im.ponies.room_emotes'] ??
<String, Event>{})
.keys
.any((String s) => s.isNotEmpty)) {
await AdaptivePageLayout.of(context)
.pushNamed('/rooms/${room.id}/emotes');
} else {
await AdaptivePageLayout.of(context)
.pushNamed('/settings/emotes');
}
},
),
PopupMenuButton(
child: ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.public_outlined)),
title: Text(
L10n.of(context).whoIsAllowedToJoinThisGroup),
subtitle: Text(
room.joinRules.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
onSelected: (JoinRules joinRule) =>
showFutureLoadingDialog(
context: context,
future: () => room.setJoinRules(joinRule),
),
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<JoinRules>>[
if (room.canChangeJoinRules)
PopupMenuItem<JoinRules>(
value: JoinRules.public,
child: Text(JoinRules.public
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
if (room.canChangeJoinRules)
PopupMenuItem<JoinRules>(
value: JoinRules.invite,
child: Text(JoinRules.invite
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
],
),
PopupMenuButton(
child: ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.visibility_outlined),
),
title: Text(
L10n.of(context).visibilityOfTheChatHistory),
subtitle: Text(
room.historyVisibility.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
onSelected: (HistoryVisibility historyVisibility) =>
showFutureLoadingDialog(
context: context,
future: () =>
room.setHistoryVisibility(historyVisibility),
),
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<HistoryVisibility>>[
if (room.canChangeHistoryVisibility)
PopupMenuItem<HistoryVisibility>(
value: HistoryVisibility.invited,
child: Text(HistoryVisibility.invited
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
if (room.canChangeHistoryVisibility)
PopupMenuItem<HistoryVisibility>(
value: HistoryVisibility.joined,
child: Text(HistoryVisibility.joined
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
if (room.canChangeHistoryVisibility)
PopupMenuItem<HistoryVisibility>(
value: HistoryVisibility.shared,
child: Text(HistoryVisibility.shared
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
if (room.canChangeHistoryVisibility)
PopupMenuItem<HistoryVisibility>(
value: HistoryVisibility.world_readable,
child: Text(HistoryVisibility.world_readable
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
],
),
if (room.joinRules == JoinRules.public)
PopupMenuButton( PopupMenuButton(
child: ListTile( child: ListTile(
leading: CircleAvatar( leading: CircleAvatar(
backgroundColor: Theme.of(context) backgroundColor:
.scaffoldBackgroundColor, Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey, foregroundColor: Colors.grey,
child: Icon(Icons.public_outlined)), child: Icon(Icons.info_outline),
title: Text(L10n.of(context) ),
.whoIsAllowedToJoinThisGroup), title: Text(
L10n.of(context).areGuestsAllowedToJoin),
subtitle: Text( subtitle: Text(
widget.room.joinRules.getLocalizedString( room.guestAccess.getLocalizedString(
MatrixLocals(L10n.of(context))), MatrixLocals(L10n.of(context))),
), ),
), ),
onSelected: (JoinRules joinRule) => onSelected: (GuestAccess guestAccess) =>
showFutureLoadingDialog( showFutureLoadingDialog(
context: context, context: context,
future: () => future: () => room.setGuestAccess(guestAccess),
widget.room.setJoinRules(joinRule),
), ),
itemBuilder: (BuildContext context) => itemBuilder: (BuildContext context) =>
<PopupMenuEntry<JoinRules>>[ <PopupMenuEntry<GuestAccess>>[
if (widget.room.canChangeJoinRules) if (room.canChangeGuestAccess)
PopupMenuItem<JoinRules>( PopupMenuItem<GuestAccess>(
value: JoinRules.public, value: GuestAccess.can_join,
child: Text(JoinRules.public child: Text(
.getLocalizedString( GuestAccess.can_join.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
if (widget.room.canChangeJoinRules)
PopupMenuItem<JoinRules>(
value: JoinRules.invite,
child: Text(JoinRules.invite
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
],
),
PopupMenuButton(
child: ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.visibility_outlined),
),
title: Text(L10n.of(context)
.visibilityOfTheChatHistory),
subtitle: Text(
widget.room.historyVisibility
.getLocalizedString(
MatrixLocals(L10n.of(context))), MatrixLocals(L10n.of(context))),
), ),
),
onSelected:
(HistoryVisibility historyVisibility) =>
showFutureLoadingDialog(
context: context,
future: () => widget.room
.setHistoryVisibility(historyVisibility),
),
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<HistoryVisibility>>[
if (widget.room.canChangeHistoryVisibility)
PopupMenuItem<HistoryVisibility>(
value: HistoryVisibility.invited,
child: Text(HistoryVisibility.invited
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
), ),
if (widget.room.canChangeHistoryVisibility) if (room.canChangeGuestAccess)
PopupMenuItem<HistoryVisibility>( PopupMenuItem<GuestAccess>(
value: HistoryVisibility.joined, value: GuestAccess.forbidden,
child: Text(HistoryVisibility.joined child: Text(
.getLocalizedString( GuestAccess.forbidden.getLocalizedString(
MatrixLocals(L10n.of(context)))), MatrixLocals(L10n.of(context))),
), ),
if (widget.room.canChangeHistoryVisibility)
PopupMenuItem<HistoryVisibility>(
value: HistoryVisibility.shared,
child: Text(HistoryVisibility.shared
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
if (widget.room.canChangeHistoryVisibility)
PopupMenuItem<HistoryVisibility>(
value: HistoryVisibility.world_readable,
child: Text(HistoryVisibility.world_readable
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
), ),
], ],
), ),
if (widget.room.joinRules == JoinRules.public) ListTile(
PopupMenuButton( title: Text(L10n.of(context).editChatPermissions),
child: ListTile( subtitle:
Text(L10n.of(context).whoCanPerformWhichAction),
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.edit_attributes_outlined),
),
onTap: () => AdaptivePageLayout.of(context)
.pushNamed('/rooms/${room.id}/permissions'),
),
Divider(thickness: 1),
ListTile(
title: Text(
actualMembersCount > 1
? L10n.of(context).countParticipants(
actualMembersCount.toString())
: L10n.of(context).emptyChat,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
),
),
),
room.canInvite
? ListTile(
title: Text(L10n.of(context).inviteContact),
leading: CircleAvatar( leading: CircleAvatar(
backgroundColor: Theme.of(context) child: Icon(Icons.add_outlined),
.scaffoldBackgroundColor, backgroundColor:
foregroundColor: Colors.grey, Theme.of(context).primaryColor,
child: Icon(Icons.info_outline), foregroundColor: Colors.white,
), ),
title: Text( onTap: () => AdaptivePageLayout.of(context)
L10n.of(context).areGuestsAllowedToJoin), .pushNamed('/rooms/${room.id}/invite'),
subtitle: Text( )
widget.room.guestAccess.getLocalizedString( : Container(),
MatrixLocals(L10n.of(context))), ],
), )
), : i < members.length + 1
onSelected: (GuestAccess guestAccess) => ? ParticipantListItem(members[i - 1])
showFutureLoadingDialog( : ListTile(
context: context, title: Text(L10n.of(context)
future: () => .loadCountMoreParticipants(
widget.room.setGuestAccess(guestAccess), (actualMembersCount - members.length)
), .toString())),
itemBuilder: (BuildContext context) => leading: CircleAvatar(
<PopupMenuEntry<GuestAccess>>[ backgroundColor:
if (widget.room.canChangeGuestAccess) Theme.of(context).scaffoldBackgroundColor,
PopupMenuItem<GuestAccess>( child: Icon(
value: GuestAccess.can_join, Icons.refresh,
child: Text( color: Colors.grey,
GuestAccess.can_join.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
if (widget.room.canChangeGuestAccess)
PopupMenuItem<GuestAccess>(
value: GuestAccess.forbidden,
child: Text(
GuestAccess.forbidden
.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
],
),
ListTile(
title: Text(L10n.of(context).editChatPermissions),
subtitle: Text(
L10n.of(context).whoCanPerformWhichAction),
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.edit_attributes_outlined),
),
onTap: () => Navigator.of(context).push(
AppRoute.defaultRoute(
context,
ChatPermissionsSettingsView(
roomId: widget.room.id),
),
), ),
), ),
Divider(thickness: 1), onTap: () => requestMoreMembersAction(context),
ListTile( ),
title: Text(
actualMembersCount > 1
? L10n.of(context).countParticipants(
actualMembersCount.toString())
: L10n.of(context).emptyChat,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold,
),
),
),
widget.room.canInvite
? ListTile(
title: Text(L10n.of(context).inviteContact),
leading: CircleAvatar(
child: Icon(Icons.add_outlined),
backgroundColor:
Theme.of(context).primaryColor,
foregroundColor: Colors.white,
),
onTap: () => Navigator.of(context).push(
AppRoute.defaultRoute(
context,
InvitationSelection(widget.room),
),
),
)
: Container(),
],
)
: i < members.length + 1
? ParticipantListItem(members[i - 1])
: ListTile(
title: Text(L10n.of(context)
.loadCountMoreParticipants(
(actualMembersCount - members.length)
.toString())),
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
child: Icon(
Icons.refresh,
color: Colors.grey,
),
),
onTap: () => requestMoreMembersAction(context),
),
),
), ),
); ),
}), );
); });
} }
} }

View File

@ -1,32 +1,14 @@
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:famedlysdk/encryption.dart'; import 'package:famedlysdk/encryption.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/avatar.dart'; import 'package:fluffychat/components/avatar.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/utils/beautify_string_extension.dart'; import 'package:fluffychat/utils/beautify_string_extension.dart';
import 'package:fluffychat/views/chat_list.dart';
import 'package:flushbar/flushbar_helper.dart'; import 'package:flushbar/flushbar_helper.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../components/dialogs/key_verification_dialog.dart'; import '../components/dialogs/key_verification_dialog.dart';
class ChatEncryptionSettingsView extends StatelessWidget {
final String id;
const ChatEncryptionSettingsView(this.id, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
firstScaffold: ChatList(
activeChat: id,
),
secondScaffold: ChatEncryptionSettings(id),
primaryPage: FocusPage.SECOND,
);
}
}
class ChatEncryptionSettings extends StatefulWidget { class ChatEncryptionSettings extends StatefulWidget {
final String id; final String id;

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/connection_status_header.dart'; import 'package:fluffychat/components/connection_status_header.dart';
import 'package:fluffychat/components/default_app_bar_search_field.dart'; import 'package:fluffychat/components/default_app_bar_search_field.dart';
@ -14,29 +15,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import '../components/adaptive_page_layout.dart';
import '../components/list_items/chat_list_item.dart'; import '../components/list_items/chat_list_item.dart';
import '../components/matrix.dart'; import '../components/matrix.dart';
import '../utils/app_route.dart';
import '../utils/matrix_file_extension.dart'; import '../utils/matrix_file_extension.dart';
import '../utils/url_launcher.dart'; import '../utils/url_launcher.dart';
import 'empty_page.dart';
import 'homeserver_picker.dart';
import 'new_private_chat.dart';
enum SelectMode { normal, share, select } enum SelectMode { normal, share, select }
class ChatListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
primaryPage: FocusPage.FIRST,
firstScaffold: ChatList(),
secondScaffold: EmptyPage(),
);
}
}
class ChatList extends StatefulWidget { class ChatList extends StatefulWidget {
final String activeChat; final String activeChat;
@ -86,9 +71,7 @@ class _ChatListState extends State<ChatList> {
void _processIncomingSharedFiles(List<SharedMediaFile> files) { void _processIncomingSharedFiles(List<SharedMediaFile> files) {
if (files?.isEmpty ?? true) return; if (files?.isEmpty ?? true) return;
if (Navigator.of(context).canPop()) { AdaptivePageLayout.of(context).popUntilIsFirst();
Navigator.of(context).popUntil((r) => r.isFirst);
}
final file = File(files.first.path); final file = File(files.first.path);
Matrix.of(context).shareContent = { Matrix.of(context).shareContent = {
@ -102,9 +85,7 @@ class _ChatListState extends State<ChatList> {
void _processIncomingSharedText(String text) { void _processIncomingSharedText(String text) {
if (text == null) return; if (text == null) return;
if (Navigator.of(context).canPop()) { AdaptivePageLayout.of(context).popUntilIsFirst();
Navigator.of(context).popUntil((r) => r.isFirst);
}
if (text.toLowerCase().startsWith(AppConfig.inviteLinkPrefix) || if (text.toLowerCase().startsWith(AppConfig.inviteLinkPrefix) ||
(text.toLowerCase().startsWith(AppConfig.schemePrefix) && (text.toLowerCase().startsWith(AppConfig.schemePrefix) &&
!RegExp(r'\s').hasMatch(text))) { !RegExp(r'\s').hasMatch(text))) {
@ -194,205 +175,178 @@ class _ChatListState extends State<ChatList> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StreamBuilder<LoginState>( return StreamBuilder(
stream: Matrix.of(context).client.onLoginStateChanged.stream, stream: Matrix.of(context).onShareContentChanged.stream,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.data == LoginState.loggedOut) { final selectMode = Matrix.of(context).shareContent == null
Timer(Duration(seconds: 1), () { ? _selectedRoomIds.isEmpty
Matrix.of(context).clean(); ? SelectMode.normal
Navigator.of(context).pushAndRemoveUntil( : SelectMode.select
AppRoute.defaultRoute(context, HomeserverPicker()), : SelectMode.share;
(r) => false); if (selectMode == SelectMode.share) {
}); _selectedRoomIds.clear();
} }
return StreamBuilder( Room selectedRoom;
stream: Matrix.of(context).onShareContentChanged.stream, if (_selectedRoomIds.length == 1) {
builder: (context, snapshot) { selectedRoom =
final selectMode = Matrix.of(context).shareContent == null Matrix.of(context).client.getRoomById(_selectedRoomIds.single);
? _selectedRoomIds.isEmpty }
? SelectMode.normal return Scaffold(
: SelectMode.select drawer: selectMode != SelectMode.normal ? null : DefaultDrawer(),
: SelectMode.share; appBar: AppBar(
if (selectMode == SelectMode.share) { centerTitle: false,
_selectedRoomIds.clear(); elevation: _scrolledToTop ? 0 : null,
} leading: selectMode == SelectMode.share
Room selectedRoom; ? IconButton(
if (_selectedRoomIds.length == 1) { icon: Icon(Icons.close),
selectedRoom = Matrix.of(context) onPressed: () => Matrix.of(context).shareContent = null,
.client )
.getRoomById(_selectedRoomIds.single); : selectMode == SelectMode.select
} ? IconButton(
return Scaffold( icon: Icon(Icons.close),
drawer: onPressed: () => setState(_selectedRoomIds.clear),
selectMode != SelectMode.normal ? null : DefaultDrawer(), )
appBar: AppBar( : null,
centerTitle: false, titleSpacing: 0,
elevation: _scrolledToTop ? 0 : null, actions: selectMode != SelectMode.select
leading: selectMode == SelectMode.share ? null
? IconButton( : [
icon: Icon(Icons.close), if (_selectedRoomIds.length == 1)
onPressed: () => IconButton(
Matrix.of(context).shareContent = null, tooltip: L10n.of(context).toggleUnread,
) icon: Icon(selectedRoom.isUnread
: selectMode == SelectMode.select ? Icons.mark_chat_read_outlined
? IconButton( : Icons.mark_chat_unread_outlined),
icon: Icon(Icons.close), onPressed: () => _toggleUnread(context),
onPressed: () =>
setState(_selectedRoomIds.clear),
)
: null,
titleSpacing: 0,
actions: selectMode != SelectMode.select
? null
: [
if (_selectedRoomIds.length == 1)
IconButton(
tooltip: L10n.of(context).toggleUnread,
icon: Icon(selectedRoom.isUnread
? Icons.mark_chat_read_outlined
: Icons.mark_chat_unread_outlined),
onPressed: () => _toggleUnread(context),
),
if (_selectedRoomIds.length == 1)
IconButton(
tooltip: L10n.of(context).toggleFavorite,
icon: Icon(Icons.push_pin_outlined),
onPressed: () => _toggleFavouriteRoom(context),
),
if (_selectedRoomIds.length == 1)
IconButton(
icon: Icon(selectedRoom.pushRuleState ==
PushRuleState.notify
? Icons.notifications_off_outlined
: Icons.notifications_outlined),
tooltip: L10n.of(context).toggleMuted,
onPressed: () => _toggleMuted(context),
),
IconButton(
icon: Icon(Icons.archive_outlined),
tooltip: L10n.of(context).archive,
onPressed: () => _archiveAction(context),
),
],
title: selectMode == SelectMode.share
? Text(L10n.of(context).share)
: selectMode == SelectMode.select
? Text(_selectedRoomIds.length.toString())
: DefaultAppBarSearchField(
searchController: searchController,
hintText: L10n.of(context).searchForAChat,
onChanged: (_) => setState(() => null),
),
),
floatingActionButton: AdaptivePageLayout.columnMode(context)
? null
: FloatingActionButton(
child: Icon(Icons.add_outlined),
backgroundColor: Theme.of(context).primaryColor,
onPressed: () => Navigator.of(context)
.pushAndRemoveUntil(
AppRoute.defaultRoute(
context, NewPrivateChatView()),
(r) => r.isFirst),
), ),
body: Column( if (_selectedRoomIds.length == 1)
children: [ IconButton(
ConnectionStatusHeader(), tooltip: L10n.of(context).toggleFavorite,
Expanded( icon: Icon(Icons.push_pin_outlined),
child: StreamBuilder( onPressed: () => _toggleFavouriteRoom(context),
stream: Matrix.of(context) ),
.client if (_selectedRoomIds.length == 1)
.onSync IconButton(
.stream icon: Icon(
.where((s) => s.hasRoomUpdate), selectedRoom.pushRuleState == PushRuleState.notify
builder: (context, snapshot) { ? Icons.notifications_off_outlined
return FutureBuilder<void>( : Icons.notifications_outlined),
future: waitForFirstSync(context), tooltip: L10n.of(context).toggleMuted,
builder: (BuildContext context, snapshot) { onPressed: () => _toggleMuted(context),
if (snapshot.hasData) { ),
var rooms = List<Room>.from( IconButton(
Matrix.of(context).client.rooms); icon: Icon(Icons.archive_outlined),
rooms.removeWhere((Room room) => tooltip: L10n.of(context).archive,
room.lastEvent == null || onPressed: () => _archiveAction(context),
(searchMode &&
!room.displayname
.toLowerCase()
.contains(searchController.text
.toLowerCase() ??
'')));
if (rooms.isEmpty && (!searchMode)) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
searchMode
? Icons.search_outlined
: Icons.chat_bubble_outline,
size: 80,
color: Colors.grey,
),
Text(searchMode
? L10n.of(context).noRoomsFound
: L10n.of(context)
.startYourFirstChat),
],
),
);
}
final totalCount = rooms.length;
return ListView.separated(
controller: _scrollController,
separatorBuilder: (BuildContext context,
int i) =>
i == totalCount
? ListTile(
title: Text(
L10n.of(context)
.publicRooms +
':',
style: TextStyle(
fontWeight:
FontWeight.bold,
color: Theme.of(context)
.primaryColor,
),
),
)
: Container(),
itemCount: totalCount,
itemBuilder:
(BuildContext context, int i) =>
ChatListItem(
rooms[i],
selected: _selectedRoomIds
.contains(rooms[i].id),
onTap: selectMode == SelectMode.select
? () =>
_toggleSelection(rooms[i].id)
: null,
onLongPress: selectMode !=
SelectMode.share
? () =>
_toggleSelection(rooms[i].id)
: null,
activeChat:
widget.activeChat == rooms[i].id,
),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}),
), ),
], ],
), title: selectMode == SelectMode.share
); ? Text(L10n.of(context).share)
}); : selectMode == SelectMode.select
? Text(_selectedRoomIds.length.toString())
: DefaultAppBarSearchField(
searchController: searchController,
hintText: L10n.of(context).searchForAChat,
onChanged: (_) => setState(() => null),
),
),
floatingActionButton:
AdaptivePageLayout.of(context).columnMode(context)
? null
: FloatingActionButton(
child: Icon(Icons.add_outlined),
backgroundColor: Theme.of(context).primaryColor,
onPressed: () => AdaptivePageLayout.of(context)
.pushNamedAndRemoveUntilIsFirst('/newprivatechat'),
),
body: Column(
children: [
ConnectionStatusHeader(),
Expanded(
child: StreamBuilder(
stream: Matrix.of(context)
.client
.onSync
.stream
.where((s) => s.hasRoomUpdate),
builder: (context, snapshot) {
return FutureBuilder<void>(
future: waitForFirstSync(context),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
var rooms = List<Room>.from(
Matrix.of(context).client.rooms);
rooms.removeWhere((Room room) =>
room.lastEvent == null ||
(searchMode &&
!room.displayname.toLowerCase().contains(
searchController.text.toLowerCase() ??
'')));
if (rooms.isEmpty && (!searchMode)) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
searchMode
? Icons.search_outlined
: Icons.chat_bubble_outline,
size: 80,
color: Colors.grey,
),
Text(searchMode
? L10n.of(context).noRoomsFound
: L10n.of(context)
.startYourFirstChat),
],
),
);
}
final totalCount = rooms.length;
return ListView.separated(
controller: _scrollController,
separatorBuilder:
(BuildContext context, int i) =>
i == totalCount
? ListTile(
title: Text(
L10n.of(context).publicRooms +
':',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryColor,
),
),
)
: Container(),
itemCount: totalCount,
itemBuilder: (BuildContext context, int i) =>
ChatListItem(
rooms[i],
selected:
_selectedRoomIds.contains(rooms[i].id),
onTap: selectMode == SelectMode.select
? () => _toggleSelection(rooms[i].id)
: null,
onLongPress: selectMode != SelectMode.share
? () => _toggleSelection(rooms[i].id)
: null,
activeChat: widget.activeChat == rooms[i].id,
),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}),
),
],
),
);
}); });
} }
} }

View File

@ -1,6 +1,5 @@
import 'dart:developer'; import 'dart:developer';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/dialogs/permission_slider_dialog.dart'; import 'package:fluffychat/components/dialogs/permission_slider_dialog.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
@ -9,29 +8,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'chat_list.dart';
class ChatPermissionsSettingsView extends StatelessWidget {
final String roomId;
const ChatPermissionsSettingsView({Key key, this.roomId}) : super(key: key);
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
firstScaffold: ChatList(
activeChat: roomId,
),
secondScaffold: ChatPermissionsSettings(roomId: roomId),
primaryPage: FocusPage.SECOND,
);
}
}
class ChatPermissionsSettings extends StatelessWidget { class ChatPermissionsSettings extends StatelessWidget {
final String roomId; final String roomId;
const ChatPermissionsSettings({Key key, @required this.roomId}) const ChatPermissionsSettings(this.roomId, {Key key}) : super(key: key);
: super(key: key);
void _editPowerLevel(BuildContext context, String key, int currentLevel, void _editPowerLevel(BuildContext context, String key, int currentLevel,
{String category}) async { {String category}) async {

View File

@ -1,32 +1,15 @@
import 'dart:async'; import 'dart:async';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/avatar.dart'; import 'package:fluffychat/components/avatar.dart';
import 'package:fluffychat/components/default_app_bar_search_field.dart'; import 'package:fluffychat/components/default_app_bar_search_field.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/views/chat.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'empty_page.dart';
class DiscoverView extends StatelessWidget {
final String alias;
const DiscoverView({Key key, this.alias}) : super(key: key);
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
firstScaffold: DiscoverPage(alias: alias),
secondScaffold: EmptyPage(),
);
}
}
class DiscoverPage extends StatefulWidget { class DiscoverPage extends StatefulWidget {
final String alias; final String alias;
@ -110,12 +93,8 @@ class _DiscoverPageState extends State<DiscoverPage> {
), ),
); );
if (success.error == null) { if (success.error == null) {
await Navigator.of(context).push( await AdaptivePageLayout.of(context)
AppRoute.defaultRoute( .pushNamedAndRemoveUntilIsFirst('/rooms/${success.result}');
context,
ChatView(success.result),
),
);
} }
} }

View File

@ -1,19 +1,16 @@
import 'dart:math'; import 'dart:math';
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/app_config.dart'; import 'package:fluffychat/app_config.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/utils/sentry_controller.dart'; import 'package:fluffychat/utils/sentry_controller.dart';
import 'package:fluffychat/views/sign_up.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'login.dart';
class HomeserverPicker extends StatelessWidget { class HomeserverPicker extends StatelessWidget {
Future<void> _setHomeserverAction(BuildContext context) async { Future<void> _setHomeserverAction(BuildContext context) async {
const prefix = 'https://'; const prefix = 'https://';
@ -45,8 +42,8 @@ class HomeserverPicker extends StatelessWidget {
context: context, context: context,
future: () => checkHomeserver(homeserver, Matrix.of(context).client)); future: () => checkHomeserver(homeserver, Matrix.of(context).client));
if (success.result == true) { if (success.result == true) {
await Navigator.of(context) await AdaptivePageLayout.of(context)
.push(AppRoute(AppConfig.enableRegistration ? SignUp() : Login())); .pushNamed(AppConfig.enableRegistration ? '/signup' : '/login');
} }
} }

View File

@ -1,3 +1,4 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/image_bubble.dart'; import 'package:fluffychat/components/image_bubble.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
@ -13,7 +14,7 @@ class ImageView extends StatelessWidget {
void _forwardAction(BuildContext context) async { void _forwardAction(BuildContext context) async {
Matrix.of(context).shareContent = event.content; Matrix.of(context).shareContent = event.content;
Navigator.of(context).popUntil((r) => r.isFirst); AdaptivePageLayout.of(context).popUntilIsFirst();
} }
@override @override

View File

@ -3,7 +3,6 @@ import 'dart:async';
import 'package:fluffychat/components/default_app_bar_search_field.dart'; import 'package:fluffychat/components/default_app_bar_search_field.dart';
import 'package:flushbar/flushbar_helper.dart'; import 'package:flushbar/flushbar_helper.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/avatar.dart'; import 'package:fluffychat/components/avatar.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
@ -11,11 +10,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../utils/localized_exception_extension.dart'; import '../utils/localized_exception_extension.dart';
import 'chat_list.dart';
class InvitationSelection extends StatefulWidget { class InvitationSelection extends StatefulWidget {
final Room room; final String roomId;
const InvitationSelection(this.room, {Key key}) : super(key: key); const InvitationSelection(this.roomId, {Key key}) : super(key: key);
@override @override
_InvitationSelectionState createState() => _InvitationSelectionState(); _InvitationSelectionState createState() => _InvitationSelectionState();
@ -27,11 +25,12 @@ class _InvitationSelectionState extends State<InvitationSelection> {
bool loading = false; bool loading = false;
List<Profile> foundProfiles = []; List<Profile> foundProfiles = [];
Timer coolDown; Timer coolDown;
Room room;
Future<List<User>> getContacts(BuildContext context) async { Future<List<User>> getContacts(BuildContext context) async {
var client2 = Matrix.of(context).client; var client2 = Matrix.of(context).client;
final client = client2; final client = client2;
var participants = await widget.room.requestParticipants(); var participants = await room.requestParticipants();
participants.removeWhere( participants.removeWhere(
(u) => ![Membership.join, Membership.invite].contains(u.membership), (u) => ![Membership.join, Membership.invite].contains(u.membership),
); );
@ -59,7 +58,7 @@ class _InvitationSelectionState extends State<InvitationSelection> {
void inviteAction(BuildContext context, String id) async { void inviteAction(BuildContext context, String id) async {
final success = await showFutureLoadingDialog( final success = await showFutureLoadingDialog(
context: context, context: context,
future: () => widget.room.invite(id), future: () => room.invite(id),
); );
if (success.error == null) { if (success.error == null) {
await FlushbarHelper.createSuccess( await FlushbarHelper.createSuccess(
@ -107,7 +106,7 @@ class _InvitationSelectionState extends State<InvitationSelection> {
Profile.fromJson({'user_id': '@$text'}), Profile.fromJson({'user_id': '@$text'}),
]); ]);
} }
final participants = widget.room final participants = room
.getParticipants() .getParticipants()
.where((user) => .where((user) =>
[Membership.join, Membership.invite].contains(user.membership)) [Membership.join, Membership.invite].contains(user.membership))
@ -119,60 +118,57 @@ class _InvitationSelectionState extends State<InvitationSelection> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final groupName = widget.room.name?.isEmpty ?? false room ??= Matrix.of(context).client.getRoomById(widget.roomId);
? L10n.of(context).group final groupName =
: widget.room.name; room.name?.isEmpty ?? false ? L10n.of(context).group : room.name;
return AdaptivePageLayout( return Scaffold(
primaryPage: FocusPage.SECOND, appBar: AppBar(
firstScaffold: ChatList(activeChat: widget.room.id), titleSpacing: 0,
secondScaffold: Scaffold( title: DefaultAppBarSearchField(
appBar: AppBar( autofocus: true,
titleSpacing: 0, hintText: L10n.of(context).inviteContactToGroup(groupName),
title: DefaultAppBarSearchField( onChanged: (String text) => searchUserWithCoolDown(context, text),
autofocus: true, ),
hintText: L10n.of(context).inviteContactToGroup(groupName), ),
onChanged: (String text) => searchUserWithCoolDown(context, text), body: foundProfiles.isNotEmpty
), ? ListView.builder(
), itemCount: foundProfiles.length,
body: foundProfiles.isNotEmpty itemBuilder: (BuildContext context, int i) => ListTile(
? ListView.builder( leading: Avatar(
itemCount: foundProfiles.length, foundProfiles[i].avatarUrl,
foundProfiles[i].displayname ?? foundProfiles[i].userId,
),
title: Text(
foundProfiles[i].displayname ??
foundProfiles[i].userId.localpart,
),
subtitle: Text(foundProfiles[i].userId),
onTap: () => inviteAction(context, foundProfiles[i].userId),
),
)
: FutureBuilder<List<User>>(
future: getContacts(context),
builder: (BuildContext context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
var contacts = snapshot.data;
return ListView.builder(
itemCount: contacts.length,
itemBuilder: (BuildContext context, int i) => ListTile( itemBuilder: (BuildContext context, int i) => ListTile(
leading: Avatar( leading: Avatar(
foundProfiles[i].avatarUrl, contacts[i].avatarUrl,
foundProfiles[i].displayname ?? foundProfiles[i].userId, contacts[i].calcDisplayname(),
), ),
title: Text( title: Text(contacts[i].calcDisplayname()),
foundProfiles[i].displayname ?? subtitle: Text(contacts[i].id),
foundProfiles[i].userId.localpart, onTap: () => inviteAction(context, contacts[i].id),
),
subtitle: Text(foundProfiles[i].userId),
onTap: () => inviteAction(context, foundProfiles[i].userId),
), ),
) );
: FutureBuilder<List<User>>( },
future: getContacts(context), ),
builder: (BuildContext context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
var contacts = snapshot.data;
return ListView.builder(
itemCount: contacts.length,
itemBuilder: (BuildContext context, int i) => ListTile(
leading: Avatar(
contacts[i].avatarUrl,
contacts[i].calcDisplayname(),
),
title: Text(contacts[i].calcDisplayname()),
subtitle: Text(contacts[i].id),
onTap: () => inviteAction(context, contacts[i].id),
),
);
},
)),
); );
} }
} }

View File

@ -0,0 +1,38 @@
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/matrix.dart';
import 'package:flushbar/flushbar_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'chat_list.dart';
import 'homeserver_picker.dart';
class LoadingView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FutureBuilder<LoginState>(
future: Matrix.of(context).client.onLoginStateChanged.stream.first,
builder: (context, snapshot) {
if (snapshot.hasError) {
WidgetsBinding.instance
.addPostFrameCallback((_) => FlushbarHelper.createError(
title: L10n.of(context).oopsSomethingWentWrong,
message: snapshot.error.toString(),
).show(context));
return HomeserverPicker();
}
if (!snapshot.hasData) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
if (Matrix.of(context).client.isLogged()) {
return ChatList();
}
return HomeserverPicker();
},
);
}
}

View File

@ -5,15 +5,10 @@ import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/utils/firebase_controller.dart';
import 'package:fluffychat/utils/sentry_controller.dart';
import 'package:flushbar/flushbar_helper.dart'; import 'package:flushbar/flushbar_helper.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'chat_list.dart';
class Login extends StatefulWidget { class Login extends StatefulWidget {
@override @override
_LoginState createState() => _LoginState(); _LoginState createState() => _LoginState();
@ -57,14 +52,8 @@ class _LoginState extends State<Login> {
setState(() => passwordError = exception.toString()); setState(() => passwordError = exception.toString());
return setState(() => loading = false); return setState(() => loading = false);
} }
await FirebaseController.setupFirebase(
matrix,
matrix.clientName,
).catchError(SentryController.captureException);
setState(() => loading = false); if (mounted) setState(() => loading = false);
await Navigator.of(context).pushAndRemoveUntil(
AppRoute.defaultRoute(context, ChatListView()), (r) => false);
} }
Timer _coolDown; Timer _coolDown;

View File

@ -1,33 +1,16 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart' as sdk; import 'package:famedlysdk/famedlysdk.dart' as sdk;
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:pedantic/pedantic.dart';
import 'chat.dart'; class NewGroup extends StatefulWidget {
import 'chat_list.dart';
import 'invitation_selection.dart';
class NewGroupView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(),
secondScaffold: _NewGroup(),
);
}
}
class _NewGroup extends StatefulWidget {
@override @override
_NewGroupState createState() => _NewGroupState(); _NewGroupState createState() => _NewGroupState();
} }
class _NewGroupState extends State<_NewGroup> { class _NewGroupState extends State<NewGroup> {
TextEditingController controller = TextEditingController(); TextEditingController controller = TextEditingController();
bool publicGroup = false; bool publicGroup = false;
@ -45,24 +28,11 @@ class _NewGroupState extends State<_NewGroup> {
name: controller.text.isNotEmpty ? controller.text : null, name: controller.text.isNotEmpty ? controller.text : null,
), ),
); );
Navigator.of(context).pop(); AdaptivePageLayout.of(context).popUntilIsFirst();
if (roomID != null) { if (roomID != null) {
unawaited( await AdaptivePageLayout.of(context).pushNamed('/rooms/${roomID.result}');
Navigator.of(context).push( await AdaptivePageLayout.of(context)
AppRoute.defaultRoute( .pushNamed('/rooms/${roomID.result}/invite');
context,
ChatView(roomID.result),
),
),
);
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InvitationSelection(
matrix.client.getRoomById(roomID.result),
),
),
);
} }
} }

View File

@ -1,35 +1,20 @@
import 'dart:async'; import 'dart:async';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/avatar.dart'; import 'package:fluffychat/components/avatar.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'chat.dart'; class NewPrivateChat extends StatefulWidget {
import 'chat_list.dart';
class NewPrivateChatView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(),
secondScaffold: _NewPrivateChat(),
);
}
}
class _NewPrivateChat extends StatefulWidget {
@override @override
_NewPrivateChatState createState() => _NewPrivateChatState(); _NewPrivateChatState createState() => _NewPrivateChatState();
} }
class _NewPrivateChatState extends State<_NewPrivateChat> { class _NewPrivateChatState extends State<NewPrivateChat> {
TextEditingController controller = TextEditingController(); TextEditingController controller = TextEditingController();
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
bool loading = false; bool loading = false;
@ -59,15 +44,10 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
context: context, context: context,
future: () => user.startDirectChat(), future: () => user.startDirectChat(),
); );
Navigator.of(context).pop();
if (roomID.error == null) { if (roomID.error == null) {
await Navigator.of(context).push( await AdaptivePageLayout.of(context)
AppRoute.defaultRoute( .popAndPushNamed('/rooms/${roomID.result}');
context,
ChatView(roomID.result),
),
);
} }
} }

View File

@ -1,9 +1,6 @@
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:fluffychat/components/dialogs/bootstrap_dialog.dart'; import 'package:fluffychat/components/dialogs/bootstrap_dialog.dart';
import 'package:fluffychat/views/log_view.dart';
import 'package:fluffychat/views/settings_3pid.dart';
import 'package:fluffychat/views/settings_notifications.dart';
import 'package:fluffychat/views/settings_style.dart';
import 'package:flushbar/flushbar_helper.dart'; import 'package:flushbar/flushbar_helper.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:file_picker_cross/file_picker_cross.dart'; import 'package:file_picker_cross/file_picker_cross.dart';
@ -12,33 +9,16 @@ import 'package:fluffychat/utils/beautify_string_extension.dart';
import 'package:fluffychat/app_config.dart'; import 'package:fluffychat/app_config.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/sentry_controller.dart'; import 'package:fluffychat/utils/sentry_controller.dart';
import 'package:fluffychat/views/settings_devices.dart';
import 'package:fluffychat/views/settings_ignore_list.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../components/adaptive_page_layout.dart';
import '../components/content_banner.dart'; import '../components/content_banner.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import '../components/matrix.dart'; import '../components/matrix.dart';
import '../utils/app_route.dart';
import '../app_config.dart'; import '../app_config.dart';
import '../config/setting_keys.dart'; import '../config/setting_keys.dart';
import 'chat_list.dart';
import 'settings_emotes.dart';
class SettingsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(),
secondScaffold: Settings(),
);
}
}
class Settings extends StatefulWidget { class Settings extends StatefulWidget {
@override @override
@ -333,12 +313,8 @@ class _SettingsState extends State<Settings> {
ListTile( ListTile(
trailing: Icon(Icons.notifications_outlined), trailing: Icon(Icons.notifications_outlined),
title: Text(L10n.of(context).notifications), title: Text(L10n.of(context).notifications),
onTap: () async => await Navigator.of(context).push( onTap: () => AdaptivePageLayout.of(context)
AppRoute.defaultRoute( .pushNamed('/settings/notifications'),
context,
SettingsNotificationsView(),
),
),
), ),
ListTile( ListTile(
title: Text( title: Text(
@ -351,12 +327,8 @@ class _SettingsState extends State<Settings> {
), ),
ListTile( ListTile(
title: Text(L10n.of(context).changeTheme), title: Text(L10n.of(context).changeTheme),
onTap: () async => await Navigator.of(context).push( onTap: () =>
AppRoute.defaultRoute( AdaptivePageLayout.of(context).pushNamed('/settings/style'),
context,
SettingsStyleView(),
),
),
trailing: Icon(Icons.style_outlined), trailing: Icon(Icons.style_outlined),
), ),
SwitchListTile( SwitchListTile(
@ -392,12 +364,8 @@ class _SettingsState extends State<Settings> {
), ),
ListTile( ListTile(
title: Text(L10n.of(context).emoteSettings), title: Text(L10n.of(context).emoteSettings),
onTap: () async => await Navigator.of(context).push( onTap: () =>
AppRoute.defaultRoute( AdaptivePageLayout.of(context).pushNamed('/settings/emotes'),
context,
EmotesSettingsView(),
),
),
trailing: Icon(Icons.insert_emoticon_outlined), trailing: Icon(Icons.insert_emoticon_outlined),
), ),
Divider(thickness: 1), Divider(thickness: 1),
@ -425,22 +393,14 @@ class _SettingsState extends State<Settings> {
ListTile( ListTile(
trailing: Icon(Icons.devices_other_outlined), trailing: Icon(Icons.devices_other_outlined),
title: Text(L10n.of(context).devices), title: Text(L10n.of(context).devices),
onTap: () async => await Navigator.of(context).push( onTap: () =>
AppRoute.defaultRoute( AdaptivePageLayout.of(context).pushNamed('/settings/devices'),
context,
DevicesSettingsView(),
),
),
), ),
ListTile( ListTile(
trailing: Icon(Icons.block_outlined), trailing: Icon(Icons.block_outlined),
title: Text(L10n.of(context).ignoredUsers), title: Text(L10n.of(context).ignoredUsers),
onTap: () async => await Navigator.of(context).push( onTap: () =>
AppRoute.defaultRoute( AdaptivePageLayout.of(context).pushNamed('/settings/ignore'),
context,
SettingsIgnoreListView(),
),
),
), ),
ListTile( ListTile(
trailing: Icon(Icons.bug_report_outlined), trailing: Icon(Icons.bug_report_outlined),
@ -458,12 +418,8 @@ class _SettingsState extends State<Settings> {
ListTile( ListTile(
trailing: Icon(Icons.email_outlined), trailing: Icon(Icons.email_outlined),
title: Text(L10n.of(context).passwordRecovery), title: Text(L10n.of(context).passwordRecovery),
onTap: () => Navigator.of(context).push( onTap: () =>
AppRoute.defaultRoute( AdaptivePageLayout.of(context).pushNamed('/settings/3pid'),
context,
Settings3PidView(),
),
),
), ),
ListTile( ListTile(
trailing: Icon(Icons.exit_to_app_outlined), trailing: Icon(Icons.exit_to_app_outlined),
@ -607,9 +563,7 @@ class _SettingsState extends State<Settings> {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
onTap: () => Navigator.of(context).push( onTap: () => AdaptivePageLayout.of(context).pushNamed('/logs'),
AppRoute.defaultRoute(context, LogViewer()),
),
), ),
ListTile( ListTile(
trailing: Icon(Icons.help_outlined), trailing: Icon(Icons.help_outlined),

View File

@ -1,24 +1,10 @@
import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'chat_list.dart';
class Settings3PidView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(),
secondScaffold: Settings3Pid(),
);
}
}
class Settings3Pid extends StatefulWidget { class Settings3Pid extends StatefulWidget {
static int sendAttempt = 0; static int sendAttempt = 0;

View File

@ -4,21 +4,8 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../components/adaptive_page_layout.dart';
import '../components/matrix.dart'; import '../components/matrix.dart';
import '../utils/date_time_extension.dart'; import '../utils/date_time_extension.dart';
import 'chat_list.dart';
class DevicesSettingsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(),
secondScaffold: DevicesSettings(),
);
}
}
class DevicesSettings extends StatefulWidget { class DevicesSettings extends StatefulWidget {
@override @override

View File

@ -10,26 +10,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import '../components/adaptive_page_layout.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import '../components/matrix.dart'; import '../components/matrix.dart';
import 'chat_list.dart';
class EmotesSettingsView extends StatelessWidget {
final Room room;
final String stateKey;
EmotesSettingsView({this.room, this.stateKey});
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(),
secondScaffold: EmotesSettings(room: room, stateKey: stateKey),
);
}
}
class EmotesSettings extends StatefulWidget { class EmotesSettings extends StatefulWidget {
final Room room; final Room room;

View File

@ -1,23 +1,10 @@
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/components/avatar.dart'; import 'package:fluffychat/components/avatar.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../components/matrix.dart'; import '../components/matrix.dart';
import 'chat_list.dart';
class SettingsIgnoreListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(),
secondScaffold: SettingsIgnoreList(),
);
}
}
class SettingsIgnoreList extends StatelessWidget { class SettingsIgnoreList extends StatelessWidget {
final controller = TextEditingController(); final controller = TextEditingController();

View File

@ -1,33 +1,17 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:fluffychat/components/matrix.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../components/adaptive_page_layout.dart';
import '../utils/app_route.dart';
import 'chat_list.dart';
import 'settings_emotes.dart';
class MultipleEmotesSettingsView extends StatelessWidget {
final Room room;
MultipleEmotesSettingsView({this.room});
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(),
secondScaffold: MultipleEmotesSettings(room: room),
);
}
}
class MultipleEmotesSettings extends StatelessWidget { class MultipleEmotesSettings extends StatelessWidget {
final Room room; final String roomId;
MultipleEmotesSettings({this.room}); MultipleEmotesSettings(this.roomId, {Key key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final room = Matrix.of(context).client.getRoomById(roomId);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(L10n.of(context).emotePacks), title: Text(L10n.of(context).emotePacks),
@ -58,11 +42,9 @@ class MultipleEmotesSettings extends StatelessWidget {
return ListTile( return ListTile(
title: Text(packName), title: Text(packName),
onTap: () async { onTap: () async {
await Navigator.of(context).push( await AdaptivePageLayout.of(context).pushNamed(
AppRoute.defaultRoute( '/settings/emotes',
context, arguments: room,
EmotesSettingsView(room: room, stateKey: keys[i]),
),
); );
}, },
); );

View File

@ -1,7 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/app_config.dart'; import 'package:fluffychat/app_config.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -11,7 +10,6 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:open_noti_settings/open_noti_settings.dart'; import 'package:open_noti_settings/open_noti_settings.dart';
import '../components/matrix.dart'; import '../components/matrix.dart';
import 'chat_list.dart';
class NotificationSettingsItem { class NotificationSettingsItem {
final PushRuleKind type; final PushRuleKind type;
@ -20,17 +18,6 @@ class NotificationSettingsItem {
NotificationSettingsItem(this.type, this.key, this.title); NotificationSettingsItem(this.type, this.key, this.title);
} }
class SettingsNotificationsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(),
secondScaffold: SettingsNotifications(),
);
}
}
class SettingsNotifications extends StatelessWidget { class SettingsNotifications extends StatelessWidget {
static List<NotificationSettingsItem> items = [ static List<NotificationSettingsItem> items = [
NotificationSettingsItem( NotificationSettingsItem(

View File

@ -1,25 +1,12 @@
import 'dart:io'; import 'dart:io';
import 'package:adaptive_theme/adaptive_theme.dart'; import 'package:adaptive_theme/adaptive_theme.dart';
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import '../components/matrix.dart'; import '../components/matrix.dart';
import 'chat_list.dart';
class SettingsStyleView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AdaptivePageLayout(
primaryPage: FocusPage.SECOND,
firstScaffold: ChatList(),
secondScaffold: SettingsStyle(),
);
}
}
class SettingsStyle extends StatefulWidget { class SettingsStyle extends StatefulWidget {
@override @override

View File

@ -1,12 +1,10 @@
import 'dart:math'; import 'dart:math';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:file_picker_cross/file_picker_cross.dart'; import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/views/login.dart';
import 'package:fluffychat/views/sign_up_password.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -61,11 +59,9 @@ class _SignUpState extends State<SignUp> {
return setState(() => loading = false); return setState(() => loading = false);
} }
setState(() => loading = false); setState(() => loading = false);
await Navigator.of(context).push( await AdaptivePageLayout.of(context).pushNamed(
AppRoute( '/signup/password/${Uri.encodeComponent(preferredUsername)}/${Uri.encodeComponent(usernameController.text)}',
SignUpPassword(preferredUsername, arguments: avatar,
avatar: avatar, displayname: usernameController.text),
),
); );
} }
@ -171,9 +167,8 @@ class _SignUpState extends State<SignUp> {
fontSize: 16, fontSize: 16,
), ),
), ),
onPressed: () => Navigator.of(context).push( onPressed: () =>
AppRoute(Login()), AdaptivePageLayout.of(context).pushNamed('/login'),
),
), ),
), ),
]), ]),

View File

@ -1,16 +1,13 @@
import 'dart:math'; import 'dart:math';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:flushbar/flushbar_helper.dart'; import 'package:flushbar/flushbar_helper.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/matrix.dart'; import 'package:fluffychat/components/matrix.dart';
import 'package:fluffychat/utils/app_route.dart';
import 'package:fluffychat/views/auth_web_view.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'chat_list.dart';
class SignUpPassword extends StatefulWidget { class SignUpPassword extends StatefulWidget {
final MatrixFile avatar; final MatrixFile avatar;
final String username; final String username;
@ -69,17 +66,11 @@ class _SignUpPasswordState extends State<SignUpPassword> {
), ),
); );
} else { } else {
await Navigator.of(context).push( await AdaptivePageLayout.of(context).pushNamed(
AppRoute.defaultRoute( '/authwebview',
arguments: () => _signUpAction(
context, context,
AuthWebView( auth: AuthenticationData(session: exception.session),
currentStage,
exception.session,
() => _signUpAction(
context,
auth: AuthenticationData(session: exception.session),
),
),
), ),
); );
return; return;
@ -111,9 +102,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
.show(context); .show(context);
} }
} }
await Navigator.of(context).pushAndRemoveUntil( if (mounted) setState(() => loading = false);
AppRoute.defaultRoute(context, ChatListView()), (r) => false);
setState(() => loading = false);
} }
@override @override