mirror of
https://github.com/krille-chan/fluffychat
synced 2024-09-17 07:35:13 +00: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
1679e33a3a
commit
44ffaa1b41
8 changed files with 499 additions and 280 deletions
|
@ -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';
|
||||||
|
@ -31,6 +32,8 @@ class ChatListView extends StatelessWidget {
|
||||||
stream: Matrix.of(context).onShareContentChanged.stream,
|
stream: Matrix.of(context).onShareContentChanged.stream,
|
||||||
builder: (_, __) {
|
builder: (_, __) {
|
||||||
final selectMode = controller.selectMode;
|
final selectMode = controller.selectMode;
|
||||||
|
final showSpaces =
|
||||||
|
controller.spaces.isNotEmpty && controller.selectedRoomIds.isEmpty;
|
||||||
return VWidgetGuard(
|
return VWidgetGuard(
|
||||||
onSystemPop: (redirector) async {
|
onSystemPop: (redirector) async {
|
||||||
final selMode = controller.selectMode;
|
final selMode = controller.selectMode;
|
||||||
|
@ -68,8 +71,7 @@ class ChatListView extends StatelessWidget {
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: L10n.of(context)!.toggleUnread,
|
tooltip: L10n.of(context)!.toggleUnread,
|
||||||
icon: Icon(
|
icon: Icon(controller.anySelectedRoomNotMarkedUnread
|
||||||
controller.anySelectedRoomNotMarkedUnread
|
|
||||||
? Icons.mark_chat_read_outlined
|
? Icons.mark_chat_read_outlined
|
||||||
: Icons.mark_chat_unread_outlined),
|
: Icons.mark_chat_unread_outlined),
|
||||||
onPressed: controller.toggleUnread,
|
onPressed: controller.toggleUnread,
|
||||||
|
@ -200,7 +202,15 @@ class ChatListView extends StatelessWidget {
|
||||||
.getRoomById(controller.activeSpaceId!)!
|
.getRoomById(controller.activeSpaceId!)!
|
||||||
.displayname),
|
.displayname),
|
||||||
),
|
),
|
||||||
body: Column(children: [
|
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(
|
AnimatedContainer(
|
||||||
height: controller.showChatBackupBanner ? 54 : 0,
|
height: controller.showChatBackupBanner ? 54 : 0,
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
|
@ -222,9 +232,37 @@ class ChatListView extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(child: _ChatListViewBody(controller)),
|
Expanded(child: _ChatListViewBody(controller)),
|
||||||
]),
|
],
|
||||||
|
),
|
||||||
|
initialSnappingPosition: showSpaces
|
||||||
|
? const SnappingPosition.pixels(
|
||||||
|
positionPixels: kSpacesBottomBarHeight)
|
||||||
|
: const SnappingPosition.factor(positionFactor: 0.0),
|
||||||
|
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
|
floatingActionButton: selectMode == SelectMode.normal
|
||||||
? KeyBoardShortcuts(
|
? 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,
|
: null,
|
||||||
bottomNavigationBar: Column(
|
bottomNavigationBar: const ConnectionStatusHeader(),
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const ConnectionStatusHeader(),
|
|
||||||
if (controller.spaces.isNotEmpty &&
|
|
||||||
controller.selectedRoomIds.isEmpty)
|
|
||||||
SpacesBottomBar(controller),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,30 @@
|
||||||
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) =>
|
||||||
|
@ -28,6 +34,72 @@ class SpacesBottomBar extends StatelessWidget {
|
||||||
false) ||
|
false) ||
|
||||||
(sync.rooms?.leave?.isNotEmpty ?? false)),
|
(sync.rooms?.leave?.isNotEmpty ?? false)),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
controller: controller.snappingSheetScrollContentController,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
child: _SpacesBottomNavigation(controller: controller),
|
||||||
|
builder: (context, child) {
|
||||||
|
if (controller.snappingSheetContainerSize == null) {
|
||||||
|
return child!;
|
||||||
|
}
|
||||||
|
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(
|
return Container(
|
||||||
height: 56,
|
height: 56,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
@ -36,20 +108,30 @@ class SpacesBottomBar extends StatelessWidget {
|
||||||
child: SalomonBottomBar(
|
child: SalomonBottomBar(
|
||||||
itemPadding: const EdgeInsets.all(8),
|
itemPadding: const EdgeInsets.all(8),
|
||||||
currentIndex: currentIndex,
|
currentIndex: currentIndex,
|
||||||
onTap: (i) => controller.setActiveSpacesEntry(
|
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(
|
||||||
|
icon: const Icon(Icons.keyboard_arrow_up),
|
||||||
|
title: Text(L10n.of(context)!.showSpaces),
|
||||||
|
),
|
||||||
|
...controller.spacesEntries
|
||||||
|
.map((space) => _buildSpacesEntryUI(context, space))
|
||||||
.toList(),
|
.toList(),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SalomonBottomBarItem _buildSpacesEntryUI(
|
SalomonBottomBarItem _buildSpacesEntryUI(
|
||||||
|
|
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 a new issue