Files
andros 7e175465bd Resolve GitHub-style emoji shortcodes (💓 -> 💓)
Some Org Social clients (org-social.el and Mastodon-bridge clients)
write the :MOOD: value or a reaction emoji as a colon-wrapped
shortcode like 💓 instead of the Unicode glyph. Until now we
just printed the raw shortcode, which looked like a bug to users.

EmojiShortcode.resolve(_:) maps the most common reaction shortcodes to
Unicode and leaves anything unknown unchanged (so private/custom
shortcodes still display rather than vanishing). Applied to:
- the per-post reaction chip in PostRowView
- the "Reacted X" badge for pure-reaction posts
- the navigation title of ReactorsSheet

5 new tests pin known/unknown/inline/multiple/empty inputs.
2026-04-26 10:10:02 +02:00

57 lines
2.1 KiB
Swift

import SwiftUI
import OrgSocialKit
/// `.sheet(item:)` requires `Identifiable`; the lib's `OrgSocialMood` isn't
/// (it doesn't need to be, the App/UI layer supplies the identity instead).
struct IdentifiedMood: Identifiable, Equatable {
let mood: OrgSocialMood
var id: String { mood.emoji }
}
/// Lists the feed URLs that reacted with a given emoji. The reactor post URL
/// shape is `https://host/social.org#<timestamp>`, so we derive both the
/// host (shown as the primary label) and the timestamp (shown as a tappable
/// link to the reaction post itself) with no extra network round-trips.
struct ReactorsSheet: View {
let mood: OrgSocialMood
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
List {
Section {
ForEach(mood.posts, id: \.self) { reactorPostURL in
reactorRow(reactorPostURL)
}
} header: {
Text("\(mood.posts.count) \(mood.posts.count == 1 ? "reaction" : "reactions")")
}
}
.listStyle(.plain)
.navigationTitle(Text(EmojiShortcode.resolve(mood.emoji)))
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Done") { dismiss() }
}
}
}
.presentationDetents([.medium, .large])
}
@ViewBuilder
private func reactorRow(_ reactorPostURL: String) -> some View {
let parts = reactorPostURL.split(separator: "#", maxSplits: 1, omittingEmptySubsequences: false)
let feedURLString = parts.first.map(String.init) ?? reactorPostURL
let host = URL(string: feedURLString)?.host ?? feedURLString
HStack(spacing: 10) {
Image(systemName: "person.circle.fill")
.foregroundStyle(.secondary)
VStack(alignment: .leading, spacing: 2) {
Text(host).font(.subheadline.weight(.medium))
Text(feedURLString).font(.caption).foregroundStyle(.secondary).lineLimit(1)
}
}
}
}