2024-08-07 10:24:16 +00:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:get/get.dart';
|
2024-09-12 15:55:31 +00:00
|
|
|
import 'package:solian/models/realm.dart';
|
|
|
|
import 'package:solian/providers/auth.dart';
|
2024-08-07 10:24:16 +00:00
|
|
|
import 'package:solian/providers/content/channel.dart';
|
2024-09-12 15:55:31 +00:00
|
|
|
import 'package:solian/providers/content/realm.dart';
|
2024-09-12 16:17:56 +00:00
|
|
|
import 'package:solian/providers/navigation.dart';
|
2024-09-12 15:55:31 +00:00
|
|
|
import 'package:solian/widgets/account/account_avatar.dart';
|
|
|
|
import 'package:solian/widgets/channel/channel_list.dart';
|
2024-08-07 10:24:16 +00:00
|
|
|
|
2024-09-12 15:55:31 +00:00
|
|
|
class AppNavigationRegion extends StatefulWidget {
|
2024-08-21 09:00:59 +00:00
|
|
|
final bool isCollapsed;
|
2024-08-07 10:24:16 +00:00
|
|
|
|
2024-08-21 11:11:27 +00:00
|
|
|
const AppNavigationRegion({
|
2024-08-21 09:00:59 +00:00
|
|
|
super.key,
|
|
|
|
this.isCollapsed = false,
|
|
|
|
});
|
2024-08-07 10:24:16 +00:00
|
|
|
|
2024-09-12 15:55:31 +00:00
|
|
|
@override
|
|
|
|
State<AppNavigationRegion> createState() => _AppNavigationRegionState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _AppNavigationRegionState extends State<AppNavigationRegion>
|
|
|
|
with SingleTickerProviderStateMixin {
|
|
|
|
bool _isTryingExit = false;
|
|
|
|
|
|
|
|
late final AnimationController _animationController = AnimationController(
|
|
|
|
vsync: this,
|
|
|
|
duration: const Duration(milliseconds: 250),
|
|
|
|
);
|
|
|
|
late final Animation<Offset> _animationTween = Tween<Offset>(
|
|
|
|
begin: Offset.zero,
|
|
|
|
end: const Offset(1.0, 0.0),
|
|
|
|
).animate(CurvedAnimation(
|
|
|
|
parent: _animationController,
|
|
|
|
curve: Curves.fastOutSlowIn,
|
|
|
|
));
|
|
|
|
|
|
|
|
void _focusRealm(Realm item) {
|
|
|
|
_animationController.animateTo(1).then((_) {
|
2024-09-12 16:17:56 +00:00
|
|
|
setState(
|
|
|
|
() => Get.find<NavigationStateProvider>().focusedRealm.value = item,
|
|
|
|
);
|
2024-09-12 15:55:31 +00:00
|
|
|
_animationController.animateTo(0);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void _unFocusRealm() {
|
|
|
|
_animationController.animateTo(1).then((_) {
|
2024-09-12 16:17:56 +00:00
|
|
|
setState(
|
|
|
|
() => Get.find<NavigationStateProvider>().focusedRealm.value = null,
|
|
|
|
);
|
2024-09-12 15:55:31 +00:00
|
|
|
_animationController.animateTo(0);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
void dispose() {
|
|
|
|
_animationController.dispose();
|
|
|
|
super.dispose();
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildRealmFocusAvatar() {
|
2024-09-12 16:17:56 +00:00
|
|
|
final focusedRealm = Get.find<NavigationStateProvider>().focusedRealm.value;
|
2024-09-12 15:55:31 +00:00
|
|
|
return MouseRegion(
|
|
|
|
child: AnimatedSwitcher(
|
|
|
|
switchInCurve: Curves.fastOutSlowIn,
|
|
|
|
switchOutCurve: Curves.fastOutSlowIn,
|
|
|
|
duration: const Duration(milliseconds: 300),
|
|
|
|
transitionBuilder: (child, animation) {
|
|
|
|
return ScaleTransition(
|
|
|
|
scale: animation,
|
|
|
|
child: child,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
child: _isTryingExit
|
|
|
|
? GestureDetector(
|
|
|
|
child: CircleAvatar(
|
|
|
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
|
|
child: const Icon(
|
|
|
|
Icons.arrow_back,
|
|
|
|
color: Colors.white,
|
|
|
|
size: 16,
|
|
|
|
),
|
|
|
|
).paddingSymmetric(
|
|
|
|
vertical: 8,
|
|
|
|
),
|
|
|
|
onTap: () => _unFocusRealm(),
|
|
|
|
)
|
2024-09-12 16:17:56 +00:00
|
|
|
: _buildEntryAvatar(focusedRealm!),
|
2024-09-12 15:55:31 +00:00
|
|
|
),
|
|
|
|
onEnter: (_) => setState(() => _isTryingExit = true),
|
|
|
|
onExit: (_) => setState(() => _isTryingExit = false),
|
2024-08-07 10:24:16 +00:00
|
|
|
);
|
2024-09-12 15:55:31 +00:00
|
|
|
}
|
2024-08-07 10:24:16 +00:00
|
|
|
|
2024-09-12 15:55:31 +00:00
|
|
|
Widget _buildEntryAvatar(Realm item) {
|
|
|
|
return Hero(
|
|
|
|
tag: Key('region-realm-avatar-${item.id}'),
|
|
|
|
child: (item.avatar?.isNotEmpty ?? false)
|
|
|
|
? AccountAvatar(content: item.avatar)
|
|
|
|
: CircleAvatar(
|
|
|
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
|
|
child: const Icon(
|
|
|
|
Icons.workspaces,
|
|
|
|
color: Colors.white,
|
|
|
|
size: 16,
|
|
|
|
),
|
|
|
|
).paddingSymmetric(
|
|
|
|
vertical: 8,
|
|
|
|
),
|
|
|
|
);
|
2024-08-07 10:24:16 +00:00
|
|
|
}
|
|
|
|
|
2024-09-12 15:55:31 +00:00
|
|
|
Widget _buildEntry(BuildContext context, Realm item) {
|
2024-08-07 10:24:16 +00:00
|
|
|
const padding = EdgeInsets.symmetric(horizontal: 20);
|
|
|
|
|
2024-09-12 15:55:31 +00:00
|
|
|
if (widget.isCollapsed) {
|
2024-08-21 09:00:59 +00:00
|
|
|
return InkWell(
|
2024-09-12 15:55:31 +00:00
|
|
|
child: _buildEntryAvatar(item),
|
|
|
|
onTap: () => _focusRealm(item),
|
2024-08-21 09:00:59 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-08-07 10:24:16 +00:00
|
|
|
return ListTile(
|
|
|
|
minTileHeight: 0,
|
2024-09-12 15:55:31 +00:00
|
|
|
leading: _buildEntryAvatar(item),
|
2024-08-07 10:24:16 +00:00
|
|
|
contentPadding: padding,
|
|
|
|
title: Text(item.name),
|
|
|
|
subtitle: Text(
|
|
|
|
item.description,
|
|
|
|
maxLines: 1,
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
),
|
2024-09-12 15:55:31 +00:00
|
|
|
onTap: () => _focusRealm(item),
|
2024-08-07 10:24:16 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2024-09-12 15:55:31 +00:00
|
|
|
final RealmProvider realms = Get.find();
|
2024-08-07 10:24:16 +00:00
|
|
|
final ChannelProvider channels = Get.find();
|
2024-09-12 15:55:31 +00:00
|
|
|
final AuthProvider auth = Get.find();
|
2024-09-12 16:17:56 +00:00
|
|
|
final NavigationStateProvider navState = Get.find();
|
|
|
|
|
|
|
|
return Obx(
|
|
|
|
() => AnimatedBuilder(
|
|
|
|
animation: _animationController,
|
|
|
|
builder: (context, child) {
|
|
|
|
return SlideTransition(
|
|
|
|
position: _animationTween,
|
|
|
|
child: child,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
child: navState.focusedRealm.value == null
|
|
|
|
? widget.isCollapsed
|
|
|
|
? CustomScrollView(
|
|
|
|
slivers: [
|
|
|
|
const SliverPadding(padding: EdgeInsets.only(top: 8)),
|
|
|
|
SliverList.builder(
|
|
|
|
itemCount: realms.availableRealms.length,
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
final element = realms.availableRealms[index];
|
|
|
|
return Tooltip(
|
|
|
|
message: element.name,
|
|
|
|
child: _buildEntry(context, element),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
: CustomScrollView(
|
|
|
|
slivers: [
|
|
|
|
const SliverPadding(padding: EdgeInsets.only(top: 8)),
|
|
|
|
SliverList.builder(
|
|
|
|
itemCount: realms.availableRealms.length,
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
final element = realms.availableRealms[index];
|
|
|
|
return _buildEntry(context, element);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
const SliverPadding(padding: EdgeInsets.only(bottom: 8)),
|
|
|
|
],
|
2024-09-12 15:55:31 +00:00
|
|
|
)
|
2024-09-12 16:17:56 +00:00
|
|
|
: Column(
|
|
|
|
children: [
|
|
|
|
if (widget.isCollapsed)
|
|
|
|
Tooltip(
|
|
|
|
message: navState.focusedRealm.value!.name,
|
|
|
|
child: _buildRealmFocusAvatar().paddingSymmetric(
|
|
|
|
vertical: 8,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
else
|
|
|
|
ListTile(
|
|
|
|
minTileHeight: 0,
|
|
|
|
tileColor:
|
|
|
|
Theme.of(context).colorScheme.surfaceContainerLow,
|
|
|
|
leading: _buildRealmFocusAvatar(),
|
|
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 20, vertical: 8),
|
|
|
|
title: Text(navState.focusedRealm.value!.name),
|
|
|
|
subtitle: Text(
|
|
|
|
navState.focusedRealm.value!.description,
|
|
|
|
maxLines: 1,
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
),
|
2024-09-12 15:55:31 +00:00
|
|
|
),
|
2024-09-12 16:17:56 +00:00
|
|
|
Expanded(
|
|
|
|
child: Obx(
|
|
|
|
() => ChannelListWidget(
|
|
|
|
channels: channels.availableChannels
|
|
|
|
.where(
|
|
|
|
(x) =>
|
|
|
|
x.realm?.id ==
|
|
|
|
navState.focusedRealm.value?.id,
|
|
|
|
)
|
|
|
|
.toList(),
|
|
|
|
selfId: auth.userProfile.value!['id'],
|
|
|
|
noCategory: true,
|
|
|
|
),
|
2024-09-12 15:55:31 +00:00
|
|
|
),
|
|
|
|
),
|
2024-09-12 16:17:56 +00:00
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
2024-09-12 15:55:31 +00:00
|
|
|
);
|
2024-08-07 10:24:16 +00:00
|
|
|
}
|
|
|
|
}
|