mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-27 23:09:35 +01:00
9f8a7f79f2
- fix spaces animation direction - animate chat list app bar title - fix ink splash radius in account selector - fix missing asset fallback in content banner Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
165 lines
5.7 KiB
Dart
165 lines
5.7 KiB
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:fluffychat/config/app_config.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/widgets/avatar.dart';
|
|
import 'package:fluffychat/widgets/matrix.dart';
|
|
|
|
const kSpacesBottomBarHeight = 56.0;
|
|
|
|
final GlobalKey _globalKey = GlobalKey();
|
|
|
|
class SpacesBottomBar extends StatelessWidget {
|
|
final ChatListController controller;
|
|
|
|
const SpacesBottomBar(this.controller, {Key? key}) : super(key: key);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Material(
|
|
color: Theme.of(context).navigationBarTheme.backgroundColor,
|
|
elevation: 6,
|
|
borderRadius: const BorderRadius.vertical(
|
|
top: Radius.circular(AppConfig.borderRadius)),
|
|
clipBehavior: Clip.hardEdge,
|
|
child: SafeArea(
|
|
child: StreamBuilder<Object>(
|
|
stream: Matrix.of(context).client.onSync.stream.where((sync) =>
|
|
(sync.rooms?.join?.values.any((r) =>
|
|
r.state?.any((s) => s.type.startsWith('m.space')) ??
|
|
false) ??
|
|
false) ||
|
|
(sync.rooms?.leave?.isNotEmpty ?? false)),
|
|
builder: (context, snapshot) {
|
|
return SingleChildScrollView(
|
|
controller: controller.snappingSheetScrollContentController,
|
|
child: AnimatedBuilder(
|
|
child: _SpacesBottomNavigation(
|
|
key: _globalKey, controller: controller),
|
|
builder: (context, child) {
|
|
if (controller.snappingSheetContainerSize == null) {
|
|
return child!;
|
|
}
|
|
final rawPosition =
|
|
controller.snappingSheetController.isAttached
|
|
? controller.snappingSheetController.currentPosition
|
|
: 0;
|
|
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.spacesEntries.indexWhere((space) =>
|
|
controller.activeSpacesEntry.runtimeType == space.runtimeType &&
|
|
(controller.activeSpaceId == space.getSpace(context)?.id)) +
|
|
1;
|
|
|
|
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()
|
|
: controller.setActiveSpacesEntry(
|
|
context,
|
|
controller.spacesEntries[i - 1],
|
|
),
|
|
selectedItemColor: Theme.of(context).colorScheme.primary,
|
|
items: [
|
|
SalomonBottomBarItem(
|
|
icon: const Icon(Icons.keyboard_arrow_up),
|
|
title: Text(L10n.of(context)!.showSpaces),
|
|
),
|
|
...controller.spacesEntries
|
|
.map((space) => _buildSpacesEntryUI(context, space))
|
|
.toList(),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
SalomonBottomBarItem _buildSpacesEntryUI(
|
|
BuildContext context, SpacesEntry entry) {
|
|
final space = entry.getSpace(context);
|
|
if (space != null) {
|
|
return SalomonBottomBarItem(
|
|
icon: InkWell(
|
|
borderRadius: BorderRadius.circular(28),
|
|
onTap: () => controller.setActiveSpacesEntry(
|
|
context,
|
|
entry,
|
|
),
|
|
onLongPress: () => controller.editSpace(context, space.id),
|
|
child: Avatar(
|
|
mxContent: space.avatar,
|
|
name: space.displayname,
|
|
size: 24,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
title: Text(entry.getName(context)),
|
|
);
|
|
}
|
|
return SalomonBottomBarItem(
|
|
icon: entry.getIcon(false),
|
|
activeIcon: entry.getIcon(true),
|
|
title: Text(entry.getName(context)),
|
|
);
|
|
}
|
|
}
|