mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-22 07:32:14 +02:00
Merge pull request #4994 from vector-im/gil/4893_support_pagination_in_the_space_summary_api
Support pagination in the Space Summary API
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
class PaginationLoadingViewCell: UITableViewCell, NibReusable, Themable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
@IBOutlet var activityIndicator: UIActivityIndicatorView!
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.activityIndicator.tintColor = theme.colors.tertiaryContent
|
||||
self.activityIndicator.startAnimating()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="68" id="gUf-SL-Ia7" customClass="PaginationLoadingViewCell" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="68"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="gUf-SL-Ia7" id="YoL-49-1Hj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="68"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" animating="YES" style="large" translatesAutoresizingMaskIntoConstraints="NO" id="Tld-uY-uDU">
|
||||
<rect key="frame" x="141.5" y="17" width="37" height="34"/>
|
||||
</activityIndicatorView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="Tld-uY-uDU" secondAttribute="bottom" constant="17" id="cib-oa-sGv"/>
|
||||
<constraint firstItem="Tld-uY-uDU" firstAttribute="top" secondItem="YoL-49-1Hj" secondAttribute="top" constant="17" id="fuX-uS-ylN"/>
|
||||
<constraint firstItem="Tld-uY-uDU" firstAttribute="centerX" secondItem="YoL-49-1Hj" secondAttribute="centerX" id="ots-Sg-fHU"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<viewLayoutGuide key="safeArea" id="wBe-er-msB"/>
|
||||
<connections>
|
||||
<outlet property="activityIndicator" destination="Tld-uY-uDU" id="mq3-lG-y9D"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="137.68115942028987" y="107.14285714285714"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -41,10 +41,11 @@ final class SpaceExploreRoomViewController: UIViewController {
|
||||
private var titleView: MainTitleView!
|
||||
private var emptyView: RootTabEmptyView!
|
||||
private var plusButtonImageView: UIImageView!
|
||||
private var hasMore: Bool = false
|
||||
|
||||
private var itemDataList: [SpaceExploreRoomListItemViewData] = [] {
|
||||
didSet {
|
||||
tableView.reloadData()
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +169,7 @@ final class SpaceExploreRoomViewController: UIViewController {
|
||||
self.tableView.allowsSelection = true
|
||||
self.tableView.register(cellType: SpaceChildViewCell.self)
|
||||
self.tableView.register(cellType: SpaceChildSpaceViewCell.self)
|
||||
self.tableView.register(cellType: PaginationLoadingViewCell.self)
|
||||
self.tableView.tableFooterView = UIView()
|
||||
}
|
||||
|
||||
@@ -177,7 +179,8 @@ final class SpaceExploreRoomViewController: UIViewController {
|
||||
self.renderLoading()
|
||||
case .spaceNameFound(let spaceName):
|
||||
self.titleView.subtitleLabel.text = spaceName
|
||||
case .loaded(let children):
|
||||
case .loaded(let children, let hasMore):
|
||||
self.hasMore = hasMore
|
||||
self.renderLoaded(children: children)
|
||||
case .emptySpace:
|
||||
self.renderEmptySpace()
|
||||
@@ -240,10 +243,16 @@ extension SpaceExploreRoomViewController: UITableViewDataSource {
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return self.itemDataList.count
|
||||
return self.itemDataList.count + (self.hasMore ? 1 : 0)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
guard indexPath.row < self.itemDataList.count else {
|
||||
let cell = tableView.dequeueReusableCell(for: indexPath, cellType: PaginationLoadingViewCell.self)
|
||||
cell.update(theme: self.theme)
|
||||
return cell
|
||||
}
|
||||
|
||||
let viewData = self.itemDataList[indexPath.row]
|
||||
|
||||
let cell = viewData.childInfo.roomType == .space ? tableView.dequeueReusableCell(for: indexPath, cellType: SpaceChildSpaceViewCell.self) : tableView.dequeueReusableCell(for: indexPath, cellType: SpaceChildViewCell.self)
|
||||
@@ -262,6 +271,12 @@ extension SpaceExploreRoomViewController: UITableViewDelegate {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
self.viewModel.process(viewAction: .complete(self.itemDataList[indexPath.row], tableView.cellForRow(at: indexPath)))
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
if self.hasMore && indexPath.row >= self.itemDataList.count {
|
||||
self.viewModel.process(viewAction: .loadData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SpaceExploreRoomViewModelViewDelegate
|
||||
|
||||
@@ -29,13 +29,15 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType {
|
||||
private let spaceName: String?
|
||||
|
||||
private var currentOperation: MXHTTPOperation?
|
||||
private var nextBatch: String?
|
||||
private var rootSpaceChildInfo: MXSpaceChildInfo?
|
||||
|
||||
private var itemDataList: [SpaceExploreRoomListItemViewData] = [] {
|
||||
didSet {
|
||||
self.updateFilteredItemList()
|
||||
}
|
||||
}
|
||||
private var searchKeyword: String? = nil {
|
||||
private var searchKeyword: String? {
|
||||
didSet {
|
||||
self.updateFilteredItemList()
|
||||
}
|
||||
@@ -49,7 +51,7 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType {
|
||||
self.update(viewState: .emptyFilterResult)
|
||||
}
|
||||
} else {
|
||||
self.update(viewState: .loaded(self.filteredItemDataList))
|
||||
self.update(viewState: .loaded(self.filteredItemDataList, self.nextBatch != nil && (self.searchKeyword ?? "").isEmpty))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,32 +92,51 @@ final class SpaceExploreRoomViewModel: SpaceExploreRoomViewModelType {
|
||||
// MARK: - Private
|
||||
|
||||
private func loadData() {
|
||||
guard self.currentOperation == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
if let spaceName = self.spaceName {
|
||||
self.update(viewState: .spaceNameFound(spaceName))
|
||||
}
|
||||
|
||||
self.update(viewState: .loading)
|
||||
if self.nextBatch == nil {
|
||||
self.update(viewState: .loading)
|
||||
}
|
||||
|
||||
self.currentOperation = self.session.spaceService.getSpaceChildrenForSpace(withId: self.spaceId, suggestedOnly: false, limit: nil, completion: { [weak self] response in
|
||||
self.currentOperation = self.session.spaceService.getSpaceChildrenForSpace(withId: self.spaceId, suggestedOnly: false, limit: nil, maxDepth: 1, paginationToken: self.nextBatch, completion: { [weak self] response in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
switch response {
|
||||
case .success(let spaceSummary):
|
||||
self.itemDataList = spaceSummary.childInfos.compactMap({ childInfo in
|
||||
guard childInfo.parentIds.contains(self.spaceId) else {
|
||||
self.nextBatch = spaceSummary.nextBatch
|
||||
// The MXSpaceChildInfo of the root space is available only in the first batch
|
||||
if let rootSpaceInfo = spaceSummary.spaceInfo {
|
||||
self.rootSpaceChildInfo = rootSpaceInfo
|
||||
}
|
||||
|
||||
let batchedItemDataList: [SpaceExploreRoomListItemViewData] = spaceSummary.childInfos.compactMap({ childInfo in
|
||||
guard let rootSpaceInfo = self.rootSpaceChildInfo, rootSpaceInfo.childrenIds.contains(childInfo.childRoomId) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let avatarViewData = AvatarViewData(matrixItemId: childInfo.childRoomId, displayName: childInfo.displayName, avatarUrl: childInfo.avatarUrl, mediaManager: self.session.mediaManager, fallbackImage: .matrixItem(childInfo.childRoomId, childInfo.name))
|
||||
let avatarViewData = AvatarViewData(matrixItemId: childInfo.childRoomId,
|
||||
displayName: childInfo.displayName,
|
||||
avatarUrl: childInfo.avatarUrl,
|
||||
mediaManager: self.session.mediaManager,
|
||||
fallbackImage: .matrixItem(childInfo.childRoomId, childInfo.name))
|
||||
return SpaceExploreRoomListItemViewData(childInfo: childInfo, avatarViewData: avatarViewData)
|
||||
}).sorted(by: { item1, item2 in
|
||||
return !item2.childInfo.suggested || item1.childInfo.suggested
|
||||
})
|
||||
self.itemDataList.append(contentsOf: batchedItemDataList)
|
||||
case .failure(let error):
|
||||
self.update(viewState: .error(error))
|
||||
}
|
||||
|
||||
self.currentOperation = nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import Foundation
|
||||
enum SpaceExploreRoomViewState {
|
||||
case loading
|
||||
case spaceNameFound(_ spaceName: String)
|
||||
case loaded(_ children: [SpaceExploreRoomListItemViewData])
|
||||
case loaded(_ children: [SpaceExploreRoomListItemViewData], _ hasMore: Bool)
|
||||
case emptySpace
|
||||
case emptyFilterResult
|
||||
case error(Error)
|
||||
|
||||
Reference in New Issue
Block a user