mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-05 19:49:29 +01:00
style: Use SliverList for chatlist
This commit is contained in:
parent
dd4f2fcc35
commit
20c1dbd00a
@ -16,7 +16,7 @@ abstract class AppConfig {
|
|||||||
static const double messageFontSize = 15.75;
|
static const double messageFontSize = 15.75;
|
||||||
static const bool allowOtherHomeservers = true;
|
static const bool allowOtherHomeservers = true;
|
||||||
static const bool enableRegistration = true;
|
static const bool enableRegistration = true;
|
||||||
static const Color primaryColor = Color.fromARGB(255, 135, 103, 172);
|
static const Color primaryColor = Color(0xFF5625BA);
|
||||||
static const Color primaryColorLight = Color(0xFFCCBDEA);
|
static const Color primaryColorLight = Color(0xFFCCBDEA);
|
||||||
static const Color secondaryColor = Color(0xFF41a2bc);
|
static const Color secondaryColor = Color(0xFF41a2bc);
|
||||||
static String _privacyUrl =
|
static String _privacyUrl =
|
||||||
|
@ -8,6 +8,7 @@ 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/chat_list_item.dart';
|
||||||
import 'package:fluffychat/pages/chat_list/search_title.dart';
|
import 'package:fluffychat/pages/chat_list/search_title.dart';
|
||||||
import 'package:fluffychat/pages/chat_list/space_view.dart';
|
import 'package:fluffychat/pages/chat_list/space_view.dart';
|
||||||
|
import 'package:fluffychat/pages/chat_list/start_chat_fab.dart';
|
||||||
import 'package:fluffychat/pages/chat_list/stories_header.dart';
|
import 'package:fluffychat/pages/chat_list/stories_header.dart';
|
||||||
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
|
||||||
import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart';
|
import 'package:fluffychat/utils/matrix_sdk_extensions/client_stories_extension.dart';
|
||||||
@ -19,6 +20,7 @@ import 'package:fluffychat/widgets/public_room_bottom_sheet.dart';
|
|||||||
import '../../config/themes.dart';
|
import '../../config/themes.dart';
|
||||||
import '../../widgets/connection_status_header.dart';
|
import '../../widgets/connection_status_header.dart';
|
||||||
import '../../widgets/matrix.dart';
|
import '../../widgets/matrix.dart';
|
||||||
|
import 'chat_list_header.dart';
|
||||||
|
|
||||||
class ChatListViewBody extends StatelessWidget {
|
class ChatListViewBody extends StatelessWidget {
|
||||||
final ChatListController controller;
|
final ChatListController controller;
|
||||||
@ -70,15 +72,13 @@ class ChatListViewBody extends StatelessWidget {
|
|||||||
ActiveFilter.messages,
|
ActiveFilter.messages,
|
||||||
}.contains(controller.activeFilter) &&
|
}.contains(controller.activeFilter) &&
|
||||||
client.storiesRooms.isNotEmpty;
|
client.storiesRooms.isNotEmpty;
|
||||||
return ListView.builder(
|
return CustomScrollView(
|
||||||
controller: controller.scrollController,
|
controller: controller.scrollController,
|
||||||
// add +1 space below in order to properly scroll below the spaces bar
|
slivers: [
|
||||||
itemCount: rooms.length + 1,
|
ChatListHeader(controller: controller),
|
||||||
itemBuilder: (BuildContext context, int i) {
|
SliverList(
|
||||||
if (i == 0) {
|
delegate: SliverChildListDelegate(
|
||||||
return Column(
|
[
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
if (controller.isSearchMode) ...[
|
if (controller.isSearchMode) ...[
|
||||||
SearchTitle(
|
SearchTitle(
|
||||||
title: L10n.of(context)!.publicRooms,
|
title: L10n.of(context)!.publicRooms,
|
||||||
@ -187,7 +187,7 @@ class ChatListViewBody extends StatelessWidget {
|
|||||||
title: L10n.of(context)!.chats,
|
title: L10n.of(context)!.chats,
|
||||||
icon: const Icon(Icons.forum_outlined),
|
icon: const Icon(Icons.forum_outlined),
|
||||||
),
|
),
|
||||||
if (rooms.isEmpty && !controller.isSearchMode)
|
if (rooms.isEmpty && !controller.isSearchMode) ...[
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(32.0),
|
padding: const EdgeInsets.all(32.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -201,29 +201,47 @@ class ChatListViewBody extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Center(
|
||||||
|
child: StartChatFloatingActionButton(
|
||||||
|
activeFilter: controller.activeFilter,
|
||||||
|
roomsIsEmpty: true,
|
||||||
|
scrolledToTop: controller.scrolledToTop,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
}
|
),
|
||||||
i--;
|
SliverList(
|
||||||
if (!rooms[i]
|
delegate: SliverChildBuilderDelegate(
|
||||||
.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!))
|
(BuildContext context, int i) {
|
||||||
.toLowerCase()
|
if (!rooms[i]
|
||||||
.contains(
|
.getLocalizedDisplayname(
|
||||||
controller.searchController.text.toLowerCase(),
|
MatrixLocals(L10n.of(context)!),
|
||||||
)) {
|
)
|
||||||
return Container();
|
.toLowerCase()
|
||||||
}
|
.contains(
|
||||||
return ChatListItem(
|
controller.searchController.text.toLowerCase(),
|
||||||
rooms[i],
|
)) {
|
||||||
key: Key('chat_list_item_${rooms[i].id}'),
|
return Container();
|
||||||
selected: controller.selectedRoomIds.contains(rooms[i].id),
|
}
|
||||||
onTap: controller.selectMode == SelectMode.select
|
return ChatListItem(
|
||||||
? () => controller.toggleSelection(rooms[i].id)
|
rooms[i],
|
||||||
: null,
|
key: Key('chat_list_item_${rooms[i].id}'),
|
||||||
onLongPress: () => controller.toggleSelection(rooms[i].id),
|
selected:
|
||||||
activeChat: controller.activeChat == rooms[i].id,
|
controller.selectedRoomIds.contains(rooms[i].id),
|
||||||
);
|
onTap: controller.selectMode == SelectMode.select
|
||||||
},
|
? () => controller.toggleSelection(rooms[i].id)
|
||||||
|
: null,
|
||||||
|
onLongPress: () =>
|
||||||
|
controller.toggleSelection(rooms[i].id),
|
||||||
|
activeChat: controller.activeChat == rooms[i].id,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
childCount: rooms.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const dummyChatCount = 5;
|
const dummyChatCount = 5;
|
||||||
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/config/app_config.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/chat_list.dart';
|
||||||
import 'package:fluffychat/pages/chat_list/client_chooser_button.dart';
|
import 'package:fluffychat/pages/chat_list/client_chooser_button.dart';
|
||||||
import '../../widgets/matrix.dart';
|
import '../../widgets/matrix.dart';
|
||||||
@ -16,7 +17,12 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final selectMode = controller.selectMode;
|
final selectMode = controller.selectMode;
|
||||||
|
|
||||||
return AppBar(
|
return SliverAppBar(
|
||||||
|
floating: true,
|
||||||
|
pinned: FluffyThemes.isColumnMode(context),
|
||||||
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
leading: selectMode == SelectMode.normal
|
leading: selectMode == SelectMode.normal
|
||||||
? null
|
? null
|
||||||
@ -38,65 +44,70 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
)
|
)
|
||||||
: SizedBox(
|
: SizedBox(
|
||||||
height: 44,
|
height: 44,
|
||||||
child: TextField(
|
child: Material(
|
||||||
controller: controller.searchController,
|
elevation:
|
||||||
textInputAction: TextInputAction.search,
|
Theme.of(context).appBarTheme.scrolledUnderElevation ??
|
||||||
onChanged: controller.onSearchEnter,
|
4,
|
||||||
decoration: InputDecoration(
|
shadowColor: Theme.of(context).appBarTheme.shadowColor,
|
||||||
fillColor: Theme.of(context)
|
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||||
.colorScheme
|
child: TextField(
|
||||||
.secondaryContainer
|
controller: controller.searchController,
|
||||||
.withAlpha(128),
|
textInputAction: TextInputAction.search,
|
||||||
border: UnderlineInputBorder(
|
onChanged: controller.onSearchEnter,
|
||||||
borderSide: BorderSide.none,
|
decoration: InputDecoration(
|
||||||
borderRadius:
|
border: UnderlineInputBorder(
|
||||||
BorderRadius.circular(AppConfig.borderRadius),
|
borderSide: BorderSide.none,
|
||||||
),
|
borderRadius:
|
||||||
hintText: L10n.of(context)!.search,
|
BorderRadius.circular(AppConfig.borderRadius),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
),
|
||||||
prefixIcon: controller.isSearchMode
|
hintText: L10n.of(context)!.search,
|
||||||
? IconButton(
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
tooltip: L10n.of(context)!.cancel,
|
prefixIcon: controller.isSearchMode
|
||||||
icon: const Icon(Icons.close_outlined),
|
? IconButton(
|
||||||
onPressed: controller.cancelSearch,
|
tooltip: L10n.of(context)!.cancel,
|
||||||
color: Theme.of(context).colorScheme.onBackground,
|
icon: const Icon(Icons.close_outlined),
|
||||||
)
|
onPressed: controller.cancelSearch,
|
||||||
: Icon(
|
color:
|
||||||
Icons.search_outlined,
|
Theme.of(context).colorScheme.onBackground,
|
||||||
color: Theme.of(context).colorScheme.onBackground,
|
)
|
||||||
),
|
: Icon(
|
||||||
suffixIcon: controller.isSearchMode
|
Icons.search_outlined,
|
||||||
? controller.isSearching
|
color:
|
||||||
? const Padding(
|
Theme.of(context).colorScheme.onBackground,
|
||||||
padding: EdgeInsets.symmetric(
|
),
|
||||||
vertical: 10.0,
|
suffixIcon: controller.isSearchMode
|
||||||
horizontal: 12,
|
? controller.isSearching
|
||||||
),
|
? const Padding(
|
||||||
child: SizedBox.square(
|
padding: EdgeInsets.symmetric(
|
||||||
dimension: 24,
|
vertical: 10.0,
|
||||||
child: CircularProgressIndicator.adaptive(
|
horizontal: 12,
|
||||||
strokeWidth: 2,
|
|
||||||
),
|
),
|
||||||
),
|
child: SizedBox.square(
|
||||||
)
|
dimension: 24,
|
||||||
: TextButton(
|
child: CircularProgressIndicator.adaptive(
|
||||||
onPressed: controller.setServer,
|
strokeWidth: 2,
|
||||||
style: TextButton.styleFrom(
|
),
|
||||||
textStyle: const TextStyle(fontSize: 12),
|
),
|
||||||
),
|
)
|
||||||
child: Text(
|
: TextButton(
|
||||||
controller.searchServer ??
|
onPressed: controller.setServer,
|
||||||
Matrix.of(context)
|
style: TextButton.styleFrom(
|
||||||
.client
|
textStyle: const TextStyle(fontSize: 12),
|
||||||
.homeserver!
|
),
|
||||||
.host,
|
child: Text(
|
||||||
maxLines: 2,
|
controller.searchServer ??
|
||||||
),
|
Matrix.of(context)
|
||||||
)
|
.client
|
||||||
: SizedBox(
|
.homeserver!
|
||||||
width: 0,
|
.host,
|
||||||
child: ClientChooserButton(controller),
|
maxLines: 2,
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
width: 0,
|
||||||
|
child: ClientChooserButton(controller),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -15,7 +15,6 @@ import 'package:fluffychat/widgets/avatar.dart';
|
|||||||
import 'package:fluffychat/widgets/unread_rooms_badge.dart';
|
import 'package:fluffychat/widgets/unread_rooms_badge.dart';
|
||||||
import '../../widgets/matrix.dart';
|
import '../../widgets/matrix.dart';
|
||||||
import 'chat_list_body.dart';
|
import 'chat_list_body.dart';
|
||||||
import 'chat_list_header.dart';
|
|
||||||
import 'start_chat_fab.dart';
|
import 'start_chat_fab.dart';
|
||||||
|
|
||||||
class ChatListView extends StatelessWidget {
|
class ChatListView extends StatelessWidget {
|
||||||
@ -46,12 +45,12 @@ class ChatListView extends StatelessWidget {
|
|||||||
icon: UnreadRoomsBadge(
|
icon: UnreadRoomsBadge(
|
||||||
badgePosition: badgePosition,
|
badgePosition: badgePosition,
|
||||||
filter: controller.getRoomFilterByActiveFilter(ActiveFilter.groups),
|
filter: controller.getRoomFilterByActiveFilter(ActiveFilter.groups),
|
||||||
child: const Icon(Icons.groups_outlined),
|
child: const Icon(Icons.group_outlined),
|
||||||
),
|
),
|
||||||
selectedIcon: UnreadRoomsBadge(
|
selectedIcon: UnreadRoomsBadge(
|
||||||
badgePosition: badgePosition,
|
badgePosition: badgePosition,
|
||||||
filter: controller.getRoomFilterByActiveFilter(ActiveFilter.groups),
|
filter: controller.getRoomFilterByActiveFilter(ActiveFilter.groups),
|
||||||
child: const Icon(Icons.groups),
|
child: const Icon(Icons.group),
|
||||||
),
|
),
|
||||||
label: L10n.of(context)!.groups,
|
label: L10n.of(context)!.groups,
|
||||||
),
|
),
|
||||||
@ -174,7 +173,6 @@ class ChatListView extends StatelessWidget {
|
|||||||
excludeFromSemantics: true,
|
excludeFromSemantics: true,
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: ChatListHeader(controller: controller),
|
|
||||||
body: ChatListViewBody(controller),
|
body: ChatListViewBody(controller),
|
||||||
bottomNavigationBar: controller.displayNavigationBar
|
bottomNavigationBar: controller.displayNavigationBar
|
||||||
? NavigationBar(
|
? NavigationBar(
|
||||||
@ -185,24 +183,24 @@ class ChatListView extends StatelessWidget {
|
|||||||
destinations: getNavigationDestinations(context),
|
destinations: getNavigationDestinations(context),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
floatingActionButtonLocation:
|
floatingActionButton: KeyBoardShortcuts(
|
||||||
controller.filteredRooms.isEmpty
|
keysToPress: {
|
||||||
? FloatingActionButtonLocation.centerFloat
|
LogicalKeyboardKey.controlLeft,
|
||||||
: null,
|
LogicalKeyboardKey.keyN
|
||||||
floatingActionButton: selectMode == SelectMode.normal
|
},
|
||||||
? KeyBoardShortcuts(
|
onKeysPressed: () =>
|
||||||
keysToPress: {
|
VRouter.of(context).to('/newprivatechat'),
|
||||||
LogicalKeyboardKey.controlLeft,
|
helpLabel: L10n.of(context)!.newChat,
|
||||||
LogicalKeyboardKey.keyN
|
child: selectMode == SelectMode.normal &&
|
||||||
},
|
controller.filteredRooms.isNotEmpty &&
|
||||||
onKeysPressed: () =>
|
!controller.isSearchMode
|
||||||
VRouter.of(context).to('/newprivatechat'),
|
? StartChatFloatingActionButton(
|
||||||
helpLabel: L10n.of(context)!.newChat,
|
activeFilter: controller.activeFilter,
|
||||||
child: StartChatFloatingActionButton(
|
roomsIsEmpty: false,
|
||||||
controller: controller,
|
scrolledToTop: controller.scrolledToTop,
|
||||||
),
|
)
|
||||||
)
|
: const SizedBox.shrink(),
|
||||||
: null,
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -14,6 +14,7 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
|||||||
import 'package:fluffychat/widgets/avatar.dart';
|
import 'package:fluffychat/widgets/avatar.dart';
|
||||||
import '../../utils/localized_exception_extension.dart';
|
import '../../utils/localized_exception_extension.dart';
|
||||||
import '../../widgets/matrix.dart';
|
import '../../widgets/matrix.dart';
|
||||||
|
import 'chat_list_header.dart';
|
||||||
|
|
||||||
class SpaceView extends StatefulWidget {
|
class SpaceView extends StatefulWidget {
|
||||||
final ChatListController controller;
|
final ChatListController controller;
|
||||||
@ -154,36 +155,44 @@ class _SpaceViewState extends State<SpaceView> {
|
|||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return ListView.builder(
|
return CustomScrollView(
|
||||||
itemCount: rootSpaces.length,
|
|
||||||
controller: widget.scrollController,
|
controller: widget.scrollController,
|
||||||
itemBuilder: (context, i) {
|
slivers: [
|
||||||
final rootSpace = rootSpaces[i];
|
ChatListHeader(controller: widget.controller),
|
||||||
final displayname = rootSpace.getLocalizedDisplayname(
|
SliverList(
|
||||||
MatrixLocals(L10n.of(context)!),
|
delegate: SliverChildBuilderDelegate(
|
||||||
);
|
(context, i) {
|
||||||
return Material(
|
final rootSpace = rootSpaces[i];
|
||||||
color: Theme.of(context).colorScheme.background,
|
final displayname = rootSpace.getLocalizedDisplayname(
|
||||||
child: ListTile(
|
MatrixLocals(L10n.of(context)!),
|
||||||
leading: Avatar(
|
);
|
||||||
mxContent: rootSpace.avatar,
|
return Material(
|
||||||
name: displayname,
|
color: Theme.of(context).colorScheme.background,
|
||||||
),
|
child: ListTile(
|
||||||
title: Text(
|
leading: Avatar(
|
||||||
displayname,
|
mxContent: rootSpace.avatar,
|
||||||
maxLines: 1,
|
name: displayname,
|
||||||
overflow: TextOverflow.ellipsis,
|
),
|
||||||
),
|
title: Text(
|
||||||
subtitle: Text(
|
displayname,
|
||||||
L10n.of(context)!
|
maxLines: 1,
|
||||||
.numChats(rootSpace.spaceChildren.length.toString()),
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
onTap: () => widget.controller.setActiveSpace(rootSpace.id),
|
subtitle: Text(
|
||||||
onLongPress: () => _onSpaceChildContextMenu(null, rootSpace),
|
L10n.of(context)!
|
||||||
trailing: const Icon(Icons.chevron_right_outlined),
|
.numChats(rootSpace.spaceChildren.length.toString()),
|
||||||
|
),
|
||||||
|
onTap: () => widget.controller.setActiveSpace(rootSpace.id),
|
||||||
|
onLongPress: () =>
|
||||||
|
_onSpaceChildContextMenu(null, rootSpace),
|
||||||
|
trailing: const Icon(Icons.chevron_right_outlined),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
childCount: rootSpaces.length,
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return FutureBuilder<GetSpaceHierarchyResponse>(
|
return FutureBuilder<GetSpaceHierarchyResponse>(
|
||||||
@ -208,7 +217,16 @@ class _SpaceViewState extends State<SpaceView> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
return const Center(child: CircularProgressIndicator.adaptive());
|
return CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
ChatListHeader(controller: widget.controller),
|
||||||
|
const SliverFillRemaining(
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator.adaptive(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
final parentSpace = allSpaces.firstWhereOrNull(
|
final parentSpace = allSpaces.firstWhereOrNull(
|
||||||
(space) =>
|
(space) =>
|
||||||
@ -224,125 +242,139 @@ class _SpaceViewState extends State<SpaceView> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: ListView.builder(
|
child: CustomScrollView(
|
||||||
itemCount: spaceChildren.length + 1 + (canLoadMore ? 1 : 0),
|
|
||||||
controller: widget.scrollController,
|
controller: widget.scrollController,
|
||||||
itemBuilder: (context, i) {
|
slivers: [
|
||||||
if (i == 0) {
|
ChatListHeader(controller: widget.controller),
|
||||||
return ListTile(
|
SliverList(
|
||||||
leading: BackButton(
|
delegate: SliverChildBuilderDelegate(
|
||||||
onPressed: () =>
|
(context, i) {
|
||||||
widget.controller.setActiveSpace(parentSpace?.id),
|
if (i == 0) {
|
||||||
),
|
return ListTile(
|
||||||
title: Text(
|
leading: BackButton(
|
||||||
parentSpace == null
|
onPressed: () =>
|
||||||
? L10n.of(context)!.allSpaces
|
widget.controller.setActiveSpace(parentSpace?.id),
|
||||||
: parentSpace.getLocalizedDisplayname(
|
),
|
||||||
MatrixLocals(L10n.of(context)!),
|
title: Text(
|
||||||
),
|
parentSpace == null
|
||||||
),
|
? L10n.of(context)!.allSpaces
|
||||||
trailing: IconButton(
|
: parentSpace.getLocalizedDisplayname(
|
||||||
icon: snapshot.connectionState != ConnectionState.done
|
MatrixLocals(L10n.of(context)!),
|
||||||
? const CircularProgressIndicator.adaptive()
|
),
|
||||||
: const Icon(Icons.refresh_outlined),
|
),
|
||||||
onPressed: snapshot.connectionState != ConnectionState.done
|
trailing: IconButton(
|
||||||
|
icon: snapshot.connectionState != ConnectionState.done
|
||||||
|
? const CircularProgressIndicator.adaptive()
|
||||||
|
: const Icon(Icons.refresh_outlined),
|
||||||
|
onPressed:
|
||||||
|
snapshot.connectionState != ConnectionState.done
|
||||||
|
? null
|
||||||
|
: _refresh,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
if (canLoadMore && i == spaceChildren.length) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(L10n.of(context)!.loadMore),
|
||||||
|
trailing: const Icon(Icons.chevron_right_outlined),
|
||||||
|
onTap: () {
|
||||||
|
prevBatch = response.nextBatch;
|
||||||
|
_refresh();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final spaceChild = spaceChildren[i];
|
||||||
|
final room = client.getRoomById(spaceChild.roomId);
|
||||||
|
if (room != null && !room.isSpace) {
|
||||||
|
return ChatListItem(
|
||||||
|
room,
|
||||||
|
onLongPress: () =>
|
||||||
|
_onSpaceChildContextMenu(spaceChild, room),
|
||||||
|
activeChat: widget.controller.activeChat == room.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final isSpace = spaceChild.roomType == 'm.space';
|
||||||
|
final topic = spaceChild.topic?.isEmpty ?? true
|
||||||
? null
|
? null
|
||||||
: _refresh,
|
: spaceChild.topic;
|
||||||
),
|
if (spaceChild.roomId == activeSpaceId) {
|
||||||
);
|
return SearchTitle(
|
||||||
}
|
title: spaceChild.name ??
|
||||||
i--;
|
|
||||||
if (canLoadMore && i == spaceChildren.length) {
|
|
||||||
return ListTile(
|
|
||||||
title: Text(L10n.of(context)!.loadMore),
|
|
||||||
trailing: const Icon(Icons.chevron_right_outlined),
|
|
||||||
onTap: () {
|
|
||||||
prevBatch = response.nextBatch;
|
|
||||||
_refresh();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final spaceChild = spaceChildren[i];
|
|
||||||
final room = client.getRoomById(spaceChild.roomId);
|
|
||||||
if (room != null && !room.isSpace) {
|
|
||||||
return ChatListItem(
|
|
||||||
room,
|
|
||||||
onLongPress: () => _onSpaceChildContextMenu(spaceChild, room),
|
|
||||||
activeChat: widget.controller.activeChat == room.id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final isSpace = spaceChild.roomType == 'm.space';
|
|
||||||
final topic =
|
|
||||||
spaceChild.topic?.isEmpty ?? true ? null : spaceChild.topic;
|
|
||||||
if (spaceChild.roomId == activeSpaceId) {
|
|
||||||
return SearchTitle(
|
|
||||||
title:
|
|
||||||
spaceChild.name ?? spaceChild.canonicalAlias ?? 'Space',
|
|
||||||
icon: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
|
||||||
child: Avatar(
|
|
||||||
size: 24,
|
|
||||||
mxContent: spaceChild.avatarUrl,
|
|
||||||
name: spaceChild.name,
|
|
||||||
fontSize: 9,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.secondaryContainer
|
|
||||||
.withAlpha(128),
|
|
||||||
trailing: const Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16.0),
|
|
||||||
child: Icon(Icons.edit_outlined),
|
|
||||||
),
|
|
||||||
onTap: () => _onJoinSpaceChild(spaceChild),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return ListTile(
|
|
||||||
leading: Avatar(
|
|
||||||
mxContent: spaceChild.avatarUrl,
|
|
||||||
name: spaceChild.name,
|
|
||||||
),
|
|
||||||
title: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
spaceChild.name ??
|
|
||||||
spaceChild.canonicalAlias ??
|
spaceChild.canonicalAlias ??
|
||||||
L10n.of(context)!.chat,
|
'Space',
|
||||||
|
icon: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
|
child: Avatar(
|
||||||
|
size: 24,
|
||||||
|
mxContent: spaceChild.avatarUrl,
|
||||||
|
name: spaceChild.name,
|
||||||
|
fontSize: 9,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.secondaryContainer
|
||||||
|
.withAlpha(128),
|
||||||
|
trailing: const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
child: Icon(Icons.edit_outlined),
|
||||||
|
),
|
||||||
|
onTap: () => _onJoinSpaceChild(spaceChild),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ListTile(
|
||||||
|
leading: Avatar(
|
||||||
|
mxContent: spaceChild.avatarUrl,
|
||||||
|
name: spaceChild.name,
|
||||||
|
),
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
spaceChild.name ??
|
||||||
|
spaceChild.canonicalAlias ??
|
||||||
|
L10n.of(context)!.chat,
|
||||||
|
maxLines: 1,
|
||||||
|
style:
|
||||||
|
const TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!isSpace) ...[
|
||||||
|
const Icon(
|
||||||
|
Icons.people_outline,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
spaceChild.numJoinedMembers.toString(),
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () => _onJoinSpaceChild(spaceChild),
|
||||||
|
onLongPress: () =>
|
||||||
|
_onSpaceChildContextMenu(spaceChild, room),
|
||||||
|
subtitle: Text(
|
||||||
|
topic ??
|
||||||
|
(isSpace
|
||||||
|
? L10n.of(context)!.enterSpace
|
||||||
|
: L10n.of(context)!.enterRoom),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onBackground,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
trailing: isSpace
|
||||||
if (!isSpace) ...[
|
? const Icon(Icons.chevron_right_outlined)
|
||||||
const Icon(
|
: null,
|
||||||
Icons.people_outline,
|
);
|
||||||
size: 16,
|
},
|
||||||
),
|
childCount: spaceChildren.length + 1 + (canLoadMore ? 1 : 0),
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
spaceChild.numJoinedMembers.toString(),
|
|
||||||
style: const TextStyle(fontSize: 14),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
onTap: () => _onJoinSpaceChild(spaceChild),
|
),
|
||||||
onLongPress: () => _onSpaceChildContextMenu(spaceChild, room),
|
],
|
||||||
subtitle: Text(
|
|
||||||
topic ??
|
|
||||||
(isSpace
|
|
||||||
? L10n.of(context)!.enterSpace
|
|
||||||
: L10n.of(context)!.enterRoom),
|
|
||||||
maxLines: 1,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.onBackground,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
trailing:
|
|
||||||
isSpace ? const Icon(Icons.chevron_right_outlined) : null,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -7,13 +7,19 @@ import '../../config/themes.dart';
|
|||||||
import 'chat_list.dart';
|
import 'chat_list.dart';
|
||||||
|
|
||||||
class StartChatFloatingActionButton extends StatelessWidget {
|
class StartChatFloatingActionButton extends StatelessWidget {
|
||||||
final ChatListController controller;
|
final ActiveFilter activeFilter;
|
||||||
|
final bool scrolledToTop;
|
||||||
|
final bool roomsIsEmpty;
|
||||||
|
|
||||||
const StartChatFloatingActionButton({Key? key, required this.controller})
|
const StartChatFloatingActionButton({
|
||||||
: super(key: key);
|
Key? key,
|
||||||
|
required this.activeFilter,
|
||||||
|
required this.scrolledToTop,
|
||||||
|
required this.roomsIsEmpty,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
void _onPressed(BuildContext context) {
|
void _onPressed(BuildContext context) {
|
||||||
switch (controller.activeFilter) {
|
switch (activeFilter) {
|
||||||
case ActiveFilter.allChats:
|
case ActiveFilter.allChats:
|
||||||
case ActiveFilter.messages:
|
case ActiveFilter.messages:
|
||||||
VRouter.of(context).to('/newprivatechat');
|
VRouter.of(context).to('/newprivatechat');
|
||||||
@ -28,10 +34,10 @@ class StartChatFloatingActionButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
IconData get icon {
|
IconData get icon {
|
||||||
switch (controller.activeFilter) {
|
switch (activeFilter) {
|
||||||
case ActiveFilter.allChats:
|
case ActiveFilter.allChats:
|
||||||
case ActiveFilter.messages:
|
case ActiveFilter.messages:
|
||||||
return Icons.edit_outlined;
|
return Icons.add_outlined;
|
||||||
case ActiveFilter.groups:
|
case ActiveFilter.groups:
|
||||||
return Icons.group_add_outlined;
|
return Icons.group_add_outlined;
|
||||||
case ActiveFilter.spaces:
|
case ActiveFilter.spaces:
|
||||||
@ -40,10 +46,10 @@ class StartChatFloatingActionButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String getLabel(BuildContext context) {
|
String getLabel(BuildContext context) {
|
||||||
switch (controller.activeFilter) {
|
switch (activeFilter) {
|
||||||
case ActiveFilter.allChats:
|
case ActiveFilter.allChats:
|
||||||
case ActiveFilter.messages:
|
case ActiveFilter.messages:
|
||||||
return controller.filteredRooms.isEmpty
|
return roomsIsEmpty
|
||||||
? L10n.of(context)!.startFirstChat
|
? L10n.of(context)!.startFirstChat
|
||||||
: L10n.of(context)!.newChat;
|
: L10n.of(context)!.newChat;
|
||||||
case ActiveFilter.groups:
|
case ActiveFilter.groups:
|
||||||
@ -58,15 +64,13 @@ class StartChatFloatingActionButton extends StatelessWidget {
|
|||||||
return AnimatedContainer(
|
return AnimatedContainer(
|
||||||
duration: FluffyThemes.animationDuration,
|
duration: FluffyThemes.animationDuration,
|
||||||
curve: FluffyThemes.animationCurve,
|
curve: FluffyThemes.animationCurve,
|
||||||
width: controller.filteredRooms.isEmpty
|
width: roomsIsEmpty
|
||||||
? null
|
? null
|
||||||
: controller.scrolledToTop
|
: scrolledToTop
|
||||||
? 144
|
? 144
|
||||||
: 56,
|
: 56,
|
||||||
child: controller.scrolledToTop
|
child: scrolledToTop
|
||||||
? FloatingActionButton.extended(
|
? FloatingActionButton.extended(
|
||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
||||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
|
||||||
onPressed: () => _onPressed(context),
|
onPressed: () => _onPressed(context),
|
||||||
icon: Icon(icon),
|
icon: Icon(icon),
|
||||||
label: Text(
|
label: Text(
|
||||||
@ -75,8 +79,6 @@ class StartChatFloatingActionButton extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: FloatingActionButton(
|
: FloatingActionButton(
|
||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
||||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
|
||||||
onPressed: () => _onPressed(context),
|
onPressed: () => _onPressed(context),
|
||||||
child: Icon(icon),
|
child: Icon(icon),
|
||||||
),
|
),
|
||||||
|
@ -47,11 +47,11 @@ class SettingsStyleController extends State<SettingsStyle> {
|
|||||||
|
|
||||||
static final List<Color?> customColors = [
|
static final List<Color?> customColors = [
|
||||||
AppConfig.chatColor,
|
AppConfig.chatColor,
|
||||||
Colors.blue.shade800,
|
Colors.indigo,
|
||||||
Colors.green.shade800,
|
Colors.green,
|
||||||
Colors.orange.shade700,
|
Colors.orange,
|
||||||
Colors.pink.shade700,
|
Colors.pink,
|
||||||
Colors.blueGrey.shade600,
|
Colors.blueGrey,
|
||||||
null,
|
null,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user