193 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'package:easy_localization/easy_localization.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
						|
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
						|
import 'package:island/models/post.dart';
 | 
						|
import 'package:island/pods/network.dart';
 | 
						|
import 'package:island/talker.dart';
 | 
						|
import 'package:material_symbols_icons/symbols.dart';
 | 
						|
import 'package:riverpod_annotation/riverpod_annotation.dart';
 | 
						|
import 'package:island/widgets/post/post_item.dart';
 | 
						|
import 'package:styled_widget/styled_widget.dart';
 | 
						|
import 'package:island/pods/config.dart'; // Import config.dart for shared preferences keys and provider
 | 
						|
 | 
						|
part 'post_featured.g.dart';
 | 
						|
 | 
						|
@riverpod
 | 
						|
Future<List<SnPost>> featuredPosts(Ref ref) async {
 | 
						|
  final apiClient = ref.watch(apiClientProvider);
 | 
						|
  final resp = await apiClient.get('/sphere/posts/featured');
 | 
						|
  return resp.data.map((e) => SnPost.fromJson(e)).cast<SnPost>().toList();
 | 
						|
}
 | 
						|
 | 
						|
class PostFeaturedList extends HookConsumerWidget {
 | 
						|
  const PostFeaturedList({super.key});
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    final featuredPostsAsync = ref.watch(featuredPostsProvider);
 | 
						|
 | 
						|
    final pageViewController = usePageController();
 | 
						|
    final prefs = ref.watch(sharedPreferencesProvider);
 | 
						|
    final pageViewCurrent = useState(0);
 | 
						|
    final previousFirstPostId = useState<String?>(null);
 | 
						|
    final storedCollapsedId = useState<String?>(
 | 
						|
      prefs.getString(kFeaturedPostsCollapsedId),
 | 
						|
    );
 | 
						|
    final isCollapsed = useState(false);
 | 
						|
 | 
						|
    useEffect(() {
 | 
						|
      pageViewController.addListener(() {
 | 
						|
        pageViewCurrent.value = pageViewController.page?.round() ?? 0;
 | 
						|
      });
 | 
						|
      return null;
 | 
						|
    }, [pageViewController]);
 | 
						|
 | 
						|
    // Log isCollapsed state changes
 | 
						|
    useEffect(() {
 | 
						|
      talker.info('isCollapsed changed to ${isCollapsed.value}');
 | 
						|
      return null;
 | 
						|
    }, [isCollapsed]);
 | 
						|
 | 
						|
    useEffect(() {
 | 
						|
      if (featuredPostsAsync.hasValue && featuredPostsAsync.value!.isNotEmpty) {
 | 
						|
        final currentFirstPostId = featuredPostsAsync.value!.first.id;
 | 
						|
        talker.info('Current first post ID: $currentFirstPostId');
 | 
						|
        talker.info('Previous first post ID: ${previousFirstPostId.value}');
 | 
						|
        talker.info('Stored collapsed ID: ${storedCollapsedId.value}');
 | 
						|
 | 
						|
        if (previousFirstPostId.value == null) {
 | 
						|
          // Initial load
 | 
						|
          previousFirstPostId.value = currentFirstPostId;
 | 
						|
          isCollapsed.value = (storedCollapsedId.value == currentFirstPostId);
 | 
						|
          talker.info('Initial load. isCollapsed set to ${isCollapsed.value}');
 | 
						|
        } else if (previousFirstPostId.value != currentFirstPostId) {
 | 
						|
          // First post changed, expand by default
 | 
						|
          previousFirstPostId.value = currentFirstPostId;
 | 
						|
          isCollapsed.value = false;
 | 
						|
          prefs.remove(
 | 
						|
            kFeaturedPostsCollapsedId,
 | 
						|
          ); // Clear stored ID if post changes
 | 
						|
          talker.info('First post changed. isCollapsed set to false.');
 | 
						|
        } else {
 | 
						|
          // Same first post, maintain current collapse state
 | 
						|
          // No change needed for isCollapsed.value unless manually toggled
 | 
						|
          talker.info('Same first post. Maintaining current collapse state.');
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        talker.info('featuredPostsAsync has no value or is empty.');
 | 
						|
      }
 | 
						|
      return null;
 | 
						|
    }, [featuredPostsAsync]);
 | 
						|
 | 
						|
    return ClipRRect(
 | 
						|
      borderRadius: const BorderRadius.all(Radius.circular(8)),
 | 
						|
      child: Card(
 | 
						|
        color: Theme.of(context).colorScheme.surfaceContainerHigh,
 | 
						|
        margin: EdgeInsets.zero,
 | 
						|
        child: Column(
 | 
						|
          children: [
 | 
						|
            SizedBox(
 | 
						|
              height: 48,
 | 
						|
              child: Row(
 | 
						|
                spacing: 8,
 | 
						|
                children: [
 | 
						|
                  const Icon(Symbols.highlight),
 | 
						|
                  const Text('highlightPost').tr(),
 | 
						|
                  Spacer(),
 | 
						|
                  IconButton(
 | 
						|
                    padding: EdgeInsets.zero,
 | 
						|
                    visualDensity: VisualDensity.compact,
 | 
						|
                    constraints: const BoxConstraints(),
 | 
						|
                    onPressed: () {
 | 
						|
                      pageViewController.animateToPage(
 | 
						|
                        pageViewCurrent.value - 1,
 | 
						|
                        duration: const Duration(milliseconds: 250),
 | 
						|
                        curve: Curves.easeInOut,
 | 
						|
                      );
 | 
						|
                    },
 | 
						|
                    icon: const Icon(Symbols.arrow_left),
 | 
						|
                  ),
 | 
						|
                  IconButton(
 | 
						|
                    padding: EdgeInsets.zero,
 | 
						|
                    visualDensity: VisualDensity.compact,
 | 
						|
                    constraints: const BoxConstraints(),
 | 
						|
                    onPressed: () {
 | 
						|
                      pageViewController.animateToPage(
 | 
						|
                        pageViewCurrent.value + 1,
 | 
						|
                        duration: const Duration(milliseconds: 250),
 | 
						|
                        curve: Curves.easeInOut,
 | 
						|
                      );
 | 
						|
                    },
 | 
						|
                    icon: const Icon(Symbols.arrow_right),
 | 
						|
                  ),
 | 
						|
                  IconButton(
 | 
						|
                    padding: EdgeInsets.zero,
 | 
						|
                    visualDensity: VisualDensity.compact,
 | 
						|
                    constraints: const BoxConstraints(),
 | 
						|
                    onPressed: () {
 | 
						|
                      isCollapsed.value = !isCollapsed.value;
 | 
						|
                      talker.info(
 | 
						|
                        'Manual toggle. isCollapsed set to ${isCollapsed.value}',
 | 
						|
                      );
 | 
						|
                      if (isCollapsed.value &&
 | 
						|
                          featuredPostsAsync.hasValue &&
 | 
						|
                          featuredPostsAsync.value!.isNotEmpty) {
 | 
						|
                        prefs.setString(
 | 
						|
                          kFeaturedPostsCollapsedId,
 | 
						|
                          featuredPostsAsync.value!.first.id,
 | 
						|
                        );
 | 
						|
                        talker.info(
 | 
						|
                          'Stored collapsed ID: ${featuredPostsAsync.value!.first.id}',
 | 
						|
                        );
 | 
						|
                      } else {
 | 
						|
                        prefs.remove(kFeaturedPostsCollapsedId);
 | 
						|
                        talker.info('Removed stored collapsed ID.');
 | 
						|
                      }
 | 
						|
                    },
 | 
						|
                    icon: Icon(
 | 
						|
                      isCollapsed.value
 | 
						|
                          ? Symbols.expand_more
 | 
						|
                          : Symbols.expand_less,
 | 
						|
                    ),
 | 
						|
                  ),
 | 
						|
                ],
 | 
						|
              ).padding(horizontal: 16, vertical: 8),
 | 
						|
            ),
 | 
						|
            AnimatedSize(
 | 
						|
              duration: const Duration(milliseconds: 300),
 | 
						|
              curve: Curves.easeInOut,
 | 
						|
              child: Visibility(
 | 
						|
                visible: !isCollapsed.value,
 | 
						|
                child: featuredPostsAsync.when(
 | 
						|
                  loading:
 | 
						|
                      () => const Center(child: CircularProgressIndicator()),
 | 
						|
                  error: (error, stack) => Center(child: Text('Error: $error')),
 | 
						|
                  data: (posts) {
 | 
						|
                    return SizedBox(
 | 
						|
                      height: 320,
 | 
						|
                      child: PageView.builder(
 | 
						|
                        controller: pageViewController,
 | 
						|
                        scrollDirection: Axis.horizontal,
 | 
						|
                        itemCount: posts.length,
 | 
						|
                        itemBuilder: (context, index) {
 | 
						|
                          return SingleChildScrollView(
 | 
						|
                            child: PostActionableItem(
 | 
						|
                              item: posts[index],
 | 
						|
                              borderRadius: 8,
 | 
						|
                            ),
 | 
						|
                          );
 | 
						|
                        },
 | 
						|
                      ),
 | 
						|
                    );
 | 
						|
                  },
 | 
						|
                ),
 | 
						|
              ),
 | 
						|
            ),
 | 
						|
          ],
 | 
						|
        ),
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 |