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:
parent
36fba12791
commit
804fdf89f8
@ -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 }
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)"
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user