diff --git a/Assets/MessageKitAssets.bundle/Images/pause.png b/Assets/MessageKitAssets.bundle/Images/pause.png new file mode 100644 index 00000000..487a308f Binary files /dev/null and b/Assets/MessageKitAssets.bundle/Images/pause.png differ diff --git a/Assets/MessageKitAssets.bundle/Images/pause@2x.png b/Assets/MessageKitAssets.bundle/Images/pause@2x.png new file mode 100644 index 00000000..8f5a3c2f Binary files /dev/null and b/Assets/MessageKitAssets.bundle/Images/pause@2x.png differ diff --git a/Assets/MessageKitAssets.bundle/Images/pause@3x.png b/Assets/MessageKitAssets.bundle/Images/pause@3x.png new file mode 100644 index 00000000..a15cadda Binary files /dev/null and b/Assets/MessageKitAssets.bundle/Images/pause@3x.png differ diff --git a/Assets/MessageKitAssets.bundle/Images/play.png b/Assets/MessageKitAssets.bundle/Images/play.png new file mode 100644 index 00000000..9ae69327 Binary files /dev/null and b/Assets/MessageKitAssets.bundle/Images/play.png differ diff --git a/Assets/MessageKitAssets.bundle/Images/play@2x.png b/Assets/MessageKitAssets.bundle/Images/play@2x.png new file mode 100644 index 00000000..c769e68f Binary files /dev/null and b/Assets/MessageKitAssets.bundle/Images/play@2x.png differ diff --git a/Assets/MessageKitAssets.bundle/Images/play@3x.png b/Assets/MessageKitAssets.bundle/Images/play@3x.png new file mode 100644 index 00000000..53fb82a0 Binary files /dev/null and b/Assets/MessageKitAssets.bundle/Images/play@3x.png differ diff --git a/CHANGELOG.md b/CHANGELOG.md index d39ed49b..3125d26d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,56 @@ The changelog for `MessageKit`. Also see the [releases](https://github.com/MessageKit/MessageKit/releases) on GitHub. --------------------------------------- +## 3.0.0 + +### Dependency Changes + +- **Breaking Change** The dependency `MessageInputBar` was replaced with `InputBarAccessoryView`. As `MessageInputBar` was previously a fork this means no functionality has been lost but improvements and bug fixes will be present. `InputBarAccessoryView` has more of a following outside of `MessageKit` making its development faster than `MessageInputBar`. Maintaining two versions only increased the workload. You can find the changelog for `InputBarAccessoryView` [here](https://github.com/nathantannar4/InputBarAccessoryView/blob/master/CHANGELOG.md). + +### Changed + +- **Breaking Change** Deprecated the Sender struct in favor of the `SenderType` protocol. [#909](https://github.com/MessageKit/MessageKit/pull/909) by [@nathantannar4](https://github.com/nathantannar4) + +- **Breaking Change** Add support for audio messages [#892](https://github.com/MessageKit/MessageKit/pull/892) by [@moldovaniosif](https://github.com/moldovaniosif). Added new protocols `AudioControllerDelegate`, `AudioItem` a new cell `AudioMessageCell` and a new controller `BasicAudioController`. + +- **Breaking Change** Moved `handleTapGesture` method to `MessageCollectionViewCell` [#950](https://github.com/MessageKit/MessageKit/pull/950) by [@nathantannar4](https://github.com/nathantannar4) + +- **Breaking Change** Renamed function `layoutBottomLabel(with:)` to `layoutMessageBottomLabel(with:)` in `MessageContentCell` class. +[#920](https://github.com/MessageKit/MessageKit/pull/920) by [@maxxx777](https://github.com/maxxx777) + +### Added + +- **Breaking Change** Added `avatarLeadingTrailingPadding` as a property of `CellSizeCalculator` and `MessagesCollectionViewLayoutAttributes` to inset the `AvatarView` layout [#944](https://github.com/MessageKit/MessageKit/pull/944) by [@nathantannar4](https://github.com/nathantannar4) + +- **Breaking Change** Added `didTapBackground(in:)` function to `MessageCellDelegate` protocol. +[#922](https://github.com/MessageKit/MessageKit/pull/922) by [@kpennacchia](https://github.com/kpennacchia) + +- **Breaking Change** Added `didTapCellBottomLabel(in:)` function to `MessageCellDelegate` protocol. +[#920](https://github.com/MessageKit/MessageKit/pull/920) by [@maxxx777](https://github.com/maxxx777) + +- **Breaking Change** Added `cellBottomLabelAttributedText(for:, at:)` function to `MessagesDataSource` protocol. +[#920](https://github.com/MessageKit/MessageKit/pull/920) by [@maxxx777](https://github.com/maxxx777) + +- **Breaking Change** Added `cellBottomLabelHeight(for:, at:, in messagesCollectionView:)` function to `MessagesLayoutDelegate` protocol. +[#920](https://github.com/MessageKit/MessageKit/pull/920) by [@maxxx777](https://github.com/maxxx777) + +- Added `cellBottomLabel` to `MessageContentCell`. +[#920](https://github.com/MessageKit/MessageKit/pull/920) by [@maxxx777](https://github.com/maxxx777) + +- Added `layoutCellBottomLabel(with:)` function to `MessageContentCell` class. +[#920](https://github.com/MessageKit/MessageKit/pull/920) by [@maxxx777](https://github.com/maxxx777) + +- Added `setMessageIncomingCellBottomLabelAlignment(_:)` and `setMessageOutgoingCellBottomLabelAlignment(_:)` functions to `MessagesCollectionViewFlowLayout` class. +[#920](https://github.com/MessageKit/MessageKit/pull/920) by [@maxxx777](https://github.com/maxxx777) + +- Added `cellBottomLabelAlignment` and `cellBottomLabelSize` variables to `MessagesCollectionViewLayoutAttributes` class. +[#920](https://github.com/MessageKit/MessageKit/pull/920) by [@maxxx777](https://github.com/maxxx777) + +- Added `incomingCellBottomLabelAlignment` and `outgoingCellBottomLabelAlignment` variables to `MessageSizeCalculator` class. +[#920](https://github.com/MessageKit/MessageKit/pull/920) by [@maxxx777](https://github.com/maxxx777) + +- Added `cellBottomLabelSize(for:, at:)` and `cellBottomLabelAlignment(for:)` functions to `MessageSizeCalculator` class. +[#920](https://github.com/MessageKit/MessageKit/pull/920) by [@maxxx777](https://github.com/maxxx777) ## [2.0.0](https://github.com/MessageKit/MessageKit/releases/tag/2.0.0) diff --git a/Cartfile b/Cartfile index 00560f59..50814158 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "MessageKit/MessageInputBar" "0.4.0" +github "nathantannar4/InputBarAccessoryView" "4.2.1" diff --git a/Cartfile.resolved b/Cartfile.resolved index 80359d80..c12c3fca 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,3 @@ -github "MessageKit/MessageInputBar" "0.4.0" -github "Quick/Nimble" "v7.3.1" -github "Quick/Quick" "v1.3.2" +github "Quick/Nimble" "v7.3.4" +github "Quick/Quick" "v1.3.4" +github "nathantannar4/InputBarAccessoryView" "4.2.1" diff --git a/Example/.Podfile.un~ b/Example/.Podfile.un~ new file mode 100644 index 00000000..8e635e87 Binary files /dev/null and b/Example/.Podfile.un~ differ diff --git a/Example/ChatExample.xcodeproj/project.pbxproj b/Example/ChatExample.xcodeproj/project.pbxproj index 2b7dc8da..7be14b8c 100644 --- a/Example/ChatExample.xcodeproj/project.pbxproj +++ b/Example/ChatExample.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 383B9EB32172A1C4008AB91A /* MockUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383B9EB22172A1C4008AB91A /* MockUser.swift */; }; 385C2922211FF32E0010B4BA /* CustomCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 385C2920211FF32E0010B4BA /* CustomCell.swift */; }; 385C2923211FF32E0010B4BA /* TableViewCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 385C2921211FF32E0010B4BA /* TableViewCells.swift */; }; 385C2927211FF33B0010B4BA /* MockSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 385C2925211FF33A0010B4BA /* MockSocket.swift */; }; @@ -24,6 +25,9 @@ 385C2946211FF38F0010B4BA /* LaunchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 385C293F211FF38F0010B4BA /* LaunchViewController.swift */; }; 385C2947211FF38F0010B4BA /* BasicExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 385C2940211FF38F0010B4BA /* BasicExampleViewController.swift */; }; 385C2948211FF38F0010B4BA /* ChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 385C2941211FF38F0010B4BA /* ChatViewController.swift */; }; + 50739F9621C5090A008CA369 /* BasicAudioController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50739F9521C5090A008CA369 /* BasicAudioController.swift */; }; + 5074EF4E2163555900D82952 /* sound2.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5074EF4C2163555900D82952 /* sound2.m4a */; }; + 5074EF4F2163555900D82952 /* sound1.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5074EF4D2163555900D82952 /* sound1.m4a */; }; 882B5E811CF7D53600B6E160 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 882B5E781CF7D53600B6E160 /* AppDelegate.swift */; }; 882B5E821CF7D53600B6E160 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 882B5E791CF7D53600B6E160 /* Assets.xcassets */; }; 882B5E831CF7D53600B6E160 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 882B5E7A1CF7D53600B6E160 /* LaunchScreen.storyboard */; }; @@ -67,6 +71,7 @@ /* Begin PBXFileReference section */ 0364943D08CDBE656E6F6DF8 /* Pods-ChatExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChatExampleTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ChatExampleTests/Pods-ChatExampleTests.debug.xcconfig"; sourceTree = ""; }; 2AC6E3F5C11E39F57598DBE6 /* Pods_ChatExampleUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ChatExampleUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 383B9EB22172A1C4008AB91A /* MockUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUser.swift; sourceTree = ""; }; 385C2920211FF32E0010B4BA /* CustomCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomCell.swift; sourceTree = ""; }; 385C2921211FF32E0010B4BA /* TableViewCells.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewCells.swift; sourceTree = ""; }; 385C2925211FF33A0010B4BA /* MockSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockSocket.swift; sourceTree = ""; }; @@ -85,6 +90,9 @@ 385C2940211FF38F0010B4BA /* BasicExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicExampleViewController.swift; sourceTree = ""; }; 385C2941211FF38F0010B4BA /* ChatViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatViewController.swift; sourceTree = ""; }; 3B316705C4717C3B4C916D62 /* Pods_ChatExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ChatExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50739F9521C5090A008CA369 /* BasicAudioController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicAudioController.swift; sourceTree = ""; }; + 5074EF4C2163555900D82952 /* sound2.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = sound2.m4a; sourceTree = ""; }; + 5074EF4D2163555900D82952 /* sound1.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = sound1.m4a; sourceTree = ""; }; 56F0AC85B38034EC92CCBC7D /* Pods_ChatExampleTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ChatExampleTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 882B5E331CF7D4B900B6E160 /* ChatExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChatExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 882B5E491CF7D4B900B6E160 /* ChatExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChatExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -146,6 +154,7 @@ children = ( 385C2926211FF33B0010B4BA /* MockMessage.swift */, 385C2925211FF33A0010B4BA /* MockSocket.swift */, + 383B9EB22172A1C4008AB91A /* MockUser.swift */, ); path = Models; sourceTree = ""; @@ -190,6 +199,23 @@ path = "View Controllers"; sourceTree = ""; }; + 50739F9421C5075D008CA369 /* AudioController */ = { + isa = PBXGroup; + children = ( + 50739F9521C5090A008CA369 /* BasicAudioController.swift */, + ); + path = AudioController; + sourceTree = ""; + }; + 5074EF4B2163554900D82952 /* Sounds */ = { + isa = PBXGroup; + children = ( + 5074EF4D2163555900D82952 /* sound1.m4a */, + 5074EF4C2163555900D82952 /* sound2.m4a */, + ); + path = Sounds; + sourceTree = ""; + }; 7523553CCB4B87460CE4DED6 /* Pods */ = { isa = PBXGroup; children = ( @@ -229,11 +255,13 @@ isa = PBXGroup; children = ( 882B5E781CF7D53600B6E160 /* AppDelegate.swift */, + 50739F9421C5075D008CA369 /* AudioController */, 385C2949211FF3930010B4BA /* View Controllers */, 385C293A211FF3800010B4BA /* Data Generation */, 385C292E211FF3540010B4BA /* Layout */, 385C2929211FF33D0010B4BA /* Models */, 385C2924211FF3310010B4BA /* Views */, + 5074EF4B2163554900D82952 /* Sounds */, 882B5E791CF7D53600B6E160 /* Assets.xcassets */, 882B5E7F1CF7D53600B6E160 /* Info.plist */, 882B5E7A1CF7D53600B6E160 /* LaunchScreen.storyboard */, @@ -383,7 +411,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5074EF4F2163555900D82952 /* sound1.m4a in Resources */, 882B5E821CF7D53600B6E160 /* Assets.xcassets in Resources */, + 5074EF4E2163555900D82952 /* sound2.m4a in Resources */, 882B5E831CF7D53600B6E160 /* LaunchScreen.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -448,12 +478,12 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-ChatExample/Pods-ChatExample-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/MessageInputBar/MessageInputBar.framework", + "${BUILT_PRODUCTS_DIR}/InputBarAccessoryView/InputBarAccessoryView.framework", "${BUILT_PRODUCTS_DIR}/MessageKit/MessageKit.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MessageInputBar.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/InputBarAccessoryView.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MessageKit.framework", ); runOnlyForDeploymentPostprocessing = 0; @@ -505,6 +535,7 @@ 385C2928211FF33B0010B4BA /* MockMessage.swift in Sources */, 385C2923211FF32E0010B4BA /* TableViewCells.swift in Sources */, 385C2942211FF38F0010B4BA /* AdvancedExampleViewController.swift in Sources */, + 50739F9621C5090A008CA369 /* BasicAudioController.swift in Sources */, 385C2937211FF37B0010B4BA /* SampleData.swift in Sources */, 385C2931211FF3630010B4BA /* UIColor+Extensions.swift in Sources */, 385C2932211FF3630010B4BA /* UIViewController+Extensions.swift in Sources */, @@ -512,6 +543,7 @@ 385C2945211FF38F0010B4BA /* NavigationController.swift in Sources */, 385C2948211FF38F0010B4BA /* ChatViewController.swift in Sources */, 385C2944211FF38F0010B4BA /* MessageContainerController.swift in Sources */, + 383B9EB32172A1C4008AB91A /* MockUser.swift in Sources */, 385C2943211FF38F0010B4BA /* SettingsViewController.swift in Sources */, 385C2927211FF33B0010B4BA /* MockSocket.swift in Sources */, 385C2947211FF38F0010B4BA /* BasicExampleViewController.swift in Sources */, diff --git a/Example/ChatExample.xcodeproj/xcshareddata/xcschemes/ChatExample.xcscheme b/Example/ChatExample.xcodeproj/xcshareddata/xcschemes/ChatExample.xcscheme index 0fa1ed4f..e34206a1 100644 --- a/Example/ChatExample.xcodeproj/xcshareddata/xcschemes/ChatExample.xcscheme +++ b/Example/ChatExample.xcodeproj/xcshareddata/xcschemes/ChatExample.xcscheme @@ -1,6 +1,6 @@ '../' - pod 'MessageInputBar', :git => 'https://github.com/MessageKit/MessageInputBar.git', :branch => 'master' target 'ChatExampleTests' do inherit! :search_paths diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 14ba1653..66d26258 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,30 +1,23 @@ PODS: - - MessageInputBar (0.4.1): - - MessageInputBar/Core (= 0.4.1) - - MessageInputBar/Core (0.4.1) - - MessageKit (2.0.0): - - MessageInputBar/Core + - InputBarAccessoryView (4.2.1) + - MessageKit (3.0.0): + - InputBarAccessoryView DEPENDENCIES: - - MessageInputBar (from `https://github.com/MessageKit/MessageInputBar.git`, branch `master`) - MessageKit (from `../`) +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - InputBarAccessoryView + EXTERNAL SOURCES: - MessageInputBar: - :branch: master - :git: https://github.com/MessageKit/MessageInputBar.git MessageKit: :path: "../" -CHECKOUT OPTIONS: - MessageInputBar: - :commit: 117332dba2681cececc9ccacc0b759c161b9798e - :git: https://github.com/MessageKit/MessageInputBar.git - SPEC CHECKSUMS: - MessageInputBar: e81c7535347f1f7b923de7080409a535a004b6e4 - MessageKit: 29c1c87e5a396d2ca7a3f712e5171dc9aba42a1e + InputBarAccessoryView: 37175becdced159cd9ea335f47ae001b1e112909 + MessageKit: f291e31fbca3020e90f0e0507a8f087304ecd005 -PODFILE CHECKSUM: 04c1a805e1997e83bacab1a34787e71e9fe4432b +PODFILE CHECKSUM: cecdb7bc8129cf99f66de9f68eea3256fec30c3d COCOAPODS: 1.6.0 diff --git a/Example/Podfile~ b/Example/Podfile~ new file mode 100644 index 00000000..bf44d23b --- /dev/null +++ b/Example/Podfile~ @@ -0,0 +1,16 @@ +platform :ios, '9.0' + +target 'ChatExample' do + use_frameworks! + pod 'MessageKit', :path => '../' + pod 'MessageInputBar', :git => 'https://github.com/MessageKit/MessageInputBar.git', :branch => 'master' + +target 'ChatExampleTests' do + inherit! :search_paths +end + +target 'ChatExampleUITests' do + inherit! :search_paths +end + +end diff --git a/Example/Sources/AudioController/BasicAudioController.swift b/Example/Sources/AudioController/BasicAudioController.swift new file mode 100644 index 00000000..4ff00d2b --- /dev/null +++ b/Example/Sources/AudioController/BasicAudioController.swift @@ -0,0 +1,214 @@ +/* + MIT License + + Copyright (c) 2017-2018 MessageKit + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + 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. + */ + +import UIKit +import AVFoundation +import MessageKit + +/// The `PlayerState` indicates the current audio controller state +public enum PlayerState { + + /// The audio controller is currently playing a sound + case playing + + /// The audio controller is currently in pause state + case pause + + /// The audio controller is not playing any sound and audioPlayer is nil + case stopped +} + +/// The `BasicAudioController` update UI for current audio cell that is playing a sound +/// and also creates and manage an `AVAudioPlayer` states, play, pause and stop. +open class BasicAudioController: NSObject, AVAudioPlayerDelegate { + + /// The `AVAudioPlayer` that is playing the sound + open var audioPlayer: AVAudioPlayer? + + /// The `AudioMessageCell` that is currently playing sound + open weak var playingCell: AudioMessageCell? + + /// The `MessageType` that is currently playing sound + open var playingMessage: MessageType? + + /// Specify if current audio controller state: playing, in pause or none + open private(set) var state: PlayerState = .stopped + + // The `MessagesCollectionView` where the playing cell exist + public weak var messageCollectionView: MessagesCollectionView? + + /// The `Timer` that update playing progress + internal var progressTimer: Timer? + + // MARK: - Init Methods + + public init(messageCollectionView: MessagesCollectionView) { + self.messageCollectionView = messageCollectionView + super.init() + } + + // MARK: - Methods + + /// Used to configure the audio cell UI: + /// 1. play button selected state; + /// 2. progresssView progress; + /// 3. durationLabel text; + /// + /// - Parameters: + /// - cell: The `AudioMessageCell` that needs to be configure. + /// - message: The `MessageType` that configures the cell. + /// + /// - Note: + /// This protocol method is called by MessageKit every time an audio cell needs to be configure + open func configureAudioCell(_ cell: AudioMessageCell, message: MessageType) { + if playingMessage?.messageId == message.messageId, let collectionView = messageCollectionView, let player = audioPlayer { + playingCell = cell + cell.progressView.progress = (player.duration == 0) ? 0 : Float(player.currentTime/player.duration) + cell.playButton.isSelected = (player.isPlaying == true) ? true : false + guard let displayDelegate = collectionView.messagesDisplayDelegate else { + fatalError("MessagesDisplayDelegate has not been set.") + } + cell.durationLabel.text = displayDelegate.audioProgressTextFormat(Float(player.currentTime), for: cell, in: collectionView) + } + } + + /// Used to start play audio sound + /// + /// - Parameters: + /// - message: The `MessageType` that contain the audio item to be played. + /// - audioCell: The `AudioMessageCell` that needs to be updated while audio is playing. + open func playSound(for message: MessageType, in audioCell: AudioMessageCell) { + switch message.kind { + case .audio(let item): + playingCell = audioCell + playingMessage = message + guard let player = try? AVAudioPlayer(contentsOf: item.url) else { + print("Failed to create audio player for URL: \(item.url)") + return + } + audioPlayer = player + audioPlayer?.prepareToPlay() + audioPlayer?.delegate = self + audioPlayer?.play() + state = .playing + audioCell.playButton.isSelected = true // show pause button on audio cell + startProgressTimer() + audioCell.delegate?.didStartAudio(in: audioCell) + default: + print("BasicAudioPlayer failed play sound becasue given message kind is not Audio") + } + } + + /// Used to pause the audio sound + /// + /// - Parameters: + /// - message: The `MessageType` that contain the audio item to be pause. + /// - audioCell: The `AudioMessageCell` that needs to be updated by the pause action. + open func pauseSound(for message: MessageType, in audioCell: AudioMessageCell) { + audioPlayer?.pause() + state = .pause + audioCell.playButton.isSelected = false // show play button on audio cell + progressTimer?.invalidate() + if let cell = playingCell { + cell.delegate?.didPauseAudio(in: cell) + } + } + + /// Stops any ongoing audio playing if exists + open func stopAnyOngoingPlaying() { + guard let player = audioPlayer, let collectionView = messageCollectionView else { return } // If the audio player is nil then we don't need to go through the stopping logic + player.stop() + state = .stopped + if let cell = playingCell { + cell.progressView.progress = 0.0 + cell.playButton.isSelected = false + guard let displayDelegate = collectionView.messagesDisplayDelegate else { + fatalError("MessagesDisplayDelegate has not been set.") + } + cell.durationLabel.text = displayDelegate.audioProgressTextFormat(Float(player.duration), for: cell, in: collectionView) + cell.delegate?.didStopAudio(in: cell) + } + progressTimer?.invalidate() + progressTimer = nil + audioPlayer = nil + playingMessage = nil + playingCell = nil + } + + /// Resume a currently pause audio sound + open func resumeSound() { + guard let player = audioPlayer, let cell = playingCell else { + stopAnyOngoingPlaying() + return + } + player.prepareToPlay() + player.play() + state = .playing + startProgressTimer() + cell.playButton.isSelected = true // show pause button on audio cell + cell.delegate?.didStartAudio(in: cell) + } + + // MARK: - Fire Methods + @objc private func didFireProgressTimer(_ timer: Timer) { + guard let player = audioPlayer, let collectionView = messageCollectionView, let cell = playingCell else { + return + } + // check if can update playing cell + if let playingCellIndexPath = collectionView.indexPath(for: cell) { + // 1. get the current message that decorates the playing cell + // 2. check if current message is the same with playing message, if so then update the cell content + // Note: Those messages differ in the case of cell reuse + let currentMessage = collectionView.messagesDataSource?.messageForItem(at: playingCellIndexPath, in: collectionView) + if currentMessage != nil && currentMessage?.messageId == playingMessage?.messageId { + // messages are the same update cell content + cell.progressView.progress = (player.duration == 0) ? 0 : Float(player.currentTime/player.duration) + guard let displayDelegate = collectionView.messagesDisplayDelegate else { + fatalError("MessagesDisplayDelegate has not been set.") + } + cell.durationLabel.text = displayDelegate.audioProgressTextFormat(Float(player.currentTime), for: cell, in: collectionView) + } else { + // if the current message is not the same with playing message stop playing sound + stopAnyOngoingPlaying() + } + } + } + + // MARK: - Private Methods + private func startProgressTimer() { + progressTimer?.invalidate() + progressTimer = nil + progressTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(BasicAudioController.didFireProgressTimer(_:)), userInfo: nil, repeats: true) + } + + // MARK: - AVAudioPlayerDelegate + open func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { + stopAnyOngoingPlaying() + } + + open func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) { + stopAnyOngoingPlaying() + } + +} diff --git a/Example/Sources/Data Generation/Lorem.swift b/Example/Sources/Data Generation/Lorem.swift index b36e22b4..61b7df48 100755 --- a/Example/Sources/Data Generation/Lorem.swift +++ b/Example/Sources/Data Generation/Lorem.swift @@ -108,7 +108,7 @@ public class Lorem { return "" } - let result: String = self.words(nbWords: variable ? nbWords.randomize(variation: 40) : nbWords) + let result: String = words(nbWords: variable ? nbWords.randomize(variation: 40) : nbWords) return result.firstCapitalized + "." } diff --git a/Example/Sources/Data Generation/SampleData.swift b/Example/Sources/Data Generation/SampleData.swift index 07b47218..46e8f3d7 100644 --- a/Example/Sources/Data Generation/SampleData.swift +++ b/Example/Sources/Data Generation/SampleData.swift @@ -24,6 +24,7 @@ import MessageKit import CoreLocation +import AVFoundation final internal class SampleData { @@ -36,11 +37,12 @@ final internal class SampleData { case AttributedText = 1 case Photo = 2 case Video = 3 - case Emoji = 4 - case Location = 5 - case Url = 6 - case Phone = 7 - case Custom = 8 + case Audio = 4 + case Emoji = 5 + case Location = 6 + case Url = 7 + case Phone = 8 + case Custom = 9 static func random() -> MessageTypes { // Update as new enumerations are added @@ -51,21 +53,20 @@ final internal class SampleData { } } - let system = Sender(id: "000000", displayName: "System") - let nathan = Sender(id: "000001", displayName: "Nathan Tannar") - let steven = Sender(id: "000002", displayName: "Steven Deutsch") - let wu = Sender(id: "000003", displayName: "Wu Zhong") + let system = MockUser(id: "000000", displayName: "System") + let nathan = MockUser(id: "000001", displayName: "Nathan Tannar") + let steven = MockUser(id: "000002", displayName: "Steven Deutsch") + let wu = MockUser(id: "000003", displayName: "Wu Zhong") lazy var senders = [nathan, steven, wu] - var currentSender: Sender { + var currentSender: MockUser { return nathan } var now = Date() let messageImages: [UIImage] = [#imageLiteral(resourceName: "img1"), #imageLiteral(resourceName: "img2")] - let emojis = [ "👍", "😂😂😂", @@ -85,7 +86,11 @@ final internal class SampleData { CLLocation(latitude: 35.3218, longitude: -127.4314), CLLocation(latitude: 39.3218, longitude: -113.3317) ] - + + let sounds: [URL] = [Bundle.main.url(forResource: "sound1", withExtension: "m4a")!, + Bundle.main.url(forResource: "sound2", withExtension: "m4a")! + ] + func attributedString(with text: String) -> NSAttributedString { let nsString = NSString(string: text) var mutableAttributedString = NSMutableAttributedString(string: text) @@ -145,42 +150,46 @@ final internal class SampleData { return messageType } - func randomMessage(allowedSenders: [Sender]) -> MockMessage { + func randomMessage(allowedSenders: [MockUser]) -> MockMessage { let randomNumberSender = Int(arc4random_uniform(UInt32(allowedSenders.count))) let uniqueID = NSUUID().uuidString - let sender = allowedSenders[randomNumberSender] + let user = allowedSenders[randomNumberSender] let date = dateAddingRandomTime() switch randomMessageType() { case .Text: let randomSentence = Lorem.sentence() - return MockMessage(text: randomSentence, sender: sender, messageId: uniqueID, date: date) + return MockMessage(text: randomSentence, user: user, messageId: uniqueID, date: date) case .AttributedText: let randomSentence = Lorem.sentence() let attributedText = attributedString(with: randomSentence) - return MockMessage(attributedText: attributedText, sender: senders[randomNumberSender], messageId: uniqueID, date: date) + return MockMessage(attributedText: attributedText, user: user, messageId: uniqueID, date: date) case .Photo: let randomNumberImage = Int(arc4random_uniform(UInt32(messageImages.count))) let image = messageImages[randomNumberImage] - return MockMessage(image: image, sender: sender, messageId: uniqueID, date: date) + return MockMessage(image: image, user: user, messageId: uniqueID, date: date) case .Video: let randomNumberImage = Int(arc4random_uniform(UInt32(messageImages.count))) let image = messageImages[randomNumberImage] - return MockMessage(thumbnail: image, sender: sender, messageId: uniqueID, date: date) + return MockMessage(thumbnail: image, user: user, messageId: uniqueID, date: date) + case .Audio: + let randomNumberSound = Int(arc4random_uniform(UInt32(sounds.count))) + let soundURL = sounds[randomNumberSound] + return MockMessage(audioURL: soundURL, user: user, messageId: uniqueID, date: date) case .Emoji: let randomNumberEmoji = Int(arc4random_uniform(UInt32(emojis.count))) - return MockMessage(emoji: emojis[randomNumberEmoji], sender: sender, messageId: uniqueID, date: date) + return MockMessage(emoji: emojis[randomNumberEmoji], user: user, messageId: uniqueID, date: date) case .Location: let randomNumberLocation = Int(arc4random_uniform(UInt32(locations.count))) - return MockMessage(location: locations[randomNumberLocation], sender: sender, messageId: uniqueID, date: date) + return MockMessage(location: locations[randomNumberLocation], user: user, messageId: uniqueID, date: date) case .Url: - return MockMessage(text: "https://github.com/MessageKit", sender: sender, messageId: uniqueID, date: date) + return MockMessage(text: "https://github.com/MessageKit", user: user, messageId: uniqueID, date: date) case .Phone: - return MockMessage(text: "123-456-7890", sender: sender, messageId: uniqueID, date: date) + return MockMessage(text: "123-456-7890", user: user, messageId: uniqueID, date: date) case .Custom: - return MockMessage(custom: "Someone left the conversation", sender: system, messageId: uniqueID, date: date) + return MockMessage(custom: "Someone left the conversation", user: system, messageId: uniqueID, date: date) } } @@ -206,7 +215,7 @@ final internal class SampleData { completion(messages) } - func getMessages(count: Int, allowedSenders: [Sender], completion: ([MockMessage]) -> Void) { + func getMessages(count: Int, allowedSenders: [MockUser], completion: ([MockMessage]) -> Void) { var messages: [MockMessage] = [] // Disable Custom Messages UserDefaults.standard.set(false, forKey: "Custom Messages") @@ -217,18 +226,18 @@ final internal class SampleData { completion(messages) } - func getAvatarFor(sender: Sender) -> Avatar { + func getAvatarFor(sender: SenderType) -> Avatar { let firstName = sender.displayName.components(separatedBy: " ").first let lastName = sender.displayName.components(separatedBy: " ").first let initials = "\(firstName?.first ?? "A")\(lastName?.first ?? "A")" - switch sender { - case nathan: + switch sender.id { + case "000001": return Avatar(image: #imageLiteral(resourceName: "Nathan-Tannar"), initials: initials) - case steven: + case "000002": return Avatar(image: #imageLiteral(resourceName: "Steven-Deutsch"), initials: initials) - case wu: + case "000003": return Avatar(image: #imageLiteral(resourceName: "Wu-Zhong"), initials: initials) - case system: + case "000000": return Avatar(image: nil, initials: "SS") default: return Avatar(image: nil, initials: initials) diff --git a/Example/Sources/Info.plist b/Example/Sources/Info.plist index aee82b18..c2d68101 100644 --- a/Example/Sources/Info.plist +++ b/Example/Sources/Info.plist @@ -28,6 +28,8 @@ armv7 + UIStatusBarStyle + UIStatusBarStyleLightContent UIStatusBarTintParameters UINavigationBar diff --git a/Example/Sources/Models/MockMessage.swift b/Example/Sources/Models/MockMessage.swift index 8a320373..e4f95d28 100644 --- a/Example/Sources/Models/MockMessage.swift +++ b/Example/Sources/Models/MockMessage.swift @@ -25,6 +25,7 @@ import Foundation import CoreLocation import MessageKit +import AVFoundation private struct CoordinateItem: LocationItem { @@ -53,49 +54,74 @@ private struct ImageMediaItem: MediaItem { } +private struct MockAudiotem: AudioItem { + + var url: URL + var size: CGSize + var duration: Float + + init(url: URL) { + self.url = url + self.size = CGSize(width: 160, height: 35) + // compute duration + let audioAsset = AVURLAsset(url: url) + self.duration = Float(CMTimeGetSeconds(audioAsset.duration)) + } + +} + internal struct MockMessage: MessageType { var messageId: String - var sender: Sender + var sender: SenderType { + return user + } var sentDate: Date var kind: MessageKind - private init(kind: MessageKind, sender: Sender, messageId: String, date: Date) { + var user: MockUser + + private init(kind: MessageKind, user: MockUser, messageId: String, date: Date) { self.kind = kind - self.sender = sender + self.user = user self.messageId = messageId self.sentDate = date } - init(custom: Any?, sender: Sender, messageId: String, date: Date) { - self.init(kind: .custom(custom), sender: sender, messageId: messageId, date: date) + init(custom: Any?, user: MockUser, messageId: String, date: Date) { + self.init(kind: .custom(custom), user: user, messageId: messageId, date: date) } - init(text: String, sender: Sender, messageId: String, date: Date) { - self.init(kind: .text(text), sender: sender, messageId: messageId, date: date) + init(text: String, user: MockUser, messageId: String, date: Date) { + self.init(kind: .text(text), user: user, messageId: messageId, date: date) } - init(attributedText: NSAttributedString, sender: Sender, messageId: String, date: Date) { - self.init(kind: .attributedText(attributedText), sender: sender, messageId: messageId, date: date) + init(attributedText: NSAttributedString, user: MockUser, messageId: String, date: Date) { + self.init(kind: .attributedText(attributedText), user: user, messageId: messageId, date: date) } - init(image: UIImage, sender: Sender, messageId: String, date: Date) { + init(image: UIImage, user: MockUser, messageId: String, date: Date) { let mediaItem = ImageMediaItem(image: image) - self.init(kind: .photo(mediaItem), sender: sender, messageId: messageId, date: date) + self.init(kind: .photo(mediaItem), user: user, messageId: messageId, date: date) } - init(thumbnail: UIImage, sender: Sender, messageId: String, date: Date) { + init(thumbnail: UIImage, user: MockUser, messageId: String, date: Date) { let mediaItem = ImageMediaItem(image: thumbnail) - self.init(kind: .video(mediaItem), sender: sender, messageId: messageId, date: date) + self.init(kind: .video(mediaItem), user: user, messageId: messageId, date: date) } - init(location: CLLocation, sender: Sender, messageId: String, date: Date) { + init(location: CLLocation, user: MockUser, messageId: String, date: Date) { let locationItem = CoordinateItem(location: location) - self.init(kind: .location(locationItem), sender: sender, messageId: messageId, date: date) + self.init(kind: .location(locationItem), user: user, messageId: messageId, date: date) } - init(emoji: String, sender: Sender, messageId: String, date: Date) { - self.init(kind: .emoji(emoji), sender: sender, messageId: messageId, date: date) + init(emoji: String, user: MockUser, messageId: String, date: Date) { + self.init(kind: .emoji(emoji), user: user, messageId: messageId, date: date) + } + + init(audioURL: URL, user: MockUser, messageId: String, date: Date) { + let audioItem = MockAudiotem(url: audioURL) + self.init(kind: .audio(audioItem), user: user, messageId: messageId, date: date) } } diff --git a/Example/Sources/Models/MockSocket.swift b/Example/Sources/Models/MockSocket.swift index 0585d9dc..c3bc2996 100644 --- a/Example/Sources/Models/MockSocket.swift +++ b/Example/Sources/Models/MockSocket.swift @@ -37,12 +37,12 @@ final class MockSocket { private var onTypingStatusCode: (() -> Void)? - private var connectedUsers: [Sender] = [] + private var connectedUsers: [MockUser] = [] private init() {} @discardableResult - func connect(with senders: [Sender]) -> Self { + func connect(with senders: [MockUser]) -> Self { disconnect() connectedUsers = senders timer = Timer.scheduledTimer(timeInterval: 2.5, target: self, selector: #selector(handleTimer), userInfo: nil, repeats: true) diff --git a/Example/Sources/Models/MockUser.swift b/Example/Sources/Models/MockUser.swift new file mode 100644 index 00000000..de17c097 --- /dev/null +++ b/Example/Sources/Models/MockUser.swift @@ -0,0 +1,31 @@ +/* + MIT License + + Copyright (c) 2017-2018 MessageKit + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + 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. + */ + +import Foundation +import MessageKit + +struct MockUser: SenderType, Equatable { + var id: String + var displayName: String +} diff --git a/Example/Sources/Sounds/sound1.m4a b/Example/Sources/Sounds/sound1.m4a new file mode 100644 index 00000000..56b31a69 Binary files /dev/null and b/Example/Sources/Sounds/sound1.m4a differ diff --git a/Example/Sources/Sounds/sound2.m4a b/Example/Sources/Sounds/sound2.m4a new file mode 100644 index 00000000..ed80ce19 Binary files /dev/null and b/Example/Sources/Sounds/sound2.m4a differ diff --git a/Example/Sources/View Controllers/AdvancedExampleViewController.swift b/Example/Sources/View Controllers/AdvancedExampleViewController.swift index a46e3d97..254c388d 100644 --- a/Example/Sources/View Controllers/AdvancedExampleViewController.swift +++ b/Example/Sources/View Controllers/AdvancedExampleViewController.swift @@ -25,7 +25,7 @@ import UIKit import MapKit import MessageKit -import MessageInputBar +import InputBarAccessoryView final class AdvancedExampleViewController: ChatViewController { @@ -135,7 +135,7 @@ final class AdvancedExampleViewController: ChatViewController { messageInputBar.sendButton.image = #imageLiteral(resourceName: "ic_up") messageInputBar.sendButton.title = nil messageInputBar.sendButton.imageView?.layer.cornerRadius = 16 - messageInputBar.textViewPadding.right = -38 + messageInputBar.middleContentViewPadding.right = -38 let charCountButton = InputBarButtonItem() .configure { $0.title = "0/140" @@ -146,15 +146,15 @@ final class AdvancedExampleViewController: ChatViewController { }.onTextViewDidChange { (item, textView) in item.title = "\(textView.text.count)/140" let isOverLimit = textView.text.count > 140 - item.messageInputBar?.shouldManageSendButtonEnabledState = !isOverLimit // Disable automated management when over limit + item.inputBarAccessoryView?.shouldManageSendButtonEnabledState = !isOverLimit // Disable automated management when over limit if isOverLimit { - item.messageInputBar?.sendButton.isEnabled = false + item.inputBarAccessoryView?.sendButton.isEnabled = false } let color = isOverLimit ? .red : UIColor(white: 0.6, alpha: 1) item.setTitleColor(color, for: .normal) } let bottomItems = [makeButton(named: "ic_at"), makeButton(named: "ic_hashtag"), makeButton(named: "ic_library"), .flexibleSpace, charCountButton] - messageInputBar.textViewPadding.bottom = 8 + messageInputBar.middleContentViewPadding.bottom = 8 messageInputBar.setStackViewItems(bottomItems, forStack: .bottom, animated: false) // This just adds some more flare @@ -178,12 +178,12 @@ final class AdvancedExampleViewController: ChatViewController { func isPreviousMessageSameSender(at indexPath: IndexPath) -> Bool { guard indexPath.section - 1 >= 0 else { return false } - return messageList[indexPath.section].sender == messageList[indexPath.section - 1].sender + return messageList[indexPath.section].user == messageList[indexPath.section - 1].user } func isNextMessageSameSender(at indexPath: IndexPath) -> Bool { guard indexPath.section + 1 < messageList.count else { return false } - return messageList[indexPath.section].sender == messageList[indexPath.section + 1].sender + return messageList[indexPath.section].user == messageList[indexPath.section + 1].user } func setTypingIndicatorHidden(_ isHidden: Bool, performUpdates updates: (() -> Void)? = nil) { @@ -366,6 +366,16 @@ extension AdvancedExampleViewController: MessagesDisplayDelegate { return LocationMessageSnapshotOptions(showsBuildings: true, showsPointsOfInterest: true, span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10)) } + // MARK: - Audio Messages + + func audioTintColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor { + return self.isFromCurrentSender(message: message) ? .white : UIColor(red: 15/255, green: 135/255, blue: 255/255, alpha: 1.0) + } + + func configureAudioCell(_ cell: AudioMessageCell, message: MessageType) { + audioController.configureAudioCell(cell, message: message) // this is needed especily when the cell is reconfigure while is playing sound + } + } // MARK: - MessagesLayoutDelegate diff --git a/Example/Sources/View Controllers/BasicExampleViewController.swift b/Example/Sources/View Controllers/BasicExampleViewController.swift index 0d7fc717..d4445831 100644 --- a/Example/Sources/View Controllers/BasicExampleViewController.swift +++ b/Example/Sources/View Controllers/BasicExampleViewController.swift @@ -25,7 +25,6 @@ import UIKit import MapKit import MessageKit -import MessageInputBar final class BasicExampleViewController: ChatViewController { @@ -96,7 +95,17 @@ extension BasicExampleViewController: MessagesDisplayDelegate { return LocationMessageSnapshotOptions(showsBuildings: true, showsPointsOfInterest: true, span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10)) } + + // MARK: - Audio Messages + + func audioTintColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor { + return isFromCurrentSender(message: message) ? .white : UIColor(red: 15/255, green: 135/255, blue: 255/255, alpha: 1.0) + } + func configureAudioCell(_ cell: AudioMessageCell, message: MessageType) { + audioController.configureAudioCell(cell, message: message) // this is needed especily when the cell is reconfigure while is playing sound + } + } // MARK: - MessagesLayoutDelegate @@ -107,6 +116,10 @@ extension BasicExampleViewController: MessagesLayoutDelegate { return 18 } + func cellBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat { + return 17 + } + func messageTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat { return 20 } diff --git a/Example/Sources/View Controllers/ChatViewController.swift b/Example/Sources/View Controllers/ChatViewController.swift index a8de9166..b78b922f 100644 --- a/Example/Sources/View Controllers/ChatViewController.swift +++ b/Example/Sources/View Controllers/ChatViewController.swift @@ -24,7 +24,6 @@ SOFTWARE. import UIKit import MessageKit -import MessageInputBar /// A base class for the example controllers class ChatViewController: MessagesViewController, MessagesDataSource { @@ -33,6 +32,9 @@ class ChatViewController: MessagesViewController, MessagesDataSource { return .lightContent } + /// The `BasicAudioController` controll the AVAudioPlayer state (play, pause, stop) and udpate audio cell UI accordingly. + open lazy var audioController = BasicAudioController(messageCollectionView: messagesCollectionView) + var messageList: [MockMessage] = [] let refreshControl = UIRefreshControl() @@ -64,6 +66,7 @@ class ChatViewController: MessagesViewController, MessagesDataSource { override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) MockSocket.shared.disconnect() + audioController.stopAnyOngoingPlaying() } func loadFirstMessages() { @@ -138,7 +141,7 @@ class ChatViewController: MessagesViewController, MessagesDataSource { // MARK: - MessagesDataSource - func currentSender() -> Sender { + func currentSender() -> SenderType { return SampleData.shared.currentSender } @@ -157,6 +160,11 @@ class ChatViewController: MessagesViewController, MessagesDataSource { return nil } + func cellBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? { + + return NSAttributedString(string: "Read", attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 10), NSAttributedString.Key.foregroundColor: UIColor.darkGray]) + } + func messageTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? { let name = message.sender.displayName return NSAttributedString(string: name, attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption1)]) @@ -186,6 +194,10 @@ extension ChatViewController: MessageCellDelegate { print("Top cell label tapped") } + func didTapCellBottomLabel(in cell: MessageCollectionViewCell) { + print("Bottom cell label tapped") + } + func didTapMessageTopLabel(in cell: MessageCollectionViewCell) { print("Top message label tapped") } @@ -193,11 +205,48 @@ extension ChatViewController: MessageCellDelegate { func didTapMessageBottomLabel(in cell: MessageCollectionViewCell) { print("Bottom label tapped") } - + + func didTapPlayButton(in cell: AudioMessageCell) { + guard let indexPath = messagesCollectionView.indexPath(for: cell), + let message = messagesCollectionView.messagesDataSource?.messageForItem(at: indexPath, in: messagesCollectionView) else { + print("Failed to identify message when audio cell receive tap gesture") + return + } + guard audioController.state != .stopped else { + // There is no audio sound playing - prepare to start playing for given audio message + audioController.playSound(for: message, in: cell) + return + } + if audioController.playingMessage?.messageId == message.messageId { + // tap occur in the current cell that is playing audio sound + if audioController.state == .playing { + audioController.pauseSound(for: message, in: cell) + } else { + audioController.resumeSound() + } + } else { + // tap occur in a difference cell that the one is currently playing sound. First stop currently playing and start the sound for given message + audioController.stopAnyOngoingPlaying() + audioController.playSound(for: message, in: cell) + } + } + + func didStartAudio(in cell: AudioMessageCell) { + print("Did start playing audio sound") + } + + func didPauseAudio(in cell: AudioMessageCell) { + print("Did pause audio sound") + } + + func didStopAudio(in cell: AudioMessageCell) { + print("Did stop audio sound") + } + func didTapAccessoryView(in cell: MessageCollectionViewCell) { print("Accessory view tapped") } - + } // MARK: - MessageLabelDelegate @@ -233,12 +282,13 @@ extension ChatViewController: MessageInputBarDelegate { func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String) { for component in inputBar.inputTextView.components { - + + let user = SampleData.shared.currentSender if let str = component as? String { - let message = MockMessage(text: str, sender: currentSender(), messageId: UUID().uuidString, date: Date()) + let message = MockMessage(text: str, user: user, messageId: UUID().uuidString, date: Date()) insertMessage(message) } else if let img = component as? UIImage { - let message = MockMessage(image: img, sender: currentSender(), messageId: UUID().uuidString, date: Date()) + let message = MockMessage(image: img, user: user, messageId: UUID().uuidString, date: Date()) insertMessage(message) } @@ -246,5 +296,5 @@ extension ChatViewController: MessageInputBarDelegate { inputBar.inputTextView.text = String() messagesCollectionView.scrollToBottom(animated: true) } - + } diff --git a/Example/Sources/View Controllers/LaunchViewController.swift b/Example/Sources/View Controllers/LaunchViewController.swift index 8340af1a..109db593 100644 --- a/Example/Sources/View Controllers/LaunchViewController.swift +++ b/Example/Sources/View Controllers/LaunchViewController.swift @@ -24,7 +24,6 @@ import UIKit import MessageKit -import MessageInputBar import SafariServices final internal class LaunchViewController: UITableViewController { diff --git a/Example/Sources/View Controllers/MessageContainerController.swift b/Example/Sources/View Controllers/MessageContainerController.swift index ec1ea24b..bf170616 100644 --- a/Example/Sources/View Controllers/MessageContainerController.swift +++ b/Example/Sources/View Controllers/MessageContainerController.swift @@ -57,7 +57,7 @@ final class MessageContainerController: UIViewController { /// Add the `ConversationViewController` as a child view controller conversationViewController.willMove(toParent: self) - self.addChild(conversationViewController) + addChild(conversationViewController) view.addSubview(conversationViewController.view) conversationViewController.didMove(toParent: self) diff --git a/Example/Sources/View Controllers/SettingsViewController.swift b/Example/Sources/View Controllers/SettingsViewController.swift index 882e0f9e..7c39887e 100644 --- a/Example/Sources/View Controllers/SettingsViewController.swift +++ b/Example/Sources/View Controllers/SettingsViewController.swift @@ -24,7 +24,6 @@ import UIKit import MessageKit -import MessageInputBar final internal class SettingsViewController: UITableViewController { @@ -34,7 +33,7 @@ final internal class SettingsViewController: UITableViewController { return .lightContent } - let cells = ["Mock messages count", "Text Messages", "AttributedText Messages", "Photo Messages", "Video Messages", "Emoji Messages", "Location Messages", "Url Messages", "Phone Messages"] + let cells = ["Mock messages count", "Text Messages", "AttributedText Messages", "Photo Messages", "Video Messages", "Audio Messages", "Emoji Messages", "Location Messages", "Url Messages", "Phone Messages"] // MARK: - Picker diff --git a/MessageKit.podspec b/MessageKit.podspec index 0d2e1b65..ee192e93 100644 --- a/MessageKit.podspec +++ b/MessageKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'MessageKit' - s.version = '2.0.0' + s.version = '3.0.0' s.license = { :type => "MIT", :file => "LICENSE.md" } s.summary = 'An elegant messages UI library for iOS.' @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.source_files = 'Sources/**/*.swift' s.pod_target_xcconfig = { - "SWIFT_VERSION" => "4.0", + "SWIFT_VERSION" => "4.2", } s.ios.deployment_target = '9.0' @@ -20,6 +20,6 @@ Pod::Spec.new do |s| s.requires_arc = true - s.dependency 'MessageInputBar/Core' + s.dependency 'InputBarAccessoryView' end diff --git a/MessageKit.xcodeproj/project.pbxproj b/MessageKit.xcodeproj/project.pbxproj index 1e87efb0..253ede80 100644 --- a/MessageKit.xcodeproj/project.pbxproj +++ b/MessageKit.xcodeproj/project.pbxproj @@ -37,6 +37,13 @@ 382C794221705D2000F4FAF5 /* HorizontalEdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382C794121705D2000F4FAF5 /* HorizontalEdgeInsets.swift */; }; 38C2AE7C20D4878D00F8079E /* MessageInputBar.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38C2AE7B20D4878D00F8079E /* MessageInputBar.framework */; }; 4C508649221C0BBA0043943C /* AccessoryPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C508648221C0BBA0043943C /* AccessoryPosition.swift */; }; + 383B9EB121728BAD008AB91A /* SenderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383B9EB021728BAD008AB91A /* SenderType.swift */; }; + 38A223112223493500D14DAF /* InputBarAccessoryView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38A223102223493400D14DAF /* InputBarAccessoryView.framework */; }; + 38F8062F2173CD8F00CDB9DB /* MockUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38F8062D2173CD4300CDB9DB /* MockUser.swift */; }; + 5073C1152175BE750040EAD5 /* AudioItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5073C1142175BE750040EAD5 /* AudioItem.swift */; }; + 5073C1192175BE960040EAD5 /* AudioMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5073C1182175BE950040EAD5 /* AudioMessageCell.swift */; }; + 5073C11D2175BEC60040EAD5 /* AudioMessageSizeCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5073C11C2175BEC60040EAD5 /* AudioMessageSizeCalculator.swift */; }; + 5073C1232175C1980040EAD5 /* sound1.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 5073C1222175C1980040EAD5 /* sound1.m4a */; }; 88916B2D1CF0DF2F00469F91 /* MessageKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88916B221CF0DF2F00469F91 /* MessageKit.framework */; }; 8962AC8A1F87AB7D0030B058 /* MessagesCollectionViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8962AC831F87AB230030B058 /* MessagesCollectionViewTests.swift */; }; 8962AC8C1F87AB7D0030B058 /* AvatarViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8962AC851F87AB230030B058 /* AvatarViewTests.swift */; }; @@ -131,8 +138,19 @@ 1FF377A920087D78004FD648 /* MessagesViewController+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessagesViewController+Menu.swift"; sourceTree = ""; }; 1FF377AB20087DA2004FD648 /* MessagesViewController+Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessagesViewController+Keyboard.swift"; sourceTree = ""; }; 382C794121705D2000F4FAF5 /* HorizontalEdgeInsets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HorizontalEdgeInsets.swift; sourceTree = ""; }; + 383B9EB021728BAD008AB91A /* SenderType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SenderType.swift; sourceTree = ""; }; + 38A2230E221FB8A300D14DAF /* MessageInputBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageInputBar.swift; sourceTree = ""; }; + 38A223102223493400D14DAF /* InputBarAccessoryView.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = InputBarAccessoryView.framework; path = Carthage/Build/iOS/InputBarAccessoryView.framework; sourceTree = ""; }; 38C2AE7B20D4878D00F8079E /* MessageInputBar.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageInputBar.framework; path = Carthage/Build/iOS/MessageInputBar.framework; sourceTree = ""; }; +<<<<<<< HEAD 4C508648221C0BBA0043943C /* AccessoryPosition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessoryPosition.swift; sourceTree = ""; }; +======= + 38F8062D2173CD4300CDB9DB /* MockUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUser.swift; sourceTree = ""; }; + 5073C1142175BE750040EAD5 /* AudioItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioItem.swift; sourceTree = ""; }; + 5073C1182175BE950040EAD5 /* AudioMessageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioMessageCell.swift; sourceTree = ""; }; + 5073C11C2175BEC60040EAD5 /* AudioMessageSizeCalculator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioMessageSizeCalculator.swift; sourceTree = ""; }; + 5073C1222175C1980040EAD5 /* sound1.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = sound1.m4a; sourceTree = ""; }; +>>>>>>> upstream/3.0.0-beta 88916B221CF0DF2F00469F91 /* MessageKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MessageKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 88916B2C1CF0DF2F00469F91 /* MessageKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MessageKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8962AC741F87AB230030B058 /* MessageKitDateFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageKitDateFormatterTests.swift; sourceTree = ""; }; @@ -187,7 +205,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 38C2AE7C20D4878D00F8079E /* MessageInputBar.framework in Frameworks */, + 38A223112223493500D14DAF /* InputBarAccessoryView.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -207,6 +225,7 @@ 1F7FC8C21FD26F22006CC979 /* Frameworks */ = { isa = PBXGroup; children = ( + 38A223102223493400D14DAF /* InputBarAccessoryView.framework */, 38C2AE7B20D4878D00F8079E /* MessageInputBar.framework */, 1F7FC8C71FD26F49006CC979 /* Nimble.framework */, 1F7FC8C51FD26F33006CC979 /* Quick.framework */, @@ -220,6 +239,7 @@ children = ( B7A03F381F866946006AEF79 /* LocationMessageCell.swift */, B7A03F391F866946006AEF79 /* MediaMessageCell.swift */, + 5073C1182175BE950040EAD5 /* AudioMessageCell.swift */, B7A03F7A1F866B85006AEF79 /* MessageCollectionViewCell.swift */, 1F6C040B206A2891007BDE44 /* MessageContentCell.swift */, B7A03F361F866946006AEF79 /* TextMessageCell.swift */, @@ -235,6 +255,14 @@ path = "Headers & Footers"; sourceTree = ""; }; + 5073C1212175C1580040EAD5 /* Resources */ = { + isa = PBXGroup; + children = ( + 5073C1222175C1980040EAD5 /* sound1.m4a */, + ); + path = Resources; + sourceTree = ""; + }; 88916B181CF0DF2F00469F91 = { isa = PBXGroup; children = ( @@ -282,6 +310,7 @@ 8962AC801F87AB230030B058 /* ProtocolsTests */, 8962AC7E1F87AB230030B058 /* Supporting Files */, 8962AC821F87AB230030B058 /* ViewsTests */, + 5073C1212175C1580040EAD5 /* Resources */, ); path = Tests; sourceTree = SOURCE_ROOT; @@ -300,6 +329,7 @@ children = ( 8962AC7B1F87AB230030B058 /* MockMessage.swift */, 8962AC7A1F87AB230030B058 /* MockMessagesDataSource.swift */, + 38F8062D2173CD4300CDB9DB /* MockUser.swift */, ); path = Mocks; sourceTree = ""; @@ -406,8 +436,10 @@ B7A03F561F8669C9006AEF79 /* MessagesDisplayDelegate.swift */, B7A03F541F8669C9006AEF79 /* MessagesLayoutDelegate.swift */, B7A03F511F8669C9006AEF79 /* MessageType.swift */, + 383B9EB021728BAD008AB91A /* SenderType.swift */, 1FD5895F2064E08A004B5081 /* MediaItem.swift */, 1FD5896320660C1C004B5081 /* LocationItem.swift */, + 5073C1142175BE750040EAD5 /* AudioItem.swift */, ); path = Protocols; sourceTree = ""; @@ -431,6 +463,7 @@ 1FE783A120662905007FA024 /* TextMessageSizeCalculator.swift */, 1FE783A3206629A5007FA024 /* MediaMessageSizeCalculator.swift */, 1FE783A5206629C2007FA024 /* LocationMessageSizeCalculator.swift */, + 5073C11C2175BEC60040EAD5 /* AudioMessageSizeCalculator.swift */, 0EF0888B206F7E83007F2F58 /* CellSizeCalculator.swift */, ); path = Layout; @@ -442,6 +475,7 @@ B7A03F711F866A06006AEF79 /* Info.plist */, B7A03F721F866A06006AEF79 /* MessageKit.h */, B7A03F701F866A06006AEF79 /* MessageKit+Availability.swift */, + 38A2230E221FB8A300D14DAF /* MessageInputBar.swift */, ); path = Supporting; sourceTree = ""; @@ -548,6 +582,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5073C1232175C1980040EAD5 /* sound1.m4a in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -581,13 +616,16 @@ B7A03F601F8669CA006AEF79 /* MessagesDisplayDelegate.swift in Sources */, 1FE783A8206633C0007FA024 /* InsetLabel.swift in Sources */, B7A03F5C1F8669CA006AEF79 /* MessageCellDelegate.swift in Sources */, + 5073C11D2175BEC60040EAD5 /* AudioMessageSizeCalculator.swift in Sources */, 1FF377A420087C82004FD648 /* MessageKitError.swift in Sources */, 1F6C040E206A2AF4007BDE44 /* MessageReusableView.swift in Sources */, B7A03F4B1F86694F006AEF79 /* MessageContainerView.swift in Sources */, B7A03F281F866895006AEF79 /* LocationMessageSnapshotOptions.swift in Sources */, B7A03F6C1F8669EB006AEF79 /* UIView+Extensions.swift in Sources */, + 383B9EB121728BAD008AB91A /* SenderType.swift in Sources */, B7A03F3A1F866946006AEF79 /* TextMessageCell.swift in Sources */, B7A03F191F86682C006AEF79 /* MessagesCollectionViewLayoutAttributes.swift in Sources */, + 38A2230F221FB8A300D14DAF /* MessageInputBar.swift in Sources */, B7A03F461F86694F006AEF79 /* AvatarView.swift in Sources */, 1FCA6D30201C1CC900BC3480 /* UIEdgeInsets+Extensions.swift in Sources */, B7A03F3D1F866946006AEF79 /* MediaMessageCell.swift in Sources */, @@ -599,6 +637,7 @@ B7A03F5E1F8669CA006AEF79 /* MessagesLayoutDelegate.swift in Sources */, B7A03F261F866895006AEF79 /* MessageKitDateFormatter.swift in Sources */, B7A03F6D1F8669EB006AEF79 /* Bundle+Extensions.swift in Sources */, + 5073C1192175BE960040EAD5 /* AudioMessageCell.swift in Sources */, 1FD589602064E08A004B5081 /* MediaItem.swift in Sources */, B7A03F611F8669CA006AEF79 /* MessagesDataSource.swift in Sources */, B7A03F6E1F8669EB006AEF79 /* NSAttributedString+Extensions.swift in Sources */, @@ -614,6 +653,7 @@ B7A03F2A1F866895006AEF79 /* MessageStyle.swift in Sources */, B7A03F4D1F86694F006AEF79 /* MessagesCollectionView.swift in Sources */, 0EF0888C206F7E83007F2F58 /* CellSizeCalculator.swift in Sources */, + 5073C1152175BE750040EAD5 /* AudioItem.swift in Sources */, 1FE783A4206629A5007FA024 /* MediaMessageSizeCalculator.swift in Sources */, B7A03F731F866A06006AEF79 /* MessageKit+Availability.swift in Sources */, 1FE7839E20662835007FA024 /* MessageSizeCalculator.swift in Sources */, @@ -641,6 +681,7 @@ 1F066E131FD90BB600E11013 /* MessagesViewControllerSpec.swift in Sources */, 1F066E1D1FDA3C1700E11013 /* SenderSpec.swift in Sources */, 8962AC8A1F87AB7D0030B058 /* MessagesCollectionViewTests.swift in Sources */, + 38F8062F2173CD8F00CDB9DB /* MockUser.swift in Sources */, 8962AC8D1F87AB7D0030B058 /* MessageCollectionViewCellTests.swift in Sources */, 8962AC991F87AB860030B058 /* MessagesDisplayDelegateTests.swift in Sources */, 1FD589622064E1B9004B5081 /* MockMessage.swift in Sources */, diff --git a/MessageKit.xcodeproj/project.pbxproj.orig b/MessageKit.xcodeproj/project.pbxproj.orig deleted file mode 100644 index 3db29518..00000000 --- a/MessageKit.xcodeproj/project.pbxproj.orig +++ /dev/null @@ -1,814 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 171D5AB91F36712B0053DF69 /* InputTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171D5AB81F36712B0053DF69 /* InputTextView.swift */; }; - 1919E6121F850A3B00DE85BF /* AvatarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E60A1F850A1100DE85BF /* AvatarTests.swift */; }; - 1919E6131F850A3E00DE85BF /* DetectorTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E60B1F850A1100DE85BF /* DetectorTypeTests.swift */; }; - 1919E6141F850A3E00DE85BF /* MessageKitDateFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E60C1F850A1100DE85BF /* MessageKitDateFormatterTests.swift */; }; - 1919E6151F850A3E00DE85BF /* MessagesDisplayDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E60D1F850A1100DE85BF /* MessagesDisplayDelegateTests.swift */; }; - 1919E6161F850A3E00DE85BF /* MessageStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E60E1F850A1100DE85BF /* MessageStyleTests.swift */; }; - 1919E6171F850A3E00DE85BF /* SenderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E60F1F850A1100DE85BF /* SenderTests.swift */; }; - 1919E61A1F850A6A00DE85BF /* AvatarViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E6001F850A1100DE85BF /* AvatarViewTests.swift */; }; - 1919E61B1F850A6A00DE85BF /* InputBarItemTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E6011F850A1100DE85BF /* InputBarItemTests.swift */; }; - 1919E61C1F850A6A00DE85BF /* InputTextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E6021F850A1100DE85BF /* InputTextViewTests.swift */; }; - 1919E61D1F850A6A00DE85BF /* MessageCollectionViewCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E6031F850A1100DE85BF /* MessageCollectionViewCellTests.swift */; }; - 1919E61E1F850A6A00DE85BF /* MessageDateHeaderViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E6041F850A1100DE85BF /* MessageDateHeaderViewTests.swift */; }; - 1919E61F1F850A6A00DE85BF /* MessageInputBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E6051F850A1100DE85BF /* MessageInputBarTests.swift */; }; - 1919E6201F850A6A00DE85BF /* MessagesCollectionViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1919E6061F850A1100DE85BF /* MessagesCollectionViewTests.swift */; }; - 195F03DD1F850C7000066E32 /* MessagesViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195F03DC1F850C7000066E32 /* MessagesViewControllerTests.swift */; }; - 195F03E11F865BD300066E32 /* MockMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195F03E01F865BD300066E32 /* MockMessage.swift */; }; - 195F03E31F865C3200066E32 /* MockMessagesDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195F03E21F865C3200066E32 /* MockMessagesDataSource.swift */; }; - 372F6AEB1F36C15600B57FBD /* AvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372F6AEA1F36C15600B57FBD /* AvatarView.swift */; }; - 37C936981F38F6AC00853DF2 /* Avatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C936971F38F6AC00853DF2 /* Avatar.swift */; }; - 38C8679A1F50EA1000811974 /* NSConstraintLayoutSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C867981F50EA1000811974 /* NSConstraintLayoutSet.swift */; }; - 38C8679B1F50EA1000811974 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C867991F50EA1000811974 /* UIView+Extensions.swift */; }; - 38C8679E1F50EA4A00811974 /* InputBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38C8679D1F50EA4A00811974 /* InputBarItem.swift */; }; - 882D75841DE507320033F95F /* MessagesDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 882D75831DE507320033F95F /* MessagesDataSource.swift */; }; - 888CEBFC1D3FD525005178DE /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 888CEBFB1D3FD525005178DE /* MessagesViewController.swift */; }; - 88916B2D1CF0DF2F00469F91 /* MessageKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88916B221CF0DF2F00469F91 /* MessageKit.framework */; }; - 88916B401CF0DF5100469F91 /* MessageKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 88916B3E1CF0DF5100469F91 /* MessageKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 88916B471CF0DFE600469F91 /* MessageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88916B461CF0DFE600469F91 /* MessageType.swift */; }; - B01049101F6F8684008DBA58 /* PlayButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B010490F1F6F8684008DBA58 /* PlayButtonView.swift */; }; - B01049121F6F86E8008DBA58 /* LocationMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01049111F6F86E8008DBA58 /* LocationMessageCell.swift */; }; - B01280F31F4E8798004BCD3E /* MessageLabelDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01280F21F4E8798004BCD3E /* MessageLabelDelegate.swift */; }; - B0147C831F5BE9220035B36E /* Bundle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0147C821F5BE9220035B36E /* Bundle+Extensions.swift */; }; - B0147C901F5ED0810035B36E /* MessageDateHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0147C8F1F5ED0810035B36E /* MessageDateHeaderView.swift */; }; - B0147C921F6002150035B36E /* MessageKitDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0147C911F6002140035B36E /* MessageKitDateFormatter.swift */; }; - B0147C961F600E290035B36E /* MessageKit+Availability.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0147C951F600E290035B36E /* MessageKit+Availability.swift */; }; - B0147C981F61AF930035B36E /* LabelAlignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0147C971F61AF910035B36E /* LabelAlignment.swift */; }; - B015E8191F24623D007EDFB6 /* MessagesCollectionViewLayoutAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B015E8181F24623D007EDFB6 /* MessagesCollectionViewLayoutAttributes.swift */; }; - B015E81F1F259D8E007EDFB6 /* MessageInputBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B015E81E1F259D8E007EDFB6 /* MessageInputBarDelegate.swift */; }; - B01E2DD81F5BBDB800E4FA1C /* MessageStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01E2DD71F5BBDB800E4FA1C /* MessageStyle.swift */; }; - B0291DA91F6DBB9F00BEDF03 /* TextMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0291DA81F6DBB9F00BEDF03 /* TextMessageCell.swift */; }; - B03FF9AF1F31BB1200754FE5 /* MessageCellDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B03FF9AE1F31BB1200754FE5 /* MessageCellDelegate.swift */; }; - B05530B51F493CFA008BB420 /* DetectorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B05530B41F493CFA008BB420 /* DetectorType.swift */; }; - B0655A2A1F23D77200542A83 /* Sender.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0655A291F23D77200542A83 /* Sender.swift */; }; - B0655A2C1F23D81600542A83 /* MessageData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0655A2B1F23D81600542A83 /* MessageData.swift */; }; - B0655A2E1F23D8BC00542A83 /* MessagesCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0655A2D1F23D8BC00542A83 /* MessagesCollectionView.swift */; }; - B0655A381F23EE8B00542A83 /* MessageInputBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0655A371F23EE8B00542A83 /* MessageInputBar.swift */; }; - B0655A4D1F244C0600542A83 /* MessageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0655A4C1F244C0600542A83 /* MessageCollectionViewCell.swift */; }; - B0655A4F1F245C5A00542A83 /* MessagesCollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0655A4E1F245C5A00542A83 /* MessagesCollectionViewFlowLayout.swift */; }; - B06D19331F70D2CA0020E416 /* LocationMessageLayoutDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B06D19321F70D2CA0020E416 /* LocationMessageLayoutDelegate.swift */; }; - B06D19351F70D3170020E416 /* MediaMessageLayoutDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B06D19341F70D3170020E416 /* MediaMessageLayoutDelegate.swift */; }; - B06D19371F70D3580020E416 /* LocationMessageSnapshotOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B06D19361F70D3580020E416 /* LocationMessageSnapshotOptions.swift */; }; - B074EE931F35587100ABB8C8 /* MessageHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B074EE921F35587100ABB8C8 /* MessageHeaderView.swift */; }; - B074EE951F35588A00ABB8C8 /* MessageFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B074EE941F35588A00ABB8C8 /* MessageFooterView.swift */; }; - B074EE971F355FBC00ABB8C8 /* MessagesLayoutDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B074EE961F355FBC00ABB8C8 /* MessagesLayoutDelegate.swift */; }; - B074EEA81F3971A600ABB8C8 /* MessageLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B074EEA71F3971A600ABB8C8 /* MessageLabel.swift */; }; - B09643861F286C9E004D0129 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09643851F286C9E004D0129 /* String+Extensions.swift */; }; - B096438E1F2890FB004D0129 /* MessagesDisplayDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B096438D1F2890FB004D0129 /* MessagesDisplayDelegate.swift */; }; - B09643901F289142004D0129 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B096438F1F289142004D0129 /* UIColor+Extensions.swift */; }; - B0AA1F511F42E91A00BAE583 /* AvatarAlignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0AA1F501F42E91A00BAE583 /* AvatarAlignment.swift */; }; - B0AA1F531F44388C00BAE583 /* NSAttributedString+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0AA1F521F44388900BAE583 /* NSAttributedString+Extensions.swift */; }; - B0C8D99B1F73076B000A86E4 /* MessageKitAssets.bundle in Resources */ = {isa = PBXBuildFile; fileRef = B0C8D99A1F73076B000A86E4 /* MessageKitAssets.bundle */; }; - B0D943BE1F6DC9AB008B7BFD /* MediaMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D943BD1F6DC9AB008B7BFD /* MediaMessageCell.swift */; }; - B0E1756F1F655A1600F0DEF6 /* AvatarHorizontalAlignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0E1756E1F655A1600F0DEF6 /* AvatarHorizontalAlignment.swift */; }; - D88EBBE01F85252E00F63AD2 /* MockMessagesDisplayDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88EBBDF1F85252E00F63AD2 /* MockMessagesDisplayDelegate.swift */; }; - E8586DB51F59A1C300C9BE9D /* MessageContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8586DB41F59A1C300C9BE9D /* MessageContainerView.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 88916B2E1CF0DF2F00469F91 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 88916B191CF0DF2F00469F91 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 88916B211CF0DF2F00469F91; - remoteInfo = MessageKit; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 171D5AB81F36712B0053DF69 /* InputTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputTextView.swift; sourceTree = ""; }; - 1919E6001F850A1100DE85BF /* AvatarViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarViewTests.swift; sourceTree = ""; }; - 1919E6011F850A1100DE85BF /* InputBarItemTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputBarItemTests.swift; sourceTree = ""; }; - 1919E6021F850A1100DE85BF /* InputTextViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputTextViewTests.swift; sourceTree = ""; }; - 1919E6031F850A1100DE85BF /* MessageCollectionViewCellTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCollectionViewCellTests.swift; sourceTree = ""; }; - 1919E6041F850A1100DE85BF /* MessageDateHeaderViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageDateHeaderViewTests.swift; sourceTree = ""; }; - 1919E6051F850A1100DE85BF /* MessageInputBarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageInputBarTests.swift; sourceTree = ""; }; - 1919E6061F850A1100DE85BF /* MessagesCollectionViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesCollectionViewTests.swift; sourceTree = ""; }; - 1919E60A1F850A1100DE85BF /* AvatarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarTests.swift; sourceTree = ""; }; - 1919E60B1F850A1100DE85BF /* DetectorTypeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectorTypeTests.swift; sourceTree = ""; }; - 1919E60C1F850A1100DE85BF /* MessageKitDateFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageKitDateFormatterTests.swift; sourceTree = ""; }; - 1919E60D1F850A1100DE85BF /* MessagesDisplayDelegateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesDisplayDelegateTests.swift; sourceTree = ""; }; - 1919E60E1F850A1100DE85BF /* MessageStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageStyleTests.swift; sourceTree = ""; }; - 1919E60F1F850A1100DE85BF /* SenderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SenderTests.swift; sourceTree = ""; }; - 1919E6111F850A1100DE85BF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 195F03DC1F850C7000066E32 /* MessagesViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewControllerTests.swift; sourceTree = ""; }; - 195F03E01F865BD300066E32 /* MockMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMessage.swift; sourceTree = ""; }; - 195F03E21F865C3200066E32 /* MockMessagesDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMessagesDataSource.swift; sourceTree = ""; }; - 372F6AEA1F36C15600B57FBD /* AvatarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarView.swift; sourceTree = ""; }; - 37C936971F38F6AC00853DF2 /* Avatar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Avatar.swift; sourceTree = ""; }; - 38C867981F50EA1000811974 /* NSConstraintLayoutSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSConstraintLayoutSet.swift; sourceTree = ""; }; - 38C867991F50EA1000811974 /* UIView+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Extensions.swift"; sourceTree = ""; }; - 38C8679D1F50EA4A00811974 /* InputBarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputBarItem.swift; sourceTree = ""; }; - 882D75831DE507320033F95F /* MessagesDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesDataSource.swift; sourceTree = ""; }; - 888CEBFB1D3FD525005178DE /* MessagesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; - 88916B221CF0DF2F00469F91 /* MessageKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MessageKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 88916B2C1CF0DF2F00469F91 /* MessageKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MessageKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 88916B3D1CF0DF5100469F91 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 88916B3E1CF0DF5100469F91 /* MessageKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessageKit.h; sourceTree = ""; }; - 88916B461CF0DFE600469F91 /* MessageType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageType.swift; sourceTree = ""; }; - B010490F1F6F8684008DBA58 /* PlayButtonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayButtonView.swift; sourceTree = ""; }; - B01049111F6F86E8008DBA58 /* LocationMessageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationMessageCell.swift; sourceTree = ""; }; - B01280F21F4E8798004BCD3E /* MessageLabelDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageLabelDelegate.swift; sourceTree = ""; }; - B0147C821F5BE9220035B36E /* Bundle+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bundle+Extensions.swift"; sourceTree = ""; }; - B0147C8F1F5ED0810035B36E /* MessageDateHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageDateHeaderView.swift; sourceTree = ""; }; - B0147C911F6002140035B36E /* MessageKitDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageKitDateFormatter.swift; sourceTree = ""; }; - B0147C951F600E290035B36E /* MessageKit+Availability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MessageKit+Availability.swift"; sourceTree = ""; }; - B0147C971F61AF910035B36E /* LabelAlignment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelAlignment.swift; sourceTree = ""; }; - B015E8181F24623D007EDFB6 /* MessagesCollectionViewLayoutAttributes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesCollectionViewLayoutAttributes.swift; sourceTree = ""; }; - B015E81E1F259D8E007EDFB6 /* MessageInputBarDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageInputBarDelegate.swift; sourceTree = ""; }; - B01E2DD71F5BBDB800E4FA1C /* MessageStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageStyle.swift; sourceTree = ""; }; - B0291DA81F6DBB9F00BEDF03 /* TextMessageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextMessageCell.swift; sourceTree = ""; }; - B03FF9AE1F31BB1200754FE5 /* MessageCellDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageCellDelegate.swift; sourceTree = ""; }; - B05530B41F493CFA008BB420 /* DetectorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetectorType.swift; sourceTree = ""; }; - B0655A291F23D77200542A83 /* Sender.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sender.swift; sourceTree = ""; }; - B0655A2B1F23D81600542A83 /* MessageData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageData.swift; sourceTree = ""; }; - B0655A2D1F23D8BC00542A83 /* MessagesCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesCollectionView.swift; sourceTree = ""; }; - B0655A371F23EE8B00542A83 /* MessageInputBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageInputBar.swift; sourceTree = ""; }; - B0655A4C1F244C0600542A83 /* MessageCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageCollectionViewCell.swift; sourceTree = ""; }; - B0655A4E1F245C5A00542A83 /* MessagesCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesCollectionViewFlowLayout.swift; sourceTree = ""; }; - B06D19321F70D2CA0020E416 /* LocationMessageLayoutDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationMessageLayoutDelegate.swift; sourceTree = ""; }; - B06D19341F70D3170020E416 /* MediaMessageLayoutDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaMessageLayoutDelegate.swift; sourceTree = ""; }; - B06D19361F70D3580020E416 /* LocationMessageSnapshotOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationMessageSnapshotOptions.swift; sourceTree = ""; }; - B074EE921F35587100ABB8C8 /* MessageHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHeaderView.swift; sourceTree = ""; }; - B074EE941F35588A00ABB8C8 /* MessageFooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageFooterView.swift; sourceTree = ""; }; - B074EE961F355FBC00ABB8C8 /* MessagesLayoutDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesLayoutDelegate.swift; sourceTree = ""; }; - B074EEA71F3971A600ABB8C8 /* MessageLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageLabel.swift; sourceTree = ""; }; - B09643851F286C9E004D0129 /* String+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; - B096438D1F2890FB004D0129 /* MessagesDisplayDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesDisplayDelegate.swift; sourceTree = ""; }; - B096438F1F289142004D0129 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = ""; }; - B0AA1F501F42E91A00BAE583 /* AvatarAlignment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarAlignment.swift; sourceTree = ""; }; - B0AA1F521F44388900BAE583 /* NSAttributedString+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Extensions.swift"; sourceTree = ""; }; - B0C8D99A1F73076B000A86E4 /* MessageKitAssets.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = MessageKitAssets.bundle; sourceTree = ""; }; - B0D943BD1F6DC9AB008B7BFD /* MediaMessageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaMessageCell.swift; sourceTree = ""; }; - B0E1756E1F655A1600F0DEF6 /* AvatarHorizontalAlignment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarHorizontalAlignment.swift; sourceTree = ""; }; - D88EBBDF1F85252E00F63AD2 /* MockMessagesDisplayDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMessagesDisplayDelegate.swift; sourceTree = ""; }; - E8586DB41F59A1C300C9BE9D /* MessageContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageContainerView.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 88916B1E1CF0DF2F00469F91 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 88916B291CF0DF2F00469F91 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 88916B2D1CF0DF2F00469F91 /* MessageKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 1919E5FF1F850A1100DE85BF /* ViewsTests */ = { - isa = PBXGroup; - children = ( - 1919E6001F850A1100DE85BF /* AvatarViewTests.swift */, - 1919E6011F850A1100DE85BF /* InputBarItemTests.swift */, - 1919E6021F850A1100DE85BF /* InputTextViewTests.swift */, - 1919E6031F850A1100DE85BF /* MessageCollectionViewCellTests.swift */, - 1919E6041F850A1100DE85BF /* MessageDateHeaderViewTests.swift */, - 1919E6051F850A1100DE85BF /* MessageInputBarTests.swift */, - 1919E6061F850A1100DE85BF /* MessagesCollectionViewTests.swift */, - ); - path = ViewsTests; - sourceTree = ""; - }; - 1919E6091F850A1100DE85BF /* ModelTests */ = { - isa = PBXGroup; - children = ( - 1919E60A1F850A1100DE85BF /* AvatarTests.swift */, - 1919E60B1F850A1100DE85BF /* DetectorTypeTests.swift */, - 1919E60C1F850A1100DE85BF /* MessageKitDateFormatterTests.swift */, - 1919E60E1F850A1100DE85BF /* MessageStyleTests.swift */, - 1919E60F1F850A1100DE85BF /* SenderTests.swift */, - ); - path = ModelTests; - sourceTree = ""; - }; - 1919E6101F850A1100DE85BF /* Supporting Files */ = { - isa = PBXGroup; - children = ( -<<<<<<< HEAD -||||||| merged common ancestors - 1919E6071F850A1100DE85BF /* TestMessageModel.swift */, - 1919E6081F850A1100DE85BF /* TestMessagesViewControllerModel.swift */, - D88EBBDF1F85252E00F63AD2 /* MockMessagesDisplayDelegate.swift */, -======= -<<<<<<< HEAD - 1919E6071F850A1100DE85BF /* TestMessageModel.swift */, - 1919E6081F850A1100DE85BF /* TestMessagesViewControllerModel.swift */, - D88EBBDF1F85252E00F63AD2 /* MockMessagesDisplayDelegate.swift */, -||||||| merged common ancestors - 1919E6071F850A1100DE85BF /* TestMessageModel.swift */, - 1919E6081F850A1100DE85BF /* TestMessagesViewControllerModel.swift */, -======= ->>>>>>> 1eedca87d925fc7c1a75859037a9fc2f422d4908 ->>>>>>> Add several tests - 1919E6111F850A1100DE85BF /* Info.plist */, - ); - path = "Supporting Files"; - sourceTree = ""; - }; - 195F03DB1F850C6200066E32 /* ControllersTest */ = { - isa = PBXGroup; - children = ( - 195F03DC1F850C7000066E32 /* MessagesViewControllerTests.swift */, - ); - path = ControllersTest; - sourceTree = ""; - }; - 195F03DF1F865B9D00066E32 /* Mocks */ = { - isa = PBXGroup; - children = ( - 195F03E01F865BD300066E32 /* MockMessage.swift */, - 195F03E21F865C3200066E32 /* MockMessagesDataSource.swift */, - ); - path = Mocks; - sourceTree = ""; - }; - 88916B181CF0DF2F00469F91 = { - isa = PBXGroup; - children = ( - 88916B3C1CF0DF5100469F91 /* Sources */, - 88916B411CF0DF5900469F91 /* Tests */, - 88916B231CF0DF2F00469F91 /* Products */, - ); - sourceTree = ""; - }; - 88916B231CF0DF2F00469F91 /* Products */ = { - isa = PBXGroup; - children = ( - 88916B221CF0DF2F00469F91 /* MessageKit.framework */, - 88916B2C1CF0DF2F00469F91 /* MessageKitTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 88916B3C1CF0DF5100469F91 /* Sources */ = { - isa = PBXGroup; - children = ( - B01E2DCC1F5BA34700E4FA1C /* Assets */, - B096439D1F295DC3004D0129 /* Layout */, - B09643991F295D58004D0129 /* Models */, - B096439A1F295D68004D0129 /* Views */, - B096439C1F295DA9004D0129 /* Controllers */, - B096439B1F295D82004D0129 /* Protocols */, - B09643981F295D43004D0129 /* Extensions */, - B096439E1F295DD7004D0129 /* Supporting */, - ); - path = Sources; - sourceTree = ""; - }; - 88916B411CF0DF5900469F91 /* Tests */ = { - isa = PBXGroup; - children = ( - 195F03DB1F850C6200066E32 /* ControllersTest */, - 195F03DF1F865B9D00066E32 /* Mocks */, - 1919E6091F850A1100DE85BF /* ModelTests */, - 1919E6101F850A1100DE85BF /* Supporting Files */, - D88EBBDC1F85249E00F63AD2 /* ProtocolsTests */, - 1919E5FF1F850A1100DE85BF /* ViewsTests */, - ); - path = Tests; - sourceTree = SOURCE_ROOT; - }; - B01E2DCC1F5BA34700E4FA1C /* Assets */ = { - isa = PBXGroup; - children = ( - B0C8D99A1F73076B000A86E4 /* MessageKitAssets.bundle */, - ); - name = Assets; - path = ../Assets; - sourceTree = ""; - }; - B09643981F295D43004D0129 /* Extensions */ = { - isa = PBXGroup; - children = ( - 38C867991F50EA1000811974 /* UIView+Extensions.swift */, - B09643851F286C9E004D0129 /* String+Extensions.swift */, - B096438F1F289142004D0129 /* UIColor+Extensions.swift */, - B0AA1F521F44388900BAE583 /* NSAttributedString+Extensions.swift */, - B0147C821F5BE9220035B36E /* Bundle+Extensions.swift */, - ); - name = Extensions; - sourceTree = ""; - }; - B09643991F295D58004D0129 /* Models */ = { - isa = PBXGroup; - children = ( - B0655A291F23D77200542A83 /* Sender.swift */, - B0655A2B1F23D81600542A83 /* MessageData.swift */, - 37C936971F38F6AC00853DF2 /* Avatar.swift */, - B0AA1F501F42E91A00BAE583 /* AvatarAlignment.swift */, - B05530B41F493CFA008BB420 /* DetectorType.swift */, - B01E2DD71F5BBDB800E4FA1C /* MessageStyle.swift */, - 38C867981F50EA1000811974 /* NSConstraintLayoutSet.swift */, - B0147C911F6002140035B36E /* MessageKitDateFormatter.swift */, - B0147C971F61AF910035B36E /* LabelAlignment.swift */, - B0E1756E1F655A1600F0DEF6 /* AvatarHorizontalAlignment.swift */, - B06D19361F70D3580020E416 /* LocationMessageSnapshotOptions.swift */, - ); - name = Models; - sourceTree = ""; - }; - B096439A1F295D68004D0129 /* Views */ = { - isa = PBXGroup; - children = ( - B0655A2D1F23D8BC00542A83 /* MessagesCollectionView.swift */, - B0655A4C1F244C0600542A83 /* MessageCollectionViewCell.swift */, - 372F6AEA1F36C15600B57FBD /* AvatarView.swift */, - B0655A371F23EE8B00542A83 /* MessageInputBar.swift */, - 38C8679D1F50EA4A00811974 /* InputBarItem.swift */, - 171D5AB81F36712B0053DF69 /* InputTextView.swift */, - B074EE921F35587100ABB8C8 /* MessageHeaderView.swift */, - B074EE941F35588A00ABB8C8 /* MessageFooterView.swift */, - B074EEA71F3971A600ABB8C8 /* MessageLabel.swift */, - E8586DB41F59A1C300C9BE9D /* MessageContainerView.swift */, - B0147C8F1F5ED0810035B36E /* MessageDateHeaderView.swift */, - B0291DA81F6DBB9F00BEDF03 /* TextMessageCell.swift */, - B0D943BD1F6DC9AB008B7BFD /* MediaMessageCell.swift */, - B010490F1F6F8684008DBA58 /* PlayButtonView.swift */, - B01049111F6F86E8008DBA58 /* LocationMessageCell.swift */, - ); - name = Views; - sourceTree = ""; - }; - B096439B1F295D82004D0129 /* Protocols */ = { - isa = PBXGroup; - children = ( - B015E81E1F259D8E007EDFB6 /* MessageInputBarDelegate.swift */, - 88916B461CF0DFE600469F91 /* MessageType.swift */, - 882D75831DE507320033F95F /* MessagesDataSource.swift */, - B096438D1F2890FB004D0129 /* MessagesDisplayDelegate.swift */, - B03FF9AE1F31BB1200754FE5 /* MessageCellDelegate.swift */, - B074EE961F355FBC00ABB8C8 /* MessagesLayoutDelegate.swift */, - B01280F21F4E8798004BCD3E /* MessageLabelDelegate.swift */, - B06D19321F70D2CA0020E416 /* LocationMessageLayoutDelegate.swift */, - B06D19341F70D3170020E416 /* MediaMessageLayoutDelegate.swift */, - ); - name = Protocols; - sourceTree = ""; - }; - B096439C1F295DA9004D0129 /* Controllers */ = { - isa = PBXGroup; - children = ( - 888CEBFB1D3FD525005178DE /* MessagesViewController.swift */, - ); - name = Controllers; - sourceTree = ""; - }; - B096439D1F295DC3004D0129 /* Layout */ = { - isa = PBXGroup; - children = ( - B0655A4E1F245C5A00542A83 /* MessagesCollectionViewFlowLayout.swift */, - B015E8181F24623D007EDFB6 /* MessagesCollectionViewLayoutAttributes.swift */, - ); - name = Layout; - sourceTree = ""; - }; - B096439E1F295DD7004D0129 /* Supporting */ = { - isa = PBXGroup; - children = ( - 88916B3D1CF0DF5100469F91 /* Info.plist */, - 88916B3E1CF0DF5100469F91 /* MessageKit.h */, - B0147C951F600E290035B36E /* MessageKit+Availability.swift */, - ); - name = Supporting; - sourceTree = ""; - }; - D88EBBDC1F85249E00F63AD2 /* ProtocolsTests */ = { - isa = PBXGroup; - children = ( - 1919E60D1F850A1100DE85BF /* MessagesDisplayDelegateTests.swift */, - ); - path = ProtocolsTests; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 88916B1F1CF0DF2F00469F91 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 88916B401CF0DF5100469F91 /* MessageKit.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 88916B211CF0DF2F00469F91 /* MessageKit */ = { - isa = PBXNativeTarget; - buildConfigurationList = 88916B361CF0DF2F00469F91 /* Build configuration list for PBXNativeTarget "MessageKit" */; - buildPhases = ( - 88916B1D1CF0DF2F00469F91 /* Sources */, - 88916B1E1CF0DF2F00469F91 /* Frameworks */, - 88916B1F1CF0DF2F00469F91 /* Headers */, - 88916B201CF0DF2F00469F91 /* Resources */, - B03FF9A51F30398900754FE5 /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = MessageKit; - productName = MessageKit; - productReference = 88916B221CF0DF2F00469F91 /* MessageKit.framework */; - productType = "com.apple.product-type.framework"; - }; - 88916B2B1CF0DF2F00469F91 /* MessageKitTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 88916B391CF0DF2F00469F91 /* Build configuration list for PBXNativeTarget "MessageKitTests" */; - buildPhases = ( - 88916B281CF0DF2F00469F91 /* Sources */, - 88916B291CF0DF2F00469F91 /* Frameworks */, - 88916B2A1CF0DF2F00469F91 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 88916B2F1CF0DF2F00469F91 /* PBXTargetDependency */, - ); - name = MessageKitTests; - productName = MessageKitTests; - productReference = 88916B2C1CF0DF2F00469F91 /* MessageKitTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 88916B191CF0DF2F00469F91 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0900; - ORGANIZATIONNAME = MessageKit; - TargetAttributes = { - 88916B211CF0DF2F00469F91 = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0800; - }; - 88916B2B1CF0DF2F00469F91 = { - CreatedOnToolsVersion = 7.3.1; - }; - }; - }; - buildConfigurationList = 88916B1C1CF0DF2F00469F91 /* Build configuration list for PBXProject "MessageKit" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = 88916B181CF0DF2F00469F91; - productRefGroup = 88916B231CF0DF2F00469F91 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 88916B211CF0DF2F00469F91 /* MessageKit */, - 88916B2B1CF0DF2F00469F91 /* MessageKitTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 88916B201CF0DF2F00469F91 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B0C8D99B1F73076B000A86E4 /* MessageKitAssets.bundle in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 88916B2A1CF0DF2F00469F91 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - B03FF9A51F30398900754FE5 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 88916B1D1CF0DF2F00469F91 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 171D5AB91F36712B0053DF69 /* InputTextView.swift in Sources */, - B06D19331F70D2CA0020E416 /* LocationMessageLayoutDelegate.swift in Sources */, - B0147C901F5ED0810035B36E /* MessageDateHeaderView.swift in Sources */, - 38C8679E1F50EA4A00811974 /* InputBarItem.swift in Sources */, - B01049121F6F86E8008DBA58 /* LocationMessageCell.swift in Sources */, - B074EE971F355FBC00ABB8C8 /* MessagesLayoutDelegate.swift in Sources */, - 38C8679A1F50EA1000811974 /* NSConstraintLayoutSet.swift in Sources */, - B06D19351F70D3170020E416 /* MediaMessageLayoutDelegate.swift in Sources */, - 195F03E11F865BD300066E32 /* MockMessage.swift in Sources */, - 882D75841DE507320033F95F /* MessagesDataSource.swift in Sources */, - 888CEBFC1D3FD525005178DE /* MessagesViewController.swift in Sources */, - B0147C831F5BE9220035B36E /* Bundle+Extensions.swift in Sources */, - B06D19371F70D3580020E416 /* LocationMessageSnapshotOptions.swift in Sources */, - E8586DB51F59A1C300C9BE9D /* MessageContainerView.swift in Sources */, - B0655A4F1F245C5A00542A83 /* MessagesCollectionViewFlowLayout.swift in Sources */, - B0655A2C1F23D81600542A83 /* MessageData.swift in Sources */, - B0E1756F1F655A1600F0DEF6 /* AvatarHorizontalAlignment.swift in Sources */, - B01049101F6F8684008DBA58 /* PlayButtonView.swift in Sources */, - B09643861F286C9E004D0129 /* String+Extensions.swift in Sources */, - B015E81F1F259D8E007EDFB6 /* MessageInputBarDelegate.swift in Sources */, - 195F03E31F865C3200066E32 /* MockMessagesDataSource.swift in Sources */, - B01280F31F4E8798004BCD3E /* MessageLabelDelegate.swift in Sources */, - B0147C981F61AF930035B36E /* LabelAlignment.swift in Sources */, - B0655A2A1F23D77200542A83 /* Sender.swift in Sources */, - B0147C961F600E290035B36E /* MessageKit+Availability.swift in Sources */, - B074EE931F35587100ABB8C8 /* MessageHeaderView.swift in Sources */, - B0655A4D1F244C0600542A83 /* MessageCollectionViewCell.swift in Sources */, - B0147C921F6002150035B36E /* MessageKitDateFormatter.swift in Sources */, - B0655A2E1F23D8BC00542A83 /* MessagesCollectionView.swift in Sources */, - B0AA1F531F44388C00BAE583 /* NSAttributedString+Extensions.swift in Sources */, - B074EE951F35588A00ABB8C8 /* MessageFooterView.swift in Sources */, - B09643901F289142004D0129 /* UIColor+Extensions.swift in Sources */, - B0655A381F23EE8B00542A83 /* MessageInputBar.swift in Sources */, - 38C8679B1F50EA1000811974 /* UIView+Extensions.swift in Sources */, - B074EEA81F3971A600ABB8C8 /* MessageLabel.swift in Sources */, - 372F6AEB1F36C15600B57FBD /* AvatarView.swift in Sources */, - D88EBBE01F85252E00F63AD2 /* MockMessagesDisplayDelegate.swift in Sources */, - B05530B51F493CFA008BB420 /* DetectorType.swift in Sources */, - B01E2DD81F5BBDB800E4FA1C /* MessageStyle.swift in Sources */, - 88916B471CF0DFE600469F91 /* MessageType.swift in Sources */, - B0D943BE1F6DC9AB008B7BFD /* MediaMessageCell.swift in Sources */, - B0291DA91F6DBB9F00BEDF03 /* TextMessageCell.swift in Sources */, - B0AA1F511F42E91A00BAE583 /* AvatarAlignment.swift in Sources */, - B03FF9AF1F31BB1200754FE5 /* MessageCellDelegate.swift in Sources */, - 37C936981F38F6AC00853DF2 /* Avatar.swift in Sources */, - B096438E1F2890FB004D0129 /* MessagesDisplayDelegate.swift in Sources */, - B015E8191F24623D007EDFB6 /* MessagesCollectionViewLayoutAttributes.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 88916B281CF0DF2F00469F91 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 1919E61F1F850A6A00DE85BF /* MessageInputBarTests.swift in Sources */, - 1919E6161F850A3E00DE85BF /* MessageStyleTests.swift in Sources */, - 1919E6121F850A3B00DE85BF /* AvatarTests.swift in Sources */, - 1919E61A1F850A6A00DE85BF /* AvatarViewTests.swift in Sources */, - 1919E61C1F850A6A00DE85BF /* InputTextViewTests.swift in Sources */, - 1919E6171F850A3E00DE85BF /* SenderTests.swift in Sources */, - 1919E6141F850A3E00DE85BF /* MessageKitDateFormatterTests.swift in Sources */, - 1919E61B1F850A6A00DE85BF /* InputBarItemTests.swift in Sources */, - 195F03DD1F850C7000066E32 /* MessagesViewControllerTests.swift in Sources */, - 1919E61E1F850A6A00DE85BF /* MessageDateHeaderViewTests.swift in Sources */, - 1919E6131F850A3E00DE85BF /* DetectorTypeTests.swift in Sources */, - 1919E61D1F850A6A00DE85BF /* MessageCollectionViewCellTests.swift in Sources */, - 1919E6151F850A3E00DE85BF /* MessagesDisplayDelegateTests.swift in Sources */, - 1919E6201F850A6A00DE85BF /* MessagesCollectionViewTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 88916B2F1CF0DF2F00469F91 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 88916B211CF0DF2F00469F91 /* MessageKit */; - targetProxy = 88916B2E1CF0DF2F00469F91 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 88916B341CF0DF2F00469F91 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 88916B351CF0DF2F00469F91 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SWIFT_VERSION = 4.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - 88916B371CF0DF2F00469F91 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.messagekit.MessageKit; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; - }; - name = Debug; - }; - 88916B381CF0DF2F00469F91 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.messagekit.MessageKit; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.0; - }; - name = Release; - }; - 88916B3A1CF0DF2F00469F91 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - INFOPLIST_FILE = "Tests/Supporting Files/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.MessageKitTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 88916B3B1CF0DF2F00469F91 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - INFOPLIST_FILE = "Tests/Supporting Files/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.hexedbits.MessageKitTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 88916B1C1CF0DF2F00469F91 /* Build configuration list for PBXProject "MessageKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 88916B341CF0DF2F00469F91 /* Debug */, - 88916B351CF0DF2F00469F91 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 88916B361CF0DF2F00469F91 /* Build configuration list for PBXNativeTarget "MessageKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 88916B371CF0DF2F00469F91 /* Debug */, - 88916B381CF0DF2F00469F91 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 88916B391CF0DF2F00469F91 /* Build configuration list for PBXNativeTarget "MessageKitTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 88916B3A1CF0DF2F00469F91 /* Debug */, - 88916B3B1CF0DF2F00469F91 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 88916B191CF0DF2F00469F91 /* Project object */; -} diff --git a/Sources/Controllers/MessagesViewController+Keyboard.swift b/Sources/Controllers/MessagesViewController+Keyboard.swift index 881b6aaa..ce6cf284 100644 --- a/Sources/Controllers/MessagesViewController+Keyboard.swift +++ b/Sources/Controllers/MessagesViewController+Keyboard.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,7 @@ */ import Foundation -import MessageInputBar +import InputBarAccessoryView extension MessagesViewController { diff --git a/Sources/Controllers/MessagesViewController+Menu.swift b/Sources/Controllers/MessagesViewController+Menu.swift index 49c87315..fc1cb5a2 100644 --- a/Sources/Controllers/MessagesViewController+Menu.swift +++ b/Sources/Controllers/MessagesViewController+Menu.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,6 @@ */ import Foundation -import MessageInputBar extension MessagesViewController { diff --git a/Sources/Controllers/MessagesViewController.swift b/Sources/Controllers/MessagesViewController.swift index ff43ffeb..706b9567 100644 --- a/Sources/Controllers/MessagesViewController.swift +++ b/Sources/Controllers/MessagesViewController.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,6 @@ */ import UIKit -import MessageInputBar /// A subclass of `UIViewController` with a `MessagesCollectionView` object /// that is used to display conversation interfaces. @@ -71,12 +70,12 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource { } } + public var selectedIndexPathForMenu: IndexPath? + private var isFirstLayout: Bool = true internal var isMessagesControllerBeingDismissed: Bool = false - internal var selectedIndexPathForMenu: IndexPath? - internal var messageCollectionViewBottomInset: CGFloat = 0 { didSet { messagesCollectionView.contentInset.bottom = messageCollectionViewBottomInset @@ -121,6 +120,13 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource { adjustScrollViewTopInset() } + open override func viewSafeAreaInsetsDidChange() { + if #available(iOS 11.0, *) { + super.viewSafeAreaInsetsDidChange() + } + messageCollectionViewBottomInset = requiredInitialScrollViewBottomInset() + } + // MARK: - Initializers deinit { @@ -208,6 +214,10 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource { let cell = messagesCollectionView.dequeueReusableCell(LocationMessageCell.self, for: indexPath) cell.configure(with: message, at: indexPath, and: messagesCollectionView) return cell + case .audio: + let cell = messagesCollectionView.dequeueReusableCell(AudioMessageCell.self, for: indexPath) + cell.configure(with: message, at: indexPath, and: messagesCollectionView) + return cell case .custom: return messagesDataSource.customCell(for: message, at: indexPath, in: messagesCollectionView) } diff --git a/Sources/Extensions/Bundle+Extensions.swift b/Sources/Extensions/Bundle+Extensions.swift index 0cda64b9..17dda2e8 100644 --- a/Sources/Extensions/Bundle+Extensions.swift +++ b/Sources/Extensions/Bundle+Extensions.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Extensions/CGRect+Extensions.swift b/Sources/Extensions/CGRect+Extensions.swift index 1a1177b5..17e4347e 100644 --- a/Sources/Extensions/CGRect+Extensions.swift +++ b/Sources/Extensions/CGRect+Extensions.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Extensions/NSAttributedString+Extensions.swift b/Sources/Extensions/NSAttributedString+Extensions.swift index a6a8f749..430a64c5 100644 --- a/Sources/Extensions/NSAttributedString+Extensions.swift +++ b/Sources/Extensions/NSAttributedString+Extensions.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Extensions/UIColor+Extensions.swift b/Sources/Extensions/UIColor+Extensions.swift index 79c4f4fc..02f5819c 100644 --- a/Sources/Extensions/UIColor+Extensions.swift +++ b/Sources/Extensions/UIColor+Extensions.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Extensions/UIEdgeInsets+Extensions.swift b/Sources/Extensions/UIEdgeInsets+Extensions.swift index 4d99e081..8e259f56 100644 --- a/Sources/Extensions/UIEdgeInsets+Extensions.swift +++ b/Sources/Extensions/UIEdgeInsets+Extensions.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Extensions/UIView+Extensions.swift b/Sources/Extensions/UIView+Extensions.swift index b38d39a5..8eb50bbf 100644 --- a/Sources/Extensions/UIView+Extensions.swift +++ b/Sources/Extensions/UIView+Extensions.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -65,7 +65,7 @@ extension UIView { } @discardableResult - internal func addConstraints(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0) -> [NSLayoutConstraint] { + internal func addConstraints(_ top: NSLayoutYAxisAnchor? = nil, left: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, right: NSLayoutXAxisAnchor? = nil, centerY: NSLayoutYAxisAnchor? = nil, centerX: NSLayoutXAxisAnchor? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, centerYConstant: CGFloat = 0, centerXConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0) -> [NSLayoutConstraint] { if self.superview == nil { return [] @@ -97,6 +97,18 @@ extension UIView { constraint.identifier = "right" constraints.append(constraint) } + + if let centerY = centerY { + let constraint = centerYAnchor.constraint(equalTo: centerY, constant: centerYConstant) + constraint.identifier = "centerY" + constraints.append(constraint) + } + + if let centerX = centerX { + let constraint = centerXAnchor.constraint(equalTo: centerX, constant: centerXConstant) + constraint.identifier = "centerX" + constraints.append(constraint) + } if widthConstant > 0 { let constraint = widthAnchor.constraint(equalToConstant: widthConstant) diff --git a/Sources/Layout/AudioMessageSizeCalculator.swift b/Sources/Layout/AudioMessageSizeCalculator.swift new file mode 100644 index 00000000..93700ede --- /dev/null +++ b/Sources/Layout/AudioMessageSizeCalculator.swift @@ -0,0 +1,43 @@ +/* + MIT License + + Copyright (c) 2017-2019 MessageKit + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + 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. + */ + +import Foundation + +open class AudioMessageSizeCalculator: MessageSizeCalculator { + + open override func messageContainerSize(for message: MessageType) -> CGSize { + switch message.kind { + case .audio(let item): + let maxWidth = messageContainerMaxWidth(for: message) + if maxWidth < item.size.width { + // Maintain the ratio if width is too great + let height = maxWidth * item.size.height / item.size.width + return CGSize(width: maxWidth, height: height) + } + return item.size + default: + fatalError("messageContainerSize received unhandled MessageDataType: \(message.kind)") + } + } +} diff --git a/Sources/Layout/CellSizeCalculator.swift b/Sources/Layout/CellSizeCalculator.swift index 7037b571..3bff16cb 100644 --- a/Sources/Layout/CellSizeCalculator.swift +++ b/Sources/Layout/CellSizeCalculator.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Layout/LocationMessageSizeCalculator.swift b/Sources/Layout/LocationMessageSizeCalculator.swift index 0efc0260..031281d4 100644 --- a/Sources/Layout/LocationMessageSizeCalculator.swift +++ b/Sources/Layout/LocationMessageSizeCalculator.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Layout/MediaMessageSizeCalculator.swift b/Sources/Layout/MediaMessageSizeCalculator.swift index 020bbd62..b7cff3d2 100644 --- a/Sources/Layout/MediaMessageSizeCalculator.swift +++ b/Sources/Layout/MediaMessageSizeCalculator.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Layout/MessageSizeCalculator.swift b/Sources/Layout/MessageSizeCalculator.swift index 0b0fee01..4cca7e1c 100644 --- a/Sources/Layout/MessageSizeCalculator.swift +++ b/Sources/Layout/MessageSizeCalculator.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -38,11 +38,16 @@ open class MessageSizeCalculator: CellSizeCalculator { public var incomingAvatarPosition = AvatarPosition(vertical: .cellBottom) public var outgoingAvatarPosition = AvatarPosition(vertical: .cellBottom) + public var avatarLeadingTrailingPadding: CGFloat = 0 + public var incomingMessagePadding = UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 30) public var outgoingMessagePadding = UIEdgeInsets(top: 0, left: 30, bottom: 0, right: 4) public var incomingCellTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero) public var outgoingCellTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero) + + public var incomingCellBottomLabelAlignment = LabelAlignment(textAlignment: .left, textInsets: UIEdgeInsets(left: 42)) + public var outgoingCellBottomLabelAlignment = LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(right: 42)) public var incomingMessageTopLabelAlignment = LabelAlignment(textAlignment: .left, textInsets: UIEdgeInsets(left: 42)) public var outgoingMessageTopLabelAlignment = LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(right: 42)) @@ -68,10 +73,13 @@ open class MessageSizeCalculator: CellSizeCalculator { attributes.avatarSize = avatarSize(for: message) attributes.avatarPosition = avatarPosition(for: message) + attributes.avatarLeadingTrailingPadding = avatarLeadingTrailingPadding attributes.messageContainerPadding = messageContainerPadding(for: message) attributes.messageContainerSize = messageContainerSize(for: message) attributes.cellTopLabelSize = cellTopLabelSize(for: message, at: indexPath) + attributes.cellBottomLabelSize = cellBottomLabelSize(for: message, at: indexPath) + attributes.cellBottomLabelAlignment = cellBottomLabelAlignment(for: message) attributes.messageTopLabelSize = messageTopLabelSize(for: message, at: indexPath) attributes.messageTopLabelAlignment = messageTopLabelAlignment(for: message) @@ -93,6 +101,7 @@ open class MessageSizeCalculator: CellSizeCalculator { open func cellContentHeight(for message: MessageType, at indexPath: IndexPath) -> CGFloat { let messageContainerHeight = messageContainerSize(for: message).height + let cellBottomLabelHeight = cellBottomLabelSize(for: message, at: indexPath).height let messageBottomLabelHeight = messageBottomLabelSize(for: message, at: indexPath).height let cellTopLabelHeight = cellTopLabelSize(for: message, at: indexPath).height let messageTopLabelHeight = messageTopLabelSize(for: message, at: indexPath).height @@ -104,12 +113,13 @@ open class MessageSizeCalculator: CellSizeCalculator { switch avatarVerticalPosition { case .messageCenter: let totalLabelHeight: CGFloat = cellTopLabelHeight + messageTopLabelHeight - + messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight + + messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight + cellBottomLabelHeight let cellHeight = max(avatarHeight, totalLabelHeight) return max(cellHeight, accessoryViewHeight) case .messageBottom: var cellHeight: CGFloat = 0 cellHeight += messageBottomLabelHeight + cellHeight += cellBottomLabelHeight let labelsHeight = messageContainerHeight + messageVerticalPadding + cellTopLabelHeight + messageTopLabelHeight cellHeight += max(labelsHeight, avatarHeight) return max(cellHeight, accessoryViewHeight) @@ -117,18 +127,18 @@ open class MessageSizeCalculator: CellSizeCalculator { var cellHeight: CGFloat = 0 cellHeight += cellTopLabelHeight cellHeight += messageTopLabelHeight - let labelsHeight = messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight + let labelsHeight = messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight + cellBottomLabelHeight cellHeight += max(labelsHeight, avatarHeight) return max(cellHeight, accessoryViewHeight) case .messageLabelTop: var cellHeight: CGFloat = 0 cellHeight += cellTopLabelHeight - let messageLabelsHeight = messageContainerHeight + messageBottomLabelHeight + messageVerticalPadding + messageTopLabelHeight + let messageLabelsHeight = messageContainerHeight + messageBottomLabelHeight + messageVerticalPadding + messageTopLabelHeight + cellBottomLabelHeight cellHeight += max(messageLabelsHeight, avatarHeight) return max(cellHeight, accessoryViewHeight) case .cellTop, .cellBottom: let totalLabelHeight: CGFloat = cellTopLabelHeight + messageTopLabelHeight - + messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight + + messageContainerHeight + messageVerticalPadding + messageBottomLabelHeight + cellBottomLabelHeight let cellHeight = max(avatarHeight, totalLabelHeight) return max(cellHeight, accessoryViewHeight) } @@ -185,8 +195,23 @@ open class MessageSizeCalculator: CellSizeCalculator { let isFromCurrentSender = dataSource.isFromCurrentSender(message: message) return isFromCurrentSender ? outgoingMessageTopLabelAlignment : incomingMessageTopLabelAlignment } + + // MARK: - Bottom cell Label + + open func cellBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize { + let layoutDelegate = messagesLayout.messagesLayoutDelegate + let collectionView = messagesLayout.messagesCollectionView + let height = layoutDelegate.cellBottomLabelHeight(for: message, at: indexPath, in: collectionView) + return CGSize(width: messagesLayout.itemWidth, height: height) + } + + open func cellBottomLabelAlignment(for message: MessageType) -> LabelAlignment { + let dataSource = messagesLayout.messagesDataSource + let isFromCurrentSender = dataSource.isFromCurrentSender(message: message) + return isFromCurrentSender ? outgoingCellBottomLabelAlignment : incomingCellBottomLabelAlignment + } - // MARK: - Bottom Label + // MARK: - Bottom Message Label open func messageBottomLabelSize(for message: MessageType, at indexPath: IndexPath) -> CGSize { let layoutDelegate = messagesLayout.messagesLayoutDelegate @@ -239,7 +264,7 @@ open class MessageSizeCalculator: CellSizeCalculator { let messagePadding = messageContainerPadding(for: message) let accessoryWidth = accessoryViewSize(for: message).width let accessoryPadding = accessoryViewPadding(for: message) - return messagesLayout.itemWidth - avatarWidth - messagePadding.horizontal - accessoryWidth - accessoryPadding.horizontal + return messagesLayout.itemWidth - avatarWidth - messagePadding.horizontal - accessoryWidth - accessoryPadding.horizontal - avatarLeadingTrailingPadding } // MARK: - Helpers diff --git a/Sources/Layout/MessagesCollectionViewFlowLayout.swift b/Sources/Layout/MessagesCollectionViewFlowLayout.swift index 7a0ba1cf..45b8e11b 100644 --- a/Sources/Layout/MessagesCollectionViewFlowLayout.swift +++ b/Sources/Layout/MessagesCollectionViewFlowLayout.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -146,6 +146,7 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout { lazy open var photoMessageSizeCalculator = MediaMessageSizeCalculator(layout: self) lazy open var videoMessageSizeCalculator = MediaMessageSizeCalculator(layout: self) lazy open var locationMessageSizeCalculator = LocationMessageSizeCalculator(layout: self) + lazy open var audioMessageSizeCalculator = AudioMessageSizeCalculator(layout: self) /// - Note: /// If you override this method, remember to call MessageLayoutDelegate's customCellSizeCalculator(for:at:in:) method for MessageKind.custom messages, if necessary @@ -164,6 +165,8 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout { return videoMessageSizeCalculator case .location: return locationMessageSizeCalculator + case .audio: + return audioMessageSizeCalculator case .custom: return messagesLayoutDelegate.customCellSizeCalculator(for: message, at: indexPath, in: messagesCollectionView) } @@ -193,6 +196,11 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout { public func setMessageOutgoingAvatarPosition(_ newPosition: AvatarPosition) { messageSizeCalculators().forEach { $0.outgoingAvatarPosition = newPosition } } + + /// Set `avatarLeadingTrailingPadding` of all `MessageSizeCalculator`s + public func setAvatarLeadingTrailingPadding(_ newPadding: CGFloat) { + messageSizeCalculators().forEach { $0.avatarLeadingTrailingPadding = newPadding } + } /// Set `incomingMessagePadding` of all `MessageSizeCalculator`s public func setMessageIncomingMessagePadding(_ newPadding: UIEdgeInsets) { @@ -214,6 +222,16 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout { messageSizeCalculators().forEach { $0.outgoingCellTopLabelAlignment = newAlignment } } + /// Set `incomingCellBottomLabelAlignment` of all `MessageSizeCalculator`s + public func setMessageIncomingCellBottomLabelAlignment(_ newAlignment: LabelAlignment) { + messageSizeCalculators().forEach { $0.incomingCellBottomLabelAlignment = newAlignment } + } + + /// Set `outgoingCellBottomLabelAlignment` of all `MessageSizeCalculator`s + public func setMessageOutgoingCellBottomLabelAlignment(_ newAlignment: LabelAlignment) { + messageSizeCalculators().forEach { $0.outgoingCellBottomLabelAlignment = newAlignment } + } + /// Set `incomingMessageTopLabelAlignment` of all `MessageSizeCalculator`s public func setMessageIncomingMessageTopLabelAlignment(_ newAlignment: LabelAlignment) { messageSizeCalculators().forEach { $0.incomingMessageTopLabelAlignment = newAlignment } @@ -266,7 +284,14 @@ open class MessagesCollectionViewFlowLayout: UICollectionViewFlowLayout { /// Get all `MessageSizeCalculator`s open func messageSizeCalculators() -> [MessageSizeCalculator] { - return [textMessageSizeCalculator, attributedTextMessageSizeCalculator, emojiMessageSizeCalculator, photoMessageSizeCalculator, videoMessageSizeCalculator, locationMessageSizeCalculator] + return [textMessageSizeCalculator, + attributedTextMessageSizeCalculator, + emojiMessageSizeCalculator, + photoMessageSizeCalculator, + videoMessageSizeCalculator, + locationMessageSizeCalculator, + audioMessageSizeCalculator + ] } } diff --git a/Sources/Layout/MessagesCollectionViewLayoutAttributes.swift b/Sources/Layout/MessagesCollectionViewLayoutAttributes.swift index 6ee95b5d..fc4541b0 100644 --- a/Sources/Layout/MessagesCollectionViewLayoutAttributes.swift +++ b/Sources/Layout/MessagesCollectionViewLayoutAttributes.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -31,6 +31,7 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu public var avatarSize: CGSize = .zero public var avatarPosition = AvatarPosition(vertical: .cellBottom) + public var avatarLeadingTrailingPadding: CGFloat = 0 public var messageContainerSize: CGSize = .zero public var messageContainerPadding: UIEdgeInsets = .zero @@ -40,6 +41,9 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu public var cellTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero) public var cellTopLabelSize: CGSize = .zero + public var cellBottomLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero) + public var cellBottomLabelSize: CGSize = .zero + public var messageTopLabelAlignment = LabelAlignment(textAlignment: .center, textInsets: .zero) public var messageTopLabelSize: CGSize = .zero @@ -57,12 +61,15 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu let copy = super.copy(with: zone) as! MessagesCollectionViewLayoutAttributes copy.avatarSize = avatarSize copy.avatarPosition = avatarPosition + copy.avatarLeadingTrailingPadding = avatarLeadingTrailingPadding copy.messageContainerSize = messageContainerSize copy.messageContainerPadding = messageContainerPadding copy.messageLabelFont = messageLabelFont copy.messageLabelInsets = messageLabelInsets copy.cellTopLabelAlignment = cellTopLabelAlignment copy.cellTopLabelSize = cellTopLabelSize + copy.cellBottomLabelAlignment = cellBottomLabelAlignment + copy.cellBottomLabelSize = cellBottomLabelSize copy.messageTopLabelAlignment = messageTopLabelAlignment copy.messageTopLabelSize = messageTopLabelSize copy.messageBottomLabelAlignment = messageBottomLabelAlignment @@ -78,13 +85,16 @@ open class MessagesCollectionViewLayoutAttributes: UICollectionViewLayoutAttribu // MARK: - LEAVE this as is if let attributes = object as? MessagesCollectionViewLayoutAttributes { return super.isEqual(object) && attributes.avatarSize == avatarSize - && attributes.avatarPosition == attributes.avatarPosition + && attributes.avatarPosition == avatarPosition + && attributes.avatarLeadingTrailingPadding == avatarLeadingTrailingPadding && attributes.messageContainerSize == messageContainerSize && attributes.messageContainerPadding == messageContainerPadding && attributes.messageLabelFont == messageLabelFont && attributes.messageLabelInsets == messageLabelInsets && attributes.cellTopLabelAlignment == cellTopLabelAlignment && attributes.cellTopLabelSize == cellTopLabelSize + && attributes.cellBottomLabelAlignment == cellBottomLabelAlignment + && attributes.cellBottomLabelSize == cellBottomLabelSize && attributes.messageTopLabelAlignment == messageTopLabelAlignment && attributes.messageTopLabelSize == messageTopLabelSize && attributes.messageBottomLabelAlignment == messageBottomLabelAlignment diff --git a/Sources/Layout/TextMessageSizeCalculator.swift b/Sources/Layout/TextMessageSizeCalculator.swift index 9f2b5cc1..65479f8d 100644 --- a/Sources/Layout/TextMessageSizeCalculator.swift +++ b/Sources/Layout/TextMessageSizeCalculator.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Models/Avatar.swift b/Sources/Models/Avatar.swift index 29fa1694..a9de31c1 100644 --- a/Sources/Models/Avatar.swift +++ b/Sources/Models/Avatar.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Models/AvatarPosition.swift b/Sources/Models/AvatarPosition.swift index e887e782..6421e48f 100644 --- a/Sources/Models/AvatarPosition.swift +++ b/Sources/Models/AvatarPosition.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Models/DetectorType.swift b/Sources/Models/DetectorType.swift index a49b7b6d..33d6c24f 100644 --- a/Sources/Models/DetectorType.swift +++ b/Sources/Models/DetectorType.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Models/HorizontalEdgeInsets.swift b/Sources/Models/HorizontalEdgeInsets.swift index 9ec2f0f5..7e92b030 100644 --- a/Sources/Models/HorizontalEdgeInsets.swift +++ b/Sources/Models/HorizontalEdgeInsets.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Models/LabelAlignment.swift b/Sources/Models/LabelAlignment.swift index 320971fc..2c6b0ba2 100644 --- a/Sources/Models/LabelAlignment.swift +++ b/Sources/Models/LabelAlignment.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Models/LocationMessageSnapshotOptions.swift b/Sources/Models/LocationMessageSnapshotOptions.swift index 21628fcb..ccef50c5 100644 --- a/Sources/Models/LocationMessageSnapshotOptions.swift +++ b/Sources/Models/LocationMessageSnapshotOptions.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Models/MessageKind.swift b/Sources/Models/MessageKind.swift index a29d76d5..78a971e6 100644 --- a/Sources/Models/MessageKind.swift +++ b/Sources/Models/MessageKind.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -51,6 +51,9 @@ public enum MessageKind { /// An emoji message. case emoji(String) + /// An audio message. + case audio(AudioItem) + /// A custom message. /// - Note: Using this case requires that you implement the following methods and handle this case: /// - MessagesDataSource: customCell(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UICollectionViewCell @@ -59,8 +62,6 @@ public enum MessageKind { // MARK: - Not supported yet -// case audio(Data) -// // case system(String) // // case placeholder diff --git a/Sources/Models/MessageKitDateFormatter.swift b/Sources/Models/MessageKitDateFormatter.swift index c0d02bc2..76e1f94e 100644 --- a/Sources/Models/MessageKitDateFormatter.swift +++ b/Sources/Models/MessageKitDateFormatter.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Models/MessageStyle.swift b/Sources/Models/MessageStyle.swift index f8abd50e..7814a2f5 100644 --- a/Sources/Models/MessageStyle.swift +++ b/Sources/Models/MessageStyle.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Models/NSConstraintLayoutSet.swift b/Sources/Models/NSConstraintLayoutSet.swift index cd063dfd..79140928 100644 --- a/Sources/Models/NSConstraintLayoutSet.swift +++ b/Sources/Models/NSConstraintLayoutSet.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Models/Sender.swift b/Sources/Models/Sender.swift index 8fc5e944..22ea0a1e 100644 --- a/Sources/Models/Sender.swift +++ b/Sources/Models/Sender.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25,7 +25,8 @@ import Foundation /// An object that groups the metadata of a messages sender. -public struct Sender { +@available(*, deprecated: 3.0.0, message: "`Sender` has been replaced with the `SenderType` protocol in 3.0.0") +public struct Sender: SenderType { /// MARK: - Properties @@ -44,14 +45,3 @@ public struct Sender { self.displayName = displayName } } - -// MARK: - Equatable Conformance - -extension Sender: Equatable { - - /// Two senders are considered equal if they have the same id. - public static func == (left: Sender, right: Sender) -> Bool { - return left.id == right.id - } - -} diff --git a/Sources/Protocols/AudioItem.swift b/Sources/Protocols/AudioItem.swift new file mode 100644 index 00000000..9e7ba659 --- /dev/null +++ b/Sources/Protocols/AudioItem.swift @@ -0,0 +1,39 @@ +/* + MIT License + + Copyright (c) 2017-2019 MessageKit + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + 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. + */ + +import class AVFoundation.AVAudioPlayer + +/// A protocol used to represent the data for an audio message. +public protocol AudioItem { + + /// The url where the audio file is located. + var url: URL { get } + + /// The audio file duration in seconds. + var duration: Float { get } + + /// The size of the audio item. + var size: CGSize { get } + +} diff --git a/Sources/Protocols/LocationItem.swift b/Sources/Protocols/LocationItem.swift index f08fccb3..51b4225a 100644 --- a/Sources/Protocols/LocationItem.swift +++ b/Sources/Protocols/LocationItem.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Protocols/MediaItem.swift b/Sources/Protocols/MediaItem.swift index cddb94c7..4242f376 100644 --- a/Sources/Protocols/MediaItem.swift +++ b/Sources/Protocols/MediaItem.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Protocols/MessageCellDelegate.swift b/Sources/Protocols/MessageCellDelegate.swift index 4fbe9da9..dd07f5d6 100644 --- a/Sources/Protocols/MessageCellDelegate.swift +++ b/Sources/Protocols/MessageCellDelegate.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,6 +27,17 @@ import Foundation /// A protocol used by `MessageContentCell` subclasses to detect taps in the cell's subviews. public protocol MessageCellDelegate: MessageLabelDelegate { + /// Triggered when a tap occurs in the background of the cell. + /// + /// - Parameters: + /// - cell: The cell where the tap occurred. + /// + /// - Note: + /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s + /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource` + /// method `messageForItem(at:indexPath:messagesCollectionView)`. + func didTapBackground(in cell: MessageCollectionViewCell) + /// Triggered when a tap occurs in the `MessageContainerView`. /// /// - Parameters: @@ -58,6 +69,16 @@ public protocol MessageCellDelegate: MessageLabelDelegate { /// method `messageForItem(at:indexPath:messagesCollectionView)`. func didTapCellTopLabel(in cell: MessageCollectionViewCell) + /// Triggered when a tap occurs in the cellBottomLabel. + /// + /// - Parameters: + /// - cell: The cell tap the touch occurred. + /// + /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s + /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource` + /// method `messageForItem(at:indexPath:messagesCollectionView)`. + func didTapCellBottomLabel(in cell: MessageCollectionViewCell) + /// Triggered when a tap occurs in the messageTopLabel. /// /// - Parameters: @@ -88,19 +109,72 @@ public protocol MessageCellDelegate: MessageLabelDelegate { /// method `messageForItem(at:indexPath:messagesCollectionView)`. func didTapAccessoryView(in cell: MessageCollectionViewCell) + /// Triggered when a tap occurs on the play button from audio cell. + /// + /// - Parameters: + /// - cell: The audio cell where the touch occurred. + /// + /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s + /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource` + /// method `messageForItem(at:indexPath:messagesCollectionView)`. + func didTapPlayButton(in cell: AudioMessageCell) + + /// Triggered when audio player start playing audio. + /// + /// - Parameters: + /// - cell: The cell where the audio sound is playing. + /// + /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s + /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource` + /// method `messageForItem(at:indexPath:messagesCollectionView)`. + func didStartAudio(in cell: AudioMessageCell) + + /// Triggered when audio player pause audio. + /// + /// - Parameters: + /// - cell: The cell where the audio sound is paused. + /// + /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s + /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource` + /// method `messageForItem(at:indexPath:messagesCollectionView)`. + func didPauseAudio(in cell: AudioMessageCell) + + /// Triggered when audio player stoped audio. + /// + /// - Parameters: + /// - cell: The cell where the audio sound is stoped. + /// + /// You can get a reference to the `MessageType` for the cell by using `UICollectionView`'s + /// `indexPath(for: cell)` method. Then using the returned `IndexPath` with the `MessagesDataSource` + /// method `messageForItem(at:indexPath:messagesCollectionView)`. + func didStopAudio(in cell: AudioMessageCell) + } public extension MessageCellDelegate { + func didTapBackground(in cell: MessageCollectionViewCell) {} + func didTapMessage(in cell: MessageCollectionViewCell) {} func didTapAvatar(in cell: MessageCollectionViewCell) {} func didTapCellTopLabel(in cell: MessageCollectionViewCell) {} + + func didTapCellBottomLabel(in cell: MessageCollectionViewCell) {} func didTapMessageTopLabel(in cell: MessageCollectionViewCell) {} + func didTapPlayButton(in cell: AudioMessageCell) {} + + func didStartAudio(in cell: AudioMessageCell) {} + + func didPauseAudio(in cell: AudioMessageCell) {} + + func didStopAudio(in cell: AudioMessageCell) {} + func didTapMessageBottomLabel(in cell: MessageCollectionViewCell) {} func didTapAccessoryView(in cell: MessageCollectionViewCell) {} + } diff --git a/Sources/Protocols/MessageLabelDelegate.swift b/Sources/Protocols/MessageLabelDelegate.swift index e84ea622..06a123e8 100644 --- a/Sources/Protocols/MessageLabelDelegate.swift +++ b/Sources/Protocols/MessageLabelDelegate.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Protocols/MessageType.swift b/Sources/Protocols/MessageType.swift index 627b7f50..664be32a 100644 --- a/Sources/Protocols/MessageType.swift +++ b/Sources/Protocols/MessageType.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import Foundation public protocol MessageType { /// The sender of the message. - var sender: Sender { get } + var sender: SenderType { get } /// The unique identifier for the message. var messageId: String { get } diff --git a/Sources/Protocols/MessagesDataSource.swift b/Sources/Protocols/MessagesDataSource.swift index 3caba08b..0c58a813 100644 --- a/Sources/Protocols/MessagesDataSource.swift +++ b/Sources/Protocols/MessagesDataSource.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -28,17 +28,17 @@ import UIKit /// the data required by a `MessagesCollectionView`. public protocol MessagesDataSource: AnyObject { - /// The `Sender` of new messages in the `MessagesCollectionView`. - func currentSender() -> Sender + /// The `SenderType` of new messages in the `MessagesCollectionView`. + func currentSender() -> SenderType - /// A helper method to determine if a given message is from the current `Sender`. + /// A helper method to determine if a given message is from the current `SenderType`. /// /// - Parameters: - /// - message: The message to check if it was sent by the current `Sender`. + /// - message: The message to check if it was sent by the current `SenderType`. /// /// - Note: /// The default implementation of this method checks for equality between - /// the message's `Sender` and the current `Sender`. + /// the message's `SenderType` and the current `SenderType`. func isFromCurrentSender(message: MessageType) -> Bool /// The message to be used for a `MessageCollectionViewCell` at the given `IndexPath`. @@ -73,6 +73,16 @@ public protocol MessagesDataSource: AnyObject { /// The default value returned by this method is `nil`. func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? + /// The attributed text to be used for cell's bottom label. + /// + /// - Parameters: + /// - message: The `MessageType` that will be displayed by this cell. + /// - indexPath: The `IndexPath` of the cell. + /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. + /// + /// The default value returned by this method is `nil`. + func cellBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? + /// The attributed text to be used for message bubble's top label. /// /// - Parameters: @@ -108,7 +118,7 @@ public protocol MessagesDataSource: AnyObject { public extension MessagesDataSource { func isFromCurrentSender(message: MessageType) -> Bool { - return message.sender == currentSender() + return message.sender.id == currentSender().id } func numberOfItems(inSection section: Int, in messagesCollectionView: MessagesCollectionView) -> Int { @@ -119,6 +129,10 @@ public extension MessagesDataSource { return nil } + func cellBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? { + return nil + } + func messageTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? { return nil } diff --git a/Sources/Protocols/MessagesDisplayDelegate.swift b/Sources/Protocols/MessagesDisplayDelegate.swift index 2af2eac5..29d43e80 100644 --- a/Sources/Protocols/MessagesDisplayDelegate.swift +++ b/Sources/Protocols/MessagesDisplayDelegate.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -50,7 +50,7 @@ public protocol MessagesDisplayDelegate: AnyObject { /// /// - Note: /// The default value is `UIColor.clear` for emoji messages. - /// For all other `MessageKind` cases, the color depends on the `Sender`. + /// For all other `MessageKind` cases, the color depends on the `SenderType`. /// /// Current sender: Green /// @@ -106,7 +106,7 @@ public protocol MessagesDisplayDelegate: AnyObject { /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. /// /// - Note: - /// The default value returned by this method is determined by the messages `Sender`. + /// The default value returned by this method is determined by the messages `SenderType`. /// /// Current sender: UIColor.white /// @@ -173,6 +173,46 @@ public protocol MessagesDisplayDelegate: AnyObject { /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. func configureMediaMessageImageView(_ imageView: UIImageView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) + // MARK: - Audio Message + + /// Used to configure the audio cell UI: + /// 1. play button selected state; + /// 2. progresssView progress; + /// 3. durationLabel text; + /// + /// - Parameters: + /// - cell: The `AudioMessageCell` that needs to be configure. + /// - message: The `MessageType` that configures the cell. + /// + /// - Note: + /// This protocol method is called by MessageKit every time an audio cell needs to be configure + func configureAudioCell(_ cell: AudioMessageCell, message: MessageType) + + /// Specifies the tint color of play button and progress bar for an `AudioMessageCell`. + /// + /// - Parameters: + /// - message: A `MessageType` with a `MessageKind` case of `.audio` to which the color will apply. + /// - indexPath: The `IndexPath` of the cell. + /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. + /// + /// - Note: + /// The default value returned by this method is UIColor.sendButtonBlue + func audioTintColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor + + /// Used to format the audio sound duration in a readable string + /// + /// - Parameters: + /// - duration: The audio sound duration is seconds. + /// - audioCell: The `AudioMessageCell` that ask for formated duration. + /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. + /// + /// - Note: + /// The default value is computed like fallow: + /// 1. return the time as 0:ss if duration is up to 59 seconds (e.g. 0:03 means 0 minutes and 3 seconds) + /// 2. return the time as m:ss if duration is greater than 59 and lower than 3600 (e.g. 12:23 means 12 mintues and 23 seconds) + /// 3. return the time as h:mm:ss for anything longer that 3600 seconds (e.g. 1:19:08 means 1 hour 19 minutes and 8 seconds) + func audioProgressTextFormat(_ duration: Float, for audioCell: AudioMessageCell, in messageCollectionView: MessagesCollectionView) -> String + } public extension MessagesDisplayDelegate { @@ -243,4 +283,32 @@ public extension MessagesDisplayDelegate { func configureMediaMessageImageView(_ imageView: UIImageView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) { } + + // MARK: - Audio Message Defaults + + func configureAudioCell(_ cell: AudioMessageCell, message: MessageType) { + + } + + func audioTintColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor { + return UIColor.sendButtonBlue + } + + func audioProgressTextFormat(_ duration: Float, for audioCell: AudioMessageCell, in messageCollectionView: MessagesCollectionView) -> String { + var retunValue = "0:00" + // print the time as 0:ss if duration is up to 59 seconds + // print the time as m:ss if duration is up to 59:59 seconds + // print the time as h:mm:ss for anything longer + if duration < 60 { + retunValue = String(format: "0:%.02d", Int(duration.rounded(.up))) + } else if duration < 3600 { + retunValue = String(format: "%.02d:%.02d", Int(duration/60), Int(duration) % 60) + } else { + let hours = Int(duration/3600) + let remainingMinutsInSeconds = Int(duration) - hours*3600 + retunValue = String(format: "%.02d:%.02d:%.02d", hours, Int(remainingMinutsInSeconds/60), Int(remainingMinutsInSeconds) % 60) + } + return retunValue + } + } diff --git a/Sources/Protocols/MessagesLayoutDelegate.swift b/Sources/Protocols/MessagesLayoutDelegate.swift index 4dd75743..57c4ec33 100644 --- a/Sources/Protocols/MessagesLayoutDelegate.swift +++ b/Sources/Protocols/MessagesLayoutDelegate.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -59,6 +59,17 @@ public protocol MessagesLayoutDelegate: AnyObject { /// The default value returned by this method is zero. func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat + /// Specifies the height for the `MessageContentCell`'s bottom label. + /// + /// - Parameters: + /// - message: The `MessageType` that will be displayed for this cell. + /// - indexPath: The `IndexPath` of the cell. + /// - messagesCollectionView: The `MessagesCollectionView` in which this cell will be displayed. + /// + /// - Note: + /// The default value returned by this method is zero. + func cellBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat + /// Specifies the height for the message bubble's top label. /// /// - Parameters: @@ -107,6 +118,10 @@ public extension MessagesLayoutDelegate { return 0 } + func cellBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat { + return 0 + } + func messageTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat { return 0 } diff --git a/Sources/Protocols/SenderType.swift b/Sources/Protocols/SenderType.swift new file mode 100644 index 00000000..82f80e28 --- /dev/null +++ b/Sources/Protocols/SenderType.swift @@ -0,0 +1,38 @@ +/* + MIT License + + Copyright (c) 2017-2019 MessageKit + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + 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. + */ + +import Foundation + +/// A standard protocol representing a sender. +/// Use this protocol to adhere a object as the sender of a MessageType +public protocol SenderType { + + /// The unique String identifier for the sender. + /// + /// Note: This value must be unique across all senders. + var id: String { get } + + /// The display name of a sender. + var displayName: String { get } +} diff --git a/Sources/Supporting/Info.plist b/Sources/Supporting/Info.plist index 60b9c008..e0f4bf77 100644 --- a/Sources/Supporting/Info.plist +++ b/Sources/Supporting/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0.0 + 3.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/Sources/Supporting/MessageInputBar.swift b/Sources/Supporting/MessageInputBar.swift new file mode 100644 index 00000000..a7db22e6 --- /dev/null +++ b/Sources/Supporting/MessageInputBar.swift @@ -0,0 +1,37 @@ +/* + MIT License + + Copyright (c) 2017-2019 MessageKit + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + 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. + */ + +import UIKit +import InputBarAccessoryView + +public typealias MessageInputBar = InputBarAccessoryView +public typealias MessageInputBarDelegate = InputBarAccessoryViewDelegate + +extension InputBarButtonItem { + + @available(*, deprecated: 3.0.0, message: "`messageInputBar` has been replaced with `inputBarAccessoryView` in 3.0.0. This is due to a dependency change from MessageInputBar to InputBarAccessoryView") + public var messageInputBar: MessageInputBar? { + return inputBarAccessoryView + } +} diff --git a/Sources/Supporting/MessageKit+Availability.swift b/Sources/Supporting/MessageKit+Availability.swift index 735409e3..16d40626 100644 --- a/Sources/Supporting/MessageKit+Availability.swift +++ b/Sources/Supporting/MessageKit+Availability.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Views/AvatarView.swift b/Sources/Views/AvatarView.swift index cf3c1456..6563143e 100644 --- a/Sources/Views/AvatarView.swift +++ b/Sources/Views/AvatarView.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Views/Cells/AudioMessageCell.swift b/Sources/Views/Cells/AudioMessageCell.swift new file mode 100644 index 00000000..295aad3c --- /dev/null +++ b/Sources/Views/Cells/AudioMessageCell.swift @@ -0,0 +1,143 @@ +/* + MIT License + + Copyright (c) 2017-2019 MessageKit + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + 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. + */ + +import UIKit +import AVFoundation + +/// A subclass of `MessageContentCell` used to display video and audio messages. +open class AudioMessageCell: MessageContentCell { + + /// The `ImageName` enum holds the names of default iamges used to decorate play button + public enum ImageName: String { + case play + case pause + } + + /// The play button view to display on audio messages. + public lazy var playButton: UIButton = { + let playButton = UIButton(type: .custom) + let playImage = AudioMessageCell.getImageWithName(.play) + let pauseImage = AudioMessageCell.getImageWithName(.pause) + playButton.setImage(playImage?.withRenderingMode(.alwaysTemplate), for: .normal) + playButton.setImage(pauseImage?.withRenderingMode(.alwaysTemplate), for: .selected) + return playButton + }() + + /// The time duration lable to display on audio messages. + public lazy var durationLabel: UILabel = { + let durationLabel = UILabel(frame: CGRect.zero) + durationLabel.textAlignment = .right + durationLabel.font = UIFont.systemFont(ofSize: 14) + durationLabel.text = "0:00" + return durationLabel + }() + + public lazy var progressView: UIProgressView = { + let progressView = UIProgressView(progressViewStyle: .default) + progressView.progress = 0.0 + return progressView + }() + + // MARK: - Methods + + /// Responsible for setting up the constraints of the cell's subviews. + open func setupConstraints() { + playButton.constraint(equalTo: CGSize(width: 25, height: 25)) + playButton.addConstraints(left: messageContainerView.leftAnchor, centerY: messageContainerView.centerYAnchor, leftConstant: 5) + durationLabel.addConstraints(right: messageContainerView.rightAnchor, centerY: messageContainerView.centerYAnchor, rightConstant: 15) + progressView.addConstraints(left: playButton.rightAnchor, right: durationLabel.leftAnchor, centerY: messageContainerView.centerYAnchor, leftConstant: 5, rightConstant: 5) + } + + open override func setupSubviews() { + super.setupSubviews() + messageContainerView.addSubview(playButton) + messageContainerView.addSubview(durationLabel) + messageContainerView.addSubview(progressView) + setupConstraints() + } + + open override func prepareForReuse() { + super.prepareForReuse() + progressView.progress = 0 + playButton.isSelected = false + durationLabel.text = "0:00" + } + + open class func getImageWithName(_ imageName: ImageName) -> UIImage? { + let assetBundle = Bundle.messageKitAssetBundle() + let imagePath = assetBundle.path(forResource: imageName.rawValue, ofType: "png", inDirectory: "Images") + let image = UIImage(contentsOfFile: imagePath ?? "") + return image + } + + /// Handle tap gesture on contentView and its subviews. + open override func handleTapGesture(_ gesture: UIGestureRecognizer) { + let touchLocation = gesture.location(in: self) + // compute play button touch area, currently play button size is (25, 25) which is hardly touchable + // add 10 px around current button frame and test the touch against this new frame + let playButtonTouchArea = CGRect(playButton.frame.origin.x - 10.0, playButton.frame.origin.y - 10, playButton.frame.size.width + 20, playButton.frame.size.height + 20) + let translateTouchLocation = convert(touchLocation, to: messageContainerView) + if playButtonTouchArea.contains(translateTouchLocation) { + delegate?.didTapPlayButton(in: self) + } else { + super.handleTapGesture(gesture) + } + } + + // MARK: - Configure Cell + + open override func configure(with message: MessageType, at indexPath: IndexPath, and messagesCollectionView: MessagesCollectionView) { + super.configure(with: message, at: indexPath, and: messagesCollectionView) + + guard let dataSource = messagesCollectionView.messagesDataSource else { + fatalError(MessageKitError.nilMessagesDataSource) + } + + let playButtonLeftConstraint = messageContainerView.constraints.filter { $0.identifier == "left" }.first + let durationLabelRightConstraint = messageContainerView.constraints.filter { $0.identifier == "right" }.first + + if !dataSource.isFromCurrentSender(message: message) { + playButtonLeftConstraint?.constant = 12 + durationLabelRightConstraint?.constant = -8 + } else { + playButtonLeftConstraint?.constant = 5 + durationLabelRightConstraint?.constant = -15 + } + + guard let displayDelegate = messagesCollectionView.messagesDisplayDelegate else { + fatalError(MessageKitError.nilMessagesDisplayDelegate) + } + + let tintColor = displayDelegate.audioTintColor(for: message, at: indexPath, in: messagesCollectionView) + playButton.imageView?.tintColor = tintColor + durationLabel.textColor = tintColor + progressView.tintColor = tintColor + + displayDelegate.configureAudioCell(self, message: message) + + if case let .audio(audioItem) = message.kind { + durationLabel.text = displayDelegate.audioProgressTextFormat(audioItem.duration, for: self, in: messagesCollectionView) + } + } +} diff --git a/Sources/Views/Cells/LocationMessageCell.swift b/Sources/Views/Cells/LocationMessageCell.swift index ed84ed75..23dcfb22 100644 --- a/Sources/Views/Cells/LocationMessageCell.swift +++ b/Sources/Views/Cells/LocationMessageCell.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Views/Cells/MediaMessageCell.swift b/Sources/Views/Cells/MediaMessageCell.swift index 94f885e8..d7e1967b 100644 --- a/Sources/Views/Cells/MediaMessageCell.swift +++ b/Sources/Views/Cells/MediaMessageCell.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -55,6 +55,11 @@ open class MediaMessageCell: MessageContentCell { messageContainerView.addSubview(playButtonView) setupConstraints() } + + open override func prepareForReuse() { + super.prepareForReuse() + self.imageView.image = nil + } open override func configure(with message: MessageType, at indexPath: IndexPath, and messagesCollectionView: MessagesCollectionView) { super.configure(with: message, at: indexPath, and: messagesCollectionView) diff --git a/Sources/Views/Cells/MessageCollectionViewCell.swift b/Sources/Views/Cells/MessageCollectionViewCell.swift index 08114cb5..51855a1c 100644 --- a/Sources/Views/Cells/MessageCollectionViewCell.swift +++ b/Sources/Views/Cells/MessageCollectionViewCell.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -37,4 +37,9 @@ open class MessageCollectionViewCell: UICollectionViewCell { super.init(coder: aDecoder) } + /// Handle tap gesture on contentView and its subviews. + open func handleTapGesture(_ gesture: UIGestureRecognizer) { + // Should be overridden + } + } diff --git a/Sources/Views/Cells/MessageContentCell.swift b/Sources/Views/Cells/MessageContentCell.swift index 4abb2011..9fdfefeb 100644 --- a/Sources/Views/Cells/MessageContentCell.swift +++ b/Sources/Views/Cells/MessageContentCell.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -46,6 +46,14 @@ open class MessageContentCell: MessageCollectionViewCell { return label }() + /// The bottom label of the cell. + open var cellBottomLabel: InsetLabel = { + let label = InsetLabel() + label.numberOfLines = 0 + label.textAlignment = .center + return label + }() + /// The top label of the messageBubble. open var messageTopLabel: InsetLabel = { let label = InsetLabel() @@ -83,6 +91,7 @@ open class MessageContentCell: MessageCollectionViewCell { contentView.addSubview(cellTopLabel) contentView.addSubview(messageTopLabel) contentView.addSubview(messageBottomLabel) + contentView.addSubview(cellBottomLabel) contentView.addSubview(messageContainerView) contentView.addSubview(avatarView) } @@ -90,6 +99,7 @@ open class MessageContentCell: MessageCollectionViewCell { open override func prepareForReuse() { super.prepareForReuse() cellTopLabel.text = nil + cellBottomLabel.text = nil messageTopLabel.text = nil messageBottomLabel.text = nil } @@ -101,7 +111,8 @@ open class MessageContentCell: MessageCollectionViewCell { guard let attributes = layoutAttributes as? MessagesCollectionViewLayoutAttributes else { return } // Call this before other laying out other subviews layoutMessageContainerView(with: attributes) - layoutBottomLabel(with: attributes) + layoutMessageBottomLabel(with: attributes) + layoutCellBottomLabel(with: attributes) layoutCellTopLabel(with: attributes) layoutMessageTopLabel(with: attributes) layoutAvatarView(with: attributes) @@ -135,16 +146,18 @@ open class MessageContentCell: MessageCollectionViewCell { messageContainerView.style = messageStyle let topCellLabelText = dataSource.cellTopLabelAttributedText(for: message, at: indexPath) + let bottomCellLabelText = dataSource.cellBottomLabelAttributedText(for: message, at: indexPath) let topMessageLabelText = dataSource.messageTopLabelAttributedText(for: message, at: indexPath) - let bottomText = dataSource.messageBottomLabelAttributedText(for: message, at: indexPath) + let bottomMessageLabelText = dataSource.messageBottomLabelAttributedText(for: message, at: indexPath) cellTopLabel.attributedText = topCellLabelText + cellBottomLabel.attributedText = bottomCellLabelText messageTopLabel.attributedText = topMessageLabelText - messageBottomLabel.attributedText = bottomText + messageBottomLabel.attributedText = bottomMessageLabelText } /// Handle tap gesture on contentView and its subviews. - open func handleTapGesture(_ gesture: UIGestureRecognizer) { + open override func handleTapGesture(_ gesture: UIGestureRecognizer) { let touchLocation = gesture.location(in: self) switch true { @@ -154,6 +167,8 @@ open class MessageContentCell: MessageCollectionViewCell { delegate?.didTapAvatar(in: self) case cellTopLabel.frame.contains(touchLocation): delegate?.didTapCellTopLabel(in: self) + case cellBottomLabel.frame.contains(touchLocation): + delegate?.didTapCellBottomLabel(in: self) case messageTopLabel.frame.contains(touchLocation): delegate?.didTapMessageTopLabel(in: self) case messageBottomLabel.frame.contains(touchLocation): @@ -161,7 +176,7 @@ open class MessageContentCell: MessageCollectionViewCell { case accessoryView.frame.contains(touchLocation): delegate?.didTapAccessoryView(in: self) default: - break + delegate?.didTapBackground(in: self) } } @@ -183,12 +198,13 @@ open class MessageContentCell: MessageCollectionViewCell { /// - attributes: The `MessagesCollectionViewLayoutAttributes` for the cell. open func layoutAvatarView(with attributes: MessagesCollectionViewLayoutAttributes) { var origin: CGPoint = .zero + let padding = attributes.avatarLeadingTrailingPadding switch attributes.avatarPosition.horizontal { case .cellLeading: - break + origin.x = padding case .cellTrailing: - origin.x = attributes.frame.width - attributes.avatarSize.width + origin.x = attributes.frame.width - attributes.avatarSize.width - padding case .natural: fatalError(MessageKitError.avatarPositionUnresolved) } @@ -218,7 +234,7 @@ open class MessageContentCell: MessageCollectionViewCell { switch attributes.avatarPosition.vertical { case .messageBottom: - origin.y = attributes.size.height - attributes.messageContainerPadding.bottom - attributes.messageBottomLabelSize.height - attributes.messageContainerSize.height - attributes.messageContainerPadding.top + origin.y = attributes.size.height - attributes.messageContainerPadding.bottom - attributes.cellBottomLabelSize.height - attributes.messageBottomLabelSize.height - attributes.messageContainerSize.height - attributes.messageContainerPadding.top case .messageCenter: if attributes.avatarSize.height > attributes.messageContainerSize.height { let messageHeight = attributes.messageContainerSize.height + attributes.messageContainerPadding.vertical @@ -235,11 +251,12 @@ open class MessageContentCell: MessageCollectionViewCell { } } + let avatarPadding = attributes.avatarLeadingTrailingPadding switch attributes.avatarPosition.horizontal { case .cellLeading: - origin.x = attributes.avatarSize.width + attributes.messageContainerPadding.left + origin.x = attributes.avatarSize.width + attributes.messageContainerPadding.left + avatarPadding case .cellTrailing: - origin.x = attributes.frame.width - attributes.avatarSize.width - attributes.messageContainerSize.width - attributes.messageContainerPadding.right + origin.x = attributes.frame.width - attributes.avatarSize.width - attributes.messageContainerSize.width - attributes.messageContainerPadding.right - avatarPadding case .natural: fatalError(MessageKitError.avatarPositionUnresolved) } @@ -253,6 +270,18 @@ open class MessageContentCell: MessageCollectionViewCell { cellTopLabel.frame = CGRect(origin: .zero, size: attributes.cellTopLabelSize) } + /// Positions the cell's bottom label. + /// - attributes: The `MessagesCollectionViewLayoutAttributes` for the cell. + open func layoutCellBottomLabel(with attributes: MessagesCollectionViewLayoutAttributes) { + cellBottomLabel.textAlignment = attributes.cellBottomLabelAlignment.textAlignment + cellBottomLabel.textInsets = attributes.cellBottomLabelAlignment.textInsets + + let y = messageBottomLabel.frame.maxY + let origin = CGPoint(x: 0, y: y) + + cellBottomLabel.frame = CGRect(origin: origin, size: attributes.cellBottomLabelSize) + } + /// Positions the message bubble's top label. /// - attributes: The `MessagesCollectionViewLayoutAttributes` for the cell. open func layoutMessageTopLabel(with attributes: MessagesCollectionViewLayoutAttributes) { @@ -265,9 +294,9 @@ open class MessageContentCell: MessageCollectionViewCell { messageTopLabel.frame = CGRect(origin: origin, size: attributes.messageTopLabelSize) } - /// Positions the cell's bottom label. + /// Positions the message bubble's bottom label. /// - attributes: The `MessagesCollectionViewLayoutAttributes` for the cell. - open func layoutBottomLabel(with attributes: MessagesCollectionViewLayoutAttributes) { + open func layoutMessageBottomLabel(with attributes: MessagesCollectionViewLayoutAttributes) { messageBottomLabel.textAlignment = attributes.messageBottomLabelAlignment.textAlignment messageBottomLabel.textInsets = attributes.messageBottomLabelAlignment.textInsets diff --git a/Sources/Views/Cells/TextMessageCell.swift b/Sources/Views/Cells/TextMessageCell.swift index 481411ba..e93675be 100644 --- a/Sources/Views/Cells/TextMessageCell.swift +++ b/Sources/Views/Cells/TextMessageCell.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Views/Headers & Footers/MessageReusableView.swift b/Sources/Views/Headers & Footers/MessageReusableView.swift index 56270dd3..4f5321c8 100644 --- a/Sources/Views/Headers & Footers/MessageReusableView.swift +++ b/Sources/Views/Headers & Footers/MessageReusableView.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Views/InsetLabel.swift b/Sources/Views/InsetLabel.swift index 72bb9f8d..5cdfa172 100644 --- a/Sources/Views/InsetLabel.swift +++ b/Sources/Views/InsetLabel.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Views/MessageContainerView.swift b/Sources/Views/MessageContainerView.swift index 71423293..cbecffa4 100644 --- a/Sources/Views/MessageContainerView.swift +++ b/Sources/Views/MessageContainerView.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Views/MessageLabel.swift b/Sources/Views/MessageLabel.swift index d451d486..061b6504 100644 --- a/Sources/Views/MessageLabel.swift +++ b/Sources/Views/MessageLabel.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/Views/MessagesCollectionView.swift b/Sources/Views/MessagesCollectionView.swift index ee1e4cd3..0fae30db 100644 --- a/Sources/Views/MessagesCollectionView.swift +++ b/Sources/Views/MessagesCollectionView.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -65,6 +65,7 @@ open class MessagesCollectionView: UICollectionView { register(TextMessageCell.self) register(MediaMessageCell.self) register(LocationMessageCell.self) + register(AudioMessageCell.self) register(MessageReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader) register(MessageReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter) } @@ -82,7 +83,7 @@ open class MessagesCollectionView: UICollectionView { let touchLocation = gesture.location(in: self) guard let indexPath = indexPathForItem(at: touchLocation) else { return } - let cell = cellForItem(at: indexPath) as? MessageContentCell + let cell = cellForItem(at: indexPath) as? MessageCollectionViewCell cell?.handleTapGesture(gesture) } diff --git a/Sources/Views/PlayButtonView.swift b/Sources/Views/PlayButtonView.swift index b446ccfd..8ccd9843 100644 --- a/Sources/Views/PlayButtonView.swift +++ b/Sources/Views/PlayButtonView.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Tests/AvatarSpec.swift b/Tests/AvatarSpec.swift index ebd15e8b..06ac9af5 100644 --- a/Tests/AvatarSpec.swift +++ b/Tests/AvatarSpec.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Tests/ControllersTest/MessageLabelSpec.swift b/Tests/ControllersTest/MessageLabelSpec.swift index abf28a00..76af3cea 100644 --- a/Tests/ControllersTest/MessageLabelSpec.swift +++ b/Tests/ControllersTest/MessageLabelSpec.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Tests/ControllersTest/MessagesViewControllerSpec.swift b/Tests/ControllersTest/MessagesViewControllerSpec.swift index 1cf4d565..8e20bec8 100644 --- a/Tests/ControllersTest/MessagesViewControllerSpec.swift +++ b/Tests/ControllersTest/MessagesViewControllerSpec.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Tests/ControllersTest/MessagesViewControllerTests.swift b/Tests/ControllersTest/MessagesViewControllerTests.swift index fb30c51f..0f8253cb 100644 --- a/Tests/ControllersTest/MessagesViewControllerTests.swift +++ b/Tests/ControllersTest/MessagesViewControllerTests.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -55,11 +55,6 @@ class MessagesViewControllerTests: XCTestCase { // MARK: - Test - func testViewDidLoad_shouldSetDelegateAndDataSourceToTheSameObject() { - XCTAssertEqual(sut.messagesCollectionView.delegate as? MessagesViewController, - sut.messagesCollectionView.dataSource as? MessagesViewController) - } - func testNumberOfSectionWithoutData_isZero() { let messagesDataSource = MockMessagesDataSource() sut.messagesCollectionView.messagesDataSource = messagesDataSource @@ -95,7 +90,7 @@ class MessagesViewControllerTests: XCTestCase { let messagesDataSource = MockMessagesDataSource() sut.messagesCollectionView.messagesDataSource = messagesDataSource messagesDataSource.messages.append(MockMessage(text: "Test", - sender: messagesDataSource.senders[0], + user: messagesDataSource.senders[0], messageId: "test_id")) sut.messagesCollectionView.reloadData() @@ -113,7 +108,7 @@ class MessagesViewControllerTests: XCTestCase { let attributes = [NSAttributedString.Key.foregroundColor: UIColor.black] let attriutedString = NSAttributedString(string: "Test", attributes: attributes) messagesDataSource.messages.append(MockMessage(attributedText: attriutedString, - sender: messagesDataSource.senders[0], + user: messagesDataSource.senders[0], messageId: "test_id")) sut.messagesCollectionView.reloadData() @@ -129,7 +124,7 @@ class MessagesViewControllerTests: XCTestCase { let messagesDataSource = MockMessagesDataSource() sut.messagesCollectionView.messagesDataSource = messagesDataSource messagesDataSource.messages.append(MockMessage(image: UIImage(), - sender: messagesDataSource.senders[0], + user: messagesDataSource.senders[0], messageId: "test_id")) sut.messagesCollectionView.reloadData() @@ -145,7 +140,7 @@ class MessagesViewControllerTests: XCTestCase { let messagesDataSource = MockMessagesDataSource() sut.messagesCollectionView.messagesDataSource = messagesDataSource messagesDataSource.messages.append(MockMessage(thumbnail: UIImage(), - sender: messagesDataSource.senders[0], + user: messagesDataSource.senders[0], messageId: "test_id")) sut.messagesCollectionView.reloadData() @@ -161,7 +156,7 @@ class MessagesViewControllerTests: XCTestCase { let messagesDataSource = MockMessagesDataSource() sut.messagesCollectionView.messagesDataSource = messagesDataSource messagesDataSource.messages.append(MockMessage(location: CLLocation(latitude: 60.0, longitude: 70.0), - sender: messagesDataSource.senders[0], + user: messagesDataSource.senders[0], messageId: "test_id")) sut.messagesCollectionView.reloadData() @@ -173,11 +168,28 @@ class MessagesViewControllerTests: XCTestCase { XCTAssertTrue(cell is LocationMessageCell) } + func testCellForItemWithAudioData_returnsAudioMessageCell() { + let messagesDataSource = MockMessagesDataSource() + sut.messagesCollectionView.messagesDataSource = messagesDataSource + messagesDataSource.messages.append(MockMessage(audioURL: URL.init(fileURLWithPath: ""), + duration: 4.0, + sender: messagesDataSource.senders[0], + messageId: "test_id")) + + sut.messagesCollectionView.reloadData() + + let cell = sut.messagesCollectionView.dataSource?.collectionView(sut.messagesCollectionView, + cellForItemAt: IndexPath(item: 0, section: 0)) + + XCTAssertNotNil(cell) + XCTAssertTrue(cell is AudioMessageCell) + } + // MARK: - Assistants - private func makeMessages(for senders: [Sender]) -> [MessageType] { - return [MockMessage(text: "Text 1", sender: senders[0], messageId: "test_id_1"), - MockMessage(text: "Text 2", sender: senders[1], messageId: "test_id_2")] + private func makeMessages(for senders: [MockUser]) -> [MessageType] { + return [MockMessage(text: "Text 1", user: senders[0], messageId: "test_id_1"), + MockMessage(text: "Text 2", user: senders[1], messageId: "test_id_2")] } } diff --git a/Tests/DetectorTypeSpec.swift b/Tests/DetectorTypeSpec.swift index 60426131..75208abd 100644 --- a/Tests/DetectorTypeSpec.swift +++ b/Tests/DetectorTypeSpec.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Tests/Mocks/MockMessage.swift b/Tests/Mocks/MockMessage.swift index 5d7e9c44..65326037 100644 --- a/Tests/Mocks/MockMessage.swift +++ b/Tests/Mocks/MockMessage.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -24,6 +24,7 @@ import Foundation import CoreLocation +import AVFoundation @testable import MessageKit struct MockLocationItem: LocationItem { @@ -53,45 +54,68 @@ struct MockMediaItem: MediaItem { } +private struct MockAudiotem: AudioItem { + + var url: URL + var size: CGSize + var duration: Float + + init(url: URL, duration: Float) { + self.url = url + self.size = CGSize(width: 160, height: 35) + self.duration = duration + } + +} + + struct MockMessage: MessageType { var messageId: String - var sender: Sender + var sender: SenderType { + return user + } var sentDate: Date var kind: MessageKind + var user: MockUser - private init(kind: MessageKind, sender: Sender, messageId: String) { + private init(kind: MessageKind, user: MockUser, messageId: String) { self.kind = kind - self.sender = sender + self.user = user self.messageId = messageId self.sentDate = Date() } - init(text: String, sender: Sender, messageId: String) { - self.init(kind: .text(text), sender: sender, messageId: messageId) + init(text: String, user: MockUser, messageId: String) { + self.init(kind: .text(text), user: user, messageId: messageId) } - init(attributedText: NSAttributedString, sender: Sender, messageId: String) { - self.init(kind: .attributedText(attributedText), sender: sender, messageId: messageId) + init(attributedText: NSAttributedString, user: MockUser, messageId: String) { + self.init(kind: .attributedText(attributedText), user: user, messageId: messageId) } - init(image: UIImage, sender: Sender, messageId: String) { + init(image: UIImage, user: MockUser, messageId: String) { let mediaItem = MockMediaItem(image: image) - self.init(kind: .photo(mediaItem), sender: sender, messageId: messageId) + self.init(kind: .photo(mediaItem), user: user, messageId: messageId) } - init(thumbnail: UIImage, sender: Sender, messageId: String) { + init(thumbnail: UIImage, user: MockUser, messageId: String) { let mediaItem = MockMediaItem(image: thumbnail) - self.init(kind: .video(mediaItem), sender: sender, messageId: messageId) + self.init(kind: .video(mediaItem), user: user, messageId: messageId) } - init(location: CLLocation, sender: Sender, messageId: String) { + init(location: CLLocation, user: MockUser, messageId: String) { let locationItem = MockLocationItem(location: location) - self.init(kind: .location(locationItem), sender: sender, messageId: messageId) + self.init(kind: .location(locationItem), user: user, messageId: messageId) } - init(emoji: String, sender: Sender, messageId: String) { - self.init(kind: .emoji(emoji), sender: sender, messageId: messageId) + init(emoji: String, user: MockUser, messageId: String) { + self.init(kind: .emoji(emoji), user: user, messageId: messageId) + } + + init(audioURL: URL, duration: Float, sender: Sender, messageId: String) { + let audioItem = MockAudiotem(url: audioURL, duration: duration) + self.init(kind: .audio(audioItem), sender: sender, messageId: messageId) } } diff --git a/Tests/Mocks/MockMessagesDataSource.swift b/Tests/Mocks/MockMessagesDataSource.swift index 566d8d27..31703420 100644 --- a/Tests/Mocks/MockMessagesDataSource.swift +++ b/Tests/Mocks/MockMessagesDataSource.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -28,13 +28,17 @@ import Foundation class MockMessagesDataSource: MessagesDataSource { var messages: [MessageType] = [] - let senders: [Sender] = [Sender(id: "sender_1", displayName: "Sender 1"), - Sender(id: "sender_2", displayName: "Sender 2")] + let senders: [MockUser] = [MockUser(id: "sender_1", displayName: "Sender 1"), + MockUser(id: "sender_2", displayName: "Sender 2")] - func currentSender() -> Sender { + var currentUser: MockUser { return senders[0] } + func currentSender() -> SenderType { + return currentUser + } + func numberOfSections(in messagesCollectionView: MessagesCollectionView) -> Int { return messages.count } diff --git a/Tests/Mocks/MockUser.swift b/Tests/Mocks/MockUser.swift new file mode 100644 index 00000000..7e0f951c --- /dev/null +++ b/Tests/Mocks/MockUser.swift @@ -0,0 +1,31 @@ +/* + MIT License + + Copyright (c) 2017-2019 MessageKit + + 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: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + 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. + */ + +import Foundation +@testable import MessageKit + +struct MockUser: SenderType { + var id: String + var displayName: String +} diff --git a/Tests/ModelTests/MessageKitDateFormatterTests.swift b/Tests/ModelTests/MessageKitDateFormatterTests.swift index e9b24175..3fe3b5df 100644 --- a/Tests/ModelTests/MessageKitDateFormatterTests.swift +++ b/Tests/ModelTests/MessageKitDateFormatterTests.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Tests/ModelTests/MessageStyleTests.swift b/Tests/ModelTests/MessageStyleTests.swift index 7534ac99..781b9402 100644 --- a/Tests/ModelTests/MessageStyleTests.swift +++ b/Tests/ModelTests/MessageStyleTests.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Tests/ProtocolsTests/MessagesDisplayDelegateTests.swift b/Tests/ProtocolsTests/MessagesDisplayDelegateTests.swift index 4d2dae9d..6cbfb632 100644 --- a/Tests/ProtocolsTests/MessagesDisplayDelegateTests.swift +++ b/Tests/ProtocolsTests/MessagesDisplayDelegateTests.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -76,7 +76,7 @@ class MessagesDisplayDelegateTests: XCTestCase { func testBackgroundColorForMessageWithEmoji_returnsClearForDefault() { sut.dataProvider.messages.append(MockMessage(emoji: "🤔", - sender: sut.dataProvider.currentSender(), + user: sut.dataProvider.currentUser, messageId: "003")) let backgroundColor = sut.backgroundColor(for: sut.dataProvider.messages[2], at: IndexPath(item: 0, section: 0), @@ -196,10 +196,10 @@ private class MockMessagesViewController: MessagesViewController, MessagesDispla fileprivate func makeDataSource() -> MockMessagesDataSource { let dataSource = MockMessagesDataSource() dataSource.messages.append(MockMessage(text: "Text 1", - sender: dataSource.senders[0], + user: dataSource.senders[0], messageId: "001")) dataSource.messages.append(MockMessage(text: "Text 2", - sender: dataSource.senders[1], + user: dataSource.senders[1], messageId: "002")) return dataSource diff --git a/Tests/Resources/sound1.m4a b/Tests/Resources/sound1.m4a new file mode 100644 index 00000000..56b31a69 Binary files /dev/null and b/Tests/Resources/sound1.m4a differ diff --git a/Tests/SenderSpec.swift b/Tests/SenderSpec.swift index 56910a3d..2ed8839c 100644 --- a/Tests/SenderSpec.swift +++ b/Tests/SenderSpec.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -32,16 +32,16 @@ final class SenderSpec: QuickSpec { describe("equality between two Senders") { context("they have the same id ") { it("should be equal") { - let sender1 = Sender(id: "1", displayName: "Steven") - let sender2 = Sender(id: "1", displayName: "Nathan") - expect(sender1).to(equal(sender2)) + let sender1 = MockUser(id: "1", displayName: "Steven") + let sender2 = MockUser(id: "1", displayName: "Nathan") + expect(sender1.id == sender2.id).to(equal(true)) } } context("they have a different id") { it("should not be equal") { - let sender1 = Sender(id: "1", displayName: "Steven") - let sender2 = Sender(id: "2", displayName: "Nathan") - expect(sender1).toNot(equal(sender2)) + let sender1 = MockUser(id: "1", displayName: "Steven") + let sender2 = MockUser(id: "2", displayName: "Nathan") + expect(sender1.id == sender2.id).to(equal(false)) } } } diff --git a/Tests/ViewsTests/AvatarViewTests.swift b/Tests/ViewsTests/AvatarViewTests.swift index a6e9a00e..e919f141 100644 --- a/Tests/ViewsTests/AvatarViewTests.swift +++ b/Tests/ViewsTests/AvatarViewTests.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Tests/ViewsTests/MessageCollectionViewCellTests.swift b/Tests/ViewsTests/MessageCollectionViewCellTests.swift index cbaa1a26..4bce2cf0 100644 --- a/Tests/ViewsTests/MessageCollectionViewCellTests.swift +++ b/Tests/ViewsTests/MessageCollectionViewCellTests.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Tests/ViewsTests/MessagesCollectionViewTests.swift b/Tests/ViewsTests/MessagesCollectionViewTests.swift index a2f35e0c..a42fa54d 100644 --- a/Tests/ViewsTests/MessagesCollectionViewTests.swift +++ b/Tests/ViewsTests/MessagesCollectionViewTests.swift @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2017-2018 MessageKit + Copyright (c) 2017-2019 MessageKit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal