├── arclib ├── __phutil_library_init__.php ├── src │ ├── .phutil_module_cache │ ├── InboxUncrustifyLintEngine.php │ └── InboxUncrustifyLinter.php ├── .phutil_module_cache └── __phutil_library_map__.php ├── InboxFramework ├── Inbox │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── Models │ │ ├── API Calls │ │ │ ├── INSaveTagTask.h │ │ │ ├── INSaveDraftTask.h │ │ │ ├── INDeleteDraftTask.h │ │ │ ├── INSendDraftTask.h │ │ │ ├── INMarkMessageAsReadTask.h │ │ │ ├── INUploadFileTask.h │ │ │ ├── INAddRemoveTagsTask.h │ │ │ ├── INMarkMessageAsReadTask.m │ │ │ ├── INAPITask.h │ │ │ ├── INSendDraftTask.m │ │ │ ├── INSaveTagTask.m │ │ │ ├── INUploadFileTask.m │ │ │ ├── INDeleteDraftTask.m │ │ │ ├── INSaveDraftTask.m │ │ │ └── INAddRemoveTagsTask.m │ │ ├── Concrete Classes │ │ │ ├── INThread+Private.h │ │ │ ├── INContact.h │ │ │ ├── INThread+Private.m │ │ │ ├── INContact.m │ │ │ ├── INNamespace.m │ │ │ ├── INTag.m │ │ │ ├── INTag.h │ │ │ ├── INMessage.h │ │ │ ├── INFile.h │ │ │ ├── INMessage.m │ │ │ ├── INNamespace.h │ │ │ ├── INFile.m │ │ │ ├── INThread.m │ │ │ ├── INDraft.h │ │ │ ├── INDraft.m │ │ │ └── INThread.h │ │ ├── INModelProvider+Private.h │ │ ├── INMessageProvider.h │ │ ├── INThreadProvider.h │ │ ├── INMessageProvider.m │ │ ├── INThreadProvider.m │ │ ├── INModelObject+Uniquing.h │ │ └── INModelObject+Uniquing.m │ ├── Categories │ │ ├── FMDatabase+INModelQueries.h │ │ ├── FMDatabase+INModelQueries.m │ │ ├── NSPredicate+Inspection.h │ │ ├── NSDictionary+FormatConversion.h │ │ ├── FMResultSet+INModelQueries.h │ │ ├── NSError+InboxErrors.h │ │ ├── NSObject+AssociatedObjects.h │ │ ├── NSString+FormatConversion.h │ │ ├── NSPredicate+Inspection.m │ │ ├── NSError+InboxErrors.m │ │ ├── FMResultSet+INModelQueries.m │ │ ├── NSObject+AssociatedObjects.m │ │ ├── NSDictionary+FormatConversion.m │ │ ├── NSString+FormatConversion.m │ │ └── NSObject+Properties.h │ ├── INSyncEngine.h │ ├── Inbox-Prefix.pch │ ├── Helpers │ │ ├── INPredicateToQueryParamConverter.h │ │ ├── INPredicateToSQLConverter.h │ │ ├── INModelResponseSerializer.h │ │ ├── INPredicateToQueryParamConverter.m │ │ └── INModelResponseSerializer.m │ ├── Info.plist │ ├── Inbox-Info.plist │ └── Inbox.h ├── InboxTests │ ├── en.lproj │ │ └── InfoPlist.strings │ └── InboxTests-Info.plist ├── InboxFramework.xcodeproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── InboxFramework.xccheckout ├── Documentation │ └── index-template.txt ├── InboxUI │ ├── Categories │ │ ├── UIView+FrameAdditions.h │ │ └── UIView+FrameAdditions.m │ └── Views │ │ ├── INRecipientsLabel.h │ │ └── INMessageContentView.h └── Vendor │ └── PDKeychainBindings │ ├── PDKeychainBindings.h │ ├── PDKeychainBindingsController.h │ └── PDKeychainBindings.m ├── InboxExamples ├── Triage │ ├── Triage │ │ ├── en.lproj │ │ │ └── InfoPlist.strings │ │ ├── TRMessageActionButton.h │ │ ├── main.m │ │ ├── TRAppDelegate.h │ │ ├── Triage-Prefix.pch │ │ ├── INPlaceholderTextView.h │ │ ├── TRMessageCardView.h │ │ ├── TRViewController.h │ │ ├── TRMessageActionButton.m │ │ ├── Info.plist │ │ ├── TRAppDelegate.m │ │ └── INPlaceholderTextView.m │ ├── Documentation │ │ └── demo.mov │ ├── Podfile │ ├── Triage.xcworkspace │ │ └── contents.xcworkspacedata │ └── Triage.xcodeproj │ │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ └── bengotow.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── Snapmail │ ├── Snapmail │ │ ├── en.lproj │ │ │ └── InfoPlist.strings │ │ ├── Images.xcassets │ │ │ ├── snap-read.imageset │ │ │ │ ├── snap-read@2x.png │ │ │ │ └── Contents.json │ │ │ ├── snap-sent.imageset │ │ │ │ ├── snap-sent@2x.png │ │ │ │ └── Contents.json │ │ │ ├── send-button.imageset │ │ │ │ ├── send-button@2x.png │ │ │ │ └── Contents.json │ │ │ ├── snap-unread.imageset │ │ │ │ ├── snap-unread@2x.png │ │ │ │ └── Contents.json │ │ │ ├── snap-sending.imageset │ │ │ │ ├── snap-sending@2x.png │ │ │ │ └── Contents.json │ │ │ ├── capture-button.imageset │ │ │ │ ├── capture-button@2x.png │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.launchimage │ │ │ │ └── Contents.json │ │ ├── main.m │ │ ├── Snapmail-Prefix.pch │ │ ├── INAppDelegate.h │ │ ├── INCaptureViewController.h │ │ ├── INSnapViewController.h │ │ ├── INContactsViewController.h │ │ ├── INHomeViewController.h │ │ ├── Snapmail-Info.plist │ │ ├── INContactsViewController.xib │ │ ├── INSnapViewController.m │ │ ├── INAppDelegate.m │ │ ├── INCaptureViewController.m │ │ ├── INHomeViewController.xib │ │ ├── INCaptureViewController.xib │ │ ├── INContactsViewController.m │ │ └── INSnapViewController.xib │ ├── Podfile │ ├── Snapmail.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── bengotow.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── Snapmail.xcodeproj │ │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── SimpleMail │ ├── SimpleMail │ │ ├── en.lproj │ │ │ └── InfoPlist.strings │ │ ├── Images.xcassets │ │ │ ├── unread_dot.imageset │ │ │ │ ├── unread_dot.png │ │ │ │ ├── unread_dot@2x.png │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.launchimage │ │ │ │ └── Contents.json │ │ ├── SMAppDelegate.h │ │ ├── main.m │ │ ├── SMInboxTableViewController.h │ │ ├── SimpleMail-Prefix.pch │ │ ├── SMThreadTableViewCell.h │ │ ├── SMThreadTableViewCell.m │ │ ├── SimpleMail-Info.plist │ │ └── SMAppDelegate.m │ ├── Podfile │ ├── SimpleMail.xcworkspace │ │ └── contents.xcworkspacedata │ └── SimpleMail.xcodeproj │ │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── SwiftEightBall │ ├── screenshot.png │ ├── SwiftEightBall │ ├── Images.xcassets │ │ ├── unread_dot.imageset │ │ │ ├── unread_dot.png │ │ │ ├── unread_dot@2x.png │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── Info.plist │ └── AppDelegate.swift │ ├── SwiftEightBall.xcodeproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── SwiftEightBall.xcworkspace │ └── contents.xcworkspacedata ├── .arcconfig ├── .gitmodules ├── LICENSE.md ├── .gitignore └── InboxKit.podspec /arclib/__phutil_library_init__.php: -------------------------------------------------------------------------------- 1 | "../../" 7 | end 8 | 9 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-read.imageset/snap-read@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-read.imageset/snap-read@2x.png -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-sent.imageset/snap-sent@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-sent.imageset/snap-sent@2x.png -------------------------------------------------------------------------------- /InboxExamples/Triage/Podfile: -------------------------------------------------------------------------------- 1 | 2 | # Uncomment this line to define a global platform for your project 3 | # platform :ios, "6.0" 4 | 5 | target "Triage" do 6 | pod 'InboxKit', :path => "../../" 7 | end 8 | 9 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/Podfile: -------------------------------------------------------------------------------- 1 | 2 | # Uncomment this line to define a global platform for your project 3 | # platform :ios, "6.0" 4 | 5 | target "SimpleMail" do 6 | pod 'InboxKit', :path => "../../" 7 | end 8 | 9 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/Images.xcassets/unread_dot.imageset/unread_dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/SimpleMail/SimpleMail/Images.xcassets/unread_dot.imageset/unread_dot.png -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/send-button.imageset/send-button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/Snapmail/Snapmail/Images.xcassets/send-button.imageset/send-button@2x.png -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-unread.imageset/snap-unread@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-unread.imageset/snap-unread@2x.png -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/Images.xcassets/unread_dot.imageset/unread_dot@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/SimpleMail/SimpleMail/Images.xcassets/unread_dot.imageset/unread_dot@2x.png -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-sending.imageset/snap-sending@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-sending.imageset/snap-sending@2x.png -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/capture-button.imageset/capture-button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/Snapmail/Snapmail/Images.xcassets/capture-button.imageset/capture-button@2x.png -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /InboxExamples/SwiftEightBall/SwiftEightBall/Images.xcassets/unread_dot.imageset/unread_dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/SwiftEightBall/SwiftEightBall/Images.xcassets/unread_dot.imageset/unread_dot.png -------------------------------------------------------------------------------- /InboxExamples/SwiftEightBall/SwiftEightBall/Images.xcassets/unread_dot.imageset/unread_dot@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/SwiftEightBall/SwiftEightBall/Images.xcassets/unread_dot.imageset/unread_dot@2x.png -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail.xcworkspace/xcuserdata/bengotow.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/Snapmail/Snapmail.xcworkspace/xcuserdata/bengotow.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /InboxFramework/InboxFramework.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /InboxExamples/SwiftEightBall/SwiftEightBall.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage.xcodeproj/project.xcworkspace/xcuserdata/bengotow.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nylas/inbox-ios/HEAD/InboxExamples/Triage/Triage.xcodeproj/project.xcworkspace/xcuserdata/bengotow.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /InboxFramework/Documentation/index-template.txt: -------------------------------------------------------------------------------- 1 | For an introduction to the Inbox iOS SDK and Getting Started guides, see the [Inbox iOS Documentation](http://inboxapp.com/docs/ios). For more information about Inbox's REST API, check out the [Inbox API Reference](inboxapp.com/docs/api). 2 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INSaveTagTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // INSaveTagTask.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 9/17/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INAPITask.h" 10 | 11 | @interface INSaveTagTask : INAPITask 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/send-button.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "2x", 6 | "filename" : "send-button@2x.png" 7 | } 8 | ], 9 | "info" : { 10 | "version" : 1, 11 | "author" : "xcode" 12 | } 13 | } -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INSaveDraftTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // INSaveDraftChange.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/16/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INAPITask.h" 10 | 11 | @interface INSaveDraftTask : INAPITask 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INDeleteDraftTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // INDeleteDraftChange.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/20/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INAPITask.h" 10 | 11 | @interface INDeleteDraftTask : INAPITask 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INSendDraftTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // INSendDraftChange.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/20/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INAddRemoveTagsTask.h" 10 | 11 | @interface INSendDraftTask : INAPITask 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Categories/FMDatabase+INModelQueries.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabase+FMDatabase_SchemaVersioning_h.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 "FMDatabase.h" 10 | 11 | @interface FMDatabase (INModelQueries) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INMarkMessageAsReadTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // INMarkMessageAsReadTask.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 6/17/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INAPITask.h" 10 | 11 | @interface INMarkMessageAsReadTask : INAPITask 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Categories/FMDatabase+INModelQueries.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabase+FMDatabase_SchemaVersioning_h.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 "FMDatabase+INModelQueries.h" 10 | 11 | @implementation FMDatabase (INModelQueries) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage/TRMessageActionButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // TRMessageActionButton.h 3 | // Triage 4 | // 5 | // Created by Ben Gotow on 5/8/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TRMessageActionButton : UIButton 12 | { 13 | UIView * _selectedView; 14 | } 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INUploadFileTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // INSaveAttachmentChange.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/21/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INAPITask.h" 10 | 11 | @interface INUploadFileTask : INAPITask 12 | 13 | - (NSMutableArray *)waitingDrafts; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /InboxExamples/SwiftEightBall/SwiftEightBall.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Categories/NSPredicate+Inspection.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSPredicate+Inspection.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 | @interface NSPredicate (Inspection) 12 | 13 | - (BOOL)containsOrMatches:(NSString*)expression; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/SMAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMAppDelegate.h 3 | // SimpleMail 4 | // 5 | // Created by Ben Gotow on 7/8/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-read.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "snap-read@2x.png" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-sent.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "snap-sent@2x.png" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-sending.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "snap-sending@2x.png" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/snap-unread.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "snap-unread@2x.png" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /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"]}}} -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Images.xcassets/capture-button.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "capture-button@2x.png" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /InboxFramework/Inbox/Categories/NSDictionary+FormatConversion.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+FormatConversion.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 | @interface NSDictionary (FormatConversion) 12 | 13 | - (id)objectForKey:(id)key asType:(NSString*)type; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INAddRemoveTagsTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // INAddRemoveTagsChange.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/16/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INAPITask.h" 10 | 11 | @interface INAddRemoveTagsTask : INAPITask 12 | 13 | - (NSMutableArray *)tagIDsToAdd; 14 | - (NSMutableArray *)tagIDsToRemove; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Categories/FMResultSet+INModelQueries.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMResultSet+INModelQueries.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 "FMResultSet.h" 10 | #import "INModelObject.h" 11 | 12 | @interface FMResultSet (INModelQueries) 13 | 14 | - (INModelObject *)nextModelOfClass:(Class)klass; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INThread+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // INThread+Private.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/22/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INThread.h" 10 | 11 | @interface INThread (Private) 12 | 13 | - (void)addDraftID:(NSString*)draftID; 14 | - (void)removeDraftID:(NSString*)draftID; 15 | 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SimpleMail 4 | // 5 | // Created by Ben Gotow on 7/8/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "SMAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([SMAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/main.m: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // main.m 4 | // Snapmail 5 | // 6 | // Created by Ben Gotow on 6/16/14. 7 | // Copyright (c) 2014 InboxApp, Inc. All rights reserved. 8 | // 9 | 10 | #import 11 | 12 | #import "INAppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) 15 | { 16 | @autoreleasepool { 17 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([INAppDelegate class])); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage/main.m: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // main.m 4 | // Snapmail 5 | // 6 | // Created by Ben Gotow on 6/16/14. 7 | // Copyright (c) 2014 InboxApp, Inc. All rights reserved. 8 | // 9 | 10 | #import 11 | 12 | #import "TRAppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) 15 | { 16 | @autoreleasepool { 17 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([TRAppDelegate class])); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/SMInboxTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMInboxTableViewController.h 3 | // SimpleMail 4 | // 5 | // Created by Ben Gotow on 7/8/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMInboxTableViewController : UITableViewController 12 | 13 | @property (nonatomic, strong) INThreadProvider * threadProvider; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/INSyncEngine.h: -------------------------------------------------------------------------------- 1 | // 2 | // INSyncEngine.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 | @protocol INSyncEngine 12 | 13 | - (BOOL)providesCompleteCacheOf:(Class)klass; 14 | 15 | /* Clear all sync state, usually called during the logout process. */ 16 | - (void)resetSyncState; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Snapmail-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 "Inbox.h" 17 | #endif 18 | -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage/TRAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // TRAppDelegate.h 3 | // Triage 4 | // 5 | // Created by Ben Gotow on 5/7/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TRViewController.h" 11 | 12 | @interface TRAppDelegate : UIResponder 13 | 14 | @property (strong, nonatomic) TRViewController * viewController; 15 | @property (strong, nonatomic) UIWindow *window; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/SimpleMail-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 "Inbox.h" 17 | #endif 18 | -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage/Triage-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 | 17 | #import "Inbox.h" 18 | #endif 19 | -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage/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 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/Images.xcassets/unread_dot.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal" 5 | }, 6 | { 7 | "idiom" : "universal", 8 | "scale" : "1x", 9 | "filename" : "unread_dot.png" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "scale" : "2x", 14 | "filename" : "unread_dot@2x.png" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/INAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // INAppDelegate.h 3 | // Snapmail 4 | // 5 | // Created by Ben Gotow on 6/16/14. 6 | // Copyright (c) 2014 InboxApp, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "INHomeViewController.h" 11 | 12 | @interface INAppDelegate : UIResponder 13 | 14 | @property (strong, nonatomic) UIWindow * window; 15 | @property (strong, nonatomic) INHomeViewController * viewController; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /InboxExamples/SwiftEightBall/SwiftEightBall/Images.xcassets/unread_dot.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal" 5 | }, 6 | { 7 | "idiom" : "universal", 8 | "scale" : "1x", 9 | "filename" : "unread_dot.png" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "scale" : "2x", 14 | "filename" : "unread_dot@2x.png" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /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"]}}} -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/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 | } -------------------------------------------------------------------------------- /InboxExamples/SwiftEightBall/SwiftEightBall/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 | } -------------------------------------------------------------------------------- /InboxFramework/Inbox/Categories/NSError+InboxErrors.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSError+InboxErrors.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/27/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSError (InboxErrors) 12 | 13 | + (NSError*)inboxErrorWithFormat:(NSString *)format, ...; 14 | + (NSError*)inboxErrorWithDescription:(NSString *)desc; 15 | + (NSError*)inboxErrorWithDescription:(NSString *)desc underlyingError:(NSError*)underlying; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Inbox-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 | #ifdef __OBJC__ 8 | 9 | #import 10 | 11 | #ifndef COCOAPODS 12 | #import "INDependencyNamespacing.h" 13 | #endif 14 | 15 | #import "AFNetworking.h" 16 | 17 | #import "FMDatabase.h" 18 | #import "FMDatabaseQueue.h" 19 | #import "FMResultSet.h" 20 | #import "FMResultSet+INModelQueries.h" 21 | #import "FMDatabaseAdditions.h" 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Helpers/INPredicateToQueryParamConverter.h: -------------------------------------------------------------------------------- 1 | // 2 | // INPredicateToQueryParamConverter.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 INPredicateToQueryParamConverter : NSObject 12 | 13 | @property (nonatomic, strong) NSDictionary * keysToParamsTable; 14 | @property (nonatomic, strong) NSDictionary * keysToLIKEParamsTable; 15 | 16 | - (NSDictionary*)paramsForPredicate:(NSPredicate*)predicate; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /InboxFramework/InboxUI/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 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/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 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/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 | } -------------------------------------------------------------------------------- /InboxExamples/SwiftEightBall/SwiftEightBall/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 | } -------------------------------------------------------------------------------- /InboxFramework/Inbox/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 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/INModelProvider+Private.h: -------------------------------------------------------------------------------- 1 | // 2 | // INModelView.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/24/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | @interface INModelProvider (Private) 10 | 11 | - (NSPredicate*)fetchPredicate; 12 | - (void)fetchFromCache; 13 | - (void)fetchFromAPI; 14 | - (NSDictionary *)queryParamsForPredicate:(NSPredicate*)predicate; 15 | 16 | #pragma mark Receiving Updates from the Database; 17 | 18 | - (void)managerDidPersistModels:(NSArray *)savedArray; 19 | - (void)managerDidUnpersistModels:(NSArray*)models; 20 | - (void)managerDidReset; 21 | 22 | @end -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/SMThreadTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMThreadTableViewCell.h 3 | // SimpleMail 4 | // 5 | // Created by Ben Gotow on 7/8/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMThreadTableViewCell : UITableViewCell 12 | 13 | @property (nonatomic, strong) IBOutlet UILabel * dateLabel; 14 | @property (nonatomic, strong) IBOutlet UILabel * fromLabel; 15 | @property (nonatomic, strong) IBOutlet UILabel * bodyLabel; 16 | @property (nonatomic, strong) IBOutlet UIImageView * unreadDot; 17 | 18 | - (void)setThread:(INThread*)thread; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /InboxFramework/InboxUI/Views/INRecipientsLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // INRecipientsLabel.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 INRecipientsLabel : UIView 12 | { 13 | NSMutableArray * _buttons; 14 | UIButton * _moreButton; 15 | } 16 | @property (nonatomic, strong) UIColor * textColor; 17 | @property (nonatomic, strong) UIFont * textFont; 18 | @property (nonatomic, assign) BOOL recipientsClickable; 19 | 20 | - (void)setPrefixString:(NSString*)prefix andRecipients:(NSArray*)recipients includeMe:(BOOL)includeMe; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "InboxFramework/Submodules/AFNetworking"] 2 | path = InboxFramework/Submodules/AFNetworking 3 | url = https://github.com/AFNetworking/AFNetworking.git 4 | [submodule "InboxFramework/Submodules/FMDB"] 5 | path = InboxFramework/Submodules/FMDB 6 | url = https://github.com/ccgus/fmdb.git 7 | [submodule "InboxFramework/Submodules/Kiwi"] 8 | path = InboxFramework/Submodules/Kiwi 9 | url = https://github.com/kiwi-bdd/Kiwi.git 10 | [submodule "InboxFramework/Submodules/PDKeychainBindingsController"] 11 | path = InboxFramework/Submodules/PDKeychainBindingsController 12 | url = https://github.com/carlbrown/PDKeychainBindingsController.git 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/INCaptureViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INCaptureViewController.h 3 | // Snapmail 4 | // 5 | // Created by Ben Gotow on 6/16/14. 6 | // Copyright (c) 2014 InboxApp, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface INCaptureViewController : UIViewController 13 | 14 | @property (nonatomic, strong) INThread * thread; 15 | @property (nonatomic, strong) UIImagePickerController * picker; 16 | @property (weak, nonatomic) IBOutlet UIButton *toggleSideButton; 17 | 18 | - (id)initWithThread:(INThread*)thread; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Helpers/INPredicateToSQLConverter.h: -------------------------------------------------------------------------------- 1 | // 2 | // INPredicateConverter.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/23/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INPredicateToSQLConverter : NSObject 12 | 13 | @property (nonatomic, strong) Class modelClass; 14 | @property (nonatomic, strong) NSMutableArray * additionalJoins; 15 | @property (nonatomic, strong) NSMutableArray * additionalJoinRHSExpressions; 16 | 17 | + (INPredicateToSQLConverter*)converterForModelClass:(Class)modelClass; 18 | 19 | - (NSString *)SQLForPredicate:(NSPredicate*)predicate; 20 | - (NSString *)SQLForSortDescriptors:(NSArray*)descriptors; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INContact.h: -------------------------------------------------------------------------------- 1 | // 2 | // INContact.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 "INModelObject.h" 10 | 11 | /** The INContact class provides a native wrapper around Inbox contacts 12 | http://inboxapp.com/docs/api#contacts 13 | */ 14 | @interface INContact : INModelObject 15 | 16 | @property (nonatomic, strong) NSString * name; 17 | @property (nonatomic, strong) NSString * email; 18 | @property (nonatomic, strong) NSString * source; 19 | @property (nonatomic, strong) NSString * providerName; 20 | @property (nonatomic, strong) NSString * accountID; 21 | @property (nonatomic, strong) NSString * UID; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INThread+Private.m: -------------------------------------------------------------------------------- 1 | // 2 | // INThread+Private.m 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/22/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INThread+Private.h" 10 | 11 | @implementation INThread (Private) 12 | 13 | - (void)removeDraftID:(NSString*)draftID 14 | { 15 | NSMutableArray * IDs = [[self draftIDs] mutableCopy]; 16 | [IDs removeObject: draftID]; 17 | [self setDraftIDs: IDs]; 18 | } 19 | 20 | - (void)addDraftID:(NSString*)draftID 21 | { 22 | NSMutableArray * IDs = [[self draftIDs] mutableCopy]; 23 | if (![IDs containsObject: draftID]) 24 | [IDs addObject: draftID]; 25 | [self setDraftIDs: IDs]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /InboxFramework/Vendor/PDKeychainBindings/PDKeychainBindings.h: -------------------------------------------------------------------------------- 1 | // 2 | // PDKeychainBindings.h 3 | // PDKeychainBindingsController 4 | // 5 | // Created by Carl Brown on 7/10/11. 6 | // Copyright 2011 PDAgent, LLC. Released under MIT License. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface PDKeychainBindings : NSObject { 13 | @private 14 | 15 | } 16 | 17 | + (PDKeychainBindings *)sharedKeychainBindings; 18 | 19 | - (id)objectForKey:(NSString *)defaultName; 20 | - (void)setObject:(NSString *)value forKey:(NSString *)defaultName; 21 | - (void)setString:(NSString *)value forKey:(NSString *)defaultName; 22 | - (void)removeObjectForKey:(NSString *)defaultName; 23 | 24 | - (NSString *)stringForKey:(NSString *)defaultName; 25 | @end 26 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/INSnapViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INSnapViewController.h 3 | // Snapmail 4 | // 5 | // Created by Ben Gotow on 6/16/14. 6 | // Copyright (c) 2014 InboxApp, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface INSnapViewController : UIViewController 12 | 13 | @property (nonatomic, weak) IBOutlet UIActivityIndicatorView * spinner; 14 | @property (nonatomic, weak) IBOutlet UIImageView * imageView; 15 | @property (nonatomic, weak) IBOutlet UILabel * errorLabel; 16 | @property (nonatomic, weak) IBOutlet UILabel * timeLabel; 17 | 18 | @property (nonatomic, strong) NSTimer * timer; 19 | @property (nonatomic, strong) INMessageProvider * provider; 20 | 21 | - (id)initWithThread:(INThread*)thread; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Categories/NSPredicate+Inspection.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSPredicate+Inspection.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 "NSPredicate+Inspection.h" 10 | 11 | @implementation NSPredicate (Inspection) 12 | 13 | - (BOOL)containsOrMatches:(NSString*)expression 14 | { 15 | NSMutableString * descriptions = [NSMutableString string]; 16 | if ([self isKindOfClass: [NSCompoundPredicate class]]) { 17 | for (NSPredicate * sub in [(NSCompoundPredicate*)self subpredicates]) 18 | [descriptions appendString: [sub description]]; 19 | } else { 20 | [descriptions appendString: [self description]]; 21 | } 22 | 23 | if ([descriptions rangeOfString: expression].location != NSNotFound) 24 | return YES; 25 | return NO; 26 | } 27 | @end 28 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/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)(NSArray * contacts); 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 | -------------------------------------------------------------------------------- /InboxFramework/InboxTests/InboxTests-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 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | INAPIPath 22 | http://localhost:5555/ 23 | 24 | 25 | -------------------------------------------------------------------------------- /InboxFramework/Vendor/PDKeychainBindings/PDKeychainBindingsController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PDKeychainBindingsController.h 3 | // PDKeychainBindingsController 4 | // 5 | // Created by Carl Brown on 7/10/11. 6 | // Copyright 2011 PDAgent, LLC. Released under MIT License. 7 | // 8 | 9 | #import 10 | #import "PDKeychainBindings.h" 11 | 12 | 13 | @interface PDKeychainBindingsController : NSObject { 14 | @private 15 | PDKeychainBindings *_keychainBindings; 16 | NSMutableDictionary *_valueBuffer; 17 | } 18 | 19 | + (PDKeychainBindingsController *)sharedKeychainBindingsController; 20 | - (PDKeychainBindings *) keychainBindings; 21 | 22 | - (id)values; // accessor object for PDKeychainBindings values. This property is observable using key-value observing. 23 | 24 | - (NSString*)stringForKey:(NSString*)key; 25 | - (BOOL)storeString:(NSString*)string forKey:(NSString*)key; 26 | 27 | @end 28 | 29 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage/TRMessageCardView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TRMessageCardView.h 3 | // Triage 4 | // 5 | // Created by Ben Gotow on 5/8/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "INPlaceholderTextView.h" 11 | 12 | @interface TRMessageCardView : UIView 13 | 14 | @property (nonatomic, strong) UIView * headersView; 15 | @property (nonatomic, strong) UILabel * subjectLabel; 16 | @property (nonatomic, strong) INRecipientsLabel * fromLabel; 17 | @property (nonatomic, strong) INMessageContentView * bodyView; 18 | @property (nonatomic, strong) INPlaceholderTextView * replyView; 19 | @property (nonatomic, strong) UIButton * exitButton; 20 | 21 | @property (nonatomic, assign) float angle; 22 | @property (nonatomic, strong) INThread * thread; 23 | 24 | - (void)setThread:(INThread*)thread; 25 | - (void)setShowReplyView:(BOOL)showReply; 26 | - (float)desiredHeight; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INContact.m: -------------------------------------------------------------------------------- 1 | // 2 | // INContact.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 "INContact.h" 10 | 11 | @implementation INContact 12 | 13 | + (NSMutableDictionary *)resourceMapping 14 | { 15 | NSMutableDictionary * mapping = [super resourceMapping]; 16 | 17 | [mapping addEntriesFromDictionary:@{ 18 | @"source": @"source", 19 | @"name": @"name", 20 | @"providerName": @"provider_name", 21 | @"email": @"email", 22 | @"accountID": @"account_id", 23 | @"UID": @"uid" 24 | }]; 25 | return mapping; 26 | } 27 | 28 | + (NSString *)resourceAPIName 29 | { 30 | return @"contacts"; 31 | } 32 | 33 | + (NSArray *)databaseIndexProperties 34 | { 35 | return [[super databaseIndexProperties] arrayByAddingObjectsFromArray: @[@"name", @"email", @"accountID"]]; 36 | } 37 | 38 | - (void)setup 39 | { 40 | } 41 | 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Inbox-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | Inbox.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Helpers/INModelResponseSerializer.h: -------------------------------------------------------------------------------- 1 | // 2 | // INModelArrayResponseSerializer.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/28/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | /** 10 | A subclass of AFJSONResponseSerializer that automatically parses Inbox API 11 | responses and saves the returned INModelObjects to the local data store. 12 | 13 | Any models that are already in memory are updated. This causes 14 | INModelObjectChangedNotifications so changes propogate to views and controllers 15 | listening on these models. 16 | */ 17 | 18 | @class AFJSONResponseSerializer; 19 | 20 | @interface INModelResponseSerializer : AFJSONResponseSerializer 21 | 22 | @property (nonatomic, strong) Class modelClass; 23 | @property (nonatomic, strong) NSArray * modelsCurrentlyMatching; 24 | 25 | - (id)initWithModelClass:(Class)klass; 26 | 27 | - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /InboxFramework/InboxUI/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 | - (float)bodyHeight; 36 | - (UIScrollView*)scrollView; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/INHomeViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // INViewController.h 3 | // Snapmail 4 | // 5 | // Created by Ben Gotow on 6/16/14. 6 | // Copyright (c) 2014 InboxApp, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "INSnapViewController.h" 11 | #import "INCaptureViewController.h" 12 | 13 | @interface INHomeViewController : UIViewController 14 | 15 | @property (weak, nonatomic) IBOutlet UITableView * tableView; 16 | @property (weak, nonatomic) IBOutlet UILabel *statusLabel; 17 | @property (strong, nonatomic) UIRefreshControl * tableRefreshControl; 18 | 19 | @property (nonatomic, strong) INSnapViewController * snapController; 20 | @property (nonatomic, strong) INCaptureViewController * captureController; 21 | 22 | @property (nonatomic, strong) INModelProvider * sendingProvider; 23 | @property (nonatomic, strong) INThreadProvider * threadProvider; 24 | 25 | @property (nonatomic, strong) INMessageProvider * messageProvider; 26 | 27 | - (void)dismissSnapViewController; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/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 | "idiom" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage/TRViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TRViewController.h 3 | // Triage 4 | // 5 | // Created by Ben Gotow on 5/7/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TRMessageActionButton.h" 11 | 12 | @interface TRViewController : UIViewController 13 | 14 | @property (nonatomic, weak) IBOutlet UIButton * messageDismissView; 15 | @property (weak, nonatomic) IBOutlet TRMessageActionButton *saveButton; 16 | @property (weak, nonatomic) IBOutlet TRMessageActionButton *archiveButton; 17 | @property (weak, nonatomic) IBOutlet UIView *emptyView; 18 | @property (weak, nonatomic) IBOutlet UILabel *emptyTextLabel; 19 | 20 | @property (nonatomic, strong) UIPanGestureRecognizer * cardDragRecognizer; 21 | @property (nonatomic, strong) UITapGestureRecognizer * cardDTapRecognizer; 22 | @property (nonatomic, strong) NSMutableArray * cardViews; 23 | @property (nonatomic, strong) UIDynamicAnimator * animator; 24 | 25 | @property (nonatomic, strong) INModelProvider * threadProvider; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/INMessageProvider.h: -------------------------------------------------------------------------------- 1 | // 2 | // INMessageProvider.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 "INModelProvider.h" 10 | 11 | /** A concrete subclass of INModelProvider specifically for managing result sets 12 | of messages and drafts. This subclass adds support for API query parameters that 13 | are specific to messages and drafts. 14 | */ 15 | @interface INMessageProvider : INModelProvider 16 | 17 | /** 18 | @param threadID The thread ID to fetch messages for. 19 | @param namespaceID The namespace in which the thread lives. 20 | @return An INMessageProvider initialized for displaying messages. 21 | */ 22 | - (id)initForMessagesInThread:(NSString *)threadID andNamespaceID:(NSString*)namespaceID; 23 | 24 | /** 25 | @param threadID The thread ID to fetch drafts for. 26 | @param namespaceID The namespace in which the thread lives. 27 | @return An INMessageProvider initialized for displaying drafts. 28 | */ 29 | - (id)initForDraftsInThread:(NSString *)threadID andNamespaceID:(NSString*)namespaceID; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /InboxExamples/SwiftEightBall/SwiftEightBall/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 | UIMainStoryboardFile 26 | Main 27 | INAPIPath 28 | http://localhost:5555/ 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /InboxFramework/Inbox/Categories/NSError+InboxErrors.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSError+InboxErrors.m 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/27/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "NSError+InboxErrors.h" 10 | 11 | @implementation NSError (InboxErrors) 12 | 13 | + (NSError*)inboxErrorWithFormat:(NSString *)format, ... 14 | { 15 | va_list args; 16 | va_start(args, format); 17 | NSString * description = [[NSString alloc] initWithFormat:format arguments:args]; 18 | va_end(args); 19 | return [NSError errorWithDomain:@"Inbox" code:-1 userInfo:@{NSLocalizedDescriptionKey: description}]; 20 | } 21 | 22 | + (NSError*)inboxErrorWithDescription:(NSString *)desc 23 | { 24 | return [NSError errorWithDomain:@"Inbox" code:-1 userInfo:@{NSLocalizedDescriptionKey: desc}]; 25 | } 26 | 27 | + (NSError*)inboxErrorWithDescription:(NSString *)desc underlyingError:(NSError*)underlying 28 | { 29 | desc = [desc stringByAppendingFormat: @" %@", [underlying localizedDescription]]; 30 | return [NSError errorWithDomain:@"Inbox" code:-1 userInfo:@{NSLocalizedDescriptionKey: desc, NSUnderlyingErrorKey: underlying}]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *Pods 2 | 3 | *Podfile.lock 4 | 5 | /.DS_Store 6 | 7 | *DerivedData 8 | 9 | /BigSur.xcodeproj/xcuserdata 10 | 11 | /BigSur.xcworkspace/xcuserdata 12 | 13 | *.DS_Store 14 | 15 | /build 16 | 17 | /file_list 18 | 19 | *.uncrustify 20 | 21 | /html/ 22 | 23 | /InboxFramework/DerivedData 24 | 25 | /InboxFramework/InboxFramework.xcodeproj/xcuserdata 26 | /InboxFramework/InboxFramework.xcworkspace/xcuserdata 27 | 28 | /InboxFramework/InboxFramework.xcodeproj/project.xcworkspace/xcuserdata 29 | 30 | /InboxFramework/Pods 31 | 32 | /BigSur/DerivedData 33 | 34 | /BigSur/Pods/ 35 | 36 | /BigSur/Podfile.lock 37 | 38 | /InboxFramework/Podfile.lock 39 | 40 | /InboxExamples/Triage/Pods 41 | 42 | /InboxExamples/Triage/Podfile.lock 43 | 44 | /InboxExamples/Triage/DerivedData 45 | 46 | /InboxExamples/Triage/Triage.xcworkspace/xcuserdata 47 | 48 | /InboxExamples/Triage/Triage.xcodeproj/xcuserdata 49 | 50 | /BigSur/BigSur.xcworkspace/xcuserdata/bengotow.xcuserdatad/UserInterfaceState.xcuserstate 51 | 52 | *.xcbkptlist 53 | 54 | *xcuserdata 55 | 56 | /InboxExamples/Snapmail/Pods 57 | 58 | /InboxExamples/Snapmail/Snapmail.xcodeproj/project.xcworkspace/xcuserdata 59 | 60 | *.xcuserstate 61 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Categories/FMResultSet+INModelQueries.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMResultSet+INModelQueries.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 "FMResultSet+INModelQueries.h" 10 | #import "INModelObject+Uniquing.h" 11 | 12 | @implementation FMResultSet (INModelQueries) 13 | 14 | - (INModelObject *)nextModelOfClass:(Class)klass 15 | { 16 | if ([klass isSubclassOfClass:[INModelObject class]] == NO) 17 | @throw @"Can only be used with subclasses of INModelObject"; 18 | 19 | [self next]; 20 | 21 | if (![self hasAnotherRow]) 22 | return nil; 23 | 24 | NSError * err = nil; 25 | NSData * jsonData = [self dataNoCopyForColumn:@"data"]; 26 | NSDictionary * json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:&err]; 27 | 28 | if (json && !err) { 29 | BOOL created = NO; 30 | INModelObject * model = [klass attachedInstanceMatchingID: json[@"id"] createIfNecessary:YES didCreate: &created]; 31 | if (created) { 32 | [model updateWithResourceDictionary: json]; 33 | [model setup]; 34 | } 35 | return model; 36 | 37 | } else { 38 | return nil; 39 | } 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/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 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/INThreadProvider.h: -------------------------------------------------------------------------------- 1 | // 2 | // INThreadProvider.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/28/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INModelProvider.h" 10 | #import "INNamespace.h" 11 | 12 | /** A concrete subclass of INModelProvider that exposes convenience functions 13 | for counting unread threads and supports additional query parameters on underlying 14 | API requests. 15 | */ 16 | @interface INThreadProvider : INModelProvider 17 | 18 | /* 19 | @param namespaceID The namespace to fetch threads for. To fetch a subset of 20 | the threads in the namespace, set the model provider's itemFilterPredicate. 21 | 22 | @return An initialized INThreadProvider for threads in the given namespace. 23 | */ 24 | - (id)initWithNamespaceID:(NSString *)namespaceID; 25 | 26 | /* 27 | Count the number of unread threads in this provider's collection. This method 28 | returns the number of threads matching the itemFilterPredicate that also have the 29 | unread tag, and does not apply the provider's itemRange, giving a total number 30 | of unread threads, not just the ones that are currently being provided to the delegate. 31 | 32 | @param callback A block to be called when the unread count is determined. The 33 | block will be called on the main thread asynchronously. 34 | */ 35 | - (void)countUnreadItemsWithCallback:(LongBlock)callback; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/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 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Inbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // Inbox.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/8/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #ifndef InboxFramework_Inb_xh_h 12 | #define InboxFramework_Inb_xh_h 13 | 14 | //! Project version number for Inbox. 15 | FOUNDATION_EXPORT double InboxVersionNumber; 16 | 17 | //! Project version string for Inbox. 18 | FOUNDATION_EXPORT const unsigned char InboxVersionString[]; 19 | 20 | 21 | /** If you're getting errors saying "Header could not be found," select the 22 | header file in Xcode and make sure it's visibility is "Public" in the framework 23 | build target. 24 | */ 25 | 26 | #import "INAPIManager.h" 27 | #import "INDatabaseManager.h" 28 | #import "INMessageProvider.h" 29 | #import "INThreadProvider.h" 30 | #import "INThread.h" 31 | #import "INModelObject.h" 32 | #import "INMessage.h" 33 | #import "INContact.h" 34 | #import "INNamespace.h" 35 | #import "INTag.h" 36 | #import "INAPITask.h" 37 | #import "INAddRemoveTagsTask.h" 38 | #import "INSendDraftTask.h" 39 | #import "INUploadFileTask.h" 40 | #import "INSaveDraftTask.h" 41 | #import "INDeleteDraftTask.h" 42 | #import "INSaveTagTask.h" 43 | #import "INSyncEngine.h" 44 | #import "INFile.h" 45 | #import "INDraft.h" 46 | #import "NSError+InboxErrors.h" 47 | 48 | #ifdef TARGET_OS_IPHONE 49 | #import "INMessageContentView.h" 50 | #import "INRecipientsLabel.h" 51 | #endif 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Categories/NSDictionary+FormatConversion.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+FormatConversion.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 "NSDictionary+FormatConversion.h" 10 | #import "NSString+FormatConversion.h" 11 | #import "INModelObject.h" 12 | 13 | @implementation NSDictionary (FormatConversion) 14 | 15 | - (id)objectForKey:(id)key asType:(NSString*)type 16 | { 17 | id value = [self objectForKey:key]; 18 | 19 | NSAssert(value != nil, @"You cannot call -objectForKey:asType: for a nonexistent key, because nonexistence cannot be distinguished from NSNull=nil in the return value."); 20 | 21 | if ([[self objectForKey:key] isKindOfClass:[NSNull class]]) { 22 | return nil; 23 | } 24 | 25 | if ([type isEqualToString:@"float"]) { 26 | return [NSNumber numberWithFloat:[value floatValue]]; 27 | } 28 | else if ([type isEqualToString:@"int"]) { 29 | return [NSNumber numberWithInt:[value intValue]]; 30 | } 31 | else if ([type isEqualToString:@"T@\"NSString\""]) { 32 | if ([value isKindOfClass:[NSNumber class]]) 33 | return [value stringValue]; 34 | else if ([value isKindOfClass:[NSString class]]) 35 | return value; 36 | else 37 | return [value stringValue]; 38 | } 39 | else if ([type isEqualToString:@"T@\"NSDate\""]) { 40 | return [NSDate dateWithTimeIntervalSince1970: [value doubleValue]]; 41 | } 42 | 43 | return value; 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INMarkMessageAsReadTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // INMarkMessageAsReadTask.m 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 6/17/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INMarkMessageAsReadTask.h" 10 | #import "INMessage.h" 11 | #import "INDatabaseManager.h" 12 | 13 | @implementation INMarkMessageAsReadTask 14 | 15 | 16 | - (NSURLRequest *)buildAPIRequest 17 | { 18 | NSAssert([self model], @"INMarkMessageAsReadTask asked to buildRequest with no access to a message model!"); 19 | NSAssert([[self model] namespaceID], @"INMarkMessageAsReadTask asked to buildRequest with no namespace!"); 20 | 21 | NSError * error = nil; 22 | NSString * path = [self.model resourceAPIPath]; 23 | NSString * url = [[NSURL URLWithString:path relativeToURL:[INAPIManager shared].AF.baseURL] absoluteString]; 24 | 25 | NSMutableDictionary * params = [NSMutableDictionary dictionary]; 26 | [params setObject:@(NO) forKey:@"unread"]; 27 | 28 | return [[[[INAPIManager shared] AF] requestSerializer] requestWithMethod:@"PUT" URLString:url parameters:params error:&error]; 29 | } 30 | 31 | - (void)applyLocally 32 | { 33 | [(INMessage*)self.model setUnread: NO]; 34 | [[INDatabaseManager shared] persistModel: self.model]; 35 | } 36 | 37 | - (void)rollbackLocally 38 | { 39 | [(INMessage*)self.model setUnread: YES]; 40 | [[INDatabaseManager shared] persistModel: self.model]; 41 | } 42 | 43 | - (BOOL)canCancelPendingTask:(INAPITask*)other 44 | { 45 | return ([[other model] isEqual: self.model] && [other isKindOfClass: [self class]]); 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage/TRMessageActionButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // TRMessageActionButton.m 3 | // Triage 4 | // 5 | // Created by Ben Gotow on 5/8/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "TRMessageActionButton.h" 10 | 11 | @implementation TRMessageActionButton 12 | 13 | - (id)initWithFrame:(CGRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | if (self) { 17 | 18 | } 19 | return self; 20 | } 21 | 22 | - (void)awakeFromNib 23 | { 24 | _selectedView = [[UIView alloc] initWithFrame: self.bounds]; 25 | [[_selectedView layer] setCornerRadius: self.bounds.size.width / 2]; 26 | [_selectedView setBackgroundColor: [self tintColor]]; 27 | [self insertSubview:_selectedView belowSubview:self.titleLabel]; 28 | [_selectedView setTransform: CGAffineTransformMakeScale(0.1, 0.1)]; 29 | [_selectedView setAlpha: 0]; 30 | 31 | [self setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected]; 32 | } 33 | 34 | - (void)setSelected:(BOOL)selected 35 | { 36 | if (self.selected == selected) 37 | return; 38 | 39 | [super setSelected: selected]; 40 | 41 | [UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.45 initialSpringVelocity:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ 42 | if (selected == NO) { 43 | [_selectedView setTransform: CGAffineTransformMakeScale(0.1, 0.1)]; 44 | [_selectedView setAlpha: 0]; 45 | } else { 46 | [_selectedView setTransform: CGAffineTransformMakeScale(1.1, 1.1)]; 47 | [_selectedView setAlpha: 1]; 48 | } 49 | } completion:NULL]; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /InboxFramework/Vendor/PDKeychainBindings/PDKeychainBindings.m: -------------------------------------------------------------------------------- 1 | // 2 | // PDKeychainBindings.m 3 | // PDKeychainBindings 4 | // 5 | // Created by Carl Brown on 7/10/11. 6 | // Copyright 2011 PDAgent, LLC. Released under MIT License. 7 | // 8 | 9 | #import "PDKeychainBindings.h" 10 | #import "PDKeychainBindingsController.h" 11 | 12 | @implementation PDKeychainBindings 13 | 14 | + (PDKeychainBindings *)sharedKeychainBindings 15 | { 16 | return [[PDKeychainBindingsController sharedKeychainBindingsController] keychainBindings]; 17 | } 18 | 19 | - (id)objectForKey:(NSString *)defaultName { 20 | //return [[[PDKeychainBindingsController sharedKeychainBindingsController] valueBuffer] objectForKey:defaultName]; 21 | return [[PDKeychainBindingsController sharedKeychainBindingsController] valueForKeyPath:[NSString stringWithFormat:@"values.%@",defaultName]]; 22 | } 23 | 24 | - (void)setObject:(NSString *)value forKey:(NSString *)defaultName { 25 | [[PDKeychainBindingsController sharedKeychainBindingsController] setValue:value forKeyPath:[NSString stringWithFormat:@"values.%@",defaultName]]; 26 | } 27 | 28 | - (void)setString:(NSString *)value forKey:(NSString *)defaultName { 29 | [[PDKeychainBindingsController sharedKeychainBindingsController] setValue:value forKeyPath:[NSString stringWithFormat:@"values.%@",defaultName]]; 30 | } 31 | 32 | - (void)removeObjectForKey:(NSString *)defaultName { 33 | [[PDKeychainBindingsController sharedKeychainBindingsController] setValue:nil forKeyPath:[NSString stringWithFormat:@"values.%@",defaultName]]; 34 | } 35 | 36 | - (NSString *)stringForKey:(NSString *)defaultName { 37 | return (NSString *) [self objectForKey:defaultName]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/SMThreadTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMThreadTableViewCell.m 3 | // SimpleMail 4 | // 5 | // Created by Ben Gotow on 7/8/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "SMThreadTableViewCell.h" 10 | 11 | static NSDateFormatter * threadDateFormatter; 12 | 13 | @implementation SMThreadTableViewCell 14 | 15 | - (void)setThread:(INThread *)thread 16 | { 17 | if (!threadDateFormatter) { 18 | threadDateFormatter = [[NSDateFormatter alloc] init]; 19 | [threadDateFormatter setDateStyle: NSDateFormatterShortStyle]; 20 | [threadDateFormatter setTimeStyle: NSDateFormatterShortStyle]; 21 | } 22 | 23 | // display subject and snippet Gmail-style 24 | NSString * body = [NSString stringWithFormat:@"%@—%@", [thread subject], [thread snippet]]; 25 | [[self bodyLabel] setText: body]; 26 | 27 | // format dates nicely 28 | NSString * date = [threadDateFormatter stringFromDate: [thread lastMessageDate]]; 29 | [[self dateLabel] setText: date]; 30 | 31 | [[self unreadDot] setHidden: ![thread unread]]; 32 | 33 | // collect the names of the participants, except for ourselves 34 | // to populate the top label of the cell 35 | INNamespace * namespace = [[[INAPIManager shared] namespaces] firstObject]; 36 | NSMutableArray * names = [NSMutableArray array]; 37 | for (NSDictionary * participant in [thread participants]) { 38 | if ([participant[@"email"] isEqualToString: [namespace emailAddress]]) 39 | continue; 40 | 41 | if ([participant[@"name"] length]) 42 | [names addObject: participant[@"name"]]; 43 | else 44 | [names addObject: participant[@"email"]]; 45 | } 46 | 47 | [[self fromLabel] setText: [names componentsJoinedByString:@", "]]; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/INMessageProvider.m: -------------------------------------------------------------------------------- 1 | // 2 | // INMessageProvider.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/30/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INMessageProvider.h" 10 | #import "INModelProvider+Private.h" 11 | #import "INPredicateToQueryParamConverter.h" 12 | #import "INThread.h" 13 | 14 | @implementation INMessageProvider 15 | 16 | - (id)initForMessagesInThread:(NSString *)threadID andNamespaceID:(NSString*)namespaceID 17 | { 18 | NSPredicate * threadPredicate = [NSComparisonPredicate predicateWithFormat:@"threadID = %@", threadID]; 19 | self = [super initWithClass:[INMessage class] andNamespaceID:namespaceID andUnderlyingPredicate:threadPredicate]; 20 | if (self) { 21 | } 22 | return self; 23 | } 24 | 25 | 26 | - (id)initForDraftsInThread:(NSString *)threadID andNamespaceID:(NSString*)namespaceID 27 | { 28 | NSPredicate * threadPredicate = [NSComparisonPredicate predicateWithFormat:@"threadID = %@", threadID]; 29 | self = [super initWithClass:[INDraft class] andNamespaceID:namespaceID andUnderlyingPredicate:threadPredicate]; 30 | if (self) { 31 | } 32 | return self; 33 | } 34 | 35 | 36 | - (NSDictionary *)queryParamsForPredicate:(NSPredicate*)predicate 37 | { 38 | INPredicateToQueryParamConverter * converter = [[INPredicateToQueryParamConverter alloc] init]; 39 | [converter setKeysToParamsTable: @{@"to": @"to", @"from": @"from", @"cc": @"cc", @"bcc": @"bcc", @"threadID": @"thread_id", @"label": @"label"}]; 40 | [converter setKeysToLIKEParamsTable:@{@"subject":@"subject"}]; 41 | 42 | NSMutableDictionary * params = [[converter paramsForPredicate: predicate] mutableCopy]; 43 | [params addEntriesFromDictionary: [super queryParamsForPredicate: predicate]]; 44 | return params; 45 | } 46 | 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /InboxKit.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint InboxSDK.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | s.name = "InboxKit" 12 | s.version = "0.0.6" 13 | s.summary = "The Inbox iOS framework provides a native interface to the Inbox API." 14 | s.description = "The Inbox iOS framework provides a native interface to the Inbox API, with additional features that make it easy to build full-fledged mail apps for iOS or add the email functionality you need to existing applications." 15 | s.homepage = "https://github.com/inboxapp/inbox-ios" 16 | s.license = { :type => "MIT", :file => "LICENSE.md" } 17 | s.platform = :ios, "7.0" 18 | 19 | s.author = { "Ben Gotow" => "ben@nylas.com" } 20 | s.social_media_url = "http://twitter.com/bengotow" 21 | 22 | s.prefix_header_file = "InboxFramework/Inbox/Inbox-Prefix.pch" 23 | 24 | s.source = { :git => "https://github.com/inboxapp/inbox-ios.git", :submodules => true, :tag => s.version } 25 | s.source_files = "InboxFramework/Inbox/*.{h,m}", "InboxFramework/Inbox/**/*.{h,m}", "InboxFramework/InboxUI/**/*.{h,m}", "InboxFramework/Submodules/PDKeychainBindingsController/PDKeychainBindingsController/*.{h,m}" 26 | 27 | s.dependency 'FMDB' 28 | s.dependency 'AFNetworking', '~> 2.0' 29 | 30 | s.public_header_files = "InboxFramework/Inbox/*.h", "InboxFramework/Inbox/**/*.h", "InboxFramework/InboxUI/**/*.h" 31 | 32 | s.libraries = "sqlite3" 33 | s.requires_arc = true 34 | 35 | end 36 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INAPITask.h: -------------------------------------------------------------------------------- 1 | // 2 | // INAPICall.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 "INModelObject.h" 11 | 12 | @class INMessage; 13 | @class AFHTTPRequestOperation; 14 | 15 | static NSString * INTaskProgressNotification = @"INTaskProgressNotification"; 16 | 17 | typedef enum : NSUInteger { 18 | INAPITaskStateWaiting, 19 | INAPITaskStateInProgress, 20 | INAPITaskStateFinished, 21 | INAPITaskStateCancelled, 22 | INAPITaskStateServerUnreachable, 23 | INAPITaskStateServerRejected 24 | } INAPITaskState; 25 | 26 | 27 | typedef void (^ CallbackBlock)(INAPITask * change, BOOL finished); 28 | 29 | @interface INAPITask : NSObject 30 | 31 | @property (nonatomic, strong) NSString * ID; 32 | @property (nonatomic, strong) INModelObject * model; 33 | @property (nonatomic, strong) NSMutableDictionary * data; 34 | @property (nonatomic, assign) INAPITaskState state; 35 | @property (nonatomic, assign) float percentComplete; 36 | 37 | + (instancetype)operationForModel:(INModelObject *)model; 38 | 39 | - (id)initWithModel:(INModelObject*)model; 40 | - (id)initWithCoder:(NSCoder *)aDecoder; 41 | - (void)encodeWithCoder:(NSCoder *)aCoder; 42 | 43 | - (NSString*)extendedDescription; 44 | 45 | - (BOOL)canCancelPendingTask:(INAPITask*)other; 46 | - (BOOL)canStartAfterTask:(INAPITask*)other; 47 | - (NSArray*)dependenciesIn:(NSArray*)others; 48 | 49 | - (BOOL)inProgress; 50 | - (NSString*)error; 51 | 52 | - (void)applyLocally; 53 | - (void)applyRemotelyWithCallback:(CallbackBlock)callback; 54 | - (void)rollbackLocally; 55 | 56 | - (void)handleSuccess:(AFHTTPRequestOperation *)operation withResponse:(id)responseObject; 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INNamespace.m: -------------------------------------------------------------------------------- 1 | // 2 | // INNamespace.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/28/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INNamespace.h" 10 | #import "INTag.h" 11 | #import "INContact.h" 12 | #import "INThreadProvider.h" 13 | #import "INMessageProvider.h" 14 | #import "INThread.h" 15 | 16 | @implementation INNamespace 17 | 18 | + (NSMutableDictionary *)resourceMapping 19 | { 20 | NSMutableDictionary * mapping = [super resourceMapping]; 21 | [mapping addEntriesFromDictionary:@{ 22 | @"emailAddress": @"email_address", 23 | @"provider": @"provider", 24 | @"status": @"status", 25 | @"scope": @"scope", 26 | @"lastSync": @"last_sync", 27 | @"name": @"name" 28 | }]; 29 | return mapping; 30 | } 31 | 32 | + (NSString *)resourceAPIName 33 | { 34 | return @"n"; 35 | } 36 | 37 | - (NSString *)resourceAPIPath 38 | { 39 | return [NSString stringWithFormat:@"/n/%@", self.ID]; 40 | } 41 | 42 | - (INModelProvider *)newContactProvider 43 | { 44 | return [[INModelProvider alloc] initWithClass:[INContact class] andNamespaceID:[self ID] andUnderlyingPredicate:nil]; 45 | } 46 | 47 | - (INModelProvider *)newTagProvider 48 | { 49 | return [[INModelProvider alloc] initWithClass:[INTag class] andNamespaceID:[self ID] andUnderlyingPredicate:nil]; 50 | } 51 | 52 | - (INThreadProvider *)newThreadProvider 53 | { 54 | return [[INThreadProvider alloc] initWithNamespaceID: [self ID]]; 55 | } 56 | 57 | - (INMessageProvider *)newDraftsProvider 58 | { 59 | return [[INMessageProvider alloc] initWithClass:[INDraft class] andNamespaceID:[self ID] andUnderlyingPredicate:nil]; 60 | } 61 | 62 | - (INMessageProvider *)newMessageProvider 63 | { 64 | return [[INMessageProvider alloc] initWithClass:[INMessage class] andNamespaceID:[self ID] andUnderlyingPredicate:nil]; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | 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 | CFBundleURLSchemes 29 | 30 | in-8ptbagaxzcpxre0h4k6fl3x6d 31 | 32 | 33 | 34 | CFBundleVersion 35 | 1.0 36 | INAPIPath 37 | https://api.nylas.com/ 38 | INAppID 39 | 8ptbagaxzcpxre0h4k6fl3x6d 40 | LSRequiresIPhoneOS 41 | 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | 50 | UISupportedInterfaceOrientations~ipad 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationPortraitUpsideDown 54 | UIInterfaceOrientationLandscapeLeft 55 | UIInterfaceOrientationLandscapeRight 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/SimpleMail-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 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 | CFBundleURLIconFile 29 | 30 | CFBundleURLName 31 | in-6fqe68ue317qr4qvddcwjfncd 32 | CFBundleURLSchemes 33 | 34 | in-6fqe68ue317qr4qvddcwjfncd 35 | 36 | 37 | 38 | CFBundleVersion 39 | 1.0 40 | INAPIPath 41 | https://api.nylas.com/ 42 | INAppID 43 | 6fqe68ue317qr4qvddcwjfncd 44 | LSRequiresIPhoneOS 45 | 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Helpers/INPredicateToQueryParamConverter.m: -------------------------------------------------------------------------------- 1 | // 2 | // INPredicateToQueryParamConverter.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 "INPredicateToQueryParamConverter.h" 10 | 11 | @implementation INPredicateToQueryParamConverter 12 | 13 | - (NSDictionary*)paramsForPredicate:(NSPredicate*)predicate 14 | { 15 | NSMutableDictionary * params = [NSMutableDictionary dictionary]; 16 | 17 | if ([predicate isKindOfClass: [NSCompoundPredicate class]]) { 18 | if ([(NSCompoundPredicate*)predicate compoundPredicateType] != NSAndPredicateType) 19 | NSAssert(false, @"Only AND predicates are currently supported in constructing queries."); 20 | 21 | for (NSPredicate * subpredicate in [(NSCompoundPredicate*)predicate subpredicates]) 22 | [params addEntriesFromDictionary: [self paramsForPredicate: subpredicate]]; 23 | 24 | } else if ([predicate isKindOfClass: [NSComparisonPredicate class]]) { 25 | NSComparisonPredicate * pred = (NSComparisonPredicate*)predicate; 26 | if ([[pred rightExpression] expressionType] != NSConstantValueExpressionType) 27 | NSAssert(false, @"Only constant values can be on the RHS of predicates."); 28 | if ([[pred leftExpression] expressionType] != NSKeyPathExpressionType) 29 | NSAssert(false, @"Only property names can be on the LHS of predicates."); 30 | 31 | 32 | NSString * keyPath = [[pred leftExpression] keyPath]; 33 | NSString * rhs = [[pred rightExpression] constantValue]; 34 | 35 | if (_keysToParamsTable[keyPath]) { 36 | NSAssert([pred predicateOperatorType] == NSEqualToPredicateOperatorType, @"Sorry, predicates for %@ can only use the '=' operator.", keyPath); 37 | NSString * param = _keysToParamsTable[keyPath]; 38 | [params setObject:rhs forKey:param]; 39 | } 40 | 41 | if (_keysToLIKEParamsTable[keyPath]) { 42 | NSString * param = _keysToLIKEParamsTable[keyPath]; 43 | [params setObject:rhs forKey:param]; 44 | } 45 | } 46 | 47 | return params; 48 | } 49 | @end 50 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/INThreadProvider.m: -------------------------------------------------------------------------------- 1 | // 2 | // INThreadProvider.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/28/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INThreadProvider.h" 10 | #import "INModelProvider+Private.h" 11 | #import "INAPIManager.h" 12 | #import "INThread.h" 13 | #import "INTag.h" 14 | #import "INModelResponseSerializer.h" 15 | #import "INPredicateToQueryParamConverter.h" 16 | 17 | @implementation INThreadProvider 18 | 19 | - (id)initWithNamespaceID:(NSString *)namespaceID 20 | { 21 | self = [super initWithClass:[INThread class] andNamespaceID:namespaceID andUnderlyingPredicate:nil]; 22 | if (self) { 23 | } 24 | return self; 25 | } 26 | 27 | - (NSDictionary *)queryParamsForPredicate:(NSPredicate*)predicate 28 | { 29 | INPredicateToQueryParamConverter * converter = [[INPredicateToQueryParamConverter alloc] init]; 30 | [converter setKeysToParamsTable: @{@"to": @"to", @"from": @"from", @"cc": @"cc", @"bcc": @"bcc", @"tagIDs":@"tag"}]; 31 | [converter setKeysToLIKEParamsTable: @{@"subject": @"subject"}]; 32 | 33 | NSMutableDictionary * params = [[converter paramsForPredicate: predicate] mutableCopy]; 34 | [params addEntriesFromDictionary: [super queryParamsForPredicate: predicate]]; 35 | 36 | NSSortDescriptor * descriptor = [[self itemSortDescriptors] firstObject]; 37 | if (descriptor) { 38 | if (![[descriptor key] isEqualToString: @"lastMessageDate"]) 39 | NSAssert(false, @"Sorry, the backend only supports ordering threads by `lastMessageDate`, so this provider cannot load threads."); 40 | } 41 | 42 | return params; 43 | } 44 | 45 | - (void)refresh 46 | { 47 | [super refresh]; 48 | } 49 | 50 | - (void)countUnreadItemsWithCallback:(LongBlock)callback 51 | { 52 | NSPredicate * unreadPredicate = [NSComparisonPredicate predicateWithFormat:@"ANY tagIDs = %@", INTagIDUnread]; 53 | NSPredicate * predicate = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:@[[self fetchPredicate], unreadPredicate]]; 54 | [[INDatabaseManager shared] countModelsOfClass:[INThread class] matching:predicate withCallback:^(long count) { 55 | callback(count); 56 | }]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INTag.m: -------------------------------------------------------------------------------- 1 | // 2 | // INLabel.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/30/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INTag.h" 10 | #import "INNamespace.h" 11 | #import "INSaveTagTask.h" 12 | 13 | @implementation INTag 14 | 15 | + (instancetype)tagWithID:(NSString*)ID 16 | { 17 | return [self instanceWithID: ID inNamespaceID: nil]; 18 | } 19 | 20 | + (NSMutableDictionary *)resourceMapping 21 | { 22 | NSMutableDictionary * mapping = [super resourceMapping]; 23 | [mapping addEntriesFromDictionary:@{ 24 | @"providedName": @"name" 25 | }]; 26 | return mapping; 27 | } 28 | 29 | + (NSString *)resourceAPIName 30 | { 31 | return @"tags"; 32 | } 33 | 34 | - (id)initInNamespace:(INNamespace*)namespace 35 | { 36 | NSAssert(namespace, @"initInNamespace: called with a nil namespace."); 37 | self = [super init]; 38 | if (self) { 39 | [self setNamespaceID: [namespace ID]]; 40 | } 41 | return self; 42 | } 43 | 44 | - (NSString*)name 45 | { 46 | // pretend we have localization 47 | NSDictionary * localized = @{INTagIDArchive: @"Archive", INTagIDInbox: @"Inbox", INTagIDTrash: @"Trash", INTagIDUnread: @"Unread", INTagIDSent: @"Sent", INTagIDStarred: @"Starred"}; 48 | if ([localized objectForKey: self.ID]) 49 | return [localized objectForKey: self.ID]; 50 | if (self.providedName) 51 | return self.providedName; 52 | return [self.ID capitalizedString]; 53 | } 54 | 55 | - (void)setName:(NSString*)name 56 | { 57 | // in the future, check the readonly flag here 58 | _providedName = name; 59 | } 60 | 61 | - (UIColor*)color 62 | { 63 | NSInteger count = 0; 64 | for (int ii = 0; ii < [[self name] length]; ii ++) 65 | count += [[self name] characterAtIndex:ii]; 66 | 67 | return [UIColor colorWithHue:(count % 1000) / 1000.0 saturation:0.8 brightness:0.6 alpha:1]; 68 | } 69 | 70 | - (void)save 71 | { 72 | INSaveTagTask * save = [INSaveTagTask operationForModel: self]; 73 | [[INAPIManager shared] queueTask: save]; 74 | } 75 | 76 | + (NSArray *)databaseIndexProperties 77 | { 78 | return [[super databaseIndexProperties] arrayByAddingObjectsFromArray: @[@"providedName"]]; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INTag.h: -------------------------------------------------------------------------------- 1 | // 2 | // INLabel.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 "INModelObject.h" 10 | 11 | static NSString * INTagIDUnread = @"unread"; 12 | static NSString * INTagIDUnseen = @"unseen"; 13 | static NSString * INTagIDArchive = @"archive"; 14 | static NSString * INTagIDTrash = @"trash"; 15 | static NSString * INTagIDDraft = @"drafts"; 16 | static NSString * INTagIDInbox = @"inbox"; 17 | static NSString * INTagIDStarred = @"starred"; 18 | static NSString * INTagIDSent = @"sent"; 19 | 20 | /** A simple wrapper around an Inbox tag. See the Inbox Tags documentation for 21 | more information about tags: http://inboxapp.com/docs/api#tags 22 | */ 23 | @interface INTag : INModelObject 24 | 25 | @property (nonatomic, strong) NSString * providedName; 26 | 27 | /** 28 | @param ID A tag ID. May be a user-generated ID, or one of the built-in Inbox tag IDs: 29 | INTagIDUnread, INTagIDSent, etc. 30 | 31 | @return An INTag model with the given ID. 32 | */ 33 | + (instancetype)tagWithID:(NSString*)ID; 34 | 35 | /** 36 | Initialize a new INTag model in the given namespace. This should only be used for creating 37 | new tags, not for retrieving existing tags. 38 | 39 | @param namespace The namespace to create the tag in. 40 | @return An INTag model 41 | */ 42 | - (id)initInNamespace:(INNamespace*)namespace; 43 | 44 | /** 45 | @return The display-ready name of the tag, localized when possible to reflect the 46 | user's locale. 47 | */ 48 | - (NSString*)name; 49 | 50 | /** 51 | Set the name of the tag. After setting the tag name, you should call -save to commit changes. 52 | Also note that provider tags (gmail-) cannot be renamed. 53 | @param name The new tag name 54 | */ 55 | - (void)setName:(NSString*)name; 56 | 57 | 58 | /** 59 | @return The color of the tag. When possible, applications should use a tag's color 60 | in their UI to give users a tag presentation that is consistent and easy to scan. 61 | */ 62 | - (UIColor*)color; 63 | 64 | /** 65 | Save the tag back to the Inbox API. Note that provider tags (gmail-) are not modifiable, 66 | and in general you can only modify the names of tags. 67 | */ 68 | - (void)save; 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/Snapmail-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 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 30 | CFBundleURLSchemes 31 | 32 | in-c3ozq7z6yx0yigb6kv132o2cr 33 | 34 | 35 | 36 | CFBundleVersion 37 | 1.0 38 | INAPIPath 39 | https://api.nylas.com/ 40 | INAppID 41 | c3ozq7z6yx0yigb6kv132o2cr 42 | LSRequiresIPhoneOS 43 | 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UIStatusBarStyle 49 | UIStatusBarStyleBlackTranslucent 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | UIViewControllerBasedStatusBarAppearance 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/INModelObject+Uniquing.h: -------------------------------------------------------------------------------- 1 | // 2 | // INModelObject+Uniquing.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/28/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INModelObject.h" 10 | 11 | @interface INModelObject (Uniquing) 12 | 13 | /** @name Globally Unique Instances */ 14 | 15 | /** 16 | Returns the model object with the given ID currently in memory, if one exists. 17 | If one does not exist and the 'shouldCreate' option is provided, it is created 18 | and attached to the instance table. If you intend to create the model, you should 19 | allow this method to do it while locking the instance lookup table to prevent 20 | threading issues that could result in two copies of the model in memory. 21 | 22 | The primary purpose of this method is to retrieve an instance of an object so 23 | you can avoid allocating a new one, and avoid scenarios where multiple copies of 24 | a single logical object are floating around, which may be out of sync with each 25 | other and the local datastore. 26 | 27 | @param ID The ID of the object you're looking for. 28 | 29 | @param shouldCreate YES if you intend to create this model. You should always let 30 | ths method create the object for you and lock while it attaches it to the instance table. 31 | 32 | @param didCreate A BOOL, passed by reference, indicating whether the model returned 33 | was just created. 34 | 35 | @return An instance, or nil. 36 | */ 37 | + (id)attachedInstanceMatchingID:(id)ID createIfNecessary:(BOOL)shouldCreate didCreate:(BOOL*)didCreate; 38 | 39 | /** 40 | Locks the instance table and attaches the provided model. This method throws an 41 | exception if another model of the same class is already in the instance table 42 | with the same ID. 43 | @param obj The object to attach as the official copy of this model. 44 | */ 45 | + (void)attachInstance:(INModelObject *)obj; 46 | 47 | /** 48 | @return A copy of the INModelObject that is not attached to the instance table. 49 | This is useful if you want to clone an object to modify it and keep two versions alive. 50 | */ 51 | - (id)detatchedCopy; 52 | 53 | /** 54 | @return YES, if this object has not been attached to the instance tree and is not the 55 | official version of the object. 56 | */ 57 | - (BOOL)isDetatched; 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /InboxExamples/SwiftEightBall/SwiftEightBall/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftMail 4 | // 5 | // Created by Ben Gotow on 7/28/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Inbox 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | 18 | func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool { 19 | // Override point for customization after application launch. 20 | return true 21 | } 22 | 23 | func applicationWillResignActive(application: UIApplication!) { 24 | // 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. 25 | // 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. 26 | } 27 | 28 | func applicationDidEnterBackground(application: UIApplication!) { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | func applicationWillEnterForeground(application: UIApplication!) { 34 | // 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. 35 | } 36 | 37 | func applicationDidBecomeActive(application: UIApplication!) { 38 | // 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. 39 | } 40 | 41 | func applicationWillTerminate(application: UIApplication!) { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/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 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INMessage.h: -------------------------------------------------------------------------------- 1 | // 2 | // INMessage.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 "INModelObject.h" 10 | 11 | @class INThread; 12 | @class INNamespace; 13 | @class INFile; 14 | 15 | /** The INMessage class provides access to message attributes and convenience 16 | methods for accessing attachments, marking as read, etc. 17 | 18 | You should not create INMessage objects directly. You can fetch messages by 19 | asking an INNamespace for a newMessageProvider and configuring that provider to 20 | return the result set you want (for example, all unread messages.) 21 | 22 | To compose or send a new message, create an instance of INDraft and call it's 23 | -save and -send methods, respectively. 24 | 25 | The Inbox platform is similar to Gmail in that many actions are taken on threads, 26 | not on messages. If you're looking to archive, label, or delete a message, see 27 | the documentation for INThread. 28 | */ 29 | @interface INMessage : INModelObject 30 | 31 | @property (nonatomic, strong) NSString * body; 32 | @property (nonatomic, strong) NSString * snippet; 33 | @property (nonatomic, strong) NSDate * date; 34 | @property (nonatomic, strong) NSString * subject; 35 | @property (nonatomic, strong) NSString * threadID; 36 | @property (nonatomic, strong) NSArray * files; 37 | @property (nonatomic, strong) NSArray * from; 38 | @property (nonatomic, strong) NSArray * to; 39 | @property (nonatomic, strong) NSArray * cc; 40 | @property (nonatomic, strong) NSArray * bcc; 41 | @property (nonatomic, assign) BOOL unread; 42 | 43 | /* 44 | @return The parent thread of this message. Depending on how this message instance was 45 | loaded, the thread may or may not be available in the cache. After calling this method, 46 | you should check if the thread's data is loaded by calling -isDataAvailable, and call 47 | -reload: if necessary to fully populate the thread. 48 | */ 49 | - (INThread*)thread; 50 | 51 | /* 52 | @return An array of INFile objects representing files on this message. 53 | INFile objects can be queried for previews, download links, etc. 54 | */ 55 | - (NSArray*)files; 56 | 57 | /* 58 | Mark the individual message as read. This change takes effect immediately in the local cache 59 | but may not sync back to the server immediately. 60 | */ 61 | - (void)markAsRead; 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /InboxExamples/SimpleMail/SimpleMail/SMAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMAppDelegate.m 3 | // SimpleMail 4 | // 5 | // Created by Ben Gotow on 7/8/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "SMAppDelegate.h" 10 | 11 | @implementation SMAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // 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. 22 | // 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. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // 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. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation 47 | { 48 | return [[INAPIManager shared] handleURL: url]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /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 | ?> -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/INSnapViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // INSnapViewController.m 3 | // Snapmail 4 | // 5 | // Created by Ben Gotow on 6/16/14. 6 | // Copyright (c) 2014 InboxApp, Inc. All rights reserved. 7 | // 8 | 9 | #import "INSnapViewController.h" 10 | #import "INHomeViewController.h" 11 | 12 | @implementation INSnapViewController 13 | 14 | - (id)initWithThread:(INThread*)thread 15 | { 16 | self = [super init]; 17 | if (self) { 18 | _provider = [thread newMessageProvider]; 19 | _provider.delegate = self; 20 | [_provider refresh]; 21 | } 22 | return self; 23 | } 24 | 25 | - (void)viewDidLoad 26 | { 27 | [super viewDidLoad]; 28 | [_errorLabel setText: @""]; 29 | [_spinner startAnimating]; 30 | } 31 | 32 | - (void)viewWillDisappear:(BOOL)animated 33 | { 34 | [_timer invalidate]; 35 | _timer = nil; 36 | } 37 | 38 | - (void)displaySnapImage 39 | { 40 | // TODO iterate over snaps and find the oldest unread one... 41 | INMessage * message = nil; 42 | for (INMessage * msg in [_provider items]) { 43 | if ([[msg attachmentIDs] count] > 0) 44 | message = msg; 45 | } 46 | 47 | if (message == nil) 48 | return; 49 | 50 | INFile * file = [[message attachments] firstObject]; 51 | if (file == nil) 52 | [(INHomeViewController*)self.parentViewController dismissSnapViewController]; 53 | 54 | [file getDataWithCallback:^(NSError *error, NSData *data) { 55 | [_spinner stopAnimating]; 56 | if (error) { 57 | [_errorLabel setText: [error localizedDescription]]; 58 | return; 59 | } 60 | 61 | [message markAsRead]; 62 | 63 | UIImage * img = [UIImage imageWithData: data]; 64 | [_imageView setImage: img]; 65 | [_timeLabel setText: @"5"]; 66 | 67 | [_timer invalidate]; 68 | _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(countdown) userInfo:nil repeats:YES]; 69 | }]; 70 | } 71 | 72 | - (void)countdown 73 | { 74 | int timeRemaining = [[_timeLabel text] intValue]; 75 | timeRemaining -= 1; 76 | 77 | if (timeRemaining == 0) { 78 | [_imageView setImage: nil]; 79 | [self displaySnapImage]; 80 | } else { 81 | [_timeLabel setText: [NSString stringWithFormat:@"%d", timeRemaining]]; 82 | } 83 | } 84 | 85 | - (void)providerDataChanged:(INModelProvider *)provider 86 | { 87 | [self displaySnapImage]; 88 | } 89 | 90 | - (void)provider:(INModelProvider *)provider dataFetchFailed:(NSError *)error 91 | { 92 | [_errorLabel setText: [error localizedDescription]]; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INSendDraftTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // INSendDraftChange.m 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/20/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INSendDraftTask.h" 10 | #import "INDatabaseManager.h" 11 | #import "INDeleteDraftTask.h" 12 | #import "INSaveDraftTask.h" 13 | #import "INDraft.h" 14 | #import "INTag.h" 15 | 16 | @implementation INSendDraftTask 17 | 18 | - (BOOL)canStartAfterTask:(INAPITask *)other 19 | { 20 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INDeleteDraftTask class]]) 21 | return NO; 22 | return YES; 23 | } 24 | 25 | - (BOOL)canCancelPendingTask:(INAPITask*)other 26 | { 27 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INSendDraftTask class]]) 28 | return YES; 29 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INDeleteDraftTask class]]) 30 | return YES; 31 | return NO; 32 | } 33 | 34 | - (NSURLRequest *)buildAPIRequest 35 | { 36 | INDraft * draft = (INDraft*)self.model; 37 | 38 | NSAssert(self.model, @"INSendDraftChange asked to buildRequest with no model!"); 39 | NSAssert([self.model namespaceID], @"INSendDraftChange asked to buildRequest with no namespace!"); 40 | NSAssert([self.model namespaceID], @"The Inbox API now requires drafts with versions. Refresh this INDraft object to get one with a version."); 41 | 42 | NSError * error = nil; 43 | NSString * sendPath = [NSString stringWithFormat:@"/n/%@/send", [draft namespaceID]]; 44 | NSString * url = [[NSURL URLWithString:sendPath relativeToURL:[INAPIManager shared].AF.baseURL] absoluteString]; 45 | 46 | return [[[[INAPIManager shared] AF] requestSerializer] requestWithMethod:@"POST" URLString:url parameters:@{@"draft_id": [draft ID], @"version": [draft version]} error:&error]; 47 | } 48 | 49 | - (NSArray*)dependenciesIn:(NSArray*)others 50 | { 51 | NSMutableArray * dependencies = [NSMutableArray array]; 52 | for (INAPITask * other in others) { 53 | if (other == self) 54 | continue; 55 | 56 | if ([other isKindOfClass: [INSaveDraftTask class]] && [[other model] isEqual: [self model]]) 57 | [dependencies addObject: other]; 58 | } 59 | return dependencies; 60 | } 61 | 62 | - (void)handleSuccess:(AFHTTPRequestOperation *)operation withResponse:(id)responseObject 63 | { 64 | [super handleSuccess: operation withResponse: responseObject]; 65 | [[INDatabaseManager shared] unpersistModel: [self model] willResaveSameModel: NO]; 66 | } 67 | 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INSaveTagTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // INSaveTagTask.m 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 9/17/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INSaveTagTask.h" 10 | #import "INTag.h" 11 | #import "INDatabaseManager.h" 12 | 13 | @implementation INSaveTagTask 14 | 15 | - (BOOL)canStartAfterTask:(INAPITask *)other 16 | { 17 | return YES; 18 | } 19 | 20 | - (BOOL)canCancelPendingTask:(INAPITask*)other 21 | { 22 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INSaveTagTask class]]) 23 | return YES; 24 | return NO; 25 | } 26 | 27 | - (NSURLRequest *)buildAPIRequest 28 | { 29 | NSAssert(self.model, @"INSaveTagTask asked to buildRequest with no model!"); 30 | NSAssert([self.model namespaceID], @"INSaveTagTask asked to buildRequest with no namespace!"); 31 | 32 | NSError * error = nil; 33 | NSString * path = nil; 34 | 35 | if ([self.model isUnsynced]) 36 | path = [NSString stringWithFormat:@"/n/%@/tags", [self.model namespaceID]]; 37 | else 38 | path = [NSString stringWithFormat:@"/n/%@/tags/%@", [self.model namespaceID], [self.model ID]]; 39 | 40 | NSString * url = [[NSURL URLWithString:path relativeToURL:[INAPIManager shared].AF.baseURL] absoluteString]; 41 | NSMutableDictionary * params = [[self.model resourceDictionary] mutableCopy]; 42 | [params removeObjectForKey: @"id"]; 43 | [params removeObjectForKey: @"namespace_id"]; 44 | 45 | return [[[[INAPIManager shared] AF] requestSerializer] requestWithMethod:@"POST" URLString:url parameters:params error:&error]; 46 | } 47 | 48 | - (void)handleSuccess:(AFHTTPRequestOperation *)operation withResponse:(id)responseObject 49 | { 50 | if (![responseObject isKindOfClass: [NSDictionary class]]) 51 | return NSLog(@"SaveTag weird response: %@", responseObject); 52 | 53 | INTag * tag = (INTag *)[self model]; 54 | 55 | // remove the tag from the local cache and then update it with the API response 56 | // and save it again. This is important, because the JSON that comes back from an 57 | // initial save gives the tag an ID and we want to replace the old tag. 58 | [[INDatabaseManager shared] unpersistModel: tag willResaveSameModel:YES]; 59 | [tag updateWithResourceDictionary: responseObject]; 60 | [[INDatabaseManager shared] persistModel: tag]; 61 | } 62 | 63 | - (void)applyLocally 64 | { 65 | [[INDatabaseManager shared] persistModel: [self model]]; 66 | } 67 | 68 | - (void)rollbackLocally 69 | { 70 | } 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/INModelObject+Uniquing.m: -------------------------------------------------------------------------------- 1 | // 2 | // INModelObject+Uniquing.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/28/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INModelObject+Uniquing.h" 10 | #import "INDatabaseManager.h" 11 | 12 | static NSMapTable * modelInstanceTable; 13 | 14 | @implementation INModelObject (Uniquing) 15 | 16 | + (id)attachedInstanceMatchingID:(id)ID createIfNecessary:(BOOL)shouldCreate didCreate:(BOOL*)didCreate 17 | { 18 | if (!modelInstanceTable) 19 | modelInstanceTable = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:1000]; 20 | 21 | id obj = nil; 22 | @synchronized(modelInstanceTable) { 23 | obj = [modelInstanceTable objectForKey:[INModelObject attachmentKeyForClass:self ID:ID]]; 24 | if (shouldCreate && !obj) { 25 | obj = [[self alloc] init]; 26 | [obj setID: ID]; 27 | [modelInstanceTable setObject:obj forKey:[INModelObject attachmentKeyForClass:[obj class] ID:[obj ID]]]; 28 | if (didCreate) *didCreate = YES; 29 | } 30 | } 31 | return obj; 32 | } 33 | 34 | + (void)attachInstance:(INModelObject *)obj 35 | { 36 | NSAssert(obj, @"-attachInstance called with a null object."); 37 | NSAssert([obj isKindOfClass:[INModelObject class]], @"Only subclasses of INModelObject can be attached."); 38 | 39 | @synchronized(modelInstanceTable) { 40 | id existing = [INModelObject attachedInstanceMatchingID:[obj ID] createIfNecessary: NO didCreate: NULL]; 41 | if (!existing) 42 | [modelInstanceTable setObject:obj forKey:[INModelObject attachmentKeyForClass:[obj class] ID:[obj ID]]]; 43 | else if (existing != obj) 44 | NSAssert(false, @"Attaching an instance when another instance is already in memory for this class+ID combination. Where did this object come from?"); 45 | } 46 | } 47 | 48 | + (NSString *)attachmentKeyForClass:(Class)klass ID:(id)ID 49 | { 50 | if ([ID isKindOfClass:[NSNumber class]]) 51 | ID = [ID stringValue]; 52 | 53 | char cString[255]; 54 | sprintf(cString, "%p-%s", (__bridge void *)klass, [ID cStringUsingEncoding:NSUTF8StringEncoding]); 55 | return [NSString stringWithCString:cString encoding:NSUTF8StringEncoding]; 56 | } 57 | 58 | - (id)detatchedCopy 59 | { 60 | Class klass = [self class]; 61 | id copy = [[klass alloc] init]; 62 | 63 | [copy updateWithResourceDictionary:[self resourceDictionary]]; 64 | return copy; 65 | } 66 | 67 | - (BOOL)isDetatched 68 | { 69 | return [INModelObject attachedInstanceMatchingID:[self ID] createIfNecessary: NO didCreate: NULL] != self; 70 | } 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INUploadFileTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // INUploadFileTask.m 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/21/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INUploadFileTask.h" 10 | #import "INDatabaseManager.h" 11 | #import "INFile.h" 12 | #import "INDraft.h" 13 | 14 | @implementation INUploadFileTask 15 | 16 | - (void)applyLocally 17 | { 18 | [[INDatabaseManager shared] persistModel: self.model]; 19 | } 20 | 21 | - (void)rollbackLocally 22 | { 23 | [[INDatabaseManager shared] unpersistModel:self.model willResaveSameModel:NO]; 24 | } 25 | 26 | - (NSURLRequest *)buildAPIRequest 27 | { 28 | INFile * file = (INFile *)self.model; 29 | 30 | NSAssert(file, @"INUploadFileTask asked to buildRequest with no model!"); 31 | NSAssert([file namespaceID], @"INUploadFileTask asked to buildRequest with no namespace!"); 32 | NSAssert([file localDataPath], @"INUploadFileTask asked to upload a file with no local data."); 33 | 34 | NSString * path = [NSString stringWithFormat:@"/n/%@/files", [file namespaceID]]; 35 | NSString * url = [[NSURL URLWithString:path relativeToURL:[INAPIManager shared].AF.baseURL] absoluteString]; 36 | 37 | return [[[[INAPIManager shared] AF] requestSerializer] multipartFormRequestWithMethod:@"POST" URLString:url parameters:nil constructingBodyWithBlock:^(id formData) { 38 | NSURL * fileURL = [NSURL fileURLWithPath: [file localDataPath]]; 39 | [formData appendPartWithFileURL:fileURL name:@"file" fileName:[file filename] mimeType:[file mimetype] error:NULL]; 40 | } error:NULL]; 41 | } 42 | 43 | - (NSMutableArray *)waitingDrafts 44 | { 45 | if (!self.data[@"waitingDrafts"]) 46 | [self.data setObject:[NSMutableArray array] forKey:@"waitingDrafts"]; 47 | return self.data[@"waitingDrafts"]; 48 | } 49 | 50 | - (void)handleSuccess:(AFHTTPRequestOperation *)operation withResponse:(id)responseObject 51 | { 52 | if ([responseObject isKindOfClass: [NSArray class]]) 53 | responseObject = [responseObject firstObject]; 54 | 55 | if (![responseObject isKindOfClass: [NSDictionary class]]) 56 | return NSLog(@"SaveDraft weird response: %@", responseObject); 57 | 58 | NSString * oldID = [self.model ID]; 59 | 60 | INFile * file = (INFile *)self.model; 61 | [[INDatabaseManager shared] unpersistModel: file willResaveSameModel:YES]; 62 | [file updateWithResourceDictionary: responseObject]; 63 | [[INDatabaseManager shared] persistModel: file]; 64 | 65 | for (INDraft * draft in [self waitingDrafts]) 66 | [draft fileWithID:oldID uploadedAs:[self.model ID]]; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage/TRAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // TRAppDelegate.m 3 | // Triage 4 | // 5 | // Created by Ben Gotow on 5/7/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "TRAppDelegate.h" 10 | 11 | @implementation TRAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | self.viewController = [[TRViewController alloc] init]; 17 | 18 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 19 | self.window.rootViewController = _viewController; 20 | [self.window makeKeyAndVisible]; 21 | 22 | return YES; 23 | } 24 | 25 | - (void)applicationWillResignActive:(UIApplication *)application 26 | { 27 | // 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. 28 | // 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. 29 | } 30 | 31 | - (void)applicationDidEnterBackground:(UIApplication *)application 32 | { 33 | // 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. 34 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 35 | } 36 | 37 | - (void)applicationWillEnterForeground:(UIApplication *)application 38 | { 39 | // 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. 40 | } 41 | 42 | - (void)applicationDidBecomeActive:(UIApplication *)application 43 | { 44 | // 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. 45 | } 46 | 47 | - (void)applicationWillTerminate:(UIApplication *)application 48 | { 49 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 50 | } 51 | 52 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation 53 | { 54 | return [[INAPIManager shared] handleURL: url]; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INDeleteDraftTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // INDeleteDraftChange.m 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/20/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INDeleteDraftTask.h" 10 | #import "INThread+Private.h" 11 | #import "INSaveDraftTask.h" 12 | #import "INSendDraftTask.h" 13 | 14 | @implementation INDeleteDraftTask 15 | 16 | - (BOOL)canCancelPendingTask:(INAPITask*)other 17 | { 18 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INSaveDraftTask class]]) 19 | return YES; 20 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INSendDraftTask class]]) 21 | return YES; 22 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INDeleteDraftTask class]]) 23 | return YES; 24 | return NO; 25 | } 26 | 27 | - (BOOL)canStartAfterTask:(INAPITask*)other 28 | { 29 | // If the other operation is sending the draft, it's too late! 30 | // Gotta tell the user the draft couldn't be deleted. 31 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INSendDraftTask class]]) 32 | return NO; 33 | return YES; 34 | } 35 | 36 | - (NSURLRequest *)buildAPIRequest 37 | { 38 | NSAssert(self.model, @"INDeleteDraftChange asked to buildRequest with no model!"); 39 | NSAssert([self.model namespaceID], @"INDeleteDraftChange asked to buildRequest with no namespace!"); 40 | 41 | NSError * error = nil; 42 | NSString * url = [[NSURL URLWithString:[self.model resourceAPIPath] relativeToURL:[INAPIManager shared].AF.baseURL] absoluteString]; 43 | return [[[[INAPIManager shared] AF] requestSerializer] requestWithMethod:@"DELETE" URLString:url parameters:nil error:&error]; 44 | } 45 | 46 | - (void)applyLocally 47 | { 48 | INDraft * draft = (INDraft *)[self model]; 49 | [[INDatabaseManager shared] unpersistModel: draft willResaveSameModel: NO]; 50 | 51 | INThread * thread = [draft thread]; 52 | if (thread) { 53 | [thread removeDraftID: [draft ID]]; 54 | [[INDatabaseManager shared] persistModel: thread]; 55 | } 56 | } 57 | 58 | - (void)applyRemotelyWithCallback:(CallbackBlock)callback 59 | { 60 | // If we're deleting a draft that was never synced to the server, 61 | // there's no need for an API call. Just return. 62 | if ([self.model isUnsynced]) 63 | callback(self, YES); 64 | else 65 | [super applyRemotelyWithCallback: callback]; 66 | } 67 | 68 | - (void)rollbackLocally 69 | { 70 | // re-persist the message to the database 71 | INDraft * draft = (INDraft *)[self model]; 72 | [[INDatabaseManager shared] persistModel: draft]; 73 | 74 | INThread * thread = [draft thread]; 75 | if (thread) { 76 | [thread addDraftID: [draft ID]]; 77 | [[INDatabaseManager shared] persistModel: thread]; 78 | } 79 | } 80 | 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INFile.h: -------------------------------------------------------------------------------- 1 | // 2 | // INAttachment.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/21/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INModelObject.h" 10 | #import "INAPIManager.h" 11 | 12 | @class INUploadFileTask; 13 | 14 | typedef void (^ AttachmentDownloadBlock)(NSError * error, NSData * data); 15 | 16 | /** INAttachment represents a single attachment on an email message. Attachments 17 | may be created, uploaded and attached to drafts, or fetched from the server for 18 | existing messages. 19 | */ 20 | @interface INFile : INModelObject 21 | 22 | @property (nonatomic, strong) NSString * localDataPath; 23 | @property (nonatomic, strong) UIImage * localPreview; 24 | @property (nonatomic, strong) NSString * filename; 25 | @property (nonatomic, strong) NSString * mimetype; 26 | 27 | /** 28 | Initialize an INAttachment with the provided image as a JPG. Images may be 29 | compressed and/or downsized automatically if they are too large. 30 | 31 | @param image The image to upload. 32 | 33 | @param namespace The namespace to add this attachment in. 34 | 35 | @return An initialized INAttachment object. To start uploading this attachment, you need 36 | to call -upload. 37 | */ 38 | - (id)initWithImage:(UIImage*)image inNamespace:(INNamespace*)namespace; 39 | 40 | /** 41 | Initialize an INAttachment with the arbitrary file data provided. 42 | 43 | @param filename The display filename of the attachment. 44 | @param mimetype The mimetype of the attachment, such as "image/jpeg" or "text/plain". 45 | @param data The attachment data. 46 | @param previewOrNil A small UIImage to use for displaying this attachment in your app. 47 | If you don't provide a preview image, subsequent requests for this attachment's localPreview 48 | may return nil. 49 | @param namespace The namespace to add this attachment in. 50 | 51 | @return An initialized INAttachment object. To start uploading this attachment, you need 52 | to call -upload. 53 | */ 54 | - (id)initWithFilename:(NSString*)filename mimetype:(NSString*)mimetype andData:(NSData*)data andPreview:(UIImage*)previewOrNil inNamespace:(INNamespace*)namespace; 55 | 56 | /** 57 | Start uploading this attachment in the background. To track the progress of the upload 58 | operation, observe the INUploadAttachmentTask returned from -uploadTask for the 59 | INTaskProgressNotification notification. 60 | */ 61 | - (void)upload; 62 | 63 | /** 64 | @return The upload task that is currently trying to upload this attachment object. To track 65 | the progress of this upload, listen for INTaskProgressNotification notifications for this 66 | object. 67 | */ 68 | - (INUploadFileTask*)uploadTask; 69 | 70 | /** 71 | Asynchronously fetches the attachment data from the server. 72 | 73 | @param callback The callback to invoke when attachment data has been successfully downloaded, 74 | or an error has occurred. 75 | */ 76 | - (void)getDataWithCallback:(AttachmentDownloadBlock)callback; 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INMessage.m: -------------------------------------------------------------------------------- 1 | // 2 | // INMessage.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/30/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INMessage.h" 10 | #import "INThread.h" 11 | #import "INFile.h" 12 | #import "NSString+FormatConversion.h" 13 | #import "INModelObject+Uniquing.h" 14 | #import "INNamespace.h" 15 | #import "INMarkMessageAsReadTask.h" 16 | 17 | @implementation INMessage 18 | 19 | 20 | + (NSMutableDictionary *)resourceMapping 21 | { 22 | NSMutableDictionary * mapping = [super resourceMapping]; 23 | [mapping addEntriesFromDictionary:@{ 24 | @"subject": @"subject", 25 | @"body": @"body", 26 | @"snippet": @"snippet", 27 | @"threadID": @"thread_id", 28 | @"date": @"date", 29 | @"from": @"from", 30 | @"to": @"to", 31 | @"cc": @"cc", 32 | @"bcc": @"bcc", 33 | @"unread": @"unread", 34 | }]; 35 | return mapping; 36 | } 37 | 38 | + (NSString *)resourceAPIName 39 | { 40 | return @"messages"; 41 | } 42 | 43 | + (NSArray *)databaseIndexProperties 44 | { 45 | return [[super databaseIndexProperties] arrayByAddingObjectsFromArray: @[@"threadID", @"subject", @"date"]]; 46 | } 47 | 48 | - (INThread*)thread 49 | { 50 | if (!_threadID) 51 | return nil; 52 | return [INThread instanceWithID: [self threadID] inNamespaceID: [self namespaceID]]; 53 | } 54 | 55 | - (NSMutableDictionary *)resourceDictionary 56 | { 57 | NSMutableDictionary * dict = [super resourceDictionary]; 58 | NSMutableArray * files = [NSMutableArray array]; 59 | NSMutableArray * fileIDs = [NSMutableArray array]; 60 | for (INFile * file in _files) { 61 | [files addObject: [file resourceDictionary]]; 62 | [fileIDs addObject: [file ID]]; 63 | } 64 | [dict setObject:files forKey:@"files"]; 65 | [dict setObject:fileIDs forKey:@"file_ids"]; 66 | 67 | return dict; 68 | } 69 | 70 | - (void)updateWithResourceDictionary:(NSDictionary *)dict 71 | { 72 | [super updateWithResourceDictionary: dict]; 73 | 74 | NSMutableArray * files = [NSMutableArray array]; 75 | if (dict[@"files"]) { 76 | for (NSDictionary * fileDict in dict[@"files"]) { 77 | INFile * file = [INFile attachedInstanceMatchingID: fileDict[@"id"] createIfNecessary:YES didCreate: NULL]; 78 | [file updateWithResourceDictionary: fileDict]; 79 | [files addObject: file]; 80 | } 81 | } else { 82 | for (NSDictionary * ID in dict[@"file_ids"]) { 83 | INFile * file = [INFile attachedInstanceMatchingID: ID createIfNecessary:YES didCreate: NULL]; 84 | [files addObject: file]; 85 | } 86 | } 87 | _files = [NSArray arrayWithArray: files]; 88 | } 89 | 90 | - (void)markAsRead 91 | { 92 | if (self.unread == NO) 93 | return; 94 | 95 | INMarkMessageAsReadTask * task = [[INMarkMessageAsReadTask alloc] initWithModel: self]; 96 | [[INAPIManager shared] queueTask: task]; 97 | } 98 | 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INNamespace.h: -------------------------------------------------------------------------------- 1 | // 2 | // INNamespace.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/28/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INModelObject.h" 10 | #import "INModelProvider.h" 11 | 12 | @class INThreadProvider; 13 | @class INMessageProvider; 14 | 15 | /** 16 | Namespaces are an important concept in Inbox. Typically, a user authenticates with 17 | the Inbox API and the access token you are given provides access to one namespace: 18 | the user's email account. Threads, messages, contacts, files, etc. live within the 19 | namespace, and the INNamespace object provides convenience methods for creating 20 | model providers for these types. You can also use the properties on INNamespace 21 | to determine the user's email address, check Inbox sync status, and more. 22 | 23 | Note: In the future, a single access token may grant you access to multiple namespaces, 24 | and a namespace may not always be an entire email account. 25 | */ 26 | @interface INNamespace : INModelObject 27 | 28 | @property (nonatomic, strong) NSString * emailAddress; 29 | @property (nonatomic, strong) NSString * name; 30 | @property (nonatomic, strong) NSString * provider; 31 | @property (nonatomic, strong) NSString * status; 32 | @property (nonatomic, strong) NSArray * scope; 33 | @property (nonatomic, strong) NSDate * lastSync; 34 | 35 | /** 36 | Initializes and returns a new INModelProvider for displaying contacts in this namespace. 37 | To display contacts matching certain criteria, create a new contact provider using this method 38 | and then set it's itemFilterPredicate to narrow the models it provides. 39 | @return An initialized INModelProvider for displaying contacts. 40 | */ 41 | - (INModelProvider *)newContactProvider; 42 | 43 | /** 44 | Initializes and returns a new INThreadProvider for displaying threads in this namespace. 45 | To display threads matching certain criteria, create a new thread provider using this method 46 | and then set it's itemFilterPredicate to narrow the models it provides. 47 | @return An initialized INThreadProvider for displaying threads. 48 | */ 49 | - (INThreadProvider *)newThreadProvider; 50 | 51 | /** 52 | Initializes and returns a new INModelProvider for displaying tags in this namespace. 53 | @return An initialized INModelProvider for displaying tags. 54 | */ 55 | - (INModelProvider *)newTagProvider; 56 | 57 | /** 58 | Initializes and returns a new INMessageProvider for displaying drafts in this namespace. 59 | @return An initialized INMessageProvider for displaying drafts. 60 | */ 61 | - (INMessageProvider *)newDraftsProvider; 62 | 63 | /** 64 | Initializes and returns a new INMessageProvider for displaying messages in this namespace. 65 | Note that threads, not messages, are the objects you most often apply changes to. In many 66 | cases, you should use threads instead of messages. For example, you archive a thread, not 67 | a message. 68 | @return An initialized INMessageProvider for displaying messages. 69 | */ 70 | - (INMessageProvider *)newMessageProvider; 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/INAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // INAppDelegate.m 3 | // Snapmail 4 | // 5 | // Created by Ben Gotow on 6/16/14. 6 | // Copyright (c) 2014 InboxApp, Inc. All rights reserved. 7 | // 8 | 9 | #import "INAppDelegate.h" 10 | #import "INAPIManager.h" 11 | 12 | @implementation INAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | _viewController = [[INHomeViewController alloc] init]; 17 | 18 | [[UIApplication sharedApplication] setStatusBarStyle: UIStatusBarStyleLightContent]; 19 | [[UINavigationBar appearance] setBarTintColor: [UIColor colorWithRed:78/255.0 green:168/255.0 blue:133/255.0 alpha:1]]; 20 | [[UINavigationBar appearance] setTintColor: [UIColor whiteColor]]; 21 | [[UINavigationBar appearance] setTitleTextAttributes: @{NSForegroundColorAttributeName: [UIColor whiteColor]}]; 22 | 23 | self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; 24 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController: _viewController];; 25 | [self.window makeKeyAndVisible]; 26 | 27 | return YES; 28 | } 29 | 30 | - (void)applicationWillResignActive:(UIApplication *)application 31 | { 32 | // 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. 33 | // 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. 34 | } 35 | 36 | - (void)applicationDidEnterBackground:(UIApplication *)application 37 | { 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 | { 44 | // 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. 45 | } 46 | 47 | - (void)applicationDidBecomeActive:(UIApplication *)application 48 | { 49 | // 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. 50 | } 51 | 52 | - (void)applicationWillTerminate:(UIApplication *)application 53 | { 54 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 55 | } 56 | 57 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation 58 | { 59 | return [[INAPIManager shared] handleURL: url]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/INCaptureViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // INCaptureViewController.m 3 | // Snapmail 4 | // 5 | // Created by Ben Gotow on 6/16/14. 6 | // Copyright (c) 2014 InboxApp, Inc. All rights reserved. 7 | // 8 | 9 | #import "INCaptureViewController.h" 10 | #import "INContactsViewController.h" 11 | 12 | @implementation INCaptureViewController 13 | 14 | - (id)initWithThread:(INThread*)thread 15 | { 16 | self = [super init]; 17 | if (self) { 18 | _thread = thread; 19 | 20 | } 21 | return self; 22 | } 23 | 24 | - (void)viewDidLoad 25 | { 26 | [super viewDidLoad]; 27 | 28 | [_toggleSideButton setHidden: ![UIImagePickerController isCameraDeviceAvailable: UIImagePickerControllerCameraDeviceFront]]; 29 | } 30 | 31 | - (void)didReceiveMemoryWarning 32 | { 33 | [super didReceiveMemoryWarning]; 34 | } 35 | 36 | - (IBAction)toggleSideTapped:(id)sender 37 | { 38 | if (_picker.cameraDevice == UIImagePickerControllerCameraDeviceFront) 39 | [_picker setCameraDevice: UIImagePickerControllerCameraDeviceRear]; 40 | else 41 | [_picker setCameraDevice: UIImagePickerControllerCameraDeviceFront]; 42 | 43 | } 44 | 45 | - (IBAction)cancelTapped:(id)sender 46 | { 47 | [_picker dismissViewControllerAnimated:YES completion:NULL]; 48 | } 49 | 50 | - (IBAction)captureTapped:(id)sender 51 | { 52 | [_picker takePicture]; 53 | } 54 | 55 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info 56 | { 57 | UIImage * img = [info objectForKey: UIImagePickerControllerOriginalImage]; 58 | 59 | if (!_thread) { 60 | INNamespace * namespace = [[[INAPIManager shared] namespaces] firstObject]; 61 | INContactsViewController * contacts = [[INContactsViewController alloc] initForSelectingContactInNamespace:namespace withCallback:^(NSArray * contacts) { 62 | [self sendImage: img to: contacts]; 63 | [picker dismissViewControllerAnimated:NO completion:NULL]; 64 | [picker dismissViewControllerAnimated:NO completion:NULL]; 65 | }]; 66 | UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController: contacts]; 67 | [picker presentViewController: nav animated: NO completion: NULL]; 68 | 69 | } else { 70 | [self sendImage: img to:nil]; 71 | [picker dismissViewControllerAnimated:YES completion:NULL]; 72 | } 73 | } 74 | 75 | - (void)sendImage:(UIImage*)image to:(NSArray*)contacts 76 | { 77 | INNamespace * namespace = [[[INAPIManager shared] namespaces] firstObject]; 78 | INDraft * draft = nil; 79 | 80 | if (_thread) { 81 | draft = [[INDraft alloc] initInNamespace:namespace inReplyTo:_thread]; 82 | } else { 83 | draft = [[INDraft alloc] initInNamespace:namespace]; 84 | [draft setSubject: @"You've got a new snap!"]; 85 | } 86 | 87 | INFile * file = [[INFile alloc] initWithImage:image inNamespace:namespace]; 88 | [file upload]; 89 | [draft addAttachment: file]; 90 | 91 | if (contacts) 92 | [draft setTo: @[@{@"name": [[contacts firstObject] name], @"email": @"bengotow@gmail.com"}]]; 93 | 94 | [draft send]; 95 | 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /InboxFramework/InboxUI/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 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/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 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INFile.m: -------------------------------------------------------------------------------- 1 | // 2 | // INAttachment.m 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/21/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INFile.h" 10 | #import "INModelObject+Uniquing.h" 11 | #import "INNamespace.h" 12 | #import "INUploadFileTask.h" 13 | 14 | @implementation INFile 15 | 16 | + (NSMutableDictionary *)resourceMapping 17 | { 18 | NSMutableDictionary * mapping = [super resourceMapping]; 19 | [mapping addEntriesFromDictionary: @{ 20 | @"filename": @"filename", 21 | @"mimetype": @"mimetype", 22 | 23 | @"localDataPath": @"__localDataPath", 24 | }]; 25 | return mapping; 26 | } 27 | 28 | + (NSString *)resourceAPIName 29 | { 30 | return @"files"; 31 | } 32 | 33 | - (id)initWithImage:(UIImage*)image inNamespace:(INNamespace*)namespace 34 | { 35 | return [self initWithFilename:@"image.jpg" mimetype:@"image/jpeg" andData:UIImageJPEGRepresentation(image, 0.85) andPreview: image inNamespace:namespace]; 36 | } 37 | 38 | - (id)initWithFilename:(NSString*)filename mimetype:(NSString*)mimetype andData:(NSData*)data andPreview:(UIImage*)previewOrNil inNamespace:(INNamespace*)namespace 39 | { 40 | NSAssert(data, @"You must provide attachment data."); 41 | NSAssert(filename, @"You must provide an attachment filename."); 42 | NSAssert(mimetype, @"You must provide an attachment mimetype."); 43 | NSAssert(namespace, @"You must provide an attachment namespace."); 44 | 45 | self = [super init]; 46 | if (self) { 47 | self.namespaceID = [namespace ID]; 48 | self.filename = filename; 49 | self.mimetype = mimetype; 50 | self.localPreview = previewOrNil; 51 | self.localDataPath = [[NSString stringWithFormat: @"~/Documents/%@.data", self.ID] stringByExpandingTildeInPath]; 52 | [data writeToFile: _localDataPath atomically:NO]; 53 | [INFile attachInstance: self]; 54 | 55 | } 56 | return self; 57 | } 58 | 59 | - (UIImage*)localPreview 60 | { 61 | if (_localPreview) 62 | return _localPreview; 63 | 64 | // TODO: Return previews 65 | return nil; 66 | } 67 | 68 | - (void)upload 69 | { 70 | NSAssert(_localDataPath, @"Before calling -upload, you need to use one of the designated initializers to provide a reference to data to upload."); 71 | if ([self uploadTask]) 72 | return; 73 | 74 | INUploadFileTask * upload = [[INUploadFileTask alloc] initWithModel: self]; 75 | [[INAPIManager shared] queueTask: upload]; 76 | } 77 | 78 | - (INUploadFileTask*)uploadTask 79 | { 80 | for (INUploadFileTask * change in [[INAPIManager shared] taskQueue]) { 81 | if ([change isKindOfClass: [INUploadFileTask class]] && [[change model] isEqual: self]) 82 | return change; 83 | } 84 | return nil; 85 | } 86 | 87 | - (void)getDataWithCallback:(AttachmentDownloadBlock)callback 88 | { 89 | NSString * path = [NSString stringWithFormat:@"/n/%@/files/%@/download", self.namespaceID, self.ID]; 90 | AFHTTPRequestOperation * op = [[INAPIManager shared].AF GET:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { 91 | callback(nil, responseObject); 92 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 93 | callback(error, nil); 94 | }]; 95 | [op setResponseSerializer: [AFHTTPResponseSerializer serializer]]; 96 | } 97 | 98 | @end 99 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/INHomeViewController.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 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /InboxExamples/Triage/Triage/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 -------------------------------------------------------------------------------- /InboxFramework/Inbox/Helpers/INModelResponseSerializer.m: -------------------------------------------------------------------------------- 1 | // 2 | // INModelArrayResponseSerializer.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/28/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INModelResponseSerializer.h" 10 | #import "INModelObject.h" 11 | #import "INModelObject+Uniquing.h" 12 | #import "INDatabaseManager.h" 13 | #import "NSError+InboxErrors.h" 14 | 15 | 16 | @implementation INModelResponseSerializer 17 | 18 | - (id)initWithModelClass:(Class)klass 19 | { 20 | self = [super init]; 21 | 22 | if (self) { 23 | _modelClass = klass; 24 | self.readingOptions = NSJSONReadingAllowFragments; 25 | } 26 | return self; 27 | } 28 | 29 | - (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError * __autoreleasing *)error 30 | { 31 | id responseObject = [super responseObjectForResponse:response data:data error:error]; 32 | if (!responseObject || (error && *error)) 33 | return nil; 34 | 35 | BOOL badJSONClass = ([responseObject isKindOfClass:[NSArray class]] == NO); 36 | BOOL badAPIResponse = ([responseObject isKindOfClass: [NSDictionary class]] && [responseObject[@"type"] isEqualToString: @"api_error"]); 37 | 38 | if (badAPIResponse) { 39 | if (error) 40 | *error = [NSError inboxErrorWithDescription: responseObject[@"message"]]; 41 | return nil; 42 | } 43 | 44 | if (badJSONClass) { 45 | if (error) 46 | *error = [NSError inboxErrorWithDescription: @"The JSON object returned was not an NSArray"]; 47 | return nil; 48 | } 49 | 50 | NSMutableArray * models = [NSMutableArray array]; 51 | NSMutableArray * modifiedOrUnloadedModels = [NSMutableArray array]; 52 | NSMutableArray * modelsToDelete = [NSMutableArray arrayWithArray: _modelsCurrentlyMatching]; 53 | 54 | dispatch_sync(dispatch_get_main_queue(), ^{ 55 | for (NSDictionary * modelDictionary in responseObject) { 56 | BOOL created = NO; 57 | 58 | // TODO: Remove this so it continues and throw an exception 59 | if (!modelDictionary[@"id"] || [modelDictionary[@"id"] isKindOfClass: [NSNull class]]) 60 | continue; 61 | 62 | INModelObject * object = [_modelClass attachedInstanceMatchingID: modelDictionary[@"id"] createIfNecessary: YES didCreate: &created]; 63 | [modelsToDelete removeObject: object]; 64 | 65 | if (created) { 66 | // If we don't have a copy of the object in memory, inflate one and write it 67 | // to the database. Note that these will probably be freed shortly. 68 | [object updateWithResourceDictionary:modelDictionary]; 69 | [object setup]; 70 | [modifiedOrUnloadedModels addObject: object]; 71 | 72 | } else { 73 | // If we have a copy of the object in memory already, let's be smart. Only apply 74 | // changes to the model if necessary. This prevents unnecessary notifications from 75 | // being fired, animations in the interface, etc. 76 | if ([object differentFromResourceDictionary: modelDictionary]) { 77 | [object updateWithResourceDictionary:modelDictionary]; 78 | [modifiedOrUnloadedModels addObject: object]; 79 | } 80 | } 81 | 82 | [models addObject:object]; 83 | } 84 | }); 85 | 86 | // Save models to our local database. The database will notify it's 87 | // observers that these models have been saved, and INModelProviders 88 | // will automatically compute alterations, triggering UI updates. 89 | [[INDatabaseManager shared] unpersistModels: modelsToDelete]; 90 | [[INDatabaseManager shared] persistModels: modifiedOrUnloadedModels]; 91 | _modelsCurrentlyMatching = nil; 92 | 93 | return models; 94 | } 95 | 96 | @end 97 | 98 | -------------------------------------------------------------------------------- /InboxFramework/InboxFramework.xcodeproj/project.xcworkspace/xcshareddata/InboxFramework.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | ACCB086E-6EB2-4819-B467-341DB1D59551 9 | IDESourceControlProjectName 10 | InboxFramework 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 09397265-E7D3-47F9-B2E2-49FB444FD53C 14 | https://github.com/kiwi-bdd/Kiwi.git 15 | 59CC8397-C42A-4C3C-9FF0-7F23BB915152 16 | https://github.com/carlbrown/PDKeychainBindingsController.git 17 | 9499EAF2-4498-47F3-8D0D-7B109B9BD5D0 18 | https://github.com/AFNetworking/AFNetworking.git 19 | AD710768-7887-4862-9ECC-7B8F562C1700 20 | https://github.com/ccgus/fmdb.git 21 | F91478D6-B52C-42BD-924C-4C00BF902D59 22 | ssh://github.com/inboxapp/inbox-ios.git 23 | 24 | IDESourceControlProjectPath 25 | InboxFramework/InboxFramework.xcodeproj/project.xcworkspace 26 | IDESourceControlProjectRelativeInstallPathDictionary 27 | 28 | 09397265-E7D3-47F9-B2E2-49FB444FD53C 29 | ../../Submodules/Kiwi 30 | 59CC8397-C42A-4C3C-9FF0-7F23BB915152 31 | ../../Submodules/PDKeychainBindingsController 32 | 9499EAF2-4498-47F3-8D0D-7B109B9BD5D0 33 | ../../Submodules/AFNetworking 34 | AD710768-7887-4862-9ECC-7B8F562C1700 35 | ../../Submodules/FMDB 36 | F91478D6-B52C-42BD-924C-4C00BF902D59 37 | ../../.. 38 | 39 | IDESourceControlProjectURL 40 | ssh://github.com/inboxapp/inbox-ios.git 41 | IDESourceControlProjectVersion 42 | 110 43 | IDESourceControlProjectWCCIdentifier 44 | F91478D6-B52C-42BD-924C-4C00BF902D59 45 | IDESourceControlProjectWCConfigurations 46 | 47 | 48 | IDESourceControlRepositoryExtensionIdentifierKey 49 | public.vcs.git 50 | IDESourceControlWCCIdentifierKey 51 | 9499EAF2-4498-47F3-8D0D-7B109B9BD5D0 52 | IDESourceControlWCCName 53 | AFNetworking 54 | 55 | 56 | IDESourceControlRepositoryExtensionIdentifierKey 57 | public.vcs.git 58 | IDESourceControlWCCIdentifierKey 59 | AD710768-7887-4862-9ECC-7B8F562C1700 60 | IDESourceControlWCCName 61 | FMDB 62 | 63 | 64 | IDESourceControlRepositoryExtensionIdentifierKey 65 | public.vcs.git 66 | IDESourceControlWCCIdentifierKey 67 | F91478D6-B52C-42BD-924C-4C00BF902D59 68 | IDESourceControlWCCName 69 | Inbox-iOS 70 | 71 | 72 | IDESourceControlRepositoryExtensionIdentifierKey 73 | public.vcs.git 74 | IDESourceControlWCCIdentifierKey 75 | 09397265-E7D3-47F9-B2E2-49FB444FD53C 76 | IDESourceControlWCCName 77 | Kiwi 78 | 79 | 80 | IDESourceControlRepositoryExtensionIdentifierKey 81 | public.vcs.git 82 | IDESourceControlWCCIdentifierKey 83 | 59CC8397-C42A-4C3C-9FF0-7F23BB915152 84 | IDESourceControlWCCName 85 | PDKeychainBindingsController 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/INCaptureViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 28 | 38 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INThread.m: -------------------------------------------------------------------------------- 1 | // 2 | // INThread.m 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/28/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INThread.h" 10 | #import "INTag.h" 11 | #import "INAddRemoveTagsTask.h" 12 | 13 | @implementation INThread 14 | 15 | + (NSMutableDictionary *)resourceMapping 16 | { 17 | NSMutableDictionary * mapping = [super resourceMapping]; 18 | 19 | [mapping addEntriesFromDictionary:@{ 20 | @"subject": @"subject", 21 | @"participants": @"participants", 22 | @"lastMessageDate": @"last_message_timestamp", 23 | @"messageIDs": @"message_ids", 24 | @"draftIDs": @"draft_ids", 25 | @"tagIDs": @"tags", 26 | @"snippet": @"snippet" 27 | }]; 28 | return mapping; 29 | } 30 | 31 | + (NSString *)resourceAPIName 32 | { 33 | return @"threads"; 34 | } 35 | 36 | - (NSArray*)tags 37 | { 38 | NSMutableArray * tags = [NSMutableArray array]; 39 | for (NSString * ID in [self tagIDs]) 40 | [tags addObject: [INTag tagWithID: ID]]; 41 | return tags; 42 | } 43 | 44 | - (void)updateWithResourceDictionary:(NSDictionary *)dict 45 | { 46 | [super updateWithResourceDictionary:dict]; 47 | if ([[[self tagIDs] firstObject] isKindOfClass: [NSDictionary class]]) 48 | _tagIDs = [_tagIDs valueForKey: @"id"]; 49 | } 50 | 51 | - (void)setTagIDs:(NSArray *)tagIDs 52 | { 53 | NSMutableArray * unique = [NSMutableArray array]; 54 | for (NSString * ID in tagIDs) 55 | if (![unique containsObject: ID]) 56 | [unique addObject: ID]; 57 | _tagIDs = unique; 58 | } 59 | 60 | - (BOOL)hasTagWithID:(NSString*)ID 61 | { 62 | return [[self tagIDs] containsObject: ID]; 63 | } 64 | 65 | - (INMessageProvider*)newMessageProvider 66 | { 67 | return [[INMessageProvider alloc] initForMessagesInThread: [self ID] andNamespaceID:[self namespaceID]]; 68 | } 69 | 70 | - (INModelProvider*)newDraftProvider 71 | { 72 | return [[INMessageProvider alloc] initForDraftsInThread:[self ID] andNamespaceID:[self namespaceID]]; 73 | } 74 | 75 | #pragma mark Database 76 | 77 | + (NSArray *)databaseIndexProperties 78 | { 79 | return [[super databaseIndexProperties] arrayByAddingObjectsFromArray: @[@"lastMessageDate", @"subject"]]; 80 | } 81 | 82 | + (NSArray *)databaseJoinTableProperties 83 | { 84 | return @[@"tagIDs"]; 85 | } 86 | 87 | #pragma mark Operations on Threads 88 | 89 | - (void)archive 90 | { 91 | INAddRemoveTagsTask * task = [INAddRemoveTagsTask operationForModel: self]; 92 | [[task tagIDsToRemove] addObject: INTagIDInbox]; 93 | [[task tagIDsToAdd] addObject: INTagIDArchive]; 94 | [[INAPIManager shared] queueTask: task]; 95 | } 96 | 97 | - (void)unarchive 98 | { 99 | INAddRemoveTagsTask * task = [INAddRemoveTagsTask operationForModel: self]; 100 | [[task tagIDsToRemove] addObject: INTagIDArchive]; 101 | [[task tagIDsToAdd] addObject: INTagIDInbox]; 102 | [[INAPIManager shared] queueTask: task]; 103 | } 104 | 105 | - (void)star 106 | { 107 | INAddRemoveTagsTask * task = [INAddRemoveTagsTask operationForModel: self]; 108 | [[task tagIDsToAdd] addObject: INTagIDStarred]; 109 | [[INAPIManager shared] queueTask: task]; 110 | } 111 | 112 | - (void)unstar 113 | { 114 | INAddRemoveTagsTask * task = [INAddRemoveTagsTask operationForModel: self]; 115 | [[task tagIDsToRemove] addObject: INTagIDStarred]; 116 | [[INAPIManager shared] queueTask: task]; 117 | } 118 | 119 | - (void)markAsRead 120 | { 121 | if ([self hasTagWithID: INTagIDUnread]) { 122 | INAddRemoveTagsTask * task = [INAddRemoveTagsTask operationForModel: self]; 123 | [[task tagIDsToRemove] addObject: INTagIDUnread]; 124 | [[INAPIManager shared] queueTask: task]; 125 | } 126 | } 127 | 128 | - (void)markAsSeen 129 | { 130 | if ([self hasTagWithID: INTagIDUnseen]) { 131 | INAddRemoveTagsTask * task = [INAddRemoveTagsTask operationForModel: self]; 132 | [[task tagIDsToRemove] addObject: INTagIDUnseen]; 133 | [[INAPIManager shared] queueTask: task]; 134 | } 135 | } 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/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 | UIBarButtonItem * right = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed: @"send-button.png"] style:UIBarButtonItemStyleBordered target:self action:@selector(sendTapped:)]; 34 | [[self navigationItem] setRightBarButtonItem: right]; 35 | 36 | [_tableView setRowHeight: 50]; 37 | 38 | self.contactsProvider = [self.namespace newContactProvider]; 39 | [_contactsProvider setItemSortDescriptors: @[[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES]]]; 40 | [_contactsProvider setDelegate:self]; 41 | [_contactsProvider refresh]; 42 | } 43 | 44 | - (IBAction)cancelTapped:(id)sender 45 | { 46 | [self dismissViewControllerAnimated:YES completion:NULL]; 47 | } 48 | 49 | - (IBAction)sendTapped:(id)sender 50 | { 51 | if (!_contactSelectionCallback) 52 | return; 53 | 54 | NSMutableArray * contacts = [NSMutableArray array]; 55 | for (NSIndexPath * ip in [_tableView indexPathsForSelectedRows]) { 56 | INContact * contact = [[_contactsProvider items] objectAtIndex: [ip row]]; 57 | [contacts addObject: contact]; 58 | } 59 | _contactSelectionCallback(contacts); 60 | } 61 | 62 | #pragma Provider Delegate 63 | 64 | - (void)provider:(INModelProvider*)provider dataAltered:(INModelProviderChangeSet *)changeSet 65 | { 66 | [_tableView beginUpdates]; 67 | [_tableView deleteRowsAtIndexPaths:[changeSet indexPathsFor: INModelProviderChangeRemove] withRowAnimation:UITableViewRowAnimationAutomatic]; 68 | [_tableView insertRowsAtIndexPaths:[changeSet indexPathsFor: INModelProviderChangeAdd] withRowAnimation:UITableViewRowAnimationAutomatic]; 69 | [_tableView endUpdates]; 70 | [_tableView reloadRowsAtIndexPaths:[changeSet indexPathsFor: INModelProviderChangeUpdate] withRowAnimation:UITableViewRowAnimationNone]; 71 | } 72 | 73 | - (void)provider:(INModelProvider*)provider dataFetchFailed:(NSError *)error 74 | { 75 | [[[UIAlertView alloc] initWithTitle:@"Error!" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show]; 76 | } 77 | 78 | - (void)providerDataChanged:(INModelProvider*)provider 79 | { 80 | [_tableView reloadData]; 81 | } 82 | 83 | 84 | #pragma mark Table View Data 85 | 86 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 87 | { 88 | return [[_contactsProvider items] count]; 89 | } 90 | 91 | - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 92 | { 93 | UITableViewCell * cell = (UITableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"contact"]; 94 | if (!cell) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"contact"]; 95 | 96 | INContact * contact = [[_contactsProvider items] objectAtIndex: [indexPath row]]; 97 | NSString * name = [contact name]; 98 | if ([name length] == 0) 99 | name = [[[contact email] componentsSeparatedByString:@"@"] firstObject]; 100 | 101 | [[cell textLabel] setText: name]; 102 | [[cell detailTextLabel] setText: [contact email]]; 103 | [[cell detailTextLabel] setTextColor: [UIColor grayColor]]; 104 | return cell; 105 | } 106 | 107 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 108 | { 109 | } 110 | 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Categories/NSObject+Properties.h: -------------------------------------------------------------------------------- 1 | /* 2 | * NSObject+Properties.h 3 | * AQToolkit 4 | * 5 | * Created by Jim Dovey on 10/7/2008. 6 | * 7 | * Copyright (c) 2008-2009, Jim Dovey 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 14 | * Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * 17 | * Redistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in the 19 | * documentation and/or other materials provided with the distribution. 20 | * 21 | * Neither the name of this project's author nor the names of its 22 | * contributors may be used to endorse or promote products derived from 23 | * this software without specific prior written permission. 24 | * 25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 28 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 31 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 32 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 33 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 34 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 35 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | * 37 | */ 38 | 39 | #import 40 | #import 41 | 42 | // type notes: 43 | // I'm not 100% certain what @encode(NSString) would return. @encode(id) returns '@', 44 | // and the types of properties are actually encoded as such along with a strong-type 45 | // value following. Therefore, if you want to check for a specific class, you can 46 | // provide a type string of '@"NSString"'. The following macro will do this for you. 47 | #define statictype(x) "@\"" #x "\"" 48 | 49 | // Also, note that the runtime information doesn't include an atomicity hint, so we 50 | // can't determine that information 51 | 52 | @interface NSObject (AQProperties) 53 | 54 | + (BOOL)hasProperties; 55 | + (BOOL)hasPropertyNamed:(NSString *)name; 56 | + (BOOL)hasPropertyNamed:(NSString *)name ofType:(const char *)type; // an @encode() or statictype() type string 57 | + (NSString *)typeOfPropertyNamed:(NSString *)name; // returns an @encode() or statictype() string. Copy to keep 58 | + (SEL)getterForPropertyNamed:(NSString *)name; 59 | + (SEL)setterForPropertyNamed:(NSString *)name; 60 | + (NSString *)retentionMethodOfPropertyNamed:(NSString *)name; // returns one of: copy, retain, assign 61 | + (NSArray *)propertyNames; 62 | 63 | // instance convenience accessors for above routines (who likes to type [myObj class] all the time ?) 64 | - (BOOL)hasProperties; 65 | - (BOOL)hasPropertyNamed:(NSString *)name; 66 | - (BOOL)hasPropertyNamed:(NSString *)name ofType:(const char *)type; 67 | - (BOOL)hasPropertyForKVCKey:(NSString *)key; 68 | - (NSString *)typeOfPropertyNamed:(NSString *)name; 69 | - (SEL)getterForPropertyNamed:(NSString *)name; 70 | - (SEL)setterForPropertyNamed:(NSString *)name; 71 | - (NSString *)retentionMethodOfPropertyNamed:(NSString *)name; 72 | - (NSArray *)propertyNames; 73 | 74 | @end 75 | 76 | // Pure C API, adding to the existing API in objc/runtime.h. 77 | // The functions above are implemented in terms of these. 78 | 79 | // returns a static buffer - copy the string to retain it, as it will 80 | // be overwritten on the next call to this function 81 | NSString *property_getTypeString(objc_property_t property); 82 | 83 | // getter/setter functions: unlike those above, these will return NULL unless a getter/setter is EXPLICITLY defined 84 | SEL property_getGetter(objc_property_t property); 85 | 86 | SEL property_getSetter(objc_property_t property); 87 | 88 | // this returns a static (data-segment) string, so the caller does not need to call free() on the result 89 | const char *property_getRetentionMethod(objc_property_t property); 90 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INSaveDraftTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // INSaveDraftChange.m 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/16/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INSaveDraftTask.h" 10 | #import "INThread.h" 11 | #import "INThread+Private.h" 12 | #import "INTag.h" 13 | #import "INDeleteDraftTask.h" 14 | #import "INSendDraftTask.h" 15 | #import "INModelObject+Uniquing.h" 16 | #import "INUploadFileTask.h" 17 | 18 | @implementation INSaveDraftTask 19 | 20 | - (BOOL)canStartAfterTask:(INAPITask *)other 21 | { 22 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INDeleteDraftTask class]]) 23 | return NO; 24 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INSendDraftTask class]]) 25 | return NO; 26 | return YES; 27 | } 28 | 29 | - (BOOL)canCancelPendingTask:(INAPITask*)other 30 | { 31 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INSaveDraftTask class]]) 32 | return YES; 33 | return NO; 34 | } 35 | 36 | - (NSArray*)dependenciesIn:(NSArray *)others 37 | { 38 | NSMutableArray * dependencies = [NSMutableArray array]; 39 | INMessage * draft = (INMessage *)[self model]; 40 | 41 | // are any requests uploading files that are referenced in our draft? 42 | // we need to wait for those to finish... 43 | for (INAPITask * other in others) { 44 | if ([other isKindOfClass: [INUploadFileTask class]] && [[draft files] containsObject: [other model]]) 45 | [dependencies addObject: other]; 46 | } 47 | 48 | return dependencies; 49 | } 50 | 51 | - (NSURLRequest *)buildAPIRequest 52 | { 53 | NSAssert(self.model, @"INSaveDraftChange asked to buildRequest with no model!"); 54 | NSAssert([self.model namespaceID], @"INSaveDraftChange asked to buildRequest with no namespace!"); 55 | 56 | NSError * error = nil; 57 | NSString * path = nil; 58 | 59 | if ([self.model isUnsynced]) 60 | path = [NSString stringWithFormat:@"/n/%@/drafts", [self.model namespaceID]]; 61 | else 62 | path = [NSString stringWithFormat:@"/n/%@/drafts/%@", [self.model namespaceID], [self.model ID]]; 63 | 64 | NSString * url = [[NSURL URLWithString:path relativeToURL:[INAPIManager shared].AF.baseURL] absoluteString]; 65 | NSMutableDictionary * params = [[self.model resourceDictionary] mutableCopy]; 66 | [params removeObjectForKey: @"id"]; 67 | INThread * thread = [(INDraft*)self.model thread]; 68 | if (thread) [params setObject:[thread ID] forKey:@"replying_to_thread"]; 69 | 70 | return [[[[INAPIManager shared] AF] requestSerializer] requestWithMethod:@"POST" URLString:url parameters:params error:&error]; 71 | } 72 | 73 | - (void)handleSuccess:(AFHTTPRequestOperation *)operation withResponse:(id)responseObject 74 | { 75 | if (![responseObject isKindOfClass: [NSDictionary class]]) 76 | return NSLog(@"SaveDraft weird response: %@", responseObject); 77 | 78 | INDraft * draft = (INDraft *)[self model]; 79 | NSString * draftInitialID = [draft ID]; 80 | 81 | // remove the draft from the local cache and then update it with the API response 82 | // and save it again. This is important, because the JSON that comes back gives the 83 | // draft a different ID and we want to replace the old draft since it's outdated. 84 | [[INDatabaseManager shared] unpersistModel: draft willResaveSameModel:YES]; 85 | 86 | [draft updateWithResourceDictionary: responseObject]; 87 | [[INDatabaseManager shared] persistModel: draft]; 88 | 89 | // if the draft ID changed, update our local cache so it has the new draft ID 90 | if (![draftInitialID isEqualToString: [draft ID]]) { 91 | INThread * thread = [draft thread]; 92 | [thread removeDraftID: draftInitialID]; 93 | [thread addDraftID: [draft ID]]; 94 | [[INDatabaseManager shared] persistModel: thread]; 95 | } 96 | } 97 | 98 | - (void)applyLocally 99 | { 100 | INDraft * draft = (INDraft *)[self model]; 101 | if ([draft thread]) { 102 | INThread * thread = [draft thread]; 103 | [thread addDraftID: [draft ID]]; 104 | [[INDatabaseManager shared] persistModel: thread]; 105 | } 106 | 107 | [[INDatabaseManager shared] persistModel: draft]; 108 | } 109 | 110 | - (void)rollbackLocally 111 | { 112 | // we deliberately do not roll back draft saves. They shouldn't ever be rejected 113 | // by the server, and we don't want to loose people's data under any circumstance. 114 | } 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /InboxExamples/Snapmail/Snapmail/INSnapViewController.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 | 41 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INDraft.h: -------------------------------------------------------------------------------- 1 | // 2 | // INDraft.h 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/22/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INMessage.h" 10 | #import "INFile.h" 11 | 12 | typedef enum : NSUInteger { 13 | INDraftStateUnsent, 14 | INDraftStateSending, 15 | INDraftStateSendingFailed, 16 | INDraftStateSent 17 | } INDraftState; 18 | 19 | /** 20 | Drafts are messages that may be sent, saved, and deleted. You can create new drafts 21 | using one of the designated initializers, save them, add files, and send them using 22 | the methods documented below. The Inbox framework takes care of persisting pending 23 | draft operations, ensuring that -save, -send and other methods are eventually persistent, 24 | even if they are called when the user is offline. 25 | 26 | To list drafts available in a namespace, retrieve an INMessageProvider from the namespace. 27 | 28 | Drafts receive a new ID each time they are saved. If your application is presenting a draft, 29 | be prepared for the draft's ID to change after calls to -save. You can monitor changes all 30 | Inbox objects by subscribing to INModelObjectChangedNotification. 31 | */ 32 | @interface INDraft : INMessage 33 | 34 | @property (nonatomic, strong) NSString * version; 35 | @property (nonatomic, strong) NSString * internalState; 36 | 37 | /* 38 | Initialize a new draft for sending a new message in the specified namespace. 39 | @return An initalized instance of INDraft 40 | */ 41 | - (id)initInNamespace:(INNamespace*)namespace; 42 | 43 | /* 44 | Initialize a new draft for replying to an existing thread. It's important to use 45 | this initializer when replying so the Inbox API can properly append the draft to 46 | the existing thread. 47 | 48 | @param The namespace to create the draft in 49 | @param The thread that this draft is being created in reply to. 50 | @return An initalized instance of INDraft 51 | */ 52 | - (id)initInNamespace:(INNamespace*)namespace inReplyTo:(INThread*)thread; 53 | 54 | /** 55 | Add a file to the thread. The file does not need to be fully uploaded 56 | to be attached to a draft. However, you should call [INFile upload] to start 57 | the upload process before adding the file to the draft. 58 | 59 | If files are still uploading when you call -save, the draft will not be 60 | saved to the server until files have finished uploading. 61 | 62 | @param file The INFile object to add to the draft. 63 | */ 64 | - (void)addFile:(INFile*)file; 65 | 66 | /** 67 | Add a file to the thread at a particular index. 68 | @param file The INFile object to add to the draft. 69 | @param index The index where the draft should be attached. 70 | */ 71 | - (void)addFile:(INFile*)file atIndex:(NSInteger)index; 72 | 73 | /** 74 | Remove the provided file from the draft. Note that you need to call -save 75 | to commit your changes to Inbox after modifying the draft. 76 | 77 | @param file The file to remove from the draft. 78 | */ 79 | - (void)removeFile:(INFile*)file; 80 | 81 | /** 82 | Remove the provided file from the draft. Note that you need to call -save 83 | to commit your changes to Inbox after modifying the draft. 84 | 85 | @param index The index of the file to remove from the draft. 86 | */ 87 | - (void)removeFileAtIndex:(NSInteger)index; 88 | 89 | /** 90 | Called internally when an file has finished uploading and it's ID has changed. 91 | @param ID The ID that was initially assigned to the INFile. 92 | @param uploadedID The ID assigned by the server that now represents the INFile. 93 | */ 94 | - (void)fileWithID:(NSString*)ID uploadedAs:(NSString*)uploadedID; 95 | 96 | /** 97 | The current state of the draft. See INDraftState for a list of available states. 98 | */ 99 | - (INDraftState)state; 100 | 101 | #pragma mark Operations on Drafts 102 | 103 | /** 104 | Save the draft. This method updates the local cache to reflect 105 | the change immediately but may be performed later on the Inbox server if an internet 106 | connection is not available. 107 | 108 | If you've added files to the draft and files are still uploading, the 109 | draft will not be saved to the server until file uploads are complete. 110 | */ 111 | - (void)save; 112 | 113 | /** 114 | Send the draft. This method is eventually persistent and may 115 | be performed later if an internet connection is not available. 116 | 117 | Drafts will not be sent until all pending saves are complete and files have 118 | been uploaded. It's safe to call -save and -send back to back 119 | */ 120 | - (void)send; 121 | 122 | /** 123 | Delete the draft. This method updates the local cache to reflect 124 | the change immediately but may be performed later on the Inbox server if an internet 125 | connection is not available. 126 | 127 | Deleting a draft will cancel any pending save or send operations. 128 | */ 129 | - (void)delete; 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INDraft.m: -------------------------------------------------------------------------------- 1 | // 2 | // INDraft.m 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/22/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INDraft.h" 10 | #import "INNamespace.h" 11 | #import "INThread.h" 12 | #import "INSaveDraftTask.h" 13 | #import "INSendDraftTask.h" 14 | #import "INDeleteDraftTask.h" 15 | #import "INUploadFileTask.h" 16 | 17 | @implementation INDraft 18 | 19 | + (NSMutableDictionary *)resourceMapping 20 | { 21 | NSMutableDictionary * mapping = [super resourceMapping]; 22 | [mapping addEntriesFromDictionary:@{ @"internalState": @"state", @"version": @"version" }]; 23 | return mapping; 24 | } 25 | 26 | + (NSString *)resourceAPIName 27 | { 28 | return @"drafts"; 29 | } 30 | 31 | - (id)initInNamespace:(INNamespace*)namespace 32 | { 33 | NSAssert(namespace, @"initInNamespace: called with a nil namespace."); 34 | self = [super init]; 35 | if (self) { 36 | [self setFrom: @[@{@"email": [namespace emailAddress], @"name": [namespace emailAddress]}]]; 37 | [self setNamespaceID: [namespace ID]]; 38 | [self setDate: [NSDate date]]; 39 | } 40 | return self; 41 | } 42 | 43 | - (id)initInNamespace:(INNamespace*)namespace inReplyTo:(INThread*)thread 44 | { 45 | NSAssert(namespace, @"initInNamespace: called with a nil namespace."); 46 | NSAssert([thread isUnsynced] == NO, @"It looks like you're creating a new draft on a new thread. Instead of creating an INThread and then creating a draft on that thread, just create a new draft with [INDraft initInNamespace:]. A new thread will be created automatically when you send the draft!"); 47 | 48 | self = [self initInNamespace: namespace]; 49 | if (self) { 50 | NSMutableDictionary * recipients = [NSMutableDictionary dictionary]; 51 | for (NSDictionary * recipient in [thread participants]) { 52 | BOOL isMe = [[[INAPIManager shared] namespaceEmailAddresses] containsObject: recipient[@"email"]]; 53 | if (!isMe) 54 | [recipients setObject: recipient forKey: recipient[@"email"]]; 55 | } 56 | [self setTo: [recipients allValues]]; 57 | [self setSubject: thread.subject]; 58 | [self setThreadID: [thread ID]]; 59 | } 60 | return self; 61 | } 62 | 63 | - (void)addFile:(INFile*)file 64 | { 65 | [self addFile:file atIndex:0]; 66 | } 67 | 68 | - (void)addFile:(INFile*)file atIndex:(NSInteger)index 69 | { 70 | NSMutableArray * files = [self.files mutableCopy]; 71 | if (!files) files = [NSMutableArray array]; 72 | if (![files containsObject: file]) 73 | [files insertObject:file atIndex: index]; 74 | self.files = files; 75 | 76 | if ([file isUnsynced]) { 77 | if (![file uploadTask]) 78 | [file upload]; 79 | 80 | // we can't save with this file ID. Find the file upload task 81 | // and tell it to update us when the draft upload has finished. 82 | [[[file uploadTask] waitingDrafts] addObject: self]; 83 | } 84 | } 85 | 86 | - (void)removeFile:(INFile*)file 87 | { 88 | NSMutableArray * files = [self.files mutableCopy]; 89 | [files removeObject: file]; 90 | self.files = files; 91 | 92 | [[[file uploadTask] waitingDrafts] removeObject: self]; 93 | } 94 | 95 | - (void)removeFileAtIndex:(NSInteger)index 96 | { 97 | [self removeFile: [self.files objectAtIndex: index]]; 98 | } 99 | 100 | - (void)fileWithID:(NSString*)ID uploadedAs:(NSString*)uploadedID 101 | { 102 | // No longer necessary, since the files array references the same 103 | // INFile objects that are being modified. Not refactoring this away 104 | // completely, because it may be useful in the future. 105 | } 106 | 107 | - (INDraftState)state 108 | { 109 | if ([_internalState isEqualToString: @"sending"]) 110 | return INDraftStateSending; 111 | else if ([_internalState isEqualToString: @"sending_failed"]) 112 | return INDraftStateSendingFailed; 113 | else if ([_internalState isEqualToString: @"sent"]) 114 | return INDraftStateSent; 115 | return INDraftStateUnsent; 116 | } 117 | 118 | #pragma mark Operations on Drafts 119 | 120 | - (void)save 121 | { 122 | INSaveDraftTask * save = [INSaveDraftTask operationForModel: self]; 123 | [[INAPIManager shared] queueTask: save]; 124 | } 125 | 126 | - (void)send 127 | { 128 | // Always save before sending, so the API consumer doesn't have to worry about it. 129 | // If they call -save themselves, this new INSaveDraftTask should invalidate the 130 | // other one and prevent a duplicate save anyway. 131 | [self save]; 132 | 133 | INSendDraftTask * send = [INSendDraftTask operationForModel: self]; 134 | [[INAPIManager shared] queueTask: send]; 135 | } 136 | 137 | - (void)delete 138 | { 139 | INDeleteDraftTask * delete = [INDeleteDraftTask operationForModel: self]; 140 | [[INAPIManager shared] queueTask: delete]; 141 | } 142 | 143 | @end 144 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/Concrete Classes/INThread.h: -------------------------------------------------------------------------------- 1 | // 2 | // INThread.h 3 | // BigSur 4 | // 5 | // Created by Ben Gotow on 4/28/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INModelObject.h" 10 | #import "INModelProvider.h" 11 | #import "INMessageProvider.h" 12 | #import "INMessage.h" 13 | #import "INDraft.h" 14 | 15 | @class INTag; 16 | 17 | /** The INThread class provides a native wrapper around Inbox threads 18 | http://inboxapp.com/docs/api#threads 19 | 20 | Threads are the central object of the Inbox API and the INThread class 21 | provides convenience methods for performing many actions, such as tagging, 22 | archiving, and deleting threads. 23 | 24 | You should not create instances of INThread directly. Instead, obtain an 25 | INNamespace object (representing an email account) and use an INThreadProvider: 26 | 27 | NSPredicate * unread = [NSComparisonPredicate predicateWithFormat:@"ANY tagIDs = %@", INTagIDUnread]; 28 | INNamespace * namespace = [[[INAPIManager shared] namespaces] firstObject]; 29 | self.threadProvider = [namespace newThreadProvider]; 30 | self.threadProvider.itemFilterPredicate = unread; 31 | self.threadProvider.delegate = self; 32 | 33 | // Implement the INModelProviderDelegate protocol to get threads 34 | 35 | If you're looking to create a new thread, see INDraft. 36 | */ 37 | @interface INThread : INModelObject 38 | 39 | @property (nonatomic, strong) NSString * subject; 40 | @property (nonatomic, strong) NSString * snippet; 41 | @property (nonatomic, strong) NSArray * participants; 42 | @property (nonatomic, strong) NSDate * lastMessageDate; 43 | @property (nonatomic, strong) NSArray * messageIDs; 44 | @property (nonatomic, strong) NSArray * draftIDs; 45 | @property (nonatomic, strong) NSArray * tagIDs; 46 | @property (nonatomic, assign) BOOL unread; 47 | 48 | /* 49 | @return An array of INTag objects for the tags on this thread. 50 | */ 51 | - (NSArray*)tags; 52 | 53 | /* 54 | @return An array of NSString tag IDs for the tags on this thread. 55 | */ 56 | - (NSArray*)tagIDs; 57 | 58 | /* 59 | @param ID The ID to check for. 60 | @return YES if this thread has a tag with the given ID. 61 | */ 62 | - (BOOL)hasTagWithID:(NSString*)ID; 63 | 64 | /** 65 | Initializes and returns a new INModelProvider for displaying messages in this thread. 66 | To further filter the messages (for example, to show just messages with attachments in 67 | the thread), you can set the itemFilterPredicate on the returned provider. 68 | 69 | @return An initialized INModelProvider for displaying messages. 70 | */ 71 | - (INMessageProvider*)newMessageProvider; 72 | 73 | /** 74 | Initializes and returns a new INModelProvider for displaying drafts on this thread. 75 | @return An initialized INModelProvider for displaying drafts. 76 | */ 77 | - (INMessageProvider*)newDraftProvider; 78 | 79 | #pragma mark Operations on Threads 80 | 81 | /** 82 | Archive the thread. This method updates the local cache to reflect the change 83 | immediately but may be performed later on the Inbox server if an internet 84 | connection is not available. 85 | */ 86 | - (void)archive; 87 | 88 | /** 89 | Unarchive the thread. This method updates the local cache to reflect the change 90 | immediately but may be performed later on the Inbox server if an internet 91 | connection is not available. 92 | */ 93 | - (void)unarchive; 94 | 95 | /** 96 | Mark this thread as read. This method removes the 'unread' tag, and marks every 97 | message in the thread as read. If the thread does not have the unread tag, it 98 | has no effect. 99 | 100 | This method updates the local cache to reflect the change immediately but may be 101 | performed later on the Inbox server if an internet connection is not available. 102 | */ 103 | - (void)markAsRead; 104 | 105 | /** 106 | Mark this thread as seen. You should mark threads as "seen" as soon as the user 107 | views any part of the thread, or sees the thread subject and snippet onscreen. 108 | This indicates that the user has seen the thread and the thread should not be 109 | presented as "new" from another device or application. If the thread does not have 110 | the unseen tag, this method has no effect. 111 | 112 | Your application should use the unseen tag, rather than the unread tag, to highlight 113 | new threads the user may be interested in. 114 | 115 | Additional notes: 116 | - Threads become "unseen" again if new messages arrive in the thread. 117 | - Threads become seen automatically if you mark any of their messages as read. 118 | 119 | This method updates the local cache to reflect the change immediately but may be 120 | performed later on the Inbox server if an internet connection is not available. 121 | */ 122 | - (void)markAsSeen; 123 | 124 | /** 125 | Star the thread. On some email platforms, this is known as 'flagging'. 126 | 127 | This method updates the local cache to reflect the change immediately but may be 128 | performed later on the Inbox server if an internet connection is not available. 129 | */ 130 | - (void)star; 131 | 132 | /** 133 | Unstar the thread. On some email platforms, this is known as 'unflagging'. 134 | 135 | This method updates the local cache to reflect the change immediately but may be 136 | performed later on the Inbox server if an internet connection is not available. 137 | */ 138 | - (void)unstar; 139 | 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /InboxFramework/Inbox/Models/API Calls/INAddRemoveTagsTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // INAddRemoveTagsChange.m 3 | // InboxFramework 4 | // 5 | // Created by Ben Gotow on 5/16/14. 6 | // Copyright (c) 2014 Inbox. All rights reserved. 7 | // 8 | 9 | #import "INSaveTagTask.h" 10 | #import "INAddRemoveTagsTask.h" 11 | #import "INThread.h" 12 | #import "INMessage.h" 13 | #import "INTag.h" 14 | 15 | @implementation INAddRemoveTagsTask 16 | 17 | 18 | - (NSMutableArray *)tagIDsToAdd 19 | { 20 | if (!self.data[@"tagIDsToAdd"]) 21 | [self.data setObject: [NSMutableArray array] forKey:@"tagIDsToAdd"]; 22 | return self.data[@"tagIDsToAdd"]; 23 | } 24 | 25 | - (NSMutableArray *)tagIDsToRemove 26 | { 27 | if (!self.data[@"tagIDsToRemove"]) 28 | [self.data setObject: [NSMutableArray array] forKey:@"tagIDsToRemove"]; 29 | return self.data[@"tagIDsToRemove"]; 30 | } 31 | 32 | - (NSMutableArray *)tagIDStringsToAdd 33 | { 34 | NSMutableArray * tagIDStrings = [NSMutableArray array]; 35 | for (NSObject * tag in [self tagIDsToAdd]) { 36 | if ([tag isKindOfClass: [INTag class]]) { 37 | [tagIDStrings addObject: [(INTag*)tag ID]]; 38 | } else { 39 | [tagIDStrings addObject: (NSString*)tag]; 40 | } 41 | } 42 | return tagIDStrings; 43 | } 44 | 45 | - (NSMutableArray *)tagIDStringsToRemove 46 | { 47 | NSMutableArray * tagIDStrings = [NSMutableArray array]; 48 | for (NSObject * tag in [self tagIDsToRemove]) { 49 | if ([tag isKindOfClass: [INTag class]]) { 50 | [tagIDStrings addObject: [(INTag*)tag ID]]; 51 | } else { 52 | [tagIDStrings addObject: (NSString*)tag]; 53 | } 54 | } 55 | return tagIDStrings; 56 | } 57 | 58 | - (INThread*)thread 59 | { 60 | if ([self.model isKindOfClass: [INThread class]]) 61 | return (INThread*)self.model; 62 | if ([self.model isKindOfClass: [INMessage class]]) 63 | return [(INMessage*)self.model thread]; 64 | return nil; 65 | } 66 | 67 | - (NSURLRequest *)buildAPIRequest 68 | { 69 | NSAssert([self thread], @"INSaveDraftChange asked to buildRequest with no access to a thread model!"); 70 | NSAssert([[self thread] namespaceID], @"INSaveDraftChange asked to buildRequest with no namespace!"); 71 | 72 | NSError * error = nil; 73 | NSString * path = [[self thread] resourceAPIPath]; 74 | NSString * url = [[NSURL URLWithString:path relativeToURL:[INAPIManager shared].AF.baseURL] absoluteString]; 75 | 76 | NSMutableDictionary * params = [NSMutableDictionary dictionary]; 77 | [params setObject:[self tagIDStringsToAdd] forKey:@"add_tags"]; 78 | [params setObject:[self tagIDStringsToRemove] forKey:@"remove_tags"]; 79 | 80 | return [[[[INAPIManager shared] AF] requestSerializer] requestWithMethod:@"PUT" URLString:url parameters:params error:&error]; 81 | } 82 | 83 | - (void)applyLocally 84 | { 85 | NSMutableArray * newTagIDs = [NSMutableArray arrayWithArray: [[self thread] tagIDs]]; 86 | [newTagIDs addObjectsFromArray: self.tagIDStringsToAdd]; 87 | [newTagIDs removeObjectsInArray: self.tagIDStringsToRemove]; 88 | [[self thread] setTagIDs: newTagIDs]; 89 | [[INDatabaseManager shared] persistModel: [self thread]]; 90 | } 91 | 92 | - (void)rollbackLocally 93 | { 94 | NSMutableArray * newTagIDs = [NSMutableArray arrayWithArray: [[self thread] tagIDs]]; 95 | [newTagIDs removeObjectsInArray: self.tagIDStringsToAdd]; 96 | [newTagIDs addObjectsFromArray: self.tagIDStringsToRemove]; 97 | [[self thread] setTagIDs: newTagIDs]; 98 | [[INDatabaseManager shared] persistModel: [self thread]]; 99 | } 100 | 101 | - (void)handleSuccess:(AFHTTPRequestOperation *)operation withResponse:(id)responseObject 102 | { 103 | if ([responseObject isKindOfClass: [NSDictionary class]]) { 104 | [[self thread] updateWithResourceDictionary: responseObject]; 105 | [[INDatabaseManager shared] persistModel: [self thread]]; 106 | } 107 | } 108 | 109 | - (BOOL)canCancelPendingTask:(INAPITask*)other 110 | { 111 | if ([[other model] isEqual: self.model] && [other isKindOfClass: [INAddRemoveTagsTask class]]) { 112 | INAddRemoveTagsTask * otherTask = (INAddRemoveTagsTask*)other; 113 | 114 | // do we add ALL the tags 'other' is removing? 115 | BOOL invalidatesRemoved = [[NSSet setWithArray: [otherTask tagIDStringsToRemove]] isSubsetOfSet:[NSSet setWithArray: [self tagIDStringsToAdd]]]; 116 | 117 | // do we remove ALL the tags 'other' is adding? 118 | BOOL invalidatesAdded = [[NSSet setWithArray: [otherTask tagIDStringsToAdd]] isSubsetOfSet:[NSSet setWithArray: [self tagIDStringsToRemove]]]; 119 | 120 | // if we do both, we effectively null the effect of this operation 121 | return invalidatesRemoved && invalidatesAdded; 122 | } 123 | return NO; 124 | } 125 | 126 | - (NSArray *)dependenciesIn:(NSArray *)others 127 | { 128 | NSMutableArray * dependencies = [NSMutableArray array]; 129 | for (INAPITask * other in others) { 130 | if (other == self) { 131 | continue; 132 | } 133 | if ([other isKindOfClass: [INSaveTagTask class]] && [[NSSet setWithArray:[self tagIDStringsToAdd]] containsObject:[[other model] ID]]) { 134 | [dependencies addObject: other]; 135 | } 136 | } 137 | return dependencies; 138 | } 139 | 140 | 141 | @end 142 | --------------------------------------------------------------------------------