diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index c164c5a3..c64ee3bf 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -651,6 +651,10 @@ "editProject": "Edit Project", "projectDetails": "Project Details", "createBot": "Create Bot", + "bots": "Bots", + "noBots": "No bots yet.", + "deleteBotHint": "Are you sure you want to delete this bot? This action cannot be undone.", + "deleteBot": "Delete Bot", "customApps": "Custom Apps", "noCustomApps": "No custom apps yet.", "createCustomApp": "Create Custom App", @@ -885,5 +889,6 @@ "socialCreditsLevelGood": "Good", "socialCreditsLevelExcellent": "Excellent", "orderByPopularity": "Sort by popularity", - "orderByReleaseDate": "Sort by release date" + "orderByReleaseDate": "Sort by release date", + "editBot": "Edit Bot" } diff --git a/lib/models/bot.dart b/lib/models/bot.dart index e834f978..9c41a5ea 100644 --- a/lib/models/bot.dart +++ b/lib/models/bot.dart @@ -1,5 +1,4 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:island/models/file.dart'; import 'package:island/models/account.dart'; part 'bot.freezed.dart'; @@ -8,20 +7,13 @@ part 'bot.g.dart'; @freezed sealed class Bot with _$Bot { const factory Bot({ - @Default('') String id, - @Default('') String name, - @Default('') String slug, - String? description, - @Default(0) int status, - SnCloudFile? picture, - SnCloudFile? background, - SnVerificationMark? verification, - BotConfig? config, - BotLinks? links, - @Default('') String publisherId, - @Default('') String appId, - DateTime? createdAt, - DateTime? updatedAt, + required String id, + required String slug, + required bool isActive, + required String projectId, + required DateTime createdAt, + required DateTime updatedAt, + required SnAccount account, }) = _Bot; factory Bot.fromJson(Map json) => _$BotFromJson(json); diff --git a/lib/models/bot.freezed.dart b/lib/models/bot.freezed.dart index 278e79a6..80ee402b 100644 --- a/lib/models/bot.freezed.dart +++ b/lib/models/bot.freezed.dart @@ -15,7 +15,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$Bot { - String get id; String get name; String get slug; String? get description; int get status; SnCloudFile? get picture; SnCloudFile? get background; SnVerificationMark? get verification; BotConfig? get config; BotLinks? get links; String get publisherId; String get appId; DateTime? get createdAt; DateTime? get updatedAt; + String get id; String get slug; bool get isActive; String get projectId; DateTime get createdAt; DateTime get updatedAt; SnAccount get account; /// Create a copy of Bot /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -28,16 +28,16 @@ $BotCopyWith get copyWith => _$BotCopyWithImpl(this as Bot, _$identity @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is Bot&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.description, description) || other.description == description)&&(identical(other.status, status) || other.status == status)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.config, config) || other.config == config)&&(identical(other.links, links) || other.links == links)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.appId, appId) || other.appId == appId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is Bot&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.isActive, isActive) || other.isActive == isActive)&&(identical(other.projectId, projectId) || other.projectId == projectId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.account, account) || other.account == account)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,name,slug,description,status,picture,background,verification,config,links,publisherId,appId,createdAt,updatedAt); +int get hashCode => Object.hash(runtimeType,id,slug,isActive,projectId,createdAt,updatedAt,account); @override String toString() { - return 'Bot(id: $id, name: $name, slug: $slug, description: $description, status: $status, picture: $picture, background: $background, verification: $verification, config: $config, links: $links, publisherId: $publisherId, appId: $appId, createdAt: $createdAt, updatedAt: $updatedAt)'; + return 'Bot(id: $id, slug: $slug, isActive: $isActive, projectId: $projectId, createdAt: $createdAt, updatedAt: $updatedAt, account: $account)'; } @@ -48,11 +48,11 @@ abstract mixin class $BotCopyWith<$Res> { factory $BotCopyWith(Bot value, $Res Function(Bot) _then) = _$BotCopyWithImpl; @useResult $Res call({ - String id, String name, String slug, String? description, int status, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, BotConfig? config, BotLinks? links, String publisherId, String appId, DateTime? createdAt, DateTime? updatedAt + String id, String slug, bool isActive, String projectId, DateTime createdAt, DateTime updatedAt, SnAccount account }); -$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;$SnVerificationMarkCopyWith<$Res>? get verification;$BotConfigCopyWith<$Res>? get config;$BotLinksCopyWith<$Res>? get links; +$SnAccountCopyWith<$Res> get account; } /// @nodoc @@ -65,84 +65,26 @@ class _$BotCopyWithImpl<$Res> /// Create a copy of Bot /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? slug = null,Object? description = freezed,Object? status = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? config = freezed,Object? links = freezed,Object? publisherId = null,Object? appId = null,Object? createdAt = freezed,Object? updatedAt = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? isActive = null,Object? projectId = null,Object? createdAt = null,Object? updatedAt = null,Object? account = null,}) { return _then(_self.copyWith( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable -as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable -as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable -as String?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable -as int,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?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable -as SnVerificationMark?,config: freezed == config ? _self.config : config // ignore: cast_nullable_to_non_nullable -as BotConfig?,links: freezed == links ? _self.links : links // ignore: cast_nullable_to_non_nullable -as BotLinks?,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable -as String,appId: null == appId ? _self.appId : appId // ignore: cast_nullable_to_non_nullable -as String,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable -as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable -as DateTime?, +as String,isActive: null == isActive ? _self.isActive : isActive // ignore: cast_nullable_to_non_nullable +as bool,projectId: null == projectId ? _self.projectId : projectId // ignore: cast_nullable_to_non_nullable +as String,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,account: null == account ? _self.account : account // ignore: cast_nullable_to_non_nullable +as SnAccount, )); } /// Create a copy of Bot /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') -$SnCloudFileCopyWith<$Res>? get picture { - if (_self.picture == null) { - return null; - } - - return $SnCloudFileCopyWith<$Res>(_self.picture!, (value) { - return _then(_self.copyWith(picture: value)); - }); -}/// Create a copy of Bot -/// with the given fields replaced by the non-null parameter values. -@override -@pragma('vm:prefer-inline') -$SnCloudFileCopyWith<$Res>? get background { - if (_self.background == null) { - return null; - } - - return $SnCloudFileCopyWith<$Res>(_self.background!, (value) { - return _then(_self.copyWith(background: value)); - }); -}/// Create a copy of Bot -/// 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)); - }); -}/// Create a copy of Bot -/// with the given fields replaced by the non-null parameter values. -@override -@pragma('vm:prefer-inline') -$BotConfigCopyWith<$Res>? get config { - if (_self.config == null) { - return null; - } - - return $BotConfigCopyWith<$Res>(_self.config!, (value) { - return _then(_self.copyWith(config: value)); - }); -}/// Create a copy of Bot -/// with the given fields replaced by the non-null parameter values. -@override -@pragma('vm:prefer-inline') -$BotLinksCopyWith<$Res>? get links { - if (_self.links == null) { - return null; - } - - return $BotLinksCopyWith<$Res>(_self.links!, (value) { - return _then(_self.copyWith(links: value)); +$SnAccountCopyWith<$Res> get account { + + return $SnAccountCopyWith<$Res>(_self.account, (value) { + return _then(_self.copyWith(account: value)); }); } } @@ -223,10 +165,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String name, String slug, String? description, int status, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, BotConfig? config, BotLinks? links, String publisherId, String appId, DateTime? createdAt, DateTime? updatedAt)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String slug, bool isActive, String projectId, DateTime createdAt, DateTime updatedAt, SnAccount account)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _Bot() when $default != null: -return $default(_that.id,_that.name,_that.slug,_that.description,_that.status,_that.picture,_that.background,_that.verification,_that.config,_that.links,_that.publisherId,_that.appId,_that.createdAt,_that.updatedAt);case _: +return $default(_that.id,_that.slug,_that.isActive,_that.projectId,_that.createdAt,_that.updatedAt,_that.account);case _: return orElse(); } @@ -244,10 +186,10 @@ return $default(_that.id,_that.name,_that.slug,_that.description,_that.status,_t /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String id, String name, String slug, String? description, int status, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, BotConfig? config, BotLinks? links, String publisherId, String appId, DateTime? createdAt, DateTime? updatedAt) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String id, String slug, bool isActive, String projectId, DateTime createdAt, DateTime updatedAt, SnAccount account) $default,) {final _that = this; switch (_that) { case _Bot(): -return $default(_that.id,_that.name,_that.slug,_that.description,_that.status,_that.picture,_that.background,_that.verification,_that.config,_that.links,_that.publisherId,_that.appId,_that.createdAt,_that.updatedAt);} +return $default(_that.id,_that.slug,_that.isActive,_that.projectId,_that.createdAt,_that.updatedAt,_that.account);} } /// A variant of `when` that fallback to returning `null` /// @@ -261,10 +203,10 @@ return $default(_that.id,_that.name,_that.slug,_that.description,_that.status,_t /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String name, String slug, String? description, int status, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, BotConfig? config, BotLinks? links, String publisherId, String appId, DateTime? createdAt, DateTime? updatedAt)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String slug, bool isActive, String projectId, DateTime createdAt, DateTime updatedAt, SnAccount account)? $default,) {final _that = this; switch (_that) { case _Bot() when $default != null: -return $default(_that.id,_that.name,_that.slug,_that.description,_that.status,_that.picture,_that.background,_that.verification,_that.config,_that.links,_that.publisherId,_that.appId,_that.createdAt,_that.updatedAt);case _: +return $default(_that.id,_that.slug,_that.isActive,_that.projectId,_that.createdAt,_that.updatedAt,_that.account);case _: return null; } @@ -276,23 +218,16 @@ return $default(_that.id,_that.name,_that.slug,_that.description,_that.status,_t @JsonSerializable() class _Bot implements Bot { - const _Bot({this.id = '', this.name = '', this.slug = '', this.description, this.status = 0, this.picture, this.background, this.verification, this.config, this.links, this.publisherId = '', this.appId = '', this.createdAt, this.updatedAt}); + const _Bot({required this.id, required this.slug, required this.isActive, required this.projectId, required this.createdAt, required this.updatedAt, required this.account}); factory _Bot.fromJson(Map json) => _$BotFromJson(json); -@override@JsonKey() final String id; -@override@JsonKey() final String name; -@override@JsonKey() final String slug; -@override final String? description; -@override@JsonKey() final int status; -@override final SnCloudFile? picture; -@override final SnCloudFile? background; -@override final SnVerificationMark? verification; -@override final BotConfig? config; -@override final BotLinks? links; -@override@JsonKey() final String publisherId; -@override@JsonKey() final String appId; -@override final DateTime? createdAt; -@override final DateTime? updatedAt; +@override final String id; +@override final String slug; +@override final bool isActive; +@override final String projectId; +@override final DateTime createdAt; +@override final DateTime updatedAt; +@override final SnAccount account; /// Create a copy of Bot /// with the given fields replaced by the non-null parameter values. @@ -307,16 +242,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _Bot&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.description, description) || other.description == description)&&(identical(other.status, status) || other.status == status)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.config, config) || other.config == config)&&(identical(other.links, links) || other.links == links)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.appId, appId) || other.appId == appId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Bot&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.isActive, isActive) || other.isActive == isActive)&&(identical(other.projectId, projectId) || other.projectId == projectId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.account, account) || other.account == account)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,name,slug,description,status,picture,background,verification,config,links,publisherId,appId,createdAt,updatedAt); +int get hashCode => Object.hash(runtimeType,id,slug,isActive,projectId,createdAt,updatedAt,account); @override String toString() { - return 'Bot(id: $id, name: $name, slug: $slug, description: $description, status: $status, picture: $picture, background: $background, verification: $verification, config: $config, links: $links, publisherId: $publisherId, appId: $appId, createdAt: $createdAt, updatedAt: $updatedAt)'; + return 'Bot(id: $id, slug: $slug, isActive: $isActive, projectId: $projectId, createdAt: $createdAt, updatedAt: $updatedAt, account: $account)'; } @@ -327,11 +262,11 @@ abstract mixin class _$BotCopyWith<$Res> implements $BotCopyWith<$Res> { factory _$BotCopyWith(_Bot value, $Res Function(_Bot) _then) = __$BotCopyWithImpl; @override @useResult $Res call({ - String id, String name, String slug, String? description, int status, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, BotConfig? config, BotLinks? links, String publisherId, String appId, DateTime? createdAt, DateTime? updatedAt + String id, String slug, bool isActive, String projectId, DateTime createdAt, DateTime updatedAt, SnAccount account }); -@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;@override $SnVerificationMarkCopyWith<$Res>? get verification;@override $BotConfigCopyWith<$Res>? get config;@override $BotLinksCopyWith<$Res>? get links; +@override $SnAccountCopyWith<$Res> get account; } /// @nodoc @@ -344,23 +279,16 @@ class __$BotCopyWithImpl<$Res> /// Create a copy of Bot /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? slug = null,Object? description = freezed,Object? status = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? config = freezed,Object? links = freezed,Object? publisherId = null,Object? appId = null,Object? createdAt = freezed,Object? updatedAt = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? isActive = null,Object? projectId = null,Object? createdAt = null,Object? updatedAt = null,Object? account = null,}) { return _then(_Bot( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable -as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable -as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable -as String?,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable -as int,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?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable -as SnVerificationMark?,config: freezed == config ? _self.config : config // ignore: cast_nullable_to_non_nullable -as BotConfig?,links: freezed == links ? _self.links : links // ignore: cast_nullable_to_non_nullable -as BotLinks?,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable -as String,appId: null == appId ? _self.appId : appId // ignore: cast_nullable_to_non_nullable -as String,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable -as DateTime?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable -as DateTime?, +as String,isActive: null == isActive ? _self.isActive : isActive // ignore: cast_nullable_to_non_nullable +as bool,projectId: null == projectId ? _self.projectId : projectId // ignore: cast_nullable_to_non_nullable +as String,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,account: null == account ? _self.account : account // ignore: cast_nullable_to_non_nullable +as SnAccount, )); } @@ -368,61 +296,10 @@ as DateTime?, /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') -$SnCloudFileCopyWith<$Res>? get picture { - if (_self.picture == null) { - return null; - } - - return $SnCloudFileCopyWith<$Res>(_self.picture!, (value) { - return _then(_self.copyWith(picture: value)); - }); -}/// Create a copy of Bot -/// with the given fields replaced by the non-null parameter values. -@override -@pragma('vm:prefer-inline') -$SnCloudFileCopyWith<$Res>? get background { - if (_self.background == null) { - return null; - } - - return $SnCloudFileCopyWith<$Res>(_self.background!, (value) { - return _then(_self.copyWith(background: value)); - }); -}/// Create a copy of Bot -/// 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)); - }); -}/// Create a copy of Bot -/// with the given fields replaced by the non-null parameter values. -@override -@pragma('vm:prefer-inline') -$BotConfigCopyWith<$Res>? get config { - if (_self.config == null) { - return null; - } - - return $BotConfigCopyWith<$Res>(_self.config!, (value) { - return _then(_self.copyWith(config: value)); - }); -}/// Create a copy of Bot -/// with the given fields replaced by the non-null parameter values. -@override -@pragma('vm:prefer-inline') -$BotLinksCopyWith<$Res>? get links { - if (_self.links == null) { - return null; - } - - return $BotLinksCopyWith<$Res>(_self.links!, (value) { - return _then(_self.copyWith(links: value)); +$SnAccountCopyWith<$Res> get account { + + return $SnAccountCopyWith<$Res>(_self.account, (value) { + return _then(_self.copyWith(account: value)); }); } } diff --git a/lib/models/bot.g.dart b/lib/models/bot.g.dart index c8cf1aa8..1fb2290f 100644 --- a/lib/models/bot.g.dart +++ b/lib/models/bot.g.dart @@ -7,60 +7,23 @@ part of 'bot.dart'; // ************************************************************************** _Bot _$BotFromJson(Map json) => _Bot( - id: json['id'] as String? ?? '', - name: json['name'] as String? ?? '', - slug: json['slug'] as String? ?? '', - description: json['description'] as String?, - status: (json['status'] as num?)?.toInt() ?? 0, - picture: - json['picture'] == null - ? null - : SnCloudFile.fromJson(json['picture'] as Map), - background: - json['background'] == null - ? null - : SnCloudFile.fromJson(json['background'] as Map), - verification: - json['verification'] == null - ? null - : SnVerificationMark.fromJson( - json['verification'] as Map, - ), - config: - json['config'] == null - ? null - : BotConfig.fromJson(json['config'] as Map), - links: - json['links'] == null - ? null - : BotLinks.fromJson(json['links'] as Map), - publisherId: json['publisher_id'] as String? ?? '', - appId: json['app_id'] as String? ?? '', - createdAt: - json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: - json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), + id: json['id'] as String, + slug: json['slug'] as String, + isActive: json['is_active'] as bool, + projectId: json['project_id'] as String, + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + account: SnAccount.fromJson(json['account'] as Map), ); Map _$BotToJson(_Bot instance) => { 'id': instance.id, - 'name': instance.name, 'slug': instance.slug, - 'description': instance.description, - 'status': instance.status, - 'picture': instance.picture?.toJson(), - 'background': instance.background?.toJson(), - 'verification': instance.verification?.toJson(), - 'config': instance.config?.toJson(), - 'links': instance.links?.toJson(), - 'publisher_id': instance.publisherId, - 'app_id': instance.appId, - 'created_at': instance.createdAt?.toIso8601String(), - 'updated_at': instance.updatedAt?.toIso8601String(), + 'is_active': instance.isActive, + 'project_id': instance.projectId, + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + 'account': instance.account.toJson(), }; _BotConfig _$BotConfigFromJson(Map json) => _BotConfig( diff --git a/lib/route.dart b/lib/route.dart index bb616140..0927a8d2 100644 --- a/lib/route.dart +++ b/lib/route.dart @@ -366,17 +366,6 @@ final routerProvider = Provider((ref) { id: state.pathParameters['id']!, ), ), - GoRoute( - name: 'developerBotDetail', - path: 'bots/:id/detail', - builder: - (context, state) => EditBotScreen( - // Assuming EditBotScreen can also serve as a detail view - publisherName: state.pathParameters['name']!, - projectId: state.pathParameters['projectId']!, - id: state.pathParameters['id']!, - ), - ), ], ), ], diff --git a/lib/screens/developers/apps.g.dart b/lib/screens/developers/apps.g.dart index de6654ac..0ce9b04d 100644 --- a/lib/screens/developers/apps.g.dart +++ b/lib/screens/developers/apps.g.dart @@ -6,7 +6,7 @@ part of 'apps.dart'; // RiverpodGenerator // ************************************************************************** -String _$customAppsHash() => r'c36e5ee59f16a29220dc0e9fba65e579d341a28f'; +String _$customAppsHash() => r'450bedaf4220b8963cb44afeb14d4c0e80f01b11'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/screens/developers/bots.dart b/lib/screens/developers/bots.dart index 8c5bf213..2fff4bbc 100644 --- a/lib/screens/developers/bots.dart +++ b/lib/screens/developers/bots.dart @@ -71,14 +71,19 @@ class BotsScreen extends HookConsumerWidget { return Card( margin: const EdgeInsets.all(8.0), child: ListTile( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8.0)), + ), leading: CircleAvatar( child: - bot.picture != null - ? CloudFileWidget(item: bot.picture!) + bot.account.profile.picture != null + ? ProfilePictureWidget( + file: bot.account.profile.picture!, + ) : const Icon(Symbols.smart_toy), ), - title: Text(bot.name), - subtitle: Text(bot.description ?? ''), + title: Text(bot.account.nick), + subtitle: Text(bot.account.name), trailing: PopupMenuButton( itemBuilder: (context) => [ @@ -135,13 +140,9 @@ class BotsScreen extends HookConsumerWidget { }, ), onTap: () { - context.pushNamed( - 'developerBotDetail', - pathParameters: { - 'name': publisherName, - 'projectId': projectId, - 'id': bot.id, - }, + context.goNamed( + 'accountProfile', + pathParameters: {'name': bot.account.name}, ); }, ), diff --git a/lib/screens/developers/bots.g.dart b/lib/screens/developers/bots.g.dart index e4e8f9f9..ad316c04 100644 --- a/lib/screens/developers/bots.g.dart +++ b/lib/screens/developers/bots.g.dart @@ -6,7 +6,7 @@ part of 'bots.dart'; // RiverpodGenerator // ************************************************************************** -String _$botsHash() => r'a54c8b4df23f94754398706779044903fcca6eea'; +String _$botsHash() => r'15cefd5781350eb68208a342e85fcb0b9e0e3269'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/screens/developers/edit_app.g.dart b/lib/screens/developers/edit_app.g.dart index f6f6c2b0..d4c79505 100644 --- a/lib/screens/developers/edit_app.g.dart +++ b/lib/screens/developers/edit_app.g.dart @@ -6,7 +6,7 @@ part of 'edit_app.dart'; // RiverpodGenerator // ************************************************************************** -String _$customAppHash() => r'17b3d1385e59bc5ee7f13fb0f11c56cf8a9ba41f'; +String _$customAppHash() => r'8e1b38f3dc9b04fad362ee1141fcbfc53f008c09'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/screens/developers/edit_bot.dart b/lib/screens/developers/edit_bot.dart index d2c51174..fb0b8fcc 100644 --- a/lib/screens/developers/edit_bot.dart +++ b/lib/screens/developers/edit_bot.dart @@ -55,31 +55,44 @@ class EditBotScreen extends HookConsumerWidget { final submitting = useState(false); final nameController = useTextEditingController(); + final nickController = useTextEditingController(); final slugController = useTextEditingController(); - final descriptionController = useTextEditingController(); final picture = useState(null); - final websiteController = useTextEditingController(); - final documentationController = useTextEditingController(); - final isPublic = useState(false); - final isInteractive = useState(false); + final firstNameController = useTextEditingController(); + final middleNameController = useTextEditingController(); + final lastNameController = useTextEditingController(); + final genderController = useTextEditingController(); + final pronounsController = useTextEditingController(); + final locationController = useTextEditingController(); + final timeZoneController = useTextEditingController(); + final bioController = useTextEditingController(); + final birthday = useState(null); + final background = useState(null); useEffect(() { if (botData?.value != null) { - nameController.text = botData!.value!.name; + nameController.text = botData!.value!.account.name; + nickController.text = botData.value!.account.nick; slugController.text = botData.value!.slug; - descriptionController.text = botData.value!.description ?? ''; - picture.value = botData.value!.picture; - websiteController.text = botData.value!.links?.website ?? ''; - documentationController.text = - botData.value!.links?.documentation ?? ''; - isPublic.value = botData.value!.config?.isPublic ?? false; - isInteractive.value = botData.value!.config?.isInteractive ?? false; + picture.value = botData.value!.account.profile.picture; + background.value = botData.value!.account.profile.background; + + // Populate from botData.value.account.profile + firstNameController.text = botData.value!.account.profile.firstName; + middleNameController.text = botData.value!.account.profile.middleName; + lastNameController.text = botData.value!.account.profile.lastName; + genderController.text = botData.value!.account.profile.gender; + pronounsController.text = botData.value!.account.profile.pronouns; + locationController.text = botData.value!.account.profile.location; + timeZoneController.text = botData.value!.account.profile.timeZone; + bioController.text = botData.value!.account.profile.bio; + birthday.value = botData.value!.account.profile.birthday?.toLocal(); } return null; }, [botData]); - void setPicture() async { + void setPicture(String position) async { showLoadingModal(context); var result = await ref .read(imagePickerProvider) @@ -94,7 +107,12 @@ class EditBotScreen extends HookConsumerWidget { result = await cropImage( context, image: result, - allowedAspectRatios: [const CropAspectRatio(height: 1, width: 1)], + allowedAspectRatios: [ + if (position == 'background') + const CropAspectRatio(height: 7, width: 16) + else + const CropAspectRatio(height: 1, width: 1), + ], ); if (result == null) { if (context.mounted) hideLoadingModal(context); @@ -122,7 +140,12 @@ class EditBotScreen extends HookConsumerWidget { if (cloudFile == null) { throw ArgumentError('Failed to upload the file...'); } - picture.value = cloudFile; + switch (position) { + case 'picture': + picture.value = cloudFile; + case 'background': + background.value = cloudFile; + } } catch (err) { showErrorAlert(err); } finally { @@ -135,37 +158,42 @@ class EditBotScreen extends HookConsumerWidget { final client = ref.read(apiClientProvider); final data = { 'name': nameController.text, + 'nick': nickController.text, 'slug': slugController.text, - 'description': descriptionController.text, 'picture_id': picture.value?.id, - 'config': { - 'is_public': isPublic.value, - 'is_interactive': isInteractive.value, - }, - 'links': { - 'website': - websiteController.text.isNotEmpty ? websiteController.text : null, - 'documentation': - documentationController.text.isNotEmpty - ? documentationController.text - : null, - }, + 'background_id': background.value?.id, + 'first_name': firstNameController.text, + 'middle_name': middleNameController.text, + 'last_name': lastNameController.text, + 'gender': genderController.text, + 'pronouns': pronounsController.text, + 'location': locationController.text, + 'time_zone': timeZoneController.text, + 'bio': bioController.text, + 'birthday': birthday.value?.toUtc().toIso8601String(), }; - if (isNew) { - await client.post( - '/develop/developers/$publisherName/projects/$projectId/bots', - data: data, - ); - } else { - await client.patch( - '/develop/developers/$publisherName/projects/$projectId/bots/$id', - data: data, - ); - } + try { + showLoadingModal(context); + if (isNew) { + await client.post( + '/develop/developers/$publisherName/projects/$projectId/bots', + data: data, + ); + } else { + await client.patch( + '/develop/developers/$publisherName/projects/$projectId/bots/$id', + data: data, + ); + } - if (context.mounted) { - context.pop(); + if (context.mounted) { + context.pop(); + } + } catch (err) { + showErrorAlert(err); + } finally { + if (context.mounted) hideLoadingModal(context); } } @@ -186,22 +214,44 @@ class EditBotScreen extends HookConsumerWidget { child: Column( children: [ AspectRatio( - aspectRatio: 1, - child: GestureDetector( - onTap: setPicture, - child: Container( - color: - Theme.of( - context, - ).colorScheme.surfaceContainerHigh, - child: - picture.value != null - ? CloudFileWidget( - item: picture.value!, - fit: BoxFit.cover, - ) - : const Icon(Symbols.smart_toy, size: 48), - ), + aspectRatio: 16 / 7, + child: Stack( + clipBehavior: Clip.none, + fit: StackFit.expand, + children: [ + GestureDetector( + child: Container( + color: + Theme.of( + context, + ).colorScheme.surfaceContainerHigh, + child: + background.value != null + ? CloudFileWidget( + item: background.value!, + fit: BoxFit.cover, + ) + : const SizedBox.shrink(), + ), + onTap: () { + setPicture('background'); + }, + ), + Positioned( + left: 20, + bottom: -32, + child: GestureDetector( + child: ProfilePictureWidget( + fileId: picture.value?.id, + radius: 40, + fallbackIcon: Symbols.smart_toy, + ), + onTap: () { + setPicture('picture'); + }, + ), + ), + ], ), ).padding(bottom: 32), Form( @@ -213,6 +263,14 @@ class EditBotScreen extends HookConsumerWidget { decoration: InputDecoration(labelText: 'name'.tr()), ), const SizedBox(height: 16), + TextFormField( + controller: nickController, + decoration: InputDecoration( + labelText: 'nickname'.tr(), + alignLabelWithHint: true, + ), + ), + const SizedBox(height: 16), TextFormField( controller: slugController, decoration: InputDecoration( @@ -222,41 +280,129 @@ class EditBotScreen extends HookConsumerWidget { ), const SizedBox(height: 16), TextFormField( - controller: descriptionController, + controller: bioController, decoration: InputDecoration( - labelText: 'description'.tr(), + labelText: 'bio'.tr(), alignLabelWithHint: true, ), maxLines: 3, ), const SizedBox(height: 16), - TextFormField( - controller: websiteController, - decoration: InputDecoration( - labelText: 'websiteUrl'.tr(), - hintText: 'https://example.com', - ), - keyboardType: TextInputType.url, + Row( + spacing: 16, + children: [ + Expanded( + child: TextFormField( + controller: firstNameController, + decoration: InputDecoration( + labelText: 'firstName'.tr(), + ), + ), + ), + Expanded( + child: TextFormField( + controller: middleNameController, + decoration: InputDecoration( + labelText: 'middleName'.tr(), + ), + ), + ), + Expanded( + child: TextFormField( + controller: lastNameController, + decoration: InputDecoration( + labelText: 'lastName'.tr(), + ), + ), + ), + ], ), const SizedBox(height: 16), - TextFormField( - controller: documentationController, - decoration: InputDecoration( - labelText: 'documentationUrl'.tr(), - hintText: 'https://example.com/docs', - ), - keyboardType: TextInputType.url, + Row( + spacing: 16, + children: [ + Expanded( + child: TextFormField( + controller: genderController, + decoration: InputDecoration( + labelText: 'gender'.tr(), + ), + ), + ), + Expanded( + child: TextFormField( + controller: pronounsController, + decoration: InputDecoration( + labelText: 'pronouns'.tr(), + ), + ), + ), + ], ), const SizedBox(height: 16), - SwitchListTile( - title: Text('isPublic').tr(), - value: isPublic.value, - onChanged: (value) => isPublic.value = value, + Row( + spacing: 16, + children: [ + Expanded( + child: TextFormField( + controller: locationController, + decoration: InputDecoration( + labelText: 'location'.tr(), + ), + ), + ), + Expanded( + child: TextFormField( + controller: timeZoneController, + decoration: InputDecoration( + labelText: 'timeZone'.tr(), + ), + ), + ), + ], ), - SwitchListTile( - title: Text('isInteractive').tr(), - value: isInteractive.value, - onChanged: (value) => isInteractive.value = value, + const SizedBox(height: 16), + GestureDetector( + onTap: () async { + final date = await showDatePicker( + context: context, + initialDate: birthday.value ?? DateTime.now(), + firstDate: DateTime(1900), + lastDate: DateTime.now(), + ); + if (date != null) { + birthday.value = date; + } + }, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor, + width: 1, + ), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + 'birthday'.tr(), + style: TextStyle( + color: Theme.of(context).hintColor, + ), + ), + Text( + birthday.value != null + ? DateFormat.yMMMd().format( + birthday.value!, + ) + : 'Select a date'.tr(), + ), + ], + ), + ), ), const SizedBox(height: 16), Align( @@ -264,7 +410,7 @@ class EditBotScreen extends HookConsumerWidget { child: TextButton.icon( onPressed: submitting.value ? null : performAction, - label: Text('saveChanges'.tr()), + label: Text('saveChanges').tr(), icon: const Icon(Symbols.save), ), ), diff --git a/lib/screens/developers/edit_bot.g.dart b/lib/screens/developers/edit_bot.g.dart index 00abb1e8..2b1ae697 100644 --- a/lib/screens/developers/edit_bot.g.dart +++ b/lib/screens/developers/edit_bot.g.dart @@ -6,7 +6,7 @@ part of 'edit_bot.dart'; // RiverpodGenerator // ************************************************************************** -String _$botHash() => r'a3e412ed575c513434bc718b7920db1d017111f4'; +String _$botHash() => r'7bec47bb2a4061a5babc6d6d19c3d4c320c91188'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/screens/developers/edit_project.g.dart b/lib/screens/developers/edit_project.g.dart index ae900733..0c525df6 100644 --- a/lib/screens/developers/edit_project.g.dart +++ b/lib/screens/developers/edit_project.g.dart @@ -6,7 +6,7 @@ part of 'edit_project.dart'; // RiverpodGenerator // ************************************************************************** -String _$devProjectHash() => r'fc68254c6e598e3fa05c86c36f1469c0b689bc43'; +String _$devProjectHash() => r'd92be3f5cdc510c2a377615ed5c70622a6842bf2'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/screens/developers/projects.dart b/lib/screens/developers/projects.dart index 0455a900..147ab297 100644 --- a/lib/screens/developers/projects.dart +++ b/lib/screens/developers/projects.dart @@ -61,6 +61,10 @@ class DevProjectsScreen extends HookConsumerWidget { return Card( margin: const EdgeInsets.all(8.0), child: ListTile( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + contentPadding: EdgeInsets.only(left: 20, right: 12), title: Text(project.name), subtitle: Text(project.description ?? ''), trailing: PopupMenuButton( diff --git a/lib/screens/developers/projects.g.dart b/lib/screens/developers/projects.g.dart index ad490b91..7cdb5b38 100644 --- a/lib/screens/developers/projects.g.dart +++ b/lib/screens/developers/projects.g.dart @@ -6,7 +6,7 @@ part of 'projects.dart'; // RiverpodGenerator // ************************************************************************** -String _$devProjectsHash() => r'4c86ea5c3c02185514dbfa32804f1529f68d56c7'; +String _$devProjectsHash() => r'87fdcab47cd7d79ab019a5625617abeb1ffa1f39'; /// Copied from Dart SDK class _SystemHash {