Custom emojis in display name

This commit is contained in:
Thomas Ricouard 2022-12-21 17:39:48 +01:00
parent ae9f78e737
commit c2a2fe1f86
11 changed files with 75 additions and 20 deletions

View file

@ -98,7 +98,7 @@ struct AccountDetailHeaderView: View {
accountAvatarView accountAvatarView
HStack { HStack {
VStack(alignment: .leading, spacing: 0) { VStack(alignment: .leading, spacing: 0) {
Text(account.displayName) account.displayNameWithEmojis
.font(.headline) .font(.headline)
Text(account.acct) Text(account.acct)
.font(.callout) .font(.callout)

View file

@ -25,6 +25,7 @@ public struct Account: Codable, Identifiable, Equatable, Hashable {
public let lastStatusAt: String? public let lastStatusAt: String?
public let fields: [Field] public let fields: [Field]
public let locked: Bool public let locked: Bool
public let emojis: [Emoji]
public static func placeholder() -> Account { public static func placeholder() -> Account {
.init(id: UUID().uuidString, .init(id: UUID().uuidString,
@ -40,6 +41,7 @@ public struct Account: Codable, Identifiable, Equatable, Hashable {
statusesCount: 10, statusesCount: 10,
lastStatusAt: nil, lastStatusAt: nil,
fields: [], fields: [],
locked: false) locked: false,
emojis: [])
} }
} }

View file

@ -16,9 +16,11 @@ extension HTMLString {
public var asSafeAttributedString: AttributedString { public var asSafeAttributedString: AttributedString {
do { do {
// Add space between hashtags that follow each other
let markdown = asMarkdown.replacingOccurrences(of: ")[#", with: ") [#")
let options = AttributedString.MarkdownParsingOptions(allowsExtendedAttributes: true, let options = AttributedString.MarkdownParsingOptions(allowsExtendedAttributes: true,
interpretedSyntax: .inlineOnlyPreservingWhitespace) interpretedSyntax: .inlineOnlyPreservingWhitespace)
return try AttributedString(markdown: asMarkdown, options: options) return try AttributedString(markdown: markdown, options: options)
} catch { } catch {
return AttributedString(stringLiteral: self) return AttributedString(stringLiteral: self)
} }

View file

@ -0,0 +1,17 @@
import Foundation
public struct Emoji: Codable, Hashable, Identifiable {
public func hash(into hasher: inout Hasher) {
hasher.combine(shortcode)
}
public var id: String {
shortcode
}
public let shortcode: String
public let url: URL
public let staticUrl: URL
public let visibleInPicker: Bool
}

View file

@ -0,0 +1,27 @@
import Foundation
import SwiftUI
extension Account {
public var displayNameWithEmojis: some View {
let splittedDisplayName = displayName.split(separator: ":")
return HStack(spacing: 0) {
ForEach(splittedDisplayName, id: \.self) { part in
if let emoji = emojis.first(where: { $0.shortcode == part }) {
AsyncImage(
url: emoji.url,
content: { image in
image.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 20, maxHeight: 20)
},
placeholder: {
ProgressView()
}
)
} else {
Text(part)
}
}
}
}
}

View file

@ -24,7 +24,7 @@ public struct Notification: Codable, Identifiable {
} }
public static func placeholders() -> [Notification] { public static func placeholders() -> [Notification] {
[.placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder()] [.placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder()]
} }
} }

View file

@ -14,6 +14,7 @@ public protocol AnyStatus {
var favourited: Bool { get } var favourited: Bool { get }
var reblogged: Bool { get } var reblogged: Bool { get }
var pinned: Bool? { get } var pinned: Bool? { get }
var emojis: [Emoji] { get }
} }
public struct Status: AnyStatus, Codable, Identifiable { public struct Status: AnyStatus, Codable, Identifiable {
@ -31,6 +32,7 @@ public struct Status: AnyStatus, Codable, Identifiable {
public let favourited: Bool public let favourited: Bool
public let reblogged: Bool public let reblogged: Bool
public let pinned: Bool? public let pinned: Bool?
public let emojis: [Emoji]
public static func placeholder() -> Status { public static func placeholder() -> Status {
.init(id: UUID().uuidString, .init(id: UUID().uuidString,
@ -46,7 +48,8 @@ public struct Status: AnyStatus, Codable, Identifiable {
card: nil, card: nil,
favourited: false, favourited: false,
reblogged: false, reblogged: false,
pinned: false) pinned: false,
emojis: [])
} }
public static func placeholders() -> [Status] { public static func placeholders() -> [Status] {
@ -68,4 +71,5 @@ public struct ReblogStatus: AnyStatus, Codable, Identifiable {
public let favourited: Bool public let favourited: Bool
public let reblogged: Bool public let reblogged: Bool
public let pinned: Bool? public let pinned: Bool?
public let emojis: [Emoji]
} }

View file

@ -25,12 +25,12 @@ struct NotificationRowView: View {
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.padding(.horizontal, 4) .padding(.horizontal, 4)
if type.displayAccountName() { if type.displayAccountName() {
Text(notification.account.displayName) notification.account.displayNameWithEmojis
.font(.headline) + .font(.subheadline)
Text(" ") Text(" ")
} }
Text(type.label()) Text(type.label())
.font(.body) .font(.subheadline)
Spacer() Spacer()
} }
if let status = notification.status { if let status = notification.status {
@ -91,7 +91,7 @@ extension Models.Notification.NotificationType {
case .status: case .status:
return "pencil" return "pencil"
case .mention: case .mention:
return "at" return "at.circle.fill"
case .reblog: case .reblog:
return "arrow.left.arrow.right.circle.fill" return "arrow.left.arrow.right.circle.fill"
case .follow, .follow_request: case .follow, .follow_request:

View file

@ -7,7 +7,6 @@ import DesignSystem
public struct NotificationsListView: View { public struct NotificationsListView: View {
@EnvironmentObject private var client: Client @EnvironmentObject private var client: Client
@StateObject private var viewModel = NotificationsViewModel() @StateObject private var viewModel = NotificationsViewModel()
@State private var didAppear: Bool = false
public init() { } public init() { }
@ -25,14 +24,12 @@ public struct NotificationsListView: View {
.padding(.top, DS.Constants.layoutPadding) .padding(.top, DS.Constants.layoutPadding)
} }
.task { .task {
if !didAppear { await viewModel.fetchNotifications()
didAppear = true
viewModel.client = client
await viewModel.fetchNotifications()
}
} }
.refreshable { .refreshable {
await viewModel.fetchNotifications() Task {
await viewModel.fetchNotifications()
}
} }
.navigationTitle(Text("Notifications")) .navigationTitle(Text("Notifications"))
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)

View file

@ -22,7 +22,9 @@ class NotificationsViewModel: ObservableObject {
func fetchNotifications() async { func fetchNotifications() async {
guard let client else { return } guard let client else { return }
do { do {
state = .loading if notifications.isEmpty {
state = .loading
}
notifications = try await client.get(endpoint: Notifications.notifications(maxId: nil)) notifications = try await client.get(endpoint: Notifications.notifications(maxId: nil))
state = .display(notifications: notifications, nextPageState: .hasNextPage) state = .display(notifications: notifications, nextPageState: .hasNextPage)
} catch { } catch {

View file

@ -31,11 +31,15 @@ public struct StatusRowView: View {
if viewModel.status.reblog != nil { if viewModel.status.reblog != nil {
HStack(spacing: 2) { HStack(spacing: 2) {
Image(systemName:"arrow.left.arrow.right.circle") Image(systemName:"arrow.left.arrow.right.circle")
Text("\(viewModel.status.account.displayName) reblogged") viewModel.status.account.displayNameWithEmojis
Text("boosted")
} }
.font(.footnote) .font(.footnote)
.foregroundColor(.gray) .foregroundColor(.gray)
.fontWeight(.semibold) .fontWeight(.semibold)
.onTapGesture {
routeurPath.navigate(to: .accountDetailWithAccount(account: viewModel.status.account))
}
} }
} }
@ -71,8 +75,8 @@ public struct StatusRowView: View {
@ViewBuilder @ViewBuilder
private func makeAccountView(status: AnyStatus) -> some View { private func makeAccountView(status: AnyStatus) -> some View {
AvatarView(url: status.account.avatar) AvatarView(url: status.account.avatar)
VStack(alignment: .leading) { VStack(alignment: .leading, spacing: 0) {
Text(status.account.displayName) status.account.displayNameWithEmojis
.font(.subheadline) .font(.subheadline)
.fontWeight(.semibold) .fontWeight(.semibold)
Group { Group {