Initial commit — iOS share extension for filing Gitea issues
Two-target Xcode project (xcodegen spec). The GiteaIssue container app holds the base URL + personal access token in a shared keychain group; the GiteaIssueShare extension reads them, surfaces a repo picker (with recents) and a title/notes form, then creates the issue, uploads the screenshot as an asset, and patches the body to embed it inline. Min iOS 26.0, signing team V3PF3M6B6U. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
final class ShareViewController: UIViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .clear
|
||||
Task { await loadAttachment() }
|
||||
}
|
||||
|
||||
private func loadAttachment() async {
|
||||
guard let item = (extensionContext?.inputItems as? [NSExtensionItem])?.first,
|
||||
let provider = item.attachments?.first(where: { $0.hasItemConformingToTypeIdentifier(UTType.image.identifier) })
|
||||
else {
|
||||
present(host: ShareSheetView(image: nil, onClose: { [weak self] in self?.complete() }))
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let data = try await loadImageData(from: provider)
|
||||
await MainActor.run {
|
||||
let image = data.flatMap { UIImage(data: $0) }
|
||||
let host = ShareSheetView(
|
||||
image: image,
|
||||
imageData: data,
|
||||
onClose: { [weak self] in self?.complete() }
|
||||
)
|
||||
self.present(host: host)
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
present(host: ShareSheetView(image: nil, errorMessage: error.localizedDescription, onClose: { [weak self] in self?.complete() }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadImageData(from provider: NSItemProvider) async throws -> Data? {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
provider.loadItem(forTypeIdentifier: UTType.image.identifier, options: nil) { item, error in
|
||||
if let error { continuation.resume(throwing: error); return }
|
||||
if let data = item as? Data { continuation.resume(returning: data); return }
|
||||
if let url = item as? URL, let data = try? Data(contentsOf: url) { continuation.resume(returning: data); return }
|
||||
if let image = item as? UIImage, let data = image.pngData() { continuation.resume(returning: data); return }
|
||||
continuation.resume(returning: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func present(host: ShareSheetView) {
|
||||
let controller = UIHostingController(rootView: host)
|
||||
controller.modalPresentationStyle = .formSheet
|
||||
addChild(controller)
|
||||
controller.view.frame = view.bounds
|
||||
controller.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
view.addSubview(controller.view)
|
||||
controller.didMove(toParent: self)
|
||||
}
|
||||
|
||||
fileprivate func complete() {
|
||||
extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
|
||||
}
|
||||
|
||||
fileprivate func cancel() {
|
||||
extensionContext?.cancelRequest(withError: NSError(domain: "GiteaIssueShare", code: 0))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user