✨ Brand new app navigation region
This commit is contained in:
		@@ -12,9 +12,12 @@ class Realm {
 | 
				
			|||||||
  String alias;
 | 
					  String alias;
 | 
				
			||||||
  String name;
 | 
					  String name;
 | 
				
			||||||
  String description;
 | 
					  String description;
 | 
				
			||||||
 | 
					  String? avatar;
 | 
				
			||||||
 | 
					  String? banner;
 | 
				
			||||||
  bool isPublic;
 | 
					  bool isPublic;
 | 
				
			||||||
  bool isCommunity;
 | 
					  bool isCommunity;
 | 
				
			||||||
  int? accountId;
 | 
					  int? accountId;
 | 
				
			||||||
 | 
					  int? externalId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Realm({
 | 
					  Realm({
 | 
				
			||||||
    required this.id,
 | 
					    required this.id,
 | 
				
			||||||
@@ -24,9 +27,12 @@ class Realm {
 | 
				
			|||||||
    required this.alias,
 | 
					    required this.alias,
 | 
				
			||||||
    required this.name,
 | 
					    required this.name,
 | 
				
			||||||
    required this.description,
 | 
					    required this.description,
 | 
				
			||||||
 | 
					    required this.avatar,
 | 
				
			||||||
 | 
					    required this.banner,
 | 
				
			||||||
    required this.isPublic,
 | 
					    required this.isPublic,
 | 
				
			||||||
    required this.isCommunity,
 | 
					    required this.isCommunity,
 | 
				
			||||||
    this.accountId,
 | 
					    this.accountId,
 | 
				
			||||||
 | 
					    this.externalId,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  factory Realm.fromJson(Map<String, dynamic> json) => _$RealmFromJson(json);
 | 
					  factory Realm.fromJson(Map<String, dynamic> json) => _$RealmFromJson(json);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,9 +16,12 @@ Realm _$RealmFromJson(Map<String, dynamic> json) => Realm(
 | 
				
			|||||||
      alias: json['alias'] as String,
 | 
					      alias: json['alias'] as String,
 | 
				
			||||||
      name: json['name'] as String,
 | 
					      name: json['name'] as String,
 | 
				
			||||||
      description: json['description'] as String,
 | 
					      description: json['description'] as String,
 | 
				
			||||||
 | 
					      avatar: json['avatar'] as String?,
 | 
				
			||||||
 | 
					      banner: json['banner'] as String?,
 | 
				
			||||||
      isPublic: json['is_public'] as bool,
 | 
					      isPublic: json['is_public'] as bool,
 | 
				
			||||||
      isCommunity: json['is_community'] as bool,
 | 
					      isCommunity: json['is_community'] as bool,
 | 
				
			||||||
      accountId: (json['account_id'] as num?)?.toInt(),
 | 
					      accountId: (json['account_id'] as num?)?.toInt(),
 | 
				
			||||||
 | 
					      externalId: (json['external_id'] as num?)?.toInt(),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Map<String, dynamic> _$RealmToJson(Realm instance) => <String, dynamic>{
 | 
					Map<String, dynamic> _$RealmToJson(Realm instance) => <String, dynamic>{
 | 
				
			||||||
@@ -29,9 +32,12 @@ Map<String, dynamic> _$RealmToJson(Realm instance) => <String, dynamic>{
 | 
				
			|||||||
      'alias': instance.alias,
 | 
					      'alias': instance.alias,
 | 
				
			||||||
      'name': instance.name,
 | 
					      'name': instance.name,
 | 
				
			||||||
      'description': instance.description,
 | 
					      'description': instance.description,
 | 
				
			||||||
 | 
					      'avatar': instance.avatar,
 | 
				
			||||||
 | 
					      'banner': instance.banner,
 | 
				
			||||||
      'is_public': instance.isPublic,
 | 
					      'is_public': instance.isPublic,
 | 
				
			||||||
      'is_community': instance.isCommunity,
 | 
					      'is_community': instance.isCommunity,
 | 
				
			||||||
      'account_id': instance.accountId,
 | 
					      'account_id': instance.accountId,
 | 
				
			||||||
 | 
					      'external_id': instance.externalId,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RealmMember _$RealmMemberFromJson(Map<String, dynamic> json) => RealmMember(
 | 
					RealmMember _$RealmMemberFromJson(Map<String, dynamic> json) => RealmMember(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -315,7 +315,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
 | 
				
			|||||||
                  Card(
 | 
					                  Card(
 | 
				
			||||||
                    child: ListTile(
 | 
					                    child: ListTile(
 | 
				
			||||||
                      contentPadding:
 | 
					                      contentPadding:
 | 
				
			||||||
                          const EdgeInsets.symmetric(horizontal: 24),
 | 
					                          const EdgeInsets.only(left: 24, right: 32),
 | 
				
			||||||
                      trailing: const Icon(Icons.inbox_outlined),
 | 
					                      trailing: const Icon(Icons.inbox_outlined),
 | 
				
			||||||
                      title: Text('notifyEmpty'.tr),
 | 
					                      title: Text('notifyEmpty'.tr),
 | 
				
			||||||
                      subtitle: Text('notifyEmptyCaption'.tr),
 | 
					                      subtitle: Text('notifyEmptyCaption'.tr),
 | 
				
			||||||
@@ -368,18 +368,23 @@ class _DashboardScreenState extends State<DashboardScreen> {
 | 
				
			|||||||
                      return SizedBox(
 | 
					                      return SizedBox(
 | 
				
			||||||
                        width: min(480, width),
 | 
					                        width: min(480, width),
 | 
				
			||||||
                        child: Card(
 | 
					                        child: Card(
 | 
				
			||||||
                          child: SingleChildScrollView(
 | 
					                          child: ClipRRect(
 | 
				
			||||||
                            child: PostListEntryWidget(
 | 
					                            borderRadius: const BorderRadius.all(
 | 
				
			||||||
                              item: item,
 | 
					                              Radius.circular(8),
 | 
				
			||||||
                              isClickable: true,
 | 
					                            ),
 | 
				
			||||||
                              isShowEmbed: true,
 | 
					                            child: SingleChildScrollView(
 | 
				
			||||||
                              isNestedClickable: true,
 | 
					                              child: PostListEntryWidget(
 | 
				
			||||||
                              onUpdate: (_) {
 | 
					                                item: item,
 | 
				
			||||||
                                _pullPosts();
 | 
					                                isClickable: true,
 | 
				
			||||||
                              },
 | 
					                                isShowEmbed: true,
 | 
				
			||||||
                              backgroundColor: Theme.of(context)
 | 
					                                isNestedClickable: true,
 | 
				
			||||||
                                  .colorScheme
 | 
					                                onUpdate: (_) {
 | 
				
			||||||
                                  .surfaceContainerLow,
 | 
					                                  _pullPosts();
 | 
				
			||||||
 | 
					                                },
 | 
				
			||||||
 | 
					                                backgroundColor: Theme.of(context)
 | 
				
			||||||
 | 
					                                    .colorScheme
 | 
				
			||||||
 | 
					                                    .surfaceContainerLow,
 | 
				
			||||||
 | 
					                              ),
 | 
				
			||||||
                            ),
 | 
					                            ),
 | 
				
			||||||
                          ),
 | 
					                          ),
 | 
				
			||||||
                        ).paddingSymmetric(horizontal: 8),
 | 
					                        ).paddingSymmetric(horizontal: 8),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -162,7 +162,13 @@ class _ChannelListWidgetState extends State<ChannelListWidget> {
 | 
				
			|||||||
        ),
 | 
					        ),
 | 
				
			||||||
        contentPadding: padding,
 | 
					        contentPadding: padding,
 | 
				
			||||||
        title: Text(item.name),
 | 
					        title: Text(item.name),
 | 
				
			||||||
        subtitle: !widget.isDense ? Text(item.description) : null,
 | 
					        subtitle: !widget.isDense
 | 
				
			||||||
 | 
					            ? Text(
 | 
				
			||||||
 | 
					                item.description,
 | 
				
			||||||
 | 
					                maxLines: 1,
 | 
				
			||||||
 | 
					                overflow: TextOverflow.ellipsis,
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					            : null,
 | 
				
			||||||
        onTap: () => _gotoChannel(item),
 | 
					        onTap: () => _gotoChannel(item),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -239,45 +239,38 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer>
 | 
				
			|||||||
          children: [
 | 
					          children: [
 | 
				
			||||||
            _buildUserInfo().paddingSymmetric(vertical: 8),
 | 
					            _buildUserInfo().paddingSymmetric(vertical: 8),
 | 
				
			||||||
            const Divider(thickness: 0.3, height: 1),
 | 
					            const Divider(thickness: 0.3, height: 1),
 | 
				
			||||||
            Column(
 | 
					            SizedBox(
 | 
				
			||||||
              children: AppNavigation.destinations
 | 
					              width: double.infinity,
 | 
				
			||||||
                  .map(
 | 
					              child: Wrap(
 | 
				
			||||||
                    (e) => _isCollapsed
 | 
					                runSpacing: 8,
 | 
				
			||||||
                        ? Tooltip(
 | 
					                spacing: 8,
 | 
				
			||||||
                            message: e.label,
 | 
					                alignment: WrapAlignment.spaceAround,
 | 
				
			||||||
                            child: InkWell(
 | 
					                children: AppNavigation.destinations
 | 
				
			||||||
                              child: Icon(e.icon, size: 20).paddingSymmetric(
 | 
					                    .map(
 | 
				
			||||||
                                horizontal: 28,
 | 
					                      (e) => Card(
 | 
				
			||||||
                                vertical: 16,
 | 
					                        elevation: 0,
 | 
				
			||||||
                              ),
 | 
					                        margin: EdgeInsets.zero,
 | 
				
			||||||
                              onTap: () {
 | 
					                        child: Tooltip(
 | 
				
			||||||
                                AppRouter.instance.goNamed(e.page);
 | 
					                          message: e.label,
 | 
				
			||||||
                                _closeDrawer();
 | 
					                          child: InkWell(
 | 
				
			||||||
                              },
 | 
					                            borderRadius:
 | 
				
			||||||
                            ),
 | 
					                                const BorderRadius.all(Radius.circular(8)),
 | 
				
			||||||
                          )
 | 
					                            child: Icon(e.icon, size: 20).paddingAll(20),
 | 
				
			||||||
                        : ListTile(
 | 
					 | 
				
			||||||
                            contentPadding: const EdgeInsets.symmetric(
 | 
					 | 
				
			||||||
                              horizontal: 20,
 | 
					 | 
				
			||||||
                            ),
 | 
					 | 
				
			||||||
                            leading: Icon(e.icon, size: 20).paddingAll(2),
 | 
					 | 
				
			||||||
                            title: !_isCollapsed ? Text(e.label) : null,
 | 
					 | 
				
			||||||
                            enabled: true,
 | 
					 | 
				
			||||||
                            onTap: () {
 | 
					                            onTap: () {
 | 
				
			||||||
                              AppRouter.instance.goNamed(e.page);
 | 
					                              AppRouter.instance.goNamed(e.page);
 | 
				
			||||||
                              _closeDrawer();
 | 
					                              _closeDrawer();
 | 
				
			||||||
                            },
 | 
					                            },
 | 
				
			||||||
                          ),
 | 
					                          ),
 | 
				
			||||||
                  )
 | 
					                        ),
 | 
				
			||||||
                  .toList(),
 | 
					                      ),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    .toList(),
 | 
				
			||||||
 | 
					              ).paddingSymmetric(vertical: 8, horizontal: 12),
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            const Divider(thickness: 0.3, height: 1),
 | 
					            const Divider(thickness: 0.3, height: 1),
 | 
				
			||||||
            Expanded(
 | 
					            Expanded(
 | 
				
			||||||
              child: AppNavigationRegion(
 | 
					              child: AppNavigationRegion(
 | 
				
			||||||
                isCollapsed: _isCollapsed,
 | 
					                isCollapsed: _isCollapsed,
 | 
				
			||||||
                onSelected: (item) {
 | 
					 | 
				
			||||||
                  _closeDrawer();
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            const Divider(thickness: 0.3, height: 1),
 | 
					            const Divider(thickness: 0.3, height: 1),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,48 +1,126 @@
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:get/get.dart';
 | 
					import 'package:get/get.dart';
 | 
				
			||||||
import 'package:solian/models/channel.dart';
 | 
					import 'package:solian/models/realm.dart';
 | 
				
			||||||
 | 
					import 'package:solian/providers/auth.dart';
 | 
				
			||||||
import 'package:solian/providers/content/channel.dart';
 | 
					import 'package:solian/providers/content/channel.dart';
 | 
				
			||||||
import 'package:solian/router.dart';
 | 
					import 'package:solian/providers/content/realm.dart';
 | 
				
			||||||
import 'package:collection/collection.dart';
 | 
					import 'package:solian/widgets/account/account_avatar.dart';
 | 
				
			||||||
 | 
					import 'package:solian/widgets/channel/channel_list.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AppNavigationRegion extends StatelessWidget {
 | 
					class AppNavigationRegion extends StatefulWidget {
 | 
				
			||||||
  final bool isCollapsed;
 | 
					  final bool isCollapsed;
 | 
				
			||||||
  final Function(Channel item) onSelected;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const AppNavigationRegion({
 | 
					  const AppNavigationRegion({
 | 
				
			||||||
    super.key,
 | 
					    super.key,
 | 
				
			||||||
    required this.onSelected,
 | 
					 | 
				
			||||||
    this.isCollapsed = false,
 | 
					    this.isCollapsed = false,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _gotoChannel(Channel item) {
 | 
					  @override
 | 
				
			||||||
    AppRouter.instance.goNamed(
 | 
					  State<AppNavigationRegion> createState() => _AppNavigationRegionState();
 | 
				
			||||||
      'channelChat',
 | 
					}
 | 
				
			||||||
      pathParameters: {'alias': item.alias},
 | 
					 | 
				
			||||||
      queryParameters: {
 | 
					 | 
				
			||||||
        if (item.realmId != null) 'realm': item.realm!.alias,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onSelected(item);
 | 
					class _AppNavigationRegionState extends State<AppNavigationRegion>
 | 
				
			||||||
 | 
					    with SingleTickerProviderStateMixin {
 | 
				
			||||||
 | 
					  Realm? _focusedRealm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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((_) {
 | 
				
			||||||
 | 
					      setState(() => _focusedRealm = item);
 | 
				
			||||||
 | 
					      _animationController.animateTo(0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Widget _buildEntry(BuildContext context, Channel item) {
 | 
					  void _unFocusRealm() {
 | 
				
			||||||
 | 
					    _animationController.animateTo(1).then((_) {
 | 
				
			||||||
 | 
					      setState(() => _focusedRealm = null);
 | 
				
			||||||
 | 
					      _animationController.animateTo(0);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void dispose() {
 | 
				
			||||||
 | 
					    _animationController.dispose();
 | 
				
			||||||
 | 
					    super.dispose();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Widget _buildRealmFocusAvatar() {
 | 
				
			||||||
 | 
					    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(),
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					            : _buildEntryAvatar(_focusedRealm!),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      onEnter: (_) => setState(() => _isTryingExit = true),
 | 
				
			||||||
 | 
					      onExit: (_) => setState(() => _isTryingExit = false),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Widget _buildEntry(BuildContext context, Realm item) {
 | 
				
			||||||
    const padding = EdgeInsets.symmetric(horizontal: 20);
 | 
					    const padding = EdgeInsets.symmetric(horizontal: 20);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (isCollapsed) {
 | 
					    if (widget.isCollapsed) {
 | 
				
			||||||
      return InkWell(
 | 
					      return InkWell(
 | 
				
			||||||
        child: const Icon(Icons.tag_outlined, size: 20).paddingSymmetric(
 | 
					        child: _buildEntryAvatar(item),
 | 
				
			||||||
          horizontal: 20,
 | 
					        onTap: () => _focusRealm(item),
 | 
				
			||||||
          vertical: 16,
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        onTap: () => _gotoChannel(item),
 | 
					 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return ListTile(
 | 
					    return ListTile(
 | 
				
			||||||
      minTileHeight: 0,
 | 
					      minTileHeight: 0,
 | 
				
			||||||
      leading: const Icon(Icons.tag_outlined),
 | 
					      leading: _buildEntryAvatar(item),
 | 
				
			||||||
      contentPadding: padding,
 | 
					      contentPadding: padding,
 | 
				
			||||||
      title: Text(item.name),
 | 
					      title: Text(item.name),
 | 
				
			||||||
      subtitle: Text(
 | 
					      subtitle: Text(
 | 
				
			||||||
@@ -50,76 +128,97 @@ class AppNavigationRegion extends StatelessWidget {
 | 
				
			|||||||
        maxLines: 1,
 | 
					        maxLines: 1,
 | 
				
			||||||
        overflow: TextOverflow.ellipsis,
 | 
					        overflow: TextOverflow.ellipsis,
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
      onTap: () => _gotoChannel(item),
 | 
					      onTap: () => _focusRealm(item),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    final RealmProvider realms = Get.find();
 | 
				
			||||||
    final ChannelProvider channels = Get.find();
 | 
					    final ChannelProvider channels = Get.find();
 | 
				
			||||||
 | 
					    final AuthProvider auth = Get.find();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Obx(() {
 | 
					    return AnimatedBuilder(
 | 
				
			||||||
      final List<Channel> noRealmGroupChannels = channels.availableChannels
 | 
					      animation: _animationController,
 | 
				
			||||||
          .where((x) => x.type == 0 && x.realmId == null)
 | 
					      builder: (context, child) {
 | 
				
			||||||
          .toList();
 | 
					        return SlideTransition(
 | 
				
			||||||
      final List<Channel> hasRealmGroupChannels = channels.availableChannels
 | 
					          position: _animationTween,
 | 
				
			||||||
          .where((x) => x.type == 0 && x.realmId != null)
 | 
					          child: child,
 | 
				
			||||||
          .toList();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (isCollapsed) {
 | 
					 | 
				
			||||||
        return CustomScrollView(
 | 
					 | 
				
			||||||
          slivers: [
 | 
					 | 
				
			||||||
            const SliverPadding(padding: EdgeInsets.only(top: 8)),
 | 
					 | 
				
			||||||
            SliverList.builder(
 | 
					 | 
				
			||||||
              itemCount:
 | 
					 | 
				
			||||||
                  noRealmGroupChannels.length + hasRealmGroupChannels.length,
 | 
					 | 
				
			||||||
              itemBuilder: (context, index) {
 | 
					 | 
				
			||||||
                final element = index >= noRealmGroupChannels.length
 | 
					 | 
				
			||||||
                    ? hasRealmGroupChannels[index - noRealmGroupChannels.length]
 | 
					 | 
				
			||||||
                    : noRealmGroupChannels[index];
 | 
					 | 
				
			||||||
                return Tooltip(
 | 
					 | 
				
			||||||
                  message: element.name,
 | 
					 | 
				
			||||||
                  child: _buildEntry(context, element),
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
          ],
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
 | 
					      child: _focusedRealm == null
 | 
				
			||||||
 | 
					          ? Obx(() {
 | 
				
			||||||
 | 
					              if (widget.isCollapsed) {
 | 
				
			||||||
 | 
					                return 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),
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					                      },
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                  ],
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return CustomScrollView(
 | 
					              return CustomScrollView(
 | 
				
			||||||
        slivers: [
 | 
					                slivers: [
 | 
				
			||||||
          const SliverPadding(padding: EdgeInsets.only(top: 8)),
 | 
					                  const SliverPadding(padding: EdgeInsets.only(top: 8)),
 | 
				
			||||||
          SliverList.builder(
 | 
					                  SliverList.builder(
 | 
				
			||||||
            itemCount: noRealmGroupChannels.length,
 | 
					                    itemCount: realms.availableRealms.length,
 | 
				
			||||||
            itemBuilder: (context, index) {
 | 
					                    itemBuilder: (context, index) {
 | 
				
			||||||
              final element = noRealmGroupChannels[index];
 | 
					                      final element = realms.availableRealms[index];
 | 
				
			||||||
              return _buildEntry(context, element);
 | 
					                      return _buildEntry(context, element);
 | 
				
			||||||
            },
 | 
					                    },
 | 
				
			||||||
          ),
 | 
					                  ),
 | 
				
			||||||
          SliverList.list(
 | 
					                  const SliverPadding(padding: EdgeInsets.only(bottom: 8)),
 | 
				
			||||||
            children: hasRealmGroupChannels
 | 
					                ],
 | 
				
			||||||
                .groupListsBy((x) => x.realm)
 | 
					 | 
				
			||||||
                .entries
 | 
					 | 
				
			||||||
                .map((element) {
 | 
					 | 
				
			||||||
              return ExpansionTile(
 | 
					 | 
				
			||||||
                minTileHeight: 0,
 | 
					 | 
				
			||||||
                initiallyExpanded: true,
 | 
					 | 
				
			||||||
                tilePadding: const EdgeInsets.only(left: 20, right: 24),
 | 
					 | 
				
			||||||
                backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
 | 
					 | 
				
			||||||
                collapsedBackgroundColor:
 | 
					 | 
				
			||||||
                    Theme.of(context).colorScheme.surfaceContainer,
 | 
					 | 
				
			||||||
                title: Text(element.value.first.realm!.name),
 | 
					 | 
				
			||||||
                leading: const Icon(Icons.workspaces, size: 16)
 | 
					 | 
				
			||||||
                    .paddingSymmetric(horizontal: 4),
 | 
					 | 
				
			||||||
                children:
 | 
					 | 
				
			||||||
                    element.value.map((x) => _buildEntry(context, x)).toList(),
 | 
					 | 
				
			||||||
              );
 | 
					              );
 | 
				
			||||||
            }).toList(),
 | 
					            })
 | 
				
			||||||
          ),
 | 
					          : Column(
 | 
				
			||||||
          const SliverPadding(padding: EdgeInsets.only(bottom: 8)),
 | 
					              children: [
 | 
				
			||||||
        ],
 | 
					                if (widget.isCollapsed)
 | 
				
			||||||
      );
 | 
					                  Tooltip(
 | 
				
			||||||
    });
 | 
					                    message: _focusedRealm!.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(_focusedRealm!.name),
 | 
				
			||||||
 | 
					                    subtitle: Text(
 | 
				
			||||||
 | 
					                      _focusedRealm!.description,
 | 
				
			||||||
 | 
					                      maxLines: 1,
 | 
				
			||||||
 | 
					                      overflow: TextOverflow.ellipsis,
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                Expanded(
 | 
				
			||||||
 | 
					                  child: Obx(
 | 
				
			||||||
 | 
					                    () => ChannelListWidget(
 | 
				
			||||||
 | 
					                      channels: channels.availableChannels
 | 
				
			||||||
 | 
					                          .where(
 | 
				
			||||||
 | 
					                            (x) => x.realm?.externalId == _focusedRealm?.id,
 | 
				
			||||||
 | 
					                          )
 | 
				
			||||||
 | 
					                          .toList(),
 | 
				
			||||||
 | 
					                      selfId: auth.userProfile.value!['id'],
 | 
				
			||||||
 | 
					                      noCategory: true,
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					              ],
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,6 @@
 | 
				
			|||||||
    <link rel="manifest" href="manifest.json">
 | 
					    <link rel="manifest" href="manifest.json">
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
 | 
					 | 
				
			||||||
  <style id="splash-screen-style">
 | 
					  <style id="splash-screen-style">
 | 
				
			||||||
    html {
 | 
					    html {
 | 
				
			||||||
      height: 100%
 | 
					      height: 100%
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user