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.

278 lines
9.3KB

  1. #import "Beam.h"
  2. static BOOL isBeamEnabled;
  3. static BOOL isTFDeletedOnly;
  4. static CGFloat pushshiftRequestTimeoutValue;
  5. %group Beam
  6. %hook CommentCell
  7. %property(strong, nonatomic) UIButton *undeleteButton;
  8. -(void) layoutSubviews{
  9. %orig;
  10. UIButton *undeleteButton = [self undeleteButton];
  11. if (undeleteButton){
  12. if ([self isCollapsed]){
  13. [undeleteButton setHidden:YES];
  14. } else {
  15. [undeleteButton setHidden:NO];
  16. }
  17. } else {
  18. NSString *commentBody = [[self comment] content];
  19. HBLogDebug(@"body: %@", commentBody);
  20. if ((isTFDeletedOnly && ([commentBody isEqualToString:@"[deleted]"] || [commentBody isEqualToString:@"[removed]"])) || !isTFDeletedOnly){
  21. CGFloat authorTextHeight = [[self authorButton] frame].size.height;
  22. undeleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
  23. [undeleteButton addTarget:self action:@selector(handleUndeleteCommentAction:) forControlEvents:UIControlEventTouchUpInside];
  24. [undeleteButton setImage:[UIImage imageWithContentsOfFile:@"/var/mobile/Library/Application Support/TFDidThatSay/eye160dark.png"] forState:UIControlStateNormal];
  25. undeleteButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
  26. undeleteButton.imageEdgeInsets = UIEdgeInsetsMake(5, 5, 5, 5);
  27. undeleteButton.frame = CGRectMake([[UIScreen mainScreen] bounds].size.width - authorTextHeight - 5, 5, authorTextHeight, authorTextHeight);
  28. [[self commentContentView] addSubview:undeleteButton];
  29. [self setUndeleteButton:undeleteButton];
  30. }
  31. }
  32. }
  33. %new
  34. -(void) handleUndeleteCommentAction:(id) sender{
  35. [sender setEnabled:NO];
  36. id comment = [self comment];
  37. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  38. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  39. [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://api.pushshift.io/reddit/search/comment/?ids=%@&fields=author,body", [comment identifier]]]];
  40. [request setHTTPMethod:@"GET"];
  41. [request setTimeoutInterval:pushshiftRequestTimeoutValue];
  42. [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
  43. NSString *author = @"[author]";
  44. NSString *body = @"[body]";
  45. if (data != nil && error == nil){
  46. id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  47. if ([[jsonData objectForKey:@"data"] count] != 0){
  48. author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"];
  49. body = [[jsonData objectForKey:@"data"][0] objectForKey:@"body"];
  50. if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){
  51. body = @"[pushshift was unable to archive this]";
  52. }
  53. } else {
  54. body = @"[pushshift has not archived this yet]";
  55. }
  56. } else if (error != nil || data == nil){
  57. body = [NSString stringWithFormat:@"[an error occured while attempting to contact pushshift api (%@)]", [error localizedDescription]];
  58. }
  59. [comment setAuthor:author];
  60. [comment setContent:body];
  61. [comment setMarkdownString:nil];
  62. [self setCommentDidChange:YES];
  63. [self performSelectorOnMainThread:@selector(reloadContents) withObject:nil waitUntilDone:YES];
  64. [[MSHookIvar<id>(self, "delegate") tableView] performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
  65. [sender setEnabled:YES];
  66. }];
  67. }
  68. %end
  69. %hook PostSelfTextPartCell
  70. -(void) setSelected:(BOOL) arg1 animated:(BOOL) arg2{
  71. %orig;
  72. id postViewController = [self _viewControllerForAncestor];
  73. if ([postViewController isMemberOfClass:objc_getClass("beam.PostDetailEmbeddedViewController")]){
  74. [postViewController setSelfTextView:self];
  75. [postViewController setPost:[self post]];
  76. }
  77. }
  78. %end
  79. %hook PostMetadataView
  80. -(void) layoutSubviews{
  81. %orig;
  82. id postViewController = MSHookIvar<id>(self, "delegate");
  83. if ([postViewController isMemberOfClass:objc_getClass("beam.PostDetailEmbeddedViewController")]){
  84. [postViewController setMetadataView:self];
  85. }
  86. }
  87. %end
  88. %hook PostToolbarView
  89. %property(strong, nonatomic) UIButton *undeleteButton;
  90. -(void) layoutSubviews{
  91. %orig;
  92. if (![self undeleteButton] && [[[self post] isSelfText] boolValue] && [MSHookIvar<id>(self, "delegate") isMemberOfClass:objc_getClass("beam.PostDetailEmbeddedViewController")]){
  93. NSString *postBody = [[self post] content];
  94. if ((isTFDeletedOnly && ([postBody isEqualToString:@"[deleted]"] || [postBody isEqualToString:@"[removed]"])) || !isTFDeletedOnly){
  95. id moreButton = MSHookIvar<id>(self, "moreButton");
  96. CGFloat buttonHeight = [moreButton frame].size.height;
  97. UIButton *undeleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
  98. [undeleteButton addTarget:MSHookIvar<id>(self, "delegate") action:@selector(handleUndeletePostAction:) forControlEvents:UIControlEventTouchUpInside];
  99. [undeleteButton setImage:[UIImage imageWithContentsOfFile:@"/var/mobile/Library/Application Support/TFDidThatSay/eye160dark.png"] forState:UIControlStateNormal];
  100. undeleteButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
  101. undeleteButton.imageEdgeInsets = UIEdgeInsetsMake(5, 5, 5, 5);
  102. undeleteButton.frame = CGRectMake([moreButton frame].origin.x - buttonHeight, 1, buttonHeight, buttonHeight);
  103. [self addSubview:undeleteButton];
  104. [self setUndeleteButton:undeleteButton];
  105. }
  106. }
  107. }
  108. %end
  109. %hook PostDetailEmbeddedViewController
  110. %property(strong, nonatomic) id selfTextView;
  111. %property(strong, nonatomic) id metadataView;
  112. %property(strong, nonatomic) id post;
  113. %new
  114. -(void) handleUndeletePostAction:(id) sender{
  115. [sender setEnabled:NO];
  116. id post = [self post];
  117. if (post){
  118. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  119. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  120. [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://api.pushshift.io/reddit/search/submission/?ids=%@&fields=author,selftext", [post identifier]]]];
  121. [request setHTTPMethod:@"GET"];
  122. [request setTimeoutInterval:pushshiftRequestTimeoutValue];
  123. [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
  124. NSString *author = @"[author]";
  125. NSString *body = @"[body]";
  126. if (data != nil && error == nil){
  127. id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  128. if ([[jsonData objectForKey:@"data"] count] != 0){
  129. author = [[jsonData objectForKey:@"data"][0] objectForKey:@"author"];
  130. body = [[jsonData objectForKey:@"data"][0] objectForKey:@"selftext"];
  131. if ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"]){
  132. body = @"[pushshift was unable to archive this]";
  133. }
  134. } else {
  135. body = @"[pushshift has not archived this yet]";
  136. }
  137. } else if (error != nil || data == nil){
  138. body = [NSString stringWithFormat:@"[an error occured while attempting to contact pushshift api (%@)]", [error localizedDescription]];
  139. }
  140. [post setAuthor:author];
  141. [post setContent:body];
  142. [post setMarkdownString:nil];
  143. if ([self selfTextView]){
  144. [[self selfTextView] performSelectorOnMainThread:@selector(reloadContents) withObject:nil waitUntilDone:YES];
  145. }
  146. if ([self metadataView]){
  147. [[self metadataView] performSelectorOnMainThread:@selector(setPost:) withObject:post waitUntilDone:YES];
  148. }
  149. [[self tableView] performSelectorOnMainThread:@selector(reloadData) withObject:post waitUntilDone:NO];
  150. [sender setEnabled:YES];
  151. }];
  152. }
  153. }
  154. %end
  155. %end
  156. static void loadPrefs(){
  157. NSMutableDictionary *prefs = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/User/Library/Preferences/com.lint.undelete.prefs.plist"];
  158. if (prefs){
  159. if ([prefs objectForKey:@"isBeamEnabled"] != nil){
  160. isBeamEnabled = [[prefs objectForKey:@"isBeamEnabled"] boolValue];
  161. } else {
  162. isBeamEnabled = YES;
  163. }
  164. if ([prefs objectForKey:@"isTFDeletedOnly"] != nil) {
  165. isTFDeletedOnly = [[prefs objectForKey:@"isTFDeletedOnly"] boolValue];
  166. } else {
  167. isTFDeletedOnly = YES;
  168. }
  169. if ([prefs objectForKey:@"requestTimeoutValue"] != nil){
  170. pushshiftRequestTimeoutValue = [[prefs objectForKey:@"requestTimeoutValue"] doubleValue];
  171. } else {
  172. pushshiftRequestTimeoutValue = 10;
  173. }
  174. } else {
  175. isBeamEnabled = YES;
  176. isTFDeletedOnly = YES;
  177. pushshiftRequestTimeoutValue = 10;
  178. }
  179. }
  180. static void prefsChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
  181. loadPrefs();
  182. }
  183. %ctor{
  184. loadPrefs();
  185. NSString* processName = [[NSProcessInfo processInfo] processName];
  186. if ([processName isEqualToString:@"beam"]){
  187. if (isBeamEnabled){
  188. CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, prefsChanged, CFSTR("com.lint.undelete.prefs.changed"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
  189. %init(Beam, CommentCell = objc_getClass("beam.CommentCell"), PostDetailEmbeddedViewController = objc_getClass("beam.PostDetailEmbeddedViewController"), PostToolbarView = objc_getClass("beam.PostToolbarView"), PostSelfTextPartCell = objc_getClass("beam.PostSelfTextPartCell"), PostMetadataView = objc_getClass("beam.PostMetadataView"));
  190. }
  191. }
  192. }