Integrating socket status indicator and remove favorites.

pull/1/head
Frederic Jacobs 11 years ago
parent 8b6ac13594
commit ad5bb92004

@ -423,7 +423,6 @@
B6AE33B91A1EB0AF003DF39D /* DemoDataFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = B6AE33B61A1EB0AF003DF39D /* DemoDataFactory.m */; };
B6AE33BA1A1EB0AF003DF39D /* DemoDataModel.m in Sources */ = {isa = PBXBuildFile; fileRef = B6AE33B81A1EB0AF003DF39D /* DemoDataModel.m */; };
B6AE33BD1A1EB121003DF39D /* GroupModel.m in Sources */ = {isa = PBXBuildFile; fileRef = B6AE33BC1A1EB121003DF39D /* GroupModel.m */; };
B6AE33C01A1EB2DD003DF39D /* Socket.m in Sources */ = {isa = PBXBuildFile; fileRef = B6AE33BF1A1EB2DD003DF39D /* Socket.m */; };
B6B095E41A1D25C5008BFAA6 /* CryptographyTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6B095DE1A1D25C5008BFAA6 /* CryptographyTests.mm */; };
B6B095E51A1D25C5008BFAA6 /* TextSecureKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B095DF1A1D25C5008BFAA6 /* TextSecureKitTests.m */; };
B6B095E61A1D25C5008BFAA6 /* TSMessageStorageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B095E01A1D25C5008BFAA6 /* TSMessageStorageTests.m */; };
@ -1129,8 +1128,6 @@
B6AE33B81A1EB0AF003DF39D /* DemoDataModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoDataModel.m; sourceTree = "<group>"; };
B6AE33BB1A1EB121003DF39D /* GroupModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GroupModel.h; sourceTree = "<group>"; };
B6AE33BC1A1EB121003DF39D /* GroupModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GroupModel.m; sourceTree = "<group>"; };
B6AE33BE1A1EB2DD003DF39D /* Socket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Socket.h; sourceTree = "<group>"; };
B6AE33BF1A1EB2DD003DF39D /* Socket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Socket.m; sourceTree = "<group>"; };
B6B095DE1A1D25C5008BFAA6 /* CryptographyTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CryptographyTests.mm; sourceTree = "<group>"; };
B6B095DF1A1D25C5008BFAA6 /* TextSecureKitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TextSecureKitTests.m; sourceTree = "<group>"; };
B6B095E01A1D25C5008BFAA6 /* TSMessageStorageTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSMessageStorageTests.m; sourceTree = "<group>"; };
@ -2752,8 +2749,6 @@
B6AE33B61A1EB0AF003DF39D /* DemoDataFactory.m */,
B6AE33B71A1EB0AF003DF39D /* DemoDataModel.h */,
B6AE33B81A1EB0AF003DF39D /* DemoDataModel.m */,
B6AE33BE1A1EB2DD003DF39D /* Socket.h */,
B6AE33BF1A1EB2DD003DF39D /* Socket.m */,
);
name = temp;
sourceTree = "<group>";
@ -3751,7 +3746,6 @@
B6B9ECFC198B31BA00C620D3 /* PushManager.m in Sources */,
76EB05D618170B33006006FC /* ZrtpResponder.m in Sources */,
7095B7B018F46D35002C66E2 /* PhoneNumberUtil.m in Sources */,
B6AE33C01A1EB2DD003DF39D /* Socket.m in Sources */,
B6B096791A1D25ED008BFAA6 /* TSRecipientPrekeyRequest.m in Sources */,
E197B61618BBEC1A00F073E5 /* StretchFactorController.m in Sources */,
FCFD257F1A154B2C00F4C644 /* RegistrationViewController.m in Sources */,

@ -22,8 +22,6 @@
@property (nonatomic, assign) BOOL isTextSecureContact;
@property (nonatomic, assign) BOOL isRedPhoneContact;
@property (nonatomic, assign) BOOL isFavourite;
+ (Contact*)contactWithFirstName:(NSString*)firstName
andLastName:(NSString *)lastName
andUserTextPhoneNumbers:(NSArray*)phoneNumbers
@ -36,7 +34,6 @@
andEmails:(NSArray*)emails
andImage:(UIImage *)image
andContactID:(ABRecordID)record
andIsFavourite:(BOOL)isFavourite
andNotes:(NSString *)notes;
- (NSString *)fullName;

@ -10,7 +10,7 @@ static NSString *const DEFAULTS_KEY_DATE = @"DefaultsKeyDate";
@implementation Contact
@synthesize firstName, lastName, emails, image, recordID, isFavourite, notes, parsedPhoneNumbers, userTextPhoneNumbers;
@synthesize firstName, lastName, emails, image, recordID, notes, parsedPhoneNumbers, userTextPhoneNumbers;
+ (Contact*)contactWithFirstName:(NSString*)firstName
andLastName:(NSString *)lastName
@ -45,7 +45,6 @@ static NSString *const DEFAULTS_KEY_DATE = @"DefaultsKeyDate";
andEmails:(NSArray*)emails
andImage:(UIImage *)image
andContactID:(ABRecordID)record
andIsFavourite:(BOOL)isFavourite
andNotes:(NSString *)notes {
Contact* contact = [Contact contactWithFirstName:firstName
@ -54,7 +53,6 @@ static NSString *const DEFAULTS_KEY_DATE = @"DefaultsKeyDate";
andEmails:emails
andContactID:record];
contact->isFavourite = isFavourite;
contact->image = image;
contact->notes = notes;
return contact;

@ -20,7 +20,6 @@ typedef void(^ABReloadRequestCompletionBlock)(NSArray *contacts);
@private TOCFuture* futureAddressBook;
@private ObservableValueController* observableContactsController;
@private ObservableValueController* observableWhisperUsersController;
@private ObservableValueController* observableFavouritesController;
@private TOCCancelTokenSource* life;
@private NSDictionary *latestContactsById;
@private NSDictionary *latestWhisperUsersById;
@ -28,17 +27,12 @@ typedef void(^ABReloadRequestCompletionBlock)(NSArray *contacts);
-(ObservableValue *) getObservableContacts;
-(ObservableValue *) getObservableWhisperUsers;
-(ObservableValue *) getObservableFavourites;
-(NSArray*) getContactsFromAddressBook:(ABAddressBookRef)addressBook;
-(Contact*) latestContactWithRecordId:(ABRecordID)recordId;
-(Contact*) latestContactForPhoneNumber:(PhoneNumber *)phoneNumber;
-(NSArray*) latestContactsWithSearchString:(NSString *)searchString;
-(void) toggleFavourite:(Contact *)contact;
-(NSArray*) contactsForContactIds:(NSArray *)favouriteIds;
+(NSArray *)favouritesForAllContacts:(NSArray *)contacts;
-(void) addContactsToKnownWhisperUsers:(NSArray*) contacts;
+(NSDictionary *)groupContactsByFirstLetter:(NSArray *)contacts matchingSearchString:(NSString *)optionalSearchString;

@ -31,7 +31,6 @@ typedef BOOL (^ContactSearchBlock)(id, NSUInteger, BOOL*);
self = [super init];
if (self) {
newUserNotificationsEnabled = [self knownUserStoreInitialized];
_favouriteContactIds = [self loadFavouriteIds];
_knownWhisperUserIds = [self loadKnownWhisperUsers];
life = [TOCCancelTokenSource new];
observableContactsController = [ObservableValueController observableValueControllerWithInitialValue:nil];
@ -119,12 +118,6 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
- (void)setupLatestWhisperUsers:(NSArray *)users {
if (users) {
latestWhisperUsersById = [ContactsManager keyContactsById:users];
if (!observableFavouritesController) {
NSArray *favourites = [self contactsForContactIds:_favouriteContactIds];
observableFavouritesController = [ObservableValueController observableValueControllerWithInitialValue:favourites];
}
}
}
@ -138,10 +131,6 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
return observableWhisperUsersController;
}
-(ObservableValue *) getObservableFavourites {
return observableFavouritesController;
}
#pragma mark - Address Book utils
+(TOCFuture*) asyncGetAddressBook {
@ -232,19 +221,12 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
NSData *image = (__bridge_transfer NSData*)ABPersonCopyImageDataWithFormat(record, kABPersonImageFormatThumbnail);
UIImage *img = [UIImage imageWithData:image];
ContactSearchBlock searchBlock = ^BOOL(NSNumber *obj, NSUInteger idx, BOOL *stop) {
return obj.intValue == recordID;
};
NSUInteger favouriteIndex = [_favouriteContactIds indexOfObjectPassingTest:searchBlock];
return [Contact contactWithFirstName:firstName
andLastName:lastName
andUserTextPhoneNumbers:phoneNumbers
andEmails:emails
andImage:img
andContactID:recordID
andIsFavourite:favouriteIndex != NSNotFound
andNotes:notes];
}
@ -403,47 +385,6 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in
return [contacts copy];
}
#pragma mark - Favourites
-(NSMutableArray *)loadFavouriteIds {
NSArray *favourites = [NSUserDefaults.standardUserDefaults objectForKey:FAVOURITES_DEFAULT_KEY];
return favourites == nil ? [NSMutableArray array] : favourites.mutableCopy;
}
-(void)saveFavouriteIds {
[NSUserDefaults.standardUserDefaults setObject:[_favouriteContactIds copy]
forKey:FAVOURITES_DEFAULT_KEY];
[NSUserDefaults.standardUserDefaults synchronize];
[observableFavouritesController updateValue:[self contactsForContactIds:_favouriteContactIds]];
}
-(void)toggleFavourite:(Contact *)contact {
require(contact != nil);
contact.isFavourite = !contact.isFavourite;
if (contact.isFavourite) {
[_favouriteContactIds addObject:@(contact.recordID)];
} else {
ContactSearchBlock removeBlock = ^BOOL(NSNumber *favouriteNumber, NSUInteger idx, BOOL *stop) {
return [favouriteNumber integerValue] == contact.recordID;
};
NSUInteger indexToRemove = [_favouriteContactIds indexOfObjectPassingTest:removeBlock];
if (indexToRemove != NSNotFound) {
[_favouriteContactIds removeObjectAtIndex:indexToRemove];
}
}
[self saveFavouriteIds];
}
+(NSArray *)favouritesForAllContacts:(NSArray *)contacts {
return [contacts filter:^int(Contact* contact) {
return contact.isFavourite;
}];
}
#pragma mark - Whisper User Management
-(NSUInteger) checkForNewWhisperUsers {

@ -9,6 +9,18 @@
#import <Foundation/Foundation.h>
#import <SRWebSocket.h>
typedef enum : NSUInteger {
kSocketStatusOpen,
kSocketStatusClosed,
kSocketStatusConnecting,
} SocketStatus;
static void *kSocketStatusObservationContext = &kSocketStatusObservationContext;
extern NSString * const SocketOpenedNotification;
extern NSString * const SocketClosedNotification;
extern NSString * const SocketConnectingNotification;
@interface TSSocketManager : NSObject <SRWebSocketDelegate>
+ (void)becomeActive;

@ -14,11 +14,15 @@
#import <CocoaLumberjack/DDLog.h>
#define kWebSocketHeartBeat 15
#define ddLogLevel LOG_LEVEL_DEBUG
NSString * const SocketOpenedNotification = @"SocketOpenedNotification";
NSString * const SocketClosedNotification = @"SocketClosedNotification";
NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
@interface TSSocketManager ()
@property (nonatomic, retain) NSTimer *timer;
@property (nonatomic, retain) SRWebSocket *websocket;
@property (nonatomic) NSUInteger status;
@end
@implementation TSSocketManager
@ -28,6 +32,7 @@
if (self) {
self.websocket = nil;
[self addObserver:self forKeyPath:@"status" options:0 context:kSocketStatusObservationContext];
}
return self;
@ -45,18 +50,22 @@
#pragma mark - Manage Socket
+ (void)becomeActive{
SRWebSocket *socket =[[self sharedManager] websocket];
TSSocketManager *sharedInstance = [self sharedManager];
SRWebSocket *socket =[sharedInstance websocket];
if (socket) {
switch ([socket readyState]) {
case SR_OPEN:
DDLogVerbose(@"WebSocket already open on connection request");
sharedInstance.status = kSocketStatusOpen;
return;
case SR_CONNECTING:
DDLogVerbose(@"WebSocket is already connecting");
sharedInstance.status = kSocketStatusConnecting;
return;
default:
[socket close];
sharedInstance.status = kSocketStatusClosed;
socket.delegate = nil;
socket = nil;
break;
@ -81,11 +90,13 @@
- (void) webSocketDidOpen:(SRWebSocket *)webSocket{
NSLog(@"WebSocket was sucessfully opened");
self.timer = [NSTimer scheduledTimerWithTimeInterval:kWebSocketHeartBeat target:self selector:@selector(webSocketHeartBeat) userInfo:nil repeats:YES];
self.status = kSocketStatusOpen;
}
- (void) webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error{
NSLog(@"Error connecting to socket %@", error);
[self.timer invalidate];
self.status = kSocketStatusClosed;
}
- (void) webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{
@ -97,7 +108,6 @@
if (!base64message || error) {
NSLog(@"WTF?!");
return;
}
@ -105,12 +115,15 @@
NSString *ackedId = [serializedMessage objectForKey:@"id"];
[self.websocket send:[[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:@{@"type":@"1", @"id":ackedId} options:0 error:nil] encoding:NSUTF8StringEncoding]];
[self.websocket send:[[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:@{@"type":@"1", @"id":ackedId}
options:0 error:nil]
encoding:NSUTF8StringEncoding]];
}
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{
DDLogVerbose(@"WebSocket did close");
[self.timer invalidate];
self.status = kSocketStatusClosed;
}
- (void)webSocketHeartBeat{
@ -122,4 +135,29 @@
return [NSString stringWithFormat:@"?login=%@&password=%@", [[TSAccountManager registeredNumber] stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"],[TSStorageManager serverAuthToken]];
}
#pragma mark UI Delegates
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == kSocketStatusObservationContext)
{
switch (self.status) {
case kSocketStatusOpen:
[[NSNotificationCenter defaultCenter] postNotificationName:SocketOpenedNotification object:self];
break;
case kSocketStatusClosed:
[[NSNotificationCenter defaultCenter] postNotificationName:SocketClosedNotification object:self];
break;
case kSocketStatusConnecting:
[[NSNotificationCenter defaultCenter] postNotificationName:SocketConnectingNotification object:self];
break;
default:
break;
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
@end

@ -110,7 +110,7 @@ static NSString *const kContactDetailSegue = @"DetailSegue";
cell.contactName.text = [c fullName];
cell.contactPhoneNumber.text = [c.userTextPhoneNumbers firstObject];
cell.contactPhoneNumber.text = [[c parsedPhoneNumbers] firstObject];
if (c.image) {
cell.contactImageView.image = c.image;

@ -122,7 +122,6 @@ static NSString *const CONTACT_BROWSE_TABLE_CELL_IDENTIFIER = @"ContactTableView
- (void)setupContacts {
ObservableValue *observableContacts = Environment.getCurrent.contactsManager.getObservableWhisperUsers;
[observableContacts watchLatestValue:^(NSArray *latestContacts) {
_latestContacts = latestContacts;
[self onSearchOrContactChange:nil];
@ -202,7 +201,6 @@ static NSString *const CONTACT_BROWSE_TABLE_CELL_IDENTIFIER = @"ContactTableView
if (self.searchController.active) {
return 1;
} else {
NSLog(@"Sections contacts %@", latestAlphabeticalContacts);
return (NSInteger)[[latestAlphabeticalContacts allKeys] count];
}
}
@ -221,7 +219,6 @@ static NSString *const CONTACT_BROWSE_TABLE_CELL_IDENTIFIER = @"ContactTableView
reuseIdentifier:CONTACT_BROWSE_TABLE_CELL_IDENTIFIER];
}
[cell configureWithContact:[self contactForIndexPath:indexPath]];
return cell;
@ -242,7 +239,6 @@ static NSString *const CONTACT_BROWSE_TABLE_CELL_IDENTIFIER = @"ContactTableView
} else {
NSArray *contactSection = [self contactsForSectionIndex:(NSUInteger)indexPath.section];
contact = contactSection[(NSUInteger)indexPath.row];
NSLog(@"Contact: %@", contact);
}
return contact;
@ -275,9 +271,7 @@ static NSString *const CONTACT_BROWSE_TABLE_CELL_IDENTIFIER = @"ContactTableView
#pragma mark - IBAction
-(IBAction)presentDialer:(id)sender
{
-(IBAction)presentDialer:(id)sender {
DialerViewController * dialer = [DialerViewController new];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:dialer];

@ -7,13 +7,10 @@
//
#import <UIKit/UIKit.h>
#import "Socket.h"
#import "TSSocketManager.h"
@interface SignalsNavigationController : UINavigationController
@property (nonatomic, strong) Socket * socket;
@property (nonatomic, strong) UIProgressView* socketStatusView;
@end

@ -17,14 +17,8 @@
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self initializeSocketStatusBar];
_socket = [[Socket alloc]init];
[self initializeObserver];
_socket.status = kSocketStatusOpen;
}
- (void)didReceiveMemoryWarning {
@ -38,7 +32,7 @@
CGRect bar = self.navigationBar.frame;
_socketStatusView.frame = CGRectMake(0, bar.size.height-1.0f, self.view.frame.size.width, 1.0f);
_socketStatusView.progressTintColor = [UIColor greenColor];
_socketStatusView.progressTintColor = [UIColor redColor];
_socketStatusView.progress = 1.0f;
[self.navigationBar addSubview:_socketStatusView];
}
@ -66,7 +60,6 @@
-(void)socketIsConnecting
{
_socketStatusView.progressTintColor = [UIColor yellowColor];
}

@ -1,29 +0,0 @@
//
// Socket.h
// Signal
//
// Created by Dylan Bourgeois on 18/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
typedef enum : NSUInteger {
kSocketStatusOpen,
kSocketStatusClosed,
kSocketStatusConnecting,
} SocketStatus;
static void *kSocketStatusObservationContext = &kSocketStatusObservationContext;
extern NSString * const SocketOpenedNotification;
extern NSString * const SocketClosedNotification;
extern NSString * const SocketConnectingNotification;
#import <Foundation/Foundation.h>
@interface Socket : NSObject
@property (nonatomic) SocketStatus status;
@end

@ -1,52 +0,0 @@
//
// Socket.m
// Signal
//
// Created by Dylan Bourgeois on 18/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import "Socket.h"
NSString * const SocketOpenedNotification = @"SocketOpenedNotification";
NSString * const SocketClosedNotification = @"SocketClosedNotification";
NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
@implementation Socket
-(instancetype)init
{
if (self = [super init])
{
_status = kSocketStatusConnecting;
[self addObserver:self forKeyPath:@"status" options:0 context:kSocketStatusObservationContext];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == kSocketStatusObservationContext)
{
switch (self.status) {
case kSocketStatusOpen:
[[NSNotificationCenter defaultCenter] postNotificationName:SocketOpenedNotification object:self];
break;
case kSocketStatusClosed:
[[NSNotificationCenter defaultCenter] postNotificationName:SocketClosedNotification object:self];
break;
case kSocketStatusConnecting:
[[NSNotificationCenter defaultCenter] postNotificationName:SocketConnectingNotification object:self];
break;
default:
break;
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
@end

@ -12,7 +12,7 @@
#import "MessagesViewController.h"
#import "SignalsViewController.h"
#import "TSSocketManager.h"
#define CELL_HEIGHT 71.0f
#define HEADER_HEIGHT 44.0f
@ -33,15 +33,8 @@ static NSString *const kSegueIndentifier = @"showSegue";
@implementation SignalsViewController
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
delegate.signalVC = self;
}
return self;
- (void)viewDidAppear:(BOOL)animated{
[TSSocketManager becomeActive];
}
- (void)viewDidLoad {
@ -49,7 +42,6 @@ static NSString *const kSegueIndentifier = @"showSegue";
_dataArray = [DemoDataFactory data];
numberOfCells = _dataArray.count;
[self tableViewSetUp];
}
- (void)didReceiveMemoryWarning {

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6250" systemVersion="14A388a" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6250" systemVersion="14B25" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6244"/>
</dependencies>
@ -55,7 +55,7 @@
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="calling mobile ..." lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jwl-C5-rlv">
<rect key="frame" x="32" y="70" width="220" height="20.5"/>
<rect key="frame" x="32" y="70" width="220" height="21"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Thin" family="Helvetica Neue" pointSize="17"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
@ -142,6 +142,7 @@
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="NVz-Yw-lv9" firstAttribute="top" secondItem="kqa-uu-A45" secondAttribute="bottom" constant="66" id="4rD-44-F17"/>
<constraint firstAttribute="trailing" secondItem="hSe-2g-y1v" secondAttribute="trailing" constant="45" id="4sH-Qk-YRX"/>

Loading…
Cancel
Save