forked from shadowfacts/Tusker
Add Profile Directory
This commit is contained in:
parent
6a927e4092
commit
bbb8707cb7
|
@ -323,7 +323,7 @@ public class Client {
|
|||
return request
|
||||
}
|
||||
|
||||
// MARK: - Trends
|
||||
// MARK: - Instance
|
||||
public static func getTrends(limit: Int? = nil) -> Request<[Hashtag]> {
|
||||
let parameters: [Parameter]
|
||||
if let limit = limit {
|
||||
|
@ -334,6 +334,20 @@ public class Client {
|
|||
return Request<[Hashtag]>(method: .get, path: "/api/v1/trends", queryParameters: parameters)
|
||||
}
|
||||
|
||||
public static func getFeaturedProfiles(local: Bool, order: DirectoryOrder, offset: Int? = nil, limit: Int? = nil) -> Request<[Account]> {
|
||||
var parameters = [
|
||||
"order" => order.rawValue,
|
||||
"local" => local,
|
||||
]
|
||||
if let offset = offset {
|
||||
parameters.append("offset" => offset)
|
||||
}
|
||||
if let limit = limit {
|
||||
parameters.append("limit" => limit)
|
||||
}
|
||||
return Request<[Account]>(method: .get, path: "/api/v1/directory", queryParameters: parameters)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Client {
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// DirectoryOrder.swift
|
||||
// Pachyderm
|
||||
//
|
||||
// Created by Shadowfacts on 2/6/21.
|
||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum DirectoryOrder: String {
|
||||
case active
|
||||
case new
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */; };
|
||||
04DACE8E212CC7CC009840C4 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DACE8D212CC7CC009840C4 /* ImageCache.swift */; };
|
||||
04ED00B121481ED800567C53 /* SteppedProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04ED00B021481ED800567C53 /* SteppedProgressView.swift */; };
|
||||
D600613E25D07E170067FAD6 /* ProfileDirectoryFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D600613D25D07E170067FAD6 /* ProfileDirectoryFilterView.swift */; };
|
||||
D6093F9B25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6093F9A25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift */; };
|
||||
D6093FB025BE0B01004811E6 /* TrendingHashtagTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6093FAE25BE0B01004811E6 /* TrendingHashtagTableViewCell.swift */; };
|
||||
D6093FB125BE0B01004811E6 /* TrendingHashtagTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6093FAF25BE0B01004811E6 /* TrendingHashtagTableViewCell.xib */; };
|
||||
|
@ -197,6 +198,10 @@
|
|||
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */; };
|
||||
D690797324A4EF9700023A34 /* UIBezierPath+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */; };
|
||||
D693A72825CF282E003A14E2 /* TrendingHashtagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */; };
|
||||
D693A72A25CF8C1E003A14E2 /* ProfileDirectoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */; };
|
||||
D693A72C25CF8D15003A14E2 /* DirectoryOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72B25CF8D15003A14E2 /* DirectoryOrder.swift */; };
|
||||
D693A72F25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */; };
|
||||
D693A73025CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */; };
|
||||
D693DE5723FE1A6A0061E07D /* EnhancedNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */; };
|
||||
D693DE5923FE24310061E07D /* InteractivePushTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D693DE5823FE24300061E07D /* InteractivePushTransition.swift */; };
|
||||
D6945C2F23AC47C3005C403C /* SavedDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6945C2E23AC47C3005C403C /* SavedDataManager.swift */; };
|
||||
|
@ -381,6 +386,7 @@
|
|||
04DACE8B212CB14B009840C4 /* MainTabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBarViewController.swift; sourceTree = "<group>"; };
|
||||
04DACE8D212CC7CC009840C4 /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = "<group>"; };
|
||||
04ED00B021481ED800567C53 /* SteppedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SteppedProgressView.swift; sourceTree = "<group>"; };
|
||||
D600613D25D07E170067FAD6 /* ProfileDirectoryFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDirectoryFilterView.swift; sourceTree = "<group>"; };
|
||||
D6093F9A25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagSearchResultsViewController.swift; sourceTree = "<group>"; };
|
||||
D6093FAE25BE0B01004811E6 /* TrendingHashtagTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrendingHashtagTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D6093FAF25BE0B01004811E6 /* TrendingHashtagTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TrendingHashtagTableViewCell.xib; sourceTree = "<group>"; };
|
||||
|
@ -564,6 +570,10 @@
|
|||
D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedPageViewController.swift; sourceTree = "<group>"; };
|
||||
D690797224A4EF9700023A34 /* UIBezierPath+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIBezierPath+Helpers.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>"; };
|
||||
D693A72B25CF8D15003A14E2 /* DirectoryOrder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectoryOrder.swift; sourceTree = "<group>"; };
|
||||
D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturedProfileCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FeaturedProfileCollectionViewCell.xib; sourceTree = "<group>"; };
|
||||
D693DE5623FE1A6A0061E07D /* EnhancedNavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnhancedNavigationViewController.swift; sourceTree = "<group>"; };
|
||||
D693DE5823FE24300061E07D /* InteractivePushTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractivePushTransition.swift; sourceTree = "<group>"; };
|
||||
D6945C2E23AC47C3005C403C /* SavedDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedDataManager.swift; sourceTree = "<group>"; };
|
||||
|
@ -818,6 +828,7 @@
|
|||
D61099E6214561FF00432DC2 /* Attachment.swift */,
|
||||
D61099E82145658300432DC2 /* Card.swift */,
|
||||
D61099EA2145661700432DC2 /* ConversationContext.swift */,
|
||||
D693A72B25CF8D15003A14E2 /* DirectoryOrder.swift */,
|
||||
D61099E22144C38900432DC2 /* Emoji.swift */,
|
||||
D61099EC2145664800432DC2 /* Filter.swift */,
|
||||
D6109A0021456B0800432DC2 /* Hashtag.swift */,
|
||||
|
@ -917,6 +928,10 @@
|
|||
D6093F9A25BDD4B9004811E6 /* HashtagSearchResultsViewController.swift */,
|
||||
D6945C3923AC75E2005C403C /* FindInstanceViewController.swift */,
|
||||
D693A72725CF282E003A14E2 /* TrendingHashtagsViewController.swift */,
|
||||
D693A72925CF8C1E003A14E2 /* ProfileDirectoryViewController.swift */,
|
||||
D693A72D25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift */,
|
||||
D693A72E25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib */,
|
||||
D600613D25D07E170067FAD6 /* ProfileDirectoryFilterView.swift */,
|
||||
);
|
||||
path = Explore;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1740,6 +1755,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D611C2D0232DC61100C86A49 /* HashtagTableViewCell.xib in Resources */,
|
||||
D693A73025CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.xib in Resources */,
|
||||
D61AC1D9232EA42D00C54D2D /* InstanceTableViewCell.xib in Resources */,
|
||||
D626493923C0FD0000612E6E /* AllPhotosTableViewCell.xib in Resources */,
|
||||
D6A3BC8B2321F79B00FD64D5 /* AccountTableViewCell.xib in Resources */,
|
||||
|
@ -1834,6 +1850,7 @@
|
|||
D61099D02144B2D700432DC2 /* Method.swift in Sources */,
|
||||
D6E6F26321603F8B006A8599 /* CharacterCounter.swift in Sources */,
|
||||
D61099FB214569F600432DC2 /* Report.swift in Sources */,
|
||||
D693A72C25CF8D15003A14E2 /* DirectoryOrder.swift in Sources */,
|
||||
D61099F92145698900432DC2 /* Relationship.swift in Sources */,
|
||||
D61099E12144C1DC00432DC2 /* Account.swift in Sources */,
|
||||
D61099E92145658300432DC2 /* Card.swift in Sources */,
|
||||
|
@ -1876,6 +1893,7 @@
|
|||
D626493523BD94CE00612E6E /* CompositionAttachmentData.swift in Sources */,
|
||||
D6531DF0246B867E000F9538 /* GifvAttachmentViewController.swift in Sources */,
|
||||
D6285B5321EA708700FE4B39 /* StatusFormat.swift in Sources */,
|
||||
D600613E25D07E170067FAD6 /* ProfileDirectoryFilterView.swift in Sources */,
|
||||
D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */,
|
||||
0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */,
|
||||
D626493C23C1000300612E6E /* AlbumTableViewCell.swift in Sources */,
|
||||
|
@ -1969,7 +1987,9 @@
|
|||
D6AEBB4A23216F0400E5038B /* UnfollowAccountActivity.swift in Sources */,
|
||||
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
|
||||
D6D3FDE024F41B8400FF50A5 /* ComposeContainerView.swift in Sources */,
|
||||
D693A72A25CF8C1E003A14E2 /* ProfileDirectoryViewController.swift in Sources */,
|
||||
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */,
|
||||
D693A72F25CF91C6003A14E2 /* FeaturedProfileCollectionViewCell.swift in Sources */,
|
||||
D627943223A5466600D38C68 /* SelectableTableViewCell.swift in Sources */,
|
||||
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */,
|
||||
D6EAE0DB2550CC8A002DB0AC /* FocusableTextField.swift in Sources */,
|
||||
|
|
|
@ -134,7 +134,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
|||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||
snapshot.appendSections(Section.allCases)
|
||||
snapshot.appendItems([.bookmarks], toSection: .bookmarks)
|
||||
snapshot.appendItems([.trendingTags], toSection: .discover)
|
||||
snapshot.appendItems([.trendingTags, .profileDirectory], toSection: .discover)
|
||||
snapshot.appendItems([.addList], toSection: .lists)
|
||||
snapshot.appendItems(SavedDataManager.shared.sortedHashtags(for: account).map { .savedHashtag($0) }, toSection: .savedHashtags)
|
||||
snapshot.appendItems([.addSavedHashtag], toSection: .savedHashtags)
|
||||
|
@ -259,6 +259,9 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
|||
case .trendingTags:
|
||||
show(TrendingHashtagsViewController(mastodonController: mastodonController), sender: nil)
|
||||
|
||||
case .profileDirectory:
|
||||
show(ProfileDirectoryViewController(mastodonController: mastodonController), sender: nil)
|
||||
|
||||
case let .list(list):
|
||||
show(ListTimelineViewController(for: list, mastodonController: mastodonController), sender: nil)
|
||||
|
||||
|
@ -336,6 +339,7 @@ extension ExploreViewController {
|
|||
enum Item: Hashable {
|
||||
case bookmarks
|
||||
case trendingTags
|
||||
case profileDirectory
|
||||
case list(List)
|
||||
case addList
|
||||
case savedHashtag(Hashtag)
|
||||
|
@ -349,6 +353,8 @@ extension ExploreViewController {
|
|||
return NSLocalizedString("Bookmarks", comment: "bookmarks nav item title")
|
||||
case .trendingTags:
|
||||
return NSLocalizedString("Trending Hashtags", comment: "trending hashtags nav item title")
|
||||
case .profileDirectory:
|
||||
return NSLocalizedString("Profile Directory", comment: "profile directory nav item title")
|
||||
case let .list(list):
|
||||
return list.title
|
||||
case .addList:
|
||||
|
@ -371,6 +377,8 @@ extension ExploreViewController {
|
|||
name = "bookmark.fill"
|
||||
case .trendingTags:
|
||||
name = "arrow.up.arrow.down"
|
||||
case .profileDirectory:
|
||||
name = "person.2.fill"
|
||||
case .list(_):
|
||||
name = "list.bullet"
|
||||
case .addList, .addSavedHashtag:
|
||||
|
@ -391,6 +399,8 @@ extension ExploreViewController {
|
|||
return true
|
||||
case (.trendingTags, .trendingTags):
|
||||
return true
|
||||
case (.profileDirectory, .profileDirectory):
|
||||
return true
|
||||
case let (.list(a), .list(b)):
|
||||
return a.id == b.id
|
||||
case (.addList, .addList):
|
||||
|
@ -414,6 +424,8 @@ extension ExploreViewController {
|
|||
hasher.combine("bookmarks")
|
||||
case .trendingTags:
|
||||
hasher.combine("trendingTags")
|
||||
case .profileDirectory:
|
||||
hasher.combine("profileDirectory")
|
||||
case let .list(list):
|
||||
hasher.combine("list")
|
||||
hasher.combine(list.id)
|
||||
|
@ -468,7 +480,7 @@ extension ExploreViewController: UICollectionViewDragDelegate {
|
|||
case let .savedInstance(url):
|
||||
provider = NSItemProvider(object: url as NSURL)
|
||||
// todo: should dragging public timelines into new windows be supported?
|
||||
case .trendingTags, .addList, .addSavedHashtag, .findInstance:
|
||||
case .trendingTags, .profileDirectory, .addList, .addSavedHashtag, .findInstance:
|
||||
return []
|
||||
}
|
||||
return [UIDragItem(itemProvider: provider)]
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// FeaturedProfileCollectionViewCell.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 2/6/21.
|
||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
class FeaturedProfileCollectionViewCell: UICollectionViewCell {
|
||||
|
||||
@IBOutlet weak var headerImageView: UIImageView!
|
||||
@IBOutlet weak var avatarContainerView: UIView!
|
||||
@IBOutlet weak var avatarImageView: UIImageView!
|
||||
@IBOutlet weak var displayNameLabel: EmojiLabel!
|
||||
@IBOutlet weak var noteTextView: StatusContentTextView!
|
||||
|
||||
var account: Account?
|
||||
|
||||
private var avatarRequest: ImageCache.Request?
|
||||
private var headerRequest: ImageCache.Request?
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView)
|
||||
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
|
||||
|
||||
noteTextView.defaultFont = .systemFont(ofSize: 16)
|
||||
noteTextView.textContainer.lineBreakMode = .byTruncatingTail
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
||||
}
|
||||
|
||||
func updateUI(account: Account) {
|
||||
self.account = account
|
||||
|
||||
displayNameLabel.updateForAccountDisplayName(account: account)
|
||||
|
||||
noteTextView.setTextFromHtml(account.note)
|
||||
noteTextView.setEmojis(account.emojis)
|
||||
|
||||
avatarImageView.image = nil
|
||||
avatarRequest = ImageCache.avatars.get(account.avatar) { [weak self] (_, image) in
|
||||
defer {
|
||||
self?.avatarRequest = nil
|
||||
}
|
||||
guard let self = self,
|
||||
let image = image,
|
||||
self.account?.id == account.id else {
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.avatarImageView.image = image
|
||||
}
|
||||
}
|
||||
|
||||
headerImageView.image = nil
|
||||
if let header = account.header {
|
||||
headerRequest = ImageCache.headers.get(header) { [weak self] (_, image) in
|
||||
defer {
|
||||
self?.headerRequest = nil
|
||||
}
|
||||
guard let self = self,
|
||||
let image = image,
|
||||
self.account?.id == account.id else {
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.headerImageView.image = image
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func preferencesChanged() {
|
||||
avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView)
|
||||
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
|
||||
|
||||
if let account = account {
|
||||
displayNameLabel.updateForAccountDisplayName(account: account)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18121" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18091"/>
|
||||
<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"/>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="FeaturedProfileCollectionViewCell" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="200"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="200"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="bo4-Sd-caI">
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="66"/>
|
||||
<color key="backgroundColor" systemColor="systemGray5Color"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="66" id="9Aa-Up-chJ"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RQe-uE-TEv">
|
||||
<rect key="frame" x="8" y="34" width="64" height="64"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="4wd-wq-Sh2">
|
||||
<rect key="frame" x="2" y="2" width="60" height="60"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="60" id="Xyl-Ry-J3r"/>
|
||||
<constraint firstAttribute="width" secondItem="4wd-wq-Sh2" secondAttribute="height" multiplier="1:1" id="YEc-fT-FRB"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="RQe-uE-TEv" secondAttribute="height" multiplier="1:1" id="4vR-IF-yS8"/>
|
||||
<constraint firstAttribute="width" secondItem="4wd-wq-Sh2" secondAttribute="width" constant="4" id="52Q-zq-k28"/>
|
||||
<constraint firstItem="4wd-wq-Sh2" firstAttribute="centerY" secondItem="RQe-uE-TEv" secondAttribute="centerY" id="Ped-H7-QtP"/>
|
||||
<constraint firstItem="4wd-wq-Sh2" firstAttribute="centerX" secondItem="RQe-uE-TEv" secondAttribute="centerX" id="bRk-uJ-JGg"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumFontSize="10" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="voW-Is-1b2" customClass="EmojiLabel" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="76" y="72" width="316" height="24"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" verticalCompressionResistancePriority="749" scrollEnabled="NO" editable="NO" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bvj-F0-ggC" customClass="StatusContentTextView" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="8" y="102" width="384" height="94"/>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||
<color key="textColor" systemColor="labelColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
</view>
|
||||
<viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="bvj-F0-ggC" firstAttribute="top" secondItem="RQe-uE-TEv" secondAttribute="bottom" constant="4" id="8Nc-FF-kRX"/>
|
||||
<constraint firstItem="bo4-Sd-caI" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="CJ1-Be-L45"/>
|
||||
<constraint firstAttribute="bottom" secondItem="bvj-F0-ggC" secondAttribute="bottom" constant="4" id="Hza-qE-Agk"/>
|
||||
<constraint firstItem="voW-Is-1b2" firstAttribute="bottom" secondItem="4wd-wq-Sh2" secondAttribute="bottom" id="N0l-fE-AAX"/>
|
||||
<constraint firstItem="RQe-uE-TEv" firstAttribute="centerY" secondItem="bo4-Sd-caI" secondAttribute="bottom" id="Ngh-DO-Q0X"/>
|
||||
<constraint firstItem="bvj-F0-ggC" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="8" id="Rjq-1i-PV2"/>
|
||||
<constraint firstItem="voW-Is-1b2" firstAttribute="leading" secondItem="RQe-uE-TEv" secondAttribute="trailing" constant="4" id="WUb-3i-BFe"/>
|
||||
<constraint firstAttribute="trailing" secondItem="bvj-F0-ggC" secondAttribute="trailing" constant="8" id="ZrT-Wa-pbY"/>
|
||||
<constraint firstItem="voW-Is-1b2" firstAttribute="top" relation="greaterThanOrEqual" secondItem="bo4-Sd-caI" secondAttribute="bottom" id="g4l-yF-2wH"/>
|
||||
<constraint firstAttribute="trailing" secondItem="bo4-Sd-caI" secondAttribute="trailing" id="geb-Qa-zZp"/>
|
||||
<constraint firstAttribute="trailing" secondItem="voW-Is-1b2" secondAttribute="trailing" constant="8" id="l91-F6-kAL"/>
|
||||
<constraint firstItem="bo4-Sd-caI" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="tUr-Oy-nXN"/>
|
||||
<constraint firstItem="RQe-uE-TEv" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="8" id="uZI-LM-bZW"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="avatarContainerView" destination="RQe-uE-TEv" id="tBI-fT-26P"/>
|
||||
<outlet property="avatarImageView" destination="4wd-wq-Sh2" id="rba-cv-8fb"/>
|
||||
<outlet property="displayNameLabel" destination="voW-Is-1b2" id="XVS-4d-PKx"/>
|
||||
<outlet property="headerImageView" destination="bo4-Sd-caI" id="YkL-Wi-BXb"/>
|
||||
<outlet property="noteTextView" destination="bvj-F0-ggC" id="Bbm-ai-bu1"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="535" y="428"/>
|
||||
</collectionViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<systemColor name="labelColor">
|
||||
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemGray5Color">
|
||||
<color red="0.89803921568627454" green="0.89803921568627454" blue="0.91764705882352937" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,132 @@
|
|||
//
|
||||
// ProfileDirectoryFilterView.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 2/7/21.
|
||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
class ProfileDirectoryFilterView: UICollectionReusableView {
|
||||
|
||||
var onFilterChanged: ((Scope, DirectoryOrder) -> Void)?
|
||||
|
||||
private var scope: UISegmentedControl!
|
||||
private var sort: UISegmentedControl!
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
commonInit()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
|
||||
commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
scope = UISegmentedControl(items: ["Instance", NSLocalizedString("Everywhere", comment: "everywhere profile directory scope")])
|
||||
scope.selectedSegmentIndex = 0
|
||||
scope.addTarget(self, action: #selector(filterChanged), for: .valueChanged)
|
||||
|
||||
sort = UISegmentedControl(items: [
|
||||
NSLocalizedString("Active", comment: "active profile directory sort"),
|
||||
NSLocalizedString("New", comment: "new profile directory sort"),
|
||||
])
|
||||
sort.selectedSegmentIndex = 0
|
||||
sort.addTarget(self, action: #selector(filterChanged), for: .valueChanged)
|
||||
|
||||
let fromLabel = UILabel()
|
||||
fromLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
fromLabel.text = NSLocalizedString("From", comment: "profile directory scope label")
|
||||
fromLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
|
||||
let sortLabel = UILabel()
|
||||
sortLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
sortLabel.text = NSLocalizedString("Sort By", comment: "profile directory sort label")
|
||||
sortLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
|
||||
let labelContainer = UIView()
|
||||
labelContainer.addSubview(sortLabel)
|
||||
labelContainer.addSubview(fromLabel)
|
||||
|
||||
let controlStack = UIStackView(arrangedSubviews: [sort, scope])
|
||||
controlStack.axis = .vertical
|
||||
controlStack.spacing = 8
|
||||
|
||||
let blurEffect = UIBlurEffect(style: .systemChromeMaterial)
|
||||
let blurView = UIVisualEffectView(effect: blurEffect)
|
||||
blurView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(blurView)
|
||||
|
||||
let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect, style: .label)
|
||||
let vibrancyView = UIVisualEffectView(effect: vibrancyEffect)
|
||||
vibrancyView.translatesAutoresizingMaskIntoConstraints = false
|
||||
blurView.contentView.addSubview(vibrancyView)
|
||||
|
||||
let filterStack = UIStackView(arrangedSubviews: [
|
||||
labelContainer,
|
||||
controlStack,
|
||||
])
|
||||
filterStack.axis = .horizontal
|
||||
filterStack.spacing = 8
|
||||
filterStack.translatesAutoresizingMaskIntoConstraints = false
|
||||
vibrancyView.contentView.addSubview(filterStack)
|
||||
|
||||
let separator = UIView()
|
||||
separator.backgroundColor = .separator
|
||||
separator.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(separator)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
fromLabel.leadingAnchor.constraint(equalTo: labelContainer.leadingAnchor),
|
||||
fromLabel.trailingAnchor.constraint(equalTo: labelContainer.trailingAnchor),
|
||||
fromLabel.centerYAnchor.constraint(equalTo: scope.centerYAnchor),
|
||||
|
||||
sortLabel.leadingAnchor.constraint(equalTo: labelContainer.leadingAnchor),
|
||||
sortLabel.trailingAnchor.constraint(equalTo: labelContainer.trailingAnchor),
|
||||
sortLabel.centerYAnchor.constraint(equalTo: sort.centerYAnchor),
|
||||
|
||||
blurView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
blurView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
blurView.topAnchor.constraint(equalTo: topAnchor),
|
||||
blurView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
|
||||
vibrancyView.leadingAnchor.constraint(equalTo: blurView.contentView.leadingAnchor),
|
||||
vibrancyView.trailingAnchor.constraint(equalTo: blurView.contentView.trailingAnchor),
|
||||
vibrancyView.topAnchor.constraint(equalTo: blurView.contentView.topAnchor),
|
||||
vibrancyView.bottomAnchor.constraint(equalTo: blurView.contentView.bottomAnchor),
|
||||
|
||||
filterStack.leadingAnchor.constraint(equalToSystemSpacingAfter: vibrancyView.contentView.leadingAnchor, multiplier: 1),
|
||||
vibrancyView.contentView.trailingAnchor.constraint(equalToSystemSpacingAfter: filterStack.trailingAnchor, multiplier: 1),
|
||||
filterStack.topAnchor.constraint(equalToSystemSpacingBelow: vibrancyView.contentView.topAnchor, multiplier: 1),
|
||||
vibrancyView.contentView.bottomAnchor.constraint(equalToSystemSpacingBelow: filterStack.bottomAnchor, multiplier: 1),
|
||||
|
||||
separator.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
separator.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
separator.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
separator.heightAnchor.constraint(equalToConstant: 0.5),
|
||||
])
|
||||
}
|
||||
|
||||
func updateUI(mastodonController: MastodonController) {
|
||||
scope.setTitle(mastodonController.accountInfo!.instanceURL.host!, forSegmentAt: 0)
|
||||
}
|
||||
|
||||
@objc private func filterChanged() {
|
||||
let scope = Scope(rawValue: scope.selectedSegmentIndex)!
|
||||
let order = sort.selectedSegmentIndex == 0 ? DirectoryOrder.active : .new
|
||||
onFilterChanged?(scope, order)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ProfileDirectoryFilterView {
|
||||
enum Scope: Int, Equatable {
|
||||
case instance, everywhere
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
//
|
||||
// ProfileDirectoryViewController.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 2/6/21.
|
||||
// Copyright © 2021 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
class ProfileDirectoryViewController: UIViewController {
|
||||
|
||||
weak var mastodonController: MastodonController!
|
||||
|
||||
private var collectionView: UICollectionView!
|
||||
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
||||
|
||||
init(mastodonController: MastodonController) {
|
||||
self.mastodonController = mastodonController
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
title = NSLocalizedString("Profile Directory", comment: "profile directory title")
|
||||
|
||||
let configuration = UICollectionViewCompositionalLayoutConfiguration()
|
||||
configuration.boundarySupplementaryItems = [
|
||||
NSCollectionLayoutBoundarySupplementaryItem(
|
||||
layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(100)),
|
||||
elementKind: "filter",
|
||||
alignment: .top
|
||||
)
|
||||
]
|
||||
let layout = UICollectionViewCompositionalLayout(sectionProvider: { (sectionIndex, layoutEnvironment) in
|
||||
let itemHeight = NSCollectionLayoutDimension.absolute(200)
|
||||
let itemWidth: NSCollectionLayoutDimension
|
||||
if case .compact = layoutEnvironment.traitCollection.horizontalSizeClass {
|
||||
itemWidth = .fractionalWidth(1)
|
||||
} else {
|
||||
itemWidth = .absolute((layoutEnvironment.container.contentSize.width - 12) / 2)
|
||||
}
|
||||
|
||||
let itemSize = NSCollectionLayoutSize(widthDimension: itemWidth, heightDimension: itemHeight)
|
||||
let item = NSCollectionLayoutItem(layoutSize: itemSize)
|
||||
let itemB = NSCollectionLayoutItem(layoutSize: itemSize)
|
||||
|
||||
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: itemHeight)
|
||||
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item, itemB])
|
||||
group.interItemSpacing = .flexible(4)
|
||||
|
||||
let section = NSCollectionLayoutSection(group: group)
|
||||
section.interGroupSpacing = 4
|
||||
if case .compact = layoutEnvironment.traitCollection.horizontalSizeClass {
|
||||
section.contentInsets = NSDirectionalEdgeInsets(top: 4, leading: 0, bottom: 4, trailing: 0)
|
||||
} else {
|
||||
section.contentInsets = NSDirectionalEdgeInsets(top: 4, leading: 4, bottom: 4, trailing: 4)
|
||||
}
|
||||
return section
|
||||
}, configuration: configuration)
|
||||
|
||||
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
|
||||
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
collectionView.backgroundColor = .secondarySystemBackground
|
||||
collectionView.register(UINib(nibName: "FeaturedProfileCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: "featuredProfileCell")
|
||||
collectionView.register(ProfileDirectoryFilterView.self, forSupplementaryViewOfKind: "filter", withReuseIdentifier: "filter")
|
||||
collectionView.delegate = self
|
||||
collectionView.dragDelegate = self
|
||||
view.addSubview(collectionView)
|
||||
|
||||
dataSource = createDataSource()
|
||||
updateProfiles(local: true, order: .active)
|
||||
}
|
||||
|
||||
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
||||
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { (collectionView, indexPath, item) in
|
||||
guard case let .account(account) = item else { fatalError() }
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "featuredProfileCell", for: indexPath) as! FeaturedProfileCollectionViewCell
|
||||
cell.updateUI(account: account)
|
||||
return cell
|
||||
}
|
||||
dataSource.supplementaryViewProvider = { (collectionView, elementKind, indexPath) in
|
||||
guard elementKind == "filter" else {
|
||||
return nil
|
||||
}
|
||||
let filterView = collectionView.dequeueReusableSupplementaryView(ofKind: "filter", withReuseIdentifier: "filter", for: indexPath) as! ProfileDirectoryFilterView
|
||||
filterView.updateUI(mastodonController: self.mastodonController)
|
||||
filterView.onFilterChanged = { [weak self] (scope, order) in
|
||||
guard let self = self else { return }
|
||||
self.dataSource.apply(.init())
|
||||
self.updateProfiles(local: scope == .instance, order: order)
|
||||
}
|
||||
return filterView
|
||||
}
|
||||
return dataSource
|
||||
}
|
||||
|
||||
private func updateProfiles(local: Bool, order: DirectoryOrder) {
|
||||
let request = Client.getFeaturedProfiles(local: local, order: order)
|
||||
mastodonController.run(request) { (response) in
|
||||
guard case let .success(accounts, _) = response else {
|
||||
return
|
||||
}
|
||||
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||
snapshot.appendSections([.featuredProfiles])
|
||||
snapshot.appendItems(accounts.map { .account($0) })
|
||||
DispatchQueue.main.async {
|
||||
self.dataSource.apply(snapshot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ProfileDirectoryViewController {
|
||||
enum Section {
|
||||
case featuredProfiles
|
||||
}
|
||||
enum Item: Hashable {
|
||||
case account(Account)
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
guard case let .account(account) = self else { return }
|
||||
hasher.combine(account.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ProfileDirectoryViewController: TuskerNavigationDelegate {
|
||||
var apiController: MastodonController { mastodonController }
|
||||
}
|
||||
|
||||
extension ProfileDirectoryViewController: MenuPreviewProvider {
|
||||
var navigationDelegate: TuskerNavigationDelegate? { self }
|
||||
}
|
||||
|
||||
extension ProfileDirectoryViewController: UICollectionViewDelegate {
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
guard let item = dataSource.itemIdentifier(for: indexPath),
|
||||
case let .account(account) = item else {
|
||||
return
|
||||
}
|
||||
show(ProfileViewController(accountID: account.id, mastodonController: mastodonController), sender: nil)
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
||||
guard let item = dataSource.itemIdentifier(for: indexPath),
|
||||
case let .account(account) = item else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return UIContextMenuConfiguration(identifier: nil) {
|
||||
return ProfileViewController(accountID: account.id, mastodonController: self.mastodonController)
|
||||
} actionProvider: { (_) in
|
||||
let actions = self.actionsForProfile(accountID: account.id, sourceView: self.collectionView.cellForItem(at: indexPath))
|
||||
return UIMenu(children: actions)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
|
||||
if let viewController = animator.previewViewController {
|
||||
animator.preferredCommitStyle = .pop
|
||||
animator.addCompletion {
|
||||
self.show(viewController, sender: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ProfileDirectoryViewController: UICollectionViewDragDelegate {
|
||||
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
||||
guard let item = dataSource.itemIdentifier(for: indexPath),
|
||||
case let .account(account) = item,
|
||||
let currentAccountID = mastodonController.accountInfo?.id else {
|
||||
return []
|
||||
}
|
||||
let provider = NSItemProvider(object: account.url as NSURL)
|
||||
let activity = UserActivityManager.showProfileActivity(id: account.id, accountID: currentAccountID)
|
||||
provider.registerObject(activity, visibility: .all)
|
||||
return [UIDragItem(itemProvider: provider)]
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ class MainSidebarViewController: UIViewController {
|
|||
}
|
||||
|
||||
var exploreTabItems: [Item] {
|
||||
var items: [Item] = [.search, .bookmarks, .trendingTags]
|
||||
var items: [Item] = [.search, .bookmarks, .trendingTags, .profileDirectory]
|
||||
let snapshot = dataSource.snapshot()
|
||||
for case let .list(list) in snapshot.itemIdentifiers(inSection: .lists) {
|
||||
items.append(.list(list))
|
||||
|
@ -143,6 +143,7 @@ class MainSidebarViewController: UIViewController {
|
|||
], toSection: .compose)
|
||||
snapshot.appendItems([
|
||||
.trendingTags,
|
||||
.profileDirectory,
|
||||
], toSection: .discover)
|
||||
dataSource.apply(snapshot, animatingDifferences: false)
|
||||
|
||||
|
@ -283,7 +284,7 @@ extension MainSidebarViewController {
|
|||
enum Item: Hashable {
|
||||
case tab(MainTabBarViewController.Tab)
|
||||
case search, bookmarks
|
||||
case trendingTags
|
||||
case trendingTags, profileDirectory
|
||||
case listsHeader, list(List), addList
|
||||
case savedHashtagsHeader, savedHashtag(Hashtag), addSavedHashtag
|
||||
case savedInstancesHeader, savedInstance(URL), addSavedInstance
|
||||
|
@ -298,6 +299,8 @@ extension MainSidebarViewController {
|
|||
return "Bookmarks"
|
||||
case .trendingTags:
|
||||
return "Trending Hashtags"
|
||||
case .profileDirectory:
|
||||
return "Profile Directory"
|
||||
case .listsHeader:
|
||||
return "Lists"
|
||||
case let .list(list):
|
||||
|
@ -329,6 +332,8 @@ extension MainSidebarViewController {
|
|||
return "bookmark"
|
||||
case .trendingTags:
|
||||
return "arrow.up.arrow.down"
|
||||
case .profileDirectory:
|
||||
return "person.2.fill"
|
||||
case .list(_):
|
||||
return "list.bullet"
|
||||
case .savedHashtag(_):
|
||||
|
|
|
@ -203,7 +203,7 @@ extension MainSplitViewController: UISplitViewControllerDelegate {
|
|||
|
||||
tabBarViewController.select(tab: .explore)
|
||||
|
||||
case .bookmarks, .trendingTags, .list(_), .savedHashtag(_), .savedInstance(_):
|
||||
case .bookmarks, .trendingTags, .profileDirectory, .list(_), .savedHashtag(_), .savedInstance(_):
|
||||
tabBarViewController.select(tab: .explore)
|
||||
// Make sure the Explore VC doesn't show it's search bar when it appears, in case the user was previously
|
||||
// in compact mode and performing a search.
|
||||
|
@ -279,6 +279,8 @@ extension MainSplitViewController: UISplitViewControllerDelegate {
|
|||
exploreItem = .savedInstance(instanceVC.instanceURL)
|
||||
} else if tabNavigationStack[1] is TrendingHashtagsViewController {
|
||||
exploreItem = .trendingTags
|
||||
} else if tabNavigationStack[1] is ProfileDirectoryViewController {
|
||||
exploreItem = .profileDirectory
|
||||
}
|
||||
transferNavigationStack(from: tabNavController, to: exploreItem!, skipFirst: 1, prepend: toPrepend)
|
||||
|
||||
|
@ -332,6 +334,8 @@ fileprivate extension MainSidebarViewController.Item {
|
|||
return BookmarksTableViewController(mastodonController: mastodonController)
|
||||
case .trendingTags:
|
||||
return TrendingHashtagsViewController(mastodonController: mastodonController)
|
||||
case .profileDirectory:
|
||||
return ProfileDirectoryViewController(mastodonController: mastodonController)
|
||||
case let .list(list):
|
||||
return ListTimelineViewController(for: list, mastodonController: mastodonController)
|
||||
case let .savedHashtag(hashtag):
|
||||
|
|
Loading…
Reference in New Issue