From a564e4ee0a130a69042f97a11ec260c0daf88cc5 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 26 Apr 2025 01:47:47 +0800 Subject: [PATCH] :sparkles: Basic posting --- devtools_options.yaml | 3 + lib/models/post.dart | 8 +- lib/models/post.freezed.dart | 46 ++++---- lib/models/post.g.dart | 13 ++- lib/route.dart | 1 + lib/route.gr.dart | 105 ++++++++++-------- lib/screens/{auth => }/account/me.dart | 0 .../{auth => }/account/me/publishers.dart | 0 .../{auth => }/account/me/publishers.g.dart | 0 lib/screens/{auth => }/account/me/update.dart | 0 lib/screens/auth/tabs.dart | 1 - lib/screens/explore.dart | 29 +++-- lib/screens/posts/compose.dart | 97 ++++++++++++++++ 13 files changed, 216 insertions(+), 87 deletions(-) create mode 100644 devtools_options.yaml rename lib/screens/{auth => }/account/me.dart (100%) rename lib/screens/{auth => }/account/me/publishers.dart (100%) rename lib/screens/{auth => }/account/me/publishers.g.dart (100%) rename lib/screens/{auth => }/account/me/update.dart (100%) create mode 100644 lib/screens/posts/compose.dart diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/models/post.dart b/lib/models/post.dart index 832db4a..e379811 100644 --- a/lib/models/post.dart +++ b/lib/models/post.dart @@ -8,10 +8,10 @@ part 'post.g.dart'; abstract class SnPost with _$SnPost { const factory SnPost({ required int id, - required String title, - required String description, - required dynamic language, - required dynamic editedAt, + required String? title, + required String? description, + required String? language, + required DateTime? editedAt, required DateTime publishedAt, required int visibility, required String content, diff --git a/lib/models/post.freezed.dart b/lib/models/post.freezed.dart index f023334..ab2c4d2 100644 --- a/lib/models/post.freezed.dart +++ b/lib/models/post.freezed.dart @@ -16,7 +16,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$SnPost { - int get id; String get title; String get description; dynamic get language; dynamic get editedAt; DateTime get publishedAt; int get visibility; String get content; int get type; Map? get meta; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; dynamic get threadedPostId; dynamic get threadedPost; dynamic get repliedPostId; dynamic get repliedPost; dynamic get forwardedPostId; dynamic get forwardedPost; List get attachments; SnPublisher get publisher; List get reactions; List get tags; List get categories; List get collections; bool get empty; DateTime get createdAt; DateTime get updatedAt; dynamic get deletedAt; + int get id; String? get title; String? get description; String? get language; DateTime? get editedAt; DateTime get publishedAt; int get visibility; String get content; int get type; Map? get meta; int get viewsUnique; int get viewsTotal; int get upvotes; int get downvotes; dynamic get threadedPostId; dynamic get threadedPost; dynamic get repliedPostId; dynamic get repliedPost; dynamic get forwardedPostId; dynamic get forwardedPost; List get attachments; SnPublisher get publisher; List get reactions; List get tags; List get categories; List get collections; bool get empty; DateTime get createdAt; DateTime get updatedAt; dynamic get deletedAt; /// Create a copy of SnPost /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -29,12 +29,12 @@ $SnPostCopyWith get copyWith => _$SnPostCopyWithImpl(this as SnP @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&const DeepCollectionEquality().equals(other.language, language)&&const DeepCollectionEquality().equals(other.editedAt, editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&const DeepCollectionEquality().equals(other.threadedPostId, threadedPostId)&&const DeepCollectionEquality().equals(other.threadedPost, threadedPost)&&const DeepCollectionEquality().equals(other.repliedPostId, repliedPostId)&&const DeepCollectionEquality().equals(other.repliedPost, repliedPost)&&const DeepCollectionEquality().equals(other.forwardedPostId, forwardedPostId)&&const DeepCollectionEquality().equals(other.forwardedPost, forwardedPost)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&const DeepCollectionEquality().equals(other.tags, tags)&&const DeepCollectionEquality().equals(other.categories, categories)&&const DeepCollectionEquality().equals(other.collections, collections)&&(identical(other.empty, empty) || other.empty == empty)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&const DeepCollectionEquality().equals(other.deletedAt, deletedAt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&const DeepCollectionEquality().equals(other.threadedPostId, threadedPostId)&&const DeepCollectionEquality().equals(other.threadedPost, threadedPost)&&const DeepCollectionEquality().equals(other.repliedPostId, repliedPostId)&&const DeepCollectionEquality().equals(other.repliedPost, repliedPost)&&const DeepCollectionEquality().equals(other.forwardedPostId, forwardedPostId)&&const DeepCollectionEquality().equals(other.forwardedPost, forwardedPost)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&const DeepCollectionEquality().equals(other.tags, tags)&&const DeepCollectionEquality().equals(other.categories, categories)&&const DeepCollectionEquality().equals(other.collections, collections)&&(identical(other.empty, empty) || other.empty == empty)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&const DeepCollectionEquality().equals(other.deletedAt, deletedAt)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hashAll([runtimeType,id,title,description,const DeepCollectionEquality().hash(language),const DeepCollectionEquality().hash(editedAt),publishedAt,visibility,content,type,const DeepCollectionEquality().hash(meta),viewsUnique,viewsTotal,upvotes,downvotes,const DeepCollectionEquality().hash(threadedPostId),const DeepCollectionEquality().hash(threadedPost),const DeepCollectionEquality().hash(repliedPostId),const DeepCollectionEquality().hash(repliedPost),const DeepCollectionEquality().hash(forwardedPostId),const DeepCollectionEquality().hash(forwardedPost),const DeepCollectionEquality().hash(attachments),publisher,const DeepCollectionEquality().hash(reactions),const DeepCollectionEquality().hash(tags),const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(collections),empty,createdAt,updatedAt,const DeepCollectionEquality().hash(deletedAt)]); +int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,type,const DeepCollectionEquality().hash(meta),viewsUnique,viewsTotal,upvotes,downvotes,const DeepCollectionEquality().hash(threadedPostId),const DeepCollectionEquality().hash(threadedPost),const DeepCollectionEquality().hash(repliedPostId),const DeepCollectionEquality().hash(repliedPost),const DeepCollectionEquality().hash(forwardedPostId),const DeepCollectionEquality().hash(forwardedPost),const DeepCollectionEquality().hash(attachments),publisher,const DeepCollectionEquality().hash(reactions),const DeepCollectionEquality().hash(tags),const DeepCollectionEquality().hash(categories),const DeepCollectionEquality().hash(collections),empty,createdAt,updatedAt,const DeepCollectionEquality().hash(deletedAt)]); @override String toString() { @@ -49,7 +49,7 @@ abstract mixin class $SnPostCopyWith<$Res> { factory $SnPostCopyWith(SnPost value, $Res Function(SnPost) _then) = _$SnPostCopyWithImpl; @useResult $Res call({ - int id, String title, String description, dynamic language, dynamic editedAt, DateTime publishedAt, int visibility, String content, int type, Map? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, dynamic threadedPostId, dynamic threadedPost, dynamic repliedPostId, dynamic repliedPost, dynamic forwardedPostId, dynamic forwardedPost, List attachments, SnPublisher publisher, List reactions, List tags, List categories, List collections, bool empty, DateTime createdAt, DateTime updatedAt, dynamic deletedAt + int id, String? title, String? description, String? language, DateTime? editedAt, DateTime publishedAt, int visibility, String content, int type, Map? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, dynamic threadedPostId, dynamic threadedPost, dynamic repliedPostId, dynamic repliedPost, dynamic forwardedPostId, dynamic forwardedPost, List attachments, SnPublisher publisher, List reactions, List tags, List categories, List collections, bool empty, DateTime createdAt, DateTime updatedAt, dynamic deletedAt }); @@ -66,14 +66,14 @@ class _$SnPostCopyWithImpl<$Res> /// Create a copy of SnPost /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = null,Object? description = null,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = null,Object? visibility = null,Object? content = null,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? empty = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = null,Object? visibility = null,Object? content = null,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? empty = null,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 int,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable -as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable -as String,language: freezed == language ? _self.language : language // ignore: cast_nullable_to_non_nullable -as dynamic,editedAt: freezed == editedAt ? _self.editedAt : editedAt // ignore: cast_nullable_to_non_nullable -as dynamic,publishedAt: null == publishedAt ? _self.publishedAt : publishedAt // 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?,language: freezed == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String?,editedAt: freezed == editedAt ? _self.editedAt : editedAt // ignore: cast_nullable_to_non_nullable +as DateTime?,publishedAt: null == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable as DateTime,visibility: null == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable as int,content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable @@ -122,10 +122,10 @@ class _SnPost implements SnPost { factory _SnPost.fromJson(Map json) => _$SnPostFromJson(json); @override final int id; -@override final String title; -@override final String description; -@override final dynamic language; -@override final dynamic editedAt; +@override final String? title; +@override final String? description; +@override final String? language; +@override final DateTime? editedAt; @override final DateTime publishedAt; @override final int visibility; @override final String content; @@ -203,12 +203,12 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&const DeepCollectionEquality().equals(other.language, language)&&const DeepCollectionEquality().equals(other.editedAt, editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&const DeepCollectionEquality().equals(other.threadedPostId, threadedPostId)&&const DeepCollectionEquality().equals(other.threadedPost, threadedPost)&&const DeepCollectionEquality().equals(other.repliedPostId, repliedPostId)&&const DeepCollectionEquality().equals(other.repliedPost, repliedPost)&&const DeepCollectionEquality().equals(other.forwardedPostId, forwardedPostId)&&const DeepCollectionEquality().equals(other.forwardedPost, forwardedPost)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&const DeepCollectionEquality().equals(other._tags, _tags)&&const DeepCollectionEquality().equals(other._categories, _categories)&&const DeepCollectionEquality().equals(other._collections, _collections)&&(identical(other.empty, empty) || other.empty == empty)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&const DeepCollectionEquality().equals(other.deletedAt, deletedAt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPost&&(identical(other.id, id) || other.id == id)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.language, language) || other.language == language)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&(identical(other.publishedAt, publishedAt) || other.publishedAt == publishedAt)&&(identical(other.visibility, visibility) || other.visibility == visibility)&&(identical(other.content, content) || other.content == content)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.viewsUnique, viewsUnique) || other.viewsUnique == viewsUnique)&&(identical(other.viewsTotal, viewsTotal) || other.viewsTotal == viewsTotal)&&(identical(other.upvotes, upvotes) || other.upvotes == upvotes)&&(identical(other.downvotes, downvotes) || other.downvotes == downvotes)&&const DeepCollectionEquality().equals(other.threadedPostId, threadedPostId)&&const DeepCollectionEquality().equals(other.threadedPost, threadedPost)&&const DeepCollectionEquality().equals(other.repliedPostId, repliedPostId)&&const DeepCollectionEquality().equals(other.repliedPost, repliedPost)&&const DeepCollectionEquality().equals(other.forwardedPostId, forwardedPostId)&&const DeepCollectionEquality().equals(other.forwardedPost, forwardedPost)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&const DeepCollectionEquality().equals(other._tags, _tags)&&const DeepCollectionEquality().equals(other._categories, _categories)&&const DeepCollectionEquality().equals(other._collections, _collections)&&(identical(other.empty, empty) || other.empty == empty)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&const DeepCollectionEquality().equals(other.deletedAt, deletedAt)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hashAll([runtimeType,id,title,description,const DeepCollectionEquality().hash(language),const DeepCollectionEquality().hash(editedAt),publishedAt,visibility,content,type,const DeepCollectionEquality().hash(_meta),viewsUnique,viewsTotal,upvotes,downvotes,const DeepCollectionEquality().hash(threadedPostId),const DeepCollectionEquality().hash(threadedPost),const DeepCollectionEquality().hash(repliedPostId),const DeepCollectionEquality().hash(repliedPost),const DeepCollectionEquality().hash(forwardedPostId),const DeepCollectionEquality().hash(forwardedPost),const DeepCollectionEquality().hash(_attachments),publisher,const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_tags),const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_collections),empty,createdAt,updatedAt,const DeepCollectionEquality().hash(deletedAt)]); +int get hashCode => Object.hashAll([runtimeType,id,title,description,language,editedAt,publishedAt,visibility,content,type,const DeepCollectionEquality().hash(_meta),viewsUnique,viewsTotal,upvotes,downvotes,const DeepCollectionEquality().hash(threadedPostId),const DeepCollectionEquality().hash(threadedPost),const DeepCollectionEquality().hash(repliedPostId),const DeepCollectionEquality().hash(repliedPost),const DeepCollectionEquality().hash(forwardedPostId),const DeepCollectionEquality().hash(forwardedPost),const DeepCollectionEquality().hash(_attachments),publisher,const DeepCollectionEquality().hash(_reactions),const DeepCollectionEquality().hash(_tags),const DeepCollectionEquality().hash(_categories),const DeepCollectionEquality().hash(_collections),empty,createdAt,updatedAt,const DeepCollectionEquality().hash(deletedAt)]); @override String toString() { @@ -223,7 +223,7 @@ abstract mixin class _$SnPostCopyWith<$Res> implements $SnPostCopyWith<$Res> { factory _$SnPostCopyWith(_SnPost value, $Res Function(_SnPost) _then) = __$SnPostCopyWithImpl; @override @useResult $Res call({ - int id, String title, String description, dynamic language, dynamic editedAt, DateTime publishedAt, int visibility, String content, int type, Map? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, dynamic threadedPostId, dynamic threadedPost, dynamic repliedPostId, dynamic repliedPost, dynamic forwardedPostId, dynamic forwardedPost, List attachments, SnPublisher publisher, List reactions, List tags, List categories, List collections, bool empty, DateTime createdAt, DateTime updatedAt, dynamic deletedAt + int id, String? title, String? description, String? language, DateTime? editedAt, DateTime publishedAt, int visibility, String content, int type, Map? meta, int viewsUnique, int viewsTotal, int upvotes, int downvotes, dynamic threadedPostId, dynamic threadedPost, dynamic repliedPostId, dynamic repliedPost, dynamic forwardedPostId, dynamic forwardedPost, List attachments, SnPublisher publisher, List reactions, List tags, List categories, List collections, bool empty, DateTime createdAt, DateTime updatedAt, dynamic deletedAt }); @@ -240,14 +240,14 @@ class __$SnPostCopyWithImpl<$Res> /// Create a copy of SnPost /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = null,Object? description = null,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = null,Object? visibility = null,Object? content = null,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? empty = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? title = freezed,Object? description = freezed,Object? language = freezed,Object? editedAt = freezed,Object? publishedAt = null,Object? visibility = null,Object? content = null,Object? type = null,Object? meta = freezed,Object? viewsUnique = null,Object? viewsTotal = null,Object? upvotes = null,Object? downvotes = null,Object? threadedPostId = freezed,Object? threadedPost = freezed,Object? repliedPostId = freezed,Object? repliedPost = freezed,Object? forwardedPostId = freezed,Object? forwardedPost = freezed,Object? attachments = null,Object? publisher = null,Object? reactions = null,Object? tags = null,Object? categories = null,Object? collections = null,Object? empty = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { return _then(_SnPost( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable -as int,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable -as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable -as String,language: freezed == language ? _self.language : language // ignore: cast_nullable_to_non_nullable -as dynamic,editedAt: freezed == editedAt ? _self.editedAt : editedAt // ignore: cast_nullable_to_non_nullable -as dynamic,publishedAt: null == publishedAt ? _self.publishedAt : publishedAt // 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?,language: freezed == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String?,editedAt: freezed == editedAt ? _self.editedAt : editedAt // ignore: cast_nullable_to_non_nullable +as DateTime?,publishedAt: null == publishedAt ? _self.publishedAt : publishedAt // ignore: cast_nullable_to_non_nullable as DateTime,visibility: null == visibility ? _self.visibility : visibility // ignore: cast_nullable_to_non_nullable as int,content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable diff --git a/lib/models/post.g.dart b/lib/models/post.g.dart index 09302bf..c8206fe 100644 --- a/lib/models/post.g.dart +++ b/lib/models/post.g.dart @@ -8,10 +8,13 @@ part of 'post.dart'; _SnPost _$SnPostFromJson(Map json) => _SnPost( id: (json['id'] as num).toInt(), - title: json['title'] as String, - description: json['description'] as String, - language: json['language'], - editedAt: json['edited_at'], + title: json['title'] as String?, + description: json['description'] as String?, + language: json['language'] as String?, + editedAt: + json['edited_at'] == null + ? null + : DateTime.parse(json['edited_at'] as String), publishedAt: DateTime.parse(json['published_at'] as String), visibility: (json['visibility'] as num).toInt(), content: json['content'] as String, @@ -47,7 +50,7 @@ Map _$SnPostToJson(_SnPost instance) => { 'title': instance.title, 'description': instance.description, 'language': instance.language, - 'edited_at': instance.editedAt, + 'edited_at': instance.editedAt?.toIso8601String(), 'published_at': instance.publishedAt.toIso8601String(), 'visibility': instance.visibility, 'content': instance.content, diff --git a/lib/route.dart b/lib/route.dart index de9c3f8..34882eb 100644 --- a/lib/route.dart +++ b/lib/route.dart @@ -27,5 +27,6 @@ class AppRouter extends RootStackRouter { page: EditPublisherRoute.page, path: '/account/me/publishers/:id', ), + AutoRoute(page: PostComposeRoute.page, path: '/posts/compose'), ]; } diff --git a/lib/route.gr.dart b/lib/route.gr.dart index 73c72b7..c9b0769 100644 --- a/lib/route.gr.dart +++ b/lib/route.gr.dart @@ -9,26 +9,27 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:auto_route/auto_route.dart' as _i9; -import 'package:flutter/material.dart' as _i10; +import 'package:auto_route/auto_route.dart' as _i10; +import 'package:flutter/material.dart' as _i11; import 'package:island/screens/account.dart' as _i1; -import 'package:island/screens/auth/account/me.dart' as _i6; -import 'package:island/screens/auth/account/me/publishers.dart' as _i3; -import 'package:island/screens/auth/account/me/update.dart' as _i8; +import 'package:island/screens/account/me.dart' as _i6; +import 'package:island/screens/account/me/publishers.dart' as _i3; +import 'package:island/screens/account/me/update.dart' as _i9; import 'package:island/screens/auth/create_account.dart' as _i2; import 'package:island/screens/auth/login.dart' as _i5; -import 'package:island/screens/auth/tabs.dart' as _i7; +import 'package:island/screens/auth/tabs.dart' as _i8; import 'package:island/screens/explore.dart' as _i4; +import 'package:island/screens/posts/compose.dart' as _i7; /// generated route for /// [_i1.AccountScreen] -class AccountRoute extends _i9.PageRouteInfo { - const AccountRoute({List<_i9.PageRouteInfo>? children}) +class AccountRoute extends _i10.PageRouteInfo { + const AccountRoute({List<_i10.PageRouteInfo>? children}) : super(AccountRoute.name, initialChildren: children); static const String name = 'AccountRoute'; - static _i9.PageInfo page = _i9.PageInfo( + static _i10.PageInfo page = _i10.PageInfo( name, builder: (data) { return const _i1.AccountScreen(); @@ -38,13 +39,13 @@ class AccountRoute extends _i9.PageRouteInfo { /// generated route for /// [_i2.CreateAccountScreen] -class CreateAccountRoute extends _i9.PageRouteInfo { - const CreateAccountRoute({List<_i9.PageRouteInfo>? children}) +class CreateAccountRoute extends _i10.PageRouteInfo { + const CreateAccountRoute({List<_i10.PageRouteInfo>? children}) : super(CreateAccountRoute.name, initialChildren: children); static const String name = 'CreateAccountRoute'; - static _i9.PageInfo page = _i9.PageInfo( + static _i10.PageInfo page = _i10.PageInfo( name, builder: (data) { return const _i2.CreateAccountScreen(); @@ -54,11 +55,11 @@ class CreateAccountRoute extends _i9.PageRouteInfo { /// generated route for /// [_i3.EditPublisherScreen] -class EditPublisherRoute extends _i9.PageRouteInfo { +class EditPublisherRoute extends _i10.PageRouteInfo { EditPublisherRoute({ - _i10.Key? key, + _i11.Key? key, String? name, - List<_i9.PageRouteInfo>? children, + List<_i10.PageRouteInfo>? children, }) : super( EditPublisherRoute.name, args: EditPublisherRouteArgs(key: key, name: name), @@ -68,7 +69,7 @@ class EditPublisherRoute extends _i9.PageRouteInfo { static const String name = 'EditPublisherRoute'; - static _i9.PageInfo page = _i9.PageInfo( + static _i10.PageInfo page = _i10.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -83,7 +84,7 @@ class EditPublisherRoute extends _i9.PageRouteInfo { class EditPublisherRouteArgs { const EditPublisherRouteArgs({this.key, this.name}); - final _i10.Key? key; + final _i11.Key? key; final String? name; @@ -95,13 +96,13 @@ class EditPublisherRouteArgs { /// generated route for /// [_i4.ExploreScreen] -class ExploreRoute extends _i9.PageRouteInfo { - const ExploreRoute({List<_i9.PageRouteInfo>? children}) +class ExploreRoute extends _i10.PageRouteInfo { + const ExploreRoute({List<_i10.PageRouteInfo>? children}) : super(ExploreRoute.name, initialChildren: children); static const String name = 'ExploreRoute'; - static _i9.PageInfo page = _i9.PageInfo( + static _i10.PageInfo page = _i10.PageInfo( name, builder: (data) { return const _i4.ExploreScreen(); @@ -111,13 +112,13 @@ class ExploreRoute extends _i9.PageRouteInfo { /// generated route for /// [_i5.LoginScreen] -class LoginRoute extends _i9.PageRouteInfo { - const LoginRoute({List<_i9.PageRouteInfo>? children}) +class LoginRoute extends _i10.PageRouteInfo { + const LoginRoute({List<_i10.PageRouteInfo>? children}) : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; - static _i9.PageInfo page = _i9.PageInfo( + static _i10.PageInfo page = _i10.PageInfo( name, builder: (data) { return const _i5.LoginScreen(); @@ -127,13 +128,13 @@ class LoginRoute extends _i9.PageRouteInfo { /// generated route for /// [_i3.ManagedPublisherScreen] -class ManagedPublisherRoute extends _i9.PageRouteInfo { - const ManagedPublisherRoute({List<_i9.PageRouteInfo>? children}) +class ManagedPublisherRoute extends _i10.PageRouteInfo { + const ManagedPublisherRoute({List<_i10.PageRouteInfo>? children}) : super(ManagedPublisherRoute.name, initialChildren: children); static const String name = 'ManagedPublisherRoute'; - static _i9.PageInfo page = _i9.PageInfo( + static _i10.PageInfo page = _i10.PageInfo( name, builder: (data) { return const _i3.ManagedPublisherScreen(); @@ -143,13 +144,13 @@ class ManagedPublisherRoute extends _i9.PageRouteInfo { /// generated route for /// [_i6.MyselfProfileScreen] -class MyselfProfileRoute extends _i9.PageRouteInfo { - const MyselfProfileRoute({List<_i9.PageRouteInfo>? children}) +class MyselfProfileRoute extends _i10.PageRouteInfo { + const MyselfProfileRoute({List<_i10.PageRouteInfo>? children}) : super(MyselfProfileRoute.name, initialChildren: children); static const String name = 'MyselfProfileRoute'; - static _i9.PageInfo page = _i9.PageInfo( + static _i10.PageInfo page = _i10.PageInfo( name, builder: (data) { return const _i6.MyselfProfileScreen(); @@ -159,13 +160,13 @@ class MyselfProfileRoute extends _i9.PageRouteInfo { /// generated route for /// [_i3.NewPublisherScreen] -class NewPublisherRoute extends _i9.PageRouteInfo { - const NewPublisherRoute({List<_i9.PageRouteInfo>? children}) +class NewPublisherRoute extends _i10.PageRouteInfo { + const NewPublisherRoute({List<_i10.PageRouteInfo>? children}) : super(NewPublisherRoute.name, initialChildren: children); static const String name = 'NewPublisherRoute'; - static _i9.PageInfo page = _i9.PageInfo( + static _i10.PageInfo page = _i10.PageInfo( name, builder: (data) { return const _i3.NewPublisherScreen(); @@ -174,33 +175,49 @@ class NewPublisherRoute extends _i9.PageRouteInfo { } /// generated route for -/// [_i7.TabsScreen] -class TabsRoute extends _i9.PageRouteInfo { - const TabsRoute({List<_i9.PageRouteInfo>? children}) - : super(TabsRoute.name, initialChildren: children); +/// [_i7.PostComposeScreen] +class PostComposeRoute extends _i10.PageRouteInfo { + const PostComposeRoute({List<_i10.PageRouteInfo>? children}) + : super(PostComposeRoute.name, initialChildren: children); - static const String name = 'TabsRoute'; + static const String name = 'PostComposeRoute'; - static _i9.PageInfo page = _i9.PageInfo( + static _i10.PageInfo page = _i10.PageInfo( name, builder: (data) { - return const _i7.TabsScreen(); + return const _i7.PostComposeScreen(); }, ); } /// generated route for -/// [_i8.UpdateProfileScreen] -class UpdateProfileRoute extends _i9.PageRouteInfo { - const UpdateProfileRoute({List<_i9.PageRouteInfo>? children}) +/// [_i8.TabsScreen] +class TabsRoute extends _i10.PageRouteInfo { + const TabsRoute({List<_i10.PageRouteInfo>? children}) + : super(TabsRoute.name, initialChildren: children); + + static const String name = 'TabsRoute'; + + static _i10.PageInfo page = _i10.PageInfo( + name, + builder: (data) { + return const _i8.TabsScreen(); + }, + ); +} + +/// generated route for +/// [_i9.UpdateProfileScreen] +class UpdateProfileRoute extends _i10.PageRouteInfo { + const UpdateProfileRoute({List<_i10.PageRouteInfo>? children}) : super(UpdateProfileRoute.name, initialChildren: children); static const String name = 'UpdateProfileRoute'; - static _i9.PageInfo page = _i9.PageInfo( + static _i10.PageInfo page = _i10.PageInfo( name, builder: (data) { - return const _i8.UpdateProfileScreen(); + return const _i9.UpdateProfileScreen(); }, ); } diff --git a/lib/screens/auth/account/me.dart b/lib/screens/account/me.dart similarity index 100% rename from lib/screens/auth/account/me.dart rename to lib/screens/account/me.dart diff --git a/lib/screens/auth/account/me/publishers.dart b/lib/screens/account/me/publishers.dart similarity index 100% rename from lib/screens/auth/account/me/publishers.dart rename to lib/screens/account/me/publishers.dart diff --git a/lib/screens/auth/account/me/publishers.g.dart b/lib/screens/account/me/publishers.g.dart similarity index 100% rename from lib/screens/auth/account/me/publishers.g.dart rename to lib/screens/account/me/publishers.g.dart diff --git a/lib/screens/auth/account/me/update.dart b/lib/screens/account/me/update.dart similarity index 100% rename from lib/screens/auth/account/me/update.dart rename to lib/screens/account/me/update.dart diff --git a/lib/screens/auth/tabs.dart b/lib/screens/auth/tabs.dart index 38e7201..77caa51 100644 --- a/lib/screens/auth/tabs.dart +++ b/lib/screens/auth/tabs.dart @@ -14,7 +14,6 @@ class TabsScreen extends StatelessWidget { builder: (context, child, _) { final tabsRouter = AutoTabsRouter.of(context); return Scaffold( - extendBody: true, extendBodyBehindAppBar: true, backgroundColor: Colors.transparent, body: child, diff --git a/lib/screens/explore.dart b/lib/screens/explore.dart index 994a57b..21670f8 100644 --- a/lib/screens/explore.dart +++ b/lib/screens/explore.dart @@ -1,9 +1,11 @@ -import 'package:auto_route/annotations.dart'; +import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:island/route.gr.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:island/models/post.dart'; import 'package:island/widgets/post/post_item.dart'; +import 'package:lucide_icons/lucide_icons.dart'; import 'package:very_good_infinite_list/very_good_infinite_list.dart'; import 'package:dio/dio.dart'; import 'package:island/pods/network.dart'; @@ -18,10 +20,24 @@ class ExploreScreen extends ConsumerWidget { return AppScaffold( appBar: AppBar(title: const Text('Explore')), + floatingActionButton: FloatingActionButton( + onPressed: () { + context.router.push(PostComposeRoute()).then((value) { + if (value != null) { + ref.invalidate(postListProvider); + } + }); + }, + child: const Icon(LucideIcons.pencil), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, body: postAsync.when( data: (controller) => RefreshIndicator( - onRefresh: controller.refresh, + onRefresh: + () => Future.sync((() { + ref.invalidate(postListProvider); + })), child: InfiniteList( padding: EdgeInsets.only( bottom: MediaQuery.of(context).padding.bottom, @@ -44,7 +60,7 @@ class ExploreScreen extends ConsumerWidget { child: Text('Error: $e', textAlign: TextAlign.center), ), onTap: () { - postAsync.value?.refresh(); + ref.invalidate(postListProvider); }, ), ), @@ -70,13 +86,6 @@ class _PostListController { final int take = 20; int total = 0; - Future refresh() async { - hasReachedMax = false; - offset = 0; - posts.clear(); - await fetchMore(); - } - Future fetchMore() async { if (isLoading || hasReachedMax) return; isLoading = true; diff --git a/lib/screens/posts/compose.dart b/lib/screens/posts/compose.dart new file mode 100644 index 0000000..9d72d0b --- /dev/null +++ b/lib/screens/posts/compose.dart @@ -0,0 +1,97 @@ +import 'package:auto_route/annotations.dart'; +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:gap/gap.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/models/post.dart'; +import 'package:island/pods/network.dart'; +import 'package:island/screens/account/me/publishers.dart'; +import 'package:island/widgets/alert.dart'; +import 'package:island/widgets/app_scaffold.dart'; +import 'package:island/widgets/content/cloud_files.dart'; +import 'package:lucide_icons/lucide_icons.dart'; +import 'package:styled_widget/styled_widget.dart'; + +@RoutePage() +class PostComposeScreen extends HookConsumerWidget { + const PostComposeScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final publishers = ref.watch(publishersManagedProvider); + + final currentPublisher = useState(null); + + useEffect(() { + if (publishers.value?.isNotEmpty ?? false) { + currentPublisher.value = publishers.value!.first; + } + return null; + }, [publishers]); + + final contentController = useTextEditingController(); + + final submitting = useState(false); + + Future performAction() async { + if (!contentController.text.isNotEmpty) { + return; + } + + try { + submitting.value = true; + final client = ref.watch(apiClientProvider); + await client.post('/posts', data: {'content': contentController.text}); + if (context.mounted) { + context.maybePop(true); + } + } catch (err) { + showErrorAlert(err); + } finally { + submitting.value = false; + } + } + + return AppScaffold( + appBar: AppBar( + leading: const PageBackButton(), + actions: [ + IconButton( + onPressed: submitting.value ? null : performAction, + icon: const Icon(LucideIcons.upload), + ), + const Gap(8), + ], + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Row( + spacing: 12, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ProfilePictureWidget( + item: currentPublisher.value?.picture, + radius: 24, + ), + Expanded( + child: TextField( + controller: contentController, + decoration: InputDecoration.collapsed( + hintText: 'What\'s happened?!', + ), + maxLines: null, + onTapOutside: + (_) => FocusManager.instance.primaryFocus?.unfocus(), + ), + ), + ], + ).padding(all: 16), + ), + ], + ), + ); + } +}