refactor: Folder structure and MVC chat ui

This commit is contained in:
Christian Pauly 2021-04-15 13:03:14 +02:00
commit cd9b4ee46b
47 changed files with 1648 additions and 1582 deletions

View file

@ -1,31 +1,31 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/controllers/archive_controller.dart';
import 'package:fluffychat/controllers/homeserver_picker_controller.dart';
import 'package:fluffychat/controllers/invitation_selection_controller.dart';
import 'package:fluffychat/controllers/sign_up_controller.dart';
import 'package:fluffychat/controllers/sign_up_password_controller.dart';
import 'package:fluffychat/views/archive.dart';
import 'package:fluffychat/views/homeserver_picker.dart';
import 'package:fluffychat/views/invitation_selection.dart';
import 'package:fluffychat/views/sign_up.dart';
import 'package:fluffychat/views/sign_up_password.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:fluffychat/views/chat.dart';
import 'package:fluffychat/controllers/chat_details_controller.dart';
import 'package:fluffychat/controllers/chat_encryption_settings_controller.dart';
import 'package:fluffychat/controllers/chat_list_controller.dart';
import 'package:fluffychat/controllers/chat_permissions_settings_controller.dart';
import 'package:fluffychat/views/empty_page.dart';
import 'package:fluffychat/views/chat_details.dart';
import 'package:fluffychat/views/chat_encryption_settings.dart';
import 'package:fluffychat/views/chat_list.dart';
import 'package:fluffychat/views/chat_permissions_settings.dart';
import 'package:fluffychat/views/ui/empty_page_ui.dart';
import 'package:fluffychat/views/widgets/loading_view.dart';
import 'package:fluffychat/views/widgets/log_view.dart';
import 'package:fluffychat/views/login.dart';
import 'package:fluffychat/controllers/new_group_controller.dart';
import 'package:fluffychat/controllers/new_private_chat_controller.dart';
import 'package:fluffychat/views/search_view.dart';
import 'package:fluffychat/views/settings.dart';
import 'package:fluffychat/views/settings_3pid.dart';
import 'package:fluffychat/controllers/device_settings_controller.dart';
import 'package:fluffychat/views/settings_emotes.dart';
import 'package:fluffychat/views/settings_ignore_list.dart';
import 'package:fluffychat/views/settings_multiple_emotes.dart';
import 'package:fluffychat/views/settings_notifications.dart';
import 'package:fluffychat/views/settings_style.dart';
import 'package:fluffychat/views/ui/login_ui.dart';
import 'package:fluffychat/views/new_group.dart';
import 'package:fluffychat/views/new_private_chat.dart';
import 'package:fluffychat/views/ui/search_ui.dart';
import 'package:fluffychat/views/ui/settings_ui.dart';
import 'package:fluffychat/views/ui/settings_3pid_ui.dart';
import 'package:fluffychat/views/device_settings.dart';
import 'package:fluffychat/views/ui/settings_emotes_ui.dart';
import 'package:fluffychat/views/ui/settings_ignore_list_ui.dart';
import 'package:fluffychat/views/ui/settings_multiple_emotes_ui.dart';
import 'package:fluffychat/views/ui/settings_notifications_ui.dart';
import 'package:fluffychat/views/ui/settings_style_ui.dart';
import 'package:flutter/material.dart';
class FluffyRoutes {

View file

@ -1,223 +0,0 @@
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:fluffychat/views/chat_details.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/utils/matrix_locals.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:image_picker/image_picker.dart';
class ChatDetails extends StatefulWidget {
final String roomId;
const ChatDetails(this.roomId);
@override
ChatDetailsController createState() => ChatDetailsController();
}
class ChatDetailsController extends State<ChatDetails> {
List<User> members;
@override
void initState() {
super.initState();
members ??=
Matrix.of(context).client.getRoomById(widget.roomId).getParticipants();
}
void setDisplaynameAction() async {
final room = Matrix.of(context).client.getRoomById(widget.roomId);
final input = await showTextInputDialog(
context: context,
title: L10n.of(context).changeTheNameOfTheGroup,
okLabel: L10n.of(context).ok,
cancelLabel: L10n.of(context).cancel,
useRootNavigator: false,
textFields: [
DialogTextField(
initialText: room.getLocalizedDisplayname(
MatrixLocals(
L10n.of(context),
),
),
)
],
);
if (input == null) return;
final success = await showFutureLoadingDialog(
context: context,
future: () => room.setName(input.single),
);
if (success.error == null) {
AdaptivePageLayout.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).displaynameHasBeenChanged)));
}
}
void setCanonicalAliasAction(context) async {
final input = await showTextInputDialog(
context: context,
title: L10n.of(context).setInvitationLink,
okLabel: L10n.of(context).ok,
cancelLabel: L10n.of(context).cancel,
useRootNavigator: false,
textFields: [
DialogTextField(
hintText: '#localpart:domain',
initialText: L10n.of(context).alias.toLowerCase(),
)
],
);
if (input == null) return;
final room = Matrix.of(context).client.getRoomById(widget.roomId);
final domain = room.client.userID.domain;
final canonicalAlias = '%23' + input.single + '%3A' + domain;
final aliasEvent = room.getState('m.room.aliases', domain);
final aliases =
aliasEvent != null ? aliasEvent.content['aliases'] ?? [] : [];
if (aliases.indexWhere((s) => s == canonicalAlias) == -1) {
final newAliases = List<String>.from(aliases);
newAliases.add(canonicalAlias);
final response = await showFutureLoadingDialog(
context: context,
future: () => room.client.requestRoomAliasInformation(canonicalAlias),
);
if (response.error != null) {
final success = await showFutureLoadingDialog(
context: context,
future: () => room.client.createRoomAlias(canonicalAlias, room.id),
);
if (success.error != null) return;
}
}
await showFutureLoadingDialog(
context: context,
future: () => room.client.sendState(room.id, 'm.room.canonical_alias', {
'alias': input.single,
}),
);
}
void setTopicAction() async {
final room = Matrix.of(context).client.getRoomById(widget.roomId);
final input = await showTextInputDialog(
context: context,
title: L10n.of(context).setGroupDescription,
okLabel: L10n.of(context).ok,
cancelLabel: L10n.of(context).cancel,
useRootNavigator: false,
textFields: [
DialogTextField(
hintText: L10n.of(context).setGroupDescription,
initialText: room.topic,
minLines: 1,
maxLines: 4,
)
],
);
if (input == null) return;
final success = await showFutureLoadingDialog(
context: context,
future: () => room.setDescription(input.single),
);
if (success.error == null) {
AdaptivePageLayout.of(context).showSnackBar(SnackBar(
content: Text(L10n.of(context).groupDescriptionHasBeenChanged)));
}
}
void setGuestAccessAction(GuestAccess guestAccess) => showFutureLoadingDialog(
context: context,
future: () => Matrix.of(context)
.client
.getRoomById(widget.roomId)
.setGuestAccess(guestAccess),
);
void setHistoryVisibilityAction(HistoryVisibility historyVisibility) =>
showFutureLoadingDialog(
context: context,
future: () => Matrix.of(context)
.client
.getRoomById(widget.roomId)
.setHistoryVisibility(historyVisibility),
);
void setJoinRulesAction(JoinRules joinRule) => showFutureLoadingDialog(
context: context,
future: () => Matrix.of(context)
.client
.getRoomById(widget.roomId)
.setJoinRules(joinRule),
);
void goToEmoteSettings() async {
final room = Matrix.of(context).client.getRoomById(widget.roomId);
// 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});
}
}
void setAvatarAction() async {
MatrixFile file;
if (PlatformInfos.isMobile) {
final result = await ImagePicker().getImage(
source: ImageSource.gallery,
imageQuality: 50,
maxWidth: 1600,
maxHeight: 1600);
if (result == null) return;
file = MatrixFile(
bytes: await result.readAsBytes(),
name: result.path,
);
} else {
final result = await FilePickerCross.importFromStorage(
type: FileTypeCross.image,
);
if (result == null) return;
file = MatrixFile(
bytes: result.toUint8List(),
name: result.fileName,
);
}
final room = Matrix.of(context).client.getRoomById(widget.roomId);
final success = await showFutureLoadingDialog(
context: context,
future: () => room.setAvatar(file),
);
if (success.error == null) {
AdaptivePageLayout.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).avatarHasBeenChanged)));
}
}
void requestMoreMembersAction() async {
final room = Matrix.of(context).client.getRoomById(widget.roomId);
final participants = await showFutureLoadingDialog(
context: context, future: () => room.requestParticipants());
if (participants.error == null) {
setState(() => members = participants.result);
}
}
@override
Widget build(BuildContext context) => ChatDetailsView(this);
}

View file

@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'matrix_file_extension.dart';
import '../controllers/image_viewer_controller.dart';
import '../views/image_viewer.dart';
extension LocalizedBody on Event {
void openFile(BuildContext context, {bool downloadOnly = false}) async {

View file

@ -1,5 +1,5 @@
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/archive_view.dart';
import 'package:fluffychat/views/ui/archive_ui.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:flutter/material.dart';
@ -19,5 +19,5 @@ class ArchiveController extends State<Archive> {
void forgetAction(int i) => setState(() => archive.removeAt(i));
@override
Widget build(BuildContext context) => ArchiveView(this);
Widget build(BuildContext context) => ArchiveUI(this);
}

File diff suppressed because it is too large Load diff

View file

@ -1,369 +1,223 @@
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/controllers/chat_details_controller.dart';
import 'package:fluffychat/views/widgets/avatar.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/widgets/chat_settings_popup_menu.dart';
import 'package:fluffychat/views/widgets/content_banner.dart';
import 'package:fluffychat/views/widgets/max_width_body.dart';
import 'package:fluffychat/views/widgets/list_items/participant_list_item.dart';
import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:fluffychat/views/ui/chat_details_ui.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/utils/matrix_locals.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix_link_text/link_text.dart';
import 'package:image_picker/image_picker.dart';
import '../utils/url_launcher.dart';
class ChatDetails extends StatefulWidget {
final String roomId;
class ChatDetailsView extends StatelessWidget {
final ChatDetailsController controller;
const ChatDetailsView(this.controller, {Key key}) : super(key: key);
const ChatDetails(this.roomId);
@override
Widget build(BuildContext context) {
final room =
Matrix.of(context).client.getRoomById(controller.widget.roomId);
if (room == null) {
return Scaffold(
appBar: AppBar(
leading: BackButton(),
title: Text(L10n.of(context).oopsSomethingWentWrong),
),
body: Center(
child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat),
),
ChatDetailsController createState() => ChatDetailsController();
}
class ChatDetailsController extends State<ChatDetails> {
List<User> members;
@override
void initState() {
super.initState();
members ??=
Matrix.of(context).client.getRoomById(widget.roomId).getParticipants();
}
void setDisplaynameAction() async {
final room = Matrix.of(context).client.getRoomById(widget.roomId);
final input = await showTextInputDialog(
context: context,
title: L10n.of(context).changeTheNameOfTheGroup,
okLabel: L10n.of(context).ok,
cancelLabel: L10n.of(context).cancel,
useRootNavigator: false,
textFields: [
DialogTextField(
initialText: room.getLocalizedDisplayname(
MatrixLocals(
L10n.of(context),
),
),
)
],
);
if (input == null) return;
final success = await showFutureLoadingDialog(
context: context,
future: () => room.setName(input.single),
);
if (success.error == null) {
AdaptivePageLayout.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).displaynameHasBeenChanged)));
}
}
void setCanonicalAliasAction(context) async {
final input = await showTextInputDialog(
context: context,
title: L10n.of(context).setInvitationLink,
okLabel: L10n.of(context).ok,
cancelLabel: L10n.of(context).cancel,
useRootNavigator: false,
textFields: [
DialogTextField(
hintText: '#localpart:domain',
initialText: L10n.of(context).alias.toLowerCase(),
)
],
);
if (input == null) return;
final room = Matrix.of(context).client.getRoomById(widget.roomId);
final domain = room.client.userID.domain;
final canonicalAlias = '%23' + input.single + '%3A' + domain;
final aliasEvent = room.getState('m.room.aliases', domain);
final aliases =
aliasEvent != null ? aliasEvent.content['aliases'] ?? [] : [];
if (aliases.indexWhere((s) => s == canonicalAlias) == -1) {
final newAliases = List<String>.from(aliases);
newAliases.add(canonicalAlias);
final response = await showFutureLoadingDialog(
context: context,
future: () => room.client.requestRoomAliasInformation(canonicalAlias),
);
if (response.error != null) {
final success = await showFutureLoadingDialog(
context: context,
future: () => room.client.createRoomAlias(canonicalAlias, room.id),
);
if (success.error != null) return;
}
}
await showFutureLoadingDialog(
context: context,
future: () => room.client.sendState(room.id, 'm.room.canonical_alias', {
'alias': input.single,
}),
);
}
void setTopicAction() async {
final room = Matrix.of(context).client.getRoomById(widget.roomId);
final input = await showTextInputDialog(
context: context,
title: L10n.of(context).setGroupDescription,
okLabel: L10n.of(context).ok,
cancelLabel: L10n.of(context).cancel,
useRootNavigator: false,
textFields: [
DialogTextField(
hintText: L10n.of(context).setGroupDescription,
initialText: room.topic,
minLines: 1,
maxLines: 4,
)
],
);
if (input == null) return;
final success = await showFutureLoadingDialog(
context: context,
future: () => room.setDescription(input.single),
);
if (success.error == null) {
AdaptivePageLayout.of(context).showSnackBar(SnackBar(
content: Text(L10n.of(context).groupDescriptionHasBeenChanged)));
}
}
void setGuestAccessAction(GuestAccess guestAccess) => showFutureLoadingDialog(
context: context,
future: () => Matrix.of(context)
.client
.getRoomById(widget.roomId)
.setGuestAccess(guestAccess),
);
void setHistoryVisibilityAction(HistoryVisibility historyVisibility) =>
showFutureLoadingDialog(
context: context,
future: () => Matrix.of(context)
.client
.getRoomById(widget.roomId)
.setHistoryVisibility(historyVisibility),
);
void setJoinRulesAction(JoinRules joinRule) => showFutureLoadingDialog(
context: context,
future: () => Matrix.of(context)
.client
.getRoomById(widget.roomId)
.setJoinRules(joinRule),
);
void goToEmoteSettings() async {
final room = Matrix.of(context).client.getRoomById(widget.roomId);
// 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});
}
}
void setAvatarAction() async {
MatrixFile file;
if (PlatformInfos.isMobile) {
final result = await ImagePicker().getImage(
source: ImageSource.gallery,
imageQuality: 50,
maxWidth: 1600,
maxHeight: 1600);
if (result == null) return;
file = MatrixFile(
bytes: await result.readAsBytes(),
name: result.path,
);
} else {
final result = await FilePickerCross.importFromStorage(
type: FileTypeCross.image,
);
if (result == null) return;
file = MatrixFile(
bytes: result.toUint8List(),
name: result.fileName,
);
}
final room = Matrix.of(context).client.getRoomById(widget.roomId);
controller.members.removeWhere((u) => u.membership == Membership.leave);
final actualMembersCount =
room.mInvitedMemberCount + room.mJoinedMemberCount;
final canRequestMoreMembers =
controller.members.length < actualMembersCount;
return StreamBuilder(
stream: room.onUpdate.stream,
builder: (context, snapshot) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) => <Widget>[
SliverAppBar(
elevation: Theme.of(context).appBarTheme.elevation,
leading: BackButton(),
expandedHeight: 300.0,
floating: true,
pinned: true,
actions: <Widget>[
if (room.canonicalAlias?.isNotEmpty ?? false)
IconButton(
tooltip: L10n.of(context).share,
icon: Icon(Icons.share_outlined),
onPressed: () => FluffyShare.share(
AppConfig.inviteLinkPrefix + room.canonicalAlias,
context),
),
ChatSettingsPopupMenu(room, false)
],
title: Text(
room.getLocalizedDisplayname(
MatrixLocals(L10n.of(context))),
style: TextStyle(
color: Theme.of(context)
.appBarTheme
.textTheme
.headline6
.color)),
backgroundColor: Theme.of(context).appBarTheme.color,
flexibleSpace: FlexibleSpaceBar(
background: ContentBanner(room.avatar,
onEdit: room.canSendEvent('m.room.avatar')
? controller.setAvatarAction
: null),
),
),
],
body: MaxWidthBody(
child: ListView.builder(
itemCount: controller.members.length +
1 +
(canRequestMoreMembers ? 1 : 0),
itemBuilder: (BuildContext context, int i) => i == 0
? Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ListTile(
leading: room.canSendEvent('m.room.topic')
? CircleAvatar(
backgroundColor: Theme.of(context)
.scaffoldBackgroundColor,
foregroundColor: Colors.grey,
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')
? controller.setTopicAction
: 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(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.people_outlined),
),
title: Text(
L10n.of(context).changeTheNameOfTheGroup),
subtitle: Text(room.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)))),
onTap: controller.setDisplaynameAction,
),
if (room.canSendEvent('m.room.canonical_alias') &&
room.joinRules == JoinRules.public)
ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.link_outlined),
),
onTap: () =>
controller.setCanonicalAliasAction(context),
title: Text(L10n.of(context).setInvitationLink),
subtitle: Text(
(room.canonicalAlias?.isNotEmpty ?? false)
? room.canonicalAlias
: L10n.of(context).none),
),
ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.insert_emoticon_outlined),
),
title: Text(L10n.of(context).emoteSettings),
subtitle: Text(L10n.of(context).setCustomEmotes),
onTap: controller.goToEmoteSettings,
),
PopupMenuButton(
onSelected: controller.setJoinRulesAction,
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<JoinRules>>[
if (room.canChangeJoinRules)
PopupMenuItem<JoinRules>(
value: JoinRules.public,
child: Text(JoinRules.public
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
if (room.canChangeJoinRules)
PopupMenuItem<JoinRules>(
value: JoinRules.invite,
child: Text(JoinRules.invite
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
],
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: controller.setHistoryVisibilityAction,
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(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.visibility_outlined),
),
title: Text(L10n.of(context)
.visibilityOfTheChatHistory),
subtitle: Text(
room.historyVisibility.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
),
if (room.joinRules == JoinRules.public)
PopupMenuButton(
onSelected: controller.setGuestAccessAction,
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<GuestAccess>>[
if (room.canChangeGuestAccess)
PopupMenuItem<GuestAccess>(
value: GuestAccess.can_join,
child: Text(
GuestAccess.can_join.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
if (room.canChangeGuestAccess)
PopupMenuItem<GuestAccess>(
value: GuestAccess.forbidden,
child: Text(
GuestAccess.forbidden
.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
],
child: ListTile(
leading: CircleAvatar(
backgroundColor: Theme.of(context)
.scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.info_outline),
),
title: Text(
L10n.of(context).areGuestsAllowedToJoin),
subtitle: Text(
room.guestAccess.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
),
ListTile(
title: Text(L10n.of(context).editChatPermissions),
subtitle: Text(
L10n.of(context).whoCanPerformWhichAction),
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.edit_attributes_outlined),
),
onTap: () => AdaptivePageLayout.of(context)
.pushNamed('/rooms/${room.id}/permissions'),
),
Divider(thickness: 1),
ListTile(
title: Text(
actualMembersCount > 1
? L10n.of(context).countParticipants(
actualMembersCount.toString())
: L10n.of(context).emptyChat,
style: TextStyle(
color: Theme.of(context).accentColor,
fontWeight: FontWeight.bold,
),
),
),
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 < controller.members.length + 1
? ParticipantListItem(controller.members[i - 1])
: ListTile(
title: Text(L10n.of(context)
.loadCountMoreParticipants(
(actualMembersCount -
controller.members.length)
.toString())),
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
child: Icon(
Icons.refresh,
color: Colors.grey,
),
),
onTap: controller.requestMoreMembersAction,
),
),
),
),
);
});
final success = await showFutureLoadingDialog(
context: context,
future: () => room.setAvatar(file),
);
if (success.error == null) {
AdaptivePageLayout.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).avatarHasBeenChanged)));
}
}
void requestMoreMembersAction() async {
final room = Matrix.of(context).client.getRoomById(widget.roomId);
final participants = await showFutureLoadingDialog(
context: context, future: () => room.requestParticipants());
if (participants.error == null) {
setState(() => members = participants.result);
}
}
@override
Widget build(BuildContext context) => ChatDetailsUI(this);
}

View file

@ -1,9 +1,9 @@
import 'package:famedlysdk/encryption.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/chat_encryption_settings_view.dart';
import 'package:fluffychat/views/ui/chat_encryption_settings_ui.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:flutter/material.dart';
import '../views/widgets/dialogs/key_verification_dialog.dart';
import 'widgets/dialogs/key_verification_dialog.dart';
class ChatEncryptionSettings extends StatefulWidget {
final String id;
@ -61,5 +61,5 @@ class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
}
@override
Widget build(BuildContext context) => ChatEncryptionSettingsView(this);
Widget build(BuildContext context) => ChatEncryptionSettingsUI(this);
}

View file

@ -5,7 +5,7 @@ import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/views/chat_list_view.dart';
import 'package:fluffychat/views/ui/chat_list_ui.dart';
import 'package:flutter/cupertino.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/utils/platform_infos.dart';
@ -13,7 +13,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import '../views/widgets/matrix.dart';
import 'widgets/matrix.dart';
import '../utils/matrix_file_extension.dart';
import '../utils/url_launcher.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -224,7 +224,7 @@ class ChatListController extends State<ChatList> {
}
@override
Widget build(BuildContext context) => ChatListView(this);
Widget build(BuildContext context) => ChatListUI(this);
}
enum ChatListPopupMenuItemActions {

View file

@ -2,7 +2,7 @@ import 'dart:developer';
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:fluffychat/views/chat_permissions_settings_view.dart';
import 'package:fluffychat/views/ui/chat_permissions_settings_ui.dart';
import 'package:fluffychat/views/widgets/dialogs/permission_slider_dialog.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
@ -92,5 +92,5 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
}
@override
Widget build(BuildContext context) => ChatPermissionsSettingsView(this);
Widget build(BuildContext context) => ChatPermissionsSettingsUI(this);
}

View file

@ -1,13 +1,13 @@
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:famedlysdk/encryption/utils/key_verification.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/device_settings_view.dart';
import 'package:fluffychat/views/ui/device_settings_ui.dart';
import 'package:fluffychat/views/widgets/dialogs/key_verification_dialog.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../views/widgets/matrix.dart';
import 'widgets/matrix.dart';
class DevicesSettings extends StatefulWidget {
@override
@ -136,5 +136,5 @@ class DevicesSettingsController extends State<DevicesSettings> {
..sort((a, b) => b.lastSeenTs.compareTo(a.lastSeenTs));
@override
Widget build(BuildContext context) => DevicesSettingsView(this);
Widget build(BuildContext context) => DevicesSettingsUI(this);
}

View file

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/homeserver_picker_view.dart';
import 'package:fluffychat/views/ui/homeserver_picker_ui.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
@ -130,5 +130,5 @@ class HomeserverPickerController extends State<HomeserverPicker> {
}
@override
Widget build(BuildContext context) => HomeserverPickerView(this);
Widget build(BuildContext context) => HomeserverPickerUI(this);
}

View file

@ -1,7 +1,7 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/views/image_viewer_view.dart';
import 'package:fluffychat/views/ui/image_viewer_ui.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:flutter/material.dart';
@ -38,5 +38,5 @@ class ImageViewerController extends State<ImageViewer> {
}
@override
Widget build(BuildContext context) => ImageViewerView(this);
Widget build(BuildContext context) => ImageViewerUI(this);
}

View file

@ -3,7 +3,7 @@ import 'dart:async';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/invitation_selection_view.dart';
import 'package:fluffychat/views/ui/invitation_selection_ui.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:flutter/material.dart';
@ -116,5 +116,5 @@ class InvitationSelectionController extends State<InvitationSelection> {
}
@override
Widget build(BuildContext context) => InvitationSelectionView(this);
Widget build(BuildContext context) => InvitationSelectionUI(this);
}

View file

@ -1,6 +1,6 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart' as sdk;
import 'package:fluffychat/views/new_group_view.dart';
import 'package:fluffychat/views/ui/new_group_ui.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:flutter/material.dart';
@ -40,5 +40,5 @@ class NewGroupController extends State<NewGroup> {
}
@override
Widget build(BuildContext context) => NewGroupView(this);
Widget build(BuildContext context) => NewGroupUI(this);
}

View file

@ -3,7 +3,7 @@ import 'dart:async';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:fluffychat/views/new_private_chat_view.dart';
import 'package:fluffychat/views/ui/new_private_chat_ui.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:flutter/material.dart';
@ -112,5 +112,5 @@ class NewPrivateChatController extends State<NewPrivateChat> {
);
@override
Widget build(BuildContext context) => NewPrivateChatView(this);
Widget build(BuildContext context) => NewPrivateChatUI(this);
}

View file

@ -1,7 +1,7 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:file_picker_cross/file_picker_cross.dart';
import 'package:fluffychat/views/sign_up_view.dart';
import 'package:fluffychat/views/ui/sign_up_ui.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:flutter/cupertino.dart';
@ -67,5 +67,5 @@ class SignUpController extends State<SignUp> {
}
@override
Widget build(BuildContext context) => SignUpView(this);
Widget build(BuildContext context) => SignUpUI(this);
}

View file

@ -2,7 +2,7 @@ import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/sign_up_password_view.dart';
import 'package:fluffychat/views/ui/sign_up_password_ui.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:flutter/material.dart';
@ -127,5 +127,5 @@ class SignUpPasswordController extends State<SignUpPassword> {
}
@override
Widget build(BuildContext context) => SignUpPasswordView(this);
Widget build(BuildContext context) => SignUpPasswordUI(this);
}

View file

@ -1,13 +1,13 @@
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/controllers/archive_controller.dart';
import 'package:fluffychat/views/archive.dart';
import 'package:fluffychat/views/widgets/list_items/chat_list_item.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class ArchiveView extends StatelessWidget {
class ArchiveUI extends StatelessWidget {
final ArchiveController controller;
const ArchiveView(this.controller, {Key key}) : super(key: key);
const ArchiveUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -0,0 +1,369 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/views/chat_details.dart';
import 'package:fluffychat/views/widgets/avatar.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:fluffychat/utils/fluffy_share.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/widgets/chat_settings_popup_menu.dart';
import 'package:fluffychat/views/widgets/content_banner.dart';
import 'package:fluffychat/views/widgets/max_width_body.dart';
import 'package:fluffychat/views/widgets/list_items/participant_list_item.dart';
import 'package:fluffychat/utils/matrix_locals.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix_link_text/link_text.dart';
import '../../utils/url_launcher.dart';
class ChatDetailsUI extends StatelessWidget {
final ChatDetailsController controller;
const ChatDetailsUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
final room =
Matrix.of(context).client.getRoomById(controller.widget.roomId);
if (room == null) {
return Scaffold(
appBar: AppBar(
leading: BackButton(),
title: Text(L10n.of(context).oopsSomethingWentWrong),
),
body: Center(
child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat),
),
);
}
controller.members.removeWhere((u) => u.membership == Membership.leave);
final actualMembersCount =
room.mInvitedMemberCount + room.mJoinedMemberCount;
final canRequestMoreMembers =
controller.members.length < actualMembersCount;
return StreamBuilder(
stream: room.onUpdate.stream,
builder: (context, snapshot) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) => <Widget>[
SliverAppBar(
elevation: Theme.of(context).appBarTheme.elevation,
leading: BackButton(),
expandedHeight: 300.0,
floating: true,
pinned: true,
actions: <Widget>[
if (room.canonicalAlias?.isNotEmpty ?? false)
IconButton(
tooltip: L10n.of(context).share,
icon: Icon(Icons.share_outlined),
onPressed: () => FluffyShare.share(
AppConfig.inviteLinkPrefix + room.canonicalAlias,
context),
),
ChatSettingsPopupMenu(room, false)
],
title: Text(
room.getLocalizedDisplayname(
MatrixLocals(L10n.of(context))),
style: TextStyle(
color: Theme.of(context)
.appBarTheme
.textTheme
.headline6
.color)),
backgroundColor: Theme.of(context).appBarTheme.color,
flexibleSpace: FlexibleSpaceBar(
background: ContentBanner(room.avatar,
onEdit: room.canSendEvent('m.room.avatar')
? controller.setAvatarAction
: null),
),
),
],
body: MaxWidthBody(
child: ListView.builder(
itemCount: controller.members.length +
1 +
(canRequestMoreMembers ? 1 : 0),
itemBuilder: (BuildContext context, int i) => i == 0
? Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ListTile(
leading: room.canSendEvent('m.room.topic')
? CircleAvatar(
backgroundColor: Theme.of(context)
.scaffoldBackgroundColor,
foregroundColor: Colors.grey,
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')
? controller.setTopicAction
: 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(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.people_outlined),
),
title: Text(
L10n.of(context).changeTheNameOfTheGroup),
subtitle: Text(room.getLocalizedDisplayname(
MatrixLocals(L10n.of(context)))),
onTap: controller.setDisplaynameAction,
),
if (room.canSendEvent('m.room.canonical_alias') &&
room.joinRules == JoinRules.public)
ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.link_outlined),
),
onTap: () =>
controller.setCanonicalAliasAction(context),
title: Text(L10n.of(context).setInvitationLink),
subtitle: Text(
(room.canonicalAlias?.isNotEmpty ?? false)
? room.canonicalAlias
: L10n.of(context).none),
),
ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.insert_emoticon_outlined),
),
title: Text(L10n.of(context).emoteSettings),
subtitle: Text(L10n.of(context).setCustomEmotes),
onTap: controller.goToEmoteSettings,
),
PopupMenuButton(
onSelected: controller.setJoinRulesAction,
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<JoinRules>>[
if (room.canChangeJoinRules)
PopupMenuItem<JoinRules>(
value: JoinRules.public,
child: Text(JoinRules.public
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
if (room.canChangeJoinRules)
PopupMenuItem<JoinRules>(
value: JoinRules.invite,
child: Text(JoinRules.invite
.getLocalizedString(
MatrixLocals(L10n.of(context)))),
),
],
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: controller.setHistoryVisibilityAction,
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(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.visibility_outlined),
),
title: Text(L10n.of(context)
.visibilityOfTheChatHistory),
subtitle: Text(
room.historyVisibility.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
),
if (room.joinRules == JoinRules.public)
PopupMenuButton(
onSelected: controller.setGuestAccessAction,
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<GuestAccess>>[
if (room.canChangeGuestAccess)
PopupMenuItem<GuestAccess>(
value: GuestAccess.can_join,
child: Text(
GuestAccess.can_join.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
if (room.canChangeGuestAccess)
PopupMenuItem<GuestAccess>(
value: GuestAccess.forbidden,
child: Text(
GuestAccess.forbidden
.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
],
child: ListTile(
leading: CircleAvatar(
backgroundColor: Theme.of(context)
.scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.info_outline),
),
title: Text(
L10n.of(context).areGuestsAllowedToJoin),
subtitle: Text(
room.guestAccess.getLocalizedString(
MatrixLocals(L10n.of(context))),
),
),
),
ListTile(
title: Text(L10n.of(context).editChatPermissions),
subtitle: Text(
L10n.of(context).whoCanPerformWhichAction),
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
foregroundColor: Colors.grey,
child: Icon(Icons.edit_attributes_outlined),
),
onTap: () => AdaptivePageLayout.of(context)
.pushNamed('/rooms/${room.id}/permissions'),
),
Divider(thickness: 1),
ListTile(
title: Text(
actualMembersCount > 1
? L10n.of(context).countParticipants(
actualMembersCount.toString())
: L10n.of(context).emptyChat,
style: TextStyle(
color: Theme.of(context).accentColor,
fontWeight: FontWeight.bold,
),
),
),
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 < controller.members.length + 1
? ParticipantListItem(controller.members[i - 1])
: ListTile(
title: Text(L10n.of(context)
.loadCountMoreParticipants(
(actualMembersCount -
controller.members.length)
.toString())),
leading: CircleAvatar(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
child: Icon(
Icons.refresh,
color: Colors.grey,
),
),
onTap: controller.requestMoreMembersAction,
),
),
),
),
);
});
}
}

View file

@ -1,17 +1,16 @@
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/controllers/chat_encryption_settings_controller.dart';
import 'package:fluffychat/views/chat_encryption_settings.dart';
import 'package:fluffychat/views/widgets/avatar.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:fluffychat/views/widgets/max_width_body.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../utils/device_extension.dart';
import '../../utils/device_extension.dart';
class ChatEncryptionSettingsView extends StatelessWidget {
class ChatEncryptionSettingsUI extends StatelessWidget {
final ChatEncryptionSettingsController controller;
const ChatEncryptionSettingsView(this.controller, {Key key})
: super(key: key);
const ChatEncryptionSettingsUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -1,19 +1,19 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/controllers/chat_list_controller.dart';
import 'package:fluffychat/views/chat_list.dart';
import 'package:fluffychat/views/widgets/connection_status_header.dart';
import 'package:fluffychat/views/widgets/list_items/chat_list_item.dart';
import 'package:flutter/cupertino.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'widgets/matrix.dart';
import '../widgets/matrix.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class ChatListView extends StatelessWidget {
class ChatListUI extends StatelessWidget {
final ChatListController controller;
const ChatListView(this.controller, {Key key}) : super(key: key);
const ChatListUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -1,4 +1,4 @@
import 'package:fluffychat/controllers/chat_permissions_settings_controller.dart';
import 'package:fluffychat/views/chat_permissions_settings.dart';
import 'package:fluffychat/views/widgets/list_items/permission_list_tile.dart';
import 'package:fluffychat/views/widgets/max_width_body.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
@ -7,11 +7,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:famedlysdk/famedlysdk.dart';
class ChatPermissionsSettingsView extends StatelessWidget {
class ChatPermissionsSettingsUI extends StatelessWidget {
final ChatPermissionsSettingsController controller;
const ChatPermissionsSettingsView(this.controller, {Key key})
: super(key: key);
const ChatPermissionsSettingsUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

747
lib/views/ui/chat_ui.dart Normal file
View file

@ -0,0 +1,747 @@
import 'dart:math';
import 'dart:ui';
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/views/chat.dart';
import 'package:fluffychat/views/widgets/avatar.dart';
import 'package:fluffychat/views/widgets/chat_settings_popup_menu.dart';
import 'package:fluffychat/views/widgets/connection_status_header.dart';
import 'package:fluffychat/views/widgets/input_bar.dart';
import 'package:fluffychat/views/widgets/unread_badge_back_button.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:fluffychat/views/widgets/encryption_button.dart';
import 'package:fluffychat/views/widgets/list_items/message.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:fluffychat/views/widgets/reply_content.dart';
import 'package:fluffychat/views/widgets/user_bottom_sheet.dart';
import 'package:fluffychat/config/app_emojis.dart';
import 'package:fluffychat/utils/matrix_locals.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/room_status_extension.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:swipe_to_action/swipe_to_action.dart';
class ChatUI extends StatelessWidget {
final ChatController controller;
const ChatUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
controller.matrix = Matrix.of(context);
final client = controller.matrix.client;
controller.room ??= client.getRoomById(controller.widget.id);
if (controller.room == null) {
return Scaffold(
appBar: AppBar(
title: Text(L10n.of(context).oopsSomethingWentWrong),
),
body: Center(
child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat),
),
);
}
controller.matrix.client.activeRoomId = controller.widget.id;
if (controller.room.membership == Membership.invite) {
showFutureLoadingDialog(
context: context, future: () => controller.room.join());
}
return Scaffold(
appBar: AppBar(
leading: controller.selectMode
? IconButton(
icon: Icon(Icons.close),
onPressed: controller.clearSelectedEvents,
tooltip: L10n.of(context).close,
)
: AdaptivePageLayout.of(context).columnMode(context)
? null
: UnreadBadgeBackButton(roomId: controller.widget.id),
titleSpacing:
AdaptivePageLayout.of(context).columnMode(context) ? null : 0,
title: controller.selectedEvents.isEmpty
? StreamBuilder(
stream: controller.room.onUpdate.stream,
builder: (context, snapshot) => ListTile(
leading: Avatar(
controller.room.avatar, controller.room.displayname),
contentPadding: EdgeInsets.zero,
onTap: controller.room.isDirectChat
? () => showModalBottomSheet(
context: context,
builder: (c) => UserBottomSheet(
user: controller.room.getUserByMXIDSync(
controller.room.directChatMatrixID),
onMention: () => controller
.sendController.text +=
'${controller.room.directChatMatrixID} ',
),
)
: () => (!AdaptivePageLayout.of(context)
.columnMode(context) ||
AdaptivePageLayout.of(context)
.viewDataStack
.length <
3)
? AdaptivePageLayout.of(context).pushNamed(
'/rooms/${controller.room.id}/details')
: null,
title: Text(
controller.room.getLocalizedDisplayname(
MatrixLocals(L10n.of(context))),
maxLines: 1),
subtitle: controller.room
.getLocalizedTypingText(context)
.isEmpty
? StreamBuilder<Object>(
stream: Matrix.of(context)
.client
.onPresence
.stream
.where((p) =>
p.senderId ==
controller.room.directChatMatrixID),
builder: (context, snapshot) => Text(
controller.room.getLocalizedStatus(context),
maxLines: 1,
//overflow: TextOverflow.ellipsis,
))
: Row(
children: <Widget>[
Icon(Icons.edit_outlined,
color: Theme.of(context).accentColor,
size: 13),
SizedBox(width: 4),
Expanded(
child: Text(
controller.room
.getLocalizedTypingText(context),
maxLines: 1,
style: TextStyle(
color: Theme.of(context).accentColor,
fontStyle: FontStyle.italic,
),
),
),
],
),
))
: Text(L10n.of(context)
.numberSelected(controller.selectedEvents.length.toString())),
actions: controller.selectMode
? <Widget>[
if (controller.selectedEvents.length == 1 &&
controller.selectedEvents.first.status > 0 &&
controller.selectedEvents.first.senderId == client.userID)
IconButton(
icon: Icon(Icons.edit_outlined),
tooltip: L10n.of(context).edit,
onPressed: controller.editSelectedEventAction,
),
PopupMenuButton(
onSelected: controller.onEventActionPopupMenuSelected,
itemBuilder: (_) => [
PopupMenuItem(
value: 'copy',
child: Text(L10n.of(context).copy),
),
if (controller.canRedactSelectedEvents)
PopupMenuItem(
value: 'redact',
child: Text(
L10n.of(context).redactMessage,
style: TextStyle(color: Colors.orange),
),
),
if (controller.selectedEvents.length == 1)
PopupMenuItem(
value: 'report',
child: Text(
L10n.of(context).reportMessage,
style: TextStyle(color: Colors.red),
),
),
],
),
]
: <Widget>[
if (controller.room.canSendDefaultStates)
IconButton(
tooltip: L10n.of(context).videoCall,
icon: Icon(Icons.video_call_outlined),
onPressed: controller.startCallAction,
),
ChatSettingsPopupMenu(
controller.room, !controller.room.isDirectChat),
],
),
floatingActionButton: controller.showScrollDownButton
? Padding(
padding: const EdgeInsets.only(bottom: 56.0),
child: FloatingActionButton(
onPressed: controller.scrollDown,
foregroundColor: Theme.of(context).textTheme.bodyText2.color,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
mini: true,
child: Icon(Icons.arrow_downward_outlined,
color: Theme.of(context).primaryColor),
),
)
: null,
body: Stack(
children: <Widget>[
if (Matrix.of(context).wallpaper != null)
Image.file(
Matrix.of(context).wallpaper,
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,
),
SafeArea(
child: Column(
children: <Widget>[
ConnectionStatusHeader(),
if (controller.room.getState(EventTypes.RoomTombstone) != null)
Container(
height: 72,
child: Material(
color: Theme.of(context).secondaryHeaderColor,
child: ListTile(
leading: CircleAvatar(
foregroundColor: Theme.of(context).accentColor,
backgroundColor: Theme.of(context).backgroundColor,
child: Icon(Icons.upgrade_outlined),
),
title: Text(
controller.room
.getState(EventTypes.RoomTombstone)
.parsedTombstoneContent
.body,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitle: Text(L10n.of(context).goToTheNewRoom),
onTap: controller.goToNewRoomAction,
),
),
),
Expanded(
child: FutureBuilder<bool>(
future: controller.getTimeline(),
builder: (BuildContext context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
// create a map of eventId --> index to greatly improve performance of
// ListView's findChildIndexCallback
final thisEventsKeyMap = <String, int>{};
for (var i = 0;
i < controller.filteredEvents.length;
i++) {
thisEventsKeyMap[controller.filteredEvents[i].eventId] =
i;
}
final horizontalPadding = max(
0,
(MediaQuery.of(context).size.width -
FluffyThemes.columnWidth *
(AdaptivePageLayout.of(context)
.currentViewData
.rightView !=
null
? 4.5
: 3.5)) /
2)
.toDouble();
return ListView.custom(
padding: EdgeInsets.only(
top: 16,
left: horizontalPadding,
right: horizontalPadding,
),
reverse: true,
controller: controller.scrollController,
childrenDelegate: SliverChildBuilderDelegate(
(BuildContext context, int i) {
return i == controller.filteredEvents.length + 1
? controller.timeline.isRequestingHistory
? Container(
height: 50,
alignment: Alignment.center,
padding: EdgeInsets.all(8),
child: CircularProgressIndicator(),
)
: controller.canLoadMore
? TextButton(
onPressed:
controller.requestHistory,
child: Text(
L10n.of(context).loadMore,
style: TextStyle(
color: Theme.of(context)
.primaryColor,
fontWeight: FontWeight.bold,
decoration:
TextDecoration.underline,
),
),
)
: Container()
: i == 0
? StreamBuilder(
stream: controller.room.onUpdate.stream,
builder: (_, __) {
final seenByText = controller.room
.getLocalizedSeenByText(
context,
controller.timeline,
controller.filteredEvents,
controller.unfolded,
);
return AnimatedContainer(
height: seenByText.isEmpty ? 0 : 24,
duration: seenByText.isEmpty
? Duration(milliseconds: 0)
: Duration(milliseconds: 300),
alignment: controller.filteredEvents
.first.senderId ==
client.userID
? Alignment.topRight
: Alignment.topLeft,
padding: EdgeInsets.only(
left: 8,
right: 8,
bottom: 8,
),
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 4),
decoration: BoxDecoration(
color: Theme.of(context)
.scaffoldBackgroundColor
.withOpacity(0.8),
borderRadius:
BorderRadius.circular(4),
),
child: Text(
seenByText,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Theme.of(context)
.accentColor,
),
),
),
);
},
)
: AutoScrollTag(
key: ValueKey(controller
.filteredEvents[i - 1].eventId),
index: i - 1,
controller: controller.scrollController,
child: Swipeable(
key: ValueKey(controller
.filteredEvents[i - 1].eventId),
background: Padding(
padding: EdgeInsets.symmetric(
horizontal: 12.0),
child: Center(
child: Icon(Icons.reply_outlined),
),
),
direction: SwipeDirection.endToStart,
onSwipe: (direction) =>
controller.replyAction(
replyTo: controller
.filteredEvents[i - 1]),
child: Message(
controller.filteredEvents[i - 1],
onAvatarTab: (Event event) =>
showModalBottomSheet(
context: context,
builder: (c) =>
UserBottomSheet(
user: event.sender,
onMention: () => controller
.sendController
.text +=
'${event.senderId} ',
),
),
unfold: controller.unfold,
onSelect:
controller.onSelectMessage,
scrollToEventId:
(String eventId) => controller
.scrollToEventId(eventId),
longPressSelect: controller
.selectedEvents.isEmpty,
selected: controller
.selectedEvents
.contains(controller
.filteredEvents[i - 1]),
timeline: controller.timeline,
nextEvent: i >= 2
? controller
.filteredEvents[i - 2]
: null),
),
);
},
childCount: controller.filteredEvents.length + 2,
findChildIndexCallback: (key) => controller
.findChildIndexCallback(key, thisEventsKeyMap),
),
);
},
),
),
AnimatedContainer(
duration: Duration(milliseconds: 300),
height: (controller.editEvent == null &&
controller.replyEvent == null &&
controller.room.canSendDefaultMessages &&
controller.selectedEvents.length == 1)
? 56
: 0,
child: Material(
color: Theme.of(context).secondaryHeaderColor,
child: Builder(builder: (context) {
if (!(controller.editEvent == null &&
controller.replyEvent == null &&
controller.selectedEvents.length == 1)) {
return Container();
}
final emojis = List<String>.from(AppEmojis.emojis);
final allReactionEvents = controller.selectedEvents.first
.aggregatedEvents(
controller.timeline, RelationshipTypes.Reaction)
?.where((event) =>
event.senderId == event.room.client.userID &&
event.type == 'm.reaction');
allReactionEvents.forEach((event) {
try {
emojis.remove(event.content['m.relates_to']['key']);
} catch (_) {}
});
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: emojis.length + 1,
itemBuilder: (c, i) => i == emojis.length
? InkWell(
borderRadius: BorderRadius.circular(8),
onTap: () => controller
.pickEmojiAction(allReactionEvents),
child: Container(
width: 56,
height: 56,
alignment: Alignment.center,
child: Icon(Icons.add_outlined),
),
)
: InkWell(
borderRadius: BorderRadius.circular(8),
onTap: () =>
controller.sendEmojiAction(emojis[i]),
child: Container(
width: 56,
height: 56,
alignment: Alignment.center,
child: Text(
emojis[i],
style: TextStyle(fontSize: 30),
),
),
),
);
}),
),
),
AnimatedContainer(
duration: Duration(milliseconds: 300),
height: controller.editEvent != null ||
controller.replyEvent != null
? 56
: 0,
child: Material(
color: Theme.of(context).secondaryHeaderColor,
child: Row(
children: <Widget>[
IconButton(
tooltip: L10n.of(context).close,
icon: Icon(Icons.close),
onPressed: controller.cancelReplyEventAction,
),
Expanded(
child: controller.replyEvent != null
? ReplyContent(controller.replyEvent,
timeline: controller.timeline)
: _EditContent(controller.editEvent
?.getDisplayEvent(controller.timeline)),
),
],
),
),
),
Divider(
height: 1,
thickness: 1,
),
controller.room.canSendDefaultMessages &&
controller.room.membership == Membership.join
? Container(
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: controller.selectMode
? <Widget>[
Container(
height: 56,
child: TextButton(
onPressed: controller.forwardEventsAction,
child: Row(
children: <Widget>[
Icon(Icons
.keyboard_arrow_left_outlined),
Text(L10n.of(context).forward),
],
),
),
),
controller.selectedEvents.length == 1
? controller.selectedEvents.first
.getDisplayEvent(
controller.timeline)
.status >
0
? Container(
height: 56,
child: TextButton(
onPressed:
controller.replyAction,
child: Row(
children: <Widget>[
Text(
L10n.of(context).reply),
Icon(Icons
.keyboard_arrow_right),
],
),
),
)
: Container(
height: 56,
child: TextButton(
onPressed:
controller.sendAgainAction,
child: Row(
children: <Widget>[
Text(L10n.of(context)
.tryToSendAgain),
SizedBox(width: 4),
Icon(Icons.send_outlined,
size: 16),
],
),
),
)
: Container(),
]
: <Widget>[
if (controller.inputText.isEmpty)
Container(
height: 56,
alignment: Alignment.center,
child: PopupMenuButton<String>(
icon: Icon(Icons.add_outlined),
onSelected: controller
.onAddPopupMenuButtonSelected,
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: 'file',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
child: Icon(
Icons.attachment_outlined),
),
title: Text(
L10n.of(context).sendFile),
contentPadding: EdgeInsets.all(0),
),
),
PopupMenuItem<String>(
value: 'image',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
child:
Icon(Icons.image_outlined),
),
title: Text(
L10n.of(context).sendImage),
contentPadding: EdgeInsets.all(0),
),
),
if (PlatformInfos.isMobile)
PopupMenuItem<String>(
value: 'camera',
child: ListTile(
leading: CircleAvatar(
backgroundColor:
Colors.purple,
foregroundColor: Colors.white,
child: Icon(Icons
.camera_alt_outlined),
),
title: Text(L10n.of(context)
.openCamera),
contentPadding:
EdgeInsets.all(0),
),
),
if (PlatformInfos.isMobile)
PopupMenuItem<String>(
value: 'voice',
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
child: Icon(
Icons.mic_none_outlined),
),
title: Text(L10n.of(context)
.voiceMessage),
contentPadding:
EdgeInsets.all(0),
),
),
],
),
),
Container(
height: 56,
alignment: Alignment.center,
child: EncryptionButton(controller.room),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4.0),
child: InputBar(
room: controller.room,
minLines: 1,
maxLines: kIsWeb ? 1 : 8,
autofocus: !PlatformInfos.isMobile,
keyboardType: !PlatformInfos.isMobile
? TextInputType.text
: TextInputType.multiline,
onSubmitted:
controller.onInputBarSubmitted,
focusNode: controller.inputFocus,
controller: controller.sendController,
decoration: InputDecoration(
hintText:
L10n.of(context).writeAMessage,
hintMaxLines: 1,
border: InputBorder.none,
enabledBorder: InputBorder.none,
filled: false,
),
onChanged: controller.onInputBarChanged,
),
),
),
if (PlatformInfos.isMobile &&
controller.inputText.isEmpty)
Container(
height: 56,
alignment: Alignment.center,
child: IconButton(
tooltip: L10n.of(context).voiceMessage,
icon: Icon(Icons.mic_none_outlined),
onPressed:
controller.voiceMessageAction,
),
),
if (!PlatformInfos.isMobile ||
controller.inputText.isNotEmpty)
Container(
height: 56,
alignment: Alignment.center,
child: IconButton(
icon: Icon(Icons.send_outlined),
onPressed: controller.send,
tooltip: L10n.of(context).send,
),
),
],
),
)
: Container(),
],
),
),
],
),
);
}
}
class _EditContent extends StatelessWidget {
final Event event;
_EditContent(this.event);
@override
Widget build(BuildContext context) {
if (event == null) {
return Container();
}
return Row(
children: <Widget>[
Icon(
Icons.edit,
color: Theme.of(context).primaryColor,
),
Container(width: 15.0),
Text(
event?.getLocalizedBody(
MatrixLocals(L10n.of(context)),
withSenderNamePrefix: false,
hideReply: true,
) ??
'',
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
color: Theme.of(context).textTheme.bodyText2.color,
),
),
],
);
}
}

View file

@ -1,14 +1,14 @@
import 'package:fluffychat/controllers/device_settings_controller.dart';
import 'package:fluffychat/views/device_settings.dart';
import 'package:fluffychat/views/widgets/max_width_body.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'widgets/list_items/user_device_list_item.dart';
import '../widgets/list_items/user_device_list_item.dart';
class DevicesSettingsView extends StatelessWidget {
class DevicesSettingsUI extends StatelessWidget {
final DevicesSettingsController controller;
const DevicesSettingsView(this.controller, {Key key}) : super(key: key);
const DevicesSettingsUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -1,4 +1,4 @@
import '../controllers/homeserver_picker_controller.dart';
import '../homeserver_picker.dart';
import 'package:fluffychat/views/widgets/default_app_bar_search_field.dart';
import 'package:fluffychat/views/widgets/fluffy_banner.dart';
import 'package:fluffychat/config/app_config.dart';
@ -10,13 +10,10 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class HomeserverPickerView extends StatelessWidget {
class HomeserverPickerUI extends StatelessWidget {
final HomeserverPickerController controller;
const HomeserverPickerView(
this.controller, {
Key key,
}) : super(key: key);
const HomeserverPickerUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -1,12 +1,12 @@
import '../controllers/image_viewer_controller.dart';
import '../image_viewer.dart';
import 'package:fluffychat/views/widgets/image_bubble.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class ImageViewerView extends StatelessWidget {
class ImageViewerUI extends StatelessWidget {
final ImageViewerController controller;
const ImageViewerView(this.controller, {Key key}) : super(key: key);
const ImageViewerUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -1,4 +1,4 @@
import 'package:fluffychat/controllers/invitation_selection_controller.dart';
import 'package:fluffychat/views/invitation_selection.dart';
import 'package:fluffychat/views/widgets/default_app_bar_search_field.dart';
import 'package:famedlysdk/famedlysdk.dart';
@ -8,13 +8,10 @@ import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class InvitationSelectionView extends StatelessWidget {
class InvitationSelectionUI extends StatelessWidget {
final InvitationSelectionController controller;
const InvitationSelectionView(
this.controller, {
Key key,
}) : super(key: key);
const InvitationSelectionUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -9,7 +9,7 @@ import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../utils/platform_infos.dart';
import '../../utils/platform_infos.dart';
import 'package:email_validator/email_validator.dart';
class Login extends StatefulWidget {

View file

@ -1,15 +1,12 @@
import 'package:fluffychat/controllers/new_group_controller.dart';
import 'package:fluffychat/views/new_group.dart';
import 'package:fluffychat/views/widgets/max_width_body.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class NewGroupView extends StatelessWidget {
class NewGroupUI extends StatelessWidget {
final NewGroupController controller;
const NewGroupView(
this.controller, {
Key key,
}) : super(key: key);
const NewGroupUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -1,5 +1,5 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:fluffychat/controllers/new_private_chat_controller.dart';
import 'package:fluffychat/views/new_private_chat.dart';
import 'package:fluffychat/views/widgets/avatar.dart';
import 'package:fluffychat/views/widgets/contacts_list.dart';
import 'package:fluffychat/views/widgets/max_width_body.dart';
@ -8,10 +8,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:famedlysdk/famedlysdk.dart';
class NewPrivateChatView extends StatelessWidget {
class NewPrivateChatUI extends StatelessWidget {
final NewPrivateChatController controller;
const NewPrivateChatView(this.controller, {Key key}) : super(key: key);
const NewPrivateChatUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -11,7 +11,7 @@ import 'package:fluffychat/views/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import '../utils/localized_exception_extension.dart';
import '../../utils/localized_exception_extension.dart';
class SearchView extends StatefulWidget {
final String alias;

View file

@ -13,7 +13,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:image_picker/image_picker.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import '../views/widgets/matrix.dart';
import '../widgets/matrix.dart';
class EmotesSettings extends StatefulWidget {
final Room room;

View file

@ -5,7 +5,7 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../views/widgets/matrix.dart';
import '../widgets/matrix.dart';
class SettingsIgnoreList extends StatefulWidget {
final String initialUserId;

View file

@ -9,9 +9,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:open_noti_settings/open_noti_settings.dart';
import '../utils/localized_exception_extension.dart';
import '../../utils/localized_exception_extension.dart';
import '../views/widgets/matrix.dart';
import '../widgets/matrix.dart';
class NotificationSettingsItem {
final PushRuleKind type;

View file

@ -7,8 +7,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:image_picker/image_picker.dart';
import '../config/app_config.dart';
import '../views/widgets/matrix.dart';
import '../../config/app_config.dart';
import '../widgets/matrix.dart';
class SettingsStyle extends StatefulWidget {
@override

View file

@ -21,11 +21,11 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:image_picker/image_picker.dart';
import 'package:url_launcher/url_launcher.dart';
import '../views/widgets/content_banner.dart';
import '../widgets/content_banner.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import '../views/widgets/matrix.dart';
import '../config/app_config.dart';
import '../config/setting_keys.dart';
import '../widgets/matrix.dart';
import '../../config/app_config.dart';
import '../../config/setting_keys.dart';
class Settings extends StatefulWidget {
@override

View file

@ -1,16 +1,13 @@
import 'package:fluffychat/controllers/sign_up_password_controller.dart';
import 'package:fluffychat/views/sign_up_password.dart';
import 'package:fluffychat/views/widgets/one_page_card.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class SignUpPasswordView extends StatelessWidget {
class SignUpPasswordUI extends StatelessWidget {
final SignUpPasswordController controller;
const SignUpPasswordView(
this.controller, {
Key key,
}) : super(key: key);
const SignUpPasswordUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -1,5 +1,5 @@
import 'package:adaptive_page_layout/adaptive_page_layout.dart';
import 'package:fluffychat/controllers/sign_up_controller.dart';
import 'package:fluffychat/views/sign_up.dart';
import 'package:fluffychat/views/widgets/fluffy_banner.dart';
import 'package:fluffychat/views/widgets/matrix.dart';
@ -8,13 +8,10 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class SignUpView extends StatelessWidget {
class SignUpUI extends StatelessWidget {
final SignUpController controller;
const SignUpView(
this.controller, {
Key key,
}) : super(key: key);
const SignUpUI(this.controller, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View file

@ -1,5 +1,5 @@
import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/controllers/image_viewer_controller.dart';
import 'package:fluffychat/views/image_viewer.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_blurhash/flutter_blurhash.dart';

View file

@ -19,7 +19,7 @@ import 'package:provider/provider.dart';
import 'package:universal_html/prefer_universal/html.dart' as html;
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
/*import 'package:fluffychat/views/chat.dart';
/*import 'package:fluffychat/views/chat_ui.dart';
import 'package:fluffychat/app_config.dart';
import 'package:dbus/dbus.dart';
import 'package:desktop_notifications/desktop_notifications.dart';*/