2
0
MasterPassword/MasterPassword/ObjC/iOS/MPUsersViewController.m

745 lines
29 KiB
Mathematica
Raw Normal View History

/**
* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
*
* See the enclosed file LICENSE for license information (LGPLv3). If you did
* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
*
* @author Maarten Billemont <lhunath@lyndir.com>
* @license http://www.gnu.org/licenses/lgpl-3.0.txt
*/
//
// MPCombinedViewController.h
// MPCombinedViewController
//
// Created by lhunath on 2014-03-08.
// Copyright, lhunath (Maarten Billemont) 2014. All rights reserved.
//
#import "MPUsersViewController.h"
#import "MPEntities.h"
#import "MPAvatarCell.h"
#import "MPiOSAppDelegate.h"
#import "MPAppDelegate_Store.h"
#import "MPAppDelegate_Key.h"
typedef NS_ENUM(NSUInteger, MPActiveUserState) {
/** The users are all inactive */
MPActiveUserStateNone,
/** The selected user is activated and being logged in with */
MPActiveUserStateLogin,
/** The selected user is activated and its user name is being asked for */
MPActiveUserStateUserName,
/** The selected user is activated and its new master password is being asked for */
MPActiveUserStateMasterPasswordChoice,
/** The selected user is activated and the confirmation of the previously entered master password is being asked for */
MPActiveUserStateMasterPasswordConfirmation,
/** The selected user is activated displayed at the top with the rest of the UI inactive */
MPActiveUserStateMinimized,
};
@interface MPUsersViewController()
@property(nonatomic) MPActiveUserState activeUserState;
@property(nonatomic, strong) NSArray *userIDs;
@property(nonatomic, strong) NSTimer *marqueeTipTimer;
@property(nonatomic, strong) NSArray *marqueeTipTexts;
@property(nonatomic) NSUInteger marqueeTipTextIndex;
@end
@implementation MPUsersViewController {
__weak id _storeObserver;
__weak id _mocObserver;
NSArray *_notificationObservers;
NSString *_masterPasswordChoice;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.marqueeTipTexts = @[
strl(@"Press and hold to change password or delete."),
strl(@"Shake for emergency generator."),
];
self.view.backgroundColor = [UIColor clearColor];
self.avatarCollectionView.allowsMultipleSelection = YES;
[self.entryField addTarget:self action:@selector(textFieldEditingChanged:) forControlEvents:UIControlEventEditingChanged];
[self setActive:YES animated:NO];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.userSelectionContainer.alpha = 0;
[self observeStore];
[self registerObservers];
[self reloadUsers];
[self.marqueeTipTimer invalidate];
self.marqueeTipTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(firedMarqueeTimer:) userInfo:nil repeats:YES];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self removeObservers];
[self stopObservingStore];
[self.marqueeTipTimer invalidate];
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
// if (motion == UIEventSubtypeMotionShake)
// [self emergencyOpenAnimated:YES];
}
#pragma mark - UITextFieldDelegate
- (void)textFieldDidEndEditing:(UITextField *)textField {
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == self.entryField) {
switch (self.activeUserState) {
case MPActiveUserStateNone: {
[textField resignFirstResponder];
break;
}
case MPActiveUserStateLogin: {
[self selectedAvatar].spinnerActive = YES;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
BOOL signedIn = NO, isNew = NO;
MPUserEntity *user = [self selectedUserInContext:context isNew:&isNew];
if (!isNew && user)
signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
usingMasterPassword:self.entryField.text];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.entryField.text = @"";
[self selectedAvatar].spinnerActive = NO;
if (!signedIn) {
// Sign in failed.
// TODO: warn user
return;
}
}];
}];
break;
}
case MPActiveUserStateUserName: {
NSString *userName = self.entryField.text;
if (![userName length]) {
// No name entered.
// TODO: warn user
return NO;
}
[self selectedAvatar].name = userName;
self.activeUserState = MPActiveUserStateMasterPasswordChoice;
break;
}
case MPActiveUserStateMasterPasswordChoice: {
NSString *masterPassword = self.entryField.text;
if (![masterPassword length]) {
// No password entered.
// TODO: warn user
return NO;
}
self.activeUserState = MPActiveUserStateMasterPasswordConfirmation;
break;
}
case MPActiveUserStateMasterPasswordConfirmation: {
NSString *masterPassword = self.entryField.text;
if (![masterPassword length]) {
// No password entered.
// TODO: warn user
return NO;
}
if (![masterPassword isEqualToString:_masterPasswordChoice]) {
// Master password confirmation failed.
// TODO: warn user
self.activeUserState = MPActiveUserStateMasterPasswordChoice;
return NO;
}
[self selectedAvatar].spinnerActive = YES;
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
BOOL isNew = NO;
MPUserEntity *user = [self selectedUserInContext:context isNew:&isNew];
if (isNew) {
user = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
inManagedObjectContext:context];
MPAvatarCell *avatarCell = [self selectedAvatar];
user.avatar = avatarCell.avatar;
user.name = avatarCell.name;
}
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context usingMasterPassword:masterPassword];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.entryField.text = @"";
[self selectedAvatar].spinnerActive = NO;
if (!signedIn) {
// Sign in failed, shouldn't happen for a new user.
// TODO: warn user
self.activeUserState = MPActiveUserStateNone;
return;
}
}];
}];
break;
}
case MPActiveUserStateMinimized: {
[textField resignFirstResponder];
break;
}
}
}
return NO;
}
// This isn't really in UITextFieldDelegate. We fake it from UITextFieldTextDidChangeNotification.
- (void)textFieldEditingChanged:(UITextField *)textField {
if (textField == self.entryField) {
switch (self.activeUserState) {
case MPActiveUserStateNone:
break;
case MPActiveUserStateLogin:
break;
case MPActiveUserStateUserName: {
NSString *userName = self.entryField.text;
[self selectedAvatar].name = [userName length]? userName: strl( @"New User" );
break;
}
case MPActiveUserStateMasterPasswordChoice:
break;
case MPActiveUserStateMasterPasswordConfirmation:
break;
case MPActiveUserStateMinimized:
break;
}
}
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (collectionView == self.avatarCollectionView)
return [self.userIDs count] + 1;
Throw(@"unexpected collection view: %@", collectionView);
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.avatarCollectionView) {
MPAvatarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[MPAvatarCell reuseIdentifier] forIndexPath:indexPath];
[cell addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(didLongPress:)]];
[self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO];
[self updateModeForAvatar:cell atIndexPath:indexPath animated:NO];
BOOL isNew = NO;
MPUserEntity *user = [self userForIndexPath:indexPath inContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]
isNew:&isNew];
if (isNew) {
// New User
cell.avatar = MPAvatarAdd;
cell.name = strl( @"New User" );
}
else {
// Existing User
cell.avatar = user.avatar;
cell.name = user.name;
}
return cell;
}
Throw(@"unexpected collection view: %@", collectionView);
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.avatarCollectionView) {
[self.avatarCollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
animated:YES];
// Deselect all other cells.
for (NSUInteger otherItem = 0; otherItem < [collectionView numberOfItemsInSection:indexPath.section]; ++otherItem)
if (otherItem != indexPath.item) {
NSIndexPath *otherIndexPath = [NSIndexPath indexPathForItem:otherItem inSection:indexPath.section];
[collectionView deselectItemAtIndexPath:otherIndexPath animated:YES];
}
BOOL isNew = NO;
MPUserEntity *user = [self userForIndexPath:indexPath inContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]
isNew:&isNew];
if (isNew)
self.activeUserState = MPActiveUserStateUserName;
else if (!user.keyID)
self.activeUserState = MPActiveUserStateMasterPasswordChoice;
else
self.activeUserState = MPActiveUserStateLogin;
}
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
if (collectionView == self.avatarCollectionView) {
self.activeUserState = MPActiveUserStateNone;
}
}
#pragma mark - UILongPressGestureRecognizer
- (void)didLongPress:(UILongPressGestureRecognizer *)recognizer {
if ([recognizer.view isKindOfClass:[MPAvatarCell class]]) {
if (recognizer.state != UIGestureRecognizerStateBegan)
// Don't show the action menu unless the state is Began.
return;
MPAvatarCell *avatarCell = (MPAvatarCell *)recognizer.view;
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
BOOL isNew = NO;
MPUserEntity *user = [self userForAvatar:avatarCell inContext:mainContext isNew:&isNew];
NSManagedObjectID *userID = user.objectID;
if (isNew || !user)
return;
[PearlSheet showSheetWithTitle:user.name
viewStyle:UIActionSheetStyleBlackTranslucent
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
if (buttonIndex == [sheet cancelButtonIndex])
return;
if (buttonIndex == [sheet destructiveButtonIndex]) {
// Delete User
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSManagedObject *user_ = [context existingObjectWithID:userID error:NULL];
if (user_) {
[context deleteObject:user_];
[context saveToStore];
}
}];
return;
}
if (buttonIndex == [sheet firstOtherButtonIndex])
// Reset Password
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPUserEntity *user_ = (MPUserEntity *)[context existingObjectWithID:userID error:NULL];
if (user_)
[[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{
dbg(@"changing mp for user: %@, keyID: %@", user_.name, user_.keyID);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell];
[self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO
scrollPosition:UICollectionViewScrollPositionNone];
[self collectionView:self.avatarCollectionView didSelectItemAtIndexPath:avatarIndexPath];
}];
}];
}];
} cancelTitle:[PearlStrings get].commonButtonCancel
destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil];
}
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset {
if (scrollView == self.avatarCollectionView) {
CGPoint offsetToCenter = CGPointMake(
self.avatarCollectionView.bounds.size.width / 2,
self.avatarCollectionView.bounds.size.height / 2 );
NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForItemAtPoint:
CGPointPlusCGPoint( *targetContentOffset, offsetToCenter )];
CGPoint targetCenter = [self.avatarCollectionView layoutAttributesForItemAtIndexPath:avatarIndexPath].center;
*targetContentOffset = CGPointMinusCGPoint( targetCenter, offsetToCenter );
NSAssert([self.avatarCollectionView indexPathForItemAtPoint:targetCenter].item == avatarIndexPath.item, @"should be same item");
}
}
#pragma mark - Private
- (void)firedMarqueeTimer:(NSTimer *)timer {
[UIView animateWithDuration:0.5 animations:^{
self.hintLabel.alpha = 0;
} completion:^(BOOL finished) {
if (!finished)
return;
self.hintLabel.text = self.marqueeTipTexts[++self.marqueeTipTextIndex % [self.marqueeTipTexts count]];
[UIView animateWithDuration:0.5 animations:^{
self.hintLabel.alpha = 1;
}];
}];
}
- (MPAvatarCell *)selectedAvatar {
NSArray *selectedIndexPaths = self.avatarCollectionView.indexPathsForSelectedItems;
if (![selectedIndexPaths count]) {
// No selected user.
return nil;
}
return (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:selectedIndexPaths.firstObject];
}
- (MPUserEntity *)selectedUserInContext:(NSManagedObjectContext *)context isNew:(BOOL *)isNew {
MPAvatarCell *selectedAvatar = [self selectedAvatar];
if (!selectedAvatar) {
// No selected user.
*isNew = NO;
return nil;
}
return [self userForAvatar:selectedAvatar inContext:context isNew:isNew];
}
- (MPUserEntity *)userForAvatar:(MPAvatarCell *)cell inContext:(NSManagedObjectContext *)context isNew:(BOOL *)isNew {
return [self userForIndexPath:[self.avatarCollectionView indexPathForCell:cell] inContext:context isNew:isNew];
}
- (MPUserEntity *)userForIndexPath:(NSIndexPath *)indexPath inContext:(NSManagedObjectContext *)context isNew:(BOOL *)isNew {
if ((*isNew = indexPath.item >= [self.userIDs count]))
return nil;
NSError *error = nil;
MPUserEntity *user = (MPUserEntity *)[context existingObjectWithID:self.userIDs[indexPath.item] error:&error];
if (error)
wrn(@"Failed to load user into context: %@", error);
return user;
}
- (void)updateAvatars {
for (NSIndexPath *indexPath in self.avatarCollectionView.indexPathsForVisibleItems)
[self updateAvatarAtIndexPath:indexPath];
}
- (void)updateAvatarAtIndexPath:(NSIndexPath *)indexPath {
MPAvatarCell *cell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath];
[self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO];
[self updateModeForAvatar:cell atIndexPath:indexPath animated:NO];
}
- (void)updateVisibilityForAvatar:(MPAvatarCell *)cell atIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated {
CGFloat current = [self.avatarCollectionView layoutAttributesForItemAtIndexPath:indexPath].center.x -
self.avatarCollectionView.contentOffset.x;
CGFloat max = self.avatarCollectionView.bounds.size.width;
[cell setVisibility:MAX(0, MIN( 1, 1 - ABS( current / (max / 2) - 1 ) )) animated:animated];
}
- (void)updateModeForAvatar:(MPAvatarCell *)avatarCell atIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated {
switch (self.activeUserState) {
case MPActiveUserStateNone: {
[self.avatarCollectionView deselectItemAtIndexPath:indexPath animated:YES];
[avatarCell setMode:MPAvatarModeLowered animated:animated];
break;
}
case MPActiveUserStateLogin:
case MPActiveUserStateUserName:
case MPActiveUserStateMasterPasswordChoice:
case MPActiveUserStateMasterPasswordConfirmation: {
if ([self.avatarCollectionView.indexPathsForSelectedItems containsObject:indexPath])
[avatarCell setMode:MPAvatarModeRaisedAndActive animated:animated];
else
[avatarCell setMode:MPAvatarModeRaisedButInactive animated:animated];
break;
}
case MPActiveUserStateMinimized: {
if ([self.avatarCollectionView.indexPathsForSelectedItems containsObject:indexPath])
[avatarCell setMode:MPAvatarModeRaisedAndMinimized animated:animated];
else
[avatarCell setMode:MPAvatarModeRaisedAndHidden animated:animated];
break;
}
}
}
- (void)registerObservers {
if ([_notificationObservers count])
return;
Weakify(self);
_notificationObservers = @[
[[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationWillResignActiveNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
// [self emergencyCloseAnimated:NO];
self.userSelectionContainer.alpha = 0;
}],
[[NSNotificationCenter defaultCenter]
addObserverForName:UIApplicationDidBecomeActiveNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
[self reloadUsers];
[UIView animateWithDuration:1 animations:^{
self.userSelectionContainer.alpha = 1;
}];
}],
];
[self observeKeyPath:@"avatarCollectionView.contentOffset" withBlock:
^(id from, id to, NSKeyValueChange cause, MPUsersViewController *_self) {
[_self updateAvatars];
}];
}
- (void)removeObservers {
for (id observer in _notificationObservers)
[[NSNotificationCenter defaultCenter] removeObserver:observer];
_notificationObservers = nil;
[self removeKeyPathObservers];
}
- (void)observeStore {
Weakify(self);
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
if (!_mocObserver && mainContext)
_mocObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextObjectsDidChangeNotification object:mainContext
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
[self reloadUsers];
}];
if (!_storeObserver)
_storeObserver = [[NSNotificationCenter defaultCenter]
addObserverForName:USMStoreDidChangeNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Strongify(self);
[self reloadUsers];
}];
}
- (void)stopObservingStore {
if (_mocObserver)
[[NSNotificationCenter defaultCenter] removeObserver:_mocObserver];
if (_storeObserver)
[[NSNotificationCenter defaultCenter] removeObserver:_storeObserver];
}
- (void)reloadUsers {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
NSArray *users = [context executeFetchRequest:fetchRequest error:&error];
if (!users) {
err(@"Failed to load users: %@", error);
self.userIDs = nil;
}
NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]];
for (MPUserEntity *user in users)
[userIDs addObject:user.objectID];
self.userIDs = userIDs;
}];
}
#pragma mark - Properties
- (void)setActive:(BOOL)active {
[self setActive:active animated:YES];
}
- (void)setActive:(BOOL)active animated:(BOOL)animated {
_active = active;
dbg(@"active -> %d", active);
if (active)
[self setActiveUserState:MPActiveUserStateNone animated:animated];
else
[self setActiveUserState:MPActiveUserStateMinimized animated:animated];
}
- (void)setUserIDs:(NSArray *)userIDs {
_userIDs = userIDs;
dbg(@"userIDs -> %lu", (unsigned long)[userIDs count]);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.avatarCollectionView reloadData];
[UIView animateWithDuration:0.3f animations:^{
self.userSelectionContainer.alpha = 1;
}];
}];
}
- (void)setActiveUserState:(MPActiveUserState)activeUserState {
[self setActiveUserState:activeUserState animated:YES];
}
- (void)setActiveUserState:(MPActiveUserState)activeUserState animated:(BOOL)animated {
_activeUserState = activeUserState;
_masterPasswordChoice = nil;
if (activeUserState != MPActiveUserStateMinimized && [MPiOSAppDelegate get].key) {
[[MPiOSAppDelegate get] signOutAnimated:YES];
return;
}
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
MPAvatarCell *selectedAvatar = [self selectedAvatar];
// Set avatar modes.
for (NSUInteger item = 0; item < [self.avatarCollectionView numberOfItemsInSection:0]; ++item) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:0];
MPAvatarCell *avatarCell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath];
[self updateModeForAvatar:avatarCell atIndexPath:indexPath animated:NO];
if (selectedAvatar && avatarCell == selectedAvatar)
[self.avatarCollectionView scrollToItemAtIndexPath:indexPath
atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
}
// Set the entry container's contents.
switch (activeUserState) {
case MPActiveUserStateNone:
dbg(@"activeUserState -> none");
break;
case MPActiveUserStateLogin: {
dbg(@"activeUserState -> login");
self.entryLabel.text = strl( @"Enter your master password:" );
self.entryField.text = nil;
self.entryField.secureTextEntry = YES;
break;
}
case MPActiveUserStateUserName: {
dbg(@"activeUserState -> userName");
self.entryLabel.text = strl( @"Enter your full name:" );
self.entryField.text = nil;
self.entryField.secureTextEntry = NO;
break;
}
case MPActiveUserStateMasterPasswordChoice: {
dbg(@"activeUserState -> masterPasswordChoice");
self.entryLabel.text = strl( @"Choose your master password:" );
self.entryField.text = nil;
self.entryField.secureTextEntry = YES;
break;
}
case MPActiveUserStateMasterPasswordConfirmation: {
dbg(@"activeUserState -> masterPasswordConfirmation");
_masterPasswordChoice = self.entryField.text;
self.entryLabel.text = strl( @"Confirm your master password:" );
self.entryField.text = nil;
self.entryField.secureTextEntry = YES;
break;
}
case MPActiveUserStateMinimized:
dbg(@"activeUserState -> minimized");
break;
}
// Manage the random avatar for the new user if selected.
if (selectedAvatar.avatar == MPAvatarAdd)
selectedAvatar.avatar = arc4random() % MPAvatarCount;
else {
NSIndexPath *newUserIndexPath = [NSIndexPath indexPathForItem:[_userIDs count] inSection:0];
MPAvatarCell *newUserAvatar = (MPAvatarCell *)[[self avatarCollectionView] cellForItemAtIndexPath:newUserIndexPath];
newUserAvatar.avatar = MPAvatarAdd;
newUserAvatar.name = strl( @"New User" );
}
// Manage the entry container depending on whether a user is activate or not.
switch (activeUserState) {
case MPActiveUserStateNone: {
self.navigationBarToTopConstraint.priority = UILayoutPriorityDefaultHigh;
self.avatarCollectionCenterConstraint.priority = UILayoutPriorityDefaultHigh;
self.avatarCollectionView.scrollEnabled = YES;
self.entryContainer.alpha = 0;
self.footerContainer.alpha = 1;
break;
}
case MPActiveUserStateLogin:
case MPActiveUserStateUserName:
case MPActiveUserStateMasterPasswordChoice:
case MPActiveUserStateMasterPasswordConfirmation: {
self.navigationBarToTopConstraint.priority = UILayoutPriorityDefaultHigh;
self.avatarCollectionCenterConstraint.priority = UILayoutPriorityDefaultLow;
self.avatarCollectionView.scrollEnabled = NO;
self.entryContainer.alpha = 1;
self.footerContainer.alpha = 1;
break;
}
case MPActiveUserStateMinimized: {
self.navigationBarToTopConstraint.priority = 1;
self.avatarCollectionCenterConstraint.priority = UILayoutPriorityDefaultLow;
self.avatarCollectionView.scrollEnabled = NO;
self.entryContainer.alpha = 0;
self.footerContainer.alpha = 0;
break;
}
}
[self.navigationBarToTopConstraint apply];
[self.avatarCollectionCenterConstraint apply];
// Toggle the keyboard.
if (!self.entryContainer.alpha)
[self.entryField resignFirstResponder];
} completion:^(BOOL finished) {
if (finished && self.entryContainer.alpha)
[self.entryField becomeFirstResponder];
}];
}
#pragma mark - Actions
- (IBAction)doSignOut:(UIBarButtonItem *)sender {
[[MPiOSAppDelegate get] signOutAnimated:YES];
}
@end