From c249cb75efa89efbf86ec012b7cfa50c695b3e31 Mon Sep 17 00:00:00 2001 From: Taksh Date: Fri, 31 Jul 2026 11:44:54 +0300 Subject: [PATCH] feat: extract MessageClipboard for plaintext and quote formatting Centralize pasteboard writes and composer quote blocks so message actions stay testable without SwiftUI. --- bitchat/Services/MessageClipboard.swift | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 bitchat/Services/MessageClipboard.swift diff --git a/bitchat/Services/MessageClipboard.swift b/bitchat/Services/MessageClipboard.swift new file mode 100644 index 00000000..6582e32c --- /dev/null +++ b/bitchat/Services/MessageClipboard.swift @@ -0,0 +1,57 @@ +// +// MessageClipboard.swift +// bitchat +// +// This is free and unencumbered software released into the public domain. +// For more information, see +// + +import Foundation +#if os(iOS) +import UIKit +#elseif os(macOS) +import AppKit +#endif + +/// Clipboard helpers for message actions. Kept out of the view so unit tests +/// can assert quote formatting without standing up SwiftUI. +enum MessageClipboard { + /// Plain message body for the pasteboard. + static func copyPlaintext(_ content: String) { + #if os(iOS) + UIPasteboard.general.string = content + #elseif os(macOS) + let pasteboard = NSPasteboard.general + pasteboard.clearContents() + pasteboard.setString(content, forType: .string) + #endif + } + + /// Builds a composer-ready quote block (`> line` per line, trailing blank). + static func quoteForComposer(_ content: String, sender: String?) -> String { + let body = content.trimmingCharacters(in: .whitespacesAndNewlines) + guard !body.isEmpty else { return "" } + let header: String + if let sender, !sender.isEmpty, sender != "system" { + header = "> @\(sender):\n" + } else { + header = "" + } + let quoted = body + .split(separator: "\n", omittingEmptySubsequences: false) + .map { "> \($0)" } + .joined(separator: "\n") + return header + quoted + "\n\n" + } + + /// Appends a quote to an existing composer draft without wiping it. + static func appendQuote(to draft: String, content: String, sender: String?) -> String { + let quote = quoteForComposer(content, sender: sender) + guard !quote.isEmpty else { return draft } + if draft.isEmpty { return quote } + if draft.hasSuffix("\n") || draft.hasSuffix(" ") { + return draft + quote + } + return draft + "\n" + quote + } +}