mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-29 03:31:02 +00:00
Custom emojis in display name
This commit is contained in:
parent
ae9f78e737
commit
c2a2fe1f86
11 changed files with 75 additions and 20 deletions
|
@ -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)
|
||||||
|
|
|
@ -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: [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
17
Packages/Models/Sources/Models/Emoji.swift
Normal file
17
Packages/Models/Sources/Models/Emoji.swift
Normal 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
|
||||||
|
}
|
27
Packages/Models/Sources/Models/Ext/AccountExt.swift
Normal file
27
Packages/Models/Sources/Models/Ext/AccountExt.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue