├── .gitignore ├── Assets ├── CollectionMulti-Message.png ├── CollectionSingle-Message.png ├── ContentNode.png ├── Custom-Message.png ├── DefaultBubble.png ├── Image-Message.png ├── ImageBubble.png ├── MessageGroup.png ├── Mg-Add.gif ├── Mg-Delete.gif ├── Mg-Replace.gif ├── NMessenger-Overview.png ├── SimpleBubble.png ├── StackedBubble.png ├── Text-Message.png ├── TypingIndicator.png └── nmessenger.png ├── Cartfile ├── LICENSE ├── NMessenger.podspec ├── Podfile ├── README.md ├── examples └── MessageGroups │ ├── MessageGroups.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── MessageGroups │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ └── Main.storyboard │ ├── EntryViewController.swift │ ├── ExampleMessengerViewController.swift │ └── Info.plist │ ├── MessageGroupsTests │ ├── Info.plist │ └── MessageGroupsTests.swift │ ├── MessageGroupsUITests │ ├── Info.plist │ └── MessageGroupsUITests.swift │ └── Podfile ├── nMessenger-iOS ├── Info.plist └── nMessenger-iOS.h ├── nMessenger.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── nMessenger-iOS.xcscheme ├── nMessenger ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── Source │ ├── Animated │ │ └── loadBubble-iOS │ │ │ ├── loadBubble@1x.gif │ │ │ ├── loadBubble@2x.gif │ │ │ ├── loadBubble@3x.gif │ │ │ ├── loadBubble_1x │ │ │ ├── loadBubble_0000_Layer-39@1x.png │ │ │ ├── loadBubble_0001_Layer-38@1x.png │ │ │ ├── loadBubble_0002_Layer-37@1x.png │ │ │ ├── loadBubble_0003_Layer-36@1x.png │ │ │ ├── loadBubble_0004_Layer-35@1x.png │ │ │ ├── loadBubble_0005_Layer-34@1x.png │ │ │ ├── loadBubble_0006_Layer-33@1x.png │ │ │ ├── loadBubble_0007_Layer-32@1x.png │ │ │ ├── loadBubble_0008_Layer-31@1x.png │ │ │ ├── loadBubble_0009_Layer-30@1x.png │ │ │ ├── loadBubble_0010_Layer-29@1x.png │ │ │ ├── loadBubble_0011_Layer-28@1x.png │ │ │ ├── loadBubble_0012_Layer-27@1x.png │ │ │ ├── loadBubble_0013_Layer-26@1x.png │ │ │ ├── loadBubble_0014_Layer-25@1x.png │ │ │ ├── loadBubble_0015_Layer-24@1x.png │ │ │ ├── loadBubble_0016_Layer-23@1x.png │ │ │ ├── loadBubble_0017_Layer-22@1x.png │ │ │ ├── loadBubble_0018_Layer-21@1x.png │ │ │ ├── loadBubble_0019_Layer-20@1x.png │ │ │ ├── loadBubble_0020_Layer-19@1x.png │ │ │ ├── loadBubble_0021_Layer-18@1x.png │ │ │ ├── loadBubble_0022_Layer-17@1x.png │ │ │ ├── loadBubble_0023_Layer-16@1x.png │ │ │ ├── loadBubble_0024_Layer-15@1x.png │ │ │ ├── loadBubble_0025_Layer-14@1x.png │ │ │ ├── loadBubble_0026_Layer-13@1x.png │ │ │ ├── loadBubble_0027_Layer-12@1x.png │ │ │ ├── loadBubble_0028_Layer-11@1x.png │ │ │ ├── loadBubble_0029_Layer-10@1x.png │ │ │ ├── loadBubble_0030_Layer-9@1x.png │ │ │ ├── loadBubble_0031_Layer-8@1x.png │ │ │ ├── loadBubble_0032_Layer-7@1x.png │ │ │ ├── loadBubble_0033_Layer-6@1x.png │ │ │ ├── loadBubble_0034_Layer-5@1x.png │ │ │ ├── loadBubble_0035_Layer-4@1x.png │ │ │ ├── loadBubble_0036_Layer-3@1x.png │ │ │ ├── loadBubble_0037_Layer-2@1x.png │ │ │ └── loadBubble_0038_Layer-1@1x.png │ │ │ ├── loadBubble_2x │ │ │ ├── loadBubble_0000_Layer-39@2x.png │ │ │ ├── loadBubble_0001_Layer-38@2x.png │ │ │ ├── loadBubble_0002_Layer-37@2x.png │ │ │ ├── loadBubble_0003_Layer-36@2x.png │ │ │ ├── loadBubble_0004_Layer-35@2x.png │ │ │ ├── loadBubble_0005_Layer-34@2x.png │ │ │ ├── loadBubble_0006_Layer-33@2x.png │ │ │ ├── loadBubble_0007_Layer-32@2x.png │ │ │ ├── loadBubble_0008_Layer-31@2x.png │ │ │ ├── loadBubble_0009_Layer-30@2x.png │ │ │ ├── loadBubble_0010_Layer-29@2x.png │ │ │ ├── loadBubble_0011_Layer-28@2x.png │ │ │ ├── loadBubble_0012_Layer-27@2x.png │ │ │ ├── loadBubble_0013_Layer-26@2x.png │ │ │ ├── loadBubble_0014_Layer-25@2x.png │ │ │ ├── loadBubble_0015_Layer-24@2x.png │ │ │ ├── loadBubble_0016_Layer-23@2x.png │ │ │ ├── loadBubble_0017_Layer-22@2x.png │ │ │ ├── loadBubble_0018_Layer-21@2x.png │ │ │ ├── loadBubble_0019_Layer-20@2x.png │ │ │ ├── loadBubble_0020_Layer-19@2x.png │ │ │ ├── loadBubble_0021_Layer-18@2x.png │ │ │ ├── loadBubble_0022_Layer-17@2x.png │ │ │ ├── loadBubble_0023_Layer-16@2x.png │ │ │ ├── loadBubble_0024_Layer-15@2x.png │ │ │ ├── loadBubble_0025_Layer-14@2x.png │ │ │ ├── loadBubble_0026_Layer-13@2x.png │ │ │ ├── loadBubble_0027_Layer-12@2x.png │ │ │ ├── loadBubble_0028_Layer-11@2x.png │ │ │ ├── loadBubble_0029_Layer-10@2x.png │ │ │ ├── loadBubble_0030_Layer-9@2x.png │ │ │ ├── loadBubble_0031_Layer-8@2x.png │ │ │ ├── loadBubble_0032_Layer-7@2x.png │ │ │ ├── loadBubble_0033_Layer-6@2x.png │ │ │ ├── loadBubble_0034_Layer-5@2x.png │ │ │ ├── loadBubble_0035_Layer-4@2x.png │ │ │ ├── loadBubble_0036_Layer-3@2x.png │ │ │ ├── loadBubble_0037_Layer-2@2x.png │ │ │ └── loadBubble_0038_Layer-1@2x.png │ │ │ └── loadBubble_3x │ │ │ ├── loadBubble_0000_Layer-39@3x.png │ │ │ ├── loadBubble_0001_Layer-38@3x.png │ │ │ ├── loadBubble_0002_Layer-37@3x.png │ │ │ ├── loadBubble_0003_Layer-36@3x.png │ │ │ ├── loadBubble_0004_Layer-35@3x.png │ │ │ ├── loadBubble_0005_Layer-34@3x.png │ │ │ ├── loadBubble_0006_Layer-33@3x.png │ │ │ ├── loadBubble_0007_Layer-32@3x.png │ │ │ ├── loadBubble_0008_Layer-31@3x.png │ │ │ ├── loadBubble_0009_Layer-30@3x.png │ │ │ ├── loadBubble_0010_Layer-29@3x.png │ │ │ ├── loadBubble_0011_Layer-28@3x.png │ │ │ ├── loadBubble_0012_Layer-27@3x.png │ │ │ ├── loadBubble_0013_Layer-26@3x.png │ │ │ ├── loadBubble_0014_Layer-25@3x.png │ │ │ ├── loadBubble_0015_Layer-24@3x.png │ │ │ ├── loadBubble_0016_Layer-23@3x.png │ │ │ ├── loadBubble_0017_Layer-22@3x.png │ │ │ ├── loadBubble_0018_Layer-21@3x.png │ │ │ ├── loadBubble_0019_Layer-20@3x.png │ │ │ ├── loadBubble_0020_Layer-19@3x.png │ │ │ ├── loadBubble_0021_Layer-18@3x.png │ │ │ ├── loadBubble_0022_Layer-17@3x.png │ │ │ ├── loadBubble_0023_Layer-16@3x.png │ │ │ ├── loadBubble_0024_Layer-15@3x.png │ │ │ ├── loadBubble_0025_Layer-14@3x.png │ │ │ ├── loadBubble_0026_Layer-13@3x.png │ │ │ ├── loadBubble_0027_Layer-12@3x.png │ │ │ ├── loadBubble_0028_Layer-11@3x.png │ │ │ ├── loadBubble_0029_Layer-10@3x.png │ │ │ ├── loadBubble_0030_Layer-9@3x.png │ │ │ ├── loadBubble_0031_Layer-8@3x.png │ │ │ ├── loadBubble_0032_Layer-7@3x.png │ │ │ ├── loadBubble_0033_Layer-6@3x.png │ │ │ ├── loadBubble_0034_Layer-5@3x.png │ │ │ ├── loadBubble_0035_Layer-4@3x.png │ │ │ ├── loadBubble_0036_Layer-3@3x.png │ │ │ ├── loadBubble_0037_Layer-2@3x.png │ │ │ └── loadBubble_0038_Layer-1@3x.png │ ├── Images │ │ ├── MessageBubble.png │ │ ├── cameraRollIcon.png │ │ ├── cameraRollIcon@2x.png │ │ ├── cameraRollIcon@3x.png │ │ ├── exitIcon.png │ │ ├── flashIcon.png │ │ ├── flashIcon@2x.png │ │ ├── flashIcon@3x.png │ │ ├── flipCameraIcon.png │ │ ├── shutterBtn.png │ │ ├── shutterBtn@2x.png │ │ └── shutterBtn@3x.png │ ├── MessageNodes │ │ ├── BubbleConfiguration │ │ │ ├── BubbleConfigurationProtocol.swift │ │ │ ├── ImageBubbleConfiguration.swift │ │ │ ├── SimpleBubbleConfiguration.swift │ │ │ └── StandardBubbleConfiguration.swift │ │ ├── Bubbles │ │ │ ├── Bubble.swift │ │ │ ├── DefaultBubble.swift │ │ │ ├── ImageBubble.swift │ │ │ ├── SimpleBubble.swift │ │ │ └── StackedBubble.swift │ │ ├── ContentNodes │ │ │ ├── CollectionViewContent │ │ │ │ ├── CollectionViewContentNode.swift │ │ │ │ └── CustomContentCellNode.swift │ │ │ ├── ContentNode.swift │ │ │ ├── CustomContentContent │ │ │ │ └── CustomContentNode.swift │ │ │ ├── ImageContent │ │ │ │ └── ImageContentNode.swift │ │ │ ├── NetworkImageContent │ │ │ │ └── NetworkImageContentNode.swift │ │ │ ├── TextContent │ │ │ │ └── TextContentNode.swift │ │ │ └── TypingIndicatorContent │ │ │ │ └── TypingIndicatorContent.swift │ │ ├── GeneralMessageCell │ │ │ └── GeneralMessengerCell.swift │ │ ├── Indicators │ │ │ ├── HeadLoadingIndicator.swift │ │ │ └── MessageSentIndicator.swift │ │ └── MessageCell │ │ │ ├── MessageCellProtocol.swift │ │ │ ├── MessageGroup.swift │ │ │ └── MessageNode.swift │ ├── Messenger │ │ ├── Components │ │ │ ├── CameraViewController.swift │ │ │ ├── InputBarView │ │ │ │ ├── InputBarView.swift │ │ │ │ ├── InputBarViewProtocol.swift │ │ │ │ ├── NMessengerBarView.swift │ │ │ │ └── NMessengerBarView.xib │ │ │ └── NMessenger.swift │ │ └── NMessengerViewController.swift │ └── Utilities │ │ ├── ModalAlertUtilities.swift │ │ ├── UIColor+N1Colors.swift │ │ └── UIFont+N1Fonts.swift └── ViewController.swift ├── nMessengerTests ├── Extensions │ ├── UIColorTests.swift │ └── UIFontTests.swift ├── Info.plist ├── Messenger │ └── NMessengerTests.swift ├── UI Components │ ├── Chat │ │ ├── BubbleConfigurationTests.swift │ │ ├── BubbleTests.swift │ │ ├── CollectionViewContentNodeTests.swift │ │ ├── HeadLoadingIndicatorTests.swift │ │ ├── MessageGroupTests.swift │ │ ├── MessageNodeTests.swift │ │ └── NetworkImageContentNodeTests.swift │ └── NMessengerVCTests.swift └── nMessengerTests.swift └── nMessengerUITests ├── Info.plist ├── OrientationUITest.swift └── nMessengerUITests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | ## Build generated 4 | build/ 5 | DerivedData/ 6 | Pods/ 7 | Podfile.lock 8 | Carthage/ 9 | Cartfile.resolved 10 | *.xcworkspace 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcscmblueprint 27 | 28 | # Swift Package Manager 29 | # 30 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 31 | Packages/ 32 | .build/ 33 | 34 | ## Obj-C/Swift specific 35 | *.hmap 36 | *.ipa 37 | 38 | ## Playgrounds 39 | timeline.xctimeline 40 | playground.xcworkspace 41 | -------------------------------------------------------------------------------- /Assets/CollectionMulti-Message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/CollectionMulti-Message.png -------------------------------------------------------------------------------- /Assets/CollectionSingle-Message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/CollectionSingle-Message.png -------------------------------------------------------------------------------- /Assets/ContentNode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/ContentNode.png -------------------------------------------------------------------------------- /Assets/Custom-Message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/Custom-Message.png -------------------------------------------------------------------------------- /Assets/DefaultBubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/DefaultBubble.png -------------------------------------------------------------------------------- /Assets/Image-Message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/Image-Message.png -------------------------------------------------------------------------------- /Assets/ImageBubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/ImageBubble.png -------------------------------------------------------------------------------- /Assets/MessageGroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/MessageGroup.png -------------------------------------------------------------------------------- /Assets/Mg-Add.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/Mg-Add.gif -------------------------------------------------------------------------------- /Assets/Mg-Delete.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/Mg-Delete.gif -------------------------------------------------------------------------------- /Assets/Mg-Replace.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/Mg-Replace.gif -------------------------------------------------------------------------------- /Assets/NMessenger-Overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/NMessenger-Overview.png -------------------------------------------------------------------------------- /Assets/SimpleBubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/SimpleBubble.png -------------------------------------------------------------------------------- /Assets/StackedBubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/StackedBubble.png -------------------------------------------------------------------------------- /Assets/Text-Message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/Text-Message.png -------------------------------------------------------------------------------- /Assets/TypingIndicator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/TypingIndicator.png -------------------------------------------------------------------------------- /Assets/nmessenger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/Assets/nmessenger.png -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "facebook/AsyncDisplayKit" "1.9.92" 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 eBay Software Foundation 4 | Initially written by Aaron Tainter and David Schecter 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /NMessenger.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint NMessenger.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "NMessenger" 19 | s.version = "1.0.80" 20 | s.summary = "A fast, lightweight messenger component built on AsyncDisplaykit and written in Swift" 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = <<-DESC 28 | NMessenger is a fast, lightweight messenger component built on AsyncDisplaykit and written in Swift. Developers can inherently achieve 60FPS scrolling and smooth transitions with rich content components. 29 | DESC 30 | 31 | s.homepage = "https://github.com/eBay/NMessenger" 32 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 33 | 34 | 35 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 36 | # 37 | # Licensing your code is important. See http://choosealicense.com for more info. 38 | # CocoaPods will detect a license file if there is a named LICENSE* 39 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 40 | # 41 | 42 | s.license = "MIT" 43 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 44 | 45 | 46 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 47 | # 48 | # Specify the authors of the library, with email addresses. Email addresses 49 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 50 | # accepts just a name if you'd rather not provide an email address. 51 | # 52 | # Specify a social_media_url where others can refer to, for example a twitter 53 | # profile URL. 54 | # 55 | 56 | # s.author = { "eBay" => "email@address.com" } 57 | # Or just: s.author = "eBay" 58 | s.authors = { "Schechter, David" => "david.schechter.mail@gmail.com", "Tainter, Aaron" => "amtainter@gmail.com" } 59 | # s.social_media_url = "http://twitter.com/eBay" 60 | 61 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 62 | # 63 | # If this Pod runs only on iOS or OS X, then specify the platform and 64 | # the deployment target. You can optionally include the target after the platform. 65 | # 66 | 67 | # s.platform = :ios 68 | s.platform = :ios, "8.2" 69 | 70 | # When using multiple platforms 71 | # s.ios.deployment_target = "5.0" 72 | # s.osx.deployment_target = "10.7" 73 | # s.watchos.deployment_target = "2.0" 74 | # s.tvos.deployment_target = "9.0" 75 | 76 | 77 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 78 | # 79 | # Specify the location from where the source should be retrieved. 80 | # Supports git, hg, bzr, svn and HTTP. 81 | # 82 | 83 | s.source = { :git => "https://github.com/eBay/NMessenger.git", :tag => s.version } 84 | 85 | 86 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 87 | # 88 | # CocoaPods is smart about how it includes source code. For source files 89 | # giving a folder will include any swift, h, m, mm, c & cpp files. 90 | # For header files it will include any header in the folder. 91 | # Not including the public_header_files will make all headers public. 92 | # 93 | 94 | s.source_files = "nMessenger/Source/**/*.swift" 95 | # s.exclude_files = "Classes/Exclude" 96 | 97 | # s.public_header_files = "Classes/**/*.h" 98 | 99 | 100 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 101 | # 102 | # A list of resources included with the Pod. These are copied into the 103 | # target bundle with a build phase script. Anything else will be cleaned. 104 | # You can preserve files from being cleaned, please don't preserve 105 | # non-essential files like tests, examples and documentation. 106 | # 107 | 108 | # s.resource = "icon.png" 109 | s.resources = ['nMessenger/Source/**/*.{png,jpeg,jpg,xib}'] 110 | 111 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 112 | 113 | 114 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 115 | # 116 | # Link your library with frameworks, or libraries. Libraries do not include 117 | # the lib prefix of their name. 118 | # 119 | 120 | # s.framework = "SomeFramework" 121 | # s.frameworks = "SomeFramework", "AnotherFramework" 122 | 123 | # s.library = "iconv" 124 | # s.libraries = "iconv", "xml2" 125 | 126 | 127 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 128 | # 129 | # If your library depends on compiler flags you can set them in the xcconfig hash 130 | # where they will only apply to your library. If you depend on other Podspecs 131 | # you can include multiple dependencies to ensure it works. 132 | 133 | s.requires_arc = true 134 | 135 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 136 | s.dependency "Texture", "2.3.2" 137 | end 138 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'nMessenger' do 5 | # Comment this line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for nMessenger 9 | pod 'Texture', '2.3.2' 10 | 11 | target 'nMessengerTests' do 12 | inherit! :search_paths 13 | # Pods for testing 14 | end 15 | 16 | target 'nMessengerUITests' do 17 | inherit! :search_paths 18 | # Pods for testing 19 | end 20 | 21 | target 'nMessenger-iOS' do 22 | inherit! :search_paths 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /examples/MessageGroups/MessageGroups.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/MessageGroups/MessageGroups/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MessageGroups 4 | // 5 | // Created by Tainter, Aaron on 8/17/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | window = UIWindow(frame: UIScreen.main.bounds) 20 | window?.makeKeyAndVisible() 21 | window?.rootViewController = UINavigationController(rootViewController: EntryViewController()) 22 | return true 23 | } 24 | 25 | func applicationWillResignActive(_ application: UIApplication) { 26 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 27 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 28 | } 29 | 30 | func applicationDidEnterBackground(_ application: UIApplication) { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | func applicationWillEnterForeground(_ application: UIApplication) { 36 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | func applicationDidBecomeActive(_ application: UIApplication) { 40 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 41 | } 42 | 43 | func applicationWillTerminate(_ application: UIApplication) { 44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 45 | } 46 | 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /examples/MessageGroups/MessageGroups/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /examples/MessageGroups/MessageGroups/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 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /examples/MessageGroups/MessageGroups/EntryViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EntryViewController.swift 3 | // MessageGroups 4 | // 5 | // Created by Max Alexander on 11/21/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import LoremIpsum 11 | 12 | class EntryViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 13 | 14 | lazy var tableView : UITableView = { 15 | let tableView = UITableView() 16 | tableView.dataSource = self 17 | tableView.delegate = self 18 | return tableView 19 | }() 20 | 21 | lazy var items : [String] = [ 22 | "Empty", 23 | "50 Preloaded Messages", 24 | "200 Preloaded Messages" 25 | ] 26 | 27 | override func viewDidLoad() { 28 | super.viewDidLoad() 29 | view.addSubview(tableView) 30 | tableView.frame = view.bounds 31 | title = "Welcome to NMessenger" 32 | } 33 | 34 | override func didReceiveMemoryWarning() { 35 | super.didReceiveMemoryWarning() 36 | // Dispose of any resources that can be recreated. 37 | } 38 | 39 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 40 | return items.count 41 | } 42 | 43 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 44 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") ?? UITableViewCell() 45 | let item = items[indexPath.row] 46 | cell.textLabel?.text = item 47 | return cell 48 | } 49 | 50 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 51 | tableView.deselectRow(at: indexPath, animated: true) 52 | let exampleViewController = ExampleMessengerViewController() 53 | if indexPath.row == 1 { 54 | exampleViewController.bootstrapWithRandomMessages = 50 55 | } 56 | if indexPath.row == 2 { 57 | exampleViewController.bootstrapWithRandomMessages = 200 58 | } 59 | navigationController?.pushViewController(exampleViewController, animated: true) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /examples/MessageGroups/MessageGroups/ExampleMessengerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import UIKit 12 | import NMessenger 13 | import AsyncDisplayKit 14 | 15 | // not needed in your implementation 16 | import LoremIpsum 17 | 18 | class ExampleMessengerViewController: NMessengerViewController { 19 | 20 | let segmentedControlPadding:CGFloat = 10 21 | let segmentedControlHeight: CGFloat = 30 22 | 23 | lazy var senderSegmentedControl : UISegmentedControl = { 24 | let control = UISegmentedControl(items: ["incoming", "outgoing"]) 25 | control.selectedSegmentIndex = 0 26 | return control 27 | }() 28 | 29 | private(set) var lastMessageGroup:MessageGroup? = nil 30 | 31 | //This is not needed in your implementation. This just for a demo purpose. 32 | var bootstrapWithRandomMessages : Int = 0 33 | 34 | override func viewDidLoad() { 35 | super.viewDidLoad() 36 | navigationItem.titleView = senderSegmentedControl 37 | 38 | //BEGIN BOOTSTRAPPING MESSAGES 39 | var messageGroups = [MessageGroup]() 40 | for _ in 0.. GeneralMessengerCell { 72 | 73 | //create a new text message 74 | let textContent = TextContentNode(textMessageString: text, currentViewController: self, bubbleConfiguration: self.sharedBubbleConfiguration) 75 | let newMessage = MessageNode(content: textContent) 76 | newMessage.cellPadding = messagePadding 77 | newMessage.currentViewController = self 78 | 79 | //add message to correct group 80 | if (self.senderSegmentedControl.selectedSegmentIndex == 0) { //incoming 81 | self.postText(newMessage, isIncomingMessage: true) 82 | } else { //outgoing 83 | self.postText(newMessage, isIncomingMessage: false) 84 | } 85 | 86 | return newMessage 87 | } 88 | 89 | //MARK: Helper Functions 90 | /** 91 | Posts a text to the correct message group. Creates a new message group *isIncomingMessage* is different than the last message group. 92 | - parameter message: The message to add 93 | - parameter isIncomingMessage: If the message is incoming or outgoing. 94 | */ 95 | private func postText(_ message: MessageNode, isIncomingMessage: Bool) { 96 | if self.lastMessageGroup == nil || self.lastMessageGroup?.isIncomingMessage == !isIncomingMessage { 97 | self.lastMessageGroup = self.createMessageGroup() 98 | 99 | //add avatar if incoming message 100 | if isIncomingMessage { 101 | self.lastMessageGroup?.avatarNode = self.createAvatar() 102 | } 103 | 104 | self.lastMessageGroup!.isIncomingMessage = isIncomingMessage 105 | self.messengerView.addMessageToMessageGroup(message, messageGroup: self.lastMessageGroup!, scrollsToLastMessage: false) 106 | self.messengerView.addMessage(self.lastMessageGroup!, scrollsToMessage: true, withAnimation: isIncomingMessage ? .left : .right) 107 | 108 | } else { 109 | self.messengerView.addMessageToMessageGroup(message, messageGroup: self.lastMessageGroup!, scrollsToLastMessage: true) 110 | } 111 | } 112 | 113 | /** 114 | Creates a new message group for *lastMessageGroup* 115 | -returns: MessageGroup 116 | */ 117 | private func createMessageGroup()->MessageGroup { 118 | let newMessageGroup = MessageGroup() 119 | newMessageGroup.currentViewController = self 120 | newMessageGroup.cellPadding = self.messagePadding 121 | return newMessageGroup 122 | } 123 | 124 | /** 125 | Creates mock avatar with an AsyncDisplaykit *ASImageNode*. 126 | - returns: ASImageNode 127 | */ 128 | private func createAvatar()->ASImageNode { 129 | let avatar = ASImageNode() 130 | avatar.backgroundColor = UIColor.lightGray 131 | avatar.style.preferredSize = CGSize(width: 20, height: 20) 132 | avatar.layer.cornerRadius = 10 133 | return avatar 134 | } 135 | 136 | /** 137 | Just a helper to give a random isIncomingValue 138 | */ 139 | func randomBool() -> Bool { 140 | return arc4random_uniform(2) == 0 141 | } 142 | 143 | deinit { 144 | print("Deinitialized") 145 | } 146 | } 147 | 148 | -------------------------------------------------------------------------------- /examples/MessageGroups/MessageGroups/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSCameraUsageDescription 26 | $(PRODUCT_NAME) wants acceess to Camera 27 | NSPhotoLibraryUsageDescription 28 | $(PRODUCT_NAME) wants acceess to Photos 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /examples/MessageGroups/MessageGroupsTests/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 | -------------------------------------------------------------------------------- /examples/MessageGroups/MessageGroupsTests/MessageGroupsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessageGroupsTests.swift 3 | // MessageGroupsTests 4 | // 5 | // Created by Tainter, Aaron on 8/17/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import MessageGroups 11 | 12 | class MessageGroupsTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /examples/MessageGroups/MessageGroupsUITests/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 | -------------------------------------------------------------------------------- /examples/MessageGroups/MessageGroupsUITests/MessageGroupsUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessageGroupsUITests.swift 3 | // MessageGroupsUITests 4 | // 5 | // Created by Tainter, Aaron on 8/17/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class MessageGroupsUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /examples/MessageGroups/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'MessageGroups' do 5 | # Comment this line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | swift_version = '3.0' 9 | 10 | # Pods for MessageGroups 11 | pod 'NMessenger', :path => '../..' 12 | pod 'LoremIpsum', '= 1.0.0' 13 | 14 | target 'MessageGroupsTests' do 15 | inherit! :search_paths 16 | # Pods for testing 17 | end 18 | 19 | target 'MessageGroupsUITests' do 20 | inherit! :search_paths 21 | # Pods for testing 22 | end 23 | 24 | end 25 | 26 | post_install do |installer| 27 | installer.pods_project.targets.each do |target| 28 | target.build_configurations.each do |config| 29 | config.build_settings['SWIFT_VERSION'] = '3.0' 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /nMessenger-iOS/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /nMessenger-iOS/nMessenger-iOS.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | //! Project version number for nMessenger-iOS. 4 | FOUNDATION_EXPORT double nMessenger_iOSVersionNumber; 5 | 6 | //! Project version string for nMessenger-iOS. 7 | FOUNDATION_EXPORT const unsigned char nMessenger_iOSVersionString[]; 8 | 9 | // In this header, you should import all the public headers of your framework using statements like #import 10 | 11 | 12 | -------------------------------------------------------------------------------- /nMessenger.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /nMessenger.xcodeproj/xcshareddata/xcschemes/nMessenger-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /nMessenger/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import UIKit 12 | 13 | @UIApplicationMain 14 | class AppDelegate: UIResponder, UIApplicationDelegate { 15 | 16 | var window: UIWindow? 17 | 18 | 19 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 20 | // Override point for customization after application launch. 21 | return true 22 | } 23 | 24 | func applicationWillResignActive(_ application: UIApplication) { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 27 | } 28 | 29 | func applicationDidEnterBackground(_ application: UIApplication) { 30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 32 | } 33 | 34 | func applicationWillEnterForeground(_ application: UIApplication) { 35 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 36 | } 37 | 38 | func applicationDidBecomeActive(_ application: UIApplication) { 39 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 40 | } 41 | 42 | func applicationWillTerminate(_ application: UIApplication) { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /nMessenger/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /nMessenger/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /nMessenger/Base.lproj/LaunchScreen.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 | 27 | 28 | -------------------------------------------------------------------------------- /nMessenger/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 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /nMessenger/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | NSPhotoLibraryUsageDescription 47 | $(PRODUCT_NAME) wants acceess to Photos 48 | NSCameraUsageDescription 49 | $(PRODUCT_NAME) wants acceess to Camera 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble@1x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble@1x.gif -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble@2x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble@2x.gif -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble@3x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble@3x.gif -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0000_Layer-39@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0000_Layer-39@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0001_Layer-38@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0001_Layer-38@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0002_Layer-37@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0002_Layer-37@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0003_Layer-36@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0003_Layer-36@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0004_Layer-35@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0004_Layer-35@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0005_Layer-34@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0005_Layer-34@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0006_Layer-33@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0006_Layer-33@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0007_Layer-32@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0007_Layer-32@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0008_Layer-31@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0008_Layer-31@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0009_Layer-30@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0009_Layer-30@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0010_Layer-29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0010_Layer-29@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0011_Layer-28@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0011_Layer-28@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0012_Layer-27@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0012_Layer-27@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0013_Layer-26@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0013_Layer-26@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0014_Layer-25@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0014_Layer-25@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0015_Layer-24@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0015_Layer-24@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0016_Layer-23@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0016_Layer-23@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0017_Layer-22@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0017_Layer-22@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0018_Layer-21@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0018_Layer-21@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0019_Layer-20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0019_Layer-20@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0020_Layer-19@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0020_Layer-19@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0021_Layer-18@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0021_Layer-18@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0022_Layer-17@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0022_Layer-17@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0023_Layer-16@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0023_Layer-16@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0024_Layer-15@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0024_Layer-15@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0025_Layer-14@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0025_Layer-14@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0026_Layer-13@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0026_Layer-13@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0027_Layer-12@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0027_Layer-12@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0028_Layer-11@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0028_Layer-11@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0029_Layer-10@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0029_Layer-10@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0030_Layer-9@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0030_Layer-9@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0031_Layer-8@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0031_Layer-8@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0032_Layer-7@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0032_Layer-7@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0033_Layer-6@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0033_Layer-6@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0034_Layer-5@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0034_Layer-5@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0035_Layer-4@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0035_Layer-4@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0036_Layer-3@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0036_Layer-3@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0037_Layer-2@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0037_Layer-2@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0038_Layer-1@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_1x/loadBubble_0038_Layer-1@1x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0000_Layer-39@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0000_Layer-39@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0001_Layer-38@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0001_Layer-38@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0002_Layer-37@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0002_Layer-37@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0003_Layer-36@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0003_Layer-36@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0004_Layer-35@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0004_Layer-35@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0005_Layer-34@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0005_Layer-34@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0006_Layer-33@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0006_Layer-33@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0007_Layer-32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0007_Layer-32@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0008_Layer-31@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0008_Layer-31@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0009_Layer-30@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0009_Layer-30@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0010_Layer-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0010_Layer-29@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0011_Layer-28@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0011_Layer-28@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0012_Layer-27@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0012_Layer-27@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0013_Layer-26@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0013_Layer-26@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0014_Layer-25@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0014_Layer-25@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0015_Layer-24@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0015_Layer-24@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0016_Layer-23@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0016_Layer-23@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0017_Layer-22@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0017_Layer-22@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0018_Layer-21@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0018_Layer-21@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0019_Layer-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0019_Layer-20@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0020_Layer-19@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0020_Layer-19@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0021_Layer-18@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0021_Layer-18@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0022_Layer-17@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0022_Layer-17@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0023_Layer-16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0023_Layer-16@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0024_Layer-15@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0024_Layer-15@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0025_Layer-14@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0025_Layer-14@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0026_Layer-13@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0026_Layer-13@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0027_Layer-12@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0027_Layer-12@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0028_Layer-11@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0028_Layer-11@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0029_Layer-10@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0029_Layer-10@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0030_Layer-9@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0030_Layer-9@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0031_Layer-8@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0031_Layer-8@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0032_Layer-7@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0032_Layer-7@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0033_Layer-6@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0033_Layer-6@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0034_Layer-5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0034_Layer-5@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0035_Layer-4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0035_Layer-4@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0036_Layer-3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0036_Layer-3@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0037_Layer-2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0037_Layer-2@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0038_Layer-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_2x/loadBubble_0038_Layer-1@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0000_Layer-39@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0000_Layer-39@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0001_Layer-38@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0001_Layer-38@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0002_Layer-37@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0002_Layer-37@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0003_Layer-36@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0003_Layer-36@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0004_Layer-35@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0004_Layer-35@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0005_Layer-34@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0005_Layer-34@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0006_Layer-33@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0006_Layer-33@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0007_Layer-32@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0007_Layer-32@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0008_Layer-31@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0008_Layer-31@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0009_Layer-30@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0009_Layer-30@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0010_Layer-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0010_Layer-29@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0011_Layer-28@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0011_Layer-28@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0012_Layer-27@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0012_Layer-27@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0013_Layer-26@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0013_Layer-26@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0014_Layer-25@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0014_Layer-25@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0015_Layer-24@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0015_Layer-24@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0016_Layer-23@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0016_Layer-23@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0017_Layer-22@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0017_Layer-22@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0018_Layer-21@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0018_Layer-21@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0019_Layer-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0019_Layer-20@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0020_Layer-19@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0020_Layer-19@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0021_Layer-18@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0021_Layer-18@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0022_Layer-17@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0022_Layer-17@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0023_Layer-16@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0023_Layer-16@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0024_Layer-15@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0024_Layer-15@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0025_Layer-14@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0025_Layer-14@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0026_Layer-13@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0026_Layer-13@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0027_Layer-12@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0027_Layer-12@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0028_Layer-11@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0028_Layer-11@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0029_Layer-10@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0029_Layer-10@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0030_Layer-9@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0030_Layer-9@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0031_Layer-8@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0031_Layer-8@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0032_Layer-7@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0032_Layer-7@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0033_Layer-6@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0033_Layer-6@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0034_Layer-5@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0034_Layer-5@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0035_Layer-4@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0035_Layer-4@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0036_Layer-3@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0036_Layer-3@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0037_Layer-2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0037_Layer-2@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0038_Layer-1@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Animated/loadBubble-iOS/loadBubble_3x/loadBubble_0038_Layer-1@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/MessageBubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/MessageBubble.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/cameraRollIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/cameraRollIcon.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/cameraRollIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/cameraRollIcon@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/cameraRollIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/cameraRollIcon@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/exitIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/exitIcon.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/flashIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/flashIcon.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/flashIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/flashIcon@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/flashIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/flashIcon@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/flipCameraIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/flipCameraIcon.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/shutterBtn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/shutterBtn.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/shutterBtn@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/shutterBtn@2x.png -------------------------------------------------------------------------------- /nMessenger/Source/Images/shutterBtn@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eBay/NMessenger/a6e9547554fbf9b66c3f67eff6c550bee0529ea1/nMessenger/Source/Images/shutterBtn@3x.png -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/BubbleConfiguration/BubbleConfigurationProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | /** Configures a bubble for a ContentNode. Implement this to create your own bubble configuration */ 15 | public protocol BubbleConfigurationProtocol { 16 | var isMasked: Bool {get set} 17 | 18 | /** Create and return a UI color representing an incoming message */ 19 | func getIncomingColor() -> UIColor 20 | 21 | /** Create and return a UI color representing an outgoing message */ 22 | func getOutgoingColor() -> UIColor 23 | 24 | /** Create and return a bubble for the ContentNode */ 25 | func getBubble() -> Bubble 26 | 27 | /** Create and return a bubble that is used by the Message group for Message nodes after the first. This is typically used to "stack" messages */ 28 | func getSecondaryBubble() -> Bubble 29 | } 30 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/BubbleConfiguration/ImageBubbleConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | /** Uses a simple bubble as primary and a simple bubble as secondary. Incoming color is pale grey and outgoing is mid grey */ 15 | open class ImageBubbleConfiguration: BubbleConfigurationProtocol { 16 | 17 | open var isMasked = false 18 | 19 | public init() {} 20 | 21 | open func getIncomingColor() -> UIColor 22 | { 23 | return UIColor.n1PaleGreyColor() 24 | } 25 | 26 | open func getOutgoingColor() -> UIColor 27 | { 28 | return UIColor.n1ActionBlueColor() 29 | } 30 | 31 | open func getBubble() -> Bubble 32 | { 33 | let newBubble = ImageBubble() 34 | newBubble.bubbleImage = UIImage(named: "MessageBubble", in: Bundle(for: NMessengerViewController.self), compatibleWith: nil) 35 | newBubble.cutInsets = UIEdgeInsetsMake(29, 32, 25, 43) 36 | newBubble.hasLayerMask = isMasked 37 | return newBubble 38 | } 39 | 40 | open func getSecondaryBubble() -> Bubble 41 | { 42 | let newBubble = ImageBubble() 43 | newBubble.bubbleImage = UIImage(named: "MessageBubble", in: Bundle(for: NMessengerViewController.self), compatibleWith: nil) 44 | newBubble.cutInsets = UIEdgeInsetsMake(29, 32, 25, 43) 45 | newBubble.hasLayerMask = isMasked 46 | return newBubble 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/BubbleConfiguration/SimpleBubbleConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | /** Uses a simple bubble as primary and a simple bubble as secondary. Incoming color is pale grey and outgoing is mid grey */ 15 | open class SimpleBubbleConfiguration: BubbleConfigurationProtocol { 16 | 17 | open var isMasked = false 18 | 19 | public init() {} 20 | 21 | open func getIncomingColor() -> UIColor 22 | { 23 | return UIColor.n1PaleGreyColor() 24 | } 25 | 26 | open func getOutgoingColor() -> UIColor 27 | { 28 | return UIColor.n1MidGreyColor() 29 | } 30 | 31 | open func getBubble() -> Bubble 32 | { 33 | let newBubble = SimpleBubble() 34 | newBubble.hasLayerMask = isMasked 35 | return newBubble 36 | } 37 | 38 | open func getSecondaryBubble() -> Bubble 39 | { 40 | let newBubble = SimpleBubble() 41 | newBubble.hasLayerMask = isMasked 42 | return newBubble 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/BubbleConfiguration/StandardBubbleConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | /** Uses a default bubble as primary and a stacked bubble as secondary. Incoming color is pale grey and outgoing is blue */ 15 | open class StandardBubbleConfiguration: BubbleConfigurationProtocol { 16 | 17 | open var isMasked = false 18 | 19 | public init() {} 20 | 21 | open func getIncomingColor() -> UIColor 22 | { 23 | return UIColor.n1PaleGreyColor() 24 | } 25 | 26 | open func getOutgoingColor() -> UIColor 27 | { 28 | return UIColor.n1ActionBlueColor() 29 | } 30 | 31 | open func getBubble() -> Bubble 32 | { 33 | let newBubble = DefaultBubble() 34 | newBubble.hasLayerMask = isMasked 35 | return newBubble 36 | } 37 | 38 | open func getSecondaryBubble() -> Bubble 39 | { 40 | let newBubble = StackedBubble() 41 | newBubble.hasLayerMask = isMasked 42 | return newBubble 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/Bubbles/Bubble.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | import AsyncDisplayKit 14 | 15 | //MARK: Bubble class 16 | /** 17 | 'Abstract' Bubble class. Subclass for creating a custom bubble 18 | */ 19 | open class Bubble { 20 | 21 | // MARK: Public Parameters 22 | open var bubbleColor : UIColor = UIColor.n1PaleGreyColor() 23 | 24 | /** When this is set, the layer mask will mask the ContentNode.*/ 25 | open var hasLayerMask = false 26 | 27 | /** 28 | A layer for the bubble. Make sure this property is first accessed on the main thread. 29 | */ 30 | open lazy var layer: CAShapeLayer = CAShapeLayer() 31 | /** 32 | A layer that holds a mask which is the same shape as the bubble. This can be used to mask anything in the ContentNode to the same shape as the bubble. 33 | */ 34 | open lazy var maskLayer: CAShapeLayer = CAShapeLayer() 35 | 36 | /** Bounds of the bubble*/ 37 | open var calculatedBounds = CGRect.zero 38 | 39 | // MARK: Initialisers 40 | public init() {} 41 | 42 | // MARK: Class methods 43 | /** 44 | Sizes the layer accordingly. This function should **always** be thread safe. 45 | -parameter bounds: The bounds of the content 46 | */ 47 | open func sizeToBounds(_ bounds: CGRect) { 48 | self.calculatedBounds = bounds 49 | } 50 | 51 | 52 | /** 53 | This function should be called on the main thread. It makes creates the layer with the calculated values from *sizeToBounds* 54 | */ 55 | open func createLayer() { 56 | self.layer.shouldRasterize = true 57 | self.layer.rasterizationScale = UIScreen.main.scale 58 | 59 | self.maskLayer.shouldRasterize = true 60 | self.maskLayer.rasterizationScale = UIScreen.main.scale 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/Bubbles/DefaultBubble.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | //MARK: DefaultBubble class 15 | /** 16 | Default bubble class is standard with our message configuration. It has three rounded corners and one square corner closest to the avatar. 17 | */ 18 | open class DefaultBubble: Bubble { 19 | 20 | //MARK: Public Variables 21 | 22 | /** Radius of the corners for the bubble. When this is set, you will need to call setNeedsLayout on your message for changes to take effect if the bubble has already been drawn*/ 23 | open var radius : CGFloat = 16 24 | /** Should be less or equal to the *radius* property. When this is set, you will need to call setNeedsLayout on your message for changes to take effect if the bubble has already been drawn*/ 25 | open var borderWidth : CGFloat = 0 //TODO: 26 | /** The color of the border around the bubble. When this is set, you will need to call setNeedsLayout on your message for changes to take effect if the bubble has already been drawn*/ 27 | open var bubbleBorderColor : UIColor = UIColor.clear 28 | /** Path used to cutout the bubble*/ 29 | open fileprivate(set) var path: CGMutablePath = CGMutablePath() 30 | 31 | // MARK: Initialisers 32 | 33 | /** 34 | Initialiser class. 35 | */ 36 | public override init() { 37 | super.init() 38 | } 39 | 40 | // MARK: Class methods 41 | 42 | /** 43 | Overriding sizeToBounds from super class 44 | -parameter bounds: The bounds of the content 45 | */ 46 | open override func sizeToBounds(_ bounds: CGRect) { 47 | super.sizeToBounds(bounds) 48 | 49 | var rect = CGRect.zero 50 | var radius2: CGFloat = 0 51 | 52 | if bounds.width < 2*radius || bounds.height < 2*radius { //if the rect calculation yeilds a negative result 53 | let newRadiusW = bounds.width/2 54 | let newRadiusH = bounds.height/2 55 | 56 | let newRadius = newRadiusW>newRadiusH ? newRadiusH : newRadiusW 57 | 58 | rect = CGRect(x: newRadius, y: newRadius, width: bounds.width - 2*newRadius, height: bounds.height - 2*newRadius) 59 | radius2 = newRadius - borderWidth / 2 60 | } else { 61 | rect = CGRect(x: radius, y: radius, width: bounds.width - 2*radius, height: bounds.height - 2*radius) 62 | radius2 = radius - borderWidth / 2 63 | } 64 | 65 | path = CGMutablePath() 66 | 67 | path.addArc(center: CGPoint(x: rect.maxX, y: rect.minY), radius: radius2, startAngle: CGFloat(-Double.pi/2), endAngle: 0, clockwise: false) 68 | path.addLine(to: CGPoint(x: rect.maxX + radius2, y: rect.maxY + radius2)) 69 | path.addArc(center: CGPoint(x: rect.minX, y: rect.maxY), radius: radius2, startAngle: CGFloat(Double.pi/2), endAngle: CGFloat(Double.pi), clockwise: false) 70 | path.addArc(center: CGPoint(x: rect.minX, y: rect.minY), radius: radius2, startAngle: CGFloat(Double.pi), endAngle: CGFloat(-Double.pi/2), clockwise: false) 71 | 72 | //CGPathAddArc(path, nil, rect.maxX, rect.minY, radius2, CGFloat(-M_PI_2), 0, false) 73 | //CGPathAddLineToPoint(path, nil, rect.maxX + radius2, rect.maxY + radius2) 74 | //CGPathAddArc(path, nil, rect.minX, rect.maxY, radius2, CGFloat(M_PI_2), CGFloat(M_PI), false) 75 | //CGPathAddArc(path, nil, rect.minX, rect.minY, radius2, CGFloat(M_PI), CGFloat(-M_PI_2), false) 76 | path.closeSubpath() 77 | } 78 | 79 | /** 80 | Overriding createLayer from super class 81 | */ 82 | open override func createLayer() { 83 | super.createLayer() 84 | 85 | CATransaction.begin() 86 | CATransaction.setDisableActions(true) 87 | self.layer.path = path 88 | self.layer.fillColor = self.bubbleColor.cgColor 89 | self.layer.strokeColor = self.bubbleBorderColor.cgColor 90 | self.layer.lineWidth = self.borderWidth 91 | self.layer.position = CGPoint.zero 92 | 93 | self.maskLayer.fillColor = UIColor.black.cgColor 94 | self.maskLayer.path = path 95 | self.maskLayer.position = CGPoint.zero 96 | CATransaction.commit() 97 | 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/Bubbles/ImageBubble.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | //MARK: ImageBubble class 15 | /** 16 | Image as background for messages in NMessenger 17 | Uses 9-Patch logic to resize image to appropriate size. 18 | */ 19 | open class ImageBubble : Bubble { 20 | 21 | // MARK: Public variables 22 | /** Image 9-Patch used for bubble. When this is set, you will need to call setNeedsLayout on your message for changes to take effect if the bubble has already been drawn*/ 23 | open var bubbleImage: UIImage? 24 | /** Image 9-Patch cut insets. When this is set, you will need to call setNeedsLayout on your message for changes to take effect if the bubble has already been drawn*/ 25 | open var cutInsets: UIEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0) 26 | 27 | // MARK: Initialisers 28 | 29 | /** 30 | Initialiser class. 31 | Sets hasLayerMask to true 32 | */ 33 | public override init() { 34 | super.init() 35 | self.hasLayerMask = true 36 | } 37 | 38 | // MARK: Class methods 39 | 40 | /** 41 | Overriding sizeToBounds from super class 42 | -parameter bounds: The bounds of the content 43 | */ 44 | open override func sizeToBounds(_ bounds: CGRect) { 45 | super.sizeToBounds(bounds) 46 | 47 | } 48 | 49 | /** 50 | Overriding createLayer from super class 51 | */ 52 | open override func createLayer() { 53 | super.createLayer() 54 | 55 | if let image = bubbleImage { 56 | UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale); 57 | let context = UIGraphicsGetCurrentContext(); 58 | self.bubbleColor.setFill() 59 | context?.translateBy(x: 0, y: image.size.height); 60 | context?.scaleBy(x: 1.0, y: -1.0); 61 | context?.clip(to: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height), mask: image.cgImage!); 62 | context?.fill(CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)); 63 | 64 | var coloredImg = UIGraphicsGetImageFromCurrentImageContext(); 65 | 66 | UIGraphicsEndImageContext(); 67 | 68 | 69 | let insets = self.cutInsets 70 | coloredImg = coloredImg?.resizableImage(withCapInsets: insets, resizingMode: .stretch) 71 | 72 | self.layer.contents = coloredImg?.cgImage 73 | self.layer.position = CGPoint.zero 74 | self.layer.frame = CGRect(x: 0, y: 0, width: self.calculatedBounds.width, height: self.calculatedBounds.height) 75 | self.layer.contentsCenter = CGRect(x: insets.left/(coloredImg?.size.width)!, 76 | y: insets.top/(coloredImg?.size.height)!, 77 | width: 1.0/(coloredImg?.size.width)!, 78 | height: 1.0/(coloredImg?.size.height)!); 79 | 80 | self.maskLayer.contents = coloredImg?.cgImage 81 | self.maskLayer.position = CGPoint.zero 82 | self.maskLayer.frame = CGRect(x: 0, y: 0, width: self.calculatedBounds.width, height: self.calculatedBounds.height) 83 | self.maskLayer.contentsCenter = CGRect(x: insets.left/(coloredImg?.size.width)!, 84 | y: insets.top/(coloredImg?.size.height)!, 85 | width: 1.0/(coloredImg?.size.width)!, 86 | height: 1.0/(coloredImg?.size.height)!); 87 | } 88 | } 89 | 90 | 91 | } 92 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/Bubbles/SimpleBubble.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | //MARK: SimpleBubble class 15 | /** 16 | Simple bubble with no layer effects which is the bounds of the content it holds 17 | */ 18 | open class SimpleBubble: Bubble { 19 | 20 | //MARK: Public Variables 21 | /** The color of the border around the bubble. When this is set, you will need to call setNeedsLayout on your message for changes to take effect if the bubble has already been drawn*/ 22 | open var bubbleBorderColor : UIColor = UIColor.clear 23 | /** Path used to cutout the bubble*/ 24 | open fileprivate(set) var path: CGMutablePath = CGMutablePath() 25 | 26 | public override init() { 27 | super.init() 28 | } 29 | 30 | // MARK: Class methods 31 | 32 | /** 33 | Overriding sizeToBounds from super class 34 | -parameter bounds: The bounds of the content 35 | */ 36 | open override func sizeToBounds(_ bounds: CGRect) { 37 | super.sizeToBounds(bounds) 38 | 39 | let rect = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height) 40 | 41 | path = CGMutablePath() 42 | 43 | path.move(to: CGPoint(x: rect.minX, y: rect.minY)) 44 | path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY)) 45 | path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) 46 | path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY)) 47 | 48 | // CGPathMoveToPoint(path, nil, rect.minX, rect.minY) 49 | // CGPathAddLineToPoint(path, nil, rect.maxX, rect.minY) 50 | // CGPathAddLineToPoint(path, nil, rect.maxX, rect.maxY) 51 | // CGPathAddLineToPoint(path, nil, rect.minX, rect.maxY) 52 | 53 | path.closeSubpath() 54 | } 55 | 56 | /** 57 | Overriding createLayer from super class 58 | */ 59 | open override func createLayer() { 60 | super.createLayer() 61 | 62 | CATransaction.begin() 63 | CATransaction.setDisableActions(true) 64 | self.layer.path = path 65 | self.layer.fillColor = self.bubbleColor.cgColor 66 | self.layer.strokeColor = self.bubbleBorderColor.cgColor 67 | self.layer.position = CGPoint.zero 68 | 69 | self.maskLayer.fillColor = UIColor.black.cgColor 70 | self.maskLayer.path = path 71 | self.maskLayer.position = CGPoint.zero 72 | CATransaction.commit() 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/Bubbles/StackedBubble.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | //MARK: StackedBubble class 15 | /** 16 | Bubble when stacked for succeeding messages. The two outmost corners are rounded. 17 | */ 18 | open class StackedBubble: Bubble { 19 | 20 | //MARK: Public Variables 21 | 22 | /** Radius of the corners for the bubble. When this is set, you will need to call setNeedsLayout on your message for changes to take effect if the bubble has already been drawn*/ 23 | open var radius : CGFloat = 16 24 | /** Should be less or equal to the *radius* property. When this is set, you will need to call setNeedsLayout on your message for changes to take effect if the bubble has already been drawn*/ 25 | open var borderWidth : CGFloat = 0 26 | /** The color of the border around the bubble. When this is set, you will need to call setNeedsLayout on your message for changes to take effect if the bubble has already been drawn*/ 27 | open var bubbleBorderColor : UIColor = UIColor.clear 28 | /** Path used to cutout the bubble*/ 29 | open fileprivate(set) var path: CGMutablePath = CGMutablePath() 30 | 31 | public override init() { 32 | super.init() 33 | } 34 | 35 | // MARK: Class methods 36 | 37 | /** 38 | Overriding sizeToBounds from super class 39 | -parameter bounds: The bounds of the content 40 | */ 41 | open override func sizeToBounds(_ bounds: CGRect) { 42 | super.sizeToBounds(bounds) 43 | var rect = CGRect.zero 44 | var radius2: CGFloat = 0 45 | 46 | if bounds.width < 2*radius || bounds.height < 2*radius { //if the rect calculation yeilds a negative result 47 | let newRadiusW = bounds.width/2 48 | let newRadiusH = bounds.height/2 49 | 50 | let newRadius = newRadiusW>newRadiusH ? newRadiusH : newRadiusW 51 | 52 | rect = CGRect(x: newRadius, y: newRadius, width: bounds.width - 2*newRadius, height: bounds.height - 2*newRadius) 53 | radius2 = newRadius - borderWidth / 2 54 | } else { 55 | rect = CGRect(x: radius, y: radius, width: bounds.width - 2*radius, height: bounds.height - 2*radius) 56 | radius2 = radius - borderWidth / 2 57 | } 58 | 59 | 60 | self.path = CGMutablePath(); 61 | 62 | 63 | path.move(to: CGPoint(x: rect.minX, y: rect.minY - radius2)) 64 | path.addLine(to: CGPoint(x: rect.maxX + radius2, y: rect.minY - radius2)) 65 | path.addLine(to: CGPoint(x: rect.maxX + radius2, y: rect.maxY + radius2)) 66 | path.addArc(center: CGPoint(x: rect.minX, y: rect.maxY), radius: radius2, startAngle: CGFloat(Double.pi/2), endAngle: CGFloat(Double.pi), clockwise: false) 67 | path.addArc(center: CGPoint(x: rect.minX, y: rect.minY), radius: radius2, startAngle: CGFloat(Double.pi), endAngle: CGFloat(-(Double.pi/2)), clockwise: false) 68 | 69 | //CGPathMoveToPoint(path, nil, rect.minX, rect.minY - radius2) 70 | //CGPathAddLineToPoint(path, nil, rect.maxX + radius2, rect.minY - radius2) 71 | //CGPathAddLineToPoint(path, nil, rect.maxX + radius2, rect.maxY + radius2) 72 | //CGPathAddArc(path, nil, rect.minX, rect.maxY, radius2, CGFloat(M_PI_2), CGFloat(M_PI), false) 73 | //CGPathAddArc(path, nil, rect.minX, rect.minY, radius2, CGFloat(M_PI), CGFloat(-M_PI_2), false) 74 | path.closeSubpath() 75 | 76 | } 77 | 78 | /** 79 | Overriding createLayer from super class 80 | */ 81 | open override func createLayer() { 82 | super.createLayer() 83 | 84 | CATransaction.begin() 85 | CATransaction.setDisableActions(true) 86 | self.layer.path = path 87 | self.layer.fillColor = self.bubbleColor.cgColor 88 | self.layer.strokeColor = self.bubbleBorderColor.cgColor 89 | self.layer.lineWidth = self.borderWidth 90 | self.layer.position = CGPoint.zero 91 | 92 | self.maskLayer.fillColor = UIColor.black.cgColor 93 | self.maskLayer.path = path 94 | self.maskLayer.position = CGPoint.zero 95 | CATransaction.commit() 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/ContentNodes/CollectionViewContent/CustomContentCellNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import UIKit 12 | import AsyncDisplayKit 13 | 14 | //MARK: CustomContentCellNode 15 | /** 16 | CustomContentCellNode class for N Messenger. 17 | Define the cell for CollectionViewContentNode 18 | */ 19 | open class CustomContentCellNode: ASCellNode { 20 | 21 | // MARK: Public Variables 22 | /** ASDisplayNode as the content of the cell*/ 23 | open var customContent:ASDisplayNode = ASDisplayNode() 24 | 25 | // MARK: Initialisers 26 | 27 | /** 28 | Initialiser for the cell. 29 | - parameter withCustomNode: Must be ASDisplayNode. Sets content for the cell. 30 | */ 31 | public init(withCustomNode node:ASDisplayNode) 32 | { 33 | super.init() 34 | self.customContent = node 35 | self.addSubnode(self.customContent) 36 | } 37 | 38 | // MARK: Override AsycDisaplyKit Methods 39 | 40 | /** 41 | Overriding layoutSpecThatFits to specifiy relatiohsips between elements in the cell 42 | */ 43 | override open func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { 44 | let customContentSpec = ASAbsoluteLayoutSpec() 45 | customContentSpec.sizing = .sizeToFit 46 | customContentSpec.children = [customContent] 47 | return customContentSpec 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/ContentNodes/ContentNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import AsyncDisplayKit 13 | 14 | //MARK: ContentNode 15 | /** 16 | Content node class for NMessenger. 17 | Define the content a a MessageNode or a MessageGroup 18 | */ 19 | open class ContentNode: ASDisplayNode { 20 | 21 | // MARK: Public Parameters 22 | /** Bubble that defines the background for the message*/ 23 | open var backgroundBubble: Bubble? 24 | /** UIViewController that holds the cell. Allows the cell the present View Controllers. Generally used for UIMenu or UIAlert Options*/ 25 | open weak var currentViewController: UIViewController? 26 | /** MessageConfigurationProtocol hold common definition for all messages. Defaults to **StandardMessageConfiguration***/ 27 | open var bubbleConfiguration : BubbleConfigurationProtocol = StandardBubbleConfiguration() { 28 | didSet { 29 | self.updateBubbleConfig(self.bubbleConfiguration) 30 | } 31 | } 32 | /** Bool if the cell is an incoming or out going message. 33 | Set backgroundBubble.bubbleColor when value is changed 34 | */ 35 | open var isIncomingMessage = true { 36 | didSet { 37 | self.backgroundBubble?.bubbleColor = isIncomingMessage ? bubbleConfiguration.getIncomingColor() : bubbleConfiguration.getOutgoingColor() 38 | 39 | self.setNeedsLayout() 40 | } 41 | } 42 | 43 | // MARK: Initialisers 44 | /** 45 | Overriding init to initialise the node 46 | */ 47 | public init(bubbleConfiguration: BubbleConfigurationProtocol? = nil) { 48 | if let bubbleConfiguration = bubbleConfiguration { 49 | self.bubbleConfiguration = bubbleConfiguration 50 | } 51 | super.init() 52 | //make sure the bubble is set correctly 53 | self.updateBubbleConfig(self.bubbleConfiguration) 54 | } 55 | 56 | //MARK: Node Lifecycle 57 | /** 58 | Overriding didLoad and calling helper method addSublayers 59 | */ 60 | override open func didLoad() { 61 | super.didLoad() 62 | self.addSublayers() 63 | } 64 | 65 | //MARK: Node Lifecycle helper methods 66 | 67 | /** Updates the bubble config by setting all necessary properties (background bubble, bubble color, layout) 68 | - parameter newValue: the new BubbleConfigurationProtocol 69 | */ 70 | open func updateBubbleConfig(_ newValue: BubbleConfigurationProtocol) { 71 | self.backgroundBubble = self.bubbleConfiguration.getBubble() 72 | 73 | self.backgroundBubble?.bubbleColor = isIncomingMessage ? bubbleConfiguration.getIncomingColor() : bubbleConfiguration.getOutgoingColor() 74 | 75 | self.setNeedsLayout() 76 | } 77 | 78 | /** 79 | Called during the initializer and makes sure layers are added on the main thread 80 | */ 81 | open func addSublayers() { 82 | if let backgroundBubble = self.backgroundBubble { 83 | //make sure the layer is at the bottom of the node 84 | backgroundBubble.layer.removeFromSuperlayer() 85 | backgroundBubble.maskLayer.removeFromSuperlayer() 86 | 87 | self.layer.insertSublayer(backgroundBubble.layer, at: 0) 88 | 89 | //If there is a layer mask, add it 90 | if backgroundBubble.hasLayerMask { 91 | self.layer.insertSublayer(backgroundBubble.maskLayer, below: backgroundBubble.layer) 92 | self.layer.mask = backgroundBubble.maskLayer 93 | } 94 | } 95 | } 96 | 97 | 98 | //MARK: Override AsycDisaplyKit Methods 99 | 100 | /** 101 | Draws the content in the bubble. This is called on a background thread. 102 | */ 103 | open func drawRect(_ bounds: CGRect, withParameters parameters: NSObjectProtocol!, 104 | isCancelled isCancelledBlock: asdisplaynode_iscancelled_block_t, isRasterizing: Bool) { 105 | self.isOpaque = false 106 | if !isRasterizing { 107 | self.calculateLayerPropertiesThatFit(bounds) 108 | 109 | //call the main queue 110 | DispatchQueue.main.async { 111 | self.layoutLayers() 112 | } 113 | } 114 | } 115 | 116 | 117 | //MARK: Override AsycDisaplyKit helper methods 118 | 119 | /** 120 | Called through the draw rect function. This should be used to create a background layer off the main thread. This layer should be added in layout. 121 | - parameter bounds: Must be CGRect 122 | */ 123 | open func calculateLayerPropertiesThatFit(_ bounds: CGRect) { 124 | if let backgroundBubble = self.backgroundBubble { 125 | backgroundBubble.sizeToBounds(bounds) 126 | } 127 | } 128 | 129 | /** 130 | Called on the main thread 131 | */ 132 | open func layoutLayers() { 133 | if let backgroundBubble = self.backgroundBubble { 134 | backgroundBubble.createLayer() 135 | 136 | //TODO: this is slightly hacky, will need to rethink 137 | if isIncomingMessage { 138 | CATransaction.begin() 139 | CATransaction.setDisableActions(true) 140 | backgroundBubble.layer.transform = CATransform3DTranslate(CATransform3DMakeScale(-1, 1, 1), -backgroundBubble.calculatedBounds.width, 0, 0) 141 | backgroundBubble.maskLayer.transform = CATransform3DTranslate(CATransform3DMakeScale(-1, 1, 1), -backgroundBubble.calculatedBounds.width, 0, 0) 142 | CATransaction.commit() 143 | } 144 | } 145 | } 146 | 147 | /** 148 | Calls closer after a time delay 149 | - parameter delay: Must be Double. 150 | - parameter closure: Must be an ()->() 151 | */ 152 | open func delay(_ delay: Double, closure: @escaping ()->()) { 153 | DispatchQueue.main.asyncAfter( 154 | deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), 155 | execute: closure 156 | ) 157 | } 158 | 159 | 160 | //MARK: UITapGestureRecognizer Selector 161 | 162 | /** 163 | Selector to handle long press on message and show custom menu 164 | - parameter recognizer: Must be UITapGestureRecognizer 165 | */ 166 | open func messageNodeLongPressSelector(_ recognizer: UITapGestureRecognizer) { 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/ContentNodes/CustomContentContent/CustomContentNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import UIKit 12 | import AsyncDisplayKit 13 | 14 | //MARK: CustomContentMessageNode 15 | /** 16 | Custom View Message class for NMessenger. Extends ContentNode. 17 | Defines content that is a custom. Content can be a view or a node. 18 | */ 19 | open class CustomContentNode: ContentNode { 20 | 21 | // MARK: Public Variables 22 | /** Insets for the node */ 23 | open var insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) { 24 | didSet { 25 | setNeedsLayout() 26 | } 27 | } 28 | /**Should the bubble be masked or not*/ 29 | open var maskedBubble = true { 30 | didSet { 31 | self.updateBubbleConfig(self.bubbleConfiguration) 32 | self.setNeedsLayout() 33 | } 34 | } 35 | 36 | // MARK: Private Variables 37 | /** ASCollectionNode as the content of the cell*/ 38 | open fileprivate(set) var customContentMessageNode:ASDisplayNode = ASDisplayNode() 39 | /** UIView as the posiible view of the cell*/ 40 | fileprivate var customView:UIView? 41 | /** ASDisplayNode as the posiible view of the cell*/ 42 | fileprivate var customNode:ASDisplayNode? 43 | 44 | 45 | // MARK: Initialisers 46 | 47 | /** 48 | Initialiser for the cell. 49 | - parameter customView: Must be UIView. Sets view for the cell. 50 | Calls helper method to setup cell 51 | */ 52 | public init(withCustomView customView: UIView, bubbleConfiguration: BubbleConfigurationProtocol? = nil) { 53 | super.init(bubbleConfiguration: bubbleConfiguration) 54 | self.setupCustomView(customView) 55 | } 56 | 57 | /** 58 | Initialiser for the cell. 59 | - parameter customNode: Must be ASDisplayNode. Sets view for the cell. 60 | Calls helper method to setup cell 61 | */ 62 | public init(withCustomNode customNode: ASDisplayNode, bubbleConfiguration: BubbleConfigurationProtocol? = nil) { 63 | super.init(bubbleConfiguration: bubbleConfiguration) 64 | self.setupCustomNode(customNode) 65 | } 66 | 67 | // MARK: Initialiser helper methods 68 | /** Override updateBubbleConfig to set bubble mask */ 69 | open override func updateBubbleConfig(_ newValue: BubbleConfigurationProtocol) { 70 | var maskedBubbleConfig = newValue 71 | maskedBubbleConfig.isMasked = self.maskedBubble 72 | super.updateBubbleConfig(maskedBubbleConfig) 73 | } 74 | 75 | /** 76 | Adds subview to the content 77 | - parameter customView: Must be UIView. Sets view for the cell. 78 | */ 79 | fileprivate func setupCustomView(_ customView: UIView) 80 | { 81 | self.customView = customView 82 | DispatchQueue.main.async { 83 | self.customContentMessageNode.view.addSubview(customView) 84 | self.customContentMessageNode.style.preferredSize = customView.frame.size 85 | } 86 | self.addSubnode(customContentMessageNode) 87 | } 88 | 89 | /** 90 | Adds subnode to the content 91 | - parameter customNode: Must be ASDisplayNode. Sets view for the cell. 92 | */ 93 | fileprivate func setupCustomNode(_ customNode: ASDisplayNode) 94 | { 95 | self.isUserInteractionEnabled = true 96 | self.customNode = customNode 97 | self.customNode?.isUserInteractionEnabled = true 98 | customContentMessageNode = customNode 99 | self.addSubnode(customContentMessageNode) 100 | } 101 | 102 | // MARK: Override AsycDisaplyKit Methods 103 | 104 | /** 105 | Overriding layoutSpecThatFits to specifiy relatiohsips between elements in the cell 106 | */ 107 | override open func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { 108 | 109 | let width = constrainedSize.max.width 110 | 111 | let max = ASLayoutSize(width: ASDimension(unit: .points, value: width), height: ASDimension(unit: .fraction, value: 1)) 112 | 113 | customContentMessageNode.style.maxWidth = max.width 114 | customContentMessageNode.style.maxHeight = max.height 115 | 116 | let customContentSpec = ASAbsoluteLayoutSpec() 117 | customContentSpec.sizing = .sizeToFit 118 | customContentSpec.children = [customContentMessageNode] 119 | return ASInsetLayoutSpec(insets: insets, child: customContentSpec) 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/ContentNodes/ImageContent/ImageContentNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import UIKit 12 | import AsyncDisplayKit 13 | 14 | //MARK: ImageContentNode 15 | /** 16 | ImageContentNode for NMessenger. Extends ContentNode. 17 | Defines content that is an image. 18 | */ 19 | open class ImageContentNode: ContentNode { 20 | 21 | // MARK: Public Variables 22 | /** UIImage as the image of the cell*/ 23 | open var image: UIImage? { 24 | get { 25 | return imageMessageNode.image 26 | } set { 27 | imageMessageNode.image = newValue 28 | } 29 | } 30 | 31 | // MARK: Private Variables 32 | /** ASImageNode as the content of the cell*/ 33 | open fileprivate(set) var imageMessageNode:ASImageNode = ASImageNode() 34 | 35 | // MARK: Initialisers 36 | 37 | /** 38 | Initialiser for the cell. 39 | - parameter image: Must be UIImage. Sets image for cell. 40 | Calls helper method to setup cell 41 | */ 42 | public init(image: UIImage, bubbleConfiguration: BubbleConfigurationProtocol? = nil) { 43 | 44 | super.init(bubbleConfiguration: bubbleConfiguration) 45 | self.setupImageNode(image) 46 | } 47 | 48 | // MARK: Initialiser helper method 49 | /** Override updateBubbleConfig to set bubble mask */ 50 | open override func updateBubbleConfig(_ newValue: BubbleConfigurationProtocol) { 51 | var maskedBubbleConfig = newValue 52 | maskedBubbleConfig.isMasked = true 53 | super.updateBubbleConfig(maskedBubbleConfig) 54 | } 55 | 56 | /** 57 | Sets the image to be display in the cell. Clips and rounds the corners. 58 | - parameter image: Must be UIImage. Sets image for cell. 59 | */ 60 | fileprivate func setupImageNode(_ image: UIImage) 61 | { 62 | imageMessageNode.image = image 63 | imageMessageNode.clipsToBounds = true 64 | imageMessageNode.contentMode = UIViewContentMode.scaleAspectFill 65 | self.imageMessageNode.accessibilityIdentifier = "imageNode" 66 | self.imageMessageNode.isAccessibilityElement = true 67 | self.addSubnode(imageMessageNode) 68 | } 69 | 70 | 71 | // MARK: Override AsycDisaplyKit Methods 72 | 73 | /** 74 | Overriding layoutSpecThatFits to specifiy relatiohsips between elements in the cell 75 | */ 76 | override open func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { 77 | 78 | let width = constrainedSize.max.width 79 | self.imageMessageNode.style.width = ASDimension(unit: .points, value: width) 80 | self.imageMessageNode.style.height = ASDimension(unit: .points, value: width/4*3) 81 | let absLayout = ASAbsoluteLayoutSpec() 82 | absLayout.sizing = .sizeToFit 83 | absLayout.children = [self.imageMessageNode] 84 | return absLayout 85 | } 86 | 87 | // MARK: UILongPressGestureRecognizer Selector Methods 88 | 89 | /** 90 | Overriding canBecomeFirstResponder to make cell first responder 91 | */ 92 | override open func canBecomeFirstResponder() -> Bool { 93 | return true 94 | } 95 | 96 | /** 97 | Override method from superclass 98 | */ 99 | open override func messageNodeLongPressSelector(_ recognizer: UITapGestureRecognizer) { 100 | if recognizer.state == UIGestureRecognizerState.began { 101 | 102 | let touchLocation = recognizer.location(in: view) 103 | if self.imageMessageNode.frame.contains(touchLocation) { 104 | 105 | view.becomeFirstResponder() 106 | 107 | delay(0.1, closure: { 108 | let menuController = UIMenuController.shared 109 | menuController.menuItems = [UIMenuItem(title: "Copy", action: #selector(ImageContentNode.copySelector))] 110 | menuController.setTargetRect(self.imageMessageNode.frame, in: self.view) 111 | menuController.setMenuVisible(true, animated:true) 112 | }) 113 | } 114 | } 115 | } 116 | 117 | /** 118 | Copy Selector for UIMenuController 119 | Puts the node's image on UIPasteboard 120 | */ 121 | open func copySelector() { 122 | if let image = self.image { 123 | UIPasteboard.general.image = image 124 | } 125 | } 126 | 127 | } 128 | 129 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/ContentNodes/NetworkImageContent/NetworkImageContentNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import UIKit 12 | import AsyncDisplayKit 13 | 14 | //MARK: NetworkImageContentNode 15 | /** 16 | NetworkImageContentNode class for N Messenger. Extends MessageNode. 17 | Defines content that is a network image (An image that is provided via url). 18 | */ 19 | open class NetworkImageContentNode: ContentNode,ASNetworkImageNodeDelegate { 20 | 21 | // MARK: Public Variables 22 | /** NSURL for the image*/ 23 | open var url: URL? { 24 | get { 25 | return networkImageMessageNode.url 26 | } set { 27 | networkImageMessageNode.url = newValue 28 | } 29 | } 30 | 31 | // MARK: Private Variables 32 | /** ASNetworkImageNode as the content of the cell*/ 33 | open fileprivate(set) var networkImageMessageNode:ASNetworkImageNode = ASNetworkImageNode() 34 | 35 | 36 | // MARK: Initialisers 37 | /** 38 | Initialiser for the cell. 39 | - parameter imageURL: Must be String. Sets url for the image in the cell. 40 | Calls helper method to setup cell 41 | */ 42 | public init(imageURL: String, bubbleConfiguration: BubbleConfigurationProtocol? = nil) { 43 | super.init(bubbleConfiguration: bubbleConfiguration) 44 | self.setupNetworkImageNode(imageURL) 45 | } 46 | 47 | // MARK: Initialiser helper method 48 | /** Override updateBubbleConfig to set bubble mask */ 49 | open override func updateBubbleConfig(_ newValue: BubbleConfigurationProtocol) { 50 | var maskedBubbleConfig = newValue 51 | maskedBubbleConfig.isMasked = true 52 | super.updateBubbleConfig(maskedBubbleConfig) 53 | } 54 | 55 | /** 56 | Sets the URL to be display in the image. Clips and rounds the corners. 57 | - parameter imageURL: Must be String. Sets url for the image in the cell. 58 | */ 59 | fileprivate func setupNetworkImageNode(_ imageURL: String) 60 | { 61 | networkImageMessageNode.url = URL(string: imageURL) 62 | networkImageMessageNode.shouldCacheImage = true 63 | networkImageMessageNode.delegate = self 64 | self.addSubnode(networkImageMessageNode) 65 | } 66 | 67 | 68 | // MARK: Override AsycDisaplyKit Methods 69 | 70 | /** 71 | Overriding layoutSpecThatFits to specifiy relatiohsips between elements in the cell 72 | */ 73 | override open func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { 74 | 75 | let width = constrainedSize.max.width 76 | self.networkImageMessageNode.style.width = ASDimension(unit: .points, value: width) 77 | self.networkImageMessageNode.style.height = ASDimension(unit: .points, value: width/4*3) 78 | let absLayoutSpec = ASAbsoluteLayoutSpec() 79 | absLayoutSpec.sizing = .sizeToFit 80 | absLayoutSpec.children = [self.networkImageMessageNode] 81 | return absLayoutSpec 82 | } 83 | 84 | // MARK: ASNetworkImageNodeDelegate 85 | /** 86 | Overriding didLoadImage to layout the node once the image is loaded 87 | */ 88 | open func imageNode(_ imageNode: ASNetworkImageNode, didLoad image: UIImage) { 89 | self.setNeedsLayout() 90 | } 91 | 92 | // MARK: UILongPressGestureRecognizer Selector Methods 93 | 94 | /** 95 | Overriding canBecomeFirstResponder to make cell first responder 96 | */ 97 | override open func canBecomeFirstResponder() -> Bool { 98 | return true 99 | } 100 | 101 | /** 102 | Override method from superclass 103 | */ 104 | open override func messageNodeLongPressSelector(_ recognizer: UITapGestureRecognizer) { 105 | if recognizer.state == UIGestureRecognizerState.began { 106 | 107 | let touchLocation = recognizer.location(in: view) 108 | if self.networkImageMessageNode.frame.contains(touchLocation) { 109 | 110 | view.becomeFirstResponder() 111 | 112 | delay(0.1, closure: { 113 | let menuController = UIMenuController.shared 114 | menuController.menuItems = [UIMenuItem(title: "Copy", action: #selector(NetworkImageContentNode.copySelector))] 115 | menuController.setTargetRect(self.networkImageMessageNode.frame, in: self.view) 116 | menuController.setMenuVisible(true, animated:true) 117 | }) 118 | } 119 | } 120 | } 121 | 122 | /** 123 | Copy Selector for UIMenuController 124 | Puts the node's image on UIPasteboard 125 | */ 126 | open func copySelector() { 127 | if let image = self.networkImageMessageNode.image { 128 | UIPasteboard.general.image = image 129 | } 130 | } 131 | 132 | } 133 | 134 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/ContentNodes/TypingIndicatorContent/TypingIndicatorContent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | 12 | import UIKit 13 | import AsyncDisplayKit 14 | 15 | //MARK: TypingIndicatorContent 16 | /** 17 | TypingIndicatorContent class for NMessenger. Extends MessageNode. 18 | Defines content that is a loading indicator. 19 | */ 20 | open class TypingIndicatorContent: ContentNode { 21 | 22 | // MARK: Private Variables 23 | /** gifNode holds the animated typing indicator*/ 24 | open fileprivate(set) var gifNode = ASDisplayNode() 25 | 26 | override open func didLoad() { 27 | super.didLoad() 28 | addIndicators() 29 | 30 | } 31 | 32 | open override func visibilityDidChange(_ isVisible: Bool) { 33 | if(isVisible){ 34 | self.gifNode.removeFromSupernode(); 35 | addIndicators() 36 | }else{ 37 | self.gifNode.removeFromSupernode(); 38 | } 39 | } 40 | 41 | func addIndicators(){ 42 | 43 | let imageNames = ["loadBubble_0038_Layer-1", "loadBubble_0037_Layer-2", "loadBubble_0036_Layer-3", "loadBubble_0035_Layer-4", "loadBubble_0034_Layer-5", "loadBubble_0033_Layer-6", "loadBubble_0032_Layer-7", "loadBubble_0031_Layer-8", "loadBubble_0030_Layer-9", "loadBubble_0029_Layer-10", "loadBubble_0028_Layer-11", "loadBubble_0027_Layer-12", "loadBubble_0026_Layer-13", "loadBubble_0025_Layer-14", "loadBubble_0024_Layer-15", "loadBubble_0023_Layer-16", "loadBubble_0022_Layer-17", "loadBubble_0021_Layer-18", "loadBubble_0020_Layer-19", "loadBubble_0019_Layer-20", "loadBubble_0018_Layer-21", "loadBubble_0017_Layer-22", "loadBubble_0016_Layer-23", "loadBubble_0015_Layer-24", "loadBubble_0014_Layer-25", "loadBubble_0013_Layer-26", "loadBubble_0012_Layer-27", "loadBubble_0011_Layer-28", "loadBubble_0010_Layer-29", "loadBubble_0009_Layer-30", "loadBubble_0008_Layer-31", "loadBubble_0007_Layer-32", "loadBubble_0006_Layer-33", "loadBubble_0005_Layer-34", "loadBubble_0004_Layer-35", "loadBubble_0003_Layer-36", "loadBubble_0002_Layer-37", "loadBubble_0001_Layer-38", "loadBubble_0000_Layer-39"]; 44 | 45 | var images = [UIImage]() 46 | 47 | for imageName in imageNames { 48 | if let image = UIImage(named: imageName, in: Bundle(for: NMessengerViewController.self), compatibleWith: nil){ 49 | images.append(image) 50 | } 51 | } 52 | 53 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: images[0].size.width - 1, height: images[0].size.height - 1)) 54 | imageView.backgroundColor = UIColor.orange 55 | imageView.contentMode = UIViewContentMode.center 56 | imageView.clipsToBounds = true 57 | imageView.animationImages = images 58 | imageView.animationDuration = 1 59 | imageView.startAnimating() 60 | 61 | self.gifNode.view.addSubview(imageView) 62 | self.gifNode.style.preferredSize = imageView.frame.size 63 | self.addSubnode(self.gifNode) 64 | self.setNeedsLayout() 65 | } 66 | 67 | // MARK: Override AsycDisaplyKit Methods 68 | 69 | /** 70 | Overriding layoutSpecThatFits to specifiy relatiohsips between elements in the cell 71 | */ 72 | override open func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { 73 | let absLayoutSpec = ASAbsoluteLayoutSpec() 74 | absLayoutSpec.sizing = .sizeToFit 75 | absLayoutSpec.children = [self.gifNode] 76 | return absLayoutSpec 77 | } 78 | 79 | } 80 | 81 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/GeneralMessageCell/GeneralMessengerCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import UIKit 12 | import AsyncDisplayKit 13 | 14 | //MARK: GeneralMessengerCell 15 | /** 16 | GeneralMessengerCell class for NMessenger. Extends ASCellNode. 17 | Defines the base class for all messages in NMessenger. 18 | */ 19 | open class GeneralMessengerCell: ASCellNode { 20 | 21 | // MARK: Public Variables 22 | /** UIEdgeInsets for cell*/ 23 | open var cellPadding: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0) 24 | /** UIViewController that holds the cell. Allows the cell the present View Controllers*/ 25 | open weak var currentViewController: UIViewController? 26 | /** The current table in which this node resides*/ 27 | open weak var currentTableNode: ASTableNode? 28 | /** message incoming/outgoing */ 29 | open var isIncomingMessage:Bool = true 30 | 31 | // MARK: Initialisers 32 | /** 33 | Default Init 34 | Sets cellPadding to 0 and selectionStyle to None 35 | */ 36 | public override init() { 37 | super.init() 38 | selectionStyle = .none 39 | cellPadding = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0) 40 | } 41 | /** 42 | Initialiser for the cell 43 | - parameter cellPadding: Can be UIEdgeInsets. Sets padding for cell. 44 | - parameter currentViewController: Can be an UIViewController. Set current view controller holding the cell. 45 | */ 46 | public convenience init(cellPadding: UIEdgeInsets?, currentViewController: UIViewController?) { 47 | self.init() 48 | 49 | //set cell padding 50 | if let cellPadding = cellPadding { 51 | self.cellPadding = cellPadding 52 | } 53 | 54 | //set current view controller 55 | self.currentViewController = currentViewController 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/Indicators/HeadLoadingIndicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import AsyncDisplayKit 12 | import UIKit 13 | 14 | //MARK: HeadLoadingIndicator class 15 | /** 16 | Spinning loading indicator class. Used by the NMessenger prefetch. 17 | */ 18 | open class HeadLoadingIndicator: GeneralMessengerCell { 19 | /** Horizontal spacing between text and spinner. Defaults to 20.*/ 20 | open var contentPadding:CGFloat = 20 { 21 | didSet { 22 | self.setNeedsLayout() 23 | } 24 | } 25 | /** Animated spinner node*/ 26 | open let spinner = SpinnerNode() 27 | /** Loading text node*/ 28 | open let text = ASTextNode() 29 | /** Sets the loading attributed text for the spinner. Defaults to *"Loading..."* */ 30 | open var loadingAttributedText:NSAttributedString? { 31 | set { 32 | text.attributedText = newValue 33 | self.setNeedsLayout() 34 | } get { 35 | return text.attributedText 36 | } 37 | } 38 | 39 | public override init() { 40 | super.init() 41 | addSubnode(text) 42 | text.attributedText = NSAttributedString( 43 | string: "Loading…", 44 | attributes: [ 45 | NSFontAttributeName: UIFont.systemFont(ofSize: 12), 46 | NSForegroundColorAttributeName: UIColor.lightGray, 47 | NSKernAttributeName: -0.3 48 | ]) 49 | addSubnode(spinner) 50 | } 51 | 52 | override open func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { 53 | let stackLayout = ASStackLayoutSpec( 54 | direction: .horizontal, 55 | spacing: contentPadding, 56 | justifyContent: .center, 57 | alignItems: .center, 58 | children: [ text, spinner ]) 59 | let paddingLayout = ASInsetLayoutSpec(insets: cellPadding, child: stackLayout) 60 | return paddingLayout 61 | } 62 | } 63 | 64 | //MARK: SpinnerNode class 65 | /** 66 | Animated spinner. Used by HeadLoadingIndicator. Defaults to *preferredFrameSize.height=32* 67 | */ 68 | open class SpinnerNode: ASDisplayNode { 69 | open var activityIndicatorView: UIActivityIndicatorView { 70 | return view as! UIActivityIndicatorView 71 | } 72 | 73 | public override init() { 74 | super.init() 75 | self.setViewBlock({ UIActivityIndicatorView(activityIndicatorStyle: .gray) }) 76 | self.style.preferredSize.height = 32 77 | } 78 | 79 | override open func didLoad() { 80 | super.didLoad() 81 | activityIndicatorView.startAnimating() 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/Indicators/MessageSentIndicator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import AsyncDisplayKit 12 | import UIKit 13 | 14 | //MARK: HeadLoadingIndicator class 15 | /** 16 | Spinning loading indicator class. Used by the NMessenger prefetch. 17 | */ 18 | open class MessageSentIndicator: GeneralMessengerCell { 19 | /** Horizontal spacing between text and spinner. Defaults to 20.*/ 20 | open var contentPadding:CGFloat = 20 { 21 | didSet { 22 | self.setNeedsLayout() 23 | } 24 | } 25 | /** Loading text node*/ 26 | open let text = ASTextNode() 27 | /** Sets the loading attributed text for the spinner. Defaults to *"Loading..."* */ 28 | open var messageSentAttributedText:NSAttributedString? { 29 | set { 30 | text.attributedText = newValue 31 | self.setNeedsLayout() 32 | } get { 33 | return text.attributedText 34 | } 35 | } 36 | open var messageSentText: String? { 37 | set { 38 | text.attributedText = NSAttributedString( 39 | string: newValue != nil ? newValue! : "", 40 | attributes: [ 41 | NSFontAttributeName: UIFont.systemFont(ofSize: 14), 42 | NSForegroundColorAttributeName: UIColor.lightGray, 43 | NSKernAttributeName: -0.3 44 | ]) 45 | self.setNeedsLayout() 46 | } get { 47 | return text.attributedText?.string 48 | } 49 | } 50 | 51 | public override init() { 52 | super.init() 53 | addSubnode(text) 54 | } 55 | 56 | override open func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { 57 | let stackLayout = ASStackLayoutSpec( 58 | direction: .horizontal, 59 | spacing: contentPadding, 60 | justifyContent: .center, 61 | alignItems: .center, 62 | children: [ text ]) 63 | let paddingLayout = ASInsetLayoutSpec(insets: cellPadding, child: stackLayout) 64 | return paddingLayout 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /nMessenger/Source/MessageNodes/MessageCell/MessageCellProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessageCellProtocol.swift 3 | // nMessenger 4 | // 5 | // Created by Tainter, Aaron on 7/15/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** Protocol used by a message cell to handle various message related actions */ 12 | @objc public protocol MessageCellProtocol { 13 | /** 14 | Called when the avatar is clicked. Notifies the delegate of the message cell whose avatar is clicked 15 | */ 16 | @objc optional func avatarClicked(_ messageCell: GeneralMessengerCell) 17 | } 18 | -------------------------------------------------------------------------------- /nMessenger/Source/Messenger/Components/InputBarView/InputBarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import UIKit 12 | 13 | //MARK: InputBarView 14 | /** 15 | InputBarView class for NMessenger. 16 | Define the input bar for NMessenger. This is where the user would type text and open the camera or photo library. 17 | */ 18 | open class InputBarView: UIView, InputBarViewProtocol { 19 | 20 | //MARK: IBOutlets 21 | //@IBOutlets for input area view 22 | @IBOutlet open weak var textInputAreaView: UIView! 23 | //@IBOutlets for input view 24 | @IBOutlet open weak var textInputView: UITextView! 25 | 26 | //MARK: Public Parameters 27 | 28 | //MARK: Private Parameters 29 | //NMessengerViewController where to input is sent to 30 | open weak var controller:NMessengerViewController! 31 | 32 | // MARK: Initialisers 33 | /** 34 | Initialiser the view. 35 | - parameter controller: Must be NMessengerViewController. Sets controller for the view. 36 | Calls helper method to setup the view 37 | */ 38 | public required init() 39 | { 40 | super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0)) 41 | } 42 | 43 | public required init(controller:NMessengerViewController) { 44 | super.init(frame: CGRect.zero) 45 | self.controller = controller 46 | } 47 | /** 48 | Initialiser the view. 49 | - parameter controller: Must be NMessengerViewController. Sets controller for the view. 50 | - parameter controller: Must be CGRect. Sets frame for the view. 51 | Calls helper method to setup the view 52 | */ 53 | public required init(controller:NMessengerViewController,frame: CGRect) { 54 | super.init(frame: frame) 55 | self.controller = controller 56 | } 57 | /** 58 | - parameter aDecoder: Must be NSCoder 59 | Calls helper method to setup the view 60 | */ 61 | public required init?(coder aDecoder: NSCoder) { 62 | super.init(coder: aDecoder) 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /nMessenger/Source/Messenger/Components/InputBarView/InputBarViewProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | @objc public protocol InputBarViewProtocol 15 | { 16 | /* Superview of textInputView - can hold send button and/or attachment button*/ 17 | var textInputAreaView: UIView! {get set} 18 | /* UITextView where the user will input the text*/ 19 | var textInputView: UITextView! {get set} 20 | //NMessengerViewController where to input is sent to 21 | var controller:NMessengerViewController! {get set} 22 | } -------------------------------------------------------------------------------- /nMessenger/Source/Utilities/ModalAlertUtilities.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | //MARK: ModalAlertUtilities class 15 | /** 16 | Custom alerts for NMessenger 17 | */ 18 | open class ModalAlertUtilities { 19 | /** 20 | General error alert message 21 | - parameter controller: Must be UIViewController. Where to present to alert. 22 | */ 23 | class func postGenericErrorModal(fromController controller: UIViewController) { 24 | let alert = UIAlertController(title: "Error", message: "An error occurred. Please try again later", preferredStyle: .alert) 25 | let cancelAction = UIAlertAction(title: "Okay", style: .cancel) { (action) in 26 | alert.dismiss(animated: true, completion: nil) 27 | } 28 | alert.addAction(cancelAction) 29 | DispatchQueue.main.async(execute: { () -> Void in 30 | controller.present(alert, animated: true, completion: nil) 31 | }) 32 | } 33 | /** 34 | Camera permission alert message 35 | - parameter controller: Must be UIViewController. Where to present to alert. 36 | Alert tells user to go into setting to enable permission for both camera and photo library 37 | */ 38 | class func postGoToSettingToEnableCameraAndLibraryModal(fromController controller: UIViewController) 39 | { 40 | let alert = UIAlertController(title: "", message: "Allow access to your camera & photo library to start uploading photos with N1", preferredStyle: .alert) 41 | let cancelAction = UIAlertAction(title: "Cancel", style: .destructive) { (action) in 42 | alert.dismiss(animated: true, completion: nil) 43 | } 44 | let settingsAction = UIAlertAction(title: "Go to Settings", style: .default) { (alertAction) in 45 | if let appSettings = URL(string: UIApplicationOpenSettingsURLString) { 46 | UIApplication.shared.openURL(appSettings) 47 | } 48 | } 49 | alert.addAction(settingsAction) 50 | alert.addAction(cancelAction) 51 | 52 | DispatchQueue.main.async(execute: { () -> Void in 53 | controller.present(alert, animated: true, completion: nil) 54 | }) 55 | } 56 | /** 57 | Camera permission alert message 58 | - parameter controller: Must be UIViewController. Where to present to alert. 59 | Alert tells user to go into setting to enable permission for camera 60 | */ 61 | class func postGoToSettingToEnableCameraModal(fromController controller: UIViewController) 62 | { 63 | let alert = UIAlertController(title: "", message: "Allow access to your camera to start taking photos and uploading photos from your library with N1", preferredStyle: .alert) 64 | let cancelAction = UIAlertAction(title: "Cancel", style: .destructive) { (action) in 65 | alert.dismiss(animated: true, completion: nil) 66 | } 67 | let settingsAction = UIAlertAction(title: "Go to Settings", style: .default) { (alertAction) in 68 | if let appSettings = URL(string: UIApplicationOpenSettingsURLString) { 69 | UIApplication.shared.openURL(appSettings) 70 | } 71 | } 72 | alert.addAction(settingsAction) 73 | alert.addAction(cancelAction) 74 | 75 | DispatchQueue.main.async(execute: { () -> Void in 76 | controller.present(alert, animated: true, completion: nil) 77 | }) 78 | } 79 | /** 80 | Camera permission alert message 81 | - parameter controller: Must be UIViewController. Where to present to alert. 82 | Alert tells user to go into setting to enable permission for photo library 83 | */ 84 | class func postGoToSettingToEnableLibraryModal(fromController controller: UIViewController) 85 | { 86 | let alert = UIAlertController(title: "", message: "Allow access to your photo library to start uploading photos from you library with N1", preferredStyle: .alert) 87 | let cancelAction = UIAlertAction(title: "Cancel", style: .destructive) { (action) in 88 | alert.dismiss(animated: true, completion: nil) 89 | } 90 | let settingsAction = UIAlertAction(title: "Go to Settings", style: .default) { (alertAction) in 91 | if let appSettings = URL(string: UIApplicationOpenSettingsURLString) { 92 | UIApplication.shared.openURL(appSettings) 93 | } 94 | } 95 | alert.addAction(settingsAction) 96 | alert.addAction(cancelAction) 97 | 98 | DispatchQueue.main.async(execute: { () -> Void in 99 | controller.present(alert, animated: true, completion: nil) 100 | }) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /nMessenger/Source/Utilities/UIColor+N1Colors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | //MARK: UIColor extension 15 | /** 16 | Custom Colors for NMessenger 17 | */ 18 | extension UIColor { 19 | class func n1MidGreyColor() -> UIColor { 20 | return UIColor(red: 144.0 / 255.0, green: 164.0 / 255.0, blue: 174.0 / 255.0, alpha: 1) 21 | } 22 | 23 | class func n1DarkestGreyColor() -> UIColor { 24 | return UIColor(red: 38.0 / 255.0, green: 50.0 / 255.0, blue: 56.0 / 255.0, alpha: 1) 25 | } 26 | 27 | class func n1DarkGreyColor() -> UIColor { 28 | return UIColor(red: 96.0 / 255.0, green: 125.0 / 255.0, blue: 139.0 / 255.0, alpha: 1) 29 | } 30 | 31 | class func n1WhiteColor() -> UIColor { 32 | return UIColor(white: 255.0 / 255.0, alpha: 1) 33 | } 34 | 35 | class func n1BrandRedColor() -> UIColor { 36 | return UIColor(red: 255.0 / 255.0, green: 38.0 / 255.0, blue: 66.0 / 255.0, alpha: 1) 37 | } 38 | 39 | class func n1ActionBlueColor() -> UIColor { 40 | return UIColor(red: 74.0 / 255.0, green: 144.0 / 255.0, blue: 226.0 / 255.0, alpha: 1) 41 | } 42 | 43 | class func n1OverlayBorderColor() -> UIColor { 44 | return UIColor(red: 38.0 / 255.0, green: 49.0 / 255.0, blue: 56.0 / 255.0, alpha: 0.1) 45 | } 46 | 47 | class func n1AlmostWhiteColor() -> UIColor { 48 | return UIColor(red: 251.0 / 255.0, green: 252.0 / 255.0, blue: 253.0 / 255.0, alpha: 1) 49 | } 50 | 51 | class func n1DarkerGreyColor() -> UIColor { 52 | return UIColor(red: 69.0 / 255.0, green: 90.0 / 255.0, blue: 100.0 / 255.0, alpha: 1) 53 | } 54 | 55 | class func n1LightGreyColor() -> UIColor { 56 | return UIColor(red: 207.0 / 255.0, green: 216.0 / 255.0, blue: 220.0 / 255.0, alpha: 1) 57 | } 58 | 59 | class func n1LighterGreyColor() -> UIColor { 60 | return UIColor(red: 233.0 / 255.0, green: 239.0 / 255.0, blue: 242.0 / 255.0, alpha: 1) 61 | } 62 | 63 | class func n1PaleGreyColor() -> UIColor { 64 | return UIColor(red: 243.0 / 255.0, green: 247.0 / 255.0, blue: 249.0 / 255.0, alpha: 1) 65 | } 66 | 67 | class func n1Black50Color() -> UIColor { 68 | return UIColor(white: 0.0, alpha: 0.5) 69 | } 70 | 71 | class func colorFromRGB(_ rgbHexValue: UInt) -> UIColor { 72 | return UIColor( 73 | red: CGFloat((rgbHexValue & 0xFF0000) >> 16) / 255.0, 74 | green: CGFloat((rgbHexValue & 0x00FF00) >> 8) / 255.0, 75 | blue: CGFloat(rgbHexValue & 0x0000FF) / 255.0, 76 | alpha: CGFloat(1.0) 77 | ) 78 | } 79 | /** returns a random color */ 80 | class func randomColor() -> UIColor{ 81 | let red = CGFloat(drand48()) 82 | let green = CGFloat(drand48()) 83 | let blue = CGFloat(drand48()) 84 | return UIColor(red: red, green: green, blue: blue, alpha: 1.0) 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /nMessenger/Source/Utilities/UIFont+N1Fonts.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import Foundation 12 | import UIKit 13 | 14 | //MARK: UIFont extension 15 | /** 16 | Custom Fonts for NMessenger 17 | */ 18 | extension UIFont { 19 | class func n1H1Font() -> UIFont { 20 | return UIFont.systemFont(ofSize: 28.0, weight: UIFontWeightThin) 21 | } 22 | 23 | class func n1H2Font() -> UIFont { 24 | return UIFont.systemFont(ofSize: 20.0, weight: UIFontWeightLight) 25 | } 26 | 27 | class func n1H3Font() -> UIFont { 28 | return UIFont.systemFont(ofSize: 18.0, weight: UIFontWeightRegular) 29 | } 30 | 31 | class func n1LinkFont() -> UIFont { 32 | return UIFont.systemFont(ofSize: 16.0, weight: UIFontWeightSemibold) 33 | } 34 | 35 | class func n1TextStyleFont() -> UIFont { 36 | return UIFont.systemFont(ofSize: 16.0, weight: UIFontWeightRegular) 37 | } 38 | 39 | class func n1B1Font() -> UIFont { 40 | return UIFont.systemFont(ofSize: 16.0, weight: UIFontWeightRegular) 41 | } 42 | 43 | class func n1TextStyle3Font() -> UIFont { 44 | return UIFont.systemFont(ofSize: 12.0, weight: UIFontWeightBold) 45 | } 46 | 47 | class func n1TextStyle3MiniFont() -> UIFont { 48 | return UIFont.systemFont(ofSize: 8.0, weight: UIFontWeightBold) 49 | } 50 | 51 | class func n1TextStyle2Font() -> UIFont { 52 | return UIFont.systemFont(ofSize: 14.0, weight: UIFontWeightRegular) 53 | } 54 | 55 | class func n1B2Font() -> UIFont { 56 | return UIFont.systemFont(ofSize: 14.0, weight: UIFontWeightRegular) 57 | } 58 | 59 | class func n1TextStyle4Font() -> UIFont { 60 | return UIFont.systemFont(ofSize: 14.0, weight: UIFontWeightLight) 61 | } 62 | 63 | class func n1CaptionFont() -> UIFont { 64 | return UIFont.systemFont(ofSize: 12.0, weight: UIFontWeightMedium) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /nMessenger/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import UIKit 12 | 13 | class ViewController: UIViewController { 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | // Do any additional setup after loading the view, typically from a nib. 18 | } 19 | 20 | override func didReceiveMemoryWarning() { 21 | super.didReceiveMemoryWarning() 22 | // Dispose of any resources that can be recreated. 23 | } 24 | 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /nMessengerTests/Extensions/UIColorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColorTests.swift 3 | // nMessenger 4 | // 5 | // Created by Tainter, Aaron on 8/23/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import nMessenger 11 | 12 | class UIColorTests: XCTestCase { 13 | func testColorsForValues() { 14 | XCTAssertNotNil(UIColor.n1WhiteColor()) 15 | XCTAssertNotNil(UIColor.n1MidGreyColor()) 16 | XCTAssertNotNil(UIColor.n1BrandRedColor()) 17 | XCTAssertNotNil(UIColor.n1DarkGreyColor()) 18 | XCTAssertNotNil(UIColor.n1PaleGreyColor()) 19 | XCTAssertNotNil(UIColor.n1LightGreyColor()) 20 | XCTAssertNotNil(UIColor.n1ActionBlueColor()) 21 | XCTAssertNotNil(UIColor.n1DarkerGreyColor()) 22 | XCTAssertNotNil(UIColor.n1AlmostWhiteColor()) 23 | XCTAssertNotNil(UIColor.n1DarkestGreyColor()) 24 | XCTAssertNotNil(UIColor.n1LighterGreyColor()) 25 | XCTAssertNotNil(UIColor.n1OverlayBorderColor()) 26 | XCTAssertNotNil(UIColor.n1Black50Color()) 27 | XCTAssertNotNil(UIColor.randomColor()) 28 | XCTAssertNotNil(UIColor.colorFromRGB(0x000000)) 29 | } 30 | } -------------------------------------------------------------------------------- /nMessengerTests/Extensions/UIFontTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIFontTests.swift 3 | // nMessenger 4 | // 5 | // Created by Tainter, Aaron on 8/23/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import nMessenger 11 | 12 | class N1FontTests: XCTestCase { 13 | func testFontsForValues() { 14 | XCTAssertNotNil(UIFont.n1B1Font()) 15 | XCTAssertNotNil(UIFont.n1B2Font()) 16 | XCTAssertNotNil(UIFont.n1H1Font()) 17 | XCTAssertNotNil(UIFont.n1H2Font()) 18 | XCTAssertNotNil(UIFont.n1H3Font()) 19 | XCTAssertNotNil(UIFont.n1LinkFont()) 20 | XCTAssertNotNil(UIFont.n1CaptionFont()) 21 | XCTAssertNotNil(UIFont.n1TextStyleFont()) 22 | XCTAssertNotNil(UIFont.n1TextStyle3Font()) 23 | XCTAssertNotNil(UIFont.n1TextStyle3MiniFont()) 24 | XCTAssertNotNil(UIFont.n1TextStyle2Font()) 25 | XCTAssertNotNil(UIFont.n1TextStyle4Font()) 26 | } 27 | } -------------------------------------------------------------------------------- /nMessengerTests/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 | -------------------------------------------------------------------------------- /nMessengerTests/UI Components/Chat/BubbleConfigurationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BubbleConfigurationTests.swift 3 | // nMessenger 4 | // 5 | // Created by Tainter, Aaron on 8/23/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | 10 | import XCTest 11 | @testable import nMessenger 12 | 13 | class BubbleConfigurationTests: XCTestCase { 14 | 15 | func testStandardConfig() { 16 | let bc = StandardBubbleConfiguration() 17 | XCTAssertNotNil(bc.getBubble()) 18 | XCTAssertNotNil(bc.getSecondaryBubble()) 19 | XCTAssertNotNil(bc.getIncomingColor()) 20 | XCTAssertNotNil(bc.getOutgoingColor()) 21 | 22 | bc.isMasked = true 23 | XCTAssertNotNil(bc.getBubble()) 24 | XCTAssertNotNil(bc.getSecondaryBubble()) 25 | XCTAssertNotNil(bc.getIncomingColor()) 26 | XCTAssertNotNil(bc.getOutgoingColor()) 27 | 28 | XCTAssertTrue(bc.getBubble().hasLayerMask) 29 | XCTAssertTrue(bc.getSecondaryBubble().hasLayerMask) 30 | 31 | } 32 | 33 | func testSimpleBubbleConfig() { 34 | let bc = SimpleBubbleConfiguration() 35 | XCTAssertNotNil(bc.getBubble()) 36 | XCTAssertNotNil(bc.getSecondaryBubble()) 37 | XCTAssertNotNil(bc.getIncomingColor()) 38 | XCTAssertNotNil(bc.getOutgoingColor()) 39 | 40 | bc.isMasked = true 41 | XCTAssertNotNil(bc.getBubble()) 42 | XCTAssertNotNil(bc.getSecondaryBubble()) 43 | XCTAssertNotNil(bc.getIncomingColor()) 44 | XCTAssertNotNil(bc.getOutgoingColor()) 45 | 46 | XCTAssertTrue(bc.getBubble().hasLayerMask) 47 | XCTAssertTrue(bc.getSecondaryBubble().hasLayerMask) 48 | } 49 | 50 | func testImageBubbleConfiguration() { 51 | let bc = ImageBubbleConfiguration() 52 | XCTAssertNotNil(bc.getBubble()) 53 | XCTAssertNotNil(bc.getSecondaryBubble()) 54 | XCTAssertNotNil(bc.getIncomingColor()) 55 | XCTAssertNotNil(bc.getOutgoingColor()) 56 | 57 | bc.isMasked = true 58 | XCTAssertNotNil(bc.getBubble()) 59 | XCTAssertNotNil(bc.getSecondaryBubble()) 60 | XCTAssertNotNil(bc.getIncomingColor()) 61 | XCTAssertNotNil(bc.getOutgoingColor()) 62 | 63 | XCTAssertTrue(bc.getBubble().hasLayerMask) 64 | XCTAssertTrue(bc.getSecondaryBubble().hasLayerMask) 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /nMessengerTests/UI Components/Chat/BubbleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BubbleTests.swift 3 | // n1 4 | // 5 | // Created by Tainter, Aaron on 4/21/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import nMessenger 11 | 12 | class BubbleTests: XCTestCase { 13 | 14 | func testBubble() { 15 | let bubble = Bubble() 16 | XCTAssertNotNil(bubble.layer) 17 | XCTAssertNotNil(bubble.maskLayer) 18 | XCTAssertEqual(bubble.hasLayerMask, false) 19 | 20 | var rect = CGRect(x: 0,y: 0,width: 50,height: 50) 21 | bubble.sizeToBounds(rect) 22 | XCTAssertEqual(rect.minX, bubble.calculatedBounds.minX) 23 | XCTAssertEqual(rect.minY, bubble.calculatedBounds.minY) 24 | XCTAssertEqual(rect.width, bubble.calculatedBounds.width) 25 | XCTAssertEqual(rect.height, bubble.calculatedBounds.height) 26 | 27 | rect = CGRect.zero 28 | bubble.sizeToBounds(rect) 29 | XCTAssertEqual(rect.minX, bubble.calculatedBounds.minX) 30 | XCTAssertEqual(rect.minY, bubble.calculatedBounds.minY) 31 | XCTAssertEqual(rect.width, bubble.calculatedBounds.width) 32 | XCTAssertEqual(rect.height, bubble.calculatedBounds.height) 33 | 34 | bubble.createLayer() 35 | XCTAssertNotNil(bubble.layer) 36 | XCTAssertNotNil(bubble.maskLayer) 37 | } 38 | 39 | func testBubbleSimple() { 40 | let bubble = SimpleBubble() 41 | XCTAssertNotNil(bubble.layer) 42 | XCTAssertNotNil(bubble.maskLayer) 43 | XCTAssertEqual(bubble.hasLayerMask, false) 44 | 45 | var rect = CGRect(x: 0,y: 0,width: 50,height: 50) 46 | bubble.sizeToBounds(rect) 47 | XCTAssertEqual(rect.minX, bubble.calculatedBounds.minX) 48 | XCTAssertEqual(rect.minY, bubble.calculatedBounds.minY) 49 | XCTAssertEqual(rect.width, bubble.calculatedBounds.width) 50 | XCTAssertEqual(rect.height, bubble.calculatedBounds.height) 51 | 52 | rect = CGRect.zero 53 | bubble.sizeToBounds(rect) 54 | XCTAssertEqual(rect.minX, bubble.calculatedBounds.minX) 55 | XCTAssertEqual(rect.minY, bubble.calculatedBounds.minY) 56 | XCTAssertEqual(rect.width, bubble.calculatedBounds.width) 57 | XCTAssertEqual(rect.height, bubble.calculatedBounds.height) 58 | 59 | bubble.createLayer() 60 | XCTAssertNotNil(bubble.layer) 61 | XCTAssertNotNil(bubble.maskLayer) 62 | } 63 | 64 | func testBubbleDefault() { 65 | let bubble = DefaultBubble() 66 | XCTAssertNotNil(bubble.layer) 67 | XCTAssertNotNil(bubble.maskLayer) 68 | XCTAssertEqual(bubble.hasLayerMask, false) 69 | 70 | var rect = CGRect(x: 0,y: 0,width: 50,height: 50) 71 | bubble.sizeToBounds(rect) 72 | XCTAssertEqual(rect.minX, bubble.calculatedBounds.minX) 73 | XCTAssertEqual(rect.minY, bubble.calculatedBounds.minY) 74 | XCTAssertEqual(rect.width, bubble.calculatedBounds.width) 75 | XCTAssertEqual(rect.height, bubble.calculatedBounds.height) 76 | 77 | rect = CGRect.zero 78 | bubble.sizeToBounds(rect) 79 | XCTAssertEqual(rect.minX, bubble.calculatedBounds.minX) 80 | XCTAssertEqual(rect.minY, bubble.calculatedBounds.minY) 81 | XCTAssertEqual(rect.width, bubble.calculatedBounds.width) 82 | XCTAssertEqual(rect.height, bubble.calculatedBounds.height) 83 | 84 | bubble.createLayer() 85 | XCTAssertNotNil(bubble.layer) 86 | XCTAssertNotNil(bubble.maskLayer) 87 | } 88 | 89 | func testBubbleStacked() { 90 | let bubble = StackedBubble() 91 | XCTAssertNotNil(bubble.layer) 92 | XCTAssertNotNil(bubble.maskLayer) 93 | XCTAssertEqual(bubble.hasLayerMask, false) 94 | 95 | var rect = CGRect(x: 0,y: 0,width: 50,height: 50) 96 | bubble.sizeToBounds(rect) 97 | XCTAssertEqual(rect.minX, bubble.calculatedBounds.minX) 98 | XCTAssertEqual(rect.minY, bubble.calculatedBounds.minY) 99 | XCTAssertEqual(rect.width, bubble.calculatedBounds.width) 100 | XCTAssertEqual(rect.height, bubble.calculatedBounds.height) 101 | 102 | rect = CGRect.zero 103 | bubble.sizeToBounds(rect) 104 | XCTAssertEqual(rect.minX, bubble.calculatedBounds.minX) 105 | XCTAssertEqual(rect.minY, bubble.calculatedBounds.minY) 106 | XCTAssertEqual(rect.width, bubble.calculatedBounds.width) 107 | XCTAssertEqual(rect.height, bubble.calculatedBounds.height) 108 | 109 | bubble.createLayer() 110 | XCTAssertNotNil(bubble.layer) 111 | XCTAssertNotNil(bubble.maskLayer) 112 | } 113 | 114 | func testBubbleImage() { 115 | let bubble = ImageBubble() 116 | bubble.bubbleImage = UIImage(named: "MessageBubble", in: Bundle(for: NMessengerViewController.self), compatibleWith: nil) 117 | XCTAssertNotNil(bubble.layer) 118 | XCTAssertNotNil(bubble.maskLayer) 119 | XCTAssertEqual(bubble.hasLayerMask, true) 120 | 121 | let rect = CGRect(x: 0,y: 0,width: 50,height: 50) 122 | bubble.sizeToBounds(rect) 123 | XCTAssertEqual(rect.minX, bubble.calculatedBounds.minX) 124 | XCTAssertEqual(rect.minY, bubble.calculatedBounds.minY) 125 | XCTAssertEqual(rect.width, bubble.calculatedBounds.width) 126 | XCTAssertEqual(rect.height, bubble.calculatedBounds.height) 127 | 128 | bubble.createLayer() 129 | XCTAssertNotNil(bubble.layer) 130 | XCTAssertNotNil(bubble.maskLayer) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /nMessengerTests/UI Components/Chat/CollectionViewContentNodeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewContentNodeTests.swift 3 | // n1 4 | // 5 | // Created by Schechter, David on 4/28/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import AsyncDisplayKit 11 | @testable import nMessenger 12 | 13 | class CollectionViewContentNodeTests: XCTestCase { 14 | 15 | func testExample() { 16 | // This is an example of a functional test case. 17 | // Use XCTAssert and related functions to verify your tests produce the correct results. 18 | let tmpView = UIView(frame: CGRect(x: 0,y: 0,width: 100,height: 100)) 19 | let tempColectionNode = CollectionViewContentNode(withCustomViews: [tmpView], andNumberOfRows: 1) 20 | let tmpSize = CGSize(width: 100, height: 100) 21 | _ = tempColectionNode.layoutSpecThatFits(ASSizeRangeMake(tmpSize, tmpSize)) 22 | let tmpCollection = UICollectionView(frame: CGRect(x: 0,y: 0,width: 100,height: 100), collectionViewLayout: UICollectionViewFlowLayout()) 23 | _ = tempColectionNode.collectionView(tmpCollection, numberOfItemsInSection: 0) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /nMessengerTests/UI Components/Chat/HeadLoadingIndicatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HeadLoadingIndicatorTests.swift 3 | // n1 4 | // 5 | // Created by Tainter, Aaron on 4/26/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | import AsyncDisplayKit 12 | @testable import nMessenger 13 | 14 | class HeadLoadingIndicatorTests: XCTestCase { 15 | 16 | func testElement() { 17 | let loadingIndicator = HeadLoadingIndicator() 18 | XCTAssertNotNil(loadingIndicator.spinner) 19 | XCTAssertNotNil(loadingIndicator.text) 20 | _ = loadingIndicator.layoutSpecThatFits(ASSizeRange(min: CGSize(width: 50, height: 10), max: CGSize(width: 200, height: 20))) 21 | } 22 | 23 | func testSpinner() { 24 | let spinner = SpinnerNode() 25 | XCTAssertNotNil(spinner.activityIndicatorView) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /nMessengerTests/UI Components/Chat/MessageGroupTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessageGroupTests.swift 3 | // n1 4 | // 5 | // Created by Tainter, Aaron on 5/9/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import AsyncDisplayKit 11 | @testable import nMessenger 12 | 13 | class MessageGroupTests: XCTestCase { 14 | func testInitialize() { 15 | let messageGroup = MessageGroup() 16 | 17 | let textContent = TextContentNode(textMessageString: "blah") 18 | let newCell = MessageNode(content: textContent) 19 | messageGroup.addMessageToGroup(newCell, completion: nil) 20 | 21 | XCTAssertEqual(messageGroup.messages.count, 1) 22 | XCTAssertEqual(messageGroup.messages.last, newCell) 23 | 24 | messageGroup.removeMessageFromGroup(newCell, completion: nil) 25 | 26 | XCTAssertEqual(messageGroup.messages.count, 0) 27 | 28 | messageGroup.removeMessageFromGroup(newCell, completion: nil) 29 | 30 | XCTAssertEqual(messageGroup.messages.count, 0) 31 | 32 | messageGroup.addMessageToGroup(newCell, completion: nil) 33 | 34 | let textContent2 = TextContentNode(textMessageString: "blah") 35 | let newCell2 = MessageNode(content: textContent2) 36 | messageGroup.replaceMessage(newCell, withMessage: newCell2, completion: nil) 37 | 38 | XCTAssertEqual(messageGroup.messages.count, 1) 39 | XCTAssertEqual(messageGroup.messages.last, newCell2) 40 | 41 | //test avatar 42 | var avatarNode = ASDisplayNode() 43 | messageGroup.avatarNode = avatarNode 44 | 45 | XCTAssertNotNil(messageGroup.avatarNode) 46 | XCTAssertEqual(messageGroup.avatarNode, avatarNode) 47 | 48 | avatarNode = ASDisplayNode() 49 | messageGroup.avatarNode = avatarNode 50 | 51 | XCTAssertNotNil(messageGroup.avatarNode) 52 | XCTAssertEqual(messageGroup.avatarNode, avatarNode) 53 | 54 | //avatar insets 55 | let insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) 56 | messageGroup.avatarInsets = insets 57 | 58 | XCTAssertNotNil(messageGroup.avatarInsets) 59 | XCTAssertEqual(messageGroup.avatarInsets, insets) 60 | 61 | //message offset 62 | let messageOffset: CGFloat = 10 63 | messageGroup.messageOffset = messageOffset 64 | 65 | XCTAssertNotNil(messageGroup.messageOffset) 66 | XCTAssertEqual(messageGroup.messageOffset, messageOffset) 67 | 68 | //incoming message 69 | messageGroup.isIncomingMessage = false 70 | 71 | for cell in messageGroup.messages { 72 | if let message = cell as? MessageNode { 73 | XCTAssertNotNil(message.contentNode) 74 | XCTAssertFalse(message.contentNode!.isIncomingMessage) 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /nMessengerTests/UI Components/Chat/MessageNodeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MessageNodeTests.swift 3 | // nMessenger 4 | // 5 | // Created by Tainter, Aaron on 8/23/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import XCTest 12 | import AsyncDisplayKit 13 | @testable import nMessenger 14 | 15 | class MessageNodeTests: XCTestCase { 16 | func testInitialize() { 17 | let textContent = TextContentNode(textMessageString: "blah") 18 | let message = MessageNode(content: textContent) 19 | 20 | XCTAssertNotNil(message) 21 | XCTAssertNotNil(message.contentNode) 22 | XCTAssertEqual(message.contentNode, textContent) 23 | 24 | //reset content 25 | let newContent = TextContentNode(textMessageString: "blah2") 26 | message.contentNode = newContent 27 | 28 | XCTAssertNotNil(message) 29 | XCTAssertNotNil(message.contentNode) 30 | XCTAssertEqual(message.contentNode, newContent) 31 | 32 | //test header 33 | var headerNode = ASDisplayNode() 34 | message.headerNode = headerNode 35 | 36 | XCTAssertNotNil(message.headerNode) 37 | XCTAssertEqual(message.headerNode, headerNode) 38 | 39 | headerNode = ASDisplayNode() 40 | message.headerNode = headerNode 41 | 42 | XCTAssertNotNil(message.headerNode) 43 | XCTAssertEqual(message.headerNode, headerNode) 44 | 45 | //test footer 46 | var footerNode = ASDisplayNode() 47 | message.footerNode = footerNode 48 | 49 | XCTAssertNotNil(message.footerNode) 50 | XCTAssertEqual(message.footerNode, footerNode) 51 | 52 | footerNode = ASDisplayNode() 53 | message.footerNode = footerNode 54 | 55 | XCTAssertNotNil(message.footerNode) 56 | XCTAssertEqual(message.footerNode, footerNode) 57 | 58 | //test avatar 59 | var avatarNode = ASDisplayNode() 60 | message.avatarNode = avatarNode 61 | 62 | XCTAssertNotNil(message.avatarNode) 63 | XCTAssertEqual(message.avatarNode, avatarNode) 64 | 65 | avatarNode = ASDisplayNode() 66 | message.avatarNode = avatarNode 67 | 68 | XCTAssertNotNil(message.avatarNode) 69 | XCTAssertEqual(message.avatarNode, avatarNode) 70 | 71 | 72 | //avatar insets 73 | let insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) 74 | message.avatarInsets = insets 75 | 76 | XCTAssertNotNil(message.avatarInsets) 77 | XCTAssertEqual(message.avatarInsets, insets) 78 | 79 | 80 | //message offset 81 | let messageOffset: CGFloat = 10 82 | message.messageOffset = messageOffset 83 | 84 | XCTAssertNotNil(message.messageOffset) 85 | XCTAssertEqual(message.messageOffset, messageOffset) 86 | 87 | 88 | //header spacing 89 | let headerSpacing: CGFloat = 10 90 | message.headerSpacing = headerSpacing 91 | 92 | XCTAssertNotNil(message.headerSpacing) 93 | XCTAssertEqual(message.headerSpacing, headerSpacing) 94 | 95 | 96 | //footer spacing 97 | let footerSpacing: CGFloat = 10 98 | message.footerSpacing = footerSpacing 99 | 100 | XCTAssertNotNil(message.footerSpacing) 101 | XCTAssertEqual(message.footerSpacing, footerSpacing) 102 | 103 | 104 | //incoming message 105 | message.isIncomingMessage = false 106 | XCTAssertNotNil(message.contentNode) 107 | XCTAssertFalse(message.contentNode!.isIncomingMessage) 108 | 109 | } 110 | } -------------------------------------------------------------------------------- /nMessengerTests/UI Components/Chat/NetworkImageContentNodeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkImageContentNodeTests.swift 3 | // n1 4 | // 5 | // Created by Schechter, David on 4/27/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import AsyncDisplayKit 11 | @testable import nMessenger 12 | 13 | class NetworkImageContentNodeTests: XCTestCase { 14 | 15 | func testNetworkImage() { 16 | // This is an example of a functional test case. 17 | // Use XCTAssert and related functions to verify your tests produce the correct results. 18 | let testNetworkImage = NetworkImageContentNode(imageURL: "http://placehold.it/100x100") 19 | let tmpSize = CGSize(width: 100, height: 100) 20 | let tmpRecongnizer = UITapGestureRecognizer() 21 | _ = testNetworkImage.layoutSpecThatFits(ASSizeRangeMake(tmpSize, tmpSize)) 22 | testNetworkImage.messageNodeLongPressSelector(tmpRecongnizer) 23 | testNetworkImage.copySelector() 24 | _ = testNetworkImage.canBecomeFirstResponder() 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /nMessengerTests/UI Components/NMessengerVCTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NMessengerVCTests.swift 3 | // n1 4 | // 5 | // Created by Tainter, Aaron on 5/13/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import nMessenger 12 | 13 | class NMessengerVCTests: XCTestCase { 14 | 15 | func testVC() { 16 | //TODO: 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /nMessengerTests/nMessengerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import XCTest 12 | @testable import nMessenger 13 | 14 | class nMessengerTests: XCTestCase { 15 | 16 | override func setUp() { 17 | super.setUp() 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | override func tearDown() { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | super.tearDown() 24 | } 25 | 26 | func testExample() { 27 | // This is an example of a functional test case. 28 | // Use XCTAssert and related functions to verify your tests produce the correct results. 29 | } 30 | 31 | func testPerformanceExample() { 32 | // This is an example of a performance test case. 33 | self.measure { 34 | // Put the code you want to measure the time of here. 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /nMessengerUITests/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 | -------------------------------------------------------------------------------- /nMessengerUITests/OrientationUITest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OrientationUITest.swift 3 | // nMessenger 4 | // 5 | // Created by Tainter, Aaron on 9/15/16. 6 | // Copyright © 2016 Ebay Inc. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class OrientationUITest: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | 25 | XCUIDevice.shared().orientation = .portrait 26 | } 27 | 28 | override func tearDown() { 29 | // Put teardown code here. This method is called after the invocation of each test method in the class. 30 | super.tearDown() 31 | 32 | XCUIDevice.shared().orientation = .portrait 33 | } 34 | 35 | func testSPSCLaunchScreenLandscape() { 36 | 37 | XCUIDevice.shared().orientation = .landscapeLeft 38 | 39 | XCTAssert(UIInterfaceOrientationIsPortrait(UIApplication.shared.statusBarOrientation)) 40 | 41 | XCUIDevice.shared().orientation = .landscapeRight 42 | 43 | XCTAssert(UIInterfaceOrientationIsPortrait(UIApplication.shared.statusBarOrientation)) 44 | 45 | XCUIDevice.shared().orientation = .portrait 46 | 47 | XCTAssert(UIInterfaceOrientationIsPortrait(UIApplication.shared.statusBarOrientation)) 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /nMessengerUITests/nMessengerUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016 eBay Software Foundation 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | // 6 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | // 10 | 11 | import XCTest 12 | 13 | class nMessengerUITests: XCTestCase { 14 | 15 | override func setUp() { 16 | super.setUp() 17 | 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | 20 | // In UI tests it is usually best to stop immediately when a failure occurs. 21 | continueAfterFailure = false 22 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 23 | XCUIApplication().launch() 24 | 25 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 26 | } 27 | 28 | override func tearDown() { 29 | // Put teardown code here. This method is called after the invocation of each test method in the class. 30 | super.tearDown() 31 | } 32 | 33 | func testExample() { 34 | // Use recording to get started writing UI tests. 35 | // Use XCTAssert and related functions to verify your tests produce the correct results. 36 | } 37 | 38 | } 39 | --------------------------------------------------------------------------------