mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-12-28 17:12:33 +01:00
Merge branch 'krille/apl' into 'main'
refactor: Use APL See merge request famedly/fluffychat!338
This commit is contained in:
commit
4b0f9fc8de
@ -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,
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.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_gen/gen_l10n/l10n.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
@ -95,9 +93,8 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
|
||||
final success = await showFutureLoadingDialog(
|
||||
context: context, future: () => widget.room.leave());
|
||||
if (success.error == null) {
|
||||
await Navigator.of(context).pushAndRemoveUntil(
|
||||
AppRoute.defaultRoute(context, ChatListView()),
|
||||
(Route r) => false);
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamedAndRemoveAllOthers('/');
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -117,12 +114,9 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
|
||||
startCallAction(context);
|
||||
break;
|
||||
case 'details':
|
||||
await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
ChatDetails(widget.room),
|
||||
),
|
||||
);
|
||||
await AdaptivePageLayout.of(context).pushNamedAndRemoveAllOthers(
|
||||
'/rooms/${widget.room.id}/details');
|
||||
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -1,11 +1,6 @@
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.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_gen/gen_l10n/l10n.dart';
|
||||
|
||||
@ -13,15 +8,9 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'matrix.dart';
|
||||
|
||||
class DefaultDrawer extends StatelessWidget {
|
||||
void _drawerTapAction(BuildContext context, Widget view) {
|
||||
void _drawerTapAction(BuildContext context, String route) {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
view,
|
||||
),
|
||||
(r) => r.isFirst,
|
||||
);
|
||||
AdaptivePageLayout.of(context).pushNamedAndRemoveUntilIsFirst(route);
|
||||
}
|
||||
|
||||
void _setStatus(BuildContext context) async {
|
||||
@ -64,12 +53,12 @@ class DefaultDrawer extends StatelessWidget {
|
||||
ListTile(
|
||||
leading: Icon(Icons.people_outline),
|
||||
title: Text(L10n.of(context).createNewGroup),
|
||||
onTap: () => _drawerTapAction(context, NewGroupView()),
|
||||
onTap: () => _drawerTapAction(context, '/newgroup'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.person_add_outlined),
|
||||
title: Text(L10n.of(context).newPrivateChat),
|
||||
onTap: () => _drawerTapAction(context, NewPrivateChatView()),
|
||||
onTap: () => _drawerTapAction(context, '/newprivatechat'),
|
||||
),
|
||||
Divider(height: 1),
|
||||
ListTile(
|
||||
@ -77,7 +66,7 @@ class DefaultDrawer extends StatelessWidget {
|
||||
title: Text(L10n.of(context).archive),
|
||||
onTap: () => _drawerTapAction(
|
||||
context,
|
||||
Archive(),
|
||||
'/archive',
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
@ -85,7 +74,7 @@ class DefaultDrawer extends StatelessWidget {
|
||||
title: Text(L10n.of(context).discoverGroups),
|
||||
onTap: () => _drawerTapAction(
|
||||
context,
|
||||
DiscoverView(),
|
||||
'/discover',
|
||||
),
|
||||
),
|
||||
Divider(height: 1),
|
||||
@ -94,7 +83,7 @@ class DefaultDrawer extends StatelessWidget {
|
||||
title: Text(L10n.of(context).settings),
|
||||
onTap: () => _drawerTapAction(
|
||||
context,
|
||||
SettingsView(),
|
||||
'/settings',
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -1,10 +1,9 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:flushbar/flushbar_helper.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_gen/gen_l10n/l10n.dart';
|
||||
|
||||
@ -23,12 +22,8 @@ class _EncryptionButtonState extends State<EncryptionButton> {
|
||||
|
||||
void _enableEncryptionAction() async {
|
||||
if (widget.room.encrypted) {
|
||||
await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
ChatEncryptionSettingsView(widget.room.id),
|
||||
),
|
||||
);
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamed('/rooms/${widget.room.id}/encryption');
|
||||
return;
|
||||
}
|
||||
if (!widget.room.client.encryptionEnabled) {
|
||||
|
@ -1,19 +1,17 @@
|
||||
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:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/utils/event_extension.dart';
|
||||
import 'package:fluffychat/utils/matrix_locals.dart';
|
||||
import 'package:fluffychat/utils/room_status_extension.dart';
|
||||
import 'package:fluffychat/views/chat.dart';
|
||||
import 'package:flushbar/flushbar_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
|
||||
import '../../utils/app_route.dart';
|
||||
import '../../utils/date_time_extension.dart';
|
||||
import '../../views/chat.dart';
|
||||
import '../avatar.dart';
|
||||
import '../dialogs/send_file_dialog.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
@ -102,11 +100,8 @@ class ChatListItem extends StatelessWidget {
|
||||
}
|
||||
Matrix.of(context).shareContent = null;
|
||||
}
|
||||
await Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
AppRoute.defaultRoute(context, ChatView(room.id)),
|
||||
(r) => r.isFirst,
|
||||
);
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamedAndRemoveUntilIsFirst('/rooms/${room.id}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/components/message_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/event_extension.dart';
|
||||
import 'package:fluffychat/utils/string_color.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../adaptive_page_layout.dart';
|
||||
import '../avatar.dart';
|
||||
import '../matrix.dart';
|
||||
import '../message_reactions.dart';
|
||||
@ -88,8 +88,7 @@ class Message extends StatelessWidget {
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(radius),
|
||||
),
|
||||
constraints:
|
||||
BoxConstraints(maxWidth: AdaptivePageLayout.defaultMinWidth),
|
||||
constraints: BoxConstraints(maxWidth: FluffyThemes.columnWidth),
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
Column(
|
||||
|
@ -1,10 +1,9 @@
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import '../../utils/app_route.dart';
|
||||
import '../../views/chat.dart';
|
||||
import '../avatar.dart';
|
||||
import '../matrix.dart';
|
||||
|
||||
@ -19,12 +18,8 @@ class PublicRoomListItem extends StatelessWidget {
|
||||
future: () => _joinRoomAndWait(context),
|
||||
);
|
||||
if (success.error == null) {
|
||||
await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
ChatView(success.result),
|
||||
),
|
||||
);
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamed('/rooms/${success.result}');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,14 +3,13 @@ import 'dart:io';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:famedlysdk/encryption.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/utils/app_route.dart';
|
||||
import 'package:fluffychat/utils/firebase_controller.dart';
|
||||
import 'package:fluffychat/utils/matrix_locals.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/utils/sentry_controller.dart';
|
||||
import 'package:fluffychat/views/settings_3pid.dart';
|
||||
import 'package:flushbar/flushbar.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -38,7 +37,16 @@ class Matrix extends StatefulWidget {
|
||||
|
||||
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
|
||||
MatrixState createState() => MatrixState();
|
||||
@ -52,7 +60,7 @@ class MatrixState extends State<Matrix> {
|
||||
Client client;
|
||||
Store store = Store();
|
||||
@override
|
||||
BuildContext context;
|
||||
BuildContext get context => widget.context;
|
||||
|
||||
Map<String, dynamic> get shareContent => _shareContent;
|
||||
set shareContent(Map<String, dynamic> content) {
|
||||
@ -76,29 +84,16 @@ class MatrixState extends State<Matrix> {
|
||||
}
|
||||
|
||||
void _initWithStore() async {
|
||||
var initLoginState = client.onLoginStateChanged.stream.first;
|
||||
try {
|
||||
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 configOptionMissing = storeItem == null || storeItem.isEmpty;
|
||||
if (configOptionMissing || (!configOptionMissing && storeItem == '1')) {
|
||||
if (configOptionMissing) {
|
||||
await store.setItem(SettingKeys.showNoPid, '0');
|
||||
}
|
||||
await Matrix.of(context)
|
||||
.client
|
||||
.requestThirdPartyIdentifiers()
|
||||
.then((l) {
|
||||
await client.requestThirdPartyIdentifiers().then((l) {
|
||||
if (l.isEmpty) {
|
||||
Flushbar(
|
||||
title: L10n.of(context).warning,
|
||||
@ -110,12 +105,8 @@ class MatrixState extends State<Matrix> {
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text(L10n.of(context).edit),
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
Settings3PidView(),
|
||||
),
|
||||
),
|
||||
onPressed: () =>
|
||||
AdaptivePageLayout.of(context).pushNamed('/settings/3pid'),
|
||||
),
|
||||
flushbarStyle: FlushbarStyle.FLOATING,
|
||||
).show(context);
|
||||
@ -133,6 +124,7 @@ class MatrixState extends State<Matrix> {
|
||||
StreamSubscription onKeyVerificationRequestSub;
|
||||
StreamSubscription onJitsiCallSub;
|
||||
StreamSubscription onNotification;
|
||||
StreamSubscription<LoginState> onLoginStateChanged;
|
||||
StreamSubscription<UiaRequest> onUiaRequest;
|
||||
StreamSubscription<html.Event> onFocusSub;
|
||||
StreamSubscription<html.Event> onBlurSub;
|
||||
@ -301,6 +293,8 @@ class MatrixState extends State<Matrix> {
|
||||
}
|
||||
}
|
||||
|
||||
LoginState loginState;
|
||||
|
||||
void initMatrix() {
|
||||
clientName =
|
||||
'${AppConfig.applicationName} ${kIsWeb ? 'Web' : Platform.operatingSystem}';
|
||||
@ -380,6 +374,19 @@ class MatrixState extends State<Matrix> {
|
||||
onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true);
|
||||
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);
|
||||
if (kIsWeb || Platform.isLinux) {
|
||||
client.onSync.stream.first.then((s) {
|
||||
|
@ -1,12 +1,11 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.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/utils/app_route.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/views/chat.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'content_banner.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 'matrix.dart';
|
||||
import 'dialogs/key_verification_dialog.dart';
|
||||
import '../utils/app_route.dart';
|
||||
|
||||
class UserBottomSheet extends StatelessWidget {
|
||||
final User user;
|
||||
@ -76,12 +74,8 @@ class UserBottomSheet extends StatelessWidget {
|
||||
break;
|
||||
case 'message':
|
||||
final roomId = await user.startDirectChat();
|
||||
await Navigator.of(context).pushAndRemoveUntil(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
ChatView(roomId),
|
||||
),
|
||||
(Route r) => r.isFirst);
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -161,8 +155,8 @@ class UserBottomSheet extends StatelessWidget {
|
||||
}
|
||||
return Center(
|
||||
child: Container(
|
||||
width: min(MediaQuery.of(context).size.width,
|
||||
AdaptivePageLayout.defaultMinWidth * 1.5),
|
||||
width: min(
|
||||
MediaQuery.of(context).size.width, FluffyThemes.columnWidth * 1.5),
|
||||
child: SafeArea(
|
||||
child: Material(
|
||||
elevation: 4,
|
||||
|
228
lib/config/routes.dart
Normal file
228
lib/config/routes.dart
Normal 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,
|
||||
),
|
||||
);
|
||||
}
|
@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class FluffyThemes {
|
||||
static const double columnWidth = 360.0;
|
||||
static ThemeData light = ThemeData(
|
||||
primaryColorDark: Colors.white,
|
||||
primaryColorLight: Color(0xff121212),
|
||||
|
@ -2,22 +2,19 @@
|
||||
import 'dart:async';
|
||||
|
||||
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/views/homeserver_picker.dart';
|
||||
import 'package:flushbar/flushbar_helper.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.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 'components/matrix.dart';
|
||||
import 'config/themes.dart';
|
||||
import 'utils/localized_exception_extension.dart';
|
||||
import 'app_config.dart';
|
||||
import 'views/chat_list.dart';
|
||||
|
||||
void main() async {
|
||||
SystemChrome.setSystemUIOverlayStyle(
|
||||
@ -31,51 +28,38 @@ void main() async {
|
||||
}
|
||||
|
||||
class App extends StatelessWidget {
|
||||
final GlobalKey<AdaptivePageLayoutState> _apl =
|
||||
GlobalKey<AdaptivePageLayoutState>();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Matrix(
|
||||
child: Builder(
|
||||
builder: (BuildContext context) => AdaptiveTheme(
|
||||
light: FluffyThemes.light,
|
||||
dark: FluffyThemes.dark,
|
||||
initial: AdaptiveThemeMode.system,
|
||||
builder: (theme, darkTheme) => MaterialApp(
|
||||
title: '${AppConfig.applicationName}',
|
||||
theme: theme,
|
||||
darkTheme: darkTheme,
|
||||
localizationsDelegates: L10n.localizationsDelegates,
|
||||
supportedLocales: L10n.supportedLocales,
|
||||
locale: kIsWeb
|
||||
? Locale(html.window.navigator.language.split('-').first)
|
||||
: null,
|
||||
home: FutureBuilder<LoginState>(
|
||||
future:
|
||||
Matrix.of(context).client.onLoginStateChanged.stream.first,
|
||||
builder: (context, snapshot) {
|
||||
LoadingDialog.defaultTitle = L10n.of(context).loadingPleaseWait;
|
||||
LoadingDialog.defaultBackLabel = L10n.of(context).close;
|
||||
LoadingDialog.defaultOnError =
|
||||
(Object e) => e.toLocalizedString(context);
|
||||
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 ChatListView();
|
||||
}
|
||||
return HomeserverPicker();
|
||||
},
|
||||
return AdaptiveTheme(
|
||||
light: FluffyThemes.light,
|
||||
dark: FluffyThemes.dark,
|
||||
initial: AdaptiveThemeMode.system,
|
||||
builder: (theme, darkTheme) => MaterialApp(
|
||||
title: '${AppConfig.applicationName}',
|
||||
theme: theme,
|
||||
darkTheme: darkTheme,
|
||||
localizationsDelegates: L10n.localizationsDelegates,
|
||||
supportedLocales: L10n.supportedLocales,
|
||||
locale: kIsWeb
|
||||
? Locale(html.window.navigator.language.split('-').first)
|
||||
: null,
|
||||
home: Builder(
|
||||
builder: (context) => Matrix(
|
||||
context: context,
|
||||
apl: _apl,
|
||||
child: Builder(
|
||||
builder: (context) => AdaptivePageLayout(
|
||||
key: _apl,
|
||||
onGenerateRoute: FluffyRoutes(context).onGenerateRoute,
|
||||
dividerColor: Theme.of(context).dividerColor,
|
||||
columnWidth: FluffyThemes.columnWidth,
|
||||
routeBuilder: (builder, settings) =>
|
||||
_apl.currentState.columnMode(context)
|
||||
? FadeRoute(page: builder(context))
|
||||
: CupertinoPageRoute(builder: builder),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -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/material.dart';
|
||||
|
||||
class AppRoute extends PageRouteBuilder {
|
||||
static Route defaultRoute(BuildContext context, Widget page) {
|
||||
return context != null && !AdaptivePageLayout.columnMode(context)
|
||||
return context != null &&
|
||||
!AdaptivePageLayout.of(context).columnMode(context)
|
||||
? CupertinoPageRoute(
|
||||
builder: (BuildContext context) => page,
|
||||
)
|
||||
|
@ -7,8 +7,6 @@ import 'package:flushbar/flushbar_helper.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.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/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
@ -112,12 +110,8 @@ abstract class FirebaseController {
|
||||
roomId = (message['data'] ?? message)['room_id'];
|
||||
}
|
||||
if (roomId?.isEmpty ?? true) throw ('Bad roomId');
|
||||
await Navigator.of(context).pushAndRemoveUntil(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
ChatView(roomId),
|
||||
),
|
||||
(r) => r.isFirst);
|
||||
await matrix.widget.apl.currentState
|
||||
.pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
|
||||
} catch (_) {
|
||||
await FlushbarHelper.createError(message: 'Failed to open chat...')
|
||||
.show(context);
|
||||
|
@ -1,11 +1,9 @@
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/components/matrix.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:url_launcher/url_launcher.dart';
|
||||
|
||||
@ -74,12 +72,8 @@ class UrlLauncher {
|
||||
}
|
||||
if (room != null) {
|
||||
// we have the room, so....just open it!
|
||||
await Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
AppRoute.defaultRoute(
|
||||
context, ChatView(room.id, scrollToEventId: event)),
|
||||
(r) => r.isFirst,
|
||||
);
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamedAndRemoveUntilIsFirst('/rooms/${room.id}/$event');
|
||||
return;
|
||||
}
|
||||
if (roomIdOrAlias.sigil == '!') {
|
||||
@ -101,21 +95,12 @@ class UrlLauncher {
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => Future.delayed(const Duration(seconds: 2)));
|
||||
await Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
AppRoute.defaultRoute(
|
||||
context, ChatView(response.result, scrollToEventId: event)),
|
||||
(r) => r.isFirst,
|
||||
);
|
||||
await AdaptivePageLayout.of(context).pushNamedAndRemoveUntilIsFirst(
|
||||
'/rooms/${response.result}/$event');
|
||||
}
|
||||
} else {
|
||||
await Navigator.of(context).pushAndRemoveUntil(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
DiscoverView(alias: roomIdOrAlias),
|
||||
),
|
||||
(r) => r.isFirst,
|
||||
);
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamedAndRemoveUntilIsFirst('/discover/${roomIdOrAlias}');
|
||||
}
|
||||
} else if (identityParts.primaryIdentifier.sigil == '@') {
|
||||
final user = User(
|
||||
@ -124,11 +109,9 @@ class UrlLauncher {
|
||||
);
|
||||
var roomId = matrix.client.getDirectChatFromUserId(user.id);
|
||||
if (roomId != null) {
|
||||
await Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
AppRoute.defaultRoute(context, ChatView(roomId)),
|
||||
(r) => r.isFirst,
|
||||
);
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -142,14 +125,10 @@ class UrlLauncher {
|
||||
future: () => user.startDirectChat(),
|
||||
))
|
||||
.result;
|
||||
Navigator.of(context).pop();
|
||||
|
||||
if (roomId != null) {
|
||||
await Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
AppRoute.defaultRoute(context, ChatView(roomId)),
|
||||
(r) => r.isFirst,
|
||||
);
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
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/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -35,36 +34,28 @@ class _ArchiveState extends State<Archive> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AdaptivePageLayout(
|
||||
firstScaffold: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).archive),
|
||||
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))),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).archive),
|
||||
elevation: _scrolledToTop ? 0 : null,
|
||||
),
|
||||
secondScaffold: Scaffold(
|
||||
body: Center(
|
||||
child: Image.asset('assets/logo.png', width: 100, height: 100),
|
||||
),
|
||||
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))),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
primaryPage: FocusPage.FIRST,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:fluffychat/components/matrix.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -23,7 +24,7 @@ class AuthWebView extends StatelessWidget {
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
AdaptivePageLayout.of(context).pop();
|
||||
onAuthDone();
|
||||
},
|
||||
),
|
||||
|
@ -3,14 +3,15 @@ import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
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:famedlysdk/famedlysdk.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/chat_settings_popup_menu.dart';
|
||||
import 'package:fluffychat/components/connection_status_header.dart';
|
||||
import 'package:fluffychat/components/dialogs/recording_dialog.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:flushbar/flushbar_helper.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.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/user_bottom_sheet.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/platform_infos.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 '../utils/filtered_timeline_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 scrollToEventId;
|
||||
|
||||
const ChatView(this.id, {Key key, this.scrollToEventId}) : super(key: key);
|
||||
|
||||
@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);
|
||||
Chat(this.id, {Key key, this.scrollToEventId})
|
||||
: super(key: key ?? Key('chatroom-$id'));
|
||||
|
||||
@override
|
||||
_ChatState createState() => _ChatState();
|
||||
}
|
||||
|
||||
class _ChatState extends State<_Chat> {
|
||||
class _ChatState extends State<Chat> {
|
||||
Room room;
|
||||
|
||||
Timeline timeline;
|
||||
@ -343,7 +323,7 @@ class _ChatState extends State<_Chat> {
|
||||
};
|
||||
}
|
||||
setState(() => selectedEvents.clear());
|
||||
Navigator.of(context).popUntil((r) => r.isFirst);
|
||||
AdaptivePageLayout.of(context).popUntilIsFirst();
|
||||
}
|
||||
|
||||
void sendAgainAction(Timeline timeline) {
|
||||
@ -509,12 +489,8 @@ class _ChatState extends State<_Chat> {
|
||||
'${room.directChatMatrixID} ',
|
||||
),
|
||||
)
|
||||
: () => Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
ChatDetails(room),
|
||||
),
|
||||
),
|
||||
: () => AdaptivePageLayout.of(context)
|
||||
.pushNamed('/rooms/${room.id}/details'),
|
||||
title: Text(
|
||||
room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context))),
|
||||
@ -643,8 +619,7 @@ class _ChatState extends State<_Chat> {
|
||||
horizontal: max(
|
||||
0,
|
||||
(MediaQuery.of(context).size.width -
|
||||
AdaptivePageLayout.defaultMinWidth *
|
||||
3.5) /
|
||||
FluffyThemes.columnWidth * 3.5) /
|
||||
2),
|
||||
),
|
||||
reverse: true,
|
||||
|
@ -1,40 +1,36 @@
|
||||
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/components/matrix.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/views/chat_permissions_settings.dart';
|
||||
import 'package:flushbar/flushbar_helper.dart';
|
||||
import 'package:famedlysdk/famedlysdk.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/content_banner.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.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/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_gen/gen_l10n/l10n.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:matrix_link_text/link_text.dart';
|
||||
|
||||
import './settings_emotes.dart';
|
||||
import './settings_multiple_emotes.dart';
|
||||
import '../utils/url_launcher.dart';
|
||||
|
||||
class ChatDetails extends StatefulWidget {
|
||||
final Room room;
|
||||
final String roomId;
|
||||
|
||||
const ChatDetails(this.room);
|
||||
const ChatDetails(this.roomId);
|
||||
|
||||
@override
|
||||
_ChatDetailsState createState() => _ChatDetailsState();
|
||||
}
|
||||
|
||||
class _ChatDetailsState extends State<ChatDetails> {
|
||||
Room room;
|
||||
List<User> members;
|
||||
void setDisplaynameAction(BuildContext context) async {
|
||||
final input = await showTextInputDialog(
|
||||
@ -42,7 +38,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||
title: L10n.of(context).changeTheNameOfTheGroup,
|
||||
textFields: [
|
||||
DialogTextField(
|
||||
initialText: widget.room.getLocalizedDisplayname(
|
||||
initialText: room.getLocalizedDisplayname(
|
||||
MatrixLocals(
|
||||
L10n.of(context),
|
||||
),
|
||||
@ -53,7 +49,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||
if (input == null) return;
|
||||
final success = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => widget.room.setName(input.single),
|
||||
future: () => room.setName(input.single),
|
||||
);
|
||||
if (success.error == null) {
|
||||
await FlushbarHelper.createSuccess(
|
||||
@ -74,9 +70,9 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||
],
|
||||
);
|
||||
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 aliasEvent = widget.room.getState('m.room.aliases', domain);
|
||||
final aliasEvent = room.getState('m.room.aliases', domain);
|
||||
final aliases =
|
||||
aliasEvent != null ? aliasEvent.content['aliases'] ?? [] : [];
|
||||
if (aliases.indexWhere((s) => s == canonicalAlias) == -1) {
|
||||
@ -84,22 +80,19 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||
newAliases.add(canonicalAlias);
|
||||
final response = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () =>
|
||||
widget.room.client.requestRoomAliasInformations(canonicalAlias),
|
||||
future: () => room.client.requestRoomAliasInformations(canonicalAlias),
|
||||
);
|
||||
if (response.error != null) {
|
||||
final success = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => widget.room.client
|
||||
.createRoomAlias(canonicalAlias, widget.room.id),
|
||||
future: () => room.client.createRoomAlias(canonicalAlias, room.id),
|
||||
);
|
||||
if (success.error != null) return;
|
||||
}
|
||||
}
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => widget.room.client
|
||||
.sendState(widget.room.id, 'm.room.canonical_alias', {
|
||||
future: () => room.client.sendState(room.id, 'm.room.canonical_alias', {
|
||||
'alias': input.single,
|
||||
}),
|
||||
);
|
||||
@ -112,7 +105,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||
textFields: [
|
||||
DialogTextField(
|
||||
hintText: L10n.of(context).setGroupDescription,
|
||||
initialText: widget.room.topic,
|
||||
initialText: room.topic,
|
||||
minLines: 1,
|
||||
maxLines: 4,
|
||||
)
|
||||
@ -121,7 +114,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||
if (input == null) return;
|
||||
final success = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => widget.room.setDescription(input.single),
|
||||
future: () => room.setDescription(input.single),
|
||||
);
|
||||
if (success.error == null) {
|
||||
await FlushbarHelper.createSuccess(
|
||||
@ -156,7 +149,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||
|
||||
final success = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => widget.room.setAvatar(file),
|
||||
future: () => room.setAvatar(file),
|
||||
);
|
||||
if (success.error == null) {
|
||||
await FlushbarHelper.createSuccess(
|
||||
@ -167,7 +160,7 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||
|
||||
void requestMoreMembersAction(BuildContext context) async {
|
||||
final participants = await showFutureLoadingDialog(
|
||||
context: context, future: () => widget.room.requestParticipants());
|
||||
context: context, future: () => room.requestParticipants());
|
||||
if (participants.error == null) {
|
||||
setState(() => members = participants.result);
|
||||
}
|
||||
@ -175,7 +168,8 @@ class _ChatDetailsState extends State<ChatDetails> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.room == null) {
|
||||
room ??= Matrix.of(context).client.getRoomById(widget.roomId);
|
||||
if (room == null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
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);
|
||||
final actualMembersCount =
|
||||
widget.room.mInvitedMemberCount + widget.room.mJoinedMemberCount;
|
||||
room.mInvitedMemberCount + room.mJoinedMemberCount;
|
||||
final canRequestMoreMembers = members.length < actualMembersCount;
|
||||
return AdaptivePageLayout(
|
||||
primaryPage: FocusPage.SECOND,
|
||||
firstScaffold: ChatList(
|
||||
activeChat: widget.room.id,
|
||||
),
|
||||
secondScaffold: StreamBuilder(
|
||||
stream: widget.room.onUpdate.stream,
|
||||
builder: (context, snapshot) {
|
||||
return Scaffold(
|
||||
body: NestedScrollView(
|
||||
headerSliverBuilder:
|
||||
(BuildContext context, bool innerBoxIsScrolled) => <Widget>[
|
||||
SliverAppBar(
|
||||
expandedHeight: 300.0,
|
||||
floating: true,
|
||||
pinned: true,
|
||||
actions: <Widget>[
|
||||
if (widget.room.canonicalAlias?.isNotEmpty ?? false)
|
||||
IconButton(
|
||||
icon: Icon(Icons.share_outlined),
|
||||
onPressed: () => FluffyShare.share(
|
||||
AppConfig.inviteLinkPrefix +
|
||||
widget.room.canonicalAlias,
|
||||
context),
|
||||
),
|
||||
ChatSettingsPopupMenu(widget.room, false)
|
||||
],
|
||||
title: Text(
|
||||
widget.room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context))),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.appBarTheme
|
||||
.textTheme
|
||||
.headline6
|
||||
.color)),
|
||||
backgroundColor: Theme.of(context).appBarTheme.color,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background: ContentBanner(widget.room.avatar,
|
||||
onEdit: widget.room.canSendEvent('m.room.avatar')
|
||||
? () => setAvatarAction(context)
|
||||
: null),
|
||||
),
|
||||
return StreamBuilder(
|
||||
stream: room.onUpdate.stream,
|
||||
builder: (context, snapshot) {
|
||||
return Scaffold(
|
||||
body: NestedScrollView(
|
||||
headerSliverBuilder:
|
||||
(BuildContext context, bool innerBoxIsScrolled) => <Widget>[
|
||||
SliverAppBar(
|
||||
expandedHeight: 300.0,
|
||||
floating: true,
|
||||
pinned: true,
|
||||
actions: <Widget>[
|
||||
if (room.canonicalAlias?.isNotEmpty ?? false)
|
||||
IconButton(
|
||||
icon: Icon(Icons.share_outlined),
|
||||
onPressed: () => FluffyShare.share(
|
||||
AppConfig.inviteLinkPrefix + room.canonicalAlias,
|
||||
context),
|
||||
),
|
||||
ChatSettingsPopupMenu(room, false)
|
||||
],
|
||||
title: Text(
|
||||
room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context))),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.appBarTheme
|
||||
.textTheme
|
||||
.headline6
|
||||
.color)),
|
||||
backgroundColor: Theme.of(context).appBarTheme.color,
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background: ContentBanner(room.avatar,
|
||||
onEdit: room.canSendEvent('m.room.avatar')
|
||||
? () => setAvatarAction(context)
|
||||
: null),
|
||||
),
|
||||
],
|
||||
body: ListView.builder(
|
||||
itemCount:
|
||||
members.length + 1 + (canRequestMoreMembers ? 1 : 0),
|
||||
itemBuilder: (BuildContext context, int i) => i == 0
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
leading: widget.room.canSendEvent('m.room.topic')
|
||||
? CircleAvatar(
|
||||
backgroundColor: Theme.of(context)
|
||||
.scaffoldBackgroundColor,
|
||||
foregroundColor: Colors.grey,
|
||||
child: Icon(Icons.edit_outlined),
|
||||
)
|
||||
: null,
|
||||
title: Text(
|
||||
'${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,
|
||||
),
|
||||
],
|
||||
body: ListView.builder(
|
||||
itemCount: members.length + 1 + (canRequestMoreMembers ? 1 : 0),
|
||||
itemBuilder: (BuildContext context, int i) => i == 0
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
leading: room.canSendEvent('m.room.topic')
|
||||
? CircleAvatar(
|
||||
backgroundColor: Theme.of(context)
|
||||
.scaffoldBackgroundColor,
|
||||
foregroundColor: Colors.grey,
|
||||
child: Icon(Icons.edit_outlined),
|
||||
)
|
||||
: null,
|
||||
title: Text('${L10n.of(context).groupDescription}:',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
color: Theme.of(context).primaryColor,
|
||||
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(
|
||||
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),
|
||||
),
|
||||
),
|
||||
if (room.canSendEvent('m.room.name'))
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor: Colors.grey,
|
||||
child: Icon(Icons.insert_emoticon_outlined),
|
||||
child: Icon(Icons.people_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 ((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),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
title: Text(
|
||||
L10n.of(context).changeTheNameOfTheGroup),
|
||||
subtitle: Text(room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)))),
|
||||
onTap: () => setDisplaynameAction(context),
|
||||
),
|
||||
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(
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Theme.of(context)
|
||||
.scaffoldBackgroundColor,
|
||||
foregroundColor: Colors.grey,
|
||||
child: Icon(Icons.public_outlined)),
|
||||
title: Text(L10n.of(context)
|
||||
.whoIsAllowedToJoinThisGroup),
|
||||
backgroundColor:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor: Colors.grey,
|
||||
child: Icon(Icons.info_outline),
|
||||
),
|
||||
title: Text(
|
||||
L10n.of(context).areGuestsAllowedToJoin),
|
||||
subtitle: Text(
|
||||
widget.room.joinRules.getLocalizedString(
|
||||
room.guestAccess.getLocalizedString(
|
||||
MatrixLocals(L10n.of(context))),
|
||||
),
|
||||
),
|
||||
onSelected: (JoinRules joinRule) =>
|
||||
onSelected: (GuestAccess guestAccess) =>
|
||||
showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () =>
|
||||
widget.room.setJoinRules(joinRule),
|
||||
future: () => room.setGuestAccess(guestAccess),
|
||||
),
|
||||
itemBuilder: (BuildContext context) =>
|
||||
<PopupMenuEntry<JoinRules>>[
|
||||
if (widget.room.canChangeJoinRules)
|
||||
PopupMenuItem<JoinRules>(
|
||||
value: JoinRules.public,
|
||||
child: Text(JoinRules.public
|
||||
.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(
|
||||
<PopupMenuEntry<GuestAccess>>[
|
||||
if (room.canChangeGuestAccess)
|
||||
PopupMenuItem<GuestAccess>(
|
||||
value: GuestAccess.can_join,
|
||||
child: Text(
|
||||
GuestAccess.can_join.getLocalizedString(
|
||||
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)
|
||||
PopupMenuItem<HistoryVisibility>(
|
||||
value: HistoryVisibility.joined,
|
||||
child: Text(HistoryVisibility.joined
|
||||
.getLocalizedString(
|
||||
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 (room.canChangeGuestAccess)
|
||||
PopupMenuItem<GuestAccess>(
|
||||
value: GuestAccess.forbidden,
|
||||
child: Text(
|
||||
GuestAccess.forbidden.getLocalizedString(
|
||||
MatrixLocals(L10n.of(context))),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (widget.room.joinRules == JoinRules.public)
|
||||
PopupMenuButton(
|
||||
child: ListTile(
|
||||
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: () => 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(
|
||||
backgroundColor: Theme.of(context)
|
||||
.scaffoldBackgroundColor,
|
||||
foregroundColor: Colors.grey,
|
||||
child: Icon(Icons.info_outline),
|
||||
child: Icon(Icons.add_outlined),
|
||||
backgroundColor:
|
||||
Theme.of(context).primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
title: Text(
|
||||
L10n.of(context).areGuestsAllowedToJoin),
|
||||
subtitle: Text(
|
||||
widget.room.guestAccess.getLocalizedString(
|
||||
MatrixLocals(L10n.of(context))),
|
||||
),
|
||||
),
|
||||
onSelected: (GuestAccess guestAccess) =>
|
||||
showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () =>
|
||||
widget.room.setGuestAccess(guestAccess),
|
||||
),
|
||||
itemBuilder: (BuildContext context) =>
|
||||
<PopupMenuEntry<GuestAccess>>[
|
||||
if (widget.room.canChangeGuestAccess)
|
||||
PopupMenuItem<GuestAccess>(
|
||||
value: GuestAccess.can_join,
|
||||
child: Text(
|
||||
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),
|
||||
),
|
||||
onTap: () => AdaptivePageLayout.of(context)
|
||||
.pushNamed('/rooms/${room.id}/invite'),
|
||||
)
|
||||
: 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,
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
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),
|
||||
),
|
||||
),
|
||||
onTap: () => requestMoreMembersAction(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,14 @@
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:famedlysdk/encryption.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
||||
import 'package:fluffychat/components/avatar.dart';
|
||||
import 'package:fluffychat/components/matrix.dart';
|
||||
import 'package:fluffychat/utils/beautify_string_extension.dart';
|
||||
import 'package:fluffychat/views/chat_list.dart';
|
||||
import 'package:flushbar/flushbar_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.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 {
|
||||
final String id;
|
||||
|
||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/components/connection_status_header.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:receive_sharing_intent/receive_sharing_intent.dart';
|
||||
|
||||
import '../components/adaptive_page_layout.dart';
|
||||
import '../components/list_items/chat_list_item.dart';
|
||||
import '../components/matrix.dart';
|
||||
import '../utils/app_route.dart';
|
||||
import '../utils/matrix_file_extension.dart';
|
||||
import '../utils/url_launcher.dart';
|
||||
import 'empty_page.dart';
|
||||
import 'homeserver_picker.dart';
|
||||
import 'new_private_chat.dart';
|
||||
|
||||
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 {
|
||||
final String activeChat;
|
||||
|
||||
@ -86,9 +71,7 @@ class _ChatListState extends State<ChatList> {
|
||||
|
||||
void _processIncomingSharedFiles(List<SharedMediaFile> files) {
|
||||
if (files?.isEmpty ?? true) return;
|
||||
if (Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).popUntil((r) => r.isFirst);
|
||||
}
|
||||
AdaptivePageLayout.of(context).popUntilIsFirst();
|
||||
final file = File(files.first.path);
|
||||
|
||||
Matrix.of(context).shareContent = {
|
||||
@ -102,9 +85,7 @@ class _ChatListState extends State<ChatList> {
|
||||
|
||||
void _processIncomingSharedText(String text) {
|
||||
if (text == null) return;
|
||||
if (Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).popUntil((r) => r.isFirst);
|
||||
}
|
||||
AdaptivePageLayout.of(context).popUntilIsFirst();
|
||||
if (text.toLowerCase().startsWith(AppConfig.inviteLinkPrefix) ||
|
||||
(text.toLowerCase().startsWith(AppConfig.schemePrefix) &&
|
||||
!RegExp(r'\s').hasMatch(text))) {
|
||||
@ -194,205 +175,178 @@ class _ChatListState extends State<ChatList> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder<LoginState>(
|
||||
stream: Matrix.of(context).client.onLoginStateChanged.stream,
|
||||
return StreamBuilder(
|
||||
stream: Matrix.of(context).onShareContentChanged.stream,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.data == LoginState.loggedOut) {
|
||||
Timer(Duration(seconds: 1), () {
|
||||
Matrix.of(context).clean();
|
||||
Navigator.of(context).pushAndRemoveUntil(
|
||||
AppRoute.defaultRoute(context, HomeserverPicker()),
|
||||
(r) => false);
|
||||
});
|
||||
final selectMode = Matrix.of(context).shareContent == null
|
||||
? _selectedRoomIds.isEmpty
|
||||
? SelectMode.normal
|
||||
: SelectMode.select
|
||||
: SelectMode.share;
|
||||
if (selectMode == SelectMode.share) {
|
||||
_selectedRoomIds.clear();
|
||||
}
|
||||
return StreamBuilder(
|
||||
stream: Matrix.of(context).onShareContentChanged.stream,
|
||||
builder: (context, snapshot) {
|
||||
final selectMode = Matrix.of(context).shareContent == null
|
||||
? _selectedRoomIds.isEmpty
|
||||
? SelectMode.normal
|
||||
: SelectMode.select
|
||||
: SelectMode.share;
|
||||
if (selectMode == SelectMode.share) {
|
||||
_selectedRoomIds.clear();
|
||||
}
|
||||
Room selectedRoom;
|
||||
if (_selectedRoomIds.length == 1) {
|
||||
selectedRoom = Matrix.of(context)
|
||||
.client
|
||||
.getRoomById(_selectedRoomIds.single);
|
||||
}
|
||||
return Scaffold(
|
||||
drawer:
|
||||
selectMode != SelectMode.normal ? null : DefaultDrawer(),
|
||||
appBar: AppBar(
|
||||
centerTitle: false,
|
||||
elevation: _scrolledToTop ? 0 : null,
|
||||
leading: selectMode == SelectMode.share
|
||||
? IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
onPressed: () =>
|
||||
Matrix.of(context).shareContent = null,
|
||||
)
|
||||
: selectMode == SelectMode.select
|
||||
? IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
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),
|
||||
Room selectedRoom;
|
||||
if (_selectedRoomIds.length == 1) {
|
||||
selectedRoom =
|
||||
Matrix.of(context).client.getRoomById(_selectedRoomIds.single);
|
||||
}
|
||||
return Scaffold(
|
||||
drawer: selectMode != SelectMode.normal ? null : DefaultDrawer(),
|
||||
appBar: AppBar(
|
||||
centerTitle: false,
|
||||
elevation: _scrolledToTop ? 0 : null,
|
||||
leading: selectMode == SelectMode.share
|
||||
? IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
onPressed: () => Matrix.of(context).shareContent = null,
|
||||
)
|
||||
: selectMode == SelectMode.select
|
||||
? IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
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),
|
||||
),
|
||||
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(),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}),
|
||||
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.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(),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
||||
import 'package:fluffychat/components/dialogs/permission_slider_dialog.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.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: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 {
|
||||
final String roomId;
|
||||
|
||||
const ChatPermissionsSettings({Key key, @required this.roomId})
|
||||
: super(key: key);
|
||||
const ChatPermissionsSettings(this.roomId, {Key key}) : super(key: key);
|
||||
|
||||
void _editPowerLevel(BuildContext context, String key, int currentLevel,
|
||||
{String category}) async {
|
||||
|
@ -1,32 +1,15 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
||||
import 'package:fluffychat/components/avatar.dart';
|
||||
import 'package:fluffychat/components/default_app_bar_search_field.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.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_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 {
|
||||
final String alias;
|
||||
|
||||
@ -110,12 +93,8 @@ class _DiscoverPageState extends State<DiscoverPage> {
|
||||
),
|
||||
);
|
||||
if (success.error == null) {
|
||||
await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
ChatView(success.result),
|
||||
),
|
||||
);
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamedAndRemoveUntilIsFirst('/rooms/${success.result}');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,16 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/components/matrix.dart';
|
||||
import 'package:fluffychat/app_config.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/views/sign_up.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'login.dart';
|
||||
|
||||
class HomeserverPicker extends StatelessWidget {
|
||||
Future<void> _setHomeserverAction(BuildContext context) async {
|
||||
const prefix = 'https://';
|
||||
@ -45,8 +42,8 @@ class HomeserverPicker extends StatelessWidget {
|
||||
context: context,
|
||||
future: () => checkHomeserver(homeserver, Matrix.of(context).client));
|
||||
if (success.result == true) {
|
||||
await Navigator.of(context)
|
||||
.push(AppRoute(AppConfig.enableRegistration ? SignUp() : Login()));
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamed(AppConfig.enableRegistration ? '/signup' : '/login');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/components/image_bubble.dart';
|
||||
import 'package:fluffychat/components/matrix.dart';
|
||||
@ -13,7 +14,7 @@ class ImageView extends StatelessWidget {
|
||||
|
||||
void _forwardAction(BuildContext context) async {
|
||||
Matrix.of(context).shareContent = event.content;
|
||||
Navigator.of(context).popUntil((r) => r.isFirst);
|
||||
AdaptivePageLayout.of(context).popUntilIsFirst();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -3,7 +3,6 @@ import 'dart:async';
|
||||
import 'package:fluffychat/components/default_app_bar_search_field.dart';
|
||||
import 'package:flushbar/flushbar_helper.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
||||
import 'package:fluffychat/components/avatar.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.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 '../utils/localized_exception_extension.dart';
|
||||
import 'chat_list.dart';
|
||||
|
||||
class InvitationSelection extends StatefulWidget {
|
||||
final Room room;
|
||||
const InvitationSelection(this.room, {Key key}) : super(key: key);
|
||||
final String roomId;
|
||||
const InvitationSelection(this.roomId, {Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_InvitationSelectionState createState() => _InvitationSelectionState();
|
||||
@ -27,11 +25,12 @@ class _InvitationSelectionState extends State<InvitationSelection> {
|
||||
bool loading = false;
|
||||
List<Profile> foundProfiles = [];
|
||||
Timer coolDown;
|
||||
Room room;
|
||||
|
||||
Future<List<User>> getContacts(BuildContext context) async {
|
||||
var client2 = Matrix.of(context).client;
|
||||
final client = client2;
|
||||
var participants = await widget.room.requestParticipants();
|
||||
var participants = await room.requestParticipants();
|
||||
participants.removeWhere(
|
||||
(u) => ![Membership.join, Membership.invite].contains(u.membership),
|
||||
);
|
||||
@ -59,7 +58,7 @@ class _InvitationSelectionState extends State<InvitationSelection> {
|
||||
void inviteAction(BuildContext context, String id) async {
|
||||
final success = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => widget.room.invite(id),
|
||||
future: () => room.invite(id),
|
||||
);
|
||||
if (success.error == null) {
|
||||
await FlushbarHelper.createSuccess(
|
||||
@ -107,7 +106,7 @@ class _InvitationSelectionState extends State<InvitationSelection> {
|
||||
Profile.fromJson({'user_id': '@$text'}),
|
||||
]);
|
||||
}
|
||||
final participants = widget.room
|
||||
final participants = room
|
||||
.getParticipants()
|
||||
.where((user) =>
|
||||
[Membership.join, Membership.invite].contains(user.membership))
|
||||
@ -119,60 +118,57 @@ class _InvitationSelectionState extends State<InvitationSelection> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final groupName = widget.room.name?.isEmpty ?? false
|
||||
? L10n.of(context).group
|
||||
: widget.room.name;
|
||||
return AdaptivePageLayout(
|
||||
primaryPage: FocusPage.SECOND,
|
||||
firstScaffold: ChatList(activeChat: widget.room.id),
|
||||
secondScaffold: Scaffold(
|
||||
appBar: AppBar(
|
||||
titleSpacing: 0,
|
||||
title: DefaultAppBarSearchField(
|
||||
autofocus: true,
|
||||
hintText: L10n.of(context).inviteContactToGroup(groupName),
|
||||
onChanged: (String text) => searchUserWithCoolDown(context, text),
|
||||
),
|
||||
),
|
||||
body: foundProfiles.isNotEmpty
|
||||
? ListView.builder(
|
||||
itemCount: foundProfiles.length,
|
||||
room ??= Matrix.of(context).client.getRoomById(widget.roomId);
|
||||
final groupName =
|
||||
room.name?.isEmpty ?? false ? L10n.of(context).group : room.name;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
titleSpacing: 0,
|
||||
title: DefaultAppBarSearchField(
|
||||
autofocus: true,
|
||||
hintText: L10n.of(context).inviteContactToGroup(groupName),
|
||||
onChanged: (String text) => searchUserWithCoolDown(context, text),
|
||||
),
|
||||
),
|
||||
body: foundProfiles.isNotEmpty
|
||||
? ListView.builder(
|
||||
itemCount: foundProfiles.length,
|
||||
itemBuilder: (BuildContext context, int i) => ListTile(
|
||||
leading: Avatar(
|
||||
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(
|
||||
leading: Avatar(
|
||||
foundProfiles[i].avatarUrl,
|
||||
foundProfiles[i].displayname ?? foundProfiles[i].userId,
|
||||
contacts[i].avatarUrl,
|
||||
contacts[i].calcDisplayname(),
|
||||
),
|
||||
title: Text(
|
||||
foundProfiles[i].displayname ??
|
||||
foundProfiles[i].userId.localpart,
|
||||
),
|
||||
subtitle: Text(foundProfiles[i].userId),
|
||||
onTap: () => inviteAction(context, foundProfiles[i].userId),
|
||||
title: Text(contacts[i].calcDisplayname()),
|
||||
subtitle: Text(contacts[i].id),
|
||||
onTap: () => inviteAction(context, contacts[i].id),
|
||||
),
|
||||
)
|
||||
: 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),
|
||||
),
|
||||
);
|
||||
},
|
||||
)),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
38
lib/views/loading_view.dart
Normal file
38
lib/views/loading_view.dart
Normal 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();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -5,15 +5,10 @@ import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.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:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'chat_list.dart';
|
||||
|
||||
class Login extends StatefulWidget {
|
||||
@override
|
||||
_LoginState createState() => _LoginState();
|
||||
@ -57,14 +52,8 @@ class _LoginState extends State<Login> {
|
||||
setState(() => passwordError = exception.toString());
|
||||
return setState(() => loading = false);
|
||||
}
|
||||
await FirebaseController.setupFirebase(
|
||||
matrix,
|
||||
matrix.clientName,
|
||||
).catchError(SentryController.captureException);
|
||||
|
||||
setState(() => loading = false);
|
||||
await Navigator.of(context).pushAndRemoveUntil(
|
||||
AppRoute.defaultRoute(context, ChatListView()), (r) => false);
|
||||
if (mounted) setState(() => loading = false);
|
||||
}
|
||||
|
||||
Timer _coolDown;
|
||||
|
@ -1,33 +1,16 @@
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
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:fluffychat/components/matrix.dart';
|
||||
import 'package:fluffychat/utils/app_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
|
||||
import 'chat.dart';
|
||||
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 {
|
||||
class NewGroup extends StatefulWidget {
|
||||
@override
|
||||
_NewGroupState createState() => _NewGroupState();
|
||||
}
|
||||
|
||||
class _NewGroupState extends State<_NewGroup> {
|
||||
class _NewGroupState extends State<NewGroup> {
|
||||
TextEditingController controller = TextEditingController();
|
||||
bool publicGroup = false;
|
||||
|
||||
@ -45,24 +28,11 @@ class _NewGroupState extends State<_NewGroup> {
|
||||
name: controller.text.isNotEmpty ? controller.text : null,
|
||||
),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
AdaptivePageLayout.of(context).popUntilIsFirst();
|
||||
if (roomID != null) {
|
||||
unawaited(
|
||||
Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
ChatView(roomID.result),
|
||||
),
|
||||
),
|
||||
);
|
||||
await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => InvitationSelection(
|
||||
matrix.client.getRoomById(roomID.result),
|
||||
),
|
||||
),
|
||||
);
|
||||
await AdaptivePageLayout.of(context).pushNamed('/rooms/${roomID.result}');
|
||||
await AdaptivePageLayout.of(context)
|
||||
.pushNamed('/rooms/${roomID.result}/invite');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,35 +1,20 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
||||
import 'package:fluffychat/components/avatar.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/components/matrix.dart';
|
||||
import 'package:fluffychat/utils/app_route.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'chat.dart';
|
||||
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 {
|
||||
class NewPrivateChat extends StatefulWidget {
|
||||
@override
|
||||
_NewPrivateChatState createState() => _NewPrivateChatState();
|
||||
}
|
||||
|
||||
class _NewPrivateChatState extends State<_NewPrivateChat> {
|
||||
class _NewPrivateChatState extends State<NewPrivateChat> {
|
||||
TextEditingController controller = TextEditingController();
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool loading = false;
|
||||
@ -59,15 +44,10 @@ class _NewPrivateChatState extends State<_NewPrivateChat> {
|
||||
context: context,
|
||||
future: () => user.startDirectChat(),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
|
||||
if (roomID.error == null) {
|
||||
await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
ChatView(roomID.result),
|
||||
),
|
||||
);
|
||||
await AdaptivePageLayout.of(context)
|
||||
.popAndPushNamed('/rooms/${roomID.result}');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,6 @@
|
||||
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/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:famedlysdk/famedlysdk.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/utils/platform_infos.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_gen/gen_l10n/l10n.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../components/adaptive_page_layout.dart';
|
||||
import '../components/content_banner.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import '../components/matrix.dart';
|
||||
import '../utils/app_route.dart';
|
||||
import '../app_config.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 {
|
||||
@override
|
||||
@ -333,12 +313,8 @@ class _SettingsState extends State<Settings> {
|
||||
ListTile(
|
||||
trailing: Icon(Icons.notifications_outlined),
|
||||
title: Text(L10n.of(context).notifications),
|
||||
onTap: () async => await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
SettingsNotificationsView(),
|
||||
),
|
||||
),
|
||||
onTap: () => AdaptivePageLayout.of(context)
|
||||
.pushNamed('/settings/notifications'),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
@ -351,12 +327,8 @@ class _SettingsState extends State<Settings> {
|
||||
),
|
||||
ListTile(
|
||||
title: Text(L10n.of(context).changeTheme),
|
||||
onTap: () async => await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
SettingsStyleView(),
|
||||
),
|
||||
),
|
||||
onTap: () =>
|
||||
AdaptivePageLayout.of(context).pushNamed('/settings/style'),
|
||||
trailing: Icon(Icons.style_outlined),
|
||||
),
|
||||
SwitchListTile(
|
||||
@ -392,12 +364,8 @@ class _SettingsState extends State<Settings> {
|
||||
),
|
||||
ListTile(
|
||||
title: Text(L10n.of(context).emoteSettings),
|
||||
onTap: () async => await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
EmotesSettingsView(),
|
||||
),
|
||||
),
|
||||
onTap: () =>
|
||||
AdaptivePageLayout.of(context).pushNamed('/settings/emotes'),
|
||||
trailing: Icon(Icons.insert_emoticon_outlined),
|
||||
),
|
||||
Divider(thickness: 1),
|
||||
@ -425,22 +393,14 @@ class _SettingsState extends State<Settings> {
|
||||
ListTile(
|
||||
trailing: Icon(Icons.devices_other_outlined),
|
||||
title: Text(L10n.of(context).devices),
|
||||
onTap: () async => await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
DevicesSettingsView(),
|
||||
),
|
||||
),
|
||||
onTap: () =>
|
||||
AdaptivePageLayout.of(context).pushNamed('/settings/devices'),
|
||||
),
|
||||
ListTile(
|
||||
trailing: Icon(Icons.block_outlined),
|
||||
title: Text(L10n.of(context).ignoredUsers),
|
||||
onTap: () async => await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
SettingsIgnoreListView(),
|
||||
),
|
||||
),
|
||||
onTap: () =>
|
||||
AdaptivePageLayout.of(context).pushNamed('/settings/ignore'),
|
||||
),
|
||||
ListTile(
|
||||
trailing: Icon(Icons.bug_report_outlined),
|
||||
@ -458,12 +418,8 @@ class _SettingsState extends State<Settings> {
|
||||
ListTile(
|
||||
trailing: Icon(Icons.email_outlined),
|
||||
title: Text(L10n.of(context).passwordRecovery),
|
||||
onTap: () => Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
Settings3PidView(),
|
||||
),
|
||||
),
|
||||
onTap: () =>
|
||||
AdaptivePageLayout.of(context).pushNamed('/settings/3pid'),
|
||||
),
|
||||
ListTile(
|
||||
trailing: Icon(Icons.exit_to_app_outlined),
|
||||
@ -607,9 +563,7 @@ class _SettingsState extends State<Settings> {
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
onTap: () => Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(context, LogViewer()),
|
||||
),
|
||||
onTap: () => AdaptivePageLayout.of(context).pushNamed('/logs'),
|
||||
),
|
||||
ListTile(
|
||||
trailing: Icon(Icons.help_outlined),
|
||||
|
@ -1,24 +1,10 @@
|
||||
import 'package:adaptive_dialog/adaptive_dialog.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:fluffychat/components/matrix.dart';
|
||||
import 'package:flutter/material.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 {
|
||||
static int sendAttempt = 0;
|
||||
|
||||
|
@ -4,21 +4,8 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import '../components/adaptive_page_layout.dart';
|
||||
import '../components/matrix.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 {
|
||||
@override
|
||||
|
@ -10,26 +10,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
import '../components/adaptive_page_layout.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.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 {
|
||||
final Room room;
|
||||
|
@ -1,23 +1,10 @@
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
||||
import 'package:fluffychat/components/avatar.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.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 {
|
||||
final controller = TextEditingController();
|
||||
|
@ -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:famedlysdk/famedlysdk.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 {
|
||||
final Room room;
|
||||
final String roomId;
|
||||
|
||||
MultipleEmotesSettings({this.room});
|
||||
MultipleEmotesSettings(this.roomId, {Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).emotePacks),
|
||||
@ -58,11 +42,9 @@ class MultipleEmotesSettings extends StatelessWidget {
|
||||
return ListTile(
|
||||
title: Text(packName),
|
||||
onTap: () async {
|
||||
await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
EmotesSettingsView(room: room, stateKey: keys[i]),
|
||||
),
|
||||
await AdaptivePageLayout.of(context).pushNamed(
|
||||
'/settings/emotes',
|
||||
arguments: room,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/app_config.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 '../components/matrix.dart';
|
||||
import 'chat_list.dart';
|
||||
|
||||
class NotificationSettingsItem {
|
||||
final PushRuleKind type;
|
||||
@ -20,17 +18,6 @@ class NotificationSettingsItem {
|
||||
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 {
|
||||
static List<NotificationSettingsItem> items = [
|
||||
NotificationSettingsItem(
|
||||
|
@ -1,25 +1,12 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:adaptive_theme/adaptive_theme.dart';
|
||||
import 'package:fluffychat/components/adaptive_page_layout.dart';
|
||||
import 'package:fluffychat/config/setting_keys.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:image_picker/image_picker.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 {
|
||||
@override
|
||||
|
@ -1,12 +1,10 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:file_picker_cross/file_picker_cross.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/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
@ -61,11 +59,9 @@ class _SignUpState extends State<SignUp> {
|
||||
return setState(() => loading = false);
|
||||
}
|
||||
setState(() => loading = false);
|
||||
await Navigator.of(context).push(
|
||||
AppRoute(
|
||||
SignUpPassword(preferredUsername,
|
||||
avatar: avatar, displayname: usernameController.text),
|
||||
),
|
||||
await AdaptivePageLayout.of(context).pushNamed(
|
||||
'/signup/password/${Uri.encodeComponent(preferredUsername)}/${Uri.encodeComponent(usernameController.text)}',
|
||||
arguments: avatar,
|
||||
);
|
||||
}
|
||||
|
||||
@ -171,9 +167,8 @@ class _SignUpState extends State<SignUp> {
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
onPressed: () => Navigator.of(context).push(
|
||||
AppRoute(Login()),
|
||||
),
|
||||
onPressed: () =>
|
||||
AdaptivePageLayout.of(context).pushNamed('/login'),
|
||||
),
|
||||
),
|
||||
]),
|
||||
|
@ -1,16 +1,13 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||
import 'package:flushbar/flushbar_helper.dart';
|
||||
import 'package:famedlysdk/famedlysdk.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_gen/gen_l10n/l10n.dart';
|
||||
|
||||
import 'chat_list.dart';
|
||||
|
||||
class SignUpPassword extends StatefulWidget {
|
||||
final MatrixFile avatar;
|
||||
final String username;
|
||||
@ -69,17 +66,11 @@ class _SignUpPasswordState extends State<SignUpPassword> {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
await AdaptivePageLayout.of(context).pushNamed(
|
||||
'/authwebview',
|
||||
arguments: () => _signUpAction(
|
||||
context,
|
||||
AuthWebView(
|
||||
currentStage,
|
||||
exception.session,
|
||||
() => _signUpAction(
|
||||
context,
|
||||
auth: AuthenticationData(session: exception.session),
|
||||
),
|
||||
),
|
||||
auth: AuthenticationData(session: exception.session),
|
||||
),
|
||||
);
|
||||
return;
|
||||
@ -111,9 +102,7 @@ class _SignUpPasswordState extends State<SignUpPassword> {
|
||||
.show(context);
|
||||
}
|
||||
}
|
||||
await Navigator.of(context).pushAndRemoveUntil(
|
||||
AppRoute.defaultRoute(context, ChatListView()), (r) => false);
|
||||
setState(() => loading = false);
|
||||
if (mounted) setState(() => loading = false);
|
||||
}
|
||||
|
||||
@override
|
||||
|
Loading…
Reference in New Issue
Block a user