parent
5b8e97287e
commit
5f9fe505d5
|
@ -305,6 +305,8 @@
|
|||
D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */; };
|
||||
D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */; };
|
||||
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */; };
|
||||
D6DEA0DE268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DEA0DC268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift */; };
|
||||
D6DEA0DF268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6DEA0DD268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib */; };
|
||||
D6DF95C12533F5DE0027A9B6 /* RelationshipMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DF95C02533F5DE0027A9B6 /* RelationshipMO.swift */; };
|
||||
D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */; };
|
||||
D6DFC6A0242C4CCC00ACC392 /* WeakArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */; };
|
||||
|
@ -709,6 +711,8 @@
|
|||
D6D4DDF1212518A200E1C4BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningCopyMode.swift; sourceTree = "<group>"; };
|
||||
D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Preferences+Notification.swift"; sourceTree = "<group>"; };
|
||||
D6DEA0DC268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmLoadMoreTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D6DEA0DD268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConfirmLoadMoreTableViewCell.xib; sourceTree = "<group>"; };
|
||||
D6DF95C02533F5DE0027A9B6 /* RelationshipMO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationshipMO.swift; sourceTree = "<group>"; };
|
||||
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackpadScrollGestureRecognizer.swift; sourceTree = "<group>"; };
|
||||
D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakArray.swift; sourceTree = "<group>"; };
|
||||
|
@ -1465,6 +1469,7 @@
|
|||
D6A3BC872321F78000FD64D5 /* Account Cell */,
|
||||
D611C2CC232DC5FC00C86A49 /* Hashtag Cell */,
|
||||
D61AC1DA232EA43100C54D2D /* Instance Cell */,
|
||||
D6DEA0DB268400AF00FE896A /* Confirm Load More Cell */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1595,6 +1600,15 @@
|
|||
path = TuskerUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D6DEA0DB268400AF00FE896A /* Confirm Load More Cell */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D6DEA0DC268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift */,
|
||||
D6DEA0DD268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib */,
|
||||
);
|
||||
path = "Confirm Load More Cell";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D6E343A9265AAD6B00C4AA01 /* OpenInTusker */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1889,6 +1903,7 @@
|
|||
D6F2E966249E8BFD005846BB /* CrashReporterViewController.xib in Resources */,
|
||||
D662AEF0263A3B880082A153 /* PollFinishedTableViewCell.xib in Resources */,
|
||||
D6A4DCCD2553667800D9DE31 /* FastAccountSwitcherViewController.xib in Resources */,
|
||||
D6DEA0DF268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -2022,6 +2037,7 @@
|
|||
D6412B0324AFF6A600F5412E /* TabBarScrollableViewController.swift in Sources */,
|
||||
D6757A822157E8FA00721E32 /* XCBSession.swift in Sources */,
|
||||
D6093FB725BE0CF3004811E6 /* HashtagHistoryView.swift in Sources */,
|
||||
D6DEA0DE268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift in Sources */,
|
||||
D6EBF01723C55E0D00AE061B /* UISceneSession+MastodonController.swift in Sources */,
|
||||
04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */,
|
||||
D68E525D24A3E8F00054355A /* SearchViewController.swift in Sources */,
|
||||
|
|
|
@ -63,6 +63,7 @@ class Preferences: Codable, ObservableObject {
|
|||
self.showFavoriteAndReblogCounts = try container.decode(Bool.self, forKey: .showFavoriteAndReblogCounts)
|
||||
self.defaultNotificationsMode = try container.decode(NotificationsMode.self, forKey: .defaultNotificationsType)
|
||||
self.grayscaleImages = try container.decodeIfPresent(Bool.self, forKey: .grayscaleImages) ?? false
|
||||
self.disableInfiniteScrolling = try container.decodeIfPresent(Bool.self, forKey: .disableInfiniteScrolling) ?? false
|
||||
|
||||
self.silentActions = try container.decode([String: Permission].self, forKey: .silentActions)
|
||||
self.statusContentType = try container.decode(StatusContentType.self, forKey: .statusContentType)
|
||||
|
@ -97,6 +98,7 @@ class Preferences: Codable, ObservableObject {
|
|||
try container.encode(showFavoriteAndReblogCounts, forKey: .showFavoriteAndReblogCounts)
|
||||
try container.encode(defaultNotificationsMode, forKey: .defaultNotificationsType)
|
||||
try container.encode(grayscaleImages, forKey: .grayscaleImages)
|
||||
try container.encode(disableInfiniteScrolling, forKey: .disableInfiniteScrolling)
|
||||
|
||||
try container.encode(silentActions, forKey: .silentActions)
|
||||
try container.encode(statusContentType, forKey: .statusContentType)
|
||||
|
@ -133,6 +135,7 @@ class Preferences: Codable, ObservableObject {
|
|||
@Published var showFavoriteAndReblogCounts = true
|
||||
@Published var defaultNotificationsMode = NotificationsMode.allNotifications
|
||||
@Published var grayscaleImages = false
|
||||
@Published var disableInfiniteScrolling = false
|
||||
|
||||
// MARK: Advanced
|
||||
@Published var silentActions: [String: Permission] = [:]
|
||||
|
@ -165,6 +168,7 @@ class Preferences: Codable, ObservableObject {
|
|||
case showFavoriteAndReblogCounts
|
||||
case defaultNotificationsType
|
||||
case grayscaleImages
|
||||
case disableInfiniteScrolling
|
||||
|
||||
case silentActions
|
||||
case statusContentType
|
||||
|
|
|
@ -16,6 +16,7 @@ struct WellnessPrefsView: View {
|
|||
showFavAndReblogCount
|
||||
notificationsMode
|
||||
grayscaleImages
|
||||
disableInfiniteScrolling
|
||||
}
|
||||
.listStyle(InsetGroupedListStyle())
|
||||
.navigationBarTitle(Text("Digital Wellness"))
|
||||
|
@ -46,6 +47,14 @@ struct WellnessPrefsView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var disableInfiniteScrolling: some View {
|
||||
Section(footer: Text("Require a button tap before loading more posts.")) {
|
||||
Toggle(isOn: $preferences.disableInfiniteScrolling) {
|
||||
Text("Disable Infinite Scrolling")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WellnessPrefsView_Previews: PreviewProvider {
|
||||
|
|
|
@ -19,6 +19,8 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
|
|||
private var newer: RequestRange?
|
||||
private var older: RequestRange?
|
||||
|
||||
private var didConfirmLoadMore = false
|
||||
|
||||
init(for timeline: Timeline, mastodonController: MastodonController) {
|
||||
self.timeline = timeline
|
||||
self.mastodonController = mastodonController
|
||||
|
@ -53,6 +55,7 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
|
|||
super.viewDidLoad()
|
||||
|
||||
tableView.register(UINib(nibName: "TimelineStatusTableViewCell", bundle: .main), forCellReuseIdentifier: "statusCell")
|
||||
tableView.register(UINib(nibName: "ConfirmLoadMoreTableViewCell", bundle: .main), forCellReuseIdentifier: "confirmLoadMoreCell")
|
||||
}
|
||||
|
||||
// MARK: - DiffableTimelineLikeTableViewController
|
||||
|
@ -61,16 +64,28 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
|
|||
return NSLocalizedString("Refresh Statuses", comment: "refresh status command discoverability title")
|
||||
}
|
||||
|
||||
override func timelineContentSections() -> [Section] {
|
||||
return [.statuses]
|
||||
}
|
||||
|
||||
override func cellProvider(_ tableView: UITableView, _ indexPath: IndexPath, _ item: Item) -> UITableViewCell? {
|
||||
guard case let .status(id: id, state: state) = item,
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else {
|
||||
return nil
|
||||
switch item {
|
||||
case let .status(id: id, state: state):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as! TimelineStatusTableViewCell
|
||||
|
||||
cell.delegate = self
|
||||
cell.updateUI(statusID: id, state: state)
|
||||
return cell
|
||||
|
||||
case .confirmLoadMore:
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "confirmLoadMoreCell", for: indexPath) as! ConfirmLoadMoreTableViewCell
|
||||
cell.confirmLoadMore = {
|
||||
self.didConfirmLoadMore = true
|
||||
self.loadOlder()
|
||||
self.didConfirmLoadMore = false
|
||||
}
|
||||
return cell
|
||||
}
|
||||
|
||||
cell.delegate = self
|
||||
cell.updateUI(statusID: id, state: state)
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
override func loadInitialItems(completion: @escaping (LoadResult) -> Void) {
|
||||
|
@ -106,6 +121,20 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
|
|||
return
|
||||
}
|
||||
|
||||
guard !Preferences.shared.disableInfiniteScrolling || didConfirmLoadMore else {
|
||||
guard !currentSnapshot.sectionIdentifiers.contains(.footer) || !currentSnapshot.itemIdentifiers(inSection: .footer).contains(.confirmLoadMore) else {
|
||||
// todo: need something more accurate than "success"/"failure"
|
||||
completion(.success(currentSnapshot))
|
||||
return
|
||||
}
|
||||
var snapshot = currentSnapshot
|
||||
snapshot.appendSections([.footer])
|
||||
snapshot.appendItems([.confirmLoadMore], toSection: .footer)
|
||||
self.dataSource.apply(snapshot)
|
||||
completion(.success(snapshot))
|
||||
return
|
||||
}
|
||||
|
||||
let request = Client.getStatuses(timeline: timeline, range: older)
|
||||
|
||||
mastodonController.run(request) { response in
|
||||
|
@ -119,6 +148,7 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
|
|||
self.mastodonController.persistentContainer.addAll(statuses: statuses) {
|
||||
var snapshot = currentSnapshot
|
||||
snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown) }, toSection: .statuses)
|
||||
snapshot.deleteSections([.footer])
|
||||
completion(.success(snapshot))
|
||||
}
|
||||
}
|
||||
|
@ -173,6 +203,7 @@ extension TimelineTableViewController {
|
|||
}
|
||||
enum Item: Hashable {
|
||||
case status(id: String, state: StatusState)
|
||||
case confirmLoadMore
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// ConfirmLoadMoreTableViewCell.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 6/23/21.
|
||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
protocol ConfirmLoadOlderTableViewCellDelegate: AnyObject {
|
||||
func confirmLoadMore()
|
||||
}
|
||||
|
||||
class ConfirmLoadMoreTableViewCell: UITableViewCell {
|
||||
|
||||
var confirmLoadMore: (() -> Void)?
|
||||
|
||||
@IBOutlet weak var confirmButton: UIButton!
|
||||
|
||||
private var isLoading = false
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
var config = UIButton.Configuration.tinted()
|
||||
config.title = "Load More"
|
||||
config.showsActivityIndicator = false
|
||||
config.imagePadding = 4
|
||||
confirmButton.configuration = config
|
||||
confirmButton.configurationUpdateHandler = { [unowned self] button in
|
||||
button.configuration?.showsActivityIndicator = self.isLoading
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
|
||||
isLoading = false
|
||||
if #available(iOS 15.0, *) {
|
||||
confirmButton.setNeedsUpdateConfiguration()
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func loadMorePressed(_ sender: Any) {
|
||||
confirmLoadMore?()
|
||||
if #available(iOS 15.0, *) {
|
||||
isLoading = true
|
||||
confirmButton.setNeedsUpdateConfiguration()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19115.3" 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="19107.5"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.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="105" id="KGk-i7-Jjw" customClass="ConfirmLoadMoreTableViewCell" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="105"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="105"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="Rpx-45-c2n">
|
||||
<rect key="frame" x="16" y="11" width="288" height="86"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Infinite scrolling is off. Do you want to keep going?" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9nv-Re-5sL">
|
||||
<rect key="frame" x="0.0" y="0.0" width="288" height="41"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NT9-ly-efr">
|
||||
<rect key="frame" x="0.0" y="45" width="288" height="41"/>
|
||||
<state key="normal" title="Button"/>
|
||||
<buttonConfiguration key="configuration" style="tinted" title="Load More"/>
|
||||
<connections>
|
||||
<action selector="loadMorePressed:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="Pgz-MB-icB"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Rpx-45-c2n" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="YfZ-rr-Omf"/>
|
||||
<constraint firstItem="Rpx-45-c2n" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" id="hhi-yX-Wa4"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="Rpx-45-c2n" secondAttribute="trailing" id="jI8-St-34M"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Rpx-45-c2n" secondAttribute="bottom" constant="8" id="mQh-0l-Eo2"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
|
||||
<connections>
|
||||
<outlet property="confirmButton" destination="NT9-ly-efr" id="Lja-th-LeH"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="131.8840579710145" y="150.33482142857142"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<systemColor name="secondaryLabelColor">
|
||||
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="secondarySystemBackgroundColor">
|
||||
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
Loading…
Reference in New Issue