mirror of
https://github.com/faroukbmiled/RyukGram.git
synced 2026-07-30 07:58:54 +02:00
modded scinsta with additional features and fixes for recent instagram version
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
#import "../../Utils.h"
|
||||
#import "../../InstagramHeaders.h"
|
||||
#import "../../../modules/JGProgressHUD/JGProgressHUD.h"
|
||||
|
||||
%hook IGCoreTextView
|
||||
- (void)didMoveToSuperview {
|
||||
%orig;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"copy_description"]) {
|
||||
[self addHandleLongPress];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
%new - (void)addHandleLongPress {
|
||||
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
||||
longPress.minimumPressDuration = 0.5;
|
||||
[self addGestureRecognizer:longPress];
|
||||
}
|
||||
|
||||
%new - (void)handleLongPress:(UILongPressGestureRecognizer *)sender {
|
||||
if (sender.state != UIGestureRecognizerStateBegan) return;
|
||||
|
||||
// Remove hashtags at end of string
|
||||
NSRegularExpression *regex =
|
||||
[NSRegularExpression regularExpressionWithPattern:@"\\s*(?:#[^\\s]+\\s*)+$"
|
||||
options:0
|
||||
error:nil];
|
||||
|
||||
NSString *result = [[regex stringByReplacingMatchesInString:self.text
|
||||
options:0
|
||||
range:NSMakeRange(0, self.text.length)
|
||||
withTemplate:@""]
|
||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
|
||||
NSLog(@"[SCInsta] Copying description");
|
||||
|
||||
// Copy text to system clipboard
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
pasteboard.string = result;
|
||||
|
||||
// Notify user
|
||||
JGProgressHUD *HUD = [[JGProgressHUD alloc] init];
|
||||
HUD.textLabel.text = @"Copied text to clipboard";
|
||||
HUD.indicatorView = [[JGProgressHUDSuccessIndicatorView alloc] init];
|
||||
|
||||
[HUD showInView:topMostController().view];
|
||||
[HUD dismissAfterDelay:2.0];
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,87 @@
|
||||
#import "../../InstagramHeaders.h"
|
||||
#import "../../Utils.h"
|
||||
|
||||
%hook IGStoryEyedropperToggleButton
|
||||
- (void)didMoveToWindow {
|
||||
%orig;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"detailed_color_picker"]) {
|
||||
[self addLongPressGestureRecognizer];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
%new - (void)addLongPressGestureRecognizer {
|
||||
if ([self.gestureRecognizers count] == 0) {
|
||||
NSLog(@"[SCInsta] Adding color eyedroppper long press gesture recognizer");
|
||||
|
||||
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
||||
longPress.minimumPressDuration = 0.25;
|
||||
[self addGestureRecognizer:longPress];
|
||||
}
|
||||
}
|
||||
%new - (void)handleLongPress:(UILongPressGestureRecognizer *)sender {
|
||||
if (sender.state != UIGestureRecognizerStateBegan) return;
|
||||
|
||||
UIColorPickerViewController *colorPickerController = [[UIColorPickerViewController alloc] init];
|
||||
|
||||
colorPickerController.delegate = (id<UIColorPickerViewControllerDelegate>)self; // cast to suppress warnings
|
||||
colorPickerController.title = @"Select color";
|
||||
colorPickerController.modalPresentationStyle = UIModalPresentationPopover;
|
||||
colorPickerController.supportsAlpha = NO;
|
||||
colorPickerController.selectedColor = self.color;
|
||||
|
||||
UIViewController *presentingVC = [SCIUtils nearestViewControllerForView:self];
|
||||
|
||||
if (presentingVC != nil) {
|
||||
[presentingVC presentViewController:colorPickerController animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
// UIColorPickerViewControllerDelegate Protocol
|
||||
%new - (void)colorPickerViewController:(UIColorPickerViewController *)viewController
|
||||
didSelectColor:(UIColor *)color
|
||||
continuously:(BOOL)continuously
|
||||
{
|
||||
NSLog(@"[SCInsta] Selected text color: %@", color);
|
||||
|
||||
UIColor *opaque = [color colorWithAlphaComponent:1.0];
|
||||
self.color = opaque;
|
||||
|
||||
[self setPushedDown:YES];
|
||||
|
||||
// Trigger change for text color
|
||||
id presentingVC = [SCIUtils nearestViewControllerForView:self];
|
||||
|
||||
if ([presentingVC isKindOfClass:%c(IGStoryTextEntryViewController)]) {
|
||||
[presentingVC textViewControllerDidUpdateWithColor:color colorSource:0];
|
||||
}
|
||||
else if (
|
||||
[presentingVC isKindOfClass:%c(IGStoryCreationDrawingViewController)]
|
||||
|| [presentingVC isKindOfClass:%c(IGDirectThreadViewDrawingViewController)]
|
||||
) {
|
||||
[presentingVC drawingControls:nil didSelectColor:color];
|
||||
}
|
||||
|
||||
};
|
||||
%end
|
||||
|
||||
%hook IGStoryColorPaletteView
|
||||
- (CGFloat)collectionView:(id)view didSelectItemAtIndexPath:(id)index {
|
||||
UIView *colorPickingControls = [self superview];
|
||||
|
||||
if (
|
||||
[colorPickingControls isKindOfClass:%c(IGStoryColorPickingControls)]
|
||||
|| [colorPickingControls isKindOfClass:%c(IGDirectThreadColorPickingControls)]
|
||||
) {
|
||||
IGStoryEyedropperToggleButton *_eyedropperToggleButton = MSHookIvar<IGStoryEyedropperToggleButton *>(colorPickingControls, "_eyedropperToggleButton");
|
||||
|
||||
if (_eyedropperToggleButton != nil) {
|
||||
[_eyedropperToggleButton setPushedDown:NO];
|
||||
}
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,36 @@
|
||||
#import "../../Utils.h"
|
||||
#import "../../InstagramHeaders.h"
|
||||
|
||||
%hook IGUnifiedVideoCollectionView
|
||||
- (void)didMoveToWindow {
|
||||
%orig;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"disable_scrolling_reels"]) {
|
||||
NSLog(@"[SCInsta] Disabling scrolling reels");
|
||||
|
||||
self.scrollEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setScrollEnabled:(BOOL)arg1 {
|
||||
if ([SCIUtils getBoolPref:@"disable_scrolling_reels"]) {
|
||||
NSLog(@"[SCInsta] Disabling scrolling reels");
|
||||
|
||||
return %orig(NO);
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
|
||||
// Disable auto-scrolling reels
|
||||
%hook _TtC19IGSundialAutoScroll19IGSundialAutoScroll
|
||||
- (void)setIsEnabled:(BOOL)enabled {
|
||||
if ([SCIUtils getBoolPref:@"disable_scrolling_reels"]) {
|
||||
%orig(NO);
|
||||
}
|
||||
else {
|
||||
%orig(enabled);
|
||||
}
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,31 @@
|
||||
#import "../../Utils.h"
|
||||
#import "../../InstagramHeaders.h"
|
||||
|
||||
%hook IGExploreGridViewController
|
||||
- (void)viewDidLoad {
|
||||
if ([SCIUtils getBoolPref:@"hide_explore_grid"]) {
|
||||
NSLog(@"[SCInsta] Hiding explore grid");
|
||||
|
||||
[[self view] removeFromSuperview];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
|
||||
%hook IGExploreViewController
|
||||
- (void)viewDidLoad {
|
||||
%orig;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"hide_explore_grid"]) {
|
||||
NSLog(@"[SCInsta] Hiding explore grid");
|
||||
|
||||
IGShimmeringGridView *shimmeringGridView = MSHookIvar<IGShimmeringGridView *>(self, "_shimmeringGridView");
|
||||
if (shimmeringGridView != nil) {
|
||||
[shimmeringGridView removeFromSuperview];
|
||||
}
|
||||
}
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,33 @@
|
||||
#import "../../Utils.h"
|
||||
|
||||
%hook IGDirectNotesTrayRowCell
|
||||
- (id)listAdapterObjects {
|
||||
NSArray *originalObjs = %orig();
|
||||
NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]];
|
||||
|
||||
for (id obj in originalObjs) {
|
||||
BOOL shouldHide = NO;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"hide_friends_map"]) {
|
||||
|
||||
if ([obj isKindOfClass:%c(IGDirectNotesTrayUserViewModel)]) {
|
||||
|
||||
if ([[obj valueForKey:@"notePk"] isEqualToString:@"friends_map"]) {
|
||||
NSLog(@"[SCInsta] Hiding friends map");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Populate new objs array
|
||||
if (!shouldHide) {
|
||||
[filteredObjs addObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
return [filteredObjs copy];
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,572 @@
|
||||
#import "../../Utils.h"
|
||||
#import "../../InstagramHeaders.h"
|
||||
|
||||
// Direct
|
||||
|
||||
// Meta AI button functionality on direct search bar
|
||||
%hook IGDirectInboxViewController
|
||||
- (void)searchBarMetaAIButtonTappedOnSearchBar:(id)arg1 {
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"])
|
||||
{
|
||||
NSLog(@"[SCInsta] Hiding meta ai: direct search bar functionality");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
|
||||
// AI agents in direct new message view
|
||||
%hook IGDirectRecipientGenAIBotsResult
|
||||
- (id)initWithGenAIBots:(id)arg1 lastFetchedTimestamp:(id)arg2 {
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"])
|
||||
{
|
||||
NSLog(@"[SCInsta] Hiding meta ai: direct recipient ai agents");
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
|
||||
// Meta AI in message composer
|
||||
%hook IGDirectCommandSystemListViewController
|
||||
- (id)objectsForListAdapter:(id)arg1 {
|
||||
NSArray *originalObjs = %orig();
|
||||
NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]];
|
||||
|
||||
for (id obj in originalObjs) {
|
||||
BOOL shouldHide = NO;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
|
||||
if ([obj isKindOfClass:%c(IGDirectCommandSystemViewModel)]) {
|
||||
IGDirectCommandSystemViewModel *typedObj = (IGDirectCommandSystemViewModel *)obj;
|
||||
IGDirectCommandSystemRow *cmdSystemRow = (IGDirectCommandSystemRow *)[typedObj row];
|
||||
|
||||
IGDirectCommandSystemResult *_commandResult_command = MSHookIvar<IGDirectCommandSystemResult *>(cmdSystemRow, "_commandResult_command");
|
||||
|
||||
if (_commandResult_command != nil) {
|
||||
|
||||
// Meta AI
|
||||
if ([[_commandResult_command title] isEqualToString:@"Meta AI"]) {
|
||||
NSLog(@"[SCInsta] Hiding meta ai: direct message composer suggestion");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
// Meta AI (Imagine)
|
||||
else if ([[_commandResult_command commandString] hasPrefix:@"/imagine"]) {
|
||||
NSLog(@"[SCInsta] Hiding meta ai: direct message composer /imagine suggestion");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Populate new objs array
|
||||
if (!shouldHide) {
|
||||
[filteredObjs addObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
return [filteredObjs copy];
|
||||
}
|
||||
%end
|
||||
|
||||
// Suggested AI chats in direct inbox header
|
||||
%hook IGDirectInboxNavigationHeaderView
|
||||
- (id)initWithFrame:(CGRect)arg1
|
||||
title:(id)arg2
|
||||
titleView:(id)arg3
|
||||
directInboxConfig:(IGDirectInboxConfig *)config
|
||||
userSession:(id)arg5
|
||||
loggingDelegate:(id)arg6
|
||||
{
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
NSLog(@"[SCInsta] Hiding meta ai: suggested ai chats in direct inbox header");
|
||||
|
||||
@try {
|
||||
[config setValue:0 forKey:@"shouldShowAIChatsEntrypointButton"];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"[SCInsta] WARNING: %@\n\nFull object: %@", exception.reason, config);
|
||||
}
|
||||
}
|
||||
|
||||
return %orig(arg1, arg2, arg3, [config copy], arg5, arg6);
|
||||
}
|
||||
%end
|
||||
|
||||
// Meta AI "imagine" in media picker
|
||||
%hook IGDirectMediaPickerViewController
|
||||
- (id)initWithUserSession:(id)arg1
|
||||
config:(IGDirectMediaPickerConfig *)config
|
||||
capabilities:(id)arg3
|
||||
threadMetadata:(id)arg4
|
||||
messageSender:(id)arg5
|
||||
threadAnalyticsLogger:(id)arg6
|
||||
multimodalPerfLogger:(id)arg7
|
||||
localSendSpeedLogger:(id)arg8
|
||||
sendAttributionFactory:(id)arg9
|
||||
{
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
NSLog(@"[SCInsta] Hiding meta ai: imagine tile in media picker");
|
||||
|
||||
@try {
|
||||
IGDirectMediaPickerGalleryConfig *galleryConfig = [config valueForKey:@"galleryConfig"];
|
||||
|
||||
[galleryConfig setValue:0 forKey:@"isImagineEntryPointEnabled"];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"[SCInsta] WARNING: %@\n\nFull object: %@", exception.reason, config);
|
||||
}
|
||||
}
|
||||
|
||||
return %orig(arg1, [config copy], arg3, arg4, arg5, arg6, arg7, arg8, arg9);
|
||||
}
|
||||
%end
|
||||
|
||||
// Write with meta ai in message composer
|
||||
%hook IGDirectComposer
|
||||
- (id)initWithLayoutSpecProvider:(id)arg1
|
||||
userLauncherSetProviding:(id)arg2
|
||||
config:(IGDirectComposerConfig *)config
|
||||
style:(id)arg4
|
||||
text:(id)arg5
|
||||
{
|
||||
return %orig(arg1, arg2, [self patchConfig:config], arg4, arg5);
|
||||
}
|
||||
|
||||
- (id)initWithLayoutSpecProvider:(id)arg1
|
||||
userLauncherSetProviding:(id)arg2
|
||||
config:(IGDirectComposerConfig *)config
|
||||
style:(id)arg4
|
||||
text:(id)arg5
|
||||
shouldUpdateModeLater:(BOOL)arg6
|
||||
{
|
||||
return %orig(arg1, arg2, [self patchConfig:config], arg4, arg5, arg6);
|
||||
}
|
||||
|
||||
- (void)setConfig:(IGDirectComposerConfig *)config {
|
||||
%orig([self patchConfig:config]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
%new - (IGDirectComposerConfig *)patchConfig:(IGDirectComposerConfig *)config {
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
|
||||
NSLog(@"[SCInsta] Hiding meta ai: reconfiguring direct composer");
|
||||
|
||||
// writeWithAIEnabled
|
||||
@try {
|
||||
[config setValue:0 forKey:@"writeWithAIEnabled"];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"[SCInsta] WARNING: %@\n\nFull object: %@", exception.reason, config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return [config copy];
|
||||
}
|
||||
%end
|
||||
|
||||
// Direct sticker tray picker view
|
||||
%hook IGStickerTrayListAdapterDataSource
|
||||
- (id)objectsForListAdapter:(id)arg1 {
|
||||
NSArray *originalObjs = %orig();
|
||||
NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]];
|
||||
|
||||
for (id obj in originalObjs) {
|
||||
BOOL shouldHide = NO;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
|
||||
if ([obj isKindOfClass:%c(IGDirectUnifiedComposerAIStickerModel)]) {
|
||||
NSLog(@"[SCInsta] Hiding meta ai: AI stickers option in sticker view");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Populate new objs array
|
||||
if (!shouldHide) {
|
||||
[filteredObjs addObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
return [filteredObjs copy];
|
||||
}
|
||||
%end
|
||||
|
||||
// Long press menu on messages
|
||||
// Demangled name: IGDirectMessageMenuConfiguration.IGDirectMessageMenuConfiguration
|
||||
%hook _TtC32IGDirectMessageMenuConfiguration32IGDirectMessageMenuConfiguration
|
||||
+ (id)menuConfigurationWithEligibleOptions:(id)options
|
||||
messageViewModel:(id)arg2
|
||||
contentType:(id)arg3
|
||||
isSticker:(_Bool)arg4
|
||||
isMusicSticker:(_Bool)arg5
|
||||
directNuxManager:(id)arg6
|
||||
sessionUserDefaults:(id)arg7
|
||||
launcherSet:(id)arg8
|
||||
userSession:(id)arg9
|
||||
tapHandler:(id)arg10
|
||||
{
|
||||
// 31: Restyle
|
||||
// 41: Make AI image
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)", @[ @(31), @(41) ]];
|
||||
NSArray *newOptions = [options filteredArrayUsingPredicate:predicate];
|
||||
|
||||
return %orig([newOptions copy], arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
|
||||
}
|
||||
%end
|
||||
|
||||
// Expanded in-chat photo UI
|
||||
// Demangled name: IGDirectAggregatedMediaViewerComponentsSwift.IGDirectAggregatedMediaViewerViewControllerTitleViewModelObject
|
||||
%hook _TtC44IGDirectAggregatedMediaViewerComponentsSwift63IGDirectAggregatedMediaViewerViewControllerTitleViewModelObject
|
||||
- (id)initWithAuthorProfileImage:(id)arg1
|
||||
authorUsername:(id)arg2
|
||||
canForward:(_Bool)arg3
|
||||
canSave:(_Bool)arg4
|
||||
canAddToStory:(_Bool)arg5
|
||||
canShowAIRestyle:(_Bool)arg6
|
||||
canUnsend:(_Bool)arg7
|
||||
canReport:(_Bool)arg8
|
||||
displayConfig:(id)arg9
|
||||
isPending:(_Bool)arg10
|
||||
isMoreMenuListStyle:(_Bool)arg11
|
||||
senderIsCurrentUser:(_Bool)arg12
|
||||
shouldHideInfoViews:(_Bool)arg13
|
||||
subtitle:(id)arg14
|
||||
entryPoint:(long long)arg15
|
||||
canTapAuthor:(_Bool)arg16
|
||||
{
|
||||
BOOL showAiRestyle = [SCIUtils getBoolPref:@"hide_meta_ai"] ? false : arg6;
|
||||
|
||||
return %orig(arg1, arg2, arg3, arg4, arg5, showAiRestyle, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16);
|
||||
}
|
||||
%end
|
||||
|
||||
// AI generated DM channel themes
|
||||
%hook IGDirectThreadThemePickerViewController
|
||||
- (id)objectsForListAdapter:(id)arg1 {
|
||||
NSArray *originalObjs = %orig();
|
||||
NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]];
|
||||
|
||||
for (id obj in originalObjs) {
|
||||
BOOL shouldHide = NO;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
|
||||
if (
|
||||
[obj isKindOfClass:%c(IGDirectThreadThemePickerOption)]
|
||||
&& [[obj valueForKey:@"themeId"] isEqualToString:@"direct_ai_theme_creation"]
|
||||
) {
|
||||
NSLog(@"[SCInsta] Hiding meta ai: AI generated DM channel themes");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Populate new objs array
|
||||
if (!shouldHide) {
|
||||
[filteredObjs addObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
return [filteredObjs copy];
|
||||
}
|
||||
%end
|
||||
|
||||
// "Click to summarize" pill under DM navigation bar
|
||||
%hook IGDirectThreadViewMetaAISummaryFeatureController
|
||||
- (id)initWithUserSession:(id)arg1 mutableStateProvider:(id)arg2 threadViewControllerFeatureDelegate:(id)arg3 presentingViewController:(id)arg4 {
|
||||
return nil;
|
||||
}
|
||||
%end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Explore
|
||||
|
||||
// Meta AI explore search summary
|
||||
%hook IGDiscoveryListKitGQLDataSource
|
||||
- (id)objectsForListAdapter:(id)arg1 {
|
||||
NSArray *originalObjs = %orig();
|
||||
NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]];
|
||||
|
||||
for (id obj in originalObjs) {
|
||||
BOOL shouldHide = NO;
|
||||
|
||||
// Meta AI summary
|
||||
if ([obj isKindOfClass:%c(IGSearchMetaAIHCMModel)]) {
|
||||
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
NSLog(@"[SCInsta] Hiding explore meta ai search summary");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Populate new objs array
|
||||
if (!shouldHide) {
|
||||
[filteredObjs addObject:obj];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return [filteredObjs copy];
|
||||
}
|
||||
%end
|
||||
|
||||
// Meta AI search bar ring button
|
||||
%hook IGSearchBarDonutButton
|
||||
- (void)didMoveToWindow {
|
||||
%orig;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
}
|
||||
%end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Reels/Sundial
|
||||
|
||||
// Suggested AI searches in comment section
|
||||
%hook IGCommentThreadAICarousel
|
||||
- (id)initWithLauncherSet:(id)arg1 hasSearchPrefix:(BOOL)arg2 {
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
NSLog(@"[SCInsta] Hiding meta ai: suggested ai searches comment carousel");
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
|
||||
%hook _TtC34IGCommentThreadAICarouselPillSwift30IGCommentThreadAICarouselSwift
|
||||
- (id)initWithLauncherSet:(id)arg1 hasSearchPrefix:(BOOL)arg2 {
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
NSLog(@"[SCInsta] Hiding meta ai: suggested ai searches comment carousel");
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Story
|
||||
|
||||
// AI images "add to story" suggestion
|
||||
// Demangled name: IGGalleryDestinationToolbar.IGGalleryDestinationToolbarView
|
||||
%hook _TtC27IGGalleryDestinationToolbar31IGGalleryDestinationToolbarView
|
||||
- (void)setTools:(id)tools {
|
||||
NSArray *newTools = [tools copy];
|
||||
|
||||
NSLog(@"[SCInsta] Hiding meta ai: ai images add to story suggestion");
|
||||
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)", @[ @(10), @(11) ]];
|
||||
newTools = [tools filteredArrayUsingPredicate:predicate];
|
||||
}
|
||||
|
||||
%orig(newTools);
|
||||
|
||||
return;
|
||||
}
|
||||
%end
|
||||
|
||||
// AI generated fonts in text entry
|
||||
%hook IGCreationTextToolView
|
||||
- (id)initWithMenuConfiguration:(unsigned long long)configuration userSession:(id)session creationEntryPoint:(long long)point isAIFontsEnabled:(_Bool)enabled genAINuxManager:(id)manager showFontBadge:(_Bool)badge {
|
||||
return %orig(configuration, session, point, [SCIUtils getBoolPref:@"hide_meta_ai"] ? false : enabled, manager, badge);
|
||||
}
|
||||
%end
|
||||
|
||||
// Text rewrite in text entry
|
||||
%hook IGStoryTextMentionLocationPickerView
|
||||
- (id)initWithIsTextRewriteEnabled:(_Bool)arg1
|
||||
isImageRewriteEnabled:(_Bool)arg2
|
||||
isStackedToolSelectorEnabled:(_Bool)arg3
|
||||
isMentionLocationVisible:(_Bool)arg4
|
||||
isEnabledForFeedCaption:(_Bool)arg5
|
||||
isFeedEntryPoint:(_Bool)arg6
|
||||
{
|
||||
_Bool isTextRewriteEnabled = [SCIUtils getBoolPref:@"hide_meta_ai"] ? false : arg1;
|
||||
_Bool isImageRewriteEnabled = [SCIUtils getBoolPref:@"hide_meta_ai"] ? false : arg2;
|
||||
|
||||
return %orig(isTextRewriteEnabled, isImageRewriteEnabled, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
%end
|
||||
|
||||
// "Imagine background" in story editor vertical action bar
|
||||
%hook _TtC17IGCreationOSSwift19IGCreationHeaderBar
|
||||
- (void)setButtons:(id)buttons maxItems:(NSInteger)max {
|
||||
NSArray *filteredObjs = buttons;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
filteredObjs = [filteredObjs filteredArrayUsingPredicate:
|
||||
[NSPredicate predicateWithBlock:^BOOL(IGCreationActionBarLabeledButton *obj, NSDictionary *bindings) {
|
||||
|
||||
return !(
|
||||
obj.button
|
||||
&& [((IGCreationActionBarButton *)obj.button).accessibilityIdentifier isEqualToString:@"contextual-background"]
|
||||
);
|
||||
|
||||
}]
|
||||
];
|
||||
}
|
||||
|
||||
%orig(filteredObjs, max);
|
||||
}
|
||||
%end
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Other
|
||||
|
||||
// Meta AI-branded search bars
|
||||
%hook IGSearchBar
|
||||
- (id)initWithConfig:(IGSearchBarConfig *)config {
|
||||
return %orig([self sanitizePlaceholderForConfig:config]);
|
||||
}
|
||||
|
||||
- (id)initWithConfig:(IGSearchBarConfig *)config userSession:(id)arg2 {
|
||||
return %orig([self sanitizePlaceholderForConfig:config], arg2);
|
||||
}
|
||||
|
||||
- (void)setConfig:(IGSearchBarConfig *)config {
|
||||
%orig([self sanitizePlaceholderForConfig:config]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
%new - (IGSearchBarConfig *)sanitizePlaceholderForConfig:(IGSearchBarConfig *)config {
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
|
||||
NSLog(@"[SCInsta] Hiding meta ai: reconfiguring search bar");
|
||||
|
||||
NSString *placeholder = [config valueForKey:@"placeholder"];
|
||||
|
||||
if ([placeholder containsString:@"Meta AI"]) {
|
||||
|
||||
// placeholder
|
||||
@try {
|
||||
[config setValue:@"Search" forKey:@"placeholder"];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"[SCInsta] WARNING: %@\n\nFull object: %@", exception.reason, config);
|
||||
}
|
||||
|
||||
// shouldAnimatePlaceholder
|
||||
@try {
|
||||
[config setValue:0 forKey:@"shouldAnimatePlaceholder"];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"[SCInsta] WARNING: %@\n\nFull object: %@", exception.reason, config);
|
||||
}
|
||||
|
||||
NSLog(@"[SCInsta] Changed search bar placeholder from: \"%@\" to \"%@\"", placeholder, [config valueForKey:@"placeholder"]);
|
||||
|
||||
// leftIconStyle
|
||||
@try {
|
||||
[config setValue:0 forKey:@"leftIconStyle"];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"[SCInsta] WARNING: %@\n\nFull object: %@", exception.reason, config);
|
||||
}
|
||||
|
||||
// rightButtonStyle
|
||||
@try {
|
||||
[config setValue:0 forKey:@"rightButtonStyle"];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"[SCInsta] WARNING: %@\n\nFull object: %@", exception.reason, config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return [config copy];
|
||||
}
|
||||
%end
|
||||
|
||||
// Themed in-app buttons
|
||||
%hook IGTapButton
|
||||
- (void)didMoveToWindow {
|
||||
%orig;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
|
||||
// Hide buttons that are associated with meta ai
|
||||
if ([self.accessibilityIdentifier containsString:@"meta_ai"]) {
|
||||
NSLog(@"[SCInsta] Hiding meta ai: meta ai associated button");
|
||||
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
%end
|
||||
|
||||
// Home feed meta ai button
|
||||
%hook IGFloatingActionButton.IGFloatingActionButton
|
||||
- (void)didMoveToSuperview {
|
||||
%orig;
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
[self removeFromSuperview];
|
||||
NSLog(@"[SCInsta] Hiding meta ai: home feed meta ai button");
|
||||
}
|
||||
}
|
||||
%end
|
||||
|
||||
// Share menu recipients
|
||||
%hook IGDirectRecipientListViewController
|
||||
- (id)objectsForListAdapter:(id)arg1 {
|
||||
NSArray *originalObjs = %orig();
|
||||
NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]];
|
||||
|
||||
for (id obj in originalObjs) {
|
||||
BOOL shouldHide = NO;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"hide_meta_ai"]) {
|
||||
if ([obj isKindOfClass:%c(IGDirectRecipientCellViewModel)]) {
|
||||
|
||||
// Meta AI (catch-all)
|
||||
if ([[[obj recipient] threadName] isEqualToString:@"Meta AI"]) {
|
||||
NSLog(@"[SCInsta] Hiding meta ai suggested as recipient (share menu)");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Populate new objs array
|
||||
if (!shouldHide) {
|
||||
[filteredObjs addObject:obj];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return [filteredObjs copy];
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,16 @@
|
||||
#import "../../Utils.h"
|
||||
#import "../../InstagramHeaders.h"
|
||||
|
||||
%hook IGDSSegmentedPillBarView
|
||||
- (void)didMoveToWindow {
|
||||
%orig;
|
||||
|
||||
if ([[self delegate] isKindOfClass:%c(IGSearchTypeaheadNavigationHeaderView)]) {
|
||||
if ([SCIUtils getBoolPref:@"hide_trending_searches"]) {
|
||||
NSLog(@"[SCInsta] Hiding trending searches");
|
||||
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
}
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,100 @@
|
||||
#import "../../Utils.h"
|
||||
|
||||
BOOL isSurfaceShown(IGMainAppSurfaceIntent *surface) {
|
||||
BOOL isShown = YES;
|
||||
|
||||
// Feed
|
||||
if ([[surface tabStringFromSurfaceIntent] isEqualToString:@"FEED"] && [SCIUtils getBoolPref:@"hide_feed_tab"]) {
|
||||
isShown = NO;
|
||||
}
|
||||
|
||||
// Reels
|
||||
else if ([[surface tabStringFromSurfaceIntent] isEqualToString:@"CLIPS"] && [SCIUtils getBoolPref:@"hide_reels_tab"]) {
|
||||
isShown = NO;
|
||||
}
|
||||
|
||||
// Explore
|
||||
else if ([[surface tabStringFromSurfaceIntent] isEqualToString:@"SEARCH"] && [SCIUtils getBoolPref:@"hide_explore_tab"]) {
|
||||
isShown = NO;
|
||||
}
|
||||
|
||||
// Create
|
||||
else if ([(NSNumber *)[surface valueForKey:@"_subtype"] unsignedIntegerValue] == 3 && [SCIUtils getBoolPref:@"hide_create_tab"]) {
|
||||
isShown = NO;
|
||||
}
|
||||
|
||||
return isShown;
|
||||
}
|
||||
|
||||
NSArray *filterSurfacesArray(NSArray *surfaces) {
|
||||
NSMutableArray *filteredSurfaces = [NSMutableArray array];
|
||||
|
||||
for (IGMainAppSurfaceIntent *surface in surfaces) {
|
||||
if (![surface isKindOfClass:%c(IGMainAppSurfaceIntent)]) break;
|
||||
|
||||
if (isSurfaceShown(surface)) {
|
||||
[filteredSurfaces addObject:surface];
|
||||
}
|
||||
}
|
||||
|
||||
return filteredSurfaces;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////
|
||||
|
||||
%hook IGTabBarControllerSwipeCoordinator
|
||||
- (id)initWithSurfaces:(id)surfaces parentViewController:(id)controller enableHaptics:(_Bool)haptics launcherSet:(id)set {
|
||||
// Removes the surface from the main swipeable app collection view
|
||||
return %orig(filterSurfacesArray(surfaces), controller, haptics, set);
|
||||
}
|
||||
%end
|
||||
|
||||
%hook IGTabBarController
|
||||
- (void)_layoutTabBar {
|
||||
// Prevents the wrong icon from being shown as selected because of mismatched surface array indexes
|
||||
NSArray *_tabBarSurfaces = [SCIUtils getIvarForObj:self name:"_tabBarSurfaces"];
|
||||
|
||||
[SCIUtils setIvarForObj:self name:"_tabBarSurfaces" value:filterSurfacesArray(_tabBarSurfaces)];
|
||||
|
||||
%orig;
|
||||
}
|
||||
|
||||
- (id)_buttonForTabBarSurface:(id)surface {
|
||||
// Prevents the button from being added to the tab bar
|
||||
id button = %orig(surface);
|
||||
|
||||
if (!isSurfaceShown(surface)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
%end
|
||||
|
||||
// Demangled name: IGNavConfiguration.IGNavConfiguration
|
||||
%hook _TtC18IGNavConfiguration18IGNavConfiguration
|
||||
- (NSInteger)tabOrdering {
|
||||
|
||||
if ([[SCIUtils getStringPref:@"nav_icon_ordering"] isEqualToString:@"classic"]) return 0;
|
||||
else if ([[SCIUtils getStringPref:@"nav_icon_ordering"] isEqualToString:@"standard"]) return 1;
|
||||
else if ([[SCIUtils getStringPref:@"nav_icon_ordering"] isEqualToString:@"alternate"]) return 2;
|
||||
|
||||
return %orig;
|
||||
|
||||
}
|
||||
- (void)setTabOrdering:(NSInteger)arg1 {
|
||||
return;
|
||||
}
|
||||
|
||||
- (BOOL)isTabSwipingEnabled {
|
||||
|
||||
if ([[SCIUtils getStringPref:@"swipe_nav_tabs"] isEqualToString:@"enabled"]) return YES;
|
||||
else if ([[SCIUtils getStringPref:@"swipe_nav_tabs"] isEqualToString:@"disabled"]) return NO;
|
||||
|
||||
return %orig;
|
||||
|
||||
}
|
||||
- (void)setIsTabSwipingEnabled:(BOOL)arg1 {
|
||||
return;
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,50 @@
|
||||
#import "../../Utils.h"
|
||||
#import "../../InstagramHeaders.h"
|
||||
|
||||
// Disable logging of searches at server-side
|
||||
%hook IGSearchEntityRouter
|
||||
- (id)initWithUserSession:(id)arg1 analyticsModule:(id)arg2 shouldAddToRecents:(BOOL)shouldAddToRecents {
|
||||
if ([SCIUtils getBoolPref:@"no_recent_searches"]) {
|
||||
NSLog(@"[SCInsta] Disabling recent searches");
|
||||
|
||||
shouldAddToRecents = false;
|
||||
}
|
||||
|
||||
return %orig(arg1, arg2, shouldAddToRecents);
|
||||
}
|
||||
%end
|
||||
|
||||
// Most in-app search bars
|
||||
%hook IGRecentSearchStore
|
||||
- (id)initWithDiskManager:(id)arg1 recentSearchStoreConfiguration:(id)arg2 {
|
||||
if ([SCIUtils getBoolPref:@"no_recent_searches"]) {
|
||||
NSLog(@"[SCInsta] Disabling recent searches");
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
- (BOOL)addItem:(id)arg1 {
|
||||
if ([SCIUtils getBoolPref:@"no_recent_searches"]) {
|
||||
NSLog(@"[SCInsta] Disabling recent searches");
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
|
||||
// Recent dm message recipients search bar
|
||||
%hook IGDirectRecipientRecentSearchStorage
|
||||
- (id)initWithDiskManager:(id)arg1 directCache:(id)arg2 userStore:(id)arg3 currentUser:(id)arg4 featureSets:(id)arg5 {
|
||||
if ([SCIUtils getBoolPref:@"no_recent_searches"]) {
|
||||
NSLog(@"[SCInsta] Disabling recent searches");
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,19 @@
|
||||
#import "../../Utils.h"
|
||||
#import "../../InstagramHeaders.h"
|
||||
|
||||
// Channels dms tab (header)
|
||||
%hook IGDirectInboxHeaderSectionController
|
||||
- (id)viewModel {
|
||||
if ([[%orig title] isEqualToString:@"Suggested"]) {
|
||||
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_chats"]) {
|
||||
NSLog(@"[SCInsta] Hiding suggested chats (header: channels tab)");
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,263 @@
|
||||
#import "../../Utils.h"
|
||||
#import "../../InstagramHeaders.h"
|
||||
|
||||
// "Welcome to instagram" suggested users in feed
|
||||
%hook IGSuggestedUnitViewModel
|
||||
- (id)initWithAYMFModel:(id)arg1 headerViewModel:(id)arg2 {
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_users"]) {
|
||||
NSLog(@"[SCInsta] Hiding suggested users: main feed welcome section");
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
%hook IGSuggestionsUnitViewModel
|
||||
- (id)initWithAYMFModel:(id)arg1 headerViewModel:(id)arg2 {
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_users"]) {
|
||||
NSLog(@"[SCInsta] Hiding suggested users: main feed welcome section");
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
return %orig;
|
||||
}
|
||||
%end
|
||||
|
||||
// Suggested users in profile header
|
||||
%hook IGProfileHeaderView
|
||||
- (id)objectsForListAdapter:(id)arg1 {
|
||||
NSArray *originalObjs = %orig();
|
||||
NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]];
|
||||
|
||||
for (id obj in originalObjs) {
|
||||
BOOL shouldHide = NO;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_users"]) {
|
||||
if ([obj isKindOfClass:%c(IGProfileChainingModel)]) {
|
||||
NSLog(@"[SCInsta] Hiding suggested users: profile header");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// Populate new objs array
|
||||
if (!shouldHide) {
|
||||
[filteredObjs addObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
return [filteredObjs copy];
|
||||
}
|
||||
%end
|
||||
|
||||
// Notifications/activity feed
|
||||
%hook IGActivityFeedViewController
|
||||
- (id)objectsForListAdapter:(id)arg1 {
|
||||
NSArray *originalObjs = %orig();
|
||||
NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]];
|
||||
|
||||
for (id obj in originalObjs) {
|
||||
BOOL shouldHide = NO;
|
||||
|
||||
// Section header
|
||||
if ([obj isKindOfClass:%c(IGLabelItemViewModel)]) {
|
||||
// Suggested for you
|
||||
if ([[obj labelTitle] isEqualToString:@"Suggested for you"]) {
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_users"]) {
|
||||
NSLog(@"[SCInsta] Hiding suggested users (header: activity feed)");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Suggested user
|
||||
else if ([obj isKindOfClass:%c(IGDiscoverPeopleItemConfiguration)]) {
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_users"]) {
|
||||
NSLog(@"[SCInsta] Hiding suggested users: (user: activity feed)");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// "See all" button
|
||||
else if ([obj isKindOfClass:%c(IGSeeAllItemConfiguration)]) {
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_users"]) {
|
||||
NSLog(@"[SCInsta] Hiding suggested users: (see all: activity feed)");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// Populate new objs array
|
||||
if (!shouldHide) {
|
||||
[filteredObjs addObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
return [filteredObjs copy];
|
||||
}
|
||||
%end
|
||||
|
||||
// Profile "following" and "followers" tabs
|
||||
%hook IGFollowListViewController
|
||||
- (id)objectsForListAdapter:(id)arg1 {
|
||||
NSArray *originalObjs = %orig(arg1);
|
||||
NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]];
|
||||
|
||||
for (IGStoryTrayViewModel *obj in originalObjs) {
|
||||
BOOL shouldHide = NO;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_users"]) {
|
||||
|
||||
// Suggested user
|
||||
if ([obj isKindOfClass:%c(IGDiscoverPeopleItemConfiguration)]) {
|
||||
NSLog(@"[SCInsta] Hiding suggested users: follow list suggested user");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
// Section header
|
||||
else if ([obj isKindOfClass:%c(IGLabelItemViewModel)]) {
|
||||
|
||||
// "Suggested for you" search results header
|
||||
if ([[obj valueForKey:@"labelTitle"] isEqualToString:@"Suggested for you"]) {
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// See all suggested users
|
||||
else if ([obj isKindOfClass:%c(IGSeeAllItemConfiguration)] && ((IGSeeAllItemConfiguration *)obj).destination == 4) {
|
||||
NSLog(@"[SCInsta] Hiding suggested users: follow list suggested user");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Populate new objs array
|
||||
if (!shouldHide) {
|
||||
[filteredObjs addObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
return [filteredObjs copy];
|
||||
}
|
||||
%end
|
||||
|
||||
%hook IGSegmentedTabControl
|
||||
- (void)setSegments:(id)segments {
|
||||
NSArray *originalObjs = segments;
|
||||
NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]];
|
||||
|
||||
for (IGStoryTrayViewModel *obj in originalObjs) {
|
||||
BOOL shouldHide = NO;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_users"]) {
|
||||
if ([obj isKindOfClass:%c(IGFindUsersViewController)]) {
|
||||
NSLog(@"[SCInsta] Hiding suggested users: find users segmented tab");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// Populate new objs array
|
||||
if (!shouldHide) {
|
||||
[filteredObjs addObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
return %orig([filteredObjs copy]);
|
||||
}
|
||||
%end
|
||||
|
||||
// Suggested subscriptions
|
||||
%hook IGFanClubSuggestedUsersDataSource
|
||||
- (id)initWithUserSession:(id)arg1 delegate:(id)arg2 {
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_users"]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return %orig(arg1, arg2);
|
||||
}
|
||||
%end
|
||||
|
||||
// Follow request/discover section (accessed through notifications page)
|
||||
// Demangled name: IGFriendingCenter.IGFriendingCenterViewController
|
||||
%hook _TtC17IGFriendingCenter31IGFriendingCenterViewController
|
||||
- (id)objectsForListAdapter:(id)arg1 {
|
||||
NSArray *originalObjs = %orig(arg1);
|
||||
NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]];
|
||||
|
||||
for (IGStoryTrayViewModel *obj in originalObjs) {
|
||||
BOOL shouldHide = NO;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_users"]) {
|
||||
|
||||
// Suggested user
|
||||
if ([obj isKindOfClass:%c(IGDiscoverPeopleItemConfiguration)]) {
|
||||
NSLog(@"[SCInsta] Hiding suggested users: follow list suggested user");
|
||||
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
// Section header
|
||||
else if ([obj isKindOfClass:%c(IGLabelItemViewModel)]) {
|
||||
|
||||
// "Suggested for you" search results header
|
||||
if ([[obj valueForKey:@"labelTitle"] isEqualToString:@"Suggested for you"]) {
|
||||
shouldHide = YES;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Populate new objs array
|
||||
if (!shouldHide) {
|
||||
[filteredObjs addObject:obj];
|
||||
}
|
||||
}
|
||||
|
||||
return [filteredObjs copy];
|
||||
}
|
||||
%end
|
||||
|
||||
%hook IGProfileActionBarViewModel
|
||||
- (id)initWithIdentifier:(id)arg1
|
||||
rows:(id)arg2
|
||||
allActionsToDisplay:(id)arg3
|
||||
overflowActions:(id)arg4
|
||||
actionToBadgeInfoMap:(id)arg5
|
||||
allBusinessActions:(id)arg6
|
||||
overflowBusinessActions:(id)arg7
|
||||
contactSheetActions:(id)arg8
|
||||
user:(id)arg9
|
||||
sponsoredInfoProvider:(id)arg10
|
||||
profileBackgroundColor:(id)arg11
|
||||
{
|
||||
NSArray *rows = arg2;
|
||||
NSOrderedSet *allActions = [arg3 copy];
|
||||
NSOrderedSet *overflowActions = [arg4 copy];
|
||||
|
||||
if ([SCIUtils getBoolPref:@"no_suggested_users"]) {
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)", @[ @(3) ]];
|
||||
|
||||
// Actions sets
|
||||
allActions = [allActions filteredOrderedSetUsingPredicate:predicate];
|
||||
overflowActions = [overflowActions filteredOrderedSetUsingPredicate:predicate];
|
||||
|
||||
// Rows of actions sets
|
||||
NSMutableArray *filteredRows = [NSMutableArray new];
|
||||
for (NSOrderedSet *set in rows) {
|
||||
[filteredRows addObject:[set filteredOrderedSetUsingPredicate:predicate]];
|
||||
}
|
||||
rows = [filteredRows copy];
|
||||
}
|
||||
|
||||
return %orig(arg1, rows, allActions, overflowActions, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,293 @@
|
||||
#import "../../Utils.h"
|
||||
|
||||
static char targetStaticRef[] = "target";
|
||||
|
||||
%hook IGDirectNotesCreationView
|
||||
- (id)initWithViewModel:(id)model
|
||||
featureSupport:(IGNotesCreationFeatureSupportModel *)support
|
||||
presentationAnimation:(id)animation
|
||||
composerUpdateListener:(id)listener
|
||||
delegate:(id)delegate
|
||||
layoutType:(long long)type
|
||||
userSession:(id)session
|
||||
{
|
||||
if ([SCIUtils getBoolPref:@"enable_notes_customization"]) {
|
||||
|
||||
// enableAnimatedEmojisInCreation
|
||||
@try {
|
||||
[support setValue:@(YES) forKey:@"enableAnimatedEmojisInCreation"];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"[SCInsta] WARNING: %@\n\nFull object: %@", exception.reason, support);
|
||||
}
|
||||
|
||||
// enableBubbleCustomization
|
||||
@try {
|
||||
[support setValue:@(YES) forKey:@"enableBubbleCustomization"];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"[SCInsta] WARNING: %@\n\nFull object: %@", exception.reason, support);
|
||||
}
|
||||
|
||||
// enableRandomThemeGenerator
|
||||
@try {
|
||||
[support setValue:@(YES) forKey:@"enableRandomThemeGenerator"];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"[SCInsta] WARNING: %@\n\nFull object: %@", exception.reason, support);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return %orig(model, support, animation, listener, delegate, type, session);
|
||||
}
|
||||
%end
|
||||
|
||||
// Demangled name: IGDirectNotesUISwift.IGDirectNotesBubbleEditorColorPaletteView
|
||||
%hook _TtC20IGDirectNotesUISwift41IGDirectNotesBubbleEditorColorPaletteView
|
||||
%property (nonatomic, copy) UIColor *backgroundColor;
|
||||
%property (nonatomic, copy) UIColor *textColor;
|
||||
%property (nonatomic, copy) NSString *emojiText;
|
||||
|
||||
- (void)didMoveToWindow {
|
||||
%orig;
|
||||
|
||||
if (![SCIUtils getBoolPref:@"custom_note_themes"]) return;
|
||||
|
||||
// Inject buttons once in view lifecycle
|
||||
static char didInjectButtons;
|
||||
if (objc_getAssociatedObject(self, &didInjectButtons)) {
|
||||
return;
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
__strong typeof(weakSelf) self = weakSelf;
|
||||
if (!self || !self.window) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIView *container = self.superview ?: self.window;
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Button config
|
||||
UIButtonConfiguration *config = [UIButtonConfiguration tintedButtonConfiguration];
|
||||
config.background.cornerRadius = 12.0;
|
||||
config.cornerStyle = UIButtonConfigurationCornerStyleFixed;
|
||||
config.contentInsets = NSDirectionalEdgeInsetsMake(13.7, 10, 13.7, 10);
|
||||
|
||||
|
||||
// Left button
|
||||
UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
leftButton.configuration = config;
|
||||
leftButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
leftButton.tintColor = [SCIUtils SCIColor_Primary];
|
||||
|
||||
NSMutableAttributedString *attrTitleLeft = [[NSMutableAttributedString alloc] initWithString:@"Background"];
|
||||
[attrTitleLeft addAttribute:NSFontAttributeName
|
||||
value:[UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]
|
||||
range:NSMakeRange(0, attrTitleLeft.length)
|
||||
];
|
||||
[leftButton setAttributedTitle:attrTitleLeft forState:UIControlStateNormal];
|
||||
[leftButton sizeToFit];
|
||||
|
||||
[leftButton addAction:[UIAction actionWithHandler:^(__kindof UIAction * _Nonnull action) {
|
||||
[self presentColorPicker:@"Background"];
|
||||
}] forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
// Middle button
|
||||
UIButton *middleButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
middleButton.configuration = config;
|
||||
middleButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
middleButton.tintColor = [SCIUtils SCIColor_Primary];
|
||||
|
||||
NSMutableAttributedString *attrTitleMiddle = [[NSMutableAttributedString alloc] initWithString:@"Text"];
|
||||
[attrTitleMiddle addAttribute:NSFontAttributeName
|
||||
value:[UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]
|
||||
range:NSMakeRange(0, attrTitleMiddle.length)
|
||||
];
|
||||
[middleButton setAttributedTitle:attrTitleMiddle forState:UIControlStateNormal];
|
||||
[middleButton sizeToFit];
|
||||
|
||||
[middleButton addAction:[UIAction actionWithHandler:^(__kindof UIAction * _Nonnull action) {
|
||||
[self presentColorPicker:@"Text"];
|
||||
}] forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
// Right button
|
||||
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
rightButton.configuration = config;
|
||||
rightButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
rightButton.tintColor = [SCIUtils SCIColor_Primary];
|
||||
|
||||
NSMutableAttributedString *attrTitleRight = [[NSMutableAttributedString alloc] initWithString:@"Emoji"];
|
||||
[attrTitleRight addAttribute:NSFontAttributeName
|
||||
value:[UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]
|
||||
range:NSMakeRange(0, attrTitleRight.length)
|
||||
];
|
||||
[rightButton setAttributedTitle:attrTitleRight forState:UIControlStateNormal];
|
||||
[rightButton sizeToFit];
|
||||
|
||||
[rightButton addAction:[UIAction actionWithHandler:^(__kindof UIAction * _Nonnull action) {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Enter Emoji Text"
|
||||
message:@"Click the Apply button after this to see the emoji"
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
textField.placeholder = @"Type emoji...";
|
||||
}];
|
||||
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"OK"
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *action) {
|
||||
self.emojiText = alert.textFields[0].text;
|
||||
[self applySCICustomTheme:@"Emoji"];
|
||||
}]];
|
||||
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:nil]];
|
||||
|
||||
UIViewController *vc = [SCIUtils nearestViewControllerForView:self];
|
||||
[vc presentViewController:alert animated:YES completion:nil];
|
||||
}] forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
|
||||
// Create stack view
|
||||
UIStackView *stack = [[UIStackView alloc] initWithArrangedSubviews:@[leftButton, middleButton, rightButton]];
|
||||
stack.axis = UILayoutConstraintAxisHorizontal;
|
||||
stack.spacing = 15.0;
|
||||
stack.alignment = UIStackViewAlignmentCenter;
|
||||
stack.distribution = UIStackViewDistributionFillEqually;
|
||||
|
||||
// Find max height among arranged subviews
|
||||
CGFloat maxHeight = 0.0;
|
||||
for (UIView *subview in stack.arrangedSubviews) {
|
||||
maxHeight = MAX(maxHeight, subview.bounds.size.height);
|
||||
}
|
||||
|
||||
// Manual frame with side padding
|
||||
CGFloat bottomMargin = 15.0;
|
||||
|
||||
CGRect viewFrame = [self convertRect:self.bounds toView:container];
|
||||
CGFloat y = CGRectGetMinY(viewFrame) - maxHeight - bottomMargin;
|
||||
CGFloat width = container.bounds.size.width - stack.spacing * 2;
|
||||
|
||||
stack.frame = CGRectMake(stack.spacing, y, width, maxHeight);
|
||||
|
||||
[stack layoutIfNeeded];
|
||||
[container addSubview:stack];
|
||||
|
||||
objc_setAssociatedObject(
|
||||
self,
|
||||
&didInjectButtons,
|
||||
@YES,
|
||||
OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
%new - (void)presentColorPicker:(NSString *)target {
|
||||
UIColorPickerViewController *colorPickerController = [[UIColorPickerViewController alloc] init];
|
||||
|
||||
colorPickerController.delegate = (id<UIColorPickerViewControllerDelegate>)self; // cast to suppress warnings
|
||||
colorPickerController.title = [NSString stringWithFormat:@"%@ color", target];
|
||||
colorPickerController.modalPresentationStyle = UIModalPresentationPopover;
|
||||
colorPickerController.supportsAlpha = NO;
|
||||
|
||||
// Show last picked color for type
|
||||
if ([target isEqualToString:@"Background"]) {
|
||||
colorPickerController.selectedColor = self.backgroundColor;
|
||||
}
|
||||
else if ([target isEqualToString:@"Text"]) {
|
||||
colorPickerController.selectedColor = self.textColor;
|
||||
}
|
||||
|
||||
UIViewController *presentingVC = [SCIUtils nearestViewControllerForView:self];
|
||||
|
||||
if (presentingVC != nil) {
|
||||
[presentingVC presentViewController:colorPickerController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
// Save which color target to update
|
||||
objc_setAssociatedObject(
|
||||
presentingVC,
|
||||
&targetStaticRef,
|
||||
target,
|
||||
OBJC_ASSOCIATION_RETAIN_NONATOMIC
|
||||
);
|
||||
}
|
||||
|
||||
// UIColorPickerViewControllerDelegate Protocol
|
||||
%new - (void)colorPickerViewController:(UIColorPickerViewController *)viewController
|
||||
didSelectColor:(UIColor *)color
|
||||
continuously:(BOOL)continuously
|
||||
{
|
||||
_TtC20IGDirectNotesUISwift41IGDirectNotesBubbleEditorColorPaletteView *bubbleEditorVC = [SCIUtils nearestViewControllerForView:self];
|
||||
|
||||
NSString *target = objc_getAssociatedObject(bubbleEditorVC, &targetStaticRef);
|
||||
if (!target) return;
|
||||
|
||||
// Update saved color target
|
||||
if ([target isEqualToString:@"Background"]) {
|
||||
self.backgroundColor = color;
|
||||
}
|
||||
else if ([target isEqualToString:@"Text"]) {
|
||||
self.textColor = color;
|
||||
}
|
||||
|
||||
[self applySCICustomTheme:target];
|
||||
};
|
||||
|
||||
%new - (void)applySCICustomTheme:(NSString *)target {
|
||||
// Get notes composer vc
|
||||
_TtC20IGDirectNotesUISwift39IGDirectNotesBubbleEditorViewController *parentVC = [SCIUtils nearestViewControllerForView:self];
|
||||
if (!parentVC) return;
|
||||
|
||||
IGDirectNotesComposerViewController *composerVC = parentVC.delegate;
|
||||
if (!composerVC) return;
|
||||
|
||||
// Get current theme model
|
||||
IGNotesCustomThemeCreationModel *model = [composerVC valueForKey:@"_selectedCustomThemeCreationModel"];
|
||||
if (!model) {
|
||||
// Create new note theme model
|
||||
model = [[%c(IGNotesCustomThemeCreationModel) alloc] init];
|
||||
if (!model) return;
|
||||
}
|
||||
|
||||
//SCILog(@"Current note theme model: %@", model);
|
||||
[model setValue:[composerVC valueForKey:@"_composerText"] forKey:@"customEmoji"];
|
||||
|
||||
// Update saved color target
|
||||
if ([target isEqualToString:@"Background"]) {
|
||||
[model setValue:self.backgroundColor forKey:@"backgroundColor"];
|
||||
}
|
||||
else if ([target isEqualToString:@"Text"]) {
|
||||
[model setValue:self.textColor forKey:@"textColor"];
|
||||
[model setValue:self.textColor forKey:@"secondaryTextColor"];
|
||||
}
|
||||
|
||||
// Always set emoji to prevent it being overwritten
|
||||
[model setValue:self.emojiText forKey:@"customEmoji"];
|
||||
|
||||
//SCILog(@"Updated note theme model: %@", model);
|
||||
|
||||
// Apply custom notes theme
|
||||
[composerVC notesBubbleEditorViewControllerDidUpdateWithCustomThemeCreationModel:model];
|
||||
|
||||
// Enable apply/cancel buttons
|
||||
UIView *parentVCView = [parentVC view];
|
||||
if (!parentVCView) return;
|
||||
|
||||
NSArray<UIView *> *parentVCSubviews = [parentVCView subviews];
|
||||
if (!parentVCSubviews) return;
|
||||
|
||||
[parentVCSubviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
||||
if ([obj isKindOfClass:%c(IGDSBottomButtonsView)]) {
|
||||
[obj setPrimaryButtonEnabled:YES];
|
||||
[obj setSecondaryButtonEnabled:YES];
|
||||
}
|
||||
}];
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,58 @@
|
||||
#import "../../InstagramHeaders.h"
|
||||
#import "../../Settings/SCISettingsViewController.h"
|
||||
|
||||
// Show SCInsta tweak settings by holding on the settings/more icon under profile for ~1 second
|
||||
%hook IGBadgedNavigationButton
|
||||
- (void)didMoveToWindow {
|
||||
%orig;
|
||||
|
||||
if ([self.accessibilityIdentifier isEqualToString:@"profile-more-button"]) {
|
||||
[self addLongPressGestureRecognizer];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
%new - (void)addLongPressGestureRecognizer {
|
||||
if ([self.gestureRecognizers count] == 0) {
|
||||
NSLog(@"[SCInsta] Adding tweak settings long press gesture recognizer");
|
||||
|
||||
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
||||
[self addGestureRecognizer:longPress];
|
||||
}
|
||||
}
|
||||
%new - (void)handleLongPress:(UILongPressGestureRecognizer *)sender {
|
||||
if (sender.state != UIGestureRecognizerStateBegan) return;
|
||||
|
||||
NSLog(@"[SCInsta] Tweak settings gesture activated");
|
||||
|
||||
[SCIUtils showSettingsVC:[self window]];
|
||||
}
|
||||
%end
|
||||
|
||||
// Quick access to tweak settings by holding on home tab button
|
||||
%hook IGTabBarButton
|
||||
- (void)didMoveToSuperview {
|
||||
%orig;
|
||||
|
||||
// Only work on home/feed tab
|
||||
if (![self.accessibilityIdentifier isEqualToString:@"mainfeed-tab"]) return;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"settings_shortcut"]) {
|
||||
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
||||
longPress.minimumPressDuration = 0.3;
|
||||
|
||||
// Take precidence over existing gesture recognizers
|
||||
for (UIGestureRecognizer *existing in self.gestureRecognizers) {
|
||||
[existing requireGestureRecognizerToFail:longPress];
|
||||
}
|
||||
|
||||
[self addGestureRecognizer:longPress];
|
||||
}
|
||||
}
|
||||
%new - (void)handleLongPress:(UILongPressGestureRecognizer *)sender {
|
||||
if (sender.state != UIGestureRecognizerStateBegan) return;
|
||||
|
||||
[SCIUtils showSettingsVC:[self window]];
|
||||
}
|
||||
%end
|
||||
@@ -0,0 +1,38 @@
|
||||
#import "../../InstagramHeaders.h"
|
||||
#import "../../Utils.h"
|
||||
|
||||
%hook IGImageWithAccessoryButton
|
||||
|
||||
- (void)didMoveToSuperview {
|
||||
%orig;
|
||||
|
||||
[self addLongPressGestureRecognizer];
|
||||
}
|
||||
|
||||
%new - (void)addLongPressGestureRecognizer {
|
||||
BOOL hasLongPress = [self.gestureRecognizers filteredArrayUsingPredicate:
|
||||
[NSPredicate predicateWithBlock:^BOOL(NSObject *item, NSDictionary *_) {
|
||||
return [item isKindOfClass:[UILongPressGestureRecognizer class]];
|
||||
}]
|
||||
].count > 0;
|
||||
|
||||
if (!hasLongPress) {
|
||||
NSLog(@"[SCInsta] Adding teen app icons long press gesture recognizer");
|
||||
|
||||
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
||||
[self addGestureRecognizer:longPress];
|
||||
}
|
||||
}
|
||||
%new - (void)handleLongPress:(UILongPressGestureRecognizer *)sender {
|
||||
if (sender.state != UIGestureRecognizerStateBegan) return;
|
||||
|
||||
if ([SCIUtils getBoolPref:@"teen_app_icons"]) {
|
||||
IGHomeFeedHeaderViewController *homeFeedHeaderVC = [SCIUtils nearestViewControllerForView:self];
|
||||
|
||||
if (homeFeedHeaderVC != nil) {
|
||||
[homeFeedHeaderVC headerDidLongPressLogo:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
%end
|
||||
Reference in New Issue
Block a user