jenkins-bot has submitted this change and it was merged.

Change subject: Add Recents to Tab UI
......................................................................


Add Recents to Tab UI

T105019

Add new tab, updating story board
Add recents data source
Made History list KVO-able
Updated App VC to handle injecting objects into multiple article lists
Injecting the history list into the article VC
Add new data source property - discovery method, so that the article list knows 
how to update the history
Updating history from Article list when card is presented
Naming history list properties "recent" in preparation for splitting recents 
from history
Recents tab has the same presentation UI glitch as Saved - to be fixed later

Change-Id: Ic47b09dbc5ee4b4ba1f8b2773932c7fd9291b470
---
M MediaWikiKit/MediaWikiKit/MWKHistoryList.h
M MediaWikiKit/MediaWikiKit/MWKHistoryList.m
M MediaWikiKit/MediaWikiKit/MWKSavedPageList.h
M MediaWikiKit/MediaWikiKit/MWKSavedPageList.m
M Wikipedia.xcodeproj/project.pbxproj
M Wikipedia/UI-V5/WMFAppViewController.m
M Wikipedia/UI-V5/WMFArticleListCollectionViewController.h
M Wikipedia/UI-V5/WMFArticleListCollectionViewController.m
M Wikipedia/UI-V5/WMFArticleListDataSource.h
A Wikipedia/UI-V5/WMFRecentPagesDataSource.h
A Wikipedia/UI-V5/WMFRecentPagesDataSource.m
M Wikipedia/UI-V5/WMFSavedPagesDataSource.h
M Wikipedia/UI-V5/WMFSavedPagesDataSource.m
M Wikipedia/UI-V5/WMFSearchResults.m
M Wikipedia/UI-V5/iPhone_Root.storyboard
15 files changed, 333 insertions(+), 61 deletions(-)

Approvals:
  Bgerstle: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/MediaWikiKit/MediaWikiKit/MWKHistoryList.h 
b/MediaWikiKit/MediaWikiKit/MWKHistoryList.h
index 90bd574..b4c0d4b 100644
--- a/MediaWikiKit/MediaWikiKit/MWKHistoryList.h
+++ b/MediaWikiKit/MediaWikiKit/MWKHistoryList.h
@@ -1,5 +1,5 @@
 
-#import "MediaWikiKit.h"
+#import "MWKDataObject.h"
 
 @class MWKTitle;
 @class MWKHistoryEntry;
@@ -8,10 +8,14 @@
 
 @interface MWKHistoryList : MWKDataObject <NSFastEnumeration>
 
-@property (readonly, weak, nonatomic) MWKDataStore* dataStore;
-@property (nonatomic, readonly, assign) NSUInteger length;
-@property (nonatomic, readonly, assign) BOOL dirty;
-@property (nonatomic, readonly, strong) MWKHistoryEntry* mostRecentEntry;
+/**
+ *  Observable - observe to get KVO notifications
+ */
+@property (nonatomic, strong, readonly)  NSArray* entries;
+
+@property (nonatomic, weak, readonly) MWKDataStore* dataStore;
+@property (nonatomic, assign, readonly) NSUInteger length;
+@property (nonatomic, assign, readonly) BOOL dirty;
 
 /**
  *  Create history list and connect with data store.
@@ -23,7 +27,6 @@
  */
 - (instancetype)initWithDataStore:(MWKDataStore*)dataStore;
 
-
 - (MWKHistoryEntry*)entryAtIndex:(NSUInteger)index;
 - (MWKHistoryEntry*)entryForTitle:(MWKTitle*)title;
 
@@ -31,6 +34,7 @@
 - (MWKHistoryEntry*)entryAfterEntry:(MWKHistoryEntry*)entry;
 - (MWKHistoryEntry*)entryBeforeEntry:(MWKHistoryEntry*)entry;
 
+- (MWKHistoryEntry*)mostRecentEntry;
 
 /**
  *  Add a page to the user history.
diff --git a/MediaWikiKit/MediaWikiKit/MWKHistoryList.m 
b/MediaWikiKit/MediaWikiKit/MWKHistoryList.m
index c48d7cc..4bd415a 100644
--- a/MediaWikiKit/MediaWikiKit/MWKHistoryList.m
+++ b/MediaWikiKit/MediaWikiKit/MWKHistoryList.m
@@ -6,9 +6,7 @@
 @interface MWKHistoryList ()
 
 @property (readwrite, weak, nonatomic) MWKDataStore* dataStore;
-@property (nonatomic, readwrite, assign) NSUInteger length;
-@property (nonatomic, readwrite, strong) MWKHistoryEntry* mostRecentEntry;
-@property (nonatomic, strong)  NSMutableArray* entries;
+@property (nonatomic, strong) NSMutableArray* mutableEntries;
 @property (nonatomic, strong) NSMutableDictionary* entriesByTitle;
 @property (nonatomic, readwrite, assign) BOOL dirty;
 
@@ -22,9 +20,10 @@
     self = [super init];
     if (self) {
         self.dataStore      = dataStore;
-        self.entries        = [[NSMutableArray alloc] init];
+        self.mutableEntries = [NSMutableArray array];
         self.entriesByTitle = [[NSMutableDictionary alloc] init];
         NSDictionary* data = [self.dataStore historyListData];
+
         [self importData:data];
     }
     return self;
@@ -45,7 +44,7 @@
     if (arr) {
         for (NSDictionary* entryDict in arr) {
             MWKHistoryEntry* entry = [[MWKHistoryEntry alloc] 
initWithDict:entryDict];
-            [self.entries addObject:entry];
+            [self.mutableEntries addObject:entry];
             self.entriesByTitle[entry.title] = entry;
         }
     }
@@ -63,16 +62,20 @@
 
 #pragma mark - Entry Access
 
-- (NSUInteger)length {
-    return [self.entries count];
+- (NSArray*)entries {
+    return _mutableEntries;
 }
 
-- (MWKHistoryEntry*)mostRecentEntry {
-    return [self.entries firstObject];
+- (NSMutableArray*)mutableEntries {
+    return [self mutableArrayValueForKey:@"entries"];
+}
+
+- (NSUInteger)length {
+    return [self countOfEntries];
 }
 
 - (MWKHistoryEntry*)entryAtIndex:(NSUInteger)index {
-    return self.entries[index];
+    return [self objectInEntriesAtIndex:index];
 }
 
 - (MWKHistoryEntry*)entryForTitle:(MWKTitle*)title {
@@ -80,7 +83,7 @@
 }
 
 - (NSUInteger)indexForEntry:(MWKHistoryEntry*)entry {
-    return [self.entries indexOfObject:entry];
+    return [self.mutableEntries indexOfObject:entry];
 }
 
 - (MWKHistoryEntry*)entryAfterEntry:(MWKHistoryEntry*)entry {
@@ -105,7 +108,11 @@
     }
 }
 
-#pragma mark - History Update
+- (MWKHistoryEntry*)mostRecentEntry {
+    return [self.mutableEntries firstObject];
+}
+
+#pragma mark - Update Methods
 
 - (void)addPageToHistoryWithTitle:(MWKTitle*)title 
discoveryMethod:(MWKHistoryDiscoveryMethod)discoveryMethod {
     if (title == nil) {
@@ -126,10 +133,10 @@
     MWKHistoryEntry* oldEntry = [self entryForTitle:entry.title];
     if (oldEntry) {
         entry.discoveryMethod = entry.discoveryMethod == 
MWKHistoryDiscoveryMethodUnknown ? oldEntry.discoveryMethod : 
entry.discoveryMethod;
-        [self.entries removeObject:oldEntry];
+        [self.mutableEntries removeObject:oldEntry];
     }
     entry.date = entry.date ? entry.date : [NSDate date];
-    [self.entries insertObject:entry atIndex:0];
+    [self.mutableEntries insertObject:entry atIndex:0];
     self.entriesByTitle[entry.title] = entry;
     self.dirty                       = YES;
 }
@@ -154,7 +161,7 @@
 
     MWKHistoryEntry* entry = [self entryForTitle:title];
     if (entry) {
-        [self.entries removeObject:entry];
+        [self.mutableEntries removeObject:entry];
         [self.entriesByTitle removeObjectForKey:entry.title];
         self.dirty = YES;
     }
@@ -166,7 +173,7 @@
     }
 
     [historyEntries enumerateObjectsUsingBlock:^(MWKHistoryEntry* entry, 
NSUInteger idx, BOOL* stop) {
-        [self.entries removeObject:entry];
+        [self.mutableEntries removeObject:entry];
         [self.entriesByTitle removeObjectForKey:entry.title];
     }];
 
@@ -174,7 +181,7 @@
 }
 
 - (void)removeAllEntriesFromHistory {
-    [self.entries removeAllObjects];
+    [self.mutableEntries removeAllObjects];
     [self.entriesByTitle removeAllObjects];
     self.dirty = YES;
 }
@@ -195,4 +202,38 @@
     });
 }
 
+#pragma mark - KVO
+
+- (NSUInteger)countOfEntries {
+    return [_mutableEntries count];
+}
+
+- (id)objectInEntriesAtIndex:(NSUInteger)idx {
+    return [_mutableEntries objectAtIndex:idx];
+}
+
+- (void)insertObject:(id)anObject inEntriesAtIndex:(NSUInteger)idx {
+    [_mutableEntries insertObject:anObject atIndex:idx];
+}
+
+- (void)insertEntries:(NSArray*)entrieArray atIndexes:(NSIndexSet*)indexes {
+    [_mutableEntries insertObjects:entrieArray atIndexes:indexes];
+}
+
+- (void)removeObjectFromEntriesAtIndex:(NSUInteger)idx {
+    [_mutableEntries removeObjectAtIndex:idx];
+}
+
+- (void)removeEntriesAtIndexes:(NSIndexSet*)indexes {
+    [_mutableEntries removeObjectsAtIndexes:indexes];
+}
+
+- (void)replaceObjectInEntriesAtIndex:(NSUInteger)idx withObject:(id)anObject {
+    [_mutableEntries replaceObjectAtIndex:idx withObject:anObject];
+}
+
+- (void)replaceEntriesAtIndexes:(NSIndexSet*)indexes 
withEntries:(NSArray*)entrieArray {
+    [_mutableEntries replaceObjectsAtIndexes:indexes withObjects:entrieArray];
+}
+
 @end
diff --git a/MediaWikiKit/MediaWikiKit/MWKSavedPageList.h 
b/MediaWikiKit/MediaWikiKit/MWKSavedPageList.h
index f99320c..297f514 100644
--- a/MediaWikiKit/MediaWikiKit/MWKSavedPageList.h
+++ b/MediaWikiKit/MediaWikiKit/MWKSavedPageList.h
@@ -3,6 +3,8 @@
 
 @class MWKTitle;
 @class MWKSavedPageEntry;
+@class MWKDataStore;
+@class AnyPromise;
 
 NS_ASSUME_NONNULL_BEGIN
 
diff --git a/MediaWikiKit/MediaWikiKit/MWKSavedPageList.m 
b/MediaWikiKit/MediaWikiKit/MWKSavedPageList.m
index 670bd02..20e392c 100644
--- a/MediaWikiKit/MediaWikiKit/MWKSavedPageList.m
+++ b/MediaWikiKit/MediaWikiKit/MWKSavedPageList.m
@@ -80,8 +80,7 @@
 }
 
 - (MWKSavedPageEntry*)entryForTitle:(MWKTitle*)title {
-    MWKSavedPageEntry* entry = self.entriesByTitle[title];
-    return entry;
+    return self.entriesByTitle[title];
 }
 
 - (BOOL)isSaved:(MWKTitle*)title {
@@ -172,6 +171,8 @@
     });
 }
 
+#pragma mark - KVO
+
 - (NSUInteger)countOfEntries {
     return [_mutableEntries count];
 }
diff --git a/Wikipedia.xcodeproj/project.pbxproj 
b/Wikipedia.xcodeproj/project.pbxproj
index 4e837de..e14502a 100644
--- a/Wikipedia.xcodeproj/project.pbxproj
+++ b/Wikipedia.xcodeproj/project.pbxproj
@@ -205,6 +205,7 @@
                0EE7687B1AF982C100A5D046 /* WMFArticleProtocol.m in Sources */ 
= {isa = PBXBuildFile; fileRef = 0EE7687A1AF982C100A5D046 /* 
WMFArticleProtocol.m */; };
                0EE768811AFD25CC00A5D046 /* WMFSearchFunnel.m in Sources */ = 
{isa = PBXBuildFile; fileRef = 0EE768801AFD25CC00A5D046 /* WMFSearchFunnel.m 
*/; };
                0EF451F31B5458F700D621BD /* UITabBarController+WMFExtensions.m 
in Sources */ = {isa = PBXBuildFile; fileRef = 0EF451F21B5458F700D621BD /* 
UITabBarController+WMFExtensions.m */; };
+               0EF451F61B545ED100D621BD /* WMFRecentPagesDataSource.m in 
Sources */ = {isa = PBXBuildFile; fileRef = 0EF451F51B545ED100D621BD /* 
WMFRecentPagesDataSource.m */; };
                0EFB0EF51B31DE7200D05C08 /* NSError+WMFExtensions.m in Sources 
*/ = {isa = PBXBuildFile; fileRef = 0EFB0EF41B31DE7200D05C08 /* 
NSError+WMFExtensions.m */; };
                0EFB0F191B31EE2D00D05C08 /* Article.m in Sources */ = {isa = 
PBXBuildFile; fileRef = 0EFB0EFB1B31EE2D00D05C08 /* Article.m */; };
                0EFB0F1A1B31EE2D00D05C08 /* ArticleData.xcdatamodeld in Sources 
*/ = {isa = PBXBuildFile; fileRef = 0EFB0EFD1B31EE2D00D05C08 /* 
ArticleData.xcdatamodeld */; };
@@ -781,6 +782,8 @@
                0EE768801AFD25CC00A5D046 /* WMFSearchFunnel.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= WMFSearchFunnel.m; sourceTree = "<group>"; };
                0EF451F11B5458F700D621BD /* UITabBarController+WMFExtensions.h 
*/ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.c.h; path = "UITabBarController+WMFExtensions.h"; sourceTree = 
"<group>"; };
                0EF451F21B5458F700D621BD /* UITabBarController+WMFExtensions.m 
*/ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.c.objc; path = "UITabBarController+WMFExtensions.m"; sourceTree = 
"<group>"; };
+               0EF451F41B545ED100D621BD /* WMFRecentPagesDataSource.h */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; 
path = WMFRecentPagesDataSource.h; sourceTree = "<group>"; };
+               0EF451F51B545ED100D621BD /* WMFRecentPagesDataSource.m */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.c.objc; path = WMFRecentPagesDataSource.m; sourceTree = "<group>"; };
                0EFB0EF31B31DE7200D05C08 /* NSError+WMFExtensions.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = 
"NSError+WMFExtensions.h"; path = "../../NSError+WMFExtensions.h"; sourceTree = 
"<group>"; };
                0EFB0EF41B31DE7200D05C08 /* NSError+WMFExtensions.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name 
= "NSError+WMFExtensions.m"; path = "../../NSError+WMFExtensions.m"; sourceTree 
= "<group>"; };
                0EFB0EF71B31ECCB00D05C08 /* Global.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
Global.h; sourceTree = "<group>"; };
@@ -1950,6 +1953,8 @@
                                0E7C6D4C1B3106A800F1F985 /* 
WMFArticleListDataSource.h */,
                                0E2B06F41B2CE45800EA2F53 /* 
WMFSavedPagesDataSource.h */,
                                0E2B06F51B2CE45800EA2F53 /* 
WMFSavedPagesDataSource.m */,
+                               0EF451F41B545ED100D621BD /* 
WMFRecentPagesDataSource.h */,
+                               0EF451F51B545ED100D621BD /* 
WMFRecentPagesDataSource.m */,
                        );
                        name = "Data Sources";
                        sourceTree = "<group>";
@@ -3249,6 +3254,7 @@
                                0EFB0F1D1B31EE2D00D05C08 /* 
NSManagedObjectContext+SimpleFetch.m in Sources */,
                                04CCCFF61935094000E3F60C /* 
PrimaryMenuViewController.m in Sources */,
                                BCE6EE101B2619E900AF603B /* MWKLanguageLink.m 
in Sources */,
+                               0EF451F61B545ED100D621BD /* 
WMFRecentPagesDataSource.m in Sources */,
                                0487048119F8262600B7D307 /* ArticleFetcher.m in 
Sources */,
                                04142A8F184F974E006EF779 /* NSDate-Utilities.m 
in Sources */,
                                0487048C19F8262600B7D307 /* 
SearchResultFetcher.m in Sources */,
diff --git a/Wikipedia/UI-V5/WMFAppViewController.m 
b/Wikipedia/UI-V5/WMFAppViewController.m
index 19d92fb..7ffaeee 100644
--- a/Wikipedia/UI-V5/WMFAppViewController.m
+++ b/Wikipedia/UI-V5/WMFAppViewController.m
@@ -6,12 +6,18 @@
 #import "WMFArticleListCollectionViewController.h"
 #import "DataMigrationProgressViewController.h"
 #import "WMFSavedPagesDataSource.h"
+#import "WMFRecentPagesDataSource.h"
 #import "UIStoryboard+WMFExtensions.h"
 #import "UITabBarController+WMFExtensions.h"
 #import <Masonry/Masonry.h>
 
+typedef NS_ENUM (NSUInteger, WMFAppTabType) {
+    WMFAppTabTypeSaved = 0,
+    WMFAppTabTypeRecent
+};
 
-@interface WMFAppViewController ()<WMFSearchViewControllerDelegate>
+
+@interface WMFAppViewController ()<WMFSearchViewControllerDelegate, 
UITabBarControllerDelegate>
 @property (strong, nonatomic) IBOutlet UIView* searchContainerView;
 @property (strong, nonatomic) IBOutlet UIView* tabControllerContainerView;
 
@@ -19,6 +25,7 @@
 
 @property (nonatomic, strong) UITabBarController* tabBarController;
 @property (nonatomic, strong, readonly) 
WMFArticleListCollectionViewController* savedArticlesViewController;
+@property (nonatomic, strong, readonly) 
WMFArticleListCollectionViewController* recentArticlesViewController;
 @property (nonatomic, strong) WMFSearchViewController* searchViewController;
 
 @property (nonatomic, strong) SessionSingleton* session;
@@ -27,15 +34,44 @@
 
 @implementation WMFAppViewController
 
-- (SessionSingleton*)session {
-    if (!_session) {
-        _session = [SessionSingleton sharedInstance];
-    }
+#pragma mark - Setup
 
-    return _session;
+- (void)loadMainUI {
+    [self configureTabController];
+    [self configureSearchViewController];
+    [self configureSavedViewController];
+    [self configureRecentViewController];
+    [self 
updateTabBarVisibilityBasedOnSearchState:self.searchViewController.state];
 }
 
-#pragma mark - Setup
+- (void)configureTabController {
+    self.tabBarController.delegate = self;
+}
+
+- (void)configureSearchViewController {
+    self.searchViewController.delegate      = self;
+    self.searchViewController.searchSite    = [self.session searchSite];
+    self.searchViewController.dataStore     = self.session.dataStore;
+    self.searchViewController.userDataStore = self.session.userDataStore;
+}
+
+- 
(void)configureArticleListController:(WMFArticleListCollectionViewController*)controller
 {
+    controller.dataStore   = self.session.dataStore;
+    controller.savedPages  = self.session.userDataStore.savedPageList;
+    controller.recentPages = self.session.userDataStore.historyList;
+}
+
+- (void)configureSavedViewController {
+    [self configureArticleListController:self.savedArticlesViewController];
+    self.savedArticlesViewController.dataSource = [[WMFSavedPagesDataSource 
alloc] initWithSavedPagesList:[self userDataStore].savedPageList];
+}
+
+- (void)configureRecentViewController {
+    [self configureArticleListController:self.recentArticlesViewController];
+    self.recentArticlesViewController.dataSource = [[WMFRecentPagesDataSource 
alloc] initWithRecentPagesList:[self userDataStore].historyList];
+}
+
+#pragma mark - Public
 
 + (WMFAppViewController*)initialAppViewControllerFromDefaultStoryBoard {
     return [[UIStoryboard wmf_appRootStoryBoard] 
instantiateInitialViewController];
@@ -50,23 +86,19 @@
     [window makeKeyAndVisible];
 }
 
-- (void)loadMainUI {
-    self.searchViewController.searchSite    = [self.session searchSite];
-    self.searchViewController.dataStore     = self.session.dataStore;
-    self.searchViewController.userDataStore = self.session.userDataStore;
-
-    self.savedArticlesViewController.dataStore  = self.session.dataStore;
-    self.savedArticlesViewController.savedPages = 
self.session.userDataStore.savedPageList;
-    self.savedArticlesViewController.dataSource = [[WMFSavedPagesDataSource 
alloc] initWithSavedPagesList:[self userDataStore].savedPageList];
-
-    [self updateListViewBasedOnSearchState:self.searchViewController.state];
-}
-
 - (void)resumeApp {
     //TODO: restore any UI, show Today
 }
 
 #pragma mark - Accessors
+
+- (SessionSingleton*)session {
+    if (!_session) {
+        _session = [SessionSingleton sharedInstance];
+    }
+
+    return _session;
+}
 
 - (MWKDataStore*)dataStore {
     return self.session.dataStore;
@@ -77,9 +109,11 @@
 }
 
 - (WMFArticleListCollectionViewController*)savedArticlesViewController {
-    return [[self.tabBarController viewControllers] bk_match:^BOOL 
(UIViewController* obj) {
-        return [obj isKindOfClass:[WMFArticleListCollectionViewController 
class]];
-    }];
+    return [self.tabBarController viewControllers][WMFAppTabTypeSaved];
+}
+
+- (WMFArticleListCollectionViewController*)recentArticlesViewController {
+    return [self.tabBarController viewControllers][WMFAppTabTypeRecent];
 }
 
 #pragma mark - UIViewController
@@ -97,11 +131,12 @@
 
 - (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender {
     if ([segue.destinationViewController 
isKindOfClass:[WMFSearchViewController class]]) {
-        self.searchViewController          = segue.destinationViewController;
-        self.searchViewController.delegate = self;
+        self.searchViewController = segue.destinationViewController;
+        [self configureSearchViewController];
     }
     if ([segue.destinationViewController isKindOfClass:[UITabBarController 
class]]) {
         self.tabBarController = segue.destinationViewController;
+        [self configureTabController];
     }
 }
 
@@ -160,13 +195,28 @@
     }];
 }
 
+- (void)tabBarController:(UITabBarController*)tabBarController 
didSelectViewController:(UIViewController*)viewController {
+    WMFAppTabType tab = [[tabBarController viewControllers] 
indexOfObject:viewController];
+
+    switch (tab) {
+        case WMFAppTabTypeSaved: {
+            [self configureSavedViewController];
+        }
+        break;
+        case WMFAppTabTypeRecent: {
+            [self configureRecentViewController];
+        }
+        break;
+    }
+}
+
 #pragma mark - WMFSearchViewControllerDelegate
 
 - (void)searchController:(WMFSearchViewController*)controller 
searchStateDidChange:(WMFSearchState)state {
-    [self updateListViewBasedOnSearchState:state];
+    [self updateTabBarVisibilityBasedOnSearchState:state];
 }
 
-- (void)updateListViewBasedOnSearchState:(WMFSearchState)state {
+- (void)updateTabBarVisibilityBasedOnSearchState:(WMFSearchState)state {
     switch (state) {
         case WMFSearchStateInactive: {
             self.tabBarController.view.hidden                      = NO;
diff --git a/Wikipedia/UI-V5/WMFArticleListCollectionViewController.h 
b/Wikipedia/UI-V5/WMFArticleListCollectionViewController.h
index 7186098..74b15bb 100644
--- a/Wikipedia/UI-V5/WMFArticleListCollectionViewController.h
+++ b/Wikipedia/UI-V5/WMFArticleListCollectionViewController.h
@@ -13,6 +13,7 @@
 
 @property (nonatomic, strong) MWKDataStore* dataStore;
 @property (nonatomic, strong) MWKSavedPageList* savedPages;
+@property (nonatomic, strong) MWKHistoryList* recentPages;
 @property (nonatomic, strong, nullable) id<WMFArticleListDataSource> 
dataSource;
 
 @property (nonatomic, assign, readonly) WMFArticleListMode mode;
diff --git a/Wikipedia/UI-V5/WMFArticleListCollectionViewController.m 
b/Wikipedia/UI-V5/WMFArticleListCollectionViewController.m
index f367e20..d5391a4 100644
--- a/Wikipedia/UI-V5/WMFArticleListCollectionViewController.m
+++ b/Wikipedia/UI-V5/WMFArticleListCollectionViewController.m
@@ -47,11 +47,6 @@
     }
 }
 
-- (void)setSavedPages:(MWKSavedPageList* __nonnull)savedPages {
-    _savedPages = savedPages;
-    [self.collectionView reloadData];
-}
-
 #pragma mark - List Mode
 
 - (void)setListMode:(WMFArticleListMode)mode animated:(BOOL)animated 
completion:(nullable dispatch_block_t)completion {
@@ -231,7 +226,10 @@
     // if keyboard is visible, dismiss it (e.g. when used to display search 
results)
     [[UIApplication sharedApplication] 
sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
 
-    [self presentViewController:vc animated:YES completion:NULL];
+    [self presentViewController:vc animated:YES completion:^{
+        [self.recentPages 
addPageToHistoryWithTitle:cell.viewController.article.title 
discoveryMethod:[self.dataSource discoveryMethod]];
+        [self.recentPages save];
+    }];
 }
 
 #pragma mark - TGLStackedLayoutDelegate
diff --git a/Wikipedia/UI-V5/WMFArticleListDataSource.h 
b/Wikipedia/UI-V5/WMFArticleListDataSource.h
index 8651036..7adece3 100644
--- a/Wikipedia/UI-V5/WMFArticleListDataSource.h
+++ b/Wikipedia/UI-V5/WMFArticleListDataSource.h
@@ -14,6 +14,8 @@
 
 - (BOOL)canDeleteItemAtIndexpath:(NSIndexPath*)indexPath;
 
+- (MWKHistoryDiscoveryMethod)discoveryMethod;
+
 @optional
 - (void)deleteArticleAtIndexPath:(NSIndexPath*)indexPath;
 
diff --git a/Wikipedia/UI-V5/WMFRecentPagesDataSource.h 
b/Wikipedia/UI-V5/WMFRecentPagesDataSource.h
new file mode 100644
index 0000000..b5d8ce4
--- /dev/null
+++ b/Wikipedia/UI-V5/WMFRecentPagesDataSource.h
@@ -0,0 +1,22 @@
+#import <Mantle/Mantle.h>
+#import "WMFArticleListDataSource.h"
+
+@class MWKHistoryList;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface WMFRecentPagesDataSource : MTLModel<WMFArticleListDataSource>
+
+/**
+ *  Observable
+ */
+@property (nonatomic, strong, readonly) NSArray* articles;
+
+@property (nonatomic, strong, readonly) MWKHistoryList* recentPages;
+
+- (nonnull instancetype)initWithRecentPagesList:(MWKHistoryList*)recentPages;
+
+@end
+
+
+NS_ASSUME_NONNULL_END
diff --git a/Wikipedia/UI-V5/WMFRecentPagesDataSource.m 
b/Wikipedia/UI-V5/WMFRecentPagesDataSource.m
new file mode 100644
index 0000000..0217f38
--- /dev/null
+++ b/Wikipedia/UI-V5/WMFRecentPagesDataSource.m
@@ -0,0 +1,86 @@
+
+#import "WMFRecentPagesDataSource.h"
+#import "MWKHistoryList.h"
+#import "MWKHistoryEntry.h"
+#import "MWKArticle.h"
+
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface WMFRecentPagesDataSource ()
+
+@property (nonatomic, strong, readwrite) MWKHistoryList* recentPages;
+
+@end
+
+@implementation WMFRecentPagesDataSource
+
+- (nonnull instancetype)initWithRecentPagesList:(MWKHistoryList*)recentPages {
+    self = [super init];
+    if (self) {
+        self.recentPages = recentPages;
+        [self.KVOController observe:recentPages 
keyPath:WMF_SAFE_KEYPATH(recentPages, entries) 
options:NSKeyValueObservingOptionPrior block:^(id observer, id object, 
NSDictionary* change) {
+            NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] 
unsignedIntegerValue];
+
+            if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
+                [self willChange:changeKind 
valuesAtIndexes:change[NSKeyValueChangeIndexesKey] forKey:@"articles"];
+            } else {
+                [self didChange:changeKind 
valuesAtIndexes:change[NSKeyValueChangeIndexesKey] forKey:@"articles"];
+            }
+        }];
+    }
+    return self;
+}
+
+- (NSArray*)articles {
+    return [[self.recentPages entries] bk_map:^id (id obj) {
+        return [self articleForEntry:obj];
+    }];
+}
+
+- (MWKArticle*)articleForEntry:(MWKHistoryEntry*)entry {
+    return [[self dataStore] articleWithTitle:entry.title];
+}
+
+- (MWKDataStore*)dataStore {
+    return self.recentPages.dataStore;
+}
+
+- (nullable NSString*)displayTitle {
+    return MWLocalizedString(@"page-history-title", nil);
+}
+
+- (NSUInteger)articleCount {
+    return [[self recentPages] length];
+}
+
+- (MWKHistoryEntry*)recentPageForIndexPath:(NSIndexPath*)indexPath {
+    MWKHistoryEntry* entry = [self.recentPages entryAtIndex:indexPath.row];
+    return entry;
+}
+
+- (MWKArticle*)articleForIndexPath:(NSIndexPath*)indexPath {
+    MWKHistoryEntry* entry = [self recentPageForIndexPath:indexPath];
+    return [self articleForEntry:entry];
+}
+
+- (BOOL)canDeleteItemAtIndexpath:(NSIndexPath*)indexPath {
+    return YES;
+}
+
+- (void)deleteArticleAtIndexPath:(NSIndexPath*)indexPath {
+    MWKHistoryEntry* entry = [self recentPageForIndexPath:indexPath];
+    if (entry) {
+        [self.recentPages removePageFromHistoryWithTitle:entry.title];
+        [self.recentPages save];
+    }
+}
+
+- (MWKHistoryDiscoveryMethod)discoveryMethod {
+    return MWKHistoryDiscoveryMethodUnknown;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
+
diff --git a/Wikipedia/UI-V5/WMFSavedPagesDataSource.h 
b/Wikipedia/UI-V5/WMFSavedPagesDataSource.h
index 40cef80..6514511 100644
--- a/Wikipedia/UI-V5/WMFSavedPagesDataSource.h
+++ b/Wikipedia/UI-V5/WMFSavedPagesDataSource.h
@@ -2,6 +2,8 @@
 #import <Mantle/Mantle.h>
 #import "WMFArticleListDataSource.h"
 
+@class MWKSavedPageList;
+
 NS_ASSUME_NONNULL_BEGIN
 
 @interface WMFSavedPagesDataSource : MTLModel<WMFArticleListDataSource>
diff --git a/Wikipedia/UI-V5/WMFSavedPagesDataSource.m 
b/Wikipedia/UI-V5/WMFSavedPagesDataSource.m
index 20d6092..0dc3e1c 100644
--- a/Wikipedia/UI-V5/WMFSavedPagesDataSource.m
+++ b/Wikipedia/UI-V5/WMFSavedPagesDataSource.m
@@ -1,11 +1,8 @@
 
 #import "WMFSavedPagesDataSource.h"
-#import "MWKUserDataStore.h"
 #import "MWKSavedPageList.h"
 #import "MWKSavedPageEntry.h"
 #import "MWKArticle.h"
-#import "Wikipedia-Swift.h"
-#import "PromiseKit.h"
 
 
 NS_ASSUME_NONNULL_BEGIN
@@ -79,6 +76,10 @@
     }
 }
 
+- (MWKHistoryDiscoveryMethod)discoveryMethod {
+    return MWKHistoryDiscoveryMethodSaved;
+}
+
 @end
 
 NS_ASSUME_NONNULL_END
\ No newline at end of file
diff --git a/Wikipedia/UI-V5/WMFSearchResults.m 
b/Wikipedia/UI-V5/WMFSearchResults.m
index 8544572..93cbefb 100644
--- a/Wikipedia/UI-V5/WMFSearchResults.m
+++ b/Wikipedia/UI-V5/WMFSearchResults.m
@@ -43,6 +43,10 @@
     return (self.searchTerm && [self.articles count] == 0);
 }
 
+- (MWKHistoryDiscoveryMethod)discoveryMethod {
+    return MWKHistoryDiscoveryMethodSearch;
+}
+
 @end
 
 NS_ASSUME_NONNULL_END
diff --git a/Wikipedia/UI-V5/iPhone_Root.storyboard 
b/Wikipedia/UI-V5/iPhone_Root.storyboard
index 186dbc2..8f298a4 100644
--- a/Wikipedia/UI-V5/iPhone_Root.storyboard
+++ b/Wikipedia/UI-V5/iPhone_Root.storyboard
@@ -207,6 +207,7 @@
                     </tabBar>
                     <connections>
                         <segue destination="qVm-oc-aaW" kind="relationship" 
relationship="viewControllers" id="Edk-Rh-NmJ"/>
+                        <segue destination="2yz-KN-V6a" kind="relationship" 
relationship="viewControllers" id="vxH-Ku-Dua"/>
                     </connections>
                 </tabBarController>
                 <placeholder placeholderIdentifier="IBFirstResponder" 
id="mEs-xw-QRr" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -258,7 +259,7 @@
                             <outlet property="delegate" 
destination="qVm-oc-aaW" id="uJd-6E-G6k"/>
                         </connections>
                     </collectionView>
-                    <tabBarItem key="tabBarItem" title="Item" id="V9D-BB-B3n"/>
+                    <tabBarItem key="tabBarItem" title="Saved" 
id="V9D-BB-B3n"/>
                 </collectionViewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" 
id="Buh-ul-uFW" userLabel="First Responder" sceneMemberID="firstResponder"/>
             </objects>
@@ -380,6 +381,57 @@
             </objects>
             <point key="canvasLocation" x="1715" y="75"/>
         </scene>
+        <!--Recent Pages List-->
+        <scene sceneID="bR7-pf-lJf">
+            <objects>
+                <collectionViewController 
storyboardIdentifier="WMFArticleListCollectionViewController_Recent" 
useStoryboardIdentifierAsRestorationIdentifier="YES" id="2yz-KN-V6a" 
userLabel="Recent Pages List" 
customClass="WMFArticleListCollectionViewController" 
sceneMemberID="viewController">
+                    <collectionView key="view" clipsSubviews="YES" 
multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" 
dataMode="prototypes" id="TGO-DA-pJe">
+                        <rect key="frame" x="0.0" y="0.0" width="600" 
height="536"/>
+                        <autoresizingMask key="autoresizingMask" 
widthSizable="YES" heightSizable="YES"/>
+                        <animations/>
+                        <collectionViewLayout key="collectionViewLayout" 
id="ETy-nN-umd" customClass="TGLStackedLayout"/>
+                        <cells>
+                            <collectionViewCell opaque="NO" 
clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" 
reuseIdentifier="WMFArticleViewControllerContainerCell" id="lcF-U0-4hY" 
customClass="WMFArticleViewControllerContainerCell">
+                                <rect key="frame" x="0.0" y="0.0" width="50" 
height="50"/>
+                                <autoresizingMask key="autoresizingMask"/>
+                                <view key="contentView" opaque="NO" 
clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                                    <rect key="frame" x="0.0" y="0.0" 
width="50" height="50"/>
+                                    <autoresizingMask key="autoresizingMask"/>
+                                    <subviews>
+                                        <view contentMode="scaleToFill" 
translatesAutoresizingMaskIntoConstraints="NO" id="Qbh-N0-jBQ" 
customClass="WMFForwardingTouchesView">
+                                            <rect key="frame" x="0.0" y="0.0" 
width="50" height="50"/>
+                                            <animations/>
+                                            <color key="backgroundColor" 
white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                                        </view>
+                                    </subviews>
+                                    <animations/>
+                                    <color key="backgroundColor" white="0.0" 
alpha="0.0" colorSpace="calibratedWhite"/>
+                                </view>
+                                <animations/>
+                                <color key="backgroundColor" white="1" 
alpha="1" colorSpace="calibratedWhite"/>
+                                <constraints>
+                                    <constraint firstAttribute="bottom" 
secondItem="Qbh-N0-jBQ" secondAttribute="bottom" id="1bs-9J-MC2"/>
+                                    <constraint firstItem="Qbh-N0-jBQ" 
firstAttribute="leading" secondItem="lcF-U0-4hY" secondAttribute="leading" 
id="OL3-2j-DYi"/>
+                                    <constraint firstAttribute="trailing" 
secondItem="Qbh-N0-jBQ" secondAttribute="trailing" id="ch9-he-QKU"/>
+                                    <constraint firstItem="Qbh-N0-jBQ" 
firstAttribute="top" secondItem="lcF-U0-4hY" secondAttribute="top" 
id="xLY-du-esw"/>
+                                </constraints>
+                                <size key="customSize" width="50" height="50"/>
+                                <connections>
+                                    <outlet property="touchView" 
destination="Qbh-N0-jBQ" id="vka-h8-zug"/>
+                                </connections>
+                            </collectionViewCell>
+                        </cells>
+                        <connections>
+                            <outlet property="dataSource" 
destination="2yz-KN-V6a" id="dqQ-eP-Tyk"/>
+                            <outlet property="delegate" 
destination="2yz-KN-V6a" id="v1H-vu-Lg3"/>
+                        </connections>
+                    </collectionView>
+                    <tabBarItem key="tabBarItem" title="Recent" 
id="Hg0-Uc-GUG"/>
+                </collectionViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" 
id="FrI-J3-ZJ1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="1748" y="1408"/>
+        </scene>
     </scenes>
     <resources>
         <image name="logo-onboarding" width="210" height="192"/>

-- 
To view, visit https://gerrit.wikimedia.org/r/224631
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ic47b09dbc5ee4b4ba1f8b2773932c7fd9291b470
Gerrit-PatchSet: 2
Gerrit-Project: apps/ios/wikipedia
Gerrit-Branch: 5.0
Gerrit-Owner: Fjalapeno <[email protected]>
Gerrit-Reviewer: Bgerstle <[email protected]>
Gerrit-Reviewer: Mhurd <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to