Skip to content

Commit

Permalink
Merge branch 'compound-decoration'
Browse files Browse the repository at this point in the history
  • Loading branch information
wiruzx committed Jul 6, 2020
2 parents 158a1fb + 9de20ec commit 2684229
Show file tree
Hide file tree
Showing 12 changed files with 649 additions and 390 deletions.
16 changes: 16 additions & 0 deletions ChattoAdditions/ChattoAdditions.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
26438961240D72C800427D14 /* Chatto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2643895C240D72B400427D14 /* Chatto.framework */; };
2645F90D2209C11C004CB829 /* HashableRepresentible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2645F90C2209C11C004CB829 /* HashableRepresentible.swift */; };
265E22EB2256398E005330E4 /* PasteActionInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 265E22EA2256398E005330E4 /* PasteActionInterceptor.swift */; };
266C7ADD24B2A56400DB0F61 /* MessageDecorationViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 266C7ADC24B2A56400DB0F61 /* MessageDecorationViewFactory.swift */; };
266C7ADF24B2A60F00DB0F61 /* MessageDecorationViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 266C7ADE24B2A60F00DB0F61 /* MessageDecorationViewLayout.swift */; };
268CD56221F9F92C00DEE2C2 /* CompoundMessageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 268CD55F21F9F92B00DEE2C2 /* CompoundMessageCollectionViewCell.swift */; };
268CD56321F9F92C00DEE2C2 /* CompoundBubbleViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 268CD56021F9F92C00DEE2C2 /* CompoundBubbleViewStyle.swift */; };
268CD56421F9F92C00DEE2C2 /* CompoundBubbleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 268CD56121F9F92C00DEE2C2 /* CompoundBubbleView.swift */; };
Expand Down Expand Up @@ -166,6 +168,8 @@
26438956240D72B400427D14 /* Chatto.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Chatto.xcodeproj; path = ../Chatto/Chatto.xcodeproj; sourceTree = "<group>"; };
2645F90C2209C11C004CB829 /* HashableRepresentible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashableRepresentible.swift; sourceTree = "<group>"; };
265E22EA2256398E005330E4 /* PasteActionInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteActionInterceptor.swift; sourceTree = "<group>"; };
266C7ADC24B2A56400DB0F61 /* MessageDecorationViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageDecorationViewFactory.swift; sourceTree = "<group>"; };
266C7ADE24B2A60F00DB0F61 /* MessageDecorationViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageDecorationViewLayout.swift; sourceTree = "<group>"; };
268CD55F21F9F92B00DEE2C2 /* CompoundMessageCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompoundMessageCollectionViewCell.swift; sourceTree = "<group>"; };
268CD56021F9F92C00DEE2C2 /* CompoundBubbleViewStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompoundBubbleViewStyle.swift; sourceTree = "<group>"; };
268CD56121F9F92C00DEE2C2 /* CompoundBubbleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompoundBubbleView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -325,9 +329,19 @@
name = Products;
sourceTree = "<group>";
};
266C7ADB24B2A55600DB0F61 /* Decoration */ = {
isa = PBXGroup;
children = (
266C7ADC24B2A56400DB0F61 /* MessageDecorationViewFactory.swift */,
266C7ADE24B2A60F00DB0F61 /* MessageDecorationViewLayout.swift */,
);
path = Decoration;
sourceTree = "<group>";
};
268CD55D21F9F8F500DEE2C2 /* CompoundMessage */ = {
isa = PBXGroup;
children = (
266C7ADB24B2A55600DB0F61 /* Decoration */,
268CD55E21F9F91000DEE2C2 /* Views */,
268CD574220334C800DEE2C2 /* Content */,
268CD56521FA260800DEE2C2 /* CompoundMessagePresenter.swift */,
Expand Down Expand Up @@ -854,13 +868,15 @@
C3C0CC571BFE496A0052747C /* LiveCameraCell.swift in Sources */,
C3C0CC591BFE496A0052747C /* PhotosChatInputItem.swift in Sources */,
C3C0CC3A1BFE496A0052747C /* PhotoMessageCollectionViewCell.swift in Sources */,
266C7ADF24B2A60F00DB0F61 /* MessageDecorationViewLayout.swift in Sources */,
26D07FA4220791AE007C1534 /* Cache.swift in Sources */,
55ABA5671FC748B600923302 /* CGRect+Additions.swift in Sources */,
C3C0CC5D1BFE496A0052747C /* PhotosInputWithPlaceholdersDataProvider.swift in Sources */,
C3C0CC5C1BFE496A0052747C /* PhotosInputCellProvider.swift in Sources */,
C3C0CC3C1BFE496A0052747C /* TextMessageModel.swift in Sources */,
2645F90D2209C11C004CB829 /* HashableRepresentible.swift in Sources */,
EA13CAF4229EE985009340C5 /* CircleProgressView.swift in Sources */,
266C7ADD24B2A56400DB0F61 /* MessageDecorationViewFactory.swift in Sources */,
C3C0CC2A1BFE496A0052747C /* BaseMessageModel.swift in Sources */,
C3C0CC4F1BFE496A0052747C /* ChatInputBar.swift in Sources */,
C3C0CC361BFE496A0052747C /* PhotoMessagePresenterBuilder.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ open class CompoundMessagePresenter<ViewModelBuilderT, InteractionHandlerT>
private let initialContentFactories: [AnyMessageContentFactory<ModelT>]
private var contentFactories: [AnyMessageContentFactory<ModelT>]!
private var contentPresenters: [MessageContentPresenterProtocol]!
private let initialDecorationFactories: [AnyMessageDecorationViewFactory<ModelT>]
private var decorationFactories: [AnyMessageDecorationViewFactory<ModelT>] = []
private var menuPresenter: ChatItemMenuPresenterProtocol?

public init(
Expand All @@ -56,13 +58,15 @@ open class CompoundMessagePresenter<ViewModelBuilderT, InteractionHandlerT>
compoundCellStyle: CompoundBubbleViewStyleProtocol,
cache: Cache<CompoundBubbleLayoutProvider.Configuration, CompoundBubbleLayoutProvider>,
accessibilityIdentifier: String?,
decorationFactories: [AnyMessageDecorationViewFactory<ModelT>] = [],
cellClass: CompoundMessageCollectionViewCell.Type = CompoundMessageCollectionViewCell.self
) {
self.compoundCellStyle = compoundCellStyle
self.initialContentFactories = contentFactories
self.cache = cache
self.accessibilityIdentifier = accessibilityIdentifier
self.cellClass = cellClass
self.initialDecorationFactories = decorationFactories
super.init(
messageModel: messageModel,
viewModelBuilder: viewModelBuilder,
Expand Down Expand Up @@ -119,6 +123,8 @@ open class CompoundMessagePresenter<ViewModelBuilderT, InteractionHandlerT>
}

self.menuPresenter = self.contentFactories.lazy.compactMap { $0.createMenuPresenter(forModel: self.messageModel) }.first

self.decorationFactories = self.initialDecorationFactories.filter { $0.canCreateDecorationView(for: self.messageModel) }
}

open func updateExistingContentPresenters(with newMessage: Any) {
Expand Down Expand Up @@ -151,20 +157,20 @@ open class CompoundMessagePresenter<ViewModelBuilderT, InteractionHandlerT>

super.configureCell(compoundCell, decorationAttributes: decorationAttributes, animated: animated) { [weak self] in
defer { additionalConfiguration?() }
guard let sSelf = self else { return }
guard let self = self else { return }

let bubbleView = compoundCell.bubbleView!

if bubbleView.decoratedContentViews == nil {
bubbleView.accessibilityIdentifier = sSelf.accessibilityIdentifier
bubbleView.decoratedContentViews = zip(sSelf.contentFactories, sSelf.contentPresenters).map { factory, presenter in
bubbleView.accessibilityIdentifier = self.accessibilityIdentifier
bubbleView.decoratedContentViews = zip(self.contentFactories, self.contentPresenters).map { factory, presenter in
return CompoundBubbleView.DecoratedView(view: factory.createContentView(), showBorder: presenter.showBorder)
}
}

bubbleView.viewModel = sSelf.messageViewModel
bubbleView.layoutProvider = sSelf.layoutProvider
bubbleView.style = sSelf.compoundCellStyle
bubbleView.viewModel = self.messageViewModel
bubbleView.layoutProvider = self.layoutProvider
bubbleView.style = self.compoundCellStyle

/*
There is a current algorithm of binding (and unbinding, as well) compoundCell's views to their presenters:
Expand All @@ -173,12 +179,18 @@ open class CompoundMessagePresenter<ViewModelBuilderT, InteractionHandlerT>
3. CompoundCell's views bound with a current compound message presenters.
*/

sSelf.contentPresenters.forEach { $0.unbindFromView() }
compoundCell.viewReferences = zip(sSelf.contentPresenters, bubbleView.decoratedContentViews!.map({ $0.view })).map { presenter, view in
self.contentPresenters.forEach { $0.unbindFromView() }
compoundCell.viewReferences = zip(self.contentPresenters, bubbleView.decoratedContentViews!.map({ $0.view })).map { presenter, view in
let viewReference = ViewReference(to: view)
presenter.bindToView(with: viewReference)
return viewReference
}

compoundCell.decorationViews = self.decorationFactories.map { factory in
let view = factory.makeDecorationView(for: self.messageModel)
let layoutProvider = factory.makeLayoutProvider(for: self.messageModel)
return (view,layoutProvider)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ public final class CompoundMessagePresenterBuilder<ViewModelBuilderT, Interactio
interactionHandler: InteractionHandlerT?,
accessibilityIdentifier: String?,
contentFactories: [AnyMessageContentFactory<ModelT>],
decorationFactories: [AnyMessageDecorationViewFactory<ModelT>] = [],
compoundCellStyle: CompoundBubbleViewStyleProtocol = DefaultCompoundBubbleViewStyle(),
baseCellStyle: BaseMessageCollectionViewCellStyleProtocol = BaseMessageCollectionViewCellDefaultStyle()) {
self.viewModelBuilder = viewModelBuilder
self.interactionHandler = interactionHandler
self.contentFactories = contentFactories
self.decorationFactories = decorationFactories
self.accessibilityIdentifier = accessibilityIdentifier
self.compoundCellStyle = compoundCellStyle
self.baseCellStyle = baseCellStyle
Expand All @@ -50,6 +52,7 @@ public final class CompoundMessagePresenterBuilder<ViewModelBuilderT, Interactio
public let viewModelBuilder: ViewModelBuilderT
public let interactionHandler: InteractionHandlerT?
private let contentFactories: [AnyMessageContentFactory<ModelT>]
private let decorationFactories: [AnyMessageDecorationViewFactory<ModelT>]
public let sizingCell: CompoundMessageCollectionViewCell = CompoundMessageCollectionViewCell()
private let compoundCellStyle: CompoundBubbleViewStyleProtocol
private let baseCellStyle: BaseMessageCollectionViewCellStyleProtocol
Expand All @@ -71,7 +74,8 @@ public final class CompoundMessagePresenterBuilder<ViewModelBuilderT, Interactio
baseCellStyle: self.baseCellStyle,
compoundCellStyle: self.compoundCellStyle,
cache: self.cache,
accessibilityIdentifier: self.accessibilityIdentifier
accessibilityIdentifier: self.accessibilityIdentifier,
decorationFactories: self.decorationFactories
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2015-present Badoo Trading Limited.
//
// 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

public protocol MessageDecorationViewFactoryProtocol: AnyObject {
associatedtype Model
func canCreateDecorationView(for model: Model) -> Bool
func makeDecorationView(for model: Model) -> UIView
func makeLayoutProvider(for model: Model) -> MessageDecorationViewLayoutProviderProtocol
}

public final class AnyMessageDecorationViewFactory<Model>: MessageDecorationViewFactoryProtocol {

private let _canCreateDecorationView: (_ model: Model) -> Bool
private let _makeDecorationView: (_ model: Model) -> UIView
private let _makeLayoutProvider: (_ model: Model) -> MessageDecorationViewLayoutProviderProtocol

public init<Base: MessageDecorationViewFactoryProtocol>(_ base: Base) where Base.Model == Model {
self._canCreateDecorationView = base.canCreateDecorationView(for:)
self._makeDecorationView = base.makeDecorationView(for:)
self._makeLayoutProvider = base.makeLayoutProvider(for:)
}

public func canCreateDecorationView(for model: Model) -> Bool {
self._canCreateDecorationView(model)
}

public func makeDecorationView(for model: Model) -> UIView {
self._makeDecorationView(model)
}

public func makeLayoutProvider(for model: Model) -> MessageDecorationViewLayoutProviderProtocol {
self._makeLayoutProvider(model)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2015-present Badoo Trading Limited.
//
// 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

public struct MessageDecorationViewLayout {
let frame: CGRect
public init(frame: CGRect) {
self.frame = frame
}
}

public protocol MessageDecorationViewLayoutProviderProtocol {
func makeLayout(from bubbleBounds: CGRect) -> MessageDecorationViewLayout
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import UIKit

@available(iOS 11, *)
open class CompoundMessageCollectionViewCell: BaseMessageCollectionViewCell<CompoundBubbleView> {

Expand All @@ -29,4 +31,30 @@ open class CompoundMessageCollectionViewCell: BaseMessageCollectionViewCell<Comp
}

public final var viewReferences: [ViewReference]?

// MARK: - Decoration Views

public typealias DecorationViewWithLayout = (UIView, MessageDecorationViewLayoutProviderProtocol)

public var decorationViews: [DecorationViewWithLayout]? {
didSet {
for (view, _) in oldValue ?? [] {
view.removeFromSuperview()
}
for (view, _) in self.decorationViews ?? [] {
self.contentView.addSubview(view)
}
}
}

open override func layoutSubviews() {
super.layoutSubviews()
guard let bubbleView = self.bubbleView,
let decorationViews = decorationViews else { return }
for (view, layoutProvider) in decorationViews {
var frame = layoutProvider.makeLayout(from: bubbleView.bounds).frame
frame.origin = bubbleView.convert(frame.origin, to: self.contentView)
view.frame = frame
}
}
}
Loading

0 comments on commit 2684229

Please sign in to comment.