bind_partner_controller.dart 10.2 KB
import 'package:doublefeel_flutter/app/models/dialog_meta_data.dart';
import 'package:doublefeel_flutter/app/modules/purchase/purchase_route_args.dart';
import 'package:doublefeel_flutter/app/modules/user_onboarding/widget/guide_common_scaffold.dart';
import 'package:doublefeel_flutter/app/modules/user_onboarding/widget/onboarding_common_widgets.dart';
import 'package:doublefeel_flutter/app/routes/app_pages.dart';
import 'package:doublefeel_flutter/app/utils/dialog_utils.dart';
import 'package:doublefeel_flutter/core/error/app_error.dart';
import 'package:doublefeel_flutter/core/error/http_error_handling_policy.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/core/util/app_toast.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
import 'package:doublefeel_flutter/data/models/friend/friend_models.dart';
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
import 'package:doublefeel_flutter/pigeon/platform_api.g.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:lottie/lottie.dart';

class BindPartnerController extends GetxController {
  BindPartnerController(this._friendApi);
  final PlatformHostApi _platformHostApi = PlatformHostApi();

  final FriendApi _friendApi;

  final TextEditingController partnerIdController = TextEditingController();

  final RxString partnerIdInput = ''.obs;

  final RxBool isSubmitting = false.obs;

  final RxString myInviteCode = ''.obs;

  late bool isShowAppBarBack;
  late bool isShowSkipButton;
  var channel = '';

  @override
  void onInit() {
    super.onInit();
    final from = Get.arguments?['from'] ?? '';
    if (from == 'onboarding') {
      channel = '新手引导';
    } else if (from == 'FriendsTab') {
      channel = '好友页';
    } else if (from == 'today') {
      channel = '首页';
    } else {
      channel = '其他';
    }
    if (from == 'onboarding') {
      isShowAppBarBack = false;
      isShowSkipButton = true;
    } else {
      isShowAppBarBack = true;
      isShowSkipButton = false;
    }

    partnerIdController.addListener(() {
      partnerIdInput.value = partnerIdController.text.trim();
    });

    final userPrefs = Get.find<UserPreferencesStorage>();
    myInviteCode.value =
        userPrefs.preferences.value.meUserInfo?.uniqueCode ?? '';
  }

  @override
  void onReady() {
    super.onReady();
    ta.track('enter_doublefeel_add_friend_page',
        properties: {'channel_type': channel});
  }

  @override
  void onClose() {
    partnerIdController.dispose();
    super.onClose();
  }

  bool get canSubmit => partnerIdInput.value.isNotEmpty;

  void onShareMyCode() async {
    try {
      ta.track('click_doublefeel_add_friend_page',
          properties: {'channel_type': channel, 'click_content': '分享我的码'});
      final success = await PlatformHostApi()
          .shareText(l10n.sharePartnerCodeTemplate(myInviteCode.value));
    } catch (e) {
      AppToast.show(e.toString());
    }
  }

  Future<void> onSubmitPartnerId() async {
    ta.track('click_doublefeel_add_friend_page',
        properties: {'channel_type': channel, 'click_content': '继续'});
    if (!canSubmit || isSubmitting.value) return;
    isSubmitting.value = true;
    try {
      final result = await _friendApi.addFriendHandleError(
          partnerIdInput.value,
          HttpErrorHandlingPolicy(
              excludeErrorCodeList: {-1011201, -1011202, -1011203}));

      switch (result) {
        case AppFailure(error: final err):
          if (err is AppHttpError) {
            switch (err.businessCode) {
              case -1011201:
                DialogUtils.showCommonDialog(DialogMetaData(
                  title: l10n.bindPartnerIdNotExistTitle,
                  message: l10n.bindPartnerIdNotExistMessage,
                  confirmText: l10n.bindPartnerDialogGotIt,
                  iconAsset: null,
                ));
                break;
              case -1011202:
                DialogUtils.showCommonDialog(DialogMetaData(
                  title: l10n.bindPartnerAddFailedTitle,
                  message: l10n.bindPartnerAddFailedMessage,
                  confirmText: l10n.bindPartnerDialogGotIt,
                  iconAsset: null,
                ));
                break;
              case -1011203:
                DialogUtils.showCommonDialog(DialogMetaData(
                  title: l10n.bindPartnerAlreadyFriendTitle,
                  message: l10n.bindPartnerAlreadyFriendMessage,
                  confirmText: l10n.bindPartnerDialogGotIt,
                  iconAsset: null,
                ));
                break;
            }
          }
          break;
        case AppSuccess(data: final friendItem):
          _platformHostApi.refreshWatchAppAndWidgets();
          Get.to(_BindSuccessPage(friendItem, isShowAppBarBack));
          ta.track('success_add_doubelfeel_friend');
          break;
      }
    } catch (e) {
    } finally {
      isSubmitting.value = false;
    }
  }

  Future<void> onSkip() async {
    ta.track('click_doublefeel_add_friend_page',
        properties: {'channel_type': channel, 'click_content': '下次再说'});
    final userStateService = Get.find<UserStateService>();
    if (!userStateService.isVip) {
      // 使用 offNamed 移除 BIND_PARTNER,并将 onboarding 标识传入 PURCHASE
      // 关闭 PURCHASE 后的跳转逻辑由 PurchaseController.onBackPressed() 接管
      Get.offNamed(
        Routes.PURCHASE,
        arguments: {
          PurchaseRouteArgs.showCloseButton: true,
          'from': 'onboarding',
        },
      );
    } else {
      Get.offAllNamed(AppRoutes.home);
    }
  }
}

class _BindSuccessPage extends StatelessWidget {
  const _BindSuccessPage(this.friendItem, this.isShowAppBarBack);
  final FriendItem friendItem;
  final bool isShowAppBarBack;
  @override
  Widget build(BuildContext context) {
    final l10n = context.l10n;
    final userPrefs = Get.find<UserPreferencesStorage>();
    final preferences = userPrefs.preferences.value;
    final me = preferences.meUserInfo;
    return GuideCommonScaffold(
      onBackPressed: () {
        Get.back();
        if (isShowAppBarBack) {
          Get.back();
        }
      },
      bottom: OnboardingBottomButton(
        label: l10n.continueButton,
        enabled: true,
        onPressed: () async {
          if (isShowAppBarBack) {
            Get.back();
            Get.back();
            return;
          }
          final userStateService = Get.find<UserStateService>();
          if (!userStateService.isVip) {
            Get.offNamed(
              Routes.PURCHASE,
              arguments: {
                PurchaseRouteArgs.showCloseButton: true,
                'from': 'onboarding',
              },
            );
          } else {
            Get.offAllNamed(AppRoutes.home);
          }
        },
      ),
      child: OnboardingPageScrollBody(
        horizontalPadding: 0,
        topPadding: 0,
        child: Stack(
          children: [
            Lottie.asset('assets/lottie/scatter_flowers.json'),
            Column(
              mainAxisAlignment: MainAxisAlignment.start,
              mainAxisSize: MainAxisSize.min,
              children: [
                SizedBox(
                  height: 509,
                  child: TweenAnimationBuilder<double>(
                    tween: Tween(begin: 160.0, end: 46.0),
                    duration: const Duration(milliseconds: 800),
                    curve: Curves.easeOut,
                    builder: (context, offset, _) {
                      final avatar = Container(
                        width: 100,
                        height: 100,
                        decoration: ShapeDecoration(
                          image: DecorationImage(
                            image: NetworkImage(me?.avatar ?? ''),
                            fit: BoxFit.cover,
                          ),
                          shape: OvalBorder(
                            side: BorderSide(width: 1.79, color: Colors.white),
                          ),
                        ),
                      );
                      final friendAvatar = Container(
                        width: 100,
                        height: 100,
                        decoration: ShapeDecoration(
                          image: DecorationImage(
                            image: NetworkImage(friendItem.avatar ?? ''),
                            fit: BoxFit.cover,
                          ),
                          shape: OvalBorder(
                            side: BorderSide(width: 1.79, color: Colors.white),
                          ),
                        ),
                      );
                      return Stack(
                        alignment: Alignment(0.5, 0.5),
                        children: [
                          Transform.translate(
                            offset: Offset(offset, 0),
                            child: friendAvatar,
                          ),
                          Transform.translate(
                            offset: Offset(-offset, 0),
                            child: avatar,
                          ),
                        ],
                      );
                    },
                  ),
                ),
                const SizedBox(height: 16),
                OnboardingTitleText(
                  context.l10n.healthCompanionIsNowAvailable,
                  maxWidth: 310,
                ),
                const SizedBox(height: 8),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 16),
                  child: Text(
                    l10n.youCanNowViewEachOtherSHrvStressLevelsAndSleepPatternsAndReachOutToCheckInWhenTheOtherPersonSeemsTired,
                    textAlign: TextAlign.center,
                    style: TextStyle(
                      color: Colors.black,
                      fontSize: 12,
                      fontWeight: FontWeight.w400,
                    ),
                  ),
                )
              ],
            ),
          ],
        ),
      ),
    );
  }
}