my_controller.dart 5.57 KB
import 'dart:async';
import 'dart:io';

import 'package:doublefeel_flutter/app/modules/watch_theme/models/watch_theme_models.dart';
import 'package:doublefeel_flutter/app/routes/app_pages.dart';
import 'package:doublefeel_flutter/core/constants/intent_keys.dart';
import 'package:doublefeel_flutter/core/logging/app_logger.dart';
import 'package:doublefeel_flutter/core/network/api/theme_api.dart';
import 'package:doublefeel_flutter/core/network/api/user_api.dart';
import 'package:doublefeel_flutter/core/network/api/vip_api.dart';
import 'package:doublefeel_flutter/core/platform/pigeon_api_facade.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
import 'package:doublefeel_flutter/data/models/local/user_preferences.dart';
import 'package:doublefeel_flutter/data/models/user/user_models.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';

class MyController extends GetxController {
  MyController(this._userApi, this._vipApi, this._themeApi);

  final UserApi _userApi;
  final VipApi _vipApi;
  final ThemeApi _themeApi;
  final ImagePicker _picker = ImagePicker();
  final watchThemeItems = <WatchThemeItem>[].obs;
  final isLoadingWatchThemes = false.obs;
  final AppPlatformHostApi _platformHostApi = AppPlatformHostApi();

  List<WatchThemeItem> get officialWatchThemes =>
      watchThemeItems.where((theme) => theme.isOfficialTheme).toList();

  @override
  void onInit() {
    super.onInit();
    unawaited(loadWatchThemes());
  }

  Future<void> loadWatchThemes() async {
    isLoadingWatchThemes.value = true;
    final result = await _themeApi.getThemeList(errorHandlingPolicy: null);
    isLoadingWatchThemes.value = false;
    if (result case AppSuccess<WatchThemeResponse>(data: final response)) {
      watchThemeItems.assignAll(response.themes);
    }
  }

  void updateNickname(String nickname) async {
    final result = await _userApi.updateOwnerUserInfo(nickname: nickname);
    if (result is AppSuccess<UserInfoResponse>) {
      await Get.find<UserPreferencesStorage>().updateMeUserInfo(result.data);
    }
  }

  /// 选择头像:支持拍照或从相册选取,选取后进入裁剪页
  Future<void> pickAvatar({bool fromCamera = false}) async {
    // 1. 用 image_picker 调起系统相机或相册
    final XFile? picked = await _picker.pickImage(
      source: fromCamera ? ImageSource.camera : ImageSource.gallery,
      imageQuality: 90,
      maxWidth: 1024,
      maxHeight: 1024,
    );
    if (picked == null) return; // 用户取消
    AppLogger.e(picked.path);
    // OHOS 版 image_cropper 使用 Flutter 页面承载裁剪 UI,必须显式传入
    // BuildContext(该插件目前通过 WebUiSettings 读取该参数)。
    final isOhos = Platform.operatingSystem.toLowerCase() == 'ohos';
    final cropperContext = Get.context ?? Get.overlayContext;
    CroppedFile? cropped;
    try {
      cropped = await ImageCropper().cropImage(
        sourcePath: picked.path,
        aspectRatio: const CropAspectRatio(ratioX: 1, ratioY: 1),
        uiSettings: [
          AndroidUiSettings(
            toolbarTitle: '裁剪头像',
            lockAspectRatio: true,
          ),
          IOSUiSettings(
            title: '裁剪头像',
            aspectRatioLockEnabled: true,
            resetAspectRatioEnabled: false,
          ),
          if (isOhos && cropperContext != null)
            WebUiSettings(context: cropperContext),
        ],
      );
    } finally {
      if (isOhos) {
        // 等待 cropper 路由的退场动画结束,防止其 AppBar 覆盖恢复的样式。
        await Future<void>.delayed(const Duration(milliseconds: 400));

        SystemChrome.setSystemUIOverlayStyle(
          const SystemUiOverlayStyle(
            statusBarColor: Colors.transparent,
            statusBarIconBrightness: Brightness.dark,
            statusBarBrightness: Brightness.light,
            systemNavigationBarColor: Colors.transparent,
            systemNavigationBarIconBrightness: Brightness.dark,
          ),
        );
      }
    }
    if (cropped == null) return; // 用户取消裁剪

    final ossPath = await _platformHostApi.uploadFile(
      cropped.path,
      HResourceType.image,
    );
    final updateResult = await _userApi.updateOwnerUserInfo(avatar: ossPath);
    if (updateResult is AppSuccess<UserInfoResponse>) {
      await Get.find<UserPreferencesStorage>()
          .updateMeUserInfo(updateResult.data);
    }
  }

  Future<void> goWatchThemePage() async {
    final hasCustomThemes = watchThemeItems.any((theme) => theme.isCustomTheme);
    await Get.toNamed(
      Routes.WATCH_THEME,
      arguments: {
        'isPremium': hasCustomThemes,
        'hasCustomThemes': hasCustomThemes,
      },
    );
    await loadWatchThemes();
  }

  Future<void> toPremiumPage() async {
    await Get.toNamed(Routes.PURCHASE, arguments: {
      IntentKeys.channelType: '我的页面会员入口',
    });
    final vipResult = await _vipApi.getVipInfo();
    if (vipResult case AppSuccess(data: final vip)) {
      try {
        final vipPrefs = UserPreferencesVipInfo.fromVipInfo(vip);
        await Get.find<UserPreferencesStorage>().updateVipInfo(vipPrefs);
      } on Exception catch (e) {}
    }
  }

  Future<void> refreshEmail() async {
    final result = await _userApi.getUserInfo();
    if (result case AppSuccess<UserInfoResponse>(data: final user)) {
      await Get.find<UserPreferencesStorage>().updateMeUserInfo(user);
    }
  }
}