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