mirror of
https://github.com/faroukbmiled/RyukGram.git
synced 2026-06-06 23:43:54 +02:00
86eaa95019
### Features - **Open Instagram links in app (Safari extension)** — bundled Safari web extension (sideload IPA only). Enable in Safari → Extensions; instagram.com links open in the app. - **Localization** — every user-facing string flows through a central translation layer. Globe button in Settings; missing keys fall back to English. Ships English only — see the "Translating RyukGram" section in the README to add more. - **Action buttons** — context-aware menus on feed, reels, and stories (expand, repost, download, copy caption, etc.) with per-context default tap action and carousel/multi-story bulk download - **Enhanced HD downloads** — up to 1080p via DASH + FFmpegKit with quality picker, preview playback, encoding-speed options, and 720p fallback - **Repost**, **media viewer**, **media zoom** (long-press), **download pill** (frosted glass, stacks concurrent downloads) - **Fake location** — overrides CoreLocation app-wide, map picker + saved presets, optional quick-toggle button on the Friends Map - **Messages-only mode** — strips every tab except DM inbox + profile - **Launch tab** — pick which tab the app opens to - Full last active date in DMs — show full date instead of "Active 2h ago" - Custom date format — 12 formats with per-surface toggles (feed, notes/comments/stories, DMs) - Send files in DMs (experimental) - View story mentions - Hide suggested stories - Story tray long-press actions — view HD profile picture from the tray menu - Advance on story reply — auto-skip to next story after sending a reply or reaction - Mark story as seen on reply or emoji reaction - Hide metrics (likes, comments, shares counts) - Hide messages tab - Hide voice/video call buttons in DM thread header (independent toggles) - Disable app haptics - Disable reels tab refresh - Disable disappearing messages mode in DMs - Follow indicator — shows whether the profile user follows you - Copy note text on long press - Zoom profile photo — long press opens full-screen viewer - Notes actions — copy text, download GIF/audio from notes long-press menu - Confirm unfollow - Feed refresh controls — disable background refresh, home button refresh, and home button scroll ### Improvements - Default tap action: added copy URL, repost, and view mentions options; dynamic menu generation per context - Settings pages reordered: General → Feed → Stories → Reels → Messages → Profile → Navigation → Saving → Confirmations - Fake location picker: native Apple Maps-style UI (search, long-press to drop pin, current location) - Liquid glass floating tab bar + dynamic sizing - Upload audio: FFmpegKit re-encode + trim for any audio/video input - Settings reorganized with per-context action button config; new Profile page - Highlight cover: full-screen viewer replaces direct download - Switched HD encoder to `h264_videotoolbox` (hardware) — no GPL FFmpegKit required - Legacy long-press download deprecated (off by default), replaced by action buttons ### Fixes - Hide suggested stories no longer removes followed users' stories on scroll - Settings search bar transparency with liquid glass off; auto-deactivates on push - HD download cancel: tapping pill aborts in-flight downloads + FFmpeg sessions cleanly - Download pill stuck state on background/foreground, progress reset per download - Disappearing messages mode confirmation not firing on swipe - Detailed color picker not working on story draw `†` - DM seen toggle menu not updating after tap - Reel refresh confirmation appearing on first app launch `†` - Reels action button displacing profile pictures on photo reels - Disappearing DM media download (expand, share, save to Photos with progress pill) - Carousel "Download all" not showing item count in feed - Encoding speed setting being ignored for HD downloads - Various upstream SCInsta merges (Meta AI hiding, suggested chats hiding, notes tray) — marked `†` > `†` Merged from upstream [SCInsta](https://github.com/SoCuul/SCInsta) by SoCuul ### Credits - Thanks to [@erupts0](https://github.com/erupts0) (John) for testing and feature suggestions - Thanks to [@euoradan](https://t.me/euoradan) (Radan) for experimental Instagram feature flag research - Safari extension forked/cleaned from [BillyCurtis/OpenInstagramSafariExtension](https://github.com/BillyCurtis/OpenInstagramSafariExtension) ### Known Issues - Preserved unsent messages can't be removed via "Delete for you"; pull-to-refresh clears them (warning available in settings) - "Delete for you" detection uses a ~2s window after the local action — a real unsend landing in that window may be missed (rare)
172 lines
7.2 KiB
Objective-C
172 lines
7.2 KiB
Objective-C
#import "SCIDateFormatPickerVC.h"
|
|
#import "../Utils.h"
|
|
#import "../Features/General/SCIDateFormatEntries.h"
|
|
|
|
static NSString *const kFmtKey = @"feed_date_format";
|
|
static NSString *const kSecKey = @"feed_date_show_seconds";
|
|
|
|
// [key, pattern, pattern_with_seconds]
|
|
static NSArray<NSArray *> *sciDateFormatOptions(void) {
|
|
static NSArray *opts = nil;
|
|
static dispatch_once_t once;
|
|
dispatch_once(&once, ^{
|
|
opts = @[
|
|
@[@"default", @"", @""],
|
|
@[@"short", @"MMM d", @"MMM d"],
|
|
@[@"medium", @"MMM d, yyyy", @"MMM d, yyyy"],
|
|
@[@"full", @"MMM d, yyyy 'at' h:mm a", @"MMM d, yyyy 'at' h:mm:ss a"],
|
|
@[@"time_12", @"MMM d 'at' h:mm a", @"MMM d 'at' h:mm:ss a"],
|
|
@[@"time_24", @"MMM d 'at' HH:mm", @"MMM d 'at' HH:mm:ss"],
|
|
@[@"dd_mmm", @"dd-MMM-yyyy 'at' h:mm a", @"dd-MMM-yyyy 'at' h:mm:ss a"],
|
|
@[@"day_slash", @"dd/MM/yyyy h:mm a", @"dd/MM/yyyy h:mm:ss a"],
|
|
@[@"month_slash", @"MM/dd/yyyy h:mm a", @"MM/dd/yyyy h:mm:ss a"],
|
|
@[@"euro", @"dd.MM.yyyy HH:mm", @"dd.MM.yyyy HH:mm:ss"],
|
|
@[@"iso", @"yyyy-MM-dd", @"yyyy-MM-dd"],
|
|
@[@"iso_time", @"yyyy-MM-dd HH:mm", @"yyyy-MM-dd HH:mm:ss"],
|
|
];
|
|
});
|
|
return opts;
|
|
}
|
|
|
|
// [pref_key, label]
|
|
static NSArray<NSArray<NSString *> *> *sciSurfaceEntries(void) {
|
|
static NSArray *entries = nil;
|
|
static dispatch_once_t once;
|
|
dispatch_once(&once, ^{
|
|
NSMutableArray *m = [NSMutableArray array];
|
|
NSMutableSet *seen = [NSMutableSet set];
|
|
#define SCI_EMIT(NAME, SEL_, LABEL, ARITY, PREF) \
|
|
if (strlen(LABEL) && ![seen containsObject:@PREF]) { \
|
|
[seen addObject:@PREF]; \
|
|
[m addObject:@[@PREF, @LABEL]]; \
|
|
}
|
|
SCI_DATE_FORMAT_ENTRIES(SCI_EMIT)
|
|
#undef SCI_EMIT
|
|
entries = [m copy];
|
|
});
|
|
return entries;
|
|
}
|
|
|
|
static NSDate *sciRefDate(void) {
|
|
static NSDate *ref = nil;
|
|
static dispatch_once_t once;
|
|
dispatch_once(&once, ^{ ref = [NSDate dateWithTimeIntervalSince1970:1736348730]; });
|
|
return ref;
|
|
}
|
|
|
|
static NSString *sciExampleForKey(NSString *key) {
|
|
if (!key.length || [key isEqualToString:@"default"]) return @"Default";
|
|
BOOL sec = [[NSUserDefaults standardUserDefaults] boolForKey:kSecKey];
|
|
for (NSArray *opt in sciDateFormatOptions()) {
|
|
if ([opt[0] isEqualToString:key]) {
|
|
NSString *pattern = sec ? opt[2] : opt[1];
|
|
if (!pattern.length) return SCILocalized(@"Default");
|
|
NSDateFormatter *df = [NSDateFormatter new];
|
|
df.dateFormat = pattern;
|
|
return [df stringFromDate:sciRefDate()];
|
|
}
|
|
}
|
|
return SCILocalized(@"Default");
|
|
}
|
|
|
|
@implementation SCIDateFormatPickerVC {
|
|
UITableView *_tableView;
|
|
}
|
|
|
|
+ (NSString *)currentFormatExample {
|
|
return sciExampleForKey([SCIUtils getStringPref:kFmtKey]);
|
|
}
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
self.title = SCILocalized(@"Date Format");
|
|
_tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleInsetGrouped];
|
|
_tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
_tableView.dataSource = self;
|
|
_tableView.delegate = self;
|
|
[self.view addSubview:_tableView];
|
|
}
|
|
|
|
// Sections: 0 = format options, 1 = show seconds, 2 = surface toggles
|
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tv { return 3; }
|
|
|
|
- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)s {
|
|
if (s == 0) return (NSInteger)sciDateFormatOptions().count;
|
|
if (s == 1) return 1;
|
|
return (NSInteger)sciSurfaceEntries().count;
|
|
}
|
|
|
|
- (NSString *)tableView:(UITableView *)tv titleForHeaderInSection:(NSInteger)s {
|
|
if (s == 0) return SCILocalized(@"Format");
|
|
if (s == 2) return SCILocalized(@"Apply to");
|
|
return @"";
|
|
}
|
|
|
|
- (NSString *)tableView:(UITableView *)tv titleForFooterInSection:(NSInteger)s {
|
|
if (s == 2) return SCILocalized(@"Toggle each NSDate formatter IG uses. Different surfaces (feed, comments, stories, DMs) go through different methods — enable the ones you want the custom format applied to.");
|
|
return nil;
|
|
}
|
|
|
|
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)ip {
|
|
if (ip.section == 1) {
|
|
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:@"sec"];
|
|
if (!cell) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"sec"];
|
|
cell.textLabel.text = SCILocalized(@"Show seconds");
|
|
UISwitch *sw = [UISwitch new];
|
|
sw.on = [[NSUserDefaults standardUserDefaults] boolForKey:kSecKey];
|
|
[sw addTarget:self action:@selector(secondsToggled:) forControlEvents:UIControlEventValueChanged];
|
|
cell.accessoryView = sw;
|
|
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
|
return cell;
|
|
}
|
|
|
|
if (ip.section == 2) {
|
|
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:@"surf"];
|
|
if (!cell) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"surf"];
|
|
NSArray *entry = sciSurfaceEntries()[ip.row];
|
|
cell.textLabel.text = entry[1];
|
|
cell.textLabel.numberOfLines = 0;
|
|
cell.textLabel.font = [UIFont systemFontOfSize:15];
|
|
UISwitch *sw = [UISwitch new];
|
|
sw.on = [[NSUserDefaults standardUserDefaults] boolForKey:entry[0]];
|
|
sw.tag = ip.row;
|
|
[sw addTarget:self action:@selector(surfaceToggled:) forControlEvents:UIControlEventValueChanged];
|
|
cell.accessoryView = sw;
|
|
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
|
return cell;
|
|
}
|
|
|
|
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:@"df"];
|
|
if (!cell) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"df"];
|
|
|
|
NSString *key = sciDateFormatOptions()[ip.row][0];
|
|
cell.textLabel.text = sciExampleForKey(key);
|
|
cell.textLabel.font = [UIFont systemFontOfSize:16];
|
|
|
|
NSString *current = [SCIUtils getStringPref:kFmtKey];
|
|
if (!current.length) current = @"default";
|
|
cell.accessoryType = [current isEqualToString:key]
|
|
? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
|
|
|
|
return cell;
|
|
}
|
|
|
|
- (void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)ip {
|
|
[tv deselectRowAtIndexPath:ip animated:YES];
|
|
if (ip.section != 0) return;
|
|
[[NSUserDefaults standardUserDefaults] setObject:sciDateFormatOptions()[ip.row][0] forKey:kFmtKey];
|
|
[tv reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
|
|
}
|
|
|
|
- (void)secondsToggled:(UISwitch *)sw {
|
|
[[NSUserDefaults standardUserDefaults] setBool:sw.on forKey:kSecKey];
|
|
[_tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
|
|
}
|
|
|
|
- (void)surfaceToggled:(UISwitch *)sw {
|
|
NSArray *entry = sciSurfaceEntries()[sw.tag];
|
|
[[NSUserDefaults standardUserDefaults] setBool:sw.on forKey:entry[0]];
|
|
}
|
|
|
|
@end
|