home_controller.dart 8.41 KB
import 'package:doublefeel_flutter/app/modules/friends/controllers/friend_trend_controller.dart';
import 'package:doublefeel_flutter/app/modules/friends/controllers/friends_controller.dart';
import 'package:doublefeel_flutter/core/network/api/friend_api.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:doublefeel_flutter/core/services/thinking_data_service.dart';
import 'package:doublefeel_flutter/core/services/user_state_service.dart';
import 'package:doublefeel_flutter/data/local/local_storage.dart';
import 'package:doublefeel_flutter/pigeon/platform_api.g.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/scheduler.dart';

import 'package:get/get.dart';

import '../../../routes/app_pages.dart';
import '../../purchase/purchase_route_args.dart';
import '../../app_review_prompt/logic/app_review_prompt_logic.dart';
import '../../report_common/models/report_period.dart';
import 'today_controller.dart';
import 'trend/trend_controller.dart';

class HomeController extends GetxController {
  static const trendTabIndex = 1;
  static const friendsTabIndex = 2;
  static const _mainChannel = MethodChannel('doublefeel_flutter_main_channel');

  final AppReviewPromptLogic _appReviewPromptLogic = AppReviewPromptLogic(
    storage: Get.find<LocalStorage>(),
  );
  final LocalStorage _storage = Get.find<LocalStorage>();
  final UserStateService userStateService = Get.find<UserStateService>();
  final PlatformHostApi _platformHostApi = PlatformHostApi();

  /// 当前选中的底部 tab 索引
  final selectedIndex = 0.obs;
  final lastFlutterUrl = ''.obs;

  @override
  void onInit() {
    super.onInit();
    _mainChannel.setMethodCallHandler(_handleNativeMethodCall);
    SchedulerBinding.instance.addPostFrameCallback((_) async {
      try {
        final route = await _platformHostApi.requestUnhandedUrl();
        // final route ='doublefeel://flutter/home?tab=today&scroll_to_latest_hrv=true';
        // final route = 'doublefeel://flutter/friend-home?user_id=131';
        // final route = 'doublefeel://flutter/home?tab=trend&type=sleep';
        // final route ='doublefeel://flutter/friend-trend?type=sleep&user_id=131';
        _checkFlutterRoute(route);
      } catch (_) {}
    });
  }

  @override
  void onClose() {
    _mainChannel.setMethodCallHandler(null);
    super.onClose();
  }

  Future<Object?> _handleNativeMethodCall(MethodCall call) async {
    switch (call.method) {
      case 'handleFlutterUrl':
        return handleFlutterUrl(call.arguments);
      default:
        debugPrint(
          '[HomeController] unhandled native method: ${call.method}, '
          'arguments: ${call.arguments}',
        );
        return null;
    }
  }

  Future<bool> handleFlutterUrl(Object? arguments) async {
    final url = switch (arguments) {
      {'url': final String value} => value.trim(),
      final String value => value.trim(),
      _ => '',
    };
    lastFlutterUrl.value = url;
    _checkFlutterRoute(url);
    return url.isNotEmpty;
  }

  Future<void> maybeShowAppReviewPrompt(BuildContext context) {
    return _appReviewPromptLogic.maybeShowPrompt(context);
  }

  Future<bool> maybeShowMembershipOffer() async {
    if (userStateService.isVip) return false;

    final userId = userStateService.userId;
    if (userId <= 0) return false;

    final today = _dateKey(DateTime.now());
    final alreadyShownToday = _storage.homeMembershipOfferPromptDate == today;
    final sameAccount = _storage.homeMembershipOfferPromptUserId == userId;
    if (alreadyShownToday && sameAccount) return false;

    await _storage.setHomeMembershipOfferPromptState(
      date: today,
      userId: userId,
    );
    await Get.toNamed(
      Routes.PURCHASE,
      arguments: {PurchaseRouteArgs.showCloseButton: true},
    );
    return true;
  }

  void changeTab(int index) {
    final previousIndex = selectedIndex.value;
    selectedIndex.value = index;
    if (index == trendTabIndex && previousIndex != trendTabIndex) {
      Get.find<TrendController>().markPageVisible();
    } else if (previousIndex == trendTabIndex && index != trendTabIndex) {
      Get.find<TrendController>().markPageHidden();
    }
    if (index == friendsTabIndex && previousIndex != friendsTabIndex) {
      Get.find<FriendsController>().markPageVisible();
    } else if (previousIndex == friendsTabIndex && index != friendsTabIndex) {
      Get.find<FriendsController>().markPageHidden();
    }
   switch (index) {
      case 0:
        // ta.track('enter_doublefeel_my_page');
        break;
      case 3:
        ta.track('enter_doublefeel_my_page');
        break;
    }
  }

  void openTrend(TrendType type, {DateTime? date, ReportPeriod? period}) {
    Get.find<TrendController>().selectType(type, date: date, period: period);
    changeTab(trendTabIndex);
  }

  int _dateKey(DateTime date) =>
      date.year * 10000 + date.month * 100 + date.day;

  _checkFlutterRoute(String? route) {
    try {
      if (route == null || route.isEmpty) return;

      final uri = Uri.tryParse(route);
      if (uri == null) return;

      final path = uri.path; // "/home", "/account-settings" 等
      final params = uri.queryParameters; // {"tab": "today"} 等

      _dispatchRoute(path, params);
    } catch (_) {}
  }

  /// 根据 path + query 参数分派导航动作
  void _dispatchRoute(String path, Map<String, String> params) {
    switch (path) {
      case AppRoutes.home:
        _switchHomeTab(params['tab'], params);
        break;
      default:
        // 其他已注册路由 → 跳转页面,query 参数作为 arguments 传入
        final isKnown = Get.routeTree.routes.any((r) => r.name == path);
        if (!isKnown) return;
        if (path == Routes.FRIEND_HOME || path == Routes.FRIEND_TREND) {
          final paramUserId = params['user_id'];
          if (paramUserId != null) {
            final friendUserId = int.tryParse(paramUserId);
            if (friendUserId != null) {
              final _friendApi = Get.find<FriendApi>();
              _friendApi.getFriendInfo(friendUserId).then((result) {
                switch (result) {
                  case AppSuccess(data: final info):
                    {
                      if (path == Routes.FRIEND_TREND) {
                        TrendType? trendType =
                            getTrendType(params['type'] ?? '');
                        if (trendType != null) {
                          Get.toNamed(path,
                              arguments: FriendTrendArguments(
                                friendItem: info,
                                initialTypeIndex: trendType.tabIndex,
                              ));
                        }
                      } else {
                        Get.toNamed(path, arguments: info);
                      }
                    }
                    break;
                  case AppFailure():
                    break;
                }
              });
            }
          }
        } else {
          Get.toNamed(path, arguments: params.isNotEmpty ? params : null);
        }
    }
  }

  /// 根据 tab 名称切换首页底部 tab
  void _switchHomeTab(String? tab, Map<String, String> params) {
    switch (tab) {
      case 'today':
        changeTab(0);
        final scrollToLatestHrv =
            params['scroll_to_latest_hrv']?.toLowerCase() == 'true';
        if (scrollToLatestHrv) {
          SchedulerBinding.instance.addPostFrameCallback((_) {
            final todayCtrl = Get.find<TodayController>();
            if (todayCtrl.scrollController.hasClients) {
              todayCtrl.scrollController.animateTo(
                850,
                duration: const Duration(milliseconds: 400),
                curve: Curves.easeInOut,
              );
            }
          });
        }
        break;
      case 'trend':
        final type = params['type'];
        final trendType = type == null ? null : getTrendType(type);
        if (trendType != null) {
          openTrend(trendType);
        } else {
          changeTab(trendTabIndex);
        }
        break;
      case 'friends':
        changeTab(friendsTabIndex);
        break;
      case 'my':
        changeTab(3);
        break;
      default:
        break;
    }
  }

  TrendType? getTrendType(String type) {
    switch (type) {
      case 'sleep':
        return TrendType.sleep;

      case 'activity':
        return TrendType.activity;

      case 'hrv':
        return TrendType.hrv;
      default:
        break;
    }
    return null;
  }
}