mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-28 11:01:02 +00:00
DM button
This commit is contained in:
parent
b235344268
commit
a0141f7750
10 changed files with 61 additions and 13 deletions
|
@ -11,6 +11,7 @@
|
||||||
"account.block-and-report" = "Block & report";
|
"account.block-and-report" = "Block & report";
|
||||||
"account.block.confirm-%@" = "Block %@?";
|
"account.block.confirm-%@" = "Block %@?";
|
||||||
"account.blocked" = "Blocked";
|
"account.blocked" = "Blocked";
|
||||||
|
"account.direct-message" = "Direct message";
|
||||||
"account.domain-block-%@" = "Block domain %@";
|
"account.domain-block-%@" = "Block domain %@";
|
||||||
"account.domain-block.confirm-%@" = "Block domain %@?";
|
"account.domain-block.confirm-%@" = "Block domain %@?";
|
||||||
"account.domain-unblock-%@" = "Unblock domain %@";
|
"account.domain-unblock-%@" = "Unblock domain %@";
|
||||||
|
|
|
@ -495,8 +495,8 @@ private extension TableViewController {
|
||||||
handle(navigation: navigation)
|
handle(navigation: navigation)
|
||||||
case let .attachment(attachmentViewModel, statusViewModel):
|
case let .attachment(attachmentViewModel, statusViewModel):
|
||||||
present(attachmentViewModel: attachmentViewModel, statusViewModel: statusViewModel)
|
present(attachmentViewModel: attachmentViewModel, statusViewModel: statusViewModel)
|
||||||
case let .compose(inReplyToViewModel, redraft):
|
case let .compose(inReplyToViewModel, redraft, directMessageTo):
|
||||||
compose(inReplyToViewModel: inReplyToViewModel, redraft: redraft)
|
compose(inReplyToViewModel: inReplyToViewModel, redraft: redraft, directMessageTo: directMessageTo)
|
||||||
case let .confirmDelete(statusViewModel, redraft):
|
case let .confirmDelete(statusViewModel, redraft):
|
||||||
confirmDelete(statusViewModel: statusViewModel, redraft: redraft)
|
confirmDelete(statusViewModel: statusViewModel, redraft: redraft)
|
||||||
case let .confirmUnfollow(accountViewModel):
|
case let .confirmUnfollow(accountViewModel):
|
||||||
|
@ -595,11 +595,12 @@ private extension TableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compose(inReplyToViewModel: StatusViewModel?, redraft: Status?) {
|
func compose(inReplyToViewModel: StatusViewModel?, redraft: Status?, directMessageTo: AccountViewModel?) {
|
||||||
rootViewModel?.navigationViewModel?.presentedNewStatusViewModel = rootViewModel?.newStatusViewModel(
|
rootViewModel?.navigationViewModel?.presentedNewStatusViewModel = rootViewModel?.newStatusViewModel(
|
||||||
identityContext: viewModel.identityContext,
|
identityContext: viewModel.identityContext,
|
||||||
inReplyTo: inReplyToViewModel,
|
inReplyTo: inReplyToViewModel,
|
||||||
redraft: redraft)
|
redraft: redraft,
|
||||||
|
directMessageTo: directMessageTo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func confirmDelete(statusViewModel: StatusViewModel, redraft: Bool) {
|
func confirmDelete(statusViewModel: StatusViewModel, redraft: Bool) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ public enum CollectionItemEvent {
|
||||||
case refresh
|
case refresh
|
||||||
case navigation(Navigation)
|
case navigation(Navigation)
|
||||||
case attachment(AttachmentViewModel, StatusViewModel)
|
case attachment(AttachmentViewModel, StatusViewModel)
|
||||||
case compose(inReplyTo: StatusViewModel?, redraft: Status?)
|
case compose(inReplyTo: StatusViewModel? = nil, redraft: Status? = nil, directMessageTo: AccountViewModel? = nil)
|
||||||
case confirmDelete(StatusViewModel, redraft: Bool)
|
case confirmDelete(StatusViewModel, redraft: Bool)
|
||||||
case confirmUnfollow(AccountViewModel)
|
case confirmUnfollow(AccountViewModel)
|
||||||
case confirmHideReblogs(AccountViewModel)
|
case confirmHideReblogs(AccountViewModel)
|
||||||
|
|
|
@ -358,6 +358,15 @@ extension CollectionItemsViewModel: CollectionViewModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension CollectionItemsViewModel {
|
||||||
|
func sendDirectMessage(accountViewModel: AccountViewModel) {
|
||||||
|
eventsSubject.send(
|
||||||
|
Just(.compose(directMessageTo: accountViewModel))
|
||||||
|
.setFailureType(to: Error.self)
|
||||||
|
.eraseToAnyPublisher())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private extension CollectionItemsViewModel {
|
private extension CollectionItemsViewModel {
|
||||||
private static let lastReadIdDebounceInterval: TimeInterval = 0.5
|
private static let lastReadIdDebounceInterval: TimeInterval = 0.5
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ public final class NewStatusViewModel: ObservableObject {
|
||||||
environment: AppEnvironment,
|
environment: AppEnvironment,
|
||||||
inReplyTo: StatusViewModel?,
|
inReplyTo: StatusViewModel?,
|
||||||
redraft: Status?,
|
redraft: Status?,
|
||||||
|
directMessageTo: AccountViewModel?,
|
||||||
extensionContext: NSExtensionContext?) {
|
extensionContext: NSExtensionContext?) {
|
||||||
self.allIdentitiesService = allIdentitiesService
|
self.allIdentitiesService = allIdentitiesService
|
||||||
self.identityContext = identityContext
|
self.identityContext = identityContext
|
||||||
|
@ -78,6 +79,9 @@ public final class NewStatusViewModel: ObservableObject {
|
||||||
.map("@".appending))
|
.map("@".appending))
|
||||||
|
|
||||||
compositionViewModel.text = mentions.joined(separator: " ").appending(" ")
|
compositionViewModel.text = mentions.joined(separator: " ").appending(" ")
|
||||||
|
} else if let directMessageTo = directMessageTo {
|
||||||
|
compositionViewModel.text = directMessageTo.accountName.appending(" ")
|
||||||
|
visibility = .direct
|
||||||
}
|
}
|
||||||
|
|
||||||
compositionViewModels = [compositionViewModel]
|
compositionViewModels = [compositionViewModel]
|
||||||
|
|
|
@ -76,6 +76,12 @@ public extension ProfileViewModel {
|
||||||
func fetchProfile() -> AnyPublisher<Never, Never> {
|
func fetchProfile() -> AnyPublisher<Never, Never> {
|
||||||
profileService.fetchProfile().assignErrorsToAlertItem(to: \.alertItem, on: self)
|
profileService.fetchProfile().assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendDirectMessage() {
|
||||||
|
guard let accountViewModel = accountViewModel else { return }
|
||||||
|
|
||||||
|
collectionViewModel.value.sendDirectMessage(accountViewModel: accountViewModel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProfileViewModel: CollectionViewModel {
|
extension ProfileViewModel: CollectionViewModel {
|
||||||
|
|
|
@ -68,13 +68,15 @@ public extension RootViewModel {
|
||||||
func newStatusViewModel(
|
func newStatusViewModel(
|
||||||
identityContext: IdentityContext,
|
identityContext: IdentityContext,
|
||||||
inReplyTo: StatusViewModel? = nil,
|
inReplyTo: StatusViewModel? = nil,
|
||||||
redraft: Status? = nil) -> NewStatusViewModel {
|
redraft: Status? = nil,
|
||||||
|
directMessageTo: AccountViewModel? = nil) -> NewStatusViewModel {
|
||||||
NewStatusViewModel(
|
NewStatusViewModel(
|
||||||
allIdentitiesService: allIdentitiesService,
|
allIdentitiesService: allIdentitiesService,
|
||||||
identityContext: identityContext,
|
identityContext: identityContext,
|
||||||
environment: environment,
|
environment: environment,
|
||||||
inReplyTo: inReplyTo,
|
inReplyTo: inReplyTo,
|
||||||
redraft: redraft,
|
redraft: redraft,
|
||||||
|
directMessageTo: directMessageTo,
|
||||||
extensionContext: nil)
|
extensionContext: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ public extension ShareExtensionNavigationViewModel {
|
||||||
environment: environment,
|
environment: environment,
|
||||||
inReplyTo: nil,
|
inReplyTo: nil,
|
||||||
redraft: nil,
|
redraft: nil,
|
||||||
|
directMessageTo: nil,
|
||||||
extensionContext: extensionContext)
|
extensionContext: extensionContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,7 +250,7 @@ public extension StatusViewModel {
|
||||||
replyViewModel.configuration = configuration.reply()
|
replyViewModel.configuration = configuration.reply()
|
||||||
|
|
||||||
eventsSubject.send(
|
eventsSubject.send(
|
||||||
Just(.compose(inReplyTo: replyViewModel, redraft: nil))
|
Just(.compose(inReplyTo: replyViewModel))
|
||||||
.setFailureType(to: Error.self)
|
.setFailureType(to: Error.self)
|
||||||
.eraseToAnyPublisher())
|
.eraseToAnyPublisher())
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ final class AccountHeaderView: UIView {
|
||||||
let avatarImageView = SDAnimatedImageView()
|
let avatarImageView = SDAnimatedImageView()
|
||||||
let avatarButton = UIButton()
|
let avatarButton = UIButton()
|
||||||
let relationshipButtonsStackView = UIStackView()
|
let relationshipButtonsStackView = UIStackView()
|
||||||
|
let directMessageButton = UIButton()
|
||||||
let followButton = UIButton(type: .system)
|
let followButton = UIButton(type: .system)
|
||||||
let unfollowButton = UIButton(type: .system)
|
let unfollowButton = UIButton(type: .system)
|
||||||
let notifyButton = UIButton()
|
let notifyButton = UIButton()
|
||||||
|
@ -216,10 +217,18 @@ final class AccountHeaderView: UIView {
|
||||||
override func layoutSubviews() {
|
override func layoutSubviews() {
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
|
|
||||||
for button in [followButton, unfollowButton, notifyButton, unnotifyButton] {
|
if let pointSize = followingButton.titleLabel?.font.pointSize {
|
||||||
let inset = (followButton.bounds.height - (button.titleLabel?.bounds.height ?? 0)) / 2
|
relationshipButtonsStackView.heightAnchor
|
||||||
|
.constraint(equalToConstant: pointSize + .defaultSpacing * 2).isActive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for button in [followButton, unfollowButton] {
|
||||||
|
let inset = (button.bounds.height - (button.titleLabel?.bounds.height ?? 0))
|
||||||
|
|
||||||
button.contentEdgeInsets = .init(top: 0, left: inset, bottom: 0, right: inset)
|
button.contentEdgeInsets = .init(top: 0, left: inset, bottom: 0, right: inset)
|
||||||
|
}
|
||||||
|
|
||||||
|
for button in [directMessageButton, followButton, unfollowButton, notifyButton, unnotifyButton] {
|
||||||
button.layer.cornerRadius = button.bounds.height / 2
|
button.layer.cornerRadius = button.bounds.height / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -299,19 +308,30 @@ private extension AccountHeaderView {
|
||||||
relationshipButtonsStackView.spacing = .defaultSpacing
|
relationshipButtonsStackView.spacing = .defaultSpacing
|
||||||
relationshipButtonsStackView.addArrangedSubview(UIView())
|
relationshipButtonsStackView.addArrangedSubview(UIView())
|
||||||
|
|
||||||
for button in [followButton, unfollowButton, notifyButton, unnotifyButton] {
|
for button in [directMessageButton, notifyButton, unnotifyButton, followButton, unfollowButton] {
|
||||||
relationshipButtonsStackView.addArrangedSubview(button)
|
relationshipButtonsStackView.addArrangedSubview(button)
|
||||||
button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
|
button.titleLabel?.font = .preferredFont(forTextStyle: .headline)
|
||||||
button.titleLabel?.adjustsFontForContentSizeCategory = true
|
button.titleLabel?.adjustsFontForContentSizeCategory = true
|
||||||
button.backgroundColor = .secondarySystemBackground
|
button.backgroundColor = .secondarySystemBackground
|
||||||
}
|
}
|
||||||
|
|
||||||
|
directMessageButton.setImage(
|
||||||
|
UIImage(
|
||||||
|
systemName: "envelope",
|
||||||
|
withConfiguration: UIImage.SymbolConfiguration(scale: .small)),
|
||||||
|
for: .normal)
|
||||||
|
directMessageButton.accessibilityLabel = NSLocalizedString("account.direct-message", comment: "")
|
||||||
|
directMessageButton.addAction(
|
||||||
|
UIAction { [weak self] _ in self?.viewModel.sendDirectMessage() },
|
||||||
|
for: .touchUpInside)
|
||||||
|
|
||||||
followButton.setImage(
|
followButton.setImage(
|
||||||
UIImage(
|
UIImage(
|
||||||
systemName: "person.badge.plus",
|
systemName: "person.badge.plus",
|
||||||
withConfiguration: UIImage.SymbolConfiguration(scale: .small)),
|
withConfiguration: UIImage.SymbolConfiguration(scale: .small)),
|
||||||
for: .normal)
|
for: .normal)
|
||||||
followButton.isHidden = true
|
followButton.isHidden = true
|
||||||
|
followButton.titleLabel?.adjustsFontSizeToFitWidth = true
|
||||||
followButton.addAction(
|
followButton.addAction(
|
||||||
UIAction { [weak self] _ in self?.viewModel.accountViewModel?.follow() },
|
UIAction { [weak self] _ in self?.viewModel.accountViewModel?.follow() },
|
||||||
for: .touchUpInside)
|
for: .touchUpInside)
|
||||||
|
@ -323,14 +343,16 @@ private extension AccountHeaderView {
|
||||||
for: .normal)
|
for: .normal)
|
||||||
unfollowButton.setTitle(NSLocalizedString("account.following", comment: ""), for: .normal)
|
unfollowButton.setTitle(NSLocalizedString("account.following", comment: ""), for: .normal)
|
||||||
unfollowButton.isHidden = true
|
unfollowButton.isHidden = true
|
||||||
|
unfollowButton.titleLabel?.adjustsFontSizeToFitWidth = true
|
||||||
unfollowButton.addAction(
|
unfollowButton.addAction(
|
||||||
UIAction { [weak self] _ in self?.viewModel.accountViewModel?.confirmUnfollow() },
|
UIAction { [weak self] _ in self?.viewModel.accountViewModel?.confirmUnfollow() },
|
||||||
for: .touchUpInside)
|
for: .touchUpInside)
|
||||||
|
|
||||||
notifyButton.setImage(
|
notifyButton.setImage(
|
||||||
UIImage(systemName: "bell",
|
UIImage(systemName: "bell",
|
||||||
withConfiguration: UIImage.SymbolConfiguration(scale: .large)),
|
withConfiguration: UIImage.SymbolConfiguration(scale: .medium)),
|
||||||
for: .normal)
|
for: .normal)
|
||||||
|
notifyButton.imageView?.contentMode = .scaleAspectFit
|
||||||
notifyButton.accessibilityLabel = NSLocalizedString("account.notify", comment: "")
|
notifyButton.accessibilityLabel = NSLocalizedString("account.notify", comment: "")
|
||||||
notifyButton.tintColor = .secondaryLabel
|
notifyButton.tintColor = .secondaryLabel
|
||||||
notifyButton.isHidden = true
|
notifyButton.isHidden = true
|
||||||
|
@ -340,7 +362,7 @@ private extension AccountHeaderView {
|
||||||
|
|
||||||
unnotifyButton.setImage(
|
unnotifyButton.setImage(
|
||||||
UIImage(systemName: "bell.fill",
|
UIImage(systemName: "bell.fill",
|
||||||
withConfiguration: UIImage.SymbolConfiguration(scale: .large)),
|
withConfiguration: UIImage.SymbolConfiguration(scale: .medium)),
|
||||||
for: .normal)
|
for: .normal)
|
||||||
unnotifyButton.accessibilityLabel = NSLocalizedString("account.unnotify", comment: "")
|
unnotifyButton.accessibilityLabel = NSLocalizedString("account.unnotify", comment: "")
|
||||||
unnotifyButton.isHidden = true
|
unnotifyButton.isHidden = true
|
||||||
|
@ -505,7 +527,9 @@ private extension AccountHeaderView {
|
||||||
equalTo: headerImageView.bottomAnchor,
|
equalTo: headerImageView.bottomAnchor,
|
||||||
constant: .defaultSpacing),
|
constant: .defaultSpacing),
|
||||||
relationshipButtonsStackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
|
relationshipButtonsStackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
|
||||||
relationshipButtonsStackView.bottomAnchor.constraint(equalTo: avatarBackgroundView.bottomAnchor),
|
directMessageButton.widthAnchor.constraint(equalTo: directMessageButton.heightAnchor),
|
||||||
|
notifyButton.widthAnchor.constraint(equalTo: notifyButton.heightAnchor),
|
||||||
|
unnotifyButton.widthAnchor.constraint(equalTo: unnotifyButton.heightAnchor),
|
||||||
baseStackView.topAnchor.constraint(equalTo: avatarBackgroundView.bottomAnchor, constant: .defaultSpacing),
|
baseStackView.topAnchor.constraint(equalTo: avatarBackgroundView.bottomAnchor, constant: .defaultSpacing),
|
||||||
baseStackView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor),
|
baseStackView.leadingAnchor.constraint(equalTo: readableContentGuide.leadingAnchor),
|
||||||
baseStackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
|
baseStackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
|
||||||
|
|
Loading…
Reference in a new issue