Arpith Siromoney đź’¬

Drafts, Unpublished Edits, and Posts

How Constellational supports them all

I’m building an app called Constellational that lets you publish your notes online. This is the story of the how a post goes from draft to published to edited to updated.

There are three kinds of posts that the app currently supports: drafts, posts with unpublished edits, and regular posts (that can be viewed on the website). But to start with, you tap the create post button and the editing page opens up. Tap cancel, and you go back to the list of posts. If you’ve typed something before you tapped cancel, this gets saved as a draft post (and appears on your list of posts, but marked draft). Let’s open that post up again to edit it. Tapping cancel again will just update the draft’s content (if you made any changes) whereas tapping Post will publish it.

Now let’s edit a post that you’ve published. Same procedure, you tap on the post in your posts page and choose edit to bring up the editing page. Notice that the Post button now says Update. If you make a change and tap cancel, this time the post will be marked editing — you’ve basically got unpublished edits. When you’re ready to publish these changes, tap on the post, choose edit, and tap on Update.

Behind the scenes, the cancel button does a lot of work:

saveDraft() {
  if (this.state.post.data !== this.initialData) {
    if (this.state.post.isDraft) {
      DraftActions.edit(this.state.post);
    } else {
      if (this.isEditing) {
        this.state.post.hasUnpublishedEdits = true;
        EditActions.save(this.state.post);
      } else {
        this.state.post.isDraft = true;
        DraftActions.create(this.state.post);
      }
    }
  this.props.navigator.pop();
}

Basically, it first checks that the post content has actually changed and then if the post was a draft, it edits it with the new content. If it hasn’t already been marked as a draft, it’s either a new post (in which case it marks it and stores it as a draft) or a post that’s being edited. In this case the post is marked as having unpublished edits before being saved.

This brings us to the Post button — again, it’s easiest to make sense of the code by going through it line by line:

savePost() {
  if (this.isEditing && this.state.post.isDraft) {
    this.state.post.isDraft = false;
    DraftActions.del(this.state.post);
    delete this.state.post.id;
    PostActions.create(this.state.post);
  } else if (this.isEditing && !this.state.post.isDraft) {
    if (this.state.post.hasUnpublishedEdits) {
      this.state.post.hasUnpublishedEdits = false;
      EditActions.del(this.state.post);
    }
    PostActions.edit(this.state.post);
  } else {
    PostActions.create(this.state.post);
  }
  this.props.navigator.pop();
}

The function first checks if you’re editing a post and it has been marked as a draft. This means it has to unmark it as a draft, delete the draft, and create the post (this involves making a request to the server). If you’re editing a post and it isn’t a draft, it first checks if it had unpublished edits, in which case it deletes the local copy (with edits), removes the flag (hasUnpublishedEdits) and edits the post (again, this involves a request to the server). If none of those conditions were true, you’re basically creating a post — this is straightforward.

All these flux actions correspond to stores, which means listing your posts (and drafts, and posts with unpublished changes) looks like this:

getAll() {
  var posts = PostStore.getAll();
  posts = posts.map((post) => {
    var unpublishedEdits = EditStore.get(post.id);
    if (unpublishedEdits) {
      post = unpublishedEdits;
      post.hasUnpublishedEdits = true;
    }
    return post;
  });
  var drafts = DraftStore.getAll();
    var all = posts.concat(drafts);
    var sorted = all.sort((a, b) => {
    if (a.updated > b.updated) return -1;
    else if (a.updated < b.updated) return 1;
   else return 0;
 });  return sorted;
}


Note that the function checks each post to see if it has unpublished edits, and if it does, it replaces the post (in the list) with the one with the changes (and marks it). It then adds the drafts to the list, and sorts the list based on the updated timestamp of the posts.