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.

454 lines
18KB

  1. #import "Slide.h"
  2. #import "assets/TFHelper.h"
  3. #import "assets/MMMarkdown/MMMarkdown.h"
  4. static BOOL isSlideEnabled;
  5. static BOOL isTFDeletedOnly;
  6. static CGFloat pushshiftRequestTimeoutValue;
  7. %group Slide
  8. @implementation FontGenerator
  9. +(UIFont *) fontOfSize:(CGFloat) size submission:(BOOL) isSubmission willOffset:(BOOL) willOffset{
  10. NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  11. NSString *fontName;
  12. CGFloat fontSize = size;
  13. if (willOffset){
  14. if (isSubmission){
  15. fontSize += ([userDefaults objectForKey:@"POST_FONT_SIZE"] == nil) ? 0 : [userDefaults integerForKey:@"POST_FONT_SIZE"];
  16. } else {
  17. fontSize += ([userDefaults objectForKey:@"COMMENT_FONT_SIZE"] == nil) ? -2 : [userDefaults integerForKey:@"COMMENT_FONT_SIZE"];
  18. }
  19. }
  20. if ([userDefaults stringForKey:(isSubmission ? @"postfont" : @"commentfont")] == nil){
  21. fontName = isSubmission ? @"AvenirNext-DemiBold" : @"AvenirNext-Medium";
  22. } else {
  23. fontName = [userDefaults stringForKey:(isSubmission ? @"postfont" : @"commentfont")];
  24. }
  25. UIFont *font = [UIFont fontWithName:fontName size:fontSize];
  26. if (!font){
  27. font = [UIFont systemFontOfSize:fontSize];
  28. }
  29. return font;
  30. }
  31. +(UIFont *) boldFontOfSize:(CGFloat) size submission:(BOOL) isSubmission willOffset:(BOOL) willOffset {
  32. UIFont *font = [self fontOfSize:size submission:isSubmission willOffset:willOffset];
  33. if ([font.fontName isEqualToString:[UIFont systemFontOfSize:10].fontName]){
  34. return [UIFont boldSystemFontOfSize:font.pointSize];
  35. } else {
  36. UIFontDescriptor *desc = [font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
  37. if (desc == nil){
  38. return font;
  39. } else {
  40. return [UIFont fontWithDescriptor:desc size: 0];
  41. }
  42. }
  43. }
  44. +(UIFont *) italicFontOfSize:(CGFloat) size submission:(BOOL) isSubmission willOffset:(BOOL) willOffset {
  45. UIFont *font = [self fontOfSize:size submission:isSubmission willOffset:willOffset];
  46. if ([font.fontName isEqualToString:[UIFont systemFontOfSize:10].fontName]){
  47. return [UIFont italicSystemFontOfSize:font.pointSize];
  48. } else {
  49. UIFontDescriptor *desc = [font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic];
  50. if (desc == nil){
  51. return font;
  52. } else {
  53. return [UIFont fontWithDescriptor:desc size: 0];
  54. }
  55. }
  56. }
  57. @end
  58. @implementation ColorUtil
  59. +(UIColor *) accentColorForSub:(NSString *) subreddit{
  60. NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  61. NSData *colorData = [userDefaults dataForKey:[NSString stringWithFormat:@"accent+%@", subreddit]];
  62. UIColor *color = [NSKeyedUnarchiver unarchiveObjectWithData:colorData];
  63. if (color) {
  64. return color;
  65. } else {
  66. UIColor *baseAccentColor = [NSKeyedUnarchiver unarchiveObjectWithData:[userDefaults dataForKey:@"accentcolor"]];
  67. if (baseAccentColor){
  68. return baseAccentColor;
  69. } else {
  70. return [UIColor colorWithRed:0.161 green:0.475 blue:1.0 alpha:1.0];
  71. }
  72. }
  73. }
  74. +(UIColor *) fontColorForTheme:(NSString *)theme{
  75. UIColor *fontColor;
  76. NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  77. //if only switch blocks worked with strings...
  78. if ([theme isEqualToString:@"light"]) {
  79. fontColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.87];
  80. } else if ([theme isEqualToString:@"dark"]) {
  81. fontColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.87];
  82. } else if ([theme isEqualToString:@"black"]) {
  83. fontColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.87];
  84. } else if ([theme isEqualToString:@"blue"]) {
  85. fontColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.87];
  86. } else if ([theme isEqualToString:@"sepia"]) {
  87. fontColor = [UIColor colorWithRed:0.243 green:.239 blue:.212 alpha:0.87];
  88. } else if ([theme isEqualToString:@"red"]) {
  89. fontColor = [UIColor colorWithRed:1.0 green:0.969 blue:0.929 alpha:0.87];
  90. } else if ([theme isEqualToString:@"deep"]) {
  91. fontColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.87];
  92. } else if ([theme isEqualToString:@"mint"]) {
  93. fontColor = [UIColor colorWithRed:0.035 green:0.212 blue:0.059 alpha:0.87];
  94. } else if ([theme isEqualToString:@"cream"]) {
  95. fontColor = [UIColor colorWithRed:0.267 green:0.255 blue:0.224 alpha:0.87];
  96. } else if ([theme isEqualToString:@"acontrast"]) {
  97. fontColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.87];
  98. } else if ([theme isEqualToString:@"pink"]) {
  99. fontColor = [UIColor colorWithRed:0.149 green:0.157 blue:0.267 alpha:0.87];
  100. } else if ([theme isEqualToString:@"solarize"]) {
  101. fontColor = [UIColor colorWithRed:0.514 green:0.580 blue:0.588 alpha:0.87];
  102. } else if (!theme) {
  103. fontColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.87];
  104. } else {
  105. NSString *customThemeString = [[userDefaults stringForKey:[NSString stringWithFormat:@"Theme+%@", theme]] stringByRemovingPercentEncoding];
  106. if (customThemeString) {
  107. NSString *customFontColorHex = [customThemeString componentsSeparatedByString:@"#"][4];
  108. fontColor = [UIColor colorWithHex:customFontColorHex];
  109. } else {
  110. fontColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.87];
  111. }
  112. }
  113. return fontColor;
  114. }
  115. +(UIColor *) backgroundColorForTheme:(NSString *) theme{
  116. UIColor *backgroundColor;
  117. NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  118. if ([theme isEqualToString:@"light"]) {
  119. backgroundColor = [UIColor colorWithRed:0.352 green:0.352 blue:0.352 alpha:1.0];
  120. } else if ([theme isEqualToString:@"dark"]) {
  121. backgroundColor = [UIColor colorWithRed:0.051 green:0.051 blue:0.051 alpha:1.0];
  122. } else if ([theme isEqualToString:@"black"]) {
  123. backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1.0];
  124. } else if ([theme isEqualToString:@"blue"]) {
  125. backgroundColor = [UIColor colorWithRed:0.071 green:0.094 blue:0.106 alpha:1.0];
  126. } else if ([theme isEqualToString:@"sepia"]) {
  127. backgroundColor = [UIColor colorWithRed:0.310 green:0.302 blue:0.267 alpha:1.0];
  128. } else if ([theme isEqualToString:@"red"]) {
  129. backgroundColor = [UIColor colorWithRed:0.075 green:0.055 blue:0.051 alpha:1.0];
  130. } else if ([theme isEqualToString:@"deep"]) {
  131. backgroundColor = [UIColor colorWithRed:0.035 green:0.035 blue:0.043 alpha:1.0];
  132. } else if ([theme isEqualToString:@"mint"]) {
  133. backgroundColor = [UIColor colorWithRed:0.364 green:0.376 blue:0.357 alpha:1.0];
  134. } else if ([theme isEqualToString:@"cream"]) {
  135. backgroundColor = [UIColor colorWithRed:0.322 green:0.314 blue:0.286 alpha:1.0];
  136. } else if ([theme isEqualToString:@"acontrast"]) {
  137. backgroundColor = [UIColor colorWithRed:0.027 green:0.027 blue:0.24 alpha:1.0];
  138. } else if ([theme isEqualToString:@"pink"]) {
  139. backgroundColor = [UIColor colorWithRed:1.0 green:0.376 blue:0.357 alpha:1.0];
  140. } else if ([theme isEqualToString:@"solarize"]) {
  141. backgroundColor = [UIColor colorWithRed:0.040 green:0.067 blue:0.082 alpha:1.0];
  142. } else if (!theme) {
  143. backgroundColor = [UIColor colorWithRed:0.352 green:0.352 blue:0.352 alpha:1.0];
  144. } else {
  145. NSString *customThemeString = [[userDefaults stringForKey:[NSString stringWithFormat:@"Theme+%@", theme]] stringByRemovingPercentEncoding];
  146. if (customThemeString) {
  147. NSString *customFontColorHex = [customThemeString componentsSeparatedByString:@"#"][3];
  148. backgroundColor = [UIColor colorWithHex:customFontColorHex];
  149. } else {
  150. backgroundColor = [UIColor colorWithRed:0.352 green:0.352 blue:0.352 alpha:1.0];
  151. }
  152. }
  153. return backgroundColor;
  154. }
  155. @end
  156. static UIButton * createUndeleteButton(){
  157. UIButton *undeleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
  158. UIImage *undeleteImage = [UIImage imageWithContentsOfFile:@"/var/mobile/Library/Application Support/TFDidThatSay/eye160white.png"];
  159. UIGraphicsBeginImageContextWithOptions(CGSizeMake(20, 20), NO, 0);
  160. [undeleteImage drawInRect:CGRectMake(0, 0, 20, 20)];
  161. undeleteImage = UIGraphicsGetImageFromCurrentImageContext();
  162. UIGraphicsEndImageContext();
  163. UIGraphicsBeginImageContextWithOptions(CGSizeMake(35, 35), NO, 0);
  164. CGContextRef context = UIGraphicsGetCurrentContext();
  165. UIGraphicsPushContext(context);
  166. [undeleteImage drawAtPoint:CGPointMake(7.5, 7.5)];
  167. UIGraphicsPopContext();
  168. undeleteImage = UIGraphicsGetImageFromCurrentImageContext();
  169. UIGraphicsEndImageContext();
  170. [undeleteButton setImage:undeleteImage forState:UIControlStateNormal];
  171. undeleteButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
  172. return undeleteButton;
  173. }
  174. //because it wont compile without this
  175. %hook RComment
  176. %end
  177. %hook UIColor
  178. %new
  179. +(UIColor *) colorWithHex:(NSString *) arg1 {
  180. if (!arg1){
  181. NSString *firstChar = [arg1 substringToIndex:1];
  182. if ([firstChar isEqualToString:@"#"]){
  183. arg1 = [arg1 substringWithRange:NSMakeRange(1, [arg1 length]-1)];
  184. }
  185. unsigned rgbValue = 0;
  186. NSScanner *scanner = [NSScanner scannerWithString:arg1];
  187. [scanner scanHexInt:&rgbValue];
  188. return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:0.87];
  189. } else {
  190. return nil;
  191. }
  192. }
  193. %new
  194. -(NSString *) hexString {
  195. const CGFloat *components = CGColorGetComponents(self.CGColor);
  196. CGFloat r = components[0];
  197. CGFloat g = components[1];
  198. CGFloat b = components[2];
  199. return [NSString stringWithFormat:@"#%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255)];
  200. }
  201. %end
  202. %hook CommentDepthCell
  203. -(void) doShortClick{
  204. %orig;
  205. [self addUndeleteButtonToMenu];
  206. }
  207. -(void) doLongClick {
  208. %orig;
  209. [self addUndeleteButtonToMenu];
  210. }
  211. %new
  212. -(void) addUndeleteButtonToMenu{
  213. NSString *body = [MSHookIvar<id>(self, "comment") body];
  214. if ((isTFDeletedOnly && ([body isEqualToString:@"[deleted]"] || [body isEqualToString:@"[removed]"])) || !isTFDeletedOnly){
  215. id controller = MSHookIvar<id>(self, "parent");
  216. if (MSHookIvar<id>(controller, "menuCell")){
  217. UIStackView *menu = MSHookIvar<UIStackView *>(self, "menu");
  218. if (![[[[menu arrangedSubviews] lastObject] actionsForTarget:self forControlEvent:UIControlEventTouchUpInside] containsObject:@"handleUndeleteComment:"]){
  219. UIButton *undeleteButton = createUndeleteButton();
  220. [undeleteButton addTarget:self action:@selector(handleUndeleteComment:) forControlEvents:UIControlEventTouchUpInside];
  221. [menu addArrangedSubview:undeleteButton];
  222. }
  223. }
  224. }
  225. }
  226. %new
  227. -(void) handleUndeleteComment:(id) sender{
  228. [sender setEnabled:NO];
  229. id comment = MSHookIvar<id>(self, "comment");
  230. [%c(TFHelper) getUndeleteDataWithID:[[comment id] componentsSeparatedByString:@"_"][1] isComment:YES timeout:pushshiftRequestTimeoutValue extraData:@{@"sender" : sender} completionTarget:self completionSelector:@selector(completeUndeleteCommentAction:)];
  231. }
  232. %new
  233. -(void) completeUndeleteCommentAction:(NSDictionary *) data{
  234. NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  235. id textStackDisplayView = MSHookIvar<id>(self, "commentBody");
  236. id comment = MSHookIvar<id>(self, "comment");
  237. NSString *author = data[@"author"];
  238. NSString *body = data[@"body"];
  239. //Attributed string generation rewrote from Slide_for_Reddit.TextDisplayStackView.createAttributedChunk(...)
  240. UIFont *font = [%c(FontGenerator) fontOfSize:MSHookIvar<CGFloat>(textStackDisplayView, "fontSize") submission:NO willOffset:YES];
  241. NSString *themeName = [userDefaults stringForKey:@"theme"];
  242. UIColor *fontColor = [%c(ColorUtil) fontColorForTheme:themeName];
  243. UIColor *accentColor = [%c(ColorUtil) accentColorForSub:[comment subreddit]];
  244. NSString *html = [%c(MMMarkdown) HTMLStringWithMarkdown:body extensions:MMMarkdownExtensionsGitHubFlavored error:nil];
  245. html = [[html stringByReplacingOccurrencesOfString:@"<sup>" withString:@"<font size=\"1\">"] stringByReplacingOccurrencesOfString:@"</sup>" withString:@"</font>"];
  246. html = [[html stringByReplacingOccurrencesOfString:@"<del>" withString:@"<font color=\"green\">"] stringByReplacingOccurrencesOfString:@"</del>" withString:@"</font>"];
  247. html = [[html stringByReplacingOccurrencesOfString:@"<code>" withString:@"<font color=\"blue\">"] stringByReplacingOccurrencesOfString:@"</code>" withString:@"</font>"];
  248. html = [html stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
  249. DTHTMLAttributedStringBuilder *dthtmlBuilder = [[%c(DTHTMLAttributedStringBuilder) alloc] initWithHTML:[html dataUsingEncoding:NSUTF8StringEncoding] options:@{@"DTUseiOS6Attributes": @YES, @"DTDefaultTextColor": fontColor, @"DTDefaultFontSize": @([font pointSize])} documentAttributes:nil];
  250. NSMutableAttributedString *htmlAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[dthtmlBuilder generatedAttributedString]];
  251. NSRange htmlStringRange = NSMakeRange(0, [htmlAttributedString length]);
  252. [[htmlAttributedString mutableString] replaceOccurrencesOfString:@"\t•\t" withString:@" • " options:0 range: htmlStringRange];
  253. [[htmlAttributedString mutableString] replaceOccurrencesOfString:@"\t◦\t" withString:@"  ◦ " options:0 range: htmlStringRange];
  254. [[htmlAttributedString mutableString] replaceOccurrencesOfString:@"\t▪\t" withString:@" ▪ " options:0 range: htmlStringRange];
  255. [htmlAttributedString removeAttribute:@"CTForegroundColorFromContext" range:htmlStringRange];
  256. [htmlAttributedString enumerateAttributesInRange:htmlStringRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop) {
  257. for (NSString *key in attributes){
  258. if ([(UIColor *) attributes[key] isKindOfClass:[UIColor class]]){
  259. UIColor *attrColor = (UIColor *) attributes[key];
  260. if ([[attrColor hexString] isEqualToString:@"#0000FF"]){
  261. UIFont *tempFont = [UIFont fontWithName:@"Courier" size:font.pointSize];
  262. [htmlAttributedString setAttributes:@{NSForegroundColorAttributeName: accentColor, NSBackgroundColorAttributeName: [%c(ColorUtil) backgroundColorForTheme:themeName], NSFontAttributeName: (tempFont ? tempFont : font)} range:range];
  263. } else if ([[attrColor hexString] isEqualToString:@"#008000"]) {
  264. [htmlAttributedString setAttributes:@{NSForegroundColorAttributeName: fontColor, NSFontAttributeName:font} range:range];
  265. }
  266. } else if ([(NSURL *) attributes[key] isKindOfClass:[NSURL class]]){
  267. NSURL *attrUrl = (NSURL *)attributes[key];
  268. if (([userDefaults objectForKey:@"ENLARGE_LINKS"] == nil) ? YES : [userDefaults boolForKey:@"ENLARGE_LINKS"]){
  269. [htmlAttributedString addAttribute:NSFontAttributeName value:[%c(FontGenerator) boldFontOfSize:18 submission:NO willOffset:YES] range:range];
  270. }
  271. [htmlAttributedString addAttribute:NSForegroundColorAttributeName value:accentColor range:range];
  272. [htmlAttributedString addAttribute:NSUnderlineColorAttributeName value:[UIColor clearColor] range:range];
  273. //skipping showLinkContentType b/c not necessary and spoilers b/c MMMarkdown doesn't support them
  274. [htmlAttributedString yy_setTextHighlightRange:range color: accentColor backgroundColor:nil userInfo:@{@"url": attrUrl}];
  275. break;
  276. }
  277. }
  278. }];
  279. [htmlAttributedString beginEditing];
  280. [htmlAttributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, [htmlAttributedString length]) options:0 usingBlock:^(id value, NSRange range, BOOL *stop){
  281. UIFont *attrFont = (UIFont *)value;
  282. BOOL isBold = (attrFont.fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold) != 0;
  283. BOOL isItalic = (attrFont.fontDescriptor.symbolicTraits & UIFontDescriptorTraitItalic) != 0;
  284. UIFont *newFont = font;
  285. if (isBold){
  286. newFont = [%c(FontGenerator) boldFontOfSize:attrFont.pointSize submission:NO willOffset:NO];
  287. } else if (isItalic){
  288. newFont = [%c(FontGenerator) italicFontOfSize:attrFont.pointSize submission:NO willOffset:NO];
  289. }
  290. [htmlAttributedString removeAttribute:NSFontAttributeName range:range];
  291. [htmlAttributedString addAttribute:NSFontAttributeName value:newFont range:range];
  292. }];
  293. [htmlAttributedString endEditing];
  294. NSMutableAttributedString *newCommentText = [MSHookIvar<NSMutableAttributedString *>(self, "cellContent") initWithAttributedString:htmlAttributedString];
  295. NSAttributedString *tempAttributedString = [[NSAttributedString alloc] initWithString:@""];
  296. [newCommentText appendAttributedString:tempAttributedString]; //to keep the compiler happy
  297. [comment setAuthor:author];
  298. [comment setBody:body];
  299. id controller = MSHookIvar<id>(self, "parent");
  300. [self showMenu:nil];
  301. [MSHookIvar<id>(controller, "tableView") reloadData];
  302. [data[@"sender"] setEnabled:YES];
  303. }
  304. %end
  305. %end
  306. static void loadPrefs(){
  307. NSMutableDictionary *prefs = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/User/Library/Preferences/com.lint.undelete.prefs.plist"];
  308. if (prefs){
  309. if ([prefs objectForKey:@"isSlideEnabled"] != nil){
  310. isSlideEnabled = [[prefs objectForKey:@"isSlideEnabled"] boolValue];
  311. } else {
  312. isSlideEnabled = YES;
  313. }
  314. if ([prefs objectForKey:@"isTFDeletedOnly"] != nil){
  315. isTFDeletedOnly = [[prefs objectForKey:@"isTFDeletedOnly"] boolValue];
  316. } else {
  317. isTFDeletedOnly = YES;
  318. }
  319. if ([prefs objectForKey:@"requestTimeoutValue"] != nil){
  320. pushshiftRequestTimeoutValue = [[prefs objectForKey:@"requestTimeoutValue"] doubleValue];
  321. } else {
  322. pushshiftRequestTimeoutValue = 10;
  323. }
  324. } else {
  325. isSlideEnabled = YES;
  326. isTFDeletedOnly = YES;
  327. pushshiftRequestTimeoutValue = 10;
  328. }
  329. }
  330. static void prefsChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
  331. loadPrefs();
  332. }
  333. %ctor {
  334. loadPrefs();
  335. NSString* processName = [[NSProcessInfo processInfo] processName];
  336. if ([processName isEqualToString:@"Slide for Reddit"]){
  337. if (isSlideEnabled){
  338. CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, prefsChanged, CFSTR("com.lint.undelete.prefs.changed"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
  339. %init(Slide, CommentDepthCell = objc_getClass("Slide_for_Reddit.CommentDepthCell"), RComment = objc_getClass("Slide_for_Reddit.RSubmission"));
  340. }
  341. }
  342. }