2
2
mirror of https://github.com/mastodon/mastodon-ios synced 2025-04-11 22:58:02 +02:00

Distinguish private mentions and replies in the status view header

This commit is contained in:
shannon 2025-03-07 11:14:30 -05:00
parent 36fba12791
commit 804fdf89f8
5 changed files with 62 additions and 76 deletions

View File

@ -36,7 +36,7 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
}
switch await statusView.viewModel.header {
case .none:
case .none, .directMention:
break
case .reply:
guard let replyToAccountID = status.entity.inReplyToAccountID else { return }

View File

@ -44,26 +44,26 @@ extension MetaLabel {
setup(style: style)
}
public func setup(style: Style) {
public func setup(style: Style, fontColor: UIColor? = nil) {
let font: UIFont
let textColor: UIColor
switch style {
case .statusHeader:
font = UIFontMetrics(forTextStyle: .footnote).scaledFont(for: .systemFont(ofSize: 13, weight: .bold))
textColor = Asset.Colors.Label.secondary.color
textColor = fontColor ?? Asset.Colors.Label.secondary.color
case .statusName:
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold))
textColor = Asset.Colors.Label.primary.color
textColor = fontColor ?? Asset.Colors.Label.primary.color
case .statusUsername:
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .regular))
textColor = Asset.Colors.Label.secondary.color
textColor = fontColor ?? Asset.Colors.Label.secondary.color
case .statusSpoilerOverlay:
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 20, weight: .semibold))
textColor = Asset.Colors.Label.primary.color
textColor = fontColor ?? Asset.Colors.Label.primary.color
textAlignment = .center
paragraphStyle.alignment = .center
numberOfLines = 0
@ -71,31 +71,31 @@ extension MetaLabel {
case .statusSpoilerBanner:
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .regular))
textColor = Asset.Colors.Label.primary.color
textColor = fontColor ?? Asset.Colors.Label.primary.color
case .notificationTitle:
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 14, weight: .regular))
textColor = Asset.Colors.Label.secondary.color
textColor = fontColor ?? Asset.Colors.Label.secondary.color
case .profileFieldName:
font = UIFontMetrics(forTextStyle: .caption1).scaledFont(for: .systemFont(ofSize: 12, weight: .regular))
textColor = Asset.Colors.Label.secondary.color
textColor = fontColor ?? Asset.Colors.Label.secondary.color
case .profileFieldValue:
font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular))
textColor = Asset.Colors.Label.primary.color
textColor = fontColor ?? Asset.Colors.Label.primary.color
case .profileCardName:
font = .systemFont(ofSize: 17, weight: .semibold)
textColor = Asset.Colors.Label.primary.color
textColor = fontColor ?? Asset.Colors.Label.primary.color
case .profileCardUsername:
font = .systemFont(ofSize: 15, weight: .regular)
textColor = Asset.Colors.Label.secondary.color
textColor = fontColor ?? Asset.Colors.Label.secondary.color
case .profileCardFamiliarFollowerFooter:
font = UIFontMetrics(forTextStyle: .footnote).scaledFont(for: .systemFont(ofSize: 13, weight: .regular), maximumPointSize: 26)
textColor = Asset.Colors.Label.secondary.color
textColor = fontColor ?? Asset.Colors.Label.secondary.color
numberOfLines = 2
textContainer.maximumNumberOfLines = 2
paragraphStyle.lineSpacing = 0
@ -103,25 +103,25 @@ extension MetaLabel {
case .titleView:
font = .systemFont(ofSize: 17, weight: .semibold)
textColor = Asset.Colors.Label.primary.color
textColor = fontColor ?? Asset.Colors.Label.primary.color
textAlignment = .center
paragraphStyle.alignment = .center
case .recommendAccountName:
font = .systemFont(ofSize: 18, weight: .semibold)
textColor = .white
textColor = fontColor ?? .white
case .autoCompletion:
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold), maximumPointSize: 22)
textColor = Asset.Colors.Brand.blurple.color
textColor = fontColor ?? Asset.Colors.Brand.blurple.color
case .accountListName:
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .regular), maximumPointSize: 22)
textColor = Asset.Colors.Label.primary.color
textColor = fontColor ?? Asset.Colors.Label.primary.color
case .accountListUsername:
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .regular), maximumPointSize: 20)
textColor = Asset.Colors.Label.secondary.color
textColor = fontColor ?? Asset.Colors.Label.secondary.color
case .sidebarHeadline(let isSelected):
font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 22, weight: .regular), maximumPointSize: 20)
@ -132,7 +132,7 @@ extension MetaLabel {
textColor = isSelected ? .white : Asset.Colors.Label.secondary.color
case .aboutInstance:
font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular))
textColor = .label
textColor = fontColor ?? .label
paragraphStyle.paragraphSpacing = 0
}

View File

@ -231,36 +231,27 @@ extension StatusView {
return .repost(info: .init(header: metaContent))
}
}()
} else if let _ = status.inReplyToID,
let inReplyToAccountID = status.inReplyToAccountID
{
} else if let inReplyToID = status.inReplyToID {
func createHeader(
name: String?,
emojis: MastodonContent.Emojis?
) -> ViewModel.Header {
let fallbackMetaContent = PlaintextMetaContent(string: L10n.Common.Controls.Status.userRepliedTo("-"))
let fallbackReplyHeader = ViewModel.Header.reply(info: .init(header: fallbackMetaContent))
guard let name = name,
let emojis = emojis
else {
return fallbackReplyHeader
let name = name ?? "-"
let emojis = emojis ?? [:]
let replyDescription = status.visibility == .direct ? L10n.Common.Controls.Status.privateReply : L10n.Common.Controls.Status.userRepliedTo(name)
let content = MastodonContent(content: replyDescription, emojis: emojis)
if let metaContent = try? MastodonMetaContent.convert(document: content) {
return ViewModel.Header.reply(info: .init(header: metaContent), isDirectMessage: status.visibility == .direct)
} else {
return ViewModel.Header.reply(info: .init(header: PlaintextMetaContent(string: replyDescription)), isDirectMessage: status.visibility == .direct)
}
let content = MastodonContent(content: L10n.Common.Controls.Status.userRepliedTo(name), emojis: emojis)
guard let metaContent = try? MastodonMetaContent.convert(document: content) else {
return fallbackReplyHeader
}
let header = ViewModel.Header.reply(info: .init(header: metaContent))
return header
}
if let inReplyToID = status.inReplyToID {
// A. replyTo status exist
/// we need to initially set an empty header, otherwise the layout gets messed up
viewModel.header = createHeader(name: "", emojis: [:])
/// finally we can load the status information and display the correct header
/// we need to initially set an empty header, otherwise the layout gets messed up
viewModel.header = createHeader(name: "", emojis: [:])
if status.visibility != .direct {
/// finally, if we need to get the account info, we can load the status information and display the correct header
if let authenticationBox = viewModel.authenticationBox {
Mastodon.API.Statuses.status(
session: .shared,
@ -279,34 +270,9 @@ extension StatusView {
})
.store(in: &disposeBag)
}
} else {
// B. replyTo status not exist
let header = createHeader(name: nil, emojis: nil)
viewModel.header = header
if let authenticationBox = viewModel.authenticationBox {
Just(inReplyToAccountID)
.asyncMap { userID in
return try await Mastodon.API.Account.accountInfo(
session: .shared,
domain: authenticationBox.domain,
userID: userID,
authorization: authenticationBox.userAuthorization
).singleOutput()
}
.receive(on: DispatchQueue.main)
.sink { completion in
// do nothing
} receiveValue: { [weak self] response in
guard let self = self else { return }
let user = response.value
let header = createHeader(name: user.displayNameWithFallback, emojis: user.emojiMeta)
self.viewModel.header = header
}
.store(in: &disposeBag)
} // end if let
} // end else B.
}
} else if status.visibility == .direct {
viewModel.header = .directMention
} else {
viewModel.header = .none
}

View File

@ -121,7 +121,8 @@ extension StatusView {
public enum Header {
case none
case reply(info: ReplyInfo)
case directMention
case reply(info: ReplyInfo, isDirectMessage: Bool)
case repost(info: RepostInfo)
// case notification(info: NotificationHeaderInfo)
@ -197,11 +198,22 @@ extension StatusView.ViewModel {
statusView.headerIconImageView.image = UIImage(systemName: "repeat")!.withRenderingMode(.alwaysTemplate)
statusView.headerInfoLabel.configure(content: info.header)
statusView.setHeaderDisplay()
case .reply(let info):
case let .reply(info, isDirect):
assert(Thread.isMainThread)
statusView.headerIconImageView.image = UIImage(systemName: "arrowshape.turn.up.left.fill")
statusView.headerIconImageView.image = UIImage(systemName: "arrowshape.turn.up.left.fill")!.withRenderingMode(.alwaysTemplate)
if isDirect {
statusView.headerIconImageView.tintColor = Asset.Colors.accent.color
statusView.headerInfoLabel.setup(style: .statusHeader, fontColor: Asset.Colors.accent.color)
}
statusView.headerInfoLabel.configure(content: info.header)
statusView.setHeaderDisplay()
case .directMention:
assert(Thread.isMainThread)
statusView.headerIconImageView.image = UIImage(systemName: "at")!.withRenderingMode(.alwaysTemplate)
statusView.headerIconImageView.tintColor = Asset.Colors.accent.color
statusView.headerInfoLabel.setup(style: .statusHeader, fontColor: Asset.Colors.accent.color)
statusView.headerInfoLabel.configure(content: PlaintextMetaContent(string: L10n.Common.Controls.Status.privateMention))
statusView.setHeaderDisplay()
}
}
.store(in: &disposeBag)
@ -679,7 +691,11 @@ extension StatusView.ViewModel {
case .none:
strings.append(authorName?.string)
strings.append(authorUsername)
case .reply(let info):
case .directMention:
strings.append(L10n.Common.Controls.Status.privateMention)
strings.append(authorName?.string)
strings.append(authorUsername)
case .reply(let info, _):
strings.append(authorName?.string)
strings.append(authorUsername)
strings.append(info.header.string)
@ -723,9 +739,11 @@ extension StatusView.ViewModel {
switch header {
case .none:
return "\(nameAndUsername), \(timestamp)"
case .directMention:
return "\(L10n.Common.Controls.Status.privateMention) \(nameAndUsername), \(timestamp)"
case .repost(info: let info):
return "\(info.header.string) \(nameAndUsername), \(timestamp)"
case .reply(info: let info):
case let .reply(info, _):
return "\(nameAndUsername) \(info.header.string), \(timestamp)"
}
}

View File

@ -544,6 +544,8 @@ public final class StatusView: UIView {
headerInfoLabel.text = nil
headerIconImageView.image = nil
headerInfoLabel.setup(style: .statusHeader)
headerIconImageView.tintColor = Asset.Colors.Label.secondary.color
}
public override init(frame: CGRect) {