v0.8.0 — Phase β-7 App-Store-Vorbereitung

Feature-komplett für TestFlight. App-Icon-Platzhalter, Siri-Shortcut,
Share-Extension, Release-Checklist mit allen externen Apple-Schritten.

- scripts/make-appicon.swift: CoreGraphics-basierter Generator für
  1024×1024 forest-green PNG mit "C"-Letter und Karten-Stack-Schatten
- Asset-Catalog auf Single-Size-AppIcon-Pattern umgestellt
- StudyCardsIntent + CardsAppShortcuts (App Intents): Siri-
  Shortcut "Karten lernen mit Cards" / "Mit Cards lernen"
- CardsShareExtension Target: ShareViewController (UIKit-Bootstrap +
  SwiftUI-Hosting), ShareEditorView mit Text-Edit
- PendingShare + PendingShareStore shared in App-Group
  group.ev.mana.cards
- DeckListView zeigt PendingShare-Banner; Tap navigiert zu
  PendingShareConsumeView mit Deck-Picker + Front/Back-Felder, Submit
  → POST /cards, danach store.remove
- Info.plist: NSPhotoLibraryUsageDescription für Image-Occlusion-
  Picker, NSUserActivityTypes für Universal-Links
- docs/RELEASE_CHECKLIST.md mit externen Schritten: Apple-Developer-
  Portal, App-IDs, App-Group, AASA, Xcode-Archive, TestFlight-Plan,
  App-Store-Connect-Felder, Compliance-Verifikation
- UI-Test robuster (akzeptiert Login oder Decks/Entdecken als
  Launch-Erfolg, unabhängig vom Simulator-Keychain-State)
- 35 Tests + 1 UI-Test grün, alle drei Targets bauen

App-Store-Submission selbst ist externe Aktion und passiert nicht
durch dieses Repo — Schritte in docs/RELEASE_CHECKLIST.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-05-13 01:13:27 +02:00
parent 55359c5333
commit 0b2ae167b7
16 changed files with 783 additions and 59 deletions

View file

@ -0,0 +1,60 @@
import SwiftUI
/// Mini-Editor in der Share-Extension. User kann den Text noch
/// anpassen, dann "Speichern" PendingShare landet in der Haupt-App.
struct ShareEditorView: View {
let initialText: String
let sourceURL: String?
let onSave: (String) -> Void
let onCancel: () -> Void
@State private var text: String
init(
text: String,
sourceURL: String?,
onSave: @escaping (String) -> Void,
onCancel: @escaping () -> Void
) {
initialText = text
self.sourceURL = sourceURL
self.onSave = onSave
self.onCancel = onCancel
_text = State(initialValue: text)
}
var body: some View {
NavigationStack {
Form {
Section("Vorderseite") {
TextField("Text", text: $text, axis: .vertical)
.lineLimit(4 ... 10)
}
if let sourceURL {
Section("Quelle") {
Text(sourceURL)
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(1)
}
}
Section {
Text("Wähle das Ziel-Deck in der Cards-App.")
.font(.caption)
.foregroundStyle(.secondary)
}
}
.navigationTitle("Als Karte speichern")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Abbrechen", action: onCancel)
}
ToolbarItem(placement: .confirmationAction) {
Button("Speichern") { onSave(text) }
.disabled(text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
}
}
}
}
}