blob: a788bcea8ce226b7e1966b22f375dc1261de91c8 [file] [log] [blame]
// Copyright 2016 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/history/history_entry_inserter.h"
#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/time/time.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
#import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
#import "ios/chrome/browser/ui/history/history_entry_item.h"
#include "ios/chrome/browser/ui/history/history_util.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface HistoryEntryInserter () {
// CollectionViewModel in which to insert history entries.
CollectionViewModel* _collectionViewModel;
// The index of the first section to contain history entries.
NSInteger _firstSectionIndex;
// Number of assigned section identifiers.
NSInteger _sectionIdentifierCount;
// Sorted set of dates that have history entries.
NSMutableOrderedSet* _dates;
// Mapping from dates to section identifiers.
NSMutableDictionary* _sectionIdentifiers;
}
@end
@implementation HistoryEntryInserter
@synthesize delegate = _delegate;
- (instancetype)initWithModel:(CollectionViewModel*)collectionViewModel {
if ((self = [super init])) {
_collectionViewModel = collectionViewModel;
_firstSectionIndex = [collectionViewModel numberOfSections];
_dates = [[NSMutableOrderedSet alloc] init];
_sectionIdentifiers = [NSMutableDictionary dictionary];
}
return self;
}
- (void)insertHistoryEntryItem:(HistoryEntryItem*)item {
NSInteger sectionIdentifier =
[self sectionIdentifierForTimestamp:item.timestamp];
NSComparator objectComparator = ^(id obj1, id obj2) {
HistoryEntryItem* firstObject =
base::mac::ObjCCastStrict<HistoryEntryItem>(obj1);
HistoryEntryItem* secondObject =
base::mac::ObjCCastStrict<HistoryEntryItem>(obj2);
if ([firstObject isEqualToHistoryEntryItem:secondObject])
return NSOrderedSame;
// History entries are ordered from most to least recent.
if (firstObject.timestamp > secondObject.timestamp)
return NSOrderedAscending;
if (firstObject.timestamp < secondObject.timestamp)
return NSOrderedDescending;
return firstObject.URL < secondObject.URL ? NSOrderedAscending
: NSOrderedDescending;
};
NSArray* items =
[_collectionViewModel itemsInSectionWithIdentifier:sectionIdentifier];
NSRange range = NSMakeRange(0, [items count]);
// If the object is not already in the section, insert it.
if ([items indexOfObject:item
inSortedRange:range
options:NSBinarySearchingFirstEqual
usingComparator:objectComparator] == NSNotFound) {
// Insert the object at the appropriate index to keep the section sorted.
NSUInteger index = [items indexOfObject:item
inSortedRange:range
options:NSBinarySearchingInsertionIndex
usingComparator:objectComparator];
[_collectionViewModel insertItem:item
inSectionWithIdentifier:sectionIdentifier
atIndex:index];
NSIndexPath* indexPath = [NSIndexPath
indexPathForItem:index
inSection:[_collectionViewModel
sectionForSectionIdentifier:sectionIdentifier]];
[self.delegate historyEntryInserter:self
didInsertItemAtIndexPath:indexPath];
}
}
- (NSUInteger)sectionIdentifierForTimestamp:(base::Time)timestamp {
base::TimeDelta timeDelta =
timestamp.LocalMidnight() - base::Time::UnixEpoch();
NSDate* date = [NSDate dateWithTimeIntervalSince1970:timeDelta.InSeconds()];
NSInteger sectionIdentifier =
[[_sectionIdentifiers objectForKey:date] integerValue];
// If there is a section identifier for the date, return it.
if (sectionIdentifier) {
return sectionIdentifier;
}
// Get the next section identifier, and add a section for date.
sectionIdentifier =
kSectionIdentifierEnumZero + _firstSectionIndex + _sectionIdentifierCount;
++_sectionIdentifierCount;
[_sectionIdentifiers setObject:@(sectionIdentifier) forKey:date];
NSComparator comparator = ^(id obj1, id obj2) {
// Dates are ordered from most to least recent.
return [obj2 compare:obj1];
};
NSUInteger index = [_dates indexOfObject:date
inSortedRange:NSMakeRange(0, [_dates count])
options:NSBinarySearchingInsertionIndex
usingComparator:comparator];
[_dates insertObject:date atIndex:index];
NSInteger insertionIndex = _firstSectionIndex + index;
CollectionViewTextItem* header =
[[CollectionViewTextItem alloc] initWithType:kItemTypeEnumZero];
header.text =
base::SysUTF16ToNSString(history::GetRelativeDateLocalized(timestamp));
[_collectionViewModel insertSectionWithIdentifier:sectionIdentifier
atIndex:insertionIndex];
[_collectionViewModel setHeader:header
forSectionWithIdentifier:sectionIdentifier];
[self.delegate historyEntryInserter:self
didInsertSectionAtIndex:insertionIndex];
return sectionIdentifier;
}
- (void)removeSection:(NSInteger)sectionIndex {
NSUInteger sectionIdentifier =
[_collectionViewModel sectionIdentifierForSection:sectionIndex];
// Sections should not be removed unless there are no items in that section.
DCHECK(![[_collectionViewModel itemsInSectionWithIdentifier:sectionIdentifier]
count]);
[_collectionViewModel removeSectionWithIdentifier:sectionIdentifier];
NSEnumerator* dateEnumerator = [_sectionIdentifiers keyEnumerator];
NSDate* date = nil;
while ((date = [dateEnumerator nextObject])) {
if ([[_sectionIdentifiers objectForKey:date] unsignedIntegerValue] ==
sectionIdentifier) {
[_sectionIdentifiers removeObjectForKey:date];
[_dates removeObject:date];
break;
}
}
[self.delegate historyEntryInserter:self
didRemoveSectionAtIndex:sectionIndex];
}
@end