192 lines
7.5 KiB
Objective-C
192 lines
7.5 KiB
Objective-C
#import "AFNetworking.h"
|
|
#import "LauncherNavigationController.h"
|
|
#import "ModpackInstallViewController.h"
|
|
#import "UIKit+AFNetworking.h"
|
|
#import "UIKit+hook.h"
|
|
#import "WFWorkflowProgressView.h"
|
|
#import "modpack/ModrinthAPI.h"
|
|
#import "config.h"
|
|
#import "ios_uikit_bridge.h"
|
|
#import "utils.h"
|
|
#include <dlfcn.h>
|
|
|
|
#define kCurseForgeGameIDMinecraft 432
|
|
#define kCurseForgeClassIDModpack 4471
|
|
#define kCurseForgeClassIDMod 6
|
|
|
|
@interface ModpackInstallViewController()<UIContextMenuInteractionDelegate>
|
|
@property(nonatomic) UISearchController *searchController;
|
|
@property(nonatomic) UIMenu *currentMenu;
|
|
@property(nonatomic) NSMutableArray *list;
|
|
@property(nonatomic) NSMutableDictionary *filters;
|
|
@property ModrinthAPI *modrinth;
|
|
@end
|
|
|
|
@implementation ModpackInstallViewController
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
|
|
//NSString *curseforgeAPIKey = CONFIG_CURSEFORGE_API_KEY;
|
|
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
|
|
self.searchController.searchResultsUpdater = self;
|
|
self.searchController.obscuresBackgroundDuringPresentation = NO;
|
|
self.navigationItem.searchController = self.searchController;
|
|
self.modrinth = [ModrinthAPI new];
|
|
self.filters = @{
|
|
@"isModpack": @(YES),
|
|
@"name": @" "
|
|
// mcVersion
|
|
}.mutableCopy;
|
|
[self updateSearchResults];
|
|
}
|
|
|
|
- (void)loadSearchResultsWithPrevList:(BOOL)prevList {
|
|
NSString *name = self.searchController.searchBar.text;
|
|
if (!prevList && [self.filters[@"name"] isEqualToString:name]) {
|
|
return;
|
|
}
|
|
|
|
[self switchToLoadingState];
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
self.filters[@"name"] = name;
|
|
self.list = [self.modrinth searchModWithFilters:self.filters previousPageResult:prevList ? self.list : nil];
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (self.list) {
|
|
[self switchToReadyState];
|
|
[self.tableView reloadData];
|
|
} else {
|
|
showDialog(localize(@"Error", nil), self.modrinth.lastError.localizedDescription);
|
|
[self actionClose];
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
- (void)updateSearchResults {
|
|
[self loadSearchResultsWithPrevList:NO];
|
|
}
|
|
|
|
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(updateSearchResults) object:nil];
|
|
[self performSelector:@selector(updateSearchResults) withObject:nil afterDelay:0.5];
|
|
}
|
|
|
|
- (void)actionClose {
|
|
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
|
}
|
|
|
|
- (void)switchToLoadingState {
|
|
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
|
|
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:indicator];
|
|
[indicator startAnimating];
|
|
self.navigationController.modalInPresentation = YES;
|
|
self.tableView.allowsSelection = NO;
|
|
}
|
|
|
|
- (void)switchToReadyState {
|
|
UIActivityIndicatorView *indicator = (id)self.navigationItem.rightBarButtonItem.customView;
|
|
[indicator stopAnimating];
|
|
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemClose target:self action:@selector(actionClose)];
|
|
self.navigationController.modalInPresentation = NO;
|
|
self.tableView.allowsSelection = YES;
|
|
}
|
|
|
|
#pragma mark UIContextMenu
|
|
|
|
- (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location
|
|
{
|
|
return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:^UIMenu * _Nullable(NSArray<UIMenuElement *> * _Nonnull suggestedActions) {
|
|
return self.currentMenu;
|
|
}];
|
|
}
|
|
|
|
- (_UIContextMenuStyle *)_contextMenuInteraction:(UIContextMenuInteraction *)interaction styleForMenuWithConfiguration:(UIContextMenuConfiguration *)configuration
|
|
{
|
|
_UIContextMenuStyle *style = [_UIContextMenuStyle defaultStyle];
|
|
style.preferredLayout = 3; // _UIContextMenuLayoutCompactMenu
|
|
return style;
|
|
}
|
|
|
|
#pragma mark UITableViewDataSource
|
|
|
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
|
return 1;
|
|
}
|
|
|
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
|
return self.list.count;
|
|
}
|
|
|
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
|
|
if (!cell) {
|
|
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];
|
|
cell.imageView.contentMode = UIViewContentModeScaleToFill;
|
|
cell.imageView.clipsToBounds = YES;
|
|
}
|
|
|
|
NSDictionary *item = self.list[indexPath.row];
|
|
cell.textLabel.text = item[@"title"];
|
|
cell.detailTextLabel.text = item[@"description"];
|
|
UIImage *fallbackImage = [UIImage imageNamed:@"DefaultProfile"];
|
|
[cell.imageView setImageWithURL:[NSURL URLWithString:item[@"imageUrl"]] placeholderImage:fallbackImage];
|
|
|
|
if (!self.modrinth.reachedLastPage && indexPath.row == self.list.count-1) {
|
|
[self loadSearchResultsWithPrevList:YES];
|
|
}
|
|
|
|
return cell;
|
|
}
|
|
|
|
- (void)showDetails:(NSDictionary *)details atIndexPath:(NSIndexPath *)indexPath {
|
|
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
|
|
|
|
NSMutableArray<UIAction *> *menuItems = [[NSMutableArray alloc] init];
|
|
[details[@"versionNames"] enumerateObjectsUsingBlock:
|
|
^(NSString *name, NSUInteger i, BOOL *stop) {
|
|
NSString *nameWithVersion = name;
|
|
NSString *mcVersion = details[@"mcVersionNames"][i];
|
|
if (![name hasSuffix:mcVersion]) {
|
|
nameWithVersion = [NSString stringWithFormat:@"%@ - %@", name, mcVersion];
|
|
}
|
|
[menuItems addObject:[UIAction
|
|
actionWithTitle:nameWithVersion
|
|
image:nil identifier:nil
|
|
handler:^(UIAction *action) {
|
|
[self actionClose];
|
|
NSString *tmpIconPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"icon.png"];
|
|
[UIImagePNGRepresentation([cell.imageView.image _imageWithSize:CGSizeMake(40, 40)]) writeToFile:tmpIconPath atomically:YES];
|
|
[self.modrinth installModpackFromDetail:self.list[indexPath.row] atIndex:i];
|
|
}]];
|
|
}];
|
|
|
|
self.currentMenu = [UIMenu menuWithTitle:@"" children:menuItems];
|
|
UIContextMenuInteraction *interaction = [[UIContextMenuInteraction alloc] initWithDelegate:self];
|
|
cell.detailTextLabel.interactions = @[interaction];
|
|
[interaction _presentMenuAtLocation:CGPointZero];
|
|
}
|
|
|
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
NSDictionary *item = self.list[indexPath.row];
|
|
if ([item[@"versionDetailsLoaded"] boolValue]) {
|
|
[self showDetails:item atIndexPath:indexPath];
|
|
return;
|
|
}
|
|
[tableView deselectRowAtIndexPath:indexPath animated:NO];
|
|
[self switchToLoadingState];
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
[self.modrinth loadDetailsOfMod:self.list[indexPath.row]];
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self switchToReadyState];
|
|
if ([item[@"versionDetailsLoaded"] boolValue]) {
|
|
[self showDetails:item atIndexPath:indexPath];
|
|
} else {
|
|
showDialog(localize(@"Error", nil), self.modrinth.lastError.localizedDescription);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
@end
|