From cdb9265d6093ef7b2c4a8723bac9ccfe25bdb11b Mon Sep 17 00:00:00 2001
From: Bruno Muniz Azevedo Filho <bruno@elixxir.io>
Date: Wed, 7 Dec 2022 01:47:22 -0300
Subject: [PATCH] Fixes group chat member list

---
 Sources/AppFeature/Dependencies.swift         |  3 +
 Sources/AppNavigation/PresentMemberList.swift | 53 ++++++++++--
 .../Controllers/GroupChatController.swift     | 36 ++++----
 .../Controllers/GroupMembersController.swift  | 30 +++++++
 .../Controllers/MembersController.swift       | 83 -------------------
 .../ChatFeature/Views/GroupHeaderView.swift   | 70 +++++-----------
 .../ChatFeature/Views/GroupMemberView.swift   | 43 ++++++++++
 .../ChatFeature/Views/GroupMembersView.swift  | 39 +++++++++
 Sources/Shared/Views/AvatarView.swift         |  2 +-
 9 files changed, 208 insertions(+), 151 deletions(-)
 create mode 100644 Sources/ChatFeature/Controllers/GroupMembersController.swift
 delete mode 100644 Sources/ChatFeature/Controllers/MembersController.swift
 create mode 100644 Sources/ChatFeature/Views/GroupMemberView.swift
 create mode 100644 Sources/ChatFeature/Views/GroupMembersView.swift

diff --git a/Sources/AppFeature/Dependencies.swift b/Sources/AppFeature/Dependencies.swift
index 1b95b798..535f7454 100644
--- a/Sources/AppFeature/Dependencies.swift
+++ b/Sources/AppFeature/Dependencies.swift
@@ -104,6 +104,9 @@ extension NavigatorKey: DependencyKey {
     PresentCountryListNavigator(
       CountryListController.init(_:)
     ),
+    PresentGroupMembersNavigator(
+      GroupMembersController.init(_:)
+    ),
     PresentOnboardingEmailNavigator(
       OnboardingEmailController.init
     ),
diff --git a/Sources/AppNavigation/PresentMemberList.swift b/Sources/AppNavigation/PresentMemberList.swift
index 3fa6582f..eedc4a30 100644
--- a/Sources/AppNavigation/PresentMemberList.swift
+++ b/Sources/AppNavigation/PresentMemberList.swift
@@ -1,14 +1,55 @@
+import UIKit
 import XXModels
 
-public struct PresentMemberList: Action {
-  public var members: [Contact]
-  public var animated: Bool
-
+/// Opens up `GroupMembers` on a given parent view controller
+public struct PresentGroupMembers: Action {
+  /// - Parameters:
+  ///   - groupInfo: Model that represents the group info that contains the members to be shown
+  ///   - parent: Parent view controller from which presentation should happen
+  ///   - animated: Animate the transition
   public init(
-    members: [Contact],
+    groupInfo: GroupInfo,
+    from parent: UIViewController,
     animated: Bool = true
   ) {
-    self.members = members
+    self.groupInfo = groupInfo
+    self.parent = parent
     self.animated = animated
   }
+
+  /// Model that represents the group info that contains the members to be shown
+  public var groupInfo: GroupInfo
+
+  /// Parent view controller from which presentation should happen
+  public var parent: UIViewController
+
+  /// Animate the transition
+  public var animated: Bool
+}
+
+/// Performs `PresentGroupMembers` action
+public struct PresentGroupMembersNavigator: TypedNavigator {
+  /// Custom transitioning delegate
+  let transitioningDelegate = BottomTransitioningDelegate()
+
+  /// View controller which should be opened up
+  var viewController: (GroupInfo) -> UIViewController
+
+  /// - Parameters:
+  ///   - viewController: view controller which should be presented
+  public init(_ viewController: @escaping (GroupInfo) -> UIViewController) {
+    self.viewController = viewController
+  }
+
+  public func perform(_ action: PresentGroupMembers, completion: @escaping () -> Void) {
+    let controller = viewController(action.groupInfo)
+    controller.transitioningDelegate = transitioningDelegate
+    controller.modalPresentationStyle = .overFullScreen
+
+    action.parent.present(
+      controller,
+      animated: action.animated,
+      completion: completion
+    )
+  }
 }
diff --git a/Sources/ChatFeature/Controllers/GroupChatController.swift b/Sources/ChatFeature/Controllers/GroupChatController.swift
index 43ed5000..bd187ca2 100644
--- a/Sources/ChatFeature/Controllers/GroupChatController.swift
+++ b/Sources/ChatFeature/Controllers/GroupChatController.swift
@@ -29,8 +29,10 @@ public final class GroupChatController: UIViewController {
 //  @Dependency var makeReportDrawer: MakeReportDrawer
 //  @Dependency var makeAppScreenshot: MakeAppScreenshot
 
+  private lazy var moreButton = UIButton()
+  private lazy var headerView = GroupHeaderView()
+
   private var collectionView: UICollectionView!
-  private lazy var header = GroupHeaderView()
   private let inputComponent: ChatInputView
 
   private var animator: ManualAnimator?
@@ -65,14 +67,18 @@ public final class GroupChatController: UIViewController {
 
     super.init(nibName: nil, bundle: nil)
 
-    let memberList = info.members.map {
-      Member(
-        title: ($0.nickname ?? $0.username) ?? "Fetching username...",
-        photo: $0.photo
-      )
+    headerView.titleLabel.text = info.group.name
+    info.members.forEach {
+      let contactTitle = ($0.nickname ?? $0.username) ?? "Fetching username..."
+      let avatarView = AvatarView()
+      avatarView.layer.borderWidth = 2
+      avatarView.layer.borderColor = UIColor.white.cgColor
+      avatarView.setupProfile(title: contactTitle, image: $0.photo, size: .small)
+      avatarView.snp.makeConstraints {
+        $0.width.height.equalTo(26.0)
+      }
+      headerView.avatarStackView.addArrangedSubview(avatarView)
     }
-
-    header.setup(title: info.group.name, memberList: memberList)
   }
 
   public required init?(coder: NSCoder) { nil }
@@ -127,12 +133,11 @@ public final class GroupChatController: UIViewController {
   }
 
   private func setupNavigationBar() {
-    let more = UIButton()
-    more.setImage(Asset.chatMore.image, for: .normal)
-    more.addTarget(self, action: #selector(didTapDots), for: .touchUpInside)
+    moreButton.setImage(Asset.chatMore.image, for: .normal)
+    moreButton.addTarget(self, action: #selector(didTapDots), for: .touchUpInside)
 
-    navigationItem.titleView = header
-    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: more)
+    navigationItem.titleView = headerView
+    navigationItem.rightBarButtonItem = UIBarButtonItem(customView: moreButton)
   }
 
   private func setupCollectionView() {
@@ -317,7 +322,10 @@ public final class GroupChatController: UIViewController {
   }
 
   @objc private func didTapDots() {
-    navigator.perform(PresentMemberList(members: viewModel.info.members))
+    navigator.perform(PresentGroupMembers(
+      groupInfo: viewModel.info,
+      from: self
+    ))
   }
 
   func scrollToBottom(completion: (() -> Void)? = nil) {
diff --git a/Sources/ChatFeature/Controllers/GroupMembersController.swift b/Sources/ChatFeature/Controllers/GroupMembersController.swift
new file mode 100644
index 00000000..b1e6398c
--- /dev/null
+++ b/Sources/ChatFeature/Controllers/GroupMembersController.swift
@@ -0,0 +1,30 @@
+import UIKit
+import XXModels
+
+public final class GroupMembersController: UIViewController {
+  private lazy var screenView = GroupMembersView()
+
+  private let groupInfo: GroupInfo
+  
+  public init(_ groupInfo: GroupInfo) {
+    self.groupInfo = groupInfo
+    super.init(nibName: nil, bundle: nil)
+  }
+  
+  required init?(coder: NSCoder) { nil }
+
+  public override func loadView() {
+    view = screenView
+  }
+  
+  public override func viewDidLoad() {
+    super.viewDidLoad()
+
+    groupInfo.members.forEach {
+      screenView.addMember(
+        title: ($0.nickname ?? $0.username) ?? "Fetching username...",
+        photo: $0.photo
+      )
+    }
+  }
+}
diff --git a/Sources/ChatFeature/Controllers/MembersController.swift b/Sources/ChatFeature/Controllers/MembersController.swift
deleted file mode 100644
index 086114ea..00000000
--- a/Sources/ChatFeature/Controllers/MembersController.swift
+++ /dev/null
@@ -1,83 +0,0 @@
-import UIKit
-import Shared
-import XXModels
-import AppResources
-
-final class MembersController: UIViewController {
-  private lazy var stackView = UIStackView()
-  
-  private let members: [Contact]
-  
-  init(with members: [Contact]) {
-    self.members = members
-    super.init(nibName: nil, bundle: nil)
-  }
-  
-  required init?(coder: NSCoder) { nil }
-  
-  public override func viewDidLoad() {
-    super.viewDidLoad()
-    
-    view.layer.cornerRadius = 15
-    view.layer.masksToBounds = true
-    view.backgroundColor = Asset.neutralWhite.color
-    
-    stackView.axis = .vertical
-    stackView.distribution = .fillEqually
-    view.addSubview(stackView)
-    
-    stackView.snp.makeConstraints {
-      $0.top.equalToSuperview().offset(10)
-      $0.left.right.equalToSuperview()
-      $0.bottom.equalTo(view.safeAreaLayoutGuide)
-    }
-    
-    members.forEach {
-      let memberView = MemberView()
-      let assignedTitle = ($0.nickname ?? $0.username) ?? "Fetching username..."
-      memberView.titleLabel.text = assignedTitle
-      memberView.avatarView.setupProfile(title: assignedTitle, image: $0.photo, size: .small)
-      stackView.addArrangedSubview(memberView)
-    }
-  }
-}
-
-private final class MemberView: UIView {
-  let titleLabel = UILabel()
-  let avatarView = AvatarView()
-  let separatorView = UIView()
-  
-  init() {
-    super.init(frame: .zero)
-    backgroundColor = Asset.neutralWhite.color
-    titleLabel.textColor = Asset.neutralBody.color
-    titleLabel.font = Fonts.Mulish.bold.font(size: 12.0)
-    separatorView.backgroundColor = Asset.neutralLine.color
-    
-    addSubview(titleLabel)
-    addSubview(avatarView)
-    addSubview(separatorView)
-    
-    avatarView.snp.makeConstraints {
-      $0.top.equalToSuperview().offset(10)
-      $0.width.height.equalTo(30)
-      $0.left.equalToSuperview().offset(25)
-      $0.centerY.equalToSuperview()
-    }
-    
-    titleLabel.snp.makeConstraints {
-      $0.centerY.equalTo(avatarView)
-      $0.left.equalTo(avatarView.snp.right).offset(14)
-      $0.right.lessThanOrEqualToSuperview().offset(-10)
-    }
-    
-    separatorView.snp.makeConstraints {
-      $0.height.equalTo(1)
-      $0.left.equalToSuperview().offset(25)
-      $0.right.equalToSuperview()
-      $0.bottom.equalToSuperview()
-    }
-  }
-  
-  required init?(coder: NSCoder) { nil }
-}
diff --git a/Sources/ChatFeature/Views/GroupHeaderView.swift b/Sources/ChatFeature/Views/GroupHeaderView.swift
index fdd49401..435ce181 100644
--- a/Sources/ChatFeature/Views/GroupHeaderView.swift
+++ b/Sources/ChatFeature/Views/GroupHeaderView.swift
@@ -2,59 +2,35 @@ import UIKit
 import Shared
 import AppResources
 
-struct Member {
-    let title: String
-    let photo: Data?
-}
-
 final class GroupHeaderView: UIView {
-    let titleLabel = UILabel()
-    let containerView = UIView()
-    let stackView = UIStackView()
-
-    init() {
-        super.init(frame: .zero)
-
-        stackView.spacing = -8
-        titleLabel.textColor = Asset.neutralActive.color
-        titleLabel.font = Fonts.Mulish.semiBold.font(size: 15.0)
+  let titleLabel = UILabel()
+  let avatarStackView = UIStackView()
 
-        containerView.addSubview(titleLabel)
-        containerView.addSubview(stackView)
-        addSubview(containerView)
+  init() {
+    super.init(frame: .zero)
 
-        titleLabel.snp.makeConstraints {
-            $0.top.equalToSuperview()
-            $0.centerX.equalToSuperview()
-            $0.left.greaterThanOrEqualToSuperview()
-            $0.right.lessThanOrEqualToSuperview()
-        }
+    avatarStackView.spacing = -5
+    titleLabel.textColor = Asset.neutralActive.color
+    titleLabel.font = Fonts.Mulish.semiBold.font(size: 15.0)
 
-        stackView.snp.makeConstraints {
-            $0.top.equalTo(titleLabel.snp.bottom)
-            $0.centerX.equalToSuperview()
-            $0.left.greaterThanOrEqualToSuperview()
-            $0.right.lessThanOrEqualToSuperview()
-            $0.bottom.equalToSuperview()
-        }
+    addSubview(titleLabel)
+    addSubview(avatarStackView)
 
-        containerView.snp.makeConstraints {
-            $0.edges.equalToSuperview()
-        }
+    titleLabel.snp.makeConstraints {
+      $0.top.equalToSuperview()
+      $0.centerX.equalToSuperview()
+      $0.left.greaterThanOrEqualToSuperview()
+      $0.right.lessThanOrEqualToSuperview()
     }
 
-    required init?(coder: NSCoder) { nil }
-
-    func setup(title: String, memberList: [Member]) {
-        titleLabel.text = title
-
-        memberList.forEach {
-            let avatarView = AvatarView()
-            avatarView.layer.borderWidth = 3
-            avatarView.layer.borderColor = UIColor.white.cgColor
-            avatarView.setupProfile(title: $0.title, image: $0.photo, size: .small)
-            avatarView.snp.makeConstraints { $0.width.height.equalTo(25.0) }
-            stackView.addArrangedSubview(avatarView)
-        }
+    avatarStackView.snp.makeConstraints {
+      $0.top.equalTo(titleLabel.snp.bottom)
+      $0.centerX.equalToSuperview()
+      $0.left.greaterThanOrEqualToSuperview()
+      $0.right.lessThanOrEqualToSuperview()
+      $0.bottom.equalToSuperview()
     }
+  }
+
+  required init?(coder: NSCoder) { nil }
 }
diff --git a/Sources/ChatFeature/Views/GroupMemberView.swift b/Sources/ChatFeature/Views/GroupMemberView.swift
new file mode 100644
index 00000000..4ae3cfc4
--- /dev/null
+++ b/Sources/ChatFeature/Views/GroupMemberView.swift
@@ -0,0 +1,43 @@
+import UIKit
+import Shared
+import AppResources
+
+final class GroupMemberView: UIView {
+  let titleLabel = UILabel()
+  let avatarView = AvatarView()
+  let separatorView = UIView()
+
+  init() {
+    super.init(frame: .zero)
+    backgroundColor = Asset.neutralWhite.color
+    titleLabel.textColor = Asset.neutralBody.color
+    titleLabel.font = Fonts.Mulish.bold.font(size: 12.0)
+    separatorView.backgroundColor = Asset.neutralLine.color
+
+    addSubview(titleLabel)
+    addSubview(avatarView)
+    addSubview(separatorView)
+
+    avatarView.snp.makeConstraints {
+      $0.top.equalToSuperview().offset(10)
+      $0.width.height.equalTo(30)
+      $0.left.equalToSuperview().offset(25)
+      $0.centerY.equalToSuperview()
+    }
+
+    titleLabel.snp.makeConstraints {
+      $0.centerY.equalTo(avatarView)
+      $0.left.equalTo(avatarView.snp.right).offset(14)
+      $0.right.lessThanOrEqualToSuperview().offset(-10)
+    }
+
+    separatorView.snp.makeConstraints {
+      $0.height.equalTo(1)
+      $0.left.equalToSuperview().offset(25)
+      $0.right.equalToSuperview().offset(-25)
+      $0.bottom.equalToSuperview()
+    }
+  }
+
+  required init?(coder: NSCoder) { nil }
+}
diff --git a/Sources/ChatFeature/Views/GroupMembersView.swift b/Sources/ChatFeature/Views/GroupMembersView.swift
new file mode 100644
index 00000000..9d439f15
--- /dev/null
+++ b/Sources/ChatFeature/Views/GroupMembersView.swift
@@ -0,0 +1,39 @@
+import UIKit
+import Shared
+import AppResources
+
+final class GroupMembersView: UIView {
+  private let stackView = UIStackView()
+
+  init() {
+    super.init(frame: .zero)
+
+    layer.cornerRadius = 40
+    layer.masksToBounds = true
+    backgroundColor = Asset.neutralWhite.color
+
+    stackView.axis = .vertical
+    stackView.distribution = .fillEqually
+    addSubview(stackView)
+
+    stackView.snp.makeConstraints {
+      $0.top.equalToSuperview().offset(20)
+      $0.left.equalToSuperview().offset(20)
+      $0.right.equalToSuperview().offset(-20)
+      $0.bottom.equalToSuperview().offset(-50)
+    }
+  }
+
+  required init?(coder: NSCoder) { nil }
+
+  func addMember(title: String, photo: Data?) {
+    let memberView = GroupMemberView()
+    memberView.titleLabel.text = title
+    memberView.avatarView.setupProfile(
+      title: title,
+      image: photo,
+      size: .small
+    )
+    stackView.addArrangedSubview(memberView)
+  }
+}
diff --git a/Sources/Shared/Views/AvatarView.swift b/Sources/Shared/Views/AvatarView.swift
index cacce36f..f8e34c3b 100644
--- a/Sources/Shared/Views/AvatarView.swift
+++ b/Sources/Shared/Views/AvatarView.swift
@@ -62,7 +62,7 @@ public final class AvatarView: UIView {
     switch size {
     case .small:
       layer.cornerRadius = 13.0
-      monogramLabel.font = Fonts.Mulish.semiBold.font(size: 14.0)
+      monogramLabel.font = Fonts.Mulish.semiBold.font(size: 12.0)
     case .medium:
       layer.cornerRadius = 13.0
       monogramLabel.font = Fonts.Mulish.semiBold.font(size: 14.0)
-- 
GitLab