// _____ _
// |_ _| |_ _ _ ___ ___ _ __ __ _
// | | | ' \| '_/ -_) -_) ' \/ _` |_
// |_| |_||_|_| \___\___|_|_|_\__,_(_)
//
// Threema iOS Client
// Copyright (c) 2014-2020 Threema GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License, version 3,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#import "BallotVoteViewController.h"
#import "Contact.h"
#import "BallotVoteTableCell.h"
#import "BallotChoice.h"
#import "BallotResult.h"
#import "MyIdentityStore.h"
#import "EntityManager.h"
#import "Ballot.h"
#import "MessageSender.h"
#import "BallotManager.h"
#import "RectUtil.h"
#import "BallotHeaderView.h"
#import "BallotCreateViewController.h"
#import "BallotResultViewController.h"
#import "UIImage+ColoredImage.h"
#import "PermissionChecker.h"
#import "NibUtil.h"
#define BALLOT_VOTE_TABLE_CELL_ID @"BallotVoteTableCellId"
#define BALLOT_CLOSE_ACK_MESSAGE NSLocalizedStringFromTable(@"ballot_close_ack", @"Ballot", nil)
@interface BallotVoteViewController ()
@property NSArray *choices;
@property EntityManager *entityManager;
@property BallotManager *ballotManager;
@property Ballot *ballot;
@property BallotHeaderView *headerView;
@end
@implementation BallotVoteViewController
+ (instancetype) ballotVoteViewControllerForBallot:(Ballot *)ballot {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Ballot" bundle:nil];
BallotVoteViewController *viewController = (BallotVoteViewController *) [storyboard instantiateViewControllerWithIdentifier:@"BallotVoteViewController"];
EntityManager *entityManager = [[EntityManager alloc] init];
viewController.entityManager = entityManager;
viewController.ballotManager = [BallotManager ballotManagerWithEntityManager: entityManager];
viewController.ballot = (Ballot *)[entityManager.entityFetcher getManagedObjectById:ballot.objectID];
return viewController;
}
-(void)viewWillLayoutSubviews {
if (_adminView.hidden && !_summaryView.hidden) {
_summaryView.frame = [RectUtil setYPositionOf:_summaryView.frame y:CGRectGetMaxY(self.view.frame) - CGRectGetHeight(_summaryView.frame)];
}
CGFloat top = self.topLayoutGuide.length;
_headerPlaceholderView.frame = [RectUtil setYPositionOf:_headerPlaceholderView.frame y:top];
_choiceTableView.frame = [RectUtil setYPositionOf:_choiceTableView.frame y:CGRectGetMaxY(_headerPlaceholderView.frame)];
CGFloat height;
if (_summaryView.hidden) {
height = self.view.bounds.size.height - CGRectGetMaxY(_headerPlaceholderView.frame);
} else {
height = CGRectGetMinY(_summaryView.frame) - CGRectGetMaxY(_headerPlaceholderView.frame);
}
_choiceTableView.frame = [RectUtil setHeightOf:_choiceTableView.frame height:height];
}
- (void)viewWillAppear:(BOOL)animated {
[self updateContent];
if (animated) {
[_headerView bounceDetailView];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
_choiceTableView.delegate = self;
_choiceTableView.dataSource = self;
_cancelButton.target = self;
_cancelButton.action = @selector(cancelPressed);
_voteButton.target = self;
_voteButton.action = @selector(votePressed);
_adminView.hidden = ![_ballot canEdit];
_summaryView.hidden = ![_ballot isIntermediate] && ![_ballot canEdit];
if ([_ballot isIntermediate]) {
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(resultsTapped)];
[_summaryView addGestureRecognizer:tapGesture];
_summaryView.accessibilityTraits = UIAccessibilityTraitButton;
_summaryView.isAccessibilityElement = YES;
_detailsImage.hidden = NO;
UIImage *tmpImage = [UIImage imageNamed:@"ArrowNext"];
_detailsImage.image = [tmpImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
_detailsImage.tintColor = [UIColor whiteColor];
} else {
_detailsImage.hidden = YES;
}
[_ballotCloseButton addTarget:self action:@selector(ballotClosePressed) forControlEvents:UIControlEventTouchUpInside];
[_ballotEditButton addTarget:self action:@selector(ballotEditPressed) forControlEvents:UIControlEventTouchUpInside];
_headerView = (BallotHeaderView *)[NibUtil loadViewFromNibWithName:@"BallotHeaderView"];
_headerView.ballot = _ballot;
_headerView.frame = _headerPlaceholderView.bounds;
[_headerPlaceholderView addSubview: _headerView];
_voteButton.title = NSLocalizedStringFromTable(@"ballot_vote", @"Ballot", nil);
[_ballotCloseButton setTitle:NSLocalizedStringFromTable(@"ballot_close", @"Ballot", nil) forState:UIControlStateNormal];
[self setupColors];
}
- (void)setupColors {
self.view.backgroundColor = [Colors background];
_adminView.backgroundColor = [Colors backgroundDark];
_summaryView.backgroundColor = [Colors backgroundInverted];
_countVotesLabel.textColor = [Colors white];
_choiceTableView.backgroundColor = [Colors background];
[_ballotCloseButton setBackgroundColor:[Colors background]];
[_ballotCloseButton setTitleColor:[Colors main] forState:UIControlStateNormal];
_headerView.backgroundColor = [Colors background];
}
- (void)updateContent {
_choices = [_ballot choicesSortedByOrder];
[self setTitle:NSLocalizedStringFromTable(@"ballot", @"Ballot", nil)];
if (_ballot.conversation == nil) {
_voteButton.enabled = NO;
}
if (![[PermissionChecker permissionChecker] canSendIn:_ballot.conversation entityManager: nil]) {
_voteButton.enabled = NO;
}
NSString *messageFormat = NSLocalizedStringFromTable(@"ballot_got_votes_count", @"Ballot", nil);
NSInteger countParticipants = _ballot.participantCount;
NSInteger countVotes = _ballot.numberOfReceivedVotes;
NSString *message = [NSString stringWithFormat:messageFormat, countVotes, countParticipants];
_countVotesLabel.text = message;
_summaryView.accessibilityValue = message;
}
- (NSString *)choiceTextAt:(NSInteger)index {
BallotChoice *choice = [_choices objectAtIndex: index];
return choice.name;
}
- (NSString *)voteCountTextAt:(NSInteger)index {
BallotChoice *choice = [_choices objectAtIndex: index];
NSInteger count = [choice totalCountOfResultsTrue];
return [NSString stringWithFormat:@"%li", (long)count];
}
- (void)setResult:(BOOL)value forChoiceAt:(NSInteger)index {
if ([_ballot isMultipleChoice] == NO) {
[self resetAllValues];
}
BallotChoice *choice = [_choices objectAtIndex:index];
[_ballotManager updateChoice:choice withOwnResult: [NSNumber numberWithBool:value]];
[self updateTable];
}
- (BOOL)resultForChoiceAt:(NSInteger)index {
BallotChoice *choice = [_choices objectAtIndex:index];
BallotResult *result = [choice getOwnResult];
if (result) {
return [result boolValue];
}
return NO;
}
- (void)resetAllValues {
for (BallotChoice *choice in _choices) {
[_ballotManager updateChoice:choice withOwnResult: [NSNumber numberWithBool: NO]];
}
}
- (void)updateTable {
for (NSInteger i=0; i<[_choices count]; i++) {
NSIndexPath *index = [NSIndexPath indexPathForRow:i inSection:0];
BallotVoteTableCell *cell = (BallotVoteTableCell *)[_choiceTableView cellForRowAtIndexPath: index];
[self updateCheckmarkForCell:cell atIndexPath: index];
[self updateAccessabilityLabelForCell:cell];
}
}
- (void)updateCheckmarkForCell:(BallotVoteTableCell *)cell atIndexPath:(NSIndexPath *) indexPath {
BOOL selected = [self resultForChoiceAt:indexPath.row];
cell.checkmarkView.hidden = !selected;
}
- (void)checkBallotClosed {
if ([_ballot isClosed]) {
_voteButton.enabled = NO;
_choiceTableView.userInteractionEnabled = NO;
NSString *title = NSLocalizedStringFromTable(@"ballot_vote_ballot_closed_title", @"Ballot", nil);
NSString *message = NSLocalizedStringFromTable(@"ballot_vote_ballot_closed_message", @"Ballot", nil);
[UIAlertTemplate showAlertWithOwner:self title:title message:message actionOk:nil];
}
}
- (void)setDefaultResultsIfMissing {
for (BallotChoice *choice in [_ballot choices]) {
if ([choice getOwnResult] == nil) {
// add default value
[_ballotManager updateChoice:choice withOwnResult:[NSNumber numberWithBool:NO]];
}
}
}
- (void)showBallotCloseAcknowledgeAlert {
NSString *title = NSLocalizedStringFromTable(@"ballot_close", @"Ballot", nil);
NSString *message = BALLOT_CLOSE_ACK_MESSAGE;
UIAlertController *ackAlert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
[ackAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ok", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
[self closeBallot];
}]];
[ackAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
}]];
[self presentViewController:ackAlert animated:YES completion:nil];
}
- (void)closeBallot {
[_ballot setClosed];
[_entityManager performSyncBlockAndSafe:nil];
[MessageSender sendCreateMessageForBallot:_ballot];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - button actions
- (void) votePressed {
[self setDefaultResultsIfMissing];
_ballot.modifyDate = [NSDate date];
[_entityManager performSyncBlockAndSafe:nil];
[MessageSender sendBallotVoteMessage:_ballot];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
- (void) ballotClosePressed {
[self showBallotCloseAcknowledgeAlert];
}
- (void)resultsTapped {
BallotResultViewController *viewController = [BallotResultViewController ballotResultViewControllerForBallot: _ballot];
[self.navigationController pushViewController:viewController animated:YES];
}
- (void) ballotEditPressed {
BallotCreateViewController *viewController = [BallotCreateViewController ballotCreateViewControllerForBallot: _ballot];
[self.navigationController pushViewController:viewController animated:YES];
}
- (void) cancelPressed {
[_entityManager rollback];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - table view data source
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[Colors updateTableViewCell:cell];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 0.001;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_choices count];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *title = [self choiceTextAt:indexPath.row];
CGRect rect = CGRectMake(0.0, 0.0, CGRectGetWidth(_choiceTableView.frame), CGFLOAT_MAX);
CGFloat height = [BallotVoteTableCell calculateHeightFor:title inFrame:rect];
return height;
}
- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *title = [self choiceTextAt:indexPath.row];
BallotVoteTableCell *cell = [tableView dequeueReusableCellWithIdentifier: BALLOT_VOTE_TABLE_CELL_ID];
if (cell == nil) {
cell = [[BallotVoteTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: BALLOT_VOTE_TABLE_CELL_ID];
}
[cell.choiceLabel setText: title];
[cell.choiceLabel sizeToFit];
cell.frame = [RectUtil setHeightOf:cell.frame height:cell.choiceLabel.bounds.size.height];
[self updateCheckmarkForCell:cell atIndexPath: indexPath];
if ([_ballot isIntermediate]) {
NSString *voteCount = [self voteCountTextAt:indexPath.row];
[cell.voteCountLabel setText: voteCount];
} else {
cell.voteCountLabel.hidden = YES;
}
[self updateAccessabilityLabelForCell:cell];
return cell;
}
- (void)updateAccessabilityLabelForCell:(BallotVoteTableCell *)cell {
NSString *votesCountFormat = NSLocalizedStringFromTable(@"ballot_votes_count", @"Ballot", nil);
NSString *selected = cell.checkmarkView.hidden ? NSLocalizedStringFromTable(@"ballot_vote_not_selected", @"Ballot", nil) : NSLocalizedStringFromTable(@"ballot_vote_selected", @"Ballot", nil);
if (_ballot.isIntermediate) {
NSString *votesCount = [NSString stringWithFormat:votesCountFormat, cell.voteCountLabel.text];
cell.accessibilityLabel = [NSString stringWithFormat:@"%@, %@, %@", cell.choiceLabel.text, votesCount, selected];
} else {
cell.accessibilityLabel = [NSString stringWithFormat:@"%@, %@", cell.choiceLabel.text, selected];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
BOOL toggleValue = [self resultForChoiceAt:indexPath.row];
[self setResult:!toggleValue forChoiceAt:indexPath.row];
[tableView deselectRowAtIndexPath:indexPath animated: YES];
}
@end