trend_controller.dart 5.28 KB
import 'package:doublefeel_flutter/core/constants/event_const.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import '../../../../../core/network/api/click_event_api.dart';
import '../../../../../core/network/api/friend_api.dart';
import '../../../../../core/result/app_result.dart';
import '../../../../../data/models/friend/friend_models.dart';
import '../../../health_trend/controllers/health_trend_analytics.dart';
import '../../../health_trend/controllers/health_trend_control.dart';
import '../../../report_common/config/report_date_range_config.dart';
import '../../../report_common/models/health_report_query.dart';
import '../../../report_common/models/report_period.dart';
import '../../widgets/trend/trend_friend_select_bottom_sheet.dart';

enum TrendType {
  hrv,
  activity,
  sleep;

  int get tabIndex => index;
}

/// 趋势页顶层 Controller,仅负责:
/// 顶层 HRV / 活动 / 睡眠 类型切换 (selectedTypeIndex)
class TrendController extends GetxController with HealthTrendControl {
  TrendController(this._friendApi);

  final FriendApi _friendApi;
  bool _isPageVisible = false;
  final refreshToken = 0.obs;

  // 当前查看的用户。null 表示查看自己;非 null 表示查看指定用户。
  final targetUserId = RxnInt();
  final targetFriendInfo = Rxn<FriendItem>();
  final friendsList = <FriendItem>[].obs;
  final isFriendListLoaded = false.obs;
  int friendsListLimit = 10;

  bool get canSwitchFriend =>
      isFriendListLoaded.value && friendsList.isNotEmpty;

  HealthReportQuery get query => HealthReportQuery(
        targetUserId: targetUserId.value,
        period: selectedPeriod.value,
        date: selectedDate.value,
      );

  HealthReportQuery queryForType(int typeIndex) => HealthReportQuery(
        targetUserId: targetUserId.value,
        period: periodForType(typeIndex),
        date: dateForType(typeIndex),
      );

  @override
  void onInit() {
    super.onInit();
    _refreshFriendList();
  }

  @override
  void changeType(int index) {
    final previousIndex = selectedTypeIndex.value;
    super.changeType(index);
    if (_isPageVisible && selectedTypeIndex.value != previousIndex) {
      _trackEnterPage();
    }
  }

  void selectType(TrendType type, {DateTime? date, ReportPeriod? period}) {
    changeType(type.tabIndex);
    if (period != null) {
      changePeriod(period);
    }
    if (date != null) {
      changeDate(date);
    }
  }

  void changeTargetUser(int? userId) {
    if (userId == targetUserId.value) return;
    targetUserId.value = userId;
    refreshToken.value++;
    _trackViewSleepStatsIfNeeded();
  }

  void selectFriend(FriendItem friendInfo) {
    if (friendInfo.friendUserId == targetFriendInfo.value?.friendUserId) {
      return;
    }
    targetFriendInfo.value = friendInfo;
    changeTargetUser(friendInfo.friendUserId);
  }

  void selectSelf() {
    if (targetFriendInfo.value == null && targetUserId.value == null) {
      return;
    }
    targetFriendInfo.value = null;
    changeTargetUser(null);
  }

  void showFriendListBottomSheet() {
    _refreshFriendList();
    if (friendsList.isEmpty) return;

    Get.bottomSheet(
      DraggableScrollableSheet(
        maxChildSize: 0.6,
        initialChildSize: 0.6,
        expand: false,
        snap: true,
        builder: (context, scrollController) {
          return TrendFriendSelectBottomSheet(
            scrollController: scrollController,
            friendsList: friendsList,
            selectedFriend: targetFriendInfo,
            onSelectSelf: selectSelf,
            onSelectFriend: selectFriend,
          );
        },
      ),
      barrierColor: Colors.black.withValues(alpha: 0.7),
      enableDrag: true,
      isScrollControlled: true,
      persistent: false,
    );
  }

  void _refreshFriendList() {
    _friendApi.friendList(false).then(
      (res) {
        switch (res) {
          case AppSuccess(:final data):
            friendsList.assignAll(data.list);
            friendsListLimit = data.limit ?? 10;
            isFriendListLoaded.value = true;
          case AppFailure():
            isFriendListLoaded.value = true;
        }
      },
    );
  }

  void setPageVisible(
    bool visible, {
    bool resetQueryOnShow = false,
  }) {
    if (!visible) {
      _markPageHidden();
      return;
    }
    if (resetQueryOnShow) {
      _resetToDefaultQuery();
    }
    _markPageVisible();
  }

  void _markPageVisible() {
    if (_isPageVisible) return;
    _isPageVisible = true;
    refreshToken.value++;
    _trackEnterPage();
  }

  void _markPageHidden() {
    _isPageVisible = false;
  }

  void _resetToDefaultQuery() {
    changeType(TrendType.hrv.tabIndex);
    changeQuery(ReportPeriod.week, ReportDateRangeConfig.lastWeekStart());
  }

  void _trackEnterPage() {
    HealthTrendAnalytics.trackEnterPage(
      selectedTypeIndex.value,
      userRole: targetFriendInfo.value == null ? '我的' : '好友的',
    );
    _trackViewSleepStatsIfNeeded();
  }

  void _trackViewSleepStatsIfNeeded() {
    if (!_isPageVisible ||
        selectedTypeIndex.value != TrendType.sleep.tabIndex) {
      return;
    }

    final eventName = targetUserId.value == null
        ? EventConst.viewSleepStats
        : EventConst.viewFriendSleepStats;
    if (!Get.isRegistered<ClickEventApi>()) return;
    Get.find<ClickEventApi>().postClickEvent(eventName);
  }
}