From 75258271af4aedcc6d82968b3167ff207e474d63 Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Fri, 9 Apr 2021 18:26:44 +0200 Subject: [PATCH] design: Column mode auto padding --- lib/views/chat_details.dart | 518 ++++++++++++----------- lib/views/chat_encryption_settings.dart | 262 ++++++------ lib/views/chat_permissions_settings.dart | 245 +++++------ lib/views/invitation_selection.dart | 84 ++-- lib/views/new_group.dart | 53 +-- lib/views/new_private_chat.dart | 227 +++++----- lib/views/settings_3pid.dart | 108 ++--- lib/views/settings_devices.dart | 152 +++---- lib/views/settings_emotes.dart | 431 +++++++++---------- lib/views/settings_ignore_list.dart | 121 +++--- lib/views/settings_notifications.dart | 163 +++---- lib/views/settings_style.dart | 181 ++++---- lib/views/widgets/max_width_body.dart | 38 ++ 13 files changed, 1344 insertions(+), 1239 deletions(-) create mode 100644 lib/views/widgets/max_width_body.dart diff --git a/lib/views/chat_details.dart b/lib/views/chat_details.dart index b5f33601..a130bb71 100644 --- a/lib/views/chat_details.dart +++ b/lib/views/chat_details.dart @@ -10,6 +10,7 @@ import 'package:famedlysdk/famedlysdk.dart'; import 'package:file_picker_cross/file_picker_cross.dart'; import 'package:fluffychat/views/widgets/chat_settings_popup_menu.dart'; import 'package:fluffychat/views/widgets/content_banner.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/views/widgets/list_items/participant_list_item.dart'; import 'package:fluffychat/utils/matrix_locals.dart'; @@ -234,223 +235,188 @@ class _ChatDetailsState extends State { ), ), ], - body: ListView.builder( - itemCount: members.length + 1 + (canRequestMoreMembers ? 1 : 0), - itemBuilder: (BuildContext context, int i) => i == 0 - ? Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ListTile( - leading: room.canSendEvent('m.room.topic') - ? CircleAvatar( - backgroundColor: Theme.of(context) - .scaffoldBackgroundColor, - foregroundColor: Colors.grey, - radius: Avatar.defaultSize / 2, - child: Icon(Icons.edit_outlined), - ) - : null, - title: Text('${L10n.of(context).groupDescription}:', - style: TextStyle( - color: Theme.of(context).accentColor, - 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).accentColor, - fontWeight: FontWeight.bold, - ), - ), - ), - if (room.canSendEvent('m.room.name')) + body: MaxWidthBody( + child: ListView.builder( + itemCount: + members.length + 1 + (canRequestMoreMembers ? 1 : 0), + itemBuilder: (BuildContext context, int i) => i == 0 + ? Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(Icons.people_outlined), - ), + leading: room.canSendEvent('m.room.topic') + ? CircleAvatar( + backgroundColor: Theme.of(context) + .scaffoldBackgroundColor, + foregroundColor: Colors.grey, + radius: Avatar.defaultSize / 2, + child: Icon(Icons.edit_outlined), + ) + : null, 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), + '${L10n.of(context).groupDescription}:', + style: TextStyle( + color: Theme.of(context).accentColor, + 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: () => setCanonicalAliasAction(context), - title: Text(L10n.of(context).setInvitationLink), - subtitle: Text( - (room.canonicalAlias?.isNotEmpty ?? false) - ? room.canonicalAlias - : L10n.of(context).none), + onTap: room.canSendEvent('m.room.topic') + ? () => setTopicAction(context) + : null, ), - 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['im.ponies.room_emotes'] ?? - {}) - .keys - .any((String s) => s.isNotEmpty)) { - await AdaptivePageLayout.of(context) - .pushNamed('/rooms/${room.id}/emotes'); - } else { - await AdaptivePageLayout.of(context).pushNamed( - '/settings/emotes', - arguments: {'room': room}); - } - }, - ), - PopupMenuButton( - onSelected: (JoinRules joinRule) => - showFutureLoadingDialog( - context: context, - future: () => room.setJoinRules(joinRule), - ), - itemBuilder: (BuildContext context) => - >[ - if (room.canChangeJoinRules) - PopupMenuItem( - value: JoinRules.public, - child: Text(JoinRules.public - .getLocalizedString( - MatrixLocals(L10n.of(context)))), + Divider(thickness: 1), + ListTile( + title: Text( + L10n.of(context).settings, + style: TextStyle( + color: Theme.of(context).accentColor, + fontWeight: FontWeight.bold, ), - if (room.canChangeJoinRules) - PopupMenuItem( - value: JoinRules.invite, - child: Text(JoinRules.invite - .getLocalizedString( - MatrixLocals(L10n.of(context)))), - ), - ], - child: ListTile( - leading: CircleAvatar( + ), + ), + if (room.canSendEvent('m.room.name')) + 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))), + child: Icon(Icons.people_outlined), + ), + title: Text( + L10n.of(context).changeTheNameOfTheGroup), + subtitle: Text(room.getLocalizedDisplayname( + MatrixLocals(L10n.of(context)))), + onTap: () => setDisplaynameAction(context), ), - ), - ), - PopupMenuButton( - onSelected: (HistoryVisibility historyVisibility) => - showFutureLoadingDialog( - context: context, - future: () => - room.setHistoryVisibility(historyVisibility), - ), - itemBuilder: (BuildContext context) => - >[ - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.invited, - child: Text(HistoryVisibility.invited - .getLocalizedString( - MatrixLocals(L10n.of(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), ), - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.joined, - child: Text(HistoryVisibility.joined - .getLocalizedString( - MatrixLocals(L10n.of(context)))), - ), - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.shared, - child: Text(HistoryVisibility.shared - .getLocalizedString( - MatrixLocals(L10n.of(context)))), - ), - if (room.canChangeHistoryVisibility) - PopupMenuItem( - value: HistoryVisibility.world_readable, - child: Text(HistoryVisibility.world_readable - .getLocalizedString( - MatrixLocals(L10n.of(context)))), - ), - ], - child: ListTile( + 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.visibility_outlined), - ), - title: Text( - L10n.of(context).visibilityOfTheChatHistory), - subtitle: Text( - room.historyVisibility.getLocalizedString( - MatrixLocals(L10n.of(context))), + 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['im.ponies.room_emotes'] ?? + {}) + .keys + .any((String s) => s.isNotEmpty)) { + await AdaptivePageLayout.of(context) + .pushNamed('/rooms/${room.id}/emotes'); + } else { + await AdaptivePageLayout.of(context) + .pushNamed('/settings/emotes', + arguments: {'room': room}); + } + }, ), - ), - if (room.joinRules == JoinRules.public) PopupMenuButton( - onSelected: (GuestAccess guestAccess) => + onSelected: (JoinRules joinRule) => showFutureLoadingDialog( context: context, - future: () => room.setGuestAccess(guestAccess), + future: () => room.setJoinRules(joinRule), ), itemBuilder: (BuildContext context) => - >[ - if (room.canChangeGuestAccess) - PopupMenuItem( - value: GuestAccess.can_join, - child: Text( - GuestAccess.can_join.getLocalizedString( - MatrixLocals(L10n.of(context))), - ), + >[ + if (room.canChangeJoinRules) + PopupMenuItem( + value: JoinRules.public, + child: Text(JoinRules.public + .getLocalizedString( + MatrixLocals(L10n.of(context)))), ), - if (room.canChangeGuestAccess) - PopupMenuItem( - value: GuestAccess.forbidden, - child: Text( - GuestAccess.forbidden.getLocalizedString( - MatrixLocals(L10n.of(context))), - ), + if (room.canChangeJoinRules) + PopupMenuItem( + value: JoinRules.invite, + child: Text(JoinRules.invite + .getLocalizedString( + MatrixLocals(L10n.of(context)))), + ), + ], + 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))), + ), + ), + ), + PopupMenuButton( + onSelected: + (HistoryVisibility historyVisibility) => + showFutureLoadingDialog( + context: context, + future: () => room + .setHistoryVisibility(historyVisibility), + ), + itemBuilder: (BuildContext context) => + >[ + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.invited, + child: Text(HistoryVisibility.invited + .getLocalizedString( + MatrixLocals(L10n.of(context)))), + ), + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.joined, + child: Text(HistoryVisibility.joined + .getLocalizedString( + MatrixLocals(L10n.of(context)))), + ), + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.shared, + child: Text(HistoryVisibility.shared + .getLocalizedString( + MatrixLocals(L10n.of(context)))), + ), + if (room.canChangeHistoryVisibility) + PopupMenuItem( + value: HistoryVisibility.world_readable, + child: Text(HistoryVisibility.world_readable + .getLocalizedString( + MatrixLocals(L10n.of(context)))), ), ], child: ListTile( @@ -458,75 +424,119 @@ class _ChatDetailsState extends State { backgroundColor: Theme.of(context).scaffoldBackgroundColor, foregroundColor: Colors.grey, - child: Icon(Icons.info_outline), + child: Icon(Icons.visibility_outlined), ), - title: Text( - L10n.of(context).areGuestsAllowedToJoin), + title: Text(L10n.of(context) + .visibilityOfTheChatHistory), subtitle: Text( - room.guestAccess.getLocalizedString( + room.historyVisibility.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: () => 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).accentColor, - fontWeight: FontWeight.bold, - ), - ), - ), - room.canInvite - ? ListTile( - title: Text(L10n.of(context).inviteContact), + if (room.joinRules == JoinRules.public) + PopupMenuButton( + onSelected: (GuestAccess guestAccess) => + showFutureLoadingDialog( + context: context, + future: () => + room.setGuestAccess(guestAccess), + ), + itemBuilder: (BuildContext context) => + >[ + if (room.canChangeGuestAccess) + PopupMenuItem( + value: GuestAccess.can_join, + child: Text( + GuestAccess.can_join.getLocalizedString( + MatrixLocals(L10n.of(context))), + ), + ), + if (room.canChangeGuestAccess) + PopupMenuItem( + value: GuestAccess.forbidden, + child: Text( + GuestAccess.forbidden + .getLocalizedString( + MatrixLocals(L10n.of(context))), + ), + ), + ], + child: ListTile( leading: CircleAvatar( - backgroundColor: - Theme.of(context).primaryColor, - foregroundColor: Colors.white, - radius: Avatar.defaultSize / 2, - child: Icon(Icons.add_outlined), + backgroundColor: Theme.of(context) + .scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(Icons.info_outline), ), - 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, + title: Text( + L10n.of(context).areGuestsAllowedToJoin), + subtitle: Text( + room.guestAccess.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: () => 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).accentColor, + fontWeight: FontWeight.bold, + ), ), ), - onTap: () => requestMoreMembersAction(context), - ), + room.canInvite + ? ListTile( + title: Text(L10n.of(context).inviteContact), + leading: CircleAvatar( + backgroundColor: + Theme.of(context).primaryColor, + foregroundColor: Colors.white, + radius: Avatar.defaultSize / 2, + child: Icon(Icons.add_outlined), + ), + 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, + ), + ), + onTap: () => requestMoreMembersAction(context), + ), + ), ), ), ); diff --git a/lib/views/chat_encryption_settings.dart b/lib/views/chat_encryption_settings.dart index ecf23c3f..7ba4a544 100644 --- a/lib/views/chat_encryption_settings.dart +++ b/lib/views/chat_encryption_settings.dart @@ -2,6 +2,7 @@ import 'package:famedlysdk/encryption.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/views/widgets/avatar.dart'; import 'package:fluffychat/views/widgets/matrix.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import '../views/widgets/dialogs/key_verification_dialog.dart'; @@ -81,65 +82,132 @@ class _ChatEncryptionSettingsState extends State { ), ), ), - body: StreamBuilder( - stream: room.onUpdate.stream, - builder: (context, snapshot) { - return FutureBuilder>( - future: room.getUserDeviceKeys(), - builder: (BuildContext context, snapshot) { - if (snapshot.hasError) { - return Center( - child: Text(L10n.of(context).oopsSomethingWentWrong + - ': ' + - snapshot.error.toString()), - ); - } - if (!snapshot.hasData) { - return Center(child: CircularProgressIndicator()); - } - final deviceKeys = snapshot.data; - return ListView.builder( - itemCount: deviceKeys.length, - itemBuilder: (BuildContext context, int i) => Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (i == 0 || - deviceKeys[i].userId != deviceKeys[i - 1].userId) ...{ - Divider(height: 1, thickness: 1), + body: MaxWidthBody( + withScrolling: true, + child: StreamBuilder( + stream: room.onUpdate.stream, + builder: (context, snapshot) { + return FutureBuilder>( + future: room.getUserDeviceKeys(), + builder: (BuildContext context, snapshot) { + if (snapshot.hasError) { + return Center( + child: Text(L10n.of(context).oopsSomethingWentWrong + + ': ' + + snapshot.error.toString()), + ); + } + if (!snapshot.hasData) { + return Center(child: CircularProgressIndicator()); + } + final deviceKeys = snapshot.data; + return ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: deviceKeys.length, + itemBuilder: (BuildContext context, int i) => Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (i == 0 || + deviceKeys[i].userId != + deviceKeys[i - 1].userId) ...{ + Divider(height: 1, thickness: 1), + PopupMenuButton( + onSelected: (action) => + onSelected(context, action, deviceKeys[i]), + itemBuilder: (c) { + var items = >[]; + if (room + .client + .userDeviceKeys[deviceKeys[i].userId] + .verified == + UserVerifiedStatus.unknown) { + items.add(PopupMenuItem( + value: 'verify_user', + child: Text(L10n.of(context).verifyUser), + )); + } + return items; + }, + child: ListTile( + leading: Avatar( + room + .getUserByMXIDSync(deviceKeys[i].userId) + .avatarUrl, + room + .getUserByMXIDSync(deviceKeys[i].userId) + .calcDisplayname(), + ), + title: Row( + children: [ + Text( + room + .getUserByMXIDSync(deviceKeys[i].userId) + .calcDisplayname(), + ), + Spacer(), + Text( + deviceKeys[i].userId, + style: TextStyle( + fontSize: 14, + color: Theme.of(context) + .textTheme + .bodyText1 + .color + .withAlpha(150), + ), + ), + ], + ), + ), + ), + }, PopupMenuButton( onSelected: (action) => onSelected(context, action, deviceKeys[i]), itemBuilder: (c) { var items = >[]; - if (room.client.userDeviceKeys[deviceKeys[i].userId] - .verified == - UserVerifiedStatus.unknown) { + if (deviceKeys[i].blocked || + !deviceKeys[i].verified) { items.add(PopupMenuItem( - value: 'verify_user', - child: Text(L10n.of(context).verifyUser), + value: + deviceKeys[i].userId == room.client.userID + ? 'verify' + : 'verify_user', + child: Text(L10n.of(context).verifyStart), + )); + } + if (deviceKeys[i].blocked) { + items.add(PopupMenuItem( + value: 'unblock', + child: Text(L10n.of(context).unblockDevice), + )); + } + if (!deviceKeys[i].blocked) { + items.add(PopupMenuItem( + value: 'block', + child: Text(L10n.of(context).blockDevice), )); } return items; }, child: ListTile( - leading: Avatar( - room - .getUserByMXIDSync(deviceKeys[i].userId) - .avatarUrl, - room - .getUserByMXIDSync(deviceKeys[i].userId) - .calcDisplayname(), + leading: CircleAvatar( + foregroundColor: + Theme.of(context).textTheme.bodyText1.color, + backgroundColor: + Theme.of(context).secondaryHeaderColor, + child: Icon(deviceKeys[i].icon), ), - title: Row( + title: Text( + deviceKeys[i].displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Row( children: [ Text( - room - .getUserByMXIDSync(deviceKeys[i].userId) - .calcDisplayname(), - ), - Spacer(), - Text( - deviceKeys[i].userId, + deviceKeys[i].deviceId, style: TextStyle( fontSize: 14, color: Theme.of(context) @@ -149,91 +217,33 @@ class _ChatEncryptionSettingsState extends State { .withAlpha(150), ), ), + Spacer(), + Text( + deviceKeys[i].blocked + ? L10n.of(context).blocked + : deviceKeys[i].verified + ? L10n.of(context).verified + : L10n.of(context).unknownDevice, + style: TextStyle( + fontSize: 14, + color: deviceKeys[i].blocked + ? Colors.red + : deviceKeys[i].verified + ? Colors.green + : Colors.orange, + ), + ), ], ), ), ), - }, - PopupMenuButton( - onSelected: (action) => - onSelected(context, action, deviceKeys[i]), - itemBuilder: (c) { - var items = >[]; - if (deviceKeys[i].blocked || - !deviceKeys[i].verified) { - items.add(PopupMenuItem( - value: deviceKeys[i].userId == room.client.userID - ? 'verify' - : 'verify_user', - child: Text(L10n.of(context).verifyStart), - )); - } - if (deviceKeys[i].blocked) { - items.add(PopupMenuItem( - value: 'unblock', - child: Text(L10n.of(context).unblockDevice), - )); - } - if (!deviceKeys[i].blocked) { - items.add(PopupMenuItem( - value: 'block', - child: Text(L10n.of(context).blockDevice), - )); - } - return items; - }, - child: ListTile( - leading: CircleAvatar( - foregroundColor: - Theme.of(context).textTheme.bodyText1.color, - backgroundColor: - Theme.of(context).secondaryHeaderColor, - child: Icon(deviceKeys[i].icon), - ), - title: Text( - deviceKeys[i].displayname, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - subtitle: Row( - children: [ - Text( - deviceKeys[i].deviceId, - style: TextStyle( - fontSize: 14, - color: Theme.of(context) - .textTheme - .bodyText1 - .color - .withAlpha(150), - ), - ), - Spacer(), - Text( - deviceKeys[i].blocked - ? L10n.of(context).blocked - : deviceKeys[i].verified - ? L10n.of(context).verified - : L10n.of(context).unknownDevice, - style: TextStyle( - fontSize: 14, - color: deviceKeys[i].blocked - ? Colors.red - : deviceKeys[i].verified - ? Colors.green - : Colors.orange, - ), - ), - ], - ), - ), - ), - ], - ), - ); - }, - ); - }), + ], + ), + ); + }, + ); + }), + ), ); } } diff --git a/lib/views/chat_permissions_settings.dart b/lib/views/chat_permissions_settings.dart index 4b08f519..b1961542 100644 --- a/lib/views/chat_permissions_settings.dart +++ b/lib/views/chat_permissions_settings.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:fluffychat/views/widgets/dialogs/permission_slider_dialog.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/views/widgets/matrix.dart'; @@ -52,133 +53,137 @@ class ChatPermissionsSettings extends StatelessWidget { leading: BackButton(), title: Text(L10n.of(context).editChatPermissions), ), - body: StreamBuilder( - stream: Matrix.of(context).client.onSync.stream.where( - (e) => - (e?.rooms?.join?.containsKey(roomId) ?? false) && - (e.rooms.join[roomId]?.timeline?.events - ?.any((s) => s.type == EventTypes.RoomPowerLevels) ?? - false), - ), - builder: (context, _) { - final room = Matrix.of(context).client.getRoomById(roomId); - final powerLevelsContent = Map.from( - room.getState(EventTypes.RoomPowerLevels).content); - final powerLevels = Map.from(powerLevelsContent) - ..removeWhere((k, v) => !(v is int)); - final eventsPowerLevels = - Map.from(powerLevelsContent['events']) - ..removeWhere((k, v) => !(v is int)); + body: MaxWidthBody( + withScrolling: true, + child: StreamBuilder( + stream: Matrix.of(context).client.onSync.stream.where( + (e) => + (e?.rooms?.join?.containsKey(roomId) ?? false) && + (e.rooms.join[roomId]?.timeline?.events?.any( + (s) => s.type == EventTypes.RoomPowerLevels) ?? + false), + ), + builder: (context, _) { + final room = Matrix.of(context).client.getRoomById(roomId); + final powerLevelsContent = Map.from( + room.getState(EventTypes.RoomPowerLevels).content); + final powerLevels = Map.from(powerLevelsContent) + ..removeWhere((k, v) => !(v is int)); + final eventsPowerLevels = + Map.from(powerLevelsContent['events']) + ..removeWhere((k, v) => !(v is int)); - return ListView( - children: [ - Column( - mainAxisSize: MainAxisSize.min, - children: [ - for (var entry in powerLevels.entries) - PermissionsListTile( - permissionKey: entry.key, - permission: entry.value, - onTap: () => - _editPowerLevel(context, entry.key, entry.value), - ), - Divider(thickness: 1), - ListTile( - title: Text( - L10n.of(context).notifications, - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - ), - ), - ), - Builder(builder: (context) { - final key = 'rooms'; - final int value = - powerLevelsContent.containsKey('notifications') - ? powerLevelsContent['notifications']['rooms'] ?? 0 - : 0; - return PermissionsListTile( - permissionKey: key, - permission: value, - category: 'notifications', - onTap: () => _editPowerLevel(context, key, value, - category: 'notifications'), - ); - }), - Divider(thickness: 1), - ListTile( - title: Text( - L10n.of(context).configureChat, - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, - ), - ), - ), - if (eventsPowerLevels != null) - for (var entry in eventsPowerLevels.entries) + return Column( + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (var entry in powerLevels.entries) PermissionsListTile( permissionKey: entry.key, - category: 'events', permission: entry.value, - onTap: () => _editPowerLevel( - context, entry.key, entry.value, - category: 'events'), + onTap: () => + _editPowerLevel(context, entry.key, entry.value), ), - if (room.ownPowerLevel >= 100) ...{ Divider(thickness: 1), - FutureBuilder( - future: room.client.requestServerCapabilities(), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return Center(child: CircularProgressIndicator()); - } - final String roomVersion = room - .getState(EventTypes.RoomCreate) - .content['room_version'] ?? - '1'; - final shouldHaveVersion = - snapshot.data.mRoomVersions.defaultVersion; - - return ListTile( - title: Text('Current room version: $roomVersion'), - subtitle: roomVersion == shouldHaveVersion - ? null - : Text( - 'Upgrade to $shouldHaveVersion available!', - style: TextStyle( - fontWeight: FontWeight.bold, - color: Theme.of(context).accentColor), - ), - onTap: () async { - final newVersion = - await showConfirmationDialog( - context: context, - title: 'Choose Room Version', - actions: snapshot - .data.mRoomVersions.available.entries - .where((r) => r.key != roomVersion) - .map((version) => AlertDialogAction( - key: version.key, - label: - '${version.key} (${version.value.toString().split('.').last})')), - ); - await showFutureLoadingDialog( - context: context, - future: () => - room.client.upgradeRoom(roomId, newVersion), - ).then((_) => AdaptivePageLayout.of(context).pop()); - }, - ); - }, + ListTile( + title: Text( + L10n.of(context).notifications, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + ), + ), ), - }, - ], - ), - ], - ); - }, + Builder(builder: (context) { + final key = 'rooms'; + final int value = powerLevelsContent + .containsKey('notifications') + ? powerLevelsContent['notifications']['rooms'] ?? 0 + : 0; + return PermissionsListTile( + permissionKey: key, + permission: value, + category: 'notifications', + onTap: () => _editPowerLevel(context, key, value, + category: 'notifications'), + ); + }), + Divider(thickness: 1), + ListTile( + title: Text( + L10n.of(context).configureChat, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold, + ), + ), + ), + if (eventsPowerLevels != null) + for (var entry in eventsPowerLevels.entries) + PermissionsListTile( + permissionKey: entry.key, + category: 'events', + permission: entry.value, + onTap: () => _editPowerLevel( + context, entry.key, entry.value, + category: 'events'), + ), + if (room.ownPowerLevel >= 100) ...{ + Divider(thickness: 1), + FutureBuilder( + future: room.client.requestServerCapabilities(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Center(child: CircularProgressIndicator()); + } + final String roomVersion = room + .getState(EventTypes.RoomCreate) + .content['room_version'] ?? + '1'; + final shouldHaveVersion = + snapshot.data.mRoomVersions.defaultVersion; + + return ListTile( + title: Text('Current room version: $roomVersion'), + subtitle: roomVersion == shouldHaveVersion + ? null + : Text( + 'Upgrade to $shouldHaveVersion available!', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).accentColor), + ), + onTap: () async { + final newVersion = + await showConfirmationDialog( + context: context, + title: 'Choose Room Version', + actions: snapshot + .data.mRoomVersions.available.entries + .where((r) => r.key != roomVersion) + .map((version) => AlertDialogAction( + key: version.key, + label: + '${version.key} (${version.value.toString().split('.').last})')), + ); + await showFutureLoadingDialog( + context: context, + future: () => + room.client.upgradeRoom(roomId, newVersion), + ).then( + (_) => AdaptivePageLayout.of(context).pop()); + }, + ); + }, + ), + }, + ], + ), + ], + ); + }, + ), ), ); } diff --git a/lib/views/invitation_selection.dart b/lib/views/invitation_selection.dart index 8b87c238..e2f79af3 100644 --- a/lib/views/invitation_selection.dart +++ b/lib/views/invitation_selection.dart @@ -5,6 +5,7 @@ import 'package:fluffychat/views/widgets/default_app_bar_search_field.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/views/widgets/avatar.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/views/widgets/matrix.dart'; import 'package:flutter/material.dart'; @@ -128,45 +129,52 @@ class _InvitationSelectionState extends State { 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>( - 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), + body: MaxWidthBody( + withScrolling: true, + child: foundProfiles.isNotEmpty + ? ListView.builder( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + 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>( + future: getContacts(context), + builder: (BuildContext context, snapshot) { + if (!snapshot.hasData) { + return Center( + child: CircularProgressIndicator(), + ); + } + var contacts = snapshot.data; + return ListView.builder( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + 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), + ), + ); + }, + ), + ), ); } } diff --git a/lib/views/new_group.dart b/lib/views/new_group.dart index 72e2e8cb..fb19890e 100644 --- a/lib/views/new_group.dart +++ b/lib/views/new_group.dart @@ -1,5 +1,6 @@ import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart' as sdk; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/views/widgets/matrix.dart'; import 'package:flutter/material.dart'; @@ -45,32 +46,34 @@ class _NewGroupState extends State { title: Text(L10n.of(context).createNewGroup), elevation: 0, ), - body: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.all(12.0), - child: TextField( - controller: controller, - autofocus: true, - autocorrect: false, - textInputAction: TextInputAction.go, - onSubmitted: (s) => submitAction(context), - decoration: InputDecoration( - labelText: L10n.of(context).optionalGroupName, - prefixIcon: Icon(Icons.people_outlined), - hintText: L10n.of(context).enterAGroupName), + body: MaxWidthBody( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.all(12.0), + child: TextField( + controller: controller, + autofocus: true, + autocorrect: false, + textInputAction: TextInputAction.go, + onSubmitted: (s) => submitAction(context), + decoration: InputDecoration( + labelText: L10n.of(context).optionalGroupName, + prefixIcon: Icon(Icons.people_outlined), + hintText: L10n.of(context).enterAGroupName), + ), ), - ), - SwitchListTile( - title: Text(L10n.of(context).groupIsPublic), - value: publicGroup, - onChanged: (bool b) => setState(() => publicGroup = b), - ), - Expanded( - child: Image.asset('assets/new_group_wallpaper.png'), - ), - ], + SwitchListTile( + title: Text(L10n.of(context).groupIsPublic), + value: publicGroup, + onChanged: (bool b) => setState(() => publicGroup = b), + ), + Expanded( + child: Image.asset('assets/new_group_wallpaper.png'), + ), + ], + ), ), floatingActionButton: FloatingActionButton( onPressed: () => submitAction(context), diff --git a/lib/views/new_private_chat.dart b/lib/views/new_private_chat.dart index 364274e0..5b87edac 100644 --- a/lib/views/new_private_chat.dart +++ b/lib/views/new_private_chat.dart @@ -4,6 +4,7 @@ import 'package:adaptive_page_layout/adaptive_page_layout.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/views/widgets/avatar.dart'; import 'package:fluffychat/views/widgets/contacts_list.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/views/widgets/matrix.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; @@ -101,123 +102,127 @@ class _NewPrivateChatState extends State { ) ], ), - body: Column( - children: [ - Padding( - padding: const EdgeInsets.all(12.0), - child: Form( - key: _formKey, - child: TextFormField( - controller: controller, - //autofocus: true, - autocorrect: false, - onChanged: (String text) => searchUserWithCoolDown(context), - textInputAction: TextInputAction.go, - onFieldSubmitted: (s) => submitAction(context), - validator: (value) { - if (value.isEmpty) { - return L10n.of(context).pleaseEnterAMatrixIdentifier; - } - final matrix = Matrix.of(context); - var mxid = '@' + controller.text.trim(); - if (mxid == matrix.client.userID) { - return L10n.of(context).youCannotInviteYourself; - } - if (!mxid.contains('@')) { - return L10n.of(context).makeSureTheIdentifierIsValid; - } - if (!mxid.contains(':')) { - return L10n.of(context).makeSureTheIdentifierIsValid; - } - return null; - }, - decoration: InputDecoration( - labelText: L10n.of(context).enterAUsername, - prefixIcon: loading - ? Container( - padding: const EdgeInsets.all(8.0), - width: 12, - height: 12, - child: CircularProgressIndicator(), - ) - : correctMxId - ? Padding( - padding: const EdgeInsets.all(8.0), - child: Avatar( - foundProfile.avatarUrl, - foundProfile.displayname ?? foundProfile.userId, - size: 12, - ), - ) - : Icon(Icons.account_circle_outlined), - prefixText: '@', - suffixIcon: IconButton( - onPressed: () => submitAction(context), - icon: Icon(Icons.arrow_forward_outlined), + body: MaxWidthBody( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(12.0), + child: Form( + key: _formKey, + child: TextFormField( + controller: controller, + //autofocus: true, + autocorrect: false, + onChanged: (String text) => searchUserWithCoolDown(context), + textInputAction: TextInputAction.go, + onFieldSubmitted: (s) => submitAction(context), + validator: (value) { + if (value.isEmpty) { + return L10n.of(context).pleaseEnterAMatrixIdentifier; + } + final matrix = Matrix.of(context); + var mxid = '@' + controller.text.trim(); + if (mxid == matrix.client.userID) { + return L10n.of(context).youCannotInviteYourself; + } + if (!mxid.contains('@')) { + return L10n.of(context).makeSureTheIdentifierIsValid; + } + if (!mxid.contains(':')) { + return L10n.of(context).makeSureTheIdentifierIsValid; + } + return null; + }, + decoration: InputDecoration( + labelText: L10n.of(context).enterAUsername, + prefixIcon: loading + ? Container( + padding: const EdgeInsets.all(8.0), + width: 12, + height: 12, + child: CircularProgressIndicator(), + ) + : correctMxId + ? Padding( + padding: const EdgeInsets.all(8.0), + child: Avatar( + foundProfile.avatarUrl, + foundProfile.displayname ?? + foundProfile.userId, + size: 12, + ), + ) + : Icon(Icons.account_circle_outlined), + prefixText: '@', + suffixIcon: IconButton( + onPressed: () => submitAction(context), + icon: Icon(Icons.arrow_forward_outlined), + ), + hintText: '${L10n.of(context).username.toLowerCase()}', ), - hintText: '${L10n.of(context).username.toLowerCase()}', ), ), ), - ), - Divider(height: 1), - ListTile( - leading: CircleAvatar( - radius: Avatar.defaultSize / 2, - foregroundColor: Theme.of(context).accentColor, - backgroundColor: Theme.of(context).secondaryHeaderColor, - child: Icon(Icons.share_outlined), - ), - onTap: () => FluffyShare.share( - L10n.of(context).inviteText(Matrix.of(context).client.userID, - 'https://matrix.to/#/${Matrix.of(context).client.userID}'), - context), - title: Text('${L10n.of(context).yourOwnUsername}:'), - subtitle: Text( - Matrix.of(context).client.userID, - style: TextStyle(color: Theme.of(context).accentColor), - ), - ), - Divider(height: 1), - if (foundProfiles.isNotEmpty) - Expanded( - child: ListView.builder( - itemCount: foundProfiles.length, - itemBuilder: (BuildContext context, int i) { - var foundProfile = foundProfiles[i]; - return ListTile( - onTap: () { - setState(() { - controller.text = currentSearchTerm = - foundProfile.userId.substring(1); - }); - }, - leading: Avatar( - foundProfile.avatarUrl, - foundProfile.displayname ?? foundProfile.userId, - //size: 24, - ), - title: Text( - foundProfile.displayname ?? foundProfile.userId.localpart, - style: TextStyle(), - maxLines: 1, - ), - subtitle: Text( - foundProfile.userId, - maxLines: 1, - style: TextStyle( - fontSize: 12, - ), - ), - ); - }, + Divider(height: 1), + ListTile( + leading: CircleAvatar( + radius: Avatar.defaultSize / 2, + foregroundColor: Theme.of(context).accentColor, + backgroundColor: Theme.of(context).secondaryHeaderColor, + child: Icon(Icons.share_outlined), + ), + onTap: () => FluffyShare.share( + L10n.of(context).inviteText(Matrix.of(context).client.userID, + 'https://matrix.to/#/${Matrix.of(context).client.userID}'), + context), + title: Text('${L10n.of(context).yourOwnUsername}:'), + subtitle: Text( + Matrix.of(context).client.userID, + style: TextStyle(color: Theme.of(context).accentColor), ), ), - if (foundProfiles.isEmpty) - Expanded( - child: ContactsList(searchController: controller), - ), - ], + Divider(height: 1), + if (foundProfiles.isNotEmpty) + Expanded( + child: ListView.builder( + itemCount: foundProfiles.length, + itemBuilder: (BuildContext context, int i) { + var foundProfile = foundProfiles[i]; + return ListTile( + onTap: () { + setState(() { + controller.text = currentSearchTerm = + foundProfile.userId.substring(1); + }); + }, + leading: Avatar( + foundProfile.avatarUrl, + foundProfile.displayname ?? foundProfile.userId, + //size: 24, + ), + title: Text( + foundProfile.displayname ?? + foundProfile.userId.localpart, + style: TextStyle(), + maxLines: 1, + ), + subtitle: Text( + foundProfile.userId, + maxLines: 1, + style: TextStyle( + fontSize: 12, + ), + ), + ); + }, + ), + ), + if (foundProfiles.isEmpty) + Expanded( + child: ContactsList(searchController: controller), + ), + ], + ), ), ); } diff --git a/lib/views/settings_3pid.dart b/lib/views/settings_3pid.dart index 1b484ef5..a7b61ca8 100644 --- a/lib/views/settings_3pid.dart +++ b/lib/views/settings_3pid.dart @@ -1,5 +1,6 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:famedlysdk/famedlysdk.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/views/widgets/matrix.dart'; import 'package:flutter/material.dart'; @@ -99,64 +100,67 @@ class _Settings3PidState extends State { ) ], ), - body: FutureBuilder>( - future: _request, - builder: (BuildContext context, - AsyncSnapshot> snapshot) { - if (snapshot.hasError) { - return Center( - child: Text( - snapshot.error.toString(), - textAlign: TextAlign.center, - ), - ); - } - if (!snapshot.hasData) { - return Center(child: CircularProgressIndicator()); - } - final identifier = snapshot.data; - return Column( - children: [ - ListTile( - leading: CircleAvatar( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - foregroundColor: - identifier.isEmpty ? Colors.orange : Colors.grey, - child: Icon( + body: MaxWidthBody( + child: FutureBuilder>( + future: _request, + builder: (BuildContext context, + AsyncSnapshot> snapshot) { + if (snapshot.hasError) { + return Center( + child: Text( + snapshot.error.toString(), + textAlign: TextAlign.center, + ), + ); + } + if (!snapshot.hasData) { + return Center(child: CircularProgressIndicator()); + } + final identifier = snapshot.data; + return Column( + children: [ + ListTile( + leading: CircleAvatar( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + foregroundColor: + identifier.isEmpty ? Colors.orange : Colors.grey, + child: Icon( + identifier.isEmpty + ? Icons.warning_outlined + : Icons.info_outlined, + ), + ), + title: Text( identifier.isEmpty - ? Icons.warning_outlined - : Icons.info_outlined, + ? L10n.of(context).noPasswordRecoveryDescription + : L10n.of(context) + .withTheseAddressesRecoveryDescription, ), ), - title: Text( - identifier.isEmpty - ? L10n.of(context).noPasswordRecoveryDescription - : L10n.of(context).withTheseAddressesRecoveryDescription, - ), - ), - Divider(height: 1), - Expanded( - child: ListView.builder( - itemCount: identifier.length, - itemBuilder: (BuildContext context, int i) => ListTile( - leading: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(identifier[i].iconData)), - title: Text(identifier[i].address), - trailing: IconButton( - tooltip: L10n.of(context).delete, - icon: Icon(Icons.delete_forever_outlined), - color: Colors.red, - onPressed: () => _delete3Pid(context, identifier[i]), + Divider(height: 1), + Expanded( + child: ListView.builder( + itemCount: identifier.length, + itemBuilder: (BuildContext context, int i) => ListTile( + leading: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(identifier[i].iconData)), + title: Text(identifier[i].address), + trailing: IconButton( + tooltip: L10n.of(context).delete, + icon: Icon(Icons.delete_forever_outlined), + color: Colors.red, + onPressed: () => _delete3Pid(context, identifier[i]), + ), ), ), ), - ), - ], - ); - }, + ], + ); + }, + ), ), ); } diff --git a/lib/views/settings_devices.dart b/lib/views/settings_devices.dart index 91776b63..a796d8ee 100644 --- a/lib/views/settings_devices.dart +++ b/lib/views/settings_devices.dart @@ -2,6 +2,7 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:famedlysdk/encryption/utils/key_verification.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/views/widgets/dialogs/key_verification_dialog.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -131,83 +132,86 @@ class DevicesSettingsState extends State { leading: BackButton(), title: Text(L10n.of(context).devices), ), - body: FutureBuilder( - future: _loadUserDevices(context), - builder: (BuildContext context, snapshot) { - if (snapshot.hasError) { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.error_outlined), - Text(snapshot.error.toString()), - ], - ), - ); - } - if (!snapshot.hasData || this.devices == null) { - return Center(child: CircularProgressIndicator()); - } - Function isOwnDevice = (Device userDevice) => - userDevice.deviceId == Matrix.of(context).client.deviceID; - final devices = List.from(this.devices); - var thisDevice = devices.firstWhere(isOwnDevice, orElse: () => null); - devices.removeWhere(isOwnDevice); - devices.sort((a, b) => b.lastSeenTs.compareTo(a.lastSeenTs)); - return Column( - children: [ - if (thisDevice != null) - UserDeviceListItem( - thisDevice, - rename: (d) => _renameDeviceAction(context, d), - remove: (d) => _removeDevicesAction(context, [d]), - verify: (d) => _verifyDeviceAction(context, d), - block: (d) => _blockDeviceAction(context, d), - unblock: (d) => _unblockDeviceAction(context, d), + body: MaxWidthBody( + child: FutureBuilder( + future: _loadUserDevices(context), + builder: (BuildContext context, snapshot) { + if (snapshot.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.error_outlined), + Text(snapshot.error.toString()), + ], ), - Divider(height: 1), - if (devices.isNotEmpty) - ListTile( - title: Text( - _errorDeletingDevices ?? - L10n.of(context).removeAllOtherDevices, - style: TextStyle(color: Colors.red), + ); + } + if (!snapshot.hasData || this.devices == null) { + return Center(child: CircularProgressIndicator()); + } + Function isOwnDevice = (Device userDevice) => + userDevice.deviceId == Matrix.of(context).client.deviceID; + final devices = List.from(this.devices); + var thisDevice = + devices.firstWhere(isOwnDevice, orElse: () => null); + devices.removeWhere(isOwnDevice); + devices.sort((a, b) => b.lastSeenTs.compareTo(a.lastSeenTs)); + return Column( + children: [ + if (thisDevice != null) + UserDeviceListItem( + thisDevice, + rename: (d) => _renameDeviceAction(context, d), + remove: (d) => _removeDevicesAction(context, [d]), + verify: (d) => _verifyDeviceAction(context, d), + block: (d) => _blockDeviceAction(context, d), + unblock: (d) => _unblockDeviceAction(context, d), ), - trailing: _loadingDeletingDevices - ? CircularProgressIndicator() - : Icon(Icons.delete_outline), - onTap: _loadingDeletingDevices - ? null - : () => _removeDevicesAction(context, devices), + Divider(height: 1), + if (devices.isNotEmpty) + ListTile( + title: Text( + _errorDeletingDevices ?? + L10n.of(context).removeAllOtherDevices, + style: TextStyle(color: Colors.red), + ), + trailing: _loadingDeletingDevices + ? CircularProgressIndicator() + : Icon(Icons.delete_outline), + onTap: _loadingDeletingDevices + ? null + : () => _removeDevicesAction(context, devices), + ), + Divider(height: 1), + Expanded( + child: devices.isEmpty + ? Center( + child: Icon( + Icons.devices_other, + size: 60, + color: Theme.of(context).secondaryHeaderColor, + ), + ) + : ListView.separated( + separatorBuilder: (BuildContext context, int i) => + Divider(height: 1), + itemCount: devices.length, + itemBuilder: (BuildContext context, int i) => + UserDeviceListItem( + devices[i], + rename: (d) => _renameDeviceAction(context, d), + remove: (d) => _removeDevicesAction(context, [d]), + verify: (d) => _verifyDeviceAction(context, d), + block: (d) => _blockDeviceAction(context, d), + unblock: (d) => _unblockDeviceAction(context, d), + ), + ), ), - Divider(height: 1), - Expanded( - child: devices.isEmpty - ? Center( - child: Icon( - Icons.devices_other, - size: 60, - color: Theme.of(context).secondaryHeaderColor, - ), - ) - : ListView.separated( - separatorBuilder: (BuildContext context, int i) => - Divider(height: 1), - itemCount: devices.length, - itemBuilder: (BuildContext context, int i) => - UserDeviceListItem( - devices[i], - rename: (d) => _renameDeviceAction(context, d), - remove: (d) => _removeDevicesAction(context, [d]), - verify: (d) => _verifyDeviceAction(context, d), - block: (d) => _blockDeviceAction(context, d), - unblock: (d) => _unblockDeviceAction(context, d), - ), - ), - ), - ], - ); - }, + ], + ); + }, + ), ), ); } diff --git a/lib/views/settings_emotes.dart b/lib/views/settings_emotes.dart index b8ded23e..7d68415a 100644 --- a/lib/views/settings_emotes.dart +++ b/lib/views/settings_emotes.dart @@ -6,6 +6,7 @@ import 'package:famedlysdk/famedlysdk.dart'; import 'package:file_picker_cross/file_picker_cross.dart'; import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -184,223 +185,227 @@ class _EmotesSettingsState extends State { child: Icon(Icons.save_outlined, color: Colors.white), ) : null, - body: StreamBuilder( - stream: widget.room?.onUpdate?.stream, - builder: (context, snapshot) { - return Column( - children: [ - if (!readonly) - Container( - padding: EdgeInsets.symmetric( - vertical: 8.0, - ), - child: ListTile( - leading: Container( - width: 180.0, - height: 38, - padding: EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - color: Theme.of(context).secondaryHeaderColor, - ), - child: TextField( - controller: newEmoteController, - autocorrect: false, - minLines: 1, - maxLines: 1, - decoration: InputDecoration( - hintText: L10n.of(context).emoteShortcode, - prefixText: ': ', - suffixText: ':', - prefixStyle: TextStyle( - color: Theme.of(context).accentColor, - fontWeight: FontWeight.bold, - ), - suffixStyle: TextStyle( - color: Theme.of(context).accentColor, - fontWeight: FontWeight.bold, - ), - border: InputBorder.none, - ), - ), + body: MaxWidthBody( + child: StreamBuilder( + stream: widget.room?.onUpdate?.stream, + builder: (context, snapshot) { + return Column( + children: [ + if (!readonly) + Container( + padding: EdgeInsets.symmetric( + vertical: 8.0, ), - title: _EmoteImagePicker(newMxcController), - trailing: InkWell( - onTap: () async { - if (newEmoteController.text == null || - newEmoteController.text.isEmpty || - newMxcController.text == null || - newMxcController.text.isEmpty) { - await showOkAlertDialog( - context: context, - message: L10n.of(context).emoteWarnNeedToPick, - okLabel: L10n.of(context).ok, - useRootNavigator: false, - ); - return; - } - final emoteCode = ':${newEmoteController.text}:'; - final mxc = newMxcController.text; - if (emotes.indexWhere((e) => - e.emote == emoteCode && e.mxc != mxc) != - -1) { - await showOkAlertDialog( - context: context, - message: L10n.of(context).emoteExists, - okLabel: L10n.of(context).ok, - useRootNavigator: false, - ); - return; - } - if (!RegExp(r'^:[-\w]+:$').hasMatch(emoteCode)) { - await showOkAlertDialog( - context: context, - message: L10n.of(context).emoteInvalid, - okLabel: L10n.of(context).ok, - useRootNavigator: false, - ); - return; - } - emotes.add(_EmoteEntry(emote: emoteCode, mxc: mxc)); - await _save(context); - setState(() { - newEmoteController.text = ''; - newMxcController.text = ''; - showSave = false; - }); - }, - child: Icon( - Icons.add_outlined, - color: Colors.green, - size: 32.0, - ), - ), - ), - ), - if (widget.room != null) - ListTile( - title: Text(L10n.of(context).enableEmotesGlobally), - trailing: Switch( - value: isGloballyActive(client), - onChanged: (bool newValue) async { - await _setIsGloballyActive(context, newValue); - setState(() => null); - }, - ), - ), - if (!readonly || widget.room != null) - Divider( - height: 2, - thickness: 2, - color: Theme.of(context).primaryColor, - ), - Expanded( - child: emotes.isEmpty - ? Center( - child: Padding( - padding: EdgeInsets.all(16), - child: Text( - L10n.of(context).noEmotesFound, - style: TextStyle(fontSize: 20), - ), + child: ListTile( + leading: Container( + width: 180.0, + height: 38, + padding: EdgeInsets.symmetric(horizontal: 8), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + color: Theme.of(context).secondaryHeaderColor, ), - ) - : ListView.separated( - separatorBuilder: (BuildContext context, int i) => - Container(), - itemCount: emotes.length + 1, - itemBuilder: (BuildContext context, int i) { - if (i >= emotes.length) { - return Container(height: 70); - } - final emote = emotes[i]; - final controller = TextEditingController(); - controller.text = emote.emoteClean; - return ListTile( - leading: Container( - width: 180.0, - height: 38, - padding: EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - borderRadius: - BorderRadius.all(Radius.circular(10)), - color: Theme.of(context).secondaryHeaderColor, - ), - child: TextField( - readOnly: readonly, - controller: controller, - autocorrect: false, - minLines: 1, - maxLines: 1, - decoration: InputDecoration( - hintText: L10n.of(context).emoteShortcode, - prefixText: ': ', - suffixText: ':', - prefixStyle: TextStyle( - color: Theme.of(context).accentColor, - fontWeight: FontWeight.bold, - ), - suffixStyle: TextStyle( - color: Theme.of(context).accentColor, - fontWeight: FontWeight.bold, - ), - border: InputBorder.none, - ), - onSubmitted: (s) { - final emoteCode = ':$s:'; - if (emotes.indexWhere((e) => - e.emote == emoteCode && - e.mxc != emote.mxc) != - -1) { - controller.text = emote.emoteClean; - showOkAlertDialog( - context: context, - message: L10n.of(context).emoteExists, - okLabel: L10n.of(context).ok, - useRootNavigator: false, - ); - return; - } - if (!RegExp(r'^:[-\w]+:$') - .hasMatch(emoteCode)) { - controller.text = emote.emoteClean; - showOkAlertDialog( - context: context, - message: L10n.of(context).emoteInvalid, - okLabel: L10n.of(context).ok, - useRootNavigator: false, - ); - return; - } - setState(() { - emote.emote = emoteCode; - showSave = true; - }); - }, - ), + child: TextField( + controller: newEmoteController, + autocorrect: false, + minLines: 1, + maxLines: 1, + decoration: InputDecoration( + hintText: L10n.of(context).emoteShortcode, + prefixText: ': ', + suffixText: ':', + prefixStyle: TextStyle( + color: Theme.of(context).accentColor, + fontWeight: FontWeight.bold, ), - title: _EmoteImage(emote.mxc), - trailing: readonly - ? null - : InkWell( - onTap: () => setState(() { - emotes.removeWhere( - (e) => e.emote == emote.emote); - showSave = true; - }), - child: Icon( - Icons.delete_forever_outlined, - color: Colors.red, - size: 32.0, - ), - ), - ); - }, + suffixStyle: TextStyle( + color: Theme.of(context).accentColor, + fontWeight: FontWeight.bold, + ), + border: InputBorder.none, + ), + ), ), - ), - ], - ); - }), + title: _EmoteImagePicker(newMxcController), + trailing: InkWell( + onTap: () async { + if (newEmoteController.text == null || + newEmoteController.text.isEmpty || + newMxcController.text == null || + newMxcController.text.isEmpty) { + await showOkAlertDialog( + context: context, + message: L10n.of(context).emoteWarnNeedToPick, + okLabel: L10n.of(context).ok, + useRootNavigator: false, + ); + return; + } + final emoteCode = ':${newEmoteController.text}:'; + final mxc = newMxcController.text; + if (emotes.indexWhere((e) => + e.emote == emoteCode && e.mxc != mxc) != + -1) { + await showOkAlertDialog( + context: context, + message: L10n.of(context).emoteExists, + okLabel: L10n.of(context).ok, + useRootNavigator: false, + ); + return; + } + if (!RegExp(r'^:[-\w]+:$').hasMatch(emoteCode)) { + await showOkAlertDialog( + context: context, + message: L10n.of(context).emoteInvalid, + okLabel: L10n.of(context).ok, + useRootNavigator: false, + ); + return; + } + emotes.add(_EmoteEntry(emote: emoteCode, mxc: mxc)); + await _save(context); + setState(() { + newEmoteController.text = ''; + newMxcController.text = ''; + showSave = false; + }); + }, + child: Icon( + Icons.add_outlined, + color: Colors.green, + size: 32.0, + ), + ), + ), + ), + if (widget.room != null) + ListTile( + title: Text(L10n.of(context).enableEmotesGlobally), + trailing: Switch( + value: isGloballyActive(client), + onChanged: (bool newValue) async { + await _setIsGloballyActive(context, newValue); + setState(() => null); + }, + ), + ), + if (!readonly || widget.room != null) + Divider( + height: 2, + thickness: 2, + color: Theme.of(context).primaryColor, + ), + Expanded( + child: emotes.isEmpty + ? Center( + child: Padding( + padding: EdgeInsets.all(16), + child: Text( + L10n.of(context).noEmotesFound, + style: TextStyle(fontSize: 20), + ), + ), + ) + : ListView.separated( + separatorBuilder: (BuildContext context, int i) => + Container(), + itemCount: emotes.length + 1, + itemBuilder: (BuildContext context, int i) { + if (i >= emotes.length) { + return Container(height: 70); + } + final emote = emotes[i]; + final controller = TextEditingController(); + controller.text = emote.emoteClean; + return ListTile( + leading: Container( + width: 180.0, + height: 38, + padding: EdgeInsets.symmetric(horizontal: 8), + decoration: BoxDecoration( + borderRadius: + BorderRadius.all(Radius.circular(10)), + color: + Theme.of(context).secondaryHeaderColor, + ), + child: TextField( + readOnly: readonly, + controller: controller, + autocorrect: false, + minLines: 1, + maxLines: 1, + decoration: InputDecoration( + hintText: L10n.of(context).emoteShortcode, + prefixText: ': ', + suffixText: ':', + prefixStyle: TextStyle( + color: Theme.of(context).accentColor, + fontWeight: FontWeight.bold, + ), + suffixStyle: TextStyle( + color: Theme.of(context).accentColor, + fontWeight: FontWeight.bold, + ), + border: InputBorder.none, + ), + onSubmitted: (s) { + final emoteCode = ':$s:'; + if (emotes.indexWhere((e) => + e.emote == emoteCode && + e.mxc != emote.mxc) != + -1) { + controller.text = emote.emoteClean; + showOkAlertDialog( + context: context, + message: L10n.of(context).emoteExists, + okLabel: L10n.of(context).ok, + useRootNavigator: false, + ); + return; + } + if (!RegExp(r'^:[-\w]+:$') + .hasMatch(emoteCode)) { + controller.text = emote.emoteClean; + showOkAlertDialog( + context: context, + message: + L10n.of(context).emoteInvalid, + okLabel: L10n.of(context).ok, + useRootNavigator: false, + ); + return; + } + setState(() { + emote.emote = emoteCode; + showSave = true; + }); + }, + ), + ), + title: _EmoteImage(emote.mxc), + trailing: readonly + ? null + : InkWell( + onTap: () => setState(() { + emotes.removeWhere( + (e) => e.emote == emote.emote); + showSave = true; + }), + child: Icon( + Icons.delete_forever_outlined, + color: Colors.red, + size: 32.0, + ), + ), + ); + }, + ), + ), + ], + ); + }), + ), ); } } diff --git a/lib/views/settings_ignore_list.dart b/lib/views/settings_ignore_list.dart index eaed55e2..5509af66 100644 --- a/lib/views/settings_ignore_list.dart +++ b/lib/views/settings_ignore_list.dart @@ -1,5 +1,6 @@ import 'package:famedlysdk/famedlysdk.dart'; import 'package:fluffychat/views/widgets/avatar.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -45,71 +46,73 @@ class _SettingsIgnoreListState extends State { leading: BackButton(), title: Text(L10n.of(context).ignoredUsers), ), - body: Column( - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: _controller, - autocorrect: false, - textInputAction: TextInputAction.done, - onSubmitted: (_) => _ignoreUser(context), - decoration: InputDecoration( - border: OutlineInputBorder(), - hintText: 'bad_guy:domain.abc', - prefixText: '@', - labelText: L10n.of(context).ignoreUsername, - suffixIcon: IconButton( - tooltip: L10n.of(context).ignore, - icon: Icon(Icons.done_outlined), - onPressed: () => _ignoreUser(context), + body: MaxWidthBody( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: _controller, + autocorrect: false, + textInputAction: TextInputAction.done, + onSubmitted: (_) => _ignoreUser(context), + decoration: InputDecoration( + border: OutlineInputBorder(), + hintText: 'bad_guy:domain.abc', + prefixText: '@', + labelText: L10n.of(context).ignoreUsername, + suffixIcon: IconButton( + tooltip: L10n.of(context).ignore, + icon: Icon(Icons.done_outlined), + onPressed: () => _ignoreUser(context), + ), ), ), - ), - SizedBox(height: 16), - Text( - L10n.of(context).ignoreListDescription, - style: TextStyle(color: Colors.orange), - ), - ], + SizedBox(height: 16), + Text( + L10n.of(context).ignoreListDescription, + style: TextStyle(color: Colors.orange), + ), + ], + ), ), - ), - Divider(height: 1), - Expanded( - child: StreamBuilder( - stream: client.onAccountData.stream - .where((a) => a.type == 'm.ignored_user_list'), - builder: (context, snapshot) { - return ListView.builder( - itemCount: client.ignoredUsers.length, - itemBuilder: (c, i) => FutureBuilder( - future: - client.getProfileFromUserId(client.ignoredUsers[i]), - builder: (c, s) => ListTile( - leading: Avatar( - s.data?.avatarUrl ?? Uri.parse(''), - s.data?.displayname ?? client.ignoredUsers[i], - ), - title: - Text(s.data?.displayname ?? client.ignoredUsers[i]), - trailing: IconButton( - tooltip: L10n.of(context).delete, - icon: Icon(Icons.delete_forever_outlined), - onPressed: () => showFutureLoadingDialog( - context: context, - future: () => - client.unignoreUser(client.ignoredUsers[i]), + Divider(height: 1), + Expanded( + child: StreamBuilder( + stream: client.onAccountData.stream + .where((a) => a.type == 'm.ignored_user_list'), + builder: (context, snapshot) { + return ListView.builder( + itemCount: client.ignoredUsers.length, + itemBuilder: (c, i) => FutureBuilder( + future: + client.getProfileFromUserId(client.ignoredUsers[i]), + builder: (c, s) => ListTile( + leading: Avatar( + s.data?.avatarUrl ?? Uri.parse(''), + s.data?.displayname ?? client.ignoredUsers[i], + ), + title: Text( + s.data?.displayname ?? client.ignoredUsers[i]), + trailing: IconButton( + tooltip: L10n.of(context).delete, + icon: Icon(Icons.delete_forever_outlined), + onPressed: () => showFutureLoadingDialog( + context: context, + future: () => + client.unignoreUser(client.ignoredUsers[i]), + ), ), ), ), - ), - ); - }), - ), - ], + ); + }), + ), + ], + ), ), ); } diff --git a/lib/views/settings_notifications.dart b/lib/views/settings_notifications.dart index 21078226..f9371a38 100644 --- a/lib/views/settings_notifications.dart +++ b/lib/views/settings_notifications.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:famedlysdk/famedlysdk.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:flutter/foundation.dart'; @@ -113,97 +114,101 @@ class SettingsNotifications extends StatelessWidget { leading: BackButton(), title: Text(L10n.of(context).notifications), ), - body: StreamBuilder( - stream: Matrix.of(context) - .client - .onAccountData - .stream - .where((event) => event.type == 'm.push_rules'), - builder: (BuildContext context, _) { - return ListView( - children: [ - SwitchListTile( - value: !Matrix.of(context).client.allPushNotificationsMuted, - title: - Text(L10n.of(context).notificationsEnabledForThisAccount), - onChanged: (_) => showFutureLoadingDialog( - context: context, - future: () => Matrix.of(context) - .client - .setMuteAllPushNotifications( - !Matrix.of(context).client.allPushNotificationsMuted, - ), - ), - ), - if (!Matrix.of(context).client.allPushNotificationsMuted) ...{ - if (!kIsWeb && Platform.isAndroid) - ListTile( - title: Text(L10n.of(context).soundVibrationLedColor), - trailing: CircleAvatar( - backgroundColor: - Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Colors.grey, - child: Icon(Icons.edit_outlined), - ), - onTap: () => _openAndroidNotificationSettingsAction(), + body: MaxWidthBody( + withScrolling: true, + child: StreamBuilder( + stream: Matrix.of(context) + .client + .onAccountData + .stream + .where((event) => event.type == 'm.push_rules'), + builder: (BuildContext context, _) { + return Column( + children: [ + SwitchListTile( + value: !Matrix.of(context).client.allPushNotificationsMuted, + title: Text( + L10n.of(context).notificationsEnabledForThisAccount), + onChanged: (_) => showFutureLoadingDialog( + context: context, + future: () => + Matrix.of(context).client.setMuteAllPushNotifications( + !Matrix.of(context) + .client + .allPushNotificationsMuted, + ), ), + ), + if (!Matrix.of(context).client.allPushNotificationsMuted) ...{ + if (!kIsWeb && Platform.isAndroid) + ListTile( + title: Text(L10n.of(context).soundVibrationLedColor), + trailing: CircleAvatar( + backgroundColor: + Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Colors.grey, + child: Icon(Icons.edit_outlined), + ), + onTap: () => _openAndroidNotificationSettingsAction(), + ), + Divider(thickness: 1), + ListTile( + title: Text( + L10n.of(context).pushRules, + style: TextStyle( + color: Theme.of(context).accentColor, + fontWeight: FontWeight.bold, + ), + ), + ), + for (var item in items) + SwitchListTile( + value: _getNotificationSetting(context, item) ?? true, + title: Text(item.title(context)), + onChanged: (bool enabled) => + _setNotificationSetting(context, item, enabled), + ), + }, Divider(thickness: 1), ListTile( title: Text( - L10n.of(context).pushRules, + L10n.of(context).devices, style: TextStyle( color: Theme.of(context).accentColor, fontWeight: FontWeight.bold, ), ), ), - for (var item in items) - SwitchListTile( - value: _getNotificationSetting(context, item) ?? true, - title: Text(item.title(context)), - onChanged: (bool enabled) => - _setNotificationSetting(context, item, enabled), - ), - }, - Divider(thickness: 1), - ListTile( - title: Text( - L10n.of(context).devices, - style: TextStyle( - color: Theme.of(context).accentColor, - fontWeight: FontWeight.bold, - ), - ), - ), - FutureBuilder>( - future: Matrix.of(context).client.requestPushers(), - builder: (context, snapshot) { - if (snapshot.hasError) { - Center( - child: Text( - snapshot.error.toLocalizedString(context), + FutureBuilder>( + future: Matrix.of(context).client.requestPushers(), + builder: (context, snapshot) { + if (snapshot.hasError) { + Center( + child: Text( + snapshot.error.toLocalizedString(context), + ), + ); + } + if (!snapshot.hasData) { + Center(child: CircularProgressIndicator()); + } + final pushers = snapshot.data; + return ListView.builder( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: pushers.length, + itemBuilder: (_, i) => ListTile( + title: Text( + '${pushers[i].appDisplayName} - ${pushers[i].appId}'), + subtitle: Text(pushers[i].data.url.toString()), ), ); - } - if (!snapshot.hasData) { - Center(child: CircularProgressIndicator()); - } - final pushers = snapshot.data; - return ListView.builder( - physics: NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: pushers.length, - itemBuilder: (_, i) => ListTile( - title: Text( - '${pushers[i].appDisplayName} - ${pushers[i].appId}'), - subtitle: Text(pushers[i].data.url.toString()), - ), - ); - }, - ), - ], - ); - }), + }, + ), + ], + ); + }), + ), ); } } diff --git a/lib/views/settings_style.dart b/lib/views/settings_style.dart index 0d1a4256..a5022764 100644 --- a/lib/views/settings_style.dart +++ b/lib/views/settings_style.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:adaptive_theme/adaptive_theme.dart'; import 'package:fluffychat/config/setting_keys.dart'; +import 'package:fluffychat/views/widgets/max_width_body.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:image_picker/image_picker.dart'; @@ -56,100 +57,104 @@ class _SettingsStyleState extends State { leading: BackButton(), title: Text(L10n.of(context).changeTheme), ), - body: ListView( - children: [ - RadioListTile( - groupValue: _currentTheme, - value: AdaptiveThemeMode.system, - title: Text(L10n.of(context).systemTheme), - onChanged: (t) => _switchTheme(t, context), - ), - RadioListTile( - groupValue: _currentTheme, - value: AdaptiveThemeMode.light, - title: Text(L10n.of(context).lightTheme), - onChanged: (t) => _switchTheme(t, context), - ), - RadioListTile( - groupValue: _currentTheme, - value: AdaptiveThemeMode.dark, - title: Text(L10n.of(context).darkTheme), - onChanged: (t) => _switchTheme(t, context), - ), - Divider(height: 1), - ListTile( - title: Text( - L10n.of(context).wallpaper, - style: TextStyle( - color: Theme.of(context).accentColor, - fontWeight: FontWeight.bold, - ), + body: MaxWidthBody( + withScrolling: true, + child: Column( + children: [ + RadioListTile( + groupValue: _currentTheme, + value: AdaptiveThemeMode.system, + title: Text(L10n.of(context).systemTheme), + onChanged: (t) => _switchTheme(t, context), ), - ), - if (Matrix.of(context).wallpaper != null) + RadioListTile( + groupValue: _currentTheme, + value: AdaptiveThemeMode.light, + title: Text(L10n.of(context).lightTheme), + onChanged: (t) => _switchTheme(t, context), + ), + RadioListTile( + groupValue: _currentTheme, + value: AdaptiveThemeMode.dark, + title: Text(L10n.of(context).darkTheme), + onChanged: (t) => _switchTheme(t, context), + ), + Divider(height: 1), ListTile( - title: Image.file( - Matrix.of(context).wallpaper, - height: 38, - fit: BoxFit.cover, - ), - trailing: Icon( - Icons.delete_forever_outlined, - color: Colors.red, - ), - onTap: () => deleteWallpaperAction(context), - ), - Builder(builder: (context) { - return ListTile( - title: Text(L10n.of(context).changeWallpaper), - trailing: Icon(Icons.wallpaper_outlined), - onTap: () => setWallpaperAction(context), - ); - }), - Divider(height: 1), - ListTile( - title: Text( - L10n.of(context).fontSize, - style: TextStyle( - color: Theme.of(context).accentColor, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text('(*${AppConfig.fontSizeFactor})'), - ), - Container( - alignment: Alignment.centerLeft, - child: Container( - margin: const EdgeInsets.symmetric(horizontal: 16), - padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 10), - decoration: BoxDecoration( - color: Theme.of(context).secondaryHeaderColor, - borderRadius: BorderRadius.circular(16), - ), - child: Text( - 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor', + title: Text( + L10n.of(context).wallpaper, style: TextStyle( - fontSize: Theme.of(context).textTheme.bodyText1.fontSize * - AppConfig.fontSizeFactor, + color: Theme.of(context).accentColor, + fontWeight: FontWeight.bold, ), ), ), - ), - Slider( - min: 0.5, - max: 2.5, - divisions: 4, - value: AppConfig.fontSizeFactor, - semanticFormatterCallback: (d) => d.toString(), - onChanged: (d) { - setState(() => AppConfig.fontSizeFactor = d); - Matrix.of(context).store.setItem( - SettingKeys.fontSizeFactor, - AppConfig.fontSizeFactor.toString(), - ); - }, - ), - ], + if (Matrix.of(context).wallpaper != null) + ListTile( + title: Image.file( + Matrix.of(context).wallpaper, + height: 38, + fit: BoxFit.cover, + ), + trailing: Icon( + Icons.delete_forever_outlined, + color: Colors.red, + ), + onTap: () => deleteWallpaperAction(context), + ), + Builder(builder: (context) { + return ListTile( + title: Text(L10n.of(context).changeWallpaper), + trailing: Icon(Icons.wallpaper_outlined), + onTap: () => setWallpaperAction(context), + ); + }), + Divider(height: 1), + ListTile( + title: Text( + L10n.of(context).fontSize, + style: TextStyle( + color: Theme.of(context).accentColor, + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text('(*${AppConfig.fontSizeFactor})'), + ), + Container( + alignment: Alignment.centerLeft, + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 16), + padding: + const EdgeInsets.symmetric(vertical: 6, horizontal: 10), + decoration: BoxDecoration( + color: Theme.of(context).secondaryHeaderColor, + borderRadius: BorderRadius.circular(16), + ), + child: Text( + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor', + style: TextStyle( + fontSize: Theme.of(context).textTheme.bodyText1.fontSize * + AppConfig.fontSizeFactor, + ), + ), + ), + ), + Slider( + min: 0.5, + max: 2.5, + divisions: 4, + value: AppConfig.fontSizeFactor, + semanticFormatterCallback: (d) => d.toString(), + onChanged: (d) { + setState(() => AppConfig.fontSizeFactor = d); + Matrix.of(context).store.setItem( + SettingKeys.fontSizeFactor, + AppConfig.fontSizeFactor.toString(), + ); + }, + ), + ], + ), ), ); } diff --git a/lib/views/widgets/max_width_body.dart b/lib/views/widgets/max_width_body.dart new file mode 100644 index 00000000..79d626f9 --- /dev/null +++ b/lib/views/widgets/max_width_body.dart @@ -0,0 +1,38 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +class MaxWidthBody extends StatelessWidget { + final Widget child; + final double maxWidth; + final bool withScrolling; + + const MaxWidthBody({ + this.child, + this.maxWidth = 600, + this.withScrolling = false, + Key key, + }) : super(key: key); + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + final padding = EdgeInsets.symmetric( + horizontal: max(0, (constraints.maxWidth - maxWidth) / 2), + ); + return withScrolling + ? SingleChildScrollView( + physics: ScrollPhysics(), + child: Padding( + padding: padding, + child: child, + ), + ) + : Padding( + padding: padding, + child: child, + ); + }, + ); + } +}