You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Apollo.xm 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #import <Cephei/HBPreferences.h>
  2. #import "Apollo.h"
  3. HBPreferences *apolloPrefs;
  4. BOOL isApolloDeletedCommentsOnly;
  5. CGFloat apolloRequestTimeoutValue;
  6. %group Apollo
  7. NSDictionary* apolloBodyAttributes = nil;
  8. %hook ApolloApolloButtonNode
  9. %end
  10. %hook RKComment
  11. -(BOOL) isDeleted{
  12. return NO;
  13. }
  14. -(BOOL) isModeratorRemoved{
  15. return NO;
  16. }
  17. %end
  18. %hook MarkdownRenderer
  19. +(id) attributedStringFromHTML:(id)arg1 attributes:(id) arg2 compact:(BOOL) arg3{
  20. apolloBodyAttributes = [arg2 copy];
  21. return %orig;
  22. }
  23. %end
  24. %hook ApolloCommentCellNode
  25. %property(strong,nonatomic) id undeleteButton;
  26. %new
  27. -(void) didTapUndeleteButton:(id) sender{
  28. [sender setEnabled:NO];
  29. id bodyNode = MSHookIvar<id>(self, "bodyNode");
  30. id authorNode = MSHookIvar<id>(self, "authorNode");
  31. id authorTextNode = [authorNode subnodes][0];
  32. id comment = MSHookIvar<id>(self, "comment");
  33. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  34. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  35. [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://api.pushshift.io/reddit/search/comment/?ids=%@&fields=author,body",[[comment fullName] componentsSeparatedByString:@"_"][1]]]];
  36. [request setHTTPMethod:@"GET"];
  37. [request setTimeoutInterval:[apolloPrefs doubleForKey:@"requestTimeoutValue" default:10]];
  38. [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
  39. NSString *author = @"[author]";
  40. NSString *body = @"[body]";
  41. if (data != nil && error == nil){
  42. id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  43. if ([[jsonData objectForKey:@"data"] count] != 0){
  44. author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"];
  45. body = [[jsonData objectForKey:@"data"][0] objectForKey:@"body"];
  46. if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){
  47. body = @"[pushshift was unable to archive this]";
  48. }
  49. } else {
  50. body = @"[pushshift has not archived this yet]";
  51. }
  52. } else if (error != nil || data == nil){
  53. body = [NSString stringWithFormat:@"[an error occured while attempting to contact pushshift api (%@)]", [error localizedDescription]];
  54. }
  55. id prevAuthorAttributedString = [authorTextNode attributedString];
  56. NSDictionary *authorStringAttributes = [prevAuthorAttributedString attributesAtIndex:0 longestEffectiveRange:nil inRange:NSMakeRange(0, [prevAuthorAttributedString length])];
  57. NSAttributedString* newAuthorAttributedString = [[NSAttributedString alloc] initWithString:author attributes:authorStringAttributes];
  58. [authorTextNode setAttributedText:newAuthorAttributedString];
  59. [authorTextNode setAttributedString:newAuthorAttributedString];
  60. [comment setAuthor:author];
  61. [bodyNode setAttributedString:[%c(MarkdownRenderer) attributedStringFromMarkdown:body withAttributes:apolloBodyAttributes]];
  62. [sender setEnabled:YES];
  63. }];
  64. }
  65. -(void) didLoad {
  66. %orig;
  67. id commentBody = [MSHookIvar<id>(self, "comment") body];
  68. BOOL isDeletedOnly = [apolloPrefs boolForKey:@"isApolloDeletedCommentsOnly"];
  69. if ((isDeletedOnly && ([commentBody isEqualToString:@"[deleted]"] || [commentBody isEqualToString:@"[removed]"])) || !isDeletedOnly) {
  70. CGFloat imageSize = 20.0f;
  71. UIButton *undeleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
  72. [undeleteButton addTarget:self action:@selector(didTapUndeleteButton:) forControlEvents:UIControlEventTouchUpInside];
  73. undeleteButton.frame = CGRectMake(0, 0, imageSize, imageSize);
  74. UIImage* undeleteImage = [UIImage imageWithContentsOfFile:@"/var/mobile/Library/Application Support/TFDidThatSay/eye160dark.png"];
  75. [undeleteButton setImage:undeleteImage forState:UIControlStateNormal];
  76. [[self view] addSubview:undeleteButton];
  77. [self setUndeleteButton:undeleteButton];
  78. }
  79. }
  80. -(void) _layoutSublayouts{
  81. %orig;
  82. if ([self undeleteButton]){
  83. CGFloat imageSize = 20.0f;
  84. id moreNode = MSHookIvar<id>(self, "moreOptionsNode");
  85. id ageNode = MSHookIvar<id>(self, "ageNode");
  86. CGRect nodeFrame = [moreNode frame];
  87. CGFloat centerHeight = (nodeFrame.size.height + nodeFrame.origin.y * 2) / 2.0f;
  88. CGFloat nodeSpacing =[ageNode frame].origin.x - nodeFrame.origin.x - nodeFrame.size.width;
  89. [[self undeleteButton] setFrame:CGRectMake(nodeFrame.origin.x - imageSize - nodeSpacing, centerHeight - (imageSize / 2), imageSize, imageSize)];
  90. }
  91. }
  92. %end
  93. %hook ApolloCommentsHeaderCellNode
  94. %property(strong, nonatomic) id undeleteButton;
  95. %new
  96. -(void) didTapUndeleteButton:(id) sender{
  97. [sender setEnabled:NO];
  98. id bodyNode = MSHookIvar<id>(self, "bodyNode");
  99. id postInfoNode = MSHookIvar<id>(self, "postInfoNode");
  100. id authorNode = MSHookIvar<id>(postInfoNode, "authorButtonNode");
  101. id authorTextNode = [authorNode subnodes][0];
  102. id post = MSHookIvar<id>(self, "link");
  103. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  104. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  105. [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://api.pushshift.io/reddit/search/submission/?ids=%@&fields=author,selftext",[[post fullName] componentsSeparatedByString:@"_"][1]]]];
  106. [request setHTTPMethod:@"GET"];
  107. [request setTimeoutInterval:[apolloPrefs doubleForKey:@"requestTimeoutValue" default:10]];
  108. [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
  109. NSString *author = @"[author]";
  110. NSString *body = @"[body]";
  111. if (data != nil && error == nil){
  112. id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  113. if ([[jsonData objectForKey:@"data"] count] != 0){
  114. author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"];
  115. body = [[jsonData objectForKey:@"data"][0] objectForKey:@"selftext"];
  116. if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){
  117. body = @"[pushshift was unable to archive this]";
  118. }
  119. } else {
  120. body = @"[pushshift has not archived this yet]";
  121. }
  122. } else if (error != nil || data == nil){
  123. body = [NSString stringWithFormat:@"[an error occured while attempting to contact pushshift api (%@)]", [error localizedDescription]];
  124. }
  125. //MSHookIvar<NSString*>(post, "_author") = author; //Crashes when clicking on author name. You will have to search the author name to go find the profile.
  126. author = [NSString stringWithFormat:@"by %@", author];
  127. id prevAuthorAttributedString = [authorTextNode attributedString];
  128. NSDictionary *authorStringAttributes = [prevAuthorAttributedString attributesAtIndex:0 longestEffectiveRange:nil inRange:NSMakeRange(0, [prevAuthorAttributedString length])];
  129. NSAttributedString* newAuthorAttributedString = [[NSAttributedString alloc] initWithString:author attributes:authorStringAttributes];
  130. [authorTextNode setAttributedText:newAuthorAttributedString];
  131. [authorTextNode setAttributedString:newAuthorAttributedString];
  132. [bodyNode setAttributedString:[%c(MarkdownRenderer) attributedStringFromMarkdown:body withAttributes:apolloBodyAttributes]];
  133. [sender setEnabled:YES];
  134. }];
  135. }
  136. -(void) didLoad{
  137. %orig;
  138. if ([MSHookIvar<id>(self, "link") isSelfPost]){
  139. CGFloat imageSize = 20.0f;
  140. UIButton *undeleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
  141. [undeleteButton addTarget:self action:@selector(didTapUndeleteButton:) forControlEvents:UIControlEventTouchUpInside];
  142. UIImage* undeleteImage = [UIImage imageWithContentsOfFile:@"/var/mobile/Library/Application Support/TFDidThatSay/eye160dark.png"];
  143. [undeleteButton setImage:undeleteImage forState:UIControlStateNormal];
  144. undeleteButton.frame = CGRectMake(0, 0, imageSize, imageSize);
  145. [[self view] addSubview:undeleteButton];
  146. [self setUndeleteButton:undeleteButton];
  147. }
  148. }
  149. -(void) _layoutSublayouts{
  150. %orig;
  151. if ([self undeleteButton]){
  152. CGFloat imageSize = 20.0f;
  153. id postInfoNode = MSHookIvar<id>(self, "postInfoNode");
  154. id ageNode = MSHookIvar<id>(postInfoNode, "ageButtonNode");
  155. CGFloat centerHeight = [postInfoNode frame].origin.y + ([ageNode frame].size.height + [ageNode frame].origin.y * 2) / 2.0f;
  156. CGFloat buttonXPos = [postInfoNode frame].origin.x + [postInfoNode frame].size.width - imageSize;
  157. [[self undeleteButton] setFrame:CGRectMake(buttonXPos, centerHeight - (imageSize / 2), imageSize, imageSize)];
  158. }
  159. }
  160. %end
  161. %end
  162. %ctor {
  163. apolloPrefs = [[HBPreferences alloc] initWithIdentifier:@"com.lint.undelete.prefs"];
  164. [apolloPrefs registerBool:&isApolloDeletedCommentsOnly default:YES forKey:@"isApolloDeletedCommentsOnly"];
  165. [apolloPrefs registerDouble:&apolloRequestTimeoutValue default:10 forKey:@"requestTimeoutValue"];
  166. NSString* processName = [[NSProcessInfo processInfo] processName];
  167. if ([processName isEqualToString:@"Apollo"]){
  168. %init(Apollo, ApolloCommentsHeaderCellNode = objc_getClass("Apollo.CommentsHeaderCellNode"), ApolloCommentCellNode = objc_getClass("Apollo.CommentCellNode"), ApolloApolloButtonNode = objc_getClass("Apollo.ApolloButtonNode"));
  169. }
  170. }