user_onboarding_controller.dart 4.66 KB
import 'package:doublefeel_flutter/core/services/user_state_service.dart';
import 'package:doublefeel_flutter/data/local/user_account_storage.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';

import '../../../../core/services/health_kit_upload_service.dart';
import '../../../../core/util/platform.dart';
import '../../../routes/app_pages.dart';

class UserOnboardingController extends GetxController {
  int totalPages = 9;

  final currentPageIndex = 0.obs;
  final selections = <int, Set<int>>{}.obs;
  final selectionVersion = 0.obs;

  bool hasSelection(int pageIndex) =>
      selections[pageIndex]?.isNotEmpty ?? false;

  bool isSelected(int pageIndex, int optionIndex) =>
      selections[pageIndex]?.contains(optionIndex) ?? false;

  bool canContinue(int pageIndex) {
    if (Get.arguments?['type'] == 'MembershipOfferPage') {
      return true;
    }
    return switch (pageIndex) {
      1 || 2 || 3 => hasSelection(pageIndex),
      _ => true,
    };
  }

  @override
  void onInit() {
    if (Get.arguments?['type'] == 'MembershipOfferPage') {
      totalPages = (Get.arguments?['needBindSuccessGuide'] == true ? 2 : 1);
    } else {
      final argument = Get.arguments?['resumeStage'];
      if (argument != null && argument is int) {
        currentPageIndex.value = argument.clamp(0, totalPages - 1);
      }
      totalPages = 9;
    }
    super.onInit();
  }

  void toggleOption({
    required int pageIndex,
    required int optionIndex,
    required bool multiple,
    bool exclusive = false,
    Set<int> exclusiveOptionIndexes = const {},
  }) {
    final next = Set<int>.of(selections[pageIndex] ?? const <int>{});

    if (multiple) {
      if (next.contains(optionIndex)) {
        next.remove(optionIndex);
      } else if (exclusive) {
        next
          ..clear()
          ..add(optionIndex);
      } else {
        next.removeAll(exclusiveOptionIndexes);
        next.add(optionIndex);
      }
    } else {
      next
        ..clear()
        ..add(optionIndex);
    }

    if (next.isEmpty) {
      selections.remove(pageIndex);
    } else {
      selections[pageIndex] = next;
    }
    selectionVersion.value++;
    selections.refresh();
  }

  void goBack() {
    if (currentPageIndex.value > 0) {
      currentPageIndex.value--;
      return;
    }
    if (Get.key.currentState?.canPop() ?? false) {
      Get.back();
    } else {
      // SystemNavigator.pop();
      Get.offAllNamed(AppRoutes.home);
    }
  }

  Future<void> goNext() async {
    final pageIndex = currentPageIndex.value;
    if (!await prepareContinue(pageIndex)) {
      return;
    }

    // 保存当前页进度(中途退出后可断点续做)
    final userId =
        Get.find<UserPreferencesStorage>().preferences.value.meUserInfo?.id ??
            0;
    if (userId > 0) {
      await Get.find<UserAccountStorage>()
          .saveOnboardingStage(userId, pageIndex);
    }

    if (pageIndex >= totalPages - 1) {
      finishOnboarding();
      return;
    }
    currentPageIndex.value++;
  }

  Future<bool> prepareContinue(int pageIndex) async {
    if (!canContinue(pageIndex)) {
      return false;
    }

    if (pageIndex == 7) {
      await requestHealthAuthorization();
    } else if (pageIndex == 8) {
      await requestNotificationAuthorization();
    }

    return true;
  }

  void finishOnboarding() {
    // 标记当前账号引导已全部完成
    final userId =
        Get.find<UserPreferencesStorage>().preferences.value.meUserInfo?.id ??
            0;
    if (userId > 0) {
      Get.find<UserAccountStorage>().markOnboardingCompleted(userId);
    }

    if (Get.arguments?['type'] == 'MembershipOfferPage') {
      Get.offAllNamed(AppRoutes.home);
    } else {
      final userStateService = Get.find<UserStateService>();
      if (userStateService.isBound) {
        Get.offAllNamed(AppRoutes.home);
      } else {
        Get.offAllNamed(AppRoutes.bindPartner);
      }
    }
  }

  Future<void> requestHealthAuthorization() async {
    if (isAndroid) {
      return;
    }
    try {
      await Get.find<HealthKitUploadService>().requestClientAuthorization();
    } catch (error) {
      debugPrint('Health authorization skipped: $error');
    }
  }

  Future<void> requestNotificationAuthorization() async {
    try {
      final result = await Permission.notification.request();
      if (result.isGranted) {
        debugPrint('Notification authorization granted');
      } else {
        debugPrint('Notification authorization skipped');
      }
    } catch (error) {
      debugPrint('Notification authorization skipped: $error');
    }
  }
}