Show message on remote profiles with no statuses

Closes #279
This commit is contained in:
Shadowfacts 2023-02-22 22:23:18 -05:00
parent 2582907919
commit 8deb502140
3 changed files with 107 additions and 2 deletions

View File

@ -222,6 +222,7 @@
D690797324A4EF9700023A34 /* UIBezierPath+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */; }; D690797324A4EF9700023A34 /* UIBezierPath+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */; };
D691771129A2B76A0054D7EF /* MainActor+Unsafe.swift in Sources */ = {isa = PBXBuildFile; fileRef = D691771029A2B76A0054D7EF /* MainActor+Unsafe.swift */; }; D691771129A2B76A0054D7EF /* MainActor+Unsafe.swift in Sources */ = {isa = PBXBuildFile; fileRef = D691771029A2B76A0054D7EF /* MainActor+Unsafe.swift */; };
D691771529A6FCAB0054D7EF /* StateRestorableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D691771429A6FCAB0054D7EF /* StateRestorableViewController.swift */; }; D691771529A6FCAB0054D7EF /* StateRestorableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D691771429A6FCAB0054D7EF /* StateRestorableViewController.swift */; };
D691771729A710520054D7EF /* ProfileNoContentCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D691771629A710520054D7EF /* ProfileNoContentCollectionViewCell.swift */; };
D693A72825CF282E003A14E2 /* TrendingHashtagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */; }; D693A72825CF282E003A14E2 /* TrendingHashtagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */; };
D693A72A25CF8C1E003A14E2 /* ProfileDirectoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */; }; D693A72A25CF8C1E003A14E2 /* ProfileDirectoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */; };
D693A72F25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */; }; D693A72F25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */; };
@ -639,6 +640,7 @@
D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBezierPath+Helpers.swift"; sourceTree = "<group>"; }; D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBezierPath+Helpers.swift"; sourceTree = "<group>"; };
D691771029A2B76A0054D7EF /* MainActor+Unsafe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainActor+Unsafe.swift"; sourceTree = "<group>"; }; D691771029A2B76A0054D7EF /* MainActor+Unsafe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainActor+Unsafe.swift"; sourceTree = "<group>"; };
D691771429A6FCAB0054D7EF /* StateRestorableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateRestorableViewController.swift; sourceTree = "<group>"; }; D691771429A6FCAB0054D7EF /* StateRestorableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateRestorableViewController.swift; sourceTree = "<group>"; };
D691771629A710520054D7EF /* ProfileNoContentCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileNoContentCollectionViewCell.swift; sourceTree = "<group>"; };
D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagsViewController.swift; sourceTree = "<group>"; }; D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagsViewController.swift; sourceTree = "<group>"; };
D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDirectoryViewController.swift; sourceTree = "<group>"; }; D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDirectoryViewController.swift; sourceTree = "<group>"; };
D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturedProfileCollectionViewCell.swift; sourceTree = "<group>"; }; D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturedProfileCollectionViewCell.swift; sourceTree = "<group>"; };
@ -1096,6 +1098,7 @@
D61DC84C28F500D200B82C6E /* ProfileViewController.swift */, D61DC84C28F500D200B82C6E /* ProfileViewController.swift */,
D61ABEF728EFC3F900B29151 /* ProfileStatusesViewController.swift */, D61ABEF728EFC3F900B29151 /* ProfileStatusesViewController.swift */,
D61DC84A28F4FD2000B82C6E /* ProfileHeaderCollectionViewCell.swift */, D61DC84A28F4FD2000B82C6E /* ProfileHeaderCollectionViewCell.swift */,
D691771629A710520054D7EF /* ProfileNoContentCollectionViewCell.swift */,
); );
path = Profile; path = Profile;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1969,6 +1972,7 @@
0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */, 0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */,
D6C3F4F5298ED0890009FCFF /* LocalPredicateStatusesViewController.swift in Sources */, D6C3F4F5298ED0890009FCFF /* LocalPredicateStatusesViewController.swift in Sources */,
D662AEEF263A3B880082A153 /* PollFinishedTableViewCell.swift in Sources */, D662AEEF263A3B880082A153 /* PollFinishedTableViewCell.swift in Sources */,
D691771729A710520054D7EF /* ProfileNoContentCollectionViewCell.swift in Sources */,
D626493C23C1000300612E6E /* AlbumTableViewCell.swift in Sources */, D626493C23C1000300612E6E /* AlbumTableViewCell.swift in Sources */,
D62275A624F1C81800B82A16 /* ComposeReplyView.swift in Sources */, D62275A624F1C81800B82A16 /* ComposeReplyView.swift in Sources */,
D60E2F292442372B005F8713 /* AccountMO.swift in Sources */, D60E2F292442372B005F8713 /* AccountMO.swift in Sources */,

View File

@ -0,0 +1,81 @@
//
// ProfileNoContentCollectionViewCell.swift
// Tusker
//
// Created by Shadowfacts on 2/22/23.
// Copyright © 2023 Shadowfacts. All rights reserved.
//
import UIKit
class ProfileNoContentCollectionViewCell: UICollectionViewListCell {
weak var delegate: TuskerNavigationDelegate?
private var accountURL: URL?
private var button: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
let title = UILabel()
title.text = "There's nothing here"
title.adjustsFontForContentSizeCategory = true
title.font = .preferredFont(forTextStyle: .headline)
title.numberOfLines = 0
title.textAlignment = .center
title.textColor = .secondaryLabel
let body = UILabel()
body.text = "Your instance may not show all of the posts from accounts on other instances."
body.adjustsFontForContentSizeCategory = true
body.font = .preferredFont(forTextStyle: .body)
body.numberOfLines = 0
body.textAlignment = .center
body.textColor = .secondaryLabel
button = UIButton(configuration: .plain(), primaryAction: UIAction(handler: { [unowned self] _ in
if let delegate = self.delegate,
let accountURL = self.accountURL {
delegate.selected(url: accountURL)
}
}))
let stack = UIStackView(arrangedSubviews: [
title,
body,
button,
])
stack.axis = .vertical
stack.alignment = .center
stack.spacing = 4
stack.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(stack)
NSLayoutConstraint.activate([
stack.leadingAnchor.constraint(equalToSystemSpacingAfter: contentView.leadingAnchor, multiplier: 1),
contentView.trailingAnchor.constraint(equalToSystemSpacingAfter: stack.trailingAnchor, multiplier: 1),
stack.topAnchor.constraint(equalToSystemSpacingBelow: contentView.topAnchor, multiplier: 1),
contentView.bottomAnchor.constraint(equalToSystemSpacingBelow: stack.bottomAnchor, multiplier: 1),
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func updateUI(accountURL: URL) {
self.accountURL = accountURL
var title: AttributedString = "View on "
var host = AttributedString(accountURL.host!)
host.font = .preferredFont(forTextStyle: .body).withTraits(.traitBold)
title += host
var config = UIButton.Configuration.plain()
config.attributedTitle = title
config.image = UIImage(systemName: "safari")
config.imagePadding = 4
button.configuration = config
}
}

View File

@ -159,6 +159,10 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
} }
let zeroHeightCell = UICollectionView.CellRegistration<ZeroHeightCollectionViewCell, Void> { _, _, _ in let zeroHeightCell = UICollectionView.CellRegistration<ZeroHeightCollectionViewCell, Void> { _, _, _ in
} }
let noContentCell = UICollectionView.CellRegistration<ProfileNoContentCollectionViewCell, URL> { [unowned self] cell, _, item in
cell.delegate = self
cell.updateUI(accountURL: item)
}
return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in
switch itemIdentifier { switch itemIdentifier {
case .header(let id): case .header(let id):
@ -197,6 +201,8 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
return loadingIndicatorCell(for: indexPath) return loadingIndicatorCell(for: indexPath)
case .confirmLoadMore: case .confirmLoadMore:
return confirmLoadMoreCell(for: indexPath) return confirmLoadMoreCell(for: indexPath)
case .noContent(let accountURL):
return collectionView.dequeueConfiguredReusableCell(using: noContentCell, for: indexPath, item: accountURL)
} }
} }
} }
@ -225,7 +231,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
guard isViewLoaded, guard isViewLoaded,
let accountID, let accountID,
state == .unloaded, state == .unloaded,
mastodonController.persistentContainer.account(for: accountID) != nil else { let account = mastodonController.persistentContainer.account(for: accountID) else {
return return
} }
@ -246,6 +252,14 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
await controller.loadInitial() await controller.loadInitial()
await tryLoadPinned() await tryLoadPinned()
var newSnapshot = dataSource.snapshot()
if newSnapshot.numberOfItems(inSection: .pinned) == 0,
newSnapshot.numberOfItems(inSection: .statuses) == 0,
account.url.host != mastodonController.instanceURL.host {
newSnapshot.appendItems([.noContent(accountURL: account.url)], toSection: .pinned)
await apply(newSnapshot, animatingDifferences: true)
}
state = .loaded state = .loaded
// remove any content inset that was added when switching pages to this VC // remove any content inset that was added when switching pages to this VC
@ -410,6 +424,7 @@ extension ProfileStatusesViewController {
case status(id: String, collapseState: CollapseState, filterState: FilterState, pinned: Bool) case status(id: String, collapseState: CollapseState, filterState: FilterState, pinned: Bool)
case loadingIndicator case loadingIndicator
case confirmLoadMore case confirmLoadMore
case noContent(accountURL: URL)
static func fromTimelineItem(_ item: String) -> Self { static func fromTimelineItem(_ item: String) -> Self {
return .status(id: item, collapseState: .unknown, filterState: .unknown, pinned: false) return .status(id: item, collapseState: .unknown, filterState: .unknown, pinned: false)
@ -425,6 +440,8 @@ extension ProfileStatusesViewController {
return true return true
case (.confirmLoadMore, .confirmLoadMore): case (.confirmLoadMore, .confirmLoadMore):
return true return true
case (.noContent(let a), .noContent(let b)):
return a == b
default: default:
return false return false
} }
@ -443,12 +460,15 @@ extension ProfileStatusesViewController {
hasher.combine(2) hasher.combine(2)
case .confirmLoadMore: case .confirmLoadMore:
hasher.combine(3) hasher.combine(3)
case .noContent(let accountURL):
hasher.combine(4)
hasher.combine(accountURL)
} }
} }
var hideSeparators: Bool { var hideSeparators: Bool {
switch self { switch self {
case .loadingIndicator, .confirmLoadMore: case .loadingIndicator, .confirmLoadMore, .noContent(_):
return true return true
default: default:
return false return false