pk_controller.dart 5.95 KB
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../../core/network/api/friend_api.dart';
import '../../../../core/result/app_result.dart';
import '../../../../data/local/user_preferences_storage.dart';
import '../../../../data/models/friend/friend_models.dart';
import '../../home/widgets/trend/trend_friend_select_bottom_sheet.dart';
import '../models/pk_data.dart';
import '../models/pk_data_mapper.dart';
import '../../../../core/network/api/health_api.dart';
import '../../../../data/models/health/health_models.dart';

class PkController extends GetxController {
  PkController(this._friendApi, this._preferences, this._healthApi);
  final HealthApi _healthApi;
  final pkLoadFailed = false.obs;
  final _partnerPk = Rxn<PkCurrentMonthData>();
  int? _pkPartnerId;
  DateTime? _pkLoadedAt;
  final FriendApi _friendApi;
  final UserPreferencesStorage _preferences;
  final friends = <FriendItem>[].obs;
  final selectedFriend = Rxn<FriendItem>();
  final selfHealth = Rxn<FriendHealthData>();
  final isLoading = false.obs;
  final loadFailed = false.obs;

  bool get isPartnerSelected =>
      selectedFriend.value?.friendUserId != null &&
      selectedFriend.value?.friendUserId ==
          _preferences.preferences.value.partnerUserInfo?.id;

  PkData get data {
    final friend = selectedFriend.value;
    final me = _preferences.preferences.value.meUserInfo;
    final remark = friend?.remarkName?.trim();
    final base = PkData(
      month: DateTime.now(),
      left: PkPlayer(
        name: remark?.isNotEmpty == true
            ? remark!
            : friend?.friendNickname ?? '好友',
        subtitle: remark?.isNotEmpty == true
            ? '(${friend?.friendNickname ?? '好友'})'
            : '',
        avatarUrl: friend?.avatar,
        steps: friend?.healthData?.totalSteps,
        calories: friend?.healthData?.totalMove,
      ),
      right: PkPlayer(
        name: me?.nickname ?? '我',
        subtitle: '(我)',
        avatarUrl: me?.avatar,
        steps: selfHealth.value?.totalSteps,
        calories: selfHealth.value?.totalMove,
      ),
    );
    final now = DateTime.now();
    final loaded = _pkLoadedAt;
    final pk = _partnerPk.value;
    if (pk != null &&
        _pkPartnerId != null &&
        friend?.friendUserId == _pkPartnerId &&
        _preferences.preferences.value.partnerUserInfo?.id == _pkPartnerId &&
        loaded?.year == now.year &&
        loaded?.month == now.month &&
        loaded?.day == now.day) {
      return mapPartnerPkData(
        pk,
        partner: base.left,
        self: base.right,
        now: now,
      );
    }
    return base;
  }

  Future<void> refreshData() async {
    if (isLoading.value) return;
    isLoading.value = true;
    try {
      final partner = _preferences.preferences.value.partnerUserInfo;
      final requestedAt = DateTime.now();
      final (result, pkResult) = await (
        _friendApi.friendList(true, withSelfHealthData: true),
        partner?.id == null
            ? Future<AppResult<PkCurrentMonthData>?>.value(null)
            : _healthApi.getCurrentMonthPkData(),
      ).wait;
      if (isClosed) return;
      switch (result) {
        case AppSuccess(:final data):
          final id = selectedFriend.value?.friendUserId;
          friends.assignAll(
            data.list.where((friend) => friend.friendUserId != null),
          );
          if (partner?.id != null &&
              !friends.any((friend) => friend.friendUserId == partner!.id)) {
            friends.insert(
              0,
              FriendItem(
                friendUserId: partner!.id,
                friendNickname: partner.nickname,
                avatar: partner.avatar,
              ),
            );
          }
          selectedFriend.value =
              friends.firstWhereOrNull((friend) => friend.friendUserId == id) ??
                  friends.firstWhereOrNull(
                    (friend) => friend.friendUserId == partner?.id,
                  ) ??
                  friends.firstOrNull;
          selfHealth.value = data.healthData;
          loadFailed.value = false;
        case AppFailure():
          loadFailed.value = true;
      }
      if (selectedFriend.value == null &&
          partner?.id != null &&
          _preferences.preferences.value.partnerUserInfo?.id == partner?.id) {
        final item = FriendItem(
          friendUserId: partner!.id,
          friendNickname: partner.nickname,
          avatar: partner.avatar,
        );
        if (!friends.any((friend) => friend.friendUserId == partner.id)) {
          friends.insert(0, item);
        }
        selectedFriend.value = item;
      }
      if (_preferences.preferences.value.partnerUserInfo?.id == partner?.id) {
        switch (pkResult) {
          case AppSuccess<PkCurrentMonthData>(:final data):
            _pkPartnerId = partner?.id;
            _pkLoadedAt = requestedAt;
            _partnerPk.value = data;
            pkLoadFailed.value = false;
          case AppFailure<PkCurrentMonthData>():
            pkLoadFailed.value = true;
          case null:
            _partnerPk.value = null;
            _pkPartnerId = null;
            pkLoadFailed.value = false;
        }
      }
    } finally {
      if (!isClosed) isLoading.value = false;
    }
  }

  void showFriendListBottomSheet() {
    unawaited(refreshData());
    if (friends.isEmpty) return;
    Get.bottomSheet(
      DraggableScrollableSheet(
        maxChildSize: .6,
        initialChildSize: .6,
        expand: false,
        snap: true,
        builder: (context, scrollController) => TrendFriendSelectBottomSheet(
          scrollController: scrollController,
          friendsList: friends,
          selectedFriend: selectedFriend,
          onSelectSelf: () {},
          showSelf: false,
          onSelectFriend: (friend) => selectedFriend.value = friend,
        ),
      ),
      barrierColor: Colors.black.withValues(alpha: .7),
      enableDrag: true,
      isScrollControlled: true,
      persistent: false,
    );
  }
}