Commit c9e6905d authored by sczs's avatar sczs Committed by Commit Bot

[ios] Adds Collapsed functionality to TableViewModel

Bug: 819462
Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: I364154d00b6fb138a7b1b85240b625984e36fee5
Reviewed-on: https://chromium-review.googlesource.com/961492
Commit-Queue: Sergio Collazos <sczs@chromium.org>
Reviewed-by: default avatarRohit Rao <rohitrao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#543514}
parent 3389b742
......@@ -14,6 +14,7 @@ source_set("table_view") {
]
deps = [
":styler",
"//base",
"//ios/chrome/app/strings",
"//ios/chrome/browser/ui/list_model",
"//ui/base",
......@@ -36,6 +37,7 @@ source_set("unit_tests") {
testonly = true
sources = [
"chrome_table_view_controller_unittest.mm",
"table_view_model_unittest.mm",
]
deps = [
":table_view",
......
......@@ -11,9 +11,27 @@
#import "ios/chrome/browser/ui/table_view/cells/table_view_header_footer_item.h"
#import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
// Key for saving collapsed state in the UserDefaults.
extern NSString* const kTableViewModelCollapsedKey;
// TableViewModel acts as a model class for table view controllers.
@interface TableViewModel<__covariant ObjectType : TableViewItem*>
: ListModel<ObjectType, TableViewHeaderFooterItem*>
@interface TableViewModel<__covariant ObjectType : TableViewItem*> :
ListModel<ObjectType, TableViewHeaderFooterItem*>
// Sets an existing |sectionIdentifier| |collapsedKey| to be used when
// collapsing or expanding a section. |collapsedKey| is a unique identifier for
// each section that will be used for persisting information about the collapsed
// state of a section. A |collapsedKey| its only needed when
// collapsing/expanding sections. You can't collapse/expand any sections without
// a |collapsedKey|.
- (void)setSectionIdentifier:(NSInteger)sectionIdentifier
collapsedKey:(NSString*)collapsedKey;
// Sets the state of an existing |sectionIdentifier| to |collapsed|. A
// collapsedKey has to be previously set or this method will DCHECK().
- (void)setSection:(NSInteger)sectionIdentifier collapsed:(BOOL)collapsed;
// Returns YES if |sectionIdentifier| is collapsed. If not collapsedKey has been
// set it will also return NO.
- (BOOL)sectionIsCollapsed:(NSInteger)sectionIdentifier;
@end
......
......@@ -4,9 +4,84 @@
#import "ios/chrome/browser/ui/table_view/table_view_model.h"
#include "base/logging.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
NSString* const kTableViewModelCollapsedKey =
@"ChromeTableViewModelCollapsedSections";
@interface TableViewModel ()
@property(strong, nonatomic) NSMutableDictionary* collapsedKeys;
@end
@implementation TableViewModel
@synthesize collapsedKeys = _collapsedKeys;
#pragma mark - UITableViewDataSource
// Override numberOfItemsInSection to return 0 if the section is collapsed.
- (NSInteger)numberOfItemsInSection:(NSInteger)section {
DCHECK_LT(section, [self numberOfSections]);
NSInteger sectionIdentifier = [self sectionIdentifierForSection:section];
// Check if the sectionType is collapsed. If sectionType is collapsed
// return 0.
if ([self sectionIsCollapsed:sectionIdentifier]) {
return 0;
} else {
return [super numberOfItemsInSection:section];
}
}
#pragma mark - Collapsing methods.
- (void)setSectionIdentifier:(NSInteger)sectionIdentifier
collapsedKey:(NSString*)collapsedKey {
// Check that the sectionIdentifier exists.
DCHECK([self hasSectionForSectionIdentifier:sectionIdentifier]);
// Check that the collapsedKey is not being used already.
DCHECK(![self.collapsedKeys objectForKey:collapsedKey]);
[self.collapsedKeys setObject:collapsedKey forKey:@(sectionIdentifier)];
}
- (void)setSection:(NSInteger)sectionIdentifier collapsed:(BOOL)collapsed {
// TODO(crbug.com/419346): Store in the browser state preference instead of
// NSUserDefaults.
DCHECK([self hasSectionForSectionIdentifier:sectionIdentifier]);
NSString* sectionKey = [self.collapsedKeys objectForKey:@(sectionIdentifier)];
DCHECK(sectionKey);
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
NSDictionary* collapsedSections =
[defaults dictionaryForKey:kTableViewModelCollapsedKey];
NSMutableDictionary* newCollapsedSection =
[NSMutableDictionary dictionaryWithDictionary:collapsedSections];
NSNumber* value = [NSNumber numberWithBool:collapsed];
[newCollapsedSection setValue:value forKey:sectionKey];
[defaults setObject:newCollapsedSection forKey:kTableViewModelCollapsedKey];
}
- (BOOL)sectionIsCollapsed:(NSInteger)sectionIdentifier {
// TODO(crbug.com/419346): Store in the profile's preference instead of the
// NSUserDefaults.
DCHECK([self hasSectionForSectionIdentifier:sectionIdentifier]);
NSString* sectionKey = [self.collapsedKeys objectForKey:@(sectionIdentifier)];
if (!sectionKey)
return NO;
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
NSDictionary* collapsedSections =
[defaults dictionaryForKey:kTableViewModelCollapsedKey];
NSNumber* value = (NSNumber*)[collapsedSections valueForKey:sectionKey];
return [value boolValue];
}
// |self.collapsedKeys| lazy instantiation.
- (NSMutableDictionary*)collapsedKeys {
if (!_collapsedKeys) {
_collapsedKeys = [[NSMutableDictionary alloc] init];
}
return _collapsedKeys;
}
@end
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/table_view/table_view_model.h"
#import "ios/chrome/browser/ui/table_view/cells/table_view_header_footer_item.h"
#import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
#include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
typedef NS_ENUM(NSInteger, SectionIdentifier) {
SectionIdentifierFoo = kSectionIdentifierEnumZero,
SectionIdentifierBar,
};
typedef NS_ENUM(NSInteger, ItemType) {
ItemTypeFooBar = kItemTypeEnumZero,
};
class TableViewModelTest : public PlatformTest {
protected:
TableViewModelTest() {
// Need to clean up NSUserDefaults before and after each test.
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:nil forKey:kTableViewModelCollapsedKey];
model = [[TableViewModel alloc] init];
[model addSectionWithIdentifier:SectionIdentifierFoo];
[model setSectionIdentifier:SectionIdentifierFoo collapsedKey:@"FooKey"];
TableViewHeaderFooterItem* header =
[[TableViewHeaderFooterItem alloc] initWithType:ItemTypeFooBar];
TableViewItem* item = [[TableViewItem alloc] initWithType:ItemTypeFooBar];
[model setHeader:header forSectionWithIdentifier:SectionIdentifierFoo];
[model addItem:item toSectionWithIdentifier:SectionIdentifierFoo];
[model addSectionWithIdentifier:SectionIdentifierBar];
[model setSectionIdentifier:SectionIdentifierBar collapsedKey:@"BarKey"];
header = [[TableViewHeaderFooterItem alloc] initWithType:ItemTypeFooBar];
item = [[TableViewItem alloc] initWithType:ItemTypeFooBar];
[model setHeader:header forSectionWithIdentifier:SectionIdentifierBar];
[model addItem:item toSectionWithIdentifier:SectionIdentifierBar];
}
~TableViewModelTest() override {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:nil forKey:kTableViewModelCollapsedKey];
}
TableViewModel* model;
};
// Tests the default collapsed value is NO.
TEST_F(TableViewModelTest, DefaultCollapsedSectionValue) {
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierFoo]);
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierBar]);
}
// Collapses all sections.
TEST_F(TableViewModelTest, SetAllCollapsed) {
[model setSection:SectionIdentifierFoo collapsed:YES];
[model setSection:SectionIdentifierBar collapsed:YES];
EXPECT_TRUE([model sectionIsCollapsed:SectionIdentifierFoo]);
EXPECT_TRUE([model sectionIsCollapsed:SectionIdentifierBar]);
[model setSection:SectionIdentifierFoo collapsed:NO];
[model setSection:SectionIdentifierBar collapsed:NO];
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierFoo]);
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierBar]);
}
// Collapses just one section at the time.
TEST_F(TableViewModelTest, SetSomeCollapsed) {
[model setSection:SectionIdentifierFoo collapsed:NO];
[model setSection:SectionIdentifierBar collapsed:YES];
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierFoo]);
EXPECT_TRUE([model sectionIsCollapsed:SectionIdentifierBar]);
[model setSection:SectionIdentifierFoo collapsed:YES];
[model setSection:SectionIdentifierBar collapsed:NO];
EXPECT_TRUE([model sectionIsCollapsed:SectionIdentifierFoo]);
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierBar]);
}
// Removes a collapsed section.
TEST_F(TableViewModelTest, RemoveCollapsedSection) {
[model setSection:SectionIdentifierFoo collapsed:NO];
[model setSection:SectionIdentifierBar collapsed:YES];
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierFoo]);
EXPECT_TRUE([model sectionIsCollapsed:SectionIdentifierBar]);
EXPECT_EQ(2, [model numberOfSections]);
[model removeSectionWithIdentifier:SectionIdentifierBar];
EXPECT_EQ(1, [model numberOfSections]);
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierFoo]);
}
// Removes a collapsed section, then re-adds it, it should still be collapsed.
TEST_F(TableViewModelTest, RemoveReaddCollapsedSection) {
[model setSection:SectionIdentifierFoo collapsed:NO];
[model setSection:SectionIdentifierBar collapsed:YES];
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierFoo]);
EXPECT_TRUE([model sectionIsCollapsed:SectionIdentifierBar]);
EXPECT_EQ(2, [model numberOfSections]);
[model removeSectionWithIdentifier:SectionIdentifierBar];
EXPECT_EQ(1, [model numberOfSections]);
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierFoo]);
[model addSectionWithIdentifier:SectionIdentifierBar];
// Use the same Key as the previously removed section.
[model setSectionIdentifier:SectionIdentifierBar collapsedKey:@"BarKey"];
TableViewHeaderFooterItem* header =
[[TableViewHeaderFooterItem alloc] initWithType:ItemTypeFooBar];
TableViewItem* item = [[TableViewItem alloc] initWithType:ItemTypeFooBar];
[model setHeader:header forSectionWithIdentifier:SectionIdentifierBar];
[model addItem:item toSectionWithIdentifier:SectionIdentifierBar];
EXPECT_EQ(2, [model numberOfSections]);
EXPECT_TRUE([model sectionIsCollapsed:SectionIdentifierBar]);
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierFoo]);
}
// Test Collapsed persistance.
TEST_F(TableViewModelTest, PersistCollapsedSections) {
[model setSection:SectionIdentifierFoo collapsed:NO];
[model setSection:SectionIdentifierBar collapsed:YES];
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierFoo]);
EXPECT_TRUE([model sectionIsCollapsed:SectionIdentifierBar]);
TableViewModel* anotherModel = [[TableViewModel alloc] init];
[anotherModel addSectionWithIdentifier:SectionIdentifierFoo];
[anotherModel setSectionIdentifier:SectionIdentifierFoo
collapsedKey:@"FooKey"];
TableViewHeaderFooterItem* header =
[[TableViewHeaderFooterItem alloc] initWithType:ItemTypeFooBar];
TableViewItem* item = [[TableViewItem alloc] initWithType:ItemTypeFooBar];
[anotherModel setHeader:header forSectionWithIdentifier:SectionIdentifierFoo];
[anotherModel addItem:item toSectionWithIdentifier:SectionIdentifierFoo];
[anotherModel addSectionWithIdentifier:SectionIdentifierBar];
[anotherModel setSectionIdentifier:SectionIdentifierBar
collapsedKey:@"BarKey"];
header = [[TableViewHeaderFooterItem alloc] initWithType:ItemTypeFooBar];
item = [[TableViewItem alloc] initWithType:ItemTypeFooBar];
[anotherModel setHeader:header forSectionWithIdentifier:SectionIdentifierBar];
[anotherModel addItem:item toSectionWithIdentifier:SectionIdentifierBar];
// Since the Keys are the same as the previous model it should have preserved
// its collapsed values.
EXPECT_FALSE([model sectionIsCollapsed:SectionIdentifierFoo]);
EXPECT_TRUE([model sectionIsCollapsed:SectionIdentifierBar]);
}
} // namespace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment