fluffychat merge - resolve conflicts
This commit is contained in:
commit
40768f9859
21 changed files with 1389 additions and 2572 deletions
|
|
@ -201,6 +201,19 @@
|
|||
"supportedVersions": {}
|
||||
}
|
||||
},
|
||||
"countChatsAndCountParticipants": "{chats} chats and {participants} participants",
|
||||
"@countChatsAndCountParticipants": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"chats": {},
|
||||
"participants": {}
|
||||
}
|
||||
},
|
||||
"noMoreChatsFound": "No more chats found...",
|
||||
"joinedChats": "Joined chats",
|
||||
"unread": "Unread",
|
||||
"space": "Space",
|
||||
"spaces": "Spaces",
|
||||
"banFromChat": "Ban from chat",
|
||||
"@banFromChat": {
|
||||
"type": "text",
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@ abstract class AppConfig {
|
|||
static bool hideRedactedEvents = false;
|
||||
static bool hideUnknownEvents = true;
|
||||
static bool hideUnimportantStateEvents = true;
|
||||
static bool separateChatTypes = false;
|
||||
static bool autoplayImages = true;
|
||||
static bool sendTypingNotifications = true;
|
||||
static bool sendPublicReadReceipts = true;
|
||||
|
|
|
|||
|
|
@ -136,12 +136,8 @@ abstract class AppRoutes {
|
|||
FluffyThemes.isColumnMode(context) &&
|
||||
state.fullPath?.startsWith('/rooms/settings') == false
|
||||
? TwoColumnLayout(
|
||||
displayNavigationRail:
|
||||
state.path?.startsWith('/rooms/settings') != true,
|
||||
mainView: ChatList(
|
||||
activeChat: state.pathParameters['roomid'],
|
||||
displayNavigationRail:
|
||||
state.path?.startsWith('/rooms/settings') != true,
|
||||
),
|
||||
sideView: child,
|
||||
)
|
||||
|
|
@ -284,7 +280,6 @@ abstract class AppRoutes {
|
|||
? TwoColumnLayout(
|
||||
mainView: const Settings(),
|
||||
sideView: child,
|
||||
displayNavigationRail: false,
|
||||
)
|
||||
: child,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ abstract class SettingKeys {
|
|||
static const String hideUnknownEvents = 'chat.fluffy.hideUnknownEvents';
|
||||
static const String hideUnimportantStateEvents =
|
||||
'chat.fluffy.hideUnimportantStateEvents';
|
||||
static const String separateChatTypes = 'chat.fluffy.separateChatTypes';
|
||||
static const String sentry = 'sentry';
|
||||
static const String theme = 'theme';
|
||||
static const String amoledEnabled = 'amoled_enabled';
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,11 +1,11 @@
|
|||
import 'package:animations/animations.dart';
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list_header.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
|
||||
import 'package:fluffychat/pages/chat_list/search_title.dart';
|
||||
import 'package:fluffychat/pages/chat_list/space_view.dart';
|
||||
import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat_list/chat_list_body_text.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat_list/chat_list_header_wrapper.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat_list/chat_list_item_wrapper.dart';
|
||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
|
|
@ -25,6 +25,17 @@ class ChatListViewBody extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final activeSpace = controller.activeSpaceId;
|
||||
if (activeSpace != null) {
|
||||
return SpaceView(
|
||||
spaceId: activeSpace,
|
||||
onBack: controller.clearActiveSpace,
|
||||
onChatTab: (room) => controller.onChatTap(room, context),
|
||||
onChatContext: (room) => controller.chatContextAction(room),
|
||||
activeChat: controller.activeChat,
|
||||
toParentSpace: controller.setActiveSpace,
|
||||
);
|
||||
}
|
||||
final publicRooms = controller.roomSearchResult?.chunk
|
||||
.where((room) => room.roomType != 'm.space')
|
||||
.toList();
|
||||
|
|
@ -39,242 +50,290 @@ class ChatListViewBody extends StatelessWidget {
|
|||
final subtitleColor =
|
||||
Theme.of(context).textTheme.bodyLarge!.color!.withAlpha(50);
|
||||
final filter = controller.searchController.text.toLowerCase();
|
||||
return PageTransitionSwitcher(
|
||||
transitionBuilder: (
|
||||
Widget child,
|
||||
Animation<double> primaryAnimation,
|
||||
Animation<double> secondaryAnimation,
|
||||
) {
|
||||
return SharedAxisTransition(
|
||||
animation: primaryAnimation,
|
||||
secondaryAnimation: secondaryAnimation,
|
||||
transitionType: SharedAxisTransitionType.vertical,
|
||||
fillColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: StreamBuilder(
|
||||
key: ValueKey(
|
||||
client.userID.toString() +
|
||||
controller.activeFilter.toString() +
|
||||
controller.activeSpaceId.toString(),
|
||||
),
|
||||
stream: client.onSync.stream
|
||||
.where((s) => s.hasRoomUpdate)
|
||||
.rateLimit(const Duration(seconds: 1)),
|
||||
builder: (context, _) {
|
||||
if (controller.activeFilter == ActiveFilter.spaces) {
|
||||
return SpaceView(
|
||||
controller,
|
||||
scrollController: controller.scrollController,
|
||||
key: Key(controller.activeSpaceId ?? 'Spaces'),
|
||||
);
|
||||
return StreamBuilder(
|
||||
key: ValueKey(
|
||||
client.userID.toString(),
|
||||
),
|
||||
stream: client.onSync.stream
|
||||
.where((s) => s.hasRoomUpdate)
|
||||
.rateLimit(const Duration(seconds: 1)),
|
||||
builder: (context, _) {
|
||||
final rooms = controller.filteredRooms;
|
||||
|
||||
final spaces = rooms.where((r) => r.isSpace);
|
||||
final spaceDelegateCandidates = <String, Room>{};
|
||||
for (final space in spaces) {
|
||||
spaceDelegateCandidates[space.id] = space;
|
||||
for (final spaceChild in space.spaceChildren) {
|
||||
final roomId = spaceChild.roomId;
|
||||
if (roomId == null) continue;
|
||||
spaceDelegateCandidates[roomId] = space;
|
||||
}
|
||||
final rooms = controller.filteredRooms;
|
||||
return SafeArea(
|
||||
child: CustomScrollView(
|
||||
controller: controller.scrollController,
|
||||
slivers: [
|
||||
// #Pangea
|
||||
// ChatListHeader(controller: controller),
|
||||
ChatListHeaderWrapper(controller: controller),
|
||||
// Pangea#
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
if (controller.isSearchMode) ...[
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.publicRooms,
|
||||
icon: const Icon(Icons.explore_outlined),
|
||||
}
|
||||
final spaceDelegates = <String>{};
|
||||
|
||||
return SafeArea(
|
||||
child: CustomScrollView(
|
||||
controller: controller.scrollController,
|
||||
slivers: [
|
||||
ChatListHeader(controller: controller),
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
if (controller.isSearchMode) ...[
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.publicRooms,
|
||||
icon: const Icon(Icons.explore_outlined),
|
||||
),
|
||||
PublicRoomsHorizontalList(publicRooms: publicRooms),
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.publicSpaces,
|
||||
icon: const Icon(Icons.workspaces_outlined),
|
||||
),
|
||||
PublicRoomsHorizontalList(publicRooms: publicSpaces),
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.users,
|
||||
icon: const Icon(Icons.group_outlined),
|
||||
),
|
||||
AnimatedContainer(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
height: userSearchResult == null ||
|
||||
userSearchResult.results.isEmpty
|
||||
? 0
|
||||
: 106,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: userSearchResult == null
|
||||
? null
|
||||
: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: userSearchResult.results.length,
|
||||
itemBuilder: (context, i) => _SearchItem(
|
||||
title:
|
||||
userSearchResult.results[i].displayName ??
|
||||
userSearchResult
|
||||
.results[i].userId.localpart ??
|
||||
L10n.of(context)!.unknownDevice,
|
||||
avatar: userSearchResult.results[i].avatarUrl,
|
||||
onPressed: () => showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
builder: (c) => UserBottomSheet(
|
||||
profile: userSearchResult.results[i],
|
||||
outerContext: context,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
// #Pangea
|
||||
// if (!controller.isSearchMode && AppConfig.showPresences)
|
||||
// GestureDetector(
|
||||
// onLongPress: () => controller.dismissStatusList(),
|
||||
// child: StatusMessageList(
|
||||
// onStatusEdit: controller.setStatus,
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
const ConnectionStatusHeader(),
|
||||
AnimatedContainer(
|
||||
height: controller.isTorBrowser ? 64 : 0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.vpn_key),
|
||||
title: Text(L10n.of(context)!.dehydrateTor),
|
||||
subtitle: Text(L10n.of(context)!.dehydrateTorLong),
|
||||
trailing: const Icon(Icons.chevron_right_outlined),
|
||||
onTap: controller.dehydrate,
|
||||
),
|
||||
PublicRoomsHorizontalList(publicRooms: publicRooms),
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.publicSpaces,
|
||||
icon: const Icon(Icons.workspaces_outlined),
|
||||
),
|
||||
PublicRoomsHorizontalList(publicRooms: publicSpaces),
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.users,
|
||||
icon: const Icon(Icons.group_outlined),
|
||||
),
|
||||
AnimatedContainer(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
height: userSearchResult == null ||
|
||||
userSearchResult.results.isEmpty
|
||||
? 0
|
||||
: 106,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: userSearchResult == null
|
||||
? null
|
||||
: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: userSearchResult.results.length,
|
||||
itemBuilder: (context, i) => _SearchItem(
|
||||
title: userSearchResult
|
||||
.results[i].displayName ??
|
||||
userSearchResult
|
||||
.results[i].userId.localpart ??
|
||||
L10n.of(context)!.unknownDevice,
|
||||
avatar:
|
||||
userSearchResult.results[i].avatarUrl,
|
||||
onPressed: () => showAdaptiveBottomSheet(
|
||||
context: context,
|
||||
builder: (c) => UserBottomSheet(
|
||||
profile: userSearchResult.results[i],
|
||||
outerContext: context,
|
||||
),
|
||||
),
|
||||
if (client.rooms.isNotEmpty && !controller.isSearchMode)
|
||||
SizedBox(
|
||||
height: 44,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 6,
|
||||
),
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: ActiveFilter.values
|
||||
.map(
|
||||
(filter) => Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
onTap: () =>
|
||||
controller.setActiveFilter(filter),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: filter == controller.activeFilter
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
filter.toLocalizedString(context),
|
||||
style: TextStyle(
|
||||
fontWeight:
|
||||
filter == controller.activeFilter
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
color:
|
||||
filter == controller.activeFilter
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimary
|
||||
: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSecondaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
// #Pangea
|
||||
// if (!controller.isSearchMode &&
|
||||
// controller.activeFilter != ActiveFilter.groups &&
|
||||
// AppConfig.showPresences)
|
||||
// GestureDetector(
|
||||
// onLongPress: () => controller.dismissStatusList(),
|
||||
// child: StatusMessageList(
|
||||
// onStatusEdit: controller.setStatus,
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
const ConnectionStatusHeader(),
|
||||
AnimatedContainer(
|
||||
height: controller.isTorBrowser ? 64 : 0,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: const BoxDecoration(),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.vpn_key),
|
||||
title: Text(L10n.of(context)!.dehydrateTor),
|
||||
subtitle: Text(L10n.of(context)!.dehydrateTorLong),
|
||||
trailing: const Icon(Icons.chevron_right_outlined),
|
||||
onTap: controller.dehydrate,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
if (controller.isSearchMode)
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.chats,
|
||||
icon: const Icon(Icons.forum_outlined),
|
||||
if (controller.isSearchMode)
|
||||
SearchTitle(
|
||||
title: L10n.of(context)!.chats,
|
||||
icon: const Icon(Icons.forum_outlined),
|
||||
),
|
||||
if (client.prevBatch != null &&
|
||||
rooms.isEmpty &&
|
||||
!controller.isSearchMode) ...[
|
||||
// #Pangea
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(32.0),
|
||||
// child: Icon(
|
||||
// CupertinoIcons.chat_bubble_2,
|
||||
// size: 128,
|
||||
// color: Theme.of(context).colorScheme.secondary,
|
||||
// ),
|
||||
// ),
|
||||
Center(
|
||||
child: ChatListBodyStartText(
|
||||
controller: controller,
|
||||
),
|
||||
if (client.prevBatch != null &&
|
||||
rooms.isEmpty &&
|
||||
!controller.isSearchMode) ...[
|
||||
// #Pangea
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(32.0),
|
||||
// child: Icon(
|
||||
// CupertinoIcons.chat_bubble_2,
|
||||
// size: 128,
|
||||
// color:
|
||||
// Theme.of(context).colorScheme.onInverseSurface,
|
||||
// ),
|
||||
// ),
|
||||
Center(
|
||||
child: ChatListBodyStartText(
|
||||
controller: controller,
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
if (client.prevBatch == null)
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, i) => Opacity(
|
||||
opacity: (dummyChatCount - i) / dummyChatCount,
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: titleColor,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 1,
|
||||
color: Theme.of(context).textTheme.bodyLarge!.color,
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
],
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: titleColor,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 36),
|
||||
Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
height: 12,
|
||||
margin: const EdgeInsets.only(right: 22),
|
||||
),
|
||||
),
|
||||
),
|
||||
childCount: dummyChatCount,
|
||||
),
|
||||
),
|
||||
if (client.prevBatch == null)
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, i) => Opacity(
|
||||
opacity: (dummyChatCount - i) / dummyChatCount,
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: titleColor,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 1,
|
||||
color:
|
||||
Theme.of(context).textTheme.bodyLarge!.color,
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: titleColor,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 36),
|
||||
Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: subtitleColor,
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
height: 12,
|
||||
margin: const EdgeInsets.only(right: 22),
|
||||
),
|
||||
),
|
||||
),
|
||||
childCount: dummyChatCount,
|
||||
),
|
||||
),
|
||||
if (client.prevBatch != null)
|
||||
SliverList.builder(
|
||||
itemCount: rooms.length,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
// #Pangea
|
||||
// return ChatListItem(
|
||||
return ChatListItemWrapper(
|
||||
controller: controller,
|
||||
// Pangea#
|
||||
rooms[i],
|
||||
key: Key('chat_list_item_${rooms[i].id}'),
|
||||
filter: filter,
|
||||
// #Pangea
|
||||
// selected:
|
||||
// controller.selectedRoomIds.contains(rooms[i].id),
|
||||
// onTap: controller.selectMode == SelectMode.select
|
||||
// ? () => controller.toggleSelection(rooms[i].id)
|
||||
// : () => onChatTap(rooms[i], context),
|
||||
// onLongPress: () =>
|
||||
// controller.toggleSelection(rooms[i].id),
|
||||
// Pangea#
|
||||
activeChat: controller.activeChat == rooms[i].id,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (client.prevBatch != null)
|
||||
SliverList.builder(
|
||||
itemCount: rooms.length,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
var room = rooms[i];
|
||||
if (controller.activeFilter != ActiveFilter.groups) {
|
||||
final parent = room.isSpace
|
||||
? room
|
||||
: spaceDelegateCandidates[room.id];
|
||||
if (parent != null) {
|
||||
if (spaceDelegates.contains(parent.id)) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
spaceDelegates.add(parent.id);
|
||||
room = parent;
|
||||
}
|
||||
}
|
||||
|
||||
return ChatListItem(
|
||||
room,
|
||||
lastEventRoom: rooms[i],
|
||||
key: Key('chat_list_item_${room.id}'),
|
||||
filter: filter,
|
||||
onTap: () => controller.onChatTap(room, context),
|
||||
onLongPress: () => controller.chatContextAction(room),
|
||||
activeChat: controller.activeChat == room.id,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/client_chooser_button.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
|
|
@ -42,24 +40,23 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||
L10n.of(context)!.share,
|
||||
key: const ValueKey(SelectMode.share),
|
||||
)
|
||||
: selectMode == SelectMode.select
|
||||
? Text(
|
||||
controller.selectedRoomIds.length.toString(),
|
||||
key: const ValueKey(SelectMode.select),
|
||||
)
|
||||
// #Pangea
|
||||
: ClientChooserButton(controller),
|
||||
// #Pangea
|
||||
: ClientChooserButton(controller),
|
||||
// : TextField(
|
||||
// controller: controller.searchController,
|
||||
// focusNode: controller.searchFocusNode,
|
||||
// textInputAction: TextInputAction.search,
|
||||
// onChanged: controller.onSearchEnter,
|
||||
// onChanged: (text) => controller.onSearchEnter(
|
||||
// text,
|
||||
// globalSearch: globalSearch,
|
||||
// ),
|
||||
// decoration: InputDecoration(
|
||||
// fillColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||
// border: UnderlineInputBorder(
|
||||
// border: OutlineInputBorder(
|
||||
// borderSide: BorderSide.none,
|
||||
// borderRadius: BorderRadius.circular(99),
|
||||
// ),
|
||||
// contentPadding: EdgeInsets.zero,
|
||||
// hintText: L10n.of(context)!.searchChatsRooms,
|
||||
// hintStyle: TextStyle(
|
||||
// color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
|
|
@ -71,20 +68,17 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||
// tooltip: L10n.of(context)!.cancel,
|
||||
// icon: const Icon(Icons.close_outlined),
|
||||
// onPressed: controller.cancelSearch,
|
||||
// color: Theme.of(context)
|
||||
// .colorScheme
|
||||
// .onPrimaryContainer,
|
||||
// color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
// )
|
||||
// : IconButton(
|
||||
// onPressed: controller.startSearch,
|
||||
// icon: Icon(
|
||||
// Icons.search_outlined,
|
||||
// color: Theme.of(context)
|
||||
// .colorScheme
|
||||
// .onPrimaryContainer,
|
||||
// color:
|
||||
// Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
// ),
|
||||
// ),
|
||||
// suffixIcon: controller.isSearchMode
|
||||
// suffixIcon: controller.isSearchMode && globalSearch
|
||||
// ? controller.isSearching
|
||||
// ? const Padding(
|
||||
// padding: EdgeInsets.symmetric(
|
||||
|
|
@ -109,10 +103,7 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||
// icon: const Icon(Icons.edit_outlined, size: 16),
|
||||
// label: Text(
|
||||
// controller.searchServer ??
|
||||
// Matrix.of(context)
|
||||
// .client
|
||||
// .homeserver!
|
||||
// .host,
|
||||
// Matrix.of(context).client.homeserver!.host,
|
||||
// maxLines: 2,
|
||||
// ),
|
||||
// )
|
||||
|
|
@ -135,80 +126,7 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||
// ),
|
||||
// Pangea#
|
||||
]
|
||||
: selectMode == SelectMode.select
|
||||
? [
|
||||
// #Pangea
|
||||
// if (controller.spaces.isNotEmpty)
|
||||
if (controller.spaces.isNotEmpty &&
|
||||
controller.selectedRoomIds.length == 1)
|
||||
// Pangea#
|
||||
IconButton(
|
||||
tooltip: L10n.of(context)!.addToSpace,
|
||||
icon: const Icon(Icons.workspaces_outlined),
|
||||
onPressed: controller.addToSpace,
|
||||
),
|
||||
IconButton(
|
||||
tooltip: L10n.of(context)!.toggleUnread,
|
||||
icon: Icon(
|
||||
controller.anySelectedRoomNotMarkedUnread
|
||||
? Icons.mark_chat_unread_outlined
|
||||
: Icons.mark_chat_read_outlined,
|
||||
),
|
||||
onPressed: controller.toggleUnread,
|
||||
),
|
||||
IconButton(
|
||||
tooltip: L10n.of(context)!.toggleFavorite,
|
||||
icon: Icon(
|
||||
controller.anySelectedRoomNotFavorite
|
||||
? Icons.push_pin
|
||||
: Icons.push_pin_outlined,
|
||||
),
|
||||
onPressed: controller.toggleFavouriteRoom,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
controller.anySelectedRoomNotMuted
|
||||
? Icons.notifications_off_outlined
|
||||
: Icons.notifications_outlined,
|
||||
),
|
||||
tooltip: L10n.of(context)!.toggleMuted,
|
||||
onPressed: controller.toggleMuted,
|
||||
),
|
||||
// #Pangea
|
||||
if (controller.selectedRoomIds.length > 1)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.arrow_forward),
|
||||
tooltip: L10n.of(context)!.leave,
|
||||
onPressed: controller.leaveAction,
|
||||
),
|
||||
if (controller.selectedRoomIds.length == 1 &&
|
||||
!(Matrix.of(context)
|
||||
.client
|
||||
.getRoomById(controller.selectedRoomIds.single)
|
||||
?.isRoomAdmin ??
|
||||
false))
|
||||
IconButton(
|
||||
icon: const Icon(Icons.arrow_forward),
|
||||
tooltip: L10n.of(context)!.leave,
|
||||
onPressed: controller.leaveAction,
|
||||
),
|
||||
if (controller.selectedRoomIds.length == 1 &&
|
||||
(Matrix.of(context)
|
||||
.client
|
||||
.getRoomById(controller.selectedRoomIds.single)
|
||||
?.isRoomAdmin ??
|
||||
false))
|
||||
// Pangea#
|
||||
IconButton(
|
||||
// #Pangea
|
||||
// icon: const Icon(Icons.delete_outlined),
|
||||
icon: const Icon(Icons.archive_outlined),
|
||||
// Pangea#
|
||||
tooltip: L10n.of(context)!.archive,
|
||||
onPressed: controller.archiveAction,
|
||||
),
|
||||
]
|
||||
: null,
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ enum ArchivedRoomAction { delete, rejoin }
|
|||
|
||||
class ChatListItem extends StatelessWidget {
|
||||
final Room room;
|
||||
final Room? lastEventRoom;
|
||||
final bool activeChat;
|
||||
final bool selected;
|
||||
final void Function()? onLongPress;
|
||||
final void Function()? onForget;
|
||||
final void Function() onTap;
|
||||
|
|
@ -29,11 +29,11 @@ class ChatListItem extends StatelessWidget {
|
|||
const ChatListItem(
|
||||
this.room, {
|
||||
this.activeChat = false,
|
||||
this.selected = false,
|
||||
required this.onTap,
|
||||
this.onLongPress,
|
||||
this.onForget,
|
||||
this.filter,
|
||||
this.lastEventRoom,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -57,10 +57,7 @@ class ChatListItem extends StatelessWidget {
|
|||
if (confirmed == OkCancelResult.cancel) return;
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
// #Pangea
|
||||
// future: () => room.leave(),
|
||||
future: () => room.archive(),
|
||||
// Pangea#
|
||||
future: () => room.leave(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -69,24 +66,23 @@ class ChatListItem extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isMuted = room.pushRuleState != PushRuleState.notify;
|
||||
final typingText = room.getLocalizedTypingText(context);
|
||||
final lastEvent = room.lastEvent;
|
||||
final lastEventRoom = this.lastEventRoom ?? room;
|
||||
final typingText = lastEventRoom.getLocalizedTypingText(context);
|
||||
final lastEvent = lastEventRoom.lastEvent;
|
||||
final ownMessage = lastEvent?.senderId == room.client.userID;
|
||||
final unread = room.isUnread || room.membership == Membership.invite;
|
||||
final unread =
|
||||
lastEventRoom.isUnread || lastEventRoom.membership == Membership.invite;
|
||||
final theme = Theme.of(context);
|
||||
final directChatMatrixId = room.directChatMatrixID;
|
||||
final isDirectChat = directChatMatrixId != null;
|
||||
final unreadBubbleSize = unread || room.hasNewMessages
|
||||
? room.notificationCount > 0
|
||||
final unreadBubbleSize = unread || lastEventRoom.hasNewMessages
|
||||
? lastEventRoom.notificationCount > 0
|
||||
? 20.0
|
||||
: 14.0
|
||||
: 0.0;
|
||||
final hasNotifications = room.notificationCount > 0;
|
||||
final backgroundColor = selected
|
||||
? theme.colorScheme.primaryContainer
|
||||
: activeChat
|
||||
? theme.colorScheme.secondaryContainer
|
||||
: null;
|
||||
final hasNotifications = lastEventRoom.notificationCount > 0;
|
||||
final backgroundColor =
|
||||
activeChat ? theme.colorScheme.secondaryContainer : null;
|
||||
final displayname = room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
);
|
||||
|
|
@ -124,11 +120,11 @@ class ChatListItem extends StatelessWidget {
|
|||
curve: FluffyThemes.animationCurve,
|
||||
scale: hovered ? 1.1 : 1.0,
|
||||
child: Avatar(
|
||||
borderRadius: room.isSpace
|
||||
? BorderRadius.circular(AppConfig.borderRadius / 3)
|
||||
: null,
|
||||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
//#Pangea
|
||||
littleIcon: room.roomTypeIcon,
|
||||
// Pangea#
|
||||
presenceUserId: directChatMatrixId,
|
||||
presenceBackgroundColor: backgroundColor,
|
||||
onTap: onLongPress,
|
||||
|
|
@ -141,14 +137,12 @@ class ChatListItem extends StatelessWidget {
|
|||
child: AnimatedScale(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
scale: (hovered || selected) ? 1.0 : 0.0,
|
||||
scale: (hovered) ? 1.0 : 0.0,
|
||||
child: Material(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Icon(
|
||||
selected
|
||||
? Icons.check_circle
|
||||
: Icons.check_circle_outlined,
|
||||
child: const Icon(
|
||||
Icons.check_circle_outlined,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
|
|
@ -188,7 +182,9 @@ class ChatListItem extends StatelessWidget {
|
|||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
if (lastEvent != null && room.membership != Membership.invite)
|
||||
if (!room.isSpace &&
|
||||
lastEvent != null &&
|
||||
room.membership != Membership.invite)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4.0),
|
||||
child: Text(
|
||||
|
|
@ -201,11 +197,30 @@ class ChatListItem extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (room.isSpace)
|
||||
const Icon(
|
||||
Icons.arrow_circle_right_outlined,
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
if (room.isSpace) ...[
|
||||
room.id != lastEventRoom.id &&
|
||||
lastEventRoom.isUnreadOrInvited
|
||||
? Avatar(
|
||||
mxContent: lastEventRoom.avatar,
|
||||
name: lastEventRoom.name,
|
||||
size: 18,
|
||||
)
|
||||
: const Icon(
|
||||
Icons.workspaces_outlined,
|
||||
size: 18,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
if (typingText.isEmpty &&
|
||||
ownMessage &&
|
||||
room.lastEvent!.status.isSending) ...[
|
||||
|
|
@ -230,71 +245,80 @@ class ChatListItem extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Expanded(
|
||||
child: typingText.isNotEmpty
|
||||
child: room.isSpace && !lastEventRoom.isUnreadOrInvited
|
||||
? Text(
|
||||
typingText,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
L10n.of(context)!.countChatsAndCountParticipants(
|
||||
room.spaceChildren.length.toString(),
|
||||
(room.summary.mJoinedMemberCount ?? 1).toString(),
|
||||
),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
)
|
||||
: FutureBuilder(
|
||||
key: ValueKey(
|
||||
'${lastEvent?.eventId}_${lastEvent?.type}',
|
||||
),
|
||||
// #Pangea
|
||||
future: room.lastEvent != null
|
||||
? GetChatListItemSubtitle().getSubtitle(
|
||||
L10n.of(context)!,
|
||||
room.lastEvent,
|
||||
MatrixState.pangeaController,
|
||||
)
|
||||
: Future.value(L10n.of(context)!.emptyChat),
|
||||
// future: needLastEventSender
|
||||
// ? lastEvent.calcLocalizedBody(
|
||||
// MatrixLocals(L10n.of(context)!),
|
||||
// hideReply: true,
|
||||
// hideEdit: true,
|
||||
// plaintextBody: true,
|
||||
// removeMarkdown: true,
|
||||
// withSenderNamePrefix: !isDirectChat ||
|
||||
// directChatMatrixId !=
|
||||
// room.lastEvent?.senderId,
|
||||
// )
|
||||
// : null,
|
||||
// Pangea#
|
||||
initialData: lastEvent?.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix: !isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent?.senderId,
|
||||
),
|
||||
builder: (context, snapshot) => Text(
|
||||
room.membership == Membership.invite
|
||||
? isDirectChat
|
||||
? L10n.of(context)!.invitePrivateChat
|
||||
: L10n.of(context)!.inviteGroupChat
|
||||
: snapshot.data ??
|
||||
L10n.of(context)!.emptyChat,
|
||||
softWrap: false,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight: unread || room.hasNewMessages
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
decoration: room.lastEvent?.redacted == true
|
||||
? TextDecoration.lineThrough
|
||||
: null,
|
||||
: typingText.isNotEmpty
|
||||
? Text(
|
||||
typingText,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
)
|
||||
: FutureBuilder(
|
||||
key: ValueKey(
|
||||
'${lastEvent?.eventId}_${lastEvent?.type}',
|
||||
),
|
||||
// #Pangea
|
||||
future: room.lastEvent != null
|
||||
? GetChatListItemSubtitle().getSubtitle(
|
||||
L10n.of(context)!,
|
||||
room.lastEvent,
|
||||
MatrixState.pangeaController,
|
||||
)
|
||||
: Future.value(L10n.of(context)!.emptyChat),
|
||||
// future: needLastEventSender
|
||||
// ? lastEvent.calcLocalizedBody(
|
||||
// MatrixLocals(L10n.of(context)!),
|
||||
// hideReply: true,
|
||||
// hideEdit: true,
|
||||
// plaintextBody: true,
|
||||
// removeMarkdown: true,
|
||||
// withSenderNamePrefix: (!isDirectChat ||
|
||||
// directChatMatrixId !=
|
||||
// room.lastEvent?.senderId),
|
||||
// )
|
||||
// : null,
|
||||
// Pangea#
|
||||
initialData:
|
||||
lastEvent?.calcLocalizedBodyFallback(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
plaintextBody: true,
|
||||
removeMarkdown: true,
|
||||
withSenderNamePrefix: (!isDirectChat ||
|
||||
directChatMatrixId !=
|
||||
room.lastEvent?.senderId),
|
||||
),
|
||||
builder: (context, snapshot) => Text(
|
||||
room.membership == Membership.invite
|
||||
? isDirectChat
|
||||
? L10n.of(context)!.invitePrivateChat
|
||||
: L10n.of(context)!.inviteGroupChat
|
||||
: snapshot.data ??
|
||||
L10n.of(context)!.emptyChat,
|
||||
softWrap: false,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontWeight:
|
||||
unread || lastEventRoom.hasNewMessages
|
||||
? FontWeight.bold
|
||||
: null,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
decoration: room.lastEvent?.redacted == true
|
||||
? TextDecoration.lineThrough
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// #Pangea
|
||||
|
|
@ -315,7 +339,9 @@ class ChatListItem extends StatelessWidget {
|
|||
width: !hasNotifications && !unread && !room.hasNewMessages
|
||||
? 0
|
||||
: (unreadBubbleSize - 9) *
|
||||
room.notificationCount.toString().length +
|
||||
lastEventRoom.notificationCount
|
||||
.toString()
|
||||
.length +
|
||||
9,
|
||||
decoration: BoxDecoration(
|
||||
color: room.highlightCount > 0 ||
|
||||
|
|
@ -330,7 +356,7 @@ class ChatListItem extends StatelessWidget {
|
|||
child: Center(
|
||||
child: hasNotifications
|
||||
? Text(
|
||||
room.notificationCount.toString(),
|
||||
lastEventRoom.notificationCount.toString(),
|
||||
style: TextStyle(
|
||||
color: room.highlightCount > 0
|
||||
? Colors.white
|
||||
|
|
|
|||
|
|
@ -1,104 +1,17 @@
|
|||
import 'package:badges/badges.dart';
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/navi_rail_item.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/utils/chat_list_handle_space_tap.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/unread_rooms_badge.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../../widgets/matrix.dart';
|
||||
import 'chat_list_body.dart';
|
||||
import 'start_chat_fab.dart';
|
||||
|
||||
class ChatListView extends StatelessWidget {
|
||||
final ChatListController controller;
|
||||
|
||||
const ChatListView(this.controller, {super.key});
|
||||
|
||||
List<NavigationDestination> getNavigationDestinations(BuildContext context) {
|
||||
final badgePosition = BadgePosition.topEnd(top: -12, end: -8);
|
||||
return [
|
||||
if (AppConfig.separateChatTypes) ...[
|
||||
NavigationDestination(
|
||||
icon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter:
|
||||
controller.getRoomFilterByActiveFilter(ActiveFilter.messages),
|
||||
child: const Icon(Icons.chat_outlined),
|
||||
),
|
||||
selectedIcon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter:
|
||||
controller.getRoomFilterByActiveFilter(ActiveFilter.messages),
|
||||
child: const Icon(Icons.chat),
|
||||
),
|
||||
//#Pangea
|
||||
// label: L10n.of(context)!.messages,
|
||||
label: L10n.of(context)!.directChats,
|
||||
//Pangea#
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter: controller.getRoomFilterByActiveFilter(ActiveFilter.groups),
|
||||
child: const Icon(Icons.group_outlined),
|
||||
),
|
||||
selectedIcon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter: controller.getRoomFilterByActiveFilter(ActiveFilter.groups),
|
||||
child: const Icon(Icons.group),
|
||||
),
|
||||
label: L10n.of(context)!.groups,
|
||||
),
|
||||
] else
|
||||
NavigationDestination(
|
||||
icon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter:
|
||||
controller.getRoomFilterByActiveFilter(ActiveFilter.allChats),
|
||||
child: const Icon(Icons.chat_outlined),
|
||||
),
|
||||
selectedIcon: UnreadRoomsBadge(
|
||||
badgePosition: badgePosition,
|
||||
filter:
|
||||
controller.getRoomFilterByActiveFilter(ActiveFilter.allChats),
|
||||
child: const Icon(Icons.chat),
|
||||
),
|
||||
// #Pangea
|
||||
// label: L10n.of(context)!.chats,
|
||||
label: L10n.of(context)!.allChats,
|
||||
// Pangea#
|
||||
),
|
||||
if (controller.spaces.isNotEmpty
|
||||
// #Pangea
|
||||
&&
|
||||
!FluffyThemes.isColumnMode(context)
|
||||
// Pangea#
|
||||
)
|
||||
// #Pangea
|
||||
// const NavigationDestination(
|
||||
// icon: Icon(Icons.workspaces_outlined),
|
||||
// selectedIcon: Icon(Icons.workspaces),
|
||||
// label: 'Spaces',
|
||||
// ),
|
||||
NavigationDestination(
|
||||
icon: const Icon(Icons.workspaces_outlined),
|
||||
selectedIcon: const Icon(Icons.workspaces),
|
||||
label: L10n.of(context)!.allSpaces,
|
||||
),
|
||||
// Pangea#
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final client = Matrix.of(context).client;
|
||||
return StreamBuilder<Object?>(
|
||||
stream: Matrix.of(context).onShareContentChanged.stream,
|
||||
builder: (_, __) {
|
||||
|
|
@ -106,10 +19,7 @@ class ChatListView extends StatelessWidget {
|
|||
return PopScope(
|
||||
canPop: controller.selectMode == SelectMode.normal &&
|
||||
!controller.isSearchMode &&
|
||||
controller.activeFilter ==
|
||||
(AppConfig.separateChatTypes
|
||||
? ActiveFilter.messages
|
||||
: ActiveFilter.allChats),
|
||||
controller.activeFilter == ActiveFilter.allChats,
|
||||
onPopInvoked: (pop) async {
|
||||
if (pop) return;
|
||||
final selMode = controller.selectMode;
|
||||
|
|
@ -121,151 +31,35 @@ class ChatListView extends StatelessWidget {
|
|||
controller.cancelAction();
|
||||
return;
|
||||
}
|
||||
if (controller.activeFilter !=
|
||||
(AppConfig.separateChatTypes
|
||||
? ActiveFilter.messages
|
||||
: ActiveFilter.allChats)) {
|
||||
controller
|
||||
.onDestinationSelected(AppConfig.separateChatTypes ? 1 : 0);
|
||||
return;
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
if (FluffyThemes.isColumnMode(context) &&
|
||||
controller.widget.displayNavigationRail) ...[
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final allSpaces =
|
||||
client.rooms.where((room) => room.isSpace);
|
||||
final rootSpaces = allSpaces
|
||||
.where(
|
||||
(space) => !allSpaces.any(
|
||||
(parentSpace) => parentSpace.spaceChildren
|
||||
.any((child) => child.roomId == space.id),
|
||||
child: GestureDetector(
|
||||
onTap: FocusManager.instance.primaryFocus?.unfocus,
|
||||
excludeFromSemantics: true,
|
||||
behavior: HitTestBehavior.translucent,
|
||||
child: Scaffold(
|
||||
body: ChatListViewBody(controller),
|
||||
floatingActionButton:
|
||||
// #Pangea
|
||||
// KeyBoardShortcuts(
|
||||
// keysToPress: {
|
||||
// LogicalKeyboardKey.controlLeft,
|
||||
// LogicalKeyboardKey.keyN,
|
||||
// },
|
||||
// onKeysPressed: () => context.go('/rooms/newprivatechat'),
|
||||
// helpLabel: L10n.of(context)!.newChat,
|
||||
// child:
|
||||
// Pangea#
|
||||
selectMode == SelectMode.normal && !controller.isSearchMode
|
||||
? FloatingActionButton.extended(
|
||||
onPressed: controller.addChatAction,
|
||||
icon: const Icon(Icons.add_outlined),
|
||||
label: Text(
|
||||
L10n.of(context)!.chat,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
final destinations = getNavigationDestinations(context);
|
||||
|
||||
return SizedBox(
|
||||
width: FluffyThemes.navRailWidth,
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.vertical,
|
||||
itemCount: rootSpaces.length + destinations.length,
|
||||
itemBuilder: (context, i) {
|
||||
if (i < destinations.length) {
|
||||
return NaviRailItem(
|
||||
// #Pangea
|
||||
// isSelected: i == controller.selectedIndex,
|
||||
isSelected: controller.isSelected(i),
|
||||
// Pangea#
|
||||
onTap: () => controller.onDestinationSelected(i),
|
||||
icon: destinations[i].icon,
|
||||
selectedIcon: destinations[i].selectedIcon,
|
||||
toolTip: destinations[i].label,
|
||||
);
|
||||
}
|
||||
i -= destinations.length;
|
||||
final isSelected =
|
||||
controller.activeFilter == ActiveFilter.spaces &&
|
||||
rootSpaces[i].id == controller.activeSpaceId;
|
||||
//#Pangea
|
||||
final Room? room = Matrix.of(context)
|
||||
.client
|
||||
.getRoomById(rootSpaces[i].id);
|
||||
// Pangea#
|
||||
return NaviRailItem(
|
||||
toolTip: rootSpaces[i].getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
),
|
||||
isSelected: isSelected,
|
||||
// #Pangea
|
||||
// onTap: () =>
|
||||
// controller.setActiveSpace(rootSpaces[i].id),
|
||||
onTap: () => chatListHandleSpaceTap(
|
||||
context,
|
||||
controller,
|
||||
rootSpaces[i],
|
||||
),
|
||||
// Pangea#
|
||||
icon: Avatar(
|
||||
mxContent: rootSpaces[i].avatar,
|
||||
name: rootSpaces[i].getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
),
|
||||
size: 32,
|
||||
// #Pangea
|
||||
littleIcon: room?.roomTypeIcon,
|
||||
// Pangea#
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: FocusManager.instance.primaryFocus?.unfocus,
|
||||
excludeFromSemantics: true,
|
||||
behavior: HitTestBehavior.translucent,
|
||||
child: Scaffold(
|
||||
body: ChatListViewBody(controller),
|
||||
bottomNavigationBar: controller.displayNavigationBar
|
||||
? NavigationBar(
|
||||
elevation: 4,
|
||||
labelBehavior:
|
||||
NavigationDestinationLabelBehavior.alwaysShow,
|
||||
shadowColor:
|
||||
Theme.of(context).colorScheme.onSurface,
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.surface,
|
||||
surfaceTintColor:
|
||||
Theme.of(context).colorScheme.surface,
|
||||
selectedIndex: controller.selectedIndex,
|
||||
onDestinationSelected:
|
||||
controller.onDestinationSelected,
|
||||
destinations: getNavigationDestinations(context),
|
||||
)
|
||||
: null,
|
||||
// #Pangea
|
||||
// floatingActionButton: KeyBoardShortcuts(
|
||||
// keysToPress: {
|
||||
// LogicalKeyboardKey.controlLeft,
|
||||
// LogicalKeyboardKey.keyN,
|
||||
// },
|
||||
// onKeysPressed: () => context.go('/rooms/newprivatechat'),
|
||||
// helpLabel: L10n.of(context)!.newChat,
|
||||
// child: selectMode == SelectMode.normal &&
|
||||
// !controller.isSearchMode
|
||||
// ? StartChatFloatingActionButton(
|
||||
// activeFilter: controller.activeFilter,
|
||||
// roomsIsEmpty: false,
|
||||
// scrolledToTop: controller.scrolledToTop,
|
||||
// createNewSpace: controller.createNewSpace,
|
||||
// )
|
||||
// : const SizedBox.shrink(),
|
||||
// ),
|
||||
floatingActionButton: selectMode == SelectMode.normal
|
||||
? StartChatFloatingActionButton(
|
||||
activeFilter: controller.activeFilter,
|
||||
roomsIsEmpty: false,
|
||||
scrolledToTop: controller.scrolledToTop,
|
||||
controller: controller,
|
||||
createNewSpace: () {},
|
||||
)
|
||||
: null,
|
||||
// Pangea#
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -392,13 +392,13 @@ class ClientChooserButton extends StatelessWidget {
|
|||
case SettingsAction.joinWithClassCode:
|
||||
SpaceCodeUtil.joinWithSpaceCodeDialog(
|
||||
context,
|
||||
controller.pangeaController,
|
||||
MatrixState.pangeaController,
|
||||
);
|
||||
break;
|
||||
case SettingsAction.findAConversationPartner:
|
||||
findConversationPartnerDialog(
|
||||
context,
|
||||
controller.pangeaController,
|
||||
MatrixState.pangeaController,
|
||||
);
|
||||
break;
|
||||
// case SettingsAction.spaceAnalytics:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,125 +0,0 @@
|
|||
import 'dart:core';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../config/themes.dart';
|
||||
import 'chat_list.dart';
|
||||
|
||||
class StartChatFloatingActionButton extends StatelessWidget {
|
||||
final ActiveFilter activeFilter;
|
||||
final ValueNotifier<bool> scrolledToTop;
|
||||
final bool roomsIsEmpty;
|
||||
// #Pangea
|
||||
final ChatListController controller;
|
||||
// Pangea#
|
||||
final void Function() createNewSpace;
|
||||
|
||||
const StartChatFloatingActionButton({
|
||||
super.key,
|
||||
required this.activeFilter,
|
||||
required this.scrolledToTop,
|
||||
required this.roomsIsEmpty,
|
||||
required this.createNewSpace,
|
||||
// #Pangea
|
||||
required this.controller,
|
||||
// Pangea#
|
||||
});
|
||||
|
||||
void _onPressed(BuildContext context) async {
|
||||
//#Pangea
|
||||
if (controller.activeSpaceId != null) {
|
||||
context.go(
|
||||
'/rooms/newgroup${controller.activeSpaceId != null ? '?spaceId=${controller.activeSpaceId}' : ''}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
//Pangea#
|
||||
switch (activeFilter) {
|
||||
case ActiveFilter.allChats:
|
||||
case ActiveFilter.messages:
|
||||
// #Pangea
|
||||
// context.go('/rooms/newprivatechat');
|
||||
// break;
|
||||
// Pangea#
|
||||
case ActiveFilter.groups:
|
||||
// #Pangea
|
||||
// context.go('/rooms/newgroup');
|
||||
context.go(
|
||||
'/rooms/newgroup${controller.activeSpaceId != null ? '?spaceId=${controller.activeSpaceId}' : ''}',
|
||||
);
|
||||
// Pangea#
|
||||
break;
|
||||
case ActiveFilter.spaces:
|
||||
// #Pangea
|
||||
// createNewSpace();
|
||||
context.go('/rooms/newspace');
|
||||
// Pangea#
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IconData get icon {
|
||||
// #Pangea
|
||||
if (controller.activeSpaceId != null) {
|
||||
return Icons.group_add_outlined;
|
||||
}
|
||||
// Pangea#
|
||||
switch (activeFilter) {
|
||||
case ActiveFilter.allChats:
|
||||
case ActiveFilter.messages:
|
||||
// #Pangea
|
||||
// return Icons.add_outlined;
|
||||
// Pangea#
|
||||
case ActiveFilter.groups:
|
||||
return Icons.group_add_outlined;
|
||||
case ActiveFilter.spaces:
|
||||
return Icons.workspaces_outlined;
|
||||
}
|
||||
}
|
||||
|
||||
String getLabel(BuildContext context) {
|
||||
// #Pangea
|
||||
if (controller.activeSpaceId != null) {
|
||||
return L10n.of(context)!.newGroup;
|
||||
}
|
||||
// Pangea#
|
||||
switch (activeFilter) {
|
||||
case ActiveFilter.allChats:
|
||||
case ActiveFilter.messages:
|
||||
return roomsIsEmpty
|
||||
? L10n.of(context)!.startFirstChat
|
||||
: L10n.of(context)!.newChat;
|
||||
case ActiveFilter.groups:
|
||||
return L10n.of(context)!.newGroup;
|
||||
case ActiveFilter.spaces:
|
||||
return L10n.of(context)!.newSpace;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: scrolledToTop,
|
||||
builder: (context, scrolledToTop, _) => AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
clipBehavior: Clip.none,
|
||||
child: scrolledToTop
|
||||
? FloatingActionButton.extended(
|
||||
onPressed: () => _onPressed(context),
|
||||
icon: Icon(icon),
|
||||
label: Text(
|
||||
getLabel(context),
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
)
|
||||
: FloatingActionButton(
|
||||
onPressed: () => _onPressed(context),
|
||||
child: Icon(icon),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:fluffychat/pages/chat/send_file_dialog.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/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 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
void onChatTap(Room room, BuildContext context) async {
|
||||
if (room.membership == Membership.invite) {
|
||||
final inviterId =
|
||||
room.getState(EventTypes.RoomMember, room.client.userID!)?.senderId;
|
||||
final inviteAction = await showModalActionSheet<InviteActions>(
|
||||
context: context,
|
||||
message: room.isDirectChat
|
||||
? L10n.of(context)!.invitePrivateChat
|
||||
: L10n.of(context)!.inviteGroupChat,
|
||||
title: room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
||||
actions: [
|
||||
SheetAction(
|
||||
key: InviteActions.accept,
|
||||
label: L10n.of(context)!.accept,
|
||||
icon: Icons.check_outlined,
|
||||
isDefaultAction: true,
|
||||
),
|
||||
SheetAction(
|
||||
key: InviteActions.decline,
|
||||
label: L10n.of(context)!.decline,
|
||||
icon: Icons.close_outlined,
|
||||
isDestructiveAction: true,
|
||||
),
|
||||
SheetAction(
|
||||
key: InviteActions.block,
|
||||
label: L10n.of(context)!.block,
|
||||
icon: Icons.block_outlined,
|
||||
isDestructiveAction: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (inviteAction == null) return;
|
||||
if (inviteAction == InviteActions.block) {
|
||||
context.go('/rooms/settings/security/ignorelist', extra: inviterId);
|
||||
return;
|
||||
}
|
||||
if (inviteAction == InviteActions.decline) {
|
||||
// #Pangea
|
||||
if (!room.isSpace &&
|
||||
room.membership == Membership.join &&
|
||||
room.isUnread) {
|
||||
await room.markUnread(false);
|
||||
}
|
||||
// Pangea#
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.leave,
|
||||
);
|
||||
return;
|
||||
}
|
||||
final joinResult = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async {
|
||||
final waitForRoom = room.client.waitForRoomInSync(
|
||||
room.id,
|
||||
join: true,
|
||||
);
|
||||
if (await room.leaveIfFull()) {
|
||||
throw L10n.of(context)!.roomFull;
|
||||
}
|
||||
await room.join();
|
||||
await waitForRoom;
|
||||
},
|
||||
);
|
||||
if (joinResult.error != null) return;
|
||||
}
|
||||
|
||||
if (room.membership == Membership.ban) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(L10n.of(context)!.youHaveBeenBannedFromThisChat),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (room.membership == Membership.leave) {
|
||||
context.go('/rooms/archive/${room.id}');
|
||||
return;
|
||||
}
|
||||
|
||||
// Share content into this room
|
||||
final shareContent = Matrix.of(context).shareContent;
|
||||
if (shareContent != null) {
|
||||
final shareFile = shareContent.tryGet<MatrixFile>('file');
|
||||
if (shareContent.tryGet<String>('msgtype') == 'chat.fluffy.shared_file' &&
|
||||
shareFile != null) {
|
||||
await showDialog(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (c) => SendFileDialog(
|
||||
files: [shareFile],
|
||||
room: room,
|
||||
),
|
||||
);
|
||||
Matrix.of(context).shareContent = null;
|
||||
} else {
|
||||
final consent = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context)!.forward,
|
||||
message: L10n.of(context)!.forwardMessageTo(
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
|
||||
),
|
||||
okLabel: L10n.of(context)!.forward,
|
||||
cancelLabel: L10n.of(context)!.cancel,
|
||||
);
|
||||
if (consent == OkCancelResult.cancel) {
|
||||
Matrix.of(context).shareContent = null;
|
||||
return;
|
||||
}
|
||||
if (consent == OkCancelResult.ok) {
|
||||
room.sendEvent(shareContent);
|
||||
Matrix.of(context).shareContent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.go('/rooms/${room.id}');
|
||||
}
|
||||
|
||||
enum InviteActions {
|
||||
accept,
|
||||
decline,
|
||||
block,
|
||||
}
|
||||
|
|
@ -185,12 +185,6 @@ class SettingsStyleView extends StatelessWidget {
|
|||
storeKey: SettingKeys.showPresences,
|
||||
defaultValue: AppConfig.showPresences,
|
||||
),
|
||||
SettingsSwitchListTile.adaptive(
|
||||
title: L10n.of(context)!.separateChatTypes,
|
||||
onChanged: (b) => AppConfig.separateChatTypes = b,
|
||||
storeKey: SettingKeys.separateChatTypes,
|
||||
defaultValue: AppConfig.separateChatTypes,
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../../../../utils/date_time_extension.dart';
|
||||
import '../../../widgets/avatar.dart';
|
||||
|
|
@ -84,8 +82,7 @@ class AnalyticsListTileState extends State<AnalyticsListTile> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Room? room =
|
||||
Matrix.of(context).client.getRoomById(widget.selected.id);
|
||||
final room = Matrix.of(context).client.getRoomById(widget.selected.id);
|
||||
return Material(
|
||||
color: widget.isSelected
|
||||
? Theme.of(context).colorScheme.secondaryContainer
|
||||
|
|
@ -101,7 +98,6 @@ class AnalyticsListTileState extends State<AnalyticsListTile> {
|
|||
: Avatar(
|
||||
mxContent: widget.avatar,
|
||||
name: widget.selected.displayName,
|
||||
littleIcon: room?.roomTypeIcon,
|
||||
),
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list_header.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// A wrapper around ChatListHeader to allow rebuilding on state changes.
|
||||
/// Prevents having to rebuild the entire ChatList when a single item changes.
|
||||
class ChatListHeaderWrapper extends StatefulWidget {
|
||||
final ChatListController controller;
|
||||
final bool globalSearch;
|
||||
|
||||
const ChatListHeaderWrapper({
|
||||
super.key,
|
||||
required this.controller,
|
||||
this.globalSearch = true,
|
||||
});
|
||||
|
||||
@override
|
||||
ChatListHeaderWrapperState createState() => ChatListHeaderWrapperState();
|
||||
}
|
||||
|
||||
class ChatListHeaderWrapperState extends State<ChatListHeaderWrapper> {
|
||||
StreamSubscription? stateSub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
stateSub = widget.controller.selectionsStream.stream.listen((roomID) {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
stateSub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChatListHeader(
|
||||
controller: widget.controller,
|
||||
globalSearch: widget.globalSearch,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
|
||||
import 'package:fluffychat/pages/chat_list/utils/on_chat_tap.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
/// A wrapper around ChatListItem to allow rebuilding on state changes.
|
||||
/// Prevents having to rebuild the entire ChatList when a single item changes.
|
||||
class ChatListItemWrapper extends StatefulWidget {
|
||||
final Room room;
|
||||
final bool activeChat;
|
||||
final void Function()? onForget;
|
||||
final String? filter;
|
||||
final ChatListController controller;
|
||||
|
||||
final void Function()? onLongPress;
|
||||
final void Function()? onTap;
|
||||
|
||||
const ChatListItemWrapper(
|
||||
this.room, {
|
||||
this.activeChat = false,
|
||||
this.onForget,
|
||||
this.filter,
|
||||
required this.controller,
|
||||
this.onLongPress,
|
||||
this.onTap,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
ChatListItemWrapperState createState() => ChatListItemWrapperState();
|
||||
}
|
||||
|
||||
class ChatListItemWrapperState extends State<ChatListItemWrapper> {
|
||||
StreamSubscription? stateSub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
stateSub = widget.controller.selectionsStream.stream.listen((roomID) {
|
||||
if (roomID == widget.room.id) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
stateSub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChatListItem(
|
||||
widget.room,
|
||||
activeChat: widget.activeChat,
|
||||
selected: widget.controller.selectedRoomIds.contains(widget.room.id),
|
||||
onTap: widget.onTap ??
|
||||
(widget.controller.selectMode == SelectMode.select
|
||||
? () => widget.controller.toggleSelection(widget.room.id)
|
||||
: () => onChatTap(widget.room, context)),
|
||||
onLongPress: widget.onLongPress ??
|
||||
() => widget.controller.toggleSelection(widget.room.id),
|
||||
onForget: widget.onForget,
|
||||
filter: widget.filter,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:fluffychat/utils/string_color.dart';
|
||||
import 'package:fluffychat/widgets/mxc_image.dart';
|
||||
import 'package:fluffychat/widgets/presence_builder.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
|
|
@ -12,9 +13,8 @@ class Avatar extends StatelessWidget {
|
|||
final Client? client;
|
||||
final String? presenceUserId;
|
||||
final Color? presenceBackgroundColor;
|
||||
//#Pangea
|
||||
final IconData? littleIcon;
|
||||
// Pangea#
|
||||
final BorderRadius? borderRadius;
|
||||
final IconData? icon;
|
||||
|
||||
const Avatar({
|
||||
this.mxContent,
|
||||
|
|
@ -24,9 +24,8 @@ class Avatar extends StatelessWidget {
|
|||
this.client,
|
||||
this.presenceUserId,
|
||||
this.presenceBackgroundColor,
|
||||
//#Pangea
|
||||
this.littleIcon,
|
||||
// Pangea#
|
||||
this.borderRadius,
|
||||
this.icon,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -53,18 +52,25 @@ class Avatar extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
);
|
||||
final borderRadius = BorderRadius.circular(size / 2);
|
||||
final borderRadius = this.borderRadius ?? BorderRadius.circular(size / 2);
|
||||
final presenceUserId = this.presenceUserId;
|
||||
final color =
|
||||
noPic ? name?.lightColorAvatar : Theme.of(context).secondaryHeaderColor;
|
||||
final container = Stack(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: borderRadius,
|
||||
child: Container(
|
||||
width: size,
|
||||
height: size,
|
||||
SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: Material(
|
||||
color: color,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: borderRadius,
|
||||
side: BorderSide(
|
||||
width: 0,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: noPic
|
||||
? textWidget
|
||||
: MxcImage(
|
||||
|
|
@ -78,71 +84,49 @@ class Avatar extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
if (littleIcon != null)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: ClipRRect(
|
||||
borderRadius: borderRadius,
|
||||
child: Container(
|
||||
height: 16,
|
||||
width: 16,
|
||||
color: Colors.white,
|
||||
child: Icon(
|
||||
littleIcon,
|
||||
color: noPic
|
||||
? name?.lightColorAvatar
|
||||
: Theme.of(context).secondaryHeaderColor,
|
||||
size: 14,
|
||||
if (presenceUserId != null)
|
||||
PresenceBuilder(
|
||||
client: client,
|
||||
userId: presenceUserId,
|
||||
builder: (context, presence) {
|
||||
if (presence == null ||
|
||||
(presence.presence == PresenceType.offline &&
|
||||
presence.lastActiveTimestamp == null)) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final dotColor = presence.presence.isOnline
|
||||
? Colors.green
|
||||
: presence.presence.isUnavailable
|
||||
? Colors.orange
|
||||
: Colors.grey;
|
||||
return Positioned(
|
||||
bottom: -3,
|
||||
right: -3,
|
||||
child: Container(
|
||||
width: 16,
|
||||
height: 16,
|
||||
decoration: BoxDecoration(
|
||||
color: presenceBackgroundColor ??
|
||||
Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Container(
|
||||
width: 10,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: dotColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
// #Pangea
|
||||
// PresenceBuilder(
|
||||
// client: client,
|
||||
// userId: presenceUserId,
|
||||
// builder: (context, presence) {
|
||||
// if (presence == null ||
|
||||
// (presence.presence == PresenceType.offline &&
|
||||
// presence.lastActiveTimestamp == null)) {
|
||||
// return const SizedBox.shrink();
|
||||
// }
|
||||
// final dotColor = presence.presence.isOnline
|
||||
// ? Colors.green
|
||||
// : presence.presence.isUnavailable
|
||||
// ? Colors.orange
|
||||
// : Colors.grey;
|
||||
// return Positioned(
|
||||
// bottom: -3,
|
||||
// right: -3,
|
||||
// child: Container(
|
||||
// width: 16,
|
||||
// height: 16,
|
||||
// decoration: BoxDecoration(
|
||||
// color: presenceBackgroundColor ??
|
||||
// Theme.of(context).colorScheme.surface,
|
||||
// borderRadius: BorderRadius.circular(32),
|
||||
// ),
|
||||
// alignment: Alignment.center,
|
||||
// child: Container(
|
||||
// width: 10,
|
||||
// height: 10,
|
||||
// decoration: BoxDecoration(
|
||||
// color: dotColor,
|
||||
// borderRadius: BorderRadius.circular(16),
|
||||
// border: Border.all(
|
||||
// width: 1,
|
||||
// color: Theme.of(context).colorScheme.surface,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// Pangea#
|
||||
],
|
||||
);
|
||||
if (onTap == null) return container;
|
||||
|
|
|
|||
|
|
@ -3,13 +3,11 @@ import 'package:flutter/material.dart';
|
|||
class TwoColumnLayout extends StatelessWidget {
|
||||
final Widget mainView;
|
||||
final Widget sideView;
|
||||
final bool displayNavigationRail;
|
||||
|
||||
const TwoColumnLayout({
|
||||
super.key,
|
||||
required this.mainView,
|
||||
required this.sideView,
|
||||
required this.displayNavigationRail,
|
||||
});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -20,7 +18,7 @@ class TwoColumnLayout extends StatelessWidget {
|
|||
Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: const BoxDecoration(),
|
||||
width: 360.0 + (displayNavigationRail ? 64 : 0),
|
||||
width: 384.0,
|
||||
child: mainView,
|
||||
),
|
||||
Container(
|
||||
|
|
|
|||
|
|
@ -485,10 +485,6 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
|
|||
store.getBool(SettingKeys.hideUnimportantStateEvents) ??
|
||||
AppConfig.hideUnimportantStateEvents;
|
||||
|
||||
AppConfig.separateChatTypes =
|
||||
store.getBool(SettingKeys.separateChatTypes) ??
|
||||
AppConfig.separateChatTypes;
|
||||
|
||||
AppConfig.autoplayImages =
|
||||
store.getBool(SettingKeys.autoplayImages) ?? AppConfig.autoplayImages;
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ static void my_application_activate(GApplication* application) {
|
|||
gtk_window_set_title(window, "FluffyChat");
|
||||
}
|
||||
|
||||
gtk_window_set_default_size(window, 864, 680);
|
||||
gtk_window_set_default_size(window, 800, 600);
|
||||
gtk_widget_show(GTK_WIDGET(window));
|
||||
|
||||
g_autoptr(FlDartProject) project = fl_dart_project_new();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue