From c926359c70fba3d28b2e778501e9f0a5dac453a2 Mon Sep 17 00:00:00 2001 From: lint <47455468+lint@users.noreply.github.com> Date: Mon, 23 Dec 2019 18:51:06 -0500 Subject: [PATCH] Add Antenna support --- prefs/Resources/Root.plist | 22 +++ tfdidthatsay.plist | Bin 229 -> 250 bytes tweak/Antenna.h | 80 +++++++++++ tweak/Antenna.xm | 279 +++++++++++++++++++++++++++++++++++++ 4 files changed, 381 insertions(+) create mode 100644 tweak/Antenna.h create mode 100644 tweak/Antenna.xm diff --git a/prefs/Resources/Root.plist b/prefs/Resources/Root.plist index 54b355d..1e6f384 100644 --- a/prefs/Resources/Root.plist +++ b/prefs/Resources/Root.plist @@ -96,6 +96,20 @@ label Enable BaconReader + + PostNotification + com.lint.undelete.prefs.changed + cell + PSSwitchCell + default + + defaults + com.lint.undelete.prefs + key + isAntennaEnabled + label + Enable Antenna + cell PSGroupCell @@ -146,6 +160,14 @@ showValue + + cell + PSGroupCell + footerText + Enable or disable undelete option appearing only on deleted/removed comments and posts. + label + visibility + title TFDidThatSay? diff --git a/tfdidthatsay.plist b/tfdidthatsay.plist index a8f55103811680037d615ea00fa0b3e161d7339e..712ff8192b244f8c89cc801b75a1ea21fd81b999 100644 GIT binary patch delta 82 zcmaFL_=|CZEXQ(IHg*n9F7Amc)9m8~1d{V}^%8S)Qj4pyfpk$a2e*)@tg2;bLHqpO R3}C>>2%#Bxpfrp+2LMw&59^&iEc-H6Hg*n9u8FGCv^ltiL}gVhLh{?@F@OOhBZOw)hSD%<7XW402tfb< diff --git a/tweak/Antenna.h b/tweak/Antenna.h new file mode 100644 index 0000000..c65ce82 --- /dev/null +++ b/tweak/Antenna.h @@ -0,0 +1,80 @@ + +/* -- Comment Interfaces -- */ + +@interface RCCommentSwift +@property(strong, nonatomic) NSString *author; +@property(strong, nonatomic) NSString *itemId; +@property(strong, nonatomic) id commentText; +@end + +@interface RCCommentTextSwift +@property(strong, nonatomic) NSString *author; +@property(strong, nonatomic) NSString *body; +@property(strong, nonatomic) NSString *bodyHTML; +@property(strong, nonatomic) NSAttributedString *bodyAttributedString; +@property(strong, nonatomic) NSAttributedString *bodyAttributedStringForPreview; +@property(strong, nonatomic) id textHeightCache; +@end + +@interface RCCommentCell : NSObject +@property(strong, nonatomic) id comment; +-(void) updateWithModelObject:(id) arg1; +@end + +@interface RCPostCommentsController +@property(strong, nonatomic) id postCommentsCollector; +@property(strong, nonatomic) id delegate; +@property(strong, nonatomic) NSMutableDictionary *commentHeightCache; +-(void) controllerWillChangeContent:(id) arg1; +-(void) controllerDidChangeContent:(id) arg1; +-(void) controller:(id) arg1 didChange:(id) arg2 at:(id) arg3 for:(long long) arg4 newIndexPath:(id) arg5; + +//custom elements +-(void)handleUndeleteCommentAction; +@end + +@interface AHKActionSheet +@property(strong, nonatomic) NSMutableArray *items; +-(void) addButtonWithTitle:(id) arg1 image:(id) arg2 type:(long long) arg3 handler:(id) arg4; +@end + +@interface AHKActionSheetItem +@property(strong, nonatomic) UIImage *image; +@end + +/* -- Post Interfaces -- */ + +@interface RCPostSwift +@property(strong, nonatomic) NSString *author; +@property(strong, nonatomic) NSNumber *isSelf; +@property(strong, nonatomic) NSString *itemId; +@property(strong, nonatomic) id selfCommentText; +-(BOOL) isSelfPost; +@end + +@interface RCPostActionsSectionHeader : UIView +@property(strong, nonatomic) NSMutableArray *defaultHeaderButtons; +@property(strong, nonatomic) id delegate; + +//custom elements +@property(strong, nonatomic) UIButton *undeleteButton; +@end + +@interface RCPostSectionHeaderButton +@property(strong, nonatomic) UIColor *defaultColor; +@end + +@interface RCPostDownloadViewController +@property(strong, nonatomic) UITableView *tableView; +@property(strong, nonatomic) id headerCellController; +@property(strong, nonatomic) id postInternal; +@end + +@interface RCPostHeaderCellController : NSObject +@property(strong, nonatomic) id post; +@property(strong, nonatomic) UITableView *tableView; +-(void) loadView; + +//custom elements +-(void) handleUndeletePostAction:(id) arg1; +@end diff --git a/tweak/Antenna.xm b/tweak/Antenna.xm new file mode 100644 index 0000000..1e77ec3 --- /dev/null +++ b/tweak/Antenna.xm @@ -0,0 +1,279 @@ + +#import "Antenna.h" +#import "assets/MMMarkdown.h" + +static BOOL isAntennaEnabled; +static BOOL isTFDeletedOnly; +static CGFloat pushshiftRequestTimeoutValue; + +%group Antenna + +BOOL shouldHaveAntennaUndeleteAction = NO; +id tfAntennaController; +id tfAntennaCommentCell; + +%hook RCPostSwift +%end + +%hook RCCommentTextSwift +%end + + +%hook RCCommentSwift + +-(BOOL) isCommentDeleted{ + return NO; +} + +%end + + +%hook RCPostCommentsController + +-(void) didLongPressCell:(id) arg1 gesture:(id) arg2 { + + NSString *commentBody = [[[arg1 comment] commentText] body]; + + if ((isTFDeletedOnly && ([commentBody isEqualToString:@"[deleted]"] || [commentBody isEqualToString:@"[removed]"])) || !isTFDeletedOnly){ + tfAntennaController = self; + tfAntennaCommentCell = arg1; + shouldHaveAntennaUndeleteAction = YES; + } + + %orig; + + shouldHaveAntennaUndeleteAction = NO; +} + +%new +-(void) handleUndeleteCommentAction{ + + id comment = [tfAntennaCommentCell comment]; + id commentText = [comment commentText]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; + NSOperationQueue *queue = [[NSOperationQueue alloc] init]; + + [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://api.pushshift.io/reddit/search/comment/?ids=%@&fields=author,body",[comment itemId]]]]; + [request setHTTPMethod:@"GET"]; + [request setTimeoutInterval:pushshiftRequestTimeoutValue]; + + [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { + + NSString *author = @"[author]"; + NSString *body = @"[body]"; + + if (data != nil && error == nil){ + id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + if ([[jsonData objectForKey:@"data"] count] != 0){ + author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"]; + body = [[jsonData objectForKey:@"data"][0] objectForKey:@"body"]; + if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){ + body = @"[pushshift was unable to archive this]"; + } + } else { + body = @"[pushshift has not archived this yet]"; + } + } else if (error != nil || data == nil){ + body = [NSString stringWithFormat:@"[an error occured while attempting to contact pushshift api (%@)]", [error localizedDescription]]; + } + + [comment setAuthor:author]; + [commentText setBody:body]; + [commentText setBodyHTML:[%c(MMMarkdown) HTMLStringWithMarkdown:body extensions:MMMarkdownExtensionsGitHubFlavored error:nil]]; + + [commentText setBodyAttributedString:nil]; + [commentText setBodyAttributedStringForPreview:nil]; + [commentText setTextHeightCache:nil]; + [self setCommentHeightCache:nil]; + + [tfAntennaCommentCell performSelectorOnMainThread:@selector(updateWithModelObject:) withObject:comment waitUntilDone:YES]; + [[[self delegate] tableView] performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; + }]; +} + +%end + + +%hook AHKActionSheet + +-(void)show{ + + if (shouldHaveAntennaUndeleteAction){ + + UIImage *undeleteImage = [UIImage imageWithContentsOfFile:@"/var/mobile/Library/Application Support/TFDidThatSay/eye160dark.png"]; + + AHKActionSheetItem *actionItem = [self items][0]; + CGSize actionItemImageSize = [[actionItem image] size]; + CGSize newUndeleteImageSize = CGSizeMake(actionItemImageSize.width * 2, actionItemImageSize.height * 2); + + UIGraphicsBeginImageContext(newUndeleteImageSize); + [undeleteImage drawInRect:CGRectMake(0, 0, newUndeleteImageSize.width, newUndeleteImageSize.height)]; + undeleteImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + undeleteImage = [[UIImage alloc] initWithCGImage:[undeleteImage CGImage] scale:2 orientation:UIImageOrientationUp]; + + [self addButtonWithTitle:@"TF did that say?" image:undeleteImage type:0 handler:^{[tfAntennaController handleUndeleteCommentAction];}]; + } + + %orig; +} + +%end + + +%hook RCPostActionsSectionHeader +%property(strong, nonatomic) UIButton *undeleteButton; + +-(void) layoutSubviews{ + + BOOL isAbleToUndeletePost = NO; + + id post = [[self delegate] postInternal]; + NSString *postBody = [[post selfCommentText] body]; + + if ([post isSelfPost]){ + if ((isTFDeletedOnly && ([postBody isEqualToString:@"[deleted]"] || [postBody isEqualToString:@"[removed]"])) || !isTFDeletedOnly) { + isAbleToUndeletePost = YES; + + NSMutableArray *barButtons = [self defaultHeaderButtons]; + + if ([barButtons count] <= 5){ + UIView *tempView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)]; + + [barButtons addObject:tempView]; + } + + %orig; + + if (![self undeleteButton]){ + UIButton *undeleteButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [undeleteButton addTarget:[[self delegate] headerCellController] action:@selector(handleUndeletePostAction:) forControlEvents:UIControlEventTouchUpInside]; + + [undeleteButton setImage:[[UIImage imageWithContentsOfFile:@"/var/mobile/Library/Application Support/TFDidThatSay/eye160dark.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal]; + undeleteButton.imageView.contentMode = UIViewContentModeScaleAspectFit; + undeleteButton.tintColor = [barButtons[4] defaultColor]; + + [self addSubview:undeleteButton]; + [self setUndeleteButton:undeleteButton]; + } + + [[self undeleteButton] setFrame:[barButtons[[barButtons count] - 1] frame]]; + } + } + + if (!isAbleToUndeletePost){ + %orig; + } +} + +%end + + +%hook RCPostHeaderCellController + +%new +-(void) handleUndeletePostAction:(id) sender{ + + [sender setEnabled:NO]; + + id post = [self post]; + id postText = [post selfCommentText]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; + NSOperationQueue *queue = [[NSOperationQueue alloc] init]; + + [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://api.pushshift.io/reddit/search/submission/?ids=%@&fields=author,selftext",[post itemId]]]]; + [request setHTTPMethod:@"GET"]; + [request setTimeoutInterval:pushshiftRequestTimeoutValue]; + + [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { + + NSString *author = @"[author]"; + NSString *body = @"[body]"; + + if (data != nil && error == nil){ + id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + if ([[jsonData objectForKey:@"data"] count] != 0){ + author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"]; + body = [[jsonData objectForKey:@"data"][0] objectForKey:@"selftext"]; + if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){ + body = @"[pushshift was unable to archive this]"; + } + } else { + body = @"[pushshift has not archived this yet]"; + } + } else if (error != nil || data == nil){ + body = [NSString stringWithFormat:@"[an error occured while attempting to contact pushshift api (%@)]", [error localizedDescription]]; + } + + [post setAuthor:author]; + [postText setBody:body]; + [postText setBodyHTML:[%c(MMMarkdown) HTMLStringWithMarkdown:body extensions:MMMarkdownExtensionsGitHubFlavored error:nil]]; + + [postText setBodyAttributedString:nil]; + [postText setBodyAttributedStringForPreview:nil]; + [postText setTextHeightCache:nil]; + + [self performSelectorOnMainThread:@selector(loadView) withObject:nil waitUntilDone:YES]; + [[self tableView] performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; + + [sender setEnabled:YES]; + }]; +} + +%end + +%end + + +static void loadPrefs(){ + NSMutableDictionary *prefs = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/User/Library/Preferences/com.lint.undelete.prefs.plist"]; + + if (prefs){ + + if ([prefs objectForKey:@"isAntennaEnabled"] != nil){ + isAntennaEnabled = [[prefs objectForKey:@"isAntennaEnabled"] boolValue]; + } else { + isAntennaEnabled = YES; + } + + if ([prefs objectForKey:@"isTFDeletedOnly"] != nil){ + isTFDeletedOnly = [[prefs objectForKey:@"isTFDeletedOnly"] boolValue]; + } else { + isTFDeletedOnly = YES; + } + + if ([prefs objectForKey:@"requestTimeoutValue"] != nil){ + pushshiftRequestTimeoutValue = [[prefs objectForKey:@"requestTimeoutValue"] doubleValue]; + } else { + pushshiftRequestTimeoutValue = 10; + } + + } else { + isAntennaEnabled = YES; + isTFDeletedOnly = YES; + pushshiftRequestTimeoutValue = 10; + } +} + +static void prefsChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { + loadPrefs(); +} + + +%ctor{ + loadPrefs(); + + NSString* processName = [[NSProcessInfo processInfo] processName]; + + if ([processName isEqualToString:@"amrc"]){ + if (isAntennaEnabled){ + + CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, prefsChanged, CFSTR("com.lint.undelete.prefs.changed"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately); + + %init(Antenna, RCCommentSwift = objc_getClass("amrc.RCCommentSwift"), RCPostSwift = objc_getClass("amrc.RCPostSwift"), RCCommentTextSwift = objc_getClass("amrc.RCCommentTextSwift")); + } + } +}