mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2025-01-11 18:22:49 +01:00
feat: improve spaces
- support to show spaces in a list - add a beautiful animation This MR makes Spaces much easier to use on desktops and allows to better find the right space in case they have no avatar. There will be another MR builting on this work as soon as https://gitlab.com/famedly/company/frontend/libraries/matrix_api_lite/-/merge_requests/58 is merged. Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
This commit is contained in:
parent
42267f263e
commit
26983a15a8
@ -1311,6 +1311,7 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
},
|
},
|
||||||
|
"showSpaces": "Show spaces list",
|
||||||
"loadMore": "Load more…",
|
"loadMore": "Load more…",
|
||||||
"@loadMore": {
|
"@loadMore": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
@ -8,11 +8,13 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
||||||
|
import 'package:snapping_sheet/snapping_sheet.dart';
|
||||||
import 'package:uni_links/uni_links.dart';
|
import 'package:uni_links/uni_links.dart';
|
||||||
import 'package:vrouter/vrouter.dart';
|
import 'package:vrouter/vrouter.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/config/app_config.dart';
|
import 'package:fluffychat/config/app_config.dart';
|
||||||
import 'package:fluffychat/pages/chat_list/chat_list_view.dart';
|
import 'package:fluffychat/pages/chat_list/chat_list_view.dart';
|
||||||
|
import 'package:fluffychat/pages/chat_list/spaces_bottom_bar.dart';
|
||||||
import 'package:fluffychat/pages/chat_list/spaces_entry.dart';
|
import 'package:fluffychat/pages/chat_list/spaces_entry.dart';
|
||||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||||
import 'package:fluffychat/utils/platform_infos.dart';
|
import 'package:fluffychat/utils/platform_infos.dart';
|
||||||
@ -40,7 +42,7 @@ class ChatList extends StatefulWidget {
|
|||||||
ChatListController createState() => ChatListController();
|
ChatListController createState() => ChatListController();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatListController extends State<ChatList> {
|
class ChatListController extends State<ChatList> with TickerProviderStateMixin {
|
||||||
StreamSubscription? _intentDataStreamSubscription;
|
StreamSubscription? _intentDataStreamSubscription;
|
||||||
|
|
||||||
StreamSubscription? _intentFileStreamSubscription;
|
StreamSubscription? _intentFileStreamSubscription;
|
||||||
@ -54,6 +56,8 @@ class ChatListController extends State<ChatList> {
|
|||||||
return (id == null || !id.stillValid(context)) ? defaultSpacesEntry : id;
|
return (id == null || !id.stillValid(context)) ? defaultSpacesEntry : id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoxConstraints? snappingSheetContainerSize;
|
||||||
|
|
||||||
String? get activeSpaceId => activeSpacesEntry.getSpace(context)?.id;
|
String? get activeSpaceId => activeSpacesEntry.getSpace(context)?.id;
|
||||||
|
|
||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
@ -61,6 +65,10 @@ class ChatListController extends State<ChatList> {
|
|||||||
|
|
||||||
final StreamController<Client> _clientStream = StreamController.broadcast();
|
final StreamController<Client> _clientStream = StreamController.broadcast();
|
||||||
|
|
||||||
|
SnappingSheetController snappingSheetController = SnappingSheetController();
|
||||||
|
|
||||||
|
ScrollController snappingSheetScrollContentController = ScrollController();
|
||||||
|
|
||||||
Stream<Client> get clientStream => _clientStream.stream;
|
Stream<Client> get clientStream => _clientStream.stream;
|
||||||
|
|
||||||
void _onScroll() {
|
void _onScroll() {
|
||||||
@ -72,7 +80,10 @@ class ChatListController extends State<ChatList> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setActiveSpacesEntry(BuildContext context, SpacesEntry spaceId) {
|
void setActiveSpacesEntry(BuildContext context, SpacesEntry? spaceId) {
|
||||||
|
if (snappingSheetController.currentPosition != kSpacesBottomBarHeight) {
|
||||||
|
snapBackSpacesSheet();
|
||||||
|
}
|
||||||
setState(() => _activeSpacesEntry = spaceId);
|
setState(() => _activeSpacesEntry = spaceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,6 +491,8 @@ class ChatListController extends State<ChatList> {
|
|||||||
VRouter.of(context).to('/rooms');
|
VRouter.of(context).to('/rooms');
|
||||||
setState(() {
|
setState(() {
|
||||||
_activeSpacesEntry = null;
|
_activeSpacesEntry = null;
|
||||||
|
snappingSheetController = SnappingSheetController();
|
||||||
|
snappingSheetScrollContentController = ScrollController();
|
||||||
selectedRoomIds.clear();
|
selectedRoomIds.clear();
|
||||||
Matrix.of(context).setActiveClient(client);
|
Matrix.of(context).setActiveClient(client);
|
||||||
});
|
});
|
||||||
@ -575,6 +588,21 @@ class ChatListController extends State<ChatList> {
|
|||||||
void _hackyWebRTCFixForWeb() {
|
void _hackyWebRTCFixForWeb() {
|
||||||
Matrix.of(context).voipPlugin?.context = context;
|
Matrix.of(context).voipPlugin?.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void snapBackSpacesSheet() {
|
||||||
|
snappingSheetController.snapToPosition(
|
||||||
|
const SnappingPosition.pixels(
|
||||||
|
positionPixels: kSpacesBottomBarHeight,
|
||||||
|
snappingDuration: Duration(milliseconds: 500),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
expandSpaces() {
|
||||||
|
snappingSheetController.snapToPosition(
|
||||||
|
const SnappingPosition.factor(positionFactor: 0.5),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EditBundleAction { addToBundle, removeFromBundle }
|
enum EditBundleAction { addToBundle, removeFromBundle }
|
||||||
|
@ -8,6 +8,7 @@ import 'package:animations/animations.dart';
|
|||||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
|
import 'package:keyboard_shortcuts/keyboard_shortcuts.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
|
import 'package:snapping_sheet/snapping_sheet.dart';
|
||||||
import 'package:vrouter/vrouter.dart';
|
import 'package:vrouter/vrouter.dart';
|
||||||
|
|
||||||
import 'package:fluffychat/config/app_config.dart';
|
import 'package:fluffychat/config/app_config.dart';
|
||||||
@ -28,203 +29,240 @@ class ChatListView extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return StreamBuilder<Object?>(
|
return StreamBuilder<Object?>(
|
||||||
stream: Matrix.of(context).onShareContentChanged.stream,
|
stream: Matrix.of(context).onShareContentChanged.stream,
|
||||||
builder: (_, __) {
|
builder: (_, __) {
|
||||||
final selectMode = controller.selectMode;
|
final selectMode = controller.selectMode;
|
||||||
return VWidgetGuard(
|
final showSpaces =
|
||||||
onSystemPop: (redirector) async {
|
controller.spaces.isNotEmpty && controller.selectedRoomIds.isEmpty;
|
||||||
final selMode = controller.selectMode;
|
return VWidgetGuard(
|
||||||
if (selMode != SelectMode.normal) controller.cancelAction();
|
onSystemPop: (redirector) async {
|
||||||
if (selMode == SelectMode.select) redirector.stopRedirection();
|
final selMode = controller.selectMode;
|
||||||
},
|
if (selMode != SelectMode.normal) controller.cancelAction();
|
||||||
child: Scaffold(
|
if (selMode == SelectMode.select) redirector.stopRedirection();
|
||||||
appBar: AppBar(
|
},
|
||||||
elevation: controller.scrolledToTop ? 0 : null,
|
child: Scaffold(
|
||||||
actionsIconTheme: IconThemeData(
|
appBar: AppBar(
|
||||||
color: controller.selectedRoomIds.isEmpty
|
elevation: controller.scrolledToTop ? 0 : null,
|
||||||
? null
|
actionsIconTheme: IconThemeData(
|
||||||
: Theme.of(context).colorScheme.primary,
|
color: controller.selectedRoomIds.isEmpty
|
||||||
),
|
|
||||||
leading: Matrix.of(context).isMultiAccount
|
|
||||||
? ClientChooserButton(controller)
|
|
||||||
: selectMode == SelectMode.normal
|
|
||||||
? null
|
|
||||||
: IconButton(
|
|
||||||
tooltip: L10n.of(context)!.cancel,
|
|
||||||
icon: const Icon(Icons.close_outlined),
|
|
||||||
onPressed: controller.cancelAction,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
centerTitle: false,
|
|
||||||
actions: selectMode == SelectMode.share
|
|
||||||
? null
|
? null
|
||||||
: selectMode == SelectMode.select
|
: Theme.of(context).colorScheme.primary,
|
||||||
? [
|
|
||||||
if (controller.spaces.isNotEmpty)
|
|
||||||
IconButton(
|
|
||||||
tooltip: L10n.of(context)!.addToSpace,
|
|
||||||
icon: const Icon(Icons.group_work_outlined),
|
|
||||||
onPressed: controller.addOrRemoveToSpace,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
tooltip: L10n.of(context)!.toggleUnread,
|
|
||||||
icon: Icon(
|
|
||||||
controller.anySelectedRoomNotMarkedUnread
|
|
||||||
? Icons.mark_chat_read_outlined
|
|
||||||
: Icons.mark_chat_unread_outlined),
|
|
||||||
onPressed: controller.toggleUnread,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
tooltip: L10n.of(context)!.toggleFavorite,
|
|
||||||
icon: Icon(controller.anySelectedRoomNotFavorite
|
|
||||||
? Icons.push_pin_outlined
|
|
||||||
: Icons.push_pin),
|
|
||||||
onPressed: controller.toggleFavouriteRoom,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(controller.anySelectedRoomNotMuted
|
|
||||||
? Icons.notifications_off_outlined
|
|
||||||
: Icons.notifications_outlined),
|
|
||||||
tooltip: L10n.of(context)!.toggleMuted,
|
|
||||||
onPressed: controller.toggleMuted,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.delete_outlined),
|
|
||||||
tooltip: L10n.of(context)!.archive,
|
|
||||||
onPressed: controller.archiveAction,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
KeyBoardShortcuts(
|
|
||||||
keysToPress: {
|
|
||||||
LogicalKeyboardKey.controlLeft,
|
|
||||||
LogicalKeyboardKey.keyF
|
|
||||||
},
|
|
||||||
onKeysPressed: () =>
|
|
||||||
VRouter.of(context).to('/search'),
|
|
||||||
helpLabel: L10n.of(context)!.search,
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(Icons.search_outlined),
|
|
||||||
tooltip: L10n.of(context)!.search,
|
|
||||||
onPressed: () =>
|
|
||||||
VRouter.of(context).to('/search'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (selectMode == SelectMode.normal)
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.camera_alt_outlined),
|
|
||||||
tooltip: L10n.of(context)!.addToStory,
|
|
||||||
onPressed: () =>
|
|
||||||
VRouter.of(context).to('/stories/create'),
|
|
||||||
),
|
|
||||||
PopupMenuButton<PopupMenuAction>(
|
|
||||||
onSelected: controller.onPopupMenuSelect,
|
|
||||||
itemBuilder: (_) => [
|
|
||||||
PopupMenuItem(
|
|
||||||
value: PopupMenuAction.setStatus,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.edit_outlined),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(L10n.of(context)!.setStatus),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: PopupMenuAction.newGroup,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.group_add_outlined),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(L10n.of(context)!.createNewGroup),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: PopupMenuAction.newSpace,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.group_work_outlined),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(L10n.of(context)!.createNewSpace),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: PopupMenuAction.invite,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.share_outlined),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(L10n.of(context)!.inviteContact),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: PopupMenuAction.archive,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.archive_outlined),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(L10n.of(context)!.archive),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: PopupMenuAction.settings,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.settings_outlined),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(L10n.of(context)!.settings),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
title: Text(selectMode == SelectMode.share
|
|
||||||
? L10n.of(context)!.share
|
|
||||||
: selectMode == SelectMode.select
|
|
||||||
? controller.selectedRoomIds.length.toString()
|
|
||||||
: controller.activeSpaceId == null
|
|
||||||
? AppConfig.applicationName
|
|
||||||
: Matrix.of(context)
|
|
||||||
.client
|
|
||||||
.getRoomById(controller.activeSpaceId!)!
|
|
||||||
.displayname),
|
|
||||||
),
|
),
|
||||||
body: Column(children: [
|
leading: Matrix.of(context).isMultiAccount
|
||||||
AnimatedContainer(
|
? ClientChooserButton(controller)
|
||||||
height: controller.showChatBackupBanner ? 54 : 0,
|
: selectMode == SelectMode.normal
|
||||||
duration: const Duration(milliseconds: 300),
|
? null
|
||||||
clipBehavior: Clip.hardEdge,
|
: IconButton(
|
||||||
curve: Curves.bounceInOut,
|
tooltip: L10n.of(context)!.cancel,
|
||||||
decoration: const BoxDecoration(),
|
icon: const Icon(Icons.close_outlined),
|
||||||
child: Material(
|
onPressed: controller.cancelAction,
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
child: ListTile(
|
),
|
||||||
leading: Image.asset(
|
centerTitle: false,
|
||||||
'assets/backup.png',
|
actions: selectMode == SelectMode.share
|
||||||
fit: BoxFit.contain,
|
? null
|
||||||
width: 44,
|
: selectMode == SelectMode.select
|
||||||
|
? [
|
||||||
|
if (controller.spaces.isNotEmpty)
|
||||||
|
IconButton(
|
||||||
|
tooltip: L10n.of(context)!.addToSpace,
|
||||||
|
icon: const Icon(Icons.group_work_outlined),
|
||||||
|
onPressed: controller.addOrRemoveToSpace,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
tooltip: L10n.of(context)!.toggleUnread,
|
||||||
|
icon: Icon(controller.anySelectedRoomNotMarkedUnread
|
||||||
|
? Icons.mark_chat_read_outlined
|
||||||
|
: Icons.mark_chat_unread_outlined),
|
||||||
|
onPressed: controller.toggleUnread,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
tooltip: L10n.of(context)!.toggleFavorite,
|
||||||
|
icon: Icon(controller.anySelectedRoomNotFavorite
|
||||||
|
? Icons.push_pin_outlined
|
||||||
|
: Icons.push_pin),
|
||||||
|
onPressed: controller.toggleFavouriteRoom,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(controller.anySelectedRoomNotMuted
|
||||||
|
? Icons.notifications_off_outlined
|
||||||
|
: Icons.notifications_outlined),
|
||||||
|
tooltip: L10n.of(context)!.toggleMuted,
|
||||||
|
onPressed: controller.toggleMuted,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.delete_outlined),
|
||||||
|
tooltip: L10n.of(context)!.archive,
|
||||||
|
onPressed: controller.archiveAction,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
KeyBoardShortcuts(
|
||||||
|
keysToPress: {
|
||||||
|
LogicalKeyboardKey.controlLeft,
|
||||||
|
LogicalKeyboardKey.keyF
|
||||||
|
},
|
||||||
|
onKeysPressed: () =>
|
||||||
|
VRouter.of(context).to('/search'),
|
||||||
|
helpLabel: L10n.of(context)!.search,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(Icons.search_outlined),
|
||||||
|
tooltip: L10n.of(context)!.search,
|
||||||
|
onPressed: () =>
|
||||||
|
VRouter.of(context).to('/search'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (selectMode == SelectMode.normal)
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.camera_alt_outlined),
|
||||||
|
tooltip: L10n.of(context)!.addToStory,
|
||||||
|
onPressed: () =>
|
||||||
|
VRouter.of(context).to('/stories/create'),
|
||||||
|
),
|
||||||
|
PopupMenuButton<PopupMenuAction>(
|
||||||
|
onSelected: controller.onPopupMenuSelect,
|
||||||
|
itemBuilder: (_) => [
|
||||||
|
PopupMenuItem(
|
||||||
|
value: PopupMenuAction.setStatus,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.edit_outlined),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(L10n.of(context)!.setStatus),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: PopupMenuAction.newGroup,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.group_add_outlined),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(L10n.of(context)!.createNewGroup),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: PopupMenuAction.newSpace,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.group_work_outlined),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(L10n.of(context)!.createNewSpace),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: PopupMenuAction.invite,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.share_outlined),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(L10n.of(context)!.inviteContact),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: PopupMenuAction.archive,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.archive_outlined),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(L10n.of(context)!.archive),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
value: PopupMenuAction.settings,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.settings_outlined),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(L10n.of(context)!.settings),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
title: Text(selectMode == SelectMode.share
|
||||||
|
? L10n.of(context)!.share
|
||||||
|
: selectMode == SelectMode.select
|
||||||
|
? controller.selectedRoomIds.length.toString()
|
||||||
|
: controller.activeSpaceId == null
|
||||||
|
? AppConfig.applicationName
|
||||||
|
: Matrix.of(context)
|
||||||
|
.client
|
||||||
|
.getRoomById(controller.activeSpaceId!)!
|
||||||
|
.displayname),
|
||||||
|
),
|
||||||
|
body: LayoutBuilder(
|
||||||
|
builder: (context, size) {
|
||||||
|
controller.snappingSheetContainerSize = size;
|
||||||
|
return SnappingSheet(
|
||||||
|
key: ValueKey(Matrix.of(context).client.userID.toString() +
|
||||||
|
showSpaces.toString()),
|
||||||
|
controller: controller.snappingSheetController,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
AnimatedContainer(
|
||||||
|
height: controller.showChatBackupBanner ? 54 : 0,
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
curve: Curves.bounceInOut,
|
||||||
|
decoration: const BoxDecoration(),
|
||||||
|
child: Material(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
child: ListTile(
|
||||||
|
leading: Image.asset(
|
||||||
|
'assets/backup.png',
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
width: 44,
|
||||||
|
),
|
||||||
|
title: Text(L10n.of(context)!.setupChatBackupNow),
|
||||||
|
trailing: const Icon(Icons.chevron_right_outlined),
|
||||||
|
onTap: controller.firstRunBootstrapAction,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
title: Text(L10n.of(context)!.setupChatBackupNow),
|
Expanded(child: _ChatListViewBody(controller)),
|
||||||
trailing: const Icon(Icons.chevron_right_outlined),
|
],
|
||||||
onTap: controller.firstRunBootstrapAction,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
initialSnappingPosition: showSpaces
|
||||||
Expanded(child: _ChatListViewBody(controller)),
|
? const SnappingPosition.pixels(
|
||||||
]),
|
positionPixels: kSpacesBottomBarHeight)
|
||||||
floatingActionButton: selectMode == SelectMode.normal
|
: const SnappingPosition.factor(positionFactor: 0.0),
|
||||||
? KeyBoardShortcuts(
|
snappingPositions: showSpaces
|
||||||
|
? const [
|
||||||
|
SnappingPosition.pixels(
|
||||||
|
positionPixels: kSpacesBottomBarHeight),
|
||||||
|
SnappingPosition.factor(positionFactor: 0.5),
|
||||||
|
SnappingPosition.factor(positionFactor: 0.9),
|
||||||
|
]
|
||||||
|
: [const SnappingPosition.factor(positionFactor: 0.0)],
|
||||||
|
sheetBelow: showSpaces
|
||||||
|
? SnappingSheetContent(
|
||||||
|
childScrollController:
|
||||||
|
controller.snappingSheetScrollContentController,
|
||||||
|
draggable: true,
|
||||||
|
child: SpacesBottomBar(controller),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
floatingActionButton: selectMode == SelectMode.normal
|
||||||
|
? Padding(
|
||||||
|
padding: showSpaces
|
||||||
|
? const EdgeInsets.only(bottom: 64.0)
|
||||||
|
: const EdgeInsets.all(0),
|
||||||
|
child: KeyBoardShortcuts(
|
||||||
child: FloatingActionButton.extended(
|
child: FloatingActionButton.extended(
|
||||||
isExtended: controller.scrolledToTop,
|
isExtended: controller.scrolledToTop,
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
@ -239,20 +277,14 @@ class ChatListView extends StatelessWidget {
|
|||||||
onKeysPressed: () =>
|
onKeysPressed: () =>
|
||||||
VRouter.of(context).to('/newprivatechat'),
|
VRouter.of(context).to('/newprivatechat'),
|
||||||
helpLabel: L10n.of(context)!.newChat,
|
helpLabel: L10n.of(context)!.newChat,
|
||||||
)
|
),
|
||||||
: null,
|
)
|
||||||
bottomNavigationBar: Column(
|
: null,
|
||||||
mainAxisSize: MainAxisSize.min,
|
bottomNavigationBar: const ConnectionStatusHeader(),
|
||||||
children: [
|
),
|
||||||
const ConnectionStatusHeader(),
|
);
|
||||||
if (controller.spaces.isNotEmpty &&
|
},
|
||||||
controller.selectedRoomIds.isEmpty)
|
);
|
||||||
SpacesBottomBar(controller),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,53 +1,135 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
import 'package:salomon_bottom_bar/salomon_bottom_bar.dart';
|
import 'package:salomon_bottom_bar/salomon_bottom_bar.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.dart';
|
||||||
|
import 'package:fluffychat/pages/chat_list/spaces_drawer.dart';
|
||||||
import 'package:fluffychat/pages/chat_list/spaces_entry.dart';
|
import 'package:fluffychat/pages/chat_list/spaces_entry.dart';
|
||||||
import 'package:fluffychat/widgets/avatar.dart';
|
import 'package:fluffychat/widgets/avatar.dart';
|
||||||
import 'package:fluffychat/widgets/matrix.dart';
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
|
|
||||||
|
const kSpacesBottomBarHeight = 56.0;
|
||||||
|
|
||||||
class SpacesBottomBar extends StatelessWidget {
|
class SpacesBottomBar extends StatelessWidget {
|
||||||
final ChatListController controller;
|
final ChatListController controller;
|
||||||
|
|
||||||
const SpacesBottomBar(this.controller, {Key? key}) : super(key: key);
|
const SpacesBottomBar(this.controller, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final foundIndex = controller.spacesEntries.indexWhere(
|
|
||||||
(se) => spacesEntryRoughEquivalence(se, controller.activeSpacesEntry));
|
|
||||||
final currentIndex = foundIndex == -1 ? 0 : foundIndex;
|
|
||||||
return Material(
|
return Material(
|
||||||
color: Theme.of(context).appBarTheme.backgroundColor,
|
color: Theme.of(context).navigationBarTheme.backgroundColor,
|
||||||
elevation: 6,
|
elevation: 6,
|
||||||
|
borderRadius: const BorderRadius.vertical(
|
||||||
|
top: Radius.circular(AppConfig.borderRadius)),
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: StreamBuilder<Object>(
|
child: StreamBuilder<Object>(
|
||||||
stream: Matrix.of(context).client.onSync.stream.where((sync) =>
|
stream: Matrix.of(context).client.onSync.stream.where((sync) =>
|
||||||
(sync.rooms?.join?.values.any((r) =>
|
(sync.rooms?.join?.values.any((r) =>
|
||||||
r.state?.any((s) => s.type.startsWith('m.space')) ??
|
r.state?.any((s) => s.type.startsWith('m.space')) ??
|
||||||
false) ??
|
false) ??
|
||||||
false) ||
|
false) ||
|
||||||
(sync.rooms?.leave?.isNotEmpty ?? false)),
|
(sync.rooms?.leave?.isNotEmpty ?? false)),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return Container(
|
return SingleChildScrollView(
|
||||||
height: 56,
|
controller: controller.snappingSheetScrollContentController,
|
||||||
alignment: Alignment.center,
|
child: AnimatedBuilder(
|
||||||
child: SingleChildScrollView(
|
child: _SpacesBottomNavigation(controller: controller),
|
||||||
scrollDirection: Axis.horizontal,
|
builder: (context, child) {
|
||||||
child: SalomonBottomBar(
|
if (controller.snappingSheetContainerSize == null) {
|
||||||
itemPadding: const EdgeInsets.all(8),
|
return child!;
|
||||||
currentIndex: currentIndex,
|
}
|
||||||
onTap: (i) => controller.setActiveSpacesEntry(
|
final rawPosition =
|
||||||
|
controller.snappingSheetController.currentPosition;
|
||||||
|
final position = rawPosition /
|
||||||
|
controller.snappingSheetContainerSize!.maxHeight;
|
||||||
|
|
||||||
|
if (rawPosition <= kSpacesBottomBarHeight) {
|
||||||
|
return child!;
|
||||||
|
} else if (position >= 0.5) {
|
||||||
|
return SpacesDrawer(controller: controller);
|
||||||
|
} else {
|
||||||
|
final normalized = (rawPosition - kSpacesBottomBarHeight) /
|
||||||
|
(controller.snappingSheetContainerSize!.maxHeight -
|
||||||
|
kSpacesBottomBarHeight) *
|
||||||
|
2;
|
||||||
|
var boxHeight = (1 - normalized) * kSpacesBottomBarHeight;
|
||||||
|
if (boxHeight < 0) boxHeight = 0;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: boxHeight,
|
||||||
|
child: ClipRect(
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
child: Opacity(
|
||||||
|
opacity: 1 - normalized, child: child!)),
|
||||||
|
),
|
||||||
|
Opacity(
|
||||||
|
opacity: normalized,
|
||||||
|
child: SpacesDrawer(controller: controller),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
animation: controller.snappingSheetController,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SpacesBottomNavigation extends StatelessWidget {
|
||||||
|
final ChatListController controller;
|
||||||
|
|
||||||
|
const _SpacesBottomNavigation({Key? key, required this.controller})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final currentIndex = controller.activeSpaceId == null
|
||||||
|
? 1
|
||||||
|
: controller.spaces
|
||||||
|
.indexWhere((space) => controller.activeSpaceId == space.id) +
|
||||||
|
2;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
height: 56,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: SalomonBottomBar(
|
||||||
|
itemPadding: const EdgeInsets.all(8),
|
||||||
|
currentIndex: currentIndex,
|
||||||
|
onTap: (i) => i == 0
|
||||||
|
? controller.expandSpaces()
|
||||||
|
: i == 1
|
||||||
|
? controller.setActiveSpacesEntry(
|
||||||
|
context,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
: controller.setActiveSpacesEntry(
|
||||||
context,
|
context,
|
||||||
controller.spacesEntries[i],
|
controller.spacesEntries[i],
|
||||||
),
|
),
|
||||||
selectedItemColor: Theme.of(context).colorScheme.primary,
|
selectedItemColor: Theme.of(context).colorScheme.primary,
|
||||||
items: controller.spacesEntries
|
items: [
|
||||||
.map((entry) => _buildSpacesEntryUI(context, entry))
|
SalomonBottomBarItem(
|
||||||
.toList(),
|
icon: const Icon(Icons.keyboard_arrow_up),
|
||||||
),
|
title: Text(L10n.of(context)!.showSpaces),
|
||||||
),
|
),
|
||||||
);
|
...controller.spacesEntries
|
||||||
}),
|
.map((space) => _buildSpacesEntryUI(context, space))
|
||||||
|
.toList(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
83
lib/pages/chat_list/spaces_drawer.dart
Normal file
83
lib/pages/chat_list/spaces_drawer.dart
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||||
|
|
||||||
|
import 'package:fluffychat/pages/chat_list/spaces_entry.dart';
|
||||||
|
import 'package:fluffychat/widgets/avatar.dart';
|
||||||
|
import 'chat_list.dart';
|
||||||
|
|
||||||
|
class SpacesDrawer extends StatelessWidget {
|
||||||
|
final ChatListController controller;
|
||||||
|
|
||||||
|
const SpacesDrawer({Key? key, required this.controller}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final currentIndex = controller.activeSpaceId == null
|
||||||
|
? 0
|
||||||
|
: controller.spaces
|
||||||
|
.indexWhere((space) => controller.activeSpaceId == space.id) +
|
||||||
|
1;
|
||||||
|
|
||||||
|
final Map<SpacesEntry, dynamic> spaceHierarchy =
|
||||||
|
Map.fromEntries(controller.spacesEntries.map((e) => MapEntry(e, null)));
|
||||||
|
|
||||||
|
// TODO(TheOeWithTheBraid): wait for space hierarchy https://gitlab.com/famedly/company/frontend/libraries/matrix_api_lite/-/merge_requests/58
|
||||||
|
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () async {
|
||||||
|
controller.snapBackSpacesSheet();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: spaceHierarchy.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
if (index == 0) {
|
||||||
|
return ListTile(
|
||||||
|
selected: currentIndex == index,
|
||||||
|
leading: const Icon(Icons.keyboard_arrow_down),
|
||||||
|
title: Text(L10n.of(context)!.allChats),
|
||||||
|
onTap: () => controller.setActiveSpacesEntry(
|
||||||
|
context,
|
||||||
|
null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final space = spaceHierarchy.keys.toList()[index];
|
||||||
|
final room = space.getSpace(context)!;
|
||||||
|
return ListTile(
|
||||||
|
selected: currentIndex == index,
|
||||||
|
leading: Avatar(
|
||||||
|
mxContent: room.avatar,
|
||||||
|
name: space.getName(context),
|
||||||
|
size: 24,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
title: Text(space.getName(context)),
|
||||||
|
subtitle: room.topic.isEmpty
|
||||||
|
? null
|
||||||
|
: Tooltip(
|
||||||
|
message: room.topic,
|
||||||
|
child: Text(
|
||||||
|
room.topic.replaceAll('\n', ' '),
|
||||||
|
softWrap: false,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => controller.setActiveSpacesEntry(
|
||||||
|
context,
|
||||||
|
space,
|
||||||
|
),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Icons.edit),
|
||||||
|
tooltip: L10n.of(context)!.edit,
|
||||||
|
onPressed: () => controller.editSpace(context, room.id),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,6 @@ import desktop_lifecycle
|
|||||||
import device_info_plus_macos
|
import device_info_plus_macos
|
||||||
import emoji_picker_flutter
|
import emoji_picker_flutter
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
import flutter_app_badger
|
|
||||||
import flutter_local_notifications
|
import flutter_local_notifications
|
||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import flutter_web_auth
|
import flutter_web_auth
|
||||||
@ -36,7 +35,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
EmojiPickerFlutterPlugin.register(with: registry.registrar(forPlugin: "EmojiPickerFlutterPlugin"))
|
EmojiPickerFlutterPlugin.register(with: registry.registrar(forPlugin: "EmojiPickerFlutterPlugin"))
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
FlutterAppBadgerPlugin.register(with: registry.registrar(forPlugin: "FlutterAppBadgerPlugin"))
|
|
||||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||||
FlutterSecureStorageMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageMacosPlugin"))
|
FlutterSecureStorageMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageMacosPlugin"))
|
||||||
FlutterWebAuthPlugin.register(with: registry.registrar(forPlugin: "FlutterWebAuthPlugin"))
|
FlutterWebAuthPlugin.register(with: registry.registrar(forPlugin: "FlutterWebAuthPlugin"))
|
||||||
|
70
pubspec.lock
70
pubspec.lock
@ -14,7 +14,7 @@ packages:
|
|||||||
name: adaptive_dialog
|
name: adaptive_dialog
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0+1"
|
version: "1.4.0"
|
||||||
adaptive_theme:
|
adaptive_theme:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -289,14 +289,14 @@ packages:
|
|||||||
name: dart_webrtc
|
name: dart_webrtc
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.4"
|
||||||
dbus:
|
dbus:
|
||||||
dependency: "direct overridden"
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
name: dbus
|
name: dbus
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.3"
|
version: "0.7.2"
|
||||||
desktop_drop:
|
desktop_drop:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -483,7 +483,7 @@ packages:
|
|||||||
name: flutter_app_badger
|
name: flutter_app_badger
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.3.0"
|
||||||
flutter_app_lock:
|
flutter_app_lock:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -551,7 +551,7 @@ packages:
|
|||||||
name: flutter_local_notifications
|
name: flutter_local_notifications
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.4.0"
|
version: "9.4.1"
|
||||||
flutter_local_notifications_linux:
|
flutter_local_notifications_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -598,7 +598,7 @@ packages:
|
|||||||
name: flutter_native_splash
|
name: flutter_native_splash
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.5"
|
version: "2.1.2+1"
|
||||||
flutter_olm:
|
flutter_olm:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -626,7 +626,7 @@ packages:
|
|||||||
name: flutter_ringtone_player
|
name: flutter_ringtone_player
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
version: "3.1.1"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -713,7 +713,7 @@ packages:
|
|||||||
name: flutter_webrtc
|
name: flutter_webrtc
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.5"
|
version: "0.8.4"
|
||||||
frontend_server_client:
|
frontend_server_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -851,13 +851,6 @@ packages:
|
|||||||
name: image_picker
|
name: image_picker
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.5"
|
|
||||||
image_picker_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: image_picker_android
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.8.4+11"
|
version: "0.8.4+11"
|
||||||
image_picker_for_web:
|
image_picker_for_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
@ -866,13 +859,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.6"
|
version: "2.1.6"
|
||||||
image_picker_ios:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: image_picker_ios
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.8.4+11"
|
|
||||||
image_picker_platform_interface:
|
image_picker_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -957,13 +943,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.1"
|
version: "0.8.1"
|
||||||
lint:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: lint
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.8.2"
|
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1140,14 +1119,14 @@ packages:
|
|||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.2"
|
version: "1.4.0"
|
||||||
package_info_plus_linux:
|
package_info_plus_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus_linux
|
name: package_info_plus_linux
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.3"
|
||||||
package_info_plus_macos:
|
package_info_plus_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1168,14 +1147,14 @@ packages:
|
|||||||
name: package_info_plus_web
|
name: package_info_plus_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.4"
|
||||||
package_info_plus_windows:
|
package_info_plus_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus_windows
|
name: package_info_plus_windows
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.4"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1273,7 +1252,7 @@ packages:
|
|||||||
name: permission_handler_apple
|
name: permission_handler_apple
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.0.4"
|
version: "9.0.3"
|
||||||
permission_handler_platform_interface:
|
permission_handler_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1420,7 +1399,7 @@ packages:
|
|||||||
name: record
|
name: record
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.4"
|
version: "3.0.3"
|
||||||
record_platform_interface:
|
record_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1539,7 +1518,7 @@ packages:
|
|||||||
name: shelf
|
name: shelf
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.2.0"
|
||||||
shelf_packages_handler:
|
shelf_packages_handler:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1573,6 +1552,15 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
|
snapping_sheet:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: listenable
|
||||||
|
resolved-ref: "3da78eea5d222baa1b266c19284acafee090f6be"
|
||||||
|
url: "https://github.com/TheOneWithTheBraid/snapping_sheet.git"
|
||||||
|
source: git
|
||||||
|
version: "3.1.0"
|
||||||
source_map_stack_trace:
|
source_map_stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1607,7 +1595,7 @@ packages:
|
|||||||
name: sqflite_common
|
name: sqflite_common
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1831,7 +1819,7 @@ packages:
|
|||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.9"
|
version: "2.0.6"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1999,14 +1987,14 @@ packages:
|
|||||||
name: webrtc_interface
|
name: webrtc_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.0.2"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.1"
|
version: "2.5.0"
|
||||||
wkt_parser:
|
wkt_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2037,4 +2025,4 @@ packages:
|
|||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.16.1 <3.0.0"
|
dart: ">=2.16.1 <3.0.0"
|
||||||
flutter: ">=2.10.0"
|
flutter: ">=2.8.0"
|
||||||
|
@ -78,6 +78,7 @@ dependencies:
|
|||||||
share: ^2.0.4
|
share: ^2.0.4
|
||||||
shared_preferences: ^2.0.13
|
shared_preferences: ^2.0.13
|
||||||
slugify: ^2.0.0
|
slugify: ^2.0.0
|
||||||
|
snapping_sheet: ^3.1.0
|
||||||
swipe_to_action: ^0.2.0
|
swipe_to_action: ^0.2.0
|
||||||
uni_links: ^0.5.1
|
uni_links: ^0.5.1
|
||||||
unifiedpush: ^4.0.0
|
unifiedpush: ^4.0.0
|
||||||
@ -146,3 +147,9 @@ dependency_overrides:
|
|||||||
url: https://github.com/TheOneWithTheBraid/keyboard_shortcuts.git
|
url: https://github.com/TheOneWithTheBraid/keyboard_shortcuts.git
|
||||||
ref: null-safety
|
ref: null-safety
|
||||||
provider: 5.0.0
|
provider: 5.0.0
|
||||||
|
# wating for `Listenable` implementation
|
||||||
|
# Upstream pull request: https://github.com/AdamJonsson/snapping_sheet/pull/84
|
||||||
|
snapping_sheet:
|
||||||
|
git:
|
||||||
|
url: https://github.com/TheOneWithTheBraid/snapping_sheet.git
|
||||||
|
ref: listenable
|
||||||
|
Loading…
Reference in New Issue
Block a user