Skip to content

Commit

Permalink
Merge pull request #1479 from wordpress-mobile/issue/post-summary-model
Browse files Browse the repository at this point in the history
Introduces PostSummary for post list sectioning
  • Loading branch information
oguzkocer authored Jan 20, 2020
2 parents 65d665c + aec610f commit 483f9e6
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.model.list.PostListDescriptor
import org.wordpress.android.fluxc.model.post.PostStatus
import org.wordpress.android.fluxc.model.post.PostStatus.PUBLISHED
import org.wordpress.android.fluxc.model.post.PostStatus.SCHEDULED
import org.wordpress.android.fluxc.persistence.PostSqlUtils
import org.wordpress.android.fluxc.store.ListStore.FetchedListItemsPayload
import org.wordpress.android.fluxc.store.PostStore
Expand Down Expand Up @@ -292,6 +293,25 @@ class PostStoreTest {
verifyNoMoreInteractions(dispatcher)
}

@Test
fun `handleFetchedPostList updates post summaries`() {
// Arrange
val post = createPostModel(postStatus = SCHEDULED)
val remotePostListItem = createRemotePostListItem(post)
val action = createFetchedPostListAction(postListItems = listOf(remotePostListItem))

// Act
store.onAction(action)

// Assert
verify(postSqlUtils).insertOrUpdatePostSummaries(argThat {
this.first().let {
it.status == post.status && it.remoteId == post.remotePostId && it.localSiteId == post.localSiteId &&
it.dateCreated == post.dateCreated
}
})
}

private fun createFetchedPostListAction(
postListItems: List<PostListItem> = listOf(),
listDescriptor: PostListDescriptor = mockedListDescriptor,
Expand Down Expand Up @@ -319,6 +339,7 @@ class PostStoreTest {
post: PostModel,
status: String = post.status,
lastModified: String = post.lastModified,
autoSaveModified: String? = post.autoSaveModified
) = PostListItem(post.remotePostId, lastModified, status, autoSaveModified)
autoSaveModified: String? = post.autoSaveModified,
dateCreated: String? = post.dateCreated
) = PostListItem(post.remotePostId, lastModified, status, autoSaveModified, dateCreated)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.wordpress.android.fluxc.model

import com.yarolegovich.wellsql.core.Identifiable
import com.yarolegovich.wellsql.core.annotation.Column
import com.yarolegovich.wellsql.core.annotation.PrimaryKey
import com.yarolegovich.wellsql.core.annotation.RawConstraints
import com.yarolegovich.wellsql.core.annotation.Table
import org.wordpress.android.fluxc.model.post.PostStatus

/**
* When we fetch the post list, we don't fetch the whole model and only keep the ids of posts in ListStore's
* ListItemModelTable. However, some fields such as `status` and `dateCreated` are necessary to be able to section the
* list properly.
*
* `PostSummaryModel` is a pattern we can utilize in situations like this. It works as a look up table where records
* in its table are updated each time posts are fetched. This way the information necessary for a list to work is
* always available, but we still don't need to fetch & update the whole model on each fetch.
*
* // TODO: We can add a link to the wiki for ListStore when that's available.
* See `ListStore` components for more details.
*/

/**
* Immutable version of PostSummaryModel that should be used by the clients.
*/
data class PostSummary(val remoteId: Long, val localSiteId: Int, val status: PostStatus) {
companion object {
@JvmStatic
fun fromPostSummaryModel(postSummaryModel: PostSummaryModel): PostSummary {
// Both remoteId and localSiteId should never be null, so it's worth crashing here to catch the bug
return PostSummary(
postSummaryModel.remoteId!!,
postSummaryModel.localSiteId!!,
PostStatus.fromPostSummary(postSummaryModel)
)
}
}
}

@Table
@RawConstraints(
"FOREIGN KEY(LOCAL_SITE_ID) REFERENCES SiteModel(_id) ON DELETE CASCADE",
"UNIQUE(REMOTE_ID) ON CONFLICT REPLACE"
)
class PostSummaryModel(@PrimaryKey @Column private var id: Int = 0) : Identifiable {
@Column var remoteId: Long? = null
@Column var localSiteId: Int? = null
@Column var status: String? = null
@Column var dateCreated: String? = null // ISO 8601-formatted date in UTC, e.g. 1955-11-05T14:15:00Z

override fun getId(): Int = id

override fun setId(id: Int) {
this.id = id
}

constructor(site: SiteModel, remoteId: Long, postStatus: String?, dateCreated: String?) : this() {
this.localSiteId = site.id
this.remoteId = remoteId
this.status = postStatus
this.dateCreated = dateCreated
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.wordpress.android.fluxc.model.post;

import org.wordpress.android.fluxc.model.PostImmutableModel;
import org.wordpress.android.fluxc.model.PostSummaryModel;
import org.wordpress.android.util.DateTimeUtils;

import java.util.Date;
Expand Down Expand Up @@ -61,10 +62,17 @@ private static synchronized PostStatus fromStringAndDateGMT(String value, long d
}

public static synchronized PostStatus fromPost(PostImmutableModel post) {
String value = post.getStatus();
return fromStringAndDateIso8601(post.getStatus(), post.getDateCreated());
}

public static synchronized PostStatus fromPostSummary(PostSummaryModel postSummaryModel) {
return fromStringAndDateIso8601(postSummaryModel.getStatus(), postSummaryModel.getDateCreated());
}

private static synchronized PostStatus fromStringAndDateIso8601(String value, String dateCreatedIso8601) {
long dateCreatedGMT = 0;

Date dateCreated = DateTimeUtils.dateUTCFromIso8601(post.getDateCreated());
Date dateCreated = DateTimeUtils.dateUTCFromIso8601(dateCreatedIso8601);
if (dateCreated != null) {
dateCreatedGMT = dateCreated.getTime();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public void fetchPostList(final PostListDescriptorForRestSite listDescriptor, fi
String url = WPCOMREST.sites.site(listDescriptor.getSite().getSiteId()).posts.getUrlV1_1();

final int pageSize = listDescriptor.getConfig().getNetworkPageSize();
String fields = TextUtils.join(",", Arrays.asList("ID", "modified", "status", "meta"));
String fields = TextUtils.join(",", Arrays.asList("ID", "modified", "status", "meta", "date"));
Map<String, String> params =
createFetchPostListParameters(false, offset, pageSize, listDescriptor.getStatusList(),
listDescriptor.getAuthor(), fields, listDescriptor.getOrder().getValue(),
Expand All @@ -131,7 +131,7 @@ public void onResponse(PostsResponse response) {
}
postListItems
.add(new PostListItem(postResponse.getRemotePostId(), postResponse.getModified(),
postResponse.getStatus(), autoSaveModified));
postResponse.getStatus(), autoSaveModified, postResponse.getDate()));
}
boolean canLoadMore = response.getFound() > offset + postListItems.size();
FetchPostListResponsePayload responsePayload =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void onErrorResponse(@NonNull BaseNetworkError error) {

public void fetchPostList(final PostListDescriptorForXmlRpcSite listDescriptor, final long offset) {
SiteModel site = listDescriptor.getSite();
List<String> fields = Arrays.asList("post_id", "post_modified_gmt", "post_status");
List<String> fields = Arrays.asList("post_id", "post_modified_gmt", "post_date_gmt", "post_status");
final int pageSize = listDescriptor.getConfig().getNetworkPageSize();
List<Object> params =
createFetchPostListParameters(site.getSelfHostedSiteId(), site.getUsername(), site.getPassword(), false,
Expand Down Expand Up @@ -346,8 +346,11 @@ public void onErrorResponse(@NonNull BaseNetworkError error) {
String postStatus = MapUtils.getMapStr(postMap, "post_status");
Date lastModifiedGmt = MapUtils.getMapDate(postMap, "post_modified_gmt");
String lastModifiedAsIso8601 = DateTimeUtils.iso8601UTCFromDate(lastModifiedGmt);
Date dateCreatedGmt = MapUtils.getMapDate(postMap, "post_date_gmt");
String dateCreatedAsIso8601 = DateTimeUtils.iso8601UTCFromDate(dateCreatedGmt);

postListItems.add(new PostListItem(Long.parseLong(postID), lastModifiedAsIso8601, postStatus, null));
postListItems.add(new PostListItem(Long.parseLong(postID), lastModifiedAsIso8601, postStatus, null,
dateCreatedAsIso8601));
}
return postListItems;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.wellsql.generated.LocalDiffModelTable;
import com.wellsql.generated.LocalRevisionModelTable;
import com.wellsql.generated.PostModelTable;
import com.wellsql.generated.PostSummaryModelTable;
import com.yarolegovich.wellsql.ConditionClauseBuilder;
import com.yarolegovich.wellsql.SelectQuery;
import com.yarolegovich.wellsql.SelectQuery.Order;
Expand All @@ -19,15 +20,18 @@
import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId;
import org.wordpress.android.fluxc.model.LocalOrRemoteId.RemoteId;
import org.wordpress.android.fluxc.model.PostModel;
import org.wordpress.android.fluxc.model.PostSummaryModel;
import org.wordpress.android.fluxc.model.SiteModel;
import org.wordpress.android.fluxc.model.revisions.LocalDiffModel;
import org.wordpress.android.fluxc.model.revisions.LocalRevisionModel;
import org.wordpress.android.fluxc.network.rest.wpcom.post.PostRemoteAutoSaveModel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import javax.inject.Inject;

Expand Down Expand Up @@ -400,4 +404,44 @@ public List<LocalId> getLocalPostIdsForFilter(SiteModel site, boolean isPage, St
}
return localPostIds;
}

public void insertOrUpdatePostSummaries(List<PostSummaryModel> postSummaryModelList) {
// The "unique(remote_id) on conflict replace" constraint will take care of updating on insert
WellSql.insert(postSummaryModelList).asSingleTransaction(true).execute();
}

public List<PostSummaryModel> getPostSummaries(@NonNull SiteModel site, @NonNull List<Long> remotePostIds) {
if (remotePostIds.isEmpty()) {
return Collections.emptyList();
}
List<PostSummaryModel> postSummaryModelList = WellSql.select(PostSummaryModel.class)
.where()
.equals(PostSummaryModelTable.LOCAL_SITE_ID, site.getId())
.isIn(PostSummaryModelTable.REMOTE_ID, remotePostIds)
.endWhere()
.getAsModel();
// Re-order the post summaries to preserve the list order
return orderPostSummaries(remotePostIds, postSummaryModelList);
}

/**
* Orders the `postSummaryModelList` for the given `remotePostIds`.
*/
private List<PostSummaryModel> orderPostSummaries(List<Long> remotePostIds,
List<PostSummaryModel> postSummaryModelList) {
Map<Long, PostSummaryModel> map = new HashMap<>();
for (PostSummaryModel postSummaryModel : postSummaryModelList) {
if (postSummaryModel.getRemoteId() != null) {
map.put(postSummaryModel.getRemoteId(), postSummaryModel);
}
}
List<PostSummaryModel> resultList = new ArrayList<>(postSummaryModelList.size());
for (Long remotePostId : remotePostIds) {
@Nullable PostSummaryModel postSummaryModel = map.get(remotePostId);
if (postSummaryModel != null) {
resultList.add(postSummaryModel);
}
}
return resultList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ open class WellSqlConfig : DefaultWellConfig {
annotation class AddOn

override fun getDbVersion(): Int {
return 96
return 97
}

override fun getDbName(): String {
Expand Down Expand Up @@ -1050,6 +1050,12 @@ open class WellSqlConfig : DefaultWellConfig {
"ON CONFLICT REPLACE)"
)
}
96 -> migrate(version) {
db.execSQL("CREATE TABLE PostSummaryModel (REMOTE_ID INTEGER,LOCAL_SITE_ID INTEGER,STATUS TEXT," +
"DATE_CREATED TEXT,_id INTEGER PRIMARY KEY AUTOINCREMENT," +
"FOREIGN KEY(LOCAL_SITE_ID) REFERENCES SiteModel(_id) ON DELETE CASCADE," +
"UNIQUE(REMOTE_ID) ON CONFLICT REPLACE)")
}
}
}
db.setTransactionSuccessful()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import org.wordpress.android.fluxc.model.LocalOrRemoteId;
import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId;
import org.wordpress.android.fluxc.model.PostModel;
import org.wordpress.android.fluxc.model.PostSummary;
import org.wordpress.android.fluxc.model.PostSummaryModel;
import org.wordpress.android.fluxc.model.PostsModel;
import org.wordpress.android.fluxc.model.SiteModel;
import org.wordpress.android.fluxc.model.list.ListOrder;
Expand Down Expand Up @@ -91,12 +93,15 @@ public static class PostListItem {
public String lastModified;
public String status;
public String autoSaveModified;
public String dateCreated;

public PostListItem(Long remotePostId, String lastModified, String status, String autoSaveModified) {
public PostListItem(Long remotePostId, String lastModified, String status, String autoSaveModified,
String dateCreated) {
this.remotePostId = remotePostId;
this.lastModified = lastModified;
this.status = status;
this.autoSaveModified = autoSaveModified;
this.dateCreated = dateCreated;
}
}

Expand Down Expand Up @@ -754,6 +759,8 @@ private void handleFetchedPostList(FetchPostListResponsePayload payload) {
}
}

updatePostSummaries(payload.listDescriptor.getSite(), payload.postListItems);

FetchedListItemsPayload fetchedListItemsPayload =
new FetchedListItemsPayload(payload.listDescriptor, postIds,
payload.loadedMore, payload.canLoadMore, fetchedListItemsError);
Expand Down Expand Up @@ -1062,4 +1069,26 @@ public void deleteLocalRevision(RevisionModel revisionModel, SiteModel site, Pos
public void deleteLocalRevisionOfAPostOrPage(PostModel post) {
mPostSqlUtils.deleteLocalRevisionAndDiffsOfAPostOrPage(post);
}

/**
* Since we only fetch the ids of a post while fetching the post list, we need to keep a small table in the
* DB to be able to check post statuses for sectioning.
*/
private void updatePostSummaries(SiteModel site, List<PostListItem> postListItems) {
List<PostSummaryModel> postSummaryModelList = new ArrayList<>(postListItems.size());
for (PostListItem item : postListItems) {
postSummaryModelList
.add(new PostSummaryModel(site, item.remotePostId, item.status, item.dateCreated));
}
mPostSqlUtils.insertOrUpdatePostSummaries(postSummaryModelList);
}

public List<PostSummary> getPostSummaries(@NonNull SiteModel site, @NonNull List<Long> remotePostIds) {
List<PostSummaryModel> postSummaryModelList = mPostSqlUtils.getPostSummaries(site, remotePostIds);
List<PostSummary> postSummaryList = new ArrayList<>(postSummaryModelList.size());
for (PostSummaryModel postSummaryModel : postSummaryModelList) {
postSummaryList.add(PostSummary.fromPostSummaryModel(postSummaryModel));
}
return postSummaryList;
}
}

0 comments on commit 483f9e6

Please sign in to comment.