friend_models.dart 7 KB
// ─── Responses ───────────────────────────────────────────────────────────────

class FriendListResponse {
  const FriendListResponse({
    this.list = const [],
    this.limit,
    this.healthData,
  });

  final List<FriendItem> list;
  final int? limit;
  final FriendHealthData? healthData;

  factory FriendListResponse.fromJson(Map<String, dynamic> json) {
    return FriendListResponse(
      list: (json['list'] as List<dynamic>?)
              ?.map((e) => FriendItem.fromJson(e as Map<String, dynamic>))
              .toList() ??
          const [],
      limit: json['limit'] as int?,
      healthData: json['health_data'] == null
          ? null
          : FriendHealthData.fromJson(
              json['health_data'] as Map<String, dynamic>,
            ),
    );
  }

  Map<String, dynamic> toJson() => {
        'list': list.map((e) => e.toJson()).toList(),
        'limit': limit,
        if (healthData != null) 'health_data': healthData!.toJson(),
      };
}

class FriendItem {
  const FriendItem({
    this.id,
    this.userId,
    this.friendUserId,
    this.remarkName,
    this.showInDial,
    this.state,
    this.createTime,
    this.updateTime,
    this.avatar,
    this.friendNickname,
    this.healthData,
  });

  final int? id;
  final int? userId;
  final int? friendUserId;
  final String? remarkName;

  /// 0 = hidden, 1 = shown in dial
  final int? showInDial;

  /// 1 = normal
  final int? state;

  final int? createTime;
  final int? updateTime;
  final String? avatar;
  final String? friendNickname;
  final FriendHealthData? healthData;

  bool get isShowInDial => showInDial == 1;

  factory FriendItem.fromJson(Map<String, dynamic> json) {
    return FriendItem(
      id: json['id'] as int?,
      userId: json['user_id'] as int?,
      friendUserId: json['friend_user_id'] as int?,
      remarkName: json['remark_name'] as String?,
      showInDial: json['show_in_dial'] as int?,
      state: json['state'] as int?,
      createTime: json['create_time'] as int?,
      updateTime: json['update_time'] as int?,
      avatar: json['avatar'] as String?,
      friendNickname: json['friend_nickname'] as String?,
      healthData: json['health_data'] == null
          ? null
          : FriendHealthData.fromJson(
              json['health_data'] as Map<String, dynamic>),
    );
  }

  Map<String, dynamic> toJson() {
    final val = <String, dynamic>{};
    if (id != null) val['id'] = id;
    if (userId != null) val['user_id'] = userId;
    if (friendUserId != null) val['friend_user_id'] = friendUserId;
    if (remarkName != null) val['remark_name'] = remarkName;
    if (showInDial != null) val['show_in_dial'] = showInDial;
    if (state != null) val['state'] = state;
    if (createTime != null) val['create_time'] = createTime;
    if (updateTime != null) val['update_time'] = updateTime;
    if (avatar != null) val['avatar'] = avatar;
    if (friendNickname != null) val['friend_nickname'] = friendNickname;
    if (healthData != null) val['health_data'] = healthData!.toJson();
    return val;
  }
}

class FriendHealthData {
  const FriendHealthData({
    this.hrvState,
    this.comprehensiveStressScore,
    this.comprehensiveStressState,
    this.latestHrv,
    this.realtimeStress,
    this.sleepEvaluate,
    this.totalSteps,
    this.lastDataTime,
  });

  /// HRV state indicator
  final int? hrvState;

  /// Comprehensive stress score.
  final int? comprehensiveStressScore;

  /// Comprehensive stress state indicator.
  final int? comprehensiveStressState;

  /// Latest HRV value used by the friend health card.
  final double? latestHrv;

  /// Real-time stress samples returned by the API (may be an empty list `[]`).
  final List<FriendRealtimeStressItem>? realtimeStress;

  /// Sleep quality score
  final int? sleepEvaluate;

  /// Total step count for the day (nullable)
  final int? totalSteps;

  /// Last health data timestamp, in seconds or milliseconds.
  final num? lastDataTime;

  factory FriendHealthData.fromJson(Map<String, dynamic> json) {
    return FriendHealthData(
      hrvState: _parseInt(json['hrv_state']),
      comprehensiveStressScore: _parseInt(json['comprehensive_stress_score']),
      comprehensiveStressState: _parseInt(json['comprehensive_stress_state']),
      latestHrv: _parseDouble(json['latest_hrv']),
      realtimeStress: (json['realtime_stress'] as List<dynamic>?)
          ?.map(
            (item) => FriendRealtimeStressItem.fromJson(
              item as Map<String, dynamic>,
            ),
          )
          .toList(),
      sleepEvaluate: _parseInt(json['sleep_evaluate']),
      totalSteps: _parseInt(json['total_steps']),
      lastDataTime: _parseNum(json['last_data_time']),
    );
  }

  Map<String, dynamic> toJson() {
    final val = <String, dynamic>{};
    if (hrvState != null) val['hrv_state'] = hrvState;
    if (comprehensiveStressScore != null) {
      val['comprehensive_stress_score'] = comprehensiveStressScore;
    }
    if (comprehensiveStressState != null) {
      val['comprehensive_stress_state'] = comprehensiveStressState;
    }
    if (latestHrv != null) val['latest_hrv'] = latestHrv;
    if (realtimeStress != null) {
      val['realtime_stress'] =
          realtimeStress!.map((item) => item.toJson()).toList();
    }
    if (sleepEvaluate != null) val['sleep_evaluate'] = sleepEvaluate;
    if (totalSteps != null) val['total_steps'] = totalSteps;
    if (lastDataTime != null) val['last_data_time'] = lastDataTime;
    return val;
  }
}

class FriendRealtimeStressItem {
  const FriendRealtimeStressItem({
    this.time,
    this.value,
    this.state,
    this.isAsleep,
  });

  /// Unix timestamp in seconds.
  final num? time;

  /// Real-time stress score.
  final double? value;

  /// Stress state indicator.
  final int? state;

  /// Whether the sample was recorded while asleep (0 = no, 1 = yes).
  final int? isAsleep;

  factory FriendRealtimeStressItem.fromJson(Map<String, dynamic> json) {
    return FriendRealtimeStressItem(
      time: _parseNum(json['time']),
      value: _parseDouble(json['value']),
      state: _parseInt(json['state']),
      isAsleep: _parseInt(json['is_asleep']),
    );
  }

  Map<String, dynamic> toJson() {
    final val = <String, dynamic>{};
    if (time != null) val['time'] = time;
    if (value != null) val['value'] = value;
    if (state != null) val['state'] = state;
    if (isAsleep != null) val['is_asleep'] = isAsleep;
    return val;
  }
}

int? _parseInt(dynamic value) {
  if (value == null) return null;
  if (value is int) return value;
  if (value is double) return value.toInt();
  if (value is String) return int.tryParse(value);
  return null;
}

double? _parseDouble(dynamic value) {
  if (value == null) return null;
  if (value is num) return value.toDouble();
  if (value is String) return double.tryParse(value);
  return null;
}

num? _parseNum(dynamic value) {
  if (value == null) return null;
  if (value is num) return value;
  if (value is String) return num.tryParse(value);
  return null;
}