#import #import "InstagramHeaders.h" #import "Tweak.h" #import "Utils.h" #import "Features/General/SCICacheManager.h" #import "Features/General/SCIChangelog.h" #include "../modules/fishhook/fishhook.h" /////////////////////////////////////////////////////////// // Screenshot handlers #define VOID_HANDLESCREENSHOT(orig) [SCIUtils getBoolPref:@"remove_screenshot_alert"] ? nil : orig; #define NONVOID_HANDLESCREENSHOT(orig) return VOID_HANDLESCREENSHOT(orig) /////////////////////////////////////////////////////////// // * Tweak version * NSString *SCIVersionString = @"v1.2.2"; // Variables that work across features BOOL dmVisualMsgsViewedButtonEnabled = false; // Tweak first-time setup %hook IGInstagramAppDelegate - (_Bool)application:(UIApplication *)application willFinishLaunchingWithOptions:(id)arg2 { // Default SCInsta config NSDictionary *sciDefaults = @{ @"hide_ads": @(YES), @"copy_description": @(YES), @"profile_copy_button": @(YES), @"detailed_color_picker": @(YES), @"remove_screenshot_alert": @(YES), @"voice_call_confirm": @(NO), @"video_call_confirm": @(NO), @"keep_deleted_message": @(NO), @"hide_suggested_stories": @(NO), @"profile_analyzer_accumulate": @(NO), @"story_tray_actions": @(NO), @"zoom_profile_photo": @(NO), @"follow_indicator": @(NO), @"profile_note_copy": @(NO), @"disable_disappearing_mode_swipe": @(NO), @"hide_voice_call_button": @(NO), @"hide_video_call_button": @(NO), @"fake_location_enabled": @(NO), @"show_fake_location_map_button": @(NO), @"fake_location_lat": @(48.8584), @"fake_location_lon": @(2.2945), @"fake_location_name": @"Eiffel Tower", @"fake_location_presets": @[], @"messages_only": @(NO), @"messages_only_hide_tabbar": @(NO), @"fake_follower_count": @(NO), @"fake_following_count": @(NO), @"fake_post_count": @(NO), @"fake_verified": @(NO), @"launch_tab": @"default", @"save_profile": @(YES), // Per-context action buttons (new in 1.1.6) @"feed_media_zoom": @(NO), @"disable_bg_refresh": @(NO), @"disable_home_refresh": @(NO), @"disable_home_scroll": @(NO), @"disable_reels_tab_refresh": @(NO), @"dm_full_last_active": @(NO), @"send_file": @(NO), @"note_actions": @(NO), @"note_copy_on_hold": @(NO), @"feed_date_format": @"default", // Per-surface date format toggles (see SCIDateFormatEntries.h) @"date_fmt_mixed": @(YES), @"date_fmt_notes_comments_stories": @(NO), @"date_fmt_dms": @(NO), @"feed_action_button": @(YES), @"feed_action_default": @"menu", @"reels_action_button": @(YES), @"reels_action_default": @"menu", @"stories_action_button": @(YES), @"stories_action_default": @"menu", @"dm_visual_action_button": @(YES), @"dm_visual_action_default": @"menu", @"dm_visual_seen_button": @(YES), @"dm_visual_audio_toggle": @(NO), // Legacy long-press gesture (off by default — kept for users who prefer it) @"dw_legacy_gesture": @(NO), @"dw_confirm": @(NO), @"enhance_download_quality": @(YES), @"default_video_quality": @"always_ask", @"default_photo_quality": @"high", @"ffmpeg_encoding_speed": @"ultrafast", @"unfollow_confirm": @(NO), @"sticker_interact_confirm": @(NO), @"sticker_interact_confirm_highlights": @(NO), @"dw_save_action": @"share", @"dw_finger_count": @(3), @"dw_finger_duration": @(0.5), @"reels_tap_control": @"default", @"reels_photo_tap_mute": @(NO), @"nav_icon_ordering": @"default", @"swipe_nav_tabs": @"default", @"enable_notes_customization": @(YES), @"custom_note_themes": @(YES), @"disable_auto_unmuting_reels": @(NO), @"auto_scroll_reels_mode": @"off", @"settings_shortcut": @(YES), @"doom_scrolling_reel_count": @(1), @"keep_seen_visual_local": @(NO), @"send_audio_as_file": @(YES), @"download_audio_message": @(NO), @"save_to_ryukgram_album": @(NO), @"unlock_password_reels": @(YES), @"seen_mode": @"button", @"seen_auto_on_interact": @(NO), @"seen_auto_on_typing": @(NO), @"seen_on_story_like": @(NO), @"seen_on_story_reply": @(NO), @"advance_on_story_reply": @(NO), @"advance_on_mark_seen": @(NO), @"advance_on_story_like": @(NO), @"indicate_unsent_messages": @(NO), @"unsent_message_toast": @(NO), @"warn_refresh_clears_preserved": @(NO), @"enable_chat_exclusions": @(YES), @"chat_blocking_mode": @"block_all", @"exclusions_default_keep_deleted": @(NO), @"chat_quick_list_button": @(YES), @"enable_story_user_exclusions": @(YES), @"story_blocking_mode": @"block_all", @"story_excluded_show_unexclude_eye": @(YES), @"story_seen_mode": @"button", @"story_audio_toggle": @(NO), @"view_story_mentions": @(YES), @"stories_show_quiz_answer": @(NO), @"stories_show_poll_votes_count": @(NO), @"reels_show_quiz_answer": @(NO), @"reels_show_poll_votes_count": @(NO), @"force_enable_quiz_sticker": @(NO), @"settings_pause_playback": @(YES), @"embed_links": @(NO), @"embed_link_domain": @"kkinstagram.com", @"strip_tracking_params": @(NO), @"download_highlight_cover": @(YES), @"open_links_external": @(NO), @"strip_browser_tracking": @(NO), @"hide_feed_repost": @(NO), @"copy_comment": @(YES), @"download_gif_comment": @(YES), @"cache_auto_clear_mode": @"off", @"cache_auto_check_size": @(YES), @"sci_changelog_force_show": @(NO), @"live_anonymous_view": @(NO), @"live_hide_comments": @(NO), @"hide_ui_on_capture": @(NO), @"paste_link_from_search": @(NO), @"sci_language": @"system", @"theme_force_dark": @(NO), @"theme_full_oled": @(NO), @"theme_oled_chat": @(NO), @"theme_keyboard": @"off", // Experimental IG features (credits: Radan). See SCIExperimentalGuard. @"igt_homecoming": @(NO), @"igt_quicksnap": @(NO), @"igt_prism": @(NO), @"igt_directnotes_friendmap": @(NO), @"igt_directnotes_audio_reply": @(NO), @"igt_directnotes_avatar_reply": @(NO), @"igt_directnotes_gifs_reply": @(NO), @"igt_directnotes_photo_reply": @(NO), @"sci_exp_warning_seen": @(NO) }; [[NSUserDefaults standardUserDefaults] registerDefaults:sciDefaults]; [SCIUtils setSciRegisteredDefaults:sciDefaults]; // Override instagram defaults if ([SCIUtils getBoolPref:@"liquid_glass_buttons"]) { [[NSUserDefaults standardUserDefaults] setValue:@(YES) forKey:@"instagram.override.project.lucent.navigation"]; } else { [[NSUserDefaults standardUserDefaults] setValue:@(NO) forKey:@"instagram.override.project.lucent.navigation"]; } return %orig; } - (_Bool)application:(UIApplication *)application didFinishLaunchingWithOptions:(id)arg2 { %orig; // Open settings for first-time users double openDelay = [SCIUtils getBoolPref:@"tweak_settings_app_launch"] ? 0.0 : 5.0; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(openDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if ( ![[[NSUserDefaults standardUserDefaults] objectForKey:@"SCInstaFirstRun"] isEqualToString:SCIVersionString] || [SCIUtils getBoolPref:@"tweak_settings_app_launch"] ) { NSLog(@"[SCInsta] First run — showing settings modal"); [SCIUtils showSettingsVC:[self window]]; } }); if ([SCIUtils getBoolPref:@"flex_app_launch"]) { [[objc_getClass("FLEXManager") sharedManager] showExplorer]; } return true; } - (void)applicationDidBecomeActive:(id)arg1 { %orig; if ([SCIUtils getBoolPref:@"flex_app_start"]) { [[objc_getClass("FLEXManager") sharedManager] showExplorer]; } } - (void)applicationDidEnterBackground:(id)arg1 { %orig; // Cache housekeeping while backgrounded — never competes with IG's foreground I/O. [SCICacheManager runAutoClearIfDue]; } %end // Tab bar only exists in the logged-in state — fire the changelog popup here // rather than at app launch (which runs pre-login). %hook IGTabBarController - (void)viewDidAppear:(BOOL)animated { %orig; static dispatch_once_t once; dispatch_once(&once, ^{ [SCIChangelog presentIfNewFromWindow:self.view.window]; }); } %end %hook IGDSLauncherConfig - (_Bool)isLiquidGlassInAppNotificationEnabled { return [SCIUtils liquidGlassEnabledBool:%orig]; } - (_Bool)isLiquidGlassContextMenuEnabled { return [SCIUtils liquidGlassEnabledBool:%orig]; } - (_Bool)isLiquidGlassToastEnabled { return [SCIUtils liquidGlassEnabledBool:%orig]; } - (_Bool)isLiquidGlassToastPeekEnabled { return [SCIUtils liquidGlassEnabledBool:%orig]; } - (_Bool)isLiquidGlassAlertDialogEnabled { return [SCIUtils liquidGlassEnabledBool:%orig]; } - (_Bool)isLiquidGlassIconBarButtonEnabled { return [SCIUtils liquidGlassEnabledBool:%orig]; } %end // Disable sending modded insta bug reports %hook IGWindow - (void)showDebugMenu { return; } %end %hook IGBugReportUploader - (id)initWithNetworker:(id)arg1 pandoGraphQLService:(id)arg2 analyticsLogger:(id)arg3 userDefaults:(id)arg4 launcherSetProvider:(id)arg5 shouldPersistLastBugReportId:(id)arg6 { return nil; } %end // Disable anti-screenshot feature on visual messages %hook IGStoryViewerContainerView - (void)setShouldBlockScreenshot:(BOOL)arg1 viewModel:(id)arg2 { VOID_HANDLESCREENSHOT(%orig); } %end // Disable screenshot logging/detection %hook IGDirectVisualMessageViewerSession - (id)visualMessageViewerController:(id)arg1 didDetectScreenshotForVisualMessage:(id)arg2 atIndex:(NSInteger)arg3 { NONVOID_HANDLESCREENSHOT(%orig); } %end %hook IGDirectVisualMessageReplayService - (id)visualMessageViewerController:(id)arg1 didDetectScreenshotForVisualMessage:(id)arg2 atIndex:(NSInteger)arg3 { NONVOID_HANDLESCREENSHOT(%orig); } %end %hook IGDirectVisualMessageReportService - (id)visualMessageViewerController:(id)arg1 didDetectScreenshotForVisualMessage:(id)arg2 atIndex:(NSInteger)arg3 { NONVOID_HANDLESCREENSHOT(%orig); } %end %hook IGDirectVisualMessageScreenshotSafetyLogger - (id)initWithUserSession:(id)arg1 entryPoint:(NSInteger)arg2 { if ([SCIUtils getBoolPref:@"remove_screenshot_alert"]) { NSLog(@"[SCInsta] Disable visual message screenshot safety logger"); return nil; } return %orig; } %end %hook IGScreenshotObserver - (id)initForController:(id)arg1 { NONVOID_HANDLESCREENSHOT(%orig); } %end %hook IGScreenshotObserverDelegate - (void)screenshotObserverDidSeeScreenshotTaken:(id)arg1 { VOID_HANDLESCREENSHOT(%orig); } - (void)screenshotObserverDidSeeActiveScreenCapture:(id)arg1 event:(NSInteger)arg2 { VOID_HANDLESCREENSHOT(%orig); } %end %hook IGDirectMediaViewerViewController - (void)screenshotObserverDidSeeScreenshotTaken:(id)arg1 { VOID_HANDLESCREENSHOT(%orig); } - (void)screenshotObserverDidSeeActiveScreenCapture:(id)arg1 event:(NSInteger)arg2 { VOID_HANDLESCREENSHOT(%orig); } %end %hook IGStoryViewerViewController - (void)screenshotObserverDidSeeScreenshotTaken:(id)arg1 { VOID_HANDLESCREENSHOT(%orig); } - (void)screenshotObserverDidSeeActiveScreenCapture:(id)arg1 event:(NSInteger)arg2 { VOID_HANDLESCREENSHOT(%orig); } %end %hook IGSundialFeedViewController - (void)screenshotObserverDidSeeScreenshotTaken:(id)arg1 { VOID_HANDLESCREENSHOT(%orig); } - (void)screenshotObserverDidSeeActiveScreenCapture:(id)arg1 event:(NSInteger)arg2 { VOID_HANDLESCREENSHOT(%orig); } %end %hook IGDirectVisualMessageViewerController - (void)screenshotObserverDidSeeScreenshotTaken:(id)arg1 { VOID_HANDLESCREENSHOT(%orig); } - (void)screenshotObserverDidSeeActiveScreenCapture:(id)arg1 event:(NSInteger)arg2 { VOID_HANDLESCREENSHOT(%orig); } %end ///////////////////////////////////////////////////////////////////////////// // Hide items // Direct suggested chats (in search bar) %hook IGDirectInboxSearchListAdapterDataSource - (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)]) { // Broadcast channels if ([[obj valueForKey:@"uniqueIdentifier"] isEqualToString:@"channels"]) { if ([SCIUtils getBoolPref:@"no_suggested_chats"]) { NSLog(@"[SCInsta] Hiding suggested chats (header)"); shouldHide = YES; } } // Ask Meta AI else if ([[obj valueForKey:@"labelTitle"] isEqualToString:@"Ask Meta AI"]) { if ([SCIUtils getBoolPref:@"hide_meta_ai"]) { NSLog(@"[SCInsta] Hiding meta ai suggested chats (header)"); shouldHide = YES; } } // AI else if ([[obj valueForKey:@"labelTitle"] isEqualToString:@"AI"]) { if ([SCIUtils getBoolPref:@"hide_meta_ai"]) { NSLog(@"[SCInsta] Hiding ai suggested chats (header)"); shouldHide = YES; } } } // AI agents section else if ( [obj isKindOfClass:%c(IGDirectInboxSearchAIAgentsPillsSectionViewModel)] || [obj isKindOfClass:%c(IGDirectInboxSearchAIAgentsSuggestedPromptViewModel)] || [obj isKindOfClass:%c(IGDirectInboxSearchAIAgentsSuggestedPromptLoggingViewModel)] ) { if ([SCIUtils getBoolPref:@"hide_meta_ai"]) { NSLog(@"[SCInsta] Hiding suggested chats (ai agents)"); shouldHide = YES; } } // Recipients list else if ([obj isKindOfClass:%c(IGDirectRecipientCellViewModel)]) { // Broadcast channels if ([[obj recipient] isBroadcastChannel]) { if ([SCIUtils getBoolPref:@"no_suggested_chats"]) { NSLog(@"[SCInsta] Hiding suggested chats (broadcast channels recipient)"); shouldHide = YES; } } // Meta AI (special section types) else if (([obj sectionType] == 20) || [obj sectionType] == 18) { if ([SCIUtils getBoolPref:@"hide_meta_ai"]) { NSLog(@"[SCInsta] Hiding meta ai suggested chats (meta ai recipient)"); shouldHide = YES; } } // Meta AI (catch-all) else if ([[[obj recipient] threadName] isEqualToString:@"Meta AI"]) { if ([SCIUtils getBoolPref:@"hide_meta_ai"]) { NSLog(@"[SCInsta] Hiding meta ai suggested chats (meta ai recipient)"); shouldHide = YES; } } } // Populate new objs array if (!shouldHide) { [filteredObjs addObject:obj]; } } return [filteredObjs copy]; } %end // Direct suggested chats (thread creation view) %hook IGDirectThreadCreationViewController - (id)objectsForListAdapter:(id)arg1 { NSArray *originalObjs = %orig(); NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]]; for (id obj in originalObjs) { BOOL shouldHide = NO; // Meta AI suggested user in direct new message view if ([SCIUtils getBoolPref:@"hide_meta_ai"]) { if ([obj isKindOfClass:%c(IGDirectCreateChatCellViewModel)]) { // "AI Chats" if ([[obj valueForKey:@"title"] isEqualToString:@"AI chats"]) { NSLog(@"[SCInsta] Hiding meta ai: direct thread creation ai chats section"); shouldHide = YES; } } else if ([obj isKindOfClass:%c(IGDirectRecipientCellViewModel)]) { // Meta AI suggested user if ([[[obj recipient] threadName] isEqualToString:@"Meta AI"]) { NSLog(@"[SCInsta] Hiding meta ai: direct thread creation ai suggestion"); shouldHide = YES; } } } // Invite friends to insta contacts upsell if ([SCIUtils getBoolPref:@"no_suggested_users"]) { if ([obj isKindOfClass:%c(IGContactInvitesSearchUpsellViewModel)]) { NSLog(@"[SCInsta] Hiding suggested users: invite contacts upsell"); shouldHide = YES; } } // Populate new objs array if (!shouldHide) { [filteredObjs addObject:obj]; } } return [filteredObjs copy]; } %end // Direct suggested chats (inbox view) %hook IGDirectInboxListAdapterDataSource - (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(IGDirectInboxHeaderCellViewModel)]) { // "Suggestions" header if ([[obj title] isEqualToString:@"Suggestions"]) { if ([SCIUtils getBoolPref:@"no_suggested_users"]) { NSLog(@"[SCInsta] Hiding suggested chats (header: messages tab)"); shouldHide = YES; } } // "Accounts to follow/message" header else if ([[obj title] hasPrefix:@"Accounts to"]) { if ([SCIUtils getBoolPref:@"no_suggested_users"]) { NSLog(@"[SCInsta] Hiding suggested users: (header: inbox view)"); shouldHide = YES; } } } // Suggested recipients else if ([obj isKindOfClass:%c(IGDirectInboxSuggestedThreadCellViewModel)]) { if ([SCIUtils getBoolPref:@"no_suggested_users"]) { NSLog(@"[SCInsta] Hiding suggested chats (recipients: channels tab)"); shouldHide = YES; } } // "Accounts to follow" recipients else if ([obj isKindOfClass:%c(IGDiscoverPeopleItemConfiguration)] || [obj isKindOfClass:%c(IGDiscoverPeopleConnectionItemConfiguration)]) { if ([SCIUtils getBoolPref:@"no_suggested_users"]) { NSLog(@"[SCInsta] Hiding suggested chats: (recipients: inbox view)"); shouldHide = YES; } } // Hide notes tray else if ([obj isKindOfClass:%c(IGDirectNotesTrayRowViewModel)]) { if ([SCIUtils getBoolPref:@"hide_notes_tray"]) { NSLog(@"[SCInsta] Hiding notes tray"); shouldHide = YES; } } // Populate new objs array if (!shouldHide) { [filteredObjs addObject:obj]; } } return [filteredObjs copy]; } %end // Explore page results %hook IGSearchListKitDataSource - (id)objectsForListAdapter:(id)arg1 { NSArray *originalObjs = %orig(); NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]]; for (id obj in originalObjs) { BOOL shouldHide = NO; // Meta AI if ([SCIUtils getBoolPref:@"hide_meta_ai"]) { // Section header if ([obj isKindOfClass:%c(IGLabelItemViewModel)]) { // "Ask Meta AI" search results header if ([[obj valueForKey:@"labelTitle"] isEqualToString:@"Ask Meta AI"]) { shouldHide = YES; } } // Empty search bar upsell view else if ([obj isKindOfClass:%c(IGSearchNullStateUpsellViewModel)]) { shouldHide = YES; } // Meta AI search suggestions else if ([obj isKindOfClass:%c(IGSearchResultNestedGroupViewModel)]) { shouldHide = YES; } // Meta AI suggested search results else if ([obj isKindOfClass:%c(IGSearchResultViewModel)]) { // itemType 6 is meta ai suggestions if ([obj itemType] == 6) { if ([SCIUtils getBoolPref:@"hide_meta_ai"]) { shouldHide = YES; } } // Meta AI user account in search results else if ([[[obj title] string] isEqualToString:@"meta.ai"]) { if ([SCIUtils getBoolPref:@"hide_meta_ai"]) { shouldHide = YES; } } } } // No suggested users if ([SCIUtils getBoolPref:@"no_suggested_users"]) { // Section header if ([obj isKindOfClass:%c(IGLabelItemViewModel)]) { // "Suggested for you" search results header if ([[obj valueForKey:@"labelTitle"] isEqualToString:@"Suggested for you"]) { shouldHide = YES; } } // Instagram users else if ([obj isKindOfClass:%c(IGDiscoverPeopleItemConfiguration)]) { shouldHide = YES; } // See all suggested users else if ([obj isKindOfClass:%c(IGSeeAllItemConfiguration)] && ((IGSeeAllItemConfiguration *)obj).destination == 4) { shouldHide = YES; } } // Populate new objs array if (!shouldHide) { [filteredObjs addObject:obj]; } } return [filteredObjs copy]; } %end // Story tray %hook IGMainStoryTrayDataSource - (id)allItemsForTrayUsingCachedValue:(BOOL)cached { NSArray *originalObjs = %orig(cached); NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]]; for (IGStoryTrayViewModel *obj in originalObjs) { BOOL shouldHide = NO; if ([SCIUtils getBoolPref:@"no_suggested_users"]) { if ([obj isKindOfClass:%c(IGStoryTrayViewModel)]) { NSNumber *type = [((IGStoryTrayViewModel *)obj) valueForKey:@"type"]; // 8/9 looks to be the types for recommended stories if ([type isEqual:@(8)] || [type isEqual:@(9)]) { NSLog(@"[SCInsta] Hiding suggested users: story tray"); shouldHide = YES; } } } if ([SCIUtils getBoolPref:@"hide_ads"]) { // "New!" account id is 3538572169 if ([obj isKindOfClass:%c(IGStoryTrayViewModel)] && (obj.isUnseenNux == YES || [obj.pk isEqualToString:@"3538572169"])) { NSLog(@"[SCInsta] Removing ads: story tray"); shouldHide = YES; } } // Populate new objs array if (!shouldHide) { [filteredObjs addObject:obj]; } } return [filteredObjs copy]; } %end // Story tray expanded footer (Suggested accounts to follow) %hook IGStoryTraySectionController - (void)storyTrayControllerShowSUPOGEducationBump { if ([SCIUtils getBoolPref:@"no_suggested_users"]) return; return %orig(); } %end // Modern IGDS app menus %hook IGDSMenu - (id)initWithMenuItems:(NSArray *)originalObjs edr:(BOOL)edr headerLabelText:(id)headerLabelText { NSMutableArray *filteredObjs = [NSMutableArray arrayWithCapacity:[originalObjs count]]; for (id obj in originalObjs) { BOOL shouldHide = NO; NSString *itemTitle = nil; @try { itemTitle = [obj valueForKey:@"title"]; } @catch (__unused id e) {} // Meta AI if ([itemTitle isEqualToString:@"AI images"] || [itemTitle isEqualToString:@"Meta AI"]) { if ([SCIUtils getBoolPref:@"hide_meta_ai"]) { shouldHide = YES; } } if (!shouldHide) { [filteredObjs addObject:obj]; } } extern NSArray *sciMaybeAppendStoryExcludeMenuItem(NSArray *); extern NSArray *sciMaybeAppendStoryAudioMenuItem(NSArray *); extern NSArray *sciMaybeAppendStoryMentionsMenuItem(NSArray *); NSArray *finalObjs = sciMaybeAppendStoryExcludeMenuItem([filteredObjs copy]); finalObjs = sciMaybeAppendStoryAudioMenuItem(finalObjs); finalObjs = sciMaybeAppendStoryMentionsMenuItem(finalObjs); return %orig(finalObjs, edr, headerLabelText); } %end ///////////////////////////////////////////////////////////////////////////// // Confirm buttons %hook IGFeedItemUFICell - (void)UFIButtonBarDidTapOnLike:(id)arg1 { if ([SCIUtils getBoolPref:@"like_confirm"]) { NSLog(@"[SCInsta] Confirm post like triggered"); [SCIUtils showConfirmation:^(void) { %orig; }]; } else { return %orig; } } - (void)UFIButtonBarDidTapOnRepost:(id)arg1 { if ([SCIUtils getBoolPref:@"repost_confirm"]) { NSLog(@"[SCInsta] Confirm repost triggered"); [SCIUtils showConfirmation:^(void) { %orig; }]; } else { return %orig; } } - (void)UFIButtonBarDidLongPressOnRepost:(id)arg1 { if ([SCIUtils getBoolPref:@"repost_confirm"]) { NSLog(@"[SCInsta] Confirm repost triggered (long press ignored)"); } else { return %orig; } } - (void)UFIButtonBarDidLongPressOnRepost:(id)arg1 withGestureRecognizer:(id)arg2 { if ([SCIUtils getBoolPref:@"repost_confirm"]) { NSLog(@"[SCInsta] Confirm repost triggered (long press ignored)"); } else { return %orig; } } %end // Hide repost button in feed (requires restart) %hook IGUFIInteractionCountsView - (void)updateUFIWithButtonsConfig:(id)config interactionCountProvider:(id)provider { %orig; if (![SCIUtils getBoolPref:@"hide_feed_repost"]) return; Ivar rv = class_getInstanceVariable(object_getClass(self), "_repostView"); if (rv) [object_getIvar((id)self, rv) setHidden:YES]; Ivar uv = class_getInstanceVariable(object_getClass(self), "_undoRepostButton"); if (uv) [object_getIvar((id)self, uv) setHidden:YES]; } %end %hook IGSundialViewerVerticalUFI - (void)_didTapLikeButton:(id)arg1 { if ([SCIUtils getBoolPref:@"like_confirm_reels"]) { NSLog(@"[SCInsta] Confirm reels like triggered"); [SCIUtils showConfirmation:^(void) { %orig; }]; } else { return %orig; } } - (void)_didLongPressLikeButton:(id)arg1 { if ([SCIUtils getBoolPref:@"like_confirm_reels"]) { NSLog(@"[SCInsta] Confirm repost triggered (long press ignored)"); } else { return %orig; } } - (void)_didTapRepostButton { if ([SCIUtils getBoolPref:@"hide_reels_repost"]) return; if ([SCIUtils getBoolPref:@"repost_confirm"]) { [SCIUtils showConfirmation:^(void) { %orig; }]; } else { %orig; } } - (void)_didLongPressRepostButton:(id)arg1 { if ([SCIUtils getBoolPref:@"hide_reels_repost"]) return; if ([SCIUtils getBoolPref:@"repost_confirm"]) return; %orig; } %end // Hide repost button at the view model level so IG's layout handles the gap %hook IGSundialViewerUFIViewModel - (BOOL)shouldShowRepostButton { if ([SCIUtils getBoolPref:@"hide_reels_repost"]) return NO; return %orig; } %end ///////////////////////////////////////////////////////////////////////////// // FLEX explorer gesture handler %hook IGRootViewController - (void)viewDidLoad { %orig; // Recognize 5-finger long press UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)]; longPress.minimumPressDuration = 1; longPress.numberOfTouchesRequired = 5; [self.view addGestureRecognizer:longPress]; } %new - (void)handleLongPress:(UILongPressGestureRecognizer *)sender { if (sender.state != UIGestureRecognizerStateBegan) return; if ([SCIUtils getBoolPref:@"flex_instagram"]) { [[objc_getClass("FLEXManager") sharedManager] showExplorer]; } } %end // Disable safe mode (defaults reset upon subsequent crashes) %hook IGSafeModeChecker - (id)initWithInstacrashCounterProvider:(void *)provider crashThreshold:(unsigned long long)threshold { if ([SCIUtils getBoolPref:@"disable_safe_mode"]) return nil; return %orig(provider, threshold); } - (unsigned long long)crashCount { if ([SCIUtils getBoolPref:@"disable_safe_mode"]) { return 0; } return %orig; } %end // liquid glass Swift class hooks static BOOL (*orig_swizzleToggle_isEnabled)(id, SEL) = NULL; static BOOL new_swizzleToggle_isEnabled(id self, SEL _cmd) { if ([SCIUtils getBoolPref:@"liquid_glass_buttons"]) return YES; return orig_swizzleToggle_isEnabled(self, _cmd); } static BOOL (*orig_expHelper_isEnabled)(id, SEL) = NULL; static BOOL new_expHelper_isEnabled(id self, SEL _cmd) { if ([SCIUtils getBoolPref:@"liquid_glass_buttons"]) return YES; return orig_expHelper_isEnabled(self, _cmd); } static BOOL (*orig_expHelper_isHomeFeed)(id, SEL) = NULL; static BOOL new_expHelper_isHomeFeed(id self, SEL _cmd) { if ([SCIUtils getBoolPref:@"liquid_glass_buttons"]) return YES; return orig_expHelper_isHomeFeed(self, _cmd); } // Liquid glass tab bar — C function hooks via fishhook // Credits: @euoradan (Radan) for discovering these flags static BOOL (*orig_IGFloatingTabBarEnabled)(void) = NULL; static BOOL (*orig_IGTabBarDynamicSizingEnabled)(void) = NULL; static BOOL (*orig_IGTabBarEnhancedDynamicSizingEnabled)(void) = NULL; static BOOL (*orig_IGTabBarHomecomingWithFloatingTabEnabled)(void) = NULL; static BOOL (*orig_IGTabBarViewPointFixEnabled)(void) = NULL; static NSInteger (*orig_IGTabBarStyleForLauncherSet)(NSInteger) = NULL; static BOOL hook_IGFloatingTabBarEnabled(void) { if ([SCIUtils getBoolPref:@"liquid_glass_surfaces"]) return YES; return orig_IGFloatingTabBarEnabled ? orig_IGFloatingTabBarEnabled() : NO; } static BOOL hook_IGTabBarDynamicSizingEnabled(void) { if ([SCIUtils getBoolPref:@"liquid_glass_surfaces"]) return YES; return orig_IGTabBarDynamicSizingEnabled ? orig_IGTabBarDynamicSizingEnabled() : NO; } static BOOL hook_IGTabBarEnhancedDynamicSizingEnabled(void) { if ([SCIUtils getBoolPref:@"liquid_glass_surfaces"]) return YES; return orig_IGTabBarEnhancedDynamicSizingEnabled ? orig_IGTabBarEnhancedDynamicSizingEnabled() : NO; } static BOOL hook_IGTabBarHomecomingWithFloatingTabEnabled(void) { if ([SCIUtils getBoolPref:@"liquid_glass_surfaces"]) return YES; return orig_IGTabBarHomecomingWithFloatingTabEnabled ? orig_IGTabBarHomecomingWithFloatingTabEnabled() : NO; } static BOOL hook_IGTabBarViewPointFixEnabled(void) { if ([SCIUtils getBoolPref:@"liquid_glass_surfaces"]) return YES; return orig_IGTabBarViewPointFixEnabled ? orig_IGTabBarViewPointFixEnabled() : NO; } static NSInteger hook_IGTabBarStyleForLauncherSet(NSInteger set) { if ([SCIUtils getBoolPref:@"liquid_glass_surfaces"]) return 1; return orig_IGTabBarStyleForLauncherSet ? orig_IGTabBarStyleForLauncherSet(set) : set; } %ctor { // ObjC hooks for liquid glass buttons Class swizzleToggle = objc_getClass("IGLiquidGlassSwizzle.IGLiquidGlassSwizzleToggle"); if (swizzleToggle) { MSHookMessageEx(swizzleToggle, @selector(isEnabled), (IMP)new_swizzleToggle_isEnabled, (IMP *)&orig_swizzleToggle_isEnabled); } Class expHelper = objc_getClass("IGLiquidGlassExperimentHelper.IGLiquidGlassNavigationExperimentHelper"); if (expHelper) { MSHookMessageEx(expHelper, @selector(isEnabled), (IMP)new_expHelper_isEnabled, (IMP *)&orig_expHelper_isEnabled); MSHookMessageEx(expHelper, @selector(isHomeFeedHeaderEnabled), (IMP)new_expHelper_isHomeFeed, (IMP *)&orig_expHelper_isHomeFeed); } // C function hooks for liquid glass tab bar / surfaces (fishhook) if ([SCIUtils getBoolPref:@"liquid_glass_surfaces"]) { int result = rebind_symbols((struct rebinding[]){ {"IGFloatingTabBarEnabled", (void *)hook_IGFloatingTabBarEnabled, (void **)&orig_IGFloatingTabBarEnabled}, {"IGTabBarDynamicSizingEnabled", (void *)hook_IGTabBarDynamicSizingEnabled, (void **)&orig_IGTabBarDynamicSizingEnabled}, {"IGTabBarEnhancedDynamicSizingEnabled", (void *)hook_IGTabBarEnhancedDynamicSizingEnabled, (void **)&orig_IGTabBarEnhancedDynamicSizingEnabled}, {"IGTabBarHomecomingWithFloatingTabEnabled", (void *)hook_IGTabBarHomecomingWithFloatingTabEnabled, (void **)&orig_IGTabBarHomecomingWithFloatingTabEnabled}, {"IGTabBarViewPointFixEnabled", (void *)hook_IGTabBarViewPointFixEnabled, (void **)&orig_IGTabBarViewPointFixEnabled}, {"IGTabBarStyleForLauncherSet", (void *)hook_IGTabBarStyleForLauncherSet, (void **)&orig_IGTabBarStyleForLauncherSet}, }, 6); NSLog(@"[SCInsta] Liquid glass fishhook result=%d floating=%p dynamic=%p enhanced=%p homecoming=%p viewpoint=%p style=%p", result, orig_IGFloatingTabBarEnabled, orig_IGTabBarDynamicSizingEnabled, orig_IGTabBarEnhancedDynamicSizingEnabled, orig_IGTabBarHomecomingWithFloatingTabEnabled, orig_IGTabBarViewPointFixEnabled, orig_IGTabBarStyleForLauncherSet); } }