mirror of
https://github.com/samsonjs/samhuri.net-ios.git
synced 2026-03-25 09:25:47 +00:00
use notifications to communicate changes to posts
This commit is contained in:
parent
99e7cc4108
commit
32829036ce
7 changed files with 101 additions and 77 deletions
|
|
@ -8,16 +8,6 @@
|
|||
|
||||
@import Foundation;
|
||||
|
||||
extern NSString *BlogStatusChangedNotification;
|
||||
extern NSString *BlogDraftsChangedNotification;
|
||||
extern NSString *BlogDraftAddedNotification;
|
||||
extern NSString *BlogDraftRemovedNotification;
|
||||
extern NSString *BlogPublishedPostsChangedNotification;
|
||||
extern NSString *BlogPublishedPostAddedNotification;
|
||||
extern NSString *BlogPublishedPostRemovedNotification;
|
||||
extern NSString *BlogPostChangedNotification;
|
||||
extern NSString *BlogPostDeletedNotification;
|
||||
|
||||
@class PMKPromise;
|
||||
@class ModelStore;
|
||||
@class BlogService;
|
||||
|
|
@ -39,9 +29,9 @@ extern NSString *BlogPostDeletedNotification;
|
|||
|
||||
- (PMKPromise *)requestCreateDraft:(Post *)draft;
|
||||
- (PMKPromise *)requestUpdatePost:(Post *)post;
|
||||
- (PMKPromise *)requestPublishDraftWithPath:(NSString *)path;
|
||||
- (PMKPromise *)requestUnpublishPostWithPath:(NSString *)path;
|
||||
- (PMKPromise *)requestDeletePostWithPath:(NSString *)path;
|
||||
- (PMKPromise *)requestPublishDraft:(Post *)post;
|
||||
- (PMKPromise *)requestUnpublishPost:(Post *)post;
|
||||
- (PMKPromise *)requestDeletePost:(Post *)post;
|
||||
|
||||
- (PMKPromise *)requestPublishToStagingEnvironment;
|
||||
- (PMKPromise *)requestPublishToProductionEnvironment;
|
||||
|
|
|
|||
|
|
@ -13,16 +13,6 @@
|
|||
#import "BlogStatus.h"
|
||||
#import "Post.h"
|
||||
|
||||
NSString *BlogStatusChangedNotification = @"BlogStatusChangedNotification";
|
||||
NSString *BlogDraftsChangedNotification = @"BlogDraftsChangedNotification";
|
||||
NSString *BlogDraftAddedNotification = @"BlogDraftAddedNotification";
|
||||
NSString *BlogDraftRemovedNotification = @"BlogDraftRemovedNotification";
|
||||
NSString *BlogPublishedPostsChangedNotification = @"BlogPublishedPostsChangedNotification";
|
||||
NSString *BlogPublishedPostAddedNotification = @"BlogPostAddedNotification";
|
||||
NSString *BlogPublishedPostRemovedNotification = @"BlogPostRemovedNotification";
|
||||
NSString *BlogPostChangedNotification = @"BlogPostChangedNotification";
|
||||
NSString *BlogPostDeletedNotification = @"BlogPostDeletedNotification";
|
||||
|
||||
@implementation BlogController {
|
||||
BlogService *_service;
|
||||
ModelStore *_store;
|
||||
|
|
@ -113,26 +103,26 @@ NSString *BlogPostDeletedNotification = @"BlogPostDeletedNotification";
|
|||
});
|
||||
}
|
||||
|
||||
- (PMKPromise *)requestPublishDraftWithPath:(NSString *)path {
|
||||
return [_service requestPublishDraftWithPath:path].then(^(Post *post) {
|
||||
[_store removeDraftWithPath:path];
|
||||
- (PMKPromise *)requestPublishDraft:(Post *)post {
|
||||
return [_service requestPublishDraftWithPath:post.path].then(^(Post *post) {
|
||||
[_store removeDraft:post];
|
||||
[_store addPublishedPost:post];
|
||||
return post;
|
||||
});
|
||||
}
|
||||
|
||||
- (PMKPromise *)requestUnpublishPostWithPath:(NSString *)path {
|
||||
return [_service requestUnpublishPostWithPath:path].then(^(Post *post) {
|
||||
[_store removePostWithPath:path];
|
||||
- (PMKPromise *)requestUnpublishPost:(Post *)post {
|
||||
return [_service requestUnpublishPostWithPath:post.path].then(^(Post *post) {
|
||||
[_store removePost:post];
|
||||
[_store addDraft:post];
|
||||
return post;
|
||||
});
|
||||
}
|
||||
|
||||
- (PMKPromise *)requestDeletePostWithPath:(NSString *)path {
|
||||
return [_service requestDeletePostWithPath:path].then(^(id _) {
|
||||
[_store removePostWithPath:path];
|
||||
[_store removeDraftWithPath:path];
|
||||
- (PMKPromise *)requestDeletePost:(Post *)post {
|
||||
return [_service requestDeletePostWithPath:post.path].then(^(id _) {
|
||||
[_store removePost:post];
|
||||
[_store removeDraft:post];
|
||||
return _;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
@property (nonatomic, strong) BlogController *blogController;
|
||||
@property (nonatomic, strong) Post *post;
|
||||
@property (nonatomic, copy) void (^postUpdatedBlock)(Post *post);
|
||||
|
||||
- (void)configureWithPost:(Post *)post;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#import "Post.h"
|
||||
#import "PreviewViewController.h"
|
||||
#import "ChangeTitleViewController.h"
|
||||
#import "ModelStore.h"
|
||||
|
||||
@interface EditorViewController () <UITextViewDelegate, UIPopoverPresentationControllerDelegate>
|
||||
|
||||
|
|
@ -74,7 +75,7 @@
|
|||
#pragma mark - Managing the detail item
|
||||
|
||||
- (void)configureWithPost:(Post *)post {
|
||||
if (![post isEqual:self.post]) {
|
||||
if (!(post && [post isEqual:self.post])) {
|
||||
self.post = post;
|
||||
self.modifiedPost = post;
|
||||
[self configureView];
|
||||
|
|
@ -93,6 +94,12 @@
|
|||
}
|
||||
|
||||
- (void)configureTitleView {
|
||||
if (!self.post) {
|
||||
self.titleLabel.text = nil;
|
||||
self.statusLabel.text = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
self.titleLabel.text = self.modifiedPost.title.length ? self.modifiedPost.title : @"Untitled";
|
||||
NSString *statusText = [self statusText];
|
||||
if (self.statusLabel && ![self.statusLabel.text isEqualToString:statusText]) {
|
||||
|
|
@ -111,7 +118,7 @@
|
|||
|
||||
- (void)configureLinkView {
|
||||
NSURL *url = self.modifiedPost.url;
|
||||
if (url || [self pasteboardHasLink]) {
|
||||
if (self.post && url || [self pasteboardHasLink]) {
|
||||
NSString *title = url ? url.absoluteString : @"Add Link from Pasteboard";
|
||||
[self.linkButton setTitle:title forState:UIControlStateNormal];
|
||||
self.removeLinkButton.hidden = !url;
|
||||
|
|
@ -136,7 +143,6 @@
|
|||
CGPoint scrollOffset = CGPointZero;
|
||||
Post *post = self.modifiedPost;
|
||||
if (post) {
|
||||
// FIXME: date, status (draft, published)
|
||||
body = post.body;
|
||||
// TODO: restore scroll offset for this post ... user defaults?
|
||||
}
|
||||
|
|
@ -166,10 +172,13 @@
|
|||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
NSAssert(self.blogController, @"blogController is required");
|
||||
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
||||
[notificationCenter addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
[notificationCenter addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
|
||||
[notificationCenter addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
|
||||
[notificationCenter addObserver:self selector:@selector(postDeleted:) name:DraftRemovedNotification object:nil];
|
||||
[notificationCenter addObserver:self selector:@selector(postDeleted:) name:PublishedPostRemovedNotification object:nil];
|
||||
[self configureView];
|
||||
}
|
||||
|
||||
|
|
@ -179,6 +188,8 @@
|
|||
[notificationCenter removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
|
||||
[notificationCenter removeObserver:self name:UIKeyboardWillShowNotification object:nil];
|
||||
[notificationCenter removeObserver:self name:UIKeyboardWillHideNotification object:nil];
|
||||
[notificationCenter removeObserver:self name:DraftRemovedNotification object:nil];
|
||||
[notificationCenter removeObserver:self name:PublishedPostRemovedNotification object:nil];
|
||||
[self savePost];
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +219,13 @@
|
|||
[self hideHideKeyboardButton];
|
||||
}
|
||||
|
||||
- (void)postDeleted:(NSNotification *)note {
|
||||
NSString *path = note.userInfo[PostPathUserInfoKey];
|
||||
if ([path isEqualToString:self.post.path]) {
|
||||
[self configureWithPost:nil];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - State restoration
|
||||
|
||||
static NSString *const StateRestorationPostKey = @"post";
|
||||
|
|
@ -247,7 +265,7 @@ static NSString *const StateRestorationModifiedPostKey = @"modifiedPost";
|
|||
|
||||
- (BOOL)isDirty;
|
||||
{
|
||||
return self.modifiedPost.new || ![self.modifiedPost isEqualToPost:self.post];
|
||||
return self.post && (self.modifiedPost.new || ![self.modifiedPost isEqualToPost:self.post]);
|
||||
}
|
||||
|
||||
- (PMKPromise *)savePost {
|
||||
|
|
@ -293,9 +311,6 @@ static NSString *const StateRestorationModifiedPostKey = @"modifiedPost";
|
|||
|
||||
// update our post because "new" may have changed, which is essential to correct operation
|
||||
[self configureWithPost:newPost];
|
||||
if (self.postUpdatedBlock) {
|
||||
self.postUpdatedBlock(self.post);
|
||||
}
|
||||
return newPost;
|
||||
}).catch(^(NSError *error) {
|
||||
NSLog(@"Failed to %@ post at path %@: %@ %@", verb, path, error.localizedDescription, error.userInfo);
|
||||
|
|
@ -332,18 +347,15 @@ static NSString *const StateRestorationModifiedPostKey = @"modifiedPost";
|
|||
PMKPromise *promise = nil;
|
||||
Post *post = self.modifiedPost;
|
||||
if (post.draft) {
|
||||
promise = [self.blogController requestPublishDraftWithPath:post.path];
|
||||
promise = [self.blogController requestPublishDraft:post];
|
||||
}
|
||||
else {
|
||||
promise = [self.blogController requestUnpublishPostWithPath:post.path];
|
||||
promise = [self.blogController requestUnpublishPost:post];
|
||||
}
|
||||
promise.then(^(Post *post) {
|
||||
self.post = post;
|
||||
self.modifiedPost = post;
|
||||
[self configureView];
|
||||
if (self.postUpdatedBlock) {
|
||||
self.postUpdatedBlock(post);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,14 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
extern NSString *const PostUpdatedNotification;
|
||||
extern NSString *const DraftRemovedNotification;
|
||||
extern NSString *const DraftAddedNotification;
|
||||
extern NSString *const PublishedPostAddedNotification;
|
||||
extern NSString *const PublishedPostRemovedNotification;
|
||||
extern NSString *const PostUserInfoKey;
|
||||
extern NSString *const PostPathUserInfoKey;
|
||||
|
||||
@class PMKPromise;
|
||||
@class YapDatabaseConnection;
|
||||
@class BlogStatus;
|
||||
|
|
@ -31,7 +39,7 @@
|
|||
- (PMKPromise *)addDraft:(Post *)post;
|
||||
- (PMKPromise *)addPublishedPost:(Post *)post;
|
||||
|
||||
- (PMKPromise *)removeDraftWithPath:(NSString *)path;
|
||||
- (PMKPromise *)removePostWithPath:(NSString *)path;
|
||||
- (PMKPromise *)removeDraft:(Post *)post;
|
||||
- (PMKPromise *)removePost:(Post *)post;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -13,6 +13,14 @@
|
|||
#import "Post.h"
|
||||
#import "NSArray+ObjectiveSugar.h"
|
||||
|
||||
NSString *const PostUpdatedNotification = @"PostUpdatedNotification";
|
||||
NSString *const DraftRemovedNotification = @"DraftRemovedNotification";
|
||||
NSString *const DraftAddedNotification = @"DraftAddedNotification";
|
||||
NSString *const PublishedPostAddedNotification = @"PublishedPostAddedNotification";
|
||||
NSString *const PublishedPostRemovedNotification = @"PublishedPostRemovedNotification";
|
||||
NSString *const PostUserInfoKey = @"PostUserInfoKey";
|
||||
NSString *const PostPathUserInfoKey = @"PostPathUserInfoKey";
|
||||
|
||||
@implementation ModelStore {
|
||||
YapDatabaseConnection *_connection;
|
||||
}
|
||||
|
|
@ -43,13 +51,18 @@
|
|||
- (PMKPromise *)saveBlogStatus:(BlogStatus *)blogStatus {
|
||||
return [PMKPromise new:^(PMKPromiseFulfiller fulfill, PMKPromiseRejecter reject) {
|
||||
[_connection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[transaction setObject:blogStatus forKey:@"status" inCollection:@"BlogStatus" withMetadata:@{@"timestamp": @([NSDate date].timeIntervalSince1970)}];
|
||||
[transaction setObject:blogStatus forKey:@"status" inCollection:@"BlogStatus" withMetadata:@{@"timestamp" : @([NSDate date].timeIntervalSince1970)}];
|
||||
} completionBlock:^{
|
||||
fulfill(blogStatus);
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)postPostUpdatedNotificationForPost:(Post *)post {
|
||||
NSDictionary *info = @{PostPathUserInfoKey : post.path, PostUserInfoKey : post};
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PostUpdatedNotification object:self userInfo:info];
|
||||
}
|
||||
|
||||
- (Post *)postWithPath:(NSString *)path {
|
||||
__block Post *post = nil;
|
||||
[_connection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
|
|
@ -63,6 +76,7 @@
|
|||
[_connection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[transaction setObject:post forKey:post.path inCollection:@"Post"];
|
||||
} completionBlock:^{
|
||||
[self postPostUpdatedNotificationForPost:post];
|
||||
fulfill(post);
|
||||
}];
|
||||
}];
|
||||
|
|
@ -111,21 +125,25 @@
|
|||
[transaction setObject:postPaths forKey:@"drafts" inCollection:@"PostCollection"];
|
||||
}
|
||||
} completionBlock:^{
|
||||
NSDictionary *info = @{PostPathUserInfoKey : post.path, PostUserInfoKey : post};
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:DraftAddedNotification object:self userInfo:info];
|
||||
fulfill(post);
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (PMKPromise *)removeDraftWithPath:(NSString *)path {
|
||||
- (PMKPromise *)removeDraft:(Post *)post {
|
||||
return [PMKPromise new:^(PMKPromiseFulfiller fulfill, PMKPromiseRejecter reject) {
|
||||
[_connection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
NSMutableArray *postPaths = [[transaction objectForKey:@"drafts" inCollection:@"PostCollection"] mutableCopy];
|
||||
if ([postPaths containsObject:path]) {
|
||||
[postPaths removeObject:path];
|
||||
if ([postPaths containsObject:post.path]) {
|
||||
[postPaths removeObject:post.path];
|
||||
[transaction setObject:postPaths forKey:@"drafts" inCollection:@"PostCollection"];
|
||||
}
|
||||
} completionBlock:^{
|
||||
fulfill(path);
|
||||
NSDictionary *info = @{PostPathUserInfoKey : post.path, PostUserInfoKey : post};
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:DraftRemovedNotification object:self userInfo:info];
|
||||
fulfill(post);
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
|
@ -174,21 +192,25 @@
|
|||
[transaction setObject:postPaths forKey:@"published" inCollection:@"PostCollection"];
|
||||
}
|
||||
} completionBlock:^{
|
||||
NSDictionary *info = @{PostPathUserInfoKey : post.path, PostUserInfoKey : post};
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PublishedPostAddedNotification object:self userInfo:info];
|
||||
fulfill(post);
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (PMKPromise *)removePostWithPath:(NSString *)path {
|
||||
- (PMKPromise *)removePost:(Post *)post {
|
||||
return [PMKPromise new:^(PMKPromiseFulfiller fulfill, PMKPromiseRejecter reject) {
|
||||
[_connection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
NSMutableArray *postPaths = [[transaction objectForKey:@"published" inCollection:@"PostCollection"] mutableCopy];
|
||||
if ([postPaths containsObject:path]) {
|
||||
[postPaths removeObject:path];
|
||||
if ([postPaths containsObject:post.path]) {
|
||||
[postPaths removeObject:post.path];
|
||||
[transaction setObject:postPaths forKey:@"published" inCollection:@"PostCollection"];
|
||||
}
|
||||
} completionBlock:^{
|
||||
fulfill(path);
|
||||
NSDictionary *info = @{PostPathUserInfoKey : post.path, PostUserInfoKey : post};
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PublishedPostRemovedNotification object:self userInfo:info];
|
||||
fulfill(post);
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#import "NSDate+marshmallows.h"
|
||||
#import "UIColor+Hex.h"
|
||||
#import "PostCollection.h"
|
||||
#import "ModelStore.h"
|
||||
|
||||
@interface PostsViewController ()
|
||||
|
||||
|
|
@ -106,6 +107,7 @@ static const NSUInteger SectionPublished = 1;
|
|||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(postUpdated:) name:PostUpdatedNotification object:nil];
|
||||
[self setupBlogStatusTimer];
|
||||
[self requestStatusWithCaching:YES];
|
||||
if (!self.postCollections) {
|
||||
|
|
@ -115,6 +117,7 @@ static const NSUInteger SectionPublished = 1;
|
|||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:PostUpdatedNotification object:nil];
|
||||
[self teardownBlogStatusTimer];
|
||||
}
|
||||
|
||||
|
|
@ -211,6 +214,25 @@ static const NSUInteger SectionPublished = 1;
|
|||
});
|
||||
}
|
||||
|
||||
- (void)postUpdated:(NSNotification *)note {
|
||||
Post *post = note.userInfo[PostUserInfoKey];
|
||||
BOOL (^isThisPost)(Post *, NSUInteger, BOOL *) = ^BOOL(Post *p, NSUInteger idx, BOOL *stop) {
|
||||
return [p.objectID isEqualToString:post.objectID];
|
||||
};
|
||||
NSUInteger section = SectionDrafts;
|
||||
NSUInteger row = [self.drafts indexOfObjectPassingTest:isThisPost];
|
||||
if (row == NSNotFound) {
|
||||
section = SectionPublished;
|
||||
row = [self.publishedPosts indexOfObjectPassingTest:isThisPost];
|
||||
}
|
||||
if (row != NSNotFound) {
|
||||
PostCollection *collection = [self postCollectionForSection:section];
|
||||
[collection.posts replaceObjectAtIndex:row withObject:post];
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
|
||||
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Segues
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
|
|
@ -222,25 +244,6 @@ static const NSUInteger SectionPublished = 1;
|
|||
[controller configureWithPost:post];
|
||||
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
|
||||
controller.navigationItem.leftItemsSupplementBackButton = YES;
|
||||
__weak __typeof__(self) welf = self;
|
||||
controller.postUpdatedBlock = ^(Post *post) {
|
||||
__typeof__(self) self = welf;
|
||||
BOOL (^isThisPost)(Post *, NSUInteger, BOOL *) = ^BOOL(Post *p, NSUInteger idx, BOOL *stop) {
|
||||
return [p.objectID isEqualToString:post.objectID];
|
||||
};
|
||||
NSUInteger section = SectionDrafts;
|
||||
NSUInteger row = [self.drafts indexOfObjectPassingTest:isThisPost];
|
||||
if (row == NSNotFound) {
|
||||
section = SectionPublished;
|
||||
row = [self.publishedPosts indexOfObjectPassingTest:isThisPost];
|
||||
}
|
||||
if (row != NSNotFound) {
|
||||
PostCollection *collection = [self postCollectionForSection:section];
|
||||
[collection.posts replaceObjectAtIndex:row withObject:post];
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
|
||||
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,7 +311,7 @@ static NSString *const StateRestorationBlogStatusTextKey = @"blogStatusText";
|
|||
PostCollection *collection = [self postCollectionForSection:indexPath.section];
|
||||
Post *post = [self postForIndexPath:indexPath];
|
||||
// TODO: activity indicator
|
||||
[self.blogController requestDeletePostWithPath:post.path].then(^{
|
||||
[self.blogController requestDeletePost:post].then(^{
|
||||
[collection.posts removeObjectAtIndex:indexPath.row];
|
||||
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue