refactor: 完成项目架构重构,统一模块导入路径
- 清理大量废弃的 barrel 导出文件,移除冗余的中间导出层 - 修复所有相对路径导入错误,统一调整为扁平化模块引用 - 更新多平台 pubspec 版本号与依赖库版本 - 补充后端功能问题管理后台与脚本工具 - 调整部分页面的快捷方式文案适配新功能 - 更新部分翻译覆盖率与API文档
This commit is contained in:
@@ -1,603 +1,9 @@
|
||||
/// ============================================================
|
||||
/// 闲言APP — 用户数据模型
|
||||
/// 闲言APP — 用户数据模型(Re-export)
|
||||
/// 创建时间: 2026-04-28
|
||||
/// 更新时间: 2026-06-05
|
||||
/// 作用: 用户信息数据模型,对应后端 tool_user 表
|
||||
/// 上次更新: UserDevice新增isActiveRecently字段,统一后端7天活跃判定
|
||||
/// 更新时间: 2026-06-12
|
||||
/// 作用: 重新导出 core/models/user_model.dart,保持现有导入者兼容
|
||||
/// 上次更新: 实际定义已移至 core/models/user_model.dart
|
||||
/// ============================================================
|
||||
|
||||
class UserModel {
|
||||
const UserModel({
|
||||
required this.id,
|
||||
required this.username,
|
||||
this.nickname = '',
|
||||
this.avatar = '',
|
||||
this.avatarUrl = '',
|
||||
this.email = '',
|
||||
this.mobile = '',
|
||||
this.score = 0,
|
||||
this.level = 1,
|
||||
this.exp = 0,
|
||||
this.expToNext = 0,
|
||||
this.expProgress = 0.0,
|
||||
this.money = '0.00',
|
||||
this.titleId = 1,
|
||||
this.signinDays = 0,
|
||||
this.lastSigninDate,
|
||||
this.noteLimit = 50,
|
||||
this.articleCount = 0,
|
||||
this.bio = '',
|
||||
this.token,
|
||||
this.title,
|
||||
this.verification,
|
||||
this.isOnline = 0,
|
||||
this.vip,
|
||||
this.cloudSpace,
|
||||
this.devices = const [],
|
||||
this.extra,
|
||||
this.profileSlug = '',
|
||||
this.secQuestion = 0,
|
||||
this.secQuestionText = '',
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String username;
|
||||
final String nickname;
|
||||
final String avatar;
|
||||
final String avatarUrl;
|
||||
final String email;
|
||||
final String mobile;
|
||||
final int score;
|
||||
final int level;
|
||||
final int exp;
|
||||
final int expToNext;
|
||||
final double expProgress;
|
||||
final String money;
|
||||
final int titleId;
|
||||
final int signinDays;
|
||||
final String? lastSigninDate;
|
||||
final int noteLimit;
|
||||
final int articleCount;
|
||||
final String bio;
|
||||
final String? token;
|
||||
final UserTitle? title;
|
||||
final UserVerification? verification;
|
||||
|
||||
final int isOnline;
|
||||
final UserVip? vip;
|
||||
final UserCloudSpace? cloudSpace;
|
||||
final List<UserDevice> devices;
|
||||
final UserExtra? extra;
|
||||
final String profileSlug;
|
||||
final int secQuestion;
|
||||
final String secQuestionText;
|
||||
|
||||
bool get hasSecQuestion => secQuestion > 0;
|
||||
|
||||
String get displayName => nickname.isNotEmpty ? nickname : username;
|
||||
|
||||
static const int _maxAvatarUrlLength = 2048;
|
||||
|
||||
String get avatarDisplayUrl {
|
||||
if (isAvatarUnderReview) return '';
|
||||
final raw = _resolveAvatarUrl();
|
||||
if (raw.isEmpty) return '';
|
||||
if (raw.length > _maxAvatarUrlLength) {
|
||||
return raw.substring(0, _maxAvatarUrlLength);
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
bool get isAvatarUnderReview {
|
||||
if (avatarUrl.isNotEmpty && avatarUrl.startsWith('http')) return true;
|
||||
if (avatar.isNotEmpty && avatar.startsWith('http')) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
String _resolveAvatarUrl() {
|
||||
if (avatarUrl.isNotEmpty) {
|
||||
if (avatarUrl.startsWith('data:')) return '';
|
||||
if (!_isValidUrl(avatarUrl)) return '';
|
||||
if (avatarUrl.startsWith('http')) return avatarUrl;
|
||||
return 'https://tools.wktyl.com$avatarUrl';
|
||||
}
|
||||
if (avatar.isNotEmpty) {
|
||||
if (avatar.startsWith('data:')) return '';
|
||||
if (!_isValidUrl(avatar)) return '';
|
||||
if (avatar.startsWith('http')) return avatar;
|
||||
return 'https://tools.wktyl.com$avatar';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
static bool _isValidUrl(String url) {
|
||||
if (url.isEmpty) return false;
|
||||
if (url.startsWith('http://') || url.startsWith('https://')) {
|
||||
try {
|
||||
final uri = Uri.parse(url);
|
||||
return uri.host.isNotEmpty;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return url.startsWith('/');
|
||||
}
|
||||
|
||||
@Deprecated('Use avatarDisplayUrl instead')
|
||||
String get avatarUrlCompat => avatarDisplayUrl;
|
||||
|
||||
bool get isVip => vip?.isVip ?? false;
|
||||
|
||||
bool get getIsOnline => isOnline == 1;
|
||||
|
||||
/// 从JSON解析sec_question,兼容顶层和extra嵌套两种路径
|
||||
/// 服务端UserCenter接口返回: extra.sec_question.question_id
|
||||
/// 服务端changeSecQuestion接口返回: sec_question (顶层int)
|
||||
static int _parseSecQuestion(Map<String, dynamic> json) {
|
||||
final topLevel = json['sec_question'];
|
||||
if (topLevel is int && topLevel > 0) return topLevel;
|
||||
final extra = json['extra'];
|
||||
if (extra is Map<String, dynamic>) {
|
||||
final nested = extra['sec_question'];
|
||||
if (nested is int && nested > 0) return nested;
|
||||
if (nested is Map<String, dynamic>) {
|
||||
return nested['question_id'] as int? ?? 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// 从JSON解析sec_question_text,兼容顶层和extra嵌套两种路径
|
||||
static String _parseSecQuestionText(Map<String, dynamic> json) {
|
||||
final topLevel = json['sec_question_text'];
|
||||
if (topLevel is String && topLevel.isNotEmpty) return topLevel;
|
||||
final extra = json['extra'];
|
||||
if (extra is Map<String, dynamic>) {
|
||||
final nested = extra['sec_question'];
|
||||
if (nested is Map<String, dynamic>) {
|
||||
final text = nested['question_text'];
|
||||
if (text is String && text.isNotEmpty) return text;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
factory UserModel.fromJson(Map<String, dynamic> json) {
|
||||
return UserModel(
|
||||
id: (json['id'] as num?)?.toInt() ?? 0,
|
||||
username: json['username'] as String? ?? '',
|
||||
nickname: json['nickname'] as String? ?? '',
|
||||
avatar: json['avatar'] as String? ?? '',
|
||||
avatarUrl: json['avatar_url'] as String? ?? '',
|
||||
email: json['email'] as String? ?? '',
|
||||
mobile: json['mobile'] as String? ?? '',
|
||||
score: (json['score'] as num?)?.toInt() ?? 0,
|
||||
level: (json['level'] as num?)?.toInt() ?? 1,
|
||||
exp: (json['exp'] as num?)?.toInt() ?? 0,
|
||||
expToNext: (json['exp_to_next'] as num?)?.toInt() ?? 0,
|
||||
expProgress: (json['exp_progress'] as num?)?.toDouble() ?? 0.0,
|
||||
money: json['money']?.toString() ?? '0.00',
|
||||
titleId: (json['title_id'] as num?)?.toInt() ?? 1,
|
||||
signinDays: (json['signin_days'] as num?)?.toInt() ?? 0,
|
||||
lastSigninDate: json['last_signin_date'] as String?,
|
||||
noteLimit: (json['note_limit'] as num?)?.toInt() ?? 50,
|
||||
articleCount: (json['article_count'] as num?)?.toInt() ?? 0,
|
||||
bio: json['bio'] as String? ?? '',
|
||||
token: json['token'] as String?,
|
||||
title: json['title'] != null
|
||||
? UserTitle.fromJson(json['title'] as Map<String, dynamic>)
|
||||
: null,
|
||||
verification: json['verification'] != null
|
||||
? UserVerification.fromJson(
|
||||
json['verification'] as Map<String, dynamic>,
|
||||
)
|
||||
: null,
|
||||
isOnline: json['is_online'] as int? ?? 0,
|
||||
vip: json['vip'] != null
|
||||
? UserVip.fromJson(json['vip'] as Map<String, dynamic>)
|
||||
: null,
|
||||
cloudSpace: json['cloud_space'] != null
|
||||
? UserCloudSpace.fromJson(json['cloud_space'] as Map<String, dynamic>)
|
||||
: null,
|
||||
devices: json['devices'] != null
|
||||
? (json['devices'] as List<dynamic>)
|
||||
.map((e) => UserDevice.fromJson(e as Map<String, dynamic>))
|
||||
.toList()
|
||||
: [],
|
||||
extra: json['extra'] != null
|
||||
? UserExtra.fromJson(json['extra'] as Map<String, dynamic>)
|
||||
: null,
|
||||
profileSlug: json['profile_slug'] as String? ?? '',
|
||||
secQuestion: _parseSecQuestion(json),
|
||||
secQuestionText: _parseSecQuestionText(json),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'username': username,
|
||||
'nickname': nickname,
|
||||
'avatar': avatar,
|
||||
'avatar_url': avatarUrl,
|
||||
'email': email,
|
||||
'mobile': mobile,
|
||||
'score': score,
|
||||
'level': level,
|
||||
'exp': exp,
|
||||
'exp_to_next': expToNext,
|
||||
'exp_progress': expProgress,
|
||||
'money': money,
|
||||
'title_id': titleId,
|
||||
'signin_days': signinDays,
|
||||
'last_signin_date': lastSigninDate,
|
||||
'note_limit': noteLimit,
|
||||
'article_count': articleCount,
|
||||
'bio': bio,
|
||||
'token': token,
|
||||
'title': title?.toJson(),
|
||||
'verification': verification?.toJson(),
|
||||
'is_online': isOnline,
|
||||
'vip': vip?.toJson(),
|
||||
'cloud_space': cloudSpace?.toJson(),
|
||||
'devices': devices.map((e) => e.toJson()).toList(),
|
||||
'extra': extra?.toJson(),
|
||||
'profile_slug': profileSlug,
|
||||
'sec_question': secQuestion,
|
||||
'sec_question_text': secQuestionText,
|
||||
'is_avatar_under_review': isAvatarUnderReview,
|
||||
};
|
||||
}
|
||||
|
||||
UserModel copyWith({
|
||||
int? id,
|
||||
String? username,
|
||||
String? nickname,
|
||||
String? avatar,
|
||||
String? avatarUrl,
|
||||
String? email,
|
||||
String? mobile,
|
||||
int? score,
|
||||
int? level,
|
||||
int? exp,
|
||||
int? expToNext,
|
||||
double? expProgress,
|
||||
String? money,
|
||||
int? titleId,
|
||||
int? signinDays,
|
||||
String? lastSigninDate,
|
||||
int? noteLimit,
|
||||
int? articleCount,
|
||||
String? bio,
|
||||
String? token,
|
||||
UserTitle? title,
|
||||
UserVerification? verification,
|
||||
int? isOnline,
|
||||
UserVip? vip,
|
||||
UserCloudSpace? cloudSpace,
|
||||
List<UserDevice>? devices,
|
||||
UserExtra? extra,
|
||||
String? profileSlug,
|
||||
int? secQuestion,
|
||||
String? secQuestionText,
|
||||
}) {
|
||||
return UserModel(
|
||||
id: id ?? this.id,
|
||||
username: username ?? this.username,
|
||||
nickname: nickname ?? this.nickname,
|
||||
avatar: avatar ?? this.avatar,
|
||||
avatarUrl: avatarUrl ?? this.avatarUrl,
|
||||
email: email ?? this.email,
|
||||
mobile: mobile ?? this.mobile,
|
||||
score: score ?? this.score,
|
||||
level: level ?? this.level,
|
||||
exp: exp ?? this.exp,
|
||||
expToNext: expToNext ?? this.expToNext,
|
||||
expProgress: expProgress ?? this.expProgress,
|
||||
money: money ?? this.money,
|
||||
titleId: titleId ?? this.titleId,
|
||||
signinDays: signinDays ?? this.signinDays,
|
||||
lastSigninDate: lastSigninDate ?? this.lastSigninDate,
|
||||
noteLimit: noteLimit ?? this.noteLimit,
|
||||
articleCount: articleCount ?? this.articleCount,
|
||||
bio: bio ?? this.bio,
|
||||
token: token ?? this.token,
|
||||
title: title ?? this.title,
|
||||
verification: verification ?? this.verification,
|
||||
isOnline: isOnline ?? this.isOnline,
|
||||
vip: vip ?? this.vip,
|
||||
cloudSpace: cloudSpace ?? this.cloudSpace,
|
||||
devices: devices ?? this.devices,
|
||||
extra: extra ?? this.extra,
|
||||
profileSlug: profileSlug ?? this.profileSlug,
|
||||
secQuestion: secQuestion ?? this.secQuestion,
|
||||
secQuestionText: secQuestionText ?? this.secQuestionText,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserTitle {
|
||||
const UserTitle({
|
||||
required this.id,
|
||||
required this.name,
|
||||
this.icon = '',
|
||||
this.color = '#999999',
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String name;
|
||||
final String icon;
|
||||
final String color;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {'id': id, 'name': name, 'icon': icon, 'color': color};
|
||||
}
|
||||
|
||||
factory UserTitle.fromJson(Map<String, dynamic> json) {
|
||||
return UserTitle(
|
||||
id: json['id'] as int? ?? 1,
|
||||
name: json['name'] as String? ?? '新手',
|
||||
icon: json['icon'] as String? ?? '',
|
||||
color: json['color'] as String? ?? '#999999',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserVerification {
|
||||
const UserVerification({this.email = 0, this.mobile = 0});
|
||||
|
||||
final int email;
|
||||
final int mobile;
|
||||
|
||||
bool get isEmailVerified => email == 1;
|
||||
bool get isMobileVerified => mobile == 1;
|
||||
|
||||
Map<String, dynamic> toJson() => {'email': email, 'mobile': mobile};
|
||||
|
||||
factory UserVerification.fromJson(Map<String, dynamic> json) {
|
||||
return UserVerification(
|
||||
email: (json['email'] as num?)?.toInt() ?? 0,
|
||||
mobile: (json['mobile'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserVip {
|
||||
const UserVip({
|
||||
this.isVip = false,
|
||||
this.startTime = 0,
|
||||
this.endTime = 0,
|
||||
this.startDate = '',
|
||||
this.endDate = '',
|
||||
});
|
||||
|
||||
final bool isVip;
|
||||
final int startTime;
|
||||
final int endTime;
|
||||
final String startDate;
|
||||
final String endDate;
|
||||
|
||||
bool get isActive => isVip && endTime > 0;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'is_vip': isVip,
|
||||
'start_time': startTime,
|
||||
'end_time': endTime,
|
||||
'start_date': startDate,
|
||||
'end_date': endDate,
|
||||
};
|
||||
}
|
||||
|
||||
factory UserVip.fromJson(Map<String, dynamic> json) {
|
||||
return UserVip(
|
||||
isVip: json['is_vip'] as bool? ?? false,
|
||||
startTime: json['start_time'] as int? ?? 0,
|
||||
endTime: json['end_time'] as int? ?? 0,
|
||||
startDate: json['start_date'] as String? ?? '',
|
||||
endDate: json['end_date'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserCloudSpace {
|
||||
const UserCloudSpace({
|
||||
this.total = 0,
|
||||
this.used = 0,
|
||||
this.free = 0,
|
||||
this.totalHuman = '',
|
||||
this.usedHuman = '',
|
||||
this.usagePercent = 0.0,
|
||||
});
|
||||
|
||||
final int total;
|
||||
final int used;
|
||||
final int free;
|
||||
final String totalHuman;
|
||||
final String usedHuman;
|
||||
final double usagePercent;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'total': total,
|
||||
'used': used,
|
||||
'free': free,
|
||||
'total_human': totalHuman,
|
||||
'used_human': usedHuman,
|
||||
'usage_percent': usagePercent,
|
||||
};
|
||||
}
|
||||
|
||||
factory UserCloudSpace.fromJson(Map<String, dynamic> json) {
|
||||
return UserCloudSpace(
|
||||
total: (json['total'] as num?)?.toInt() ?? 0,
|
||||
used: (json['used'] as num?)?.toInt() ?? 0,
|
||||
free: (json['free'] as num?)?.toInt() ?? 0,
|
||||
totalHuman: json['total_human'] as String? ?? '',
|
||||
usedHuman: json['used_human'] as String? ?? '',
|
||||
usagePercent: (json['usage_percent'] as num?)?.toDouble() ?? 0.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserDevice {
|
||||
const UserDevice({
|
||||
this.id = 0,
|
||||
this.deviceName = '',
|
||||
this.deviceModel = '',
|
||||
this.platform = '',
|
||||
this.appName = '',
|
||||
this.ip = '',
|
||||
this.ipCity = '',
|
||||
this.ipRange = '',
|
||||
this.lastActiveTime = 0,
|
||||
this.isOnline = 0,
|
||||
this.createtime = 0,
|
||||
this.lastActiveText = '',
|
||||
this.createtimeText = '',
|
||||
this.isActiveRecently = 0,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String deviceName;
|
||||
final String deviceModel;
|
||||
final String platform;
|
||||
final String appName;
|
||||
final String ip;
|
||||
final String ipCity;
|
||||
final String ipRange;
|
||||
final int lastActiveTime;
|
||||
final int isOnline;
|
||||
final int createtime;
|
||||
final String lastActiveText;
|
||||
final String createtimeText;
|
||||
/// 7天内是否活跃(后端计算)
|
||||
final int isActiveRecently;
|
||||
|
||||
bool get getIsOnline => isOnline == 1;
|
||||
|
||||
/// 7天内是否活跃在线(后端计算)
|
||||
bool get getIsActiveRecently => isActiveRecently == 1;
|
||||
|
||||
/// 前端兜底:根据 lastActiveTime 判断7天内是否活跃
|
||||
/// 解决后端 is_online 5分钟超时置0 导致 onlineCount 始终为0的问题
|
||||
bool get getIsActiveByTime {
|
||||
if (lastActiveTime <= 0) return false;
|
||||
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
return (now - lastActiveTime) < 7 * 86400;
|
||||
}
|
||||
|
||||
bool get hasIpCity => ipCity.isNotEmpty;
|
||||
|
||||
bool get hasIpRange => ipRange.isNotEmpty;
|
||||
|
||||
String get displayLocation => hasIpCity ? '📍 $ipCity' : '';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'device_name': deviceName,
|
||||
'device_model': deviceModel,
|
||||
'platform': platform,
|
||||
'app_name': appName,
|
||||
'ip': ip,
|
||||
'ip_city': ipCity,
|
||||
'ip_range': ipRange,
|
||||
'last_active_time': lastActiveTime,
|
||||
'is_online': isOnline,
|
||||
'createtime': createtime,
|
||||
'last_active_text': lastActiveText,
|
||||
'createtime_text': createtimeText,
|
||||
'is_active_recently': isActiveRecently,
|
||||
};
|
||||
}
|
||||
|
||||
factory UserDevice.fromJson(Map<String, dynamic> json) {
|
||||
return UserDevice(
|
||||
id: (json['id'] as num?)?.toInt() ?? 0,
|
||||
deviceName: json['device_name'] as String? ?? '',
|
||||
deviceModel: json['device_model'] as String? ?? '',
|
||||
platform: json['platform'] as String? ?? '',
|
||||
appName: json['app_name'] as String? ?? '',
|
||||
ip: json['ip'] as String? ?? '',
|
||||
ipCity: json['ip_city'] as String? ?? '',
|
||||
ipRange: json['ip_range'] as String? ?? '',
|
||||
lastActiveTime: (json['last_active_time'] as num?)?.toInt() ?? 0,
|
||||
isOnline: (json['is_online'] as num?)?.toInt() ?? 0,
|
||||
createtime: (json['createtime'] as num?)?.toInt() ?? 0,
|
||||
lastActiveText: json['last_active_text'] as String? ?? '',
|
||||
createtimeText: json['createtime_text'] as String? ?? '',
|
||||
isActiveRecently: (json['is_active_recently'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
UserDevice copyWith({
|
||||
int? id,
|
||||
String? deviceName,
|
||||
String? deviceModel,
|
||||
String? platform,
|
||||
String? appName,
|
||||
String? ip,
|
||||
String? ipCity,
|
||||
String? ipRange,
|
||||
int? lastActiveTime,
|
||||
int? isOnline,
|
||||
int? createtime,
|
||||
String? lastActiveText,
|
||||
String? createtimeText,
|
||||
int? isActiveRecently,
|
||||
}) {
|
||||
return UserDevice(
|
||||
id: id ?? this.id,
|
||||
deviceName: deviceName ?? this.deviceName,
|
||||
deviceModel: deviceModel ?? this.deviceModel,
|
||||
platform: platform ?? this.platform,
|
||||
appName: appName ?? this.appName,
|
||||
ip: ip ?? this.ip,
|
||||
ipCity: ipCity ?? this.ipCity,
|
||||
ipRange: ipRange ?? this.ipRange,
|
||||
lastActiveTime: lastActiveTime ?? this.lastActiveTime,
|
||||
isOnline: isOnline ?? this.isOnline,
|
||||
createtime: createtime ?? this.createtime,
|
||||
lastActiveText: lastActiveText ?? this.lastActiveText,
|
||||
createtimeText: createtimeText ?? this.createtimeText,
|
||||
isActiveRecently: isActiveRecently ?? this.isActiveRecently,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserExtra {
|
||||
const UserExtra({
|
||||
this.money = '0.00',
|
||||
this.noteLimit = 50,
|
||||
this.verification,
|
||||
this.lastSigninDate = '',
|
||||
});
|
||||
|
||||
final String money;
|
||||
final int noteLimit;
|
||||
final UserVerification? verification;
|
||||
final String lastSigninDate;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'money': money,
|
||||
'note_limit': noteLimit,
|
||||
'verification': verification?.toJson(),
|
||||
'last_signin_date': lastSigninDate,
|
||||
};
|
||||
}
|
||||
|
||||
factory UserExtra.fromJson(Map<String, dynamic> json) {
|
||||
return UserExtra(
|
||||
money: json['money']?.toString() ?? '0.00',
|
||||
noteLimit: (json['note_limit'] as num?)?.toInt() ?? 50,
|
||||
verification: json['verification'] != null
|
||||
? UserVerification.fromJson(
|
||||
json['verification'] as Map<String, dynamic>,
|
||||
)
|
||||
: null,
|
||||
lastSigninDate: json['last_signin_date'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
export '../../../core/models/user_model.dart';
|
||||
|
||||
Reference in New Issue
Block a user