From c3dea79a28affa70383b434236f1ca324320c23a Mon Sep 17 00:00:00 2001 From: lint <47455468+lint@users.noreply.github.com> Date: Tue, 8 Oct 2019 01:03:45 -0400 Subject: [PATCH] Add Narwhal support, fix empty data crash --- Makefile | 6 +- tfdidthatsay.plist | 3 +- tweak/Apollo.xm | 34 +++--- tweak/Narwhal.h | 131 ++++++++++++++++++++ tweak/Narwhal.xm | 289 +++++++++++++++++++++++++++++++++++++++++++++ tweak/Reddit.xm | 36 +++--- 6 files changed, 461 insertions(+), 38 deletions(-) create mode 100644 tweak/Narwhal.h create mode 100644 tweak/Narwhal.xm diff --git a/Makefile b/Makefile index c2a0dc4..df0db51 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,14 @@ -ARCHS = arm64 +ARCHS = arm64 arm64e include $(THEOS)/makefiles/common.mk TWEAK_NAME = tfdidthatsay tfdidthatsay_FILES = $(wildcard tweak/*.xm) -#tfdidthatsay_CFLAGS = -fobjc-arc +tweak/Narwhal.xm_CFLAGS = -fobjc-arc include $(THEOS_MAKE_PATH)/tweak.mk after-install:: install.exec "killall -9 Reddit" + install.exec "killall -9 Apollo" + install.exec "killall -9 narwhal" \ No newline at end of file diff --git a/tfdidthatsay.plist b/tfdidthatsay.plist index be1e7da..344fea0 100644 --- a/tfdidthatsay.plist +++ b/tfdidthatsay.plist @@ -2,7 +2,8 @@ Filter = { Bundles = ( "com.reddit.Reddit", - "com.christianselig.Apollo" + "com.christianselig.Apollo", + "com.rickharrison.narwhal" ); }; } \ No newline at end of file diff --git a/tweak/Apollo.xm b/tweak/Apollo.xm index 5c84b5b..b5e7768 100644 --- a/tweak/Apollo.xm +++ b/tweak/Apollo.xm @@ -50,18 +50,18 @@ NSDictionary* apolloBodyAttributes = nil; NSString *author = @"[author]"; NSString *body = @"[body]"; - + if (data != nil && error == nil){ - id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; - - author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"]; - body = [[jsonData objectForKey:@"data"][0] objectForKey:@"body"]; - - if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){ - body = @"[comment was unable to be archived]"; + 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 = @"[an error occured]"; } @@ -138,16 +138,16 @@ NSDictionary* apolloBodyAttributes = nil; NSString *body = @"[body]"; if (data != nil && error == nil){ - id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; - - author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"]; - body = [[jsonData objectForKey:@"data"][0] objectForKey:@"selftext"]; - - if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){ - body = @"[comment was unable to be archived]"; + 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 = @"[an error occured]"; } diff --git a/tweak/Narwhal.h b/tweak/Narwhal.h new file mode 100644 index 0000000..6dbe6d3 --- /dev/null +++ b/tweak/Narwhal.h @@ -0,0 +1,131 @@ + +/* -- Comment Interfaces -- */ + +@interface RKComment +@property(assign,nonatomic) NSString* author; +@property(assign,nonatomic) NSString* parentID; +@property(assign,nonatomic) NSString* body; +@property(assign,nonatomic) NSString* fullName; + +-(BOOL) isSaved; +@end + +@interface NRTCommentsManager +@property(assign,nonatomic) NSMutableArray* comments; + +-(void) updateComment:(id) arg1 fromEdited:(id) arg2; +@end + +/* -- Post Interfaces -- */ + +@interface RKLink +@property(assign,nonatomic) NSString* author; +@property(assign,nonatomic) NSString* selfText; +@property(assign,nonatomic) NSString* fullName; +@end + +@interface NRTLinkTitleCell +-(void) configureCellForLink:(id) arg1; +@end + +@interface NRTLinkTextCell +@property(assign,nonatomic) id bodyLabel; + +-(void) configureCellForText:(id) arg1 links:(id) arg2; +@end + +/* -- Other Interfaces -- */ + +@interface NRTAuthManager +@property(assign,nonatomic) NSString* currentUsername; + ++(id) sharedManager; +@end + +@interface NRTMarkdownManager ++(id) attributedStringFromMarkdown:(id) arg1 type:(id) arg2; +@end + +@interface NRTAttributedLabel +@property(assign,nonatomic) id attributedText; +@end + +@interface NRTMediaViewController : UIViewController +@end + +@interface NRTLinkViewController : UIViewController +@property(assign,nonatomic) id comment; +@property(assign,nonatomic) id commentsManager; +@property(assign,nonatomic) id linkTextOffscreenCell; +@property(assign,nonatomic) id linkTitleOffscreenCell; +@property(assign,nonatomic) id link; +@property(assign,nonatomic) id linkText; +@property(assign,nonatomic) id tableView; + +-(void) _handleActionSheetCopyCommentText:(id) arg1; +-(void) _handleActionSheetDeleteComment:(id) arg1; +-(void) _handleActionSheetDeletePost; +-(void) _handleActionSheetEditComment:(id) arg1; +-(void) _handleActionSheetEditPost; +-(void) _handleActionSheetOpenChrome; +-(void) _handleActionSheetOpenSafari; +-(void) _handleActionSheetPrivateMessage:(id) arg1; +-(void) _handleActionSheetRefreshComments; +-(void) _handleActionSheetRefreshPost; +-(void) _handleActionSheetReportComment:(id) arg1; +-(void) _handleActionSheetReportPost; +-(void) _handleActionSheetSaveComment:(id) arg1 index:(NSUInteger) arg2; +-(void) _handleActionSheetShareComment:(id) arg1; +-(void) _handleActionSheetShareLink; +-(void) _handleActionSheetSharePost; +-(void) _handleActionSheetSortComments; +-(void) _handleActionSheetUnsaveComment:(id) arg1 index:(NSUInteger) arg2; +-(void) _handleActionSheetViewParent:(id) arg1; +-(void) _handleActionSheetViewProfile:(id) arg1; + +//custom elements +-(void) handleUndeleteAction:(id) arg1; +-(void) handleUndeletePostAction; +-(void) mainThreadTest:(id) arg1; +@end + +@interface NRTMediaTableViewDataSource +@property(assign,nonatomic) id commentsManager; +@property(assign,nonatomic) id parentController; + +-(void) _handleActionSheetCopyCommentText:(id) arg1; +-(void) _handleActionSheetDeleteComment:(id) arg1; +-(void) _handleActionSheetDeletePost; +-(void) _handleActionSheetEditComment:(id) arg1; +-(void) _handleActionSheetEditPost; +-(void) _handleActionSheetOpenChrome; +-(void) _handleActionSheetOpenSafari; +-(void) _handleActionSheetPrivateMessage:(id) arg1; +-(void) _handleActionSheetRefreshComments; +-(void) _handleActionSheetRefreshPost; +-(void) _handleActionSheetReportComment:(id) arg1; +-(void) _handleActionSheetReportPost; +-(void) _handleActionSheetSaveComment:(id) arg1 index:(NSUInteger) arg2; +-(void) _handleActionSheetShareComment:(id) arg1; +-(void) _handleActionSheetShareLink; +-(void) _handleActionSheetSharePost; +-(void) _handleActionSheetSortComments; +-(void) _handleActionSheetUnsaveComment:(id) arg1 index:(NSUInteger) arg2; +-(void) _handleActionSheetViewParent:(id) arg1; +-(void) _handleActionSheetViewProfile:(id) arg1; + +//custom elements +-(void) handleUndeleteCommentAction:(id) comment; +@end + + + + + + + + + + + + diff --git a/tweak/Narwhal.xm b/tweak/Narwhal.xm new file mode 100644 index 0000000..dea2c66 --- /dev/null +++ b/tweak/Narwhal.xm @@ -0,0 +1,289 @@ + +#import "Narwhal.h" + +%group Narwhal + +UIAlertController* recreateActionSheet(id controller, id comment, NSInteger commentIndex){ + + UIAlertController* alert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:nil]; + + UIAlertAction* pmAction = [UIAlertAction actionWithTitle:[NSString stringWithFormat:@"private message %@", [comment author]] style:nil handler:^(UIAlertAction* action){[controller _handleActionSheetPrivateMessage:comment];}]; + UIAlertAction* viewProfileAction = [UIAlertAction actionWithTitle:@"view profile" style:nil handler:^(UIAlertAction* action){[controller _handleActionSheetViewProfile:comment];}]; + UIAlertAction* shareAction = [UIAlertAction actionWithTitle:@"share comment" style:nil handler:^(UIAlertAction* action){[controller _handleActionSheetShareComment:comment];}]; + UIAlertAction* copyAction = [UIAlertAction actionWithTitle:@"copy text" style:nil handler:^(UIAlertAction* action){[controller _handleActionSheetCopyCommentText:comment];}]; + UIAlertAction* reportAction = [UIAlertAction actionWithTitle:@"report comment" style:nil handler:^(UIAlertAction* action){[controller _handleActionSheetReportComment:comment];}]; + UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"cancel" style:UIAlertActionStyleCancel handler:nil]; + + [alert addAction:pmAction]; + [alert addAction:viewProfileAction]; + [alert addAction:shareAction]; + [alert addAction:copyAction]; + + if ([comment isSaved]){ + UIAlertAction* unsaveAction = [UIAlertAction actionWithTitle:@"unsave comment" style:nil handler:^(UIAlertAction* action){[controller _handleActionSheetUnsaveComment:comment index:commentIndex];}]; + [alert addAction:unsaveAction]; + + } else { + UIAlertAction* saveAction = [UIAlertAction actionWithTitle:@"save comment" style:nil handler:^(UIAlertAction* action){[controller _handleActionSheetSaveComment:comment index:commentIndex];}]; + [alert addAction:saveAction]; + } + + if ([[[comment parentID] componentsSeparatedByString:@"_"][0] isEqualToString:@"t1"]){ + UIAlertAction* viewParentAction = [UIAlertAction actionWithTitle:@"view parent" style: nil handler:^(UIAlertAction* action){[controller _handleActionSheetViewParent:comment];}]; + [alert addAction:viewParentAction]; + } + + if ([[comment author] isEqualToString:[[%c(NRTAuthManager) sharedManager] currentUsername]]) { + UIAlertAction* editAction = [UIAlertAction actionWithTitle:@"edit comment" style:nil handler:^(UIAlertAction* action){[controller _handleActionSheetEditComment:comment];}]; + UIAlertAction* deleteAction = [UIAlertAction actionWithTitle:@"delete comment" style:nil handler:^(UIAlertAction* action){[controller _handleActionSheetDeleteComment:comment];}]; + + [alert addAction:editAction]; + [alert addAction:deleteAction]; + } + + [alert addAction:reportAction]; + [alert addAction:cancelAction]; + + UIAlertAction* undeleteAction = [UIAlertAction actionWithTitle:@"tf did that say?" style:nil handler:^(UIAlertAction* action){[controller handleUndeleteCommentAction:comment];}]; + [alert addAction:undeleteAction]; + + return alert; +} + +void getUndeleteCommentData(id controller, id comment){ + + 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 fullName] componentsSeparatedByString:@"_"][1]]]]; + [request setHTTPMethod:@"GET"]; + + [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]"; + } + if (!body){ + body = @"[wtf]"; + } + if (!author){ + author = @"[wtf]"; + } + } else { + body = @"[pushshift has not archived this yet]"; + } + } else if (error != nil || data == nil){ + body = @"[an error occured]"; + } + + [controller performSelectorOnMainThread:@selector(completeUndeleteComment:) withObject:@{@"body":body, @"author":author, @"comment":comment} waitUntilDone:NO]; + }]; + +} + + +%hook NRTLinkViewController + +%new +-(void) completeUndeleteComment:(id) data{ + + id comment = data[@"comment"]; + + if (comment){ + + MSHookIvar(comment, "_author") = data[@"author"]; + MSHookIvar(comment, "_body") = data[@"body"]; + + [[self commentsManager] updateComment:comment fromEdited:comment]; + } +} + +%new +-(void) completeUndeletePost:(id) data{ + + id post = data[@"post"]; + + MSHookIvar(post, "_author") = data[@"author"]; + + NSAttributedString* postBodyAttributedString = [%c(NRTMarkdownManager) attributedStringFromMarkdown:data[@"body"] type:0]; + [self setLinkText:postBodyAttributedString]; + + [[self tableView] reloadData]; +} + +%new +-(void) handleUndeleteCommentAction:(id) comment{ + getUndeleteCommentData(self, comment); +} + +%new +-(void) handleUndeletePostAction{ + + id post = [self link]; + + 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 fullName] componentsSeparatedByString:@"_"][1]]]]; + [request setHTTPMethod:@"GET"]; + + [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 = @"[an error occured]"; + } + + [self performSelectorOnMainThread:@selector(completeUndeletePost:) withObject:@{@"body":body, @"author":author, @"post":post} waitUntilDone:NO]; + + }]; + + +} + +-(void) swipeCell:(id) arg1 didEndDragWithState:(NSUInteger) arg2{ + + if (arg2 == 2){ + + if ([arg1 isKindOfClass:[%c(NRTCommentTableViewCell) class]]) { + + id comment = [arg1 comment]; + NSInteger commentIndex = [[[self commentsManager] comments] indexOfObject:comment]; + + UIAlertController* alert = recreateActionSheet(self, comment, commentIndex); + + [self presentViewController:alert animated:YES completion:nil]; + + } else { + %orig; + } + } else { + %orig; + } +} + + +-(void) _dotsButtonTouched:(id) arg1{ + + id post = [self link]; + BOOL shouldHaveUndeleteAction = NO; + + UIAlertController* alert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:nil]; + + UIAlertAction* undeletePostAction; + + UIAlertAction* sharePostAction = [UIAlertAction actionWithTitle:@"share reddit post" style:nil handler:^(UIAlertAction* action){[self _handleActionSheetSharePost];}]; + UIAlertAction* sortCommentsAction = [UIAlertAction actionWithTitle:@"sort comments" style:nil handler:^(UIAlertAction* action){[self _handleActionSheetSortComments];}]; + UIAlertAction* refreshCommentsAction = [UIAlertAction actionWithTitle:@"refresh comments" style:nil handler:^(UIAlertAction* action){[self _handleActionSheetRefreshComments];}]; + UIAlertAction* reportPostAction = [UIAlertAction actionWithTitle:@"report post" style:nil handler:^(UIAlertAction* action){[self _handleActionSheetReportPost];}]; + UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"cancel" style:UIAlertActionStyleCancel handler:nil]; + + [alert addAction:sharePostAction]; + [alert addAction:sortCommentsAction]; + [alert addAction:refreshCommentsAction]; + + if ([self linkTextOffscreenCell]){ + UIAlertAction* refreshPostAction = [UIAlertAction actionWithTitle:@"refresh post" style:nil handler:^(UIAlertAction* action){[self _handleActionSheetRefreshPost];}]; + [alert addAction:refreshPostAction]; + + undeletePostAction = [UIAlertAction actionWithTitle:@"tf did that say?" style:nil handler:^(UIAlertAction* action){[self handleUndeletePostAction];}]; + shouldHaveUndeleteAction = YES; + } + + if ([[post author] isEqualToString:[[%c(NRTAuthManager) sharedManager] currentUsername]]){ + UIAlertAction* editPostAction = [UIAlertAction actionWithTitle:@"edit post" style:nil handler:^(UIAlertAction* action){[self _handleActionSheetEditPost];}]; + UIAlertAction* deletePostAction = [UIAlertAction actionWithTitle:@"delete post" style:nil handler:^(UIAlertAction* action){[self _handleActionSheetDeletePost];}]; + + [alert addAction:editPostAction]; + [alert addAction:deletePostAction]; + } + + [alert addAction:reportPostAction]; + [alert addAction:cancelAction]; + + if (shouldHaveUndeleteAction){ + [alert addAction:undeletePostAction]; + } + + [self presentViewController:alert animated:YES completion:nil]; + +} + +%end + + +%hook NRTMediaTableViewDataSource + +%new +-(void) completeUndeleteComment:(id) data{ + + id comment = data[@"comment"]; + + if (comment){ + + MSHookIvar(comment, "_author") = data[@"author"]; + MSHookIvar(comment, "_body") = data[@"body"]; + + [[self commentsManager] updateComment:comment fromEdited:comment]; + } +} + +%new +-(void) handleUndeleteCommentAction:(id) comment{ + getUndeleteCommentData(self, comment); +} + +-(void) swipeCell:(id) arg1 didEndDragWithState:(NSUInteger) arg2{ + + if (arg2 == 2){ + + if ([arg1 isKindOfClass:[%c(NRTCommentTableViewCell) class]]) { + + id comment = [arg1 comment]; + NSInteger commentIndex = [[[self commentsManager] comments] indexOfObject:comment]; + + UIAlertController* alert = recreateActionSheet(self, comment, commentIndex); + + [[self parentController] presentViewController:alert animated:YES completion:nil]; + + } else { + %orig; + } + } else { + %orig; + } +} + +%end + +%end + +%ctor { + NSString* processName = [[NSProcessInfo processInfo] processName]; + + if ([processName isEqualToString:@"narwhal"]){ + %init(Narwhal); + } +} + diff --git a/tweak/Reddit.xm b/tweak/Reddit.xm index 24e3ba8..211eacc 100644 --- a/tweak/Reddit.xm +++ b/tweak/Reddit.xm @@ -72,16 +72,16 @@ NSString *body = @"[body]"; if (data != nil && error == nil){ - id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; - - author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"]; - body = [[jsonData objectForKey:@"data"][0] objectForKey:@"body"]; - - if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){ - body = @"[comment was unable to be archived]"; + 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 = @"[an error occured]"; } @@ -188,21 +188,21 @@ NSString *author = @"[author]"; NSString *body = @"[body]"; - + if (data != nil && error == nil){ - id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; - - author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"]; - body = [[jsonData objectForKey:@"data"][0] objectForKey:@"selftext"]; - - if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){ - body = @"[comment was unable to be archived]"; + 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 = @"[an error occured]"; - } + } id themeManager = [[%c(ThemeManager) alloc] initWithTraitCollection:nil appSettings:[%c(AppSettings) sharedSettings]]; id isNightMode = [[[%c(AccountManager) sharedManager] defaults] objectForKey:@"kUseNightKey"];