Skip to content

Commit

Permalink
TW-1836: Move shared media to direct chat profile (#1856)
Browse files Browse the repository at this point in the history
* TW-1836: Add shared media tabs into chat profile

* TW-1836: Delete Shared media screen

* TW-1836: Update `chat_details`
  • Loading branch information
hieutbui authored Jun 18, 2024
1 parent e2e7668 commit 3c88bfa
Show file tree
Hide file tree
Showing 11 changed files with 476 additions and 683 deletions.
289 changes: 9 additions & 280 deletions lib/pages/chat_details/chat_details.dart
Original file line number Diff line number Diff line change
@@ -1,38 +1,16 @@
import 'dart:async';
import 'package:fluffychat/di/global/get_it_initializer.dart';
import 'package:fluffychat/domain/model/room/room_extension.dart';
import 'package:fluffychat/pages/chat_details/chat_details_edit.dart';
import 'package:fluffychat/pages/chat_details/chat_details_page_view/chat_details_members_page.dart';
import 'package:fluffychat/pages/chat_details/chat_details_page_view/chat_details_page_enum.dart';
import 'package:fluffychat/pages/chat_details/chat_details_page_view/files/chat_details_files_page.dart';
import 'package:fluffychat/pages/chat_details/chat_details_page_view/links/chat_details_links_page.dart';
import 'package:fluffychat/pages/chat_details/chat_details_page_view/media/chat_details_media_page.dart';
import 'package:fluffychat/pages/chat_details/chat_details_view_style.dart';
import 'package:fluffychat/presentation/extensions/event_update_extension.dart';
import 'package:fluffychat/presentation/same_type_events_builder/same_type_events_controller.dart';
import 'package:fluffychat/pages/invitation_selection/invitation_selection.dart';
import 'package:fluffychat/pages/invitation_selection/invitation_selection_web.dart';
import 'package:fluffychat/presentation/extensions/room_summary_extension.dart';
import 'package:fluffychat/presentation/enum/chat/chat_details_screen_enum.dart';
import 'package:fluffychat/presentation/mixins/chat_details_tab_mixin.dart';
import 'package:fluffychat/presentation/mixins/handle_video_download_mixin.dart';
import 'package:fluffychat/presentation/mixins/play_video_action_mixin.dart';
import 'package:fluffychat/presentation/model/chat_details/chat_details_page_model.dart';
import 'package:fluffychat/utils/clipboard.dart';
import 'package:fluffychat/utils/dialog/twake_dialog.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart';
import 'package:fluffychat/utils/responsive/responsive_utils.dart';
import 'package:fluffychat/utils/scroll_controller_extension.dart';
import 'package:fluffychat/utils/twake_snackbar.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';

import 'package:matrix/matrix.dart';

import 'package:fluffychat/pages/chat_details/chat_details_view.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/matrix.dart';

enum AliasActions { copy, delete, setCanonical }
Expand All @@ -57,287 +35,38 @@ class ChatDetailsController extends State<ChatDetails>
with
HandleVideoDownloadMixin,
PlayVideoActionMixin,
SingleTickerProviderStateMixin {
static const _mediaFetchLimit = 20;

static const _linksFetchLimit = 20;

static const _filesFetchLimit = 20;

final invitationSelectionMobileAndTabletKey =
const Key('InvitationSelectionMobileAndTabletKey');

final invitationSelectionWebAndDesktopKey =
const Key('InvitationSelectionWebAndDesktopKey');

SingleTickerProviderStateMixin,
ChatDetailsTabMixin<ChatDetails> {
final actionsMobileAndTabletKey = const Key('ActionsMobileAndTabletKey');

final actionsWebAndDesktopKey = const Key('ActionsWebAndDesktopKey');

final GlobalKey<NestedScrollViewState> nestedScrollViewState = GlobalKey();

final List<ChatDetailsPage> chatDetailsPageView = [
ChatDetailsPage.members,
ChatDetailsPage.media,
ChatDetailsPage.links,
ChatDetailsPage.files,
];

final responsive = getIt.get<ResponsiveUtils>();

final Map<EventId, ImageData> _mediaCacheMap = {};

final muteNotifier = ValueNotifier<PushRuleState>(
PushRuleState.notify,
);

SameTypeEventsBuilderController? mediaListController;
SameTypeEventsBuilderController? linksListController;
SameTypeEventsBuilderController? filesListController;

Room? room;

TabController? tabController;
@override
Room? get room => Matrix.of(context).client.getRoomById(widget.roomId);

ValueNotifier<List<User>?> membersNotifier = ValueNotifier(null);
@override
ChatDetailsScreenEnum get chatType => ChatDetailsScreenEnum.group;

String? get roomId => widget.roomId;

bool get isMobileAndTablet =>
responsive.isMobile(context) || responsive.isTablet(context);

Timeline? _timeline;

Future<Timeline> getTimeline() async {
_timeline ??= await room!.getTimeline();
return _timeline!;
}

int get actualMembersCount => room!.summary.actualMembersCount;

StreamSubscription? _onRoomEventChangedSubscription;

@override
void initState() {
super.initState();
initControllers();
WidgetsBinding.instance.addPostFrameCallback((_) {
nestedScrollViewState.currentState?.innerController.addListener(
_listenerInnerController,
);
_refreshDataInTabviewInit();
});
initValueNotifiers();
_listenForRoomMembersChanged();
}

void initControllers() {
tabController = TabController(
length: chatDetailsPageView.length,
vsync: this,
);
mediaListController = SameTypeEventsBuilderController(
getTimeline: getTimeline,
searchFunc: (event) => event.isVideoOrImage,
limit: _mediaFetchLimit,
);
linksListController = SameTypeEventsBuilderController(
getTimeline: getTimeline,
searchFunc: (event) => event.isContainsLink,
limit: _linksFetchLimit,
);
filesListController = SameTypeEventsBuilderController(
getTimeline: getTimeline,
searchFunc: (event) => event.isAFile,
limit: _filesFetchLimit,
);
}

void initValueNotifiers() {
room = Matrix.of(context).client.getRoomById(roomId!);
muteNotifier.value = room?.pushRuleState ?? PushRuleState.notify;
membersNotifier.value ??= room?.getParticipants();
}

void _listenForRoomMembersChanged() {
_onRoomEventChangedSubscription =
Matrix.of(context).client.onEvent.stream.listen((event) {
if (event.isMemberChangedEvent && room?.id == event.roomID) {
membersNotifier.value = room?.getParticipants();
}
});
}

@override
void dispose() {
tabController?.dispose();
muteNotifier.dispose();
membersNotifier.dispose();
mediaListController?.dispose();
linksListController?.dispose();
filesListController?.dispose();
nestedScrollViewState.currentState?.innerController.dispose();
_onRoomEventChangedSubscription?.cancel();
super.dispose();
}

void _listenerInnerController() {
Logs().d("ChatDetails::currentTab - ${tabController?.index}");
if (nestedScrollViewState.currentState?.innerController.shouldLoadMore ==
true &&
tabController?.index != null) {
switch (chatDetailsPageView[tabController!.index]) {
case ChatDetailsPage.media:
mediaListController?.loadMore();
break;
case ChatDetailsPage.links:
linksListController?.loadMore();
break;
case ChatDetailsPage.files:
filesListController?.loadMore();
break;
default:
break;
}
}
}

void _refreshDataInTabviewInit() {
linksListController?.refresh();
mediaListController?.refresh();
filesListController?.refresh();
}

void requestMoreMembersAction() async {
final room = Matrix.of(context).client.getRoomById(roomId!);
final participants = await TwakeDialog.showFutureLoadingDialogFullScreen(
future: () => room!.requestParticipants(),
);
if (participants.error == null) {
membersNotifier.value = participants.result;
}
}

void openDialogInvite() {
if (PlatformInfos.isMobile) {
Navigator.of(context).push(
CupertinoPageRoute(
builder: (_) => InvitationSelection(
roomId: roomId!,
),
),
);
return;
}
showDialog(
context: context,
barrierDismissible: false,
useSafeArea: false,
useRootNavigator: !PlatformInfos.isMobile,
builder: (context) {
return SlotLayout(
config: <Breakpoint, SlotLayoutConfig>{
const WidthPlatformBreakpoint(
begin: ResponsiveUtils.minDesktopWidth,
): SlotLayout.from(
key: invitationSelectionWebAndDesktopKey,
builder: (_) => InvitationSelectionWebView(
roomId: roomId!,
),
),
const WidthPlatformBreakpoint(
end: ResponsiveUtils.minDesktopWidth,
): SlotLayout.from(
key: invitationSelectionMobileAndTabletKey,
builder: (_) => InvitationSelection(
roomId: roomId!,
),
),
},
);
},
);
}

Future<void> onUpdateMembers() async {
final members = await room!.requestParticipantsFromServer();
membersNotifier.value = members;
}

List<ChatDetailsPageModel> chatDetailsPages() => chatDetailsPageView.map(
(page) {
switch (page) {
case ChatDetailsPage.members:
return ChatDetailsPageModel(
page: page,
child: ChatDetailsMembersPage(
key: const PageStorageKey("members"),
membersNotifier: membersNotifier,
actualMembersCount: actualMembersCount,
canRequestMoreMembers:
(membersNotifier.value?.length ?? 0) < actualMembersCount,
requestMoreMembersAction: requestMoreMembersAction,
openDialogInvite: openDialogInvite,
isMobileAndTablet: isMobileAndTablet,
onUpdatedMembers: onUpdateMembers,
),
);
case ChatDetailsPage.media:
return ChatDetailsPageModel(
page: page,
child: mediaListController == null
? const SizedBox()
: ChatDetailsMediaPage(
key: const PageStorageKey<String>('Media'),
controller: mediaListController!,
cacheMap: _mediaCacheMap,
handleDownloadVideoEvent: _handleDownloadAndPlayVideo,
closeRightColumn: widget.closeRightColumn,
),
);
case ChatDetailsPage.links:
return ChatDetailsPageModel(
page: page,
child: linksListController == null
? const SizedBox()
: ChatDetailsLinksPage(
key: const PageStorageKey<String>('Links'),
controller: linksListController!,
),
);
case ChatDetailsPage.files:
return ChatDetailsPageModel(
page: page,
child: filesListController == null
? const SizedBox()
: ChatDetailsFilesPage(
key: const PageStorageKey<String>('Files'),
controller: filesListController!,
),
);
default:
return ChatDetailsPageModel(
page: page,
child: const SizedBox(),
);
}
},
).toList();

Future<String> _handleDownloadAndPlayVideo(Event event) {
return handleDownloadVideoEvent(
event: event,
playVideoAction: (path) => playVideoAction(
context,
path,
event: event,
isReplacement: false,
),
);
}

void onTapAddMembers() {
openDialogInvite();
muteNotifier.dispose();
}

void onToggleNotification() async {
Expand Down
6 changes: 3 additions & 3 deletions lib/pages/chat_details/chat_details_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,10 @@ class ChatDetailsView extends StatelessWidget {
color:
Theme.of(context).colorScheme.onSurfaceVariant,
),
tabs: controller.chatDetailsPages().map((pages) {
tabs: controller.tabList.map((page) {
return Tab(
child: Text(
pages.page.getTitle(context),
page.getTitle(context),
textAlign: TextAlign.center,
maxLines: 1,
overflow: TextOverflow.fade,
Expand All @@ -226,7 +226,7 @@ class ChatDetailsView extends StatelessWidget {
child: TabBarView(
physics: const NeverScrollableScrollPhysics(),
controller: controller.tabController,
children: controller.chatDetailsPages().map((pages) {
children: controller.sharedPages().map((pages) {
return pages.child;
}).toList(),
),
Expand Down
Loading

0 comments on commit 3c88bfa

Please sign in to comment.