diff --git a/CHANGELOG.md b/CHANGELOG.md index 9528f501..fc600602 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# Version 0.5.4 +## Bug fixes +- Fixed posts duplication + +# Version 0.5.3 +## Bug fixes +- Fixed a bug when creating a poll + # Version 0.5.2 ## Bug fixes - Fixed a bug when creating a post diff --git a/lib/entities/posts/polls/poll_answer.dart b/lib/entities/posts/polls/poll_answer.dart index 11a05d4d..3b4f4ff3 100644 --- a/lib/entities/posts/polls/poll_answer.dart +++ b/lib/entities/posts/polls/poll_answer.dart @@ -46,4 +46,9 @@ class PollAnswer extends Equatable { List get props { return [answer, user.address]; } + + @override + String toString() { + return 'PollAnswer { answer: $answer, user: ${user.address} }'; + } } diff --git a/lib/entities/posts/polls/poll_option.dart b/lib/entities/posts/polls/poll_option.dart index e98ad73a..19fe307d 100644 --- a/lib/entities/posts/polls/poll_option.dart +++ b/lib/entities/posts/polls/poll_option.dart @@ -48,4 +48,9 @@ class PollOption extends Equatable { List get props { return [id, text]; } + + @override + String toString() { + return 'PollOption { id: $id, text: $text }'; + } } diff --git a/lib/entities/posts/polls/post_poll.dart b/lib/entities/posts/polls/post_poll.dart index b919cbbf..74334a5c 100644 --- a/lib/entities/posts/polls/post_poll.dart +++ b/lib/entities/posts/polls/post_poll.dart @@ -1,6 +1,3 @@ -import 'dart:convert'; - -import 'package:crypto/crypto.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/cupertino.dart'; import 'package:intl/intl.dart'; @@ -88,20 +85,11 @@ class PostPoll extends Equatable { options?.isNotEmpty == true; } - /// Returns `true` is the poll is open considering the current time, - /// or `false` otherwise. + /// Tells if the poll is still open or not based on the current date time. bool get isOpen { return DateTime.now().isBefore(endDateTime); } - /// Returns the SHA-256 of all the posts contents as a JSON object. - /// Some contents are excluded, such as the user answers. - String hashContents() { - final json = toJson(); - json.remove('user_answers'); - return sha256.convert(utf8.encode(jsonEncode(json))).toString(); - } - /// Returns the JSON representation of this post poll as a [Map]. Map toJson() { return _$PostPollToJson(this); diff --git a/lib/entities/posts/post.dart b/lib/entities/posts/post.dart index 16ab30ac..c5c70850 100644 --- a/lib/entities/posts/post.dart +++ b/lib/entities/posts/post.dart @@ -1,7 +1,4 @@ -import 'dart:convert'; - import 'package:collection/collection.dart'; -import 'package:crypto/crypto.dart'; import 'package:equatable/equatable.dart'; import 'package:intl/intl.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -32,8 +29,6 @@ class Post extends Equatable implements Comparable { /// Identifier used to reference the post creation date. static const DATE_FIELD = 'created'; - static const LAST_EDITED_FIELD = 'last_edited'; - /// Identifier used to reference post ids. static const ID_FIELD = 'id'; @@ -43,15 +38,7 @@ class Post extends Equatable implements Comparable { /// Identifier used to reference the user field. static const OWNER_FIELD = 'user'; - static const LINK_PREVIEW_FIELD = 'link_preview'; - - static const REACTIONS_FIELD = 'reactions'; - - static const MEDIA_FIELD = 'media'; - - static const POLL_FIELD = 'poll'; - - static const COMMENTS_FIELD = 'children'; + static const LOCAL_ID_KEY = 'local_id'; /// Returns the current date and time in UTC time zone, formatted as /// it should be to be used as a post creation date or last edit date. @@ -73,7 +60,7 @@ class Post extends Equatable implements Comparable { @JsonKey(name: DATE_FIELD) final String created; - @JsonKey(name: LAST_EDITED_FIELD) + @JsonKey(name: 'last_edited') final String lastEdited; @JsonKey(name: 'allows_comments') @@ -88,16 +75,16 @@ class Post extends Equatable implements Comparable { @JsonKey(name: 'optional_data', defaultValue: {}) final Map optionalData; - @JsonKey(name: MEDIA_FIELD, defaultValue: []) + @JsonKey(name: 'media', defaultValue: []) final List medias; - @JsonKey(name: POLL_FIELD, nullable: true) + @JsonKey(name: 'poll', nullable: true) final PostPoll poll; - @JsonKey(name: REACTIONS_FIELD, defaultValue: []) + @JsonKey(name: 'reactions', defaultValue: []) final List reactions; - @JsonKey(name: COMMENTS_FIELD, defaultValue: []) + @JsonKey(name: 'children', defaultValue: []) final List commentsIds; /// Tells if the post has been synced with the blockchain or not @@ -106,9 +93,7 @@ class Post extends Equatable implements Comparable { /// Static method used to implement a custom deserialization of posts. static PostStatus _postStatusFromJson(Map json) { - return json == null - ? PostStatus(value: PostStatusValue.TX_SUCCESSFULL) - : PostStatus.fromJson(json); + return json == null ? PostStatus.txSuccessful() : PostStatus.fromJson(json); } /// Tells whether or not the post has been hidden from the user. @@ -116,13 +101,13 @@ class Post extends Equatable implements Comparable { final bool hidden; /// Represents the link preview associated to this post - @JsonKey(name: LINK_PREVIEW_FIELD, nullable: true) + @JsonKey(name: 'link_preview', nullable: true) final RichLinkPreview linkPreview; Post({ @required this.id, this.parentId = '', - this.message, + @required this.message, @required this.created, this.lastEdited, this.allowsComments = false, @@ -178,31 +163,6 @@ class Post extends Equatable implements Comparable { []; } - /// Returns the SHA-256 of all the posts contents as a JSON object. - /// Some contents are excluded. Such as: - /// - the date - /// - the id - /// - the status - /// - the link preview - /// - the reactions - /// - the comments - String hashContents() { - final json = toJson(); - json.remove(DATE_FIELD); - json.remove(LAST_EDITED_FIELD); - json.remove(ID_FIELD); - json.remove(STATUS_FIELD); - json.remove(LINK_PREVIEW_FIELD); - json.remove(REACTIONS_FIELD); - json.remove(COMMENTS_FIELD); - json.remove(MEDIA_FIELD); - - json.remove(POLL_FIELD); - json['poll_data'] = poll?.hashContents(); - - return sha256.convert(utf8.encode(jsonEncode(json))).toString(); - } - /// Returns a new [Post] having the same data as `this` one, but /// with the specified data replaced. Post copyWith({ diff --git a/lib/entities/posts/post_status.dart b/lib/entities/posts/post_status.dart index c29b020e..16e4d5d3 100644 --- a/lib/entities/posts/post_status.dart +++ b/lib/entities/posts/post_status.dart @@ -19,11 +19,27 @@ class PostStatus extends Equatable { this.data, }) : assert(value != null); + /// Builds a [PostStatus] having as value [PostStatusValue.STORED_LOCALLY] + /// and as data the given [address]. factory PostStatus.storedLocally(String address) { - return PostStatus( - value: PostStatusValue.STORED_LOCALLY, - data: address, - ); + return PostStatus(value: PostStatusValue.STORED_LOCALLY, data: address); + } + + /// Builds a new [PostStatus] with value [PostStatusValue.SENDING_TX]. + factory PostStatus.sendingTx() { + return PostStatus(value: PostStatusValue.SENDING_TX); + } + + factory PostStatus.txSent(String txHash) { + return PostStatus(value: PostStatusValue.TX_SENT, data: txHash); + } + + factory PostStatus.txSuccessful({String txHash}) { + return PostStatus(value: PostStatusValue.TX_SUCCESSFULL, data: txHash); + } + + factory PostStatus.errored(String error) { + return PostStatus(value: PostStatusValue.ERRORED, data: error); } /// Returns true if the status contains an error message. diff --git a/lib/main.dart b/lib/main.dart index 7a7279d4..52e9a2c3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -80,9 +80,9 @@ Future _setupDependencyInjection() async { ), postsDatabase: await factory.openDatabase( 'posts.db', - version: 4, + version: 6, onVersionChanged: (db, oldVersion, newVersion) async { - if (oldVersion < 4) { + if (oldVersion < 6) { await deletePosts(db); } }, diff --git a/lib/repositories/posts/posts_repository_impl.dart b/lib/repositories/posts/posts_repository_impl.dart index e4f97c59..b06cbb99 100644 --- a/lib/repositories/posts/posts_repository_impl.dart +++ b/lib/repositories/posts/posts_repository_impl.dart @@ -110,16 +110,10 @@ class PostsRepositoryImpl extends PostsRepository { PostStatus postStatus; switch (result.success) { case true: - postStatus = PostStatus( - value: PostStatusValue.TX_SENT, - data: result.hash, - ); + postStatus = PostStatus.txSent(result.hash); break; case false: - postStatus = PostStatus( - value: PostStatusValue.ERRORED, - data: result.error.errorMessage, - ); + postStatus = PostStatus.errored(result.error.errorMessage); break; } @@ -136,9 +130,8 @@ class PostsRepositoryImpl extends PostsRepository { } // Set the posts as syncing - final syncingStatus = PostStatus(value: PostStatusValue.SENDING_TX); final syncingPosts = posts.map((post) { - return post.copyWith(status: syncingStatus); + return post.copyWith(status: PostStatus.sendingTx()); }).toList(); await _localPostsSource.savePosts(syncingPosts); @@ -151,10 +144,12 @@ class PostsRepositoryImpl extends PostsRepository { // emit event that tx has been successfully added to the chain if (status.value == PostStatusValue.TX_SENT) { - final currentTxAmount = - await _localSettingsSource.get(SettingKeys.TX_AMOUNT) ?? 0; + final key = SettingKeys.TX_AMOUNT; + final currentTxAmount = await _localSettingsSource.get(key) ?? 0; await _localSettingsSource.save( - SettingKeys.TX_AMOUNT, currentTxAmount + updatedPosts.length); + key, + currentTxAmount + updatedPosts.length, + ); } } diff --git a/lib/sources/chain/models/msgs/chain_poll_data.dart b/lib/sources/chain/models/msgs/chain_poll_data.dart index ca72e95b..3aa678d1 100644 --- a/lib/sources/chain/models/msgs/chain_poll_data.dart +++ b/lib/sources/chain/models/msgs/chain_poll_data.dart @@ -16,9 +16,6 @@ class ChainPollData { @JsonKey(name: 'end_date') final String endDate; - @JsonKey(name: 'is_open') - final bool isOpen; - @JsonKey(name: 'allows_multiple_answers') final bool allowsMultipleAnswers; @@ -32,13 +29,11 @@ class ChainPollData { @required this.question, @required this.endDate, @required this.options, - @required this.isOpen, @required this.allowsMultipleAnswers, @required this.allowsAnswerEdits, }) : assert(question != null), assert(endDate != null), assert(options != null), - assert(isOpen != null), assert(allowsMultipleAnswers != null), assert(allowsAnswerEdits != null); diff --git a/lib/sources/chain/models/msgs/chain_poll_data.g.dart b/lib/sources/chain/models/msgs/chain_poll_data.g.dart index 538ac24a..3f9598e6 100644 --- a/lib/sources/chain/models/msgs/chain_poll_data.g.dart +++ b/lib/sources/chain/models/msgs/chain_poll_data.g.dart @@ -15,7 +15,6 @@ ChainPollData _$ChainPollDataFromJson(Map json) { ? null : ChainPollOption.fromJson(e as Map)) ?.toList(), - isOpen: json['is_open'] as bool, allowsMultipleAnswers: json['allows_multiple_answers'] as bool, allowsAnswerEdits: json['allows_answer_edits'] as bool, ); @@ -25,7 +24,6 @@ Map _$ChainPollDataToJson(ChainPollData instance) => { 'question': instance.question, 'end_date': instance.endDate, - 'is_open': instance.isOpen, 'allows_multiple_answers': instance.allowsMultipleAnswers, 'allows_answer_edits': instance.allowsAnswerEdits, 'provided_answers': instance.options?.map((e) => e?.toJson())?.toList(), diff --git a/lib/sources/posts/local/local_posts_source.dart b/lib/sources/posts/local/local_posts_source.dart index 93735969..e91b768d 100644 --- a/lib/sources/posts/local/local_posts_source.dart +++ b/lib/sources/posts/local/local_posts_source.dart @@ -33,7 +33,10 @@ class LocalPostsSourceImpl implements LocalPostsSource { /// given [post]. @visibleForTesting String getPostKey(Post post) { - return post.hashContents(); + if (post.optionalData.containsKey(Post.LOCAL_ID_KEY)) { + return post.optionalData[Post.LOCAL_ID_KEY]; + } + return post.id; } /// Returns a [Filter] that allows to filter out all the posts that are diff --git a/lib/sources/posts/remote/converters/posts_msg_converter.dart b/lib/sources/posts/remote/converters/posts_msg_converter.dart index ed9bdced..9e5228ab 100644 --- a/lib/sources/posts/remote/converters/posts_msg_converter.dart +++ b/lib/sources/posts/remote/converters/posts_msg_converter.dart @@ -23,7 +23,6 @@ class PostsMsgConverter { return ChainPollData( question: poll.question, endDate: poll.endDate, - isOpen: true, allowsMultipleAnswers: poll.allowsMultipleAnswers, allowsAnswerEdits: poll.allowsAnswerEdits, options: poll.options.map((e) { diff --git a/lib/ui/blocs/posts_list/posts_list_bloc.dart b/lib/ui/blocs/posts_list/posts_list_bloc.dart index 9ff38e2a..66969b9d 100644 --- a/lib/ui/blocs/posts_list/posts_list_bloc.dart +++ b/lib/ui/blocs/posts_list/posts_list_bloc.dart @@ -442,20 +442,14 @@ class PostsListBloc extends Bloc { /// Handles the event that tells the Bloc that a transaction has /// been successful. void _handleTxSuccessfulEvent(TxSuccessful event) async { - final status = PostStatus( - value: PostStatusValue.TX_SUCCESSFULL, - data: event.txHash, - ); + final status = PostStatus.txSuccessful(txHash: event.txHash); await _updatePostsStatusUseCase.update(event.txHash, status); } /// Handles the event that tells the Bloc that a transaction has not /// been successful. void _handleTxFailedEvent(TxFailed event) async { - final status = PostStatus( - value: PostStatusValue.ERRORED, - data: event.error, - ); + final status = PostStatus.errored(event.error); await _updatePostsStatusUseCase.update(event.txHash, status); } diff --git a/lib/ui/screens/post_create_screen/blocs/post_input/post_input_bloc.dart b/lib/ui/screens/post_create_screen/blocs/post_input/post_input_bloc.dart index a94a3e17..50093c11 100644 --- a/lib/ui/screens/post_create_screen/blocs/post_input/post_input_bloc.dart +++ b/lib/ui/screens/post_create_screen/blocs/post_input/post_input_bloc.dart @@ -193,7 +193,7 @@ class PostInputBloc extends Bloc { final post = await _createPostUseCase.create( // If the post has a poll, the entered message should // be the poll question instead - message: state.hasPoll ? null : state.message, + message: state.hasPoll ? '' : state.message, allowsComments: state.allowsComments, parentId: state.parentPost?.id, medias: state.medias, diff --git a/lib/usecases/posts/usecase_create_post.dart b/lib/usecases/posts/usecase_create_post.dart index 37364149..bb62d1eb 100644 --- a/lib/usecases/posts/usecase_create_post.dart +++ b/lib/usecases/posts/usecase_create_post.dart @@ -33,6 +33,9 @@ class CreatePostUseCase { subspace: Constants.SUBSPACE, owner: account.toUser(), status: PostStatus.storedLocally(account.address), + optionalData: { + Post.LOCAL_ID_KEY: date, + }, ); } } diff --git a/pubspec.yaml b/pubspec.yaml index 1228da62..5c375d34 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: mooncake description: A decentralized microblogging application based on Desmos -version: 0.5.2+05200 +version: 0.5.4+05400 environment: sdk: ">=2.9.0 <3.0.0"