Merge branch 'krille/apl' into 'main'

refactor: Use APL

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

View File

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

View File

@ -1,10 +1,8 @@
import 'dart:async';
import '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;
}
},

View File

@ -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',
),
),
],

View File

@ -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) {

View File

@ -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}');
}
}
}

View File

@ -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(

View File

@ -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}');
}
}

View File

@ -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) {

View File

@ -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
View File

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

View File

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

View File

@ -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),
),
),
),
),

View File

@ -1,10 +1,11 @@
import 'package:fluffychat/components/adaptive_page_layout.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/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,
)

View File

@ -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);

View File

@ -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}');
}
}
}

View File

@ -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,
);
}
}

View File

@ -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();
},
),

View File

@ -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,

View File

@ -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),
),
),
);
}),
);
),
);
});
}
}

View File

@ -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;

View File

@ -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(),
);
}
},
);
}),
),
],
),
);
});
}
}

View File

@ -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 {

View File

@ -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}');
}
}

View File

@ -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');
}
}

View File

@ -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

View File

@ -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),
),
);
},
)),
);
},
),
);
}
}

View File

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

View File

@ -5,15 +5,10 @@ import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package: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;

View File

@ -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');
}
}

View File

@ -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}');
}
}

View File

@ -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),

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -1,33 +1,17 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:fluffychat/components/matrix.dart';
import 'package:flutter/material.dart';
import 'package: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,
);
},
);

View File

@ -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(

View File

@ -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

View File

@ -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'),
),
),
]),

View File

@ -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