From 36b9026e9eb42d2d1361c3da5e9860de19341c1c Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 11 Jun 2025 00:09:19 +0800 Subject: [PATCH] :sparkles: Verification mark --- lib/models/post.dart | 1 + lib/models/post.freezed.dart | 59 ++++++-- lib/models/post.g.dart | 7 + lib/models/user.dart | 14 ++ lib/models/user.freezed.dart | 201 ++++++++++++++++++++++++-- lib/models/user.g.dart | 23 +++ lib/screens/account.dart | 13 +- lib/widgets/account/account_name.dart | 63 ++++++++ lib/widgets/content/cloud_files.dart | 26 +++- lib/widgets/post/post_item.dart | 15 +- 10 files changed, 377 insertions(+), 45 deletions(-) create mode 100644 lib/widgets/account/account_name.dart diff --git a/lib/models/post.dart b/lib/models/post.dart index d551152..98bfd38 100644 --- a/lib/models/post.dart +++ b/lib/models/post.dart @@ -59,6 +59,7 @@ sealed class SnPublisher with _$SnPublisher { required DateTime updatedAt, required DateTime? deletedAt, required String? realmId, + required SnVerificationMark? verification, }) = _SnPublisher; factory SnPublisher.fromJson(Map json) => diff --git a/lib/models/post.freezed.dart b/lib/models/post.freezed.dart index 17f0592..bcf5b04 100644 --- a/lib/models/post.freezed.dart +++ b/lib/models/post.freezed.dart @@ -370,7 +370,7 @@ $SnPublisherCopyWith<$Res> get publisher { /// @nodoc mixin _$SnPublisher { - String get id; int get type; String get name; String get nick; String get bio; SnCloudFile? get picture; SnCloudFile? get background; SnAccount? get account; String? get accountId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String? get realmId; + String get id; int get type; String get name; String get nick; String get bio; SnCloudFile? get picture; SnCloudFile? get background; SnAccount? get account; String? get accountId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String? get realmId; SnVerificationMark? get verification; /// Create a copy of SnPublisher /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -383,16 +383,16 @@ $SnPublisherCopyWith get copyWith => _$SnPublisherCopyWithImpl Object.hash(runtimeType,id,type,name,nick,bio,picture,background,account,accountId,createdAt,updatedAt,deletedAt,realmId); +int get hashCode => Object.hash(runtimeType,id,type,name,nick,bio,picture,background,account,accountId,createdAt,updatedAt,deletedAt,realmId,verification); @override String toString() { - return 'SnPublisher(id: $id, type: $type, name: $name, nick: $nick, bio: $bio, picture: $picture, background: $background, account: $account, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, realmId: $realmId)'; + return 'SnPublisher(id: $id, type: $type, name: $name, nick: $nick, bio: $bio, picture: $picture, background: $background, account: $account, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, realmId: $realmId, verification: $verification)'; } @@ -403,11 +403,11 @@ abstract mixin class $SnPublisherCopyWith<$Res> { factory $SnPublisherCopyWith(SnPublisher value, $Res Function(SnPublisher) _then) = _$SnPublisherCopyWithImpl; @useResult $Res call({ - String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String? realmId + String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String? realmId, SnVerificationMark? verification }); -$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;$SnAccountCopyWith<$Res>? get account; +$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;$SnAccountCopyWith<$Res>? get account;$SnVerificationMarkCopyWith<$Res>? get verification; } /// @nodoc @@ -420,7 +420,7 @@ class _$SnPublisherCopyWithImpl<$Res> /// Create a copy of SnPublisher /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? realmId = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? realmId = freezed,Object? verification = freezed,}) { return _then(_self.copyWith( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable @@ -435,7 +435,8 @@ as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?,realmId: freezed == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable -as String?, +as String?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable +as SnVerificationMark?, )); } /// Create a copy of SnPublisher @@ -474,6 +475,18 @@ $SnAccountCopyWith<$Res>? get account { return $SnAccountCopyWith<$Res>(_self.account!, (value) { return _then(_self.copyWith(account: value)); }); +}/// Create a copy of SnPublisher +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SnVerificationMarkCopyWith<$Res>? get verification { + if (_self.verification == null) { + return null; + } + + return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) { + return _then(_self.copyWith(verification: value)); + }); } } @@ -482,7 +495,7 @@ $SnAccountCopyWith<$Res>? get account { @JsonSerializable() class _SnPublisher implements SnPublisher { - const _SnPublisher({required this.id, required this.type, required this.name, required this.nick, this.bio = '', required this.picture, required this.background, required this.account, required this.accountId, required this.createdAt, required this.updatedAt, required this.deletedAt, required this.realmId}); + const _SnPublisher({required this.id, required this.type, required this.name, required this.nick, this.bio = '', required this.picture, required this.background, required this.account, required this.accountId, required this.createdAt, required this.updatedAt, required this.deletedAt, required this.realmId, required this.verification}); factory _SnPublisher.fromJson(Map json) => _$SnPublisherFromJson(json); @override final String id; @@ -498,6 +511,7 @@ class _SnPublisher implements SnPublisher { @override final DateTime updatedAt; @override final DateTime? deletedAt; @override final String? realmId; +@override final SnVerificationMark? verification; /// Create a copy of SnPublisher /// with the given fields replaced by the non-null parameter values. @@ -512,16 +526,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPublisher&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.account, account) || other.account == account)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.realmId, realmId) || other.realmId == realmId)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPublisher&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.name, name) || other.name == name)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.account, account) || other.account == account)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.realmId, realmId) || other.realmId == realmId)&&(identical(other.verification, verification) || other.verification == verification)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,type,name,nick,bio,picture,background,account,accountId,createdAt,updatedAt,deletedAt,realmId); +int get hashCode => Object.hash(runtimeType,id,type,name,nick,bio,picture,background,account,accountId,createdAt,updatedAt,deletedAt,realmId,verification); @override String toString() { - return 'SnPublisher(id: $id, type: $type, name: $name, nick: $nick, bio: $bio, picture: $picture, background: $background, account: $account, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, realmId: $realmId)'; + return 'SnPublisher(id: $id, type: $type, name: $name, nick: $nick, bio: $bio, picture: $picture, background: $background, account: $account, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, realmId: $realmId, verification: $verification)'; } @@ -532,11 +546,11 @@ abstract mixin class _$SnPublisherCopyWith<$Res> implements $SnPublisherCopyWith factory _$SnPublisherCopyWith(_SnPublisher value, $Res Function(_SnPublisher) _then) = __$SnPublisherCopyWithImpl; @override @useResult $Res call({ - String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String? realmId + String id, int type, String name, String nick, String bio, SnCloudFile? picture, SnCloudFile? background, SnAccount? account, String? accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String? realmId, SnVerificationMark? verification }); -@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;@override $SnAccountCopyWith<$Res>? get account; +@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;@override $SnAccountCopyWith<$Res>? get account;@override $SnVerificationMarkCopyWith<$Res>? get verification; } /// @nodoc @@ -549,7 +563,7 @@ class __$SnPublisherCopyWithImpl<$Res> /// Create a copy of SnPublisher /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? realmId = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? type = null,Object? name = null,Object? nick = null,Object? bio = null,Object? picture = freezed,Object? background = freezed,Object? account = freezed,Object? accountId = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? realmId = freezed,Object? verification = freezed,}) { return _then(_SnPublisher( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable @@ -564,7 +578,8 @@ as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?,realmId: freezed == realmId ? _self.realmId : realmId // ignore: cast_nullable_to_non_nullable -as String?, +as String?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable +as SnVerificationMark?, )); } @@ -604,6 +619,18 @@ $SnAccountCopyWith<$Res>? get account { return $SnAccountCopyWith<$Res>(_self.account!, (value) { return _then(_self.copyWith(account: value)); }); +}/// Create a copy of SnPublisher +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SnVerificationMarkCopyWith<$Res>? get verification { + if (_self.verification == null) { + return null; + } + + return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) { + return _then(_self.copyWith(verification: value)); + }); } } diff --git a/lib/models/post.g.dart b/lib/models/post.g.dart index 40e9702..6f4f24f 100644 --- a/lib/models/post.g.dart +++ b/lib/models/post.g.dart @@ -120,6 +120,12 @@ _SnPublisher _$SnPublisherFromJson(Map json) => _SnPublisher( ? null : DateTime.parse(json['deleted_at'] as String), realmId: json['realm_id'] as String?, + verification: + json['verification'] == null + ? null + : SnVerificationMark.fromJson( + json['verification'] as Map, + ), ); Map _$SnPublisherToJson(_SnPublisher instance) => @@ -137,6 +143,7 @@ Map _$SnPublisherToJson(_SnPublisher instance) => 'updated_at': instance.updatedAt.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(), 'realm_id': instance.realmId, + 'verification': instance.verification?.toJson(), }; _SnPublisherStats _$SnPublisherStatsFromJson(Map json) => diff --git a/lib/models/user.dart b/lib/models/user.dart index 54f3543..1dee290 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -36,6 +36,7 @@ sealed class SnAccountProfile with _$SnAccountProfile { required double levelingProgress, required SnCloudFile? picture, required SnCloudFile? background, + required SnVerificationMark? verification, required DateTime createdAt, required DateTime updatedAt, required DateTime? deletedAt, @@ -123,3 +124,16 @@ sealed class SnNotification with _$SnNotification { factory SnNotification.fromJson(Map json) => _$SnNotificationFromJson(json); } + +@freezed +sealed class SnVerificationMark with _$SnVerificationMark { + const factory SnVerificationMark({ + required int type, + required String? title, + required String? description, + required String? verifiedBy, + }) = _SnVerificationMark; + + factory SnVerificationMark.fromJson(Map json) => + _$SnVerificationMarkFromJson(json); +} diff --git a/lib/models/user.freezed.dart b/lib/models/user.freezed.dart index b30b876..ba297e4 100644 --- a/lib/models/user.freezed.dart +++ b/lib/models/user.freezed.dart @@ -200,7 +200,7 @@ $SnAccountProfileCopyWith<$Res> get profile { /// @nodoc mixin _$SnAccountProfile { - String get id; String? get firstName; String? get middleName; String? get lastName; String get bio; int get experience; int get level; double get levelingProgress; SnCloudFile? get picture; SnCloudFile? get background; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; + String get id; String? get firstName; String? get middleName; String? get lastName; String get bio; int get experience; int get level; double get levelingProgress; SnCloudFile? get picture; SnCloudFile? get background; SnVerificationMark? get verification; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; /// Create a copy of SnAccountProfile /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -213,16 +213,16 @@ $SnAccountProfileCopyWith get copyWith => _$SnAccountProfileCo @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,firstName,middleName,lastName,bio,experience,level,levelingProgress,picture,background,createdAt,updatedAt,deletedAt); +int get hashCode => Object.hash(runtimeType,id,firstName,middleName,lastName,bio,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt); @override String toString() { - return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; + return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; } @@ -233,11 +233,11 @@ abstract mixin class $SnAccountProfileCopyWith<$Res> { factory $SnAccountProfileCopyWith(SnAccountProfile value, $Res Function(SnAccountProfile) _then) = _$SnAccountProfileCopyWithImpl; @useResult $Res call({ - String id, String? firstName, String? middleName, String? lastName, String bio, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt + String id, String? firstName, String? middleName, String? lastName, String bio, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt }); -$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background; +$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;$SnVerificationMarkCopyWith<$Res>? get verification; } /// @nodoc @@ -250,7 +250,7 @@ class _$SnAccountProfileCopyWithImpl<$Res> /// Create a copy of SnAccountProfile /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? firstName = freezed,Object? middleName = freezed,Object? lastName = freezed,Object? bio = null,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? firstName = freezed,Object? middleName = freezed,Object? lastName = freezed,Object? bio = null,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { return _then(_self.copyWith( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,firstName: freezed == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable @@ -262,7 +262,8 @@ as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_no as int,levelingProgress: null == levelingProgress ? _self.levelingProgress : levelingProgress // ignore: cast_nullable_to_non_nullable as double,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable -as SnCloudFile?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as SnCloudFile?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable +as SnVerificationMark?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?, @@ -292,6 +293,18 @@ $SnCloudFileCopyWith<$Res>? get background { return $SnCloudFileCopyWith<$Res>(_self.background!, (value) { return _then(_self.copyWith(background: value)); }); +}/// Create a copy of SnAccountProfile +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SnVerificationMarkCopyWith<$Res>? get verification { + if (_self.verification == null) { + return null; + } + + return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) { + return _then(_self.copyWith(verification: value)); + }); } } @@ -300,7 +313,7 @@ $SnCloudFileCopyWith<$Res>? get background { @JsonSerializable() class _SnAccountProfile implements SnAccountProfile { - const _SnAccountProfile({required this.id, required this.firstName, required this.middleName, required this.lastName, this.bio = '', required this.experience, required this.level, required this.levelingProgress, required this.picture, required this.background, required this.createdAt, required this.updatedAt, required this.deletedAt}); + const _SnAccountProfile({required this.id, required this.firstName, required this.middleName, required this.lastName, this.bio = '', required this.experience, required this.level, required this.levelingProgress, required this.picture, required this.background, required this.verification, required this.createdAt, required this.updatedAt, required this.deletedAt}); factory _SnAccountProfile.fromJson(Map json) => _$SnAccountProfileFromJson(json); @override final String id; @@ -313,6 +326,7 @@ class _SnAccountProfile implements SnAccountProfile { @override final double levelingProgress; @override final SnCloudFile? picture; @override final SnCloudFile? background; +@override final SnVerificationMark? verification; @override final DateTime createdAt; @override final DateTime updatedAt; @override final DateTime? deletedAt; @@ -330,16 +344,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,firstName,middleName,lastName,bio,experience,level,levelingProgress,picture,background,createdAt,updatedAt,deletedAt); +int get hashCode => Object.hash(runtimeType,id,firstName,middleName,lastName,bio,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt); @override String toString() { - return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; + return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; } @@ -350,11 +364,11 @@ abstract mixin class _$SnAccountProfileCopyWith<$Res> implements $SnAccountProfi factory _$SnAccountProfileCopyWith(_SnAccountProfile value, $Res Function(_SnAccountProfile) _then) = __$SnAccountProfileCopyWithImpl; @override @useResult $Res call({ - String id, String? firstName, String? middleName, String? lastName, String bio, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt + String id, String? firstName, String? middleName, String? lastName, String bio, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt }); -@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background; +@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;@override $SnVerificationMarkCopyWith<$Res>? get verification; } /// @nodoc @@ -367,7 +381,7 @@ class __$SnAccountProfileCopyWithImpl<$Res> /// Create a copy of SnAccountProfile /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? firstName = freezed,Object? middleName = freezed,Object? lastName = freezed,Object? bio = null,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? firstName = freezed,Object? middleName = freezed,Object? lastName = freezed,Object? bio = null,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { return _then(_SnAccountProfile( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,firstName: freezed == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable @@ -379,7 +393,8 @@ as int,level: null == level ? _self.level : level // ignore: cast_nullable_to_no as int,levelingProgress: null == levelingProgress ? _self.levelingProgress : levelingProgress // ignore: cast_nullable_to_non_nullable as double,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable -as SnCloudFile?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as SnCloudFile?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable +as SnVerificationMark?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?, @@ -410,6 +425,18 @@ $SnCloudFileCopyWith<$Res>? get background { return $SnCloudFileCopyWith<$Res>(_self.background!, (value) { return _then(_self.copyWith(background: value)); }); +}/// Create a copy of SnAccountProfile +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SnVerificationMarkCopyWith<$Res>? get verification { + if (_self.verification == null) { + return null; + } + + return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) { + return _then(_self.copyWith(verification: value)); + }); } } @@ -1072,6 +1099,148 @@ as String, } +} + + +/// @nodoc +mixin _$SnVerificationMark { + + int get type; String? get title; String? get description; String? get verifiedBy; +/// Create a copy of SnVerificationMark +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SnVerificationMarkCopyWith get copyWith => _$SnVerificationMarkCopyWithImpl(this as SnVerificationMark, _$identity); + + /// Serializes this SnVerificationMark to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SnVerificationMark&&(identical(other.type, type) || other.type == type)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.verifiedBy, verifiedBy) || other.verifiedBy == verifiedBy)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,type,title,description,verifiedBy); + +@override +String toString() { + return 'SnVerificationMark(type: $type, title: $title, description: $description, verifiedBy: $verifiedBy)'; +} + + +} + +/// @nodoc +abstract mixin class $SnVerificationMarkCopyWith<$Res> { + factory $SnVerificationMarkCopyWith(SnVerificationMark value, $Res Function(SnVerificationMark) _then) = _$SnVerificationMarkCopyWithImpl; +@useResult +$Res call({ + int type, String? title, String? description, String? verifiedBy +}); + + + + +} +/// @nodoc +class _$SnVerificationMarkCopyWithImpl<$Res> + implements $SnVerificationMarkCopyWith<$Res> { + _$SnVerificationMarkCopyWithImpl(this._self, this._then); + + final SnVerificationMark _self; + final $Res Function(SnVerificationMark) _then; + +/// Create a copy of SnVerificationMark +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? title = freezed,Object? description = freezed,Object? verifiedBy = freezed,}) { + return _then(_self.copyWith( +type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable +as int,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable +as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?,verifiedBy: freezed == verifiedBy ? _self.verifiedBy : verifiedBy // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + +} + + +/// @nodoc +@JsonSerializable() + +class _SnVerificationMark implements SnVerificationMark { + const _SnVerificationMark({required this.type, required this.title, required this.description, required this.verifiedBy}); + factory _SnVerificationMark.fromJson(Map json) => _$SnVerificationMarkFromJson(json); + +@override final int type; +@override final String? title; +@override final String? description; +@override final String? verifiedBy; + +/// Create a copy of SnVerificationMark +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SnVerificationMarkCopyWith<_SnVerificationMark> get copyWith => __$SnVerificationMarkCopyWithImpl<_SnVerificationMark>(this, _$identity); + +@override +Map toJson() { + return _$SnVerificationMarkToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnVerificationMark&&(identical(other.type, type) || other.type == type)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.verifiedBy, verifiedBy) || other.verifiedBy == verifiedBy)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,type,title,description,verifiedBy); + +@override +String toString() { + return 'SnVerificationMark(type: $type, title: $title, description: $description, verifiedBy: $verifiedBy)'; +} + + +} + +/// @nodoc +abstract mixin class _$SnVerificationMarkCopyWith<$Res> implements $SnVerificationMarkCopyWith<$Res> { + factory _$SnVerificationMarkCopyWith(_SnVerificationMark value, $Res Function(_SnVerificationMark) _then) = __$SnVerificationMarkCopyWithImpl; +@override @useResult +$Res call({ + int type, String? title, String? description, String? verifiedBy +}); + + + + +} +/// @nodoc +class __$SnVerificationMarkCopyWithImpl<$Res> + implements _$SnVerificationMarkCopyWith<$Res> { + __$SnVerificationMarkCopyWithImpl(this._self, this._then); + + final _SnVerificationMark _self; + final $Res Function(_SnVerificationMark) _then; + +/// Create a copy of SnVerificationMark +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? title = freezed,Object? description = freezed,Object? verifiedBy = freezed,}) { + return _then(_SnVerificationMark( +type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable +as int,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable +as String?,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String?,verifiedBy: freezed == verifiedBy ? _self.verifiedBy : verifiedBy // ignore: cast_nullable_to_non_nullable +as String?, + )); +} + + } // dart format on diff --git a/lib/models/user.g.dart b/lib/models/user.g.dart index 45546f3..20d0196 100644 --- a/lib/models/user.g.dart +++ b/lib/models/user.g.dart @@ -60,6 +60,12 @@ _SnAccountProfile _$SnAccountProfileFromJson(Map json) => : SnCloudFile.fromJson( json['background'] as Map, ), + verification: + json['verification'] == null + ? null + : SnVerificationMark.fromJson( + json['verification'] as Map, + ), createdAt: DateTime.parse(json['created_at'] as String), updatedAt: DateTime.parse(json['updated_at'] as String), deletedAt: @@ -80,6 +86,7 @@ Map _$SnAccountProfileToJson(_SnAccountProfile instance) => 'leveling_progress': instance.levelingProgress, 'picture': instance.picture?.toJson(), 'background': instance.background?.toJson(), + 'verification': instance.verification?.toJson(), 'created_at': instance.createdAt.toIso8601String(), 'updated_at': instance.updatedAt.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(), @@ -226,3 +233,19 @@ Map _$SnNotificationToJson(_SnNotification instance) => 'viewed_at': instance.viewedAt?.toIso8601String(), 'account_id': instance.accountId, }; + +_SnVerificationMark _$SnVerificationMarkFromJson(Map json) => + _SnVerificationMark( + type: (json['type'] as num).toInt(), + title: json['title'] as String?, + description: json['description'] as String?, + verifiedBy: json['verified_by'] as String?, + ); + +Map _$SnVerificationMarkToJson(_SnVerificationMark instance) => + { + 'type': instance.type, + 'title': instance.title, + 'description': instance.description, + 'verified_by': instance.verifiedBy, + }; diff --git a/lib/screens/account.dart b/lib/screens/account.dart index e1d66d3..a1d8a20 100644 --- a/lib/screens/account.dart +++ b/lib/screens/account.dart @@ -11,6 +11,7 @@ import 'package:island/pods/userinfo.dart'; import 'package:island/route.gr.dart'; import 'package:island/screens/notification.dart'; import 'package:island/services/responsive.dart'; +import 'package:island/widgets/account/account_name.dart'; import 'package:island/widgets/account/status.dart'; import 'package:island/widgets/account/leveling_progress.dart'; import 'package:island/widgets/app_scaffold.dart'; @@ -83,7 +84,7 @@ class AccountScreen extends HookConsumerWidget { child: AspectRatio( aspectRatio: 16 / 7, child: CloudImageWidget( - fileId: user.value!.profile.background!.id, + file: user.value?.profile.background, fit: BoxFit.cover, ), ), @@ -94,7 +95,7 @@ class AccountScreen extends HookConsumerWidget { children: [ GestureDetector( child: ProfilePictureWidget( - fileId: user.value?.profile.picture?.id, + file: user.value?.profile.picture, radius: 24, ), onTap: () { @@ -112,7 +113,13 @@ class AccountScreen extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, children: [ - Text(user.value!.nick).bold().fontSize(16), + AccountName( + account: user.value!, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), Text('@${user.value!.name}'), ], ), diff --git a/lib/widgets/account/account_name.dart b/lib/widgets/account/account_name.dart new file mode 100644 index 0000000..91761db --- /dev/null +++ b/lib/widgets/account/account_name.dart @@ -0,0 +1,63 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:island/models/user.dart'; +import 'package:material_symbols_icons/symbols.dart'; + +const kVerificationMarkColors = [ + Colors.teal, + Colors.blue, + Colors.amber, + Colors.blueGrey, + Colors.lightBlue, +]; + +class AccountName extends StatelessWidget { + final SnAccount account; + final TextStyle? style; + const AccountName({super.key, required this.account, this.style}); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + spacing: 4, + children: [ + Flexible(child: Text(account.nick, style: style)), + if (account.profile.verification != null) + VerificationMark(mark: account.profile.verification!), + ], + ); + } +} + +class VerificationMark extends StatelessWidget { + final SnVerificationMark mark; + const VerificationMark({super.key, required this.mark}); + + @override + Widget build(BuildContext context) { + return Tooltip( + richMessage: TextSpan( + text: mark.title ?? 'No title', + children: [ + TextSpan(text: '\n'), + TextSpan( + text: mark.description ?? 'descriptionNone'.tr(), + style: TextStyle(fontWeight: FontWeight.normal), + ), + ], + style: TextStyle(fontWeight: FontWeight.bold), + ), + child: Icon( + mark.type == 4 + ? Symbols.play_circle + : mark.type == 0 + ? Symbols.build_circle + : Symbols.verified, + size: 16, + color: kVerificationMarkColors[mark.type], + fill: 1, + ), + ); + } +} diff --git a/lib/widgets/content/cloud_files.dart b/lib/widgets/content/cloud_files.dart index b1fbbe4..f10346b 100644 --- a/lib/widgets/content/cloud_files.dart +++ b/lib/widgets/content/cloud_files.dart @@ -54,13 +54,15 @@ class CloudFileWidget extends ConsumerWidget { } class CloudImageWidget extends ConsumerWidget { - final String fileId; + final String? fileId; + final SnCloudFile? file; final BoxFit fit; final double aspectRatio; final String? blurHash; const CloudImageWidget({ super.key, - required this.fileId, + this.fileId, + this.file, this.aspectRatio = 1, this.fit = BoxFit.cover, this.blurHash, @@ -68,11 +70,17 @@ class CloudImageWidget extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + assert(fileId != null || file != null); + final serverUrl = ref.watch(serverUrlProvider); - final uri = '$serverUrl/files/$fileId'; + final uri = '$serverUrl/files/${file?.id ?? fileId}'; + return AspectRatio( aspectRatio: aspectRatio, - child: UniversalImage(uri: uri, blurHash: blurHash), + child: + file != null + ? CloudFileWidget(item: file!, fit: fit) + : UniversalImage(uri: uri, blurHash: blurHash, fit: fit), ); } @@ -88,12 +96,14 @@ class CloudImageWidget extends ConsumerWidget { class ProfilePictureWidget extends ConsumerWidget { final String? fileId; + final SnCloudFile? file; final double radius; final IconData? fallbackIcon; final Color? fallbackColor; const ProfilePictureWidget({ super.key, - required this.fileId, + this.fileId, + this.file, this.radius = 20, this.fallbackIcon, this.fallbackColor, @@ -101,8 +111,10 @@ class ProfilePictureWidget extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + assert(fileId != null || file != null); + final serverUrl = ref.watch(serverUrlProvider); - final uri = '$serverUrl/files/$fileId'; + final uri = '$serverUrl/files/${file?.id ?? fileId}'; return ClipRRect( borderRadius: BorderRadius.all(Radius.circular(radius)), @@ -119,6 +131,8 @@ class ProfilePictureWidget extends ConsumerWidget { fallbackColor ?? Theme.of(context).colorScheme.onPrimaryContainer, ).center() + : file != null + ? CloudFileWidget(item: file!, fit: BoxFit.cover) : UniversalImage(uri: uri, fit: BoxFit.cover), ), ); diff --git a/lib/widgets/post/post_item.dart b/lib/widgets/post/post_item.dart index f074876..073c019 100644 --- a/lib/widgets/post/post_item.dart +++ b/lib/widgets/post/post_item.dart @@ -11,6 +11,7 @@ import 'package:island/pods/network.dart'; import 'package:island/pods/userinfo.dart'; import 'package:island/route.gr.dart'; import 'package:island/services/responsive.dart'; +import 'package:island/widgets/account/account_name.dart'; import 'package:island/widgets/alert.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/content/cloud_file_collection.dart'; @@ -133,9 +134,7 @@ class PostItem extends HookConsumerWidget { spacing: 12, children: [ GestureDetector( - child: ProfilePictureWidget( - fileId: item.publisher.picture?.id, - ), + child: ProfilePictureWidget(file: item.publisher.picture), onTap: () { context.router.push( PublisherProfileRoute(name: item.publisher.name), @@ -147,7 +146,15 @@ class PostItem extends HookConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(item.publisher.nick).bold(), + Row( + children: [ + Text(item.publisher.nick).bold(), + if (item.publisher.verification != null) + VerificationMark( + mark: item.publisher.verification!, + ).padding(left: 4), + ], + ), // Add visibility indicator if not public (visibility != 0) if (item.visibility != 0) Row(