mirror of
https://github.com/samsonjs/samhuri.net-ios.git
synced 2026-03-25 09:25:47 +00:00
implement basic editing, preview, and improve article list
- new dark look that matches samhuri.net - improved cell layout for article list - fix yap caching - update service layer to match new API - implement authentication - fix bugs all over - add a preview
This commit is contained in:
parent
905c60d434
commit
bd704e4241
23 changed files with 456 additions and 146 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
xcuserdata
|
||||
auth.json
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1BCFCC637C63C780248D685E /* PostCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BCFCB5C36F301B2B93F8069 /* PostCell.m */; };
|
||||
30089596C2F733D451A454E8 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1613DC56A86AFA7E50460A37 /* libPods.a */; };
|
||||
7B4070531AE46BC9000C2E43 /* auth.json in Resources */ = {isa = PBXBuildFile; fileRef = 7B4070521AE46BC9000C2E43 /* auth.json */; };
|
||||
7B5C4BDF19F2606900667D48 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5C4BDE19F2606900667D48 /* main.m */; };
|
||||
7B5C4BE219F2606900667D48 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5C4BE119F2606900667D48 /* AppDelegate.m */; };
|
||||
7B5C4BE519F2606900667D48 /* MasterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B5C4BE419F2606900667D48 /* MasterViewController.m */; };
|
||||
|
|
@ -22,6 +24,7 @@
|
|||
7B9E64421A22F3840072FF42 /* BlogService.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9E64411A22F3840072FF42 /* BlogService.m */; };
|
||||
7B9E644C1A230B940072FF42 /* JSONHTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9E644B1A230B940072FF42 /* JSONHTTPClient.m */; };
|
||||
7B9E644F1A23129B0072FF42 /* BlogStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B9E644E1A23129B0072FF42 /* BlogStatus.m */; };
|
||||
7BE3A0351AE461E700E45CCB /* PreviewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BE3A0341AE461E700E45CCB /* PreviewViewController.m */; };
|
||||
7BF029331A27117200E42EDE /* ModelStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BF029321A27117200E42EDE /* ModelStore.m */; };
|
||||
7BF029381A280CB200E42EDE /* BlogController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BF029371A280CB200E42EDE /* BlogController.m */; };
|
||||
B8B8958B2AA40812EFE04FEF /* libPods-BlogTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C36BC848680D4F831E4DE23 /* libPods-BlogTests.a */; };
|
||||
|
|
@ -39,6 +42,9 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
1613DC56A86AFA7E50460A37 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1BCFCB5C36F301B2B93F8069 /* PostCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PostCell.m; sourceTree = "<group>"; };
|
||||
1BCFCCF30594E0E2DCC32116 /* PostCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostCell.h; sourceTree = "<group>"; };
|
||||
7B4070521AE46BC9000C2E43 /* auth.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = auth.json; sourceTree = "<group>"; };
|
||||
7B5C4BD919F2606900667D48 /* Blog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Blog.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7B5C4BDD19F2606900667D48 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
7B5C4BDE19F2606900667D48 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
|
|
@ -66,6 +72,8 @@
|
|||
7B9E644B1A230B940072FF42 /* JSONHTTPClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONHTTPClient.m; sourceTree = "<group>"; };
|
||||
7B9E644D1A23129B0072FF42 /* BlogStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlogStatus.h; sourceTree = "<group>"; };
|
||||
7B9E644E1A23129B0072FF42 /* BlogStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlogStatus.m; sourceTree = "<group>"; };
|
||||
7BE3A0331AE461E700E45CCB /* PreviewViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreviewViewController.h; sourceTree = "<group>"; };
|
||||
7BE3A0341AE461E700E45CCB /* PreviewViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreviewViewController.m; sourceTree = "<group>"; };
|
||||
7BF029311A27117200E42EDE /* ModelStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModelStore.h; sourceTree = "<group>"; };
|
||||
7BF029321A27117200E42EDE /* ModelStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModelStore.m; sourceTree = "<group>"; };
|
||||
7BF029361A280CB200E42EDE /* BlogController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlogController.h; sourceTree = "<group>"; };
|
||||
|
|
@ -140,10 +148,14 @@
|
|||
7B5C4BE419F2606900667D48 /* MasterViewController.m */,
|
||||
7B5C4BE619F2606900667D48 /* DetailViewController.h */,
|
||||
7B5C4BE719F2606900667D48 /* DetailViewController.m */,
|
||||
7BE3A0331AE461E700E45CCB /* PreviewViewController.h */,
|
||||
7BE3A0341AE461E700E45CCB /* PreviewViewController.m */,
|
||||
7B5C4BE919F2606900667D48 /* Main.storyboard */,
|
||||
7B5C4BEC19F2606900667D48 /* Images.xcassets */,
|
||||
7B5C4BEE19F2606900667D48 /* LaunchScreen.xib */,
|
||||
7B5C4BDC19F2606900667D48 /* Supporting Files */,
|
||||
1BCFCB5C36F301B2B93F8069 /* PostCell.m */,
|
||||
1BCFCCF30594E0E2DCC32116 /* PostCell.h */,
|
||||
);
|
||||
path = Blog;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -151,6 +163,7 @@
|
|||
7B5C4BDC19F2606900667D48 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7B4070521AE46BC9000C2E43 /* auth.json */,
|
||||
7B5C4BDD19F2606900667D48 /* Info.plist */,
|
||||
7B5C4BDE19F2606900667D48 /* main.m */,
|
||||
);
|
||||
|
|
@ -273,6 +286,7 @@
|
|||
TargetAttributes = {
|
||||
7B5C4BD819F2606900667D48 = {
|
||||
CreatedOnToolsVersion = 6.0.1;
|
||||
DevelopmentTeam = B2W6993X5Z;
|
||||
};
|
||||
7B5C4BF419F2606900667D48 = {
|
||||
CreatedOnToolsVersion = 6.0.1;
|
||||
|
|
@ -307,6 +321,7 @@
|
|||
7B5C4BEB19F2606900667D48 /* Main.storyboard in Resources */,
|
||||
7B5C4BF019F2606900667D48 /* LaunchScreen.xib in Resources */,
|
||||
7B5C4BED19F2606900667D48 /* Images.xcassets in Resources */,
|
||||
7B4070531AE46BC9000C2E43 /* auth.json in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -398,7 +413,9 @@
|
|||
7BF029331A27117200E42EDE /* ModelStore.m in Sources */,
|
||||
7BF029381A280CB200E42EDE /* BlogController.m in Sources */,
|
||||
7B5C4BE819F2606900667D48 /* DetailViewController.m in Sources */,
|
||||
7BE3A0351AE461E700E45CCB /* PreviewViewController.m in Sources */,
|
||||
7B9E644F1A23129B0072FF42 /* BlogStatus.m in Sources */,
|
||||
1BCFCC637C63C780248D685E /* PostCell.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,28 +7,78 @@
|
|||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "MasterViewController.h"
|
||||
#import "DetailViewController.h"
|
||||
#import "BlogService.h"
|
||||
#import "YapDatabase.h"
|
||||
#import "ModelStore.h"
|
||||
#import "JSONHTTPClient.h"
|
||||
#import "BlogController.h"
|
||||
|
||||
@interface AppDelegate () <UISplitViewControllerDelegate>
|
||||
|
||||
@property (nonatomic, readonly, strong) BlogService *blogService;
|
||||
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
_blogService = [BlogService new];
|
||||
|
||||
// Override point for customization after application launch.
|
||||
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
|
||||
UINavigationController *navigationController = [splitViewController.viewControllers lastObject];
|
||||
UINavigationController *navigationController = splitViewController.viewControllers.lastObject;
|
||||
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;
|
||||
splitViewController.delegate = self;
|
||||
[self setupBlogController];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (MasterViewController *)masterViewController {
|
||||
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
|
||||
UINavigationController *navigationController = splitViewController.viewControllers.firstObject;
|
||||
MasterViewController *masterViewController = (MasterViewController *)navigationController.viewControllers.firstObject;
|
||||
return masterViewController;
|
||||
}
|
||||
|
||||
- (void)setupBlogController {
|
||||
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;
|
||||
NSString *dbPath = [cachesPath stringByAppendingPathComponent:@"blog.sqlite"];
|
||||
ModelStore *store = [self newModelStoreWithPath:dbPath];
|
||||
BlogController *blogController = [self newBlogControllerWithModelStore:store rootURL:@"http://ocean.samhuri.net:6706/"];
|
||||
|
||||
[self masterViewController].blogController = blogController;
|
||||
}
|
||||
|
||||
- (ModelStore *)newModelStoreWithPath:(NSString *)dbPath {
|
||||
YapDatabase *database = [[YapDatabase alloc] initWithPath:dbPath];
|
||||
YapDatabaseConnection *connection = [database newConnection];
|
||||
ModelStore *store = [[ModelStore alloc] initWithConnection:connection];
|
||||
return store;
|
||||
}
|
||||
|
||||
- (BlogController *)newBlogControllerWithModelStore:(ModelStore *)store rootURL:(NSString *)rootURL {
|
||||
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
|
||||
JSONHTTPClient *client = [[JSONHTTPClient alloc] initWithSession:session];
|
||||
client.defaultHeaders = [self defaultBlogHeaders];
|
||||
BlogService *service = [[BlogService alloc] initWithRootURL:rootURL client:client];
|
||||
BlogController *blogController = [[BlogController alloc] initWithService:service store:store];
|
||||
return blogController;
|
||||
}
|
||||
|
||||
- (NSDictionary *)defaultBlogHeaders {
|
||||
NSString *authPath = [[NSBundle mainBundle] pathForResource:@"auth.json" ofType:nil];
|
||||
if (authPath.length) {
|
||||
NSData *data = [NSData dataWithContentsOfFile:authPath];
|
||||
NSError *error = nil;
|
||||
NSDictionary *auth = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
||||
if (auth) {
|
||||
return @{@"Auth" : [NSString stringWithFormat:@"%@|%@", auth[@"username"], auth[@"password"]]};
|
||||
}
|
||||
NSLog(@"auth.json: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
|
||||
NSLog(@"[ERROR] Failed to parse auth.json: %@ %@", error.localizedDescription, error.userInfo);
|
||||
}
|
||||
NSLog(@"[WARNING] No auth.json found. Blog will be read-only.");
|
||||
return nil;
|
||||
};
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7531" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7520"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
|
@ -14,17 +14,18 @@
|
|||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="blög" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="backgroundColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="tintColor" red="0.7953414352" green="0.0" blue="0.013255690590000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="548" y="455"/>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6250" systemVersion="14B25" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="H1p-Uh-vWS">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7531" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="H1p-Uh-vWS">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6244"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7520"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Master-->
|
||||
<scene sceneID="pY4-Hu-kfo">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="Master Nav Controller" title="Master" useStoryboardIdentifierAsRestorationIdentifier="YES" id="RMx-3f-FxP" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="Pmd-2v-anx">
|
||||
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" barStyle="black" translucent="NO" id="Pmd-2v-anx">
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="tintColor" red="0.79534143518518507" green="0.0" blue="0.013255690586419705" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="barTintColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="7bK-jq-Zjz" kind="relationship" relationship="rootViewController" id="tsl-Nk-0bq"/>
|
||||
|
|
@ -28,39 +32,118 @@
|
|||
<viewControllerLayoutGuide type="bottom" id="GAO-Cl-Wes"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="svH-Pt-448">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="536"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" text="Detail view content goes here" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="0XM-y9-sOw">
|
||||
<rect key="frame" x="20" y="292" width="560" height="17"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" size="system"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" barStyle="black" translucent="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YUD-Xe-6Cc">
|
||||
<rect key="frame" x="0.0" y="492" width="600" height="44"/>
|
||||
<items>
|
||||
<barButtonItem title="Preview" id="uGx-Jy-rOz">
|
||||
<connections>
|
||||
<segue destination="ixd-IL-hNy" kind="show" identifier="showPreview" id="eVY-Ks-1b3"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</items>
|
||||
<color key="barTintColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</toolbar>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wrG-1y-ZY3">
|
||||
<rect key="frame" x="0.0" y="64" width="600" height="492"/>
|
||||
<color key="backgroundColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="JEX-9P-axG" id="vKD-HY-lGQ"/>
|
||||
</connections>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="backgroundColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="tintColor" red="0.7953414352" green="0.0" blue="0.013255690590000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="0XM-y9-sOw" firstAttribute="leading" secondItem="svH-Pt-448" secondAttribute="leading" constant="20" symbolic="YES" id="Tsc-yG-G1q"/>
|
||||
<constraint firstItem="0XM-y9-sOw" firstAttribute="centerY" secondItem="svH-Pt-448" secondAttribute="centerY" id="jWN-iV-94e"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0XM-y9-sOw" secondAttribute="trailing" constant="20" symbolic="YES" id="tHV-ZD-HQj"/>
|
||||
<constraint firstAttribute="centerY" secondItem="wrG-1y-ZY3" secondAttribute="centerY" id="8IO-mb-1kq"/>
|
||||
<constraint firstAttribute="width" secondItem="wrG-1y-ZY3" secondAttribute="width" id="8hl-Ab-ACB"/>
|
||||
<constraint firstItem="YUD-Xe-6Cc" firstAttribute="bottom" secondItem="GAO-Cl-Wes" secondAttribute="top" id="DLQ-Z3-h8m"/>
|
||||
<constraint firstAttribute="centerX" secondItem="wrG-1y-ZY3" secondAttribute="centerX" id="FEY-jO-Mjl"/>
|
||||
<constraint firstAttribute="centerX" secondItem="YUD-Xe-6Cc" secondAttribute="centerX" id="JTh-pp-5hr"/>
|
||||
<constraint firstAttribute="height" secondItem="wrG-1y-ZY3" secondAttribute="height" id="ORx-N8-ZLB"/>
|
||||
<constraint firstItem="SYR-Wa-9uf" firstAttribute="top" secondItem="wrG-1y-ZY3" secondAttribute="top" id="VlL-zs-EVE"/>
|
||||
<constraint firstAttribute="height" secondItem="YUD-Xe-6Cc" secondAttribute="height" id="dwI-2s-ERv"/>
|
||||
<constraint firstAttribute="height" secondItem="wrG-1y-ZY3" secondAttribute="height" id="ecu-u5-SXU"/>
|
||||
<constraint firstItem="YUD-Xe-6Cc" firstAttribute="top" secondItem="wrG-1y-ZY3" secondAttribute="bottom" id="iTL-zi-eKI"/>
|
||||
<constraint firstAttribute="width" secondItem="YUD-Xe-6Cc" secondAttribute="width" id="n19-6i-zpg"/>
|
||||
<constraint firstItem="wrG-1y-ZY3" firstAttribute="top" secondItem="SYR-Wa-9uf" secondAttribute="bottom" id="tUU-ig-gJV"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="VlL-zs-EVE"/>
|
||||
<exclude reference="8IO-mb-1kq"/>
|
||||
<exclude reference="ORx-N8-ZLB"/>
|
||||
<exclude reference="ecu-u5-SXU"/>
|
||||
<exclude reference="dwI-2s-ERv"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</view>
|
||||
<toolbarItems/>
|
||||
<navigationItem key="navigationItem" title="Detail" id="mOI-FS-AaM"/>
|
||||
<navigationItem key="navigationItem" title="Article Title" id="mOI-FS-AaM">
|
||||
<barButtonItem key="backBarButtonItem" title=" " id="g7z-xe-9m6"/>
|
||||
</navigationItem>
|
||||
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||
<connections>
|
||||
<outlet property="detailDescriptionLabel" destination="0XM-y9-sOw" id="deQ-Na-JPF"/>
|
||||
<outlet property="textView" destination="wrG-1y-ZY3" id="lvo-lm-t7Z"/>
|
||||
<outlet property="toolbar" destination="YUD-Xe-6Cc" id="SIv-cT-WDD"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="FJe-Yq-33r" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="709" y="129"/>
|
||||
</scene>
|
||||
<!--Preview-->
|
||||
<scene sceneID="MEA-t1-FD6">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="Preview View Controller" useStoryboardIdentifierAsRestorationIdentifier="YES" id="ixd-IL-hNy" customClass="PreviewViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="VoQ-Bk-8rs"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="qQh-ht-rVd"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="d1U-C6-XQQ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="536"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<webView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0n2-Ma-NtP">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="536"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="ixd-IL-hNy" id="UhD-wi-jtK"/>
|
||||
</connections>
|
||||
</webView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerX" secondItem="0n2-Ma-NtP" secondAttribute="centerX" id="Iad-IJ-UKT"/>
|
||||
<constraint firstAttribute="centerY" secondItem="0n2-Ma-NtP" secondAttribute="centerY" id="cwx-9Q-nmc"/>
|
||||
<constraint firstItem="0n2-Ma-NtP" firstAttribute="height" secondItem="d1U-C6-XQQ" secondAttribute="height" id="eZP-iJ-reC"/>
|
||||
<constraint firstItem="0n2-Ma-NtP" firstAttribute="width" secondItem="d1U-C6-XQQ" secondAttribute="width" id="myh-gl-cb6"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" title="Preview" id="qGX-iM-tle">
|
||||
<barButtonItem key="backBarButtonItem" title=" " id="Cyw-0C-8qX"/>
|
||||
</navigationItem>
|
||||
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||
<connections>
|
||||
<outlet property="webView" destination="0n2-Ma-NtP" id="4yz-yf-YCI"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="wK2-TI-hhX" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1478" y="129"/>
|
||||
</scene>
|
||||
<!--Split View Controller-->
|
||||
<scene sceneID="Nki-YV-4Qg">
|
||||
<objects>
|
||||
<splitViewController storyboardIdentifier="Split View Controller" useStoryboardIdentifierAsRestorationIdentifier="YES" id="H1p-Uh-vWS" sceneMemberID="viewController">
|
||||
<toolbarItems/>
|
||||
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||
<connections>
|
||||
<segue destination="RMx-3f-FxP" kind="relationship" relationship="masterViewController" id="BlO-5A-QYV"/>
|
||||
<segue destination="vC3-pB-5Vb" kind="relationship" relationship="detailViewController" id="Tll-UG-LXB"/>
|
||||
|
|
@ -75,34 +158,49 @@
|
|||
<objects>
|
||||
<tableViewController storyboardIdentifier="Master View Controller" title="Master" useStoryboardIdentifierAsRestorationIdentifier="YES" clearsSelectionOnViewWillAppear="NO" id="7bK-jq-Zjz" customClass="MasterViewController" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="r7i-6Z-zg0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="536"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="tintColor" red="0.7953414352" green="0.0" blue="0.013255690590000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<prototypes>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="Arm-wq-HPj" detailTextLabel="3Mi-b7-7nb" style="IBUITableViewCellStyleValue1" id="WCw-Qf-5nD">
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" id="WCw-Qf-5nD" customClass="PostCell">
|
||||
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WCw-Qf-5nD" id="37f-cq-3Eg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="Diagram of An Ember Application and More Long Text, Hooray!" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" adjustsLetterSpacingToFitWidth="YES" id="Arm-wq-HPj">
|
||||
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="2015-04-19" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vd6-ZS-EnM">
|
||||
<rect key="frame" x="494" y="19" width="65" height="15"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" horizontalCompressionResistancePriority="751" text="Aug 31, 2013" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" adjustsLetterSpacingToFitWidth="YES" id="3Mi-b7-7nb">
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="I Can't Wait to See What Trey Parker & Matt Stone Do With This" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sTp-VX-x9k">
|
||||
<rect key="frame" x="8" y="14" width="478" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="vd6-ZS-EnM" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="sTp-VX-x9k" secondAttribute="trailing" constant="8" id="CVb-7u-yaW"/>
|
||||
<constraint firstItem="vd6-ZS-EnM" firstAttribute="trailing" secondItem="37f-cq-3Eg" secondAttribute="trailingMargin" id="ErG-lz-QR2"/>
|
||||
<constraint firstAttribute="centerY" secondItem="sTp-VX-x9k" secondAttribute="centerY" id="L2O-P8-B0I"/>
|
||||
<constraint firstItem="sTp-VX-x9k" firstAttribute="bottom" secondItem="37f-cq-3Eg" secondAttribute="bottomMargin" id="O7g-0A-fpG"/>
|
||||
<constraint firstItem="vd6-ZS-EnM" firstAttribute="baseline" secondItem="sTp-VX-x9k" secondAttribute="baseline" id="WBb-wO-bu6"/>
|
||||
<constraint firstItem="sTp-VX-x9k" firstAttribute="leading" secondItem="37f-cq-3Eg" secondAttribute="leadingMargin" id="iU2-pk-NdH"/>
|
||||
</constraints>
|
||||
<variation key="default">
|
||||
<mask key="constraints">
|
||||
<exclude reference="L2O-P8-B0I"/>
|
||||
</mask>
|
||||
</variation>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="backgroundColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<outlet property="dateLabel" destination="vd6-ZS-EnM" id="mER-WA-QZz"/>
|
||||
<outlet property="titleLabel" destination="sTp-VX-x9k" id="qTw-yO-gEL"/>
|
||||
<segue destination="vC3-pB-5Vb" kind="showDetail" identifier="showDetail" id="6S0-TO-JiA"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
|
|
@ -113,12 +211,14 @@
|
|||
<outlet property="delegate" destination="7bK-jq-Zjz" id="RA6-mI-bju"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Master" id="Zdf-7t-Un8">
|
||||
<navigationItem key="navigationItem" title="samhuri.net" id="Zdf-7t-Un8">
|
||||
<barButtonItem key="backBarButtonItem" title=" " id="UCo-qs-2hI"/>
|
||||
<connections>
|
||||
<outlet property="leftBarButtonItem" destination="8HS-W8-a6l" id="CwH-zD-2Qm"/>
|
||||
<outlet property="rightBarButtonItem" destination="u2a-vi-nHQ" id="zsl-st-de9"/>
|
||||
</connections>
|
||||
</navigationItem>
|
||||
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||
<connections>
|
||||
<outlet property="addButton" destination="u2a-vi-nHQ" id="BNL-ge-ZGw"/>
|
||||
<outlet property="publishButton" destination="8HS-W8-a6l" id="amK-fb-yQq"/>
|
||||
|
|
@ -142,8 +242,11 @@
|
|||
<scene sceneID="r7l-gg-dq7">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="Detail Nav Controller" useStoryboardIdentifierAsRestorationIdentifier="YES" id="vC3-pB-5Vb" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="DjV-YW-jjY">
|
||||
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics" statusBarStyle="lightContent"/>
|
||||
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" barStyle="black" translucent="NO" id="DjV-YW-jjY">
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="tintColor" red="0.7953414352" green="0.0" blue="0.013255690590000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="barTintColor" red="0.1333333333" green="0.1333333333" blue="0.1333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="JEX-9P-axG" kind="relationship" relationship="rootViewController" id="GKi-kA-LjT"/>
|
||||
|
|
@ -155,6 +258,6 @@
|
|||
</scene>
|
||||
</scenes>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="6S0-TO-JiA"/>
|
||||
<segue reference="Tll-UG-LXB"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
</document>
|
||||
|
|
|
|||
|
|
@ -8,15 +8,15 @@
|
|||
|
||||
@import Foundation;
|
||||
|
||||
NSString *BlogStatusChangedNotification;
|
||||
NSString *BlogDraftsChangedNotification;
|
||||
NSString *BlogDraftAddedNotification;
|
||||
NSString *BlogDraftRemovedNotification;
|
||||
NSString *BlogPublishedPostsChangedNotification;
|
||||
NSString *BlogPublishedPostAddedNotification;
|
||||
NSString *BlogPublishedPostRemovedNotification;
|
||||
NSString *BlogPostChangedNotification;
|
||||
NSString *BlogPostDeletedNotification;
|
||||
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;
|
||||
|
|
@ -26,7 +26,7 @@ NSString *BlogPostDeletedNotification;
|
|||
|
||||
- (instancetype)initWithService:(BlogService *)service store:(ModelStore *)store;
|
||||
|
||||
- (NSURL *)previewURLForPostWithPath:(NSString *)path;
|
||||
- (NSMutableURLRequest *)previewRequestWithPath:(NSString *)path;
|
||||
|
||||
- (PMKPromise *)requestBlogStatus;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,8 +35,11 @@ NSString *BlogPostDeletedNotification = @"BlogPostDeletedNotification";
|
|||
return self;
|
||||
}
|
||||
|
||||
- (NSURL *)previewURLForPostWithPath:(NSString *)path {
|
||||
return [_service previewURLForPostWithPath:path];
|
||||
- (NSMutableURLRequest *)previewRequestWithPath:(NSString *)path;
|
||||
{
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[_service urlFor:path]];
|
||||
[request addValue:@"text/html" forHTTPHeaderField:@"Accept"];
|
||||
return request;
|
||||
}
|
||||
|
||||
- (PMKPromise *)requestBlogStatus {
|
||||
|
|
@ -55,9 +58,11 @@ NSString *BlogPostDeletedNotification = @"BlogPostDeletedNotification";
|
|||
- (PMKPromise *)requestDrafts {
|
||||
NSArray *posts = [_store drafts];
|
||||
if (posts) {
|
||||
NSLog(@"returning %@ cached drafts", @(posts.count));
|
||||
return [PMKPromise promiseWithValue:posts];
|
||||
}
|
||||
else {
|
||||
NSLog(@"requesting drafts from server");
|
||||
return [_service requestDrafts].then(^(NSArray *posts) {
|
||||
[_store saveDrafts:posts];
|
||||
return posts;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ typedef NS_ENUM(NSUInteger, BlogServiceErrorCode) {
|
|||
|
||||
- (instancetype)initWithRootURL:(NSString *)rootURL client:(JSONHTTPClient *)client;
|
||||
|
||||
- (NSURL *)previewURLForPostWithPath:(NSString *)path;
|
||||
- (NSURL *)urlFor:(NSString *)path, ...;
|
||||
|
||||
- (PMKPromise *)requestBlogStatus;
|
||||
- (PMKPromise *)requestPublishEnvironment:(NSString *)environment;
|
||||
|
|
|
|||
|
|
@ -33,10 +33,6 @@ NSString * const BlogServiceErrorDomain = @"BlogServiceErrorDomain";
|
|||
return self;
|
||||
}
|
||||
|
||||
- (NSURL *)previewURLForPostWithPath:(NSString *)path {
|
||||
return [self urlFor:@"%@/preview", path];
|
||||
}
|
||||
|
||||
- (NSURL *)urlFor:(NSString *)path, ... {
|
||||
va_list args;
|
||||
va_start(args, path);
|
||||
|
|
@ -82,7 +78,7 @@ NSString * const BlogServiceErrorDomain = @"BlogServiceErrorDomain";
|
|||
}
|
||||
|
||||
- (PMKPromise *)requestDrafts {
|
||||
return [self.client get:[self urlFor:@"/drafts"] headers:nil].then([self decodePostsBlock]);
|
||||
return [self.client get:[self urlFor:@"/posts/drafts"] headers:nil].then([self decodePostsBlock]);
|
||||
}
|
||||
|
||||
- (PMKPromise *)requestPublishedPosts {
|
||||
|
|
@ -97,9 +93,9 @@ NSString * const BlogServiceErrorDomain = @"BlogServiceErrorDomain";
|
|||
NSDictionary *fields = @{@"id": draftID,
|
||||
@"title": title,
|
||||
@"body": body,
|
||||
@"link": link,
|
||||
@"link": link ?: [NSNull null],
|
||||
};
|
||||
return [self.client postJSON:[self urlFor:@"/drafts"] headers:nil fields:fields].then([self decodePostBlock]);
|
||||
return [self.client postJSON:[self urlFor:@"/posts/drafts"] headers:nil fields:fields].then([self decodePostBlock]);
|
||||
}
|
||||
|
||||
- (PMKPromise *)requestPublishDraftWithPath:(NSString *)path {
|
||||
|
|
@ -113,7 +109,7 @@ NSString * const BlogServiceErrorDomain = @"BlogServiceErrorDomain";
|
|||
- (PMKPromise *)requestUpdatePostWithPath:(NSString *)path title:(NSString *)title body:(NSString *)body link:(NSString *)link {
|
||||
NSDictionary *fields = @{@"title": title,
|
||||
@"body": body,
|
||||
@"link": link,
|
||||
@"link": link ?: [NSNull null],
|
||||
};
|
||||
return [self.client putJSON:[self urlFor:path] headers:nil fields:fields];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@
|
|||
|
||||
@import UIKit;
|
||||
|
||||
@class BlogController;
|
||||
@class Post;
|
||||
|
||||
@interface DetailViewController : UIViewController
|
||||
|
||||
@property (strong, nonatomic) BlogController *blogController;
|
||||
@property (strong, nonatomic) Post *post;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -6,12 +6,16 @@
|
|||
// Copyright (c) 2014 Guru Logic Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <PromiseKit/Promise.h>
|
||||
#import "DetailViewController.h"
|
||||
#import "BlogController.h"
|
||||
#import "Post.h"
|
||||
#import "PreviewViewController.h"
|
||||
|
||||
@interface DetailViewController ()
|
||||
@interface DetailViewController () <UITextViewDelegate>
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
|
||||
@property (nonatomic, weak) IBOutlet UITextView *textView;
|
||||
@property (nonatomic, weak) IBOutlet UIToolbar *toolbar;
|
||||
|
||||
@end
|
||||
|
||||
|
|
@ -22,30 +26,60 @@
|
|||
- (void)setPost:(id)newPost {
|
||||
if (_post != newPost) {
|
||||
_post = newPost;
|
||||
|
||||
// Update the view.
|
||||
[self configureView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)configureView {
|
||||
// Update the user interface for the detail item.
|
||||
if (self.post) {
|
||||
// FIXME: date, link (edit, open), status (draft, published), delete, preview, publish
|
||||
// FIXME: date, status (draft, published)
|
||||
self.navigationItem.title = self.post.title ?: @"Untitled";
|
||||
self.detailDescriptionLabel.text = self.post.body;
|
||||
self.textView.text = self.post.body;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view, typically from a nib.
|
||||
[self configureView];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
- (void)viewWillAppear:(BOOL)animated;
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(savePostBody) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
[self savePostBody];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
|
||||
}
|
||||
|
||||
- (PMKPromise *)savePostBody {
|
||||
if (!self.post || !self.textView) { return [PMKPromise promiseWithValue:nil]; }
|
||||
|
||||
Post *newPost = [self.post copyWithBody:self.textView.text];
|
||||
if (![self.post isEqual:newPost])
|
||||
{
|
||||
self.post = newPost;
|
||||
return [self.blogController requestUpdatePostWithPath:self.post.path title:self.post.title body:self.post.body link:self.post.url.absoluteString]
|
||||
.then(^(Post *post) {
|
||||
NSLog(@"saved post at path %@", self.post.path);
|
||||
}).catch(^(NSError *error) {
|
||||
NSLog(@"Error saving post at path %@: %@ %@", self.post.path, error.localizedDescription, error.userInfo);
|
||||
});
|
||||
}
|
||||
return [PMKPromise promiseWithValue:self.post];
|
||||
}
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
[super prepareForSegue:segue sender:sender];
|
||||
if ([segue.identifier isEqualToString:@"showPreview"]) {
|
||||
PreviewViewController *previewViewController = segue.destinationViewController;
|
||||
previewViewController.promise = [self savePostBody];
|
||||
previewViewController.initialRequest = [self.blogController previewRequestWithPath:self.post.path];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@
|
|||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleLightContent</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>UIStatusBarTintParameters</key>
|
||||
<dict>
|
||||
<key>UINavigationBar</key>
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ NSString * const JSONHTTPClientErrorDomain = @"JSONHTTPClientErrorDomain";
|
|||
[request setValue:headers[key] forHTTPHeaderField:key];
|
||||
}
|
||||
if (data) {
|
||||
[request setValue:[NSString stringWithFormat:@"%lu", [data length]] forKey:@"Content-Length"];
|
||||
[request setValue:[NSString stringWithFormat:@"%lu", [data length]] forHTTPHeaderField:@"Content-Length"];
|
||||
[request setHTTPBody:data];
|
||||
}
|
||||
return request;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@
|
|||
|
||||
@import UIKit;
|
||||
|
||||
@class BlogController;
|
||||
|
||||
@interface MasterViewController : UITableViewController
|
||||
|
||||
@property (strong, nonatomic) BlogController *blogController;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,15 +6,12 @@
|
|||
// Copyright (c) 2014 Guru Logic Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <PromiseKit/Promise.h>
|
||||
#import "MasterViewController.h"
|
||||
#import "DetailViewController.h"
|
||||
#import "Post.h"
|
||||
#import "BlogController.h"
|
||||
#import "ModelStore.h"
|
||||
#import "BlogService.h"
|
||||
#import "YapDatabaseConnection.h"
|
||||
#import "YapDatabase.h"
|
||||
#import "JSONHTTPClient.h"
|
||||
#import "PostCell.h"
|
||||
|
||||
@interface MasterViewController ()
|
||||
|
||||
|
|
@ -22,7 +19,6 @@
|
|||
@property (strong, nonatomic) DetailViewController *detailViewController;
|
||||
@property (strong, nonatomic) IBOutlet UIBarButtonItem *publishButton;
|
||||
@property (strong, nonatomic) IBOutlet UIBarButtonItem *addButton;
|
||||
@property (strong, nonatomic) BlogController *blogController;
|
||||
|
||||
@end
|
||||
|
||||
|
|
@ -34,16 +30,6 @@
|
|||
self.clearsSelectionOnViewWillAppear = NO;
|
||||
self.preferredContentSize = CGSizeMake(320.0, 600.0);
|
||||
}
|
||||
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;
|
||||
NSString *path = [cachesPath stringByAppendingPathComponent:@"blog.sqlite"];
|
||||
YapDatabase *database = [[YapDatabase alloc] initWithPath:path];
|
||||
YapDatabaseConnection *connection = [database newConnection];
|
||||
ModelStore *store = [[ModelStore alloc] initWithConnection:connection];
|
||||
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
|
||||
JSONHTTPClient *client = [[JSONHTTPClient alloc] initWithSession:session];
|
||||
BlogService *service = [[BlogService alloc] initWithRootURL:@"http://ocean.samhuri.net:6706/" client:client];
|
||||
self.blogController = [[BlogController alloc] initWithService:service store:store];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
|
|
@ -65,10 +51,10 @@
|
|||
// TODO: show a spinner
|
||||
[self.blogController requestDrafts].then(^(NSArray *drafts) {
|
||||
return [self.blogController requestPublishedPosts].then(^(NSArray *posts) {
|
||||
NSLog(@"drafts = %@", drafts);
|
||||
NSLog(@"posts = %@", posts);
|
||||
self.posts = [drafts mutableCopy];
|
||||
[self.posts addObjectsFromArray:posts];
|
||||
for (Post *post in [posts reverseObjectEnumerator]) {
|
||||
[self.posts addObject:post];
|
||||
}
|
||||
[self.tableView reloadData];
|
||||
});
|
||||
});
|
||||
|
|
@ -93,10 +79,11 @@
|
|||
#pragma mark - Segues
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
if ([[segue identifier] isEqualToString:@"showDetail"]) {
|
||||
if ([segue.identifier isEqualToString:@"showDetail"]) {
|
||||
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
|
||||
Post *post = self.posts[indexPath.row];
|
||||
DetailViewController *controller = (DetailViewController *)[[segue destinationViewController] topViewController];
|
||||
controller.blogController = self.blogController;
|
||||
[controller setPost:post];
|
||||
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
|
||||
controller.navigationItem.leftItemsSupplementBackButton = YES;
|
||||
|
|
@ -114,12 +101,13 @@
|
|||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
|
||||
PostCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
|
||||
|
||||
Post *post = self.posts[indexPath.row];
|
||||
// FIXME: unique title
|
||||
cell.textLabel.text = post.title ?: @"Untitled";
|
||||
cell.detailTextLabel.text = post.draft ? @"Draft" : post.formattedDate;
|
||||
NSString *title = post.title ?: @"Untitled";
|
||||
NSString *date = post.draft ? @"Draft" : post.formattedDate;
|
||||
[cell configureWithTitle:title date:date];
|
||||
return cell;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,12 @@
|
|||
NSArray *postPaths = [transaction objectForKey:@"drafts" inCollection:@"PostCollection"];
|
||||
if (postPaths) {
|
||||
[transaction enumerateObjectsForKeys:postPaths inCollection:@"Post" unorderedUsingBlock:^(NSUInteger keyIndex, id object, BOOL *stop) {
|
||||
[posts addObject:object];
|
||||
if (object) {
|
||||
if (!posts) {
|
||||
posts = [NSMutableArray new];
|
||||
}
|
||||
[posts addObject:object];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
|
|
@ -49,7 +54,12 @@
|
|||
NSArray *postPaths = [transaction objectForKey:@"published" inCollection:@"PostCollection"];
|
||||
if (postPaths) {
|
||||
[transaction enumerateObjectsForKeys:postPaths inCollection:@"Post" unorderedUsingBlock:^(NSUInteger keyIndex, id object, BOOL *stop) {
|
||||
[posts addObject:object];
|
||||
if (object) {
|
||||
if (!posts) {
|
||||
posts = [NSMutableArray new];
|
||||
}
|
||||
[posts addObject:object];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
|
|
@ -90,7 +100,7 @@
|
|||
NSMutableArray *postIDs = [NSMutableArray array];
|
||||
for (Post *post in posts) {
|
||||
[transaction setObject:post forKey:post.path inCollection:@"Post"];
|
||||
[postIDs addObject:post.objectID];
|
||||
[postIDs addObject:post.path];
|
||||
}
|
||||
[transaction setObject:postIDs forKey:@"drafts" inCollection:@"PostCollection"];
|
||||
} completionBlock:^{
|
||||
|
|
@ -105,7 +115,7 @@
|
|||
NSMutableArray *postIDs = [NSMutableArray array];
|
||||
for (Post *post in posts) {
|
||||
[transaction setObject:post forKey:post.path inCollection:@"Post"];
|
||||
[postIDs addObject:post.objectID];
|
||||
[postIDs addObject:post.path];
|
||||
}
|
||||
[transaction setObject:postIDs forKey:@"published" inCollection:@"PostCollection"];
|
||||
} completionBlock:^{
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
@property (nonatomic, readonly) NSInteger mm_day;
|
||||
|
||||
+ (NSDate *)mm_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
|
||||
+ (NSDate *)mm_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second;
|
||||
- (NSString *)mm_relativeToNow;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -18,112 +18,108 @@
|
|||
@implementation NSDate (Marshmallows)
|
||||
|
||||
+ (NSDate *)mm_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day {
|
||||
NSCalendar *calendar = [NSCalendar currentCalendar];
|
||||
NSDateComponents *components = [NSDateComponents new];
|
||||
[components setYear:year];
|
||||
[components setMonth:month];
|
||||
[components setDay:day];
|
||||
return [calendar dateFromComponents:components];
|
||||
}
|
||||
return [self mm_dateWithYear:year month:month day:day hour:0 minute:0 second:0];
|
||||
}
|
||||
|
||||
+ (NSDate *)mm_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second {
|
||||
return [[NSCalendar currentCalendar] dateWithEra:1 year:year month:month day:day hour:hour minute:minute second:second nanosecond:0];
|
||||
}
|
||||
|
||||
- (NSString *)mm_relativeToNow {
|
||||
double diff = [[NSDate date] timeIntervalSinceDate:self];
|
||||
NSString *result = nil;
|
||||
|
||||
// future
|
||||
if (diff < -2 * YEAR) {
|
||||
result = [NSString stringWithFormat:@"in %d years", abs(diff / YEAR)];
|
||||
return [NSString stringWithFormat:@"in %d years", abs((int)(diff / YEAR))];
|
||||
}
|
||||
else if (diff < -YEAR) {
|
||||
result = @"next year";
|
||||
return @"next year";
|
||||
}
|
||||
else if (diff < -8 * WEEK) {
|
||||
result = [NSString stringWithFormat:@"in %d months", abs(diff / MONTH)];
|
||||
return [NSString stringWithFormat:@"in %d months", abs((int)(diff / MONTH))];
|
||||
}
|
||||
else if (diff < -4 * WEEK) {
|
||||
result = @"next month";
|
||||
return @"next month";
|
||||
}
|
||||
else if (diff < -2 * WEEK) {
|
||||
result = [NSString stringWithFormat:@"in %d weeks", abs(diff / WEEK)];
|
||||
return [NSString stringWithFormat:@"in %d weeks", abs((int)(diff / WEEK))];
|
||||
}
|
||||
else if (diff < -WEEK) {
|
||||
result = @"next week";
|
||||
return @"next week";
|
||||
}
|
||||
else if (diff < -2 * DAY) {
|
||||
result = [NSString stringWithFormat:@"in %d days", abs(diff / DAY)];
|
||||
return [NSString stringWithFormat:@"in %d days", abs((int)(diff / DAY))];
|
||||
}
|
||||
else if (diff < -DAY) {
|
||||
result = @"tomorrow";
|
||||
return @"tomorrow";
|
||||
}
|
||||
else if (diff < -2 * HOUR) {
|
||||
result = [NSString stringWithFormat:@"in %d hours", abs(diff / HOUR)];
|
||||
return [NSString stringWithFormat:@"in %d hours", abs((int)(diff / HOUR))];
|
||||
}
|
||||
else if (diff < -HOUR) {
|
||||
result = @"in an hour";
|
||||
return @"in an hour";
|
||||
}
|
||||
else if (diff < -2 * MINUTE) {
|
||||
result = [NSString stringWithFormat:@"in %d minutes", abs(diff / MINUTE)];
|
||||
return [NSString stringWithFormat:@"in %d minutes", abs((int)(diff / MINUTE))];
|
||||
}
|
||||
else if (diff < -MINUTE) {
|
||||
result = @"in a minute";
|
||||
return @"in a minute";
|
||||
}
|
||||
|
||||
// present
|
||||
else if (diff < MINUTE) {
|
||||
result = @"right now";
|
||||
return @"right now";
|
||||
}
|
||||
|
||||
// past
|
||||
else if (diff < 2 * MINUTE) {
|
||||
result = @"a minute ago";
|
||||
return @"a minute ago";
|
||||
}
|
||||
else if (diff < HOUR) {
|
||||
result = [NSString stringWithFormat:@"%d minutes ago", (int)(diff / MINUTE)];
|
||||
return [NSString stringWithFormat:@"%d minutes ago", (int)(diff / MINUTE)];
|
||||
}
|
||||
else if (diff < 2 * HOUR) {
|
||||
result = @"an hour ago";
|
||||
return @"an hour ago";
|
||||
}
|
||||
else if (diff < DAY) {
|
||||
result = [NSString stringWithFormat:@"%d hours ago", (int)(diff / HOUR)];
|
||||
return [NSString stringWithFormat:@"%d hours ago", (int)(diff / HOUR)];
|
||||
}
|
||||
else if (diff < 2 * DAY) {
|
||||
result = @"yesterday";
|
||||
return @"yesterday";
|
||||
}
|
||||
else if (diff < WEEK) {
|
||||
result = [NSString stringWithFormat:@"%d days ago", (int)(diff / DAY)];
|
||||
return [NSString stringWithFormat:@"%d days ago", (int)(diff / DAY)];
|
||||
}
|
||||
else if (diff < 2 * WEEK) {
|
||||
result = @"last week";
|
||||
return @"last week";
|
||||
}
|
||||
else if (diff < 4 * WEEK) {
|
||||
result = [NSString stringWithFormat:@"%d weeks ago", (int)(diff / WEEK)];
|
||||
return [NSString stringWithFormat:@"%d weeks ago", (int)(diff / WEEK)];
|
||||
}
|
||||
else if (diff < 8 * WEEK) {
|
||||
result = @"last month";
|
||||
return @"last month";
|
||||
}
|
||||
else if (diff < YEAR) {
|
||||
result = [NSString stringWithFormat:@"%d months ago", (int)(diff / MONTH)];
|
||||
return [NSString stringWithFormat:@"%d months ago", (int)(diff / MONTH)];
|
||||
}
|
||||
else if (diff < 2 * YEAR) {
|
||||
result = @"last year";
|
||||
return @"last year";
|
||||
}
|
||||
else {
|
||||
result = [NSString stringWithFormat:@"%d years ago", (int)(diff / YEAR)];
|
||||
return [NSString stringWithFormat:@"%d years ago", (int)(diff / YEAR)];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSInteger)mm_year {
|
||||
return [[NSCalendar currentCalendar] components:NSCalendarUnitYear fromDate:self].year;
|
||||
return [[NSCalendar currentCalendar] component:NSCalendarUnitYear fromDate:self];
|
||||
}
|
||||
|
||||
- (NSInteger)mm_month {
|
||||
return [[NSCalendar currentCalendar] components:NSCalendarUnitMonth fromDate:self].month;
|
||||
return [[NSCalendar currentCalendar] component:NSCalendarUnitMonth fromDate:self];
|
||||
}
|
||||
|
||||
- (NSInteger)mm_day {
|
||||
return [[NSCalendar currentCalendar] components:NSCalendarUnitDay fromDate:self].day;
|
||||
return [[NSCalendar currentCalendar] component:NSCalendarUnitDay fromDate:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
14
Blog/Post.m
14
Blog/Post.m
|
|
@ -81,7 +81,12 @@
|
|||
}
|
||||
|
||||
- (BOOL)isEqualToPost:(Post *)other {
|
||||
return [self.objectID isEqual:other.objectID];
|
||||
return [self.objectID isEqualToString:other.objectID]
|
||||
&& [self.path isEqualToString:other.path]
|
||||
&& [self.title isEqualToString:other.title]
|
||||
&& [self.body isEqualToString:other.body]
|
||||
&& self.draft == other.draft
|
||||
&& ((!self.url && !other.url) || [self.url isEqual:other.url]);
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
|
|
@ -139,11 +144,12 @@
|
|||
}
|
||||
|
||||
- (NSString *)path {
|
||||
if (!_path && self.slug) {
|
||||
if (!_path) {
|
||||
if (self.draft) {
|
||||
_path = [NSString stringWithFormat:@"/drafts/%@", self.slug];
|
||||
_path = [NSString stringWithFormat:@"/posts/drafts/%@", self.objectID];
|
||||
}
|
||||
else if (self.date) {
|
||||
else {
|
||||
NSAssert(self.slug && self.date, @"slug and date are required");
|
||||
NSString *paddedMonth = [self paddedMonthForDate:self.date];
|
||||
_path = [NSString stringWithFormat:@"/posts/%ld/%@/%@", (long)self.time.mm_year, paddedMonth, self.slug];
|
||||
}
|
||||
|
|
|
|||
11
Blog/PostCell.h
Normal file
11
Blog/PostCell.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// Created by Sami Samhuri on 15-04-19.
|
||||
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||
//
|
||||
@import UIKit;
|
||||
|
||||
@interface PostCell : UITableViewCell
|
||||
|
||||
- (void)configureWithTitle:(NSString *)title date:(NSString *)date;
|
||||
|
||||
@end
|
||||
21
Blog/PostCell.m
Normal file
21
Blog/PostCell.m
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// Created by Sami Samhuri on 15-04-19.
|
||||
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||
//
|
||||
#import "PostCell.h"
|
||||
|
||||
@interface PostCell ()
|
||||
|
||||
@property (nonatomic, weak) IBOutlet UILabel *titleLabel;
|
||||
@property (nonatomic, weak) IBOutlet UILabel *dateLabel;
|
||||
|
||||
@end
|
||||
|
||||
@implementation PostCell
|
||||
|
||||
- (void)configureWithTitle:(NSString *)title date:(NSString *)date {
|
||||
self.titleLabel.text = title;
|
||||
self.dateLabel.text = date;
|
||||
}
|
||||
|
||||
@end
|
||||
17
Blog/PreviewViewController.h
Normal file
17
Blog/PreviewViewController.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// PreviewViewController.h
|
||||
// Blog
|
||||
//
|
||||
// Created by Sami Samhuri on 2015-04-19.
|
||||
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <PromiseKit/Promise.h>
|
||||
|
||||
@interface PreviewViewController : UIViewController
|
||||
|
||||
@property (nonatomic, strong) NSURLRequest *initialRequest;
|
||||
@property (nonatomic, strong) PMKPromise *promise;
|
||||
|
||||
@end
|
||||
43
Blog/PreviewViewController.m
Normal file
43
Blog/PreviewViewController.m
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// PreviewViewController.m
|
||||
// Blog
|
||||
//
|
||||
// Created by Sami Samhuri on 2015-04-19.
|
||||
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import "PreviewViewController.h"
|
||||
|
||||
@interface PreviewViewController () <UIWebViewDelegate>
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIWebView *webView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation PreviewViewController
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (self.initialRequest) {
|
||||
if (self.promise) {
|
||||
self.promise.then(^{
|
||||
[self.webView loadRequest:self.initialRequest];
|
||||
}).finally(^{
|
||||
self.promise = nil;
|
||||
});
|
||||
}
|
||||
else {
|
||||
[self.webView loadRequest:self.initialRequest];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setInitialRequest:(NSURLRequest *)initialRequest {
|
||||
_initialRequest = initialRequest;
|
||||
[self.webView loadHTMLString:@"<!doctype html><html><head><title></title></head><body></body></html>" baseURL:nil];
|
||||
}
|
||||
|
||||
#pragma mark - UIWebViewDelegate methods
|
||||
|
||||
@end
|
||||
Loading…
Reference in a new issue