mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-01-02 20:38:49 +00:00
Support Custom Emojis (#61)
* Support Custom Emojis * Update EmojiText * Update EmojiText * Use EmojiText in StatusEditorAutoCompleteView * Update EmojiText * Display Account displayName without emojis in navigation title Co-authored-by: Thomas Ricouard <ricouard77@gmail.com>
This commit is contained in:
parent
150cb5a8c5
commit
3acd5aced4
17 changed files with 78 additions and 68 deletions
|
@ -1,5 +1,14 @@
|
|||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "emojitext",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/divadretlaw/EmojiText",
|
||||
"state" : {
|
||||
"revision" : "f349e481499d2c832ab9d2dc28af238e53b1f9b4",
|
||||
"version" : "1.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "html2markdown",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
@ -23,8 +32,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kean/Nuke",
|
||||
"state" : {
|
||||
"revision" : "81f6a3dea0c8ce3b87389c241c48601be07af0b1",
|
||||
"version" : "11.5.1"
|
||||
"revision" : "2e9337168d08acccf72c039bf9324be24a1cf7d7",
|
||||
"version" : "11.5.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -41,8 +50,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/scinfu/SwiftSoup.git",
|
||||
"state" : {
|
||||
"revision" : "6778575285177365cbad3e5b8a72f2a20583cfec",
|
||||
"version" : "2.4.3"
|
||||
"revision" : "f707b8680cddb96dc1855632340a572ef37bbb98",
|
||||
"version" : "2.5.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import SwiftUI
|
||||
import DesignSystem
|
||||
import Env
|
||||
import EmojiText
|
||||
|
||||
struct AppAccountView: View {
|
||||
@EnvironmentObject private var routeurPath: RouterPath
|
||||
|
@ -21,7 +22,7 @@ struct AppAccountView: View {
|
|||
}
|
||||
VStack(alignment: .leading) {
|
||||
if let account = viewModel.account {
|
||||
account.displayNameWithEmojis
|
||||
EmojiText(account.safeDisplayName, emojis: account.emojis)
|
||||
Text("\(account.username)@\(viewModel.appAccount.server)")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.gray)
|
||||
|
|
|
@ -4,6 +4,7 @@ import DesignSystem
|
|||
import Env
|
||||
import Shimmer
|
||||
import NukeUI
|
||||
import EmojiText
|
||||
|
||||
struct AccountDetailHeaderView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
@ -108,7 +109,7 @@ struct AccountDetailHeaderView: View {
|
|||
accountAvatarView
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
account.displayNameWithEmojis
|
||||
EmojiText(account.safeDisplayName, emojis: account.emojis)
|
||||
.font(.headline)
|
||||
Text("@\(account.acct)")
|
||||
.font(.callout)
|
||||
|
@ -120,7 +121,7 @@ struct AccountDetailHeaderView: View {
|
|||
relationship: relationship))
|
||||
}
|
||||
}
|
||||
Text(account.note.asSafeAttributedString)
|
||||
EmojiText(account.note, emojis: account.emojis)
|
||||
.font(.body)
|
||||
.padding(.top, 8)
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
|
|
|
@ -5,6 +5,7 @@ import Status
|
|||
import Shimmer
|
||||
import DesignSystem
|
||||
import Env
|
||||
import EmojiText
|
||||
|
||||
public struct AccountDetailView: View {
|
||||
@Environment(\.redactionReasons) private var reasons
|
||||
|
@ -217,7 +218,7 @@ public struct AccountDetailView: View {
|
|||
Image(systemName: "checkmark.seal")
|
||||
.foregroundColor(Color.green.opacity(0.80))
|
||||
}
|
||||
Text(field.value.asSafeAttributedString)
|
||||
EmojiText(field.value, emojis: viewModel.account?.emojis ?? [])
|
||||
.foregroundColor(theme.tintColor)
|
||||
}
|
||||
.font(.body)
|
||||
|
@ -335,7 +336,8 @@ public struct AccountDetailView: View {
|
|||
if scrollOffset < -200 {
|
||||
switch viewModel.accountState {
|
||||
case let .data(account):
|
||||
account.displayNameWithEmojis.font(.headline)
|
||||
EmojiText(account.safeDisplayName, emojis: account.emojis)
|
||||
.font(.headline)
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import Models
|
|||
import Network
|
||||
import DesignSystem
|
||||
import Env
|
||||
import EmojiText
|
||||
|
||||
@MainActor
|
||||
public class AccountsListRowViewModel: ObservableObject {
|
||||
|
@ -32,13 +33,13 @@ public struct AccountsListRow: View {
|
|||
HStack(alignment: .top) {
|
||||
AvatarView(url: viewModel.account.avatar, size: .status)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
viewModel.account.displayNameWithEmojis
|
||||
EmojiText(viewModel.account.safeDisplayName, emojis: viewModel.account.emojis)
|
||||
.font(.subheadline)
|
||||
.fontWeight(.semibold)
|
||||
Text("@\(viewModel.account.acct)")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
Text(viewModel.account.note.asSafeAttributedString)
|
||||
EmojiText(viewModel.account.note, emojis: viewModel.account.emojis)
|
||||
.font(.footnote)
|
||||
.lineLimit(3)
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
|
|
|
@ -17,7 +17,9 @@ let package = Package(
|
|||
.package(name: "Models", path: "../Models"),
|
||||
.package(name: "Env", path: "../Env"),
|
||||
.package(url: "https://github.com/markiv/SwiftUI-Shimmer", exact: "1.1.0"),
|
||||
.package(url: "https://github.com/kean/Nuke", from: "11.5.0")],
|
||||
.package(url: "https://github.com/kean/Nuke", from: "11.5.0"),
|
||||
.package(url: "https://github.com/divadretlaw/EmojiText", from: "1.1.0")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "DesignSystem",
|
||||
|
@ -26,7 +28,8 @@ let package = Package(
|
|||
.product(name: "Env", package: "Env"),
|
||||
.product(name: "Shimmer", package: "SwiftUI-Shimmer"),
|
||||
.product(name: "NukeUI", package: "Nuke"),
|
||||
.product(name: "Nuke", package: "Nuke")
|
||||
.product(name: "Nuke", package: "Nuke"),
|
||||
.product(name: "EmojiText", package: "EmojiText")
|
||||
]),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -3,7 +3,6 @@ import SwiftUI
|
|||
import NukeUI
|
||||
import Models
|
||||
|
||||
@MainActor
|
||||
extension Account {
|
||||
private struct Part: Identifiable {
|
||||
let id = UUID().uuidString
|
||||
|
@ -17,34 +16,11 @@ extension Account {
|
|||
return displayName
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
public var displayNameWithEmojis: some View {
|
||||
if displayName.isEmpty {
|
||||
Text(safeDisplayName)
|
||||
}
|
||||
let splittedDisplayName = displayName.split(separator: ":").map{ Part(value: $0) }
|
||||
HStack(spacing: 0) {
|
||||
if displayName.isEmpty {
|
||||
Text(" ")
|
||||
}
|
||||
ForEach(splittedDisplayName, id: \.id) { part in
|
||||
if let emoji = emojis.first(where: { $0.shortcode == part.value }) {
|
||||
LazyImage(url: emoji.url) { state in
|
||||
if let image = state.image {
|
||||
image
|
||||
.resizingMode(.aspectFit)
|
||||
} else if state.isLoading {
|
||||
ProgressView()
|
||||
} else {
|
||||
ProgressView()
|
||||
}
|
||||
}
|
||||
.processors([.resize(size: .init(width: 20, height: 20))])
|
||||
.frame(width: 20, height: 20)
|
||||
} else {
|
||||
Text(part.value)
|
||||
}
|
||||
}
|
||||
public var displayNameWithoutEmojis: String {
|
||||
var name = safeDisplayName
|
||||
for emoji in emojis {
|
||||
name = name.replacingOccurrences(of: ":\(emoji.shortcode):", with: "")
|
||||
}
|
||||
return name.split(separator: " ", omittingEmptySubsequences: true).joined(separator: " ")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import Foundation
|
||||
import EmojiText
|
||||
import Models
|
||||
import HTML2Markdown
|
||||
|
||||
public extension EmojiText {
|
||||
init(_ string: HTMLString, emojis: [Emoji]) {
|
||||
let markdown = string.asMarkdown
|
||||
self.init(markdown: markdown, emojis: emojis.map { RemoteEmoji(shortcode: $0.shortcode, url: $0.url) })
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import SwiftUI
|
|||
import Models
|
||||
import DesignSystem
|
||||
import Network
|
||||
import EmojiText
|
||||
|
||||
public struct ListEditView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
@ -30,7 +31,7 @@ public struct ListEditView: View {
|
|||
HStack {
|
||||
AvatarView(url: account.avatar, size: .status)
|
||||
VStack(alignment: .leading) {
|
||||
account.displayNameWithEmojis
|
||||
EmojiText(account.safeDisplayName, emojis: account.emojis)
|
||||
Text("@\(account.acct)")
|
||||
.foregroundColor(.gray)
|
||||
.font(.footnote)
|
||||
|
|
|
@ -10,6 +10,8 @@ extension HTMLString {
|
|||
do {
|
||||
let dom = try HTMLParser().parse(html: self)
|
||||
return dom.toMarkdown()
|
||||
// Add space between hashtags and mentions that follow each other
|
||||
.replacingOccurrences(of: ")[", with: ") [")
|
||||
} catch {
|
||||
return self
|
||||
}
|
||||
|
@ -44,9 +46,7 @@ extension HTMLString {
|
|||
|
||||
public var asSafeAttributedString: AttributedString {
|
||||
do {
|
||||
// Add space between hashtags and mentions that follow each other
|
||||
let markdown = asMarkdown
|
||||
.replacingOccurrences(of: ")[", with: ") [")
|
||||
let options = AttributedString.MarkdownParsingOptions(allowsExtendedAttributes: true,
|
||||
interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
||||
return try AttributedString(markdown: markdown, options: options)
|
||||
|
|
|
@ -3,6 +3,7 @@ import Models
|
|||
import DesignSystem
|
||||
import Status
|
||||
import Env
|
||||
import EmojiText
|
||||
|
||||
struct NotificationRowView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
@ -50,18 +51,18 @@ struct NotificationRowView: View {
|
|||
private func makeMainLabel(type: Models.Notification.NotificationType) -> some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
Text(notification.account.safeDisplayName)
|
||||
.font(.subheadline)
|
||||
.fontWeight(.semibold) +
|
||||
Text(" ") +
|
||||
Text(type.label())
|
||||
.font(.subheadline) +
|
||||
Text(" ⸱ ")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray) +
|
||||
Text(notification.createdAt.formatted)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
EmojiText(notification.account.safeDisplayName, emojis: notification.account.emojis)
|
||||
.append {
|
||||
Text(" ") +
|
||||
Text(type.label())
|
||||
.font(.subheadline) +
|
||||
Text(" ⸱ ")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray) +
|
||||
Text(notification.createdAt.formatted)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +87,7 @@ struct NotificationRowView: View {
|
|||
.foregroundColor(.gray)
|
||||
|
||||
if type == .follow {
|
||||
Text(notification.account.note.asSafeAttributedString)
|
||||
EmojiText(notification.account.note, emojis: notification.account.emojis)
|
||||
.lineLimit(3)
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
|
|
|
@ -62,7 +62,7 @@ class StatusDetailViewModel: ObservableObject {
|
|||
let status: Status = try await client.get(endpoint: Statuses.status(id: statusId))
|
||||
let context: StatusContext = try await client.get(endpoint: Statuses.context(id: statusId))
|
||||
state = .display(status: status, context: context)
|
||||
title = "Post from \(status.account.displayName)"
|
||||
title = "Post from \(status.account.displayNameWithoutEmojis)"
|
||||
} catch {
|
||||
state = .error(error: error)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Foundation
|
||||
import SwiftUI
|
||||
import DesignSystem
|
||||
import EmojiText
|
||||
|
||||
struct StatusEditorAutoCompleteView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
@ -31,7 +32,7 @@ struct StatusEditorAutoCompleteView: View {
|
|||
HStack {
|
||||
AvatarView(url: account.avatar, size: .badge)
|
||||
VStack(alignment: .leading) {
|
||||
Text(account.displayName)
|
||||
EmojiText(account.safeDisplayName, emojis: account.emojis)
|
||||
.font(.footnote)
|
||||
.foregroundColor(theme.labelColor)
|
||||
Text("@\(account.acct)")
|
||||
|
|
|
@ -7,6 +7,7 @@ import Models
|
|||
import Network
|
||||
import PhotosUI
|
||||
import NukeUI
|
||||
import EmojiText
|
||||
|
||||
public struct StatusEditorView: View {
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
|
|
|
@ -33,9 +33,9 @@ extension StatusEditorViewModel {
|
|||
case .edit:
|
||||
return "Editing your post"
|
||||
case let .replyTo(status):
|
||||
return "Replying to \(status.reblog?.account.displayName ?? status.account.displayName)"
|
||||
return "Replying to \(status.reblog?.account.displayNameWithoutEmojis ?? status.account.displayNameWithoutEmojis)"
|
||||
case let .quote(status):
|
||||
return "Quote of \(status.reblog?.account.displayName ?? status.account.displayName)"
|
||||
return "Quote of \(status.reblog?.account.displayNameWithoutEmojis ?? status.account.displayNameWithoutEmojis)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import DesignSystem
|
||||
import EmojiText
|
||||
|
||||
@MainActor
|
||||
public struct StatusEmbededView: View {
|
||||
|
@ -34,7 +35,7 @@ public struct StatusEmbededView: View {
|
|||
HStack(alignment: .center) {
|
||||
AvatarView(url: account.avatar, size: .embed)
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
status.account.displayNameWithEmojis
|
||||
EmojiText(status.account.safeDisplayName, emojis: account.emojis)
|
||||
.font(.footnote)
|
||||
.fontWeight(.semibold)
|
||||
Group {
|
||||
|
|
|
@ -4,6 +4,7 @@ import Env
|
|||
import DesignSystem
|
||||
import Network
|
||||
import Shimmer
|
||||
import EmojiText
|
||||
|
||||
public struct StatusRowView: View {
|
||||
@Environment(\.redactionReasons) private var reasons
|
||||
|
@ -90,7 +91,7 @@ public struct StatusRowView: View {
|
|||
HStack(spacing: 2) {
|
||||
Image(systemName:"arrow.left.arrow.right.circle.fill")
|
||||
AvatarView(url: viewModel.status.account.avatar, size: .boost)
|
||||
viewModel.status.account.displayNameWithEmojis
|
||||
EmojiText(viewModel.status.account.safeDisplayName, emojis: viewModel.status.account.emojis)
|
||||
Text("boosted")
|
||||
}
|
||||
.font(.footnote)
|
||||
|
@ -164,7 +165,7 @@ public struct StatusRowView: View {
|
|||
private func makeStatusContentView(status: AnyStatus) -> some View {
|
||||
Group {
|
||||
if !status.spoilerText.isEmpty {
|
||||
Text(status.spoilerText)
|
||||
EmojiText(status.spoilerText, emojis: status.emojis)
|
||||
.font(.body)
|
||||
Button {
|
||||
withAnimation {
|
||||
|
@ -177,7 +178,7 @@ public struct StatusRowView: View {
|
|||
}
|
||||
if !viewModel.displaySpoiler {
|
||||
HStack {
|
||||
Text(status.content.asSafeAttributedString)
|
||||
EmojiText(status.content, emojis: status.emojis)
|
||||
.font(.body)
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
routeurPath.handleStatus(status: status, url: url)
|
||||
|
@ -233,7 +234,7 @@ public struct StatusRowView: View {
|
|||
AvatarView(url: status.account.avatar, size: .status)
|
||||
}
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
status.account.displayNameWithEmojis
|
||||
EmojiText(status.account.safeDisplayName, emojis: status.account.emojis)
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
Group {
|
||||
|
|
Loading…
Reference in a new issue