summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2019-10-31 16:16:48 +0300
committerGravatar Vitaly Takmazov2020-05-12 16:38:25 +0300
commit9d78e7d654c51c22ce4be87efef5bc1f8d5a2d08 (patch)
treedfb028dfca07fdb7bbacc00771f9e865f28fd6d4
parent742e1a0e615a7e07a9f4b4c249227cd63839325f (diff)
Fix authentication
-rw-r--r--Juick.xcodeproj/project.pbxproj12
-rw-r--r--Juick/API.h (renamed from Juick/APIClient.h)15
-rw-r--r--Juick/API.m (renamed from Juick/APIClient.m)118
-rw-r--r--Juick/AppDelegate.h5
-rw-r--r--Juick/AppDelegate.m15
-rw-r--r--Juick/Main.storyboard113
-rw-r--r--Juick/Model/Message.m1
-rw-r--r--Juick/Model/User.h1
-rw-r--r--Juick/Model/User.m2
-rw-r--r--Juick/Supporting Files/Juick-Prefix.pch3
-rw-r--r--Juick/Supporting Files/main.m2
-rw-r--r--Juick/ViewControllers/ChatViewController.m6
-rw-r--r--Juick/ViewControllers/DialogsViewController.m4
-rw-r--r--Juick/ViewControllers/DiscoverViewController.m3
-rw-r--r--Juick/ViewControllers/DiscussionsController.m5
-rw-r--r--Juick/ViewControllers/FeedViewController.m16
-rw-r--r--Juick/ViewControllers/JuickNavigationController.h4
-rw-r--r--Juick/ViewControllers/JuickNavigationController.m35
-rw-r--r--Juick/ViewControllers/LoginViewController.h7
-rw-r--r--Juick/ViewControllers/LoginViewController.m77
-rw-r--r--Juick/ViewControllers/MessagesViewController.m8
-rw-r--r--Juick/ViewControllers/NewPostViewController.m8
-rw-r--r--Juick/ViewControllers/ThreadViewController.m8
-rw-r--r--Juick/Views/BubbleMessageCell.m3
-rw-r--r--Juick/Views/ContentLoadingCell.xib6
-rw-r--r--Juick/Views/ConversationCell.m3
-rw-r--r--Juick/Views/MessageCell.m7
-rw-r--r--Juick/Views/MessageInputView.xib6
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>