friends_controller.dart 4.16 KB
import 'dart:async';

import 'package:doublefeel_flutter/app/actions/dialog_action.dart';
import 'package:doublefeel_flutter/app/models/dialog_meta_data.dart';
import 'package:doublefeel_flutter/app/models/input_dialog_meta_data.dart';
import 'package:doublefeel_flutter/app/utils/dialog_utils.dart';
import 'package:doublefeel_flutter/core/logging/app_logger.dart';
import 'package:get/get.dart';

class FriendsController extends GetxController {
  static const maxFriends = 10;

  final friends = <FriendHealthData>[].obs;
  final isLoading = false.obs;

  bool get isFull => friends.length >= maxFriends;

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

  Future<void> loadFriends() async {
    if (isLoading.value) return;
    isLoading.value = true;
    try {
      // TODO: Replace mock data with the friend list API when it is available.
      await Future<void>.delayed(const Duration(milliseconds: 150));
      friends.assignAll(const [
        FriendHealthData(
          userId: 10001,
          name: '女朋友(不爱吃热干面)',
          updatedAt: '更新于15:12',
          sleepQualityScore: 92,
          steps: '10254步',
          statusText: '注意压力',
          stressValue: 72,
          isOnWatchFace: true,
        ),
        FriendHealthData(
          userId: 10002,
          name: 'John',
          updatedAt: '更新于15:12',
          sleepQualityScore: null,
          steps: null,
          statusText: '等待数据',
          stressValue: null,
        ),
      ]);
    } catch (error, stackTrace) {
      AppLogger.e('FriendsController.loadFriends failed', error, stackTrace);
    } finally {
      isLoading.value = false;
    }
  }

  Future<void> showEditRemarkDialog(FriendHealthData friend) async {
    final result = await DialogUtils.showInputDialog(
      InputDialogMetaData(
        title: '修改好友备注',
        initialValue: friend.name,
        hintText: '请输入昵称',
        confirmText: '保存',
        maxLength: 6,
      ),
    );

    final remark = result.data?.trim();
    if (result.action != DialogAction.confirm ||
        remark == null ||
        remark.isEmpty) {
      return;
    }

    final index = friends.indexOf(friend);
    if (index < 0) return;
    friends[index] = friend.copyWith(name: remark);

    // TODO: Submit the remark to the friend API when it is available.
  }

  Future<void> showDeleteConfirmDialog(FriendHealthData friend) async {
    try {
      final dialogMetaData = DialogMetaData(
        iconAsset: null,
        title: '确认要和${friend.name}解除好友关系吗?',
        message: '解除后你将无法查看对方的情绪、健康状态',
        confirmText: '确认解除',
        cancelText: '取消',
      );
      await DialogUtils.showCommonDialog(dialogMetaData);
    } catch (error, stackTrace) {
      AppLogger.e(
        'FriendsController.showDeleteConfirmDialog failed',
        error,
        stackTrace,
      );
    }
  }
}

class FriendHealthData {
  const FriendHealthData({
    this.userId,
    this.avatarUrl,
    required this.name,
    required this.updatedAt,
    required this.sleepQualityScore,
    required this.steps,
    required this.statusText,
    required this.stressValue,
    this.isOnWatchFace = false,
  });

  final int? userId;
  final String? avatarUrl;
  final String name;
  final String updatedAt;
  final int? sleepQualityScore;
  final String? steps;
  final String statusText;
  final double? stressValue;
  final bool isOnWatchFace;

  FriendHealthData copyWith({
    int? userId,
    String? avatarUrl,
    String? name,
    String? updatedAt,
    int? sleepQualityScore,
    String? steps,
    String? statusText,
    double? stressValue,
    bool? isOnWatchFace,
  }) {
    return FriendHealthData(
      userId: userId ?? this.userId,
      avatarUrl: avatarUrl ?? this.avatarUrl,
      name: name ?? this.name,
      updatedAt: updatedAt ?? this.updatedAt,
      sleepQualityScore: sleepQualityScore ?? this.sleepQualityScore,
      steps: steps ?? this.steps,
      statusText: statusText ?? this.statusText,
      stressValue: stressValue ?? this.stressValue,
      isOnWatchFace: isOnWatchFace ?? this.isOnWatchFace,
    );
  }
}