From b9639dc36fb0587d361eec0c51ad4cb38daa5a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Mon, 3 Apr 2023 14:53:19 -0300 Subject: [PATCH 01/66] implement messaging button on home view --- TreeTracker.xcodeproj/project.pbxproj | 4 ++ TreeTracker/Resources/Localizable.strings | 1 + TreeTracker/SwiftGen/Strings.swift | 4 ++ .../Home/Components/MessagingButton.swift | 59 +++++++++++++++++++ TreeTracker/UI/Views/Home/Home.storyboard | 34 +++++++++-- .../UI/Views/Home/HomeViewController.swift | 17 +++++- 6 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 TreeTracker/UI/Views/Home/Components/MessagingButton.swift diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index 296b175e..1fea0fc3 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 59BEAE0465C5A575ABE7FA13 /* Pods_TreeTracker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B03AD2E94D3E7D6987A59BB0 /* Pods_TreeTracker.framework */; }; + 8D75250629DB13FA00361CC1 /* MessagingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75250529DB13FA00361CC1 /* MessagingButton.swift */; }; 9311609126B40E5E007FCA52 /* DestructiveButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609026B40E5E007FCA52 /* DestructiveButton.swift */; }; 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609A26B4108A007FCA52 /* RoundedButton.swift */; }; 931C37B8249BACFA00EE24DC /* SelfieViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */; }; @@ -94,6 +95,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 8D75250529DB13FA00361CC1 /* MessagingButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagingButton.swift; sourceTree = ""; }; 9311609026B40E5E007FCA52 /* DestructiveButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestructiveButton.swift; sourceTree = ""; }; 9311609A26B4108A007FCA52 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfieViewModel.swift; sourceTree = ""; }; @@ -537,6 +539,7 @@ 93EC8C4524AB7A6900946C0C /* AddTreeButton.swift */, 93EC8C4924AB7EA100946C0C /* UploadsButton.swift */, 93814CD92569605900FC70E5 /* MyTreesButton.swift */, + 8D75250529DB13FA00361CC1 /* MessagingButton.swift */, ); path = Components; sourceTree = ""; @@ -849,6 +852,7 @@ 93989D08246ED51C00A1B21F /* SignInTextField.swift in Sources */, 93E18933245B737D00217F21 /* SceneDelegate.swift in Sources */, 932EA1B0247954B40035394D /* BaseNavigationViewController.swift in Sources */, + 8D75250629DB13FA00361CC1 /* MessagingButton.swift in Sources */, 93989CF5246EA33600A1B21F /* SignUpViewController.swift in Sources */, 932EA1AC2478311F0035394D /* KeyboardDismissing.swift in Sources */, 93E18965245C95F200217F21 /* StoryboardScenes.swift in Sources */, diff --git a/TreeTracker/Resources/Localizable.strings b/TreeTracker/Resources/Localizable.strings index 432faa1f..bc371cfb 100644 --- a/TreeTracker/Resources/Localizable.strings +++ b/TreeTracker/Resources/Localizable.strings @@ -47,6 +47,7 @@ "Home.UploadTreesButton.Title.Start" = "START UPLOAD"; "Home.UploadTreesButton.Title.Stop" = "STOP UPLOAD"; "Home.MyTreesButton.Title" = "MY TREES"; +"Home.MessagingButton.Title" = "MESSAGES"; //Add Tree Screen "AddTree.Title" = "Add Tree"; diff --git a/TreeTracker/SwiftGen/Strings.swift b/TreeTracker/SwiftGen/Strings.swift index 75d9929b..a2d20959 100644 --- a/TreeTracker/SwiftGen/Strings.swift +++ b/TreeTracker/SwiftGen/Strings.swift @@ -95,6 +95,10 @@ internal enum L10n { /// Change User internal static let title = L10n.tr("Localizable", "Home.LogoutButton.Title", fallback: "Change User") } + internal enum MessagingButton { + /// MESSAGES + internal static let title = L10n.tr("Localizable", "Home.MessagingButton.Title", fallback: "MESSAGES") + } internal enum MyTreesButton { /// MY TREES internal static let title = L10n.tr("Localizable", "Home.MyTreesButton.Title", fallback: "MY TREES") diff --git a/TreeTracker/UI/Views/Home/Components/MessagingButton.swift b/TreeTracker/UI/Views/Home/Components/MessagingButton.swift new file mode 100644 index 00000000..8e7f35d4 --- /dev/null +++ b/TreeTracker/UI/Views/Home/Components/MessagingButton.swift @@ -0,0 +1,59 @@ +// +// MessagingButton.swift +// TreeTracker +// +// Created by Frédéric Helfer on 03/04/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import UIKit + +class MessagingButton: UIButton { + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } +} + +private extension MessagingButton { + + func commonInit() { + backgroundColor = Asset.Colors.primaryGreen.color + tintColor = .white + layer.cornerRadius = 5.0 + layer.masksToBounds = true + setAttributedTitle(attributedTitle, for: .normal) + } + + var attributedTitle: NSAttributedString { + let attributedString = NSMutableAttributedString() + attributedString.append(NSAttributedString(attachment: messageIconTextAttachment)) + attributedString.append(NSAttributedString(string: " ")) + attributedString.append(NSAttributedString(string: title, attributes: textAttributes)) + return attributedString + } + + var messageIconTextAttachment: NSTextAttachment { + let textAttachment = NSTextAttachment() + textAttachment.image = Asset.Assets.mail.image + textAttachment.bounds = CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: 50.0, height: 50.0)) + return textAttachment + } + + var title: String { + return L10n.Home.MessagingButton.title + } + + var textAttributes: [NSAttributedString.Key: Any] { + return [ + .font: FontFamily.Montserrat.semiBold.font(size: 20.0) + ] + } + +} diff --git a/TreeTracker/UI/Views/Home/Home.storyboard b/TreeTracker/UI/Views/Home/Home.storyboard index bc82b272..5cb7f09d 100644 --- a/TreeTracker/UI/Views/Home/Home.storyboard +++ b/TreeTracker/UI/Views/Home/Home.storyboard @@ -1,9 +1,9 @@ - + - + @@ -14,11 +14,11 @@ - + - + @@ -153,7 +153,7 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift new file mode 100644 index 00000000..be791351 --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -0,0 +1,27 @@ +// +// MessagesViewController.swift +// TreeTracker +// +// Created by Frédéric Helfer on 03/04/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import UIKit + +class MessagesViewController: UIViewController { + + var viewModel: MessagesViewModel? { + didSet { + viewModel?.viewDelegate = self + } + } + + override func viewDidLoad() { + super.viewDidLoad() + } +} + +// MARK: - MessagesViewModelViewDelegate +extension MessagesViewController: MessagesViewModelViewDelegate { + +} diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift new file mode 100644 index 00000000..f27e0d46 --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift @@ -0,0 +1,26 @@ +// +// MessagesViewModel.swift +// TreeTracker +// +// Created by Frédéric Helfer on 03/04/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import Foundation +import Treetracker_Core + +protocol MessagesViewModelViewDelegate: AnyObject { + +} + +class MessagesViewModel { + + weak var viewDelegate: MessagesViewModelViewDelegate? + + let planter: Planter + var messages: [String] = [] + + init(planter: Planter) { + self.planter = planter + } +} From 173cf5477873a8127c0966ae1a4142fa4eb26a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Tue, 4 Apr 2023 14:42:05 -0300 Subject: [PATCH 06/66] implement messagesTableViewCell --- .../Messages/Cell/MessagesTableViewCell.swift | 30 ++++++++++++++ .../Messages/Cell/MessagesTableViewCell.xib | 40 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.swift create mode 100644 TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.xib diff --git a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.swift b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.swift new file mode 100644 index 00000000..a080bb35 --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.swift @@ -0,0 +1,30 @@ +// +// MessagesTableViewCell.swift +// TreeTracker +// +// Created by Frédéric Helfer on 04/04/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import UIKit +import Treetracker_Core + +class MessagesTableViewCell: UITableViewCell { + + @IBOutlet private var messageLabel: UILabel! + + static let identifier: String = "MessagesTableViewCell" + static func nib() -> UINib { + UINib(nibName: identifier, bundle: nil) + } + +} + +// MARK: - Public func +extension MessagesTableViewCell { + + func setupCell(message: Message?) { + messageLabel.text = message?.body + } + +} diff --git a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.xib b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.xib new file mode 100644 index 00000000..5f2ab73b --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.xib @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From b0f4aad341c678e5a23ce945cc9efd22852587c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Tue, 4 Apr 2023 14:43:29 -0300 Subject: [PATCH 07/66] implement messagingService and navigation to messagesView --- TreeTracker.xcodeproj/project.pbxproj | 16 ++++++++ .../UI/Navigation/Home/HomeCoordinator.swift | 23 +++++++++++ .../ChatList/Cell/ChatListTableViewCell.swift | 11 ----- .../ChatList/ChatListViewController.swift | 6 ++- .../ChatList/ChatListViewModel.swift | 11 ++++- .../Messaging/Messages/Messages.storyboard | 11 +++++ .../Messages/MessagesViewController.swift | 41 +++++++++++++++++++ .../Messages/MessagesViewModel.swift | 35 ++++++++++++++-- 8 files changed, 137 insertions(+), 17 deletions(-) diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index d0c6f4f9..819c44b4 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -17,6 +17,8 @@ 8D75252929DB879F00361CC1 /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75252829DB879F00361CC1 /* MessagesViewController.swift */; }; 8D75252B29DB87AA00361CC1 /* MessagesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75252A29DB87AA00361CC1 /* MessagesViewModel.swift */; }; 8D75252D29DB87B800361CC1 /* Messages.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D75252C29DB87B800361CC1 /* Messages.storyboard */; }; + 8D75253129DC601F00361CC1 /* MessagesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75252F29DC601F00361CC1 /* MessagesTableViewCell.swift */; }; + 8D75253229DC601F00361CC1 /* MessagesTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8D75253029DC601F00361CC1 /* MessagesTableViewCell.xib */; }; 9311609126B40E5E007FCA52 /* DestructiveButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609026B40E5E007FCA52 /* DestructiveButton.swift */; }; 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609A26B4108A007FCA52 /* RoundedButton.swift */; }; 931C37B8249BACFA00EE24DC /* SelfieViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */; }; @@ -112,6 +114,8 @@ 8D75252829DB879F00361CC1 /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; 8D75252A29DB87AA00361CC1 /* MessagesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewModel.swift; sourceTree = ""; }; 8D75252C29DB87B800361CC1 /* Messages.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Messages.storyboard; sourceTree = ""; }; + 8D75252F29DC601F00361CC1 /* MessagesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesTableViewCell.swift; sourceTree = ""; }; + 8D75253029DC601F00361CC1 /* MessagesTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MessagesTableViewCell.xib; sourceTree = ""; }; 9311609026B40E5E007FCA52 /* DestructiveButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestructiveButton.swift; sourceTree = ""; }; 9311609A26B4108A007FCA52 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfieViewModel.swift; sourceTree = ""; }; @@ -252,6 +256,7 @@ 8D75252329DB83F300361CC1 /* Messages */ = { isa = PBXGroup; children = ( + 8D75252E29DC5F9A00361CC1 /* Cell */, 8D75252A29DB87AA00361CC1 /* MessagesViewModel.swift */, 8D75252829DB879F00361CC1 /* MessagesViewController.swift */, 8D75252C29DB87B800361CC1 /* Messages.storyboard */, @@ -259,6 +264,15 @@ path = Messages; sourceTree = ""; }; + 8D75252E29DC5F9A00361CC1 /* Cell */ = { + isa = PBXGroup; + children = ( + 8D75252F29DC601F00361CC1 /* MessagesTableViewCell.swift */, + 8D75253029DC601F00361CC1 /* MessagesTableViewCell.xib */, + ); + path = Cell; + sourceTree = ""; + }; 9311609426B40EE0007FCA52 /* Buttons */ = { isa = PBXGroup; children = ( @@ -767,6 +781,7 @@ buildActionMask = 2147483647; files = ( 93E1893D245B738100217F21 /* LaunchScreen.storyboard in Resources */, + 8D75253229DC601F00361CC1 /* MessagesTableViewCell.xib in Resources */, 938294B227F9A7AB00888E42 /* Settings.storyboard in Resources */, 93EC8C3D24AA52F000946C0C /* Home.storyboard in Resources */, 931C37BA249BAD0900EE24DC /* Selfie.storyboard in Resources */, @@ -934,6 +949,7 @@ 93E1896B245DEBDB00217F21 /* Coordinator.swift in Sources */, 93E82F66246C439100006C91 /* Strings.swift in Sources */, 93989CFB246EABF100A1B21F /* TermsViewController.swift in Sources */, + 8D75253129DC601F00361CC1 /* MessagesTableViewCell.swift in Sources */, 8D75252029DB6E7E00361CC1 /* ChatListTableViewCell.swift in Sources */, 93EC8CAB24BBA9E100946C0C /* AddTreeViewController.swift in Sources */, 93EC8C4624AB7A6900946C0C /* AddTreeButton.swift in Sources */, diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index 4c5c4afc..4dadb4a0 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -68,6 +68,13 @@ private extension HomeCoordinator { ) } + func showMessages(planter: Planter) { + configuration.navigationController.pushViewController( + messagesViewController(planter: planter), + animated: true + ) + } + func showNotes(note: String?) { configuration.navigationController.pushViewController( notesViewController(note: note), @@ -149,6 +156,18 @@ private extension HomeCoordinator { return viewcontroller } + func messagesViewController(planter: Planter) -> UIViewController { + let viewcontroller = StoryboardScene.Messages.initialScene.instantiate() + viewcontroller.viewModel = { + let viewModel = MessagesViewModel( + planter: planter, + messagingService: treetrackerSDK.messagingService + ) + return viewModel + }() + return viewcontroller + } + func notesViewController(note: String?) -> UIViewController { let viewController = StoryboardScene.Notes.initialScene.instantiate() viewController.viewModel = { @@ -269,4 +288,8 @@ extension HomeCoordinator: NotesViewModelCoordinatorDelegate { // MARK: - ChatListViewModelCoordinatorDelegate extension HomeCoordinator: ChatListViewModelCoordinatorDelegate { + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectMessagesForPlanter planter: Planter) { + showMessages(planter: planter) + } + } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift index 7f172b14..cb1307bf 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift @@ -39,17 +39,6 @@ class ChatListTableViewCell: UITableViewCell { return UINib(nibName: identifier, bundle: nil) } - override func awakeFromNib() { - super.awakeFromNib() - // Initialization code - } - - override func setSelected(_ selected: Bool, animated: Bool) { - super.setSelected(selected, animated: animated) - - // Configure the view for the selected state - } - } // MARK: - Public Actions diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index 9dc9c64b..10592d8f 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -41,6 +41,7 @@ extension ChatListViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: ChatListTableViewCell.identifier, for: indexPath) as? ChatListTableViewCell + // TODO: setup cell?.setupCell() return cell ?? UITableViewCell() @@ -56,7 +57,10 @@ extension ChatListViewController: UITableViewDataSource { extension ChatListViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - // + + // TODO: work on type of message + viewModel?.messagesSelectet() + } } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index 2f3d519e..f778af5f 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -10,7 +10,7 @@ import Foundation import Treetracker_Core protocol ChatListViewModelCoordinatorDelegate: AnyObject { - + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectMessagesForPlanter planter: Planter) } protocol ChatListViewModelViewDelegate: AnyObject { @@ -32,3 +32,12 @@ class ChatListViewModel { return planter.firstName ?? "" } } + +// MARK: - Navigation +extension ChatListViewModel { + + func messagesSelectet() { + coordinatorDelegate?.chatListViewModel(self, didSelectMessagesForPlanter: planter) + } + +} diff --git a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard index e076a550..7d680fe3 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard +++ b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard @@ -36,15 +36,26 @@ + + + + + + + + + + + diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index be791351..ee115979 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -7,9 +7,18 @@ // import UIKit +import Treetracker_Core class MessagesViewController: UIViewController { + @IBOutlet private var messagesTableView: UITableView! { + didSet { + messagesTableView.dataSource = self + messagesTableView.delegate = self + messagesTableView.register(MessagesTableViewCell.nib(), forCellReuseIdentifier: MessagesTableViewCell.identifier) + } + } + var viewModel: MessagesViewModel? { didSet { viewModel?.viewDelegate = self @@ -18,10 +27,42 @@ class MessagesViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + viewModel?.fetchMessages() + } +} + +// MARK: - UITableViewDataSource +extension MessagesViewController: UITableViewDataSource { + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewModel?.getNumberOfRowsInSection() ?? 0 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: MessagesTableViewCell.identifier, for: indexPath) as? MessagesTableViewCell + + let message = viewModel?.getMessageForRowAt(indexPath: indexPath) + cell?.setupCell(message: message) + + return cell ?? UITableViewCell() } + +} + +// MARK: - UITableViewDelegate +extension MessagesViewController: UITableViewDelegate { + } // MARK: - MessagesViewModelViewDelegate extension MessagesViewController: MessagesViewModelViewDelegate { + func messagesViewModel(_ messagesViewModel: MessagesViewModel, didFetchMessages messages: [Message]) { + messagesTableView.reloadData() + } + + func messagesViewModel(_ messagesViewModel: MessagesViewModel, didReceiveError error: Error) { + // do something + } + } diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift index f27e0d46..e084489b 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift @@ -10,17 +10,44 @@ import Foundation import Treetracker_Core protocol MessagesViewModelViewDelegate: AnyObject { - + func messagesViewModel(_ messagesViewModel: MessagesViewModel, didFetchMessages messages: [Message]) + func messagesViewModel(_ messagesViewModel: MessagesViewModel, didReceiveError error: Error) } class MessagesViewModel { weak var viewDelegate: MessagesViewModelViewDelegate? - let planter: Planter - var messages: [String] = [] + private let planter: Planter + private let messagingService: MessagingService + + private var messages: [Message] = [] - init(planter: Planter) { + init(planter: Planter, messagingService: MessagingService) { self.planter = planter + self.messagingService = messagingService + } + + func getNumberOfRowsInSection() -> Int { + messages.count + } + + func getMessageForRowAt(indexPath: IndexPath) -> Message { + messages[indexPath.row] + } + + func fetchMessages() { + + messagingService.getMessages(planter: planter) { result in + + switch result { + case .success(let messages): + self.messages = messages + self.viewDelegate?.messagesViewModel(self, didFetchMessages: messages) + case .failure(let error): + self.viewDelegate?.messagesViewModel(self, didReceiveError: error) + } + } + } } From 3f5650920afa3731fa8cb1e07932de9e2dee234e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Tue, 4 Apr 2023 18:39:36 -0300 Subject: [PATCH 08/66] add bell icon and some small refactor --- TreeTracker.xcodeproj/project.pbxproj | 4 +++ .../Icons/bell.imageset/Contents.json | 24 ++++++++++++++++++ .../Icons/bell.imageset/bell.png | Bin 0 -> 18408 bytes TreeTracker/SwiftGen/Assets.swift | 1 + .../Home/Components/MessagingButton.swift | 2 +- .../UI/Views/Home/HomeViewController.swift | 2 +- .../ChatList/Cell/ChatListTableViewCell.swift | 5 ++-- .../ChatList/Cell/ChatListTableViewCell.xib | 18 ++++++------- .../ChatList/ChatListViewController.swift | 3 ++- .../ChatList/ChatListViewModel.swift | 2 +- 10 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 TreeTracker/Resources/Assets.xcassets/Icons/bell.imageset/Contents.json create mode 100644 TreeTracker/Resources/Assets.xcassets/Icons/bell.imageset/bell.png diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index 819c44b4..e1ce7a2a 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 8D75252D29DB87B800361CC1 /* Messages.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D75252C29DB87B800361CC1 /* Messages.storyboard */; }; 8D75253129DC601F00361CC1 /* MessagesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75252F29DC601F00361CC1 /* MessagesTableViewCell.swift */; }; 8D75253229DC601F00361CC1 /* MessagesTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8D75253029DC601F00361CC1 /* MessagesTableViewCell.xib */; }; + 8D75253629DCBD7400361CC1 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75253529DCBD7400361CC1 /* UIImage+Extensions.swift */; }; 9311609126B40E5E007FCA52 /* DestructiveButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609026B40E5E007FCA52 /* DestructiveButton.swift */; }; 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609A26B4108A007FCA52 /* RoundedButton.swift */; }; 931C37B8249BACFA00EE24DC /* SelfieViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */; }; @@ -116,6 +117,7 @@ 8D75252C29DB87B800361CC1 /* Messages.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Messages.storyboard; sourceTree = ""; }; 8D75252F29DC601F00361CC1 /* MessagesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesTableViewCell.swift; sourceTree = ""; }; 8D75253029DC601F00361CC1 /* MessagesTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MessagesTableViewCell.xib; sourceTree = ""; }; + 8D75253529DCBD7400361CC1 /* UIImage+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = ""; }; 9311609026B40E5E007FCA52 /* DestructiveButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestructiveButton.swift; sourceTree = ""; }; 9311609A26B4108A007FCA52 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfieViewModel.swift; sourceTree = ""; }; @@ -618,6 +620,7 @@ isa = PBXGroup; children = ( 93EC8CA724B28B5D00946C0C /* UITextField+Extensions.swift */, + 8D75253529DCBD7400361CC1 /* UIImage+Extensions.swift */, ); path = Extensions; sourceTree = ""; @@ -957,6 +960,7 @@ 938294BA27F9D00800888E42 /* PhotoQuality.swift in Sources */, B0AAD80628C235C900640D4A /* NotesViewController.swift in Sources */, 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */, + 8D75253629DCBD7400361CC1 /* UIImage+Extensions.swift in Sources */, 93EC8C3424AA4AE100946C0C /* HomeCoordinator.swift in Sources */, 938294AE27F9A79900888E42 /* SettingsViewModel.swift in Sources */, ); diff --git a/TreeTracker/Resources/Assets.xcassets/Icons/bell.imageset/Contents.json b/TreeTracker/Resources/Assets.xcassets/Icons/bell.imageset/Contents.json new file mode 100644 index 00000000..ad93c442 --- /dev/null +++ b/TreeTracker/Resources/Assets.xcassets/Icons/bell.imageset/Contents.json @@ -0,0 +1,24 @@ +{ + "images" : [ + { + "filename" : "bell.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/TreeTracker/Resources/Assets.xcassets/Icons/bell.imageset/bell.png b/TreeTracker/Resources/Assets.xcassets/Icons/bell.imageset/bell.png new file mode 100644 index 0000000000000000000000000000000000000000..3b36e8569329b4f1b457002734f0eb350de33aaf GIT binary patch literal 18408 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

40q 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 A?f?J) literal 0 HcmV?d00001 diff --git a/TreeTracker/SwiftGen/Assets.swift b/TreeTracker/SwiftGen/Assets.swift index 81dc5aa7..91b7d3ce 100644 --- a/TreeTracker/SwiftGen/Assets.swift +++ b/TreeTracker/SwiftGen/Assets.swift @@ -31,6 +31,7 @@ internal enum Asset { internal static let padlock = ImageAsset(name: "Padlock") internal static let add = ImageAsset(name: "add") internal static let arrow = ImageAsset(name: "arrow") + internal static let bell = ImageAsset(name: "bell") internal static let logout = ImageAsset(name: "logout") internal static let mail = ImageAsset(name: "mail") internal static let note = ImageAsset(name: "note") diff --git a/TreeTracker/UI/Views/Home/Components/MessagingButton.swift b/TreeTracker/UI/Views/Home/Components/MessagingButton.swift index 5bdafd9a..9a6b8397 100644 --- a/TreeTracker/UI/Views/Home/Components/MessagingButton.swift +++ b/TreeTracker/UI/Views/Home/Components/MessagingButton.swift @@ -42,7 +42,7 @@ private extension MessagingButton { var messageIconTextAttachment: NSTextAttachment { let textAttachment = NSTextAttachment() textAttachment.image = Asset.Assets.mail.image - textAttachment.bounds = CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: 50.0, height: 50.0)) + textAttachment.bounds = CGRect(origin: CGPoint(x: 0.0, y: -15.0), size: CGSize(width: 50.0, height: 40.0)) return textAttachment } diff --git a/TreeTracker/UI/Views/Home/HomeViewController.swift b/TreeTracker/UI/Views/Home/HomeViewController.swift index b0d262e3..8d3f6fd1 100644 --- a/TreeTracker/UI/Views/Home/HomeViewController.swift +++ b/TreeTracker/UI/Views/Home/HomeViewController.swift @@ -122,7 +122,7 @@ class HomeViewController: UIViewController, AlertPresenting { @IBOutlet private var messagingAlert: UIImageView! { didSet { - messagingAlert.image = Asset.Assets.add.image + messagingAlert.image = Asset.Assets.bell.image.withRenderingMode(.alwaysTemplate) messagingAlert.tintColor = .white messagingAlert.backgroundColor = Asset.Colors.secondaryRed.color messagingAlert.layer.cornerRadius = 10 diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift index cb1307bf..e0496cca 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift @@ -20,13 +20,14 @@ class ChatListTableViewCell: UITableViewCell { @IBOutlet private var chatTitle: UILabel! { didSet { - chatTitle.font = FontFamily.Montserrat.semiBold.font(size: 20) + chatTitle.font = FontFamily.Montserrat.semiBold.font(size: 19) + chatTitle.textColor = Asset.Colors.grayDark.color } } @IBOutlet private var chatAlert: UIImageView! { didSet { - chatAlert.image = Asset.Assets.add.image + chatAlert.image = Asset.Assets.bell.image.withRenderingMode(.alwaysTemplate) chatAlert.tintColor = .white chatAlert.backgroundColor = Asset.Colors.secondaryRed.color chatAlert.layer.cornerRadius = 10 diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib index 771fcb89..db437c36 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib @@ -10,22 +10,22 @@ - - + + - + - + - - + + diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index 10592d8f..16c89703 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -43,12 +43,13 @@ extension ChatListViewController: UITableViewDataSource { // TODO: setup cell?.setupCell() + cell?.selectionStyle = .none return cell ?? UITableViewCell() } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - 140 + 130 } } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index f778af5f..66cb8e9c 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -39,5 +39,5 @@ extension ChatListViewModel { func messagesSelectet() { coordinatorDelegate?.chatListViewModel(self, didSelectMessagesForPlanter: planter) } - + } From 7b4052c215b2365936bf14b27c92a18928a09dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 5 Apr 2023 10:57:41 -0300 Subject: [PATCH 09/66] message view with bubbles --- .../Messages/Cell/MessagesTableViewCell.swift | 45 +++++++++++- .../Messages/Cell/MessagesTableViewCell.xib | 46 ++++++++---- .../Messaging/Messages/Messages.storyboard | 72 +++++++++++-------- .../Messages/MessagesViewController.swift | 67 +++++++++++++++-- .../Messages/MessagesViewModel.swift | 12 ++++ 5 files changed, 193 insertions(+), 49 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.swift b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.swift index a080bb35..41e6078e 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.swift @@ -11,20 +11,63 @@ import Treetracker_Core class MessagesTableViewCell: UITableViewCell { - @IBOutlet private var messageLabel: UILabel! + @IBOutlet private var messageBackgroundView: UIView! { + didSet { + messageBackgroundView.layer.cornerRadius = 16 + messageBackgroundView.clipsToBounds = true + messageBackgroundView.backgroundColor = .systemBlue + } + } + + @IBOutlet private var messageLabel: UILabel! { + didSet { + messageLabel.font = FontFamily.Lato.regular.font(size: 16) + messageLabel.numberOfLines = 0 + messageLabel.textColor = UIColor.white + } + } + + var trailingConstraint: NSLayoutConstraint! + var leadingConstraint: NSLayoutConstraint! static let identifier: String = "MessagesTableViewCell" static func nib() -> UINib { UINib(nibName: identifier, bundle: nil) } + override func prepareForReuse() { + super.prepareForReuse() + messageLabel.text = nil + trailingConstraint.isActive = false + leadingConstraint.isActive = false + } + } // MARK: - Public func extension MessagesTableViewCell { func setupCell(message: Message?) { + trailingConstraint = messageBackgroundView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20) + leadingConstraint = messageBackgroundView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20) + messageLabel.text = message?.body + + // TODO: if messsage.from == planter.name? + if message?.from == "admin" { + + messageBackgroundView.backgroundColor = .systemGreen + leadingConstraint.isActive = true + messageLabel.textAlignment = .left + + } else { + + messageBackgroundView.backgroundColor = .systemBlue + trailingConstraint.isActive = true + messageLabel.textAlignment = .right + + } + } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.xib b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.xib index 5f2ab73b..225bbfed 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.xib +++ b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessagesTableViewCell.xib @@ -5,36 +5,56 @@ + - - + + - + - + + + + + + + + + + + + + - - - + + + + + - + + + + + + diff --git a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard index 7d680fe3..e58cb11d 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard +++ b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard @@ -17,57 +17,67 @@ - - + + - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + - + + + + - - - diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index ee115979..955d2164 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -13,12 +13,41 @@ class MessagesViewController: UIViewController { @IBOutlet private var messagesTableView: UITableView! { didSet { + messagesTableView.separatorStyle = .none messagesTableView.dataSource = self - messagesTableView.delegate = self messagesTableView.register(MessagesTableViewCell.nib(), forCellReuseIdentifier: MessagesTableViewCell.identifier) } } + @IBOutlet private var inputTextView: UITextView! { + didSet { + inputTextView.layer.masksToBounds = true + inputTextView.layer.cornerRadius = 10 + inputTextView.layer.borderWidth = 2 + inputTextView.layer.borderColor = Asset.Colors.secondaryGreen.color.cgColor + inputTextView.font = FontFamily.Lato.regular.font(size: 16) + inputTextView.textColor = Asset.Colors.grayDark.color + inputTextView.textAlignment = .left + inputTextView.dataDetectorTypes = .all + inputTextView.layer.shadowOpacity = 0.5 + inputTextView.isEditable = true + inputTextView.delegate = self + + // TODO: Let it grow as the text grows + inputTextView.isScrollEnabled = false + inputTextView.sizeToFit() + } + } + + @IBOutlet private var sendMessageButton: UIButton! { + didSet { + // TODO: Change the button icon + + sendMessageButton.titleLabel?.font = FontFamily.Lato.regular.font(size: 30) + sendMessageButton.backgroundColor = Asset.Colors.secondaryOrangeDark.color + } + } + var viewModel: MessagesViewModel? { didSet { viewModel?.viewDelegate = self @@ -27,8 +56,25 @@ class MessagesViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + // TODO: change color + view.backgroundColor = .systemGray viewModel?.fetchMessages() } + +} + +// MARK: - Button Actions +private extension MessagesViewController { + + @IBAction func sendMessageButtonPressed() { + + guard let messageText = inputTextView.text else { return } + + print(messageText) + viewModel?.sendMessage(text: messageText) + + } + } // MARK: - UITableViewDataSource @@ -43,15 +89,28 @@ extension MessagesViewController: UITableViewDataSource { let message = viewModel?.getMessageForRowAt(indexPath: indexPath) cell?.setupCell(message: message) + cell?.selectionStyle = .none return cell ?? UITableViewCell() } + func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + return 49 + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return UITableView.automaticDimension + } + } -// MARK: - UITableViewDelegate -extension MessagesViewController: UITableViewDelegate { +// MARK: - UITextViewDelegate +extension MessagesViewController: UITextViewDelegate { + func textViewDidChange(_ textView: UITextView) { +// let size = textView.sizeThatFits(CGSize(width: textView.bounds.width, height: .infinity)) +// textView.heightAnchor.constraint(equalToConstant: size.height).isActive = true + } } // MARK: - MessagesViewModelViewDelegate @@ -62,7 +121,7 @@ extension MessagesViewController: MessagesViewModelViewDelegate { } func messagesViewModel(_ messagesViewModel: MessagesViewModel, didReceiveError error: Error) { - // do something + // TODO: show some alert? } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift index e084489b..03a1ef5c 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift @@ -21,6 +21,7 @@ class MessagesViewModel { private let planter: Planter private let messagingService: MessagingService + // TODO: Ask what's preferable, private, set, or public? private var messages: [Message] = [] init(planter: Planter, messagingService: MessagingService) { @@ -50,4 +51,15 @@ class MessagesViewModel { } } + + func sendMessage(text: String) { +// let newMessage = Message(...) + + // TODO: Cache new message. - Needs a variable to know if it was uploaded or not? + +// messages.append(newMessage) + + // TODO: Try to upload message + + } } From f7e19cac3b97cdf776af4163b758de8e75dc658f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 5 Apr 2023 15:40:10 -0300 Subject: [PATCH 10/66] refactoring --- TreeTracker.xcodeproj/project.pbxproj | 20 +++++------- .../ChatList/Cell/ChatListTableViewCell.xib | 10 +++--- .../ChatList/ChatListViewController.swift | 31 ++++++++++++------- .../ChatList/ChatListViewModel.swift | 20 ++++++++++-- ...wCell.swift => MessageTableViewCell.swift} | 14 ++++----- ...eViewCell.xib => MessageTableViewCell.xib} | 2 +- .../Messages/MessagesViewController.swift | 27 ++++++++++------ .../Messages/MessagesViewModel.swift | 18 ++++++----- 8 files changed, 85 insertions(+), 57 deletions(-) rename TreeTracker/UI/Views/Messaging/Messages/Cell/{MessagesTableViewCell.swift => MessageTableViewCell.swift} (82%) rename TreeTracker/UI/Views/Messaging/Messages/Cell/{MessagesTableViewCell.xib => MessageTableViewCell.xib} (98%) diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index e1ce7a2a..678087ea 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -17,9 +17,8 @@ 8D75252929DB879F00361CC1 /* MessagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75252829DB879F00361CC1 /* MessagesViewController.swift */; }; 8D75252B29DB87AA00361CC1 /* MessagesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75252A29DB87AA00361CC1 /* MessagesViewModel.swift */; }; 8D75252D29DB87B800361CC1 /* Messages.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D75252C29DB87B800361CC1 /* Messages.storyboard */; }; - 8D75253129DC601F00361CC1 /* MessagesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75252F29DC601F00361CC1 /* MessagesTableViewCell.swift */; }; - 8D75253229DC601F00361CC1 /* MessagesTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8D75253029DC601F00361CC1 /* MessagesTableViewCell.xib */; }; - 8D75253629DCBD7400361CC1 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75253529DCBD7400361CC1 /* UIImage+Extensions.swift */; }; + 8D75253129DC601F00361CC1 /* MessageTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75252F29DC601F00361CC1 /* MessageTableViewCell.swift */; }; + 8D75253229DC601F00361CC1 /* MessageTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8D75253029DC601F00361CC1 /* MessageTableViewCell.xib */; }; 9311609126B40E5E007FCA52 /* DestructiveButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609026B40E5E007FCA52 /* DestructiveButton.swift */; }; 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609A26B4108A007FCA52 /* RoundedButton.swift */; }; 931C37B8249BACFA00EE24DC /* SelfieViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */; }; @@ -115,9 +114,8 @@ 8D75252829DB879F00361CC1 /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; }; 8D75252A29DB87AA00361CC1 /* MessagesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewModel.swift; sourceTree = ""; }; 8D75252C29DB87B800361CC1 /* Messages.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Messages.storyboard; sourceTree = ""; }; - 8D75252F29DC601F00361CC1 /* MessagesTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesTableViewCell.swift; sourceTree = ""; }; - 8D75253029DC601F00361CC1 /* MessagesTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MessagesTableViewCell.xib; sourceTree = ""; }; - 8D75253529DCBD7400361CC1 /* UIImage+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = ""; }; + 8D75252F29DC601F00361CC1 /* MessageTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageTableViewCell.swift; sourceTree = ""; }; + 8D75253029DC601F00361CC1 /* MessageTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MessageTableViewCell.xib; sourceTree = ""; }; 9311609026B40E5E007FCA52 /* DestructiveButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestructiveButton.swift; sourceTree = ""; }; 9311609A26B4108A007FCA52 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfieViewModel.swift; sourceTree = ""; }; @@ -269,8 +267,8 @@ 8D75252E29DC5F9A00361CC1 /* Cell */ = { isa = PBXGroup; children = ( - 8D75252F29DC601F00361CC1 /* MessagesTableViewCell.swift */, - 8D75253029DC601F00361CC1 /* MessagesTableViewCell.xib */, + 8D75252F29DC601F00361CC1 /* MessageTableViewCell.swift */, + 8D75253029DC601F00361CC1 /* MessageTableViewCell.xib */, ); path = Cell; sourceTree = ""; @@ -620,7 +618,6 @@ isa = PBXGroup; children = ( 93EC8CA724B28B5D00946C0C /* UITextField+Extensions.swift */, - 8D75253529DCBD7400361CC1 /* UIImage+Extensions.swift */, ); path = Extensions; sourceTree = ""; @@ -784,7 +781,7 @@ buildActionMask = 2147483647; files = ( 93E1893D245B738100217F21 /* LaunchScreen.storyboard in Resources */, - 8D75253229DC601F00361CC1 /* MessagesTableViewCell.xib in Resources */, + 8D75253229DC601F00361CC1 /* MessageTableViewCell.xib in Resources */, 938294B227F9A7AB00888E42 /* Settings.storyboard in Resources */, 93EC8C3D24AA52F000946C0C /* Home.storyboard in Resources */, 931C37BA249BAD0900EE24DC /* Selfie.storyboard in Resources */, @@ -952,7 +949,7 @@ 93E1896B245DEBDB00217F21 /* Coordinator.swift in Sources */, 93E82F66246C439100006C91 /* Strings.swift in Sources */, 93989CFB246EABF100A1B21F /* TermsViewController.swift in Sources */, - 8D75253129DC601F00361CC1 /* MessagesTableViewCell.swift in Sources */, + 8D75253129DC601F00361CC1 /* MessageTableViewCell.swift in Sources */, 8D75252029DB6E7E00361CC1 /* ChatListTableViewCell.swift in Sources */, 93EC8CAB24BBA9E100946C0C /* AddTreeViewController.swift in Sources */, 93EC8C4624AB7A6900946C0C /* AddTreeButton.swift in Sources */, @@ -960,7 +957,6 @@ 938294BA27F9D00800888E42 /* PhotoQuality.swift in Sources */, B0AAD80628C235C900640D4A /* NotesViewController.swift in Sources */, 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */, - 8D75253629DCBD7400361CC1 /* UIImage+Extensions.swift in Sources */, 93EC8C3424AA4AE100946C0C /* HomeCoordinator.swift in Sources */, 938294AE27F9A79900888E42 /* SettingsViewModel.swift in Sources */, ); diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib index db437c36..bcec0108 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib @@ -10,22 +10,22 @@ - - + + - + - +

40q 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 A?f?J) diff --git a/TreeTracker/SwiftGen/Assets.swift b/TreeTracker/SwiftGen/Assets.swift index 91b7d3ce..81dc5aa7 100644 --- a/TreeTracker/SwiftGen/Assets.swift +++ b/TreeTracker/SwiftGen/Assets.swift @@ -31,7 +31,6 @@ internal enum Asset { internal static let padlock = ImageAsset(name: "Padlock") internal static let add = ImageAsset(name: "add") internal static let arrow = ImageAsset(name: "arrow") - internal static let bell = ImageAsset(name: "bell") internal static let logout = ImageAsset(name: "logout") internal static let mail = ImageAsset(name: "mail") internal static let note = ImageAsset(name: "note") diff --git a/TreeTracker/UI/Views/Home/Home.storyboard b/TreeTracker/UI/Views/Home/Home.storyboard index 5cb7f09d..e2cf34ee 100644 --- a/TreeTracker/UI/Views/Home/Home.storyboard +++ b/TreeTracker/UI/Views/Home/Home.storyboard @@ -214,23 +214,38 @@ - - + + + + + + - - + + + + - + + - - + @@ -239,7 +254,8 @@ - + + diff --git a/TreeTracker/UI/Views/Home/HomeViewController.swift b/TreeTracker/UI/Views/Home/HomeViewController.swift index 8d3f6fd1..f58cdfae 100644 --- a/TreeTracker/UI/Views/Home/HomeViewController.swift +++ b/TreeTracker/UI/Views/Home/HomeViewController.swift @@ -120,13 +120,19 @@ class HomeViewController: UIViewController, AlertPresenting { @IBOutlet private var messagingButton: MessagingButton! - @IBOutlet private var messagingAlert: UIImageView! { + @IBOutlet private var messageAlertCountView: UIView! { didSet { - messagingAlert.image = Asset.Assets.bell.image.withRenderingMode(.alwaysTemplate) - messagingAlert.tintColor = .white - messagingAlert.backgroundColor = Asset.Colors.secondaryRed.color - messagingAlert.layer.cornerRadius = 10 - messagingAlert.layer.masksToBounds = true + messageAlertCountView.backgroundColor = Asset.Colors.secondaryRed.color + messageAlertCountView.layer.cornerRadius = 15 + } + } + + @IBOutlet private var messageAlertCountLabel: UILabel! { + didSet { + messageAlertCountLabel.textColor = .white + messageAlertCountLabel.font = FontFamily.Montserrat.semiBold.font(size: 20.0) + messageAlertCountLabel.textAlignment = .center + messageAlertCountLabel.text = "7" } } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift index e0496cca..88da00f4 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift @@ -25,16 +25,28 @@ class ChatListTableViewCell: UITableViewCell { } } - @IBOutlet private var chatAlert: UIImageView! { + @IBOutlet private var chatAlertCountView: UIView! { didSet { - chatAlert.image = Asset.Assets.bell.image.withRenderingMode(.alwaysTemplate) - chatAlert.tintColor = .white - chatAlert.backgroundColor = Asset.Colors.secondaryRed.color - chatAlert.layer.cornerRadius = 10 - chatAlert.layer.masksToBounds = true + chatAlertCountView.backgroundColor = Asset.Colors.secondaryRed.color + chatAlertCountView.layer.cornerRadius = 15 + chatAlertCountView.isHidden = true } } + @IBOutlet private var chatAlertCountLabel: UILabel! { + didSet { + chatAlertCountLabel.textColor = .white + chatAlertCountLabel.font = FontFamily.Montserrat.semiBold.font(size: 20.0) + chatAlertCountLabel.textAlignment = .center + chatAlertCountLabel.isHidden = true + } + } + + override func prepareForReuse() { + chatAlertCountView.isHidden = true + chatAlertCountLabel.isHidden = true + } + static let identifier = "ChatListTableViewCell" static func nib() -> UINib { return UINib(nibName: identifier, bundle: nil) @@ -48,5 +60,9 @@ extension ChatListTableViewCell { func setupCell() { chatImage.image = Asset.Assets.trees.image chatTitle.text = "Admin" + + chatAlertCountView.isHidden = false + chatAlertCountLabel.isHidden = false + chatAlertCountLabel.text = "7" } } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib index bcec0108..5649d912 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib @@ -5,6 +5,7 @@ + @@ -30,31 +31,52 @@ - - + + + + + + - - + + + + - + - - + + - + + + + + + + From f1b563a7f2c126bf1282c985ae400c87ef69e130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Helfer?= Date: Wed, 12 Apr 2023 15:52:17 -0300 Subject: [PATCH 13/66] refactor: input text view grows when text get bigger --- .DS_Store | Bin 6148 -> 6148 bytes .../Messaging/Messages/Messages.storyboard | 84 ++++++++++-------- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/.DS_Store b/.DS_Store index 32890b42bdeb532f85398f061f2056a48d80ef62..c46262aae54061ce4eb074aaed5433b38e835435 100644 GIT binary patch delta 49 zcmV-10M7q}FoZC$NCW~y_mj>9E|Yi!3;ba}2>lOP CCJ+Mv diff --git a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard index e58cb11d..97670807 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard +++ b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard @@ -4,6 +4,7 @@ + @@ -17,48 +18,58 @@ - - + + + + + + - - + + - - - - - - - - - - - - - - - + + + + + + + + - + + + + + + + + + + - - - - + + + + + + + @@ -73,6 +84,9 @@ + + + From c6ec76b79b01a9c865c0d5aee50f38191bd6b625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 12 Apr 2023 16:47:24 -0300 Subject: [PATCH 14/66] some design changes, added backgroundGreen Color --- .../BackgroundGreen.colorset/Contents.json | 20 +++++++++++++++++++ .../Colors.xcassets/Background/Contents.json | 6 ++++++ TreeTracker/SwiftGen/Assets.swift | 1 + .../ChatList/ChatListViewController.swift | 2 ++ .../Messaging/Messages/Messages.storyboard | 9 ++++----- .../Messages/MessagesViewController.swift | 16 ++++++++------- 6 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json create mode 100644 TreeTracker/Resources/Colors.xcassets/Background/Contents.json diff --git a/TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json b/TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json new file mode 100644 index 00000000..fb50632e --- /dev/null +++ b/TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.810", + "green" : "0.929", + "red" : "0.873" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TreeTracker/Resources/Colors.xcassets/Background/Contents.json b/TreeTracker/Resources/Colors.xcassets/Background/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/TreeTracker/Resources/Colors.xcassets/Background/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TreeTracker/SwiftGen/Assets.swift b/TreeTracker/SwiftGen/Assets.swift index 81dc5aa7..f72191e6 100644 --- a/TreeTracker/SwiftGen/Assets.swift +++ b/TreeTracker/SwiftGen/Assets.swift @@ -54,6 +54,7 @@ internal enum Asset { } } internal enum Colors { + internal static let backgroundGreen = ColorAsset(name: "BackgroundGreen") internal static let grayDark = ColorAsset(name: "GrayDark") internal static let grayLight = ColorAsset(name: "GrayLight") internal static let grayMedium = ColorAsset(name: "GrayMedium") diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index dff51cfe..7b13e5ff 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -15,6 +15,7 @@ class ChatListViewController: UIViewController { chatListTableView.delegate = self chatListTableView.dataSource = self chatListTableView.register(ChatListTableViewCell.nib(), forCellReuseIdentifier: ChatListTableViewCell.identifier) + chatListTableView.backgroundColor = Asset.Colors.backgroundGreen.color } } @@ -27,6 +28,7 @@ class ChatListViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = Asset.Colors.backgroundGreen.color } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard index 97670807..c573e7b2 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard +++ b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard @@ -26,7 +26,7 @@ - + @@ -36,22 +36,21 @@ - - + diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index 49a78c79..ffd5e651 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -23,13 +23,12 @@ class MessagesViewController: UIViewController { didSet { inputTextView.layer.masksToBounds = true inputTextView.layer.cornerRadius = 10 - inputTextView.layer.borderWidth = 2 - inputTextView.layer.borderColor = Asset.Colors.secondaryGreen.color.cgColor + inputTextView.layer.borderWidth = 1 + inputTextView.layer.borderColor = Asset.Colors.primaryGreen.color.cgColor inputTextView.font = FontFamily.Lato.regular.font(size: 16) inputTextView.textColor = Asset.Colors.grayDark.color inputTextView.delegate = self - // TODO: Let it grow as the text grows inputTextView.isScrollEnabled = false inputTextView.sizeToFit() } @@ -38,8 +37,12 @@ class MessagesViewController: UIViewController { @IBOutlet private var sendMessageButton: UIButton! { didSet { // TODO: Change the button icon - sendMessageButton.titleLabel?.font = FontFamily.Lato.regular.font(size: 30) - sendMessageButton.backgroundColor = Asset.Colors.secondaryOrangeDark.color + sendMessageButton.titleLabel?.font = FontFamily.Montserrat.bold.font(size: 30) + sendMessageButton.backgroundColor = Asset.Colors.primaryGreen.color + sendMessageButton.setTitle(">", for: .normal) + sendMessageButton.tintColor = Asset.Colors.grayDark.color + sendMessageButton.layer.cornerRadius = 15 + sendMessageButton.clipsToBounds = true } } @@ -51,8 +54,7 @@ class MessagesViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - // TODO: change color - view.backgroundColor = .systemGray + view.backgroundColor = Asset.Colors.backgroundGreen.color viewModel?.fetchMessages() } From 7e791dc32b0bfdb27d5d5c5c7af8b53a3fe26758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Helfer?= Date: Fri, 14 Apr 2023 16:58:55 -0300 Subject: [PATCH 15/66] add image to navigation bar --- TreeTracker.xcodeproj/project.pbxproj | 1 - .../UI/Navigation/Home/HomeCoordinator.swift | 3 +- .../ChatList/ChatListViewController.swift | 113 ++++++++++++++++++ .../ChatList/ChatListViewModel.swift | 27 ++++- 4 files changed, 140 insertions(+), 4 deletions(-) diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index 5a2e67d1..686b407c 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 59BEAE0465C5A575ABE7FA13 /* Pods_TreeTracker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B03AD2E94D3E7D6987A59BB0 /* Pods_TreeTracker.framework */; }; 8D75250629DB13FA00361CC1 /* MessagingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75250529DB13FA00361CC1 /* MessagingButton.swift */; }; 8D75251729DB61A000361CC1 /* ChatListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75251629DB61A000361CC1 /* ChatListViewController.swift */; }; 8D75251929DB61C000361CC1 /* ChatList.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D75251829DB61C000361CC1 /* ChatList.storyboard */; }; diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index 4dadb4a0..850ee612 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -148,7 +148,8 @@ private extension HomeCoordinator { let viewcontroller = StoryboardScene.ChatList.initialScene.instantiate() viewcontroller.viewModel = { let viewModel = ChatListViewModel( - planter: planter + planter: planter, + selfieService: self.treetrackerSDK.selfieService ) viewModel.coordinatorDelegate = self return viewModel diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index 7b13e5ff..edfe8780 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -19,6 +19,14 @@ class ChatListViewController: UIViewController { } } + private var planterImage: UIImageView = { + let image = UIImageView() + image.translatesAutoresizingMaskIntoConstraints = false + image.clipsToBounds = true + image.layer.cornerRadius = Const.ImageSizeForLargeState / 2 + return image + }() + var viewModel: ChatListViewModel? { didSet { viewModel?.viewDelegate = self @@ -29,7 +37,24 @@ class ChatListViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = Asset.Colors.backgroundGreen.color + setupNavBarImage() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + viewModel?.fetchProfileImage() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + showImage(true) + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + showImage(false) } + } // MARK: - UITableViewDataSource @@ -80,4 +105,92 @@ extension ChatListViewController: UITableViewDelegate { // MARK: - ChatListViewModelViewDelegate extension ChatListViewController: ChatListViewModelViewDelegate { + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didFetchProfile image: UIImage) { + planterImage.image = image + } + +} + +// MARK: - ImageOnNavigationBar +extension ChatListViewController { + + private func setupNavBarImage() { + navigationController?.navigationBar.prefersLargeTitles = true + + guard let navigationBar = self.navigationController?.navigationBar else { return } + navigationBar.addSubview(planterImage) + + NSLayoutConstraint.activate([ + + planterImage.rightAnchor.constraint(equalTo: navigationBar.rightAnchor, constant: -Const.ImageRightMargin), + planterImage.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor, constant: -Const.ImageBottomMarginForLargeState), + planterImage.heightAnchor.constraint(equalToConstant: Const.ImageSizeForLargeState), + planterImage.widthAnchor.constraint(equalTo: planterImage.heightAnchor) + + ]) + } + + private func moveAndResizeImage(for height: CGFloat) { + let coeff: CGFloat = { + let delta = height - Const.NavBarHeightSmallState + let heightDifferenceBetweenStates = (Const.NavBarHeightLargeState - Const.NavBarHeightSmallState) + return delta / heightDifferenceBetweenStates + }() + + let factor = Const.ImageSizeForSmallState / Const.ImageSizeForLargeState + + let scale: CGFloat = { + let sizeAddendumFactor = coeff * (1.0 - factor) + return min(1.0, sizeAddendumFactor + factor) + }() + + // Value of difference between icons for large and small states + let sizeDiff = Const.ImageSizeForLargeState * (1.0 - factor) // 8.0 + let yTranslation: CGFloat = { + /// This value = 14. It equals to difference of 12 and 6 (bottom margin for large and small states). Also it adds 8.0 (size difference when the image gets smaller size) + let maxYTranslation = Const.ImageBottomMarginForLargeState - Const.ImageBottomMarginForSmallState + sizeDiff + return max(0, min(maxYTranslation, (maxYTranslation - coeff * (Const.ImageBottomMarginForSmallState + sizeDiff)))) + }() + + let xTranslation = max(0, sizeDiff - coeff * sizeDiff) + + planterImage.transform = CGAffineTransform.identity + .scaledBy(x: scale, y: scale) + .translatedBy(x: xTranslation, y: yTranslation) + } + + private func showImage(_ show: Bool) { + UIView.animate(withDuration: 0.2) { + self.planterImage.alpha = show ? 1.0 : 0.0 + } + } + + /// WARNING: Change these constants according to your project's design + private struct Const { + /// Image height/width for Large NavBar state + static let ImageSizeForLargeState: CGFloat = 65 + /// Margin from right anchor of safe area to right anchor of Image + static let ImageRightMargin: CGFloat = 16 + /// Margin from bottom anchor of NavBar to bottom anchor of Image for Large NavBar state + static let ImageBottomMarginForLargeState: CGFloat = 15 + /// Margin from bottom anchor of NavBar to bottom anchor of Image for Small NavBar state + static let ImageBottomMarginForSmallState: CGFloat = 6 + /// Image height/width for Small NavBar state + static let ImageSizeForSmallState: CGFloat = 32 + /// Height of NavBar for Small state. Usually it's just 44 + static let NavBarHeightSmallState: CGFloat = 44 + /// Height of NavBar for Large state. Usually it's just 96.5 but if you have a custom font for the title, please make sure to edit this value since it changes the height for Large state of NavBar + static let NavBarHeightLargeState: CGFloat = 96.5 + } + +} + +// MARK: - +extension ChatListViewController: UIScrollViewDelegate { + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + guard let height = navigationController?.navigationBar.frame.height else { return } + moveAndResizeImage(for: height) + } + } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index e74f3a34..542d759f 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -8,13 +8,14 @@ import Foundation import Treetracker_Core +import UIKit protocol ChatListViewModelCoordinatorDelegate: AnyObject { func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectMessagesForPlanter planter: Planter) } protocol ChatListViewModelViewDelegate: AnyObject { - + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didFetchProfile image: UIImage) } class ChatListViewModel { @@ -22,13 +23,15 @@ class ChatListViewModel { weak var coordinatorDelegate: ChatListViewModelCoordinatorDelegate? weak var viewDelegate: ChatListViewModelViewDelegate? + private let selfieService: SelfieService private var planter: Planter // TODO: Change to a message model that has messageType private var chatType: [ChatType] = [.chat, .chat, .quiz] - init(planter: Planter) { + init(planter: Planter, selfieService: SelfieService) { self.planter = planter + self.selfieService = selfieService } var title: String { @@ -44,6 +47,26 @@ class ChatListViewModel { } } +// MARK: - Profile +extension ChatListViewModel { + + func fetchProfileImage() { + + selfieService.fetchSelfie(forPlanter: planter) { (result) in + switch result { + case .success(let data): + guard let image = UIImage(data: data) else { + fallthrough + } + viewDelegate?.chatListViewModel(self, didFetchProfile: image) + case .failure: + viewDelegate?.chatListViewModel(self, didFetchProfile: Asset.Assets.profile.image) + } + } + } + +} + // MARK: - Navigation extension ChatListViewModel { From f8284e04ff6a3f6b92391a10bc7ee41345bcfe89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 19 Apr 2023 10:34:07 -0300 Subject: [PATCH 16/66] implement UITableView extension to change tableview top bounce color and maintain --- TreeTracker.xcodeproj/project.pbxproj | 4 ++++ .../Extensions/UITableView+Extensions.swift | 22 +++++++++++++++++++ .../ChatList/Cell/ChatListTableViewCell.swift | 1 + .../ChatList/ChatListViewController.swift | 2 +- 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 TreeTracker/UI/Extensions/UITableView+Extensions.swift diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index 686b407c..cb56fd2d 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 8D036C6F29F022120079F4B1 /* UITableView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D036C6E29F022120079F4B1 /* UITableView+Extensions.swift */; }; 8D75250629DB13FA00361CC1 /* MessagingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75250529DB13FA00361CC1 /* MessagingButton.swift */; }; 8D75251729DB61A000361CC1 /* ChatListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75251629DB61A000361CC1 /* ChatListViewController.swift */; }; 8D75251929DB61C000361CC1 /* ChatList.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D75251829DB61C000361CC1 /* ChatList.storyboard */; }; @@ -107,6 +108,7 @@ /* Begin PBXFileReference section */ 229566C2A487A3F9C7D82AD8 /* Pods_TreeTracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TreeTracker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D036C6E29F022120079F4B1 /* UITableView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+Extensions.swift"; sourceTree = ""; }; 8D75250529DB13FA00361CC1 /* MessagingButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagingButton.swift; sourceTree = ""; }; 8D75251629DB61A000361CC1 /* ChatListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListViewController.swift; sourceTree = ""; }; 8D75251829DB61C000361CC1 /* ChatList.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ChatList.storyboard; sourceTree = ""; }; @@ -621,6 +623,7 @@ isa = PBXGroup; children = ( 93EC8CA724B28B5D00946C0C /* UITextField+Extensions.swift */, + 8D036C6E29F022120079F4B1 /* UITableView+Extensions.swift */, ); path = Extensions; sourceTree = ""; @@ -941,6 +944,7 @@ 93EC8CAD24BBA9F600946C0C /* AddTreeViewModel.swift in Sources */, 938294B027F9A7A500888E42 /* SettingsViewController.swift in Sources */, 93EC8C4A24AB7EA100946C0C /* UploadsButton.swift in Sources */, + 8D036C6F29F022120079F4B1 /* UITableView+Extensions.swift in Sources */, 93E1896F245DEE7700217F21 /* RootCoordinator.swift in Sources */, 93D7295824C49CA700271256 /* GPSAccuracyLabel.swift in Sources */, 93EC8C3924AA52DF00946C0C /* HomeViewController.swift in Sources */, diff --git a/TreeTracker/UI/Extensions/UITableView+Extensions.swift b/TreeTracker/UI/Extensions/UITableView+Extensions.swift new file mode 100644 index 00000000..1ae0d7c9 --- /dev/null +++ b/TreeTracker/UI/Extensions/UITableView+Extensions.swift @@ -0,0 +1,22 @@ +// +// UITableView+Extensions.swift +// TreeTracker +// +// Created by Frédéric Helfer on 19/04/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import UIKit + +extension UITableView { + + func addTopBounceAreaView(color: UIColor = .white) { + var frame = UIScreen.main.bounds + frame.origin.y = -frame.size.height + + let view = UIView(frame: frame) + view.backgroundColor = color + + self.addSubview(view) + } +} diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift index 88da00f4..7fba1d3e 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift @@ -58,6 +58,7 @@ class ChatListTableViewCell: UITableViewCell { extension ChatListTableViewCell { func setupCell() { + contentView.backgroundColor = Asset.Colors.backgroundGreen.color.withAlphaComponent(0.25) chatImage.image = Asset.Assets.trees.image chatTitle.text = "Admin" diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index edfe8780..affd47d3 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -15,7 +15,7 @@ class ChatListViewController: UIViewController { chatListTableView.delegate = self chatListTableView.dataSource = self chatListTableView.register(ChatListTableViewCell.nib(), forCellReuseIdentifier: ChatListTableViewCell.identifier) - chatListTableView.backgroundColor = Asset.Colors.backgroundGreen.color + chatListTableView.addTopBounceAreaView(color: Asset.Colors.backgroundGreen.color) } } From 3a46f397efdb5df0ab046c7415f1192c4bf6004d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Mon, 24 Apr 2023 16:24:34 -0300 Subject: [PATCH 17/66] fetchMessages from home screen on viewWillAppear. Update and display unread messages count. --- .../UI/Navigation/Home/HomeCoordinator.swift | 3 ++- .../UI/Views/Home/HomeViewController.swift | 15 ++++++++++- TreeTracker/UI/Views/Home/HomeViewModel.swift | 25 ++++++++++++++++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index 850ee612..516981c5 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -113,7 +113,8 @@ private extension HomeCoordinator { planter: planter, treeMonitoringService: self.treetrackerSDK.treeMonitoringService, selfieService: self.treetrackerSDK.selfieService, - uploadManager: self.treetrackerSDK.uploadManager + uploadManager: self.treetrackerSDK.uploadManager, + messagingService: self.treetrackerSDK.messagingService ) viewModel.coordinatorDelegate = self return viewModel diff --git a/TreeTracker/UI/Views/Home/HomeViewController.swift b/TreeTracker/UI/Views/Home/HomeViewController.swift index f58cdfae..d40bfda5 100644 --- a/TreeTracker/UI/Views/Home/HomeViewController.swift +++ b/TreeTracker/UI/Views/Home/HomeViewController.swift @@ -124,6 +124,7 @@ class HomeViewController: UIViewController, AlertPresenting { didSet { messageAlertCountView.backgroundColor = Asset.Colors.secondaryRed.color messageAlertCountView.layer.cornerRadius = 15 + messageAlertCountView.isHidden = true } } @@ -132,7 +133,7 @@ class HomeViewController: UIViewController, AlertPresenting { messageAlertCountLabel.textColor = .white messageAlertCountLabel.font = FontFamily.Montserrat.semiBold.font(size: 20.0) messageAlertCountLabel.textAlignment = .center - messageAlertCountLabel.text = "7" + messageAlertCountLabel.isHidden = true } } @@ -152,6 +153,7 @@ class HomeViewController: UIViewController, AlertPresenting { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) viewModel?.fetchProfileData() + viewModel?.fetchMessages() } override func viewDidLayoutSubviews() { @@ -235,6 +237,17 @@ extension HomeViewController: HomeViewModelViewDelegate { configureNameButton(name: profile.name) } + func homeViewModel(_ homeViewModel: HomeViewModel, didUpdateUnreadMessagesCount unreadMessages: String) { + if unreadMessages != "0" { + messageAlertCountLabel.text = unreadMessages + messageAlertCountLabel.isHidden = false + messageAlertCountView.isHidden = false + } else { + messageAlertCountLabel.isHidden = true + messageAlertCountView.isHidden = true + } + } + func homeViewModelDidStartUploadingTrees(_ homeViewModel: HomeViewModel) { uploadTreesButton.uploadState = .stop } diff --git a/TreeTracker/UI/Views/Home/HomeViewModel.swift b/TreeTracker/UI/Views/Home/HomeViewModel.swift index ced6f5e9..483b4ed8 100644 --- a/TreeTracker/UI/Views/Home/HomeViewModel.swift +++ b/TreeTracker/UI/Views/Home/HomeViewModel.swift @@ -22,6 +22,7 @@ protocol HomeViewModelViewDelegate: AnyObject { func homeViewModel(_ homeViewModel: HomeViewModel, didReceiveError error: Error) func homeViewModel(_ homeViewModel: HomeViewModel, didUpdateTreeCount data: HomeViewModel.TreeCountData) func homeViewModel(_ homeViewModel: HomeViewModel, didFetchProfile profile: HomeViewModel.ProfileData) + func homeViewModel(_ homeViewModel: HomeViewModel, didUpdateUnreadMessagesCount unreadMessages: String) func homeViewModelDidStartUploadingTrees(_ homeViewModel: HomeViewModel) func homeViewModelDidStopUploadingTrees(_ homeViewModel: HomeViewModel) } @@ -34,14 +35,16 @@ class HomeViewModel { private let treeMonitoringService: TreeMonitoringService private let selfieService: SelfieService private let uploadManager: UploadManaging + private let messagingService: MessagingService private let planter: Planter - init(planter: Planter, treeMonitoringService: TreeMonitoringService, selfieService: SelfieService, uploadManager: UploadManaging) { + init(planter: Planter, treeMonitoringService: TreeMonitoringService, selfieService: SelfieService, uploadManager: UploadManaging, messagingService: MessagingService) { self.planter = planter self.treeMonitoringService = treeMonitoringService self.uploadManager = uploadManager self.selfieService = selfieService + self.messagingService = messagingService self.treeMonitoringService.delegate = self self.uploadManager.delegate = self @@ -50,6 +53,12 @@ class HomeViewModel { var title: String { return L10n.Home.title } + + var unreadMessagesCount: Int? { + didSet { + viewDelegate?.homeViewModel(self, didUpdateUnreadMessagesCount: String(unreadMessagesCount ?? 0)) + } + } } // MARK: - Profile @@ -100,6 +109,20 @@ extension HomeViewModel { } } +// MARK: - Messaging +extension HomeViewModel { + + func fetchMessages() { + unreadMessagesCount = messagingService.getUnreadMessagesCount() + + messagingService.getMessages(planter: planter) { [weak self] _ in + // nothing + self?.unreadMessagesCount = self?.messagingService.getUnreadMessagesCount() + } + } + +} + // MARK: - Navigation extension HomeViewModel { From 62b9d703b8877b9145fcfcbd68fb22acfc5a69ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 26 Apr 2023 17:36:04 -0300 Subject: [PATCH 18/66] small refactor on home view --- TreeTracker/UI/Views/Home/HomeViewController.swift | 6 +++--- TreeTracker/UI/Views/Home/HomeViewModel.swift | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/TreeTracker/UI/Views/Home/HomeViewController.swift b/TreeTracker/UI/Views/Home/HomeViewController.swift index d40bfda5..85bdf7d5 100644 --- a/TreeTracker/UI/Views/Home/HomeViewController.swift +++ b/TreeTracker/UI/Views/Home/HomeViewController.swift @@ -237,9 +237,9 @@ extension HomeViewController: HomeViewModelViewDelegate { configureNameButton(name: profile.name) } - func homeViewModel(_ homeViewModel: HomeViewModel, didUpdateUnreadMessagesCount unreadMessages: String) { - if unreadMessages != "0" { - messageAlertCountLabel.text = unreadMessages + func homeViewModel(_ homeViewModel: HomeViewModel, didUpdateUnreadMessagesCount unreadMessages: Int) { + if unreadMessages != 0 { + messageAlertCountLabel.text = String(unreadMessages) messageAlertCountLabel.isHidden = false messageAlertCountView.isHidden = false } else { diff --git a/TreeTracker/UI/Views/Home/HomeViewModel.swift b/TreeTracker/UI/Views/Home/HomeViewModel.swift index 483b4ed8..e1ae65ca 100644 --- a/TreeTracker/UI/Views/Home/HomeViewModel.swift +++ b/TreeTracker/UI/Views/Home/HomeViewModel.swift @@ -22,7 +22,7 @@ protocol HomeViewModelViewDelegate: AnyObject { func homeViewModel(_ homeViewModel: HomeViewModel, didReceiveError error: Error) func homeViewModel(_ homeViewModel: HomeViewModel, didUpdateTreeCount data: HomeViewModel.TreeCountData) func homeViewModel(_ homeViewModel: HomeViewModel, didFetchProfile profile: HomeViewModel.ProfileData) - func homeViewModel(_ homeViewModel: HomeViewModel, didUpdateUnreadMessagesCount unreadMessages: String) + func homeViewModel(_ homeViewModel: HomeViewModel, didUpdateUnreadMessagesCount unreadMessages: Int) func homeViewModelDidStartUploadingTrees(_ homeViewModel: HomeViewModel) func homeViewModelDidStopUploadingTrees(_ homeViewModel: HomeViewModel) } @@ -56,7 +56,7 @@ class HomeViewModel { var unreadMessagesCount: Int? { didSet { - viewDelegate?.homeViewModel(self, didUpdateUnreadMessagesCount: String(unreadMessagesCount ?? 0)) + viewDelegate?.homeViewModel(self, didUpdateUnreadMessagesCount: unreadMessagesCount ?? 0) } } } @@ -113,11 +113,11 @@ extension HomeViewModel { extension HomeViewModel { func fetchMessages() { - unreadMessagesCount = messagingService.getUnreadMessagesCount() + unreadMessagesCount = messagingService.getUnreadMessagesCount(planter: planter) messagingService.getMessages(planter: planter) { [weak self] _ in - // nothing - self?.unreadMessagesCount = self?.messagingService.getUnreadMessagesCount() + guard let self else { return } + unreadMessagesCount = messagingService.getUnreadMessagesCount(planter: planter) } } From 6026c8326191d49f1b3b685b6c3024ad86564501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 26 Apr 2023 17:39:26 -0300 Subject: [PATCH 19/66] Add messaging service to ChatList screen. Cell are working with saved messages from planter. --- .../UI/Navigation/Home/HomeCoordinator.swift | 3 +- .../ChatList/Cell/ChatListTableViewCell.swift | 18 ++- .../ChatList/ChatListViewController.swift | 29 ++-- .../ChatList/ChatListViewModel.swift | 134 ++++++++++++++++-- 4 files changed, 148 insertions(+), 36 deletions(-) diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index 516981c5..d665356b 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -150,7 +150,8 @@ private extension HomeCoordinator { viewcontroller.viewModel = { let viewModel = ChatListViewModel( planter: planter, - selfieService: self.treetrackerSDK.selfieService + selfieService: self.treetrackerSDK.selfieService, + messagingService: self.treetrackerSDK.messagingService ) viewModel.coordinatorDelegate = self return viewModel diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift index 7fba1d3e..64b1949e 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift @@ -57,13 +57,21 @@ class ChatListTableViewCell: UITableViewCell { // MARK: - Public Actions extension ChatListTableViewCell { - func setupCell() { + func setupCell(data: ChatListViewModel.Chat) { contentView.backgroundColor = Asset.Colors.backgroundGreen.color.withAlphaComponent(0.25) chatImage.image = Asset.Assets.trees.image - chatTitle.text = "Admin" + chatImage.image = data.image + chatTitle.text = data.title + + if data.unreadCount == 0 { + chatAlertCountView.isHidden = true + chatAlertCountLabel.isHidden = true + chatAlertCountLabel.text = nil + } else { + chatAlertCountView.isHidden = false + chatAlertCountLabel.isHidden = false + chatAlertCountLabel.text = String(data.unreadCount) + } - chatAlertCountView.isHidden = false - chatAlertCountLabel.isHidden = false - chatAlertCountLabel.text = "7" } } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index affd47d3..bd4d081f 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -43,6 +43,7 @@ class ChatListViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) viewModel?.fetchProfileImage() + viewModel?.fetchMessages() } override func viewDidAppear(_ animated: Bool) { @@ -65,26 +66,10 @@ extension ChatListViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - - let chatType = viewModel?.cellForRowAt(indexPath: indexPath) - - switch chatType { - case .chat: - - let cell = tableView.dequeueReusableCell(withIdentifier: ChatListTableViewCell.identifier, for: indexPath) as? ChatListTableViewCell - cell?.setupCell() - return cell ?? UITableViewCell() - - case .quiz: - - // TODO: Create a new cell of type Quiz - let cell = tableView.dequeueReusableCell(withIdentifier: ChatListTableViewCell.identifier, for: indexPath) as? ChatListTableViewCell - cell?.setupCell() - return cell ?? UITableViewCell() - - default: - return UITableViewCell() - } + let cell = tableView.dequeueReusableCell(withIdentifier: ChatListTableViewCell.identifier, for: indexPath) as? ChatListTableViewCell + let chat = viewModel?.cellForRowAt(indexPath: indexPath) + cell?.setupCell(data: chat!) // remove force unwrap + return cell ?? UITableViewCell() } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { @@ -109,6 +94,10 @@ extension ChatListViewController: ChatListViewModelViewDelegate { planterImage.image = image } + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateChats messages: [ChatListViewModel.Chat]) { + chatListTableView.reloadData() + } + } // MARK: - ImageOnNavigationBar diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index 542d759f..93fbc57c 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -16,6 +16,7 @@ protocol ChatListViewModelCoordinatorDelegate: AnyObject { protocol ChatListViewModelViewDelegate: AnyObject { func chatListViewModel(_ chatListViewModel: ChatListViewModel, didFetchProfile image: UIImage) + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateChats chats: [ChatListViewModel.Chat]) } class ChatListViewModel { @@ -24,14 +25,13 @@ class ChatListViewModel { weak var viewDelegate: ChatListViewModelViewDelegate? private let selfieService: SelfieService + private let messagingService: MessagingService private var planter: Planter - // TODO: Change to a message model that has messageType - private var chatType: [ChatType] = [.chat, .chat, .quiz] - - init(planter: Planter, selfieService: SelfieService) { + init(planter: Planter, selfieService: SelfieService, messagingService: MessagingService) { self.planter = planter self.selfieService = selfieService + self.messagingService = messagingService } var title: String { @@ -39,11 +39,73 @@ class ChatListViewModel { } var numberOfRowsInSection: Int { - chatType.count + chats.count + } + + func cellForRowAt(indexPath: IndexPath) -> ChatListViewModel.Chat { + chats[indexPath.row] + } + + private var chats: [Chat] = [] { + didSet { + viewDelegate?.chatListViewModel(self, didUpdateChats: chats) + } } +} + +// MARK: = Messaging +extension ChatListViewModel { + + func fetchMessages() { + + var chats: [Chat] = [] + let allMessages = messagingService.getSavedMessages(planter: planter) + + for message in allMessages { + + let messageType = messageType(type: message.type) + + var messageDetail = MessageDetail( + from: message.from ?? "", + to: message.to ?? "", + body: message.body, + composedAt: message.composedAt ?? "", + videoLink: message.videoLink, + unread: message.unread + ) + + switch messageType { + + case .message: + + // check if already exists a message chat + if let index = chats.firstIndex(where: { $0.type == .message }) { + + chats[index].messages.append(messageDetail) + + } else { + + let newChat = Chat( + title: message.subject ?? "Admin", + type: messageType, + messages: [messageDetail] + ) + chats.insert(newChat, at: 0) + } - func cellForRowAt(indexPath: IndexPath) -> ChatType { - chatType[indexPath.row] + case .announce, .survey: + + let newChat = Chat( + title: message.subject ?? "Announce!", + type: messageType, + messages: [messageDetail] + ) + chats.append(newChat) + + } + } + + self.chats = chats } } @@ -76,7 +138,59 @@ extension ChatListViewModel { } -enum ChatType { - case chat - case quiz +// MARK: - Nested Types +extension ChatListViewModel { + + struct Chat { + let title: String + let type: MessageType + var messages: [MessageDetail] + + var image: UIImage { + switch type { + case .message: + return Asset.Assets.forest.image + case .announce: + return Asset.Assets.gardening.image + case .survey: + return Asset.Assets.logo.image + } + } + + var unreadCount: Int { + messages.reduce(0) { $0 + ($1.unread ? 1 : 0) } + } + } + + struct MessageDetail { + let from: String + let to: String + let body: String? + let composedAt: String + let videoLink: String? +// let survey: SurveyResponse? +// let surveyResponse: [String]? + + let unread: Bool + var isFromAdmin: Bool { + from == "Admin" + } + } + + enum MessageType: String, Decodable { + case message + case announce + case survey +// case survey_response + } + + func messageType(type: String?) -> MessageType { + switch type { + case "message": return .message + case "announce": return .announce + case "survey": return .survey + default: return .message + } + } + } From 8ae588a129f6ccc0e33d7ab44f5fa5eccfb4ad03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 26 Apr 2023 19:59:19 -0300 Subject: [PATCH 20/66] message view are working with saved messages from core data --- .../UI/Navigation/Home/HomeCoordinator.swift | 13 ++--- .../ChatList/ChatListViewController.swift | 2 +- .../ChatList/ChatListViewModel.swift | 11 +++-- .../Messages/Cell/MessageTableViewCell.swift | 32 ++++++------- .../Messages/MessagesViewController.swift | 23 ++++++--- .../Messages/MessagesViewModel.swift | 48 +++++++++++-------- 6 files changed, 75 insertions(+), 54 deletions(-) diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index d665356b..e7e83201 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -68,9 +68,9 @@ private extension HomeCoordinator { ) } - func showMessages(planter: Planter) { + func showMessages(planter: Planter, chat: ChatListViewModel.Chat) { configuration.navigationController.pushViewController( - messagesViewController(planter: planter), + messagesViewController(planter: planter, chat: chat), animated: true ) } @@ -159,12 +159,13 @@ private extension HomeCoordinator { return viewcontroller } - func messagesViewController(planter: Planter) -> UIViewController { + func messagesViewController(planter: Planter, chat: ChatListViewModel.Chat) -> UIViewController { let viewcontroller = StoryboardScene.Messages.initialScene.instantiate() viewcontroller.viewModel = { let viewModel = MessagesViewModel( planter: planter, - messagingService: treetrackerSDK.messagingService + messagingService: treetrackerSDK.messagingService, + chat: chat ) return viewModel }() @@ -291,8 +292,8 @@ extension HomeCoordinator: NotesViewModelCoordinatorDelegate { // MARK: - ChatListViewModelCoordinatorDelegate extension HomeCoordinator: ChatListViewModelCoordinatorDelegate { - func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectMessagesForPlanter planter: Planter) { - showMessages(planter: planter) + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectChat chat: ChatListViewModel.Chat, forPlanter planter: Planter) { + showMessages(planter: planter, chat: chat) } } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index bd4d081f..1759d801 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -82,7 +82,7 @@ extension ChatListViewController: UITableViewDataSource { extension ChatListViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - viewModel?.messagesSelected(indexPath: indexPath) + viewModel?.chatSelected(indexPath: indexPath) } } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index 93fbc57c..d23d8151 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -11,7 +11,7 @@ import Treetracker_Core import UIKit protocol ChatListViewModelCoordinatorDelegate: AnyObject { - func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectMessagesForPlanter planter: Planter) + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectChat chat: ChatListViewModel.Chat, forPlanter planter: Planter) } protocol ChatListViewModelViewDelegate: AnyObject { @@ -132,8 +132,11 @@ extension ChatListViewModel { // MARK: - Navigation extension ChatListViewModel { - func messagesSelected(indexPath: IndexPath) { - coordinatorDelegate?.chatListViewModel(self, didSelectMessagesForPlanter: planter) + func chatSelected(indexPath: IndexPath) { + + let selectedChat = chats[indexPath.row] + + coordinatorDelegate?.chatListViewModel(self, didSelectChat: selectedChat, forPlanter: planter) } } @@ -173,7 +176,7 @@ extension ChatListViewModel { let unread: Bool var isFromAdmin: Bool { - from == "Admin" + from == "admin" } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift index bc566a82..b8013656 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift @@ -46,25 +46,25 @@ class MessageTableViewCell: UITableViewCell { // MARK: - Public func extension MessageTableViewCell { - func setupCell(message: Message?, planterName: String) { + func setupCell(message: ChatListViewModel.MessageDetail) { trailingConstraint = messageBackgroundView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20) leadingConstraint = messageBackgroundView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20) -// messageLabel.text = message?.body -// -// if message?.from != planterName { -// -// messageBackgroundView.backgroundColor = .systemGreen -// leadingConstraint.isActive = true -// messageLabel.textAlignment = .left -// -// } else { -// -// messageBackgroundView.backgroundColor = .systemBlue -// trailingConstraint.isActive = true -// messageLabel.textAlignment = .right -// -// } + messageLabel.text = message.body + + if message.isFromAdmin { + + messageBackgroundView.backgroundColor = .systemGreen + leadingConstraint.isActive = true + messageLabel.textAlignment = .left + + } else { + + messageBackgroundView.backgroundColor = .systemBlue + trailingConstraint.isActive = true + messageLabel.textAlignment = .right + + } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index ffd5e651..aee6fa83 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -49,13 +49,13 @@ class MessagesViewController: UIViewController { var viewModel: MessagesViewModel? { didSet { viewModel?.viewDelegate = self + title = viewModel?.title } } override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = Asset.Colors.backgroundGreen.color - viewModel?.fetchMessages() } } @@ -95,8 +95,7 @@ extension MessagesViewController: UITableViewDataSource { let cell = tableView.dequeueReusableCell(withIdentifier: MessageTableViewCell.identifier, for: indexPath) as? MessageTableViewCell let message = viewModel?.getMessageForRowAt(indexPath: indexPath) - let planterName = viewModel?.getPlanterName() ?? "" - cell?.setupCell(message: message, planterName: planterName) + cell?.setupCell(message: message!) // remove force unwrap? cell?.selectionStyle = .none return cell ?? UITableViewCell() @@ -115,10 +114,22 @@ extension MessagesViewController: UITableViewDataSource { // MARK: - UITextViewDelegate extension MessagesViewController: UITextViewDelegate { + + // change textview heigh func textViewDidChange(_ textView: UITextView) { - // TODO: Let it grow as the text grows -// let size = textView.sizeThatFits(CGSize(width: textView.bounds.width, height: .infinity)) -// textView.heightAnchor.constraint(equalToConstant: size.height).isActive = true + let maxNumberOfLines = 5 // maximum number of lines + let maxHeight = textView.font!.lineHeight * CGFloat(maxNumberOfLines) // maximum height based on number of lines + print("maxHeight: \(maxHeight)") + let contentHeight = textView.sizeThatFits(CGSize(width: textView.frame.width, height: CGFloat.greatestFiniteMagnitude)).height // current content height + print("contentHeight: \(contentHeight)") + + if contentHeight > maxHeight { + textView.isScrollEnabled = true // enable scrolling if content height exceeds maximum height + textView.frame.size.height = maxHeight // set textView height to maximum height + } else { + textView.isScrollEnabled = false // disable scrolling if content height is within maximum height + textView.frame.size.height = contentHeight // set textView height to current content height + } } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift index 7c15eb97..48b89d9f 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift @@ -20,44 +20,50 @@ class MessagesViewModel { private let planter: Planter private let messagingService: MessagingService + private var chat: ChatListViewModel.Chat private(set) var messages: [Message] = [] - init(planter: Planter, messagingService: MessagingService) { + init(planter: Planter, messagingService: MessagingService, chat: ChatListViewModel.Chat) { self.planter = planter self.messagingService = messagingService + self.chat = chat + } + + var title: String { + return chat.title } var numberOfRowsInSection: Int { - messages.count + chat.messages.count } - func getMessageForRowAt(indexPath: IndexPath) -> Message { - messages[indexPath.row] + func getMessageForRowAt(indexPath: IndexPath) -> ChatListViewModel.MessageDetail { + chat.messages[indexPath.row] } func getPlanterName() -> String { - planter.firstName ?? "" + chat.title } - func fetchMessages() { - - messagingService.getMessages(planter: planter) { result in - - switch result { - case .success(let messages): - self.messages = messages - self.viewDelegate?.messagesViewModel(self, didFetchMessages: messages) - case .failure(let error): - self.viewDelegate?.messagesViewModel(self, didReceiveError: error) - } - } - - } +// func fetchMessages() { +// +// messagingService.getMessages(planter: planter) { result in +// +// switch result { +// case .success(let messages): +// self.messages = messages +// self.viewDelegate?.messagesViewModel(self, didFetchMessages: messages) +// case .failure(let error): +// self.viewDelegate?.messagesViewModel(self, didReceiveError: error) +// } +// } +// +// } func sendMessage(text: String) { - let newMessage = messages[3] - messages.append(newMessage) + let newMessage = chat.messages[3] + chat.messages.append(newMessage) // let newMessage = Message // TODO: Cache new message. Send new message. - Needs a variable to know if it was uploaded or not? From 238f63b1c05974a5273d6c26a9f24c4341941e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 28 Apr 2023 10:05:25 -0300 Subject: [PATCH 21/66] some refactor on fetching unread messages count --- TreeTracker/UI/Views/Home/HomeViewController.swift | 2 +- TreeTracker/UI/Views/Home/HomeViewModel.swift | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/TreeTracker/UI/Views/Home/HomeViewController.swift b/TreeTracker/UI/Views/Home/HomeViewController.swift index 85bdf7d5..52235123 100644 --- a/TreeTracker/UI/Views/Home/HomeViewController.swift +++ b/TreeTracker/UI/Views/Home/HomeViewController.swift @@ -153,7 +153,7 @@ class HomeViewController: UIViewController, AlertPresenting { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) viewModel?.fetchProfileData() - viewModel?.fetchMessages() + viewModel?.fetchUnreadMessagesCount() } override func viewDidLayoutSubviews() { diff --git a/TreeTracker/UI/Views/Home/HomeViewModel.swift b/TreeTracker/UI/Views/Home/HomeViewModel.swift index e1ae65ca..55383e47 100644 --- a/TreeTracker/UI/Views/Home/HomeViewModel.swift +++ b/TreeTracker/UI/Views/Home/HomeViewModel.swift @@ -54,9 +54,9 @@ class HomeViewModel { return L10n.Home.title } - var unreadMessagesCount: Int? { + var unreadMessagesCount: Int = 0 { didSet { - viewDelegate?.homeViewModel(self, didUpdateUnreadMessagesCount: unreadMessagesCount ?? 0) + viewDelegate?.homeViewModel(self, didUpdateUnreadMessagesCount: unreadMessagesCount) } } } @@ -112,12 +112,10 @@ extension HomeViewModel { // MARK: - Messaging extension HomeViewModel { - func fetchMessages() { - unreadMessagesCount = messagingService.getUnreadMessagesCount(planter: planter) - - messagingService.getMessages(planter: planter) { [weak self] _ in + func fetchUnreadMessagesCount() { + messagingService.getUnreadMessagesCount(for: planter) { [weak self] count in guard let self else { return } - unreadMessagesCount = messagingService.getUnreadMessagesCount(planter: planter) + unreadMessagesCount = count } } From cc25de5c63151d11b485446dbb0651fd03233419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 28 Apr 2023 11:30:36 -0300 Subject: [PATCH 22/66] add placeholder to messages screen and fix textview autoresizing --- TreeTracker/Resources/Localizable.strings | 4 ++ TreeTracker/SwiftGen/Strings.swift | 6 +++ .../Messaging/Messages/Messages.storyboard | 4 +- .../Messages/MessagesViewController.swift | 47 ++++++++++++------- 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/TreeTracker/Resources/Localizable.strings b/TreeTracker/Resources/Localizable.strings index ba9ad237..d36aca6c 100644 --- a/TreeTracker/Resources/Localizable.strings +++ b/TreeTracker/Resources/Localizable.strings @@ -100,3 +100,7 @@ "Settings.PhotoQuality.Option.Medium.Info" = "Medium resolution, medium data usage."; "Settings.PhotoQuality.Option.High.Title" = "High"; "Settings.PhotoQuality.Option.High.Info" = "High resolution, high data usage."; + +//Messages Screen +"Messages.InputTextView.PlaceHolder" = "Click to write a message"; + diff --git a/TreeTracker/SwiftGen/Strings.swift b/TreeTracker/SwiftGen/Strings.swift index 50ff739b..8f3d5d7e 100644 --- a/TreeTracker/SwiftGen/Strings.swift +++ b/TreeTracker/SwiftGen/Strings.swift @@ -134,6 +134,12 @@ internal enum L10n { internal static let title = L10n.tr("Localizable", "LogOutConfirmation.LogOutButton.Title", fallback: "Log Out") } } + internal enum Messages { + internal enum InputTextView { + /// Click to write a message + internal static let placeHolder = L10n.tr("Localizable", "Messages.InputTextView.PlaceHolder", fallback: "Click to write a message") + } + } internal enum Notes { /// Add tree notes here. internal static let placeholder = L10n.tr("Localizable", "Notes.Placeholder", fallback: "Add tree notes here.") diff --git a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard index c573e7b2..f5bf7c23 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard +++ b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard @@ -1,9 +1,9 @@ - + - + diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index aee6fa83..6d325e25 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -16,6 +16,7 @@ class MessagesViewController: UIViewController { messagesTableView.separatorStyle = .none messagesTableView.dataSource = self messagesTableView.register(MessageTableViewCell.nib(), forCellReuseIdentifier: MessageTableViewCell.identifier) + messagesTableView.addTopBounceAreaView(color: Asset.Colors.backgroundGreen.color) } } @@ -28,9 +29,10 @@ class MessagesViewController: UIViewController { inputTextView.font = FontFamily.Lato.regular.font(size: 16) inputTextView.textColor = Asset.Colors.grayDark.color inputTextView.delegate = self - inputTextView.isScrollEnabled = false - inputTextView.sizeToFit() + + inputTextView.textColor = Asset.Colors.grayLight.color + inputTextView.text = L10n.Messages.InputTextView.placeHolder } } @@ -79,7 +81,6 @@ private extension MessagesViewController { messagesTableView.endUpdates() messagesTableView.scrollToRow(at: IndexPath(row: row - 1, section: 0), at: .top, animated: true) inputTextView.text = "" - } } @@ -114,21 +115,35 @@ extension MessagesViewController: UITableViewDataSource { // MARK: - UITextViewDelegate extension MessagesViewController: UITextViewDelegate { + func textViewDidBeginEditing(_ textView: UITextView) { + // placeholder + if textView.textColor == Asset.Colors.grayLight.color { + textView.text = nil + textView.textColor = Asset.Colors.grayDark.color + } + } + + func textViewDidEndEditing(_ textView: UITextView) { + // placeholder + if textView.text.isEmpty { + textView.text = L10n.Messages.InputTextView.placeHolder + textView.textColor = Asset.Colors.grayLight.color + } + } - // change textview heigh func textViewDidChange(_ textView: UITextView) { - let maxNumberOfLines = 5 // maximum number of lines - let maxHeight = textView.font!.lineHeight * CGFloat(maxNumberOfLines) // maximum height based on number of lines - print("maxHeight: \(maxHeight)") - let contentHeight = textView.sizeThatFits(CGSize(width: textView.frame.width, height: CGFloat.greatestFiniteMagnitude)).height // current content height - print("contentHeight: \(contentHeight)") - - if contentHeight > maxHeight { - textView.isScrollEnabled = true // enable scrolling if content height exceeds maximum height - textView.frame.size.height = maxHeight // set textView height to maximum height - } else { - textView.isScrollEnabled = false // disable scrolling if content height is within maximum height - textView.frame.size.height = contentHeight // set textView height to current content height + // autoresizing + let size = CGSize(width: textView.frame.size.width, height: .infinity) + let estimatedSize = textView.sizeThatFits(size) + + guard textView.contentSize.height < 120.0 else { textView.isScrollEnabled = true; return } + + textView.isScrollEnabled = false + + textView.constraints.forEach { (constraint) in + if constraint.firstAttribute == .height { + constraint.constant = estimatedSize.height + } } } } From 55ad35eea1e7f7f8945d2a43a4225b9ef2d7d4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Mon, 1 May 2023 15:15:44 -0300 Subject: [PATCH 23/66] move view when keyboard appears --- .../Messaging/Messages/Messages.storyboard | 3 +- .../Messages/MessagesViewController.swift | 40 +++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard index f5bf7c23..4162fda7 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard +++ b/TreeTracker/UI/Views/Messaging/Messages/Messages.storyboard @@ -64,14 +64,15 @@ - + + diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index 6d325e25..854f56b9 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -9,7 +9,7 @@ import UIKit import Treetracker_Core -class MessagesViewController: UIViewController { +class MessagesViewController: UIViewController, KeyboardDismissing { @IBOutlet private var messagesTableView: UITableView! { didSet { @@ -48,6 +48,8 @@ class MessagesViewController: UIViewController { } } + @IBOutlet private var bottomContraint: NSLayoutConstraint! + var viewModel: MessagesViewModel? { didSet { viewModel?.viewDelegate = self @@ -58,6 +60,8 @@ class MessagesViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = Asset.Colors.backgroundGreen.color + addEndEditingBackgroundTapGesture() + addKeyboardObservers() } } @@ -69,10 +73,10 @@ private extension MessagesViewController { guard let messageText = inputTextView.text, - !messageText.isEmpty + !messageText.isEmpty, + inputTextView.textColor != Asset.Colors.grayLight.color else { return } - print(messageText) viewModel?.sendMessage(text: messageText) let row = viewModel?.numberOfRowsInSection ?? 0 @@ -80,7 +84,9 @@ private extension MessagesViewController { messagesTableView.insertRows(at: [IndexPath(row: row - 1, section: 0)], with: .fade) messagesTableView.endUpdates() messagesTableView.scrollToRow(at: IndexPath(row: row - 1, section: 0), at: .top, animated: true) + inputTextView.text = "" + textViewDidChange(inputTextView) } } @@ -148,6 +154,34 @@ extension MessagesViewController: UITextViewDelegate { } } +// MARK: - KeyboardObserving +extension MessagesViewController: KeyboardObserving { + + func keyboardWillShow(notification: Notification) { + let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] + guard let keyboardSize = (keyboardFrame as? NSValue)?.cgRectValue else { + return + } + + let bottomSafeAreaHeight = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0.0 + + UIView.animate(withDuration: 0.30) { + self.bottomContraint.constant = keyboardSize.height - bottomSafeAreaHeight + self.view.layoutIfNeeded() + } + + let row = viewModel?.numberOfRowsInSection ?? 0 + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { + self.messagesTableView.scrollToRow(at: IndexPath(row: row - 1, section: 0), at: .bottom, animated: true) + } + } + + func keyboardWillHide(notification: Notification) { + bottomContraint.constant = 0 + } +} + // MARK: - MessagesViewModelViewDelegate extension MessagesViewController: MessagesViewModelViewDelegate { From 7cb96c554427cd8d33770a4a3e835cd71d9e047f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Tue, 2 May 2023 11:53:17 -0300 Subject: [PATCH 24/66] fix navigation bar appearance bug with a custom extension that can be used in every viewController to change the backgroundColor. --- TreeTracker.xcodeproj/project.pbxproj | 4 ++ .../BaseNavigationViewController.swift | 11 +--- .../UINavigationBar+Extensions.swift | 55 +++++++++++++++++++ .../UI/Views/Home/HomeViewController.swift | 1 + .../ChatList/ChatListViewController.swift | 13 +---- .../Messages/MessagesViewController.swift | 9 ++- 6 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 TreeTracker/UI/Extensions/UINavigationBar+Extensions.swift diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index cb56fd2d..fc22faa1 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 8D036C6F29F022120079F4B1 /* UITableView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D036C6E29F022120079F4B1 /* UITableView+Extensions.swift */; }; + 8D3AF01F2A01346D00976763 /* UINavigationBar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3AF01E2A01346D00976763 /* UINavigationBar+Extensions.swift */; }; 8D75250629DB13FA00361CC1 /* MessagingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75250529DB13FA00361CC1 /* MessagingButton.swift */; }; 8D75251729DB61A000361CC1 /* ChatListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75251629DB61A000361CC1 /* ChatListViewController.swift */; }; 8D75251929DB61C000361CC1 /* ChatList.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D75251829DB61C000361CC1 /* ChatList.storyboard */; }; @@ -109,6 +110,7 @@ /* Begin PBXFileReference section */ 229566C2A487A3F9C7D82AD8 /* Pods_TreeTracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TreeTracker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8D036C6E29F022120079F4B1 /* UITableView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+Extensions.swift"; sourceTree = ""; }; + 8D3AF01E2A01346D00976763 /* UINavigationBar+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+Extensions.swift"; sourceTree = ""; }; 8D75250529DB13FA00361CC1 /* MessagingButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagingButton.swift; sourceTree = ""; }; 8D75251629DB61A000361CC1 /* ChatListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListViewController.swift; sourceTree = ""; }; 8D75251829DB61C000361CC1 /* ChatList.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ChatList.storyboard; sourceTree = ""; }; @@ -624,6 +626,7 @@ children = ( 93EC8CA724B28B5D00946C0C /* UITextField+Extensions.swift */, 8D036C6E29F022120079F4B1 /* UITableView+Extensions.swift */, + 8D3AF01E2A01346D00976763 /* UINavigationBar+Extensions.swift */, ); path = Extensions; sourceTree = ""; @@ -986,6 +989,7 @@ 93E82F5A246C36B500006C91 /* LoadingViewController.swift in Sources */, 938294BA27F9D00800888E42 /* PhotoQuality.swift in Sources */, B0AAD80628C235C900640D4A /* NotesViewController.swift in Sources */, + 8D3AF01F2A01346D00976763 /* UINavigationBar+Extensions.swift in Sources */, 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */, 93EC8C3424AA4AE100946C0C /* HomeCoordinator.swift in Sources */, 938294AE27F9A79900888E42 /* SettingsViewModel.swift in Sources */, diff --git a/TreeTracker/UI/Components/ViewControllers/BaseNavigationViewController.swift b/TreeTracker/UI/Components/ViewControllers/BaseNavigationViewController.swift index 29d803b6..aaa46307 100644 --- a/TreeTracker/UI/Components/ViewControllers/BaseNavigationViewController.swift +++ b/TreeTracker/UI/Components/ViewControllers/BaseNavigationViewController.swift @@ -12,15 +12,6 @@ class BaseNavigationViewController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() - navigationBar.tintColor = Asset.Colors.grayDark.color - navigationBar.prefersLargeTitles = true - navigationBar.titleTextAttributes = [ - .font: FontFamily.Montserrat.bold.font(size: 15), - .foregroundColor: Asset.Colors.grayDark.color - ] - navigationBar.largeTitleTextAttributes = [ - .font: FontFamily.Montserrat.bold.font(size: 35), - .foregroundColor: Asset.Colors.grayDark.color - ] + navigationBar.setupNavigationAppearance() } } diff --git a/TreeTracker/UI/Extensions/UINavigationBar+Extensions.swift b/TreeTracker/UI/Extensions/UINavigationBar+Extensions.swift new file mode 100644 index 00000000..4c672465 --- /dev/null +++ b/TreeTracker/UI/Extensions/UINavigationBar+Extensions.swift @@ -0,0 +1,55 @@ +// +// UINavigationBar+Extensions.swift +// TreeTracker +// +// Created by Frédéric Helfer on 02/05/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import UIKit + +extension UINavigationBar { + + func setupNavigationAppearance( + prefersLargeTitles: Bool = true, + backgroundColor: UIColor = .white + ) { + + self.prefersLargeTitles = prefersLargeTitles + self.tintColor = Asset.Colors.grayDark.color + self.isTranslucent = false + + if #available(iOS 13.0, *) { + + let navBarAppearance = UINavigationBarAppearance() + navBarAppearance.configureWithDefaultBackground() + navBarAppearance.shadowColor = .clear + navBarAppearance.shadowImage = .init() + navBarAppearance.backgroundColor = backgroundColor + navBarAppearance.titleTextAttributes = [ + .font: FontFamily.Montserrat.bold.font(size: 15), + .foregroundColor: Asset.Colors.grayDark.color + ] + navBarAppearance.largeTitleTextAttributes = [ + .font: FontFamily.Montserrat.bold.font(size: 35), + .foregroundColor: Asset.Colors.grayDark.color + ] + self.standardAppearance = navBarAppearance + self.scrollEdgeAppearance = navBarAppearance + + } else { + + self.backgroundColor = backgroundColor + self.barTintColor = backgroundColor + self.titleTextAttributes = [ + .font: FontFamily.Montserrat.bold.font(size: 15), + .foregroundColor: Asset.Colors.grayDark.color + ] + self.largeTitleTextAttributes = [ + .font: FontFamily.Montserrat.bold.font(size: 35), + .foregroundColor: Asset.Colors.grayDark.color + ] + + } + } +} diff --git a/TreeTracker/UI/Views/Home/HomeViewController.swift b/TreeTracker/UI/Views/Home/HomeViewController.swift index 52235123..76ceff2e 100644 --- a/TreeTracker/UI/Views/Home/HomeViewController.swift +++ b/TreeTracker/UI/Views/Home/HomeViewController.swift @@ -152,6 +152,7 @@ class HomeViewController: UIViewController, AlertPresenting { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + navigationController?.navigationBar.setupNavigationAppearance() viewModel?.fetchProfileData() viewModel?.fetchUnreadMessagesCount() } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index 1759d801..78187d67 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -36,18 +36,15 @@ class ChatListViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = Asset.Colors.backgroundGreen.color setupNavBarImage() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + view.backgroundColor = Asset.Colors.backgroundGreen.color + navigationController?.navigationBar.setupNavigationAppearance(backgroundColor: Asset.Colors.backgroundGreen.color) viewModel?.fetchProfileImage() viewModel?.fetchMessages() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) showImage(true) } @@ -104,18 +101,14 @@ extension ChatListViewController: ChatListViewModelViewDelegate { extension ChatListViewController { private func setupNavBarImage() { - navigationController?.navigationBar.prefersLargeTitles = true - guard let navigationBar = self.navigationController?.navigationBar else { return } navigationBar.addSubview(planterImage) NSLayoutConstraint.activate([ - planterImage.rightAnchor.constraint(equalTo: navigationBar.rightAnchor, constant: -Const.ImageRightMargin), planterImage.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor, constant: -Const.ImageBottomMarginForLargeState), planterImage.heightAnchor.constraint(equalToConstant: Const.ImageSizeForLargeState), planterImage.widthAnchor.constraint(equalTo: planterImage.heightAnchor) - ]) } @@ -149,7 +142,7 @@ extension ChatListViewController { } private func showImage(_ show: Bool) { - UIView.animate(withDuration: 0.2) { + UIView.animate(withDuration: 0.3) { self.planterImage.alpha = show ? 1.0 : 0.0 } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index 854f56b9..3ca107e1 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -59,11 +59,18 @@ class MessagesViewController: UIViewController, KeyboardDismissing { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = Asset.Colors.backgroundGreen.color addEndEditingBackgroundTapGesture() addKeyboardObservers() } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + view.backgroundColor = Asset.Colors.backgroundGreen.color + navigationController?.navigationBar.setupNavigationAppearance( + backgroundColor: Asset.Colors.backgroundGreen.color + ) + } + } // MARK: - Button Actions From 5b6ea03a22261d100f10fae3b6fe0d405d3e0005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Tue, 2 May 2023 22:18:03 -0300 Subject: [PATCH 25/66] add method to set unread message variable to false when the user read the messages --- .../ChatList/ChatListViewController.swift | 4 +- .../ChatList/ChatListViewModel.swift | 52 ++++++++++++------- .../Messages/Cell/MessageTableViewCell.xib | 12 ++--- .../Messages/MessagesViewController.swift | 4 -- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index 78187d67..ffc066ac 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -36,6 +36,8 @@ class ChatListViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + viewModel?.fetchProfileImage() + viewModel?.fetchMessages() setupNavBarImage() } @@ -43,8 +45,6 @@ class ChatListViewController: UIViewController { super.viewWillAppear(animated) view.backgroundColor = Asset.Colors.backgroundGreen.color navigationController?.navigationBar.setupNavigationAppearance(backgroundColor: Asset.Colors.backgroundGreen.color) - viewModel?.fetchProfileImage() - viewModel?.fetchMessages() showImage(true) } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index d23d8151..fffd661a 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -57,15 +57,14 @@ class ChatListViewModel { extension ChatListViewModel { func fetchMessages() { - - var chats: [Chat] = [] + var chatList: [Chat] = [] let allMessages = messagingService.getSavedMessages(planter: planter) for message in allMessages { - let messageType = messageType(type: message.type) - var messageDetail = MessageDetail( + let messageDetail = MessageDetail( + messageId: message.messageId ?? "", from: message.from ?? "", to: message.to ?? "", body: message.body, @@ -75,38 +74,48 @@ extension ChatListViewModel { ) switch messageType { - case .message: - // check if already exists a message chat - if let index = chats.firstIndex(where: { $0.type == .message }) { - - chats[index].messages.append(messageDetail) + if let index = chatList.firstIndex(where: { $0.type == .message }) { + chatList[index].messages.append(messageDetail) } else { - let newChat = Chat( title: message.subject ?? "Admin", type: messageType, messages: [messageDetail] ) - chats.insert(newChat, at: 0) + chatList.insert(newChat, at: 0) } - case .announce, .survey: - + case .announce, .survey, .surveyResponse: let newChat = Chat( title: message.subject ?? "Announce!", type: messageType, messages: [messageDetail] ) - chats.append(newChat) - + chatList.append(newChat) } } - self.chats = chats + self.chats = chatList } + + func updateUnreadMessagesCount(indexPath: IndexPath) { + var messagesId: [String] = [] + + var updatedMessages = chats[indexPath.row].messages.map { messageDetail -> MessageDetail in + messagesId.append(messageDetail.messageId) + var message = messageDetail + message.unread = false + return message + } + + self.chats[indexPath.row].messages = updatedMessages + + messagingService.updateUnreadMessages(planter: planter, messageId: messagesId) + } + } // MARK: - Profile @@ -133,9 +142,8 @@ extension ChatListViewModel { extension ChatListViewModel { func chatSelected(indexPath: IndexPath) { - + updateUnreadMessagesCount(indexPath: indexPath) let selectedChat = chats[indexPath.row] - coordinatorDelegate?.chatListViewModel(self, didSelectChat: selectedChat, forPlanter: planter) } @@ -157,6 +165,8 @@ extension ChatListViewModel { return Asset.Assets.gardening.image case .survey: return Asset.Assets.logo.image + case .surveyResponse: + return Asset.Assets.people.image } } @@ -166,6 +176,7 @@ extension ChatListViewModel { } struct MessageDetail { + let messageId: String let from: String let to: String let body: String? @@ -174,7 +185,7 @@ extension ChatListViewModel { // let survey: SurveyResponse? // let surveyResponse: [String]? - let unread: Bool + var unread: Bool var isFromAdmin: Bool { from == "admin" } @@ -184,7 +195,7 @@ extension ChatListViewModel { case message case announce case survey -// case survey_response + case surveyResponse } func messageType(type: String?) -> MessageType { @@ -192,6 +203,7 @@ extension ChatListViewModel { case "message": return .message case "announce": return .announce case "survey": return .survey + case "survey_response": return .surveyResponse default: return .message } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.xib b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.xib index 58e8ed1e..2911d00a 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.xib +++ b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.xib @@ -1,9 +1,9 @@ - + - + @@ -19,10 +19,10 @@ - + - + - + diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index 3ca107e1..271bf609 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -115,10 +115,6 @@ extension MessagesViewController: UITableViewDataSource { return cell ?? UITableViewCell() } - func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { - return 49 - } - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return UITableView.automaticDimension } From 1bb58f4898a5d7ce101fa17443b731e2943527b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 3 May 2023 13:18:57 -0300 Subject: [PATCH 26/66] refactor preset messages mode using an array of MessageEntity --- .../ChatList/ChatListViewController.swift | 2 +- .../ChatList/ChatListViewModel.swift | 70 +++++-------------- .../Messages/Cell/MessageTableViewCell.swift | 4 +- .../Messages/MessagesViewModel.swift | 19 +---- 4 files changed, 23 insertions(+), 72 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index ffc066ac..6db3c2d2 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -91,7 +91,7 @@ extension ChatListViewController: ChatListViewModelViewDelegate { planterImage.image = image } - func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateChats messages: [ChatListViewModel.Chat]) { + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateChatList chatList: [ChatListViewModel.Chat]) { chatListTableView.reloadData() } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index fffd661a..9ba4461b 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -16,7 +16,7 @@ protocol ChatListViewModelCoordinatorDelegate: AnyObject { protocol ChatListViewModelViewDelegate: AnyObject { func chatListViewModel(_ chatListViewModel: ChatListViewModel, didFetchProfile image: UIImage) - func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateChats chats: [ChatListViewModel.Chat]) + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateChatList chatList: [ChatListViewModel.Chat]) } class ChatListViewModel { @@ -38,19 +38,20 @@ class ChatListViewModel { return planter.firstName ?? "" } + private var chatList: [Chat] = [] { + didSet { + viewDelegate?.chatListViewModel(self, didUpdateChatList: chatList) + } + } + var numberOfRowsInSection: Int { - chats.count + chatList.count } func cellForRowAt(indexPath: IndexPath) -> ChatListViewModel.Chat { - chats[indexPath.row] + chatList[indexPath.row] } - private var chats: [Chat] = [] { - didSet { - viewDelegate?.chatListViewModel(self, didUpdateChats: chats) - } - } } // MARK: = Messaging @@ -63,27 +64,16 @@ extension ChatListViewModel { for message in allMessages { let messageType = messageType(type: message.type) - let messageDetail = MessageDetail( - messageId: message.messageId ?? "", - from: message.from ?? "", - to: message.to ?? "", - body: message.body, - composedAt: message.composedAt ?? "", - videoLink: message.videoLink, - unread: message.unread - ) - switch messageType { case .message: - if let index = chatList.firstIndex(where: { $0.type == .message }) { - chatList[index].messages.append(messageDetail) + chatList[index].messages.append(message) } else { let newChat = Chat( title: message.subject ?? "Admin", type: messageType, - messages: [messageDetail] + messages: [message] ) chatList.insert(newChat, at: 0) } @@ -92,28 +82,20 @@ extension ChatListViewModel { let newChat = Chat( title: message.subject ?? "Announce!", type: messageType, - messages: [messageDetail] + messages: [message] ) chatList.append(newChat) } } - self.chats = chatList + self.chatList = chatList } func updateUnreadMessagesCount(indexPath: IndexPath) { - var messagesId: [String] = [] - - var updatedMessages = chats[indexPath.row].messages.map { messageDetail -> MessageDetail in - messagesId.append(messageDetail.messageId) - var message = messageDetail - message.unread = false - return message - } - - self.chats[indexPath.row].messages = updatedMessages + let messagesToUpdate = chatList[indexPath.row].messages - messagingService.updateUnreadMessages(planter: planter, messageId: messagesId) + let updatedMessages = messagingService.updateUnreadMessages(messages: messagesToUpdate) + self.chatList[indexPath.row].messages = updatedMessages } } @@ -143,7 +125,7 @@ extension ChatListViewModel { func chatSelected(indexPath: IndexPath) { updateUnreadMessagesCount(indexPath: indexPath) - let selectedChat = chats[indexPath.row] + let selectedChat = chatList[indexPath.row] coordinatorDelegate?.chatListViewModel(self, didSelectChat: selectedChat, forPlanter: planter) } @@ -155,7 +137,7 @@ extension ChatListViewModel { struct Chat { let title: String let type: MessageType - var messages: [MessageDetail] + var messages: [MessageEntity] var image: UIImage { switch type { @@ -175,22 +157,6 @@ extension ChatListViewModel { } } - struct MessageDetail { - let messageId: String - let from: String - let to: String - let body: String? - let composedAt: String - let videoLink: String? -// let survey: SurveyResponse? -// let surveyResponse: [String]? - - var unread: Bool - var isFromAdmin: Bool { - from == "admin" - } - } - enum MessageType: String, Decodable { case message case announce diff --git a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift index b8013656..2be8e7b4 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift @@ -46,13 +46,13 @@ class MessageTableViewCell: UITableViewCell { // MARK: - Public func extension MessageTableViewCell { - func setupCell(message: ChatListViewModel.MessageDetail) { + func setupCell(message: MessageEntity) { trailingConstraint = messageBackgroundView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20) leadingConstraint = messageBackgroundView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20) messageLabel.text = message.body - if message.isFromAdmin { + if message.from == "admin" { messageBackgroundView.backgroundColor = .systemGreen leadingConstraint.isActive = true diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift index 48b89d9f..6e5bb18d 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift @@ -29,7 +29,7 @@ class MessagesViewModel { self.messagingService = messagingService self.chat = chat } - + var title: String { return chat.title } @@ -38,7 +38,7 @@ class MessagesViewModel { chat.messages.count } - func getMessageForRowAt(indexPath: IndexPath) -> ChatListViewModel.MessageDetail { + func getMessageForRowAt(indexPath: IndexPath) -> MessageEntity { chat.messages[indexPath.row] } @@ -46,21 +46,6 @@ class MessagesViewModel { chat.title } -// func fetchMessages() { -// -// messagingService.getMessages(planter: planter) { result in -// -// switch result { -// case .success(let messages): -// self.messages = messages -// self.viewDelegate?.messagesViewModel(self, didFetchMessages: messages) -// case .failure(let error): -// self.viewDelegate?.messagesViewModel(self, didReceiveError: error) -// } -// } -// -// } - func sendMessage(text: String) { let newMessage = chat.messages[3] chat.messages.append(newMessage) From 3130adf284f4d4ec2576f228f8fc4911114b2d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 3 May 2023 18:56:11 -0300 Subject: [PATCH 27/66] create new message and save it on core data --- .../Messaging/Messages/MessagesViewModel.swift | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift index 6e5bb18d..070b017b 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift @@ -22,8 +22,6 @@ class MessagesViewModel { private let messagingService: MessagingService private var chat: ChatListViewModel.Chat - private(set) var messages: [Message] = [] - init(planter: Planter, messagingService: MessagingService, chat: ChatListViewModel.Chat) { self.planter = planter self.messagingService = messagingService @@ -47,12 +45,13 @@ class MessagesViewModel { } func sendMessage(text: String) { - let newMessage = chat.messages[3] - chat.messages.append(newMessage) -// let newMessage = Message - - // TODO: Cache new message. Send new message. - Needs a variable to know if it was uploaded or not? - -// messages.append(newMessage) + let trimmedText = text.trimmingCharacters(in: .whitespaces) + + do { + let newMessage = try messagingService.createMessage(planter: planter, text: trimmedText) + chat.messages.append(newMessage) + } catch { + print(error.localizedDescription) + } } } From cbe708ff1ee3d271f0bc7b4891faa05588c6ea71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 3 May 2023 19:00:00 -0300 Subject: [PATCH 28/66] adjust message cell UI --- .../Messages/Cell/MessageTableViewCell.swift | 8 +------- .../Messaging/Messages/Cell/MessageTableViewCell.xib | 12 ++++++------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift index 2be8e7b4..b360fe9b 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift @@ -23,6 +23,7 @@ class MessageTableViewCell: UITableViewCell { messageLabel.font = FontFamily.Lato.regular.font(size: 16) messageLabel.numberOfLines = 0 messageLabel.textColor = UIColor.white + messageLabel.textAlignment = .left } } @@ -53,19 +54,12 @@ extension MessageTableViewCell { messageLabel.text = message.body if message.from == "admin" { - messageBackgroundView.backgroundColor = .systemGreen leadingConstraint.isActive = true - messageLabel.textAlignment = .left - } else { - messageBackgroundView.backgroundColor = .systemBlue trailingConstraint.isActive = true - messageLabel.textAlignment = .right - } - } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.xib b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.xib index 2911d00a..f7f4cdd3 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.xib +++ b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.xib @@ -19,10 +19,10 @@ - + - + - + - + - + From dd9e063721a3a6626386b4504afd6c0ab6851644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Thu, 4 May 2023 18:35:51 -0300 Subject: [PATCH 29/66] update logic to display message --- .../Views/Messaging/Messages/Cell/MessageTableViewCell.swift | 4 ++-- .../UI/Views/Messaging/Messages/MessagesViewController.swift | 3 ++- .../UI/Views/Messaging/Messages/MessagesViewModel.swift | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift index b360fe9b..eb3d9e4e 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift @@ -47,13 +47,13 @@ class MessageTableViewCell: UITableViewCell { // MARK: - Public func extension MessageTableViewCell { - func setupCell(message: MessageEntity) { + func setupCell(planterId: String, message: MessageEntity) { trailingConstraint = messageBackgroundView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20) leadingConstraint = messageBackgroundView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20) messageLabel.text = message.body - if message.from == "admin" { + if message.to == planterId { messageBackgroundView.backgroundColor = .systemGreen leadingConstraint.isActive = true } else { diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index 271bf609..18028f11 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -109,7 +109,8 @@ extension MessagesViewController: UITableViewDataSource { let cell = tableView.dequeueReusableCell(withIdentifier: MessageTableViewCell.identifier, for: indexPath) as? MessageTableViewCell let message = viewModel?.getMessageForRowAt(indexPath: indexPath) - cell?.setupCell(message: message!) // remove force unwrap? + let planterIdentifier = viewModel?.getPlanterIdentifier() + cell?.setupCell(planterId: planterIdentifier!, message: message!) // remove force unwrap? cell?.selectionStyle = .none return cell ?? UITableViewCell() diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift index 070b017b..94f789b2 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift @@ -40,8 +40,8 @@ class MessagesViewModel { chat.messages[indexPath.row] } - func getPlanterName() -> String { - chat.title + func getPlanterIdentifier() -> String? { + planter.identifier } func sendMessage(text: String) { From 5d925f05aeeae4df6583b5c7da9dc71fe6688bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 5 May 2023 17:07:41 -0300 Subject: [PATCH 30/66] Change messages color --- .../Background/BackgroundGreen.colorset/Contents.json | 8 ++++---- .../Messaging/Messages/Cell/MessageTableViewCell.swift | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json b/TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json index fb50632e..f86303cb 100644 --- a/TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json +++ b/TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json @@ -2,12 +2,12 @@ "colors" : [ { "color" : { - "color-space" : "srgb", + "color-space" : "display-p3", "components" : { "alpha" : "1.000", - "blue" : "0.810", - "green" : "0.929", - "red" : "0.873" + "blue" : "0.761", + "green" : "0.914", + "red" : "0.855" } }, "idiom" : "universal" diff --git a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift index eb3d9e4e..e6c04162 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/Cell/MessageTableViewCell.swift @@ -22,7 +22,7 @@ class MessageTableViewCell: UITableViewCell { didSet { messageLabel.font = FontFamily.Lato.regular.font(size: 16) messageLabel.numberOfLines = 0 - messageLabel.textColor = UIColor.white + messageLabel.textColor = Asset.Colors.grayDark.color messageLabel.textAlignment = .left } } @@ -54,10 +54,10 @@ extension MessageTableViewCell { messageLabel.text = message.body if message.to == planterId { - messageBackgroundView.backgroundColor = .systemGreen + messageBackgroundView.backgroundColor = Asset.Colors.grayLight.color.withAlphaComponent(0.18) leadingConstraint.isActive = true } else { - messageBackgroundView.backgroundColor = .systemBlue + messageBackgroundView.backgroundColor = Asset.Colors.primaryGreen.color.withAlphaComponent(0.3) trailingConstraint.isActive = true } } From f9168f208a06634f70df295187c3ba689e4f91ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Tue, 16 May 2023 15:31:06 -0300 Subject: [PATCH 31/66] weak reference to self --- .../UI/Views/Messaging/ChatList/ChatListViewController.swift | 4 ++-- .../UI/Views/Messaging/ChatList/ChatListViewModel.swift | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index 6db3c2d2..d17697a8 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -142,8 +142,8 @@ extension ChatListViewController { } private func showImage(_ show: Bool) { - UIView.animate(withDuration: 0.3) { - self.planterImage.alpha = show ? 1.0 : 0.0 + UIView.animate(withDuration: 0.3) { [weak self] in + self?.planterImage.alpha = show ? 1.0 : 0.0 } } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index 9ba4461b..c565ece4 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -105,7 +105,8 @@ extension ChatListViewModel { func fetchProfileImage() { - selfieService.fetchSelfie(forPlanter: planter) { (result) in + selfieService.fetchSelfie(forPlanter: planter) { [weak self] (result) in + guard let self else { return } switch result { case .success(let data): guard let image = UIImage(data: data) else { From 89e0832d54033bf38bb48e5f7b235e05a5711d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Tue, 16 May 2023 21:25:13 -0300 Subject: [PATCH 32/66] new logic to messages screen. MessageViewModel fetch data directly from coreData --- TreeTracker/Resources/Localizable.strings | 1 + TreeTracker/SwiftGen/Strings.swift | 2 ++ .../UI/Navigation/Home/HomeCoordinator.swift | 13 ++++++------- .../Messaging/ChatList/ChatListViewModel.swift | 12 +++++++++--- .../Messages/MessagesViewController.swift | 1 + .../Messaging/Messages/MessagesViewModel.swift | 18 +++++++++++------- 6 files changed, 30 insertions(+), 17 deletions(-) diff --git a/TreeTracker/Resources/Localizable.strings b/TreeTracker/Resources/Localizable.strings index d36aca6c..1810f7f9 100644 --- a/TreeTracker/Resources/Localizable.strings +++ b/TreeTracker/Resources/Localizable.strings @@ -102,5 +102,6 @@ "Settings.PhotoQuality.Option.High.Info" = "High resolution, high data usage."; //Messages Screen +"Messages.Title" = "Admin"; "Messages.InputTextView.PlaceHolder" = "Click to write a message"; diff --git a/TreeTracker/SwiftGen/Strings.swift b/TreeTracker/SwiftGen/Strings.swift index 8f3d5d7e..cd322a7f 100644 --- a/TreeTracker/SwiftGen/Strings.swift +++ b/TreeTracker/SwiftGen/Strings.swift @@ -135,6 +135,8 @@ internal enum L10n { } } internal enum Messages { + /// Admin + internal static let title = L10n.tr("Localizable", "Messages.Title", fallback: "Admin") internal enum InputTextView { /// Click to write a message internal static let placeHolder = L10n.tr("Localizable", "Messages.InputTextView.PlaceHolder", fallback: "Click to write a message") diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index e7e83201..90d9f5b6 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -68,9 +68,9 @@ private extension HomeCoordinator { ) } - func showMessages(planter: Planter, chat: ChatListViewModel.Chat) { + func showMessages(planter: Planter) { configuration.navigationController.pushViewController( - messagesViewController(planter: planter, chat: chat), + messagesViewController(planter: planter), animated: true ) } @@ -159,13 +159,12 @@ private extension HomeCoordinator { return viewcontroller } - func messagesViewController(planter: Planter, chat: ChatListViewModel.Chat) -> UIViewController { + func messagesViewController(planter: Planter) -> UIViewController { let viewcontroller = StoryboardScene.Messages.initialScene.instantiate() viewcontroller.viewModel = { let viewModel = MessagesViewModel( planter: planter, - messagingService: treetrackerSDK.messagingService, - chat: chat + messagingService: treetrackerSDK.messagingService ) return viewModel }() @@ -292,8 +291,8 @@ extension HomeCoordinator: NotesViewModelCoordinatorDelegate { // MARK: - ChatListViewModelCoordinatorDelegate extension HomeCoordinator: ChatListViewModelCoordinatorDelegate { - func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectChat chat: ChatListViewModel.Chat, forPlanter planter: Planter) { - showMessages(planter: planter, chat: chat) + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectMessages planter: Planter) { + showMessages(planter: planter) } } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index c565ece4..eb60b2c4 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -11,7 +11,7 @@ import Treetracker_Core import UIKit protocol ChatListViewModelCoordinatorDelegate: AnyObject { - func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectChat chat: ChatListViewModel.Chat, forPlanter planter: Planter) + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectMessages planter: Planter) } protocol ChatListViewModelViewDelegate: AnyObject { @@ -125,9 +125,15 @@ extension ChatListViewModel { extension ChatListViewModel { func chatSelected(indexPath: IndexPath) { - updateUnreadMessagesCount(indexPath: indexPath) let selectedChat = chatList[indexPath.row] - coordinatorDelegate?.chatListViewModel(self, didSelectChat: selectedChat, forPlanter: planter) + updateUnreadMessagesCount(indexPath: indexPath) + + switch selectedChat.type { + case .message: + coordinatorDelegate?.chatListViewModel(self, didSelectMessages: planter) + case .announce, .survey, .surveyResponse: + break + } } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index 18028f11..d5cac91c 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -61,6 +61,7 @@ class MessagesViewController: UIViewController, KeyboardDismissing { super.viewDidLoad() addEndEditingBackgroundTapGesture() addKeyboardObservers() + viewModel?.getMessages() } override func viewWillAppear(_ animated: Bool) { diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift index 94f789b2..b8cf6da3 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift @@ -20,24 +20,24 @@ class MessagesViewModel { private let planter: Planter private let messagingService: MessagingService - private var chat: ChatListViewModel.Chat - init(planter: Planter, messagingService: MessagingService, chat: ChatListViewModel.Chat) { + init(planter: Planter, messagingService: MessagingService) { self.planter = planter self.messagingService = messagingService - self.chat = chat } + + private var messages: [MessageEntity] = [] var title: String { - return chat.title + L10n.Messages.title } var numberOfRowsInSection: Int { - chat.messages.count + messages.count } func getMessageForRowAt(indexPath: IndexPath) -> MessageEntity { - chat.messages[indexPath.row] + messages[indexPath.row] } func getPlanterIdentifier() -> String? { @@ -49,9 +49,13 @@ class MessagesViewModel { do { let newMessage = try messagingService.createMessage(planter: planter, text: trimmedText) - chat.messages.append(newMessage) + messages.append(newMessage) } catch { print(error.localizedDescription) } } + + func getMessages() { + messages = messagingService.getMessagesToPresent(planter: planter) + } } From e57fc5a4eb2d61c0e7bd155c7f4c7fa8c0f8144e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Thu, 18 May 2023 16:02:09 -0300 Subject: [PATCH 33/66] add method to fetch messages with offset. When user scrolls up, it fetched more messages from db automatically --- .../ChatList/Cell/ChatListTableViewCell.swift | 1 + .../Messages/MessagesViewController.swift | 87 ++++++++++++++++--- .../Messages/MessagesViewModel.swift | 20 +++-- 3 files changed, 91 insertions(+), 17 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift index 64b1949e..caef6ff4 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift @@ -22,6 +22,7 @@ class ChatListTableViewCell: UITableViewCell { didSet { chatTitle.font = FontFamily.Montserrat.semiBold.font(size: 19) chatTitle.textColor = Asset.Colors.grayDark.color + chatTitle.numberOfLines = 0 } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index d5cac91c..c49acb73 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -15,6 +15,7 @@ class MessagesViewController: UIViewController, KeyboardDismissing { didSet { messagesTableView.separatorStyle = .none messagesTableView.dataSource = self + messagesTableView.delegate = self messagesTableView.register(MessageTableViewCell.nib(), forCellReuseIdentifier: MessageTableViewCell.identifier) messagesTableView.addTopBounceAreaView(color: Asset.Colors.backgroundGreen.color) } @@ -49,6 +50,8 @@ class MessagesViewController: UIViewController, KeyboardDismissing { } @IBOutlet private var bottomContraint: NSLayoutConstraint! + private var lastContentOffsetY: CGFloat = 0 + private var cachedHeights = [IndexPath: CGFloat]() var viewModel: MessagesViewModel? { didSet { @@ -61,7 +64,7 @@ class MessagesViewController: UIViewController, KeyboardDismissing { super.viewDidLoad() addEndEditingBackgroundTapGesture() addKeyboardObservers() - viewModel?.getMessages() + viewModel?.loadMoreMessages() } override func viewWillAppear(_ animated: Bool) { @@ -70,6 +73,16 @@ class MessagesViewController: UIViewController, KeyboardDismissing { navigationController?.navigationBar.setupNavigationAppearance( backgroundColor: Asset.Colors.backgroundGreen.color ) + + scrollToBottom() + } + + private func scrollToBottom() { + guard let row = viewModel?.numberOfRowsInSection else { + return + } + let bottomIndexPath = IndexPath(row: row - 1, section: 0) + messagesTableView.scrollToRow(at: bottomIndexPath, at: .bottom, animated: false) } } @@ -107,14 +120,18 @@ extension MessagesViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: MessageTableViewCell.identifier, for: indexPath) as? MessageTableViewCell + guard let cell = tableView.dequeueReusableCell(withIdentifier: MessageTableViewCell.identifier, for: indexPath) as? MessageTableViewCell else { + return UITableViewCell() + } - let message = viewModel?.getMessageForRowAt(indexPath: indexPath) - let planterIdentifier = viewModel?.getPlanterIdentifier() - cell?.setupCell(planterId: planterIdentifier!, message: message!) // remove force unwrap? - cell?.selectionStyle = .none + guard let message = viewModel?.getMessageForRowAt(indexPath: indexPath), + let planterIdentifier = viewModel?.getPlanterIdentifier() else { + return UITableViewCell() + } - return cell ?? UITableViewCell() + cell.setupCell(planterId: planterIdentifier, message: message) + cell.selectionStyle = .none + return cell } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { @@ -123,6 +140,37 @@ extension MessagesViewController: UITableViewDataSource { } +// MARK: - UITableViewDelegate +extension MessagesViewController: UITableViewDelegate { + + func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + cachedHeights[indexPath] = cell.frame.size.height + } + + func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { + cachedHeights.removeValue(forKey: indexPath) + } + + func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + var height = tableView.estimatedRowHeight + if let cachedHeight = cachedHeights[indexPath] { + height = cachedHeight + } + return height + } + +} + +// MARK: - UIScrollViewDelegate +extension MessagesViewController: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { + if lastContentOffsetY > scrollView.contentOffset.y && scrollView.contentOffset.y < 100 { + viewModel?.loadMoreMessages() + } + lastContentOffsetY = scrollView.contentOffset.y + } +} + // MARK: - UITextViewDelegate extension MessagesViewController: UITextViewDelegate { @@ -190,12 +238,27 @@ extension MessagesViewController: KeyboardObserving { // MARK: - MessagesViewModelViewDelegate extension MessagesViewController: MessagesViewModelViewDelegate { - func messagesViewModel(_ messagesViewModel: MessagesViewModel, didFetchMessages messages: [Message]) { - messagesTableView.reloadData() - } + func messagesViewModel(didFetchMessages messages: [MessageEntity], newMessages: [MessageEntity]) { + guard !newMessages.isEmpty else { return } - func messagesViewModel(_ messagesViewModel: MessagesViewModel, didReceiveError error: Error) { - // TODO: show some alert? + var currentMessage: MessageEntity? + var offsetFromTopOfMessage: CGFloat = 0 + if let topIndexPath = messagesTableView.indexPathsForVisibleRows?.first { + var topMessageRect = messagesTableView.rectForRow(at: topIndexPath) + topMessageRect = topMessageRect.offsetBy(dx: -messagesTableView.contentOffset.x, dy: -messagesTableView.contentOffset.y) + offsetFromTopOfMessage = topMessageRect.minY + + let indexPath = IndexPath(row: (topIndexPath.row + newMessages.count), section: 0) + currentMessage = viewModel?.getMessageForRowAt(indexPath: indexPath) + } + + messagesTableView.reloadData() + if let targetMessage = currentMessage, + let targetIndex = messages.firstIndex(where: { $0.messageId == targetMessage.messageId }) { + let targetIndexPath = IndexPath(row: targetIndex, section: 0) + messagesTableView.scrollToRow(at: targetIndexPath, at: .top, animated: false) + messagesTableView.contentOffset.y -= offsetFromTopOfMessage + } } } diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift index b8cf6da3..4c92d7c6 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewModel.swift @@ -10,8 +10,7 @@ import Foundation import Treetracker_Core protocol MessagesViewModelViewDelegate: AnyObject { - func messagesViewModel(_ messagesViewModel: MessagesViewModel, didFetchMessages messages: [Message]) - func messagesViewModel(_ messagesViewModel: MessagesViewModel, didReceiveError error: Error) + func messagesViewModel(didFetchMessages messages: [MessageEntity], newMessages: [MessageEntity]) } class MessagesViewModel { @@ -25,13 +24,15 @@ class MessagesViewModel { self.planter = planter self.messagingService = messagingService } - + private var messages: [MessageEntity] = [] var title: String { L10n.Messages.title } + private var isLoading = false + var numberOfRowsInSection: Int { messages.count } @@ -55,7 +56,16 @@ class MessagesViewModel { } } - func getMessages() { - messages = messagingService.getMessagesToPresent(planter: planter) + func loadMoreMessages() { + guard !isLoading else { return } + + isLoading = true + + let offset = messages.count + let savedMessages = messagingService.getMessagesToPresent(planter: planter, offset: offset) + messages.insert(contentsOf: savedMessages, at: 0) + + viewDelegate?.messagesViewModel(didFetchMessages: messages, newMessages: savedMessages) + isLoading = false } } From 1c5bbb9604953e8e92510da545db5c2c91abdad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 19 May 2023 17:51:04 -0300 Subject: [PATCH 34/66] some cleanup --- TreeTracker.xcodeproj/project.pbxproj | 4 --- .../Extensions/UITableView+Extensions.swift | 22 -------------- .../ChatList/ChatListViewController.swift | 6 ++-- .../Messages/MessagesViewController.swift | 30 ++++--------------- 4 files changed, 10 insertions(+), 52 deletions(-) delete mode 100644 TreeTracker/UI/Extensions/UITableView+Extensions.swift diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index fc22faa1..f6c10f0e 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 8D036C6F29F022120079F4B1 /* UITableView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D036C6E29F022120079F4B1 /* UITableView+Extensions.swift */; }; 8D3AF01F2A01346D00976763 /* UINavigationBar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3AF01E2A01346D00976763 /* UINavigationBar+Extensions.swift */; }; 8D75250629DB13FA00361CC1 /* MessagingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75250529DB13FA00361CC1 /* MessagingButton.swift */; }; 8D75251729DB61A000361CC1 /* ChatListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75251629DB61A000361CC1 /* ChatListViewController.swift */; }; @@ -109,7 +108,6 @@ /* Begin PBXFileReference section */ 229566C2A487A3F9C7D82AD8 /* Pods_TreeTracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TreeTracker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8D036C6E29F022120079F4B1 /* UITableView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+Extensions.swift"; sourceTree = ""; }; 8D3AF01E2A01346D00976763 /* UINavigationBar+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+Extensions.swift"; sourceTree = ""; }; 8D75250529DB13FA00361CC1 /* MessagingButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagingButton.swift; sourceTree = ""; }; 8D75251629DB61A000361CC1 /* ChatListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListViewController.swift; sourceTree = ""; }; @@ -625,7 +623,6 @@ isa = PBXGroup; children = ( 93EC8CA724B28B5D00946C0C /* UITextField+Extensions.swift */, - 8D036C6E29F022120079F4B1 /* UITableView+Extensions.swift */, 8D3AF01E2A01346D00976763 /* UINavigationBar+Extensions.swift */, ); path = Extensions; @@ -947,7 +944,6 @@ 93EC8CAD24BBA9F600946C0C /* AddTreeViewModel.swift in Sources */, 938294B027F9A7A500888E42 /* SettingsViewController.swift in Sources */, 93EC8C4A24AB7EA100946C0C /* UploadsButton.swift in Sources */, - 8D036C6F29F022120079F4B1 /* UITableView+Extensions.swift in Sources */, 93E1896F245DEE7700217F21 /* RootCoordinator.swift in Sources */, 93D7295824C49CA700271256 /* GPSAccuracyLabel.swift in Sources */, 93EC8C3924AA52DF00946C0C /* HomeViewController.swift in Sources */, diff --git a/TreeTracker/UI/Extensions/UITableView+Extensions.swift b/TreeTracker/UI/Extensions/UITableView+Extensions.swift deleted file mode 100644 index 1ae0d7c9..00000000 --- a/TreeTracker/UI/Extensions/UITableView+Extensions.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// UITableView+Extensions.swift -// TreeTracker -// -// Created by Frédéric Helfer on 19/04/23. -// Copyright © 2023 Greenstand. All rights reserved. -// - -import UIKit - -extension UITableView { - - func addTopBounceAreaView(color: UIColor = .white) { - var frame = UIScreen.main.bounds - frame.origin.y = -frame.size.height - - let view = UIView(frame: frame) - view.backgroundColor = color - - self.addSubview(view) - } -} diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index d17697a8..2a2028a6 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -15,7 +15,6 @@ class ChatListViewController: UIViewController { chatListTableView.delegate = self chatListTableView.dataSource = self chatListTableView.register(ChatListTableViewCell.nib(), forCellReuseIdentifier: ChatListTableViewCell.identifier) - chatListTableView.addTopBounceAreaView(color: Asset.Colors.backgroundGreen.color) } } @@ -44,7 +43,10 @@ class ChatListViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) view.backgroundColor = Asset.Colors.backgroundGreen.color - navigationController?.navigationBar.setupNavigationAppearance(backgroundColor: Asset.Colors.backgroundGreen.color) + navigationController?.navigationBar.setupNavigationAppearance( + prefersLargeTitles: true, + backgroundColor: Asset.Colors.backgroundGreen.color + ) showImage(true) } diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index c49acb73..d88670bf 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -17,7 +17,6 @@ class MessagesViewController: UIViewController, KeyboardDismissing { messagesTableView.dataSource = self messagesTableView.delegate = self messagesTableView.register(MessageTableViewCell.nib(), forCellReuseIdentifier: MessageTableViewCell.identifier) - messagesTableView.addTopBounceAreaView(color: Asset.Colors.backgroundGreen.color) } } @@ -51,7 +50,6 @@ class MessagesViewController: UIViewController, KeyboardDismissing { @IBOutlet private var bottomContraint: NSLayoutConstraint! private var lastContentOffsetY: CGFloat = 0 - private var cachedHeights = [IndexPath: CGFloat]() var viewModel: MessagesViewModel? { didSet { @@ -60,6 +58,7 @@ class MessagesViewController: UIViewController, KeyboardDismissing { } } + // MARK: - LifeCycle override func viewDidLoad() { super.viewDidLoad() addEndEditingBackgroundTapGesture() @@ -73,14 +72,11 @@ class MessagesViewController: UIViewController, KeyboardDismissing { navigationController?.navigationBar.setupNavigationAppearance( backgroundColor: Asset.Colors.backgroundGreen.color ) - scrollToBottom() } private func scrollToBottom() { - guard let row = viewModel?.numberOfRowsInSection else { - return - } + guard let row = viewModel?.numberOfRowsInSection else { return } let bottomIndexPath = IndexPath(row: row - 1, section: 0) messagesTableView.scrollToRow(at: bottomIndexPath, at: .bottom, animated: false) } @@ -134,41 +130,27 @@ extension MessagesViewController: UITableViewDataSource { return cell } - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - return UITableView.automaticDimension - } - } // MARK: - UITableViewDelegate extension MessagesViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - cachedHeights[indexPath] = cell.frame.size.height - } - - func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { - cachedHeights.removeValue(forKey: indexPath) - } - - func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { - var height = tableView.estimatedRowHeight - if let cachedHeight = cachedHeights[indexPath] { - height = cachedHeight - } - return height + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return UITableView.automaticDimension } } // MARK: - UIScrollViewDelegate extension MessagesViewController: UIScrollViewDelegate { + func scrollViewDidScroll(_ scrollView: UIScrollView) { if lastContentOffsetY > scrollView.contentOffset.y && scrollView.contentOffset.y < 100 { viewModel?.loadMoreMessages() } lastContentOffsetY = scrollView.contentOffset.y } + } // MARK: - UITextViewDelegate From 3187f8ba5bb5aaf6f8fae32f643a431d55887816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 19 May 2023 18:01:31 -0300 Subject: [PATCH 35/66] tap to hidekeyboard ignores send message uibutton --- .../Messages/MessagesViewController.swift | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index d88670bf..5d799507 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -9,7 +9,7 @@ import UIKit import Treetracker_Core -class MessagesViewController: UIViewController, KeyboardDismissing { +class MessagesViewController: UIViewController { @IBOutlet private var messagesTableView: UITableView! { didSet { @@ -61,7 +61,7 @@ class MessagesViewController: UIViewController, KeyboardDismissing { // MARK: - LifeCycle override func viewDidLoad() { super.viewDidLoad() - addEndEditingBackgroundTapGesture() + hideKeyboardWhenTappedAround() addKeyboardObservers() viewModel?.loadMoreMessages() } @@ -217,6 +217,26 @@ extension MessagesViewController: KeyboardObserving { } } +// MARK: - KeyboardDismissing +extension MessagesViewController: UIGestureRecognizerDelegate { + + private func hideKeyboardWhenTappedAround() { + let tap = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard)) + tap.delegate = self + tap.cancelsTouchesInView = false + view.addGestureRecognizer(tap) + } + + @objc private func dismissKeyboard() { + view.endEditing(true) + } + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + return !(touch.view is UIButton) + } + +} + // MARK: - MessagesViewModelViewDelegate extension MessagesViewController: MessagesViewModelViewDelegate { From 6543a7c203c72f9ea85abeaa8bb2aa3ad653abb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 19 May 2023 18:44:31 -0300 Subject: [PATCH 36/66] fix tableview content up/down following keyboard --- .../Messages/MessagesViewController.swift | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index 5d799507..dc0f85d8 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -200,20 +200,22 @@ extension MessagesViewController: KeyboardObserving { let bottomSafeAreaHeight = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0.0 - UIView.animate(withDuration: 0.30) { - self.bottomContraint.constant = keyboardSize.height - bottomSafeAreaHeight - self.view.layoutIfNeeded() - } - - let row = viewModel?.numberOfRowsInSection ?? 0 - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { - self.messagesTableView.scrollToRow(at: IndexPath(row: row - 1, section: 0), at: .bottom, animated: true) - } + self.bottomContraint.constant = keyboardSize.height - bottomSafeAreaHeight + self.messagesTableView.contentOffset.y += keyboardSize.height - bottomSafeAreaHeight + self.view.layoutIfNeeded() } func keyboardWillHide(notification: Notification) { - bottomContraint.constant = 0 + let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] + guard let keyboardSize = (keyboardFrame as? NSValue)?.cgRectValue else { + return + } + + let bottomSafeAreaHeight = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0.0 + + self.bottomContraint.constant = 0 + self.messagesTableView.contentOffset.y -= keyboardSize.height - bottomSafeAreaHeight + self.view.layoutIfNeeded() } } From 32fe059e734ac94490ae235f569bb786d90c9ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 19 May 2023 19:14:30 -0300 Subject: [PATCH 37/66] small design changes on chat screen --- .../ChatList/Cell/ChatListTableViewCell.swift | 6 ++-- .../ChatList/Cell/ChatListTableViewCell.xib | 36 +++++++++---------- .../ChatList/ChatListViewController.swift | 8 ++--- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift index caef6ff4..87fdb8fe 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift @@ -13,7 +13,7 @@ class ChatListTableViewCell: UITableViewCell { @IBOutlet private var chatImage: UIImageView! { didSet { chatImage.contentMode = .scaleAspectFill - chatImage.layer.cornerRadius = 20 + chatImage.layer.cornerRadius = 13 chatImage.backgroundColor = Asset.Colors.secondaryGreen.color } } @@ -29,7 +29,7 @@ class ChatListTableViewCell: UITableViewCell { @IBOutlet private var chatAlertCountView: UIView! { didSet { chatAlertCountView.backgroundColor = Asset.Colors.secondaryRed.color - chatAlertCountView.layer.cornerRadius = 15 + chatAlertCountView.layer.cornerRadius = 13 chatAlertCountView.isHidden = true } } @@ -37,7 +37,7 @@ class ChatListTableViewCell: UITableViewCell { @IBOutlet private var chatAlertCountLabel: UILabel! { didSet { chatAlertCountLabel.textColor = .white - chatAlertCountLabel.font = FontFamily.Montserrat.semiBold.font(size: 20.0) + chatAlertCountLabel.font = FontFamily.Montserrat.semiBold.font(size: 18.0) chatAlertCountLabel.textAlignment = .center chatAlertCountLabel.isHidden = true } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib index 5649d912..d698c575 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.xib @@ -1,9 +1,9 @@ - + - + @@ -11,34 +11,34 @@ - - + + - + - + - - + + - + - - - - + + + + @@ -60,7 +60,7 @@ - + diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index 2a2028a6..b0c48a1e 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -71,15 +71,15 @@ extension ChatListViewController: UITableViewDataSource { return cell ?? UITableViewCell() } - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - 130 - } - } // MARK: - UITableViewDelegate extension ChatListViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + 110 + } + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { viewModel?.chatSelected(indexPath: indexPath) } From 85a89118534ca17e4b00e7b273b737008f5847c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 19 May 2023 21:41:07 -0300 Subject: [PATCH 38/66] cache row height --- .../Views/Messaging/Messages/MessagesViewController.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index dc0f85d8..594a2fa6 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -50,6 +50,7 @@ class MessagesViewController: UIViewController { @IBOutlet private var bottomContraint: NSLayoutConstraint! private var lastContentOffsetY: CGFloat = 0 + private var heightCache: [IndexPath: CGFloat] = [:] var viewModel: MessagesViewModel? { didSet { @@ -135,8 +136,12 @@ extension MessagesViewController: UITableViewDataSource { // MARK: - UITableViewDelegate extension MessagesViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + heightCache[indexPath] = cell.bounds.size.height + } + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - return UITableView.automaticDimension + return heightCache[indexPath] ?? UITableView.automaticDimension } } From 4a03dcabe665beb84f1a73d7a471983ca82accc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 31 May 2023 10:38:44 -0300 Subject: [PATCH 39/66] Add no messages view --- TreeTracker/Resources/Localizable.strings | 3 ++ TreeTracker/SwiftGen/Strings.swift | 6 +++ .../Messaging/ChatList/ChatList.storyboard | 38 +++++++++++++++++-- .../ChatList/ChatListViewController.swift | 29 +++++++++++++- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/TreeTracker/Resources/Localizable.strings b/TreeTracker/Resources/Localizable.strings index 1810f7f9..6f8950fa 100644 --- a/TreeTracker/Resources/Localizable.strings +++ b/TreeTracker/Resources/Localizable.strings @@ -101,6 +101,9 @@ "Settings.PhotoQuality.Option.High.Title" = "High"; "Settings.PhotoQuality.Option.High.Info" = "High resolution, high data usage."; +//ChatList Screen +"ChatList.NoMessagesLabel.Text" = "No messages, Yet."; + //Messages Screen "Messages.Title" = "Admin"; "Messages.InputTextView.PlaceHolder" = "Click to write a message"; diff --git a/TreeTracker/SwiftGen/Strings.swift b/TreeTracker/SwiftGen/Strings.swift index cd322a7f..1278f7fe 100644 --- a/TreeTracker/SwiftGen/Strings.swift +++ b/TreeTracker/SwiftGen/Strings.swift @@ -48,6 +48,12 @@ internal enum L10n { /// Copyright © 2020 Greenstand. All rights reserved. internal static let title = L10n.tr("Localizable", "App.Title", fallback: "Greenstand Treetracker") } + internal enum ChatList { + internal enum NoMessagesLabel { + /// No messages, Yet. + internal static let text = L10n.tr("Localizable", "ChatList.NoMessagesLabel.Text", fallback: "No messages, Yet.") + } + } internal enum DeleteAccountConfirmation { /// Are you sure you want to delete tour account? All your trees on this device will be lost. internal static let message = L10n.tr("Localizable", "DeleteAccountConfirmation.Message", fallback: "Are you sure you want to delete tour account? All your trees on this device will be lost.") diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatList.storyboard b/TreeTracker/UI/Views/Messaging/ChatList/ChatList.storyboard index fb9bd9d3..dd32e809 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatList.storyboard +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatList.storyboard @@ -1,9 +1,9 @@ - + - + @@ -21,23 +21,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index b0c48a1e..f10ad153 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -18,6 +18,28 @@ class ChatListViewController: UIViewController { } } + @IBOutlet private var noMessagesView: UIView! { + didSet { + noMessagesView.isHidden = true + } + } + + @IBOutlet weak var noMessagesImageView: UIImageView! { + didSet { + noMessagesImageView.image = Asset.Assets.mail.image + noMessagesImageView.tintColor = Asset.Colors.primaryGreen.color + } + } + + @IBOutlet private var noMessagesLabel: UILabel! { + didSet { + noMessagesLabel.font = FontFamily.Montserrat.semiBold.font(size: 20) + noMessagesLabel.numberOfLines = 0 + noMessagesLabel.textColor = Asset.Colors.grayDark.color + noMessagesLabel.text = L10n.ChatList.NoMessagesLabel.text + } + } + private var planterImage: UIImageView = { let image = UIImageView() image.translatesAutoresizingMaskIntoConstraints = false @@ -94,7 +116,12 @@ extension ChatListViewController: ChatListViewModelViewDelegate { } func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateChatList chatList: [ChatListViewModel.Chat]) { - chatListTableView.reloadData() + if chatList.isEmpty { + noMessagesView.isHidden = false + } else { + noMessagesView.isHidden = true + chatListTableView.reloadData() + } } } From 273b92ac481446c63b2b5fc5a40f0fa235831be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 31 May 2023 16:29:08 -0300 Subject: [PATCH 40/66] add announce view --- TreeTracker/Resources/Localizable.strings | 2 + TreeTracker/SwiftGen/StoryboardScenes.swift | 5 ++ TreeTracker/SwiftGen/Strings.swift | 4 ++ .../UI/Navigation/Home/HomeCoordinator.swift | 22 +++++++ .../Messaging/Announce/Announce.storyboard | 61 +++++++++++++++++++ .../Announce/AnnounceViewController.swift | 60 ++++++++++++++++++ .../Announce/AnnounceViewModel.swift | 33 ++++++++++ .../ChatList/ChatListViewModel.swift | 5 +- 8 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 TreeTracker/UI/Views/Messaging/Announce/Announce.storyboard create mode 100644 TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift create mode 100644 TreeTracker/UI/Views/Messaging/Announce/AnnounceViewModel.swift diff --git a/TreeTracker/Resources/Localizable.strings b/TreeTracker/Resources/Localizable.strings index 6f8950fa..386815ee 100644 --- a/TreeTracker/Resources/Localizable.strings +++ b/TreeTracker/Resources/Localizable.strings @@ -108,3 +108,5 @@ "Messages.Title" = "Admin"; "Messages.InputTextView.PlaceHolder" = "Click to write a message"; +//Announce Screen +"Announce.Title" = "Announce"; diff --git a/TreeTracker/SwiftGen/StoryboardScenes.swift b/TreeTracker/SwiftGen/StoryboardScenes.swift index 9ce713c1..d41c4cd2 100644 --- a/TreeTracker/SwiftGen/StoryboardScenes.swift +++ b/TreeTracker/SwiftGen/StoryboardScenes.swift @@ -18,6 +18,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: AddTree.self) } + internal enum Announce: StoryboardType { + internal static let storyboardName = "Announce" + + internal static let initialScene = InitialSceneType(storyboard: Announce.self) + } internal enum ChatList: StoryboardType { internal static let storyboardName = "ChatList" diff --git a/TreeTracker/SwiftGen/Strings.swift b/TreeTracker/SwiftGen/Strings.swift index 1278f7fe..5be1d195 100644 --- a/TreeTracker/SwiftGen/Strings.swift +++ b/TreeTracker/SwiftGen/Strings.swift @@ -40,6 +40,10 @@ internal enum L10n { internal static let error = L10n.tr("Localizable", "Alert.Title.Error", fallback: "Error") } } + internal enum Announce { + /// Announce + internal static let title = L10n.tr("Localizable", "Announce.Title", fallback: "Announce") + } internal enum App { /// Localizable.strings /// TreeTracker diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index 90d9f5b6..380cd715 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -75,6 +75,13 @@ private extension HomeCoordinator { ) } + func showAnnounce(chat: ChatListViewModel.Chat) { + configuration.navigationController.pushViewController( + announceViewController(chat: chat), + animated: true + ) + } + func showNotes(note: String?) { configuration.navigationController.pushViewController( notesViewController(note: note), @@ -170,6 +177,17 @@ private extension HomeCoordinator { }() return viewcontroller } + + func announceViewController(chat: ChatListViewModel.Chat) -> UIViewController { + let viewController = StoryboardScene.Announce.initialScene.instantiate() + viewController.viewModel = { + let viewModel = AnnounceViewModel( + chat: chat + ) + return viewModel + }() + return viewController + } func notesViewController(note: String?) -> UIViewController { let viewController = StoryboardScene.Notes.initialScene.instantiate() @@ -295,4 +313,8 @@ extension HomeCoordinator: ChatListViewModelCoordinatorDelegate { showMessages(planter: planter) } + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectAnnounce chat: ChatListViewModel.Chat) { + showAnnounce(chat: chat) + } + } diff --git a/TreeTracker/UI/Views/Messaging/Announce/Announce.storyboard b/TreeTracker/UI/Views/Messaging/Announce/Announce.storyboard new file mode 100644 index 00000000..1f0a891e --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Announce/Announce.storyboard @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift b/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift new file mode 100644 index 00000000..c09ade75 --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift @@ -0,0 +1,60 @@ +// +// AnnounceViewController.swift +// TreeTracker +// +// Created by Frédéric Helfer on 31/05/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import UIKit + +class AnnounceViewController: UIViewController { + + @IBOutlet private var titleLabel: UILabel! { + didSet { + titleLabel.font = FontFamily.Montserrat.bold.font(size: 28) + titleLabel.numberOfLines = 0 + titleLabel.textColor = Asset.Colors.grayDark.color + } + } + + @IBOutlet private var bodyLabel: UILabel! { + didSet { + bodyLabel.font = FontFamily.Montserrat.semiBold.font(size: 18) + bodyLabel.numberOfLines = 0 + bodyLabel.textAlignment = .justified + bodyLabel.textColor = Asset.Colors.grayMedium.color + } + } + + var viewModel: AnnounceViewModel? { + didSet { + viewModel?.viewDelegate = self + title = viewModel?.title + } + } + + // MARK: - LifeCycle + override func viewDidLoad() { + super.viewDidLoad() + viewModel?.updateView() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationController?.navigationBar.setupNavigationAppearance( + prefersLargeTitles: true, + backgroundColor: Asset.Colors.backgroundGreen.color + ) + } +} + +// MARK: - AnnounceViewModelViewDelegate +extension AnnounceViewController: AnnounceViewModelViewDelegate { + + func announceViewModel(_ announceViewModel: AnnounceViewModel, updateView chat: ChatListViewModel.Chat) { + titleLabel.text = chat.title + bodyLabel.text = chat.messages.first?.body + } + +} diff --git a/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewModel.swift b/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewModel.swift new file mode 100644 index 00000000..b17e60f5 --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewModel.swift @@ -0,0 +1,33 @@ +// +// AnnounceViewModel.swift +// TreeTracker +// +// Created by Frédéric Helfer on 31/05/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import Foundation + +protocol AnnounceViewModelViewDelegate: AnyObject { + func announceViewModel(_ announceViewModel: AnnounceViewModel, updateView chat: ChatListViewModel.Chat) +} + +class AnnounceViewModel { + + weak var viewDelegate: AnnounceViewModelViewDelegate? + + private var chat: ChatListViewModel.Chat + + init(chat: ChatListViewModel.Chat) { + self.chat = chat + } + + var title: String { + return L10n.Announce.title + } + + func updateView() { + viewDelegate?.announceViewModel(self, updateView: chat) + } + +} diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index eb60b2c4..7b1d1fcb 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -12,6 +12,7 @@ import UIKit protocol ChatListViewModelCoordinatorDelegate: AnyObject { func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectMessages planter: Planter) + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectAnnounce chat: ChatListViewModel.Chat) } protocol ChatListViewModelViewDelegate: AnyObject { @@ -131,7 +132,9 @@ extension ChatListViewModel { switch selectedChat.type { case .message: coordinatorDelegate?.chatListViewModel(self, didSelectMessages: planter) - case .announce, .survey, .surveyResponse: + case .announce: + coordinatorDelegate?.chatListViewModel(self, didSelectAnnounce: selectedChat) + case .survey, .surveyResponse: break } } From 3fc9a2965b2ff6f5707fbe1aa4c6bf51d307cc55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Thu, 1 Jun 2023 19:28:58 -0300 Subject: [PATCH 41/66] announce view --- TreeTracker.xcodeproj/project.pbxproj | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index f6c10f0e..a0453388 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -19,6 +19,9 @@ 8D75252D29DB87B800361CC1 /* Messages.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D75252C29DB87B800361CC1 /* Messages.storyboard */; }; 8D75253129DC601F00361CC1 /* MessageTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75252F29DC601F00361CC1 /* MessageTableViewCell.swift */; }; 8D75253229DC601F00361CC1 /* MessageTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8D75253029DC601F00361CC1 /* MessageTableViewCell.xib */; }; + 8D8A9B6F2A27898100115E22 /* Announce.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8A9B6E2A27898100115E22 /* Announce.storyboard */; }; + 8D8A9B712A2789A600115E22 /* AnnounceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8A9B702A2789A600115E22 /* AnnounceViewController.swift */; }; + 8D8A9B732A278A2E00115E22 /* AnnounceViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8A9B722A278A2E00115E22 /* AnnounceViewModel.swift */; }; 9311609126B40E5E007FCA52 /* DestructiveButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609026B40E5E007FCA52 /* DestructiveButton.swift */; }; 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609A26B4108A007FCA52 /* RoundedButton.swift */; }; 931C37B8249BACFA00EE24DC /* SelfieViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */; }; @@ -120,6 +123,9 @@ 8D75252C29DB87B800361CC1 /* Messages.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Messages.storyboard; sourceTree = ""; }; 8D75252F29DC601F00361CC1 /* MessageTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageTableViewCell.swift; sourceTree = ""; }; 8D75253029DC601F00361CC1 /* MessageTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MessageTableViewCell.xib; sourceTree = ""; }; + 8D8A9B6E2A27898100115E22 /* Announce.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Announce.storyboard; sourceTree = ""; }; + 8D8A9B702A2789A600115E22 /* AnnounceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnounceViewController.swift; sourceTree = ""; }; + 8D8A9B722A278A2E00115E22 /* AnnounceViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnounceViewModel.swift; sourceTree = ""; }; 9311609026B40E5E007FCA52 /* DestructiveButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestructiveButton.swift; sourceTree = ""; }; 9311609A26B4108A007FCA52 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfieViewModel.swift; sourceTree = ""; }; @@ -233,6 +239,7 @@ children = ( 8D75250929DB5BF400361CC1 /* ChatList */, 8D75252329DB83F300361CC1 /* Messages */, + 8D8A9B6B2A27892600115E22 /* Announce */, ); path = Messaging; sourceTree = ""; @@ -277,6 +284,16 @@ path = Cell; sourceTree = ""; }; + 8D8A9B6B2A27892600115E22 /* Announce */ = { + isa = PBXGroup; + children = ( + 8D8A9B702A2789A600115E22 /* AnnounceViewController.swift */, + 8D8A9B722A278A2E00115E22 /* AnnounceViewModel.swift */, + 8D8A9B6E2A27898100115E22 /* Announce.storyboard */, + ); + path = Announce; + sourceTree = ""; + }; 9311609426B40EE0007FCA52 /* Buttons */ = { isa = PBXGroup; children = ( @@ -788,6 +805,7 @@ buildActionMask = 2147483647; files = ( 93E1893D245B738100217F21 /* LaunchScreen.storyboard in Resources */, + 8D8A9B6F2A27898100115E22 /* Announce.storyboard in Resources */, 8D75253229DC601F00361CC1 /* MessageTableViewCell.xib in Resources */, 938294B227F9A7AB00888E42 /* Settings.storyboard in Resources */, 93EC8C3D24AA52F000946C0C /* Home.storyboard in Resources */, @@ -942,6 +960,7 @@ 93E82F56246C35FF00006C91 /* SignInViewModel.swift in Sources */, 93D3FE2E25C0CC4100EB3B3C /* Configuration.swift in Sources */, 93EC8CAD24BBA9F600946C0C /* AddTreeViewModel.swift in Sources */, + 8D8A9B712A2789A600115E22 /* AnnounceViewController.swift in Sources */, 938294B027F9A7A500888E42 /* SettingsViewController.swift in Sources */, 93EC8C4A24AB7EA100946C0C /* UploadsButton.swift in Sources */, 93E1896F245DEE7700217F21 /* RootCoordinator.swift in Sources */, @@ -979,6 +998,7 @@ 93E82F66246C439100006C91 /* Strings.swift in Sources */, 93989CFB246EABF100A1B21F /* TermsViewController.swift in Sources */, 8D75253129DC601F00361CC1 /* MessageTableViewCell.swift in Sources */, + 8D8A9B732A278A2E00115E22 /* AnnounceViewModel.swift in Sources */, 8D75252029DB6E7E00361CC1 /* ChatListTableViewCell.swift in Sources */, 93EC8CAB24BBA9E100946C0C /* AddTreeViewController.swift in Sources */, 93EC8C4624AB7A6900946C0C /* AddTreeButton.swift in Sources */, From 608c6678d5bb52e4f3ee7915a7eff24c5e3fdacd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Thu, 1 Jun 2023 19:32:19 -0300 Subject: [PATCH 42/66] update syncMessages method. Added recursion to get and post methods. --- TreeTracker/UI/Views/Home/HomeViewModel.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/TreeTracker/UI/Views/Home/HomeViewModel.swift b/TreeTracker/UI/Views/Home/HomeViewModel.swift index 55383e47..4210efd4 100644 --- a/TreeTracker/UI/Views/Home/HomeViewModel.swift +++ b/TreeTracker/UI/Views/Home/HomeViewModel.swift @@ -113,10 +113,11 @@ extension HomeViewModel { extension HomeViewModel { func fetchUnreadMessagesCount() { - messagingService.getUnreadMessagesCount(for: planter) { [weak self] count in - guard let self else { return } - unreadMessagesCount = count - } + messagingService.syncMessages(for: planter) +// messagingService.getUnreadMessagesCount(for: planter) { [weak self] count in +// guard let self else { return } +// unreadMessagesCount = count +// } } } From 1766205c5b7ab6fefda61dfacc29730af7694cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 2 Jun 2023 11:24:56 -0300 Subject: [PATCH 43/66] implement SurveyView base files --- TreeTracker.xcodeproj/project.pbxproj | 44 ++++++++++ TreeTracker/SwiftGen/StoryboardScenes.swift | 5 ++ .../UI/Navigation/Home/HomeCoordinator.swift | 24 +++++- .../ChatList/ChatListViewModel.swift | 3 +- .../Survey/Cell/SurveyTableViewCell.swift | 42 ++++++++++ .../Survey/Cell/SurveyTableViewCell.xib | 50 ++++++++++++ .../Views/Messaging/Survey/Survey.storyboard | 74 +++++++++++++++++ .../Survey/SurveyViewController.swift | 80 +++++++++++++++++++ .../Messaging/Survey/SurveyViewModel.swift | 31 +++++++ 9 files changed, 351 insertions(+), 2 deletions(-) create mode 100644 TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift create mode 100644 TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.xib create mode 100644 TreeTracker/UI/Views/Messaging/Survey/Survey.storyboard create mode 100644 TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift create mode 100644 TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index a0453388..c541399b 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -22,6 +22,11 @@ 8D8A9B6F2A27898100115E22 /* Announce.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8A9B6E2A27898100115E22 /* Announce.storyboard */; }; 8D8A9B712A2789A600115E22 /* AnnounceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8A9B702A2789A600115E22 /* AnnounceViewController.swift */; }; 8D8A9B732A278A2E00115E22 /* AnnounceViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8A9B722A278A2E00115E22 /* AnnounceViewModel.swift */; }; + 8D8A9B782A2975E600115E22 /* Survey.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8A9B772A2975E600115E22 /* Survey.storyboard */; }; + 8D8A9B7A2A29761C00115E22 /* SurveyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8A9B792A29761C00115E22 /* SurveyViewModel.swift */; }; + 8D8A9B7C2A29762900115E22 /* SurveyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8A9B7B2A29762900115E22 /* SurveyViewController.swift */; }; + 8D8A9B832A2A217000115E22 /* SurveyTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8D8A9B822A2A217000115E22 /* SurveyTableViewCell.xib */; }; + 8D8A9B852A2A21AB00115E22 /* SurveyTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8A9B842A2A21AB00115E22 /* SurveyTableViewCell.swift */; }; 9311609126B40E5E007FCA52 /* DestructiveButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609026B40E5E007FCA52 /* DestructiveButton.swift */; }; 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609A26B4108A007FCA52 /* RoundedButton.swift */; }; 931C37B8249BACFA00EE24DC /* SelfieViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */; }; @@ -126,6 +131,11 @@ 8D8A9B6E2A27898100115E22 /* Announce.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Announce.storyboard; sourceTree = ""; }; 8D8A9B702A2789A600115E22 /* AnnounceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnounceViewController.swift; sourceTree = ""; }; 8D8A9B722A278A2E00115E22 /* AnnounceViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnounceViewModel.swift; sourceTree = ""; }; + 8D8A9B772A2975E600115E22 /* Survey.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Survey.storyboard; sourceTree = ""; }; + 8D8A9B792A29761C00115E22 /* SurveyViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveyViewModel.swift; sourceTree = ""; }; + 8D8A9B7B2A29762900115E22 /* SurveyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveyViewController.swift; sourceTree = ""; }; + 8D8A9B822A2A217000115E22 /* SurveyTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SurveyTableViewCell.xib; sourceTree = ""; }; + 8D8A9B842A2A21AB00115E22 /* SurveyTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveyTableViewCell.swift; sourceTree = ""; }; 9311609026B40E5E007FCA52 /* DestructiveButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestructiveButton.swift; sourceTree = ""; }; 9311609A26B4108A007FCA52 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfieViewModel.swift; sourceTree = ""; }; @@ -240,6 +250,7 @@ 8D75250929DB5BF400361CC1 /* ChatList */, 8D75252329DB83F300361CC1 /* Messages */, 8D8A9B6B2A27892600115E22 /* Announce */, + 8D8A9B762A2975C900115E22 /* Survey */, ); path = Messaging; sourceTree = ""; @@ -294,6 +305,34 @@ path = Announce; sourceTree = ""; }; + 8D8A9B762A2975C900115E22 /* Survey */ = { + isa = PBXGroup; + children = ( + 8D8A9B862A2A31EC00115E22 /* Components */, + 8D8A9B7D2A297B4C00115E22 /* Cell */, + 8D8A9B792A29761C00115E22 /* SurveyViewModel.swift */, + 8D8A9B7B2A29762900115E22 /* SurveyViewController.swift */, + 8D8A9B772A2975E600115E22 /* Survey.storyboard */, + ); + path = Survey; + sourceTree = ""; + }; + 8D8A9B7D2A297B4C00115E22 /* Cell */ = { + isa = PBXGroup; + children = ( + 8D8A9B842A2A21AB00115E22 /* SurveyTableViewCell.swift */, + 8D8A9B822A2A217000115E22 /* SurveyTableViewCell.xib */, + ); + path = Cell; + sourceTree = ""; + }; + 8D8A9B862A2A31EC00115E22 /* Components */ = { + isa = PBXGroup; + children = ( + ); + path = Components; + sourceTree = ""; + }; 9311609426B40EE0007FCA52 /* Buttons */ = { isa = PBXGroup; children = ( @@ -809,11 +848,13 @@ 8D75253229DC601F00361CC1 /* MessageTableViewCell.xib in Resources */, 938294B227F9A7AB00888E42 /* Settings.storyboard in Resources */, 93EC8C3D24AA52F000946C0C /* Home.storyboard in Resources */, + 8D8A9B782A2975E600115E22 /* Survey.storyboard in Resources */, 931C37BA249BAD0900EE24DC /* Selfie.storyboard in Resources */, 93E82F63246C41B000006C91 /* Localizable.strings in Resources */, 8D75252129DB6E7E00361CC1 /* ChatListTableViewCell.xib in Resources */, 93989CFD246EABFA00A1B21F /* Terms.storyboard in Resources */, 8D75251929DB61C000361CC1 /* ChatList.storyboard in Resources */, + 8D8A9B832A2A217000115E22 /* SurveyTableViewCell.xib in Resources */, 93989D01246EB20D00A1B21F /* Terms.html in Resources */, 937D78C325C70E45007A7A37 /* Montserrat-Bold.ttf in Resources */, 93E82F52246C35C700006C91 /* SignIn.storyboard in Resources */, @@ -974,6 +1015,7 @@ 93E82F4C246C262900006C91 /* SignInCoordinator.swift in Sources */, 93E18966245C95F200217F21 /* StoryboardSegues.swift in Sources */, 93989CF9246EA35500A1B21F /* SignUpViewModel.swift in Sources */, + 8D8A9B852A2A21AB00115E22 /* SurveyTableViewCell.swift in Sources */, 8D75251729DB61A000361CC1 /* ChatListViewController.swift in Sources */, 93EC8CA824B28B5D00946C0C /* UITextField+Extensions.swift in Sources */, 93989D08246ED51C00A1B21F /* SignInTextField.swift in Sources */, @@ -987,6 +1029,7 @@ 93E82F4F246C27E100006C91 /* SignInViewController.swift in Sources */, 937D78D425C76450007A7A37 /* KeyboardObserving.swift in Sources */, 93EC8C3B24AA52E900946C0C /* HomeViewModel.swift in Sources */, + 8D8A9B7C2A29762900115E22 /* SurveyViewController.swift in Sources */, 931C37B8249BACFA00EE24DC /* SelfieViewModel.swift in Sources */, 8D75252B29DB87AA00361CC1 /* MessagesViewModel.swift in Sources */, 937FC5DC2486F1530018B247 /* SelfieViewController.swift in Sources */, @@ -998,6 +1041,7 @@ 93E82F66246C439100006C91 /* Strings.swift in Sources */, 93989CFB246EABF100A1B21F /* TermsViewController.swift in Sources */, 8D75253129DC601F00361CC1 /* MessageTableViewCell.swift in Sources */, + 8D8A9B7A2A29761C00115E22 /* SurveyViewModel.swift in Sources */, 8D8A9B732A278A2E00115E22 /* AnnounceViewModel.swift in Sources */, 8D75252029DB6E7E00361CC1 /* ChatListTableViewCell.swift in Sources */, 93EC8CAB24BBA9E100946C0C /* AddTreeViewController.swift in Sources */, diff --git a/TreeTracker/SwiftGen/StoryboardScenes.swift b/TreeTracker/SwiftGen/StoryboardScenes.swift index d41c4cd2..d3e8dd21 100644 --- a/TreeTracker/SwiftGen/StoryboardScenes.swift +++ b/TreeTracker/SwiftGen/StoryboardScenes.swift @@ -78,6 +78,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: SignUp.self) } + internal enum Survey: StoryboardType { + internal static let storyboardName = "Survey" + + internal static let initialScene = InitialSceneType(storyboard: Survey.self) + } internal enum Terms: StoryboardType { internal static let storyboardName = "Terms" diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index 380cd715..3b8041b8 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -82,6 +82,13 @@ private extension HomeCoordinator { ) } + func showSurvey(chat: ChatListViewModel.Chat) { + configuration.navigationController.pushViewController( + surveyViewController(chat: chat), + animated: true + ) + } + func showNotes(note: String?) { configuration.navigationController.pushViewController( notesViewController(note: note), @@ -177,7 +184,7 @@ private extension HomeCoordinator { }() return viewcontroller } - + func announceViewController(chat: ChatListViewModel.Chat) -> UIViewController { let viewController = StoryboardScene.Announce.initialScene.instantiate() viewController.viewModel = { @@ -189,6 +196,17 @@ private extension HomeCoordinator { return viewController } + func surveyViewController(chat: ChatListViewModel.Chat) -> UIViewController { + let viewController = StoryboardScene.Survey.initialScene.instantiate() + viewController.viewModel = { + let viewModel = SurveyViewModel( + chat: chat + ) + return viewModel + }() + return viewController + } + func notesViewController(note: String?) -> UIViewController { let viewController = StoryboardScene.Notes.initialScene.instantiate() viewController.viewModel = { @@ -317,4 +335,8 @@ extension HomeCoordinator: ChatListViewModelCoordinatorDelegate { showAnnounce(chat: chat) } + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectSurvey chat: ChatListViewModel.Chat) { + showSurvey(chat: chat) + } + } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index 7b1d1fcb..8ad03306 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -13,6 +13,7 @@ import UIKit protocol ChatListViewModelCoordinatorDelegate: AnyObject { func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectMessages planter: Planter) func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectAnnounce chat: ChatListViewModel.Chat) + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didSelectSurvey chat: ChatListViewModel.Chat) } protocol ChatListViewModelViewDelegate: AnyObject { @@ -135,7 +136,7 @@ extension ChatListViewModel { case .announce: coordinatorDelegate?.chatListViewModel(self, didSelectAnnounce: selectedChat) case .survey, .surveyResponse: - break + coordinatorDelegate?.chatListViewModel(self, didSelectSurvey: selectedChat) } } diff --git a/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift b/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift new file mode 100644 index 00000000..a3caf14c --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift @@ -0,0 +1,42 @@ +// +// SurveyTableViewCell.swift +// TreeTracker +// +// Created by Frédéric Helfer on 02/06/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import UIKit + +class SurveyTableViewCell: UITableViewCell { + + @IBOutlet private var buttonView: UIView! { + didSet { + buttonView.backgroundColor = Asset.Colors.backgroundGreen.color + buttonView.layer.cornerRadius = 15 + buttonView.clipsToBounds = true + } + } + + @IBOutlet private var buttonTitle: UILabel! { + didSet { + buttonTitle.font = FontFamily.Montserrat.semiBold.font(size: 18) + buttonTitle.textColor = Asset.Colors.grayDark.color + } + } + + static let identifier: String = "SurveyTableViewCell" + static func nib() -> UINib { + UINib(nibName: identifier, bundle: nil) + } + +} + +// MARK: - Public Action +extension SurveyTableViewCell { + + func setupCell() { + buttonTitle.text = "Choise A" + } + +} diff --git a/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.xib b/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.xib new file mode 100644 index 00000000..23e7c827 --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.xib @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TreeTracker/UI/Views/Messaging/Survey/Survey.storyboard b/TreeTracker/UI/Views/Messaging/Survey/Survey.storyboard new file mode 100644 index 00000000..08db3133 --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Survey/Survey.storyboard @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift new file mode 100644 index 00000000..9126e807 --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift @@ -0,0 +1,80 @@ +// +// SurveyViewController.swift +// TreeTracker +// +// Created by Frédéric Helfer on 01/06/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import UIKit + +class SurveyViewController: UIViewController { + + @IBOutlet private var questionLabel: UILabel! { + didSet { + questionLabel.font = FontFamily.Montserrat.bold.font(size: 28) + questionLabel.textColor = Asset.Colors.grayDark.color + questionLabel.numberOfLines = 0 + questionLabel.textAlignment = .center + questionLabel.text = "Do you want more questions?" // remove + } + } + + @IBOutlet private var tableView: UITableView! { + didSet { + tableView.dataSource = self + tableView.delegate = self + tableView.separatorStyle = .none + tableView.register(SurveyTableViewCell.nib(), forCellReuseIdentifier: SurveyTableViewCell.identifier) + } + } + + @IBOutlet private var actionButton: UIButton! { + didSet { + actionButton.backgroundColor = Asset.Colors.primaryGreen.color + actionButton.layer.cornerRadius = 15 + actionButton.tintColor = Asset.Colors.grayDark.color + } + } + + var viewModel: SurveyViewModel? { + didSet { + viewModel?.viewDelegate = self + } + } + + // MARK: - LifeCycle + override func viewDidLoad() { + super.viewDidLoad() + } + +} + +// MARK: - UITableViewDataSource +extension SurveyViewController: UITableViewDataSource { + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + 3 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: SurveyTableViewCell.identifier, for: indexPath) as? SurveyTableViewCell + cell?.setupCell() + return cell ?? UITableViewCell() + } + +} + +// MARK: - UITableViewDelegate +extension SurveyViewController: UITableViewDelegate { + + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + 90 + } +} + +// MARK: - SurveyViewModelViewDelegate +extension SurveyViewController: SurveyViewModelViewDelegate { + +} diff --git a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift new file mode 100644 index 00000000..c7927a69 --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift @@ -0,0 +1,31 @@ +// +// SurveyViewModel.swift +// TreeTracker +// +// Created by Frédéric Helfer on 01/06/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import Foundation + +protocol SurveyViewModelCoordinatorDelegate: AnyObject { + +} + +protocol SurveyViewModelViewDelegate: AnyObject { + +} + +class SurveyViewModel { + + weak var coordinatorDelegate: SurveyViewModelCoordinatorDelegate? + weak var viewDelegate: SurveyViewModelViewDelegate? + + private let chat: ChatListViewModel.Chat + + init(chat: ChatListViewModel.Chat) { + self.chat = chat + } + +} + From 37b7816ad8dfae48f05d7db77fe4ab103264a62a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 2 Jun 2023 15:09:29 -0300 Subject: [PATCH 44/66] add custom button to survey screen --- TreeTracker.xcodeproj/project.pbxproj | 4 + TreeTracker/Resources/Localizable.strings | 5 + TreeTracker/SwiftGen/Strings.swift | 12 ++ .../Survey/Cell/SurveyTableViewCell.swift | 2 +- .../Survey/Cell/SurveyTableViewCell.xib | 10 +- .../Survey/Components/ActionButton.swift | 109 ++++++++++++++++++ .../Views/Messaging/Survey/Survey.storyboard | 14 +-- .../Survey/SurveyViewController.swift | 8 +- 8 files changed, 144 insertions(+), 20 deletions(-) create mode 100644 TreeTracker/UI/Views/Messaging/Survey/Components/ActionButton.swift diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index c541399b..87b78504 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 8D8A9B7C2A29762900115E22 /* SurveyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8A9B7B2A29762900115E22 /* SurveyViewController.swift */; }; 8D8A9B832A2A217000115E22 /* SurveyTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8D8A9B822A2A217000115E22 /* SurveyTableViewCell.xib */; }; 8D8A9B852A2A21AB00115E22 /* SurveyTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8A9B842A2A21AB00115E22 /* SurveyTableViewCell.swift */; }; + 8D8A9B8A2A2A356600115E22 /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8A9B892A2A356600115E22 /* ActionButton.swift */; }; 9311609126B40E5E007FCA52 /* DestructiveButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609026B40E5E007FCA52 /* DestructiveButton.swift */; }; 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9311609A26B4108A007FCA52 /* RoundedButton.swift */; }; 931C37B8249BACFA00EE24DC /* SelfieViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */; }; @@ -136,6 +137,7 @@ 8D8A9B7B2A29762900115E22 /* SurveyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveyViewController.swift; sourceTree = ""; }; 8D8A9B822A2A217000115E22 /* SurveyTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SurveyTableViewCell.xib; sourceTree = ""; }; 8D8A9B842A2A21AB00115E22 /* SurveyTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveyTableViewCell.swift; sourceTree = ""; }; + 8D8A9B892A2A356600115E22 /* ActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = ""; }; 9311609026B40E5E007FCA52 /* DestructiveButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestructiveButton.swift; sourceTree = ""; }; 9311609A26B4108A007FCA52 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; 931C37B7249BACFA00EE24DC /* SelfieViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfieViewModel.swift; sourceTree = ""; }; @@ -329,6 +331,7 @@ 8D8A9B862A2A31EC00115E22 /* Components */ = { isa = PBXGroup; children = ( + 8D8A9B892A2A356600115E22 /* ActionButton.swift */, ); path = Components; sourceTree = ""; @@ -1053,6 +1056,7 @@ 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */, 93EC8C3424AA4AE100946C0C /* HomeCoordinator.swift in Sources */, 938294AE27F9A79900888E42 /* SettingsViewModel.swift in Sources */, + 8D8A9B8A2A2A356600115E22 /* ActionButton.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/TreeTracker/Resources/Localizable.strings b/TreeTracker/Resources/Localizable.strings index 386815ee..8d471a69 100644 --- a/TreeTracker/Resources/Localizable.strings +++ b/TreeTracker/Resources/Localizable.strings @@ -110,3 +110,8 @@ //Announce Screen "Announce.Title" = "Announce"; + +//Survey Screen +"Survey.Title" = "Survey"; +"Survey.ActionButton.Title.Next" = "Next"; +"Survey.ActionButton.Title.Finish" = "Finish"; diff --git a/TreeTracker/SwiftGen/Strings.swift b/TreeTracker/SwiftGen/Strings.swift index 5be1d195..aa11e542 100644 --- a/TreeTracker/SwiftGen/Strings.swift +++ b/TreeTracker/SwiftGen/Strings.swift @@ -276,6 +276,18 @@ internal enum L10n { } } } + internal enum Survey { + /// Survey + internal static let title = L10n.tr("Localizable", "Survey.Title", fallback: "Survey") + internal enum ActionButton { + internal enum Title { + /// Finish + internal static let finish = L10n.tr("Localizable", "Survey.ActionButton.Title.Finish", fallback: "Finish") + /// Next + internal static let next = L10n.tr("Localizable", "Survey.ActionButton.Title.Next", fallback: "Next") + } + } + } internal enum Terms { /// Terms & Conditions internal static let title = L10n.tr("Localizable", "Terms.Title", fallback: "Terms & Conditions") diff --git a/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift b/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift index a3caf14c..35c34e69 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift @@ -13,7 +13,7 @@ class SurveyTableViewCell: UITableViewCell { @IBOutlet private var buttonView: UIView! { didSet { buttonView.backgroundColor = Asset.Colors.backgroundGreen.color - buttonView.layer.cornerRadius = 15 + buttonView.layer.cornerRadius = 5.0 buttonView.clipsToBounds = true } } diff --git a/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.xib b/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.xib index 23e7c827..852e4525 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.xib +++ b/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.xib @@ -9,18 +9,18 @@ - - + + - + - + - + diff --git a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift index 6ab075e1..d9371732 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift @@ -46,6 +46,14 @@ class SurveyViewController: UIViewController { super.viewDidLoad() viewModel?.updateView() } +} + +// MARK: - Button Action +private extension SurveyViewController { + + @IBAction func nextButtonPressed() { + viewModel?.actionButtonPressed() + } } @@ -88,7 +96,7 @@ extension SurveyViewController: SurveyViewModelViewDelegate { if survey.showQuestionNum == survey.questions.count - 1 { actionButton.buttonStyle = .finish } - + if survey.surveyResponse.indices.contains(survey.showQuestionNum) { actionButton.isEnabled = true } diff --git a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift index f4758b30..4ede76db 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift @@ -7,9 +7,11 @@ // import Foundation +import Treetracker_Core protocol SurveyViewModelCoordinatorDelegate: AnyObject { - + func surveyViewModel(_ surveyViewModel: SurveyViewModel, showNextQuestion survey: SurveyViewModel.Survey, planter: Planter) + func surveyViewModel(_ surveyViewModel: SurveyViewModel, didFinishSurvey survey: SurveyViewModel.Survey) } protocol SurveyViewModelViewDelegate: AnyObject { @@ -21,11 +23,15 @@ class SurveyViewModel { weak var coordinatorDelegate: SurveyViewModelCoordinatorDelegate? weak var viewDelegate: SurveyViewModelViewDelegate? + private let planter: Planter private var survey: SurveyViewModel.Survey + private let messagingService: MessagingService let questionNumber: Int - init(survey: SurveyViewModel.Survey) { + init(planter: Planter, survey: SurveyViewModel.Survey, messagingService: MessagingService) { + self.planter = planter self.survey = survey + self.messagingService = messagingService self.questionNumber = survey.showQuestionNum } @@ -68,6 +74,22 @@ class SurveyViewModel { } +// MARK: - Navigation +extension SurveyViewModel { + + func actionButtonPressed() { + survey.showQuestionNum = questionNumber + 1 + + if survey.questions.indices.contains(survey.showQuestionNum) { + coordinatorDelegate?.surveyViewModel(self, showNextQuestion: survey, planter: planter) + } else { + messagingService.createSurveyResponse(planter: planter, surveyId: survey.surveyId, surveyResponse: survey.surveyResponse) + coordinatorDelegate?.surveyViewModel(self, didFinishSurvey: survey) + } + } + +} + // MARK: - Nested Types extension SurveyViewModel { From 3d3a7bf166712327468607d467f1fcd305871afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 9 Jun 2023 16:27:41 -0300 Subject: [PATCH 47/66] adjust some logic to the survey flow --- TreeTracker/Resources/Localizable.strings | 3 +- TreeTracker/SwiftGen/Strings.swift | 8 +++-- .../Announce/AnnounceViewController.swift | 1 + .../ChatList/ChatListViewModel.swift | 2 +- .../Survey/Components/ActionButton.swift | 36 +++++++++++-------- .../Messaging/Survey/SurveyViewModel.swift | 20 +++++++---- 6 files changed, 45 insertions(+), 25 deletions(-) diff --git a/TreeTracker/Resources/Localizable.strings b/TreeTracker/Resources/Localizable.strings index 8d471a69..55b21317 100644 --- a/TreeTracker/Resources/Localizable.strings +++ b/TreeTracker/Resources/Localizable.strings @@ -112,6 +112,7 @@ "Announce.Title" = "Announce"; //Survey Screen -"Survey.Title" = "Survey"; +"Survey.Title.Question" = "Question"; +"Survey.Title.Response" = "Response"; "Survey.ActionButton.Title.Next" = "Next"; "Survey.ActionButton.Title.Finish" = "Finish"; diff --git a/TreeTracker/SwiftGen/Strings.swift b/TreeTracker/SwiftGen/Strings.swift index aa11e542..b8db6b99 100644 --- a/TreeTracker/SwiftGen/Strings.swift +++ b/TreeTracker/SwiftGen/Strings.swift @@ -277,8 +277,6 @@ internal enum L10n { } } internal enum Survey { - /// Survey - internal static let title = L10n.tr("Localizable", "Survey.Title", fallback: "Survey") internal enum ActionButton { internal enum Title { /// Finish @@ -287,6 +285,12 @@ internal enum L10n { internal static let next = L10n.tr("Localizable", "Survey.ActionButton.Title.Next", fallback: "Next") } } + internal enum Title { + /// Question + internal static let question = L10n.tr("Localizable", "Survey.Title.Question", fallback: "Question") + /// Response + internal static let response = L10n.tr("Localizable", "Survey.Title.Response", fallback: "Response") + } } internal enum Terms { /// Terms & Conditions diff --git a/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift b/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift index c09ade75..6d38febf 100644 --- a/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift @@ -14,6 +14,7 @@ class AnnounceViewController: UIViewController { didSet { titleLabel.font = FontFamily.Montserrat.bold.font(size: 28) titleLabel.numberOfLines = 0 + titleLabel.textAlignment = .center titleLabel.textColor = Asset.Colors.grayDark.color } } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index c12909a3..701ce4c5 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -159,7 +159,7 @@ extension ChatListViewModel { choices: question.choices ?? []) }), surveyResponse: message.surveyResponse ?? [], - showQuestionNum: 0, + showQuestionNum: -1, response: message.survey?.response ?? false ) diff --git a/TreeTracker/UI/Views/Messaging/Survey/Components/ActionButton.swift b/TreeTracker/UI/Views/Messaging/Survey/Components/ActionButton.swift index 97c62e74..845c41e8 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/Components/ActionButton.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/Components/ActionButton.swift @@ -31,6 +31,12 @@ class ActionButton: UIButton { } } + override var isHighlighted: Bool { + didSet { + updateState() + } + } + var buttonStyle: ButtonStyle = .next { didSet { updateButtonStyle() @@ -50,15 +56,26 @@ private extension ActionButton { } func updateState() { - if isEnabled { - backgroundColor = Asset.Colors.primaryGreen.color - tintColor = .white - } else { + if !isEnabled { backgroundColor = Asset.Colors.grayLight.color.withAlphaComponent(0.2) tintColor = Asset.Colors.grayLight.color + } else if isHighlighted { + backgroundColor = Asset.Colors.secondaryGreen.color + tintColor = .white + } else { + backgroundColor = Asset.Colors.primaryGreen.color + tintColor = .white } } + @objc private func handleTouchDown() { + updateState() + } + + @objc private func handleTouchUp() { + updateState() + } + func updateButtonStyle() { switch buttonStyle { case .next: @@ -73,16 +90,12 @@ private extension ActionButton { func nextQuestionAttributedTitle(withAtributes attributes: [NSAttributedString.Key: Any]) -> NSAttributedString { let attributedString = NSMutableAttributedString() attributedString.append(NSAttributedString(string: L10n.Survey.ActionButton.Title.next, attributes: attributes)) -// attributedString.append(NSAttributedString(attachment: iconTextAttachment)) -// attributedString.append(NSAttributedString(string: " ")) return attributedString } func finishSurveyAttributedTitle(withAtributes attributes: [NSAttributedString.Key: Any]) -> NSAttributedString { let attributedString = NSMutableAttributedString() attributedString.append(NSAttributedString(string: L10n.Survey.ActionButton.Title.finish, attributes: attributes)) -// attributedString.append(NSAttributedString(attachment: iconTextAttachment)) -// attributedString.append(NSAttributedString(string: " ")) return attributedString } @@ -99,11 +112,4 @@ private extension ActionButton { .foregroundColor: Asset.Colors.grayLight.color.withAlphaComponent(0.5) ] } - -// var iconTextAttachment: NSTextAttachment { -// let textAttachment = NSTextAttachment() -// textAttachment.image = Asset.Assets.upload.image -// textAttachment.bounds = CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: 50.0, height: 50)) -// return textAttachment -// } } diff --git a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift index 4ede76db..45093e0b 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift @@ -26,17 +26,24 @@ class SurveyViewModel { private let planter: Planter private var survey: SurveyViewModel.Survey private let messagingService: MessagingService - let questionNumber: Int init(planter: Planter, survey: SurveyViewModel.Survey, messagingService: MessagingService) { self.planter = planter self.survey = survey self.messagingService = messagingService + updateQuestion() + } + + var questionNumber: Int = 0 + + private func updateQuestion() { + survey.showQuestionNum += 1 self.questionNumber = survey.showQuestionNum } var title: String { - return "Survey \(questionNumber + 1)/\(survey.questions.count)" + let title = survey.response ? L10n.Survey.Title.response : L10n.Survey.Title.question + return "\(title) \(questionNumber + 1)/\(survey.questions.count)" } var numberOfRowsInSection: Int { @@ -78,12 +85,13 @@ class SurveyViewModel { extension SurveyViewModel { func actionButtonPressed() { - survey.showQuestionNum = questionNumber + 1 - - if survey.questions.indices.contains(survey.showQuestionNum) { + if survey.questions.indices.contains(survey.showQuestionNum + 1) { coordinatorDelegate?.surveyViewModel(self, showNextQuestion: survey, planter: planter) } else { - messagingService.createSurveyResponse(planter: planter, surveyId: survey.surveyId, surveyResponse: survey.surveyResponse) + + if survey.response == false { + messagingService.createSurveyResponse(planter: planter, surveyId: survey.surveyId, surveyResponse: survey.surveyResponse) + } coordinatorDelegate?.surveyViewModel(self, didFinishSurvey: survey) } } From a9bd2f379e38d84f5dae7f7f0cf73cba573f9bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 9 Jun 2023 17:57:51 -0300 Subject: [PATCH 48/66] add scrollView to announce screen --- .../Messaging/Announce/Announce.storyboard | 64 +++++++++++++------ 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/Announce/Announce.storyboard b/TreeTracker/UI/Views/Messaging/Announce/Announce.storyboard index 1f0a891e..9fa36750 100644 --- a/TreeTracker/UI/Views/Messaging/Announce/Announce.storyboard +++ b/TreeTracker/UI/Views/Messaging/Announce/Announce.storyboard @@ -17,30 +17,56 @@ - - + + - - + + + + + + + + + + + + + + + + + - + + + + + + + + + + + - - - + + + + @@ -50,7 +76,7 @@ - + From 65f23fe1eaac98864591d73a9355ded374943116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Mon, 12 Jun 2023 15:43:56 -0300 Subject: [PATCH 49/66] get unread messages count on home screen --- TreeTracker/UI/Views/Home/HomeViewModel.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/TreeTracker/UI/Views/Home/HomeViewModel.swift b/TreeTracker/UI/Views/Home/HomeViewModel.swift index 4210efd4..f72bdf56 100644 --- a/TreeTracker/UI/Views/Home/HomeViewModel.swift +++ b/TreeTracker/UI/Views/Home/HomeViewModel.swift @@ -114,10 +114,7 @@ extension HomeViewModel { func fetchUnreadMessagesCount() { messagingService.syncMessages(for: planter) -// messagingService.getUnreadMessagesCount(for: planter) { [weak self] count in -// guard let self else { return } -// unreadMessagesCount = count -// } + unreadMessagesCount = messagingService.getUnreadMessagesCount(for: planter) } } From 1157540df2cc9de9f39851581ccb3356524cf9a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Tue, 13 Jun 2023 21:10:07 -0300 Subject: [PATCH 50/66] Change the logic to present a survey view controller --- .../UI/Navigation/Home/HomeCoordinator.swift | 13 +++---- .../ChatList/Cell/ChatListTableViewCell.swift | 2 +- .../ChatList/ChatListViewController.swift | 8 +++-- .../ChatList/ChatListViewModel.swift | 13 ++----- .../Survey/SurveyViewController.swift | 6 ++-- .../Messaging/Survey/SurveyViewModel.swift | 34 ++++++++----------- 6 files changed, 32 insertions(+), 44 deletions(-) diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index 5f47a6c6..e048f0ea 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -82,9 +82,9 @@ private extension HomeCoordinator { ) } - func showSurvey(planter: Planter, survey: SurveyViewModel.Survey) { + func showSurvey(planter: Planter, survey: SurveyViewModel.Survey, index: Int = 0) { configuration.navigationController.pushViewController( - surveyViewController(planter: planter, survey: survey), + surveyViewController(planter: planter, survey: survey, index: index), animated: true ) } @@ -196,13 +196,14 @@ private extension HomeCoordinator { return viewController } - func surveyViewController(planter: Planter, survey: SurveyViewModel.Survey) -> UIViewController { + func surveyViewController(planter: Planter, survey: SurveyViewModel.Survey, index: Int) -> UIViewController { let viewController = StoryboardScene.Survey.initialScene.instantiate() viewController.viewModel = { let viewModel = SurveyViewModel( planter: planter, survey: survey, - messagingService: self.treetrackerSDK.messagingService + messagingService: self.treetrackerSDK.messagingService, + index: index ) viewModel.coordinatorDelegate = self return viewModel @@ -348,8 +349,8 @@ extension HomeCoordinator: ChatListViewModelCoordinatorDelegate { // MARK: - SurveyViewModelCoordinatorDelegate extension HomeCoordinator: SurveyViewModelCoordinatorDelegate { - func surveyViewModel(_ surveyViewModel: SurveyViewModel, showNextQuestion survey: SurveyViewModel.Survey, planter: Planter) { - showSurvey(planter: planter, survey: survey) + func surveyViewModel(_ surveyViewModel: SurveyViewModel, showNextQuestion survey: SurveyViewModel.Survey, planter: Planter, index: Int) { + showSurvey(planter: planter, survey: survey, index: index) } func surveyViewModel(_ surveyViewModel: SurveyViewModel, didFinishSurvey survey: SurveyViewModel.Survey) { diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift index 87fdb8fe..121269a1 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift @@ -63,7 +63,7 @@ extension ChatListTableViewCell { chatImage.image = Asset.Assets.trees.image chatImage.image = data.image chatTitle.text = data.title - + if data.unreadCount == 0 { chatAlertCountView.isHidden = true chatAlertCountLabel.isHidden = true diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index f10ad153..e25d1d9f 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -58,7 +58,6 @@ class ChatListViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel?.fetchProfileImage() - viewModel?.fetchMessages() setupNavBarImage() } @@ -70,6 +69,7 @@ class ChatListViewController: UIViewController { backgroundColor: Asset.Colors.backgroundGreen.color ) showImage(true) + viewModel?.fetchMessages() } override func viewWillDisappear(_ animated: Bool) { @@ -88,8 +88,10 @@ extension ChatListViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: ChatListTableViewCell.identifier, for: indexPath) as? ChatListTableViewCell - let chat = viewModel?.cellForRowAt(indexPath: indexPath) - cell?.setupCell(data: chat!) // remove force unwrap + if let chat = viewModel?.cellForRowAt(indexPath: indexPath) { + cell?.setupCell(data: chat) + } + cell?.selectionStyle = .none return cell ?? UITableViewCell() } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index 701ce4c5..32807581 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -80,17 +80,9 @@ extension ChatListViewModel { chatList.insert(newChat, at: 0) } - case .announce: - let newChat = Chat( - title: message.subject ?? "Announce!", - type: messageType, - messages: [message] - ) - chatList.append(newChat) - - case .survey, .surveyResponse: + default: let newChat = Chat( - title: message.subject ?? "Survey!", + title: message.subject ?? "", type: messageType, messages: [message] ) @@ -159,7 +151,6 @@ extension ChatListViewModel { choices: question.choices ?? []) }), surveyResponse: message.surveyResponse ?? [], - showQuestionNum: -1, response: message.survey?.response ?? false ) diff --git a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift index d9371732..dd6217c7 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift @@ -91,13 +91,13 @@ extension SurveyViewController: UITableViewDelegate { extension SurveyViewController: SurveyViewModelViewDelegate { func surveyViewModel(_ surveyViewModel: SurveyViewModel, updateViewWith survey: SurveyViewModel.Survey) { - questionLabel.text = survey.questions[survey.showQuestionNum].prompt + questionLabel.text = survey.questions[surveyViewModel.index].prompt - if survey.showQuestionNum == survey.questions.count - 1 { + if surveyViewModel.index == survey.questions.count - 1 { actionButton.buttonStyle = .finish } - if survey.surveyResponse.indices.contains(survey.showQuestionNum) { + if survey.surveyResponse.indices.contains(surveyViewModel.index) { actionButton.isEnabled = true } diff --git a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift index 45093e0b..3e4e4c9f 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift @@ -10,7 +10,7 @@ import Foundation import Treetracker_Core protocol SurveyViewModelCoordinatorDelegate: AnyObject { - func surveyViewModel(_ surveyViewModel: SurveyViewModel, showNextQuestion survey: SurveyViewModel.Survey, planter: Planter) + func surveyViewModel(_ surveyViewModel: SurveyViewModel, showNextQuestion survey: SurveyViewModel.Survey, planter: Planter, index: Int) func surveyViewModel(_ surveyViewModel: SurveyViewModel, didFinishSurvey survey: SurveyViewModel.Survey) } @@ -27,34 +27,29 @@ class SurveyViewModel { private var survey: SurveyViewModel.Survey private let messagingService: MessagingService - init(planter: Planter, survey: SurveyViewModel.Survey, messagingService: MessagingService) { + init(planter: Planter, survey: SurveyViewModel.Survey, messagingService: MessagingService, index: Int) { self.planter = planter self.survey = survey self.messagingService = messagingService - updateQuestion() + self.index = index } - var questionNumber: Int = 0 - - private func updateQuestion() { - survey.showQuestionNum += 1 - self.questionNumber = survey.showQuestionNum - } + var index: Int var title: String { let title = survey.response ? L10n.Survey.Title.response : L10n.Survey.Title.question - return "\(title) \(questionNumber + 1)/\(survey.questions.count)" + return "\(title) \(index + 1)/\(survey.questions.count)" } var numberOfRowsInSection: Int { - return survey.questions[questionNumber].choices.count + return survey.questions[index].choices.count } func getChoiceForRowAt(indexPath: IndexPath) -> Choice { - let choiceText = survey.questions[questionNumber].choices[indexPath.row] + let choiceText = survey.questions[index].choices[indexPath.row] - if survey.surveyResponse.indices.contains(questionNumber) { - if survey.surveyResponse[questionNumber] == choiceText { + if survey.surveyResponse.indices.contains(index) { + if survey.surveyResponse[index] == choiceText { return Choice(text: choiceText, isSelected: true) } } @@ -65,9 +60,9 @@ class SurveyViewModel { func didSelectRowAt(indexPath: IndexPath) { guard survey.response == false else { return } - let selectedChoice = survey.questions[questionNumber].choices[indexPath.row] - if survey.surveyResponse.indices.contains(questionNumber) { - survey.surveyResponse[questionNumber] = selectedChoice + let selectedChoice = survey.questions[index].choices[indexPath.row] + if survey.surveyResponse.indices.contains(index) { + survey.surveyResponse[index] = selectedChoice } else { survey.surveyResponse.append(selectedChoice) } @@ -85,8 +80,8 @@ class SurveyViewModel { extension SurveyViewModel { func actionButtonPressed() { - if survey.questions.indices.contains(survey.showQuestionNum + 1) { - coordinatorDelegate?.surveyViewModel(self, showNextQuestion: survey, planter: planter) + if index < survey.questions.count - 1 { + coordinatorDelegate?.surveyViewModel(self, showNextQuestion: survey, planter: planter, index: index + 1) } else { if survey.response == false { @@ -106,7 +101,6 @@ extension SurveyViewModel { let title: String let questions: [Question] var surveyResponse: [String] - var showQuestionNum: Int var response: Bool } From 8b1b70c856a1417853730c4adc2868b88383e62d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 14 Jun 2023 09:35:13 -0300 Subject: [PATCH 51/66] Add notification center to home screen. Updates unread message count when finish fetching new messages. --- .../UI/Navigation/Home/HomeCoordinator.swift | 7 +++++-- .../UI/Views/Home/HomeViewController.swift | 15 +++++++++++++++ TreeTracker/UI/Views/Home/HomeViewModel.swift | 5 ++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index e048f0ea..68d282f8 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -354,7 +354,10 @@ extension HomeCoordinator: SurveyViewModelCoordinatorDelegate { } func surveyViewModel(_ surveyViewModel: SurveyViewModel, didFinishSurvey survey: SurveyViewModel.Survey) { - let chatListViewController = configuration.navigationController.viewControllers[1] - configuration.navigationController.popToViewController(chatListViewController, animated: true) + if let chatListViewController = configuration.navigationController.viewControllers.first(where: { $0 is ChatListViewController }) as? ChatListViewController { + configuration.navigationController.popToViewController(chatListViewController, animated: true) + } else { + configuration.navigationController.popToRootViewController(animated: true) + } } } diff --git a/TreeTracker/UI/Views/Home/HomeViewController.swift b/TreeTracker/UI/Views/Home/HomeViewController.swift index 76ceff2e..f15725dd 100644 --- a/TreeTracker/UI/Views/Home/HomeViewController.swift +++ b/TreeTracker/UI/Views/Home/HomeViewController.swift @@ -147,6 +147,7 @@ class HomeViewController: UIViewController, AlertPresenting { override func viewDidLoad() { super.viewDidLoad() addSettingsButton() + configureNotificationCenter() viewModel?.startMonitoringTrees() } @@ -154,6 +155,7 @@ class HomeViewController: UIViewController, AlertPresenting { super.viewWillAppear(animated) navigationController?.navigationBar.setupNavigationAppearance() viewModel?.fetchProfileData() + viewModel?.syncMessages() viewModel?.fetchUnreadMessagesCount() } @@ -188,6 +190,19 @@ private extension HomeViewController { action: #selector(settingsButtonPressed) )] } + + func configureNotificationCenter() { + NotificationCenter.default.addObserver(self, + selector: #selector(fetchUnreadMessagesCount), + name: NSNotification.Name("didFinishFetchingMessages"), + object: nil + ) + } + + @objc + func fetchUnreadMessagesCount() { + viewModel?.fetchUnreadMessagesCount() + } } // MARK: - Button Actions diff --git a/TreeTracker/UI/Views/Home/HomeViewModel.swift b/TreeTracker/UI/Views/Home/HomeViewModel.swift index f72bdf56..285dc8c0 100644 --- a/TreeTracker/UI/Views/Home/HomeViewModel.swift +++ b/TreeTracker/UI/Views/Home/HomeViewModel.swift @@ -113,10 +113,13 @@ extension HomeViewModel { extension HomeViewModel { func fetchUnreadMessagesCount() { - messagingService.syncMessages(for: planter) unreadMessagesCount = messagingService.getUnreadMessagesCount(for: planter) } + func syncMessages() { + messagingService.syncMessages(for: planter) + } + } // MARK: - Navigation From 04e78e4611a27594ac9906d667c3e074bcd50ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 14 Jun 2023 10:51:02 -0300 Subject: [PATCH 52/66] add darker backgroundcolor when button is highlighted --- .../Views/Home/Components/AddTreeButton.swift | 14 ++++++++++++++ .../Views/Home/Components/MessagingButton.swift | 14 ++++++++++++++ .../Views/Home/Components/UploadsButton.swift | 17 +++++++++++++---- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/TreeTracker/UI/Views/Home/Components/AddTreeButton.swift b/TreeTracker/UI/Views/Home/Components/AddTreeButton.swift index b0fcaa58..7ec0a782 100644 --- a/TreeTracker/UI/Views/Home/Components/AddTreeButton.swift +++ b/TreeTracker/UI/Views/Home/Components/AddTreeButton.swift @@ -19,6 +19,12 @@ class AddTreeButton: UIButton { super.init(coder: coder) commonInit() } + + override var isHighlighted: Bool { + didSet { + updateState() + } + } } // MARK: - Private @@ -32,6 +38,14 @@ private extension AddTreeButton { setAttributedTitle(attributedTitle, for: .normal) } + func updateState() { + if isHighlighted { + backgroundColor = Asset.Colors.secondaryGreen.color + } else { + backgroundColor = Asset.Colors.primaryGreen.color + } + } + var attributedTitle: NSAttributedString { let attributedString = NSMutableAttributedString() attributedString.append(NSAttributedString(attachment: treeIconTextAttachment)) diff --git a/TreeTracker/UI/Views/Home/Components/MessagingButton.swift b/TreeTracker/UI/Views/Home/Components/MessagingButton.swift index 9a6b8397..9ec355e0 100644 --- a/TreeTracker/UI/Views/Home/Components/MessagingButton.swift +++ b/TreeTracker/UI/Views/Home/Components/MessagingButton.swift @@ -19,6 +19,12 @@ class MessagingButton: UIButton { super.init(coder: coder) commonInit() } + + override var isHighlighted: Bool { + didSet { + updateState() + } + } } private extension MessagingButton { @@ -31,6 +37,14 @@ private extension MessagingButton { setAttributedTitle(attributedTitle, for: .normal) } + func updateState() { + if isHighlighted { + backgroundColor = Asset.Colors.secondaryGreen.color + } else { + backgroundColor = Asset.Colors.primaryGreen.color + } + } + var attributedTitle: NSAttributedString { let attributedString = NSMutableAttributedString() attributedString.append(NSAttributedString(attachment: messageIconTextAttachment)) diff --git a/TreeTracker/UI/Views/Home/Components/UploadsButton.swift b/TreeTracker/UI/Views/Home/Components/UploadsButton.swift index fd4d3070..cf549cb5 100644 --- a/TreeTracker/UI/Views/Home/Components/UploadsButton.swift +++ b/TreeTracker/UI/Views/Home/Components/UploadsButton.swift @@ -37,6 +37,12 @@ class UploadsButton: UIButton { } } + override var isHighlighted: Bool { + didSet { + updateState() + } + } + var uploadState: UploadState = .start { didSet { updateUploadState() @@ -60,12 +66,15 @@ private extension UploadsButton { } func updateState() { - if isEnabled { - backgroundColor = Asset.Colors.secondaryOrangeLight.color - tintColor = .white - } else { + if !isEnabled { backgroundColor = Asset.Colors.grayLight.color.withAlphaComponent(0.2) tintColor = Asset.Colors.grayLight.color + } else if isHighlighted { + backgroundColor = Asset.Colors.secondaryOrangeDark.color + tintColor = .white + } else { + backgroundColor = Asset.Colors.secondaryOrangeLight.color + tintColor = .white } } From af5e7362678c1a091ed0e565c15404c352f3c7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Thu, 15 Jun 2023 10:11:33 -0300 Subject: [PATCH 53/66] improve chatlist screen fetch request --- .../UI/Views/Messaging/ChatList/ChatListViewModel.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index 32807581..41bc72aa 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -61,7 +61,7 @@ extension ChatListViewModel { func fetchMessages() { var chatList: [Chat] = [] - let allMessages = messagingService.getSavedMessages(planter: planter) + let allMessages = messagingService.getChatListMessages(planter: planter) for message in allMessages { let messageType = messageType(type: message.type) @@ -95,9 +95,7 @@ extension ChatListViewModel { func updateUnreadMessagesCount(indexPath: IndexPath) { let messagesToUpdate = chatList[indexPath.row].messages - - let updatedMessages = messagingService.updateUnreadMessages(messages: messagesToUpdate) - self.chatList[indexPath.row].messages = updatedMessages + messagingService.updateUnreadMessages(messages: messagesToUpdate) } } From fd0c7d41cdbd5eb80639eddef6ce9c6aee7db5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Thu, 15 Jun 2023 11:21:17 -0300 Subject: [PATCH 54/66] remove profile image from chatlist screen --- .../ChatList/ChatListViewController.swift | 104 ------------------ .../ChatList/ChatListViewModel.swift | 22 ---- 2 files changed, 126 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index e25d1d9f..99b32f9f 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -40,14 +40,6 @@ class ChatListViewController: UIViewController { } } - private var planterImage: UIImageView = { - let image = UIImageView() - image.translatesAutoresizingMaskIntoConstraints = false - image.clipsToBounds = true - image.layer.cornerRadius = Const.ImageSizeForLargeState / 2 - return image - }() - var viewModel: ChatListViewModel? { didSet { viewModel?.viewDelegate = self @@ -55,12 +47,6 @@ class ChatListViewController: UIViewController { } } - override func viewDidLoad() { - super.viewDidLoad() - viewModel?.fetchProfileImage() - setupNavBarImage() - } - override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) view.backgroundColor = Asset.Colors.backgroundGreen.color @@ -68,15 +54,9 @@ class ChatListViewController: UIViewController { prefersLargeTitles: true, backgroundColor: Asset.Colors.backgroundGreen.color ) - showImage(true) viewModel?.fetchMessages() } - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - showImage(false) - } - } // MARK: - UITableViewDataSource @@ -113,10 +93,6 @@ extension ChatListViewController: UITableViewDelegate { // MARK: - ChatListViewModelViewDelegate extension ChatListViewController: ChatListViewModelViewDelegate { - func chatListViewModel(_ chatListViewModel: ChatListViewModel, didFetchProfile image: UIImage) { - planterImage.image = image - } - func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateChatList chatList: [ChatListViewModel.Chat]) { if chatList.isEmpty { noMessagesView.isHidden = false @@ -127,83 +103,3 @@ extension ChatListViewController: ChatListViewModelViewDelegate { } } - -// MARK: - ImageOnNavigationBar -extension ChatListViewController { - - private func setupNavBarImage() { - guard let navigationBar = self.navigationController?.navigationBar else { return } - navigationBar.addSubview(planterImage) - - NSLayoutConstraint.activate([ - planterImage.rightAnchor.constraint(equalTo: navigationBar.rightAnchor, constant: -Const.ImageRightMargin), - planterImage.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor, constant: -Const.ImageBottomMarginForLargeState), - planterImage.heightAnchor.constraint(equalToConstant: Const.ImageSizeForLargeState), - planterImage.widthAnchor.constraint(equalTo: planterImage.heightAnchor) - ]) - } - - private func moveAndResizeImage(for height: CGFloat) { - let coeff: CGFloat = { - let delta = height - Const.NavBarHeightSmallState - let heightDifferenceBetweenStates = (Const.NavBarHeightLargeState - Const.NavBarHeightSmallState) - return delta / heightDifferenceBetweenStates - }() - - let factor = Const.ImageSizeForSmallState / Const.ImageSizeForLargeState - - let scale: CGFloat = { - let sizeAddendumFactor = coeff * (1.0 - factor) - return min(1.0, sizeAddendumFactor + factor) - }() - - // Value of difference between icons for large and small states - let sizeDiff = Const.ImageSizeForLargeState * (1.0 - factor) // 8.0 - let yTranslation: CGFloat = { - /// This value = 14. It equals to difference of 12 and 6 (bottom margin for large and small states). Also it adds 8.0 (size difference when the image gets smaller size) - let maxYTranslation = Const.ImageBottomMarginForLargeState - Const.ImageBottomMarginForSmallState + sizeDiff - return max(0, min(maxYTranslation, (maxYTranslation - coeff * (Const.ImageBottomMarginForSmallState + sizeDiff)))) - }() - - let xTranslation = max(0, sizeDiff - coeff * sizeDiff) - - planterImage.transform = CGAffineTransform.identity - .scaledBy(x: scale, y: scale) - .translatedBy(x: xTranslation, y: yTranslation) - } - - private func showImage(_ show: Bool) { - UIView.animate(withDuration: 0.3) { [weak self] in - self?.planterImage.alpha = show ? 1.0 : 0.0 - } - } - - /// WARNING: Change these constants according to your project's design - private struct Const { - /// Image height/width for Large NavBar state - static let ImageSizeForLargeState: CGFloat = 65 - /// Margin from right anchor of safe area to right anchor of Image - static let ImageRightMargin: CGFloat = 16 - /// Margin from bottom anchor of NavBar to bottom anchor of Image for Large NavBar state - static let ImageBottomMarginForLargeState: CGFloat = 15 - /// Margin from bottom anchor of NavBar to bottom anchor of Image for Small NavBar state - static let ImageBottomMarginForSmallState: CGFloat = 6 - /// Image height/width for Small NavBar state - static let ImageSizeForSmallState: CGFloat = 32 - /// Height of NavBar for Small state. Usually it's just 44 - static let NavBarHeightSmallState: CGFloat = 44 - /// Height of NavBar for Large state. Usually it's just 96.5 but if you have a custom font for the title, please make sure to edit this value since it changes the height for Large state of NavBar - static let NavBarHeightLargeState: CGFloat = 96.5 - } - -} - -// MARK: - -extension ChatListViewController: UIScrollViewDelegate { - - func scrollViewDidScroll(_ scrollView: UIScrollView) { - guard let height = navigationController?.navigationBar.frame.height else { return } - moveAndResizeImage(for: height) - } - -} diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index 41bc72aa..51d8366f 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -17,7 +17,6 @@ protocol ChatListViewModelCoordinatorDelegate: AnyObject { } protocol ChatListViewModelViewDelegate: AnyObject { - func chatListViewModel(_ chatListViewModel: ChatListViewModel, didFetchProfile image: UIImage) func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateChatList chatList: [ChatListViewModel.Chat]) } @@ -100,27 +99,6 @@ extension ChatListViewModel { } -// MARK: - Profile -extension ChatListViewModel { - - func fetchProfileImage() { - - selfieService.fetchSelfie(forPlanter: planter) { [weak self] (result) in - guard let self else { return } - switch result { - case .success(let data): - guard let image = UIImage(data: data) else { - fallthrough - } - viewDelegate?.chatListViewModel(self, didFetchProfile: image) - case .failure: - viewDelegate?.chatListViewModel(self, didFetchProfile: Asset.Assets.profile.image) - } - } - } - -} - // MARK: - Navigation extension ChatListViewModel { From e1d54960c744afad192dd0bb2c4c39cf53ad702a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Tue, 8 Aug 2023 15:06:16 -0300 Subject: [PATCH 55/66] remove setupNavigationAppearance and revert to the origin --- TreeTracker.xcodeproj/project.pbxproj | 4 -- .../BaseNavigationViewController.swift | 11 +++- .../UINavigationBar+Extensions.swift | 55 ------------------- .../UI/Views/Home/HomeViewController.swift | 1 - .../Announce/AnnounceViewController.swift | 7 --- .../ChatList/ChatListViewController.swift | 5 -- .../Messages/MessagesViewController.swift | 4 -- 7 files changed, 10 insertions(+), 77 deletions(-) delete mode 100644 TreeTracker/UI/Extensions/UINavigationBar+Extensions.swift diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index 87b78504..fb7eb64e 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 8D3AF01F2A01346D00976763 /* UINavigationBar+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3AF01E2A01346D00976763 /* UINavigationBar+Extensions.swift */; }; 8D75250629DB13FA00361CC1 /* MessagingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75250529DB13FA00361CC1 /* MessagingButton.swift */; }; 8D75251729DB61A000361CC1 /* ChatListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75251629DB61A000361CC1 /* ChatListViewController.swift */; }; 8D75251929DB61C000361CC1 /* ChatList.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D75251829DB61C000361CC1 /* ChatList.storyboard */; }; @@ -117,7 +116,6 @@ /* Begin PBXFileReference section */ 229566C2A487A3F9C7D82AD8 /* Pods_TreeTracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TreeTracker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8D3AF01E2A01346D00976763 /* UINavigationBar+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationBar+Extensions.swift"; sourceTree = ""; }; 8D75250529DB13FA00361CC1 /* MessagingButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagingButton.swift; sourceTree = ""; }; 8D75251629DB61A000361CC1 /* ChatListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListViewController.swift; sourceTree = ""; }; 8D75251829DB61C000361CC1 /* ChatList.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ChatList.storyboard; sourceTree = ""; }; @@ -682,7 +680,6 @@ isa = PBXGroup; children = ( 93EC8CA724B28B5D00946C0C /* UITextField+Extensions.swift */, - 8D3AF01E2A01346D00976763 /* UINavigationBar+Extensions.swift */, ); path = Extensions; sourceTree = ""; @@ -1052,7 +1049,6 @@ 93E82F5A246C36B500006C91 /* LoadingViewController.swift in Sources */, 938294BA27F9D00800888E42 /* PhotoQuality.swift in Sources */, B0AAD80628C235C900640D4A /* NotesViewController.swift in Sources */, - 8D3AF01F2A01346D00976763 /* UINavigationBar+Extensions.swift in Sources */, 9311609B26B4108A007FCA52 /* RoundedButton.swift in Sources */, 93EC8C3424AA4AE100946C0C /* HomeCoordinator.swift in Sources */, 938294AE27F9A79900888E42 /* SettingsViewModel.swift in Sources */, diff --git a/TreeTracker/UI/Components/ViewControllers/BaseNavigationViewController.swift b/TreeTracker/UI/Components/ViewControllers/BaseNavigationViewController.swift index aaa46307..29d803b6 100644 --- a/TreeTracker/UI/Components/ViewControllers/BaseNavigationViewController.swift +++ b/TreeTracker/UI/Components/ViewControllers/BaseNavigationViewController.swift @@ -12,6 +12,15 @@ class BaseNavigationViewController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() - navigationBar.setupNavigationAppearance() + navigationBar.tintColor = Asset.Colors.grayDark.color + navigationBar.prefersLargeTitles = true + navigationBar.titleTextAttributes = [ + .font: FontFamily.Montserrat.bold.font(size: 15), + .foregroundColor: Asset.Colors.grayDark.color + ] + navigationBar.largeTitleTextAttributes = [ + .font: FontFamily.Montserrat.bold.font(size: 35), + .foregroundColor: Asset.Colors.grayDark.color + ] } } diff --git a/TreeTracker/UI/Extensions/UINavigationBar+Extensions.swift b/TreeTracker/UI/Extensions/UINavigationBar+Extensions.swift deleted file mode 100644 index 4c672465..00000000 --- a/TreeTracker/UI/Extensions/UINavigationBar+Extensions.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// UINavigationBar+Extensions.swift -// TreeTracker -// -// Created by Frédéric Helfer on 02/05/23. -// Copyright © 2023 Greenstand. All rights reserved. -// - -import UIKit - -extension UINavigationBar { - - func setupNavigationAppearance( - prefersLargeTitles: Bool = true, - backgroundColor: UIColor = .white - ) { - - self.prefersLargeTitles = prefersLargeTitles - self.tintColor = Asset.Colors.grayDark.color - self.isTranslucent = false - - if #available(iOS 13.0, *) { - - let navBarAppearance = UINavigationBarAppearance() - navBarAppearance.configureWithDefaultBackground() - navBarAppearance.shadowColor = .clear - navBarAppearance.shadowImage = .init() - navBarAppearance.backgroundColor = backgroundColor - navBarAppearance.titleTextAttributes = [ - .font: FontFamily.Montserrat.bold.font(size: 15), - .foregroundColor: Asset.Colors.grayDark.color - ] - navBarAppearance.largeTitleTextAttributes = [ - .font: FontFamily.Montserrat.bold.font(size: 35), - .foregroundColor: Asset.Colors.grayDark.color - ] - self.standardAppearance = navBarAppearance - self.scrollEdgeAppearance = navBarAppearance - - } else { - - self.backgroundColor = backgroundColor - self.barTintColor = backgroundColor - self.titleTextAttributes = [ - .font: FontFamily.Montserrat.bold.font(size: 15), - .foregroundColor: Asset.Colors.grayDark.color - ] - self.largeTitleTextAttributes = [ - .font: FontFamily.Montserrat.bold.font(size: 35), - .foregroundColor: Asset.Colors.grayDark.color - ] - - } - } -} diff --git a/TreeTracker/UI/Views/Home/HomeViewController.swift b/TreeTracker/UI/Views/Home/HomeViewController.swift index f15725dd..bdd859ce 100644 --- a/TreeTracker/UI/Views/Home/HomeViewController.swift +++ b/TreeTracker/UI/Views/Home/HomeViewController.swift @@ -153,7 +153,6 @@ class HomeViewController: UIViewController, AlertPresenting { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - navigationController?.navigationBar.setupNavigationAppearance() viewModel?.fetchProfileData() viewModel?.syncMessages() viewModel?.fetchUnreadMessagesCount() diff --git a/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift b/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift index 6d38febf..dac05f95 100644 --- a/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Announce/AnnounceViewController.swift @@ -41,13 +41,6 @@ class AnnounceViewController: UIViewController { viewModel?.updateView() } - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - navigationController?.navigationBar.setupNavigationAppearance( - prefersLargeTitles: true, - backgroundColor: Asset.Colors.backgroundGreen.color - ) - } } // MARK: - AnnounceViewModelViewDelegate diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index 99b32f9f..e4b57ebb 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -49,11 +49,6 @@ class ChatListViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - view.backgroundColor = Asset.Colors.backgroundGreen.color - navigationController?.navigationBar.setupNavigationAppearance( - prefersLargeTitles: true, - backgroundColor: Asset.Colors.backgroundGreen.color - ) viewModel?.fetchMessages() } diff --git a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift index 594a2fa6..4951d82d 100644 --- a/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Messages/MessagesViewController.swift @@ -69,10 +69,6 @@ class MessagesViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - view.backgroundColor = Asset.Colors.backgroundGreen.color - navigationController?.navigationBar.setupNavigationAppearance( - backgroundColor: Asset.Colors.backgroundGreen.color - ) scrollToBottom() } From dafa13631cd1cad3de90249e05f20e5e9bed5ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Helfer?= Date: Wed, 9 Aug 2023 16:40:03 -0300 Subject: [PATCH 56/66] changed some localizable text strings --- TreeTracker/Resources/Localizable.strings | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TreeTracker/Resources/Localizable.strings b/TreeTracker/Resources/Localizable.strings index 55b21317..7571d6e2 100644 --- a/TreeTracker/Resources/Localizable.strings +++ b/TreeTracker/Resources/Localizable.strings @@ -102,14 +102,14 @@ "Settings.PhotoQuality.Option.High.Info" = "High resolution, high data usage."; //ChatList Screen -"ChatList.NoMessagesLabel.Text" = "No messages, Yet."; +"ChatList.NoMessagesLabel.Text" = "No messages"; //Messages Screen "Messages.Title" = "Admin"; "Messages.InputTextView.PlaceHolder" = "Click to write a message"; //Announce Screen -"Announce.Title" = "Announce"; +"Announce.Title" = "Announcement"; //Survey Screen "Survey.Title.Question" = "Question"; From 56d80e150ba4b3d2bf24c6962647e039d3d47d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Helfer?= Date: Wed, 9 Aug 2023 16:53:26 -0300 Subject: [PATCH 57/66] updated generated strings --- TreeTracker/SwiftGen/Strings.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TreeTracker/SwiftGen/Strings.swift b/TreeTracker/SwiftGen/Strings.swift index b8db6b99..d86d5e79 100644 --- a/TreeTracker/SwiftGen/Strings.swift +++ b/TreeTracker/SwiftGen/Strings.swift @@ -41,8 +41,8 @@ internal enum L10n { } } internal enum Announce { - /// Announce - internal static let title = L10n.tr("Localizable", "Announce.Title", fallback: "Announce") + /// Announcement + internal static let title = L10n.tr("Localizable", "Announce.Title", fallback: "Announcement") } internal enum App { /// Localizable.strings @@ -54,8 +54,8 @@ internal enum L10n { } internal enum ChatList { internal enum NoMessagesLabel { - /// No messages, Yet. - internal static let text = L10n.tr("Localizable", "ChatList.NoMessagesLabel.Text", fallback: "No messages, Yet.") + /// No messages + internal static let text = L10n.tr("Localizable", "ChatList.NoMessagesLabel.Text", fallback: "No messages") } } internal enum DeleteAccountConfirmation { From 203790c68f16a174913093aaa1519b1f32efaa73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Wed, 9 Aug 2023 17:08:41 -0300 Subject: [PATCH 58/66] removed all green background color from the project --- .../BackgroundGreen.colorset/Contents.json | 20 ------------------- .../Colors.xcassets/Background/Contents.json | 6 ------ TreeTracker/SwiftGen/Assets.swift | 1 - .../ChatList/Cell/ChatListTableViewCell.swift | 2 +- .../Survey/Cell/SurveyTableViewCell.swift | 6 +++--- 5 files changed, 4 insertions(+), 31 deletions(-) delete mode 100644 TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json delete mode 100644 TreeTracker/Resources/Colors.xcassets/Background/Contents.json diff --git a/TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json b/TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json deleted file mode 100644 index f86303cb..00000000 --- a/TreeTracker/Resources/Colors.xcassets/Background/BackgroundGreen.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0.761", - "green" : "0.914", - "red" : "0.855" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/TreeTracker/Resources/Colors.xcassets/Background/Contents.json b/TreeTracker/Resources/Colors.xcassets/Background/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/TreeTracker/Resources/Colors.xcassets/Background/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/TreeTracker/SwiftGen/Assets.swift b/TreeTracker/SwiftGen/Assets.swift index f72191e6..81dc5aa7 100644 --- a/TreeTracker/SwiftGen/Assets.swift +++ b/TreeTracker/SwiftGen/Assets.swift @@ -54,7 +54,6 @@ internal enum Asset { } } internal enum Colors { - internal static let backgroundGreen = ColorAsset(name: "BackgroundGreen") internal static let grayDark = ColorAsset(name: "GrayDark") internal static let grayLight = ColorAsset(name: "GrayLight") internal static let grayMedium = ColorAsset(name: "GrayMedium") diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift index 121269a1..9b51780b 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/Cell/ChatListTableViewCell.swift @@ -59,7 +59,7 @@ class ChatListTableViewCell: UITableViewCell { extension ChatListTableViewCell { func setupCell(data: ChatListViewModel.Chat) { - contentView.backgroundColor = Asset.Colors.backgroundGreen.color.withAlphaComponent(0.25) + contentView.backgroundColor = Asset.Colors.grayLight.color.withAlphaComponent(0.2) chatImage.image = Asset.Assets.trees.image chatImage.image = data.image chatTitle.text = data.title diff --git a/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift b/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift index efc4dcd6..021d96c2 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/Cell/SurveyTableViewCell.swift @@ -12,7 +12,7 @@ class SurveyTableViewCell: UITableViewCell { @IBOutlet private var buttonView: UIView! { didSet { - buttonView.backgroundColor = Asset.Colors.backgroundGreen.color + buttonView.backgroundColor = Asset.Colors.primaryGreen.color buttonView.layer.cornerRadius = 5.0 buttonView.clipsToBounds = true } @@ -39,9 +39,9 @@ extension SurveyTableViewCell { buttonTitle.text = choice?.text if let choice, choice.isSelected { - buttonView.backgroundColor = Asset.Colors.primaryGreen.color + buttonView.backgroundColor = Asset.Colors.secondaryGreen.color } else { - buttonView.backgroundColor = Asset.Colors.backgroundGreen.color + buttonView.backgroundColor = Asset.Colors.primaryGreen.color } } From 18e3d18e28daadbf11286f59897311bcc74a744c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Helfer?= Date: Thu, 10 Aug 2023 10:59:59 -0300 Subject: [PATCH 59/66] fix indentation and camel case --- .../UI/Navigation/Home/HomeCoordinator.swift | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index 68d282f8..8a04457c 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -144,8 +144,8 @@ private extension HomeCoordinator { } func addTreeViewController(planter: Planter) -> UIViewController { - let viewcontroller = StoryboardScene.AddTree.initialScene.instantiate() - viewcontroller.viewModel = { + let viewController = StoryboardScene.AddTree.initialScene.instantiate() + viewController.viewModel = { let viewModel = AddTreeViewModel( locationProvider: self.treetrackerSDK.locationService, treeService: self.treetrackerSDK.treeService, @@ -156,33 +156,33 @@ private extension HomeCoordinator { viewModel.coordinatorDelegate = self return viewModel }() - return viewcontroller + return viewController } func chatListViewController(planter: Planter) -> UIViewController { - let viewcontroller = StoryboardScene.ChatList.initialScene.instantiate() - viewcontroller.viewModel = { + let viewController = StoryboardScene.ChatList.initialScene.instantiate() + viewController.viewModel = { let viewModel = ChatListViewModel( - planter: planter, - selfieService: self.treetrackerSDK.selfieService, - messagingService: self.treetrackerSDK.messagingService + planter: planter, + selfieService: self.treetrackerSDK.selfieService, + messagingService: self.treetrackerSDK.messagingService ) viewModel.coordinatorDelegate = self return viewModel }() - return viewcontroller + return viewController } func messagesViewController(planter: Planter) -> UIViewController { - let viewcontroller = StoryboardScene.Messages.initialScene.instantiate() - viewcontroller.viewModel = { + let viewController = StoryboardScene.Messages.initialScene.instantiate() + viewController.viewModel = { let viewModel = MessagesViewModel( - planter: planter, - messagingService: treetrackerSDK.messagingService + planter: planter, + messagingService: treetrackerSDK.messagingService ) return viewModel }() - return viewcontroller + return viewController } func announceViewController(chat: ChatListViewModel.Chat) -> UIViewController { From 1f2eea29c59fcd90843f0ea3653af2d32843dcec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Helfer?= Date: Thu, 10 Aug 2023 11:30:14 -0300 Subject: [PATCH 60/66] added chatList fallback text to localizable strings --- TreeTracker/Resources/Localizable.strings | 2 ++ TreeTracker/SwiftGen/Strings.swift | 8 ++++++++ .../UI/Views/Messaging/ChatList/ChatListViewModel.swift | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/TreeTracker/Resources/Localizable.strings b/TreeTracker/Resources/Localizable.strings index 7571d6e2..9befe5c8 100644 --- a/TreeTracker/Resources/Localizable.strings +++ b/TreeTracker/Resources/Localizable.strings @@ -103,6 +103,8 @@ //ChatList Screen "ChatList.NoMessagesLabel.Text" = "No messages"; +"ChatList.MessageChatCell.FallbackTitle" = "Admin"; +"ChatList.DefaultCell.FallbackTitle" = ""; //Messages Screen "Messages.Title" = "Admin"; diff --git a/TreeTracker/SwiftGen/Strings.swift b/TreeTracker/SwiftGen/Strings.swift index d86d5e79..db973590 100644 --- a/TreeTracker/SwiftGen/Strings.swift +++ b/TreeTracker/SwiftGen/Strings.swift @@ -53,6 +53,14 @@ internal enum L10n { internal static let title = L10n.tr("Localizable", "App.Title", fallback: "Greenstand Treetracker") } internal enum ChatList { + internal enum DefaultCell { + /// + internal static let fallbackTitle = L10n.tr("Localizable", "ChatList.DefaultCell.FallbackTitle", fallback: "") + } + internal enum MessageChatCell { + /// Admin + internal static let fallbackTitle = L10n.tr("Localizable", "ChatList.MessageChatCell.FallbackTitle", fallback: "Admin") + } internal enum NoMessagesLabel { /// No messages internal static let text = L10n.tr("Localizable", "ChatList.NoMessagesLabel.Text", fallback: "No messages") diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index 51d8366f..80e99b88 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -72,7 +72,7 @@ extension ChatListViewModel { } else { let newChat = Chat( - title: message.subject ?? "Admin", + title: message.subject ?? L10n.ChatList.MessageChatCell.fallbackTitle, type: messageType, messages: [message] ) @@ -81,7 +81,7 @@ extension ChatListViewModel { default: let newChat = Chat( - title: message.subject ?? "", + title: message.subject ?? L10n.ChatList.DefaultCell.fallbackTitle, type: messageType, messages: [message] ) From cbb188c903e470821ca72729d9693b2627e253c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Helfer?= Date: Thu, 10 Aug 2023 12:15:57 -0300 Subject: [PATCH 61/66] removed duplicated MessageType model --- .../Messaging/ChatList/ChatListViewModel.swift | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index 80e99b88..4ae1a94d 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -111,7 +111,7 @@ extension ChatListViewModel { coordinatorDelegate?.chatListViewModel(self, didSelectMessages: planter) case .announce: coordinatorDelegate?.chatListViewModel(self, didSelectAnnounce: selectedChat) - case .survey, .surveyResponse: + case .survey, .survey_response: guard let message = selectedChat.messages.first, let questions = message.survey?.questions?.array as? [SurveyQuestion] else { @@ -141,7 +141,7 @@ extension ChatListViewModel { struct Chat { let title: String - let type: MessageType + let type: Message.MessageType var messages: [MessageEntity] var image: UIImage { @@ -152,7 +152,7 @@ extension ChatListViewModel { return Asset.Assets.gardening.image case .survey: return Asset.Assets.logo.image - case .surveyResponse: + case .survey_response: return Asset.Assets.people.image } } @@ -162,19 +162,12 @@ extension ChatListViewModel { } } - enum MessageType: String, Decodable { - case message - case announce - case survey - case surveyResponse - } - - func messageType(type: String?) -> MessageType { + func messageType(type: String?) -> Message.MessageType { switch type { case "message": return .message case "announce": return .announce case "survey": return .survey - case "survey_response": return .surveyResponse + case "survey_response": return .survey_response default: return .message } } From 9eb1e4b981ed1da583ab0a76e3d06482294df43a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Helfer?= Date: Thu, 10 Aug 2023 15:47:22 -0300 Subject: [PATCH 62/66] added completion handlers for syncMessages method --- TreeTracker/UI/Views/Home/HomeViewModel.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/TreeTracker/UI/Views/Home/HomeViewModel.swift b/TreeTracker/UI/Views/Home/HomeViewModel.swift index 285dc8c0..f2dd4560 100644 --- a/TreeTracker/UI/Views/Home/HomeViewModel.swift +++ b/TreeTracker/UI/Views/Home/HomeViewModel.swift @@ -117,7 +117,16 @@ extension HomeViewModel { } func syncMessages() { - messagingService.syncMessages(for: planter) + messagingService.syncMessages(for: planter) { [weak self] result in + switch result { + case .success(_): + self?.fetchUnreadMessagesCount() + + case .failure(let error): + // do something + print(error) + } + } } } From 49d50afe16d2b37cbf5a99b502c5d9844032dde1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Helfer?= Date: Thu, 10 Aug 2023 15:50:40 -0300 Subject: [PATCH 63/66] removed notification center --- TreeTracker/UI/Views/Home/HomeViewController.swift | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/TreeTracker/UI/Views/Home/HomeViewController.swift b/TreeTracker/UI/Views/Home/HomeViewController.swift index bdd859ce..5973228c 100644 --- a/TreeTracker/UI/Views/Home/HomeViewController.swift +++ b/TreeTracker/UI/Views/Home/HomeViewController.swift @@ -147,7 +147,6 @@ class HomeViewController: UIViewController, AlertPresenting { override func viewDidLoad() { super.viewDidLoad() addSettingsButton() - configureNotificationCenter() viewModel?.startMonitoringTrees() } @@ -189,19 +188,6 @@ private extension HomeViewController { action: #selector(settingsButtonPressed) )] } - - func configureNotificationCenter() { - NotificationCenter.default.addObserver(self, - selector: #selector(fetchUnreadMessagesCount), - name: NSNotification.Name("didFinishFetchingMessages"), - object: nil - ) - } - - @objc - func fetchUnreadMessagesCount() { - viewModel?.fetchUnreadMessagesCount() - } } // MARK: - Button Actions From 041b88045a92a36b7089fce27d10338a4ffc49f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Thu, 17 Aug 2023 15:20:05 -0300 Subject: [PATCH 64/66] changed survey index to questionIndex --- .../UI/Navigation/Home/HomeCoordinator.swift | 12 ++++---- .../Survey/SurveyViewController.swift | 6 ++-- .../Messaging/Survey/SurveyViewModel.swift | 28 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift index 8a04457c..a79db192 100644 --- a/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift +++ b/TreeTracker/UI/Navigation/Home/HomeCoordinator.swift @@ -82,9 +82,9 @@ private extension HomeCoordinator { ) } - func showSurvey(planter: Planter, survey: SurveyViewModel.Survey, index: Int = 0) { + func showSurvey(planter: Planter, survey: SurveyViewModel.Survey, questionIndex: Int = 0) { configuration.navigationController.pushViewController( - surveyViewController(planter: planter, survey: survey, index: index), + surveyViewController(planter: planter, survey: survey, questionIndex: questionIndex), animated: true ) } @@ -196,14 +196,14 @@ private extension HomeCoordinator { return viewController } - func surveyViewController(planter: Planter, survey: SurveyViewModel.Survey, index: Int) -> UIViewController { + func surveyViewController(planter: Planter, survey: SurveyViewModel.Survey, questionIndex: Int) -> UIViewController { let viewController = StoryboardScene.Survey.initialScene.instantiate() viewController.viewModel = { let viewModel = SurveyViewModel( planter: planter, survey: survey, messagingService: self.treetrackerSDK.messagingService, - index: index + questionIndex: questionIndex ) viewModel.coordinatorDelegate = self return viewModel @@ -349,8 +349,8 @@ extension HomeCoordinator: ChatListViewModelCoordinatorDelegate { // MARK: - SurveyViewModelCoordinatorDelegate extension HomeCoordinator: SurveyViewModelCoordinatorDelegate { - func surveyViewModel(_ surveyViewModel: SurveyViewModel, showNextQuestion survey: SurveyViewModel.Survey, planter: Planter, index: Int) { - showSurvey(planter: planter, survey: survey, index: index) + func surveyViewModel(_ surveyViewModel: SurveyViewModel, showNextQuestion survey: SurveyViewModel.Survey, planter: Planter, questionIndex: Int) { + showSurvey(planter: planter, survey: survey, questionIndex: questionIndex) } func surveyViewModel(_ surveyViewModel: SurveyViewModel, didFinishSurvey survey: SurveyViewModel.Survey) { diff --git a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift index dd6217c7..bf9a7208 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift @@ -91,13 +91,13 @@ extension SurveyViewController: UITableViewDelegate { extension SurveyViewController: SurveyViewModelViewDelegate { func surveyViewModel(_ surveyViewModel: SurveyViewModel, updateViewWith survey: SurveyViewModel.Survey) { - questionLabel.text = survey.questions[surveyViewModel.index].prompt + questionLabel.text = survey.questions[surveyViewModel.questionIndex].prompt - if surveyViewModel.index == survey.questions.count - 1 { + if surveyViewModel.questionIndex == survey.questions.count - 1 { actionButton.buttonStyle = .finish } - if survey.surveyResponse.indices.contains(surveyViewModel.index) { + if survey.surveyResponse.indices.contains(surveyViewModel.questionIndex) { actionButton.isEnabled = true } diff --git a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift index 3e4e4c9f..1be1cdeb 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift @@ -10,7 +10,7 @@ import Foundation import Treetracker_Core protocol SurveyViewModelCoordinatorDelegate: AnyObject { - func surveyViewModel(_ surveyViewModel: SurveyViewModel, showNextQuestion survey: SurveyViewModel.Survey, planter: Planter, index: Int) + func surveyViewModel(_ surveyViewModel: SurveyViewModel, showNextQuestion survey: SurveyViewModel.Survey, planter: Planter, questionIndex: Int) func surveyViewModel(_ surveyViewModel: SurveyViewModel, didFinishSurvey survey: SurveyViewModel.Survey) } @@ -27,29 +27,29 @@ class SurveyViewModel { private var survey: SurveyViewModel.Survey private let messagingService: MessagingService - init(planter: Planter, survey: SurveyViewModel.Survey, messagingService: MessagingService, index: Int) { + init(planter: Planter, survey: SurveyViewModel.Survey, messagingService: MessagingService, questionIndex: Int) { self.planter = planter self.survey = survey self.messagingService = messagingService - self.index = index + self.questionIndex = questionIndex } - var index: Int + var questionIndex: Int var title: String { let title = survey.response ? L10n.Survey.Title.response : L10n.Survey.Title.question - return "\(title) \(index + 1)/\(survey.questions.count)" + return "\(title) \(questionIndex + 1)/\(survey.questions.count)" } var numberOfRowsInSection: Int { - return survey.questions[index].choices.count + return survey.questions[questionIndex].choices.count } func getChoiceForRowAt(indexPath: IndexPath) -> Choice { - let choiceText = survey.questions[index].choices[indexPath.row] + let choiceText = survey.questions[questionIndex].choices[indexPath.row] - if survey.surveyResponse.indices.contains(index) { - if survey.surveyResponse[index] == choiceText { + if survey.surveyResponse.indices.contains(questionIndex) { + if survey.surveyResponse[questionIndex] == choiceText { return Choice(text: choiceText, isSelected: true) } } @@ -60,9 +60,9 @@ class SurveyViewModel { func didSelectRowAt(indexPath: IndexPath) { guard survey.response == false else { return } - let selectedChoice = survey.questions[index].choices[indexPath.row] - if survey.surveyResponse.indices.contains(index) { - survey.surveyResponse[index] = selectedChoice + let selectedChoice = survey.questions[questionIndex].choices[indexPath.row] + if survey.surveyResponse.indices.contains(questionIndex) { + survey.surveyResponse[questionIndex] = selectedChoice } else { survey.surveyResponse.append(selectedChoice) } @@ -80,8 +80,8 @@ class SurveyViewModel { extension SurveyViewModel { func actionButtonPressed() { - if index < survey.questions.count - 1 { - coordinatorDelegate?.surveyViewModel(self, showNextQuestion: survey, planter: planter, index: index + 1) + if questionIndex < survey.questions.count - 1 { + coordinatorDelegate?.surveyViewModel(self, showNextQuestion: survey, planter: planter, questionIndex: questionIndex + 1) } else { if survey.response == false { From 2851b2ffda9b4898a63c87a0630ec3ceb588f91a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 18 Aug 2023 10:00:25 -0300 Subject: [PATCH 65/66] added syncMessages button to chatListScreen --- TreeTracker.xcodeproj/project.pbxproj | 12 +++ TreeTracker/Resources/Localizable.strings | 4 + TreeTracker/SwiftGen/Strings.swift | 20 ++++ .../UI/Views/Home/HomeViewController.swift | 1 - TreeTracker/UI/Views/Home/HomeViewModel.swift | 13 --- .../Messaging/ChatList/ChatList.storyboard | 34 ++++++- .../ChatList/ChatListViewController.swift | 39 +++++++- .../ChatList/ChatListViewModel.swift | 38 ++++++++ .../Components/SyncMessagesButton.swift | 95 +++++++++++++++++++ 9 files changed, 237 insertions(+), 19 deletions(-) create mode 100644 TreeTracker/UI/Views/Messaging/ChatList/Components/SyncMessagesButton.swift diff --git a/TreeTracker.xcodeproj/project.pbxproj b/TreeTracker.xcodeproj/project.pbxproj index fb7eb64e..b4d6160f 100644 --- a/TreeTracker.xcodeproj/project.pbxproj +++ b/TreeTracker.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 8D681BED2A8EA13B009D9569 /* SyncMessagesButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D681BEC2A8EA13B009D9569 /* SyncMessagesButton.swift */; }; 8D75250629DB13FA00361CC1 /* MessagingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75250529DB13FA00361CC1 /* MessagingButton.swift */; }; 8D75251729DB61A000361CC1 /* ChatListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D75251629DB61A000361CC1 /* ChatListViewController.swift */; }; 8D75251929DB61C000361CC1 /* ChatList.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D75251829DB61C000361CC1 /* ChatList.storyboard */; }; @@ -116,6 +117,7 @@ /* Begin PBXFileReference section */ 229566C2A487A3F9C7D82AD8 /* Pods_TreeTracker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TreeTracker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D681BEC2A8EA13B009D9569 /* SyncMessagesButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncMessagesButton.swift; sourceTree = ""; }; 8D75250529DB13FA00361CC1 /* MessagingButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagingButton.swift; sourceTree = ""; }; 8D75251629DB61A000361CC1 /* ChatListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListViewController.swift; sourceTree = ""; }; 8D75251829DB61C000361CC1 /* ChatList.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ChatList.storyboard; sourceTree = ""; }; @@ -244,6 +246,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 8D681BEA2A8EA11F009D9569 /* Components */ = { + isa = PBXGroup; + children = ( + 8D681BEC2A8EA13B009D9569 /* SyncMessagesButton.swift */, + ); + path = Components; + sourceTree = ""; + }; 8D75250829DB5BC900361CC1 /* Messaging */ = { isa = PBXGroup; children = ( @@ -258,6 +268,7 @@ 8D75250929DB5BF400361CC1 /* ChatList */ = { isa = PBXGroup; children = ( + 8D681BEA2A8EA11F009D9569 /* Components */, 8D75251D29DB6E0F00361CC1 /* Cell */, 8D75251A29DB61CB00361CC1 /* ChatListViewModel.swift */, 8D75251629DB61A000361CC1 /* ChatListViewController.swift */, @@ -1046,6 +1057,7 @@ 8D75252029DB6E7E00361CC1 /* ChatListTableViewCell.swift in Sources */, 93EC8CAB24BBA9E100946C0C /* AddTreeViewController.swift in Sources */, 93EC8C4624AB7A6900946C0C /* AddTreeButton.swift in Sources */, + 8D681BED2A8EA13B009D9569 /* SyncMessagesButton.swift in Sources */, 93E82F5A246C36B500006C91 /* LoadingViewController.swift in Sources */, 938294BA27F9D00800888E42 /* PhotoQuality.swift in Sources */, B0AAD80628C235C900640D4A /* NotesViewController.swift in Sources */, diff --git a/TreeTracker/Resources/Localizable.strings b/TreeTracker/Resources/Localizable.strings index 9befe5c8..1c14fc66 100644 --- a/TreeTracker/Resources/Localizable.strings +++ b/TreeTracker/Resources/Localizable.strings @@ -102,6 +102,10 @@ "Settings.PhotoQuality.Option.High.Info" = "High resolution, high data usage."; //ChatList Screen +"ChatList.SyncMessages.normal.Title" = "Sync Messages"; +"ChatList.SyncMessages.loading.Title" = "Loading..."; +"ChatList.LastSyncLabel.WithouDate.Text" = "Please, tap the button above to sync messages."; +"ChatList.LastSyncLabel.WithDate.Text" = "Last sync time:"; "ChatList.NoMessagesLabel.Text" = "No messages"; "ChatList.MessageChatCell.FallbackTitle" = "Admin"; "ChatList.DefaultCell.FallbackTitle" = ""; diff --git a/TreeTracker/SwiftGen/Strings.swift b/TreeTracker/SwiftGen/Strings.swift index db973590..4cbe9496 100644 --- a/TreeTracker/SwiftGen/Strings.swift +++ b/TreeTracker/SwiftGen/Strings.swift @@ -57,6 +57,16 @@ internal enum L10n { /// internal static let fallbackTitle = L10n.tr("Localizable", "ChatList.DefaultCell.FallbackTitle", fallback: "") } + internal enum LastSyncLabel { + internal enum WithDate { + /// Last sync time: + internal static let text = L10n.tr("Localizable", "ChatList.LastSyncLabel.WithDate.Text", fallback: "Last sync time:") + } + internal enum WithouDate { + /// Please, tap the button above to sync messages. + internal static let text = L10n.tr("Localizable", "ChatList.LastSyncLabel.WithouDate.Text", fallback: "Please, tap the button above to sync messages.") + } + } internal enum MessageChatCell { /// Admin internal static let fallbackTitle = L10n.tr("Localizable", "ChatList.MessageChatCell.FallbackTitle", fallback: "Admin") @@ -65,6 +75,16 @@ internal enum L10n { /// No messages internal static let text = L10n.tr("Localizable", "ChatList.NoMessagesLabel.Text", fallback: "No messages") } + internal enum SyncMessages { + internal enum Loading { + /// Loading... + internal static let title = L10n.tr("Localizable", "ChatList.SyncMessages.loading.Title", fallback: "Loading...") + } + internal enum Normal { + /// Sync Messages + internal static let title = L10n.tr("Localizable", "ChatList.SyncMessages.normal.Title", fallback: "Sync Messages") + } + } } internal enum DeleteAccountConfirmation { /// Are you sure you want to delete tour account? All your trees on this device will be lost. diff --git a/TreeTracker/UI/Views/Home/HomeViewController.swift b/TreeTracker/UI/Views/Home/HomeViewController.swift index 5973228c..52235123 100644 --- a/TreeTracker/UI/Views/Home/HomeViewController.swift +++ b/TreeTracker/UI/Views/Home/HomeViewController.swift @@ -153,7 +153,6 @@ class HomeViewController: UIViewController, AlertPresenting { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) viewModel?.fetchProfileData() - viewModel?.syncMessages() viewModel?.fetchUnreadMessagesCount() } diff --git a/TreeTracker/UI/Views/Home/HomeViewModel.swift b/TreeTracker/UI/Views/Home/HomeViewModel.swift index f2dd4560..73696680 100644 --- a/TreeTracker/UI/Views/Home/HomeViewModel.swift +++ b/TreeTracker/UI/Views/Home/HomeViewModel.swift @@ -116,19 +116,6 @@ extension HomeViewModel { unreadMessagesCount = messagingService.getUnreadMessagesCount(for: planter) } - func syncMessages() { - messagingService.syncMessages(for: planter) { [weak self] result in - switch result { - case .success(_): - self?.fetchUnreadMessagesCount() - - case .failure(let error): - // do something - print(error) - } - } - } - } // MARK: - Navigation diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatList.storyboard b/TreeTracker/UI/Views/Messaging/ChatList/ChatList.storyboard index dd32e809..341fddbb 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatList.storyboard +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatList.storyboard @@ -1,9 +1,9 @@ - + - + @@ -18,7 +18,7 @@ - + @@ -46,25 +46,51 @@ + + - + + + + + + + + + diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift index e4b57ebb..149c821a 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewController.swift @@ -8,7 +8,7 @@ import UIKit -class ChatListViewController: UIViewController { +class ChatListViewController: UIViewController, AlertPresenting { @IBOutlet private var chatListTableView: UITableView! { didSet { @@ -40,6 +40,17 @@ class ChatListViewController: UIViewController { } } + @IBOutlet private var syncMessagesButton: SyncMessagesButton! + + @IBOutlet private var lastSyncLabel: UILabel! { + didSet { + lastSyncLabel.text = L10n.ChatList.LastSyncLabel.WithouDate.text + lastSyncLabel.font = FontFamily.Lato.regular.font(size: 14.0) + lastSyncLabel.textColor = Asset.Colors.grayLight.color + lastSyncLabel.numberOfLines = 0 + } + } + var viewModel: ChatListViewModel? { didSet { viewModel?.viewDelegate = self @@ -50,6 +61,16 @@ class ChatListViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) viewModel?.fetchMessages() + viewModel?.getLastSyncTime() + } + +} + +// MARK: - Button Action +private extension ChatListViewController { + + @IBAction func syncMessagesButtonPressed() { + viewModel?.syncMessages() } } @@ -97,4 +118,20 @@ extension ChatListViewController: ChatListViewModelViewDelegate { } } + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateLastSyncTime lastSyncTime: String) { + lastSyncLabel.text = lastSyncTime + } + + func chatListViewModel(_ chatListViewModel: ChatListViewModel, showErrorAlert error: Error) { + present(alert: Alert.error(error)) + } + + func chatListViewModelDidStartSyncingMessages(_ chatListViewModel: ChatListViewModel) { + syncMessagesButton.isLoading = true + } + + func chatListViewModelDidStopSyncingMessages(_ chatListViewModel: ChatListViewModel) { + syncMessagesButton.isLoading = false + } + } diff --git a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift index 4ae1a94d..0a196f33 100644 --- a/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/ChatList/ChatListViewModel.swift @@ -18,6 +18,10 @@ protocol ChatListViewModelCoordinatorDelegate: AnyObject { protocol ChatListViewModelViewDelegate: AnyObject { func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateChatList chatList: [ChatListViewModel.Chat]) + func chatListViewModel(_ chatListViewModel: ChatListViewModel, didUpdateLastSyncTime lastSyncTime: String) + func chatListViewModel(_ chatListViewModel: ChatListViewModel, showErrorAlert error: Error) + func chatListViewModelDidStartSyncingMessages(_ chatListViewModel: ChatListViewModel) + func chatListViewModelDidStopSyncingMessages(_ chatListViewModel: ChatListViewModel) } class ChatListViewModel { @@ -53,6 +57,40 @@ class ChatListViewModel { chatList[indexPath.row] } + func getLastSyncTime() { + guard let date = messagingService.getLastSyncTime() else { + return + } + + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .medium + dateFormatter.timeStyle = .short + let dateString = dateFormatter.string(from: date) + + let lastSyncLabelText = L10n.ChatList.LastSyncLabel.WithDate.text + " " + dateString + + viewDelegate?.chatListViewModel(self, didUpdateLastSyncTime: lastSyncLabelText) + } + + func syncMessages() { + viewDelegate?.chatListViewModelDidStartSyncingMessages(self) + + messagingService.syncMessages(for: planter) { [weak self] result in + guard let self else { return } + + switch result { + case .success(): + self.fetchMessages() + self.getLastSyncTime() + self.viewDelegate?.chatListViewModelDidStopSyncingMessages(self) + + case .failure(let error): + self.viewDelegate?.chatListViewModelDidStopSyncingMessages(self) + self.viewDelegate?.chatListViewModel(self, showErrorAlert: error) + } + } + } + } // MARK: = Messaging diff --git a/TreeTracker/UI/Views/Messaging/ChatList/Components/SyncMessagesButton.swift b/TreeTracker/UI/Views/Messaging/ChatList/Components/SyncMessagesButton.swift new file mode 100644 index 00000000..a7f5320f --- /dev/null +++ b/TreeTracker/UI/Views/Messaging/ChatList/Components/SyncMessagesButton.swift @@ -0,0 +1,95 @@ +// +// SyncMessagesButton.swift +// TreeTracker +// +// Created by Frédéric Helfer on 17/08/23. +// Copyright © 2023 Greenstand. All rights reserved. +// + +import UIKit + +class SyncMessagesButton: UIButton { + + private lazy var loadingSpinner: UIActivityIndicatorView = { + let activityIndicatorView = UIActivityIndicatorView(style: .white) + activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false + return activityIndicatorView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + override var isHighlighted: Bool { + didSet { + updateState() + } + } + + var isLoading: Bool = false { + didSet { + updateUploadState() + } + } +} + +// MARK: - Private +private extension SyncMessagesButton { + + func commonInit() { + backgroundColor = Asset.Colors.primaryGreen.color + tintColor = .white + layer.cornerRadius = 5.0 + layer.masksToBounds = true + setAttributedTitle(normalAttributedTitle, for: .normal) + + addSubview(loadingSpinner) + loadingSpinner.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true + loadingSpinner.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 25.0).isActive = true + + updateState() + updateUploadState() + } + + func updateState() { + if isHighlighted { + backgroundColor = Asset.Colors.secondaryGreen.color + } else { + backgroundColor = Asset.Colors.primaryGreen.color + } + } + + func updateUploadState() { + if isLoading { + loadingSpinner.startAnimating() + setAttributedTitle(loadingAttributedTitle, for: .normal) + } else { + loadingSpinner.stopAnimating() + setAttributedTitle(normalAttributedTitle, for: .normal) + } + } + + var normalAttributedTitle: NSAttributedString { + let attributedString = NSMutableAttributedString() + attributedString.append(NSAttributedString(string: L10n.ChatList.SyncMessages.Normal.title, attributes: textAttributes)) + return attributedString + } + + var loadingAttributedTitle: NSAttributedString { + let attributedString = NSMutableAttributedString() + attributedString.append(NSAttributedString(string: L10n.ChatList.SyncMessages.Loading.title, attributes: textAttributes)) + return attributedString + } + + var textAttributes: [NSAttributedString.Key: Any] { + return [ + .font: FontFamily.Montserrat.semiBold.font(size: 20.0) + ] + } +} From e339db0c7498f883dad13a83da0f1665db4b4966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fre=CC=81de=CC=81ric=20Helfer?= Date: Fri, 18 Aug 2023 11:03:11 -0300 Subject: [PATCH 66/66] changed some methods name on surveyViewModel --- .../Views/Messaging/Survey/SurveyViewController.swift | 6 +++--- .../UI/Views/Messaging/Survey/SurveyViewModel.swift | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift index bf9a7208..d09dabed 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewController.swift @@ -61,12 +61,12 @@ private extension SurveyViewController { extension SurveyViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return viewModel?.numberOfRowsInSection ?? 0 + return viewModel?.numberOfChoices ?? 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: SurveyTableViewCell.identifier, for: indexPath) as? SurveyTableViewCell - let choice = viewModel?.getChoiceForRowAt(indexPath: indexPath) + let choice = viewModel?.choice(atIndex: indexPath.row) cell?.setupCell(choice: choice) cell?.selectionStyle = .none return cell ?? UITableViewCell() @@ -82,7 +82,7 @@ extension SurveyViewController: UITableViewDelegate { } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - viewModel?.didSelectRowAt(indexPath: indexPath) + viewModel?.selectChoice(atIndex: indexPath.row) } } diff --git a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift index 1be1cdeb..9a4719ed 100644 --- a/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift +++ b/TreeTracker/UI/Views/Messaging/Survey/SurveyViewModel.swift @@ -41,12 +41,12 @@ class SurveyViewModel { return "\(title) \(questionIndex + 1)/\(survey.questions.count)" } - var numberOfRowsInSection: Int { + var numberOfChoices: Int { return survey.questions[questionIndex].choices.count } - func getChoiceForRowAt(indexPath: IndexPath) -> Choice { - let choiceText = survey.questions[questionIndex].choices[indexPath.row] + func choice(atIndex index: Int) -> Choice { + let choiceText = survey.questions[questionIndex].choices[index] if survey.surveyResponse.indices.contains(questionIndex) { if survey.surveyResponse[questionIndex] == choiceText { @@ -57,10 +57,10 @@ class SurveyViewModel { return Choice(text: choiceText, isSelected: false) } - func didSelectRowAt(indexPath: IndexPath) { + func selectChoice(atIndex index: Int) { guard survey.response == false else { return } - let selectedChoice = survey.questions[questionIndex].choices[indexPath.row] + let selectedChoice = survey.questions[questionIndex].choices[index] if survey.surveyResponse.indices.contains(questionIndex) { survey.surveyResponse[questionIndex] = selectedChoice } else {