mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-23 18:12:44 +02:00
Message edits history: Update EditHistoryViewController to use UITableView to display messages and dates.
This commit is contained in:
@@ -18,70 +18,26 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9U2-KL-ZVA">
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="iJl-eI-xdT">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="e7g-um-WO4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="500"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="voD-3Q-ryt">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="500"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="A message" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
|
||||
<rect key="frame" x="20" y="86" width="335" height="250"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DOt-5E-FjF">
|
||||
<rect key="frame" x="104" y="368" width="167" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<state key="normal" title="Load more"/>
|
||||
<connections>
|
||||
<action selector="loadMoreButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="uvI-tt-Nfj"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" priority="750" constant="500" id="glD-Sz-73O"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="voD-3Q-ryt" secondAttribute="bottom" id="63a-5e-ptU"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="centerX" secondItem="e7g-um-WO4" secondAttribute="centerX" id="P2G-mq-gQW"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="voD-3Q-ryt" secondAttribute="trailing" id="QgV-SO-5yf"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="e7g-um-WO4" secondAttribute="leading" id="YPo-u1-PtT"/>
|
||||
<constraint firstAttribute="height" constant="500" id="n8g-KW-BYU"/>
|
||||
<constraint firstItem="voD-3Q-ryt" firstAttribute="top" secondItem="e7g-um-WO4" secondAttribute="top" id="rhQ-96-szL"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="e7g-um-WO4" secondAttribute="trailing" id="GyG-Fh-PME"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="width" secondItem="9U2-KL-ZVA" secondAttribute="width" id="Ok2-WQ-Zgc"/>
|
||||
<constraint firstAttribute="bottom" secondItem="e7g-um-WO4" secondAttribute="bottom" constant="147" id="Y46-NP-zAc"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="leading" secondItem="9U2-KL-ZVA" secondAttribute="leading" id="aoV-Yh-AcD"/>
|
||||
<constraint firstItem="e7g-um-WO4" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="pFN-bA-SHw"/>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="V8j-Lb-PgC" id="TKo-2F-ubj"/>
|
||||
<outlet property="delegate" destination="V8j-Lb-PgC" id="QSd-c9-l6N"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="9U2-KL-ZVA" secondAttribute="bottom" id="7Cb-nY-CsO"/>
|
||||
<constraint firstItem="9U2-KL-ZVA" firstAttribute="leading" secondItem="bFg-jh-JZB" secondAttribute="leading" id="GdQ-hK-muG"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="trailing" secondItem="9U2-KL-ZVA" secondAttribute="trailing" id="sbD-ek-vGJ"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="wTB-V6-IHV"/>
|
||||
<constraint firstItem="iJl-eI-xdT" firstAttribute="top" secondItem="bFg-jh-JZB" secondAttribute="top" id="A4b-5i-zYL"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="trailing" secondItem="iJl-eI-xdT" secondAttribute="trailing" id="F8l-eq-shB"/>
|
||||
<constraint firstAttribute="bottom" secondItem="iJl-eI-xdT" secondAttribute="bottom" id="jjr-KJ-D44"/>
|
||||
<constraint firstItem="iJl-eI-xdT" firstAttribute="leading" secondItem="bFg-jh-JZB" secondAttribute="leading" id="lyi-X4-jJ7"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="loadMoreButton" destination="DOt-5E-FjF" id="ktw-U4-efQ"/>
|
||||
<outlet property="messageLabel" destination="bxI-mu-qng" id="pbX-aZ-inC"/>
|
||||
<outlet property="scrollView" destination="9U2-KL-ZVA" id="ojG-2y-X7b"/>
|
||||
<outlet property="tableView" destination="iJl-eI-xdT" id="sV4-yD-YJq"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="zK0-v6-7Wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
|
||||
@@ -17,31 +17,47 @@
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
final class EditHistoryViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let aConstant: Int = 666
|
||||
static let estimatedRowHeight: CGFloat = 38.0
|
||||
static let estimatedSectionHeaderHeight: CGFloat = 28.0
|
||||
static let editHistoryMessageTimeFormat = "HH:mm"
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var scrollView: UIScrollView!
|
||||
|
||||
@IBOutlet private weak var messageLabel: UILabel!
|
||||
@IBOutlet private weak var loadMoreButton: UIButton!
|
||||
@IBOutlet private weak var tableView: UITableView!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: EditHistoryViewModelType!
|
||||
private var theme: Theme!
|
||||
private var keyboardAvoider: KeyboardAvoider?
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
private var activityIndicatorPresenter: ActivityIndicatorPresenter!
|
||||
|
||||
private var editHistorySections: [EditHistorySection] = []
|
||||
|
||||
private lazy var sectionDateFormatter: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateStyle = .full
|
||||
dateFormatter.timeStyle = .none
|
||||
dateFormatter.doesRelativeDateFormatting = true
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
private lazy var messageDateFormatter: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = Constants.editHistoryMessageTimeFormat
|
||||
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@@ -59,11 +75,10 @@ final class EditHistoryViewController: UIViewController {
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.title = "Edits history"
|
||||
self.title = VectorL10n.roomMessageEditsHistoryTitle
|
||||
|
||||
self.setupViews()
|
||||
self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView)
|
||||
self.activityPresenter = ActivityIndicatorPresenter()
|
||||
self.activityIndicatorPresenter = ActivityIndicatorPresenter()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
@@ -73,18 +88,6 @@ final class EditHistoryViewController: UIViewController {
|
||||
|
||||
self.viewModel.process(viewAction: .loadMore)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.keyboardAvoider?.startAvoiding()
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
self.keyboardAvoider?.stopAvoiding()
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
@@ -95,18 +98,12 @@ final class EditHistoryViewController: UIViewController {
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
self.view.backgroundColor = theme.backgroundColor
|
||||
self.tableView.backgroundColor = theme.backgroundColor
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
|
||||
|
||||
// TODO:
|
||||
self.messageLabel.textColor = theme.textPrimaryColor
|
||||
|
||||
self.loadMoreButton.backgroundColor = theme.backgroundColor
|
||||
theme.applyStyle(onButton: self.loadMoreButton)
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
@@ -118,74 +115,109 @@ final class EditHistoryViewController: UIViewController {
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
let closeBarButtonItem = MXKBarButtonItem(title: "Close", style: .plain) { [weak self] in
|
||||
let closeBarButtonItem = MXKBarButtonItem(title: VectorL10n.close, style: .plain) { [weak self] in
|
||||
self?.closeButtonAction()
|
||||
}
|
||||
|
||||
self.navigationItem.rightBarButtonItem = closeBarButtonItem
|
||||
|
||||
self.scrollView.keyboardDismissMode = .interactive
|
||||
self.setupTableView()
|
||||
}
|
||||
|
||||
private func setupTableView() {
|
||||
self.tableView.rowHeight = UITableView.automaticDimension
|
||||
self.tableView.estimatedRowHeight = Constants.estimatedRowHeight
|
||||
self.tableView.register(cellType: EditHistoryCell.self)
|
||||
|
||||
self.messageLabel.text = "VectorL10n.editHistoryTitle"
|
||||
self.messageLabel.isHidden = true
|
||||
self.tableView.sectionHeaderHeight = UITableView.automaticDimension
|
||||
self.tableView.estimatedSectionHeaderHeight = Constants.estimatedSectionHeaderHeight
|
||||
self.tableView.register(headerFooterViewType: EditHistoryHeaderView.self)
|
||||
|
||||
self.tableView.tableFooterView = UIView()
|
||||
}
|
||||
|
||||
private func render(viewState: EditHistoryViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(let messages, let addedCount):
|
||||
self.renderLoaded(messages: messages, addedCount: addedCount)
|
||||
case .allLoaded:
|
||||
self.renderAllLoaded()
|
||||
case .loaded(let sections, let addedCount, let allDataLoaded):
|
||||
self.renderLoaded(sections: sections, addedCount: addedCount, allDataLoaded: allDataLoaded)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
self.activityIndicatorPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(messages: [EditHistoryMessage], addedCount: Int) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
let calendar = Calendar.current
|
||||
|
||||
let attributedText = NSMutableAttributedString()
|
||||
for message in messages {
|
||||
let time=calendar.dateComponents([.hour, .minute], from: message.date)
|
||||
attributedText.append(NSAttributedString(string: "\(time.hour!):\(time.minute!)"))
|
||||
attributedText.append(NSAttributedString(string: " - "))
|
||||
attributedText.append(message.message)
|
||||
attributedText.append(NSAttributedString(string: "\n"))
|
||||
}
|
||||
|
||||
self.messageLabel.attributedText = attributedText
|
||||
self.messageLabel.isHidden = false
|
||||
}
|
||||
|
||||
private func renderAllLoaded() {
|
||||
self.loadMoreButton.isHidden = true
|
||||
private func renderLoaded(sections: [EditHistorySection], addedCount: Int, allDataLoaded: Bool) {
|
||||
self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.editHistorySections = sections
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func loadMoreButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .loadMore)
|
||||
}
|
||||
|
||||
private func closeButtonAction() {
|
||||
self.viewModel.process(viewAction: .close)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
extension EditHistoryViewController: UITableViewDataSource {
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return self.editHistorySections.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return self.editHistorySections[section].messages.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let editHistoryCell = tableView.dequeueReusableCell(for: indexPath, cellType: EditHistoryCell.self)
|
||||
|
||||
let editHistoryMessage = self.editHistorySections[indexPath.section].messages[indexPath.row]
|
||||
|
||||
let timeString = self.messageDateFormatter.string(from: editHistoryMessage.date)
|
||||
|
||||
editHistoryCell.update(theme: self.theme)
|
||||
editHistoryCell.fill(with: timeString, and: editHistoryMessage.message)
|
||||
|
||||
return editHistoryCell
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
guard let editHistoryHeaderView: EditHistoryHeaderView = tableView.dequeueReusableHeaderFooterView() else {
|
||||
return nil
|
||||
}
|
||||
let editHistorySection = self.editHistorySections[section]
|
||||
let dateString = self.sectionDateFormatter.string(from: editHistorySection.date)
|
||||
|
||||
editHistoryHeaderView.update(theme: self.theme)
|
||||
editHistoryHeaderView.fill(with: dateString)
|
||||
return editHistoryHeaderView
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
extension EditHistoryViewController: UITableViewDelegate {
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
|
||||
// Check if a scroll beyond scroll view content occurs
|
||||
let distanceFromBottom = scrollView.contentSize.height - scrollView.contentOffset.y
|
||||
if distanceFromBottom < scrollView.frame.size.height {
|
||||
self.viewModel.process(viewAction: .loadMore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - EditHistoryViewModelViewDelegate
|
||||
extension EditHistoryViewController: EditHistoryViewModelViewDelegate {
|
||||
|
||||
Reference in New Issue
Block a user