Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for nested global entries #651

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

#pragma mark - FLEXFilteringTableViewController
/// A table view which implements \c UITableView* methods using arrays of
/// \c FLEXTableViewSection objects provied by a special delegate.
/// \c FLEXTableViewSection objects provided by a special delegate.
@interface FLEXFilteringTableViewController : FLEXTableViewController <FLEXTableViewFiltering>

/// Stores the current search query.
Expand Down
7 changes: 6 additions & 1 deletion Classes/ExplorerInterface/FLEXExplorerViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#import "FLEXWindowManagerController.h"
#import "FLEXViewControllersViewController.h"
#import "NSUserDefaults+FLEX.h"
#import "FLEXManager+Extensibility.h"
#import "FLEXUserGlobalEntriesContainer+Private.h"

typedef NS_ENUM(NSUInteger, FLEXExplorerMode) {
FLEXExplorerModeDefault,
Expand Down Expand Up @@ -1030,7 +1032,10 @@ - (void)toggleViewsToolWithCompletion:(void(^)(void))completion {

- (void)toggleMenuTool {
[self toggleToolWithViewControllerProvider:^UINavigationController *{
return [FLEXNavigationController withRootViewController:[FLEXGlobalsViewController new]];
FLEXGlobalsViewController *controller = [FLEXGlobalsViewController new];
controller.customEntries = FLEXManager.sharedManager.globalEntriesContainer.entries;
controller.showsDefaultEntries = YES;
return [FLEXNavigationController withRootViewController:controller];
} completion:nil];
}

Expand Down
14 changes: 12 additions & 2 deletions Classes/ExplorerInterface/Tabs/FLEXTabsViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#import "FLEXExplorerViewController.h"
#import "FLEXGlobalsViewController.h"
#import "FLEXBookmarksViewController.h"
#import "FLEXManager+Extensibility.h"
#import "FLEXUserGlobalEntriesContainer+Private.h"

@interface FLEXTabsViewController ()
@property (nonatomic, copy) NSArray<UINavigationController *> *openTabs;
Expand Down Expand Up @@ -198,8 +200,12 @@ - (void)addTabButtonPressed:(UIBarButtonItem *)sender {
[FLEXAlert makeSheet:^(FLEXAlert *make) {
make.title(@"New Tab");
make.button(@"Main Menu").handler(^(NSArray<NSString *> *strings) {
FLEXGlobalsViewController *controller = [FLEXGlobalsViewController new];
controller.customEntries = FLEXManager.sharedManager.globalEntriesContainer.entries;
controller.showsDefaultEntries = YES;

[self addTabAndDismiss:[FLEXNavigationController
withRootViewController:[FLEXGlobalsViewController new]
withRootViewController:controller
]];
});
make.button(@"Choose from Bookmarks").handler(^(NSArray<NSString *> *strings) {
Expand All @@ -210,9 +216,13 @@ - (void)addTabButtonPressed:(UIBarButtonItem *)sender {
make.button(@"Cancel").cancelStyle();
} showFrom:self source:sender];
} else {
FLEXGlobalsViewController *controller = [FLEXGlobalsViewController new];
controller.customEntries = FLEXManager.sharedManager.globalEntriesContainer.entries;
controller.showsDefaultEntries = YES;

// No bookmarks, just open the main menu
[self addTabAndDismiss:[FLEXNavigationController
withRootViewController:[FLEXGlobalsViewController new]
withRootViewController:controller
]];
}
}
Expand Down
13 changes: 11 additions & 2 deletions Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

NS_ASSUME_NONNULL_BEGIN

@class FLEXUserGlobalEntriesContainer;

typedef NS_ENUM(NSUInteger, FLEXGlobalsRow) {
FLEXGlobalsRowProcessInfo,
FLEXGlobalsRowNetworkHistory,
Expand Down Expand Up @@ -51,8 +53,10 @@ typedef UIViewController * _Nullable (^FLEXGlobalsEntryViewControllerFuture)(voi
/// Do something like present an alert, then use the host
/// view controller to present or push another view controller.
typedef void (^FLEXGlobalsEntryRowAction)(__kindof UITableViewController * _Nonnull host);
/// Use the container to register nested global entries.
typedef void (^FLEXNestedGlobalEntriesHandler)(FLEXUserGlobalEntriesContainer * _Nonnull container);

/// For view controllers to conform to to indicate they support being used
/// For view controllers to conform and to indicate they support being used
/// in the globals table view controller. These methods help create concrete entries.
///
/// Previously, the concrete entries relied on "futures" for the view controller and title.
Expand Down Expand Up @@ -81,15 +85,20 @@ typedef void (^FLEXGlobalsEntryRowAction)(__kindof UITableViewController * _Nonn
@interface FLEXGlobalsEntry : NSObject

@property (nonatomic, readonly, nonnull) FLEXGlobalsEntryNameFuture entryNameFuture;
@property (nonatomic, readonly) UITableViewCellAccessoryType cellAccessoryType;
@property (nonatomic, readonly, nullable) FLEXGlobalsEntryViewControllerFuture viewControllerFuture;
@property (nonatomic, readonly, nullable) FLEXGlobalsEntryRowAction rowAction;

+ (instancetype)entryWithEntry:(Class<FLEXGlobalsEntry>)entry row:(FLEXGlobalsRow)row;
+ (instancetype)entryWithEntry:(Class<FLEXGlobalsEntry>)entry
cellAccessoryType:(UITableViewCellAccessoryType)cellAccessoryType
row:(FLEXGlobalsRow)row;

+ (instancetype)entryWithNameFuture:(FLEXGlobalsEntryNameFuture)nameFuture
cellAccessoryType:(UITableViewCellAccessoryType)cellAccessoryType
viewControllerFuture:(FLEXGlobalsEntryViewControllerFuture)viewControllerFuture;

+ (instancetype)entryWithNameFuture:(FLEXGlobalsEntryNameFuture)nameFuture
cellAccessoryType:(UITableViewCellAccessoryType)cellAccessoryType
action:(FLEXGlobalsEntryRowAction)rowSelectedAction;

@end
Expand Down
14 changes: 12 additions & 2 deletions Classes/GlobalStateExplorers/Globals/FLEXGlobalsEntry.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@

@implementation FLEXGlobalsEntry

+ (instancetype)entryWithEntry:(Class<FLEXGlobalsEntry>)cls row:(FLEXGlobalsRow)row {
+ (instancetype)entryWithEntry:(Class<FLEXGlobalsEntry>)cls
cellAccessoryType:(UITableViewCellAccessoryType)cellAccessoryType
row:(FLEXGlobalsRow)row {
BOOL providesVCs = [cls respondsToSelector:@selector(globalsEntryViewController:)];
BOOL providesActions = [cls respondsToSelector:@selector(globalsEntryRowAction:)];
NSParameterAssert(cls);
NSParameterAssert(providesVCs || providesActions);

FLEXGlobalsEntry *entry = [self new];
entry->_entryNameFuture = ^{ return [cls globalsEntryTitle:row]; };
entry->_cellAccessoryType = cellAccessoryType;

if (providesVCs) {
id action = providesActions ? [cls globalsEntryRowAction:row] : nil;
Expand All @@ -34,24 +37,28 @@ + (instancetype)entryWithEntry:(Class<FLEXGlobalsEntry>)cls row:(FLEXGlobalsRow)
}

+ (instancetype)entryWithNameFuture:(FLEXGlobalsEntryNameFuture)nameFuture
cellAccessoryType:(UITableViewCellAccessoryType)cellAccessoryType
viewControllerFuture:(FLEXGlobalsEntryViewControllerFuture)viewControllerFuture {
NSParameterAssert(nameFuture);
NSParameterAssert(viewControllerFuture);

FLEXGlobalsEntry *entry = [self new];
entry->_entryNameFuture = [nameFuture copy];
entry->_cellAccessoryType = cellAccessoryType;
entry->_viewControllerFuture = [viewControllerFuture copy];

return entry;
}

+ (instancetype)entryWithNameFuture:(FLEXGlobalsEntryNameFuture)nameFuture
cellAccessoryType:(UITableViewCellAccessoryType)cellAccessoryType
action:(FLEXGlobalsEntryRowAction)rowSelectedAction {
NSParameterAssert(nameFuture);
NSParameterAssert(rowSelectedAction);

FLEXGlobalsEntry *entry = [self new];
entry->_entryNameFuture = [nameFuture copy];
entry->_cellAccessoryType = cellAccessoryType;
entry->_rowAction = [rowSelectedAction copy];

return entry;
Expand All @@ -77,7 +84,10 @@ @implementation NSObject (FLEXGlobalsEntry)

+ (FLEXGlobalsEntry *)flex_concreteGlobalsEntry:(FLEXGlobalsRow)row {
if ([self conformsToProtocol:@protocol(FLEXGlobalsEntry)]) {
return [FLEXGlobalsEntry entryWithEntry:self row:row];
return [FLEXGlobalsEntry entryWithEntry:self
cellAccessoryType:UITableViewCellAccessoryDisclosureIndicator
row:row
];
}

return nil;
Expand Down
2 changes: 1 addition & 1 deletion Classes/GlobalStateExplorers/Globals/FLEXGlobalsSection.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ - (UIViewController *)viewControllerToPushForRow:(NSInteger)row {
}

- (void)configureCell:(__kindof UITableViewCell *)cell forRow:(NSInteger)row {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.accessoryType = self.rows[row].cellAccessoryType;
cell.textLabel.font = UIFont.flex_defaultTableCellFont;
cell.textLabel.text = self.rows[row].entryNameFuture();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

#import "FLEXFilteringTableViewController.h"
@class FLEXGlobalsEntry;
@protocol FLEXGlobalsTableViewControllerDelegate;

typedef NS_ENUM(NSUInteger, FLEXGlobalsSectionKind) {
Expand All @@ -25,4 +26,8 @@ typedef NS_ENUM(NSUInteger, FLEXGlobalsSectionKind) {

@interface FLEXGlobalsViewController : FLEXFilteringTableViewController

@property (nonatomic, nullable) NSString *customTitle;
@property (nonatomic, nonnull) NSArray<FLEXGlobalsEntry *> *customEntries;
@property (nonatomic) BOOL showsDefaultEntries;

@end
25 changes: 14 additions & 11 deletions Classes/GlobalStateExplorers/Globals/FLEXGlobalsViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#import "FLEXCookiesViewController.h"
#import "FLEXGlobalsEntry.h"
#import "FLEXManager+Private.h"
#import "FLEXUserGlobalEntriesContainer+Private.h"
#import "FLEXSystemLogViewController.h"
#import "FLEXNetworkMITMViewController.h"
#import "FLEXAddressExplorerCoordinator.h"
Expand Down Expand Up @@ -97,10 +98,10 @@ + (FLEXGlobalsEntry *)globalsEntryForRow:(FLEXGlobalsRow)row {
case FLEXGlobalsRowMainThread:
case FLEXGlobalsRowOperationQueue:
return [FLEXObjectExplorerFactory flex_concreteGlobalsEntry:row];

case FLEXGlobalsRowCount: break;
}

@throw [NSException
exceptionWithName:NSInternalInconsistencyException
reason:@"Missing globals case in switch" userInfo:nil
Expand Down Expand Up @@ -157,7 +158,7 @@ + (FLEXGlobalsEntry *)globalsEntryForRow:(FLEXGlobalsRow)row {
[sections addObject:[FLEXGlobalsSection title:title rows:rowsBySection[@(i)]]];
}
});

return sections;
}

Expand All @@ -167,19 +168,19 @@ + (FLEXGlobalsEntry *)globalsEntryForRow:(FLEXGlobalsRow)row {
- (void)viewDidLoad {
[super viewDidLoad];

self.title = @"💪 FLEX";
self.title = self.customTitle ?: @"💪 FLEX";
self.showsSearchBar = YES;
self.searchBarDebounceInterval = kFLEXDebounceInstant;
self.navigationItem.backBarButtonItem = [UIBarButtonItem flex_backItemWithTitle:@"Back"];
self.navigationItem.backBarButtonItem = [UIBarButtonItem flex_backItemWithTitle:self.showsDefaultEntries ? @"Back" : self.title];

_manuallyDeselectOnAppear = NSProcessInfo.processInfo.operatingSystemVersion.majorVersion < 10;
}

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];

[self disableToolbar];

if (self.manuallyDeselectOnAppear) {
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
}
Expand All @@ -188,16 +189,18 @@ - (void)viewWillAppear:(BOOL)animated {
- (NSArray<FLEXGlobalsSection *> *)makeSections {
NSMutableArray<FLEXGlobalsSection *> *sections = [NSMutableArray array];
// Do we have custom sections to add?
if (FLEXManager.sharedManager.userGlobalEntries.count) {
if (self.customEntries.count) {
NSString *title = [[self class] globalsTitleForSection:FLEXGlobalsSectionCustom];
FLEXGlobalsSection *custom = [FLEXGlobalsSection
title:title
rows:FLEXManager.sharedManager.userGlobalEntries
rows:self.customEntries
];
[sections addObject:custom];
}

[sections addObjectsFromArray:[self.class defaultGlobalSections]];
if (self.showsDefaultEntries) {
[sections addObjectsFromArray:[self.class defaultGlobalSections]];
}

return sections;
}
Expand Down
121 changes: 121 additions & 0 deletions Classes/GlobalStateExplorers/Globals/FLEXUserGlobalEntriesContainer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//
// FLEXUserGlobalEntriesContainer.h
// FLEX
//
// Created by Iulian Onofrei on 2023-02-10.
// Copyright © 2023 FLEX Team. All rights reserved.
//

#import <UIKit/UIKit.h>

#import "FLEXGlobalsEntry.h"

NS_ASSUME_NONNULL_BEGIN

@interface FLEXUserGlobalEntriesContainer : NSObject

/// Adds an entry at the top of the list of Global State items.
/// Call this method before this view controller is displayed.
/// @param entryName The string to be displayed in the cell.
/// @param objectFutureBlock When you tap on the row, information about the object returned
/// by this block will be displayed. Passing a block that returns an object allows you to display
/// information about an object whose actual pointer may change at runtime (e.g. +currentUser)
/// @note This method must be called from the main thread.
/// The objectFutureBlock will be invoked from the main thread and may return nil.
/// @note The passed block will be copied and retain for the duration of the application,
/// you may want to use __weak references.
- (void)registerGlobalEntryWithName:(NSString *)entryName objectFutureBlock:(id (^)(void))objectFutureBlock;

/// Adds an entry at the top of the list of Global State items.
/// Call this method before this view controller is displayed.
/// @param entryName The string to be displayed in the cell.
/// @param cellAccessoryType The accessory type to be used for the cell.
/// @param objectFutureBlock When you tap on the row, information about the object returned
/// by this block will be displayed. Passing a block that returns an object allows you to display
/// information about an object whose actual pointer may change at runtime (e.g. +currentUser)
/// @note This method must be called from the main thread.
/// The objectFutureBlock will be invoked from the main thread and may return nil.
/// @note The passed block will be copied and retain for the duration of the application,
/// you may want to use __weak references.
- (void)registerGlobalEntryWithName:(NSString *)entryName
cellAccessoryType:(UITableViewCellAccessoryType)cellAccessoryType
objectFutureBlock:(id (^)(void))objectFutureBlock;

/// Adds an entry at the top of the list of Global State items.
/// Call this method before this view controller is displayed.
/// @param entryName The string to be displayed in the cell.
/// @param viewControllerFutureBlock When you tap on the row, view controller returned
/// by this block will be pushed on the navigation controller stack.
/// @note This method must be called from the main thread.
/// The viewControllerFutureBlock will be invoked from the main thread and may not return nil.
/// @note The passed block will be copied and retain for the duration of the application,
/// you may want to use __weak references as needed.
- (void)registerGlobalEntryWithName:(NSString *)entryName
viewControllerFutureBlock:(UIViewController * (^)(void))viewControllerFutureBlock;

/// Adds an entry at the top of the list of Global State items.
/// Call this method before this view controller is displayed.
/// @param entryName The string to be displayed in the cell.
/// @param cellAccessoryType The accessory type to be used for the cell.
/// @param viewControllerFutureBlock When you tap on the row, view controller returned
/// by this block will be pushed on the navigation controller stack.
/// @note This method must be called from the main thread.
/// The viewControllerFutureBlock will be invoked from the main thread and may not return nil.
/// @note The passed block will be copied and retain for the duration of the application,
/// you may want to use __weak references as needed.
- (void)registerGlobalEntryWithName:(NSString *)entryName
cellAccessoryType:(UITableViewCellAccessoryType)cellAccessoryType
viewControllerFutureBlock:(UIViewController * (^)(void))viewControllerFutureBlock;

/// Adds an entry at the top of the list of Global State items.
/// @param entryName The string to be displayed in the cell.
/// @param rowSelectedAction When you tap on the row, this block will be invoked
/// with the host table view view controller. Use it to deselect the row or present an alert.
/// @note This method must be called from the main thread.
/// The rowSelectedAction will be invoked from the main thread.
/// @note The passed block will be copied and retained for the duration of the application,
/// you may want to use __weak references as needed.
- (void)registerGlobalEntryWithName:(NSString *)entryName action:(FLEXGlobalsEntryRowAction)rowSelectedAction;

/// Adds an entry at the top of the list of Global State items.
/// @param entryName The string to be displayed in the cell.
/// @param cellAccessoryType The accessory type to be used for the cell.
/// @param rowSelectedAction When you tap on the row, this block will be invoked
/// with the host table view view controller. Use it to deselect the row or present an alert.
/// @note This method must be called from the main thread.
/// The rowSelectedAction will be invoked from the main thread.
/// @note The passed block will be copied and retained for the duration of the application,
/// you may want to use __weak references as needed.
- (void)registerGlobalEntryWithName:(NSString *)entryName
cellAccessoryType:(UITableViewCellAccessoryType)cellAccessoryType
action:(FLEXGlobalsEntryRowAction)rowSelectedAction;

/// Adds an entry at the top of the list of Global State items.
/// @param entryName The string to be displayed in the cell.
/// @param nestedEntriesHandler When you tap on the row, this block will be invoked
/// with the container object. Use it to register nested entries.
/// @note This method must be called from the main thread.
/// The nestedEntriesHandler will be invoked from the main thread.
/// @note The passed block will be copied and retained for the duration of the application,
/// you may want to use __weak references as needed.
- (void)registerNestedGlobalEntryWithName:(NSString *)entryName handler:(FLEXNestedGlobalEntriesHandler)nestedEntriesHandler;

/// Adds an entry at the top of the list of Global State items.
/// @param entryName The string to be displayed in the cell.
/// @param cellAccessoryType The accessory type to be used for the cell.
/// @param nestedEntriesHandler When you tap on the row, this block will be invoked
/// with the container object. Use it to register nested entries.
/// @note This method must be called from the main thread.
/// The nestedEntriesHandler will be invoked from the main thread.
/// @note The passed block will be copied and retained for the duration of the application,
/// you may want to use __weak references as needed.
- (void)registerNestedGlobalEntryWithName:(NSString *)entryName
cellAccessoryType:(UITableViewCellAccessoryType)cellAccessoryType
handler:(FLEXNestedGlobalEntriesHandler)nestedEntriesHandler;

/// Removes all registered global entries.
- (void)clearGlobalEntries;

@end

NS_ASSUME_NONNULL_END
Loading