├── .gitignore ├── Canvas.xcodeproj ├── project.pbxproj └── xcshareddata │ └── xcschemes │ ├── Canvas — Development.xcscheme │ └── Canvas.xcscheme ├── Cartfile ├── Cartfile.resolved ├── LICENSE ├── Rakefile ├── Readme.markdown ├── Sources ├── APIClient+Canvas.swift ├── AlertController.swift ├── Analytics.swift ├── AppDelegate.swift ├── AuthorizationClient+Canvas.swift ├── AvatarView.swift ├── AvatarsView.swift ├── BannerView.swift ├── BillboardView.swift ├── CGRect+Canvas.swift ├── Canvas+Canvas.swift ├── CanvasActivitySource.swift ├── CanvasCell.swift ├── CanvasIconView.swift ├── CanvasTextView+Gestures.swift ├── CanvasTextView.swift ├── CanvasesResultsViewController.swift ├── CanvasesViewController.swift ├── ChromeActivity.swift ├── Color+Canvas.swift ├── Configuration.swift ├── CopyLinkActivity.swift ├── CopyLinkView.swift ├── CopyRepresentationActivity.swift ├── DestructiveValueCell.swift ├── DragBackgroundView.swift ├── DragContext.swift ├── DragProgressView.swift ├── EditorViewController+Actions.swift ├── EditorViewController+Connection.swift ├── EditorViewController.swift ├── FooterButton.swift ├── GrayButton.swift ├── GroupedSectionHeaderView.swift ├── IndicatorButton.swift ├── Interpolate.swift ├── LineView.swift ├── LoadCanvasViewController.swift ├── LogInViewController.swift ├── ModelsViewController.swift ├── NSDate+Canvas.swift ├── NSProcessInfo+Canvas.swift ├── NavigationBar.swift ├── NavigationController.swift ├── OAuthClient+Canvas.swift ├── OnboardingBillboardViewController.swift ├── OnboardingGesturesViewController.swift ├── OnboardingOrigamiViewController.swift ├── OnboardingSharingViewController.swift ├── OnboardingViewController.swift ├── OnboardingWelcomeViewController.swift ├── Organization+Canvas.swift ├── OrganizationAvatarView.swift ├── OrganizationCanvasesViewController.swift ├── OrganizationCell.swift ├── OrganizationsViewController.swift ├── PillButton.swift ├── PlaceholderViewController.swift ├── PrefaceButton.swift ├── PresenceViewController.swift ├── RefreshContentView.swift ├── RootViewController.swift ├── SafariActivity.swift ├── SearchBarContainer.swift ├── SectionHeaderView.swift ├── SessionFormViewController.swift ├── SettingsViewController.swift ├── SharedWebCredentials.swift ├── SignUpViewController.swift ├── SleepPickerViewController.swift ├── SleepPrevention.swift ├── SplitViewController.swift ├── StackViewController.swift ├── TableView.swift ├── TableViewController.swift ├── TextField.swift ├── TextView.swift ├── TickingLabel.swift ├── TintableEnvironment.swift ├── TintableView.swift ├── TitleView.swift ├── UIActivity+Canvas.swift ├── UIColor+Canvas.swift ├── UIEdgeInsets+Canvas.swift ├── UIFont+Canvas.swift ├── UISplitViewController+Canvas.swift ├── UIViewController+Canvas.swift ├── UserCell.swift ├── ValueCell.swift ├── VerifyViewController.swift ├── WebActivity.swift └── WebViewController.swift ├── Support ├── Assets.xcassets │ ├── 1Password.imageset │ │ ├── 1Password.pdf │ │ └── Contents.json │ ├── Activity Icons │ │ ├── Chrome.imageset │ │ │ ├── Chrome.pdf │ │ │ ├── Chrome~iPad.pdf │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Copy HTML.imageset │ │ │ ├── Contents.json │ │ │ ├── Copy HTML.pdf │ │ │ └── Copy HTML~iPad.pdf │ │ ├── Copy JSON.imageset │ │ │ ├── Contents.json │ │ │ ├── Copy JSON.pdf │ │ │ └── Copy JSON~iPad.pdf │ │ ├── Copy Link.imageset │ │ │ ├── Contents.json │ │ │ ├── Copy Link.pdf │ │ │ └── Copy Link~iPad.pdf │ │ ├── Copy Markdown.imageset │ │ │ ├── Contents.json │ │ │ ├── Copy Markdown.pdf │ │ │ └── Copy Markdown~iPad.pdf │ │ └── Safari.imageset │ │ │ ├── Contents.json │ │ │ ├── Safari.pdf │ │ │ └── Safari~iPad.pdf │ ├── AppIcon-Dev.appiconset │ │ ├── Contents.json │ │ ├── Settings.png │ │ ├── Settings@2x-1.png │ │ ├── Settings@2x.png │ │ ├── Settings@3x.png │ │ ├── Spotlight.png │ │ ├── Spotlight@2x-1.png │ │ ├── Spotlight@2x.png │ │ ├── Spotlight@3x.png │ │ ├── iPad Pro@2x.png │ │ ├── iPad.png │ │ ├── iPad@2x.png │ │ ├── iPhone@2x.png │ │ └── iPhone@3x.png │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Settings.png │ │ ├── Settings@2x-1.png │ │ ├── Settings@2x.png │ │ ├── Settings@3x.png │ │ ├── Spotlight.png │ │ ├── Spotlight@2x-1.png │ │ ├── Spotlight@2x.png │ │ ├── Spotlight@3x.png │ │ ├── iPad Pro@2x.png │ │ ├── iPad.png │ │ ├── iPad@2x.png │ │ ├── iPhone@2x.png │ │ └── iPhone@3x.png │ ├── Contents.json │ ├── Document Icons │ │ ├── Contents.json │ │ ├── Document Globe-Background.imageset │ │ │ ├── Contents.json │ │ │ └── Document Globe-Background.pdf │ │ ├── Document Globe.imageset │ │ │ ├── Contents.json │ │ │ └── Document Globe.pdf │ │ ├── Document-Blank.imageset │ │ │ ├── Contents.json │ │ │ └── empty-document.pdf │ │ └── Document.imageset │ │ │ ├── Contents.json │ │ │ └── document.pdf │ ├── Gesture Icons │ │ ├── CheckList.imageset │ │ │ ├── CheckList.pdf │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Heading2.imageset │ │ │ ├── Contents.json │ │ │ └── Heading2.pdf │ │ ├── Heading3.imageset │ │ │ ├── Contents.json │ │ │ └── Heading3.pdf │ │ ├── Indent.imageset │ │ │ ├── Contents.json │ │ │ └── Indent.pdf │ │ ├── OrderedList.imageset │ │ │ ├── Contents.json │ │ │ └── OrderedList.pdf │ │ ├── Outdent.imageset │ │ │ ├── Contents.json │ │ │ └── Outdent.pdf │ │ └── Paragraph.imageset │ │ │ ├── Contents.json │ │ │ └── Paragraph.pdf │ ├── Icon-Small.imageset │ │ ├── Contents.json │ │ └── Icon-Small.pdf │ ├── Illustration.imageset │ │ ├── Contents.json │ │ └── Illustration.pdf │ ├── IllustrationLight.imageset │ │ ├── Contents.json │ │ └── IllustrationLight.pdf │ ├── Illustrations │ │ ├── Contents.json │ │ ├── Email.imageset │ │ │ ├── Contents.json │ │ │ ├── Email@2x.png │ │ │ └── Email@3x.png │ │ ├── No Participants.imageset │ │ │ ├── Contents.json │ │ │ ├── No Participants.png │ │ │ ├── No Participants@2x.png │ │ │ └── No Participants@3x.png │ │ └── Not Found.imageset │ │ │ ├── 404 Illustration.png │ │ │ ├── 404 Illustration@2x.png │ │ │ ├── 404 Illustration@3x.png │ │ │ └── Contents.json │ ├── New Canvas Shortcut.imageset │ │ ├── Compose-Shortcut.pdf │ │ └── Contents.json │ ├── Onboarding │ │ ├── Contents.json │ │ ├── GesturesCompact.imageset │ │ │ ├── Contents.json │ │ │ ├── GesturesCompact@2x.png │ │ │ ├── GesturesCompact@2x~iPad.png │ │ │ └── GesturesCompact@3x.png │ │ ├── GesturesRegular.imageset │ │ │ ├── Contents.json │ │ │ └── GesturesRegular@2x~iPad.png │ │ ├── OrigamiCompact.imageset │ │ │ ├── Contents.json │ │ │ ├── OrigamiCompact@2x.png │ │ │ ├── OrigamiCompact@2x~iPad.png │ │ │ └── OrigamiCompact@3x.png │ │ ├── OrigamiRegular.imageset │ │ │ ├── Contents.json │ │ │ └── OrigamiRegular@2x~iPad.png │ │ ├── ShareCompact.imageset │ │ │ ├── Contents.json │ │ │ ├── ShareCompact@2x.png │ │ │ ├── ShareCompact@2x~iPad.png │ │ │ └── ShareCompact@3x.png │ │ ├── ShareRegular.imageset │ │ │ ├── Contents.json │ │ │ └── ShareRegular@2x~iPad.png │ │ ├── WelcomeCompact.imageset │ │ │ ├── Contents.json │ │ │ ├── WelcomeCompact@2x.png │ │ │ ├── WelcomeCompact@2x~iPad.png │ │ │ └── WelcomeCompact@3x.png │ │ └── WelcomeRegular.imageset │ │ │ ├── Contents.json │ │ │ └── WelcomeRegular@2x~iPad.png │ ├── Primaries │ │ ├── ChevronLeft.imageset │ │ │ ├── ChevronLeft.pdf │ │ │ └── Contents.json │ │ ├── ChevronRightSmall.imageset │ │ │ ├── ChevronRightSmall.pdf │ │ │ └── Contents.json │ │ ├── Close.imageset │ │ │ ├── Close.pdf │ │ │ └── Contents.json │ │ ├── Compose.imageset │ │ │ ├── Compose.pdf │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Gear.imageset │ │ │ ├── Contents.json │ │ │ └── Gear.pdf │ │ ├── Help.imageset │ │ │ ├── Contents.json │ │ │ ├── Help@2x.png │ │ │ └── Help@3x.png │ │ ├── Lock.imageset │ │ │ ├── Contents.json │ │ │ └── Lock.pdf │ │ ├── Moon.imageset │ │ │ ├── Contents.json │ │ │ ├── Moon@2x.png │ │ │ └── Moon@3x.png │ │ ├── More.imageset │ │ │ ├── Contents.json │ │ │ └── More.pdf │ │ ├── SearchSmall.imageset │ │ │ ├── Contents.json │ │ │ └── SearchSmall.pdf │ │ ├── SidebarLeft.imageset │ │ │ ├── Contents.json │ │ │ └── SidebarLeft.pdf │ │ ├── SignOut.imageset │ │ │ ├── Contents.json │ │ │ ├── SignOut@2x.png │ │ │ └── SignOut@3x.png │ │ ├── User.imageset │ │ │ ├── Contents.json │ │ │ ├── User@2x.png │ │ │ └── User@3x.png │ │ └── Username.imageset │ │ │ ├── Contents.json │ │ │ ├── Username@2x.png │ │ │ └── Username@3x.png │ └── Pull To Refresh │ │ ├── Contents.json │ │ ├── RefreshViewBackground.imageset │ │ ├── Background.pdf │ │ └── Contents.json │ │ ├── RefreshViewGradient.imageset │ │ ├── Contents.json │ │ └── Gradient 2.pdf │ │ └── RefreshViewOutline.imageset │ │ ├── Contents.json │ │ └── Outline.pdf ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Canvas Dev.entitlements ├── Canvas.entitlements ├── Info.plist ├── Settings.bundle │ ├── Root.plist │ └── en.lproj │ │ └── Root.strings ├── UITests-Info.plist └── apple-app-site-association.json ├── UITests ├── SnapshotHelper.swift └── Snapshots.swift └── fastlane ├── Appfile ├── Deliverfile ├── Fastfile ├── README.md ├── metadata ├── copyright.txt ├── en-US │ ├── description.txt │ ├── keywords.txt │ ├── marketing_url.txt │ ├── name.txt │ ├── privacy_url.txt │ ├── release_notes.txt │ └── support_url.txt ├── primary_category.txt └── secondary_category.txt └── screenshots ├── README.txt └── en-US ├── ipadPro_1.png ├── ipadPro_2.png ├── ipadPro_3.png ├── ipad_1.png ├── ipad_2.png ├── ipad_3.png ├── iphone35_1.png ├── iphone35_2.png ├── iphone35_3.png ├── iphone35_4.png ├── iphone35_5.png ├── iphone4_1.png ├── iphone4_2.png ├── iphone4_3.png ├── iphone4_4.png ├── iphone4_5.png ├── iphone6Plus_1.png ├── iphone6Plus_2.png ├── iphone6Plus_3.png ├── iphone6Plus_4.png ├── iphone6Plus_5.png ├── iphone6_1.png ├── iphone6_2.png ├── iphone6_3.png ├── iphone6_4.png └── iphone6_5.png /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | *xcuserdata/ 6 | *.mode1v3 7 | *.pbxuser 8 | *.xcworkspace 9 | 10 | # Carthage 11 | Carthage 12 | 13 | # Fastlane 14 | fastlane/report.xml 15 | fastlane/Preview.html 16 | fastlane/test_output 17 | Preview.html 18 | 19 | # Products 20 | *.zip 21 | *.ipa 22 | -------------------------------------------------------------------------------- /Canvas.xcodeproj/xcshareddata/xcschemes/Canvas — Development.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "intercom/intercom-ios" 2 | github "soffes/GradientView" 3 | github "soffes/Mixpanel" 4 | github "soffes/SSPullToRefresh" "v2.0.0-beta.2" 5 | github "usecanvas/CanvasCore" "master" 6 | github "usecanvas/onepassword-app-extension" 7 | github "usecanvas/sentry-swift" 8 | github "venmo/Static" 9 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "soffes/Cache" "v0.1.0" 2 | github "soffes/CommonCrypto" "v1.0" 3 | github "soffes/Diff" "v0.1.2" 4 | github "soffes/GradientView" "v2.1.1" 5 | github "soffes/ISO8601" "v0.6.0" 6 | github "usecanvas/KSCrash" "1.6.4" 7 | github "soffes/Mixpanel" "v0.1.1" 8 | github "usecanvas/OperationalTransformation" "v0.1.1" 9 | github "soffes/SAMKeychain" "v1.5.0" 10 | github "soffes/SSPullToRefresh" "v2.0.0-beta.2" 11 | github "usecanvas/Starscream" "1.1.3" 12 | github "venmo/Static" "v1.1.1" 13 | github "soffes/X" "v0.3.1" 14 | github "intercom/intercom-ios" "3.0.10" 15 | github "usecanvas/onepassword-app-extension" "framework/1.8.2" 16 | github "usecanvas/CanvasKit" "v0.5.0" 17 | github "usecanvas/CanvasNative" "v0.1.2" 18 | github "soffes/Crypto" "v0.4.0" 19 | github "usecanvas/sentry-swift" "0.3.1" 20 | github "usecanvas/CanvasText" "566bd91dda39ac98960cb6aafaf16f48a073be7f" 21 | github "usecanvas/CanvasCore" "6948ef3d63925a33b7e37c1aae65f0ad9895b809" 22 | -------------------------------------------------------------------------------- /Readme.markdown: -------------------------------------------------------------------------------- 1 | # Canvas for iOS 2 | 3 | Canvas client for iOS V1. 4 | 5 | *Note:* This app was designed to work against V1 of Canvas, which is not released. There is no iOS client for V2. This repo is open sourced for reference only. 6 | 7 | 8 | ## Building 9 | 10 | You will need [Xcode](https://itunes.apple.com/app/xcode/id497799835) 7.3.1 and [Carthage](https://github.com/carthage/carthage) 0.17.2 to build Canvas for iOS. You should ensure you have Xcode installed before beginning since that can take quite awhile. Be sure to open it at least once after downloading it. 11 | 12 | 1. Clone the code. 13 | 14 | $ git clone https://github.com/usecanvas/ios-v1 15 | $ cd ios-v1 16 | 17 | 2. Now, simply run the following command: 18 | 19 | $ rake bootstrap 20 | 21 | This will walk you through setting up everything you need to build Canvas for iOS. 22 | 23 | 3. Open `Canvas.xcodeproj` and click ▶️ 24 | 25 | If you have trouble building, quit Xcode, run `rake clean bootstrap`, and open Xcode again. If you are still having trouble, ask @soffes in Slack. 26 | 27 | 28 | ## Running 29 | 30 | By default, the app will use the production services. You can change this in `Configuration.swift` at the bottom. 31 | -------------------------------------------------------------------------------- /Sources/APIClient+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIClient+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/22/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import CanvasCore 10 | import CanvasKit 11 | 12 | extension CanvasCore.APIClient { 13 | convenience init(account: Account) { 14 | self.init(account: account, config: config) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/AlertController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AlertController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 11/27/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | final class AlertController: UIAlertController { 13 | 14 | // MARK: - Properties 15 | 16 | /// Used when return is pressed while the controller is showing 17 | var primaryAction: (Void -> Void)? 18 | 19 | 20 | // MARK: - UIResponder 21 | 22 | override func canBecomeFirstResponder() -> Bool { 23 | return true 24 | } 25 | 26 | override var keyCommands: [UIKeyCommand]? { 27 | return (super.keyCommands ?? []) + [ 28 | UIKeyCommand(input: UIKeyInputEscape, modifierFlags: [], action: #selector(cancel)), 29 | UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(selectFirstAction)) 30 | ] 31 | } 32 | 33 | 34 | // MARK: - UIViewController 35 | 36 | override func viewWillAppear(animated: Bool) { 37 | super.viewWillAppear(animated) 38 | 39 | adjustSubviews([view]) 40 | } 41 | 42 | 43 | // MARK: - Actions 44 | 45 | func cancel(sender: AnyObject?) { 46 | dismissViewControllerAnimated(true, completion: nil) 47 | } 48 | 49 | func selectFirstAction(sender: AnyObject?) { 50 | dismissViewControllerAnimated(true) { 51 | self.primaryAction?() 52 | } 53 | } 54 | 55 | 56 | // MARK: - Private 57 | 58 | private func adjustSubviews(subviews: [UIView]) { 59 | for subview in subviews { 60 | if let label = subview as? UILabel { 61 | adjustLabel(label) 62 | } else if subview.bounds.height > 0 && subview.bounds.height <= 1 { 63 | subview.backgroundColor = Swatch.border 64 | } 65 | 66 | adjustSubviews(subview.subviews) 67 | } 68 | } 69 | 70 | private func adjustLabel(label: UILabel) { 71 | for action in actions { 72 | if label.text == title { 73 | label.attributedText = NSAttributedString(string: label.text ?? "", attributes: [ 74 | NSFontAttributeName: label.font, 75 | NSForegroundColorAttributeName: Swatch.darkGray 76 | ]) 77 | return 78 | } 79 | 80 | if label.text == action.title { 81 | switch action.style { 82 | case .Default, .Cancel: 83 | label.attributedText = NSAttributedString(string: label.text ?? "", attributes: [ 84 | NSFontAttributeName: label.font, 85 | NSForegroundColorAttributeName: Swatch.brand 86 | ]) 87 | case .Destructive: 88 | label.attributedText = NSAttributedString(string: label.text ?? "", attributes: [ 89 | NSFontAttributeName: label.font, 90 | NSForegroundColorAttributeName: Swatch.destructive 91 | ]) 92 | } 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Sources/Analytics.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Analytics.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 11/25/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import Mixpanel 10 | import Intercom 11 | import CanvasKit 12 | import CanvasCore 13 | 14 | struct Analytics { 15 | 16 | // MARK: - Types 17 | 18 | enum Event { 19 | case LoggedOut 20 | case LoggedIn 21 | case LaunchedApp 22 | case ChangedOrganization(organization: Organization) 23 | case OpenedCanvas 24 | 25 | var name: String { 26 | switch self { 27 | case .LoggedOut: return "Logged Out" 28 | case .LoggedIn: return "Logged In" 29 | case .LaunchedApp: return "Launched App" 30 | case .ChangedOrganization(_): return "Changed Organization" 31 | case .OpenedCanvas: return "Opened Canvas" 32 | } 33 | } 34 | 35 | var parameters: [String: AnyObject]? { 36 | switch self { 37 | case .ChangedOrganization(let organization): return ["organization_name": organization.name] 38 | default: return nil 39 | } 40 | } 41 | } 42 | 43 | 44 | // MARK: - Properties 45 | 46 | private static let mixpanel: Mixpanel = { 47 | var mp = Mixpanel(token: config.mixpanelToken) 48 | 49 | let uniqueIdentifier: String 50 | let key = "Identifier" 51 | if let identifier = NSUserDefaults.standardUserDefaults().stringForKey(key) { 52 | uniqueIdentifier = identifier 53 | } else { 54 | let identifier = NSUUID().UUIDString 55 | NSUserDefaults.standardUserDefaults().setObject(identifier, forKey: key) 56 | uniqueIdentifier = identifier 57 | } 58 | 59 | mp.identify(uniqueIdentifier) 60 | 61 | #if DEBUG 62 | mp.enabled = false 63 | #endif 64 | 65 | return mp 66 | }() 67 | 68 | 69 | // MARK: - Tracking 70 | 71 | static func track(event: Event) { 72 | // Params 73 | let params = event.parameters ?? [:] 74 | var mixpanelParams = params 75 | 76 | // Current user 77 | if let account = AccountController.sharedController.currentAccount { 78 | mixpanelParams["id"] = account.user.id 79 | mixpanelParams["$username"] = account.user.username 80 | } 81 | 82 | // Mixpanel 83 | mixpanel.track(event.name, parameters: mixpanelParams) 84 | 85 | // Intercom 86 | var intercomParams = params 87 | intercomParams["source"] = "ios" 88 | Intercom.logEventWithName(event.name, metaData: params) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Sources/AuthorizationClient+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AuthorizationClient.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/19/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import CanvasKit 10 | 11 | extension AuthorizationClient { 12 | init() { 13 | self.init(clientID: config.canvasClientID, clientSecret: config.canvasClientSecret, baseURL: config.environment.apiURL) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/AvatarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AvatarView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/8/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasKit 12 | 13 | final class AvatarView: UIImageView { 14 | 15 | // MARK: - Properties 16 | 17 | var user: User? { 18 | didSet { 19 | updateAvatar() 20 | } 21 | } 22 | 23 | 24 | // MARK: - Initializers 25 | 26 | init(user: User? = nil) { 27 | self.user = user 28 | 29 | super.init(frame: .zero) 30 | 31 | backgroundColor = Swatch.lightGray 32 | layer.cornerRadius = 16 33 | layer.masksToBounds = true 34 | 35 | updateAvatar() 36 | } 37 | 38 | required init?(coder aDecoder: NSCoder) { 39 | fatalError("init(coder:) has not been implemented") 40 | } 41 | 42 | 43 | // MARK: - UIView 44 | 45 | override func intrinsicContentSize() -> CGSize { 46 | return CGSize(width: 32, height: 32) 47 | } 48 | 49 | 50 | // MARK: - Private 51 | 52 | private func updateAvatar() { 53 | guard let user = user, url = user.avatarURL.flatMap(imgix) else { 54 | image = nil 55 | return 56 | } 57 | 58 | image = AvatarsController.sharedController.fetchImage(id: user.id, url: url) { [weak self] id, image in 59 | if id == self?.user?.id { 60 | self?.image = image 61 | } 62 | } 63 | } 64 | 65 | private func imgix(url: NSURL) -> NSURL? { 66 | let parameters = [ 67 | NSURLQueryItem(name: "dpr", value: "\(Int(traitCollection.displayScale))"), 68 | NSURLQueryItem(name: "w", value: "\(32)"), 69 | NSURLQueryItem(name: "h", value: "\(32)"), 70 | NSURLQueryItem(name: "fit", value: "crop"), 71 | NSURLQueryItem(name: "crop", value: "faces") 72 | ] 73 | 74 | return ImgixController.sign(url: url, parameters: parameters, configuration: config) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Sources/AvatarsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AvatarsView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/8/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasKit 11 | 12 | final class AvatarsView: UIStackView { 13 | 14 | // MARK: - Properties 15 | 16 | var users = [User]() { 17 | didSet { 18 | arrangedSubviews.forEach { $0.removeFromSuperview() } 19 | 20 | for user in users { 21 | let view = AvatarView(user: user) 22 | addArrangedSubview(view) 23 | } 24 | } 25 | } 26 | 27 | 28 | // MARK: - Initializers 29 | 30 | convenience init() { 31 | self.init(frame: .zero) 32 | } 33 | 34 | override init(frame: CGRect) { 35 | super.init(frame: frame) 36 | 37 | axis = .Vertical 38 | spacing = 8 39 | } 40 | 41 | required init?(coder aDecoder: NSCoder) { 42 | fatalError("init(coder:) has not been implemented") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/BannerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BannerView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/1/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasText 12 | 13 | final class BannerView: UIView { 14 | 15 | // MARK: - Types 16 | 17 | enum Style { 18 | case success 19 | case info 20 | case failure 21 | 22 | var foregroundColor: UIColor { 23 | return Swatch.white 24 | } 25 | 26 | var backgroundColor: UIColor { 27 | switch self { 28 | case .success: return Swatch.green 29 | case .info: return Swatch.darkGray 30 | case .failure: return Swatch.destructive 31 | } 32 | } 33 | } 34 | 35 | 36 | // MARK: - Properties 37 | 38 | let textLabel: UILabel = { 39 | let label = UILabel() 40 | label.translatesAutoresizingMaskIntoConstraints = false 41 | label.numberOfLines = 0 42 | label.textAlignment = .Center 43 | return label 44 | }() 45 | 46 | 47 | // MARK: - Initializers 48 | 49 | init(style: Style) { 50 | super.init(frame: .zero) 51 | 52 | backgroundColor = style.backgroundColor 53 | 54 | textLabel.textColor = style.foregroundColor 55 | addSubview(textLabel) 56 | 57 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(updateFont), name: UIContentSizeCategoryDidChangeNotification, object: nil) 58 | updateFont() 59 | 60 | NSLayoutConstraint.activateConstraints([ 61 | textLabel.centerXAnchor.constraintEqualToAnchor(centerXAnchor), 62 | textLabel.centerYAnchor.constraintEqualToAnchor(centerYAnchor), 63 | textLabel.leadingAnchor.constraintGreaterThanOrEqualToAnchor(leadingAnchor, constant: 16), 64 | textLabel.trailingAnchor.constraintLessThanOrEqualToAnchor(trailingAnchor, constant: -16), 65 | textLabel.topAnchor.constraintGreaterThanOrEqualToAnchor(topAnchor, constant: 12), 66 | textLabel.bottomAnchor.constraintGreaterThanOrEqualToAnchor(bottomAnchor, constant: -12), 67 | ]) 68 | } 69 | 70 | required init?(coder aDecoder: NSCoder) { 71 | fatalError("init(coder:) has not been implemented") 72 | } 73 | 74 | 75 | // MARK: - Private 76 | 77 | @objc private func updateFont() { 78 | textLabel.font = TextStyle.callout.font(weight: .medium) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Sources/BillboardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BillboardView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/5/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasText 12 | 13 | final class BillboardView: UIStackView { 14 | 15 | // MARK: - Properties 16 | 17 | let illustrationView = UIImageView() 18 | 19 | let titleLabel: UILabel = { 20 | let label = UILabel() 21 | label.textColor = Swatch.black 22 | return label 23 | }() 24 | 25 | let subtitleLabel: UILabel = { 26 | let label = UILabel() 27 | label.textColor = Swatch.darkGray 28 | label.numberOfLines = 0 29 | label.textAlignment = .Center 30 | return label 31 | }() 32 | 33 | 34 | // MARK: - Initializers 35 | 36 | convenience init() { 37 | self.init(frame: .zero) 38 | } 39 | 40 | override init(frame: CGRect) { 41 | super.init(frame: frame) 42 | 43 | axis = .Vertical 44 | alignment = .Center 45 | layoutMargins = UIEdgeInsets(top: 16, left: 32, bottom: 16, right: 32) 46 | layoutMarginsRelativeArrangement = true 47 | 48 | addArrangedSubview(illustrationView) 49 | addSpace(32) 50 | addArrangedSubview(titleLabel) 51 | addSpace(8) 52 | addArrangedSubview(subtitleLabel) 53 | 54 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(updateFonts), name: UIContentSizeCategoryDidChangeNotification, object: nil) 55 | updateFonts() 56 | } 57 | 58 | required init?(coder aDecoder: NSCoder) { 59 | fatalError("init(coder:) has not been implemented") 60 | } 61 | 62 | 63 | // MARK: - Private 64 | 65 | @objc private func updateFonts() { 66 | titleLabel.font = TextStyle.title1.font() 67 | subtitleLabel.font = TextStyle.body.font() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Sources/CGRect+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGRect+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 11/26/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | 11 | extension CGRect { 12 | var floor: CGRect { 13 | return CGRect( 14 | x: CoreGraphics.floor(origin.x), 15 | y: CoreGraphics.floor(origin.y), 16 | width: CoreGraphics.floor(size.width), 17 | height: CoreGraphics.floor(size.height) 18 | ) 19 | } 20 | 21 | var ceil: CGRect { 22 | return CGRect( 23 | x: CoreGraphics.ceil(origin.x), 24 | y: CoreGraphics.ceil(origin.y), 25 | width: CoreGraphics.ceil(size.width), 26 | height: CoreGraphics.ceil(size.height) 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Canvas+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Canvas+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 12/2/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasKit 12 | import Static 13 | 14 | extension Canvas { 15 | enum Kind { 16 | case Document 17 | case Blank 18 | 19 | var icon: UIImage! { 20 | switch self { 21 | case .Document: return UIImage(named: "Document") 22 | case .Blank: return UIImage(named: "Document-Blank") 23 | } 24 | } 25 | } 26 | 27 | var row: Row { 28 | return Row( 29 | text: displayTitle, 30 | detailText: summary, 31 | cellClass: CanvasCell.self, 32 | context: ["canvas": self] 33 | ) 34 | } 35 | 36 | var kind: Kind { 37 | return isEmpty ? .Blank : .Document 38 | } 39 | 40 | var displayTitle: String { 41 | return title.isEmpty ? LocalizedString.Untitled.string : title 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/CanvasActivitySource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CanvasActivitySource.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/18/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasKit 11 | 12 | final class CanvasActivitySource: NSObject { 13 | 14 | // MARK: - Properties 15 | 16 | private let title: String 17 | private let url: NSURL 18 | 19 | 20 | // MARK: - Initializers 21 | 22 | init?(canvas: Canvas) { 23 | guard let url = canvas.url else { return nil } 24 | 25 | title = canvas.title 26 | self.url = url 27 | 28 | super.init() 29 | } 30 | } 31 | 32 | 33 | extension CanvasActivitySource: UIActivityItemSource { 34 | func activityViewControllerPlaceholderItem(activityViewController: UIActivityViewController) -> AnyObject { 35 | return url 36 | } 37 | 38 | func activityViewController(activityViewController: UIActivityViewController, itemForActivityType activityType: String) -> AnyObject? { 39 | switch activityType { 40 | case UIActivityTypePostToFacebook, UIActivityTypePostToTwitter, UIActivityTypePostToWeibo, UIActivityTypeMessage, UIActivityTypeMail, UIActivityTypePostToTencentWeibo: 41 | return "\(title) — \(url)" 42 | case UIActivityTypeAirDrop: 43 | return url 44 | default: 45 | return url 46 | } 47 | } 48 | 49 | func activityViewController(activityViewController: UIActivityViewController, subjectForActivityType activityType: String?) -> String { 50 | // TODO: Localize 51 | return "Check out this Canvas" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/CanvasTextView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CanvasTextView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 4/19/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasNative 12 | import CanvasText 13 | 14 | protocol CanvasTextViewFormattingDelegate: class { 15 | func textViewDidToggleBoldface(textView: CanvasTextView, sender: AnyObject?) 16 | func textViewDidToggleItalics(textView: CanvasTextView, sender: AnyObject?) 17 | } 18 | 19 | final class CanvasTextView: TextView { 20 | 21 | // MARK: - Properties 22 | 23 | weak var textController: TextController? { 24 | didSet { 25 | guard let theme = textController?.theme else { return } 26 | 27 | var attributes = theme.titleAttributes 28 | attributes[NSForegroundColorAttributeName] = theme.titlePlaceholderColor 29 | 30 | placeholderLabel.attributedText = NSAttributedString( 31 | string: LocalizedString.CanvasTitlePlaceholder.string, 32 | attributes: attributes 33 | ) 34 | } 35 | } 36 | 37 | weak var formattingDelegate: CanvasTextViewFormattingDelegate? 38 | 39 | let dragGestureRecognizer: UIPanGestureRecognizer 40 | let dragThreshold: CGFloat = 60 41 | var dragContext: DragContext? 42 | 43 | let placeholderLabel: UILabel = { 44 | let label = UILabel() 45 | label.userInteractionEnabled = false 46 | label.hidden = true 47 | return label 48 | }() 49 | 50 | 51 | // MARK: - Initializers 52 | 53 | override init(frame: CGRect, textContainer: NSTextContainer?) { 54 | dragGestureRecognizer = UIPanGestureRecognizer() 55 | 56 | super.init(frame: frame, textContainer: textContainer) 57 | 58 | // allowsEditingTextAttributes = true 59 | alwaysBounceVertical = true 60 | keyboardDismissMode = .Interactive 61 | backgroundColor = .clearColor() 62 | 63 | registerGestureRecognizers() 64 | 65 | managedSubviews.insert(placeholderLabel) 66 | addSubview(placeholderLabel) 67 | } 68 | 69 | required init?(coder aDecoder: NSCoder) { 70 | fatalError("init(coder:) has not been implemented") 71 | } 72 | 73 | 74 | // MARK: - UIResponder 75 | 76 | override func toggleBoldface(sender: AnyObject?) { 77 | formattingDelegate?.textViewDidToggleBoldface(self, sender: sender) 78 | } 79 | 80 | override func toggleItalics(sender: AnyObject?) { 81 | formattingDelegate?.textViewDidToggleItalics(self, sender: sender) 82 | } 83 | 84 | override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool { 85 | // Disable underline 86 | if action == #selector(toggleUnderline) { 87 | return false 88 | } 89 | 90 | return super.canPerformAction(action, withSender: sender) 91 | } 92 | 93 | 94 | // MARK: - UIView 95 | 96 | override func layoutSubviews() { 97 | super.layoutSubviews() 98 | layoutPlaceholder() 99 | } 100 | 101 | override func tintColorDidChange() { 102 | super.tintColorDidChange() 103 | 104 | textController?.setTintColor(tintColor) 105 | } 106 | 107 | 108 | // MARK: - Private 109 | 110 | private func layoutPlaceholder() { 111 | placeholderLabel.sizeToFit() 112 | 113 | var frame = placeholderLabel.frame 114 | frame.origin.x = textContainerInset.left 115 | frame.origin.y = textContainerInset.top 116 | placeholderLabel.frame = frame 117 | } 118 | } 119 | 120 | 121 | extension CanvasTextView: TextControllerAnnotationDelegate { 122 | func textController(textController: TextController, willAddAnnotation annotation: Annotation) { 123 | annotation.view.backgroundColor = .clearColor() 124 | managedSubviews.insert(annotation.view) 125 | insertSubview(annotation.view, atIndex: 0) 126 | } 127 | 128 | func textController(textController: TextController, willRemoveAnnotation annotation: Annotation) { 129 | managedSubviews.remove(annotation.view) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Sources/CanvasesResultsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CanvasesResultsViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 12/9/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasKit 12 | 13 | final class CanvasesResultsViewController: CanvasesViewController { 14 | 15 | // MARK: - UIViewController 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | canRefresh = false 21 | 22 | let line = LineView() 23 | line.translatesAutoresizingMaskIntoConstraints = false 24 | view.addSubview(line) 25 | 26 | NSLayoutConstraint.activateConstraints([ 27 | // Add search bar height :( 28 | line.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor, constant: 44), 29 | 30 | line.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor), 31 | line.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor) 32 | ]) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/CanvasesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CanvasesViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 12/8/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Static 11 | import CanvasCore 12 | import CanvasKit 13 | import CanvasNative 14 | 15 | class CanvasesViewController: ModelsViewController, Accountable { 16 | 17 | // MARK: - Properties 18 | 19 | var account: Account 20 | 21 | 22 | // MARK: - Initializers 23 | 24 | init(account: Account, style: UITableViewStyle = .Plain) { 25 | self.account = account 26 | super.init(style: style) 27 | } 28 | 29 | required init?(coder aDecoder: NSCoder) { 30 | fatalError("init(coder:) has not been implemented") 31 | } 32 | 33 | 34 | // MARK: - UIViewController 35 | 36 | override func viewDidLoad() { 37 | super.viewDidLoad() 38 | 39 | tableView.rowHeight = 72 40 | 41 | navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil) 42 | } 43 | 44 | 45 | // MARK: - ModelsViewController 46 | 47 | func openCanvas(canvas: Canvas) { 48 | guard !opening else { return } 49 | 50 | if let editor = currentEditor() where editor.canvas == canvas { 51 | return 52 | } 53 | 54 | opening = true 55 | 56 | if !CanvasNative.supports(nativeVersion: canvas.nativeVersion) { 57 | if let indexPath = tableView.indexPathForSelectedRow { 58 | tableView.deselectRowAtIndexPath(indexPath, animated: true) 59 | } 60 | 61 | let alert = UIAlertController(title: LocalizedString.UnsupportedTitle.string, message: LocalizedString.UnsupportedMessage.string, preferredStyle: .Alert) 62 | 63 | #if !APP_STORE 64 | alert.addAction(UIAlertAction(title: LocalizedString.CheckForUpdatesButton.string, style: .Default, handler: { _ in 65 | UIApplication.sharedApplication().openURL(config.updatesURL) 66 | })) 67 | #endif 68 | 69 | alert.addAction(UIAlertAction(title: LocalizedString.OpenInSafariButton.string, style: .Default, handler: { _ in 70 | guard let url = canvas.url else { return } 71 | UIApplication.sharedApplication().openURL(url) 72 | })) 73 | alert.addAction(UIAlertAction(title: LocalizedString.Cancel.string, style: .Cancel, handler: nil)) 74 | 75 | opening = false 76 | presentViewController(alert, animated: true, completion: nil) 77 | 78 | return 79 | } 80 | 81 | Analytics.track(.OpenedCanvas) 82 | let viewController = EditorViewController(account: account, canvas: canvas) 83 | showDetailViewController(NavigationController(rootViewController: viewController), sender: self) 84 | 85 | dispatch_async(dispatch_get_main_queue()) { [weak self] in 86 | self?.opening = false 87 | } 88 | } 89 | 90 | 91 | // MARK: - Utilities 92 | 93 | func currentEditor() -> EditorViewController? { 94 | guard let splitViewController = splitViewController where splitViewController.viewControllers.count == 2 else { return nil } 95 | return (splitViewController.viewControllers.last as? UINavigationController)?.topViewController as? EditorViewController 96 | } 97 | 98 | func rowForCanvas(canvas: Canvas) -> Row { 99 | var row = canvas.row 100 | row.selection = { [weak self] in self?.openCanvas(canvas) } 101 | return row 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Sources/ChromeActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChromeActivity.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 11/18/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class ChromeActivity: WebActivity { 12 | 13 | // MARK: - UIActivity 14 | 15 | override func activityType() -> String? { 16 | return "open-in-chrome" 17 | } 18 | 19 | override func activityTitle() -> String? { 20 | return "Open in Chrome" 21 | } 22 | 23 | override func activityImage() -> UIImage? { 24 | return UIImage(named: "Chrome") 25 | } 26 | 27 | override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool { 28 | for activityItem in activityItems { 29 | if let activityURL = activityItem as? NSURL, chromeScheme = chromeSchemeForURL(activityURL), chromeURL = NSURL(string: "\(chromeScheme)://") where UIApplication.sharedApplication().canOpenURL(chromeURL) { 30 | return true 31 | } 32 | } 33 | 34 | return false 35 | } 36 | 37 | override func performActivity() { 38 | guard let URL = self.URL else { 39 | activityDidFinish(false) 40 | return 41 | } 42 | 43 | 44 | guard let components = NSURLComponents(URL: URL, resolvingAgainstBaseURL: true), 45 | chromeScheme = chromeSchemeForURL(URL) 46 | else { 47 | activityDidFinish(false) 48 | return 49 | } 50 | 51 | components.scheme = chromeScheme 52 | 53 | let completed = components.URL.flatMap { UIApplication.sharedApplication().openURL($0) } ?? false 54 | activityDidFinish(completed) 55 | } 56 | 57 | 58 | // MARK: - Private 59 | 60 | private func chromeSchemeForURL(URL: NSURL) -> String? { 61 | if URL.scheme == "http" { 62 | return "googlechrome" 63 | } 64 | 65 | if URL.scheme == "https" { 66 | return "googlechromes" 67 | } 68 | 69 | return nil 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Sources/Color+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Color+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 1/25/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasKit 11 | 12 | extension Organization.Color { 13 | var uiColor: UIColor { 14 | return UIColor(red: CGFloat(red), green: CGFloat(green), blue: CGFloat(blue), alpha: 1) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Configuration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Configuration.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 12/2/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import CanvasCore 10 | 11 | private let canvasClientID = "5QdrPgUUYQs2yvGLIUT5PL" 12 | 13 | private let canvasClientSecretPart4 = "aef895c32" 14 | private let canvasClientSecretPart2 = "f5bd59c7866e85" 15 | private let canvasClientSecretPart1 = "60ff40c860274eb9afb6" 16 | private let canvasClientSecretPart3 = "97bdcc48ae89946" 17 | private let canvasClientSecret = "\(canvasClientSecretPart1)fb\(canvasClientSecretPart2)2e\(canvasClientSecretPart3)75\(canvasClientSecretPart4)" 18 | 19 | let config = Configuration(environment: .production, canvasClientID: canvasClientID, canvasClientSecret: canvasClientSecret) 20 | -------------------------------------------------------------------------------- /Sources/CopyLinkActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CopyLinkActivity.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/15/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // TODO: Localize 12 | final class CopyLinkActivity: UIActivity { 13 | 14 | // MARK: - Properties 15 | 16 | private var url: NSURL? 17 | 18 | 19 | // MARK: - UIActivity 20 | 21 | override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool { 22 | guard let url = activityItems.first as? NSURL else { return false } 23 | 24 | self.url = url 25 | return true 26 | } 27 | 28 | override func performActivity() { 29 | UIPasteboard.generalPasteboard().URL = url 30 | showBanner(text: "Copied link!") 31 | } 32 | 33 | override func activityType() -> String? { 34 | return "copy-link" 35 | } 36 | 37 | override func activityTitle() -> String? { 38 | return "Copy Link" 39 | } 40 | 41 | override func activityImage() -> UIImage? { 42 | return UIImage(named: "Copy Link") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/CopyLinkView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CopyLinkView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 8/9/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | // TODO: Localize 13 | final class CopyLinkView: UIStackView { 14 | 15 | // MARK: - Properties 16 | 17 | let button: UIButton = { 18 | let button = GrayButton() 19 | button.setTitle("Copy Link", forState: .Normal) 20 | return button 21 | }() 22 | 23 | 24 | // MARK: - Initializers 25 | 26 | init() { 27 | super.init(frame: .zero) 28 | 29 | axis = .Vertical 30 | alignment = .Center 31 | spacing = 16 32 | 33 | let label = UILabel() 34 | label.text = "Invite more participants" 35 | label.textColor = Swatch.gray 36 | addArrangedSubview(label) 37 | 38 | addArrangedSubview(button) 39 | } 40 | 41 | required init?(coder aDecoder: NSCoder) { 42 | fatalError("init(coder:) has not been implemented") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/CopyRepresentationActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CopyRepresentationActivity.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/15/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class CopyRepresentationActivity: UIActivity { 12 | 13 | // MARK: - Types 14 | 15 | // TODO: Localize 16 | enum Representation: String { 17 | case markdown 18 | case html 19 | case json 20 | 21 | var activityType: String { 22 | return "copy-\(rawValue)" 23 | } 24 | 25 | var activityTitle: String { 26 | switch self { 27 | case .markdown: return "Copy Markdown" 28 | case .html: return "Copy HTML" 29 | case .json: return "Copy JSON" 30 | } 31 | } 32 | 33 | var activityImage: UIImage? { 34 | switch self { 35 | case .markdown: return UIImage(named: "Copy Markdown") 36 | case .html: return UIImage(named: "Copy HTML") 37 | case .json: return UIImage(named: "Copy JSON") 38 | } 39 | } 40 | 41 | var ext: String { 42 | return rawValue 43 | } 44 | 45 | var successMessage: String { 46 | switch self { 47 | case .markdown: return "Copied markdown!" 48 | case .html: return "Copied HTML!" 49 | case .json: return "Copied JSON!" 50 | } 51 | } 52 | 53 | var failureMessage: String { 54 | switch self { 55 | case .markdown: return "Failed to copy markdown." 56 | case .html: return "Failed to copy HTML." 57 | case .json: return "Failed to copy JSON." 58 | } 59 | } 60 | } 61 | 62 | 63 | // MARK: - Properties 64 | 65 | let representation: Representation 66 | let session: NSURLSession 67 | 68 | private var canvasID: String? 69 | 70 | 71 | // MARK: - Initializers 72 | 73 | init(representation: Representation, session: NSURLSession = NSURLSession.sharedSession()) { 74 | self.representation = representation 75 | self.session = session 76 | super.init() 77 | } 78 | 79 | 80 | // MARK: - UIActivity 81 | 82 | override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool { 83 | guard let url = activityItems.first as? NSURL else { return false } 84 | 85 | if url.host == "usecanvas.com", let components = url.pathComponents where components.count == 4 && (components[3] as NSString).length == 22 { 86 | canvasID = components[3] 87 | return true 88 | } 89 | 90 | return false 91 | } 92 | 93 | override func performActivity() { 94 | guard let canvasID = canvasID, 95 | url = NSURL(string: "https://usecanvas.com/-/-/\(canvasID).\(representation.ext)") 96 | else { 97 | showBanner(text: representation.failureMessage, style: .failure) 98 | return 99 | } 100 | 101 | let request = NSURLRequest(URL: url) 102 | session.dataTaskWithRequest(request) { [weak self] data, _, _ in 103 | guard let representation = self?.representation else{ return } 104 | let string = data.flatMap { String(data: $0, encoding: NSUTF8StringEncoding) } 105 | 106 | dispatch_async(dispatch_get_main_queue()) { 107 | if let string = string { 108 | UIPasteboard.generalPasteboard().string = string 109 | self?.showBanner(text: representation.successMessage) 110 | } else { 111 | self?.showBanner(text: representation.failureMessage, style: .failure) 112 | } 113 | } 114 | }.resume() 115 | } 116 | 117 | override func activityType() -> String? { 118 | return representation.activityType 119 | } 120 | 121 | override func activityTitle() -> String? { 122 | return representation.activityTitle 123 | } 124 | 125 | override func activityImage() -> UIImage? { 126 | return representation.activityImage 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Sources/DestructiveValueCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DestructiveValueCell.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/18/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Static 11 | import CanvasCore 12 | 13 | final class DestructiveButtonCell: UITableViewCell, CellType { 14 | override func tintColorDidChange() { 15 | textLabel?.textColor = tintAdjustmentMode == .Dimmed ? tintColor: Swatch.destructive 16 | imageView?.tintColor = textLabel?.textColor 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/DragBackgroundView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DragBackgroundView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/20/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | // TODO: Get colors from theme 13 | final class DragBackgroundView: UIView { 14 | override init(frame: CGRect) { 15 | super.init(frame: frame) 16 | 17 | backgroundColor = .whiteColor() 18 | userInteractionEnabled = false 19 | 20 | let topBorder = LineView() 21 | topBorder.translatesAutoresizingMaskIntoConstraints = false 22 | addSubview(topBorder) 23 | 24 | let bottomBorder = LineView() 25 | bottomBorder.translatesAutoresizingMaskIntoConstraints = false 26 | addSubview(bottomBorder) 27 | 28 | NSLayoutConstraint.activateConstraints([ 29 | topBorder.leadingAnchor.constraintEqualToAnchor(leadingAnchor), 30 | topBorder.trailingAnchor.constraintEqualToAnchor(trailingAnchor), 31 | topBorder.bottomAnchor.constraintEqualToAnchor(topAnchor), 32 | 33 | bottomBorder.leadingAnchor.constraintEqualToAnchor(leadingAnchor), 34 | bottomBorder.trailingAnchor.constraintEqualToAnchor(trailingAnchor), 35 | bottomBorder.topAnchor.constraintEqualToAnchor(bottomAnchor) 36 | ]) 37 | } 38 | 39 | required init?(coder aDecoder: NSCoder) { 40 | fatalError("init(coder:) has not been implemented") 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/DragProgressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DragProgressView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/20/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | final class DragProgressView: UIView { 13 | 14 | // MARK: - Properties 15 | 16 | private let imageView: UIImageView = { 17 | let view = UIImageView() 18 | view.translatesAutoresizingMaskIntoConstraints = false 19 | view.tintColor = Swatch.gray 20 | view.contentMode = .Center 21 | return view 22 | }() 23 | 24 | 25 | // MARK: - Initializers 26 | 27 | init(icon: UIImage?, isLeading: Bool) { 28 | super.init(frame: .zero) 29 | backgroundColor = Swatch.extraLightGray 30 | userInteractionEnabled = false 31 | 32 | imageView.image = icon 33 | addSubview(imageView) 34 | 35 | imageView.centerYAnchor.constraintEqualToAnchor(centerYAnchor).active = true 36 | 37 | if isLeading { 38 | let x = imageView.trailingAnchor.constraintEqualToAnchor(trailingAnchor, constant: -8) 39 | x.priority = UILayoutPriorityDefaultLow 40 | 41 | NSLayoutConstraint.activateConstraints([ 42 | x, 43 | imageView.trailingAnchor.constraintLessThanOrEqualToAnchor(leadingAnchor, constant: DragContext.threshold) 44 | ]) 45 | } else { 46 | let x = imageView.leadingAnchor.constraintEqualToAnchor(leadingAnchor, constant: 8) 47 | x.priority = UILayoutPriorityDefaultLow 48 | 49 | NSLayoutConstraint.activateConstraints([ 50 | x, 51 | imageView.leadingAnchor.constraintGreaterThanOrEqualToAnchor(trailingAnchor, constant: -DragContext.threshold) 52 | ]) 53 | } 54 | } 55 | 56 | required init?(coder aDecoder: NSCoder) { 57 | fatalError("init(coder:) has not been implemented") 58 | } 59 | 60 | 61 | // MARK: - Translation 62 | 63 | func translate(x x: CGFloat) { 64 | let progress = min(abs(x) / DragContext.threshold, 1) 65 | imageView.tintColor = Swatch.extraLightGray.interpolateTo(color: Swatch.darkGray, progress: progress) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Sources/FooterButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FooterButton.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/13/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FooterButton: PrefaceButton { 12 | 13 | // MARK: - Properties 14 | 15 | let lineView: LineView = { 16 | let view = LineView() 17 | view.translatesAutoresizingMaskIntoConstraints = false 18 | return view 19 | }() 20 | 21 | 22 | // MARK: - Initializers 23 | 24 | override init(frame: CGRect) { 25 | super.init(frame: frame) 26 | 27 | addSubview(lineView) 28 | 29 | NSLayoutConstraint.activateConstraints([ 30 | lineView.leadingAnchor.constraintEqualToAnchor(leadingAnchor), 31 | lineView.trailingAnchor.constraintEqualToAnchor(trailingAnchor), 32 | lineView.topAnchor.constraintEqualToAnchor(topAnchor), 33 | 34 | heightAnchor.constraintEqualToConstant(48) 35 | ]) 36 | } 37 | 38 | required init?(coder aDecoder: NSCoder) { 39 | fatalError("init(coder:) has not been implemented") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/GrayButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GrayButton.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 8/9/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasText 12 | 13 | final class GrayButton: UIButton { 14 | 15 | // MARK: - Initializers 16 | 17 | override init(frame: CGRect) { 18 | super.init(frame: frame) 19 | 20 | backgroundColor = Swatch.lightGray 21 | 22 | layer.cornerRadius = 4 23 | 24 | setTitleColor(Swatch.darkGray, forState: .Normal) 25 | setTitleColor(Swatch.black, forState: .Highlighted) 26 | setTitleColor(Swatch.extraLightGray, forState: .Disabled) 27 | 28 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(updateFont), name: UIContentSizeCategoryDidChangeNotification, object: nil) 29 | updateFont() 30 | } 31 | 32 | required init?(coder aDecoder: NSCoder) { 33 | fatalError("init(coder:) has not been implemented") 34 | } 35 | 36 | 37 | // MARK: - UIView 38 | 39 | override func intrinsicContentSize() -> CGSize { 40 | var size = super.intrinsicContentSize() 41 | size.height = 32 42 | size.width += 32 * 2 43 | return size 44 | } 45 | 46 | 47 | // MARK: - UIControl 48 | 49 | override var enabled: Bool { 50 | didSet { 51 | backgroundColor = enabled ? Swatch.lightGray.colorWithAlphaComponent(0.5) : Swatch.lightGray 52 | } 53 | } 54 | 55 | override var highlighted: Bool { 56 | didSet { 57 | backgroundColor = highlighted ? Swatch.lightGray.colorWithAlphaComponent(0.8) : Swatch.lightGray 58 | } 59 | } 60 | 61 | override var selected: Bool { 62 | didSet { 63 | backgroundColor = selected ? Swatch.lightGray.colorWithAlphaComponent(0.8) : Swatch.lightGray 64 | } 65 | } 66 | 67 | 68 | // MARK: - Private 69 | 70 | @objc func updateFont() { 71 | titleLabel?.font = TextStyle.body.font(weight: .medium) 72 | } 73 | } -------------------------------------------------------------------------------- /Sources/GroupedSectionHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GroupedSectionHeaderView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/6/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasText 12 | 13 | final class GroupedSectionHeaderView: SectionHeaderView { 14 | 15 | // MARK: - Initializers 16 | 17 | override init(frame: CGRect) { 18 | super.init(frame: frame) 19 | 20 | backgroundColor = Swatch.groupedTableBackground 21 | tintColor = Swatch.darkGray 22 | } 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | 29 | // MARK: - UIView 30 | 31 | override func tintColorDidChange() { 32 | super.tintColorDidChange() 33 | textLabel.textColor = tintColor 34 | } 35 | 36 | 37 | // MARK: - Private 38 | 39 | override func updateFont() { 40 | super.updateFont() 41 | textLabel.font = TextStyle.footnote.font() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/IndicatorButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndicatorButton.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 5/13/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | final class IndicatorButton: PillButton { 13 | 14 | // MARK: - Properties 15 | 16 | var loading = false { 17 | didSet { 18 | titleLabel?.alpha = loading ? 0 : 1 19 | enabled = !loading 20 | 21 | if loading { 22 | activityIndicator.startAnimating() 23 | } else { 24 | activityIndicator.stopAnimating() 25 | } 26 | } 27 | } 28 | 29 | let activityIndicator: UIActivityIndicatorView = { 30 | let indicator = UIActivityIndicatorView(activityIndicatorStyle: .Gray) 31 | indicator.translatesAutoresizingMaskIntoConstraints = false 32 | indicator.userInteractionEnabled = false 33 | indicator.hidesWhenStopped = true 34 | indicator.color = Swatch.darkGray 35 | return indicator 36 | }() 37 | 38 | 39 | // MARK: - Initializers 40 | 41 | override init(frame: CGRect) { 42 | super.init(frame: frame) 43 | 44 | addSubview(activityIndicator) 45 | 46 | NSLayoutConstraint.activateConstraints([ 47 | activityIndicator.centerXAnchor.constraintEqualToAnchor(centerXAnchor), 48 | activityIndicator.centerYAnchor.constraintEqualToAnchor(centerYAnchor) 49 | ]) 50 | } 51 | 52 | required init?(coder aDecoder: NSCoder) { 53 | fatalError("init(coder:) has not been implemented") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Interpolate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Interpolate.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/25/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | func interpolate(start start: CGFloat, end: CGFloat, progress: CGFloat) -> CGFloat { 12 | return (end - start) * progress + start 13 | } 14 | 15 | 16 | extension UIColor { 17 | func interpolateTo(color end: UIColor, progress: CGFloat) -> UIColor { 18 | var r1: CGFloat = 0 19 | var g1: CGFloat = 0 20 | var b1: CGFloat = 0 21 | var a1: CGFloat = 0 22 | getRed(&r1, green: &g1, blue: &b1, alpha: &a1) 23 | 24 | var r2: CGFloat = 0 25 | var g2: CGFloat = 0 26 | var b2: CGFloat = 0 27 | var a2: CGFloat = 0 28 | end.getRed(&r2, green: &g2, blue: &b2, alpha: &a2) 29 | 30 | return UIColor( 31 | red: interpolate(start: r1, end: r2, progress: progress), 32 | green: interpolate(start: g1, end: g2, progress: progress), 33 | blue: interpolate(start: b1, end: b2, progress: progress), 34 | alpha: interpolate(start: a1, end: a2, progress: progress) 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/LineView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LineView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/3/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | final class LineView: UIView { 13 | 14 | // MARK: - Initializers 15 | 16 | override init(frame: CGRect) { 17 | super.init(frame: frame) 18 | backgroundColor = Swatch.border 19 | } 20 | 21 | required init?(coder aDecoder: NSCoder) { 22 | fatalError("init(coder:) has not been implemented") 23 | } 24 | 25 | 26 | // MARK: - UIView 27 | 28 | override func sizeThatFits(size: CGSize) -> CGSize { 29 | return CGSize(width: size.width, height: intrinsicContentSize().height) 30 | } 31 | 32 | override func intrinsicContentSize() -> CGSize { 33 | return CGSize(width: UIViewNoIntrinsicMetric, height: 1 / max(1, traitCollection.displayScale)) 34 | } 35 | 36 | override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) { 37 | super.traitCollectionDidChange(previousTraitCollection) 38 | invalidateIntrinsicContentSize() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/LoadCanvasViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadCanvasViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 5/31/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasKit 12 | 13 | final class LoadCanvasViewController: UIViewController, Accountable { 14 | 15 | // MARK: - Properties 16 | 17 | var account: Account 18 | let canvasID: String 19 | 20 | private var fetching = false 21 | private let activityIndicator: UIActivityIndicatorView = { 22 | let view = UIActivityIndicatorView(activityIndicatorStyle: .Gray) 23 | view.translatesAutoresizingMaskIntoConstraints = false 24 | view.startAnimating() 25 | return view 26 | }() 27 | 28 | 29 | // MARK: - Initializers 30 | 31 | init(account: Account, canvasID: String) { 32 | self.account = account 33 | self.canvasID = canvasID 34 | 35 | super.init(nibName: nil, bundle: nil) 36 | } 37 | 38 | required init?(coder aDecoder: NSCoder) { 39 | fatalError("init(coder:) has not been implemented") 40 | } 41 | 42 | 43 | // MARK: - UIViewController 44 | 45 | override func viewDidLoad() { 46 | super.viewDidLoad() 47 | 48 | view.backgroundColor = Swatch.white 49 | view.addSubview(activityIndicator) 50 | 51 | navigationItem.hidesBackButton = true 52 | 53 | NSLayoutConstraint.activateConstraints([ 54 | activityIndicator.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor), 55 | activityIndicator.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor), 56 | ]) 57 | } 58 | 59 | override func viewDidAppear(animated: Bool) { 60 | super.viewDidAppear(animated) 61 | fetch() 62 | } 63 | 64 | 65 | // MARK: - Private 66 | 67 | private func fetch() { 68 | if fetching { 69 | return 70 | } 71 | 72 | fetching = true 73 | 74 | APIClient(account: account).showCanvas(id: canvasID) { result in 75 | dispatch_async(dispatch_get_main_queue()) { [weak self] in 76 | switch result { 77 | case .Success(let canvas): self?.showEditor(canvas: canvas) 78 | case .Failure(let message): self?.showError(message: message) 79 | } 80 | } 81 | } 82 | } 83 | 84 | private func showEditor(canvas canvas: Canvas) { 85 | guard let navigationController = navigationController else { return } 86 | 87 | let viewController = EditorViewController(account: account, canvas: canvas) 88 | viewController.navigationItem.leftBarButtonItem = UIBarButtonItem(title: LocalizedString.CloseCommand.string, style: .Plain, target: viewController, action: #selector(EditorViewController.closeNavigationControllerModal)) 89 | 90 | var viewControllers = navigationController.viewControllers 91 | viewControllers[viewControllers.count - 1] = viewController 92 | navigationController.setViewControllers(viewControllers, animated: false) 93 | } 94 | 95 | private func showError(message message: String) { 96 | activityIndicator.stopAnimating() 97 | 98 | let billboard = BillboardView() 99 | billboard.translatesAutoresizingMaskIntoConstraints = false 100 | billboard.illustrationView.image = UIImage(named: "Not Found") 101 | billboard.titleLabel.text = LocalizedString.NotFoundHeading.string 102 | billboard.subtitleLabel.text = LocalizedString.NotFoundMessage.string 103 | view.addSubview(billboard) 104 | 105 | NSLayoutConstraint.activateConstraints([ 106 | billboard.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor), 107 | billboard.widthAnchor.constraintLessThanOrEqualToAnchor(view.widthAnchor), 108 | billboard.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor), 109 | ]) 110 | 111 | title = LocalizedString.NotFoundTitle.string 112 | navigationItem.leftBarButtonItem = UIBarButtonItem(title: LocalizedString.CloseCommand.string, style: .Plain, target: self, action: #selector(close)) 113 | } 114 | 115 | @objc private func close() { 116 | dismissViewControllerAnimated(true, completion: nil) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Sources/ModelsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModelsViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 11/23/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasKit 12 | import Static 13 | import PullToRefresh 14 | 15 | // TODO: Localize this class 16 | class ModelsViewController: TableViewController { 17 | 18 | // MARK: - Properties 19 | 20 | var loading = false { 21 | didSet { 22 | UIApplication.sharedApplication().networkActivityIndicatorVisible = loading 23 | 24 | if loading { 25 | refreshView.startRefreshing(false) 26 | } else { 27 | refreshView.finishRefreshing() 28 | } 29 | } 30 | } 31 | 32 | var opening = false 33 | 34 | let refreshView = RefreshView() 35 | 36 | 37 | // MARK: - Initializers 38 | 39 | override init(style: UITableViewStyle) { 40 | super.init(style: style) 41 | 42 | refreshView.expandedHeight = 48 + 32 43 | refreshView.delegate = self 44 | refreshView.contentView = RefreshContentView() 45 | } 46 | 47 | required init?(coder aDecoder: NSCoder) { 48 | fatalError("init(coder:) has not been implemented") 49 | } 50 | 51 | deinit { 52 | refreshView.scrollView = nil 53 | } 54 | 55 | 56 | // MARK: - UIResponder 57 | 58 | override func canBecomeFirstResponder() -> Bool { 59 | return true 60 | } 61 | 62 | override var keyCommands: [UIKeyCommand] { 63 | var commands = super.keyCommands ?? [] 64 | 65 | if let navigationController = navigationController where navigationController.viewControllers.count > 1 { 66 | let previousTitle = (navigationController.viewControllers[navigationController.viewControllers.count - 2]).title 67 | let backTitle = previousTitle.flatMap { "Back to \($0)" } ?? "Back" 68 | 69 | commands += [ 70 | UIKeyCommand(input: UIKeyInputLeftArrow, modifierFlags: [], action: #selector(goBack), discoverabilityTitle: backTitle), 71 | UIKeyCommand(input: "w", modifierFlags: [.Command], action: #selector(goBack)) 72 | ] 73 | } 74 | 75 | if canRefresh { 76 | commands.append(UIKeyCommand(input: "R", modifierFlags: [.Command], action: #selector(refresh), discoverabilityTitle: "Refresh")) 77 | } 78 | 79 | return commands 80 | } 81 | 82 | 83 | // MARK: - UIViewController 84 | 85 | override func viewWillAppear(animated: Bool) { 86 | super.viewWillAppear(animated) 87 | opening = false 88 | refresh() 89 | } 90 | 91 | override func viewDidLayoutSubviews() { 92 | super.viewDidLayoutSubviews() 93 | 94 | if canRefresh && refreshView.scrollView == nil { 95 | refreshView.scrollView = tableView 96 | } 97 | 98 | refreshView.defaultContentInsets = UIEdgeInsetsMake(topLayoutGuide.length, 0, 0, 0) 99 | } 100 | 101 | 102 | // MARK: - Configuration 103 | 104 | var canRefresh = true 105 | 106 | 107 | // MARK: - Actions 108 | 109 | func refresh() { 110 | // Subclasses should override this 111 | } 112 | 113 | 114 | // MARK: - Private 115 | 116 | @objc private func goBack() { 117 | navigationController?.popViewControllerAnimated(true) 118 | } 119 | } 120 | 121 | 122 | extension ModelsViewController: RefreshViewDelegate { 123 | func refreshViewDidStartRefreshing(refreshView: RefreshView) { 124 | refresh() 125 | } 126 | 127 | func refreshViewShouldStartRefreshing(refreshView: RefreshView) -> Bool { return true } 128 | func refreshViewDidFinishRefreshing(refreshView: RefreshView) {} 129 | func lastUpdatedAtForRefreshView(refreshView: RefreshView) -> NSDate? { return nil } 130 | func refreshView(refreshView: RefreshView, didUpdateContentInset contentInset: UIEdgeInsets) {} 131 | func refreshView(refreshView: RefreshView, willTransitionTo to: RefreshView.State, from: RefreshView.State, animated: Bool) {} 132 | func refreshView(refreshView: RefreshView, didTransitionTo to: RefreshView.State, from: RefreshView.State, animated: Bool) {} 133 | } 134 | -------------------------------------------------------------------------------- /Sources/NSDate+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 12/2/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension NSDate { 12 | var briefTimeAgoInWords: String { 13 | let components = NSCalendar.currentCalendar().components([.Second, .Minute, .Hour, .Day, .Year], fromDate: self, toDate: NSDate(), options: []) 14 | 15 | if components.year > 0 { 16 | return "\(components.year)y" 17 | } 18 | 19 | if components.day > 0 { 20 | return "\(components.day)d" 21 | } 22 | 23 | if components.hour > 0 { 24 | return "\(components.hour)h" 25 | } 26 | 27 | if components.minute > 0 { 28 | return "\(components.minute)m" 29 | } 30 | 31 | return "\(components.second)s" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/NSProcessInfo+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSProcessInfo+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/18/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension NSProcessInfo { 12 | var isSnapshotting: Bool { 13 | return arguments.contains("-snapshot") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/NavigationBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationBar.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 2/5/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | final class NavigationBar: UINavigationBar { 13 | 14 | // MARK: - Properties 15 | 16 | var titleColor: UIColor? { 17 | didSet { 18 | updateTitleColor() 19 | } 20 | } 21 | 22 | var borderColor: UIColor? { 23 | set { 24 | borderView.backgroundColor = newValue 25 | } 26 | 27 | get { 28 | return borderView.backgroundColor 29 | } 30 | } 31 | 32 | private let borderView: LineView = { 33 | let view = LineView() 34 | view.translatesAutoresizingMaskIntoConstraints = false 35 | return view 36 | }() 37 | 38 | 39 | // MARK: - Initializers 40 | 41 | override init(frame: CGRect) { 42 | super.init(frame: frame) 43 | 44 | barTintColor = Swatch.white 45 | translucent = false 46 | shadowImage = UIImage() 47 | backIndicatorImage = UIImage(named: "ChevronLeft") 48 | backIndicatorTransitionMaskImage = UIImage(named: "ChevronLeft") 49 | 50 | borderColor = Swatch.border 51 | 52 | addSubview(borderView) 53 | 54 | NSLayoutConstraint.activateConstraints([ 55 | borderView.topAnchor.constraintEqualToAnchor(bottomAnchor), 56 | borderView.leadingAnchor.constraintEqualToAnchor(leadingAnchor), 57 | borderView.trailingAnchor.constraintEqualToAnchor(trailingAnchor) 58 | ]) 59 | } 60 | 61 | required init?(coder aDecoder: NSCoder) { 62 | fatalError("init(coder:) has not been implemented") 63 | } 64 | 65 | override func tintColorDidChange() { 66 | super.tintColorDidChange() 67 | updateTitleColor() 68 | } 69 | 70 | 71 | // MARK: - Private 72 | 73 | private func updateTitleColor() { 74 | titleTextAttributes = [ 75 | NSFontAttributeName: Font.sansSerif(weight: .medium), 76 | NSForegroundColorAttributeName: tintAdjustmentMode == .Dimmed ? tintColor : (titleColor ?? Swatch.darkGray) 77 | ] 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Sources/NavigationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 1/12/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | final class NavigationController: UINavigationController { 13 | 14 | // MARK: - Properties 15 | 16 | private let defaultTintColor = Swatch.brand 17 | private let defaultTitleColor = Swatch.black 18 | 19 | 20 | // MARK: - Initializers 21 | 22 | override init(rootViewController: UIViewController) { 23 | super.init(navigationBarClass: NavigationBar.self, toolbarClass: nil) 24 | 25 | viewControllers = [rootViewController] 26 | 27 | updateTintColor(view.tintColor) 28 | 29 | delegate = self 30 | } 31 | 32 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { 33 | super.init(nibName: nil, bundle: nil) 34 | } 35 | 36 | required init?(coder aDecoder: NSCoder) { 37 | fatalError("init(coder:) has not been implemented") 38 | } 39 | 40 | 41 | // MARK: - Private 42 | 43 | private func updateTintColor(viewController: UIViewController) { 44 | var target = viewController 45 | 46 | // Handle nested navigation controllers for when the split view is collapsed 47 | if let top = (viewController as? UINavigationController)?.topViewController { 48 | target = top 49 | } 50 | 51 | let tintColor = (target as? TintableEnvironment)?.preferredTintColor 52 | updateTintColor(tintColor) 53 | } 54 | 55 | private func updateTintColor(tintColor: UIColor?) { 56 | let itemsColor = tintColor ?? defaultTintColor 57 | view.tintColor = itemsColor 58 | navigationBar.tintColor = itemsColor 59 | } 60 | } 61 | 62 | 63 | extension NavigationController: UINavigationControllerDelegate { 64 | func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) { 65 | // Call didShow if the animation is canceled 66 | transitionCoordinator()?.notifyWhenInteractionEndsUsingBlock { [weak self] context in 67 | guard context.isCancelled() else { return } 68 | guard let delegate = self, from = context.viewControllerForKey(UITransitionContextFromViewControllerKey) else { return } 69 | delegate.navigationController(navigationController, willShowViewController: from, animated: animated) 70 | 71 | let animationCompletion = context.transitionDuration() * NSTimeInterval(context.percentComplete()) 72 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(animationCompletion) * Int64(NSEC_PER_SEC)), dispatch_get_main_queue()) { 73 | delegate.navigationController(navigationController, didShowViewController: from, animated: animated) 74 | } 75 | } 76 | 77 | updateTintColor(viewController) 78 | } 79 | 80 | func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) { 81 | updateTintColor(viewController) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Sources/OAuthClient+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OAuthClient+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 8/12/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import CanvasKit 10 | 11 | extension OAuthClient { 12 | init() { 13 | self.init(clientID: config.canvasClientID, clientSecret: config.canvasClientSecret, baseURL: config.environment.apiURL) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/OnboardingBillboardViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/7/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasText 12 | 13 | class OnboardingBillboardViewController: StackViewController { 14 | 15 | // MARK: - UIViewController 16 | 17 | var illustrationName: String? { 18 | didSet { 19 | updateIllustration() 20 | } 21 | } 22 | 23 | private let illustrationView: UIImageView = { 24 | let view = UIImageView() 25 | view.contentMode = .Center 26 | return view 27 | }() 28 | 29 | var text: String? { 30 | get { 31 | return textLabel.text 32 | } 33 | 34 | set { 35 | textLabel.text = newValue 36 | } 37 | } 38 | 39 | private let textLabel: UILabel = { 40 | let label = UILabel() 41 | label.textColor = Swatch.black 42 | label.numberOfLines = 0 43 | label.textAlignment = .Center 44 | return label 45 | }() 46 | 47 | var detailText: String? { 48 | get { 49 | return detailTextLabel.text 50 | } 51 | 52 | set { 53 | detailTextLabel.text = newValue 54 | } 55 | } 56 | 57 | private let detailTextLabel: UILabel = { 58 | let label = UILabel() 59 | label.textColor = Swatch.darkGray 60 | label.numberOfLines = 0 61 | label.textAlignment = .Center 62 | return label 63 | }() 64 | 65 | private var textIllustrationSpacing: NSLayoutConstraint! 66 | 67 | 68 | // MARK: - UIViewController 69 | 70 | override func viewDidLoad() { 71 | super.viewDidLoad() 72 | 73 | stackView.axis = .Vertical 74 | stackView.alignment = .Center 75 | stackView.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 16 + 44, right: 16) 76 | stackView.layoutMarginsRelativeArrangement = true 77 | 78 | stackView.addArrangedSubview(textLabel) 79 | stackView.addSpace(12) 80 | stackView.addArrangedSubview(detailTextLabel) 81 | 82 | let spacer = UIView() 83 | spacer.translatesAutoresizingMaskIntoConstraints = false 84 | stackView.addArrangedSubview(spacer) 85 | 86 | textIllustrationSpacing = spacer.heightAnchor.constraintEqualToConstant(0) 87 | textIllustrationSpacing.active = true 88 | 89 | stackView.addArrangedSubview(illustrationView) 90 | 91 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(updateFonts), name: UIContentSizeCategoryDidChangeNotification, object: nil) 92 | updateFonts() 93 | } 94 | 95 | override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) { 96 | super.traitCollectionDidChange(previousTraitCollection) 97 | 98 | let spacing: CGFloat 99 | 100 | if view.bounds.height > 480 { 101 | spacing = traitCollection.horizontalSizeClass == .Regular ? 48 : 32 102 | } else { 103 | spacing = 16 104 | } 105 | 106 | textIllustrationSpacing.constant = spacing 107 | 108 | updateIllustration() 109 | } 110 | 111 | 112 | // MARK: - Private 113 | 114 | @objc private func updateFonts() { 115 | textLabel.font = TextStyle.title1.font() 116 | detailTextLabel.font = TextStyle.body.font() 117 | } 118 | 119 | private func updateIllustration() { 120 | guard let illustrationName = illustrationName else { 121 | illustrationView.image = nil 122 | return 123 | } 124 | 125 | var name = illustrationName 126 | 127 | switch traitCollection.horizontalSizeClass { 128 | case .Compact, .Unspecified: 129 | name += "Compact" 130 | case .Regular: 131 | name += "Regular" 132 | } 133 | 134 | illustrationView.image = UIImage(named: name) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Sources/OnboardingGesturesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingGesturesViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/7/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // TODO: Localize 12 | final class OnboardingGesturesViewController: OnboardingBillboardViewController { 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | text = "Easy to the Touch" 17 | detailText = "Swipe gestures turn paragraphs\ninto lists and headings." 18 | illustrationName = "Gestures" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/OnboardingOrigamiViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingOrigamiViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/7/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // TODO: Localize 12 | final class OnboardingOrigamiViewController: OnboardingBillboardViewController { 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | text = "Origami Markdown" 17 | detailText = "Folds away Markdown syntax\nwhen you don’t need it." 18 | illustrationName = "Origami" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/OnboardingSharingViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingSharingViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/7/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // TODO: Localize 12 | final class OnboardingSharingViewController: OnboardingBillboardViewController { 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | text = "Simple Sharing" 17 | detailText = "Collaboration is as easy\nas sharing a URL." 18 | illustrationName = "Share" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/OnboardingWelcomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingWelcomeViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/7/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // TODO: Localize 12 | final class OnboardingWelcomeViewController: OnboardingBillboardViewController { 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | text = "Welcome to Canvas" 17 | detailText = "Collaborative notes\nfor teams of nerds." 18 | illustrationName = "Welcome" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Organization+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Organization+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 1/12/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasKit 12 | import Static 13 | 14 | extension Organization { 15 | var isPersonalNotes: Bool { 16 | guard let account = AccountController.sharedController.currentAccount else { return false } 17 | return slug == account.user.username 18 | } 19 | 20 | var displayName: String { 21 | return isPersonalNotes ? LocalizedString.PersonalNotes.string : name 22 | } 23 | 24 | var row: Row { 25 | // TODO: Localize 26 | var detailText = "\(membersCount) member" 27 | if membersCount != 1 { 28 | detailText += "s" 29 | } 30 | 31 | return Row( 32 | text: displayName, 33 | detailText: detailText, 34 | context: [ 35 | "organization": self 36 | ], 37 | cellClass: OrganizationCell.self 38 | ) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/OrganizationAvatarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OrganizationAvatarView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 1/12/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasKit 12 | 13 | final class OrganizationAvatarView: UIView { 14 | 15 | // MARK: - Properties 16 | 17 | var highlighted = false { 18 | didSet { 19 | updateUI() 20 | } 21 | } 22 | 23 | var organization: Organization? { 24 | didSet { 25 | updateUI() 26 | } 27 | } 28 | 29 | private let avatarView = AvatarView() 30 | 31 | private let initialsLabel: UILabel = { 32 | let label = UILabel() 33 | label.textColor = Swatch.white 34 | label.textAlignment = .Center 35 | label.font = Font.sansSerif(weight: .medium, size: .small) 36 | return label 37 | }() 38 | 39 | 40 | // MARK: - UIView 41 | 42 | override func tintColorDidChange() { 43 | if let organization = organization where organization.isPersonalNotes { 44 | return 45 | } 46 | 47 | backgroundColor = tintColor 48 | } 49 | 50 | override func layoutSubviews() { 51 | if initialsLabel.superview != nil { 52 | initialsLabel.frame = bounds 53 | } 54 | 55 | if avatarView.superview != nil { 56 | avatarView.frame = bounds 57 | } 58 | } 59 | 60 | 61 | // MARK: - Private 62 | 63 | private func updateUI() { 64 | guard let organization = organization else { 65 | initialsLabel.removeFromSuperview() 66 | initialsLabel.text = nil 67 | tintColor = highlighted ? Swatch.white : Swatch.cellDisclosureIndicator 68 | avatarView.removeFromSuperview() 69 | avatarView.user = nil 70 | 71 | tintColor = highlighted ? Swatch.white : Swatch.cellDisclosureIndicator 72 | return 73 | } 74 | 75 | if organization.isPersonalNotes, let user = AccountController.sharedController.currentAccount?.user { 76 | initialsLabel.removeFromSuperview() 77 | initialsLabel.text = nil 78 | 79 | avatarView.user = user 80 | 81 | if avatarView.superview == nil { 82 | layer.cornerRadius = 0 83 | addSubview(avatarView) 84 | setNeedsLayout() 85 | } 86 | 87 | backgroundColor = .clearColor() 88 | return 89 | } 90 | 91 | avatarView.removeFromSuperview() 92 | avatarView.user = nil 93 | 94 | let orgColor = organization.color?.uiColor ?? Swatch.brand 95 | 96 | tintColor = highlighted ? Swatch.white : orgColor 97 | 98 | let name = organization.name 99 | initialsLabel.text = name.substringToIndex(name.startIndex.advancedBy(2)) 100 | initialsLabel.textColor = highlighted ? orgColor : Swatch.white 101 | 102 | if initialsLabel.superview == nil { 103 | layer.cornerRadius = 4 104 | tintColorDidChange() 105 | addSubview(initialsLabel) 106 | setNeedsLayout() 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Sources/OrganizationCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OrganizationCell.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 1/13/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Static 11 | import CanvasCore 12 | import CanvasKit 13 | import CanvasText 14 | 15 | final class OrganizationCell: UITableViewCell, CellType { 16 | 17 | // MARK: - Properties 18 | 19 | let avatarView: OrganizationAvatarView = { 20 | let view = OrganizationAvatarView() 21 | view.translatesAutoresizingMaskIntoConstraints = false 22 | return view 23 | }() 24 | 25 | let titleLabel: UILabel = { 26 | let label = UILabel() 27 | label.translatesAutoresizingMaskIntoConstraints = false 28 | label.backgroundColor = Swatch.white 29 | label.textColor = Swatch.black 30 | label.highlightedTextColor = Swatch.white 31 | label.setContentCompressionResistancePriority(UILayoutPriorityDefaultLow, forAxis: .Horizontal) 32 | return label 33 | }() 34 | 35 | let disclosureIndicatorView = UIImageView(image: UIImage(named: "ChevronRightSmall")) 36 | 37 | 38 | // MARK: - Initializers 39 | 40 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 41 | super.init(style: .Subtitle, reuseIdentifier: reuseIdentifier) 42 | 43 | let view = UIView() 44 | view.backgroundColor = tintColor 45 | selectedBackgroundView = view 46 | 47 | accessoryView = disclosureIndicatorView 48 | 49 | setupViews() 50 | setupConstraints() 51 | 52 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(updateFont), name: UIContentSizeCategoryDidChangeNotification, object: nil) 53 | updateFont() 54 | } 55 | 56 | required init?(coder aDecoder: NSCoder) { 57 | fatalError("init(coder:) has not been implemented") 58 | } 59 | 60 | 61 | // MARK: - UITableViewCell 62 | 63 | override func setHighlighted(highlighted: Bool, animated: Bool) { 64 | super.setHighlighted(highlighted, animated: animated) 65 | updateHighlighted() 66 | } 67 | 68 | override func setSelected(selected: Bool, animated: Bool) { 69 | super.setSelected(selected, animated: animated) 70 | updateHighlighted() 71 | } 72 | 73 | 74 | // MARK: - Configuration 75 | 76 | func setupViews() { 77 | contentView.addSubview(avatarView) 78 | contentView.addSubview(titleLabel) 79 | } 80 | 81 | func setupConstraints() { 82 | NSLayoutConstraint.activateConstraints([ 83 | contentView.heightAnchor.constraintGreaterThanOrEqualToConstant(56), 84 | 85 | NSLayoutConstraint(item: avatarView, attribute: .Leading, relatedBy: .Equal, toItem: contentView, attribute: .LeadingMargin, multiplier: 1, constant: 0), 86 | avatarView.widthAnchor.constraintEqualToConstant(32), 87 | avatarView.heightAnchor.constraintEqualToConstant(32), 88 | avatarView.centerYAnchor.constraintEqualToAnchor(contentView.centerYAnchor), 89 | 90 | titleLabel.centerYAnchor.constraintEqualToAnchor(contentView.centerYAnchor), 91 | titleLabel.leadingAnchor.constraintEqualToAnchor(avatarView.trailingAnchor, constant: 8), 92 | NSLayoutConstraint(item: titleLabel, attribute: .Trailing, relatedBy: .LessThanOrEqual, toItem: contentView, attribute: .TrailingMargin, multiplier: 1, constant: 0), 93 | ]) 94 | } 95 | 96 | 97 | // MARK: - CellType 98 | 99 | func configure(row row: Row) { 100 | titleLabel.text = row.text 101 | 102 | let organization = row.context?["organization"] as? Organization 103 | avatarView.organization = organization 104 | selectedBackgroundView?.backgroundColor = organization?.color?.uiColor ?? Swatch.brand 105 | } 106 | 107 | 108 | // MARK: - Private 109 | 110 | private func updateHighlighted() { 111 | avatarView.highlighted = highlighted || selected 112 | disclosureIndicatorView.tintColor = highlighted || selected ? Swatch.white : Swatch.cellDisclosureIndicator 113 | } 114 | 115 | @objc private func updateFont() { 116 | titleLabel.font = TextStyle.body.font(weight: .medium) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Sources/PillButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PillButton.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 11/25/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasText 12 | 13 | class PillButton: UIButton { 14 | 15 | // MARK: - Initializers 16 | 17 | override init(frame: CGRect) { 18 | super.init(frame: frame) 19 | 20 | backgroundColor = .clearColor() 21 | 22 | layer.cornerRadius = 24 23 | layer.borderWidth = 2 24 | 25 | setTitleColor(Swatch.brand, forState: .Normal) 26 | setTitleColor(Swatch.lightBlue, forState: .Highlighted) 27 | setTitleColor(Swatch.darkGray, forState: .Disabled) 28 | 29 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(updateFont), name: UIContentSizeCategoryDidChangeNotification, object: nil) 30 | updateFont() 31 | updateBorderColor() 32 | } 33 | 34 | required init?(coder aDecoder: NSCoder) { 35 | fatalError("init(coder:) has not been implemented") 36 | } 37 | 38 | 39 | // MARK: - UIView 40 | 41 | override func intrinsicContentSize() -> CGSize { 42 | var size = super.intrinsicContentSize() 43 | size.height = 48 44 | size.width += 32 * 2 45 | return size 46 | } 47 | 48 | 49 | // MARK: - UIControl 50 | 51 | override var enabled: Bool { 52 | didSet { 53 | updateBorderColor() 54 | } 55 | } 56 | 57 | override var highlighted: Bool { 58 | didSet { 59 | updateBorderColor() 60 | } 61 | } 62 | 63 | override var selected: Bool { 64 | didSet { 65 | updateBorderColor() 66 | } 67 | } 68 | 69 | 70 | // MARK: - Private 71 | 72 | private func updateBorderColor() { 73 | layer.borderColor = titleColorForState(state)?.CGColor 74 | } 75 | 76 | @objc func updateFont() { 77 | titleLabel?.font = TextStyle.body.font(weight: .medium) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Sources/PlaceholderViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlaceholderViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 5/10/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasText 12 | 13 | final class PlaceholderViewController: UIViewController { 14 | 15 | // MARK: - Properties 16 | 17 | private let textLabel: UILabel = { 18 | let label = UILabel() 19 | label.translatesAutoresizingMaskIntoConstraints = false 20 | label.text = "No Canvas Selected" 21 | label.textColor = Swatch.darkGray 22 | return label 23 | }() 24 | 25 | 26 | // MARK: - UIViewController 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | 31 | view.backgroundColor = Swatch.white 32 | 33 | view.addSubview(textLabel) 34 | 35 | NSLayoutConstraint.activateConstraints([ 36 | textLabel.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor), 37 | textLabel.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor), 38 | ]) 39 | 40 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(updateFont), name: UIContentSizeCategoryDidChangeNotification, object: nil) 41 | updateFont() 42 | } 43 | 44 | override func viewDidAppear(animated: Bool) { 45 | super.viewDidAppear(animated) 46 | UIDevice.currentDevice().batteryMonitoringEnabled = false 47 | } 48 | 49 | 50 | // MARK: - Private 51 | 52 | @objc private func updateFont() { 53 | textLabel.font = TextStyle.body.font() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/PrefaceButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrefaceButton.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/13/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | class PrefaceButton: PillButton { 13 | 14 | // MARK: - Initializers 15 | 16 | override init(frame: CGRect) { 17 | super.init(frame: frame) 18 | 19 | titleLabel?.numberOfLines = 0 20 | titleLabel?.textAlignment = .Center 21 | layer.borderWidth = 0 22 | } 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | 29 | // MARK: - Preface 30 | 31 | func set(preface preface: String, title: String) { 32 | // Use non-breaking spaces for the title 33 | let title = title.stringByReplacingOccurrencesOfString(" ", withString: "\u{00A0}") 34 | 35 | // TODO: Localize 36 | let string = "\(preface) \(title)" 37 | let emphasizedRange = NSRange( 38 | location: (preface as NSString).length + 1, 39 | length: (title as NSString).length 40 | ) 41 | 42 | let normalText = NSMutableAttributedString(string: string, attributes: [ 43 | NSFontAttributeName: Font.sansSerif(size: .body), 44 | NSForegroundColorAttributeName: Swatch.darkGray 45 | ]) 46 | 47 | normalText.setAttributes([ 48 | NSFontAttributeName: Font.sansSerif(size: .body, weight: .medium), 49 | NSForegroundColorAttributeName: Swatch.brand 50 | ], range: emphasizedRange) 51 | 52 | setAttributedTitle(normalText, forState: .Normal) 53 | 54 | let highlightedText = NSMutableAttributedString(string: string, attributes: [ 55 | NSFontAttributeName: Font.sansSerif(size: .body), 56 | 57 | // TODO: Use a named color for this 58 | NSForegroundColorAttributeName: Swatch.darkGray.colorWithAlphaComponent(0.6) 59 | ]) 60 | 61 | highlightedText.setAttributes([ 62 | NSFontAttributeName: Font.sansSerif(size: .body, weight: .medium), 63 | NSForegroundColorAttributeName: Swatch.lightBlue 64 | ], range: emphasizedRange) 65 | 66 | setAttributedTitle(highlightedText, forState: .Highlighted) 67 | 68 | let disabledText = NSAttributedString(string: string, attributes: [ 69 | NSFontAttributeName: Font.sansSerif(size: .body), 70 | NSForegroundColorAttributeName: Swatch.darkGray 71 | ]) 72 | setAttributedTitle(disabledText, forState: .Disabled) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Sources/SafariActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SafariActivity.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 11/18/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class SafariActivity: WebActivity { 12 | 13 | // MARK: - UIActivity 14 | 15 | override func activityType() -> String? { 16 | return "open-in-safari" 17 | } 18 | 19 | override func activityTitle() -> String? { 20 | return "Open in Safari" 21 | } 22 | 23 | override func activityImage() -> UIImage? { 24 | return UIImage(named: "Safari") 25 | } 26 | 27 | override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool { 28 | for activityItem in activityItems { 29 | if let URL = activityItem as? NSURL where UIApplication.sharedApplication().canOpenURL(URL) { 30 | return true 31 | } 32 | } 33 | 34 | return false 35 | } 36 | 37 | override func performActivity() { 38 | let completed = URL.flatMap { UIApplication.sharedApplication().openURL($0) } ?? false 39 | activityDidFinish(completed) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/SearchBarContainer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchBarContainer.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 2/9/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | final class SearchBarContainer: UIView { 13 | 14 | // MARK: - Properties 15 | 16 | let searchBar: UISearchBar 17 | 18 | private let topBorderView = UIView() 19 | private let bottomBorderView = UIView() 20 | 21 | 22 | // MARK: - Initializers 23 | 24 | init(searchBar: UISearchBar) { 25 | self.searchBar = searchBar 26 | 27 | super.init(frame: searchBar.bounds) 28 | 29 | autoresizingMask = [.FlexibleWidth] 30 | 31 | searchBar.barTintColor = Swatch.white 32 | searchBar.layer.borderColor = Swatch.white.CGColor 33 | searchBar.layer.borderWidth = 1 34 | searchBar.backgroundColor = Swatch.white 35 | searchBar.translucent = false 36 | searchBar.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] 37 | searchBar.setImage(UIImage(named: "SearchSmall"), forSearchBarIcon: .Search, state: .Normal) 38 | addSubview(searchBar) 39 | 40 | if let string = searchBar.placeholder { 41 | let placeholder = NSAttributedString(string: string, attributes: [ 42 | NSForegroundColorAttributeName: Swatch.darkGray, 43 | NSFontAttributeName: Font.sansSerif(size: .small) 44 | ]) 45 | UITextField.appearanceWhenContainedInInstancesOfClasses([UISearchBar.self]).attributedPlaceholder = placeholder 46 | } 47 | 48 | topBorderView.backgroundColor = Swatch.border 49 | addSubview(topBorderView) 50 | 51 | bottomBorderView.backgroundColor = Swatch.border 52 | addSubview(bottomBorderView) 53 | } 54 | 55 | required init?(coder aDecoder: NSCoder) { 56 | fatalError("init(coder:) has not been implemented") 57 | } 58 | 59 | 60 | // MARK: - UIView 61 | 62 | override func addSubview(view: UIView) { 63 | super.addSubview(view) 64 | 65 | // UISearchController removes this view and then adds it back. Move it to the back when it's added so it stays 66 | // below the borders. 67 | if view == searchBar { 68 | sendSubviewToBack(view) 69 | } 70 | } 71 | 72 | override func sizeThatFits(size: CGSize) -> CGSize { 73 | return CGSize(width: size.width, height: 44) 74 | } 75 | 76 | override func layoutSubviews() { 77 | let borderHeight = 1 / traitCollection.displayScale 78 | 79 | topBorderView.frame = CGRect(x: 0, y: 0, width: bounds.width, height: borderHeight) 80 | bottomBorderView.frame = CGRect(x: 0, y: bounds.height - borderHeight, width: bounds.width, height: borderHeight) 81 | } 82 | 83 | override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) { 84 | super.traitCollectionDidChange(previousTraitCollection) 85 | setNeedsLayout() 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Sources/SectionHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SectionHeaderView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/3/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasText 12 | 13 | class SectionHeaderView: UIView { 14 | 15 | // MARK: - Properties 16 | 17 | let textLabel: UILabel = { 18 | let label = UILabel() 19 | label.translatesAutoresizingMaskIntoConstraints = false 20 | label.textColor = Swatch.black 21 | return label 22 | }() 23 | 24 | 25 | // MARK: - Initializers 26 | 27 | convenience init(title: String) { 28 | self.init(frame: CGRect(x: 0, y: 0, width: 320, height: 30)) 29 | textLabel.text = title 30 | } 31 | 32 | override init(frame: CGRect) { 33 | super.init(frame: frame) 34 | 35 | autoresizingMask = [.FlexibleWidth] 36 | 37 | addSubview(textLabel) 38 | 39 | NSLayoutConstraint.activateConstraints([ 40 | textLabel.leadingAnchor.constraintEqualToAnchor(leadingAnchor, constant: 16), 41 | textLabel.trailingAnchor.constraintLessThanOrEqualToAnchor(trailingAnchor, constant: -16), 42 | textLabel.topAnchor.constraintEqualToAnchor(topAnchor, constant: 4), 43 | textLabel.bottomAnchor.constraintEqualToAnchor(bottomAnchor, constant: -4) 44 | ]) 45 | 46 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(updateFont), name: UIContentSizeCategoryDidChangeNotification, object: nil) 47 | updateFont() 48 | } 49 | 50 | required init?(coder aDecoder: NSCoder) { 51 | fatalError("init(coder:) has not been implemented") 52 | } 53 | 54 | 55 | // MARK: - UIView 56 | 57 | override func tintColorDidChange() { 58 | super.tintColorDidChange() 59 | backgroundColor = tintAdjustmentMode == .Dimmed ? Swatch.extraLightGray.desaturated : Swatch.extraLightGray 60 | } 61 | 62 | 63 | // MARK: - Fonts 64 | 65 | func updateFont() { 66 | textLabel.font = TextStyle.callout.font(weight: .medium) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/SharedWebCredentials.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SharedWebCredentials.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/10/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Security 11 | 12 | struct SharedWebCredentials { 13 | 14 | // MARK: - Types 15 | 16 | struct Credential { 17 | let domain: String 18 | let account: String 19 | let password: String 20 | 21 | init?(dictionary: NSDictionary) { 22 | let dict = dictionary as Dictionary 23 | 24 | guard let domain = dict[kSecAttrServer] as? String, 25 | account = dict[kSecAttrAccount] as? String, 26 | password = dict[kSecSharedPassword] as? String 27 | else { return nil } 28 | 29 | self.domain = domain 30 | self.account = account 31 | self.password = password 32 | } 33 | } 34 | 35 | 36 | // MARK: - Accessing Credentials 37 | 38 | static func get(domain domain: String? = nil, account: String? = nil, completion: (credential: Credential?, error: CFError?) -> Void) { 39 | if NSProcessInfo.processInfo().isSnapshotting { 40 | completion(credential: nil, error: nil) 41 | return 42 | } 43 | 44 | SecRequestSharedWebCredential(domain, account) { array, error in 45 | let credential: Credential? 46 | 47 | if let array = array as Array?, dictionary = array.first as? NSDictionary { 48 | credential = Credential(dictionary: dictionary) 49 | } else { 50 | credential = nil 51 | } 52 | 53 | completion(credential: credential, error: error) 54 | } 55 | } 56 | 57 | static func add(domain domain: String, account: String, password: String, completion: ((error: CFError?) -> Void)? = nil) { 58 | if NSProcessInfo.processInfo().isSnapshotting { 59 | completion?(error: nil) 60 | return 61 | } 62 | 63 | SecAddSharedWebCredential(domain, account, password) { error in 64 | completion?(error: error) 65 | } 66 | } 67 | 68 | static func remove(domain domain: String, account: String, completion: ((error: CFError?) -> Void)? = nil) { 69 | if NSProcessInfo.processInfo().isSnapshotting { 70 | completion?(error: nil) 71 | return 72 | } 73 | 74 | SecAddSharedWebCredential(domain, account, nil) { error in 75 | completion?(error: error) 76 | } 77 | } 78 | 79 | static func generatePassword() -> String? { 80 | return SecCreateSharedWebCredentialPassword() as String? 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Sources/SignUpViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignUpViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/25/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasKit 12 | import OnePasswordExtension 13 | 14 | // TODO: Localize 15 | final class SignUpViewController: SessionFormViewController { 16 | 17 | // MARK: - Properties 18 | 19 | let usernameTextField: UITextField = { 20 | let textField = TextField() 21 | textField.keyboardType = .ASCIICapable 22 | textField.placeholder = "username" 23 | textField.returnKeyType = .Next 24 | textField.autocapitalizationType = .None 25 | textField.autocorrectionType = .No 26 | textField.setContentCompressionResistancePriority(UILayoutPriorityDefaultLow, forAxis: .Horizontal) 27 | return textField 28 | }() 29 | 30 | 31 | // MARK: - UIViewController 32 | 33 | override func viewDidLoad() { 34 | super.viewDidLoad() 35 | 36 | title = "Sign up for Canvas" 37 | submitButton.setTitle("Sign Up", forState: .Normal) 38 | 39 | emailTextField.placeholder = "email@example.com" 40 | 41 | footerButton.set(preface: "Already have an account?", title: "Log in.") 42 | } 43 | 44 | 45 | // MARK: - SessionsViewController 46 | 47 | override var textFields: [UITextField] { 48 | var fields = super.textFields 49 | fields.insert(usernameTextField, atIndex: 0) 50 | return fields 51 | } 52 | 53 | 54 | // MARK: - Actions 55 | 56 | override func submit() { 57 | guard let email = emailTextField.text, username = usernameTextField.text, password = passwordTextField.text 58 | where !email.isEmpty && !username.isEmpty && !password.isEmpty 59 | else { return } 60 | 61 | loading = true 62 | 63 | let client = AuthorizationClient() 64 | client.createAccount(email: email, username: username, password: password) { [weak self] in 65 | switch $0 { 66 | case .Success(_): 67 | dispatch_async(dispatch_get_main_queue()) { 68 | UIApplication.sharedApplication().networkActivityIndicatorVisible = false 69 | self?.showVerify() 70 | // Analytics.track(.LoggedIn) 71 | } 72 | case .Failure(let errorMessage): 73 | dispatch_async(dispatch_get_main_queue()) { [weak self] in 74 | self?.loading = false 75 | // self?.passwordTextField.becomeFirstResponder() 76 | // 77 | let alert = UIAlertController(title: "Double Check That", message: errorMessage, preferredStyle: .Alert) 78 | alert.addAction(UIAlertAction(title: LocalizedString.Okay.string, style: .Cancel, handler: nil)) 79 | self?.presentViewController(alert, animated: true, completion: nil) 80 | } 81 | } 82 | } 83 | } 84 | 85 | override func onePassword(sender: AnyObject?) { 86 | let details = [ 87 | AppExtensionTitleKey: "Canvas" 88 | ] 89 | 90 | let passwordOptions = [ 91 | AppExtensionGeneratedPasswordMinLengthKey: 8, 92 | AppExtensionGeneratedPasswordMaxLengthKey: 128 93 | ] 94 | 95 | OnePasswordExtension.sharedExtension().storeLoginForURLString("https://usecanvas.com", loginDetails: details, passwordGenerationOptions: passwordOptions, forViewController: self, sender: sender, completion: signUp) 96 | } 97 | 98 | 99 | // MARK: - Private 100 | 101 | private func showVerify() { 102 | guard let rootViewController = UIApplication.sharedApplication().delegate?.window??.rootViewController as? RootViewController else { return } 103 | rootViewController.viewController = VerifyViewController() 104 | } 105 | 106 | private func signUp(onePassword loginDictionary: [NSObject: AnyObject]?, error: NSError?) { 107 | if let username = loginDictionary?[AppExtensionUsernameKey] as? String { 108 | if username.containsString("@") { 109 | emailTextField.text = username 110 | usernameTextField.becomeFirstResponder() 111 | } else { 112 | usernameTextField.text = username 113 | emailTextField.becomeFirstResponder() 114 | } 115 | } 116 | 117 | if let password = loginDictionary?[AppExtensionPasswordKey] as? String { 118 | passwordTextField.text = password 119 | } 120 | } 121 | } 122 | 123 | -------------------------------------------------------------------------------- /Sources/SleepPickerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SleepPickerViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/28/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Static 11 | 12 | final class SleepPickerViewController: TableViewController { 13 | 14 | // MARK: - Initializers 15 | 16 | convenience init() { 17 | self.init(style: .Grouped) 18 | title = "Prevent Display Sleep" 19 | } 20 | 21 | 22 | // MARK: - UIViewController 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | let preference = SleepPrevention.currentPreference 28 | let rows: [Row] = SleepPrevention.all.map { option in 29 | let accessory: Row.Accessory = option == preference ? .Checkmark : .None 30 | return Row(text: option.description, selection: { [weak self] in 31 | self?.select(option) 32 | }, accessory: accessory) 33 | } 34 | 35 | dataSource.sections = [ 36 | Section(rows: rows) 37 | ] 38 | } 39 | 40 | 41 | // MARK: - Private 42 | 43 | private func select(preference: SleepPrevention) { 44 | SleepPrevention.select(preference) 45 | navigationController?.popViewControllerAnimated(true) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/SleepPrevention.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SleepPrevention.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/28/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum SleepPrevention: String, CustomStringConvertible { 12 | case never 13 | case whilePluggedIn 14 | case always 15 | 16 | var description: String { 17 | switch self { 18 | case .never: return "System Default" 19 | case .whilePluggedIn: return "While Plugged In" 20 | case .always: return "Never Sleep" 21 | } 22 | } 23 | 24 | static let all: [SleepPrevention] = [.never, .whilePluggedIn, .always] 25 | 26 | static let defaultsKey = "PreventSleep" 27 | 28 | static var currentPreference: SleepPrevention { 29 | guard let string = NSUserDefaults.standardUserDefaults().stringForKey(defaultsKey) else { return .whilePluggedIn } 30 | return SleepPrevention(rawValue: string) ?? .whilePluggedIn 31 | } 32 | 33 | static func select(preference: SleepPrevention) { 34 | NSUserDefaults.standardUserDefaults().setObject(preference.rawValue, forKey: defaultsKey) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/StackViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StackViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/25/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | class StackViewController: UIViewController { 13 | 14 | // MARK: - Properties 15 | 16 | let stackView: UIStackView = { 17 | let view = UIStackView() 18 | view.translatesAutoresizingMaskIntoConstraints = false 19 | view.axis = .Vertical 20 | view.alignment = .Fill 21 | return view 22 | }() 23 | 24 | private var centerYConstraint: NSLayoutConstraint? { 25 | willSet { 26 | guard let old = centerYConstraint else { return } 27 | NSLayoutConstraint.deactivateConstraints([old]) 28 | } 29 | 30 | didSet { 31 | guard let new = centerYConstraint else { return } 32 | NSLayoutConstraint.activateConstraints([new]) 33 | } 34 | } 35 | 36 | private var keyboardFrame: CGRect? { 37 | didSet { 38 | keyboardFrameDidChange() 39 | } 40 | } 41 | 42 | private var visible = false 43 | 44 | 45 | // MARK: - UIViewController 46 | 47 | override func viewDidLoad() { 48 | super.viewDidLoad() 49 | 50 | view.backgroundColor = Swatch.white 51 | view.addSubview(stackView) 52 | 53 | let width = stackView.widthAnchor.constraintEqualToAnchor(view.widthAnchor, multiplier: 0.8) 54 | width.priority = UILayoutPriorityDefaultHigh 55 | 56 | let top = stackView.topAnchor.constraintGreaterThanOrEqualToAnchor(view.topAnchor, constant: 64) 57 | top.priority = UILayoutPriorityDefaultLow 58 | 59 | NSLayoutConstraint.activateConstraints([ 60 | stackView.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor), 61 | top, 62 | width, 63 | stackView.widthAnchor.constraintLessThanOrEqualToConstant(400) 64 | ]) 65 | 66 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillChangeFrame), name: UIKeyboardWillChangeFrameNotification, object: nil) 67 | keyboardFrameDidChange() 68 | } 69 | 70 | override func viewDidAppear(animated: Bool) { 71 | super.viewDidAppear(animated) 72 | 73 | dispatch_async(dispatch_get_main_queue()) { [weak self] in 74 | self?.visible = true 75 | } 76 | } 77 | 78 | override func viewDidDisappear(animated: Bool) { 79 | super.viewDidDisappear(animated) 80 | visible = false 81 | } 82 | 83 | 84 | // MARK: - Private 85 | 86 | @objc private func keyboardWillChangeFrame(notification: NSNotification) { 87 | guard let dictionary = notification.userInfo as? [String: AnyObject], 88 | duration = dictionary[UIKeyboardAnimationDurationUserInfoKey] as? NSTimeInterval, 89 | curve = (dictionary[UIKeyboardAnimationCurveUserInfoKey] as? Int).flatMap(UIViewAnimationCurve.init), 90 | rect = (dictionary[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() 91 | else { return } 92 | 93 | let frame = view.convertRect(rect, fromView: nil) 94 | 95 | let change = { [weak self] in 96 | self?.keyboardFrame = frame 97 | self?.view.layoutIfNeeded() 98 | } 99 | 100 | if visible { 101 | UIView.beginAnimations(nil, context: nil) 102 | UIView.setAnimationDuration(duration) 103 | UIView.setAnimationCurve(curve) 104 | change() 105 | UIView.commitAnimations() 106 | } else { 107 | UIView.performWithoutAnimation(change) 108 | } 109 | } 110 | 111 | private func keyboardFrameDidChange() { 112 | guard let keyboardFrame = keyboardFrame else { 113 | centerYConstraint = stackView.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor) 114 | return 115 | } 116 | 117 | var rect = view.bounds 118 | rect.size.height -= rect.intersect(keyboardFrame).height 119 | rect.origin.y += UIApplication.sharedApplication().statusBarFrame.size.height 120 | rect.size.height -= UIApplication.sharedApplication().statusBarFrame.size.height 121 | 122 | let contstraint = stackView.centerYAnchor.constraintEqualToAnchor(view.topAnchor, constant: rect.midY) 123 | contstraint.priority = UILayoutPriorityDefaultHigh 124 | 125 | centerYConstraint = contstraint 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Sources/TableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/6/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | final class TableView: UITableView { 13 | override func tintColorDidChange() { 14 | super.tintColorDidChange() 15 | 16 | if style != .Grouped { 17 | return 18 | } 19 | 20 | backgroundColor = tintAdjustmentMode == .Dimmed ? Swatch.groupedTableBackground.desaturated : Swatch.groupedTableBackground 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/TableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 11/23/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Static 11 | import CanvasCore 12 | 13 | class TableViewController: UIViewController { 14 | 15 | // MARK: - Properties 16 | 17 | let tableView: UITableView 18 | 19 | /// Table view data source. 20 | var dataSource = DataSource() { 21 | willSet { 22 | dataSource.tableView = nil 23 | } 24 | 25 | didSet { 26 | dataSource.tableView = tableView 27 | } 28 | } 29 | 30 | // MARK: - Initializers 31 | 32 | init(style: UITableViewStyle) { 33 | tableView = TableView(frame: .zero, style: style) 34 | tableView.translatesAutoresizingMaskIntoConstraints = false 35 | tableView.separatorColor = Swatch.border 36 | 37 | dataSource.tableView = tableView 38 | 39 | super.init(nibName: nil, bundle: nil) 40 | } 41 | 42 | required init?(coder aDecoder: NSCoder) { 43 | fatalError("init(coder:) has not been implemented") 44 | } 45 | 46 | 47 | // MARK: - UIViewController 48 | 49 | override func viewDidLoad() { 50 | super.viewDidLoad() 51 | 52 | view.addSubview(tableView) 53 | 54 | NSLayoutConstraint.activateConstraints([ 55 | tableView.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor), 56 | tableView.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor), 57 | tableView.topAnchor.constraintEqualToAnchor(view.topAnchor), 58 | tableView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor), 59 | ]) 60 | 61 | dataSource.automaticallyDeselectRows = false 62 | dataSource.tableView = tableView 63 | } 64 | 65 | override func viewWillAppear(animated: Bool) { 66 | super.viewWillAppear(animated) 67 | 68 | tableView.indexPathsForSelectedRows?.forEach { indexPath in 69 | tableView.deselectRowAtIndexPath(indexPath, animated: false) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Sources/TextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextField.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 11/26/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasText 12 | 13 | final class TextField: UITextField { 14 | 15 | // MARK: - Properties 16 | 17 | override var placeholder: String? { 18 | didSet { 19 | guard let placeholder = placeholder, font = font else { return } 20 | attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [ 21 | NSFontAttributeName: font, 22 | NSForegroundColorAttributeName: Swatch.darkGray 23 | ]) 24 | } 25 | } 26 | 27 | // MARK: - Initializers 28 | 29 | override init(frame: CGRect) { 30 | super.init(frame: frame) 31 | 32 | backgroundColor = Swatch.extraLightGray 33 | 34 | textColor = Swatch.black 35 | tintColor = Swatch.brand 36 | 37 | layer.cornerRadius = 4 38 | 39 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(updateFont), name: UIContentSizeCategoryDidChangeNotification, object: nil) 40 | updateFont() 41 | } 42 | 43 | required init?(coder aDecoder: NSCoder) { 44 | fatalError("init(coder:) has not been implemented") 45 | } 46 | 47 | 48 | // MARK: - UIView 49 | 50 | override func intrinsicContentSize() -> CGSize { 51 | var size = super.intrinsicContentSize() 52 | size.height = 48 53 | return size 54 | } 55 | 56 | 57 | // MARK: - UITextField 58 | 59 | override func textRectForBounds(bounds: CGRect) -> CGRect { 60 | var rect = bounds 61 | 62 | if rightView != nil { 63 | rect.size.width -= rect.intersect(rightViewRectForBounds(bounds)).width 64 | } 65 | 66 | return CGRectInset(rect, 12, 12) 67 | } 68 | 69 | override func placeholderRectForBounds(bounds: CGRect) -> CGRect { 70 | return textRectForBounds(bounds) 71 | } 72 | 73 | override func editingRectForBounds(bounds: CGRect) -> CGRect { 74 | return textRectForBounds(bounds) 75 | } 76 | 77 | override func rightViewRectForBounds(bounds: CGRect) -> CGRect { 78 | var rect = super.rightViewRectForBounds(bounds) 79 | rect.origin.x -= 6 80 | return rect 81 | } 82 | 83 | 84 | // MARK: - Private 85 | 86 | @objc private func updateFont() { 87 | font = TextStyle.body.font() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Sources/TextView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 3/8/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasText 11 | 12 | class TextView: UITextView { 13 | 14 | // MARK: - Properties 15 | 16 | var managedSubviews = Set() 17 | 18 | 19 | // MARK: - UIView 20 | 21 | // Allow subviews to receive user input 22 | override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? { 23 | for view in managedSubviews { 24 | if view.superview == self && view.userInteractionEnabled && view.frame.contains(point) { 25 | return view 26 | } 27 | } 28 | 29 | return super.hitTest(point, withEvent: event) 30 | } 31 | 32 | 33 | // MARK: - UITextInput 34 | 35 | // Only display the caret in the used rect (if available). 36 | override func caretRectForPosition(position: UITextPosition) -> CGRect { 37 | var rect = super.caretRectForPosition(position) 38 | 39 | if let layoutManager = textContainer.layoutManager { 40 | layoutManager.ensureLayoutForTextContainer(textContainer) 41 | 42 | let characterIndex = offsetFromPosition(beginningOfDocument, toPosition: position) 43 | if characterIndex == textStorage.length { 44 | return rect 45 | } 46 | 47 | let glyphIndex = layoutManager.glyphIndexForCharacterAtIndex(characterIndex) 48 | 49 | if UInt(glyphIndex) == UInt.max - 1 { 50 | return rect 51 | } 52 | 53 | let usedRect = layoutManager.lineFragmentUsedRectForGlyphAtIndex(glyphIndex, effectiveRange: nil) 54 | 55 | if usedRect.height > 0 { 56 | rect.origin.y = usedRect.minY + textContainerInset.top 57 | rect.size.height = usedRect.height 58 | } 59 | } 60 | 61 | return rect 62 | } 63 | 64 | // Omit empty width rect when drawing selection rects. 65 | override func selectionRectsForRange(range: UITextRange) -> [AnyObject] { 66 | let selectionRects = super.selectionRectsForRange(range) 67 | return selectionRects.filter({ selection in 68 | guard let selection = selection as? UITextSelectionRect else { return false } 69 | return selection.rect.size.width > 0 70 | }) 71 | } 72 | 73 | func rectsForRange(range: NSRange) -> [CGRect] { 74 | // Become first responder if we aren't already. The text view has to be first responder for any of the position 75 | // or text range APIs to work. Annoying :( 76 | let wasFirstResponder = isFirstResponder() 77 | if !wasFirstResponder { 78 | becomeFirstResponder() 79 | } 80 | 81 | // Get starting position 82 | guard let start = positionFromPosition(beginningOfDocument, offset: range.location) else { 83 | if !wasFirstResponder { 84 | resignFirstResponder() 85 | } 86 | return [] 87 | } 88 | 89 | // Empty selection 90 | if range.length == 0 { 91 | if !wasFirstResponder { 92 | resignFirstResponder() 93 | } 94 | return [caretRectForPosition(start)] 95 | } 96 | 97 | // Selection 98 | guard let end = positionFromPosition(start, offset: range.length), 99 | textRange = textRangeFromPosition(start, toPosition: end), 100 | selectionRects = (super.selectionRectsForRange(textRange) as? [UITextSelectionRect])?.map({ $0.rect }), 101 | firstRect = selectionRects.first 102 | else { 103 | // Use extra line if there aren't any rects 104 | var rect = layoutManager.extraLineFragmentUsedRect 105 | rect.origin.x += textContainerInset.left 106 | rect.origin.y += textContainerInset.top 107 | 108 | if !wasFirstResponder { 109 | resignFirstResponder() 110 | } 111 | 112 | return [rect] 113 | } 114 | 115 | if !wasFirstResponder { 116 | resignFirstResponder() 117 | } 118 | 119 | let filtered = selectionRects.filter { $0.width > 0 } 120 | 121 | if filtered.isEmpty { 122 | return [firstRect] 123 | } 124 | 125 | return filtered 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Sources/TickingLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TickingLabel.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 1/28/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class TickingLabel: UILabel { 12 | 13 | // MARK: - Properties 14 | 15 | var date: NSDate? { 16 | didSet { 17 | tick() 18 | } 19 | } 20 | 21 | private static var timer: NSTimer? 22 | private static let tickNotificationName = "TickingLabel.tickNotification" 23 | private static var setupToken: dispatch_once_t = 0 24 | 25 | 26 | // MARK: - Initializers 27 | 28 | override init(frame: CGRect) { 29 | super.init(frame: frame) 30 | 31 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(tick), name: self.dynamicType.tickNotificationName, object: nil) 32 | 33 | dispatch_once(&self.dynamicType.setupToken) { 34 | self.dynamicType.setupTimer() 35 | } 36 | } 37 | 38 | required init?(coder aDecoder: NSCoder) { 39 | fatalError("init(coder:) has not been implemented") 40 | } 41 | 42 | 43 | // MARK: - Private 44 | 45 | private class func setupTimer() { 46 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(applicationWillResignActive), name: UIApplicationWillResignActiveNotification, object: nil) 47 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(applicationDidBecomeActive), name: UIApplicationDidBecomeActiveNotification, object: nil) 48 | applicationDidBecomeActive() 49 | } 50 | 51 | @objc private class func fire() { 52 | NSNotificationCenter.defaultCenter().postNotificationName(tickNotificationName, object: nil) 53 | } 54 | 55 | @objc private class func applicationWillResignActive() { 56 | timer?.invalidate() 57 | timer = nil 58 | } 59 | 60 | @objc private class func applicationDidBecomeActive() { 61 | let timer = NSTimer(timeInterval: 1, target: self, selector: #selector(fire), userInfo: nil, repeats: true) 62 | timer.tolerance = 0.5 63 | self.timer = timer 64 | 65 | NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes) 66 | } 67 | 68 | @objc private func tick() { 69 | text = date?.briefTimeAgoInWords 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Sources/TintableEnvironment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TintableEnvironment.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 1/12/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol TintableEnvironment { 12 | var preferredTintColor: UIColor { get } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/TintableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TintableView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 1/14/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class TintableView: UIView { 12 | 13 | // MARK: - Properties 14 | 15 | var highlighted = false { 16 | didSet { 17 | updateTintColor() 18 | } 19 | } 20 | 21 | var normalTintColor: UIColor? { 22 | didSet { 23 | updateTintColor() 24 | } 25 | } 26 | 27 | var highlightedTintColor: UIColor? { 28 | didSet { 29 | updateTintColor() 30 | } 31 | } 32 | 33 | private var settingTintColor = false 34 | 35 | 36 | // MARK: - UIView 37 | 38 | override func tintColorDidChange() { 39 | super.tintColorDidChange() 40 | 41 | if settingTintColor { 42 | settingTintColor = false 43 | return 44 | } 45 | 46 | normalTintColor = tintColor 47 | } 48 | 49 | 50 | // MARK: - Tinting 51 | 52 | func updateTintColor() { 53 | settingTintColor = true 54 | tintColor = highlighted ? highlightedTintColor : normalTintColor 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/TitleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TitleView.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/29/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | 12 | final class TitleView: UIView { 13 | 14 | // MARK: - Properties 15 | 16 | var showsLock = false { 17 | didSet { 18 | lockView.hidden = !showsLock 19 | setNeedsLayout() 20 | } 21 | } 22 | 23 | var title = "" { 24 | didSet { 25 | titleLabel.text = title 26 | setNeedsLayout() 27 | } 28 | } 29 | 30 | private let lockView: UIImageView = { 31 | let view = UIImageView(image: UIImage(named: "Lock")) 32 | view.tintColor = Swatch.darkGray 33 | return view 34 | }() 35 | 36 | private let titleLabel: UILabel = { 37 | let label = UILabel() 38 | label.textColor = Swatch.darkGray 39 | label.font = Font.sansSerif(weight: .medium) 40 | return label 41 | }() 42 | 43 | private let spacing: CGFloat = 4 44 | 45 | 46 | // MARK: - Initializers 47 | 48 | init() { 49 | super.init(frame: .zero) 50 | 51 | autoresizingMask = [.FlexibleWidth] 52 | 53 | lockView.sizeToFit() 54 | 55 | addSubview(lockView) 56 | addSubview(titleLabel) 57 | } 58 | 59 | required init?(coder aDecoder: NSCoder) { 60 | fatalError("init(coder:) has not been implemented") 61 | } 62 | 63 | 64 | // MARK: - UIView 65 | 66 | override class func layerClass() -> AnyClass { 67 | return CATransformLayer.self 68 | } 69 | 70 | override func layoutSubviews() { 71 | let size = bounds.size 72 | let titleSize = titleLabel.sizeThatFits(size) 73 | var titleFrame = CGRect(x: round((size.width - titleSize.width) / 2), y: round((size.height - titleSize.height) / 2), width: titleSize.width, height: titleSize.height) 74 | 75 | if showsLock { 76 | let lockSize = lockView.bounds.size 77 | titleFrame.origin.x += round((lockSize.width + spacing) / 2) 78 | lockView.frame = CGRect(x: titleFrame.origin.x - lockSize.width - spacing, y: round((size.height - lockSize.height) / 2), width: lockSize.width, height: lockSize.height) 79 | } 80 | 81 | if titleFrame.maxX > bounds.width { 82 | if showsLock { 83 | lockView.frame.origin.x = 0 84 | titleFrame.origin.x = lockView.frame.maxX + spacing 85 | } else { 86 | titleFrame.origin.x = 0 87 | } 88 | 89 | titleFrame.size.width = bounds.width - titleFrame.minX 90 | } 91 | 92 | titleLabel.frame = titleFrame 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Sources/UIActivity+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIActivity+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/16/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIActivity { 12 | func showBanner(text text: String, style: BannerView.Style = .success) { 13 | guard let rootViewController = UIApplication.sharedApplication().delegate?.window??.rootViewController as? RootViewController, 14 | splitViewController = rootViewController.viewController as? SplitViewController, 15 | var viewController = splitViewController.viewControllers.last 16 | else { return } 17 | 18 | if let top = (viewController as? UINavigationController)?.topViewController { 19 | viewController = top 20 | } 21 | 22 | rootViewController._showBanner(text: text, style: style, inViewController: viewController) 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/UIColor+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/6/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | var desaturated: UIColor { 13 | var hue: CGFloat = 0 14 | var brightness: CGFloat = 0 15 | var alpha: CGFloat = 0 16 | 17 | getHue(&hue, saturation: nil, brightness: &brightness, alpha: &alpha) 18 | 19 | return self.dynamicType.init(hue: hue, saturation: 0, brightness: brightness, alpha: alpha) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/UIEdgeInsets+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIEdgeInsets+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 11/26/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIEdgeInsets { 12 | init(_ value: CGFloat) { 13 | top = value 14 | left = value 15 | right = value 16 | bottom = value 17 | } 18 | 19 | static let zero = UIEdgeInsets(0) 20 | } 21 | -------------------------------------------------------------------------------- /Sources/UIFont+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIFont+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 12/16/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIFont { 12 | var fontWithMonospaceNumbers: UIFont { 13 | let fontDescriptor = UIFontDescriptor(name: fontName, size: pointSize).fontDescriptorByAddingAttributes([ 14 | UIFontDescriptorFeatureSettingsAttribute: [ 15 | [ 16 | UIFontFeatureTypeIdentifierKey: kNumberSpacingType, 17 | UIFontFeatureSelectorIdentifierKey: kMonospacedNumbersSelector 18 | ] 19 | ] 20 | ]) 21 | 22 | return UIFont(descriptor: fontDescriptor, size: pointSize) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/UISplitViewController+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UISplitViewController+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 6/8/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UISplitViewController { 12 | convenience init(masterViewController: UIViewController, detailViewController: UIViewController) { 13 | self.init() 14 | viewControllers = [masterViewController, detailViewController] 15 | } 16 | 17 | var masterViewController: UIViewController? { 18 | return viewControllers.first 19 | } 20 | 21 | var detailViewController: UIViewController? { 22 | guard viewControllers.count == 2 else { return nil } 23 | return viewControllers.last 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/UIViewController+Canvas.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Canvas.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 5/11/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIViewController { 12 | func present(actionSheet actionSheet: UIViewController, sender: AnyObject?, animated: Bool = true, completion: (() -> Void)? = nil) { 13 | if let popover = actionSheet.popoverPresentationController { 14 | if let button = sender as? UIBarButtonItem { 15 | popover.barButtonItem = button 16 | } else if let sourceView = sender as? UIView { 17 | popover.sourceView = sourceView 18 | } else { 19 | popover.sourceView = view 20 | } 21 | } 22 | 23 | presentViewController(actionSheet, animated: animated, completion: completion) 24 | } 25 | 26 | func dismissDetailViewController(sender: AnyObject?) { 27 | if let splitViewController = splitViewController where !splitViewController.collapsed { 28 | splitViewController.dismissDetailViewController(sender) 29 | return 30 | } 31 | 32 | if let presenter = targetViewControllerForAction(#selector(dismissDetailViewController), sender: sender) { 33 | presenter.dismissDetailViewController(self) 34 | } 35 | } 36 | 37 | func showBanner(text text: String, style: BannerView.Style = .success) { 38 | guard let rootViewController = UIApplication.sharedApplication().delegate?.window??.rootViewController as? RootViewController else { return } 39 | rootViewController._showBanner(text: text, style: style, inViewController: self) 40 | 41 | } 42 | } 43 | 44 | 45 | extension UINavigationController { 46 | override func dismissDetailViewController(sender: AnyObject?) { 47 | // Hack to fix nested navigation controllers that split view makes. Ugh. 48 | if viewControllers.count == 1 { 49 | navigationController?.popViewControllerAnimated(true) 50 | return 51 | } 52 | popViewControllerAnimated(true) 53 | } 54 | } 55 | 56 | 57 | extension UISplitViewController { 58 | override func dismissDetailViewController(sender: AnyObject?) { 59 | showDetailViewController(NavigationController(rootViewController: PlaceholderViewController()), sender: sender) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/UserCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserCell.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 8/9/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CanvasCore 11 | import CanvasKit 12 | import Static 13 | 14 | final class UserCell: UITableViewCell { 15 | 16 | // MARK: - Properties 17 | 18 | private let avatarView: AvatarView = { 19 | let view = AvatarView() 20 | view.translatesAutoresizingMaskIntoConstraints = false 21 | return view 22 | }() 23 | 24 | private let usernameLabel: UILabel = { 25 | let label = UILabel() 26 | label.translatesAutoresizingMaskIntoConstraints = false 27 | label.textColor = Swatch.black 28 | return label 29 | }() 30 | 31 | 32 | // MARK: - Initializers 33 | 34 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 35 | super.init(style: style, reuseIdentifier: reuseIdentifier) 36 | 37 | contentView.addSubview(avatarView) 38 | contentView.addSubview(usernameLabel) 39 | 40 | NSLayoutConstraint.activateConstraints([ 41 | avatarView.topAnchor.constraintEqualToAnchor(contentView.layoutMarginsGuide.topAnchor), 42 | avatarView.bottomAnchor.constraintEqualToAnchor(contentView.layoutMarginsGuide.bottomAnchor), 43 | avatarView.leadingAnchor.constraintEqualToAnchor(contentView.layoutMarginsGuide.leadingAnchor), 44 | 45 | usernameLabel.centerYAnchor.constraintEqualToAnchor(avatarView.centerYAnchor), 46 | usernameLabel.leadingAnchor.constraintEqualToAnchor(avatarView.trailingAnchor, constant: 16), 47 | usernameLabel.trailingAnchor.constraintLessThanOrEqualToAnchor(contentView.layoutMarginsGuide.trailingAnchor) 48 | ]) 49 | } 50 | 51 | required init?(coder aDecoder: NSCoder) { 52 | fatalError("init(coder:) has not been implemented") 53 | } 54 | } 55 | 56 | 57 | extension UserCell: CellType { 58 | func configure(row row: Row) { 59 | usernameLabel.text = row.text 60 | avatarView.user = row.context?["user"] as? User 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/ValueCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ValueCell.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 7/1/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Static 11 | import CanvasCore 12 | 13 | final class ValueCell: UITableViewCell, CellType { 14 | override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 15 | super.init(style: .Value1, reuseIdentifier: reuseIdentifier) 16 | textLabel?.textColor = Swatch.black 17 | detailTextLabel?.textColor = Swatch.darkGray 18 | } 19 | 20 | required init?(coder aDecoder: NSCoder) { 21 | fatalError("init(coder:) has not been implemented") 22 | } 23 | 24 | func configure(row row: Row) { 25 | textLabel?.text = row.text 26 | detailTextLabel?.text = row.detailText 27 | imageView?.image = row.image 28 | 29 | switch row.accessory { 30 | case .DisclosureIndicator: 31 | let view = UIImageView(image: UIImage(named: "ChevronRightSmall")) 32 | view.tintColor = Swatch.lightGray 33 | accessoryView = view 34 | default: 35 | accessoryType = row.accessory.type 36 | accessoryView = row.accessory.view 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/WebActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebActivity.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 11/18/15. 6 | // Copyright © 2015 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class WebActivity: UIActivity { 12 | 13 | // MARK: - Properties 14 | 15 | var URL: NSURL? 16 | var schemePrefix: String? 17 | 18 | 19 | // MARK: - UIActivity 20 | 21 | override func prepareWithActivityItems(activityItems: [AnyObject]) { 22 | for activityItem in activityItems { 23 | if let URL = activityItem as? NSURL { 24 | self.URL = URL 25 | return 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/WebViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebViewController.swift 3 | // Canvas 4 | // 5 | // Created by Sam Soffes on 1/28/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SafariServices 11 | 12 | final class WebViewController: SFSafariViewController { 13 | 14 | // MARK: - Properties 15 | 16 | let originalURL: NSURL 17 | 18 | 19 | // MARK: - Initializers 20 | 21 | convenience init(URL: NSURL) { 22 | self.init(URL: URL, entersReaderIfAvailable: false) 23 | } 24 | 25 | override init(URL: NSURL, entersReaderIfAvailable: Bool) { 26 | originalURL = URL 27 | super.init(URL: URL, entersReaderIfAvailable: entersReaderIfAvailable) 28 | } 29 | 30 | 31 | // MARK: - UIViewController 32 | 33 | override func previewActionItems() -> [UIPreviewActionItem] { 34 | let copyAction = UIPreviewAction(title: "Copy URL", style: .Default) { [weak self] _, _ in 35 | UIPasteboard.generalPasteboard().URL = self?.originalURL 36 | } 37 | 38 | let safariAction = UIPreviewAction(title: "Open in Safari", style: .Default) { [weak self] _, _ in 39 | guard let URL = self?.originalURL else { return } 40 | UIApplication.sharedApplication().openURL(URL) 41 | } 42 | 43 | return [copyAction, safariAction] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Support/Assets.xcassets/1Password.imageset/1Password.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/1Password.imageset/1Password.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/1Password.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "1Password.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Chrome.imageset/Chrome.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Chrome.imageset/Chrome.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Chrome.imageset/Chrome~iPad.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Chrome.imageset/Chrome~iPad.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Chrome.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "filename" : "Chrome.pdf" 6 | }, 7 | { 8 | "idiom" : "ipad", 9 | "filename" : "Chrome~iPad.pdf" 10 | } 11 | ], 12 | "info" : { 13 | "version" : 1, 14 | "author" : "xcode" 15 | } 16 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy HTML.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "filename" : "Copy HTML.pdf" 6 | }, 7 | { 8 | "idiom" : "ipad", 9 | "filename" : "Copy HTML~iPad.pdf" 10 | } 11 | ], 12 | "info" : { 13 | "version" : 1, 14 | "author" : "xcode" 15 | } 16 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy HTML.imageset/Copy HTML.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Copy HTML.imageset/Copy HTML.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy HTML.imageset/Copy HTML~iPad.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Copy HTML.imageset/Copy HTML~iPad.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy JSON.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "filename" : "Copy JSON.pdf" 6 | }, 7 | { 8 | "idiom" : "ipad", 9 | "filename" : "Copy JSON~iPad.pdf" 10 | } 11 | ], 12 | "info" : { 13 | "version" : 1, 14 | "author" : "xcode" 15 | } 16 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy JSON.imageset/Copy JSON.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Copy JSON.imageset/Copy JSON.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy JSON.imageset/Copy JSON~iPad.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Copy JSON.imageset/Copy JSON~iPad.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy Link.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "filename" : "Copy Link.pdf" 6 | }, 7 | { 8 | "idiom" : "ipad", 9 | "filename" : "Copy Link~iPad.pdf" 10 | } 11 | ], 12 | "info" : { 13 | "version" : 1, 14 | "author" : "xcode" 15 | } 16 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy Link.imageset/Copy Link.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Copy Link.imageset/Copy Link.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy Link.imageset/Copy Link~iPad.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Copy Link.imageset/Copy Link~iPad.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy Markdown.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "filename" : "Copy Markdown.pdf" 6 | }, 7 | { 8 | "idiom" : "ipad", 9 | "filename" : "Copy Markdown~iPad.pdf" 10 | } 11 | ], 12 | "info" : { 13 | "version" : 1, 14 | "author" : "xcode" 15 | } 16 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy Markdown.imageset/Copy Markdown.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Copy Markdown.imageset/Copy Markdown.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Copy Markdown.imageset/Copy Markdown~iPad.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Copy Markdown.imageset/Copy Markdown~iPad.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Safari.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "filename" : "Safari.pdf" 6 | }, 7 | { 8 | "idiom" : "ipad", 9 | "filename" : "Safari~iPad.pdf" 10 | } 11 | ], 12 | "info" : { 13 | "version" : 1, 14 | "author" : "xcode" 15 | } 16 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Safari.imageset/Safari.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Safari.imageset/Safari.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Activity Icons/Safari.imageset/Safari~iPad.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Activity Icons/Safari.imageset/Safari~iPad.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "Settings@2x-1.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "Settings@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "Spotlight@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "Spotlight@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "60x60", 29 | "idiom" : "iphone", 30 | "filename" : "iPhone@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "iPhone@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "29x29", 41 | "idiom" : "ipad", 42 | "filename" : "Settings.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "Settings@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "40x40", 53 | "idiom" : "ipad", 54 | "filename" : "Spotlight.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "Spotlight@2x-1.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "76x76", 65 | "idiom" : "ipad", 66 | "filename" : "iPad.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "76x76", 71 | "idiom" : "ipad", 72 | "filename" : "iPad@2x.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "83.5x83.5", 77 | "idiom" : "ipad", 78 | "filename" : "iPad Pro@2x.png", 79 | "scale" : "2x" 80 | } 81 | ], 82 | "info" : { 83 | "version" : 1, 84 | "author" : "xcode" 85 | }, 86 | "properties" : { 87 | "pre-rendered" : true 88 | } 89 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/Settings.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/Settings@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/Settings@2x-1.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/Settings@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/Settings@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/Settings@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/Settings@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/Spotlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/Spotlight.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/Spotlight@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/Spotlight@2x-1.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/Spotlight@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/Spotlight@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/Spotlight@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/Spotlight@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/iPad Pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/iPad Pro@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/iPad.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/iPad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/iPad@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/iPhone@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/iPhone@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon-Dev.appiconset/iPhone@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon-Dev.appiconset/iPhone@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "Settings@2x-1.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "Settings@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "Spotlight@2x-1.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "Spotlight@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "60x60", 29 | "idiom" : "iphone", 30 | "filename" : "iPhone@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "iPhone@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "29x29", 41 | "idiom" : "ipad", 42 | "filename" : "Settings.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "Settings@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "40x40", 53 | "idiom" : "ipad", 54 | "filename" : "Spotlight.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "Spotlight@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "76x76", 65 | "idiom" : "ipad", 66 | "filename" : "iPad.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "76x76", 71 | "idiom" : "ipad", 72 | "filename" : "iPad@2x.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "83.5x83.5", 77 | "idiom" : "ipad", 78 | "filename" : "iPad Pro@2x.png", 79 | "scale" : "2x" 80 | } 81 | ], 82 | "info" : { 83 | "version" : 1, 84 | "author" : "xcode" 85 | }, 86 | "properties" : { 87 | "pre-rendered" : true 88 | } 89 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/Settings.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/Settings@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/Settings@2x-1.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/Settings@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/Settings@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/Settings@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/Settings@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/Spotlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/Spotlight.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/Spotlight@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/Spotlight@2x-1.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/Spotlight@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/Spotlight@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/Spotlight@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/Spotlight@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/iPad Pro@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/iPad Pro@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/iPad.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/iPad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/iPad@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/iPhone@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/iPhone@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/AppIcon.appiconset/iPhone@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/AppIcon.appiconset/iPhone@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Document Icons/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Document Icons/Document Globe-Background.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Document Globe-Background.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Document Icons/Document Globe-Background.imageset/Document Globe-Background.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Document Icons/Document Globe-Background.imageset/Document Globe-Background.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Document Icons/Document Globe.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Document Globe.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Document Icons/Document Globe.imageset/Document Globe.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Document Icons/Document Globe.imageset/Document Globe.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Document Icons/Document-Blank.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "empty-document.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Document Icons/Document-Blank.imageset/empty-document.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Document Icons/Document-Blank.imageset/empty-document.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Document Icons/Document.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "document.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "original" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Document Icons/Document.imageset/document.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Document Icons/Document.imageset/document.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/CheckList.imageset/CheckList.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Gesture Icons/CheckList.imageset/CheckList.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/CheckList.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "CheckList.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/Heading2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Heading2.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/Heading2.imageset/Heading2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Gesture Icons/Heading2.imageset/Heading2.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/Heading3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Heading3.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/Heading3.imageset/Heading3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Gesture Icons/Heading3.imageset/Heading3.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/Indent.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Indent.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/Indent.imageset/Indent.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Gesture Icons/Indent.imageset/Indent.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/OrderedList.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "OrderedList.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/OrderedList.imageset/OrderedList.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Gesture Icons/OrderedList.imageset/OrderedList.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/Outdent.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Outdent.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/Outdent.imageset/Outdent.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Gesture Icons/Outdent.imageset/Outdent.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/Paragraph.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Paragraph.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Gesture Icons/Paragraph.imageset/Paragraph.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Gesture Icons/Paragraph.imageset/Paragraph.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Icon-Small.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Icon-Small.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Icon-Small.imageset/Icon-Small.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Icon-Small.imageset/Icon-Small.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustration.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Illustration.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustration.imageset/Illustration.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Illustration.imageset/Illustration.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/IllustrationLight.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "IllustrationLight.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/IllustrationLight.imageset/IllustrationLight.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/IllustrationLight.imageset/IllustrationLight.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/Email.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Email@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "Email@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/Email.imageset/Email@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Illustrations/Email.imageset/Email@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/Email.imageset/Email@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Illustrations/Email.imageset/Email@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/No Participants.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "No Participants.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "No Participants@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "No Participants@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/No Participants.imageset/No Participants.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Illustrations/No Participants.imageset/No Participants.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/No Participants.imageset/No Participants@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Illustrations/No Participants.imageset/No Participants@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/No Participants.imageset/No Participants@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Illustrations/No Participants.imageset/No Participants@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/Not Found.imageset/404 Illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Illustrations/Not Found.imageset/404 Illustration.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/Not Found.imageset/404 Illustration@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Illustrations/Not Found.imageset/404 Illustration@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/Not Found.imageset/404 Illustration@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Illustrations/Not Found.imageset/404 Illustration@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Illustrations/Not Found.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "404 Illustration.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "404 Illustration@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "404 Illustration@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/New Canvas Shortcut.imageset/Compose-Shortcut.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/New Canvas Shortcut.imageset/Compose-Shortcut.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/New Canvas Shortcut.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Compose-Shortcut.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/GesturesCompact.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "iphone", 9 | "filename" : "GesturesCompact@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "iphone", 14 | "filename" : "GesturesCompact@3x.png", 15 | "scale" : "3x" 16 | }, 17 | { 18 | "idiom" : "ipad", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "idiom" : "ipad", 23 | "filename" : "GesturesCompact@2x~iPad.png", 24 | "scale" : "2x" 25 | } 26 | ], 27 | "info" : { 28 | "version" : 1, 29 | "author" : "xcode" 30 | } 31 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/GesturesCompact.imageset/GesturesCompact@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/GesturesCompact.imageset/GesturesCompact@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/GesturesCompact.imageset/GesturesCompact@2x~iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/GesturesCompact.imageset/GesturesCompact@2x~iPad.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/GesturesCompact.imageset/GesturesCompact@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/GesturesCompact.imageset/GesturesCompact@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/GesturesRegular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "ipad", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "ipad", 9 | "filename" : "GesturesRegular@2x~iPad.png", 10 | "scale" : "2x" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/GesturesRegular.imageset/GesturesRegular@2x~iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/GesturesRegular.imageset/GesturesRegular@2x~iPad.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/OrigamiCompact.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "iphone", 9 | "filename" : "OrigamiCompact@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "iphone", 14 | "filename" : "OrigamiCompact@3x.png", 15 | "scale" : "3x" 16 | }, 17 | { 18 | "idiom" : "ipad", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "idiom" : "ipad", 23 | "filename" : "OrigamiCompact@2x~iPad.png", 24 | "scale" : "2x" 25 | } 26 | ], 27 | "info" : { 28 | "version" : 1, 29 | "author" : "xcode" 30 | } 31 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/OrigamiCompact.imageset/OrigamiCompact@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/OrigamiCompact.imageset/OrigamiCompact@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/OrigamiCompact.imageset/OrigamiCompact@2x~iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/OrigamiCompact.imageset/OrigamiCompact@2x~iPad.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/OrigamiCompact.imageset/OrigamiCompact@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/OrigamiCompact.imageset/OrigamiCompact@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/OrigamiRegular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "ipad", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "ipad", 9 | "filename" : "OrigamiRegular@2x~iPad.png", 10 | "scale" : "2x" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/OrigamiRegular.imageset/OrigamiRegular@2x~iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/OrigamiRegular.imageset/OrigamiRegular@2x~iPad.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/ShareCompact.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "iphone", 9 | "filename" : "ShareCompact@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "iphone", 14 | "filename" : "ShareCompact@3x.png", 15 | "scale" : "3x" 16 | }, 17 | { 18 | "idiom" : "ipad", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "idiom" : "ipad", 23 | "filename" : "ShareCompact@2x~iPad.png", 24 | "scale" : "2x" 25 | } 26 | ], 27 | "info" : { 28 | "version" : 1, 29 | "author" : "xcode" 30 | } 31 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/ShareCompact.imageset/ShareCompact@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/ShareCompact.imageset/ShareCompact@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/ShareCompact.imageset/ShareCompact@2x~iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/ShareCompact.imageset/ShareCompact@2x~iPad.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/ShareCompact.imageset/ShareCompact@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/ShareCompact.imageset/ShareCompact@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/ShareRegular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "ipad", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "ipad", 9 | "filename" : "ShareRegular@2x~iPad.png", 10 | "scale" : "2x" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/ShareRegular.imageset/ShareRegular@2x~iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/ShareRegular.imageset/ShareRegular@2x~iPad.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/WelcomeCompact.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "iphone", 9 | "filename" : "WelcomeCompact@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "iphone", 14 | "filename" : "WelcomeCompact@3x.png", 15 | "scale" : "3x" 16 | }, 17 | { 18 | "idiom" : "ipad", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "idiom" : "ipad", 23 | "filename" : "WelcomeCompact@2x~iPad.png", 24 | "scale" : "2x" 25 | } 26 | ], 27 | "info" : { 28 | "version" : 1, 29 | "author" : "xcode" 30 | } 31 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/WelcomeCompact.imageset/WelcomeCompact@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/WelcomeCompact.imageset/WelcomeCompact@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/WelcomeCompact.imageset/WelcomeCompact@2x~iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/WelcomeCompact.imageset/WelcomeCompact@2x~iPad.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/WelcomeCompact.imageset/WelcomeCompact@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/WelcomeCompact.imageset/WelcomeCompact@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/WelcomeRegular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "ipad", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "ipad", 9 | "filename" : "WelcomeRegular@2x~iPad.png", 10 | "scale" : "2x" 11 | } 12 | ], 13 | "info" : { 14 | "version" : 1, 15 | "author" : "xcode" 16 | } 17 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Onboarding/WelcomeRegular.imageset/WelcomeRegular@2x~iPad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Onboarding/WelcomeRegular.imageset/WelcomeRegular@2x~iPad.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/ChevronLeft.imageset/ChevronLeft.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/ChevronLeft.imageset/ChevronLeft.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/ChevronLeft.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ChevronLeft.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/ChevronRightSmall.imageset/ChevronRightSmall.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/ChevronRightSmall.imageset/ChevronRightSmall.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/ChevronRightSmall.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ChevronRightSmall.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Close.imageset/Close.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/Close.imageset/Close.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Close.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Compose.imageset/Compose.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/Compose.imageset/Compose.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Compose.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Compose.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Gear.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Gear.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Gear.imageset/Gear.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/Gear.imageset/Gear.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Help.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Help@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "Help@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Help.imageset/Help@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/Help.imageset/Help@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Help.imageset/Help@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/Help.imageset/Help@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Lock.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Lock.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Lock.imageset/Lock.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/Lock.imageset/Lock.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Moon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Moon@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "Moon@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Moon.imageset/Moon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/Moon.imageset/Moon@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Moon.imageset/Moon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/Moon.imageset/Moon@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/More.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "More.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/More.imageset/More.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/More.imageset/More.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/SearchSmall.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "SearchSmall.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/SearchSmall.imageset/SearchSmall.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/SearchSmall.imageset/SearchSmall.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/SidebarLeft.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "SidebarLeft.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/SidebarLeft.imageset/SidebarLeft.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/SidebarLeft.imageset/SidebarLeft.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/SignOut.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "SignOut@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "SignOut@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/SignOut.imageset/SignOut@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/SignOut.imageset/SignOut@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/SignOut.imageset/SignOut@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/SignOut.imageset/SignOut@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/User.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "User@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "User@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/User.imageset/User@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/User.imageset/User@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/User.imageset/User@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/User.imageset/User@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Username.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Username@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "Username@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | }, 22 | "properties" : { 23 | "template-rendering-intent" : "template" 24 | } 25 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Username.imageset/Username@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/Username.imageset/Username@2x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Primaries/Username.imageset/Username@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Primaries/Username.imageset/Username@3x.png -------------------------------------------------------------------------------- /Support/Assets.xcassets/Pull To Refresh/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Pull To Refresh/RefreshViewBackground.imageset/Background.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Pull To Refresh/RefreshViewBackground.imageset/Background.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Pull To Refresh/RefreshViewBackground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Background.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "original" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Pull To Refresh/RefreshViewGradient.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Gradient 2.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "original" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Pull To Refresh/RefreshViewGradient.imageset/Gradient 2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Pull To Refresh/RefreshViewGradient.imageset/Gradient 2.pdf -------------------------------------------------------------------------------- /Support/Assets.xcassets/Pull To Refresh/RefreshViewOutline.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Outline.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } -------------------------------------------------------------------------------- /Support/Assets.xcassets/Pull To Refresh/RefreshViewOutline.imageset/Outline.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Assets.xcassets/Pull To Refresh/RefreshViewOutline.imageset/Outline.pdf -------------------------------------------------------------------------------- /Support/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Support/Canvas Dev.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Support/Canvas.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.associated-domains 6 | 7 | webcredentials:usecanvas.com 8 | applinks:usecanvas.com 9 | 10 | com.apple.security.application-groups 11 | 12 | group.com.usecanvas.canvas 13 | 14 | keychain-access-groups 15 | 16 | $(AppIdentifierPrefix)com.usecanvas.canvas 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Support/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | $(BUNDLE_DISPLAY_NAME) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleURLTypes 24 | 25 | 26 | CFBundleTypeRole 27 | Editor 28 | CFBundleURLName 29 | canvas 30 | CFBundleURLSchemes 31 | 32 | canvas 33 | 34 | 35 | 36 | CFBundleVersion 37 | 0 38 | ITSAppUsesNonExemptEncryption 39 | 40 | LSApplicationQueriesSchemes 41 | 42 | googlechrome 43 | googlechromes 44 | org-appextension-feature-password-management 45 | 46 | LSRequiresIPhoneOS 47 | 48 | UILaunchStoryboardName 49 | LaunchScreen 50 | UIMainStoryboardFile 51 | Main 52 | UIRequiredDeviceCapabilities 53 | 54 | armv7 55 | 56 | UIRequiresFullScreen 57 | 58 | UIStatusBarStyle 59 | UIStatusBarStyleDefault 60 | UISupportedInterfaceOrientations 61 | 62 | UIInterfaceOrientationLandscapeRight 63 | UIInterfaceOrientationLandscapeLeft 64 | UIInterfaceOrientationPortrait 65 | 66 | UISupportedInterfaceOrientations~ipad 67 | 68 | UIInterfaceOrientationPortrait 69 | UIInterfaceOrientationPortraitUpsideDown 70 | UIInterfaceOrientationLandscapeLeft 71 | UIInterfaceOrientationLandscapeRight 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Support/Settings.bundle/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | Title 9 | ABOUT 10 | Type 11 | PSGroupSpecifier 12 | 13 | 14 | Type 15 | PSTitleValueSpecifier 16 | Title 17 | VERSION 18 | DefaultValue 19 | 20 | Key 21 | HumanReadableVersion 22 | 23 | 24 | StringsTable 25 | Root 26 | 27 | 28 | -------------------------------------------------------------------------------- /Support/Settings.bundle/en.lproj/Root.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/Support/Settings.bundle/en.lproj/Root.strings -------------------------------------------------------------------------------- /Support/UITests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Support/apple-app-site-association.json: -------------------------------------------------------------------------------- 1 | { 2 | "activitycontinuation": { 3 | "apps": [ 4 | "676PZY29MZ.com.usecanvas.canvas" 5 | ] 6 | }, 7 | "webcredentials": { 8 | "apps": [ 9 | "676PZY29MZ.com.usecanvas.canvas" 10 | ] 11 | }, 12 | "applinks": { 13 | "apps": [], 14 | "details": [ 15 | { 16 | "appID": "676PZY29MZ.com.usecanvas.canvas", 17 | "paths": [ 18 | "NOT /*/*/*/*", 19 | "/*/*/*" 20 | ] 21 | } 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UITests/Snapshots.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CanvasUITests.swift 3 | // CanvasUITests 4 | // 5 | // Created by Sam Soffes on 7/18/16. 6 | // Copyright © 2016 Canvas Labs, Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class Snapshots: XCTestCase { 12 | override func setUp() { 13 | super.setUp() 14 | continueAfterFailure = false 15 | 16 | let app = XCUIApplication() 17 | app.launchArguments.append("-snapshot") 18 | 19 | setupSnapshot(app) 20 | 21 | app.launch() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | app_identifier 'com.usecanvas.canvas' 2 | apple_id 'sam@usecanvas.com' 3 | team_id '676PZY29MZ' 4 | -------------------------------------------------------------------------------- /fastlane/Deliverfile: -------------------------------------------------------------------------------- 1 | app_identifier 'com.usecanvas.canvas' 2 | username 'sam@usecanvas.com' 3 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | fastlane_version '1.97.2' 2 | 3 | default_platform :ios 4 | 5 | platform :ios do 6 | # before_all do 7 | # carthage 8 | # end 9 | 10 | desc 'Submit a new Beta Build to Apple TestFlight' 11 | desc 'This will also make sure the profile is up to date' 12 | lane :beta do 13 | gym(scheme: 'Canvas') 14 | pilot 15 | end 16 | 17 | desc 'Deploy a new version to the App Store' 18 | lane :appstore do 19 | gym(scheme: 'Canvas') 20 | deliver(force: true) 21 | end 22 | 23 | after_all do |lane| 24 | # slack( 25 | # message: 'Successfully deployed new App Update.' 26 | # ) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | ``` 5 | sudo gem install fastlane 6 | ``` 7 | # Available Actions 8 | ## iOS 9 | ### ios beta 10 | ``` 11 | fastlane ios beta 12 | ``` 13 | Submit a new Beta Build to Apple TestFlight 14 | 15 | This will also make sure the profile is up to date 16 | ### ios appstore 17 | ``` 18 | fastlane ios appstore 19 | ``` 20 | Deploy a new version to the App Store 21 | 22 | ---- 23 | 24 | This README.md is auto-generated and will be re-generated every time to run [fastlane](https://fastlane.tools). 25 | More information about fastlane can be found on [https://fastlane.tools](https://fastlane.tools). 26 | The documentation of fastlane can be found on [GitHub](https://github.com/fastlane/fastlane/tree/master/fastlane). -------------------------------------------------------------------------------- /fastlane/metadata/copyright.txt: -------------------------------------------------------------------------------- 1 | 2015–2016 Canvas Labs, Inc. 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/description.txt: -------------------------------------------------------------------------------- 1 | Write with your team, in realtime, using markdown with Canvas. Canvas is the best way to take meeting notes, work on product features, or keep track of those personal notes. 2 | 3 | ## Realtime Collaboration 4 | 5 | Canvas makes it easy to collaborate with anyone in the blink of an eye. Share the link to a canvas with your co-workers and see them show up. Then it’s just a matter of typing. 6 | 7 | ## Swipe to Format 8 | 9 | Use easy swipe gestures to make a checklist, headers and more. Prefer to type? No one likes preview panes. Canvas lets you write Markdown and elegantly folds away the markup when it’s superfluous. 10 | 11 | ## Find What You’re Looking For 12 | 13 | Use blazing fast search to find what you’re looking for without digging through folders. 14 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/keywords.txt: -------------------------------------------------------------------------------- 1 | notes,writing,markdown,realtime,collaboration,team,meeting,work,sync 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/marketing_url.txt: -------------------------------------------------------------------------------- 1 | https://usecanvas.com/ios 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/name.txt: -------------------------------------------------------------------------------- 1 | Canvas — Collaborative Notes 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/privacy_url.txt: -------------------------------------------------------------------------------- 1 | https://usecanvas.com/privacy 2 | -------------------------------------------------------------------------------- /fastlane/metadata/en-US/release_notes.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/metadata/en-US/release_notes.txt -------------------------------------------------------------------------------- /fastlane/metadata/en-US/support_url.txt: -------------------------------------------------------------------------------- 1 | https://usecanvas.com/support 2 | -------------------------------------------------------------------------------- /fastlane/metadata/primary_category.txt: -------------------------------------------------------------------------------- 1 | Productivity 2 | -------------------------------------------------------------------------------- /fastlane/metadata/secondary_category.txt: -------------------------------------------------------------------------------- 1 | Business 2 | -------------------------------------------------------------------------------- /fastlane/screenshots/README.txt: -------------------------------------------------------------------------------- 1 | Put all screenshots you want to use inside the folder of its language (e.g. en-US). 2 | The device type will automatically be recognized using the image resolution. Apple TV screenshots 3 | should be stored in a subdirectory named appleTV with language folders inside of it. 4 | 5 | The screenshots can be named whatever you want, but keep in mind they are sorted alphabetically. -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/ipadPro_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/ipadPro_1.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/ipadPro_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/ipadPro_2.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/ipadPro_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/ipadPro_3.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/ipad_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/ipad_1.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/ipad_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/ipad_2.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/ipad_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/ipad_3.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone35_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone35_1.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone35_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone35_2.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone35_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone35_3.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone35_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone35_4.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone35_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone35_5.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone4_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone4_1.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone4_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone4_2.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone4_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone4_3.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone4_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone4_4.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone4_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone4_5.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone6Plus_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone6Plus_1.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone6Plus_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone6Plus_2.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone6Plus_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone6Plus_3.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone6Plus_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone6Plus_4.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone6Plus_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone6Plus_5.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone6_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone6_1.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone6_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone6_2.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone6_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone6_3.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone6_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone6_4.png -------------------------------------------------------------------------------- /fastlane/screenshots/en-US/iphone6_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usecanvas/ios-v1/c740206a294c775ebc48894b8b36ce477c5cec82/fastlane/screenshots/en-US/iphone6_5.png --------------------------------------------------------------------------------