├── BigSur ├── plugins │ ├── base │ │ ├── test.html │ │ ├── index.js │ │ ├── package.json │ │ └── README │ ├── swa.plugin │ │ ├── package.json │ │ ├── resources │ │ │ └── swa_flight.png │ │ └── plugin.js │ ├── evernote.plugin │ │ ├── package.json │ │ └── plugin.js │ └── fastspring.plugin │ │ ├── package.json │ │ └── plugin.js ├── en.lproj │ └── InfoPlist.strings ├── Sounds │ └── notif-unread-sound.aiff ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── bigsur.png │ │ ├── bigsur76.png │ │ ├── bigsur@2x.png │ │ ├── bigsur76@2x.png │ │ └── Contents.json │ ├── icon_reply.imageset │ │ ├── icon_reply@2x.png │ │ └── Contents.json │ ├── icon_archive.imageset │ │ ├── icon_archive@2x.png │ │ └── Contents.json │ ├── icon_cancel.imageset │ │ ├── icon_cancel@2x.png │ │ └── Contents.json │ ├── icon_compose.imageset │ │ ├── icon_compose@2x.png │ │ └── Contents.json │ ├── icon_sidebar.imageset │ │ ├── icon_sidebar@2x.png │ │ └── Contents.json │ ├── popover_arrow.imageset │ │ ├── popover_arrow@2x.png │ │ └── Contents.json │ ├── icon_unarchive.imageset │ │ ├── icon_unarchive@2x.png │ │ └── Contents.json │ ├── icon_draft_edit.imageset │ │ ├── icon_draft_edit@2x.png │ │ └── Contents.json │ ├── icon_draft_delete.imageset │ │ ├── icon_draft_delete@2x.png │ │ └── Contents.json │ ├── sidebar_icon_sent.imageset │ │ ├── sidebar_icon_sent@2x.png │ │ └── Contents.json │ ├── icon_add_recipient.imageset │ │ ├── icon_add_recipient@2x.png │ │ └── Contents.json │ ├── popover_arrow_down.imageset │ │ ├── popover_arrow_down@2x.png │ │ └── Contents.json │ ├── popover_background.imageset │ │ ├── popover_background@2x.png │ │ └── Contents.json │ ├── sidebar_account_on.imageset │ │ ├── sidebar_account_on@2x.png │ │ └── Contents.json │ ├── sidebar_icon_inbox.imageset │ │ ├── sidebar_icon_inbox@2x.png │ │ └── Contents.json │ ├── icon_add_attachment.imageset │ │ ├── icon_add_attachment@2x.png │ │ └── Contents.json │ ├── icon_has_attachment.imageset │ │ ├── icon_has_attachment@2x.png │ │ └── Contents.json │ ├── profile_placeholder.imageset │ │ ├── profile_placeholder@2x.png │ │ └── Contents.json │ ├── sidebar_account_off.imageset │ │ ├── sidebar_account_off@2x.png │ │ └── Contents.json │ ├── sidebar_icon_draft.imageset │ │ ├── sidebar_icon_drafts@2x.png │ │ └── Contents.json │ ├── sidebar_icon_archive.imageset │ │ ├── sidebar_icon_archive@2x.png │ │ └── Contents.json │ ├── sidebar_icon_flagged.imageset │ │ ├── sidebar_icon_flagged@2x.png │ │ └── Contents.json │ ├── sidebar_icon_starred.imageset │ │ ├── sidebar_icon_starred@2x.png │ │ └── Contents.json │ ├── sidebar_icon_attachment.imageset │ │ ├── sidebar_icon_attachments@2x.png │ │ └── Contents.json │ ├── frontViewControllerDropShadow.imageset │ │ ├── frontViewControllerDropShadow.png │ │ ├── frontViewControllerDropShadow@2x.png │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Controllers │ ├── INDismissSegue.h │ ├── INTaskQueueViewController.h │ ├── INRootViewController.h │ ├── INDismissSegue.m │ ├── INLogViewController.h │ ├── INSplitViewController.h │ ├── INSidebarViewController.h │ ├── INContactsViewController.h │ ├── INMailViewController.h │ ├── INComposeViewController.h │ ├── INThreadViewController.h │ ├── INComposeViewController.xib │ ├── INRootViewController.m │ ├── INTaskQueueViewController.m │ ├── INContactsViewController.xib │ ├── INLogViewController.m │ ├── INMailViewController.xib │ ├── INContactsViewController.m │ ├── INSidebarViewController.xib │ └── INSplitViewController.m ├── Views │ ├── INSidebarTableViewCell.h │ ├── INLeftJustifiedFlowLayout.h │ ├── INMessageTableViewCell.h │ ├── INDeleteDetectingTextField.h │ ├── INComposeSubjectRowView.h │ ├── INPlaceholderTextView.h │ ├── INInboxNavTitleView.h │ ├── INThreadTableViewCell.h │ ├── INAttachmentTableViewCell.h │ ├── INComposeRowView.h │ ├── BPopdownButton.h │ ├── INComposeRecipientCollectionViewCell.h │ ├── INAutocompletionResultsView.h │ ├── BPopdownMenu.h │ ├── INDeleteDetectingTextField.m │ ├── INMailItemTableViewCell.h │ ├── INTagsView.h │ ├── INComposeAttachmentsRowView.h │ ├── INMessageContentView.h │ ├── INComposeRecipientRowView.h │ ├── INMessageTableViewCell.m │ ├── INMessageCollectionViewCell.h │ ├── INSidebarTableViewCell.m │ ├── INLeftJustifiedFlowLayout.m │ ├── INInboxNavTitleView.m │ ├── INTagsView.m │ ├── INComposeRecipientCollectionViewCell.m │ ├── INMailItemTableViewCell.m │ ├── INComposeSubjectRowView.m │ ├── INThreadTableViewCell.m │ ├── INPlaceholderTextView.m │ ├── INComposeRowView.m │ ├── INAttachmentTableViewCell.m │ ├── INAutocompletionResultsView.m │ ├── BPopdownButton.m │ ├── BPopdownMenu.m │ ├── INComposeAttachmentsRowView.m │ └── INMessageContentView.m ├── INThemeManager.h ├── main.m ├── BigSur-Prefix.pch ├── Debugging │ ├── INStressTestSyncEngine.h │ ├── INDebuggingHelpers.m │ └── INStressTestSyncEngine.m ├── Categories │ ├── UIView+FrameAdditions.h │ ├── NSObject+AssociatedObjects.h │ ├── NSString+FormatConversion.h │ ├── UIImage+BlurEffects.h │ ├── INConvenienceCategories.h │ ├── NSObject+AssociatedObjects.m │ ├── UIView+FrameAdditions.m │ ├── NSString+FormatConversion.m │ └── INConvenienceCategories.m ├── INThemeManager.m ├── INStupidFullSyncEngine.h ├── INAppDelegate.h ├── INDeltaSyncEngine.h ├── BigSur-Info.plist ├── INPluginManager.h ├── INStupidFullSyncEngine.m └── INPluginManager.m ├── arclib ├── __phutil_library_init__.php ├── src │ ├── .phutil_module_cache │ ├── InboxUncrustifyLintEngine.php │ └── InboxUncrustifyLinter.php ├── .phutil_module_cache └── __phutil_library_map__.php ├── Screenshots ├── compose.png ├── overview.png ├── sidebar.png ├── thread.png ├── threads.png ├── hand-compose.png └── hand-threads.png ├── Explorations ├── WebPluginTest │ ├── public │ │ ├── plugin_base.js │ │ └── index.html │ ├── package.json │ └── app.coffee ├── TinderPaging │ ├── TinderPaging.xcodeproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ └── TinderPaging │ │ ├── AppDelegate.h │ │ ├── ViewController.h │ │ ├── main.m │ │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── INPagingContainerViewController.h │ │ ├── ViewController.m │ │ └── AppDelegate.m └── WebPluginTest-PostMessage │ ├── public │ ├── plugin_base.js │ └── index.html │ ├── package.json │ └── app.coffee ├── BigSur.xcworkspace └── contents.xcworkspacedata ├── BigSur.xcodeproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── .arcconfig ├── Podfile ├── .gitignore ├── LICENSE.md └── README.md /BigSur/plugins/base/test.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /BigSur/plugins/swa.plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "roles": ["message-body"] 3 | } -------------------------------------------------------------------------------- /BigSur/plugins/evernote.plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "roles": ["message-action"] 3 | } -------------------------------------------------------------------------------- /BigSur/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /BigSur/plugins/fastspring.plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "roles": ["message-action"] 3 | } -------------------------------------------------------------------------------- /arclib/__phutil_library_init__.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_add_recipient.imageset/icon_add_recipient@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/icon_add_recipient.imageset/icon_add_recipient@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/popover_arrow_down.imageset/popover_arrow_down@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/popover_arrow_down.imageset/popover_arrow_down@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/popover_background.imageset/popover_background@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/popover_background.imageset/popover_background@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_account_on.imageset/sidebar_account_on@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/sidebar_account_on.imageset/sidebar_account_on@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_inbox.imageset/sidebar_icon_inbox@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/sidebar_icon_inbox.imageset/sidebar_icon_inbox@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_add_attachment.imageset/icon_add_attachment@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/icon_add_attachment.imageset/icon_add_attachment@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_has_attachment.imageset/icon_has_attachment@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/icon_has_attachment.imageset/icon_has_attachment@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/profile_placeholder.imageset/profile_placeholder@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/profile_placeholder.imageset/profile_placeholder@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_account_off.imageset/sidebar_account_off@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/sidebar_account_off.imageset/sidebar_account_off@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_draft.imageset/sidebar_icon_drafts@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/sidebar_icon_draft.imageset/sidebar_icon_drafts@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_archive.imageset/sidebar_icon_archive@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/sidebar_icon_archive.imageset/sidebar_icon_archive@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_flagged.imageset/sidebar_icon_flagged@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/sidebar_icon_flagged.imageset/sidebar_icon_flagged@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_starred.imageset/sidebar_icon_starred@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/sidebar_icon_starred.imageset/sidebar_icon_starred@2x.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_attachment.imageset/sidebar_icon_attachments@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/sidebar_icon_attachment.imageset/sidebar_icon_attachments@2x.png -------------------------------------------------------------------------------- /BigSur.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BigSur/Images.xcassets/frontViewControllerDropShadow.imageset/frontViewControllerDropShadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/frontViewControllerDropShadow.imageset/frontViewControllerDropShadow.png -------------------------------------------------------------------------------- /BigSur/Images.xcassets/frontViewControllerDropShadow.imageset/frontViewControllerDropShadow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-scaffold-ios/HEAD/BigSur/Images.xcassets/frontViewControllerDropShadow.imageset/frontViewControllerDropShadow@2x.png -------------------------------------------------------------------------------- /Explorations/TinderPaging/TinderPaging.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BigSur/plugins/base/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Base", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "author": "", 7 | "license": "ISC", 8 | "dependencies": { 9 | "browserify": "*", 10 | "cheerio": "*" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.arcconfig: -------------------------------------------------------------------------------- 1 | { 2 | "project_id" : "bigsur", 3 | "conduit_uri" : "https://review.inboxapp.com/", 4 | "lint.engine" : "InboxUncrustifyLintEngine", 5 | "lint.uncrustify.script": "uncrustify -c uncrustify.cfg -l OC --no-backup --mtime ", 6 | "load" : [ 7 | "arclib" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /BigSur/Controllers/INDismissSegue.h: -------------------------------------------------------------------------------- 1 | // 2 | // INDismissSegue.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INDismissSegue : UIStoryboardSegue 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /BigSur/plugins/evernote.plugin/plugin.js: -------------------------------------------------------------------------------- 1 | 2 | plugin.isAvailableForThread = function(thread) { 3 | return true; 4 | } 5 | 6 | plugin.actionTitleForThread = function(thread) { 7 | return "Send to Evernote"; 8 | } 9 | 10 | plugin.performForThread = function(thread) { 11 | app.alert("HIi"); 12 | } -------------------------------------------------------------------------------- /BigSur/Views/INSidebarTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // INSidebarTableViewCell.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/13/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INSidebarTableViewCell : UITableViewCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | 2 | # Uncomment this line to define a global platform for your project 3 | # platform :ios, "6.0" 4 | 5 | target "BigSur" do 6 | pod 'DateTools', '1.4.3' 7 | pod 'MBProgressHUD' 8 | pod 'UIActionSheet+Blocks' 9 | pod 'InboxKit' 10 | pod 'AFNetworking' 11 | pod 'UIAlertView+Blocks' 12 | end 13 | 14 | -------------------------------------------------------------------------------- /BigSur/Views/INLeftJustifiedFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // INLeftJustifiedFlowLayout.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INLeftJustifiedFlowLayout : UICollectionViewFlowLayout 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /BigSur/plugins/base/README: -------------------------------------------------------------------------------- 1 | Use Browserify to convert index.js into a flat file with all of the required modules: 2 | 3 | npm install -g browserify 4 | browserify index.js > compiled.js 5 | 6 | At the very top of the compiled.js file, add: 7 | 8 | window = this; 9 | 10 | Otherwise lodash flips a shit because and overwrites Object with null. -------------------------------------------------------------------------------- /BigSur/INThemeManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // INThemeManager.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/12/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INThemeManager : NSObject 12 | 13 | + (INThemeManager *)shared; 14 | 15 | - (UIColor*)tintColor; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /BigSur/Views/INMessageTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // INDraftTableViewCell.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/22/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INMailItemTableViewCell.h" 10 | 11 | @interface INMessageTableViewCell : INMailItemTableViewCell 12 | 13 | @property (nonatomic, strong) INMessage * message; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /arclib/src/.phutil_module_cache: -------------------------------------------------------------------------------- 1 | {"__symbol_cache_version__":11,"c04d6a5c54d3a7b7ce80cbe38ad327be":{"have":{"class":{"InboxUncrustifyLinter":19,"UncrustifyLintEngine":1112}},"need":{"function":{"Futures":410},"class":{"ArcanistLinter":49,"ArcanistLintEngine":1141,"ExecFuture":297,"ArcanistUsageException":760}},"xmap":{"InboxUncrustifyLinter":["ArcanistLinter"],"UncrustifyLintEngine":["ArcanistLintEngine"]}}} -------------------------------------------------------------------------------- /Explorations/TinderPaging/TinderPaging/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TinderPaging 4 | // 5 | // Created by Ben Gotow on 6/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /BigSur/Views/INDeleteDetectingTextField.h: -------------------------------------------------------------------------------- 1 | // 2 | // INDeleteDetectingTextField.h 3 | // Pods 4 | // 5 | // Created by Ben Gotow on 5/6/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface INDeleteDetectingTextField : UITextField 12 | 13 | @property (nonatomic, strong) VoidBlock didDeleteBlock; 14 | 15 | - (void)hideInsertionPoint; 16 | - (void)showInsertionPoint; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *Pods 2 | *Podfile.lock 3 | *DerivedData 4 | /BigSur.xcodeproj/xcuserdata 5 | /BigSur.xcworkspace/xcuserdata 6 | *.DS_Store 7 | /build 8 | /file_list 9 | *.uncrustify 10 | /html/ 11 | /DerivedData 12 | /Pods/ 13 | /Podfile.lock 14 | /BigSur.xcworkspace/xcuserdata/bengotow.xcuserdatad/UserInterfaceState.xcuserstate 15 | *.xcbkptlist 16 | *xcuserdata 17 | BigSur.xcworkspace/xcshareddata/BigSur.xccheckout -------------------------------------------------------------------------------- /BigSur/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/22/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "INAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([INAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Explorations/TinderPaging/TinderPaging/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // TinderPaging 4 | // 5 | // Created by Ben Gotow on 6/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "INPagingContainerViewController.h" 11 | 12 | @interface ViewController : UIViewController 13 | 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /BigSur/Controllers/INTaskQueueViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INChangeQueueViewController.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INTaskQueueViewController : UITableViewController 12 | @property (weak, nonatomic) IBOutlet UISwitch *suspendedSwitch; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Explorations/TinderPaging/TinderPaging/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TinderPaging 4 | // 5 | // Created by Ben Gotow on 6/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_reply.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "icon_reply@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_archive.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "icon_archive@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_cancel.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "icon_cancel@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_compose.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "icon_compose@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_sidebar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "icon_sidebar@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/popover_arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "popover_arrow@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Views/INComposeSubjectRowView.h: -------------------------------------------------------------------------------- 1 | // 2 | // INComposeSubjectRowView.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INComposeRowView.h" 10 | #import "INPlaceholderTextView.h" 11 | 12 | @interface INComposeSubjectRowView : INComposeRowView 13 | 14 | @property (nonatomic, strong) INPlaceholderTextView * subjectField; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_draft_edit.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "icon_draft_edit@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_unarchive.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "icon_unarchive@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_add_recipient.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "icon_add_recipient@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_draft_delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "icon_draft_delete@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/popover_arrow_down.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "popover_arrow_down@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/popover_background.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "popover_background@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_account_on.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "sidebar_account_on@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_inbox.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "sidebar_icon_inbox@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_sent.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "sidebar_icon_sent@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_add_attachment.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "icon_add_attachment@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/icon_has_attachment.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "icon_has_attachment@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/profile_placeholder.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "profile_placeholder@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_account_off.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "sidebar_account_off@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_archive.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "sidebar_icon_archive@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_draft.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "sidebar_icon_drafts@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_flagged.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "sidebar_icon_flagged@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_starred.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "sidebar_icon_starred@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Views/INPlaceholderTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // INPlaceholderTextView.h 3 | // BigSur 4 | // 5 | // DRAWN FROM http://stackoverflow.com/questions/1328638/placeholder-in-uitextview 6 | 7 | #import 8 | 9 | @interface INPlaceholderTextView : UITextView 10 | 11 | @property (nonatomic, retain) NSString *placeholder; 12 | @property (nonatomic, retain) UIColor *placeholderColor; 13 | 14 | -(void)textChanged:(NSNotification*)notification; 15 | 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /BigSur/Images.xcassets/sidebar_icon_attachment.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "sidebar_icon_attachments@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /BigSur/Controllers/INRootViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INRootViewController.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/12/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INRootViewController : UIViewController 12 | 13 | - (void)smartPushViewController:(UIViewController*)vc animated:(BOOL)animated; 14 | - (void)smartPresentViewController:(UIViewController*)vc animated:(BOOL)animated; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /BigSur/Views/INInboxNavTitleView.h: -------------------------------------------------------------------------------- 1 | // 2 | // INInboxNavTitleView.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/13/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INInboxNavTitleView : UIView 12 | 13 | @property (strong, nonatomic) UILabel * titleLabel; 14 | @property (strong, nonatomic) UILabel * titleUnreadLabel; 15 | 16 | - (void)setTitle:(NSString*)title andUnreadCount:(long)count; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /BigSur/BigSur-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #import 17 | #import "Inbox.h" 18 | #endif 19 | -------------------------------------------------------------------------------- /BigSur/Controllers/INDismissSegue.m: -------------------------------------------------------------------------------- 1 | // 2 | // INDismissSegue.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INDismissSegue.h" 10 | 11 | @implementation INDismissSegue 12 | 13 | - (void)perform { 14 | UIViewController *sourceViewController = self.sourceViewController; 15 | [sourceViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil]; 16 | } 17 | 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /arclib/.phutil_module_cache: -------------------------------------------------------------------------------- 1 | {"__symbol_cache_version__":11,"d24912c462effca0c0ff3edcc1756454":{"have":{"class":{"InboxUncrustifyLintEngine":19}},"need":{"class":{"ArcanistLintEngine":53,"InboxUncrustifyLinter":472}},"xmap":{"InboxUncrustifyLintEngine":["ArcanistLintEngine"]}},"5f8e940e7021ae81e87a0e19aefcd275":{"have":{"class":{"InboxUncrustifyLinter":19}},"need":{"function":{"Futures":410},"class":{"ArcanistLinter":49,"ExecFuture":297,"ArcanistUsageException":882}},"xmap":{"InboxUncrustifyLinter":["ArcanistLinter"]}}} -------------------------------------------------------------------------------- /BigSur/Views/INThreadTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // INThreadTableViewCell.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/22/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INMailItemTableViewCell.h" 10 | 11 | @interface INThreadTableViewCell : INMailItemTableViewCell 12 | 13 | @property (nonatomic, strong) INThread * thread; 14 | 15 | @property (nonatomic, strong) UILabel * threadCountLabel; 16 | @property (nonatomic, strong) UIView * unreadDot; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /BigSur/Controllers/INLogViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INLogViewController.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INLogViewController : UIViewController 12 | 13 | @property (nonatomic, weak) IBOutlet UITextView * appLog; 14 | @property (nonatomic, strong) NSTimer * updateTimer; 15 | 16 | - (IBAction)shareLog:(id)sender; 17 | - (IBAction)clearLog:(id)sender; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /BigSur/Debugging/INStressTestSyncEngine.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTroubleshootingSyncEngine.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 6/13/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INStressTestSyncEngine : NSObject 12 | 13 | @property (nonatomic, strong) NSTimer * timer; 14 | @property (nonatomic, strong) NSMutableArray * threads; 15 | 16 | - (id)initWithConfiguration:(NSDictionary*)config; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Explorations/TinderPaging/TinderPaging/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /BigSur/Images.xcassets/frontViewControllerDropShadow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "frontViewControllerDropShadow.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "frontViewControllerDropShadow@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /BigSur/Views/INAttachmentTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // INAttachmentTableViewCell.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/9/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INAttachmentTableViewCell : UITableViewCell 12 | 13 | @property (nonatomic, strong) UIProgressView * progressView; 14 | @property (nonatomic, strong) UIButton * xButton; 15 | @property (nonatomic, strong) VoidBlock deleteCallback; 16 | 17 | - (void)setAttachment:(id)attachment; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /BigSur/Categories/UIView+FrameAdditions.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface UIView (FrameAdditions) 4 | 5 | - (void)in_setFrameY:(float)y; 6 | - (void)in_setFrameX:(float)x; 7 | - (void)in_shiftFrame:(CGPoint)offset; 8 | - (void)in_setFrameOrigin:(CGPoint)origin; 9 | - (void)in_setFrameSize:(CGSize)size; 10 | - (void)in_setFrameCenter:(CGPoint)p; 11 | - (void)in_setFrameWidth:(float)w; 12 | - (void)in_setFrameHeight:(float)h; 13 | - (CGPoint)in_topRight; 14 | - (CGPoint)in_bottomRight; 15 | - (CGPoint)in_bottomLeft; 16 | - (id)viewAncestorOfClass:(Class)klass; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /BigSur/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /BigSur/Views/INComposeRowView.h: -------------------------------------------------------------------------------- 1 | // 2 | // INComposeRowView.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INComposeRowView : UIView 12 | { 13 | UIView * _bottomBorder; 14 | } 15 | @property (nonatomic, strong) UILabel * rowLabel; 16 | @property (nonatomic, strong) UIView * bodyView; 17 | @property (nonatomic, strong) UIButton * actionButton; 18 | @property (nonatomic, assign) BOOL animatesBottomBorder; 19 | 20 | - (void)positionBottomBorder; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /BigSur/plugins/fastspring.plugin/plugin.js: -------------------------------------------------------------------------------- 1 | plugin.isAvailableForThread = function(thread) { 2 | return thread.subject.substring(0, 6) == 'Order:'; 3 | } 4 | 5 | plugin.actionTitleForThread = function(thread) { 6 | return "Open in FastSpring..."; 7 | } 8 | 9 | plugin.performForThread = function(thread) { 10 | // find the order id 11 | var messages = thread.messages(); 12 | var order_id = thread.subject.substring(7); 13 | order_id = order_id.substring(0, order_id.indexOf(' - ')); 14 | app.openURL("https://springboard.fastspring.com/order/search.xml?query="+order_id) 15 | } -------------------------------------------------------------------------------- /BigSur/Categories/NSObject+AssociatedObjects.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+AssociatedObjects.h 3 | // 4 | // Created by Andy Matuschak on 8/27/09. 5 | // Public domain because I love you. 6 | // 7 | 8 | #import 9 | 10 | @interface NSObject (AMAssociatedObjects) 11 | 12 | - (void)associateValue:(id)value withKey:(void *)key; // Strong reference 13 | - (void)weaklyAssociateValue:(id)value withKey:(void *)key; 14 | - (id)associatedValueForKey:(void *)key; 15 | - (void)performSelectorOnMainThreadOnce:(SEL)selector; 16 | - (void)markPerformedSelector:(SEL)selector; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /BigSur/Views/BPopdownButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // BPopdownButton.h 3 | // Bloganizer 4 | // 5 | // Created by Ben Gotow on 7/11/13. 6 | // Copyright (c) 2013 Bloganizer Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "BPopdownMenu.h" 11 | 12 | @interface BPopdownButton : UIButton 13 | { 14 | UIView * _menuBackground; 15 | } 16 | @property (nonatomic, strong) BPopdownMenu * menu; 17 | 18 | - (void)setMenuOptions:(NSArray*)options; 19 | - (void)setMenuDelegate:(NSObject*)delegate; 20 | 21 | - (void)presentMenu; 22 | - (void)dismissMenu; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /BigSur/Categories/NSString+FormatConversion.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+FormatConversion.h 3 | // Mib.io 4 | // 5 | // Created by Ben Gotow on 6/7/12. 6 | // Copyright (c) 2012 Foundry376. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (FormatConversion) 12 | 13 | + (NSString *)stringWithDate:(NSDate *)date format:(NSString *)f; 14 | - (NSDate *)dateValueWithFormat:(NSString *)f; 15 | - (NSString *)md5Value; 16 | 17 | + (NSString *)generateUUIDWithExtension:(NSString *)ext; 18 | - (NSString *)urlencode; 19 | 20 | - (id)asJSONObjectOfClass:(Class)klass; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Explorations/TinderPaging/TinderPaging/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /arclib/__phutil_library_map__.php: -------------------------------------------------------------------------------- 1 | 2, 11 | 'class' => 12 | array( 13 | 'InboxUncrustifyLintEngine' => 'src/InboxUncrustifyLintEngine.php', 14 | 'InboxUncrustifyLinter' => 'src/InboxUncrustifyLinter.php', 15 | ), 16 | 'function' => 17 | array( 18 | ), 19 | 'xmap' => 20 | array( 21 | 'InboxUncrustifyLintEngine' => 'ArcanistLintEngine', 22 | 'InboxUncrustifyLinter' => 'ArcanistLinter', 23 | ), 24 | )); 25 | -------------------------------------------------------------------------------- /BigSur/Categories/UIImage+BlurEffects.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+BlurEffects.h 3 | // Expo 4 | // 5 | // Created by Ben Gotow on 10/23/13. 6 | // Copyright (c) 2013 Expo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIImage (BlurEffects) 12 | 13 | + (UIImage *)imageWithColor:(UIColor*)color; 14 | 15 | - (UIImage *)imageScaledAspectFill:(CGSize)targetSize; 16 | 17 | // from http://stackoverflow.com/questions/17041669/creating-a-blurring-overlay-view 18 | - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /BigSur/INThemeManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // INThemeManager.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/12/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INThemeManager.h" 10 | 11 | @implementation INThemeManager 12 | 13 | + (INThemeManager *)shared 14 | { 15 | static INThemeManager * sharedManager = nil; 16 | static dispatch_once_t onceToken; 17 | 18 | dispatch_once(&onceToken, ^{ 19 | sharedManager = [[INThemeManager alloc] init]; 20 | }); 21 | return sharedManager; 22 | } 23 | 24 | 25 | - (UIColor*)tintColor 26 | { 27 | return [UIColor colorWithRed:0 green:153.0/255.0 blue:204.0/255.0 alpha:1]; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /BigSur/Views/INComposeRecipientCollectionViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // INComposeRecipientCollectionViewCell.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/6/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define INComposeRecipientFont [UIFont systemFontOfSize: 15] 12 | #define INComposeRecipientVPadding 3 13 | 14 | @interface INComposeRecipientCollectionViewCell : UICollectionViewCell 15 | 16 | @property (nonatomic, strong) UIView * insetView; 17 | @property (nonatomic, strong) UILabel * nameLabel; 18 | @property (nonatomic, strong) UIImageView * profileImage; 19 | 20 | - (void)setRecipient:(NSDictionary*)recipient; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /BigSur/Debugging/INDebuggingHelpers.m: -------------------------------------------------------------------------------- 1 | // 2 | // INDebuggingHelpers.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 6/13/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | 10 | void scrollLikeACrazyMan(UITableView* tableView) 11 | { 12 | [tableView setContentOffset: CGPointMake(0,1000) animated:YES]; 13 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 14 | [tableView setContentOffset: CGPointMake(0,0) animated:YES]; 15 | }); 16 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 17 | scrollLikeACrazyMan(tableView); 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /BigSur/Categories/INConvenienceCategories.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+INConvenienceCategories.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/2/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (INConvenienceCategories) 12 | 13 | + (NSString *)stringForMessageDate:(NSDate*)date; 14 | + (NSString *)stringForMessageDate:(NSDate*)date withStyle:(NSDateFormatterStyle)style; 15 | 16 | + (NSString *)stringByCleaningWhitespaceInString:(NSString*)snippet; 17 | 18 | - (NSArray *)arrayOfValidEmailAddresses; 19 | 20 | @end 21 | 22 | @interface NSURL (INConvenienceCategories) 23 | 24 | + (NSURL*)URLForGravatar:(NSString*)email; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /BigSur/INStupidFullSyncEngine.h: -------------------------------------------------------------------------------- 1 | // 2 | // INStupidFullSyncEngine.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/16/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INStupidFullSyncEngine : NSObject 12 | 13 | @property (nonatomic, assign) int syncInProgress; 14 | @property (nonatomic, strong) NSMutableArray * syncOperations; 15 | 16 | - (id)initWithConfiguration:(NSDictionary*)config; 17 | - (BOOL)providesCompleteCacheOf:(Class)klass; 18 | 19 | - (void)sync; 20 | - (void)syncWithCallback:(ErrorBlock)callback; 21 | - (void)syncClass:(Class)klass callback:(ErrorBlock)callback; 22 | 23 | - (void)resetSyncState; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /BigSur/Controllers/INSplitViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INSplitViewController.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/30/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INSplitViewController : UIViewController 12 | 13 | @property (nonatomic, strong) NSMutableArray * paneViewControllers; 14 | @property (nonatomic, strong) NSMutableArray * paneNavigationBars; 15 | @property (nonatomic, strong) NSMutableArray * paneTapRecognizers; 16 | @property (nonatomic, strong) NSMutableArray * paneShadows; 17 | 18 | - (void)setViewControllers:(NSArray*)controllers; 19 | - (void)pushViewController:(UIViewController*)controller animated:(BOOL)animated; 20 | - (void)popPane:(BOOL)animated; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /BigSur/Controllers/INSidebarViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INSidebarViewController.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/13/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INSidebarViewController : UIViewController 12 | { 13 | NSArray * _tableSectionData; 14 | NSString * _tableViewSelectedItemName; 15 | } 16 | 17 | @property (nonatomic, weak) IBOutlet UITableView * tableView; 18 | @property (nonatomic, strong) INModelProvider * tagProvider; 19 | 20 | - (void)refresh; 21 | - (void)selectItemWithName:(NSString*)name; 22 | 23 | - (IBAction)unauthenticateTapped:(id)sender; 24 | - (IBAction)syncStatusTapped:(id)sender; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /BigSur/Views/INAutocompletionResultsView.h: -------------------------------------------------------------------------------- 1 | // 2 | // INAutocompletionResultsView.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/9/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol INAutocompletionResultsViewDelegate 12 | @optional 13 | - (void)autocompletionResultPicked:(id)item; 14 | 15 | @end 16 | 17 | @interface INAutocompletionResultsView : UIView 18 | 19 | @property (nonatomic, weak) NSObject * delegate; 20 | 21 | @property (nonatomic, strong) UITableView * tableView; 22 | @property (nonatomic, strong) INModelProvider * provider; 23 | 24 | - (void)setProvider:(INModelProvider *)provider; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /BigSur/Views/BPopdownMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // BPopdownMenu.h 3 | // Bloganizer 4 | // 5 | // Created by Ben Gotow on 7/10/13. 6 | // Copyright (c) 2013 Bloganizer Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class BPopdownMenu; 12 | 13 | @protocol BPopdownMenuDelegate 14 | 15 | - (void)popdownMenu:(BPopdownMenu*)menu optionSelected:(NSInteger)index; 16 | 17 | @end 18 | 19 | @interface BPopdownMenu : UIView 20 | { 21 | UIImageView * _arrow; 22 | UITableView * _tableView; 23 | int _optionChecked; 24 | } 25 | 26 | @property (nonatomic, assign) NSObject* delegate; 27 | @property (nonatomic, retain) NSArray * options; 28 | 29 | - (id)init; 30 | 31 | - (void)setCheckedItemIndex:(int)index; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Explorations/WebPluginTest-PostMessage/public/plugin_base.js: -------------------------------------------------------------------------------- 1 | var app = {}; 2 | app.alert = function(msg) { 3 | window.parent.postMessage('{"command":"alert", "param":"'+msg+'"}', '*'); 4 | } 5 | 6 | app.log = function(msg) { 7 | window.parent.postMessage('{"command":"log", "param":"'+msg+'"}', '*'); 8 | } 9 | 10 | app.openURL = function(url) { 11 | window.parent.postMessage('{"command":"openURL", "param":"'+url+'"}', '*'); 12 | } 13 | 14 | window.addEventListener('message', function (e) { 15 | var result = ''; 16 | try { 17 | result = eval(e.data); 18 | } catch (e) { 19 | result = 'eval() threw an exception.'; 20 | } 21 | var mainWindow = e.source; 22 | mainWindow.postMessage(result, event.origin); 23 | }); 24 | 25 | var plugin = {}; 26 | window.plugin = plugin; 27 | 28 | // END OF PLUGIN_BASE // 29 | 30 | -------------------------------------------------------------------------------- /BigSur/Views/INDeleteDetectingTextField.m: -------------------------------------------------------------------------------- 1 | // 2 | // INDeleteDetectingTextField.m 3 | // Pods 4 | // 5 | // Created by Ben Gotow on 5/6/14. 6 | // 7 | // 8 | 9 | #import "INDeleteDetectingTextField.h" 10 | #import "INThemeManager.h" 11 | 12 | @implementation INDeleteDetectingTextField 13 | 14 | - (void)deleteBackward 15 | { 16 | BOOL noTextToDelete = (self.text.length == 0); 17 | BOOL noInsertionPoint = [[self tintColor] isEqual: [UIColor clearColor]]; 18 | 19 | if ((noTextToDelete || noInsertionPoint) && _didDeleteBlock) 20 | _didDeleteBlock(); 21 | else 22 | [super deleteBackward]; 23 | } 24 | 25 | - (void)hideInsertionPoint 26 | { 27 | [self setTintColor: [UIColor clearColor]]; 28 | } 29 | 30 | - (void)showInsertionPoint 31 | { 32 | [self setTintColor: [[INThemeManager shared] tintColor]]; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /BigSur/Controllers/INContactsViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INContactsViewController.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^ ContactSelectionBlock)(INContact * object); 12 | 13 | @interface INContactsViewController : UIViewController 14 | 15 | @property (nonatomic, strong) INNamespace * namespace; 16 | @property (nonatomic, strong) INModelProvider * contactsProvider; 17 | @property (nonatomic, strong) ContactSelectionBlock contactSelectionCallback; 18 | @property (nonatomic, weak) IBOutlet UITableView * tableView; 19 | 20 | - (id)initForSelectingContactInNamespace:(INNamespace*)ns withCallback:(ContactSelectionBlock)block; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /BigSur/Views/INMailItemTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // INThreadTableViewCell.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/1/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define INSETS UIEdgeInsetsMake(8, 10, 8, 10) 12 | 13 | @class INMailItemTableViewCell; 14 | 15 | typedef void (^ DetatchBlock)(INMailItemTableViewCell * cell); 16 | 17 | @interface INMailItemTableViewCell : UITableViewCell 18 | 19 | @property (nonatomic, strong) UILabel * dateLabel; 20 | @property (nonatomic, strong) UILabel * bodyLabel; 21 | @property (nonatomic, strong) UILabel * subjectLabel; 22 | @property (nonatomic, strong) INRecipientsLabel * participantsLabel; 23 | 24 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /BigSur/Views/INTagsView.h: -------------------------------------------------------------------------------- 1 | // 2 | // INTagsView.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/14/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INTagsView : UIView 12 | { 13 | NSMutableArray * _tagViews; 14 | } 15 | 16 | @property (nonatomic, assign) NSTextAlignment alignment; 17 | 18 | /* Set the tags that are displayed in the view. 19 | 20 | @param tags An array of INTag objects. 21 | */ 22 | - (void)setTags:(NSArray*)tags; 23 | 24 | /* Set the tags that are displayed in the view, omitting tags in the given 25 | set that you may want to hide. 26 | 27 | @param tags An array of INTag objects. 28 | @param omitted An array of tag IDs that you want to omit from display. 29 | */ 30 | - (void)setTags:(NSArray*)tags withOmmittedTagIDs:(NSArray*)omitted; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /BigSur/Views/INComposeAttachmentsRowView.h: -------------------------------------------------------------------------------- 1 | // 2 | // INComposeAttachmentsRowView.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/9/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INComposeRowView.h" 10 | 11 | @class INComposeAttachmentsRowView; 12 | 13 | @protocol INComposeAttachmentsRowViewDelegate 14 | 15 | - (NSArray*)attachmentsForAttachmentsView:(INComposeAttachmentsRowView*)view; 16 | - (void)attachmentsView:(INComposeAttachmentsRowView*)view confirmRemoveAttachmentAtIndex:(NSInteger)index; 17 | 18 | @end 19 | 20 | @interface INComposeAttachmentsRowView : INComposeRowView 21 | { 22 | UITableView * _attachmentsTableView; 23 | } 24 | @property (nonatomic, weak) NSObject * delegate; 25 | 26 | - (void)animateAttachmentAdditionAtIndex:(NSInteger)index withBlock:(VoidBlock)block; 27 | - (void)animateAttachmentRemovalAtIndex:(NSInteger)index withBlock:(VoidBlock)block; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Explorations/TinderPaging/TinderPaging/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | Inbox.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /BigSur/Views/INMessageContentView.h: -------------------------------------------------------------------------------- 1 | // 2 | // INMessageContentView.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/23/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol INMessageContentViewDelegate 12 | 13 | - (void)messageContentViewSizeDetermined:(CGSize)size; 14 | 15 | @end 16 | 17 | @interface INMessageContentView : UIView 18 | { 19 | NSString * _content; 20 | BOOL _contentLoadCompleted; 21 | } 22 | 23 | @property (nonatomic, weak) IBOutlet NSObject * delegate; 24 | 25 | @property (nonatomic, strong) UIColor * tintColor; 26 | @property (nonatomic, strong) UIWebView * webView; 27 | @property (nonatomic, strong) UITextView * textView; 28 | @property (nonatomic, assign) UIEdgeInsets contentMargin; 29 | @property (nonatomic, strong) NSURL * contentBaseURL; 30 | 31 | - (void)clearContent; 32 | - (void)setContent:(NSString*)content; 33 | - (void)setContentMargin:(UIEdgeInsets)margin; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /BigSur/Views/INComposeRecipientRowView.h: -------------------------------------------------------------------------------- 1 | // 2 | // INComposeRecipientsView.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "INComposeRowView.h" 11 | #import "INDeleteDetectingTextField.h" 12 | #import "INAutocompletionResultsView.h" 13 | 14 | @interface INComposeRecipientRowView : INComposeRowView 15 | { 16 | INDeleteDetectingTextField * _textField; 17 | } 18 | @property (nonatomic, strong, readonly) NSMutableArray * recipients; 19 | @property (nonatomic, strong) UICollectionView * recipientsCollectionView; 20 | @property (nonatomic, strong) INAutocompletionResultsView * autocompletionView; 21 | 22 | - (void)addRecipients:(NSObject*)recipients; 23 | - (void)addRecipientFromContact:(INContact*)contact; 24 | 25 | - (BOOL)containsInvalidRecipients; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /BigSur/Controllers/INMailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INViewController.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/22/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "INRootViewController.h" 11 | #import "INInboxNavTitleView.h" 12 | 13 | 14 | @interface INMailViewController : INRootViewController 15 | 16 | @property (weak, nonatomic) IBOutlet UITableView * tableView; 17 | @property (weak, nonatomic) IBOutlet UISearchBar *searchBar; 18 | @property (strong, nonatomic) IBOutlet UIRefreshControl * refreshControl; 19 | @property (strong, nonatomic) IBOutlet INInboxNavTitleView * titleView; 20 | 21 | @property (strong, nonatomic) INModelProvider * provider; 22 | 23 | #pragma Actions 24 | 25 | - (IBAction)composeTapped:(id)sender; 26 | 27 | #pragma Refreshing & Thread Ppovider 28 | 29 | - (void)setProvider:(INModelProvider*)provider; 30 | - (void)setProvider:(INModelProvider *)provider andTitle:(NSString*)title; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Explorations/WebPluginTest-PostMessage/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 33 | 34 | 36 | 37 |
38 | Imagine the Message Body Here 39 |
40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ---- 3 | 4 | Copyright (c) 2014 InboxApp, Inc. and Contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /BigSur/Views/INMessageTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // INDraftTableViewCell.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/22/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INMessageTableViewCell.h" 10 | #import "INConvenienceCategories.h" 11 | 12 | @implementation INMessageTableViewCell 13 | 14 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 15 | { 16 | self = [super initWithStyle:style reuseIdentifier: reuseIdentifier]; 17 | if (self) { 18 | 19 | } 20 | return self; 21 | } 22 | 23 | - (void)setMessage:(INMessage *)message 24 | { 25 | _message = message; 26 | 27 | NSMutableArray * addresses = [NSMutableArray array]; 28 | [addresses addObjectsFromArray: [_message to]]; 29 | [self.participantsLabel setPrefixString: @"" andRecipients:addresses includeMe: YES]; 30 | [self.dateLabel setText: [NSString stringForMessageDate: [_message date]]]; 31 | [self.bodyLabel setText: [NSString stringByCleaningWhitespaceInString: [_message body]]]; 32 | if ([[_message subject] length] > 0) 33 | [self.subjectLabel setText: [_message subject]]; 34 | else 35 | [self.subjectLabel setText: @"(no subject)"]; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Explorations/WebPluginTest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Palomar", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "NODE_ENV=development foreman start -e configs/development.env", 8 | "test": "NODE_ENV=test foreman run -e configs/test.env ./node_modules/.bin/mocha --compilers coffee:coffee-script --reporter spec tests/**.coffee" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "3.4.7", 14 | "jade": "*", 15 | "sequelize": "1.7.0-beta.2", 16 | "mysql": "~2.0.0-rc2", 17 | "pg": "~2.8.3", 18 | "coffee-script": "1.6.2", 19 | "coffeescript-compiler": "0.1.1", 20 | "mocha": "~1.15.1", 21 | "supertest": "~0.8.2", 22 | "should": "2.1.1", 23 | "coffee-errors": "~0.8.4", 24 | "aws-sdk": "~2.0.0-rc3", 25 | "connect-coffee-script": "0.1.1", 26 | "connect-less": "0.3.1", 27 | "basic-auth": "0.0.1", 28 | "restler": "~3.1.0", 29 | "crypto-js": "~3.1.2-2", 30 | "underscore": "~1.5.2", 31 | "async": "~0.2.9", 32 | "handlebars": "~1.3.0", 33 | "twilio": "~1.5.0", 34 | "html-to-text": "0.0.2" 35 | }, 36 | "engines": { 37 | "node": "0.8.x", 38 | "npm": "1.1.x" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BigSur/INAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // INAppDelegate.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/22/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JSSlidingViewController.h" 11 | #import "INSidebarViewController.h" 12 | #import "INMailViewController.h" 13 | #import "INSplitViewController.h" 14 | 15 | static NSString * BigSurNamespaceChanged = @"BigSurNamespaceChanged"; 16 | 17 | @interface INAppDelegate : UIResponder 18 | 19 | @property (strong, nonatomic) NSString * runtimeLogPath; 20 | 21 | @property (strong, nonatomic) JSSlidingViewController * slidingViewController; // iPhone 22 | @property (strong, nonatomic) INSplitViewController * splitViewController; // iPad 23 | @property (strong, nonatomic) INSidebarViewController * sidebarViewController; 24 | @property (strong, nonatomic) INMailViewController * mainViewController; 25 | 26 | @property (strong, nonatomic) UIWindow * window; 27 | @property (strong, nonatomic) INNamespace * currentNamespace; 28 | 29 | + (INAppDelegate*)current; 30 | 31 | #pragma mark Showing Content 32 | 33 | - (void)showDrafts; 34 | - (void)showThreadsWithTag:(INTag*)tag; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Explorations/WebPluginTest-PostMessage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Palomar", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "NODE_ENV=development foreman start -e configs/development.env", 8 | "test": "NODE_ENV=test foreman run -e configs/test.env ./node_modules/.bin/mocha --compilers coffee:coffee-script --reporter spec tests/**.coffee" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "3.4.7", 14 | "jade": "*", 15 | "sequelize": "1.7.0-beta.2", 16 | "mysql": "~2.0.0-rc2", 17 | "pg": "~2.8.3", 18 | "coffee-script": "1.6.2", 19 | "coffeescript-compiler": "0.1.1", 20 | "mocha": "~1.15.1", 21 | "supertest": "~0.8.2", 22 | "should": "2.1.1", 23 | "coffee-errors": "~0.8.4", 24 | "aws-sdk": "~2.0.0-rc3", 25 | "connect-coffee-script": "0.1.1", 26 | "connect-less": "0.3.1", 27 | "basic-auth": "0.0.1", 28 | "restler": "~3.1.0", 29 | "crypto-js": "~3.1.2-2", 30 | "underscore": "~1.5.2", 31 | "async": "~0.2.9", 32 | "handlebars": "~1.3.0", 33 | "twilio": "~1.5.0", 34 | "html-to-text": "0.0.2" 35 | }, 36 | "engines": { 37 | "node": "0.8.x", 38 | "npm": "1.1.x" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Explorations/WebPluginTest/app.coffee: -------------------------------------------------------------------------------- 1 | express = require("express") 2 | http = require("http") 3 | path = require("path") 4 | global.fs = require("fs") 5 | 6 | app = express() 7 | 8 | # all environments 9 | app.set "port", process.env.PORT or 3000 10 | app.use express.favicon() 11 | app.use express.json() 12 | app.use express.urlencoded() 13 | app.use express.multipart(defer: false) 14 | app.use express.methodOverride() 15 | app.use app.router 16 | app.use express.static(path.join(__dirname, "public")) 17 | app.use express.errorHandler() if "development" is app.get("env") 18 | 19 | plugin_root = '../BigSur/plugins' 20 | app.get "/plugin-frame/:name", (req, res) -> 21 | res.write("") 22 | res.end() 23 | 24 | app.get "/plugin/:name", (req, res) -> 25 | fs.readFile "public/plugin_base.js", (err, base_data) -> 26 | path = "#{plugin_root}/#{req.params.name}.plugin/plugin.js" 27 | fs.readFile path, (err, data) -> 28 | if data 29 | res.write(base_data) 30 | res.write(data) 31 | res.end() 32 | else 33 | res.end(404) 34 | 35 | server = http.createServer(app) 36 | server.listen app.get("port"), -> 37 | console.log "Express server listening on port " + app.get("port") 38 | -------------------------------------------------------------------------------- /Explorations/WebPluginTest-PostMessage/app.coffee: -------------------------------------------------------------------------------- 1 | express = require("express") 2 | http = require("http") 3 | path = require("path") 4 | global.fs = require("fs") 5 | 6 | app = express() 7 | 8 | # all environments 9 | app.set "port", process.env.PORT or 3000 10 | app.use express.favicon() 11 | app.use express.json() 12 | app.use express.urlencoded() 13 | app.use express.multipart(defer: false) 14 | app.use express.methodOverride() 15 | app.use app.router 16 | app.use express.static(path.join(__dirname, "public")) 17 | app.use express.errorHandler() if "development" is app.get("env") 18 | 19 | plugin_root = '../BigSur/plugins' 20 | app.get "/plugin-frame/:name", (req, res) -> 21 | res.write("") 22 | res.end() 23 | 24 | app.get "/plugin/:name", (req, res) -> 25 | fs.readFile "public/plugin_base.js", (err, base_data) -> 26 | path = "#{plugin_root}/#{req.params.name}.plugin/plugin.js" 27 | fs.readFile path, (err, data) -> 28 | if data 29 | res.write(base_data) 30 | res.write(data) 31 | res.end() 32 | else 33 | res.end(404) 34 | 35 | server = http.createServer(app) 36 | server.listen app.get("port"), -> 37 | console.log "Express server listening on port " + app.get("port") 38 | -------------------------------------------------------------------------------- /BigSur/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "size" : "60x60", 15 | "idiom" : "iphone", 16 | "filename" : "bigsur@2x.png", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "idiom" : "iphone", 21 | "size" : "60x60", 22 | "scale" : "3x" 23 | }, 24 | { 25 | "idiom" : "ipad", 26 | "size" : "29x29", 27 | "scale" : "1x" 28 | }, 29 | { 30 | "idiom" : "ipad", 31 | "size" : "29x29", 32 | "scale" : "2x" 33 | }, 34 | { 35 | "idiom" : "ipad", 36 | "size" : "40x40", 37 | "scale" : "1x" 38 | }, 39 | { 40 | "idiom" : "ipad", 41 | "size" : "40x40", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "size" : "76x76", 46 | "idiom" : "ipad", 47 | "filename" : "bigsur76.png", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "size" : "76x76", 52 | "idiom" : "ipad", 53 | "filename" : "bigsur76@2x.png", 54 | "scale" : "2x" 55 | } 56 | ], 57 | "info" : { 58 | "version" : 1, 59 | "author" : "xcode" 60 | } 61 | } -------------------------------------------------------------------------------- /Explorations/TinderPaging/TinderPaging/INPagingContainerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INPagingContainerViewController.h 3 | // TinderPaging 4 | // 5 | // Created by Ben Gotow on 6/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum : NSUInteger { 12 | Hidden, 13 | Appearing, 14 | Visible, 15 | Disappearing 16 | } VisibilityState; 17 | 18 | @class INPagingContainerViewController; 19 | 20 | @protocol INPagingChildViewController 21 | 22 | - (void)prepareHeaderViews:(INPagingContainerViewController*)controller; 23 | 24 | @end 25 | 26 | @interface INPagingContainerViewController : UIViewController 27 | { 28 | NSMutableDictionary * _visibility; 29 | } 30 | 31 | @property (nonatomic, strong) UIScrollView * scrollView; 32 | @property (nonatomic, strong) UIView * headerContainerView; 33 | @property (nonatomic, strong) NSMutableDictionary * headerViews; 34 | @property (nonatomic, strong) NSMutableDictionary * headerRules; 35 | 36 | @property (nonatomic, strong) NSArray * viewControllers; 37 | 38 | 39 | #pragma mark Header Views 40 | 41 | - (void)declareHeaderView:(UIView*)v withName:(NSString*)name; 42 | - (void)declareCenter:(CGPoint)p of:(NSString*)name forController:(UIViewController*)controller; 43 | 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /BigSur/Controllers/INComposeViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INComposeViewController.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "INComposeRecipientRowView.h" 11 | #import "INComposeSubjectRowView.h" 12 | #import "INComposeAttachmentsRowView.h" 13 | #import "INPlaceholderTextView.h" 14 | 15 | @interface INComposeViewController : UIViewController 16 | { 17 | NSMutableArray * _verticalLayoutConstraints; 18 | UIPopoverController * _attachmentsPopover; 19 | 20 | } 21 | 22 | @property (nonatomic, strong) INDraft * draft; 23 | 24 | @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; 25 | @property (weak, nonatomic) NSMutableArray *scrollViewRows; 26 | @property (strong, nonatomic) INPlaceholderTextView * bodyTextView; 27 | @property (strong, nonatomic) INComposeRecipientRowView * toRecipientsView; 28 | @property (strong, nonatomic) INComposeRecipientRowView * ccBccRecipientsView; 29 | @property (strong, nonatomic) INComposeSubjectRowView * subjectView; 30 | @property (strong, nonatomic) INComposeAttachmentsRowView * attachmentsView; 31 | 32 | - (id)initWithDraft:(INMessage*)draft; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Explorations/WebPluginTest/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 42 | 43 | 45 | 46 |
47 | Imagine the Message Body Here 48 |
49 | -------------------------------------------------------------------------------- /BigSur/INDeltaSyncEngine.h: -------------------------------------------------------------------------------- 1 | // 2 | // INDeltaSyncEngine.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/27/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | static NSString * INSyncFinishedNotification = @"INSyncFinishedNotification"; 12 | 13 | @interface INDeltaSyncEngine : NSObject 14 | 15 | @property (nonatomic, strong) NSMutableArray * syncOperations; 16 | @property (nonatomic, strong) NSDate * syncDate; 17 | @property (nonatomic, strong) NSTimer * syncTimer; 18 | 19 | @property (nonatomic, strong) UILocalNotification * unreadNotification; 20 | 21 | /* Creates a new instance of the sync engine with the desired configuration. 22 | 23 | @param configuration Currenty unused. 24 | @return A configured sync engine instance 25 | */ 26 | - (id)initWithConfiguration:(NSDictionary*)config; 27 | 28 | /* Start a sync, and call the callback when sync has completely 29 | finished or failed with an error. 30 | 31 | @param callback A block to invoke when the sync has finished or has failed. 32 | If a sync is already in progress or there is no current namespace, 33 | the callback is called immediately with success=NO, error=nil. 34 | */ 35 | - (void)syncWithCallback:(ErrorBlock)callback; 36 | 37 | /* Clear all sync state, usually called during the logout process. */ 38 | - (void)resetSyncState; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /BigSur/Categories/NSObject+AssociatedObjects.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+AssociatedObjects.m 3 | // 4 | // Created by Andy Matuschak on 8/27/09. 5 | // Public domain because I love you. 6 | // 7 | 8 | #import "NSObject+AssociatedObjects.h" 9 | #import 10 | 11 | @implementation NSObject (AMAssociatedObjects) 12 | 13 | - (void)associateValue:(id)value withKey:(void *)key 14 | { 15 | objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN); 16 | } 17 | 18 | - (void)weaklyAssociateValue:(id)value withKey:(void *)key 19 | { 20 | objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_ASSIGN); 21 | } 22 | 23 | - (id)associatedValueForKey:(void *)key 24 | { 25 | return objc_getAssociatedObject(self, key); 26 | } 27 | 28 | #pragma clang diagnostic push 29 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 30 | 31 | - (void)performSelectorOnMainThreadOnce:(SEL)selector 32 | { 33 | [self associateValue:[NSNumber numberWithBool:YES] withKey:(void *)selector]; 34 | 35 | dispatch_async(dispatch_get_main_queue(), ^{ 36 | if ([self associatedValueForKey:(void *)selector]) { 37 | [self performSelector:selector]; 38 | [self associateValue:nil withKey:(void *)selector]; 39 | } 40 | }); 41 | } 42 | 43 | - (void)markPerformedSelector:(SEL)selector 44 | { 45 | [self associateValue:nil withKey:(void *)selector]; 46 | } 47 | 48 | #pragma clang diagnostic pop 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /BigSur/Views/INMessageCollectionViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // INMessageCollectionViewCell.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/1/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class INMessageCollectionViewCell; 12 | 13 | typedef void (^ CellBlock)(); 14 | 15 | @interface INMessageCollectionViewCell : UICollectionViewCell 16 | 17 | @property (nonatomic, strong) INMessage * message; 18 | @property (nonatomic, strong) CellBlock messageHeightDeterminedBlock; 19 | 20 | @property (nonatomic, strong) NSMutableArray * bodySegments; 21 | 22 | @property (nonatomic, weak) IBOutlet INMessageContentView * bodyView; 23 | @property (nonatomic, weak) IBOutlet UIButton * fromProfileButton; 24 | @property (nonatomic, weak) IBOutlet INRecipientsLabel * fromField; 25 | @property (nonatomic, weak) IBOutlet INRecipientsLabel * toField; 26 | @property (nonatomic, weak) IBOutlet UILabel * dateField; 27 | @property (nonatomic, weak) IBOutlet UIButton * draftDeleteButton; 28 | @property (nonatomic, weak) IBOutlet UIButton * draftEditButton; 29 | @property (nonatomic, weak) IBOutlet UIView * draftOptionsView; 30 | @property (nonatomic, weak) IBOutlet UIView * headerContainerView; 31 | @property (nonatomic, strong) CALayer * headerBorderLayer; 32 | @property (nonatomic, assign) BOOL collapsed; 33 | 34 | + (float)cachedHeightForMessage:(INMessage*)message; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /BigSur/Controllers/INThreadViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INThreadViewController.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/30/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "INTagsView.h" 11 | #import "BPopdownButton.h" 12 | 13 | @interface INThreadViewController : UIViewController 14 | 15 | @property (weak, nonatomic) IBOutlet UICollectionView * collectionView; 16 | @property (weak, nonatomic) IBOutlet UILabel * threadSubjectLabel; 17 | @property (weak, nonatomic) IBOutlet UIView *threadHeaderView; 18 | @property (weak, nonatomic) IBOutlet INTagsView *tagsView; 19 | @property (weak, nonatomic) IBOutlet UIView *errorView; 20 | @property (weak, nonatomic) IBOutlet UILabel *errorLabel; 21 | 22 | @property (nonatomic, strong) INModelProvider * messageProvider; 23 | @property (nonatomic, strong) INModelProvider * draftProvider; 24 | 25 | @property (nonatomic, strong) INThread * thread; 26 | @property (nonatomic, strong) NSArray * messages; 27 | @property (nonatomic, strong) NSMutableDictionary * messagesCollapsedState; 28 | @property (nonatomic, strong) NSArray * drafts; 29 | 30 | @property (nonatomic, strong) NSMutableArray * actions; 31 | @property (nonatomic, strong) BPopdownButton * actionsButton; 32 | 33 | - (id)initWithThread:(INThread*)thread; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /BigSur/Views/INSidebarTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // INSidebarTableViewCell.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/13/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INSidebarTableViewCell.h" 10 | 11 | @implementation INSidebarTableViewCell 12 | 13 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 14 | { 15 | self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier]; 16 | if (self) { 17 | [[self textLabel] setFont: [UIFont fontWithName:@"HelveticaNeue-Light" size:16]]; 18 | [[self textLabel] setTextColor: [UIColor whiteColor]]; 19 | [[self detailTextLabel] setFont: [UIFont fontWithName:@"HelveticaNeue-Medium" size:14]]; 20 | [[self detailTextLabel] setTextColor: [UIColor colorWithWhite:1 alpha:0.5]]; 21 | [[self detailTextLabel] setTextAlignment: NSTextAlignmentRight]; 22 | [self setBackgroundColor: [UIColor colorWithWhite:0 alpha:0.05]]; 23 | 24 | [self setSelectedBackgroundView: [[UIView alloc] init]]; 25 | [[self selectedBackgroundView] setBackgroundColor: [UIColor colorWithWhite:0 alpha:0.3]]; 26 | } 27 | return self; 28 | } 29 | 30 | - (void)awakeFromNib 31 | { 32 | // Initialization code 33 | } 34 | 35 | - (void)layoutSubviews 36 | { 37 | [super layoutSubviews]; 38 | [[self imageView] setContentMode: UIViewContentModeCenter]; 39 | 40 | float h = self.frame.size.height; 41 | [[self imageView] setFrame: CGRectMake(8, (h-20)/2 + 1, 20, 20)]; 42 | [[self textLabel] setFrame: CGRectMake(8 + 20 + 8, 0, self.frame.size.width, h-1)]; 43 | [[self detailTextLabel] setFrame: CGRectMake(self.frame.size.width - 66, 0, 50, h)]; 44 | } 45 | 46 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 47 | { 48 | [super setSelected:selected animated:animated]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /arclib/src/InboxUncrustifyLintEngine.php: -------------------------------------------------------------------------------- 1 | getPaths(); 12 | 13 | $uncrustify_linter = new InboxUncrustifyLinter(); 14 | 15 | // Remove any paths that don't exist before we add paths to linters. We want 16 | // to do this for linters that operate on file contents because the 17 | // generated list of paths will include deleted paths when a file is 18 | // removed. 19 | foreach ($paths as $key => $path) { 20 | if (!$this->pathExists($path)) { 21 | unset($paths[$key]); 22 | } 23 | } 24 | 25 | foreach ($paths as $path) { 26 | if (!preg_match('/^.*\.(m|h)$/i', $path)) { 27 | // This isn't a python file, so don't try to apply the PyLint linter 28 | // to it. 29 | continue; 30 | } else { 31 | echo $path; 32 | } 33 | 34 | // Add the path, to tell the linter it should examine the source code 35 | // to try to find problems. 36 | $uncrustify_linter->addPath($path); 37 | } 38 | 39 | // We only built one linter, but you can build more than one (e.g., a 40 | // Javascript linter for JS), and return a list of linters to execute. You 41 | // can also add a path to more than one linter (for example, if you want 42 | // to run a Python linter and a more general text linter on every .py file). 43 | 44 | return array( 45 | $uncrustify_linter 46 | ); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /BigSur/BigSur-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Inbox 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.inbox.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleURLTypes 24 | 25 | 26 | CFBundleTypeRole 27 | Editor 28 | CFBundleURLName 29 | inbox-api 30 | CFBundleURLSchemes 31 | 32 | 33 | 34 | CFBundleVersion 35 | 1.0 36 | INAPIPath 37 | https://api.inboxapp.com/ 38 | INAppID 39 | 40 | LSRequiresIPhoneOS 41 | 42 | UIBackgroundModes 43 | 44 | fetch 45 | 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UIViewControllerBasedStatusBarAppearance 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /BigSur/INPluginManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // INPluginManager.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 6/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @protocol INGlobalExports 13 | 14 | - (void)alert:(NSString*)alert; 15 | - (void)log:(NSString*)msg; 16 | - (void)openURL:(NSString*)url; 17 | - (void)getJSON:(NSString*)url withCallback:(JSValue*)callback; 18 | 19 | @end 20 | 21 | @protocol INThreadExports 22 | @property (nonatomic, strong) NSString * subject; 23 | @property (nonatomic, strong) NSString * snippet; 24 | @property (nonatomic, strong) NSArray * participants; 25 | @property (nonatomic, strong) NSDate * lastMessageDate; 26 | @property (nonatomic, assign) BOOL unread; 27 | - (NSArray*)messages; 28 | @end 29 | 30 | @protocol INMessageExports 31 | @property (nonatomic, strong) NSString * body; 32 | @property (nonatomic, strong) NSDate * date; 33 | @property (nonatomic, strong) NSString * subject; 34 | @property (nonatomic, strong) NSArray * from; 35 | @property (nonatomic, strong) NSArray * to; 36 | @end 37 | 38 | @interface INThread (PluginSupport) 39 | - (NSArray*)messages; 40 | @end 41 | 42 | @interface INMessage (PluginSupport) 43 | @end 44 | 45 | 46 | @interface INPluginManager : NSObject 47 | 48 | @property (nonatomic, strong) NSMutableDictionary * pluginContexts; 49 | @property (nonatomic, strong) NSMutableDictionary * pluginNamesByRole; 50 | @property (nonatomic, strong) dispatch_queue_t pluginBackgroundQueue; 51 | @property (nonatomic, strong) NSString * pluginCompiledResources; 52 | 53 | + (INPluginManager *)shared; 54 | 55 | - (NSURL*)webViewBaseURL; 56 | 57 | - (JSContext*)contextForPluginWithName:(NSString*)name; 58 | - (NSArray*)pluginNamesForRole:(NSString*)role; 59 | 60 | #pragma Exposed Methods 61 | 62 | - (void)alert:(NSString*)alert; 63 | - (void)log:(NSString*)msg; 64 | - (void)openURL:(NSString*)url; 65 | - (void)getJSON:(NSString*)url withCallback:(JSValue*)callback; 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /BigSur/Controllers/INComposeViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /BigSur/Views/INLeftJustifiedFlowLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // INLeftJustifiedFlowLayout.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INLeftJustifiedFlowLayout.h" 10 | 11 | @implementation INLeftJustifiedFlowLayout 12 | 13 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 14 | { 15 | NSArray * layoutAttributes = [super layoutAttributesForElementsInRect: rect]; 16 | for (UICollectionViewLayoutAttributes * attributes in layoutAttributes) 17 | [self adjustAttributes: attributes]; 18 | return layoutAttributes; 19 | } 20 | 21 | - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 22 | { 23 | id attributes = (UICollectionViewLayoutAttributes*)[super layoutAttributesForItemAtIndexPath: indexPath]; 24 | [self adjustAttributes: attributes]; 25 | return attributes; 26 | } 27 | 28 | - (void)adjustAttributes:(UICollectionViewLayoutAttributes *)attributes 29 | { 30 | // Important: This function calls itself on other cells in some scenarios. It must be FAST. 31 | // Don't do time-consuming things here. 32 | NSIndexPath * indexPath = [attributes indexPath]; 33 | 34 | // make sure that we always left-justify the cell in a non-full row. The default flow layout 35 | // implementation sort of spreads the cells across the center and increases spacing. This is 36 | // kind of strange. 37 | CGRect previousFrame = CGRectZero; 38 | if ([indexPath row] > 0) { 39 | NSIndexPath* previousIndexPath = [NSIndexPath indexPathForItem:indexPath.item-1 inSection:indexPath.section]; 40 | if ([previousIndexPath row] != NSNotFound) 41 | previousFrame = [self layoutAttributesForItemAtIndexPath:previousIndexPath].frame; 42 | } 43 | CGRect frame = attributes.frame; 44 | 45 | if (previousFrame.origin.y == frame.origin.y) 46 | frame.origin.x = previousFrame.origin.x + previousFrame.size.width + self.minimumInteritemSpacing; 47 | else 48 | frame.origin.x = [self sectionInset].left; 49 | 50 | [attributes setFrame: frame]; 51 | } 52 | 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /BigSur/Views/INInboxNavTitleView.m: -------------------------------------------------------------------------------- 1 | // 2 | // INInboxNavTitleView.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/13/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INInboxNavTitleView.h" 10 | #import "UIView+FrameAdditions.h" 11 | 12 | @implementation INInboxNavTitleView 13 | 14 | - (id)initWithFrame:(CGRect)frame 15 | { 16 | self = [super initWithFrame:frame]; 17 | if (self) { 18 | _titleLabel = [[UILabel alloc] initWithFrame: CGRectMake(0, 0, 0, 30)]; 19 | [_titleLabel setFont: [[UINavigationBar appearance] titleTextAttributes][NSFontAttributeName]]; 20 | [_titleLabel setTextColor: [[UINavigationBar appearance] titleTextAttributes][NSForegroundColorAttributeName]]; 21 | _titleUnreadLabel = [[UILabel alloc] initWithFrame: CGRectMake(0, 8, 0, 18)]; 22 | [_titleUnreadLabel setFont: [UIFont fontWithName:@"HelveticaNeue" size: 14]]; 23 | [_titleUnreadLabel setTextColor: [UIColor colorWithWhite:0.4 alpha:1]]; 24 | [_titleUnreadLabel setTextAlignment: NSTextAlignmentCenter]; 25 | [_titleUnreadLabel setBackgroundColor: [UIColor whiteColor]]; 26 | 27 | [self addSubview: _titleLabel]; 28 | [self addSubview: _titleUnreadLabel]; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)setTitle:(NSString*)title andUnreadCount:(long)count 34 | { 35 | float x = 0; 36 | float width = ceilf([_titleLabel.text sizeWithAttributes: @{NSFontAttributeName: [_titleLabel font]}].width) + 6; 37 | if (width > 180) 38 | width = 180; 39 | 40 | [_titleLabel setText: title]; 41 | [_titleLabel in_setFrameWidth: width]; 42 | x += [_titleLabel frame].size.width; 43 | 44 | if ((count == NSNotFound) || (count == 0)) { 45 | [_titleUnreadLabel in_setFrameWidth: 0]; 46 | } else { 47 | [_titleUnreadLabel setText: [NSString stringWithFormat: @"%ld", count]]; 48 | [_titleUnreadLabel in_setFrameWidth: ceilf([_titleUnreadLabel.text sizeWithAttributes: @{NSFontAttributeName: [_titleUnreadLabel font]}].width) + 6]; 49 | [_titleUnreadLabel in_setFrameX: x]; 50 | x += [_titleUnreadLabel frame].size.width; 51 | } 52 | 53 | [self setBounds:CGRectMake(0, 0, x, 30)]; 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /BigSur/Debugging/INStressTestSyncEngine.m: -------------------------------------------------------------------------------- 1 | // 2 | // INTroubleshootingSyncEngine.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 6/13/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INStressTestSyncEngine.h" 10 | 11 | @implementation INStressTestSyncEngine 12 | 13 | - (id)initWithConfiguration:(NSDictionary*)config 14 | { 15 | self = [super init]; 16 | if (self) { 17 | _timer = [NSTimer scheduledTimerWithTimeInterval:0.025 target:self selector:@selector(update) userInfo:nil repeats:YES]; 18 | [[NSRunLoop currentRunLoop] addTimer:_timer forMode:UITrackingRunLoopMode]; 19 | 20 | dispatch_async(dispatch_get_main_queue(), ^{ 21 | INNamespace * namespace = [[INNamespace alloc] init]; 22 | [namespace updateWithResourceDictionary:@{@"email_address":@"bengotow@gmail.com", @"id":@"namespace_id"}]; 23 | [[INDatabaseManager shared] persistModel: namespace]; 24 | [[NSNotificationCenter defaultCenter] postNotificationName:INNamespacesChangedNotification object:nil]; 25 | }); 26 | 27 | _threads = [NSMutableArray array]; 28 | for (int ii = 0; ii < 200; ii ++) { 29 | INThread * thread = [[INThread alloc] init]; 30 | NSDictionary * d = @{@"id":[NSString stringWithFormat: @"%d", ii], 31 | @"subject":@"Hello World", 32 | @"namespace":@"namespace_id", 33 | @"tags": @[ @{@"id":INTagIDInbox, @"name":@"inbox"}, 34 | @{@"id":INTagIDUnread, @"name":@"unread"} 35 | ], 36 | @"last_message_date": @([[NSDate date] timeIntervalSince1970]), 37 | @"from": @[@{@"email": @"bengotow@gmail.com"}], 38 | @"participants":@[@{@"email": @"ben@inboxapp.com", @"name": @"Ben Gotow"}] 39 | }; 40 | [thread updateWithResourceDictionary:d]; 41 | [_threads addObject: thread]; 42 | } 43 | } 44 | return self; 45 | } 46 | 47 | - (BOOL)providesCompleteCacheOf:(Class)klass 48 | { 49 | return YES; 50 | } 51 | 52 | - (void)resetSyncState 53 | { 54 | 55 | } 56 | 57 | - (void)update 58 | { 59 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 60 | for (INThread * thread in _threads) { 61 | NSString * r = [NSString stringWithFormat:@"%d",rand()]; 62 | [thread setSubject: r]; 63 | } 64 | [[INDatabaseManager shared] persistModels: _threads]; 65 | }); 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /BigSur/plugins/swa.plugin/plugin.js: -------------------------------------------------------------------------------- 1 | plugin.isAvailableForMessage = function(message) { 2 | return (message.from[0].email.indexOf('luv.southwest.com') != -1); 3 | } 4 | 5 | plugin.initialHTMLForMessage = function(message) { 6 | return "
Loading flight status...
"; 7 | } 8 | 9 | plugin.finalHTMLForMessage = function(message, callback) { 10 | $ = cheerio.load(message.body); 11 | 12 | var passengerName = $('table tbody tr td table tbody tr td table tbody tr td tbody tr td div').eq(4).text().trim() 13 | var flightNumber = $('table tbody tr td table tbody tr td table tbody tr td tbody tr td div').eq(10).text().trim() 14 | var dateString = $('table tbody tr td table tbody tr td table tbody tr td tbody tr td div').eq(9).text().trim() 15 | var dateYear = (new Date).getFullYear(); 16 | var date = new Date(Date.parse(dateString + " " + dateYear)) 17 | var dateMonth = date.getMonth() + 1; 18 | var dateDay = date.getDate(); 19 | 20 | 21 | var url = "https://api.flightstats.com/flex/flightstatus/rest/v2/json/flight/status/SWA/"+flightNumber+"/dep/"+dateYear+"/"+dateMonth+"/"+dateDay+"?appId=b16bee5a&appKey=8027de581e3af06afab756d41b50c935&utc=false"; 22 | 23 | app.getJSONWithCallback(url, function(json, error) { 24 | var result = "" 25 | if (json['error']) { 26 | result = json.error.errorMessage; 27 | } else { 28 | var flight = json.flightStatuses[0]; 29 | var departTerminal = flight.airportResources.departureTerminal; 30 | var departAirport = flight.departureAirportFsCode; 31 | var publishedDeparture = flight.operationalTimes.publishedDeparture.dateLocal; 32 | var scheduledDeparture = flight.operationalTimes.scheduledGateDeparture.dateLocal; 33 | result = "Departing from "+departAirport+" Terminal "+departTerminal+" at "+scheduledDeparture; 34 | 35 | } 36 | app.log(JSON.stringify(json)); 37 | var html = "
"+result+"
"; 38 | callback(html); 39 | }); 40 | } -------------------------------------------------------------------------------- /Explorations/TinderPaging/TinderPaging/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // TinderPaging 4 | // 5 | // Created by Ben Gotow on 6/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | 12 | @implementation ViewController 13 | 14 | - (void)viewWillAppear:(BOOL)animated 15 | { 16 | UIColor * lightGreen = [UIColor colorWithRed:0.5 green:1 blue:0.5 alpha:1]; 17 | NSAssert([self.view.backgroundColor isEqual: lightGreen] == NO, @"Already fired willAppear!"); 18 | [UIView animateWithDuration:(animated ? 0.3 : 0) animations:^{ 19 | [UIView setAnimationBeginsFromCurrentState: YES]; 20 | [self.view setBackgroundColor: lightGreen]; 21 | }]; 22 | } 23 | 24 | - (void)viewDidAppear:(BOOL)animated 25 | { 26 | NSAssert([self.view.backgroundColor isEqual: [UIColor greenColor]] == NO, @"Already fired didAppear!"); 27 | [UIView animateWithDuration:(animated ? 0.3 : 0) animations:^{ 28 | [UIView setAnimationBeginsFromCurrentState: YES]; 29 | [self.view setBackgroundColor: [UIColor greenColor]]; 30 | }]; 31 | } 32 | 33 | - (void)viewWillDisappear:(BOOL)animated 34 | { 35 | UIColor * lightRed = [UIColor colorWithRed:1 green:0.5 blue:0.5 alpha:1]; 36 | NSAssert([self.view.backgroundColor isEqual: lightRed] == NO, @"Already fired willDisppear!"); 37 | [UIView animateWithDuration:(animated ? 0.3 : 0) animations:^{ 38 | [UIView setAnimationBeginsFromCurrentState: YES]; 39 | [self.view setBackgroundColor: lightRed]; 40 | }]; 41 | } 42 | 43 | - (void)viewDidDisappear:(BOOL)animated 44 | { 45 | NSAssert([self.view.backgroundColor isEqual: [UIColor redColor]] == NO, @"Already fired didDisappear!"); 46 | [UIView animateWithDuration:(animated ? 0.3 : 0) animations:^{ 47 | [UIView setAnimationBeginsFromCurrentState: YES]; 48 | [self.view setBackgroundColor: [UIColor redColor]]; 49 | }]; 50 | } 51 | 52 | - (void)viewDidLoad 53 | { 54 | [super viewDidLoad]; 55 | } 56 | 57 | - (void)didReceiveMemoryWarning 58 | { 59 | [super didReceiveMemoryWarning]; 60 | } 61 | 62 | - (void)prepareHeaderViews:(INPagingContainerViewController*)controller 63 | { 64 | UILabel * label = [[UILabel alloc] initWithFrame: CGRectMake(0, 0, 100, 20)]; 65 | [label setText: @"Label Here!"]; 66 | 67 | [controller declareHeaderView:label withName:@"label"]; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /BigSur/Controllers/INRootViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // INRootViewController.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/12/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INRootViewController.h" 10 | #import "JSSlidingViewController.h" 11 | #import "INAppDelegate.h" 12 | 13 | @implementation INRootViewController 14 | 15 | - (void)viewDidLoad 16 | { 17 | [super viewDidLoad]; 18 | 19 | if ([self parentSlidingViewController]) { 20 | UIImage * sidebarIcon = [[UIImage imageNamed: @"icon_sidebar.png"] imageWithRenderingMode: UIImageRenderingModeAlwaysOriginal]; 21 | UIBarButtonItem * sidebar = [[UIBarButtonItem alloc] initWithImage:sidebarIcon style:UIBarButtonItemStyleBordered target:self action:@selector(sidebarTapped:)]; 22 | [sidebar setTintColor: [UIColor darkGrayColor]]; 23 | [self.navigationItem setLeftBarButtonItem: sidebar]; 24 | } 25 | } 26 | 27 | - (void)viewDidAppear:(BOOL)animated 28 | { 29 | } 30 | 31 | - (void)viewWillDisappear:(BOOL)animated 32 | { 33 | } 34 | 35 | - (JSSlidingViewController*)parentSlidingViewController 36 | { 37 | JSSlidingViewController * js = (JSSlidingViewController *)[self parentViewController]; 38 | if ([js isKindOfClass: [JSSlidingViewController class]] == NO) 39 | js = (JSSlidingViewController *)[js parentViewController]; 40 | return js; 41 | } 42 | 43 | - (IBAction)sidebarTapped:(id)sender 44 | { 45 | JSSlidingViewController * js = [self parentSlidingViewController]; 46 | if ([js isOpen]) 47 | [js closeSlider:YES completion:nil]; 48 | else 49 | [js openSlider:YES completion:nil]; 50 | } 51 | 52 | - (void)smartPushViewController:(UIViewController*)vc animated:(BOOL)animated 53 | { 54 | if (self.navigationController) 55 | [self.navigationController pushViewController:vc animated:animated]; 56 | else { 57 | if ([self.parentViewController isKindOfClass: [INSplitViewController class]]){ 58 | INSplitViewController * split = (INSplitViewController*)self.parentViewController; 59 | if ([[split paneViewControllers] count] == 3) { 60 | [split popPane: NO]; 61 | [split pushViewController: vc animated: NO]; 62 | } else { 63 | [split pushViewController: vc animated: animated]; 64 | } 65 | } 66 | } 67 | } 68 | 69 | - (void)smartPresentViewController:(UIViewController*)vc animated:(BOOL)animated 70 | { 71 | [self presentViewController:vc animated:animated completion:NULL]; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /arclib/src/InboxUncrustifyLinter.php: -------------------------------------------------------------------------------- 1 | getConfiguredScript(); 7 | $root = $this->getEngine()->getWorkingCopy()->getProjectRoot(); 8 | 9 | $futures = array(); 10 | foreach ($paths as $path) { 11 | $future = new ExecFuture('%C %s', $script, $path); 12 | $future->setCWD($root); 13 | $futures[$path] = $future; 14 | } 15 | 16 | foreach (Futures($futures)->limit(4) as $path => $future) { 17 | list($stdout) = $future->resolvex(); 18 | $this->output[$path] = $stdout; 19 | } 20 | } 21 | 22 | public function getLinterName() { 23 | return "Inbox Uncrustify Linter"; 24 | } 25 | 26 | public function lintPath($path) { 27 | // Not necessary to do anything because uncrustify writes directly stderr when there's a problem 28 | 29 | // $dict = array( 30 | // 'path' => idx($match, 'file', $path), 31 | // 'line' => $line, 32 | // 'char' => $char, 33 | // 'code' => idx($match, 'code', $this->getLinterName()), 34 | // 'severity' => $this->getMatchSeverity($match), 35 | // 'name' => idx($match, 'name', 'Lint'), 36 | // 'description' => idx($match, 'message', 'Undefined Lint Message'), 37 | // ); 38 | // 39 | // $original = idx($match, 'original'); 40 | // if ($original !== null) { 41 | // $dict['original'] = $original; 42 | // } 43 | // 44 | // $replacement = idx($match, 'replacement'); 45 | // if ($replacement !== null) { 46 | // $dict['replacement'] = $replacement; 47 | // } 48 | // 49 | // $lint = ArcanistLintMessage::newFromDictionary($dict); 50 | // $this->addLintMessage($lint); 51 | } 52 | 53 | private function getConfiguredScript() { 54 | $key = 'lint.uncrustify.script'; 55 | $config = $this->getEngine() 56 | ->getConfigurationManager() 57 | ->getConfigFromAnySource($key); 58 | 59 | if (!$config) { 60 | throw new ArcanistUsageException( 61 | "InboxUncrustifyLinter: ". 62 | "You must configure '{$key}' to point to an uncrustify script to execute."); 63 | } 64 | 65 | // NOTE: No additional validation since the "script" can be some random 66 | // shell command and/or include flags, so it does not need to point to some 67 | // file on disk. 68 | 69 | return $config; 70 | } 71 | } 72 | 73 | ?> -------------------------------------------------------------------------------- /BigSur/Views/INTagsView.m: -------------------------------------------------------------------------------- 1 | // 2 | // INTagsView.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/14/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INTagsView.h" 10 | #import "UIView+FrameAdditions.h" 11 | 12 | @implementation INTagsView 13 | 14 | - (id)initWithFrame:(CGRect)frame 15 | { 16 | self = [super initWithFrame:frame]; 17 | if (self) { 18 | [self setup]; 19 | } 20 | return self; 21 | } 22 | 23 | - (void)awakeFromNib 24 | { 25 | [self setup]; 26 | } 27 | 28 | - (void)setup 29 | { 30 | _tagViews = [NSMutableArray array]; 31 | } 32 | 33 | - (void)setTags:(NSArray*)tags 34 | { 35 | [self setTags:tags withOmmittedTagIDs:@[]]; 36 | } 37 | 38 | - (void)setTags:(NSArray*)tags withOmmittedTagIDs:(NSArray*)omitted 39 | { 40 | [_tagViews makeObjectsPerformSelector: @selector(removeFromSuperview)]; 41 | [_tagViews removeAllObjects]; 42 | 43 | for (INTag * tag in tags) { 44 | if ([omitted containsObject: [tag ID]]) 45 | continue; 46 | 47 | UILabel * tagLabel = [[UILabel alloc] initWithFrame: CGRectZero]; 48 | [tagLabel setTextColor: [UIColor whiteColor]]; 49 | [tagLabel setFont: [UIFont fontWithName:@"HelveticaNeue-Medium" size:9]]; 50 | [tagLabel setBackgroundColor: [tag color]]; 51 | [[tagLabel layer] setCornerRadius: 3]; 52 | [tagLabel setClipsToBounds: YES]; 53 | [tagLabel setText: [[tag name] uppercaseString]]; 54 | [tagLabel setTextAlignment: NSTextAlignmentCenter]; 55 | 56 | CGSize textSize = [[tagLabel text] sizeWithAttributes: @{NSFontAttributeName: [tagLabel font]}]; 57 | [tagLabel in_setFrameSize: CGSizeMake(textSize.width + 8, 13)]; 58 | [self addSubview: tagLabel]; 59 | [_tagViews addObject: tagLabel]; 60 | } 61 | 62 | [self setNeedsLayout]; 63 | [self layoutIfNeeded]; 64 | } 65 | 66 | - (void)layoutSubviews 67 | { 68 | NSAssert((_alignment == NSTextAlignmentLeft) || (_alignment == NSTextAlignmentRight), @"Sorry, only left and right alignment supported."); 69 | 70 | float x = 0; 71 | float y = 0; 72 | 73 | for (UIView * tagView in _tagViews) { 74 | 75 | if (_alignment == NSTextAlignmentLeft) 76 | [tagView in_setFrameOrigin: CGPointMake(x, y)]; 77 | else 78 | [tagView in_setFrameOrigin: CGPointMake(self.frame.size.width - x - tagView.frame.size.width, y)]; 79 | 80 | x += [tagView frame].size.width + 5; 81 | if (x >= self.frame.size.width) 82 | y += [tagView frame].size.height + 5; 83 | } 84 | 85 | [self in_setFrameHeight: y + [[_tagViews lastObject] frame].size.height]; 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /BigSur/Controllers/INTaskQueueViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // INChangeQueueViewController.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INTaskQueueViewController.h" 10 | 11 | @implementation INTaskQueueViewController 12 | 13 | 14 | - (void)viewDidLoad 15 | { 16 | [[NSNotificationCenter defaultCenter] addObserver: self selector:@selector(update) name:INTaskQueueChangedNotification object:nil]; 17 | [super viewDidLoad]; 18 | [self update]; 19 | } 20 | 21 | - (void)dealloc 22 | { 23 | [[NSNotificationCenter defaultCenter] removeObserver: self]; 24 | } 25 | 26 | - (void)didReceiveMemoryWarning 27 | { 28 | [super didReceiveMemoryWarning]; 29 | } 30 | 31 | - (void)update 32 | { 33 | [self.tableView reloadData]; 34 | [_suspendedSwitch setOn: ![[INAPIManager shared] taskQueueSuspended]]; 35 | } 36 | 37 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 38 | { 39 | return [[[INAPIManager shared] taskQueue] count]; 40 | } 41 | 42 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 43 | { 44 | INAPITask * change = [[[INAPIManager shared] taskQueue] objectAtIndex: [indexPath row]]; 45 | CGRect bounding = [[change description] boundingRectWithSize:CGSizeMake(300, INT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:14]} context:nil]; 46 | return bounding.size.height + 10; 47 | } 48 | 49 | - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 50 | { 51 | INAPITask * change = [[[INAPIManager shared] taskQueue] objectAtIndex: [indexPath row]]; 52 | UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: @"cell"]; 53 | UITextView * text = (UITextView*)[cell viewWithTag: 1]; 54 | [text setText: [change description]]; 55 | return cell; 56 | } 57 | 58 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 59 | { 60 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 61 | 62 | INAPITask * change = [[[INAPIManager shared] taskQueue] objectAtIndex: [indexPath row]]; 63 | [[[UIAlertView alloc] initWithTitle:nil message:[change extendedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show]; 64 | } 65 | 66 | - (IBAction)taskQueueSuspendedToggled:(id)sender 67 | { 68 | [[INAPIManager shared] setTaskQueueSuspended: ![sender isOn]]; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /BigSur/Controllers/INContactsViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /BigSur/Controllers/INLogViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // INLogViewController.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INLogViewController.h" 10 | #import "INAppDelegate.h" 11 | 12 | #define MAX_CHARS 20000 13 | 14 | @implementation INLogViewController 15 | 16 | - (void)viewDidLoad 17 | { 18 | [super viewDidLoad]; 19 | 20 | UIBarButtonItem * share = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(shareLog:)]; 21 | UIBarButtonItem * clear = [[UIBarButtonItem alloc] initWithTitle:@"Clear" style:UIBarButtonItemStyleBordered target:self action:@selector(clearLog:)]; 22 | [[self navigationItem] setRightBarButtonItems:@[share, clear]]; 23 | } 24 | 25 | - (void)viewWillAppear:(BOOL)animated 26 | { 27 | _updateTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(update) userInfo:nil repeats:YES]; 28 | [self update]; 29 | } 30 | 31 | - (void)viewWillDisappear:(BOOL)animated 32 | { 33 | [_updateTimer invalidate]; 34 | } 35 | 36 | - (void)didReceiveMemoryWarning 37 | { 38 | [super didReceiveMemoryWarning]; 39 | } 40 | 41 | - (void)update 42 | { 43 | NSString * text = nil; 44 | #if DEBUG 45 | text = @"The app log is printed to the system console for DEBUG builds."; 46 | #else 47 | text = [NSString stringWithContentsOfFile:[[INAppDelegate current] runtimeLogPath] encoding:NSUTF8StringEncoding error:nil]; 48 | #endif 49 | 50 | if ([text length] > MAX_CHARS) 51 | text = [text substringFromIndex: [text length] - MAX_CHARS]; 52 | [_appLog setText: text]; 53 | [_appLog scrollRangeToVisible:NSMakeRange([_appLog.text length], 0)]; 54 | 55 | // see http://stackoverflow.com/questions/19124037/scroll-to-bottom-of-uitextview-erratic-in-ios-7 56 | [_appLog setScrollEnabled:NO]; 57 | [_appLog setScrollEnabled:YES]; 58 | } 59 | 60 | - (IBAction)shareLog:(id)sender 61 | { 62 | UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[_appLog.text] applicationActivities:nil]; 63 | [self.navigationController presentViewController:activityViewController animated:YES completion:^{ 64 | 65 | }]; 66 | } 67 | 68 | - (IBAction)clearLog:(id)sender 69 | { 70 | NSString * path = [[INAppDelegate current] runtimeLogPath]; 71 | [[NSFileManager defaultManager] removeItemAtPath:path error:NULL]; 72 | freopen([path cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr); 73 | [self update]; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /Explorations/TinderPaging/TinderPaging/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TinderPaging 4 | // 5 | // Created by Ben Gotow on 6/19/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "INPagingContainerViewController.h" 11 | #import "ViewController.h" 12 | 13 | 14 | @implementation AppDelegate 15 | 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | INPagingContainerViewController * paging = [[INPagingContainerViewController alloc] init]; 20 | ViewController * a = [[ViewController alloc] init]; 21 | ViewController * b = [[ViewController alloc] init]; 22 | ViewController * c = [[ViewController alloc] init]; 23 | [paging setViewControllers: @[a,b,c]]; 24 | 25 | self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; 26 | self.window.rootViewController = paging; 27 | [self.window makeKeyAndVisible]; 28 | 29 | return YES; 30 | } 31 | 32 | - (void)applicationWillResignActive:(UIApplication *)application { 33 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 34 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 35 | } 36 | 37 | - (void)applicationDidEnterBackground:(UIApplication *)application { 38 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 39 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 40 | } 41 | 42 | - (void)applicationWillEnterForeground:(UIApplication *)application { 43 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 44 | } 45 | 46 | - (void)applicationDidBecomeActive:(UIApplication *)application { 47 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 48 | } 49 | 50 | - (void)applicationWillTerminate:(UIApplication *)application { 51 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /BigSur/Views/INComposeRecipientCollectionViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // INComposeRecipientCollectionViewCell.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/6/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INComposeRecipientCollectionViewCell.h" 10 | #import "UIImageView+AFNetworking.h" 11 | #import "INConvenienceCategories.h" 12 | #import "INThemeManager.h" 13 | 14 | @implementation INComposeRecipientCollectionViewCell 15 | 16 | - (id)initWithFrame:(CGRect)frame 17 | { 18 | self = [super initWithFrame:frame]; 19 | if (self) { 20 | _insetView = [[UIView alloc] initWithFrame: CGRectZero]; 21 | [[self contentView] addSubview: _insetView]; 22 | 23 | _nameLabel = [[UILabel alloc] initWithFrame: CGRectZero]; 24 | [_nameLabel setTextAlignment: NSTextAlignmentCenter]; 25 | [_nameLabel setFont: INComposeRecipientFont]; 26 | [_insetView addSubview: _nameLabel]; 27 | 28 | _profileImage = [[UIImageView alloc] initWithFrame: CGRectZero]; 29 | [_insetView addSubview: _profileImage]; 30 | 31 | [[_insetView layer] setBorderWidth: 1.0 / [[UIScreen mainScreen] scale]]; 32 | [[_insetView layer] setBorderColor: [[UIColor colorWithWhite:215.0/255.0 alpha:1] CGColor]]; 33 | [[_insetView layer] setCornerRadius: 4]; 34 | 35 | [_insetView setClipsToBounds: YES]; 36 | [_insetView setBackgroundColor: [UIColor colorWithWhite:244.0/255.0 alpha:1]]; 37 | } 38 | return self; 39 | } 40 | 41 | - (void)layoutSubviews 42 | { 43 | CGRect f = CGRectMake(0, INComposeRecipientVPadding, self.frame.size.width, self.frame.size.height - INComposeRecipientVPadding * 2); 44 | [_insetView setFrame: f]; 45 | 46 | float imageWidth = _insetView.frame.size.height; 47 | [_nameLabel setFrame: CGRectMake(imageWidth, 0, _insetView.frame.size.width - imageWidth, _insetView.frame.size.height)]; 48 | [_profileImage setFrame: CGRectMake(0, 0, imageWidth, _insetView.frame.size.height)]; 49 | 50 | [super layoutSubviews]; 51 | } 52 | 53 | - (void)setRecipient:(NSDictionary*)recipient 54 | { 55 | [[self nameLabel] setText: recipient[@"name"]]; 56 | [[self profileImage] setImageWithURL:[NSURL URLForGravatar: recipient[@"email"]] placeholderImage:[UIImage imageNamed: @"profile_placeholder.png"]]; 57 | } 58 | 59 | - (void)setSelected:(BOOL)selected 60 | { 61 | [super setSelected: selected]; 62 | 63 | if (selected) { 64 | [_insetView setBackgroundColor: [[INThemeManager shared] tintColor]]; 65 | [[[self contentView] layer] setBorderColor: [[UIColor colorWithRed:0 green:0.42 blue:0.56 alpha:1] CGColor]]; 66 | } else { 67 | [_insetView setBackgroundColor: [UIColor colorWithWhite:244.0/255.0 alpha:1]]; 68 | [[[self contentView] layer] setBorderColor: [[UIColor colorWithWhite:215.0/255.0 alpha:1] CGColor]]; 69 | } 70 | } 71 | 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /BigSur/Views/INMailItemTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // INThreadTableViewCell.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/1/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INMailItemTableViewCell.h" 10 | #import "NSString+FormatConversion.h" 11 | #import "UIView+FrameAdditions.h" 12 | #import "UIImageView+AFNetworking.h" 13 | #import "INConvenienceCategories.h" 14 | #import "INThemeManager.h" 15 | 16 | 17 | @implementation INMailItemTableViewCell 18 | 19 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 20 | { 21 | self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier: reuseIdentifier]; 22 | if (self) { 23 | _participantsLabel = [[INRecipientsLabel alloc] initWithFrame: CGRectZero]; 24 | [_participantsLabel setTextColor: [UIColor colorWithWhite:0.2 alpha:1]]; 25 | [_participantsLabel setTextFont: [UIFont systemFontOfSize: 13]]; 26 | [self.contentView addSubview: _participantsLabel]; 27 | 28 | _dateLabel = [[UILabel alloc] initWithFrame: CGRectZero]; 29 | [_dateLabel setFont: [UIFont systemFontOfSize: 13]]; 30 | [_dateLabel setTextColor: [UIColor grayColor]]; 31 | [_dateLabel setTextAlignment: NSTextAlignmentRight]; 32 | [self.contentView addSubview: _dateLabel]; 33 | 34 | _bodyLabel = [[UILabel alloc] initWithFrame: CGRectZero]; 35 | [_bodyLabel setLineBreakMode: NSLineBreakByWordWrapping]; 36 | _subjectLabel = [self textLabel]; 37 | [_subjectLabel setFont: [UIFont boldSystemFontOfSize: 15]]; 38 | [_subjectLabel setContentMode: UIViewContentModeCenter]; 39 | [_subjectLabel removeFromSuperview]; 40 | [self.contentView addSubview:_subjectLabel]; 41 | 42 | [_bodyLabel setTextColor: [UIColor grayColor]]; 43 | [_bodyLabel setFont: [UIFont systemFontOfSize: 13]]; 44 | [_bodyLabel setNumberOfLines: 2]; 45 | [self.contentView addSubview: _bodyLabel]; 46 | } 47 | return self; 48 | } 49 | 50 | - (float)textLeftInset 51 | { 52 | return INSETS.left; 53 | } 54 | 55 | - (void)layoutSubviews 56 | { 57 | CGRect f = self.frame; 58 | 59 | [super layoutSubviews]; 60 | 61 | [_dateLabel sizeToFit]; 62 | [_dateLabel in_setFrameOrigin: CGPointMake(f.size.width - INSETS.right - _dateLabel.frame.size.width, INSETS.top)]; 63 | 64 | float textX = [self textLeftInset]; 65 | float textW = f.size.width - textX - INSETS.right; 66 | 67 | [_participantsLabel setFrame: CGRectMake(textX, INSETS.top, _dateLabel.frame.origin.x - textX, 16)]; 68 | [_subjectLabel setFrame: CGRectMake(textX, [_participantsLabel in_bottomRight].y, textW, 20)]; 69 | [_bodyLabel setFrame: CGRectMake(textX, [_subjectLabel in_bottomRight].y, textW, 35)]; 70 | [_bodyLabel sizeToFit]; 71 | } 72 | 73 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 74 | { 75 | [super setSelected:selected animated:animated]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /BigSur/Views/INComposeSubjectRowView.m: -------------------------------------------------------------------------------- 1 | // 2 | // INComposeSubjectRowView.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INComposeSubjectRowView.h" 10 | 11 | @implementation INComposeSubjectRowView 12 | 13 | - (id)initWithFrame:(CGRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | if (self) { 17 | _subjectField = [[INPlaceholderTextView alloc] initWithFrame: CGRectZero]; 18 | [_subjectField setText: @""]; 19 | [_subjectField setPlaceholder: @"Subject"]; 20 | [_subjectField setDelegate: self]; 21 | [_subjectField setReturnKeyType: UIReturnKeyNext]; 22 | [_subjectField setScrollEnabled: NO]; 23 | [_subjectField setTextContainerInset: UIEdgeInsetsZero]; 24 | [_subjectField setTranslatesAutoresizingMaskIntoConstraints: NO]; 25 | [_subjectField setFont: [UIFont systemFontOfSize: 15]]; 26 | [_subjectField setBackgroundColor: [UIColor clearColor]]; 27 | [self addSubview: _subjectField]; 28 | 29 | [self.actionButton setImage: [UIImage imageNamed: @"icon_add_attachment.png"] forState:UIControlStateNormal]; 30 | [self.rowLabel setText: @""]; 31 | 32 | self.bodyView = _subjectField; 33 | } 34 | return self; 35 | } 36 | 37 | - (void)updateConstraints 38 | { 39 | NSDictionary * views = @{@"body":self.bodyView, @"label": self.rowLabel, @"action": self.actionButton}; 40 | [self addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(12)-[body]-(16)-|" options:0 metrics:nil views: views]]; 41 | [self addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(0)-[action]" options:0 metrics:nil views: views]]; 42 | 43 | [super updateConstraints]; 44 | } 45 | 46 | - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text 47 | { 48 | NSString * newText = [[textView text] stringByReplacingCharactersInRange:range withString:text]; 49 | 50 | // if the user has inserted a tab or return, reject the change and move to the next text field. 51 | // Unfortunately iOS doesn't expose any equivalent to Mac OS X's nextKeyResponder, so we have 52 | // to do it ourselves. The simplest way is to look at our sibling views and find the next one 53 | // that can become first responder. 54 | if (([newText rangeOfString: @"\n"].location != NSNotFound) || ([newText rangeOfString: @"\t"].location != NSNotFound)) { 55 | 56 | NSArray * siblings = [[self superview] subviews]; 57 | NSUInteger ii = [siblings indexOfObject: self]; 58 | for (NSUInteger x = ii; x < [siblings count]; x++){ 59 | UIView * v = [siblings objectAtIndex: x]; 60 | if ([v canBecomeFirstResponder]) { 61 | [v becomeFirstResponder]; 62 | break; 63 | } 64 | } 65 | return NO; 66 | } 67 | return YES; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /BigSur/Categories/UIView+FrameAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewAdditions.m 3 | // PAR Works iOS SDK 4 | // 5 | // Copyright 2013 PAR Works, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | 21 | 22 | #import "UIView+FrameAdditions.h" 23 | 24 | @implementation UIView (FrameAdditions) 25 | 26 | - (void)in_setFrameY:(float)y 27 | { 28 | CGRect frame = [self frame]; 29 | frame.origin.y = y; 30 | [self setFrame: frame]; 31 | } 32 | 33 | - (void)in_setFrameX:(float)x 34 | { 35 | CGRect frame = [self frame]; 36 | frame.origin.x = x; 37 | [self setFrame: frame]; 38 | } 39 | 40 | - (void)in_shiftFrame:(CGPoint)offset 41 | { 42 | CGRect frame = [self frame]; 43 | [self setFrame: CGRectMake(frame.origin.x + offset.x, frame.origin.y + offset.y, frame.size.width, frame.size.height)]; 44 | } 45 | 46 | - (void)in_setFrameOrigin:(CGPoint)origin 47 | { 48 | CGRect frame = [self frame]; 49 | [self setFrame: CGRectMake(origin.x, origin.y, frame.size.width, frame.size.height)]; 50 | } 51 | 52 | - (void)in_setFrameSize:(CGSize)size 53 | { 54 | CGRect frame = [self frame]; 55 | [self setFrame: CGRectMake(frame.origin.x, frame.origin.y, size.width, size.height)]; 56 | } 57 | 58 | - (void)in_setFrameCenter:(CGPoint)p 59 | { 60 | CGRect frame = [self frame]; 61 | [self setFrame: CGRectMake(p.x - frame.size.width / 2, p.y - frame.size.height / 2, frame.size.width, frame.size.height)]; 62 | } 63 | 64 | - (void)in_setFrameWidth:(float)w 65 | { 66 | CGRect frame = [self frame]; 67 | frame.size.width = w; 68 | [self setFrame: frame]; 69 | } 70 | 71 | - (void)in_setFrameHeight:(float)h 72 | { 73 | CGRect frame = [self frame]; 74 | frame.size.height = h; 75 | [self setFrame: frame]; 76 | } 77 | 78 | - (CGPoint)in_topRight 79 | { 80 | return CGPointMake([self frame].origin.x + [self frame].size.width, [self frame].origin.y); 81 | } 82 | 83 | - (CGPoint)in_bottomRight 84 | { 85 | return CGPointMake([self frame].origin.x + [self frame].size.width, [self frame].origin.y + [self frame].size.height); 86 | } 87 | 88 | - (CGPoint)in_bottomLeft 89 | { 90 | return CGPointMake([self frame].origin.x, [self frame].origin.y + [self frame].size.height); 91 | } 92 | 93 | - (id)viewAncestorOfClass:(Class)klass 94 | { 95 | if ([[self superview] isKindOfClass: klass]) 96 | return [self superview]; 97 | else 98 | return [[self superview] viewAncestorOfClass: klass]; 99 | } 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /BigSur/Categories/NSString+FormatConversion.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+DateConversion.m 3 | // Mib.io 4 | // 5 | // Created by Ben Gotow on 6/7/12. 6 | // Copyright (c) 2012 Foundry376. All rights reserved. 7 | // 8 | 9 | #import "NSString+FormatConversion.h" 10 | #import 11 | 12 | static NSMutableDictionary * formatters; 13 | 14 | @implementation NSString (FormatConversion) 15 | 16 | + (NSDateFormatter *)formatterForFormat:(NSString *)f 17 | { 18 | if (formatters == nil) 19 | formatters = [[NSMutableDictionary alloc] init]; 20 | 21 | NSDateFormatter * formatter = [formatters objectForKey:f]; 22 | 23 | if (!formatter) { 24 | formatter = [[NSDateFormatter alloc] init]; 25 | [formatter setDateFormat:f]; 26 | [formatters setObject:formatter forKey:f]; 27 | } 28 | return formatter; 29 | } 30 | 31 | + (NSString *)stringWithDate:(NSDate *)date format:(NSString *)f 32 | { 33 | return [[NSString formatterForFormat:f] stringFromDate:date]; 34 | } 35 | 36 | - (NSDate *)dateValueWithFormat:(NSString *)f 37 | { 38 | return [[NSString formatterForFormat:f] dateFromString:self]; 39 | } 40 | 41 | - (NSString *)md5Value 42 | { 43 | const char * cStr = [self UTF8String]; 44 | unsigned char digest[16]; 45 | 46 | CC_MD5(cStr, (CC_LONG)strlen(cStr), digest); // This is the md5 call 47 | 48 | NSMutableString * output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; 49 | 50 | for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) 51 | [output appendFormat:@"%02x", digest[i]]; 52 | 53 | return output; 54 | } 55 | 56 | + (NSString *)generateUUIDWithExtension:(NSString *)ext 57 | { 58 | CFUUIDRef theUUID = CFUUIDCreate(NULL); 59 | CFStringRef string = CFUUIDCreateString(NULL, theUUID); 60 | 61 | CFRelease(theUUID); 62 | NSString * s = (NSString *)CFBridgingRelease(string); 63 | 64 | if (ext) 65 | s = [s stringByAppendingFormat:@".%@", ext]; 66 | return s; 67 | } 68 | 69 | - (NSString *)urlencode 70 | { 71 | NSMutableString * output = [NSMutableString string]; 72 | const unsigned char * source = (const unsigned char *)[self UTF8String]; 73 | unsigned long sourceLen = strlen((const char *)source); 74 | 75 | for (int i = 0; i < sourceLen; ++i) { 76 | const unsigned char thisChar = source[i]; 77 | 78 | if (thisChar == ' ') 79 | [output appendString:@"+"]; 80 | else if ((thisChar == '.') || (thisChar == '-') || (thisChar == '_') || (thisChar == '~') || 81 | ((thisChar >= 'a') && (thisChar <= 'z')) || 82 | ((thisChar >= 'A') && (thisChar <= 'Z')) || 83 | ((thisChar >= '0') && (thisChar <= '9'))) 84 | [output appendFormat:@"%c", thisChar]; 85 | else 86 | [output appendFormat:@"%%%02X", thisChar]; 87 | } 88 | 89 | return output; 90 | } 91 | 92 | - (id)asJSONObjectOfClass:(Class)klass 93 | { 94 | if ([self length] == 0) 95 | return nil; 96 | 97 | NSData * data = [self dataUsingEncoding:NSUTF8StringEncoding]; 98 | id obj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:NULL]; 99 | 100 | if ([obj isKindOfClass:klass]) 101 | return obj; 102 | else 103 | return nil; 104 | } 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /BigSur/Controllers/INMailViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /BigSur/Views/INThreadTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // INThreadTableViewCell.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/22/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INThreadTableViewCell.h" 10 | #import "UIView+FrameAdditions.h" 11 | #import "INConvenienceCategories.h" 12 | #import "INThemeManager.h" 13 | 14 | @implementation INThreadTableViewCell 15 | 16 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 17 | { 18 | self = [super initWithStyle:style reuseIdentifier: reuseIdentifier]; 19 | if (self) { 20 | _unreadDot = [[UIView alloc] initWithFrame: CGRectMake(0, 0, 14, 14)]; 21 | [_unreadDot setBackgroundColor: [[INThemeManager shared] tintColor]]; 22 | [[_unreadDot layer] setCornerRadius: _unreadDot.frame.size.width / 2]; 23 | [self.contentView addSubview: _unreadDot]; 24 | 25 | _threadCountLabel = [[UILabel alloc] initWithFrame: CGRectZero]; 26 | [_threadCountLabel setFont: [UIFont systemFontOfSize: 13]]; 27 | [_threadCountLabel setTextColor: [UIColor grayColor]]; 28 | [_threadCountLabel setTextAlignment: NSTextAlignmentCenter]; 29 | [[_threadCountLabel layer] setBorderWidth: 1]; 30 | [[_threadCountLabel layer] setCornerRadius: 3]; 31 | [[_threadCountLabel layer] setBorderColor: [[UIColor colorWithWhite:0.7 alpha:1] CGColor]]; 32 | [self.contentView addSubview: _threadCountLabel]; 33 | } 34 | return self; 35 | } 36 | 37 | - (float)textLeftInset 38 | { 39 | return 40; 40 | } 41 | 42 | - (void)layoutSubviews 43 | { 44 | CGRect f = self.frame; 45 | 46 | [super layoutSubviews]; 47 | 48 | float textW = [self.subjectLabel frame].size.width; 49 | 50 | if ([[_threadCountLabel text] length]){ 51 | CGSize s = [[_threadCountLabel text] sizeWithAttributes: @{NSFontAttributeName: [_threadCountLabel font]}]; 52 | s.width += 8; 53 | s.height += 4; 54 | [_threadCountLabel setFrame: CGRectMake(f.size.width - INSETS.right - s.width, (f.size.height - s.height) / 2, s.width, s.height)]; 55 | [_threadCountLabel setHidden: NO]; 56 | 57 | textW -= s.width + INSETS.right; 58 | [self.subjectLabel in_setFrameWidth: textW]; 59 | [self.bodyLabel in_setFrameSize: CGSizeMake(textW, 1000)]; 60 | [self.bodyLabel sizeToFit]; 61 | 62 | } else { 63 | [_threadCountLabel setHidden: YES]; 64 | } 65 | 66 | [_unreadDot in_setFrameCenter: CGPointMake(20, self.subjectLabel.center.y)]; 67 | } 68 | 69 | 70 | - (void)setThread:(INThread *)thread 71 | { 72 | _thread = thread; 73 | 74 | BOOL includeMe = (([[_thread messageIDs] count] > 1) || ([[_thread participants] count] > 2)); 75 | [self.participantsLabel setPrefixString: @"" andRecipients:[_thread participants] includeMe: includeMe]; 76 | [self.dateLabel setText: [NSString stringForMessageDate: [_thread lastMessageDate]]]; 77 | [self.subjectLabel setText: [_thread subject]]; 78 | [self.bodyLabel setText: [NSString stringByCleaningWhitespaceInString: [_thread snippet]]]; 79 | [_unreadDot setHidden: (![_thread hasTagWithID: INTagIDUnread])]; 80 | 81 | if ([[_thread messageIDs] count] > 1) 82 | [_threadCountLabel setText: [NSString stringWithFormat:@"%d", (int)[[_thread messageIDs] count]]]; 83 | else 84 | [_threadCountLabel setText: @""]; 85 | 86 | [self setNeedsLayout]; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /BigSur/Categories/INConvenienceCategories.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+INConvenienceCategories.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/2/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INConvenienceCategories.h" 10 | #import "NSString+FormatConversion.h" 11 | 12 | @implementation NSString (INConvenienceCategories) 13 | 14 | + (NSString *)stringForMessageDate:(NSDate*)date 15 | { 16 | return [self stringForMessageDate:date withStyle:NSDateFormatterShortStyle]; 17 | } 18 | 19 | + (NSString *)stringForMessageDate:(NSDate*)date withStyle:(NSDateFormatterStyle)style 20 | { 21 | NSTimeInterval twelveHours = -60 * 60 * 12; 22 | if ([date timeIntervalSinceNow] > twelveHours) 23 | return [NSString stringWithDate:date format:@"h:mm aa"]; 24 | else { 25 | if (style == NSDateFormatterShortStyle) { 26 | return [NSString stringWithDate:date format:@"MMM d, YYYY"]; 27 | } else if (style == NSDateFormatterLongStyle) { 28 | return [NSString stringWithDate:date format:@"MMM d, YYYY 'at' h:mm aa"]; 29 | } 30 | return @"Unknown Date Style"; 31 | } 32 | } 33 | 34 | + (NSString *)stringByCleaningWhitespaceInString:(NSString*)snippet 35 | { 36 | unichar * cleaned = calloc([snippet length], sizeof(unichar)); 37 | int cleanedLength = 0; 38 | 39 | NSCharacterSet * punctuationSet = [NSCharacterSet punctuationCharacterSet]; 40 | NSCharacterSet * whitespaceSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; 41 | 42 | BOOL hasSpace = YES; 43 | for (int ii = 0; ii < [snippet length]; ii ++) { 44 | unichar c = [snippet characterAtIndex: ii]; 45 | BOOL isWhitespace = [whitespaceSet characterIsMember: c]; 46 | 47 | if (isWhitespace) { 48 | // If this character is whitespace, only add it to our string if we don't 49 | // already have a whitespace character. 50 | if (!hasSpace) 51 | cleaned[cleanedLength++] = ' '; 52 | hasSpace = YES; 53 | } else { 54 | // If this character is punctuation and our trailing character is a whitespace 55 | // character, place the punctutation where the whitespace is. Otherwise just 56 | // append the character. 57 | if (hasSpace && [punctuationSet characterIsMember: c]) 58 | cleanedLength = fmaxf(0, cleanedLength-1); 59 | cleaned[cleanedLength++] = c; 60 | hasSpace = NO; 61 | } 62 | } 63 | return [[NSString alloc] initWithCharactersNoCopy:cleaned length:cleanedLength freeWhenDone: YES]; 64 | } 65 | 66 | - (NSArray *)arrayOfValidEmailAddresses 67 | { 68 | NSRegularExpression * regex = [NSRegularExpression regularExpressionWithPattern: @"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?" options:NSRegularExpressionCaseInsensitive error:nil]; 69 | NSArray * matches = [regex matchesInString:self options: 0 range:NSMakeRange(0, [self length])]; 70 | NSMutableArray * results = [NSMutableArray array]; 71 | 72 | for (NSTextCheckingResult * match in matches) { 73 | NSString * email = [self substringWithRange: [match range]]; 74 | [results addObject: email]; 75 | } 76 | return results; 77 | } 78 | 79 | @end 80 | 81 | @implementation NSURL (INConvenienceCategories) 82 | 83 | + (NSURL*)URLForGravatar:(NSString*)email 84 | { 85 | email = [[email stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]] lowercaseString]; 86 | NSString * p = [NSString stringWithFormat: @"http://www.gravatar.com/avatar/%@?s=184", [email md5Value]]; 87 | return [NSURL URLWithString: p]; 88 | } 89 | 90 | @end 91 | 92 | -------------------------------------------------------------------------------- /BigSur/Views/INPlaceholderTextView.m: -------------------------------------------------------------------------------- 1 | // 2 | // INPlaceholderTextView.m 3 | // BigSur 4 | // 5 | 6 | #import "INPlaceholderTextView.h" 7 | 8 | 9 | @interface INPlaceholderTextView () 10 | 11 | @property (nonatomic, retain) UILabel *placeHolderLabel; 12 | 13 | @end 14 | 15 | @implementation INPlaceholderTextView 16 | 17 | CGFloat const UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION = 0.25; 18 | 19 | - (void)dealloc 20 | { 21 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 22 | #if __has_feature(objc_arc) 23 | #else 24 | [_placeHolderLabel release]; _placeHolderLabel = nil; 25 | [_placeholderColor release]; _placeholderColor = nil; 26 | [_placeholder release]; _placeholder = nil; 27 | [super dealloc]; 28 | #endif 29 | } 30 | 31 | - (void)awakeFromNib 32 | { 33 | [super awakeFromNib]; 34 | 35 | // Use Interface Builder User Defined Runtime Attributes to set 36 | // placeholder and placeholderColor in Interface Builder. 37 | if (!self.placeholder) { 38 | [self setPlaceholder:@""]; 39 | } 40 | 41 | if (!self.placeholderColor) { 42 | [self setPlaceholderColor:[UIColor lightGrayColor]]; 43 | } 44 | 45 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil]; 46 | } 47 | 48 | - (id)initWithFrame:(CGRect)frame 49 | { 50 | if( (self = [super initWithFrame:frame]) ) 51 | { 52 | [self setPlaceholder:@""]; 53 | [self setPlaceholderColor:[UIColor lightGrayColor]]; 54 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil]; 55 | } 56 | return self; 57 | } 58 | 59 | - (void)textChanged:(NSNotification *)notification 60 | { 61 | if([[self placeholder] length] == 0) 62 | { 63 | return; 64 | } 65 | 66 | [UIView animateWithDuration:UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION animations:^{ 67 | if([[self text] length] == 0) 68 | { 69 | [[self viewWithTag:999] setAlpha:1]; 70 | } 71 | else 72 | { 73 | [[self viewWithTag:999] setAlpha:0]; 74 | } 75 | }]; 76 | } 77 | 78 | - (void)setText:(NSString *)text { 79 | [super setText:text]; 80 | [self textChanged:nil]; 81 | } 82 | 83 | - (void)drawRect:(CGRect)rect 84 | { 85 | if( [[self placeholder] length] > 0 ) 86 | { 87 | if (_placeHolderLabel == nil ) 88 | { 89 | _placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.textContainerInset.left + 5,self.textContainerInset.top,self.bounds.size.width - 16,0)]; 90 | _placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping; 91 | _placeHolderLabel.numberOfLines = 0; 92 | _placeHolderLabel.font = self.font; 93 | _placeHolderLabel.backgroundColor = [UIColor clearColor]; 94 | _placeHolderLabel.textColor = self.placeholderColor; 95 | _placeHolderLabel.alpha = 0; 96 | _placeHolderLabel.tag = 999; 97 | [self addSubview:_placeHolderLabel]; 98 | } 99 | 100 | _placeHolderLabel.text = self.placeholder; 101 | [_placeHolderLabel sizeToFit]; 102 | [self sendSubviewToBack:_placeHolderLabel]; 103 | } 104 | 105 | if( [[self text] length] == 0 && [[self placeholder] length] > 0 ) 106 | { 107 | [[self viewWithTag:999] setAlpha:1]; 108 | } 109 | 110 | [super drawRect:rect]; 111 | } 112 | 113 | @end -------------------------------------------------------------------------------- /BigSur/Controllers/INContactsViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // INContactsViewController.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INContactsViewController.h" 10 | #import "INAppDelegate.h" 11 | 12 | 13 | @implementation INContactsViewController 14 | 15 | - (id)initForSelectingContactInNamespace:(INNamespace*)ns withCallback:(ContactSelectionBlock)block; 16 | { 17 | self = [super init]; 18 | if (self) { 19 | _namespace = ns; 20 | _contactSelectionCallback = block; 21 | } 22 | return self; 23 | } 24 | 25 | - (void)viewDidLoad 26 | { 27 | [super viewDidLoad]; 28 | 29 | [self setTitle: @"Contacts"]; 30 | 31 | UIBarButtonItem * left = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStyleBordered target:self action:@selector(cancelTapped:)]; 32 | [[self navigationItem] setLeftBarButtonItem: left]; 33 | 34 | [_tableView setRowHeight: 50]; 35 | 36 | self.contactsProvider = [self.namespace newContactProvider]; 37 | [_contactsProvider setItemSortDescriptors: @[[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES]]]; 38 | [_contactsProvider setDelegate:self]; 39 | [_contactsProvider refresh]; 40 | } 41 | 42 | - (IBAction)cancelTapped:(id)sender 43 | { 44 | [self dismissViewControllerAnimated:YES completion:NULL]; 45 | } 46 | 47 | #pragma Provider Delegate 48 | 49 | - (void)provider:(INModelProvider*)provider dataAltered:(INModelProviderChangeSet *)changeSet 50 | { 51 | [_tableView beginUpdates]; 52 | [_tableView deleteRowsAtIndexPaths:[changeSet indexPathsFor: INModelProviderChangeRemove] withRowAnimation:UITableViewRowAnimationAutomatic]; 53 | [_tableView insertRowsAtIndexPaths:[changeSet indexPathsFor: INModelProviderChangeAdd] withRowAnimation:UITableViewRowAnimationAutomatic]; 54 | [_tableView endUpdates]; 55 | [_tableView reloadRowsAtIndexPaths:[changeSet indexPathsFor: INModelProviderChangeUpdate] withRowAnimation:UITableViewRowAnimationNone]; 56 | } 57 | 58 | - (void)provider:(INModelProvider*)provider dataFetchFailed:(NSError *)error 59 | { 60 | [[[UIAlertView alloc] initWithTitle:@"Error!" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show]; 61 | } 62 | 63 | - (void)providerDataChanged:(INModelProvider*)provider 64 | { 65 | [_tableView reloadData]; 66 | } 67 | 68 | 69 | #pragma mark Table View Data 70 | 71 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 72 | { 73 | return [[_contactsProvider items] count]; 74 | } 75 | 76 | - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 77 | { 78 | UITableViewCell * cell = (UITableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"contact"]; 79 | if (!cell) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"contact"]; 80 | 81 | INContact * contact = [[_contactsProvider items] objectAtIndex: [indexPath row]]; 82 | [[cell textLabel] setText: [contact name]]; 83 | [[cell detailTextLabel] setText: [contact email]]; 84 | [[cell detailTextLabel] setTextColor: [UIColor grayColor]]; 85 | return cell; 86 | } 87 | 88 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 89 | { 90 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 91 | 92 | INContact * contact = [[_contactsProvider items] objectAtIndex: [indexPath row]]; 93 | if (_contactSelectionCallback) { 94 | _contactSelectionCallback(contact); 95 | } 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /BigSur/Views/INComposeRowView.m: -------------------------------------------------------------------------------- 1 | // 2 | // INComposeRowView.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INComposeRowView.h" 10 | 11 | @implementation INComposeRowView 12 | 13 | 14 | - (id)initWithFrame:(CGRect)frame 15 | { 16 | self = [super initWithFrame:frame]; 17 | if (self) { 18 | _rowLabel = [[UILabel alloc] initWithFrame: CGRectZero]; 19 | _rowLabel.translatesAutoresizingMaskIntoConstraints = NO; 20 | [_rowLabel setTextColor: [UIColor colorWithWhite:0.7 alpha:1]]; 21 | [_rowLabel setFont: [UIFont systemFontOfSize: 15]]; 22 | [_rowLabel setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal]; 23 | [self addSubview: _rowLabel]; 24 | 25 | _actionButton = [UIButton buttonWithType: UIButtonTypeCustom]; 26 | [_actionButton setTranslatesAutoresizingMaskIntoConstraints: NO]; 27 | [_actionButton setTitleColor: [_rowLabel textColor] forState:UIControlStateNormal]; 28 | [[_actionButton titleLabel] setFont: [UIFont boldSystemFontOfSize: 35]]; 29 | [_actionButton setFrame: CGRectMake(0, 0, 40, 40)]; 30 | [_actionButton setHidden: YES]; 31 | [self addSubview: _actionButton]; 32 | 33 | _bottomBorder = [[UIView alloc] init]; 34 | [_bottomBorder setFrame: CGRectMake(0, 0, frame.size.width, 0.5)]; 35 | [_bottomBorder setTranslatesAutoresizingMaskIntoConstraints: NO]; 36 | [_bottomBorder setBackgroundColor: [UIColor colorWithWhite:0.84 alpha:1]]; 37 | [self addSubview: _bottomBorder]; 38 | 39 | [self setClipsToBounds: YES]; 40 | [self setBackgroundColor: [UIColor whiteColor]]; 41 | } 42 | return self; 43 | } 44 | 45 | - (BOOL)requiresConstraintBasedLayout 46 | { 47 | return YES; 48 | } 49 | 50 | - (void)layoutSubviews 51 | { 52 | [super layoutSubviews]; 53 | [self positionBottomBorder]; 54 | } 55 | 56 | - (void)positionBottomBorder 57 | { 58 | [CATransaction begin]; 59 | [CATransaction setDisableActions: !_animatesBottomBorder]; 60 | [_bottomBorder setFrame: CGRectMake(-1, self.frame.size.height - 0.5, self.frame.size.width + 2, 0.5)]; 61 | [CATransaction commit]; 62 | } 63 | 64 | - (UIButton*)actionButton 65 | { 66 | [_actionButton setHidden: NO]; 67 | return _actionButton; 68 | } 69 | 70 | - (void)updateConstraints 71 | { 72 | NSDictionary * views = @{@"label":_rowLabel, @"body": _bodyView, @"action": _actionButton}; 73 | BOOL hasLabel = [[_rowLabel text] length]; 74 | BOOL hasAction = ![_actionButton isHidden]; 75 | 76 | // todo: this is a little silly 77 | 78 | if (hasLabel && hasAction) { 79 | [self addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(9)-[label]-(2)-[body]-[action(==40)]-(0)-|" options:0 metrics:nil views: views]]; 80 | } else if (hasLabel && !hasAction) { 81 | [self addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(9)-[label]-(2)-[body]-(4)-|" options:0 metrics:nil views: views]]; 82 | } else if (!hasLabel && hasAction) { 83 | [self addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(4)-[body]-[action(==40)]-(0)-|" options:0 metrics:nil views: views]]; 84 | } else { 85 | [self addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(4)-[body]-(4)-|" options:0 metrics:nil views: views]]; 86 | } 87 | 88 | [self addConstraint: [NSLayoutConstraint constraintWithItem:_rowLabel attribute:NSLayoutAttributeBaseline relatedBy:NSLayoutRelationEqual toItem:_actionButton attribute:NSLayoutAttributeBaseline multiplier:1 constant:-4]]; 89 | [super updateConstraints]; 90 | } 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /BigSur/INStupidFullSyncEngine.m: -------------------------------------------------------------------------------- 1 | // 2 | // INStupidFullSyncEngine.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/16/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INStupidFullSyncEngine.h" 10 | #import "INAppDelegate.h" 11 | 12 | #define REQUEST_PAGE_SIZE 50 13 | 14 | @implementation INStupidFullSyncEngine 15 | 16 | - (id)initWithConfiguration:(NSDictionary*)config 17 | { 18 | self = [super init]; 19 | if (self) { 20 | self.syncOperations = [NSMutableArray array]; 21 | 22 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkAndSync) name:INAuthenticationChangedNotification object:nil]; 23 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkAndSync) name:BigSurNamespaceChanged object:nil]; 24 | [self checkAndSync]; 25 | } 26 | return self; 27 | } 28 | 29 | - (BOOL)providesCompleteCacheOf:(Class)klass 30 | { 31 | if (klass == [INMessage class]) 32 | return NO; 33 | return YES; 34 | } 35 | 36 | - (void)checkAndSync 37 | { 38 | dispatch_async(dispatch_get_main_queue(), ^{ 39 | if ([[INAPIManager shared] isAuthenticated]) { 40 | [self sync]; 41 | 42 | } else { 43 | [_syncOperations makeObjectsPerformSelector: @selector(cancel)]; 44 | [_syncOperations removeAllObjects]; 45 | } 46 | }); 47 | } 48 | 49 | - (void)sync 50 | { 51 | if (_syncInProgress > 0) 52 | return; 53 | 54 | [self syncClass:[INTag class] callback: NULL]; 55 | [self syncClass:[INContact class] callback: NULL]; 56 | [self syncClass:[INThread class] callback: NULL]; 57 | [self syncClass:[INDraft class] callback: NULL]; 58 | 59 | [[INAPIManager shared] retryTasks]; 60 | } 61 | 62 | - (void)syncWithCallback:(ErrorBlock)callback 63 | { 64 | [self syncClass:[INThread class] callback:callback]; 65 | } 66 | 67 | - (void)syncClass:(Class)klass callback:(ErrorBlock)callback 68 | { 69 | [self syncClass:klass page: 0 callback:callback]; 70 | } 71 | 72 | - (void)syncClass:(Class)klass page:(int)page callback:(ErrorBlock)callback 73 | { 74 | INNamespace * namespace = [[INAppDelegate current] currentNamespace]; 75 | if (!namespace) return; 76 | 77 | NSString * path = [NSString stringWithFormat:@"/n/%@/%@", [namespace ID], [klass resourceAPIName]]; 78 | NSLog(@"SYNC: %@ - %d", path, page); 79 | 80 | _syncInProgress += 1; 81 | AFHTTPRequestOperation * op = [[INAPIManager shared].AF GET:path parameters:@{@"offset":@(page * REQUEST_PAGE_SIZE), @"limit":@(REQUEST_PAGE_SIZE)} success:^(AFHTTPRequestOperation *operation, id models) { 82 | if ([models count] >= REQUEST_PAGE_SIZE) { 83 | [self syncClass: klass page: page + 1 callback: callback]; 84 | } else { 85 | if (callback) 86 | callback(YES, nil); 87 | } 88 | [_syncOperations removeObject: operation]; 89 | _syncInProgress -= 1; 90 | 91 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 92 | // sync interrupted 93 | if (callback) 94 | callback(NO, error); 95 | [_syncOperations removeObject: operation]; 96 | _syncInProgress -= 1; 97 | }]; 98 | 99 | AFHTTPResponseSerializer * serializer = [[INAPIManager shared] responseSerializerForClass: klass]; 100 | [op setResponseSerializer:serializer]; 101 | [_syncOperations addObject: op]; 102 | } 103 | 104 | - (void)resetSyncState 105 | { 106 | 107 | } 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /BigSur/Views/INAttachmentTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // INAttachmentTableViewCell.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/9/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INAttachmentTableViewCell.h" 10 | 11 | @implementation INAttachmentTableViewCell 12 | 13 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 14 | { 15 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 16 | if (self) { 17 | [[self imageView] setClipsToBounds: YES]; 18 | [[self imageView] setContentMode: UIViewContentModeScaleAspectFill]; 19 | 20 | self.progressView = [[UIProgressView alloc] initWithProgressViewStyle: UIProgressViewStyleBar]; 21 | [_progressView setProgress: 0.2]; 22 | [_progressView setTrackTintColor: [UIColor colorWithWhite:0.9 alpha:1]]; 23 | [[self contentView] addSubview: _progressView]; 24 | 25 | self.xButton = [UIButton buttonWithType: UIButtonTypeCustom]; 26 | [_xButton setTitle:@"X" forState:UIControlStateNormal]; 27 | [_xButton addTarget:self action:@selector(triggerDeleteCallback:) forControlEvents:UIControlEventTouchUpInside]; 28 | [_xButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal]; 29 | [[self contentView] addSubview: _xButton]; 30 | 31 | [self setSelectionStyle: UITableViewCellSelectionStyleNone]; 32 | } 33 | return self; 34 | } 35 | 36 | - (void)layoutSubviews 37 | { 38 | [super layoutSubviews]; 39 | 40 | float h = self.frame.size.height - 8; 41 | [[self imageView] setFrame: CGRectMake(8, 4, h, h)]; 42 | [[self progressView] setFrame: CGRectMake(h + 13, (self.frame.size.height - 3)/2, self.frame.size.width - (h + 13 + 30 + 8), 3)]; 43 | [[self xButton] setFrame: CGRectMake(self.frame.size.width - 30, 4, 30, h)]; 44 | } 45 | 46 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 47 | { 48 | [super setSelected:selected animated:animated]; 49 | } 50 | 51 | - (void)setAttachment:(INFile*)attachment 52 | { 53 | [[self imageView] setImage: [attachment localPreview]]; 54 | 55 | INUploadFileTask * task = [attachment uploadTask]; 56 | [[NSNotificationCenter defaultCenter] removeObserver: self]; 57 | if (task) { 58 | [[self progressView] setAlpha: 1]; 59 | [[self progressView] setProgress: [task percentComplete] animated: NO]; 60 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateProgress:) name:INTaskProgressNotification object:task]; 61 | } else { 62 | [[self progressView] setAlpha: 0]; 63 | } 64 | } 65 | 66 | - (void)updateProgress:(NSNotification*)notif 67 | { 68 | INUploadFileTask * task = [notif object]; 69 | 70 | if (([task percentComplete] >= 1.0) && ([[self progressView] progress] < 1)) { 71 | [UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionCurveEaseOut animations:^{ 72 | [[self progressView] setAlpha: 0]; 73 | } completion:NULL]; 74 | } 75 | 76 | [UIView animateWithDuration:0.3 animations:^{ 77 | [[self progressView] setProgress: [task percentComplete] animated: YES]; 78 | }]; 79 | } 80 | 81 | - (void)dealloc 82 | { 83 | [[NSNotificationCenter defaultCenter] removeObserver: self]; 84 | } 85 | 86 | - (void)prepareForReuse 87 | { 88 | [[NSNotificationCenter defaultCenter] removeObserver: self]; 89 | [super prepareForReuse]; 90 | } 91 | 92 | - (void)triggerDeleteCallback:(id)sender 93 | { 94 | if (_deleteCallback) 95 | _deleteCallback(); 96 | } 97 | 98 | 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /BigSur/Views/INAutocompletionResultsView.m: -------------------------------------------------------------------------------- 1 | // 2 | // INAutocompletionResultsView.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/9/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INAutocompletionResultsView.h" 10 | #import "UIView+FrameAdditions.h" 11 | 12 | #define ROW_HEIGHT 36 13 | 14 | @implementation INAutocompletionResultsView 15 | 16 | - (id)initWithFrame:(CGRect)frame 17 | { 18 | self = [super initWithFrame:frame]; 19 | if (self) { 20 | _tableView = [[UITableView alloc] initWithFrame: CGRectMake(0, 0, self.frame.size.width, 0)]; 21 | [_tableView setDelegate: self]; 22 | [_tableView setDataSource: self]; 23 | [self addSubview: _tableView]; 24 | 25 | [[self layer] setShadowOffset: CGSizeMake(0, 3)]; 26 | [[self layer] setShadowRadius: 2]; 27 | [[self layer] setShadowOpacity: 0.3]; 28 | } 29 | return self; 30 | } 31 | 32 | - (void)setProvider:(INModelProvider *)provider 33 | { 34 | [_provider setDelegate: nil]; 35 | _provider = provider; 36 | [_provider setDelegate: self]; 37 | [_provider setItemRange:NSMakeRange(0, 3)]; 38 | [_tableView reloadData]; 39 | } 40 | 41 | - (void)setFrame:(CGRect)frame 42 | { 43 | [super setFrame: frame]; 44 | [_tableView setFrame: self.bounds]; 45 | 46 | CGPathRef path = CGPathCreateWithRect(self.bounds, NULL); 47 | [[self layer] setShadowPath: path]; 48 | CGPathRelease(path); 49 | } 50 | 51 | #pragma mark Autocompletion 52 | 53 | - (void)providerDataChanged:(INModelProvider*)provider 54 | { 55 | [_tableView reloadData]; 56 | 57 | CGRect worldFrame = [self convertRect:self.bounds toView:self.window]; 58 | float worldMaxY = self.window.frame.size.height - 216; 59 | // todo: make this check if keyboard is actually onscreen 60 | 61 | float resultsHeight = [[_provider items] count] * ROW_HEIGHT; 62 | float availableHeight = worldMaxY - worldFrame.origin.y; 63 | float height = fminf(availableHeight, resultsHeight); 64 | [self in_setFrameHeight: height]; 65 | [_tableView setScrollEnabled: (height < resultsHeight)]; 66 | } 67 | 68 | - (void)providerDataFetchCompleted:(INModelProvider*)provider 69 | { 70 | 71 | } 72 | 73 | - (void)provider:(INModelProvider*)provider dataFetchFailed:(NSError *)error 74 | { 75 | 76 | } 77 | 78 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 79 | { 80 | return [[_provider items] count]; 81 | } 82 | 83 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 84 | { 85 | return ROW_HEIGHT; 86 | } 87 | 88 | - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 89 | { 90 | UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 91 | if (!cell) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; 92 | 93 | INContact * contact = [[_provider items] objectAtIndex: [indexPath row]]; 94 | if ([[contact name] length]) 95 | [[cell textLabel] setText: [NSString stringWithFormat:@"%@ (%@)", [contact name],[contact email]]]; 96 | else 97 | [[cell textLabel] setText: [contact email]]; 98 | [[cell textLabel] setFont: [UIFont systemFontOfSize: 14]]; 99 | [[cell textLabel] setTextColor: [UIColor grayColor]]; 100 | 101 | return cell; 102 | } 103 | 104 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 105 | { 106 | INContact * contact = [[_provider items] objectAtIndex: [indexPath row]]; 107 | if ([self.delegate respondsToSelector: @selector(autocompletionResultPicked:)]) 108 | [self.delegate autocompletionResultPicked: contact]; 109 | 110 | [self.tableView deselectRowAtIndexPath: indexPath animated:YES]; 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /BigSur/Views/BPopdownButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // BPopdownButton.m 3 | // Bloganizer 4 | // 5 | // Created by Ben Gotow on 7/11/13. 6 | // Copyright (c) 2013 Bloganizer Inc. All rights reserved. 7 | // 8 | 9 | #import "BPopdownButton.h" 10 | #import "UIView+FrameAdditions.h" 11 | 12 | @implementation BPopdownButton 13 | 14 | - (id)initWithFrame:(CGRect)frame 15 | { 16 | self = [super initWithFrame:frame]; 17 | if (self) { 18 | [self setup]; 19 | } 20 | return self; 21 | } 22 | 23 | - (void)awakeFromNib 24 | { 25 | [self setup]; 26 | } 27 | 28 | - (void)setup 29 | { 30 | _menu = [[BPopdownMenu alloc] init]; 31 | _menuBackground = [[UIView alloc] initWithFrame: CGRectMake(0, 0, 1000, 1000)]; 32 | [_menuBackground setBackgroundColor: [UIColor colorWithWhite:0 alpha:0.1]]; 33 | [_menuBackground addGestureRecognizer: [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissMenu)]]; 34 | [self addTarget:self action:@selector(toggleMenu:) forControlEvents:UIControlEventTouchUpInside]; 35 | } 36 | 37 | - (void)setMenuOptions:(NSArray*)options 38 | { 39 | [_menu setOptions: options]; 40 | [self setEnabled: [options count] > 0]; 41 | } 42 | 43 | - (void)setMenuDelegate:(NSObject*)delegate 44 | { 45 | [_menu setDelegate: delegate]; 46 | } 47 | 48 | - (void)toggleMenu:(id)sender 49 | { 50 | if ([_menu superview]) 51 | [self dismissMenu]; 52 | else 53 | [self presentMenu]; 54 | } 55 | 56 | - (void)presentMenu 57 | { 58 | if ([_menu.options count] == 0) 59 | return; 60 | 61 | UIView * v = self; 62 | while ([[v superview] isKindOfClass: [UIWindow class]] == NO) 63 | v = [v superview]; 64 | 65 | CGRect root = [self convertRect:self.bounds toView:v]; 66 | float menuWidth = [_menu frame].size.width; 67 | float menuY = root.origin.y + root.size.height + 10; 68 | 69 | if (root.origin.x + menuWidth > v.frame.size.width) { 70 | float menuX = fminf(v.frame.size.width - 5 - menuWidth, root.origin.x + root.size.width - menuWidth); 71 | [[_menu layer] setAnchorPoint: CGPointMake((menuWidth - self.bounds.size.width/2 + 5)/menuWidth, 0)]; 72 | [_menu in_setFrameOrigin: CGPointMake(menuX, menuY)]; 73 | } else { 74 | [[_menu layer] setAnchorPoint: CGPointMake((self.bounds.size.width/2)/menuWidth, 0)]; 75 | [_menu in_setFrameOrigin: CGPointMake(root.origin.x, menuY)]; 76 | } 77 | 78 | [_menuBackground setAlpha: 0]; 79 | [v addSubview: _menuBackground]; 80 | 81 | [_menu setAlpha: 0]; 82 | [_menu setTransform: CGAffineTransformMakeScale(0.9, 0.9)]; 83 | [v addSubview: _menu]; 84 | 85 | [UIView animateWithDuration:0.25 animations:^{ 86 | [UIView setAnimationCurve: UIViewAnimationCurveEaseOut]; 87 | [_menu setAlpha: 1]; 88 | [_menuBackground setAlpha: 1]; 89 | [_menu setTransform: CGAffineTransformMakeScale(1.05, 1.05)]; 90 | } completion:^(BOOL finished) { 91 | [UIView animateWithDuration:0.1 animations:^{ 92 | [UIView setAnimationCurve: UIViewAnimationCurveEaseOut]; 93 | [_menu setTransform: CGAffineTransformIdentity]; 94 | }]; 95 | }]; 96 | } 97 | 98 | - (void)dismissMenu 99 | { 100 | [UIView animateWithDuration:0.08 animations:^{ 101 | [UIView setAnimationCurve: UIViewAnimationCurveEaseInOut]; 102 | [_menu setTransform: CGAffineTransformMakeScale(1.05, 1.05)]; 103 | [_menuBackground setAlpha: 0]; 104 | } completion:^(BOOL finished) { 105 | [UIView animateWithDuration:0.15 animations:^{ 106 | [UIView setAnimationCurve: UIViewAnimationCurveEaseIn]; 107 | [_menu setTransform: CGAffineTransformMakeScale(0.7, 0.7)]; 108 | [_menu setAlpha: 0]; 109 | [_menu in_shiftFrame: CGPointMake(0, -15)]; 110 | } completion:^(BOOL finished) { 111 | [_menu setTransform: CGAffineTransformIdentity]; 112 | [_menu removeFromSuperview]; 113 | [_menuBackground removeFromSuperview]; 114 | }]; 115 | }]; 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /BigSur/Views/BPopdownMenu.m: -------------------------------------------------------------------------------- 1 | // 2 | // BPopdownMenu.m 3 | // Bloganizer 4 | // 5 | // Created by Ben Gotow on 7/10/13. 6 | // Copyright (c) 2013 Bloganizer Inc. All rights reserved. 7 | // 8 | 9 | #import "BPopdownMenu.h" 10 | #import "UIView+FrameAdditions.h" 11 | 12 | #define ITEM_FONT [UIFont fontWithName:@"HelveticaNeue" size:15] 13 | 14 | @implementation BPopdownMenu 15 | 16 | - (id)init 17 | { 18 | self = [super initWithFrame:CGRectMake(0, 0, 160, 0)]; 19 | if (self) { 20 | _tableView = [[UITableView alloc] initWithFrame: self.bounds]; 21 | [_tableView setDelegate: self]; 22 | [_tableView setDataSource: self]; 23 | [_tableView setRowHeight: 36]; 24 | [_tableView setScrollEnabled: NO]; 25 | [_tableView setBounces: NO]; 26 | [_tableView setAlwaysBounceVertical: NO]; 27 | [_tableView setBackgroundColor: [UIColor whiteColor]]; 28 | [[_tableView layer] setCornerRadius: 2]; 29 | [_tableView setClipsToBounds: YES]; 30 | [self addSubview: _tableView]; 31 | 32 | _optionChecked = -1; 33 | 34 | _arrow = [[UIImageView alloc] initWithFrame: CGRectMake(0, -14, 16, 16)]; 35 | [_arrow setImage: [UIImage imageNamed: @"popover_arrow.png"]]; 36 | [self setClipsToBounds: NO]; 37 | [self addSubview: _arrow]; 38 | 39 | [[self layer] setBackgroundColor: [[UIColor whiteColor] CGColor]]; 40 | [[self layer] setCornerRadius: 3]; 41 | [[self layer] setShadowOffset: CGSizeMake(0, 1)]; 42 | [[self layer] setShadowOpacity: 0.3]; 43 | [[self layer] setShadowRadius: 5]; 44 | } 45 | return self; 46 | } 47 | 48 | - (void)layoutSubviews 49 | { 50 | [super layoutSubviews]; 51 | [_tableView setFrame: CGRectInset(self.bounds, 4,4)]; 52 | [_arrow in_setFrameX: [[self layer] anchorPoint].x * (self.bounds.size.width) - _arrow.bounds.size.width/2]; 53 | } 54 | 55 | - (void)setOptions:(NSArray *)options 56 | { 57 | _options = options; 58 | 59 | float maxWidth = 0; 60 | for (NSString * option in options) { 61 | float optionWidth = 40 + [option boundingRectWithSize:CGSizeMake(1000, 30) options:NSStringDrawingUsesDeviceMetrics attributes:@{NSFontAttributeName: ITEM_FONT} context:nil].size.width; 62 | maxWidth = fmaxf(maxWidth, optionWidth); 63 | } 64 | 65 | [self in_setFrameHeight: [_options count] * 36 + 4]; 66 | [self in_setFrameWidth: maxWidth]; 67 | [_tableView reloadData]; 68 | } 69 | 70 | - (void)setCheckedItemIndex:(int)index 71 | { 72 | _optionChecked = index; 73 | [_tableView reloadData]; 74 | } 75 | 76 | 77 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 78 | { 79 | return [_options count]; 80 | } 81 | 82 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 83 | { 84 | UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: @"cell"]; 85 | if (!cell) 86 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; 87 | 88 | UIView * backView = [[UIView alloc] initWithFrame:cell.frame]; 89 | backView.backgroundColor = [UIColor whiteColor]; 90 | cell.selectedBackgroundView = backView; 91 | 92 | [cell setAccessoryType: (_optionChecked == [indexPath row]) ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone]; 93 | 94 | [[cell textLabel] setHighlightedTextColor: [UIColor blackColor]]; 95 | [[cell textLabel] setTextColor: [UIColor colorWithWhite:0.1 alpha:1]]; 96 | [[cell textLabel] setFont: ITEM_FONT]; 97 | [[cell textLabel] setText: [_options objectAtIndex: [indexPath row]]]; 98 | return cell; 99 | } 100 | 101 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 102 | { 103 | if ([_delegate respondsToSelector: @selector(popdownMenu:optionSelected:)]) 104 | [_delegate popdownMenu:self optionSelected:[indexPath row]]; 105 | 106 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 107 | } 108 | 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /BigSur/Views/INComposeAttachmentsRowView.m: -------------------------------------------------------------------------------- 1 | // 2 | // INComposeAttachmentsRowView.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/9/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INComposeAttachmentsRowView.h" 10 | #import "INAttachmentTableViewCell.h" 11 | #import "UIView+FrameAdditions.h" 12 | 13 | @implementation INComposeAttachmentsRowView 14 | 15 | - (id)initWithFrame:(CGRect)frame 16 | { 17 | self = [super initWithFrame:frame]; 18 | if (self) { 19 | _attachmentsTableView = [[UITableView alloc] initWithFrame: CGRectZero]; 20 | [_attachmentsTableView setScrollEnabled: NO]; 21 | [_attachmentsTableView setSeparatorStyle: UITableViewCellSeparatorStyleNone]; 22 | [_attachmentsTableView setTranslatesAutoresizingMaskIntoConstraints: NO]; 23 | [_attachmentsTableView setDataSource: self]; 24 | [_attachmentsTableView setBackgroundColor: [UIColor clearColor]]; 25 | [self addSubview: _attachmentsTableView]; 26 | self.bodyView = _attachmentsTableView; 27 | 28 | self.animatesBottomBorder = YES; 29 | } 30 | return self; 31 | } 32 | 33 | - (CGSize)intrinsicContentSize 34 | { 35 | NSArray * attachments = [self.delegate attachmentsForAttachmentsView: self]; 36 | if ([attachments count] == 0) 37 | return CGSizeMake(UIViewNoIntrinsicMetric, 0); 38 | 39 | return CGSizeMake(UIViewNoIntrinsicMetric, [attachments count] * [_attachmentsTableView rowHeight] + 8); 40 | } 41 | 42 | - (void)setDelegate:(NSObject *)delegate 43 | { 44 | _delegate = delegate; 45 | [_attachmentsTableView reloadData]; 46 | } 47 | 48 | - (void)layoutSubviews 49 | { 50 | [super layoutSubviews]; 51 | [_attachmentsTableView setFrame: CGRectMake(0, 3, self.frame.size.width, 1000)]; 52 | } 53 | 54 | - (void)animateAttachmentAdditionAtIndex:(NSInteger)index withBlock:(VoidBlock)block 55 | { 56 | [self animateAttachmentChange:^{ 57 | block(); 58 | } withTableUpdates:^{ 59 | [_attachmentsTableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForItem:index inSection:0]] withRowAnimation:UITableViewRowAnimationTop]; 60 | }]; 61 | } 62 | 63 | - (void)animateAttachmentRemovalAtIndex:(NSInteger)index withBlock:(VoidBlock)block 64 | { 65 | [self animateAttachmentChange:^{ 66 | block(); 67 | } withTableUpdates:^{ 68 | [_attachmentsTableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForItem:index inSection:0]] withRowAnimation:UITableViewRowAnimationRight]; 69 | }]; 70 | } 71 | 72 | - (void)animateAttachmentChange:(VoidBlock)changeBlock withTableUpdates:(VoidBlock)tableBlock 73 | { 74 | [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 75 | [_attachmentsTableView beginUpdates]; 76 | changeBlock(); 77 | [self invalidateIntrinsicContentSize]; 78 | [self updateConstraints]; 79 | [self.superview layoutIfNeeded]; 80 | [self positionBottomBorder]; 81 | tableBlock(); 82 | [_attachmentsTableView endUpdates]; 83 | } completion:^(BOOL finished) { 84 | }]; 85 | } 86 | 87 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 88 | { 89 | NSArray * attachments = [self.delegate attachmentsForAttachmentsView: self]; 90 | return [attachments count]; 91 | } 92 | 93 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 94 | { 95 | INAttachmentTableViewCell * cell = (INAttachmentTableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"cell"]; 96 | if (!cell) cell = [[INAttachmentTableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:@"cell"]; 97 | 98 | NSArray * attachments = [self.delegate attachmentsForAttachmentsView: self]; 99 | INFile * attachment = [attachments objectAtIndex: [indexPath row]]; 100 | [cell setAttachment: attachment]; 101 | [cell setDeleteCallback: ^{ 102 | NSUInteger index = [attachments indexOfObject: attachment]; 103 | [[self delegate] attachmentsView:self confirmRemoveAttachmentAtIndex: index]; 104 | }]; 105 | 106 | return cell; 107 | } 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Inbox App Scaffold - iOS 2 | ====== 3 | 4 | ## **NOTE**: This scaffold is currently not actively maintained, and may need some TLC. Please feel free to use it and send us a pull request if you fix anything or add a feature, though. :) 5 | 6 | The Inbox iOS App Scaffold is a full-featured mail client built on top of the Inbox API. It leverages the SQLite cache and model layer of the [Inbox iOS SDK](https://github.com/inboxapp/inbox-ios)'s, and adds the pre-packaged views and controllers you need to build a first-class mail experience. We've created a polished composer with tokenizing recipient fields, collection view and table view cells for displaying mail content, and more. Start your next project with the Inbox App Scaffold and focus on creating delightful interactions! 7 | 8 | 9 | 10 | 11 | 12 | 13 | ## Features 14 | 15 | - Thread List: View threads in a table view with support for popular interactions like pull-to-refresh, swipe-to-archive, and infinite scrolling. 16 | - Thread Detail: View messages in a collection view with mobile-optimized HTML message bodies, gravatar support, and draft actions. 17 | - Composer: Create, edit, and send drafts with a beautiful Gmail-style composer. Includes tokenizing recipient fields powered by UICollectionView, autocompletion with the Inbox Contacts API, and support for uploading attachments with progress indicators. 18 | - Tags: Switch tags from the sidebar. Browse your inbox or view built-in tags like Archive or Flagged and custom tags created via Inbox. 19 | - Offline Access: The app scaffold is backed by the iOS SDK's SQLite cache and automatically queues actions like archiving and sending for completion when internet is available, even complex chained interactions like creating a draft, adding an attachment, and then sending it. 20 | 21 | 22 | ## By Developers, For Developers 23 | 24 | The Inbox App Scaffold is intended for developers - to use it, you need an Inbox Developer Program membership or a copy of the open-source [Inbox Sync Engine](http://github.com/inboxapp/inbox). When you download or fork the Inbox App Scaffold, you'll need to add your Inbox App ID before you can connect your account. 25 | 26 | ### Environment Setup 27 | 28 | The Inbox iOS App Scaffold uses Cocoapods, a dependency management system for iOS apps similiar to npm and rpm. To set up your local development environment, you'll need to install cocoapods and do a pod install. 29 | 30 | 1. `sudo gem install cocoapods` 31 | 32 | 2. `cd ` 33 | 34 | 3. `pod install`. After Cocoapods has installed dependencies, open the project's .xcworkspace. 35 | 36 | 4. Open the app's `Info.plist` file in Xcode. Before you can run the app and authenticate an account with Inbox, you need to create an App ID by signing in to your Inbox Developer Account and creating a new application. 37 | - Fill in the `INAppID` with a valid App ID. 38 | - Update the URL Scheme for the `inbox-api` URL Type to be `in-` 39 | 40 | 41 | ### Communication 42 | 43 | - If you need help, use **Stack Overflow**. (Tag 'inbox-ios') 44 | - If you'd like to ask a general question, use **Stack Overflow**. 45 | - If you **found a bug**, open an issue. 46 | - If you **have a feature request**, open an issue. 47 | - If you **want to contribute**, submit a pull request. 48 | 49 | ### Tips 50 | 51 | 1. If you're extending the functionality of the Inbox iOS SDK while developing your application, you may want to check out the Inbox-iOS repository and update the app scaffold `Podfile` to point to your local copy of the SDK. We welcome pull requests against the Inbox iOS SDK repository as well as the app scaffold! 52 | 53 | ```ruby 54 | target "Inbox" do 55 | pod 'InboxKit', :path => "../Inbox-iOS" 56 | end 57 | ``` 58 | 59 | 2. If you're debugging network interaction, check out [the Charles HTTP Proxy](http://www.charlesproxy.com/). You can configure it to [intercept SSL requests](http://www.charlesproxy.com/documentation/faqs/ssl-connections-from-within-iphone-applications/) with the iOS Simulator. 60 | 61 | 3. If you're debugging view issues, you can save time (and get bonus points) with the [Spark Inspector](http://sparkinspector.com/), developed by the Inbox iOS lead! 62 | -------------------------------------------------------------------------------- /BigSur/Controllers/INSidebarViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 41 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /BigSur/INPluginManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // INPluginManager.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 6/5/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INPluginManager.h" 10 | 11 | @implementation INThread (PluginSupport) 12 | 13 | - (NSArray*)messages 14 | { 15 | NSMutableArray * msgs = [NSMutableArray array]; 16 | for (NSString * ID in self.messageIDs) 17 | [msgs addObject: [INMessage instanceWithID:ID inNamespaceID:self.namespaceID]]; 18 | return msgs; 19 | } 20 | 21 | @end 22 | 23 | @implementation INMessage (PluginSupport) 24 | 25 | @end 26 | 27 | 28 | @implementation INPluginManager 29 | 30 | + (INPluginManager *)shared 31 | { 32 | static INPluginManager * sharedManager = nil; 33 | static dispatch_once_t onceToken; 34 | 35 | dispatch_once(&onceToken, ^{ 36 | sharedManager = [[INPluginManager alloc] init]; 37 | }); 38 | return sharedManager; 39 | } 40 | 41 | - (id)init 42 | { 43 | self = [super init]; 44 | if (self) { 45 | _pluginBackgroundQueue = dispatch_queue_create("Plugin Processing", NULL); 46 | _pluginContexts = [[NSMutableDictionary alloc] init]; 47 | [self discoverPlugins]; 48 | } 49 | return self; 50 | } 51 | 52 | - (NSURL*)webViewBaseURL 53 | { 54 | return [NSURL fileURLWithPath: _pluginCompiledResources]; 55 | } 56 | 57 | - (void)discoverPlugins 58 | { 59 | _pluginNamesByRole = [[NSMutableDictionary alloc] init]; 60 | _pluginCompiledResources = [[NSString stringWithFormat:@"~/Documents/Web_Plugin_Root"] stringByExpandingTildeInPath]; 61 | [[NSFileManager defaultManager] removeItemAtPath:_pluginCompiledResources error:NULL]; 62 | [[NSFileManager defaultManager] createDirectoryAtPath:_pluginCompiledResources withIntermediateDirectories:NO attributes:NULL error:NULL]; 63 | 64 | NSArray * pluginPaths = [[NSBundle mainBundle] pathsForResourcesOfType:@"plugin" inDirectory:@"plugins"]; 65 | for (NSString * path in pluginPaths) { 66 | NSString * pluginName = [[path lastPathComponent] stringByDeletingPathExtension]; 67 | NSString * pluginInfoPath = [path stringByAppendingPathComponent:@"package.json"]; 68 | NSDictionary * pluginInfo = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:pluginInfoPath] options:0 error:NULL]; 69 | 70 | for (NSString * pluginRole in pluginInfo[@"roles"]) { 71 | NSMutableArray * roles = _pluginNamesByRole[pluginRole]; 72 | if (!roles) roles = [NSMutableArray array]; 73 | [roles addObject: pluginName]; 74 | _pluginNamesByRole[pluginRole] = roles; 75 | } 76 | 77 | NSString * resourcesPath = [path stringByAppendingPathComponent: @"resources"]; 78 | for (NSString * resourceName in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:resourcesPath error:NULL]) { 79 | NSString * src = [resourcesPath stringByAppendingPathComponent: resourceName]; 80 | NSString * dst = [_pluginCompiledResources stringByAppendingPathComponent: resourceName]; 81 | [[NSFileManager defaultManager] copyItemAtPath:src toPath:dst error:NULL]; 82 | } 83 | } 84 | } 85 | 86 | - (JSContext*)contextForPluginWithName:(NSString*)name 87 | { 88 | if (_pluginContexts[name]) 89 | return _pluginContexts[name]; 90 | 91 | NSString * bundlePath = [[NSBundle mainBundle] pathForResource:name ofType:@"plugin" inDirectory:@"plugins"]; 92 | NSBundle * pluginBundle = [NSBundle bundleWithPath: bundlePath]; 93 | 94 | if (!pluginBundle) 95 | return nil; 96 | 97 | NSString * baseJSPath = [[NSBundle mainBundle] pathForResource:@"compiled" ofType:@"js" inDirectory:@"plugins/base"]; 98 | NSString * baseJS = [NSString stringWithContentsOfFile:baseJSPath encoding:NSUTF8StringEncoding error:NULL]; 99 | 100 | NSString * pluginJSPath = [pluginBundle pathForResource:@"plugin" ofType:@"js"]; 101 | NSString * pluginJS = [NSString stringWithContentsOfFile:pluginJSPath encoding:NSUTF8StringEncoding error:NULL]; 102 | 103 | JSContext * context = [[JSContext alloc] initWithVirtualMachine: [[JSVirtualMachine alloc] init]]; 104 | context.exceptionHandler = ^(JSContext *context, JSValue *exception) { 105 | context.exception = exception; 106 | NSLog(@"%@", exception); 107 | }; 108 | 109 | context[@"app"] = self; 110 | [context evaluateScript: @"plugin = {}"]; 111 | 112 | [context evaluateScript: baseJS]; 113 | if (context.exception) { 114 | NSLog(@"Plugin %@ not loaded due to exception reading compiled.js base code.", name); 115 | context.exception = nil; 116 | return nil; 117 | } 118 | [context evaluateScript: pluginJS]; 119 | if (context.exception) { 120 | NSLog(@"Plugin %@ not loaded due to exception reading plugin.js", name); 121 | context.exception = nil; 122 | return nil; 123 | } 124 | 125 | [_pluginContexts setObject:context forKey:name]; 126 | return context; 127 | } 128 | 129 | - (NSArray*)pluginNamesForRole:(NSString*)role 130 | { 131 | return _pluginNamesByRole[role]; 132 | } 133 | 134 | #pragma Exposed Methods 135 | 136 | - (void)alert:(NSString*)alert 137 | { 138 | [[[UIAlertView alloc] initWithTitle:@"Plugin" message:alert delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; 139 | } 140 | 141 | - (void)log:(NSString*)msg 142 | { 143 | NSLog(@"%@", msg); 144 | } 145 | 146 | - (void)openURL:(NSString*)url 147 | { 148 | [[UIApplication sharedApplication] openURL: [NSURL URLWithString: url]]; 149 | } 150 | 151 | - (void)getJSON:(NSString*)url withCallback:(JSValue*)block 152 | { 153 | AFHTTPRequestOperationManager * manager = [[AFHTTPRequestOperationManager alloc] init]; 154 | AFHTTPRequestOperation * op = [manager GET:url parameters:NULL success:^(AFHTTPRequestOperation *operation, id responseObject) { 155 | [block callWithArguments: @[responseObject]]; 156 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 157 | [block callWithArguments: @[[NSNull null], error]]; 158 | }]; 159 | [op setResponseSerializer: [[AFJSONResponseSerializer alloc] init]]; 160 | 161 | } 162 | 163 | @end 164 | -------------------------------------------------------------------------------- /BigSur/Controllers/INSplitViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // INSplitViewController.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/30/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INSplitViewController.h" 10 | #import "UIView+FrameAdditions.h" 11 | 12 | #define NAV_HEIGHT (44 + 20) 13 | 14 | @implementation INSplitViewController 15 | 16 | - (id)init 17 | { 18 | self = [super init]; 19 | if (self) { 20 | _paneViewControllers = [NSMutableArray array]; 21 | _paneNavigationBars = [NSMutableArray array]; 22 | _paneShadows = [NSMutableArray array]; 23 | } 24 | return self; 25 | } 26 | 27 | - (void)viewDidLoad 28 | { 29 | [super viewDidLoad]; 30 | } 31 | 32 | - (void)didReceiveMemoryWarning 33 | { 34 | [super didReceiveMemoryWarning]; 35 | } 36 | 37 | - (void)setViewControllers:(NSArray*)controllers 38 | { 39 | for (UIViewController * controller in controllers) 40 | [self pushViewController: controller animated: NO]; 41 | } 42 | 43 | - (void)pushViewController:(UIViewController*)controller animated:(BOOL)animated 44 | { 45 | NSAssert(([_paneViewControllers count] < 3), @"More than three VCs not currently supported."); 46 | 47 | UINavigationBar * navBar = [[UINavigationBar alloc] initWithFrame: CGRectZero]; 48 | [navBar setClipsToBounds: NO]; 49 | if (controller.navigationItem) { 50 | [navBar pushNavigationItem:controller.navigationItem animated:NO]; 51 | } else { 52 | [navBar setHidden: YES]; 53 | } 54 | [_paneNavigationBars addObject: navBar]; 55 | [self.view addSubview: navBar]; 56 | 57 | [self addChildViewController: controller]; 58 | [self.view addSubview: controller.view]; 59 | [_paneViewControllers addObject: controller]; 60 | 61 | UIView * shadow = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 1024)]; 62 | UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(popPane)]; 63 | [shadow setAlpha: 0]; 64 | [shadow setBackgroundColor: [UIColor colorWithWhite:0 alpha:0.15]]; 65 | [shadow addGestureRecognizer: tap]; 66 | [shadow setUserInteractionEnabled: YES]; 67 | UIImageView * shadowImage = [[UIImageView alloc] initWithFrame: CGRectMake(1024-20, 0, 20, 1024)]; 68 | [shadowImage setImage: [UIImage imageNamed: @"frontViewControllerDropShadow.png"]]; 69 | [shadow addSubview: shadowImage]; 70 | [self.view addSubview: shadow]; 71 | [_paneShadows addObject: shadow]; 72 | 73 | NSArray * paneWidths = [self paneWidths]; 74 | float width = [[paneWidths lastObject] floatValue]; 75 | float x = self.view.bounds.size.width; 76 | [navBar setFrame: CGRectMake(x, 0, width, NAV_HEIGHT)]; 77 | [controller.view setFrame: CGRectMake(x, NAV_HEIGHT, width, self.view.bounds.size.height - NAV_HEIGHT)]; 78 | [shadow in_setFrameX: x - shadow.frame.size.width]; 79 | 80 | if (animated) { 81 | [UIView animateWithDuration:0.3 animations:^{ 82 | [UIView setAnimationCurve: UIViewAnimationCurveEaseOut]; 83 | [self recomputeLayout]; 84 | }]; 85 | } else { 86 | [self recomputeLayout]; 87 | } 88 | } 89 | 90 | - (void)popPane 91 | { 92 | [self popPane: YES]; 93 | } 94 | 95 | - (void)popPane:(BOOL)animated 96 | { 97 | UIViewController * last = [_paneViewControllers lastObject]; 98 | UINavigationBar * lastBar = [_paneNavigationBars lastObject]; 99 | UINavigationBar * lastShadow = [_paneShadows lastObject]; 100 | 101 | [_paneViewControllers removeLastObject]; 102 | [_paneNavigationBars removeLastObject]; 103 | [_paneShadows removeLastObject]; 104 | 105 | VoidBlock completion = ^{ 106 | [last.view removeFromSuperview]; 107 | [last removeFromParentViewController]; 108 | [lastBar removeFromSuperview]; 109 | [lastShadow removeFromSuperview]; 110 | }; 111 | 112 | if (animated) { 113 | [UIView animateWithDuration:0.3 animations:^{ 114 | [UIView setAnimationCurve: UIViewAnimationCurveEaseOut]; 115 | [self recomputeLayout]; 116 | [last.view in_setFrameX: self.view.bounds.size.width]; 117 | [lastBar in_setFrameX: self.view.bounds.size.width]; 118 | [lastShadow in_setFrameX: self.view.bounds.size.width - lastShadow.frame.size.width]; 119 | [lastShadow setAlpha: 0]; 120 | 121 | } completion:^(BOOL finished) { 122 | completion(); 123 | }]; 124 | } else { 125 | completion(); 126 | } 127 | } 128 | 129 | - (NSArray*)paneWidths 130 | { 131 | NSUInteger count = [_paneViewControllers count]; 132 | if (count == 2) 133 | return @[@(256), @(512)]; 134 | if (count == 3) 135 | return @[@(256), @(512), @(452)]; 136 | 137 | return @[@(self.view.bounds.size.width)]; 138 | } 139 | 140 | - (BOOL)paneOccluded:(int)index 141 | { 142 | if (index >= [_paneViewControllers count] - 1) 143 | return NO; 144 | 145 | UIViewController * pane = [_paneViewControllers objectAtIndex: index]; 146 | UIViewController * next = [_paneViewControllers objectAtIndex: index + 1]; 147 | return (next.view.frame.origin.x < (pane.view.frame.origin.x + pane.view.frame.size.width)); 148 | } 149 | 150 | - (void)recomputeLayout 151 | { 152 | NSArray * paneWidths = [self paneWidths]; 153 | float paneCount = [_paneViewControllers count]; 154 | float lastWidth = [[paneWidths lastObject] floatValue]; 155 | BOOL lastOccluded = NO; 156 | float x = 0; 157 | 158 | for (int ii = 0; ii < paneCount; ii ++) { 159 | UIViewController * pane = [_paneViewControllers objectAtIndex: ii]; 160 | UINavigationBar * navbar = [_paneNavigationBars objectAtIndex: ii]; 161 | UIImageView * shadow = [_paneShadows objectAtIndex: ii]; 162 | 163 | float w = [[paneWidths objectAtIndex: ii] floatValue]; 164 | float y = ([navbar isHidden] ? 0 : NAV_HEIGHT); 165 | [navbar setFrame:CGRectMake(x, 0, w, NAV_HEIGHT)]; 166 | [[pane view] setFrame: CGRectMake(x, y, w, self.view.bounds.size.height-y)]; 167 | [shadow in_setFrameX: x - [shadow frame].size.width]; 168 | [shadow setAlpha: (lastOccluded ? 1.0 : 0.0)]; 169 | 170 | float visibleW = fminf(w, ((self.view.bounds.size.width - lastWidth - x) / ([_paneViewControllers count] - 2))); 171 | if ((paneCount == 3) && (ii == 0)) { 172 | visibleW = 60; 173 | } 174 | x += visibleW; 175 | lastOccluded = (w != visibleW); 176 | } 177 | } 178 | 179 | - (void)viewDidLayoutSubviews 180 | { 181 | [self recomputeLayout]; 182 | } 183 | 184 | - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation 185 | { 186 | [self recomputeLayout]; 187 | } 188 | 189 | @end 190 | -------------------------------------------------------------------------------- /BigSur/Views/INMessageContentView.m: -------------------------------------------------------------------------------- 1 | // 2 | // INMessageContentView.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 5/23/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INMessageContentView.h" 10 | #import "UIView+FrameAdditions.h" 11 | #import "INPluginManager.h" 12 | 13 | static NSString * messageCSS = @"\ 14 | html, body {\ 15 | font-family: sans-serif;\ 16 | font-size:0.9em;\ 17 | margin-top:%dpx;\ 18 | margin-left:%dpx;\ 19 | margin-bottom:%dpx;\ 20 | margin-right:%dpx;\ 21 | border:0;\ 22 | width:%dpx;\ 23 | -webkit-text-size-adjust: auto;\ 24 | word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;\ 25 | }\ 26 | a {\ 27 | color:rgb(%d,%d,%d);\ 28 | }\ 29 | div {\ 30 | max-width:100%%;\ 31 | }\ 32 | .gmail_extra {\ 33 | display:none;\ 34 | }\ 35 | blockquote, .gmail_quote {\ 36 | display:none;\ 37 | }\ 38 | img {\ 39 | max-width: 100%;\ 40 | height:auto;\ 41 | }"; 42 | 43 | @implementation INMessageContentView 44 | 45 | - (id)initWithFrame:(CGRect)frame 46 | { 47 | self = [super initWithFrame:frame]; 48 | if (self) { 49 | // Initialization code 50 | } 51 | return self; 52 | } 53 | 54 | - (void)awakeFromNib 55 | { 56 | [super awakeFromNib]; 57 | [self setup]; 58 | } 59 | 60 | - (void)setup 61 | { 62 | [self setClipsToBounds: YES]; 63 | if (!_tintColor) 64 | [self setTintColor: [UIColor blueColor]]; 65 | } 66 | 67 | - (void)layoutSubviews 68 | { 69 | [super layoutSubviews]; 70 | if (_contentLoadCompleted) { 71 | [_textView setFrame: self.bounds]; 72 | [_webView setFrame: self.bounds]; 73 | } 74 | } 75 | 76 | - (void)setFrame:(CGRect)frame 77 | { 78 | BOOL viewportWidthChange = (frame.size.width != self.frame.size.width); 79 | [super setFrame: frame]; 80 | if (viewportWidthChange) 81 | [self setContent: _content]; 82 | } 83 | 84 | - (void)clearContent 85 | { 86 | [_webView removeFromSuperview]; 87 | [_webView setDelegate: nil]; 88 | _webView = nil; 89 | 90 | [_textView setText: @""]; 91 | 92 | _contentLoadCompleted = NO; 93 | } 94 | 95 | - (void)setContent:(NSString*)content 96 | { 97 | _content = content; 98 | _contentLoadCompleted = NO; 99 | 100 | if ([content rangeOfString:@"<[^<]+>" options:NSRegularExpressionSearch].location != NSNotFound) 101 | [self setContentWebView: content]; 102 | else 103 | [self setContentTextView: content]; 104 | } 105 | 106 | - (void)setContentMargin:(UIEdgeInsets)margin 107 | { 108 | _contentMargin = margin; 109 | } 110 | 111 | - (void)setContentWebView:(NSString*)content 112 | { 113 | [_textView removeFromSuperview]; 114 | _textView = nil; 115 | 116 | if (!_webView) { 117 | /* Note: It's important the web view has a small initial height because it always 118 | reports it's rendered content size to be at least it's height. We'll make it the 119 | appropriate size once it's content loads. This height must be > 0 or it won't load 120 | at all. */ 121 | _webView = [[UIWebView alloc] initWithFrame: CGRectMake(0, 0, self.frame.size.width, 5)]; 122 | [_webView setDelegate: self]; 123 | [_webView setTintColor: _tintColor]; 124 | [_webView setScalesPageToFit: YES]; 125 | [_webView setDataDetectorTypes: UIDataDetectorTypeAll]; 126 | [_webView setBackgroundColor:[UIColor whiteColor]]; 127 | [[_webView scrollView] setScrollEnabled: NO]; 128 | [[_webView scrollView] setBackgroundColor:[UIColor whiteColor]]; 129 | [self addSubview: _webView]; 130 | } 131 | 132 | float s = 1.0 / [[UIScreen mainScreen] scale]; 133 | int viewportWidth = self.frame.size.width - (_contentMargin.left + _contentMargin.right); 134 | 135 | const CGFloat * components = CGColorGetComponents([[self tintColor] CGColor]); 136 | int tintR = (int)(components[0] * 256); 137 | int tintG = (int)(components[1] * 256); 138 | int tintB = (int)(components[2] * 256); 139 | 140 | 141 | NSString * css = [NSString stringWithFormat: messageCSS, (int)(_contentMargin.top * s), (int)(_contentMargin.left * s), (int)(_contentMargin.bottom * s), (int)(_contentMargin.right * s), viewportWidth, tintR, tintG, tintB]; 142 | NSString * html = [NSString stringWithFormat: @"\n%@", css, viewportWidth, content]; 143 | [html writeToFile:[@"~/Documents/test_email.html" stringByExpandingTildeInPath] atomically:NO encoding:NSUTF8StringEncoding error:nil]; 144 | 145 | [_webView setAlpha: 0.01]; 146 | [_webView loadHTMLString:html baseURL: _contentBaseURL]; 147 | } 148 | 149 | - (void)setContentTextView:(NSString*)content 150 | { 151 | [_webView removeFromSuperview]; 152 | [_webView setDelegate: nil]; 153 | _webView = nil; 154 | 155 | if (!_textView) { 156 | _textView = [[UITextView alloc] initWithFrame: CGRectMake(0, 0, self.bounds.size.width, 1000)]; 157 | [_textView setEditable: NO]; 158 | [_textView setDataDetectorTypes: UIDataDetectorTypeAll]; 159 | [_textView setTintColor: _tintColor]; 160 | [_textView setFont: [UIFont systemFontOfSize: 13]]; 161 | [_textView setTextContainerInset: _contentMargin]; 162 | [_textView setScrollEnabled: NO]; 163 | [self addSubview: _textView]; 164 | } 165 | 166 | [_textView setText: content]; 167 | CGSize size = [_textView sizeThatFits: CGSizeMake(self.bounds.size.width, MAXFLOAT)]; 168 | [_textView setFrame: CGRectMake(0, 0, size.width, size.height)]; 169 | 170 | _contentLoadCompleted = YES; 171 | if ([self.delegate respondsToSelector: @selector(messageContentViewSizeDetermined:)]) 172 | [self.delegate messageContentViewSizeDetermined: size]; 173 | } 174 | 175 | - (void)webViewDidFinishLoad:(UIWebView *)webView 176 | { 177 | CGSize s = _webView.scrollView.contentSize; 178 | [_webView in_setFrameHeight: s.height]; 179 | [_webView setAlpha: 1]; 180 | 181 | _contentLoadCompleted = YES; 182 | 183 | if ([self.delegate respondsToSelector: @selector(messageContentViewSizeDetermined:)]) { 184 | [self.delegate messageContentViewSizeDetermined: s]; 185 | } 186 | } 187 | 188 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 189 | { 190 | if ((navigationType == UIWebViewNavigationTypeOther) || (navigationType == UIWebViewNavigationTypeReload)) 191 | return YES; 192 | 193 | [[UIApplication sharedApplication] openURL: [request URL]]; 194 | return NO; 195 | } 196 | 197 | @end 198 | --------------------------------------------------------------------------------