diff options
author | Vitaly Takmazov | 2019-10-31 16:16:48 +0300 |
---|---|---|
committer | Vitaly Takmazov | 2020-05-12 16:38:25 +0300 |
commit | 9d78e7d654c51c22ce4be87efef5bc1f8d5a2d08 (patch) | |
tree | dfb028dfca07fdb7bbacc00771f9e865f28fd6d4 | |
parent | 742e1a0e615a7e07a9f4b4c249227cd63839325f (diff) |
Fix authentication
28 files changed, 273 insertions, 220 deletions
diff --git a/Juick.xcodeproj/project.pbxproj b/Juick.xcodeproj/project.pbxproj index f6b10ec..c43b8fd 100644 --- a/Juick.xcodeproj/project.pbxproj +++ b/Juick.xcodeproj/project.pbxproj @@ -62,7 +62,7 @@ 77E35A82189A5B5A00B2D216 /* LoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E35A81189A5B5A00B2D216 /* LoginViewController.m */; }; 77E61A5B1FD467FC00B4E304 /* QuoteView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 77E61A5A1FD467FC00B4E304 /* QuoteView.xib */; }; 77E61A5E1FD4682B00B4E304 /* QuoteView.m in Sources */ = {isa = PBXBuildFile; fileRef = 77E61A5D1FD4682B00B4E304 /* QuoteView.m */; }; - 77FCADDF1D6A50DA00CBA649 /* APIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 77FCADDE1D6A50DA00CBA649 /* APIClient.m */; }; + 77FCADDF1D6A50DA00CBA649 /* API.m in Sources */ = {isa = PBXBuildFile; fileRef = 77FCADDE1D6A50DA00CBA649 /* API.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -187,8 +187,8 @@ 77E61A5A1FD467FC00B4E304 /* QuoteView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QuoteView.xib; sourceTree = "<group>"; }; 77E61A5C1FD4682B00B4E304 /* QuoteView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QuoteView.h; sourceTree = "<group>"; }; 77E61A5D1FD4682B00B4E304 /* QuoteView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QuoteView.m; sourceTree = "<group>"; }; - 77FCADDE1D6A50DA00CBA649 /* APIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APIClient.m; sourceTree = "<group>"; }; - 77FCADE01D6A50EC00CBA649 /* APIClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = APIClient.h; sourceTree = "<group>"; }; + 77FCADDE1D6A50DA00CBA649 /* API.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = API.m; sourceTree = "<group>"; }; + 77FCADE01D6A50EC00CBA649 /* API.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = API.h; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -269,8 +269,8 @@ 77317BB2181BBE8500D60005 /* Supporting Files */, 774DD45F1D735E0300C7F290 /* ViewControllers */, 77FFC0151D5FD13C003BD81A /* Views */, - 77FCADE01D6A50EC00CBA649 /* APIClient.h */, - 77FCADDE1D6A50DA00CBA649 /* APIClient.m */, + 77FCADE01D6A50EC00CBA649 /* API.h */, + 77FCADDE1D6A50DA00CBA649 /* API.m */, 77317BBA181BBE8500D60005 /* AppDelegate.h */, 77317BBB181BBE8500D60005 /* AppDelegate.m */, 77317BC6181BBE8500D60005 /* Images.xcassets */, @@ -549,7 +549,7 @@ 77E61A5E1FD4682B00B4E304 /* QuoteView.m in Sources */, 77317BBC181BBE8500D60005 /* AppDelegate.m in Sources */, 776C41C11FD3FF6E0063B82E /* FeedViewController.m in Sources */, - 77FCADDF1D6A50DA00CBA649 /* APIClient.m in Sources */, + 77FCADDF1D6A50DA00CBA649 /* API.m in Sources */, 77A0954A181F1F25002BDECD /* Message.m in Sources */, 773E63A0204BDF16008B8F8D /* ChatViewController.m in Sources */, 773E639D204BD0F2008B8F8D /* Chat.m in Sources */, diff --git a/Juick/APIClient.h b/Juick/API.h index d8317a4..fcf9fb1 100644 --- a/Juick/APIClient.h +++ b/Juick/API.h @@ -9,12 +9,13 @@ #import "Message.h" #import "DeviceRegistration.h" -@interface APIClient : NSObject<NSURLSessionDelegate, NSURLSessionTaskDelegate> +@protocol JuickAuthorizationDelegate +-(void) unauthorized; +@end -@property (nonatomic, strong) NSDateFormatter *dateFormatter; -@property (nonatomic, strong) NSURLCredential *credential; +@interface API : NSObject<NSURLSessionDelegate, NSURLSessionTaskDelegate> -+(APIClient *) sharedClient; +@property (nonatomic, strong) id<JuickAuthorizationDelegate> delegate; -(void) pullNextFromPath:(NSString *)path params:(NSDictionary *)params callback:(void(^)(NSArray<Message *>*, NSError *))callback; -(void) postMessage:(NSString *)text result:(void(^)(Message *, NSError *))callback; @@ -22,11 +23,13 @@ -(void) postPMToUser:(NSString *)uname text:(NSString *)text result:(void(^)(NSError *))callback; -(void) fetchChats:(void(^)(NSArray *, NSError *))callback; -(void) fetchChatWithUser:(NSString *)uname callback:(void(^)(NSArray *, NSError *))callback; --(void) authenticate:(void(^)(User *, NSError *))callback; +-(void) authenticateWithUser:(NSString *)username password:(NSString *)password callback:(void(^)(BOOL))completed; +-(void) signout; +-(void) me:(void(^)(User *, NSError *))callback; -(void) refreshDeviceRegistration:(DeviceRegistration *)registrationData callback:(void(^)(BOOL))callback; -(void) getUserByName:(NSString *) name callback:(void(^)(User *))callback; --(BOOL) isAuthenticated; -(void) fetchImageWithURL:(NSURL *) url callback:(void(^)(NSData *))callback; +@property (nonatomic, strong) User * currentUser; +(NSString *) messagesUrl; +(NSString *) threadUrl; diff --git a/Juick/APIClient.m b/Juick/API.m index e988edc..4f57dc6 100644 --- a/Juick/APIClient.m +++ b/Juick/API.m @@ -1,51 +1,69 @@ // -// APIClient.m +// API.m // Juick // // Created by Vitaly Takmazov on 22/08/16. // Copyright © 2016 com.juick. All rights reserved. // -#import "APIClient.h" #import "Message.h" #import "Chat.h" +#import "API.h" -@interface APIClient () +@interface API () @property(nonatomic, strong) NSOperationQueue *backgroundQueue; @property(nonatomic, strong) NSURL *baseURL; @property(nonatomic, strong) NSURLSession *urlSession; -@property(nonatomic, strong) NSURLProtectionSpace *apiProtectionSpace; --(void) get:(NSString *)path params:(NSDictionary *)params callback:(void(^)(NSDictionary *, NSError *))callback; -@end -@implementation APIClient +-(void) get:(NSString *)path params:(NSDictionary *)params callback:(void(^)(NSDictionary *, NSError *))callback; +-(NSString *) authorizationHeader; -@synthesize credential = _credential; +@end -+(APIClient *) sharedClient { - static APIClient *sharedAPIClient = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedAPIClient = [[self alloc] init]; - }); - return sharedAPIClient; -} +@implementation API -(id)init { if (self = [super init]) { NSString *baseURLString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"base_url"]; NSLog(@"Initializing with %@ base URL", baseURLString); self.baseURL = [NSURL URLWithString:baseURLString]; - self.apiProtectionSpace = [[NSURLProtectionSpace alloc] initWithHost:self.baseURL.host port:0 protocol:NSURLProtectionSpaceHTTPS realm:@"Juick" authenticationMethod:NSURLAuthenticationMethodHTTPBasic]; self.backgroundQueue = [NSOperationQueue new]; - self.urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:self.backgroundQueue]; - - self.dateFormatter = [[NSDateFormatter alloc] init]; - self.dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; - [self.dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]]; + self.urlSession = [NSURLSession sharedSession]; } return self; } +- (NSString *) authorizationHeader { + NSString *token = [[NSUserDefaults standardUserDefaults] stringForKey:@"token"]; + if (token) { + return [NSString stringWithFormat:@"Juick %@", token]; + } + return nil; +} + +- (void)authenticateWithUser:(NSString *)username password:(NSString *)password callback:(void(^)(BOOL))completed { + NSURL *url = [NSURL URLWithString:@"me"relativeToURL:self.baseURL]; + NSString *basicAuthString = [NSString stringWithFormat:@"Basic %@", [[[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]]; + + [self fetchDataWithURL:url data:nil boundary:nil authorizationHeader:basicAuthString callback:^(NSData *response, NSError *err) { + if (!err) { + NSError *jsonError; + NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:response options:0 error:&jsonError]; + if (!jsonError) { + User *me = [User fromJSON:jsonData]; + [[NSUserDefaults standardUserDefaults] setValue:me.token forKeyPath:@"token"]; + self.currentUser = me; + completed(YES); + } + } + completed(NO); + }]; +} + +- (void)signout { + self.currentUser = nil; + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"token"]; +} + -(void) pullNextFromPath:(NSString *)path params:(NSDictionary *) params callback:(void(^)(NSArray<Message *> *, NSError *))callback { [self get:path params:params callback:^(NSDictionary *response, NSError *error) { NSMutableArray *messages = [NSMutableArray new]; @@ -58,6 +76,17 @@ }]; } +- (void)me:(void (^)(User *, NSError *error))callback { + [self get:@"me" params:nil callback:^(NSDictionary *response, NSError *err) { + User *visitor; + if (!err) { + visitor = [User fromJSON:response]; + self.currentUser = visitor; + } + callback(visitor, err); + }]; +} + -(void) postMessage:(NSString *)text result:(void (^)(Message *, NSError *))callback { [self post:@"post" params:@{ @"body": text @@ -147,44 +176,16 @@ NSError *error; NSDictionary *token = [registrationData toJSON]; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@[token] options:kNilOptions error:&error]; - [self fetchDataWithURL:notificationsUrl data:jsonData boundary:nil callback:^(NSData *response, NSError *err) { + [self fetchDataWithURL:notificationsUrl data:jsonData boundary:nil authorizationHeader:self.authorizationHeader callback:^(NSData *response, NSError *err) { callback(!err); }]; } - -- (NSURLCredential *)credential { - NSDictionary *credentials = [[NSURLCredentialStorage sharedCredentialStorage] credentialsForProtectionSpace:self.apiProtectionSpace]; - return [credentials.objectEnumerator nextObject]; -} - -- (void)setCredential:(NSURLCredential *)credential { - if (credential) { - [[NSURLCredentialStorage sharedCredentialStorage] removeCredential:_credential forProtectionSpace:self.apiProtectionSpace options:@{NSURLCredentialStorageRemoveSynchronizableCredentials:@YES}]; - [[NSURLCredentialStorage sharedCredentialStorage] setCredential:credential forProtectionSpace:self.apiProtectionSpace]; - } else { - [[NSURLCredentialStorage sharedCredentialStorage] removeCredential:_credential forProtectionSpace:self.apiProtectionSpace]; - } -} - - --(BOOL) isAuthenticated { - NSString *password = self.credential.password; - return password != nil; -} - -- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler { - if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic) { - if (self.isAuthenticated) { - completionHandler(NSURLSessionAuthChallengeUseCredential, self.credential); - return; - } - } - completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); -} - --(void) fetchDataWithURL:(NSURL *) url data:(NSData *)postData boundary:(NSString *)boundary callback:(void(^)(NSData *, NSError *))callback { +-(void) fetchDataWithURL:(NSURL *) url data:(NSData *)postData boundary:(NSString *)boundary authorizationHeader:(NSString *)header callback:(void(^)(NSData *, NSError *))callback { NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + if (header) { + [request addValue:header forHTTPHeaderField:@"Authorization"]; + } if (boundary) { request.HTTPMethod = @"POST"; request.HTTPBody = postData; @@ -250,7 +251,7 @@ -(void) get:(NSString *) path params:(NSDictionary *)params callback:(void(^)(NSDictionary *, NSError *))callback { NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL]; NSURL *requestUrl = params ? [self url:url withParameters:params] : url; - [self fetchDataWithURL:requestUrl data:nil boundary:nil callback:^(NSData *data, NSError *err) { + [self fetchDataWithURL:requestUrl data:nil boundary:nil authorizationHeader:self.authorizationHeader callback:^(NSData *data, NSError *err) { if (err) { callback(nil, err); } else { @@ -268,7 +269,8 @@ -(void) post:(NSString *) path params:(NSDictionary *)params callback:(void(^)(NSDictionary *, NSError *))callback { NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL]; NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]]; - [self fetchDataWithURL:url data:[self multipartData:params withBoundary:boundary] boundary:boundary callback:^(NSData *data, NSError *err) { + [self fetchDataWithURL:url data:[self multipartData:params withBoundary:boundary] boundary:boundary + authorizationHeader:self.authorizationHeader callback:^(NSData *data, NSError *err) { if (!err) { NSError *jsonError; NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; @@ -284,7 +286,7 @@ } -(void) fetchImageWithURL:(NSURL *) url callback:(void(^)(NSData *))callback { - [self fetchDataWithURL:url data:nil boundary:nil callback:^(NSData *data, NSError *err) { + [self fetchDataWithURL:url data:nil boundary:nil authorizationHeader:self.authorizationHeader callback:^(NSData *data, NSError *err) { if (err) { callback(nil); } else { diff --git a/Juick/AppDelegate.h b/Juick/AppDelegate.h index 8b89131..6c277a9 100644 --- a/Juick/AppDelegate.h +++ b/Juick/AppDelegate.h @@ -9,10 +9,11 @@ @import UIKit; #import "ThreadViewController.h" #import "User.h" +#import "API.h" extern NSString * const UserUpdatedNotificationName; -@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate> +@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate, JuickAuthorizationDelegate> @property (strong, nonatomic) UIWindow *window; @@ -30,6 +31,8 @@ extern NSString * const UserUpdatedNotificationName; @property (strong, nonatomic) NSString *pushedUname; @property (strong, nonatomic) NSNumber *pushedReplyId; +@property (strong, nonatomic) API *api; +@property (strong, nonatomic) NSDateFormatter *sharedDateFormatter; @end diff --git a/Juick/AppDelegate.m b/Juick/AppDelegate.m index d4c2304..47c18ef 100644 --- a/Juick/AppDelegate.m +++ b/Juick/AppDelegate.m @@ -6,11 +6,9 @@ // Copyright (c) 2013 com.juick. All rights reserved. // -#import "AppDelegate.h" #import "MessagesViewController.h" #import "LoginViewController.h" -#import "APIClient.h" #import "Message.h" #import "User.h" #import "DeviceRegistration.h" @@ -28,6 +26,10 @@ NSString * const UserUpdatedNotificationName = @"UserUpdated"; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + self.api = [API new]; + self.sharedDateFormatter = [NSDateFormatter new]; + self.sharedDateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; + [self.sharedDateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]]; [[UINavigationBar appearance] setTintColor:[UIColor colorNamed:@"Title"]]; [[UINavigationBar appearance] setBarTintColor:[UIColor colorNamed:@"Background"]]; [[UINavigationBar appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor colorNamed:@"Muted"]}]; @@ -36,8 +38,9 @@ NSString * const UserUpdatedNotificationName = @"UserUpdated"; [[UITabBar appearance] setTintColor:[UIColor colorNamed:@"Title"]]; [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault; - +#if !TARGET_IPHONE_SIMULATOR [self registerForRemoteNotifications]; +#endif NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; if (userInfo) { [self parseNotificationPayload:userInfo]; @@ -63,7 +66,7 @@ NSString * const UserUpdatedNotificationName = @"UserUpdated"; DeviceRegistration *registration = [DeviceRegistration new]; registration.type = @"apns"; registration.token = token; - [[APIClient sharedClient] refreshDeviceRegistration:registration callback:^(BOOL success) { + [self.api refreshDeviceRegistration:registration callback:^(BOOL success) { if (success) { NSLog(@"successfully refreshed registration with %@", token); } @@ -156,4 +159,8 @@ NSString * const UserUpdatedNotificationName = @"UserUpdated"; [[self navigator] performSegueWithIdentifier:@"loginSegue" sender:vc]; } +- (void)unauthorized { + [self presentLoginView:self.window.rootViewController]; +} + @end diff --git a/Juick/Main.storyboard b/Juick/Main.storyboard index 212e384..bfa1a3f 100644 --- a/Juick/Main.storyboard +++ b/Juick/Main.storyboard @@ -1,9 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Rbr-km-xhI"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Rbr-km-xhI"> <device id="retina4_7" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15510"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/> + <capability name="Named colors" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> @@ -98,62 +99,63 @@ <rect key="frame" x="0.0" y="0.0" width="375" height="647"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> - <visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="iqI-7F-B8z"> - <rect key="frame" x="6" y="185.5" width="363" height="332"/> - <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="7kK-2B-ICG"> - <rect key="frame" x="0.0" y="0.0" width="363" height="332"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <stackView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" axis="vertical" spacing="24" translatesAutoresizingMaskIntoConstraints="NO" id="6Df-q8-WGI"> - <rect key="frame" x="12" y="12" width="339" height="308"/> - <subviews> - <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Splash.png" translatesAutoresizingMaskIntoConstraints="NO" id="8jW-MS-8v7"> - <rect key="frame" x="0.0" y="0.0" width="339" height="192"/> - </imageView> - <textField opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" verticalCompressionResistancePriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="BMn-5D-Cqa"> - <rect key="frame" x="0.0" y="216" width="339" height="34"/> - <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> - <textInputTraits key="textInputTraits" returnKeyType="next" textContentType="username"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="placeholder" value="Username..."/> - </userDefinedRuntimeAttributes> - </textField> - <textField opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="xGs-fu-6K0"> - <rect key="frame" x="0.0" y="274" width="339" height="34"/> - <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> - <textInputTraits key="textInputTraits" returnKeyType="go" secureTextEntry="YES" textContentType="password"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="string" keyPath="placeholder" value="Password..."/> - </userDefinedRuntimeAttributes> - </textField> - </subviews> - </stackView> - </subviews> - <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstItem="6Df-q8-WGI" firstAttribute="leading" secondItem="7kK-2B-ICG" secondAttribute="leading" constant="12" id="3oh-QW-8bt"/> - <constraint firstItem="6Df-q8-WGI" firstAttribute="top" secondItem="7kK-2B-ICG" secondAttribute="top" constant="12" id="Le8-iC-kpd"/> - <constraint firstAttribute="trailing" secondItem="6Df-q8-WGI" secondAttribute="trailing" constant="12" id="PZv-WX-f13"/> - <constraint firstAttribute="bottom" secondItem="6Df-q8-WGI" secondAttribute="bottom" constant="12" id="TyY-LG-msW"/> - </constraints> - </view> - <blurEffect style="light"/> - </visualEffectView> + <stackView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" axis="vertical" spacing="24" translatesAutoresizingMaskIntoConstraints="NO" id="axR-g5-sfd"> + <rect key="frame" x="12" y="68" width="351" height="313.5"/> + <subviews> + <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Splash.png" translatesAutoresizingMaskIntoConstraints="NO" id="ohW-YQ-JZL"> + <rect key="frame" x="0.0" y="0.0" width="351" height="96"/> + </imageView> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Anonymous" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PUg-SX-ZdC" userLabel="Current user"> + <rect key="frame" x="0.0" y="120" width="351" height="20.5"/> + <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> + <color key="textColor" name="Title"/> + <nil key="highlightedColor"/> + </label> + <textField opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" verticalCompressionResistancePriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="rhb-m4-21c"> + <rect key="frame" x="0.0" y="164.5" width="351" height="34"/> + <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> + <textInputTraits key="textInputTraits" returnKeyType="next" textContentType="username"/> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="string" keyPath="placeholder" value="Username..."/> + </userDefinedRuntimeAttributes> + </textField> + <textField opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="T1M-x6-Ghc"> + <rect key="frame" x="0.0" y="222.5" width="351" height="34"/> + <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> + <textInputTraits key="textInputTraits" returnKeyType="go" secureTextEntry="YES" textContentType="password"/> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="string" keyPath="placeholder" value="Password..."/> + </userDefinedRuntimeAttributes> + </textField> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gPQ-xI-b5J"> + <rect key="frame" x="0.0" y="280.5" width="351" height="33"/> + <color key="backgroundColor" name="Chat"/> + <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> + <state key="normal" title="Sign out"> + <color key="titleColor" name="Title"/> + </state> + <connections> + <action selector="signoutPressed:" destination="4g9-hM-bzq" eventType="touchUpInside" id="x5D-vO-T3b"/> + </connections> + </button> + </subviews> + </stackView> </subviews> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <constraints> - <constraint firstItem="fue-ZI-ech" firstAttribute="trailing" secondItem="iqI-7F-B8z" secondAttribute="trailing" constant="6" id="JYV-Ry-3R8"/> - <constraint firstItem="iqI-7F-B8z" firstAttribute="leading" secondItem="fue-ZI-ech" secondAttribute="leading" constant="6" id="NBq-s7-VYY"/> - <constraint firstItem="iqI-7F-B8z" firstAttribute="centerY" secondItem="fue-ZI-ech" secondAttribute="centerY" id="mzO-cF-iKK"/> + <constraint firstItem="fue-ZI-ech" firstAttribute="trailing" secondItem="axR-g5-sfd" secondAttribute="trailing" constant="12" id="CP5-In-mwY"/> + <constraint firstItem="axR-g5-sfd" firstAttribute="leading" secondItem="fue-ZI-ech" secondAttribute="leading" constant="12" id="YIV-CU-Vyy"/> + <constraint firstItem="axR-g5-sfd" firstAttribute="top" secondItem="fue-ZI-ech" secondAttribute="top" constant="12" id="ZUC-uk-RH7"/> </constraints> <viewLayoutGuide key="safeArea" id="fue-ZI-ech"/> </view> <navigationItem key="navigationItem" id="QaM-45-gms"/> <connections> - <outlet property="bottomConstraint" destination="TyY-LG-msW" id="pwp-vP-jtH"/> - <outlet property="passwordField" destination="xGs-fu-6K0" id="zY6-lx-9PQ"/> - <outlet property="usernameField" destination="BMn-5D-Cqa" id="DQU-Fb-Q4e"/> - <outlet property="visualEffectView" destination="iqI-7F-B8z" id="oBZ-aP-B0n"/> + <outlet property="currentUser" destination="PUg-SX-ZdC" id="hEq-3R-Dxu"/> + <outlet property="imageView" destination="ohW-YQ-JZL" id="dxm-uI-j6a"/> + <outlet property="passwordField" destination="T1M-x6-Ghc" id="y4o-Hf-w22"/> + <outlet property="signOutButton" destination="gPQ-xI-b5J" id="ahj-XW-fPQ"/> + <outlet property="usernameField" destination="rhb-m4-21c" id="Q6M-FC-MVs"/> </connections> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="3yb-xh-Hei" userLabel="First Responder" sceneMemberID="firstResponder"/> @@ -360,6 +362,11 @@ <tabBarController id="XnR-Hh-t65" customClass="JuickNavigationController" sceneMemberID="viewController"> <extendedEdge key="edgesForExtendedLayout"/> <navigationItem key="navigationItem" id="Pgj-hT-yHb"> + <barButtonItem key="leftBarButtonItem" title="..." id="an5-75-hlO"> + <connections> + <action selector="showLoginForm:" destination="XnR-Hh-t65" id="s3S-UY-fUi"/> + </connections> + </barButtonItem> <barButtonItem key="rightBarButtonItem" image="ei-pencil" id="fvy-mZ-USk"> <connections> <segue destination="mKa-Ib-r2p" kind="modal" identifier="editorSegue" id="mG5-hj-MTY"/> @@ -441,5 +448,11 @@ <image name="ei-envelope" width="23" height="16"/> <image name="ei-pencil" width="23" height="22"/> <image name="ei-search" width="23" height="23"/> + <namedColor name="Chat"> + <color red="0.97254901960784312" green="0.97254901960784312" blue="0.97254901960784312" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + </namedColor> + <namedColor name="Title"> + <color red="0.23529411764705882" green="0.46666666666666667" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + </namedColor> </resources> </document> diff --git a/Juick/Model/Message.m b/Juick/Model/Message.m index f5c2640..d39be54 100644 --- a/Juick/Model/Message.m +++ b/Juick/Model/Message.m @@ -7,7 +7,6 @@ // #import "Message.h" -#import "AppDelegate.h" @implementation Message diff --git a/Juick/Model/User.h b/Juick/Model/User.h index cbf4e46..344626e 100644 --- a/Juick/Model/User.h +++ b/Juick/Model/User.h @@ -12,6 +12,7 @@ @property (nonatomic, strong) NSString *uname; @property (nonatomic, strong) NSString *uid; @property (nonatomic, strong) NSString *avatar; +@property (nonatomic, strong) NSString *token; @property (nonatomic) NSInteger unreadCount; + (User *) fromJSON:(NSDictionary *)jsonData; diff --git a/Juick/Model/User.m b/Juick/Model/User.m index 94b77c4..a0c34f9 100644 --- a/Juick/Model/User.m +++ b/Juick/Model/User.m @@ -7,7 +7,6 @@ // #import "User.h" -#import "APIClient.h" @implementation User @@ -16,6 +15,7 @@ user.uid = jsonData[@"uid"]; user.uname = jsonData[@"uname"]; user.avatar = jsonData[@"avatar"]; + user.token = jsonData[@"hash"]; user.unreadCount = [jsonData[@"messagesCount"] integerValue]; return user; } diff --git a/Juick/Supporting Files/Juick-Prefix.pch b/Juick/Supporting Files/Juick-Prefix.pch index 7278b52..957a052 100644 --- a/Juick/Supporting Files/Juick-Prefix.pch +++ b/Juick/Supporting Files/Juick-Prefix.pch @@ -14,9 +14,10 @@ #import <UIKit/UIKit.h> #import <Foundation/Foundation.h> #import <SystemConfiguration/SystemConfiguration.h> - #import <MobileCoreServices/MobileCoreServices.h> + #import <CoreServices/CoreServices.h> #import <UserNotifications/UserNotifications.h> #import "UIImage+Utils.h" #import "UIView+Shimmer.h" + #import "AppDelegate.h" #endif diff --git a/Juick/Supporting Files/main.m b/Juick/Supporting Files/main.m index 67b222f..8e01106 100644 --- a/Juick/Supporting Files/main.m +++ b/Juick/Supporting Files/main.m @@ -8,8 +8,6 @@ #import <UIKit/UIKit.h> -#import "AppDelegate.h" - int main(int argc, char * argv[]) { @autoreleasepool { diff --git a/Juick/ViewControllers/ChatViewController.m b/Juick/ViewControllers/ChatViewController.m index 2612719..695d7ec 100644 --- a/Juick/ViewControllers/ChatViewController.m +++ b/Juick/ViewControllers/ChatViewController.m @@ -8,7 +8,6 @@ #import "ChatViewController.h" #import "BubbleMessageCell.h" -#import "APIClient.h" #import "MessageInputView.h" @@ -29,7 +28,6 @@ self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive; self.tableView.allowsSelection = NO; self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic; - self.me = [APIClient sharedClient].credential.user; [self reloadChat]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; self.refreshControl = [UIRefreshControl new]; @@ -40,7 +38,7 @@ -(void) reloadChat { self.messages = [NSMutableArray array]; [self.tableView reloadData]; - [[APIClient sharedClient] fetchChatWithUser:self.uname callback:^(NSArray *messages, NSError *err) { + [[AppDelegate shared].api fetchChatWithUser:self.uname callback:^(NSArray *messages, NSError *err) { if (err == nil) { [self.messages addObjectsFromArray:[[messages reverseObjectEnumerator] allObjects]]; NSMutableArray *indexPaths = [NSMutableArray new]; @@ -107,7 +105,7 @@ } -(void) textSent:(NSString *)text { - [[APIClient sharedClient] postPMToUser:self.uname text:text result:^(NSError *err) { + [[AppDelegate shared].api postPMToUser:self.uname text:text result:^(NSError *err) { if (!err) { NSLog(@"Success!"); [self.accessoryView becomeFirstResponder]; diff --git a/Juick/ViewControllers/DialogsViewController.m b/Juick/ViewControllers/DialogsViewController.m index 0b7f2ca..432cd53 100644 --- a/Juick/ViewControllers/DialogsViewController.m +++ b/Juick/ViewControllers/DialogsViewController.m @@ -9,8 +9,6 @@ #import "DialogsViewController.h" #import "ChatViewController.h" #import "ConversationCell.h" -#import "APIClient.h" -#import "AppDelegate.h" @implementation DialogsViewController - (void)viewDidLoad { @@ -31,7 +29,7 @@ - (void) refreshData { self.chats = [NSMutableArray array]; [self.tableView reloadData]; - [[APIClient sharedClient] fetchChats:^(NSArray *groups, NSError *err) { + [[AppDelegate shared].api fetchChats:^(NSArray *groups, NSError *err) { if (err == nil) { [self.chats addObjectsFromArray:groups]; NSMutableArray *indexPaths = [NSMutableArray new]; diff --git a/Juick/ViewControllers/DiscoverViewController.m b/Juick/ViewControllers/DiscoverViewController.m index 7bf9f74..10185ef 100644 --- a/Juick/ViewControllers/DiscoverViewController.m +++ b/Juick/ViewControllers/DiscoverViewController.m @@ -7,7 +7,6 @@ // #import "DiscoverViewController.h" -#import "APIClient.h" @interface DiscoverViewController () @@ -17,7 +16,7 @@ - (void)viewDidLoad { self.messagesDelegate = self; self.title = @"Discover"; - self.path = [APIClient messagesUrl]; + self.path = [API messagesUrl]; [self setShouldScrollToUnreadOnRefresh:NO]; [super viewDidLoad]; } diff --git a/Juick/ViewControllers/DiscussionsController.m b/Juick/ViewControllers/DiscussionsController.m index 607ac26..47ee7f2 100644 --- a/Juick/ViewControllers/DiscussionsController.m +++ b/Juick/ViewControllers/DiscussionsController.m @@ -7,7 +7,6 @@ // #import "DiscussionsController.h" -#import "APIClient.h" @interface DiscussionsController () @@ -18,7 +17,7 @@ - (void)viewDidLoad { self.messagesDelegate = self; self.title = @"Discussions"; - self.path = [APIClient discussionsUrl]; + self.path = [API discussionsUrl]; [self setShouldScrollToUnreadOnRefresh:NO]; [super viewDidLoad]; } @@ -26,7 +25,7 @@ - (void)loadMore { Message *lastMsg = [self.messages lastObject]; if (lastMsg != nil) { - NSDate *msgDate = [[APIClient sharedClient].dateFormatter dateFromString:lastMsg.timestamp]; + NSDate *msgDate = [[AppDelegate shared].sharedDateFormatter dateFromString:lastMsg.timestamp]; self.params = [@{@"to" : [NSString stringWithFormat:@"%.0f", [msgDate timeIntervalSince1970] * 1000]} mutableCopy]; [self setShouldScrollToUnreadOnRefresh:NO]; [self refreshData]; diff --git a/Juick/ViewControllers/FeedViewController.m b/Juick/ViewControllers/FeedViewController.m index da1b914..c492f2e 100644 --- a/Juick/ViewControllers/FeedViewController.m +++ b/Juick/ViewControllers/FeedViewController.m @@ -10,8 +10,6 @@ #import "ThreadViewController.h" #import "BlogViewController.h" #import "MessageCell.h" -#import "APIClient.h" -#import "AppDelegate.h" #import "LoginViewController.h" NSString * const UserNotAuthenticatedNotificationName = @"UserNotAuthenticated"; @@ -28,7 +26,7 @@ NSString * const UserNotAuthenticatedNotificationName = @"UserNotAuthenticated"; [self refreshPath]; [self setShouldScrollToUnreadOnRefresh:NO]; [super viewDidLoad]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDidSignedIn:) name:UserSignedInNotificationName object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDidSignedIn:) name:UserChangedNotificationName object:nil]; } -(void)avatarClicked:(NSString *)uname { @@ -46,17 +44,11 @@ NSString * const UserNotAuthenticatedNotificationName = @"UserNotAuthenticated"; } } - - -- (void)didReceiveChallenge { - -} - -(void) refreshPath { - if ([[APIClient sharedClient] isAuthenticated]) { - self.path = [APIClient feedUrl]; + if ([[AppDelegate shared].api currentUser]) { + self.path = [API feedUrl]; } else { - self.path = [APIClient messagesUrl]; + self.path = [API messagesUrl]; self.params = @{@"popular": @1}; } } diff --git a/Juick/ViewControllers/JuickNavigationController.h b/Juick/ViewControllers/JuickNavigationController.h index b508466..974c177 100644 --- a/Juick/ViewControllers/JuickNavigationController.h +++ b/Juick/ViewControllers/JuickNavigationController.h @@ -11,7 +11,9 @@ NS_ASSUME_NONNULL_BEGIN @interface JuickNavigationController : UITabBarController -- (IBAction)newMessage:(id)sender; +- (IBAction)showLoginForm:(id)sender; +- (void) refreshStatus; +@property (weak, nonatomic) IBOutlet UIBarButtonItem *leftButton; @end NS_ASSUME_NONNULL_END diff --git a/Juick/ViewControllers/JuickNavigationController.m b/Juick/ViewControllers/JuickNavigationController.m index 8673866..aaf3765 100644 --- a/Juick/ViewControllers/JuickNavigationController.m +++ b/Juick/ViewControllers/JuickNavigationController.m @@ -8,31 +8,54 @@ #import "JuickNavigationController.h" -#import "AppDelegate.h" -#import "APIClient.h" #import "MessagesViewController.h" #import "ThreadViewController.h" #import "NewPostViewController.h" #import "Message.h" +#import "LoginViewController.h" @interface JuickNavigationController () +@property(nonatomic, strong) UIButton *avatarButton; @end @implementation JuickNavigationController + +-(void) refreshStatus { + [[AppDelegate shared].api me:^(User *user, NSError *err) { + NSString *avatarUrl; + if (err || !user) { + avatarUrl = @"https://i.juick.com/av-96.png"; + } else { + avatarUrl = user.avatar; + } + [[AppDelegate shared].api fetchImageWithURL:[NSURL URLWithString:avatarUrl] callback:^(NSData *data) { + self.avatarButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [self.avatarButton addTarget:self action:@selector(showLoginForm:) forControlEvents:UIControlEventTouchUpInside]; + [self.avatarButton setImage:[UIImage imageWithImage:[UIImage imageWithData:data] fitInsideWidth:44 fitInsideHeight:44] forState:UIControlStateNormal]; + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.avatarButton]; + }]; + }]; +} + - (void)viewDidLoad { [super viewDidLoad]; - // Do any additional setup after loading the view. + [self refreshStatus]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDidSignedIn:) name:UserChangedNotificationName object:nil]; +} + +- (void)userDidSignedIn:(NSNotification *) notification { + [self refreshStatus]; } #pragma mark - Navigation - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender { if ([identifier isEqualToString:@"editorSegue"]) { - if ([[APIClient sharedClient] isAuthenticated]) { + if ([[AppDelegate shared].api currentUser]) { return YES; } else { [[AppDelegate shared] presentLoginView:self]; @@ -71,7 +94,7 @@ } } -- (IBAction)newMessage:(id)sender { - [[AppDelegate shared] presentLoginView:self]; +- (IBAction)showLoginForm:(id)sender { + [self performSegueWithIdentifier:@"loginSegue" sender:self]; } @end diff --git a/Juick/ViewControllers/LoginViewController.h b/Juick/ViewControllers/LoginViewController.h index e1a4900..451a99c 100644 --- a/Juick/ViewControllers/LoginViewController.h +++ b/Juick/ViewControllers/LoginViewController.h @@ -10,11 +10,14 @@ #import "User.h" -extern NSString * const UserSignedInNotificationName; +extern NSString * const UserChangedNotificationName; @interface LoginViewController : UIViewController +@property (weak, nonatomic) IBOutlet UILabel *currentUser; @property (weak, nonatomic) IBOutlet UITextField *usernameField; @property (weak, nonatomic) IBOutlet UITextField *passwordField; -@property (weak, nonatomic) IBOutlet UIVisualEffectView *visualEffectView; +@property (weak, nonatomic) IBOutlet UIButton *signOutButton; +@property (weak, nonatomic) IBOutlet UIImageView *imageView; +- (IBAction)signoutPressed:(id)sender; @property (strong, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint; @end diff --git a/Juick/ViewControllers/LoginViewController.m b/Juick/ViewControllers/LoginViewController.m index 55099eb..a0e9b97 100644 --- a/Juick/ViewControllers/LoginViewController.m +++ b/Juick/ViewControllers/LoginViewController.m @@ -8,33 +8,32 @@ #import "LoginViewController.h" #import "User.h" -#import "AppDelegate.h" -#import "APIClient.h" -NSString * const UserSignedInNotificationName = @"UserSignedIn"; +NSString * const UserChangedNotificationName = @"UserSignedIn"; @interface LoginViewController() @property (nonatomic, assign) int paddingValue; +-(void) refreshState; @end @implementation LoginViewController - (void) awakeFromNib { [super awakeFromNib]; - self.title = @"Sign in"; [self.view setBackgroundColor:[UIColor colorNamed:@"Background"]]; - [self.visualEffectView.contentView setBackgroundColor:[UIColor colorNamed:@"Background"]]; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelSignIn)]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(doneSignIn)]; - self.usernameField.text = [APIClient sharedClient].credential.user; - self.passwordField.text = [APIClient sharedClient].credential.password; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil]; } +- (void)viewWillAppear:(BOOL)animated { + [self refreshState]; +} + -(void) keyboardDidShow:(NSNotification *)sender { CGRect keyboardRect = [sender.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; self.bottomConstraint.constant = keyboardRect.size.height - self.view.safeAreaInsets.bottom + self.paddingValue; @@ -47,32 +46,54 @@ NSString * const UserSignedInNotificationName = @"UserSignedIn"; - (void) cancelSignIn { - CATransition* transition = [CATransition animation]; - transition.duration = 0.3; - transition.type = kCATransitionFade; - transition.subtype = kCATransitionFromTop; - - [self.navigationController.view.layer addAnimation:transition forKey:kCATransition]; - [self.navigationController dismissViewControllerAnimated:NO completion:nil]; + [self.navigationController dismissViewControllerAnimated:YES completion:nil]; } - (void) doneSignIn { - [[APIClient sharedClient] setCredential:[NSURLCredential credentialWithUser:self.usernameField.text password:self.passwordField.text persistence:NSURLCredentialPersistenceSynchronizable]]; - [[APIClient sharedClient] authenticate:^(User *user, NSError *error) { - if (user) { - [[NSNotificationCenter defaultCenter] postNotificationName:UserSignedInNotificationName object:user]; + + [[NSURLCache sharedURLCache] removeAllCachedResponses]; + [[AppDelegate shared].api authenticateWithUser:self.usernameField.text password:self.passwordField.text callback:^(BOOL success){ + if (success) { + [[NSNotificationCenter defaultCenter] postNotificationName:UserChangedNotificationName object:nil]; [[AppDelegate shared] registerForRemoteNotifications]; - CATransition* transition = [CATransition animation]; - transition.duration = 0.3; - transition.type = kCATransitionFade; - transition.subtype = kCATransitionFromTop; - - [self.navigationController.view.layer addAnimation:transition forKey:kCATransition]; - [self.navigationController dismissViewControllerAnimated:NO completion:nil]; - } else { - [User throwUnableToLogin:self error:error]; - [[APIClient sharedClient] setCredential:nil]; + [self.navigationController dismissViewControllerAnimated:YES completion:nil]; } }]; } + +- (void)refreshState { + if ([AppDelegate shared].api.currentUser) { + self.title = @"Profile"; + [self.usernameField setHidden:YES]; + [self.passwordField setHidden:YES]; + [self.currentUser setHidden:NO]; + self.currentUser.text = [AppDelegate shared].api.currentUser.uname; + __weak UIImageView *weakAttach = self.imageView; + [[AppDelegate shared].api fetchImageWithURL:[NSURL URLWithString:[AppDelegate shared].api.currentUser.avatar] callback:^(NSData *data) { + [UIView transitionWithView:weakAttach + duration:0.3 + options:UIViewAnimationOptionTransitionCrossDissolve + animations:^{ + weakAttach.image = [UIImage imageWithData:data]; + } + completion:nil]; + }]; + [self.signOutButton setHidden:NO]; + } else { + self.title = @"Sign in"; + self.imageView.image = [UIImage imageNamed:@"Splash"]; + [self.usernameField setHidden:NO]; + [self.passwordField setHidden:NO]; + [self.currentUser setHidden:YES]; + [self.signOutButton setHidden:YES]; + } +} + +- (IBAction)signoutPressed:(id)sender { + [[AppDelegate shared].api signout]; + [[NSURLCache sharedURLCache] removeAllCachedResponses]; + [self.navigationController dismissViewControllerAnimated:YES completion:^{ + [[NSNotificationCenter defaultCenter] postNotificationName:UserChangedNotificationName object:nil]; + }]; +} @end diff --git a/Juick/ViewControllers/MessagesViewController.m b/Juick/ViewControllers/MessagesViewController.m index 25a2ca5..f61d8b4 100644 --- a/Juick/ViewControllers/MessagesViewController.m +++ b/Juick/ViewControllers/MessagesViewController.m @@ -9,8 +9,6 @@ #import "MessagesViewController.h" #import "MessageCell.h" -#import "AppDelegate.h" -#import "APIClient.h" #import "Message.h" #import "NewPostViewController.h" @@ -35,20 +33,20 @@ NSString* const messageCellIdentifier = @"messageCell"; if (self.messages.count == 0) { [self.tableView reloadData]; } - [[APIClient sharedClient] pullNextFromPath:self.path params:self.params callback:^(NSArray *next, NSError *err) { + [[AppDelegate shared].api pullNextFromPath:self.path params:self.params callback:^(NSArray *next, NSError *err) { if (err) { [User throwUnableToLogin:self error:err]; return; } NSArray *newMsgs = next; if ([self isAtTop:self.params]) { - if (![self.path isEqualToString:[APIClient threadUrl]]) { + if (![self.path isEqualToString:[API threadUrl]]) { [self.messages removeAllObjects]; [self.tableView reloadData]; } } NSUInteger oldCount = [self.messages count]; - if ([self.path isEqualToString:[APIClient threadUrl]]) { + if ([self.path isEqualToString:[API threadUrl]]) { NSUInteger lastRid = [((Message *)[self.messages lastObject]).rid unsignedIntegerValue] + 1; NSUInteger count = [next count]; if (oldCount > 0) { diff --git a/Juick/ViewControllers/NewPostViewController.m b/Juick/ViewControllers/NewPostViewController.m index e4b5ac7..344e7b9 100644 --- a/Juick/ViewControllers/NewPostViewController.m +++ b/Juick/ViewControllers/NewPostViewController.m @@ -9,8 +9,6 @@ #import "NewPostViewController.h" #import "MessagesViewController.h" #import "QuoteView.h" -#import "APIClient.h" -#import "AppDelegate.h" NSString * const NewMessageNotificationName = @"NewMessage"; NSString * const ReplyPostedNotificationName = @"ReplyPosted"; @@ -47,8 +45,8 @@ NSString * const ReplyPostedNotificationName = @"ReplyPosted"; - (IBAction)sendAction:(id)sender { self.navigationItem.rightBarButtonItem.enabled = NO; if (_replyTo == nil) { - self.navigationItem.rightBarButtonItem.enabled = YES; - [[APIClient sharedClient] postMessage:self.textView.text result:^(Message *msg, NSError *err) { + [[AppDelegate shared].api postMessage:self.textView.text result:^(Message *msg, NSError *err) { + self.navigationItem.rightBarButtonItem.enabled = YES; if (!err) { [self dismissViewControllerAnimated:YES completion:^{ [[NSNotificationCenter defaultCenter] postNotificationName:NewMessageNotificationName object:msg]; @@ -60,7 +58,7 @@ NSString * const ReplyPostedNotificationName = @"ReplyPosted"; }]; } else { self.navigationItem.rightBarButtonItem.enabled = NO; - [[APIClient sharedClient] postReplyToThread:_replyTo.mid inReplyTo:_replyTo.rid text:self.textView.text result:^(Message *msg, NSError *err) { + [[AppDelegate shared].api postReplyToThread:_replyTo.mid inReplyTo:_replyTo.rid text:self.textView.text result:^(Message *msg, NSError *err) { if (!err) { [self dismissViewControllerAnimated:YES completion:^{ [[NSNotificationCenter defaultCenter] postNotificationName:ReplyPostedNotificationName object:msg]; diff --git a/Juick/ViewControllers/ThreadViewController.m b/Juick/ViewControllers/ThreadViewController.m index c345822..8ee7623 100644 --- a/Juick/ViewControllers/ThreadViewController.m +++ b/Juick/ViewControllers/ThreadViewController.m @@ -9,14 +9,12 @@ #import "ThreadViewController.h" #import "NewPostViewController.h" #import "MessageCell.h" -#import "APIClient.h" -#import "AppDelegate.h" @implementation ThreadViewController -(void) viewDidLoad { - [self setPath:[APIClient threadUrl]]; + [self setPath:[API threadUrl]]; [super viewDidLoad]; self.messagesDelegate = self; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(replyPosted:) name:ReplyPostedNotificationName object:nil]; @@ -30,10 +28,10 @@ - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender { if ([identifier isEqualToString:@"editorSegue"]) { - if ([[APIClient sharedClient] isAuthenticated]) { + if ([[NSUserDefaults standardUserDefaults] stringForKey:@"token"]) { return YES; } else { - [[AppDelegate shared] presentLoginView:self]; + [[AppDelegate shared] unauthorized]; return NO; } } diff --git a/Juick/Views/BubbleMessageCell.m b/Juick/Views/BubbleMessageCell.m index 65d3c0c..a5131e6 100644 --- a/Juick/Views/BubbleMessageCell.m +++ b/Juick/Views/BubbleMessageCell.m @@ -7,7 +7,6 @@ // #import "BubbleMessageCell.h" -#import "APIClient.h" @implementation BubbleMessageCell @@ -31,7 +30,7 @@ self.message.text = message.text; self.unreadMarker.text = @""; __weak UIImageView *weakAvatar = self.avatarView; - [[APIClient sharedClient] fetchImageWithURL:[NSURL URLWithString:message.user.avatar] callback:^(NSData *data) { + [[AppDelegate shared].api fetchImageWithURL:[NSURL URLWithString:message.user.avatar] callback:^(NSData *data) { [UIView transitionWithView:weakAvatar duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve diff --git a/Juick/Views/ContentLoadingCell.xib b/Juick/Views/ContentLoadingCell.xib index f7ca251..21c121b 100644 --- a/Juick/Views/ContentLoadingCell.xib +++ b/Juick/Views/ContentLoadingCell.xib @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <device id="retina4_7" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/> <capability name="Named colors" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> @@ -92,7 +92,7 @@ <color red="0.97254901960784312" green="0.97254901960784312" blue="0.97254901960784312" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </namedColor> <namedColor name="Title"> - <color red="0.0" green="0.40000000000000002" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <color red="0.23529411764705882" green="0.46666666666666667" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </namedColor> </resources> </document> diff --git a/Juick/Views/ConversationCell.m b/Juick/Views/ConversationCell.m index dd17963..4c1e71c 100644 --- a/Juick/Views/ConversationCell.m +++ b/Juick/Views/ConversationCell.m @@ -7,7 +7,6 @@ // #import "ConversationCell.h" -#import "APIClient.h" @implementation ConversationCell @@ -17,7 +16,7 @@ self.lastMessage.text = chat.lastMessageText; self.unreadMarker.hidden = true; __weak UIImageView *weakAvatar = self.avatar; - [[APIClient sharedClient] fetchImageWithURL:[NSURL URLWithString:chat.avatar] callback:^(NSData *data) { + [[AppDelegate shared].api fetchImageWithURL:[NSURL URLWithString:chat.avatar] callback:^(NSData *data) { [UIView transitionWithView:weakAvatar duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve diff --git a/Juick/Views/MessageCell.m b/Juick/Views/MessageCell.m index a64b7c3..ad1a3a3 100644 --- a/Juick/Views/MessageCell.m +++ b/Juick/Views/MessageCell.m @@ -7,7 +7,6 @@ // #import "MessageCell.h" -#import "APIClient.h" #import "Entity.h" #import "NSDate+TimeAgo.h" @@ -50,7 +49,7 @@ const NSString *unreadMarker = @"●"; - (void) configureWithMessage:(Message *)msg { self.avatar.image = nil; __weak UIImageView *weakAvatar = self.avatar; - [[APIClient sharedClient] fetchImageWithURL:[NSURL URLWithString:msg.user.avatar] callback:^(NSData *data) { + [[AppDelegate shared].api fetchImageWithURL:[NSURL URLWithString:msg.user.avatar] callback:^(NSData *data) { [UIView transitionWithView:weakAvatar duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve @@ -67,7 +66,7 @@ const NSString *unreadMarker = @"●"; self.attachmentHeight.constant = imageHeight; self.attachmentWidth.constant = imageWidth; __weak UIImageView *weakAttach = self.attach; - [[APIClient sharedClient] fetchImageWithURL:[NSURL URLWithString:msg.attachment.small.url] callback:^(NSData *data) { + [[AppDelegate shared].api fetchImageWithURL:[NSURL URLWithString:msg.attachment.small.url] callback:^(NSData *data) { [UIView transitionWithView:weakAttach duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve @@ -82,7 +81,7 @@ const NSString *unreadMarker = @"●"; } self.title.text = msg.user.uname; - self.timestamp.text = [[[APIClient sharedClient].dateFormatter dateFromString:msg.timestamp] timeAgo]; + self.timestamp.text = [[[AppDelegate shared].sharedDateFormatter dateFromString:msg.timestamp] timeAgo]; NSUInteger count = [msg.repliesCount unsignedIntegerValue]; if (count > 0) { if ([msg.repliesBy length] > 0) { diff --git a/Juick/Views/MessageInputView.xib b/Juick/Views/MessageInputView.xib index 876be37..e13beca 100644 --- a/Juick/Views/MessageInputView.xib +++ b/Juick/Views/MessageInputView.xib @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <device id="retina4_7" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/> <capability name="Named colors" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> @@ -75,7 +75,7 @@ <color red="0.13333333333333333" green="0.13333333333333333" blue="0.13333333333333333" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </namedColor> <namedColor name="Title"> - <color red="0.0" green="0.40000000000000002" blue="0.59999999999999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <color red="0.23529411764705882" green="0.46666666666666667" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </namedColor> </resources> </document> |