mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-25 01:31:02 +00:00
View following / followers
This commit is contained in:
parent
03a4d1299d
commit
acfd127672
9 changed files with 87 additions and 14 deletions
14
Extensions/UIButton+Extensions.swift
Normal file
14
Extensions/UIButton+Extensions.swift
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UIButton {
|
||||||
|
func setAttributedLocalizedTitle(localizationKey: String, count: Int) {
|
||||||
|
let localizedTitle = String.localizedStringWithFormat(NSLocalizedString(localizationKey, comment: ""), count)
|
||||||
|
|
||||||
|
setAttributedTitle(localizedTitle.countEmphasizedAttributedString(count: count), for: .normal)
|
||||||
|
setAttributedTitle(
|
||||||
|
localizedTitle.countEmphasizedAttributedString(count: count, highlighted: true),
|
||||||
|
for: .highlighted)
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
"account.field.verified" = "Verified %@";
|
"account.field.verified" = "Verified %@";
|
||||||
"account.follow" = "Follow";
|
"account.follow" = "Follow";
|
||||||
"account.following" = "Following";
|
"account.following" = "Following";
|
||||||
|
"account.following-count" = "%ld Following";
|
||||||
"account.hide-reblogs" = "Hide boosts";
|
"account.hide-reblogs" = "Hide boosts";
|
||||||
"account.mute" = "Mute";
|
"account.mute" = "Mute";
|
||||||
"account.request" = "Request";
|
"account.request" = "Request";
|
||||||
|
|
|
@ -9,6 +9,8 @@ public enum AccountsEndpoint {
|
||||||
case favouritedBy(id: Status.Id)
|
case favouritedBy(id: Status.Id)
|
||||||
case mutes
|
case mutes
|
||||||
case blocks
|
case blocks
|
||||||
|
case accountsFollowers(id: Account.Id)
|
||||||
|
case accountsFollowing(id: Account.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AccountsEndpoint: Endpoint {
|
extension AccountsEndpoint: Endpoint {
|
||||||
|
@ -20,6 +22,8 @@ extension AccountsEndpoint: Endpoint {
|
||||||
return defaultContext + ["statuses"]
|
return defaultContext + ["statuses"]
|
||||||
case .mutes, .blocks:
|
case .mutes, .blocks:
|
||||||
return defaultContext
|
return defaultContext
|
||||||
|
case .accountsFollowers, .accountsFollowing:
|
||||||
|
return defaultContext + ["accounts"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +37,10 @@ extension AccountsEndpoint: Endpoint {
|
||||||
return ["mutes"]
|
return ["mutes"]
|
||||||
case .blocks:
|
case .blocks:
|
||||||
return ["blocks"]
|
return ["blocks"]
|
||||||
|
case let .accountsFollowers(id):
|
||||||
|
return [id, "followers"]
|
||||||
|
case let .accountsFollowing(id):
|
||||||
|
return [id, "following"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
D08B8D72254246E200B1EBEF /* PollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08B8D71254246E200B1EBEF /* PollView.swift */; };
|
D08B8D72254246E200B1EBEF /* PollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08B8D71254246E200B1EBEF /* PollView.swift */; };
|
||||||
D08B8D822544D80000B1EBEF /* PollOptionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08B8D812544D80000B1EBEF /* PollOptionButton.swift */; };
|
D08B8D822544D80000B1EBEF /* PollOptionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08B8D812544D80000B1EBEF /* PollOptionButton.swift */; };
|
||||||
D08B8D8D2544E6EC00B1EBEF /* PollResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08B8D8C2544E6EC00B1EBEF /* PollResultView.swift */; };
|
D08B8D8D2544E6EC00B1EBEF /* PollResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08B8D8C2544E6EC00B1EBEF /* PollResultView.swift */; };
|
||||||
|
D08E512125786A6600FA2C5F /* UIButton+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08E512025786A6600FA2C5F /* UIButton+Extensions.swift */; };
|
||||||
D0A1F4F7252E7D4B004435BF /* TableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A1F4F6252E7D4B004435BF /* TableViewDataSource.swift */; };
|
D0A1F4F7252E7D4B004435BF /* TableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A1F4F6252E7D4B004435BF /* TableViewDataSource.swift */; };
|
||||||
D0A7AC7325748BFF00E4E8AB /* ReportStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A7AC7225748BFF00E4E8AB /* ReportStatusView.swift */; };
|
D0A7AC7325748BFF00E4E8AB /* ReportStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A7AC7225748BFF00E4E8AB /* ReportStatusView.swift */; };
|
||||||
D0B32F50250B373600311912 /* RegistrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B32F4F250B373600311912 /* RegistrationView.swift */; };
|
D0B32F50250B373600311912 /* RegistrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B32F4F250B373600311912 /* RegistrationView.swift */; };
|
||||||
|
@ -157,6 +158,7 @@
|
||||||
D08B8D71254246E200B1EBEF /* PollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollView.swift; sourceTree = "<group>"; };
|
D08B8D71254246E200B1EBEF /* PollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollView.swift; sourceTree = "<group>"; };
|
||||||
D08B8D812544D80000B1EBEF /* PollOptionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionButton.swift; sourceTree = "<group>"; };
|
D08B8D812544D80000B1EBEF /* PollOptionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollOptionButton.swift; sourceTree = "<group>"; };
|
||||||
D08B8D8C2544E6EC00B1EBEF /* PollResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollResultView.swift; sourceTree = "<group>"; };
|
D08B8D8C2544E6EC00B1EBEF /* PollResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollResultView.swift; sourceTree = "<group>"; };
|
||||||
|
D08E512025786A6600FA2C5F /* UIButton+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
D0A1F4F6252E7D4B004435BF /* TableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewDataSource.swift; sourceTree = "<group>"; };
|
D0A1F4F6252E7D4B004435BF /* TableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewDataSource.swift; sourceTree = "<group>"; };
|
||||||
D0A7AC7225748BFF00E4E8AB /* ReportStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportStatusView.swift; sourceTree = "<group>"; };
|
D0A7AC7225748BFF00E4E8AB /* ReportStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportStatusView.swift; sourceTree = "<group>"; };
|
||||||
D0AD03552505814D0085A466 /* Base16 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Base16; sourceTree = "<group>"; };
|
D0AD03552505814D0085A466 /* Base16 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Base16; sourceTree = "<group>"; };
|
||||||
|
@ -433,6 +435,7 @@
|
||||||
D0C7D46B24F76169001EBDBB /* NSMutableAttributedString+Extensions.swift */,
|
D0C7D46B24F76169001EBDBB /* NSMutableAttributedString+Extensions.swift */,
|
||||||
D0B5FE9A251583DB00478838 /* ProfileCollection+Extensions.swift */,
|
D0B5FE9A251583DB00478838 /* ProfileCollection+Extensions.swift */,
|
||||||
D0C7D46A24F76169001EBDBB /* String+Extensions.swift */,
|
D0C7D46A24F76169001EBDBB /* String+Extensions.swift */,
|
||||||
|
D08E512025786A6600FA2C5F /* UIButton+Extensions.swift */,
|
||||||
D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */,
|
D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */,
|
||||||
D0030981250C6C8500EACB32 /* URL+Extensions.swift */,
|
D0030981250C6C8500EACB32 /* URL+Extensions.swift */,
|
||||||
D0C7D46F24F76169001EBDBB /* View+Extensions.swift */,
|
D0C7D46F24F76169001EBDBB /* View+Extensions.swift */,
|
||||||
|
@ -674,6 +677,7 @@
|
||||||
D0C7D49724F7616A001EBDBB /* IdentitiesView.swift in Sources */,
|
D0C7D49724F7616A001EBDBB /* IdentitiesView.swift in Sources */,
|
||||||
D01EF22425182B1F00650C6B /* AccountHeaderView.swift in Sources */,
|
D01EF22425182B1F00650C6B /* AccountHeaderView.swift in Sources */,
|
||||||
D036AA17254CA824009094DF /* StatusBodyView.swift in Sources */,
|
D036AA17254CA824009094DF /* StatusBodyView.swift in Sources */,
|
||||||
|
D08E512125786A6600FA2C5F /* UIButton+Extensions.swift in Sources */,
|
||||||
D0EA59482522B8B600804347 /* ViewConstants.swift in Sources */,
|
D0EA59482522B8B600804347 /* ViewConstants.swift in Sources */,
|
||||||
D04226FD2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift in Sources */,
|
D04226FD2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift in Sources */,
|
||||||
D036AA0C254B612B009094DF /* NotificationContentConfiguration.swift in Sources */,
|
D036AA0C254B612B009094DF /* NotificationContentConfiguration.swift in Sources */,
|
||||||
|
|
|
@ -90,6 +90,20 @@ public extension AccountService {
|
||||||
func report(_ elements: ReportElements) -> AnyPublisher<Never, Error> {
|
func report(_ elements: ReportElements) -> AnyPublisher<Never, Error> {
|
||||||
mastodonAPIClient.request(ReportEndpoint.create(elements)).ignoreOutput().eraseToAnyPublisher()
|
mastodonAPIClient.request(ReportEndpoint.create(elements)).ignoreOutput().eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func followingService() -> AccountListService {
|
||||||
|
AccountListService(
|
||||||
|
endpoint: .accountsFollowing(id: account.id),
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
|
}
|
||||||
|
|
||||||
|
func followersService() -> AccountListService {
|
||||||
|
AccountListService(
|
||||||
|
endpoint: .accountsFollowers(id: account.id),
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension AccountService {
|
private extension AccountService {
|
||||||
|
|
|
@ -19,7 +19,7 @@ final class ProfileViewController: TableViewController {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
// Initial size is to avoid unsatisfiable constraint warning
|
// Initial size is to avoid unsatisfiable constraint warning
|
||||||
let accountHeaderView = AccountHeaderView(frame: .init(origin: .zero, size: .init(width: 100, height: 100)))
|
let accountHeaderView = AccountHeaderView(frame: .init(origin: .zero, size: .init(width: 300, height: 300)))
|
||||||
|
|
||||||
accountHeaderView.viewModel = viewModel
|
accountHeaderView.viewModel = viewModel
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,10 @@ public extension AccountViewModel {
|
||||||
|
|
||||||
var emoji: [Emoji] { accountService.account.emojis }
|
var emoji: [Emoji] { accountService.account.emojis }
|
||||||
|
|
||||||
|
var followingCount: Int { accountService.account.followingCount }
|
||||||
|
|
||||||
|
var followersCount: Int { accountService.account.followersCount }
|
||||||
|
|
||||||
var isSelf: Bool { accountService.account.id == identification.identity.account?.id }
|
var isSelf: Bool { accountService.account.id == identification.identity.account?.id }
|
||||||
|
|
||||||
func avatarURL(profile: Bool = false) -> URL {
|
func avatarURL(profile: Bool = false) -> URL {
|
||||||
|
@ -66,6 +70,20 @@ public extension AccountViewModel {
|
||||||
.eraseToAnyPublisher())
|
.eraseToAnyPublisher())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func followingSelected() {
|
||||||
|
eventsSubject.send(
|
||||||
|
Just(.navigation(.collection(accountService.followingService())))
|
||||||
|
.setFailureType(to: Error.self)
|
||||||
|
.eraseToAnyPublisher())
|
||||||
|
}
|
||||||
|
|
||||||
|
func followersSelected() {
|
||||||
|
eventsSubject.send(
|
||||||
|
Just(.navigation(.collection(accountService.followersService())))
|
||||||
|
.setFailureType(to: Error.self)
|
||||||
|
.eraseToAnyPublisher())
|
||||||
|
}
|
||||||
|
|
||||||
func reportViewModel() -> ReportViewModel {
|
func reportViewModel() -> ReportViewModel {
|
||||||
ReportViewModel(accountService: accountService, identification: identification)
|
ReportViewModel(accountService: accountService, identification: identification)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@ final class AccountHeaderView: UIView {
|
||||||
let lockedImageView = UIImageView()
|
let lockedImageView = UIImageView()
|
||||||
let fieldsStackView = UIStackView()
|
let fieldsStackView = UIStackView()
|
||||||
let noteTextView = TouchFallthroughTextView()
|
let noteTextView = TouchFallthroughTextView()
|
||||||
|
let followStackView = UIStackView()
|
||||||
|
let followingButton = UIButton()
|
||||||
|
let followersButton = UIButton()
|
||||||
let segmentedControl = UISegmentedControl()
|
let segmentedControl = UISegmentedControl()
|
||||||
|
|
||||||
var viewModel: ProfileViewModel? {
|
var viewModel: ProfileViewModel? {
|
||||||
|
@ -100,8 +103,17 @@ final class AccountHeaderView: UIView {
|
||||||
mutableNote.resizeAttachments(toLineHeight: noteFont.lineHeight)
|
mutableNote.resizeAttachments(toLineHeight: noteFont.lineHeight)
|
||||||
noteTextView.attributedText = mutableNote
|
noteTextView.attributedText = mutableNote
|
||||||
noteTextView.isHidden = false
|
noteTextView.isHidden = false
|
||||||
|
|
||||||
|
followingButton.setAttributedLocalizedTitle(
|
||||||
|
localizationKey: "account.following-count",
|
||||||
|
count: accountViewModel.followingCount)
|
||||||
|
followersButton.setAttributedLocalizedTitle(
|
||||||
|
localizationKey: "account.followers-count",
|
||||||
|
count: accountViewModel.followersCount)
|
||||||
|
followStackView.isHidden = false
|
||||||
} else {
|
} else {
|
||||||
noteTextView.isHidden = true
|
noteTextView.isHidden = true
|
||||||
|
followStackView.isHidden = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,6 +276,19 @@ private extension AccountHeaderView {
|
||||||
noteTextView.isScrollEnabled = false
|
noteTextView.isScrollEnabled = false
|
||||||
noteTextView.delegate = self
|
noteTextView.delegate = self
|
||||||
|
|
||||||
|
baseStackView.addArrangedSubview(followStackView)
|
||||||
|
followStackView.distribution = .fillEqually
|
||||||
|
|
||||||
|
followingButton.addAction(
|
||||||
|
UIAction { [weak self] _ in self?.viewModel?.accountViewModel?.followingSelected() },
|
||||||
|
for: .touchUpInside)
|
||||||
|
followStackView.addArrangedSubview(followingButton)
|
||||||
|
|
||||||
|
followersButton.addAction(
|
||||||
|
UIAction { [weak self] _ in self?.viewModel?.accountViewModel?.followersSelected() },
|
||||||
|
for: .touchUpInside)
|
||||||
|
followStackView.addArrangedSubview(followersButton)
|
||||||
|
|
||||||
for (index, collection) in ProfileCollection.allCases.enumerated() {
|
for (index, collection) in ProfileCollection.allCases.enumerated() {
|
||||||
segmentedControl.insertSegment(
|
segmentedControl.insertSegment(
|
||||||
action: UIAction(title: collection.title) { [weak self] _ in
|
action: UIAction(title: collection.title) { [weak self] _ in
|
||||||
|
|
|
@ -347,13 +347,11 @@ private extension StatusView {
|
||||||
let noFavorites = viewModel.favoritesCount == 0
|
let noFavorites = viewModel.favoritesCount == 0
|
||||||
let noInteractions = !isContextParent || (noReblogs && noFavorites)
|
let noInteractions = !isContextParent || (noReblogs && noFavorites)
|
||||||
|
|
||||||
setAttributedLocalizedTitle(
|
rebloggedByButton.setAttributedLocalizedTitle(
|
||||||
button: rebloggedByButton,
|
|
||||||
localizationKey: "status.reblogs-count",
|
localizationKey: "status.reblogs-count",
|
||||||
count: viewModel.reblogsCount)
|
count: viewModel.reblogsCount)
|
||||||
rebloggedByButton.isHidden = noReblogs
|
rebloggedByButton.isHidden = noReblogs
|
||||||
setAttributedLocalizedTitle(
|
favoritedByButton.setAttributedLocalizedTitle(
|
||||||
button: favoritedByButton,
|
|
||||||
localizationKey: "status.favorites-count",
|
localizationKey: "status.favorites-count",
|
||||||
count: viewModel.favoritesCount)
|
count: viewModel.favoritesCount)
|
||||||
favoritedByButton.isHidden = noFavorites
|
favoritedByButton.isHidden = noFavorites
|
||||||
|
@ -421,15 +419,6 @@ private extension StatusView {
|
||||||
menuButton.setImage(UIImage(systemName: "ellipsis",
|
menuButton.setImage(UIImage(systemName: "ellipsis",
|
||||||
withConfiguration: UIImage.SymbolConfiguration(scale: scale)), for: .normal)
|
withConfiguration: UIImage.SymbolConfiguration(scale: scale)), for: .normal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAttributedLocalizedTitle(button: UIButton, localizationKey: String, count: Int) {
|
|
||||||
let localizedTitle = String.localizedStringWithFormat(NSLocalizedString(localizationKey, comment: ""), count)
|
|
||||||
|
|
||||||
button.setAttributedTitle(localizedTitle.countEmphasizedAttributedString(count: count), for: .normal)
|
|
||||||
button.setAttributedTitle(
|
|
||||||
localizedTitle.countEmphasizedAttributedString(count: count, highlighted: true),
|
|
||||||
for: .highlighted)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension UIButton {
|
private extension UIButton {
|
||||||
|
|
Loading…
Reference in a new issue