選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

Apollo.xm 9.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. #import "Apollo.h"
  2. #import "assets/TFHelper.h"
  3. static BOOL isTFDeletedOnly;
  4. static BOOL isApolloEnabled;
  5. static CGFloat pushshiftRequestTimeoutValue;
  6. %group Apollo
  7. NSDictionary* apolloBodyAttributes = nil;
  8. BOOL shouldAddUndeleteCell = NO;
  9. id apolloCommentCell;
  10. id apolloCommentController;
  11. %hook ApolloButtonNode
  12. %end
  13. %hook IconActionTableViewCell
  14. %end
  15. %hook RKComment
  16. -(BOOL) isDeleted{
  17. return NO;
  18. }
  19. -(BOOL) isModeratorRemoved{
  20. return NO;
  21. }
  22. %end
  23. %hook RKLink
  24. %property(strong, nonatomic) NSString *undeleteAuthor;
  25. -(id) author{
  26. if ([self undeleteAuthor]){
  27. return [self undeleteAuthor];
  28. } else {
  29. return %orig;
  30. }
  31. }
  32. %end
  33. %hook MarkdownRenderer
  34. +(id) attributedStringFromHTML:(id)arg1 attributes:(id) arg2 compact:(BOOL) arg3{
  35. apolloBodyAttributes = [arg2 copy];
  36. return %orig;
  37. }
  38. %end
  39. %hook ActionController
  40. -(id) tableView:(id) arg1 cellForRowAtIndexPath:(NSIndexPath *) arg2{
  41. if (shouldAddUndeleteCell){
  42. if ([arg2 row] == [self tableView:arg1 numberOfRowsInSection:0] - 1){
  43. id undeleteCell = [[objc_getClass("Apollo.IconActionTableViewCell") alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"IconActionCell"];
  44. NSArray *visibleCells = [arg1 visibleCells];
  45. id prevCell = visibleCells[[visibleCells count] - 2];
  46. UIImageView *prevCellImageView = MSHookIvar<UIImageView *>(prevCell, "iconImageView");
  47. CGSize prevImageSize = [[prevCellImageView image] size];
  48. UIColor *menuColor = [prevCellImageView tintColor];
  49. UIImage *undeleteImage = [UIImage imageWithContentsOfFile:@"/var/mobile/Library/Application Support/TFDidThatSay/eye160dark.png"];
  50. CGFloat undeleteImageSizeValue = prevImageSize.width > prevImageSize.height ? prevImageSize.width : prevImageSize.height;
  51. UIGraphicsBeginImageContextWithOptions(CGSizeMake(undeleteImageSizeValue, undeleteImageSizeValue), NO, 0);
  52. [undeleteImage drawInRect:CGRectMake(0, 0, undeleteImageSizeValue, undeleteImageSizeValue)];
  53. undeleteImage = [UIGraphicsGetImageFromCurrentImageContext() imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  54. UIGraphicsEndImageContext();
  55. UILabel *undeleteLabel = MSHookIvar<UILabel *>(undeleteCell, "actionTitleLabel");
  56. UIImageView *undeleteImageView = MSHookIvar<UIImageView *>(undeleteCell, "iconImageView");
  57. undeleteLabel.text = @"TF Did That Say?";
  58. undeleteLabel.textColor = menuColor;
  59. undeleteImageView.image = undeleteImage;
  60. undeleteImageView.tintColor = menuColor;
  61. return undeleteCell;
  62. }
  63. }
  64. return %orig;
  65. }
  66. -(void) tableView:(id) arg1 didSelectRowAtIndexPath:(NSIndexPath *)arg2{
  67. if (shouldAddUndeleteCell){
  68. if ([arg2 row] == [self tableView:arg1 numberOfRowsInSection:0] - 1){
  69. if (apolloCommentCell){
  70. [apolloCommentCell undeleteCellWasSelected];
  71. } else {
  72. [apolloCommentController undeleteCellWasSelected];
  73. }
  74. }
  75. }
  76. %orig;
  77. }
  78. -(NSInteger) tableView:(id) arg1 numberOfRowsInSection:(NSInteger) arg2{
  79. if (shouldAddUndeleteCell){
  80. return %orig + 1;
  81. } else {
  82. return %orig;
  83. }
  84. }
  85. -(id) animationControllerForDismissedController:(id) arg1{
  86. shouldAddUndeleteCell = NO;
  87. return %orig;
  88. }
  89. %end
  90. %hook CommentCellNode
  91. -(void) moreOptionsTappedWithSender:(id) arg1{
  92. NSString *commentBody = [MSHookIvar<RKComment *>(self, "comment") body];
  93. if ([%c(TFHelper) shouldShowUndeleteButtonWithInfo:commentBody isDeletedOnly:isTFDeletedOnly]){
  94. shouldAddUndeleteCell = YES;
  95. apolloCommentCell = self;
  96. apolloCommentController = nil;
  97. }
  98. %orig;
  99. }
  100. -(void) longPressedWithGestureRecognizer:(id) arg1{
  101. NSString *commentBody = [MSHookIvar<RKComment *>(self, "comment") body];
  102. if ([%c(TFHelper) shouldShowUndeleteButtonWithInfo:commentBody isDeletedOnly:isTFDeletedOnly]){
  103. shouldAddUndeleteCell = YES;
  104. apolloCommentCell = self;
  105. apolloCommentController = nil;
  106. }
  107. %orig;
  108. }
  109. %new
  110. -(void) undeleteCellWasSelected{
  111. RKComment *comment = MSHookIvar<RKComment *>(self, "comment");
  112. [%c(TFHelper) getUndeleteDataWithID:[[comment fullName] componentsSeparatedByString:@"_"][1] isComment:YES timeout:pushshiftRequestTimeoutValue extraData:nil completionTarget:self completionSelector:@selector(completeUndeleteCommentAction:)];
  113. }
  114. %new
  115. -(void) completeUndeleteCommentAction:(NSDictionary *) data{
  116. RKComment *comment = MSHookIvar<RKComment *>(self, "comment");
  117. id bodyNode = MSHookIvar<id>(self, "bodyNode");
  118. id authorNode = MSHookIvar<id>(self, "authorNode");
  119. NSString *author = data[@"author"];
  120. NSString *body = data[@"body"];
  121. [comment setAuthor:author];
  122. [comment setBody:body];
  123. NSAttributedString *prevAuthorAttributedString = [authorNode attributedTitleForState:UIControlStateNormal];
  124. NSDictionary *authorStringAttributes = [prevAuthorAttributedString attributesAtIndex:0 longestEffectiveRange:nil inRange:NSMakeRange(0, [prevAuthorAttributedString length])];
  125. NSAttributedString *newAuthorAttributedString = [[NSAttributedString alloc] initWithString:author attributes:authorStringAttributes];
  126. [authorNode setAttributedTitle:newAuthorAttributedString forState:UIControlStateNormal];
  127. [bodyNode setAttributedString:[%c(MarkdownRenderer) attributedStringFromMarkdown:body withAttributes:apolloBodyAttributes]];
  128. }
  129. %end
  130. %hook CommentsViewController
  131. %property(strong, nonatomic) id headerCellNode;
  132. -(void) moreOptionsBarButtonItemTappedWithSender:(id) arg1{
  133. RKLink *post = MSHookIvar<RKLink *>(self, "link");
  134. NSString *postBody = [post selfText];
  135. if ([post isSelfPost]){
  136. if ([%c(TFHelper) shouldShowUndeleteButtonWithInfo:postBody isDeletedOnly:isTFDeletedOnly]){
  137. shouldAddUndeleteCell = YES;
  138. apolloCommentCell = nil;
  139. apolloCommentController = self;
  140. }
  141. }
  142. %orig;
  143. }
  144. %new
  145. -(void) undeleteCellWasSelected{
  146. RKLink *post = MSHookIvar<RKLink *>(self, "link");
  147. [%c(TFHelper) getUndeleteDataWithID:[[post fullName] componentsSeparatedByString:@"_"][1] isComment:NO timeout:pushshiftRequestTimeoutValue extraData:nil completionTarget:self completionSelector:@selector(completeUndeletePostAction:)];
  148. }
  149. %new
  150. -(void) completeUndeletePostAction:(NSDictionary *) data{
  151. RKLink *post = MSHookIvar<RKLink *>(self, "link");
  152. id headerCellNode = [self headerCellNode];
  153. id bodyNode = MSHookIvar<id>(headerCellNode, "bodyNode");
  154. id postInfoNode = MSHookIvar<id>(headerCellNode, "postInfoNode");
  155. id authorNode = MSHookIvar<id>(postInfoNode, "authorButtonNode");
  156. NSString *author = data[@"author"];
  157. NSString *authorTextString = [NSString stringWithFormat:@"by %@", author];
  158. NSString *body = data[@"body"];
  159. [post setUndeleteAuthor:author];
  160. [post setSelfText:body];
  161. NSAttributedString *prevAuthorAttributedString = [authorNode attributedTitleForState:UIControlStateNormal];
  162. NSDictionary *authorStringAttributes = [prevAuthorAttributedString attributesAtIndex:0 longestEffectiveRange:nil inRange:NSMakeRange(0, [prevAuthorAttributedString length])];
  163. NSAttributedString* newAuthorAttributedString = [[NSAttributedString alloc] initWithString:authorTextString attributes:authorStringAttributes];
  164. [authorNode setAttributedTitle:newAuthorAttributedString forState:UIControlStateNormal];
  165. [bodyNode setAttributedString:[%c(MarkdownRenderer) attributedStringFromMarkdown:body withAttributes:apolloBodyAttributes]];
  166. }
  167. %end
  168. %hook CommentsHeaderCellNode
  169. -(void) didLoad{
  170. %orig;
  171. [[self closestViewController] setHeaderCellNode:self];
  172. }
  173. -(void) _layoutSublayouts{
  174. %orig;
  175. [[self closestViewController] setHeaderCellNode:self];
  176. }
  177. -(void) longPressedWithGestureRecognizer:(id) arg1{
  178. RKLink *post = MSHookIvar<RKLink *>(self, "link");
  179. NSString *postBody = [post selfText];
  180. if ([post isSelfPost]){
  181. if ([%c(TFHelper) shouldShowUndeleteButtonWithInfo:postBody isDeletedOnly:isTFDeletedOnly]){
  182. shouldAddUndeleteCell = YES;
  183. apolloCommentCell = nil;
  184. apolloCommentController = self;
  185. }
  186. }
  187. %orig;
  188. }
  189. %end
  190. %end
  191. static void loadPrefs(){
  192. NSMutableDictionary *prefs = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/User/Library/Preferences/com.lint.undelete.prefs.plist"];
  193. if (prefs){
  194. if ([prefs objectForKey:@"isApolloEnabled"] != nil) {
  195. isApolloEnabled = [[prefs objectForKey:@"isApolloEnabled"] boolValue];
  196. } else {
  197. isApolloEnabled = YES;
  198. }
  199. if ([prefs objectForKey:@"isTFDeletedOnly"] != nil) {
  200. isTFDeletedOnly = [[prefs objectForKey:@"isTFDeletedOnly"] boolValue];
  201. } else {
  202. isTFDeletedOnly = YES;
  203. }
  204. if ([prefs objectForKey:@"requestTimeoutValue"] != nil){
  205. pushshiftRequestTimeoutValue = [[prefs objectForKey:@"requestTimeoutValue"] doubleValue];
  206. } else {
  207. pushshiftRequestTimeoutValue = 10;
  208. }
  209. } else {
  210. isApolloEnabled = YES;
  211. isTFDeletedOnly = YES;
  212. pushshiftRequestTimeoutValue = 10;
  213. }
  214. }
  215. static void prefsChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
  216. loadPrefs();
  217. }
  218. %ctor {
  219. loadPrefs();
  220. NSString* processName = [[NSProcessInfo processInfo] processName];
  221. if ([processName isEqualToString:@"Apollo"]){
  222. if (isApolloEnabled){
  223. CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, prefsChanged, CFSTR("com.lint.undelete.prefs.changed"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
  224. %init(Apollo, CommentsHeaderCellNode = objc_getClass("Apollo.CommentsHeaderCellNode"), CommentCellNode = objc_getClass("Apollo.CommentCellNode"), ApolloButtonNode = objc_getClass("Apollo.ApolloButtonNode"), ActionController = objc_getClass("Apollo.ActionController"), IconActionTableViewCell = objc_getClass("Apollo.IconActionTableViewCell"), CommentsViewController = objc_getClass("Apollo.CommentsViewController"));
  225. }
  226. }
  227. }