mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-24 04:59:26 +01:00
design: Column mode auto padding
This commit is contained in:
parent
0146767e8a
commit
75258271af
@ -10,6 +10,7 @@ import 'package:famedlysdk/famedlysdk.dart';
|
|||||||
import 'package:file_picker_cross/file_picker_cross.dart';
|
import 'package:file_picker_cross/file_picker_cross.dart';
|
||||||
import 'package:fluffychat/views/widgets/chat_settings_popup_menu.dart';
|
import 'package:fluffychat/views/widgets/chat_settings_popup_menu.dart';
|
||||||
import 'package:fluffychat/views/widgets/content_banner.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:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:fluffychat/views/widgets/list_items/participant_list_item.dart';
|
import 'package:fluffychat/views/widgets/list_items/participant_list_item.dart';
|
||||||
import 'package:fluffychat/utils/matrix_locals.dart';
|
import 'package:fluffychat/utils/matrix_locals.dart';
|
||||||
@ -234,223 +235,188 @@ class _ChatDetailsState extends State<ChatDetails> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
body: ListView.builder(
|
body: MaxWidthBody(
|
||||||
itemCount: members.length + 1 + (canRequestMoreMembers ? 1 : 0),
|
child: ListView.builder(
|
||||||
itemBuilder: (BuildContext context, int i) => i == 0
|
itemCount:
|
||||||
? Column(
|
members.length + 1 + (canRequestMoreMembers ? 1 : 0),
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
itemBuilder: (BuildContext context, int i) => i == 0
|
||||||
children: <Widget>[
|
? Column(
|
||||||
ListTile(
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
leading: room.canSendEvent('m.room.topic')
|
children: <Widget>[
|
||||||
? 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'))
|
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: CircleAvatar(
|
leading: room.canSendEvent('m.room.topic')
|
||||||
backgroundColor:
|
? CircleAvatar(
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
backgroundColor: Theme.of(context)
|
||||||
foregroundColor: Colors.grey,
|
.scaffoldBackgroundColor,
|
||||||
child: Icon(Icons.people_outlined),
|
foregroundColor: Colors.grey,
|
||||||
),
|
radius: Avatar.defaultSize / 2,
|
||||||
|
child: Icon(Icons.edit_outlined),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).changeTheNameOfTheGroup),
|
'${L10n.of(context).groupDescription}:',
|
||||||
subtitle: Text(room.getLocalizedDisplayname(
|
style: TextStyle(
|
||||||
MatrixLocals(L10n.of(context)))),
|
color: Theme.of(context).accentColor,
|
||||||
onTap: () => setDisplaynameAction(context),
|
fontWeight: FontWeight.bold)),
|
||||||
),
|
subtitle: LinkText(
|
||||||
if (room.canSendEvent('m.room.canonical_alias') &&
|
text: room.topic?.isEmpty ?? true
|
||||||
room.joinRules == JoinRules.public)
|
? L10n.of(context).addGroupDescription
|
||||||
ListTile(
|
: room.topic,
|
||||||
leading: CircleAvatar(
|
linkStyle: TextStyle(color: Colors.blueAccent),
|
||||||
backgroundColor:
|
textStyle: TextStyle(
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
fontSize: 14,
|
||||||
foregroundColor: Colors.grey,
|
color: Theme.of(context)
|
||||||
child: Icon(Icons.link_outlined),
|
.textTheme
|
||||||
|
.bodyText2
|
||||||
|
.color,
|
||||||
|
),
|
||||||
|
onLinkTap: (url) =>
|
||||||
|
UrlLauncher(context, url).launchUrl(),
|
||||||
),
|
),
|
||||||
onTap: () => setCanonicalAliasAction(context),
|
onTap: room.canSendEvent('m.room.topic')
|
||||||
title: Text(L10n.of(context).setInvitationLink),
|
? () => setTopicAction(context)
|
||||||
subtitle: Text(
|
: null,
|
||||||
(room.canonicalAlias?.isNotEmpty ?? false)
|
|
||||||
? room.canonicalAlias
|
|
||||||
: L10n.of(context).none),
|
|
||||||
),
|
),
|
||||||
ListTile(
|
Divider(thickness: 1),
|
||||||
leading: CircleAvatar(
|
ListTile(
|
||||||
backgroundColor:
|
title: Text(
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
L10n.of(context).settings,
|
||||||
foregroundColor: Colors.grey,
|
style: TextStyle(
|
||||||
child: Icon(Icons.insert_emoticon_outlined),
|
color: Theme.of(context).accentColor,
|
||||||
),
|
fontWeight: FontWeight.bold,
|
||||||
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'] ??
|
|
||||||
<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',
|
|
||||||
arguments: {'room': room});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
PopupMenuButton(
|
|
||||||
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,
|
if (room.canSendEvent('m.room.name'))
|
||||||
child: Text(JoinRules.invite
|
ListTile(
|
||||||
.getLocalizedString(
|
leading: CircleAvatar(
|
||||||
MatrixLocals(L10n.of(context)))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: ListTile(
|
|
||||||
leading: CircleAvatar(
|
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
foregroundColor: Colors.grey,
|
foregroundColor: Colors.grey,
|
||||||
child: Icon(Icons.public_outlined)),
|
child: Icon(Icons.people_outlined),
|
||||||
title: Text(
|
),
|
||||||
L10n.of(context).whoIsAllowedToJoinThisGroup),
|
title: Text(
|
||||||
subtitle: Text(
|
L10n.of(context).changeTheNameOfTheGroup),
|
||||||
room.joinRules.getLocalizedString(
|
subtitle: Text(room.getLocalizedDisplayname(
|
||||||
MatrixLocals(L10n.of(context))),
|
MatrixLocals(L10n.of(context)))),
|
||||||
|
onTap: () => setDisplaynameAction(context),
|
||||||
),
|
),
|
||||||
),
|
if (room.canSendEvent('m.room.canonical_alias') &&
|
||||||
),
|
room.joinRules == JoinRules.public)
|
||||||
PopupMenuButton(
|
ListTile(
|
||||||
onSelected: (HistoryVisibility historyVisibility) =>
|
leading: CircleAvatar(
|
||||||
showFutureLoadingDialog(
|
backgroundColor:
|
||||||
context: context,
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
future: () =>
|
foregroundColor: Colors.grey,
|
||||||
room.setHistoryVisibility(historyVisibility),
|
child: Icon(Icons.link_outlined),
|
||||||
),
|
|
||||||
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)
|
onTap: () => setCanonicalAliasAction(context),
|
||||||
PopupMenuItem<HistoryVisibility>(
|
title: Text(L10n.of(context).setInvitationLink),
|
||||||
value: HistoryVisibility.joined,
|
subtitle: Text(
|
||||||
child: Text(HistoryVisibility.joined
|
(room.canonicalAlias?.isNotEmpty ?? false)
|
||||||
.getLocalizedString(
|
? room.canonicalAlias
|
||||||
MatrixLocals(L10n.of(context)))),
|
: L10n.of(context).none),
|
||||||
),
|
),
|
||||||
if (room.canChangeHistoryVisibility)
|
ListTile(
|
||||||
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)))),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: ListTile(
|
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
foregroundColor: Colors.grey,
|
foregroundColor: Colors.grey,
|
||||||
child: Icon(Icons.visibility_outlined),
|
child: Icon(Icons.insert_emoticon_outlined),
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
L10n.of(context).visibilityOfTheChatHistory),
|
|
||||||
subtitle: Text(
|
|
||||||
room.historyVisibility.getLocalizedString(
|
|
||||||
MatrixLocals(L10n.of(context))),
|
|
||||||
),
|
),
|
||||||
|
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'] ??
|
||||||
|
<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',
|
||||||
|
arguments: {'room': room});
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
if (room.joinRules == JoinRules.public)
|
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
onSelected: (GuestAccess guestAccess) =>
|
onSelected: (JoinRules joinRule) =>
|
||||||
showFutureLoadingDialog(
|
showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => room.setGuestAccess(guestAccess),
|
future: () => room.setJoinRules(joinRule),
|
||||||
),
|
),
|
||||||
itemBuilder: (BuildContext context) =>
|
itemBuilder: (BuildContext context) =>
|
||||||
<PopupMenuEntry<GuestAccess>>[
|
<PopupMenuEntry<JoinRules>>[
|
||||||
if (room.canChangeGuestAccess)
|
if (room.canChangeJoinRules)
|
||||||
PopupMenuItem<GuestAccess>(
|
PopupMenuItem<JoinRules>(
|
||||||
value: GuestAccess.can_join,
|
value: JoinRules.public,
|
||||||
child: Text(
|
child: Text(JoinRules.public
|
||||||
GuestAccess.can_join.getLocalizedString(
|
.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context))),
|
MatrixLocals(L10n.of(context)))),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (room.canChangeGuestAccess)
|
if (room.canChangeJoinRules)
|
||||||
PopupMenuItem<GuestAccess>(
|
PopupMenuItem<JoinRules>(
|
||||||
value: GuestAccess.forbidden,
|
value: JoinRules.invite,
|
||||||
child: Text(
|
child: Text(JoinRules.invite
|
||||||
GuestAccess.forbidden.getLocalizedString(
|
.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context))),
|
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) =>
|
||||||
|
<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)))),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
@ -458,75 +424,119 @@ class _ChatDetailsState extends State<ChatDetails> {
|
|||||||
backgroundColor:
|
backgroundColor:
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
foregroundColor: Colors.grey,
|
foregroundColor: Colors.grey,
|
||||||
child: Icon(Icons.info_outline),
|
child: Icon(Icons.visibility_outlined),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(L10n.of(context)
|
||||||
L10n.of(context).areGuestsAllowedToJoin),
|
.visibilityOfTheChatHistory),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
room.guestAccess.getLocalizedString(
|
room.historyVisibility.getLocalizedString(
|
||||||
MatrixLocals(L10n.of(context))),
|
MatrixLocals(L10n.of(context))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
if (room.joinRules == JoinRules.public)
|
||||||
title: Text(L10n.of(context).editChatPermissions),
|
PopupMenuButton(
|
||||||
subtitle:
|
onSelected: (GuestAccess guestAccess) =>
|
||||||
Text(L10n.of(context).whoCanPerformWhichAction),
|
showFutureLoadingDialog(
|
||||||
leading: CircleAvatar(
|
context: context,
|
||||||
backgroundColor:
|
future: () =>
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
room.setGuestAccess(guestAccess),
|
||||||
foregroundColor: Colors.grey,
|
),
|
||||||
child: Icon(Icons.edit_attributes_outlined),
|
itemBuilder: (BuildContext context) =>
|
||||||
),
|
<PopupMenuEntry<GuestAccess>>[
|
||||||
onTap: () => AdaptivePageLayout.of(context)
|
if (room.canChangeGuestAccess)
|
||||||
.pushNamed('/rooms/${room.id}/permissions'),
|
PopupMenuItem<GuestAccess>(
|
||||||
),
|
value: GuestAccess.can_join,
|
||||||
Divider(thickness: 1),
|
child: Text(
|
||||||
ListTile(
|
GuestAccess.can_join.getLocalizedString(
|
||||||
title: Text(
|
MatrixLocals(L10n.of(context))),
|
||||||
actualMembersCount > 1
|
),
|
||||||
? L10n.of(context).countParticipants(
|
),
|
||||||
actualMembersCount.toString())
|
if (room.canChangeGuestAccess)
|
||||||
: L10n.of(context).emptyChat,
|
PopupMenuItem<GuestAccess>(
|
||||||
style: TextStyle(
|
value: GuestAccess.forbidden,
|
||||||
color: Theme.of(context).accentColor,
|
child: Text(
|
||||||
fontWeight: FontWeight.bold,
|
GuestAccess.forbidden
|
||||||
),
|
.getLocalizedString(
|
||||||
),
|
MatrixLocals(L10n.of(context))),
|
||||||
),
|
),
|
||||||
room.canInvite
|
),
|
||||||
? ListTile(
|
],
|
||||||
title: Text(L10n.of(context).inviteContact),
|
child: ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor:
|
backgroundColor: Theme.of(context)
|
||||||
Theme.of(context).primaryColor,
|
.scaffoldBackgroundColor,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.grey,
|
||||||
radius: Avatar.defaultSize / 2,
|
child: Icon(Icons.info_outline),
|
||||||
child: Icon(Icons.add_outlined),
|
|
||||||
),
|
),
|
||||||
onTap: () => AdaptivePageLayout.of(context)
|
title: Text(
|
||||||
.pushNamed('/rooms/${room.id}/invite'),
|
L10n.of(context).areGuestsAllowedToJoin),
|
||||||
)
|
subtitle: Text(
|
||||||
: Container(),
|
room.guestAccess.getLocalizedString(
|
||||||
],
|
MatrixLocals(L10n.of(context))),
|
||||||
)
|
),
|
||||||
: i < members.length + 1
|
),
|
||||||
? ParticipantListItem(members[i - 1])
|
),
|
||||||
: ListTile(
|
ListTile(
|
||||||
title: Text(L10n.of(context)
|
title: Text(L10n.of(context).editChatPermissions),
|
||||||
.loadCountMoreParticipants(
|
subtitle: Text(
|
||||||
(actualMembersCount - members.length)
|
L10n.of(context).whoCanPerformWhichAction),
|
||||||
.toString())),
|
leading: CircleAvatar(
|
||||||
leading: CircleAvatar(
|
backgroundColor:
|
||||||
backgroundColor:
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
foregroundColor: Colors.grey,
|
||||||
child: Icon(
|
child: Icon(Icons.edit_attributes_outlined),
|
||||||
Icons.refresh,
|
),
|
||||||
color: Colors.grey,
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ import 'package:famedlysdk/encryption.dart';
|
|||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/views/widgets/avatar.dart';
|
import 'package:fluffychat/views/widgets/avatar.dart';
|
||||||
import 'package:fluffychat/views/widgets/matrix.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/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import '../views/widgets/dialogs/key_verification_dialog.dart';
|
import '../views/widgets/dialogs/key_verification_dialog.dart';
|
||||||
@ -81,65 +82,132 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: StreamBuilder(
|
body: MaxWidthBody(
|
||||||
stream: room.onUpdate.stream,
|
withScrolling: true,
|
||||||
builder: (context, snapshot) {
|
child: StreamBuilder(
|
||||||
return FutureBuilder<List<DeviceKeys>>(
|
stream: room.onUpdate.stream,
|
||||||
future: room.getUserDeviceKeys(),
|
builder: (context, snapshot) {
|
||||||
builder: (BuildContext context, snapshot) {
|
return FutureBuilder<List<DeviceKeys>>(
|
||||||
if (snapshot.hasError) {
|
future: room.getUserDeviceKeys(),
|
||||||
return Center(
|
builder: (BuildContext context, snapshot) {
|
||||||
child: Text(L10n.of(context).oopsSomethingWentWrong +
|
if (snapshot.hasError) {
|
||||||
': ' +
|
return Center(
|
||||||
snapshot.error.toString()),
|
child: Text(L10n.of(context).oopsSomethingWentWrong +
|
||||||
);
|
': ' +
|
||||||
}
|
snapshot.error.toString()),
|
||||||
if (!snapshot.hasData) {
|
);
|
||||||
return Center(child: CircularProgressIndicator());
|
}
|
||||||
}
|
if (!snapshot.hasData) {
|
||||||
final deviceKeys = snapshot.data;
|
return Center(child: CircularProgressIndicator());
|
||||||
return ListView.builder(
|
}
|
||||||
itemCount: deviceKeys.length,
|
final deviceKeys = snapshot.data;
|
||||||
itemBuilder: (BuildContext context, int i) => Column(
|
return ListView.builder(
|
||||||
mainAxisSize: MainAxisSize.min,
|
shrinkWrap: true,
|
||||||
children: <Widget>[
|
physics: NeverScrollableScrollPhysics(),
|
||||||
if (i == 0 ||
|
itemCount: deviceKeys.length,
|
||||||
deviceKeys[i].userId != deviceKeys[i - 1].userId) ...{
|
itemBuilder: (BuildContext context, int i) => Column(
|
||||||
Divider(height: 1, thickness: 1),
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
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 = <PopupMenuEntry<String>>[];
|
||||||
|
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(
|
PopupMenuButton(
|
||||||
onSelected: (action) =>
|
onSelected: (action) =>
|
||||||
onSelected(context, action, deviceKeys[i]),
|
onSelected(context, action, deviceKeys[i]),
|
||||||
itemBuilder: (c) {
|
itemBuilder: (c) {
|
||||||
var items = <PopupMenuEntry<String>>[];
|
var items = <PopupMenuEntry<String>>[];
|
||||||
if (room.client.userDeviceKeys[deviceKeys[i].userId]
|
if (deviceKeys[i].blocked ||
|
||||||
.verified ==
|
!deviceKeys[i].verified) {
|
||||||
UserVerifiedStatus.unknown) {
|
|
||||||
items.add(PopupMenuItem(
|
items.add(PopupMenuItem(
|
||||||
value: 'verify_user',
|
value:
|
||||||
child: Text(L10n.of(context).verifyUser),
|
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;
|
return items;
|
||||||
},
|
},
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Avatar(
|
leading: CircleAvatar(
|
||||||
room
|
foregroundColor:
|
||||||
.getUserByMXIDSync(deviceKeys[i].userId)
|
Theme.of(context).textTheme.bodyText1.color,
|
||||||
.avatarUrl,
|
backgroundColor:
|
||||||
room
|
Theme.of(context).secondaryHeaderColor,
|
||||||
.getUserByMXIDSync(deviceKeys[i].userId)
|
child: Icon(deviceKeys[i].icon),
|
||||||
.calcDisplayname(),
|
|
||||||
),
|
),
|
||||||
title: Row(
|
title: Text(
|
||||||
|
deviceKeys[i].displayname,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
subtitle: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
room
|
deviceKeys[i].deviceId,
|
||||||
.getUserByMXIDSync(deviceKeys[i].userId)
|
|
||||||
.calcDisplayname(),
|
|
||||||
),
|
|
||||||
Spacer(),
|
|
||||||
Text(
|
|
||||||
deviceKeys[i].userId,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
@ -149,91 +217,33 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
|||||||
.withAlpha(150),
|
.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 = <PopupMenuEntry<String>>[];
|
}),
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import 'dart:developer';
|
|||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||||
import 'package:fluffychat/views/widgets/dialogs/permission_slider_dialog.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:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:fluffychat/views/widgets/matrix.dart';
|
import 'package:fluffychat/views/widgets/matrix.dart';
|
||||||
|
|
||||||
@ -52,133 +53,137 @@ class ChatPermissionsSettings extends StatelessWidget {
|
|||||||
leading: BackButton(),
|
leading: BackButton(),
|
||||||
title: Text(L10n.of(context).editChatPermissions),
|
title: Text(L10n.of(context).editChatPermissions),
|
||||||
),
|
),
|
||||||
body: StreamBuilder(
|
body: MaxWidthBody(
|
||||||
stream: Matrix.of(context).client.onSync.stream.where(
|
withScrolling: true,
|
||||||
(e) =>
|
child: StreamBuilder(
|
||||||
(e?.rooms?.join?.containsKey(roomId) ?? false) &&
|
stream: Matrix.of(context).client.onSync.stream.where(
|
||||||
(e.rooms.join[roomId]?.timeline?.events
|
(e) =>
|
||||||
?.any((s) => s.type == EventTypes.RoomPowerLevels) ??
|
(e?.rooms?.join?.containsKey(roomId) ?? false) &&
|
||||||
false),
|
(e.rooms.join[roomId]?.timeline?.events?.any(
|
||||||
),
|
(s) => s.type == EventTypes.RoomPowerLevels) ??
|
||||||
builder: (context, _) {
|
false),
|
||||||
final room = Matrix.of(context).client.getRoomById(roomId);
|
),
|
||||||
final powerLevelsContent = Map<String, dynamic>.from(
|
builder: (context, _) {
|
||||||
room.getState(EventTypes.RoomPowerLevels).content);
|
final room = Matrix.of(context).client.getRoomById(roomId);
|
||||||
final powerLevels = Map<String, dynamic>.from(powerLevelsContent)
|
final powerLevelsContent = Map<String, dynamic>.from(
|
||||||
..removeWhere((k, v) => !(v is int));
|
room.getState(EventTypes.RoomPowerLevels).content);
|
||||||
final eventsPowerLevels =
|
final powerLevels = Map<String, dynamic>.from(powerLevelsContent)
|
||||||
Map<String, dynamic>.from(powerLevelsContent['events'])
|
..removeWhere((k, v) => !(v is int));
|
||||||
..removeWhere((k, v) => !(v is int));
|
final eventsPowerLevels =
|
||||||
|
Map<String, dynamic>.from(powerLevelsContent['events'])
|
||||||
|
..removeWhere((k, v) => !(v is int));
|
||||||
|
|
||||||
return ListView(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
for (var entry in powerLevels.entries)
|
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)
|
|
||||||
PermissionsListTile(
|
PermissionsListTile(
|
||||||
permissionKey: entry.key,
|
permissionKey: entry.key,
|
||||||
category: 'events',
|
|
||||||
permission: entry.value,
|
permission: entry.value,
|
||||||
onTap: () => _editPowerLevel(
|
onTap: () =>
|
||||||
context, entry.key, entry.value,
|
_editPowerLevel(context, entry.key, entry.value),
|
||||||
category: 'events'),
|
|
||||||
),
|
),
|
||||||
if (room.ownPowerLevel >= 100) ...{
|
|
||||||
Divider(thickness: 1),
|
Divider(thickness: 1),
|
||||||
FutureBuilder<ServerCapabilities>(
|
ListTile(
|
||||||
future: room.client.requestServerCapabilities(),
|
title: Text(
|
||||||
builder: (context, snapshot) {
|
L10n.of(context).notifications,
|
||||||
if (!snapshot.hasData) {
|
style: TextStyle(
|
||||||
return Center(child: CircularProgressIndicator());
|
color: Theme.of(context).primaryColor,
|
||||||
}
|
fontWeight: FontWeight.bold,
|
||||||
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<String>(
|
|
||||||
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());
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
},
|
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<ServerCapabilities>(
|
||||||
|
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<String>(
|
||||||
|
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());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import 'package:fluffychat/views/widgets/default_app_bar_search_field.dart';
|
|||||||
|
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/views/widgets/avatar.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:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:fluffychat/views/widgets/matrix.dart';
|
import 'package:fluffychat/views/widgets/matrix.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -128,45 +129,52 @@ class _InvitationSelectionState extends State<InvitationSelection> {
|
|||||||
onChanged: (String text) => searchUserWithCoolDown(context, text),
|
onChanged: (String text) => searchUserWithCoolDown(context, text),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: foundProfiles.isNotEmpty
|
body: MaxWidthBody(
|
||||||
? ListView.builder(
|
withScrolling: true,
|
||||||
itemCount: foundProfiles.length,
|
child: foundProfiles.isNotEmpty
|
||||||
itemBuilder: (BuildContext context, int i) => ListTile(
|
? ListView.builder(
|
||||||
leading: Avatar(
|
physics: NeverScrollableScrollPhysics(),
|
||||||
foundProfiles[i].avatarUrl,
|
shrinkWrap: true,
|
||||||
foundProfiles[i].displayname ?? foundProfiles[i].userId,
|
itemCount: foundProfiles.length,
|
||||||
),
|
itemBuilder: (BuildContext context, int i) => ListTile(
|
||||||
title: Text(
|
leading: Avatar(
|
||||||
foundProfiles[i].displayname ??
|
foundProfiles[i].avatarUrl,
|
||||||
foundProfiles[i].userId.localpart,
|
foundProfiles[i].displayname ?? foundProfiles[i].userId,
|
||||||
),
|
|
||||||
subtitle: Text(foundProfiles[i].userId),
|
|
||||||
onTap: () => inviteAction(context, foundProfiles[i].userId),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: FutureBuilder<List<User>>(
|
|
||||||
future: getContacts(context),
|
|
||||||
builder: (BuildContext context, snapshot) {
|
|
||||||
if (!snapshot.hasData) {
|
|
||||||
return Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
var contacts = snapshot.data;
|
|
||||||
return ListView.builder(
|
|
||||||
itemCount: contacts.length,
|
|
||||||
itemBuilder: (BuildContext context, int i) => ListTile(
|
|
||||||
leading: Avatar(
|
|
||||||
contacts[i].avatarUrl,
|
|
||||||
contacts[i].calcDisplayname(),
|
|
||||||
),
|
|
||||||
title: Text(contacts[i].calcDisplayname()),
|
|
||||||
subtitle: Text(contacts[i].id),
|
|
||||||
onTap: () => inviteAction(context, contacts[i].id),
|
|
||||||
),
|
),
|
||||||
);
|
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(
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
||||||
import 'package:famedlysdk/famedlysdk.dart' as sdk;
|
import 'package:famedlysdk/famedlysdk.dart' as sdk;
|
||||||
|
import 'package:fluffychat/views/widgets/max_width_body.dart';
|
||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:fluffychat/views/widgets/matrix.dart';
|
import 'package:fluffychat/views/widgets/matrix.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -45,32 +46,34 @@ class _NewGroupState extends State<NewGroup> {
|
|||||||
title: Text(L10n.of(context).createNewGroup),
|
title: Text(L10n.of(context).createNewGroup),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
body: Column(
|
body: MaxWidthBody(
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: <Widget>[
|
mainAxisSize: MainAxisSize.min,
|
||||||
Padding(
|
children: <Widget>[
|
||||||
padding: const EdgeInsets.all(12.0),
|
Padding(
|
||||||
child: TextField(
|
padding: const EdgeInsets.all(12.0),
|
||||||
controller: controller,
|
child: TextField(
|
||||||
autofocus: true,
|
controller: controller,
|
||||||
autocorrect: false,
|
autofocus: true,
|
||||||
textInputAction: TextInputAction.go,
|
autocorrect: false,
|
||||||
onSubmitted: (s) => submitAction(context),
|
textInputAction: TextInputAction.go,
|
||||||
decoration: InputDecoration(
|
onSubmitted: (s) => submitAction(context),
|
||||||
labelText: L10n.of(context).optionalGroupName,
|
decoration: InputDecoration(
|
||||||
prefixIcon: Icon(Icons.people_outlined),
|
labelText: L10n.of(context).optionalGroupName,
|
||||||
hintText: L10n.of(context).enterAGroupName),
|
prefixIcon: Icon(Icons.people_outlined),
|
||||||
|
hintText: L10n.of(context).enterAGroupName),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
SwitchListTile(
|
||||||
SwitchListTile(
|
title: Text(L10n.of(context).groupIsPublic),
|
||||||
title: Text(L10n.of(context).groupIsPublic),
|
value: publicGroup,
|
||||||
value: publicGroup,
|
onChanged: (bool b) => setState(() => publicGroup = b),
|
||||||
onChanged: (bool b) => setState(() => publicGroup = b),
|
),
|
||||||
),
|
Expanded(
|
||||||
Expanded(
|
child: Image.asset('assets/new_group_wallpaper.png'),
|
||||||
child: Image.asset('assets/new_group_wallpaper.png'),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () => submitAction(context),
|
onPressed: () => submitAction(context),
|
||||||
|
@ -4,6 +4,7 @@ import 'package:adaptive_page_layout/adaptive_page_layout.dart';
|
|||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/views/widgets/avatar.dart';
|
import 'package:fluffychat/views/widgets/avatar.dart';
|
||||||
import 'package:fluffychat/views/widgets/contacts_list.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:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:fluffychat/views/widgets/matrix.dart';
|
import 'package:fluffychat/views/widgets/matrix.dart';
|
||||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||||
@ -101,123 +102,127 @@ class _NewPrivateChatState extends State<NewPrivateChat> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Column(
|
body: MaxWidthBody(
|
||||||
children: <Widget>[
|
child: Column(
|
||||||
Padding(
|
children: <Widget>[
|
||||||
padding: const EdgeInsets.all(12.0),
|
Padding(
|
||||||
child: Form(
|
padding: const EdgeInsets.all(12.0),
|
||||||
key: _formKey,
|
child: Form(
|
||||||
child: TextFormField(
|
key: _formKey,
|
||||||
controller: controller,
|
child: TextFormField(
|
||||||
//autofocus: true,
|
controller: controller,
|
||||||
autocorrect: false,
|
//autofocus: true,
|
||||||
onChanged: (String text) => searchUserWithCoolDown(context),
|
autocorrect: false,
|
||||||
textInputAction: TextInputAction.go,
|
onChanged: (String text) => searchUserWithCoolDown(context),
|
||||||
onFieldSubmitted: (s) => submitAction(context),
|
textInputAction: TextInputAction.go,
|
||||||
validator: (value) {
|
onFieldSubmitted: (s) => submitAction(context),
|
||||||
if (value.isEmpty) {
|
validator: (value) {
|
||||||
return L10n.of(context).pleaseEnterAMatrixIdentifier;
|
if (value.isEmpty) {
|
||||||
}
|
return L10n.of(context).pleaseEnterAMatrixIdentifier;
|
||||||
final matrix = Matrix.of(context);
|
}
|
||||||
var mxid = '@' + controller.text.trim();
|
final matrix = Matrix.of(context);
|
||||||
if (mxid == matrix.client.userID) {
|
var mxid = '@' + controller.text.trim();
|
||||||
return L10n.of(context).youCannotInviteYourself;
|
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;
|
||||||
if (!mxid.contains(':')) {
|
}
|
||||||
return L10n.of(context).makeSureTheIdentifierIsValid;
|
if (!mxid.contains(':')) {
|
||||||
}
|
return L10n.of(context).makeSureTheIdentifierIsValid;
|
||||||
return null;
|
}
|
||||||
},
|
return null;
|
||||||
decoration: InputDecoration(
|
},
|
||||||
labelText: L10n.of(context).enterAUsername,
|
decoration: InputDecoration(
|
||||||
prefixIcon: loading
|
labelText: L10n.of(context).enterAUsername,
|
||||||
? Container(
|
prefixIcon: loading
|
||||||
padding: const EdgeInsets.all(8.0),
|
? Container(
|
||||||
width: 12,
|
padding: const EdgeInsets.all(8.0),
|
||||||
height: 12,
|
width: 12,
|
||||||
child: CircularProgressIndicator(),
|
height: 12,
|
||||||
)
|
child: CircularProgressIndicator(),
|
||||||
: correctMxId
|
)
|
||||||
? Padding(
|
: correctMxId
|
||||||
padding: const EdgeInsets.all(8.0),
|
? Padding(
|
||||||
child: Avatar(
|
padding: const EdgeInsets.all(8.0),
|
||||||
foundProfile.avatarUrl,
|
child: Avatar(
|
||||||
foundProfile.displayname ?? foundProfile.userId,
|
foundProfile.avatarUrl,
|
||||||
size: 12,
|
foundProfile.displayname ??
|
||||||
),
|
foundProfile.userId,
|
||||||
)
|
size: 12,
|
||||||
: Icon(Icons.account_circle_outlined),
|
),
|
||||||
prefixText: '@',
|
)
|
||||||
suffixIcon: IconButton(
|
: Icon(Icons.account_circle_outlined),
|
||||||
onPressed: () => submitAction(context),
|
prefixText: '@',
|
||||||
icon: Icon(Icons.arrow_forward_outlined),
|
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),
|
||||||
Divider(height: 1),
|
ListTile(
|
||||||
ListTile(
|
leading: CircleAvatar(
|
||||||
leading: CircleAvatar(
|
radius: Avatar.defaultSize / 2,
|
||||||
radius: Avatar.defaultSize / 2,
|
foregroundColor: Theme.of(context).accentColor,
|
||||||
foregroundColor: Theme.of(context).accentColor,
|
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
||||||
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
child: Icon(Icons.share_outlined),
|
||||||
child: Icon(Icons.share_outlined),
|
),
|
||||||
),
|
onTap: () => FluffyShare.share(
|
||||||
onTap: () => FluffyShare.share(
|
L10n.of(context).inviteText(Matrix.of(context).client.userID,
|
||||||
L10n.of(context).inviteText(Matrix.of(context).client.userID,
|
'https://matrix.to/#/${Matrix.of(context).client.userID}'),
|
||||||
'https://matrix.to/#/${Matrix.of(context).client.userID}'),
|
context),
|
||||||
context),
|
title: Text('${L10n.of(context).yourOwnUsername}:'),
|
||||||
title: Text('${L10n.of(context).yourOwnUsername}:'),
|
subtitle: Text(
|
||||||
subtitle: Text(
|
Matrix.of(context).client.userID,
|
||||||
Matrix.of(context).client.userID,
|
style: TextStyle(color: Theme.of(context).accentColor),
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (foundProfiles.isEmpty)
|
Divider(height: 1),
|
||||||
Expanded(
|
if (foundProfiles.isNotEmpty)
|
||||||
child: ContactsList(searchController: controller),
|
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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
import 'package:fluffychat/views/widgets/max_width_body.dart';
|
||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:fluffychat/views/widgets/matrix.dart';
|
import 'package:fluffychat/views/widgets/matrix.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -99,64 +100,67 @@ class _Settings3PidState extends State<Settings3Pid> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: FutureBuilder<List<ThirdPartyIdentifier>>(
|
body: MaxWidthBody(
|
||||||
future: _request,
|
child: FutureBuilder<List<ThirdPartyIdentifier>>(
|
||||||
builder: (BuildContext context,
|
future: _request,
|
||||||
AsyncSnapshot<List<ThirdPartyIdentifier>> snapshot) {
|
builder: (BuildContext context,
|
||||||
if (snapshot.hasError) {
|
AsyncSnapshot<List<ThirdPartyIdentifier>> snapshot) {
|
||||||
return Center(
|
if (snapshot.hasError) {
|
||||||
child: Text(
|
return Center(
|
||||||
snapshot.error.toString(),
|
child: Text(
|
||||||
textAlign: TextAlign.center,
|
snapshot.error.toString(),
|
||||||
),
|
textAlign: TextAlign.center,
|
||||||
);
|
),
|
||||||
}
|
);
|
||||||
if (!snapshot.hasData) {
|
}
|
||||||
return Center(child: CircularProgressIndicator());
|
if (!snapshot.hasData) {
|
||||||
}
|
return Center(child: CircularProgressIndicator());
|
||||||
final identifier = snapshot.data;
|
}
|
||||||
return Column(
|
final identifier = snapshot.data;
|
||||||
children: [
|
return Column(
|
||||||
ListTile(
|
children: [
|
||||||
leading: CircleAvatar(
|
ListTile(
|
||||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
leading: CircleAvatar(
|
||||||
foregroundColor:
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
identifier.isEmpty ? Colors.orange : Colors.grey,
|
foregroundColor:
|
||||||
child: Icon(
|
identifier.isEmpty ? Colors.orange : Colors.grey,
|
||||||
|
child: Icon(
|
||||||
|
identifier.isEmpty
|
||||||
|
? Icons.warning_outlined
|
||||||
|
: Icons.info_outlined,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
identifier.isEmpty
|
identifier.isEmpty
|
||||||
? Icons.warning_outlined
|
? L10n.of(context).noPasswordRecoveryDescription
|
||||||
: Icons.info_outlined,
|
: L10n.of(context)
|
||||||
|
.withTheseAddressesRecoveryDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: Text(
|
Divider(height: 1),
|
||||||
identifier.isEmpty
|
Expanded(
|
||||||
? L10n.of(context).noPasswordRecoveryDescription
|
child: ListView.builder(
|
||||||
: L10n.of(context).withTheseAddressesRecoveryDescription,
|
itemCount: identifier.length,
|
||||||
),
|
itemBuilder: (BuildContext context, int i) => ListTile(
|
||||||
),
|
leading: CircleAvatar(
|
||||||
Divider(height: 1),
|
backgroundColor:
|
||||||
Expanded(
|
Theme.of(context).scaffoldBackgroundColor,
|
||||||
child: ListView.builder(
|
foregroundColor: Colors.grey,
|
||||||
itemCount: identifier.length,
|
child: Icon(identifier[i].iconData)),
|
||||||
itemBuilder: (BuildContext context, int i) => ListTile(
|
title: Text(identifier[i].address),
|
||||||
leading: CircleAvatar(
|
trailing: IconButton(
|
||||||
backgroundColor:
|
tooltip: L10n.of(context).delete,
|
||||||
Theme.of(context).scaffoldBackgroundColor,
|
icon: Icon(Icons.delete_forever_outlined),
|
||||||
foregroundColor: Colors.grey,
|
color: Colors.red,
|
||||||
child: Icon(identifier[i].iconData)),
|
onPressed: () => _delete3Pid(context, identifier[i]),
|
||||||
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]),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'package:adaptive_dialog/adaptive_dialog.dart';
|
|||||||
import 'package:famedlysdk/encryption/utils/key_verification.dart';
|
import 'package:famedlysdk/encryption/utils/key_verification.dart';
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/views/widgets/dialogs/key_verification_dialog.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:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
@ -131,83 +132,86 @@ class DevicesSettingsState extends State<DevicesSettings> {
|
|||||||
leading: BackButton(),
|
leading: BackButton(),
|
||||||
title: Text(L10n.of(context).devices),
|
title: Text(L10n.of(context).devices),
|
||||||
),
|
),
|
||||||
body: FutureBuilder<bool>(
|
body: MaxWidthBody(
|
||||||
future: _loadUserDevices(context),
|
child: FutureBuilder<bool>(
|
||||||
builder: (BuildContext context, snapshot) {
|
future: _loadUserDevices(context),
|
||||||
if (snapshot.hasError) {
|
builder: (BuildContext context, snapshot) {
|
||||||
return Center(
|
if (snapshot.hasError) {
|
||||||
child: Column(
|
return Center(
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: <Widget>[
|
mainAxisSize: MainAxisSize.min,
|
||||||
Icon(Icons.error_outlined),
|
children: <Widget>[
|
||||||
Text(snapshot.error.toString()),
|
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<Device>.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: <Widget>[
|
|
||||||
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),
|
|
||||||
),
|
),
|
||||||
Divider(height: 1),
|
);
|
||||||
if (devices.isNotEmpty)
|
}
|
||||||
ListTile(
|
if (!snapshot.hasData || this.devices == null) {
|
||||||
title: Text(
|
return Center(child: CircularProgressIndicator());
|
||||||
_errorDeletingDevices ??
|
}
|
||||||
L10n.of(context).removeAllOtherDevices,
|
Function isOwnDevice = (Device userDevice) =>
|
||||||
style: TextStyle(color: Colors.red),
|
userDevice.deviceId == Matrix.of(context).client.deviceID;
|
||||||
|
final devices = List<Device>.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: <Widget>[
|
||||||
|
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
|
Divider(height: 1),
|
||||||
? CircularProgressIndicator()
|
if (devices.isNotEmpty)
|
||||||
: Icon(Icons.delete_outline),
|
ListTile(
|
||||||
onTap: _loadingDeletingDevices
|
title: Text(
|
||||||
? null
|
_errorDeletingDevices ??
|
||||||
: () => _removeDevicesAction(context, devices),
|
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),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import 'package:famedlysdk/famedlysdk.dart';
|
|||||||
import 'package:file_picker_cross/file_picker_cross.dart';
|
import 'package:file_picker_cross/file_picker_cross.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/utils/platform_infos.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/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
@ -184,223 +185,227 @@ class _EmotesSettingsState extends State<EmotesSettings> {
|
|||||||
child: Icon(Icons.save_outlined, color: Colors.white),
|
child: Icon(Icons.save_outlined, color: Colors.white),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
body: StreamBuilder(
|
body: MaxWidthBody(
|
||||||
stream: widget.room?.onUpdate?.stream,
|
child: StreamBuilder(
|
||||||
builder: (context, snapshot) {
|
stream: widget.room?.onUpdate?.stream,
|
||||||
return Column(
|
builder: (context, snapshot) {
|
||||||
children: <Widget>[
|
return Column(
|
||||||
if (!readonly)
|
children: <Widget>[
|
||||||
Container(
|
if (!readonly)
|
||||||
padding: EdgeInsets.symmetric(
|
Container(
|
||||||
vertical: 8.0,
|
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
title: _EmoteImagePicker(newMxcController),
|
child: ListTile(
|
||||||
trailing: InkWell(
|
leading: Container(
|
||||||
onTap: () async {
|
width: 180.0,
|
||||||
if (newEmoteController.text == null ||
|
height: 38,
|
||||||
newEmoteController.text.isEmpty ||
|
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||||
newMxcController.text == null ||
|
decoration: BoxDecoration(
|
||||||
newMxcController.text.isEmpty) {
|
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||||
await showOkAlertDialog(
|
color: Theme.of(context).secondaryHeaderColor,
|
||||||
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: TextField(
|
||||||
: ListView.separated(
|
controller: newEmoteController,
|
||||||
separatorBuilder: (BuildContext context, int i) =>
|
autocorrect: false,
|
||||||
Container(),
|
minLines: 1,
|
||||||
itemCount: emotes.length + 1,
|
maxLines: 1,
|
||||||
itemBuilder: (BuildContext context, int i) {
|
decoration: InputDecoration(
|
||||||
if (i >= emotes.length) {
|
hintText: L10n.of(context).emoteShortcode,
|
||||||
return Container(height: 70);
|
prefixText: ': ',
|
||||||
}
|
suffixText: ':',
|
||||||
final emote = emotes[i];
|
prefixStyle: TextStyle(
|
||||||
final controller = TextEditingController();
|
color: Theme.of(context).accentColor,
|
||||||
controller.text = emote.emoteClean;
|
fontWeight: FontWeight.bold,
|
||||||
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),
|
suffixStyle: TextStyle(
|
||||||
trailing: readonly
|
color: Theme.of(context).accentColor,
|
||||||
? null
|
fontWeight: FontWeight.bold,
|
||||||
: InkWell(
|
),
|
||||||
onTap: () => setState(() {
|
border: InputBorder.none,
|
||||||
emotes.removeWhere(
|
),
|
||||||
(e) => e.emote == emote.emote);
|
),
|
||||||
showSave = true;
|
|
||||||
}),
|
|
||||||
child: Icon(
|
|
||||||
Icons.delete_forever_outlined,
|
|
||||||
color: Colors.red,
|
|
||||||
size: 32.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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: 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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:fluffychat/views/widgets/avatar.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:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
@ -45,71 +46,73 @@ class _SettingsIgnoreListState extends State<SettingsIgnoreList> {
|
|||||||
leading: BackButton(),
|
leading: BackButton(),
|
||||||
title: Text(L10n.of(context).ignoredUsers),
|
title: Text(L10n.of(context).ignoredUsers),
|
||||||
),
|
),
|
||||||
body: Column(
|
body: MaxWidthBody(
|
||||||
children: [
|
child: Column(
|
||||||
Padding(
|
children: [
|
||||||
padding: const EdgeInsets.all(16.0),
|
Padding(
|
||||||
child: Column(
|
padding: const EdgeInsets.all(16.0),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
TextField(
|
children: [
|
||||||
controller: _controller,
|
TextField(
|
||||||
autocorrect: false,
|
controller: _controller,
|
||||||
textInputAction: TextInputAction.done,
|
autocorrect: false,
|
||||||
onSubmitted: (_) => _ignoreUser(context),
|
textInputAction: TextInputAction.done,
|
||||||
decoration: InputDecoration(
|
onSubmitted: (_) => _ignoreUser(context),
|
||||||
border: OutlineInputBorder(),
|
decoration: InputDecoration(
|
||||||
hintText: 'bad_guy:domain.abc',
|
border: OutlineInputBorder(),
|
||||||
prefixText: '@',
|
hintText: 'bad_guy:domain.abc',
|
||||||
labelText: L10n.of(context).ignoreUsername,
|
prefixText: '@',
|
||||||
suffixIcon: IconButton(
|
labelText: L10n.of(context).ignoreUsername,
|
||||||
tooltip: L10n.of(context).ignore,
|
suffixIcon: IconButton(
|
||||||
icon: Icon(Icons.done_outlined),
|
tooltip: L10n.of(context).ignore,
|
||||||
onPressed: () => _ignoreUser(context),
|
icon: Icon(Icons.done_outlined),
|
||||||
|
onPressed: () => _ignoreUser(context),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
SizedBox(height: 16),
|
||||||
SizedBox(height: 16),
|
Text(
|
||||||
Text(
|
L10n.of(context).ignoreListDescription,
|
||||||
L10n.of(context).ignoreListDescription,
|
style: TextStyle(color: Colors.orange),
|
||||||
style: TextStyle(color: Colors.orange),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
Divider(height: 1),
|
||||||
Divider(height: 1),
|
Expanded(
|
||||||
Expanded(
|
child: StreamBuilder<Object>(
|
||||||
child: StreamBuilder<Object>(
|
stream: client.onAccountData.stream
|
||||||
stream: client.onAccountData.stream
|
.where((a) => a.type == 'm.ignored_user_list'),
|
||||||
.where((a) => a.type == 'm.ignored_user_list'),
|
builder: (context, snapshot) {
|
||||||
builder: (context, snapshot) {
|
return ListView.builder(
|
||||||
return ListView.builder(
|
itemCount: client.ignoredUsers.length,
|
||||||
itemCount: client.ignoredUsers.length,
|
itemBuilder: (c, i) => FutureBuilder<Profile>(
|
||||||
itemBuilder: (c, i) => FutureBuilder<Profile>(
|
future:
|
||||||
future:
|
client.getProfileFromUserId(client.ignoredUsers[i]),
|
||||||
client.getProfileFromUserId(client.ignoredUsers[i]),
|
builder: (c, s) => ListTile(
|
||||||
builder: (c, s) => ListTile(
|
leading: Avatar(
|
||||||
leading: Avatar(
|
s.data?.avatarUrl ?? Uri.parse(''),
|
||||||
s.data?.avatarUrl ?? Uri.parse(''),
|
s.data?.displayname ?? client.ignoredUsers[i],
|
||||||
s.data?.displayname ?? client.ignoredUsers[i],
|
),
|
||||||
),
|
title: Text(
|
||||||
title:
|
s.data?.displayname ?? client.ignoredUsers[i]),
|
||||||
Text(s.data?.displayname ?? client.ignoredUsers[i]),
|
trailing: IconButton(
|
||||||
trailing: IconButton(
|
tooltip: L10n.of(context).delete,
|
||||||
tooltip: L10n.of(context).delete,
|
icon: Icon(Icons.delete_forever_outlined),
|
||||||
icon: Icon(Icons.delete_forever_outlined),
|
onPressed: () => showFutureLoadingDialog(
|
||||||
onPressed: () => showFutureLoadingDialog(
|
context: context,
|
||||||
context: context,
|
future: () =>
|
||||||
future: () =>
|
client.unignoreUser(client.ignoredUsers[i]),
|
||||||
client.unignoreUser(client.ignoredUsers[i]),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
}),
|
||||||
}),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:famedlysdk/famedlysdk.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:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:fluffychat/config/app_config.dart';
|
import 'package:fluffychat/config/app_config.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -113,97 +114,101 @@ class SettingsNotifications extends StatelessWidget {
|
|||||||
leading: BackButton(),
|
leading: BackButton(),
|
||||||
title: Text(L10n.of(context).notifications),
|
title: Text(L10n.of(context).notifications),
|
||||||
),
|
),
|
||||||
body: StreamBuilder(
|
body: MaxWidthBody(
|
||||||
stream: Matrix.of(context)
|
withScrolling: true,
|
||||||
.client
|
child: StreamBuilder(
|
||||||
.onAccountData
|
stream: Matrix.of(context)
|
||||||
.stream
|
.client
|
||||||
.where((event) => event.type == 'm.push_rules'),
|
.onAccountData
|
||||||
builder: (BuildContext context, _) {
|
.stream
|
||||||
return ListView(
|
.where((event) => event.type == 'm.push_rules'),
|
||||||
children: [
|
builder: (BuildContext context, _) {
|
||||||
SwitchListTile(
|
return Column(
|
||||||
value: !Matrix.of(context).client.allPushNotificationsMuted,
|
children: [
|
||||||
title:
|
SwitchListTile(
|
||||||
Text(L10n.of(context).notificationsEnabledForThisAccount),
|
value: !Matrix.of(context).client.allPushNotificationsMuted,
|
||||||
onChanged: (_) => showFutureLoadingDialog(
|
title: Text(
|
||||||
context: context,
|
L10n.of(context).notificationsEnabledForThisAccount),
|
||||||
future: () => Matrix.of(context)
|
onChanged: (_) => showFutureLoadingDialog(
|
||||||
.client
|
context: context,
|
||||||
.setMuteAllPushNotifications(
|
future: () =>
|
||||||
!Matrix.of(context).client.allPushNotificationsMuted,
|
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(),
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
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),
|
Divider(thickness: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
L10n.of(context).pushRules,
|
L10n.of(context).devices,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).accentColor,
|
color: Theme.of(context).accentColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
for (var item in items)
|
FutureBuilder<List<Pusher>>(
|
||||||
SwitchListTile(
|
future: Matrix.of(context).client.requestPushers(),
|
||||||
value: _getNotificationSetting(context, item) ?? true,
|
builder: (context, snapshot) {
|
||||||
title: Text(item.title(context)),
|
if (snapshot.hasError) {
|
||||||
onChanged: (bool enabled) =>
|
Center(
|
||||||
_setNotificationSetting(context, item, enabled),
|
child: Text(
|
||||||
),
|
snapshot.error.toLocalizedString(context),
|
||||||
},
|
),
|
||||||
Divider(thickness: 1),
|
);
|
||||||
ListTile(
|
}
|
||||||
title: Text(
|
if (!snapshot.hasData) {
|
||||||
L10n.of(context).devices,
|
Center(child: CircularProgressIndicator());
|
||||||
style: TextStyle(
|
}
|
||||||
color: Theme.of(context).accentColor,
|
final pushers = snapshot.data;
|
||||||
fontWeight: FontWeight.bold,
|
return ListView.builder(
|
||||||
),
|
physics: NeverScrollableScrollPhysics(),
|
||||||
),
|
shrinkWrap: true,
|
||||||
),
|
itemCount: pushers.length,
|
||||||
FutureBuilder<List<Pusher>>(
|
itemBuilder: (_, i) => ListTile(
|
||||||
future: Matrix.of(context).client.requestPushers(),
|
title: Text(
|
||||||
builder: (context, snapshot) {
|
'${pushers[i].appDisplayName} - ${pushers[i].appId}'),
|
||||||
if (snapshot.hasError) {
|
subtitle: Text(pushers[i].data.url.toString()),
|
||||||
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()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:adaptive_theme/adaptive_theme.dart';
|
import 'package:adaptive_theme/adaptive_theme.dart';
|
||||||
import 'package:fluffychat/config/setting_keys.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/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
@ -56,100 +57,104 @@ class _SettingsStyleState extends State<SettingsStyle> {
|
|||||||
leading: BackButton(),
|
leading: BackButton(),
|
||||||
title: Text(L10n.of(context).changeTheme),
|
title: Text(L10n.of(context).changeTheme),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: MaxWidthBody(
|
||||||
children: [
|
withScrolling: true,
|
||||||
RadioListTile<AdaptiveThemeMode>(
|
child: Column(
|
||||||
groupValue: _currentTheme,
|
children: [
|
||||||
value: AdaptiveThemeMode.system,
|
RadioListTile<AdaptiveThemeMode>(
|
||||||
title: Text(L10n.of(context).systemTheme),
|
groupValue: _currentTheme,
|
||||||
onChanged: (t) => _switchTheme(t, context),
|
value: AdaptiveThemeMode.system,
|
||||||
),
|
title: Text(L10n.of(context).systemTheme),
|
||||||
RadioListTile<AdaptiveThemeMode>(
|
onChanged: (t) => _switchTheme(t, context),
|
||||||
groupValue: _currentTheme,
|
|
||||||
value: AdaptiveThemeMode.light,
|
|
||||||
title: Text(L10n.of(context).lightTheme),
|
|
||||||
onChanged: (t) => _switchTheme(t, context),
|
|
||||||
),
|
|
||||||
RadioListTile<AdaptiveThemeMode>(
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
RadioListTile<AdaptiveThemeMode>(
|
||||||
if (Matrix.of(context).wallpaper != null)
|
groupValue: _currentTheme,
|
||||||
|
value: AdaptiveThemeMode.light,
|
||||||
|
title: Text(L10n.of(context).lightTheme),
|
||||||
|
onChanged: (t) => _switchTheme(t, context),
|
||||||
|
),
|
||||||
|
RadioListTile<AdaptiveThemeMode>(
|
||||||
|
groupValue: _currentTheme,
|
||||||
|
value: AdaptiveThemeMode.dark,
|
||||||
|
title: Text(L10n.of(context).darkTheme),
|
||||||
|
onChanged: (t) => _switchTheme(t, context),
|
||||||
|
),
|
||||||
|
Divider(height: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Image.file(
|
title: Text(
|
||||||
Matrix.of(context).wallpaper,
|
L10n.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(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.bodyText1.fontSize *
|
color: Theme.of(context).accentColor,
|
||||||
AppConfig.fontSizeFactor,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (Matrix.of(context).wallpaper != null)
|
||||||
Slider(
|
ListTile(
|
||||||
min: 0.5,
|
title: Image.file(
|
||||||
max: 2.5,
|
Matrix.of(context).wallpaper,
|
||||||
divisions: 4,
|
height: 38,
|
||||||
value: AppConfig.fontSizeFactor,
|
fit: BoxFit.cover,
|
||||||
semanticFormatterCallback: (d) => d.toString(),
|
),
|
||||||
onChanged: (d) {
|
trailing: Icon(
|
||||||
setState(() => AppConfig.fontSizeFactor = d);
|
Icons.delete_forever_outlined,
|
||||||
Matrix.of(context).store.setItem(
|
color: Colors.red,
|
||||||
SettingKeys.fontSizeFactor,
|
),
|
||||||
AppConfig.fontSizeFactor.toString(),
|
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(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
38
lib/views/widgets/max_width_body.dart
Normal file
38
lib/views/widgets/max_width_body.dart
Normal file
@ -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,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user