wordeck-native/Sources/Features/Study/RatingBar.swift
Till JS 542082772a refactor(big-bang): cards-native → wordeck-native
Code + Identity-Rename zur Vorbereitung auf Apple-Dev-Portal-Aktion
(Bundle ev.mana.wordeck, App-Group group.ev.mana.wordeck, AASA
applinks:wordeck.com). Build bleibt funktional, aber gegen die
neue text-only-API können image-occlusion-Creates 422 zurückgeben —
das wird mit der Wordeck-Native v1.0-Welle (parallele Apple-Aktion)
sauber gemacht.

Umbenennung:
- 41 Files: cardecky/Cardecky → wordeck/Wordeck (Display, Strings,
  Kommentare)
- 57 Files: CardsNative → WordeckNative, CardsAPI → WordeckAPI,
  CardsTheme → WordeckTheme, CardsBrand → WordeckBrand, CardsWidget →
  WordeckWidget, CardsDueWidget → WordeckDueWidget
- Bundle-ID ev.mana.cardecky → ev.mana.wordeck (project.yml,
  Info.plist, entitlements, Keychain-Service, App-Group)
- AASA applinks:cardecky.mana.how → applinks:wordeck.com
- API-Base cardecky-api.mana.how → api.wordeck.com
- 10 Files renamed (App-Entry, API-Extensions, Theme, Widget,
  Entitlements, Tests)
- xcodeproj regenerated via xcodegen → WordeckNative.xcodeproj
- MaskRegionsTests.swift gelöscht (image-occlusion entfällt mit
  Wordeck text-only)

Forgejo-Repo git.mana.how/till/cards-native → wordeck-native umbenannt
(Auto-Redirect aktiv). Lokales Verzeichnis Code/cards-native/ bleibt
vorerst — wird beim nächsten Apple-Setup mit Bundle-Test umbenannt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 23:10:42 +02:00

93 lines
3.3 KiB
Swift

import SwiftUI
#if canImport(UIKit)
import UIKit
#endif
/// Vier Rating-Buttons mit emphasis auf "Good" (full-width primary).
/// Web-Vorbild: `cards/apps/web/src/routes/study/[deckId]/+page.svelte`
/// `.grade.again/.hard/.good/.easy`-Klassen.
struct RatingBar: View {
let onRate: (Rating) -> Void
var body: some View {
HStack(spacing: 8) {
ForEach(Rating.allCases, id: \.self) { rating in
Button {
triggerHaptic(for: rating)
onRate(rating)
} label: {
HStack(spacing: 6) {
Text(rating.label)
.font(.subheadline.weight(.semibold))
Text(rating.shortcut)
.font(.caption2.weight(.semibold))
.padding(.horizontal, 5)
.padding(.vertical, 1)
.background(kbdBackground(for: rating), in: RoundedRectangle(cornerRadius: 4))
.foregroundStyle(kbdForeground(for: rating))
}
.frame(maxWidth: .infinity)
.padding(.vertical, 14)
.background(background(for: rating), in: RoundedRectangle(cornerRadius: 10, style: .continuous))
.foregroundStyle(foreground(for: rating))
.overlay(
RoundedRectangle(cornerRadius: 10, style: .continuous)
.stroke(borderColor(for: rating), lineWidth: rating == .good ? 0 : 1)
)
}
.buttonStyle(.plain)
}
}
.padding(.horizontal, 16)
}
/// `good` ist die Hero-Action (primary full background) analog
/// zum Web-Default-Klick. Andere bekommen subtle tinted borders.
private func background(for rating: Rating) -> Color {
switch rating {
case .again: WordeckTheme.error.opacity(0.06)
case .hard: WordeckTheme.warning.opacity(0.06)
case .good: WordeckTheme.primary
case .easy: WordeckTheme.success.opacity(0.06)
}
}
private func foreground(for rating: Rating) -> Color {
switch rating {
case .again: WordeckTheme.error
case .hard: WordeckTheme.warning
case .good: WordeckTheme.primaryForeground
case .easy: WordeckTheme.success
}
}
private func borderColor(for rating: Rating) -> Color {
switch rating {
case .again: WordeckTheme.error.opacity(0.4)
case .hard: WordeckTheme.warning.opacity(0.4)
case .good: .clear
case .easy: WordeckTheme.success.opacity(0.4)
}
}
private func kbdBackground(for rating: Rating) -> Color {
rating == .good
? WordeckTheme.primaryForeground.opacity(0.18)
: WordeckTheme.muted
}
private func kbdForeground(for rating: Rating) -> Color {
rating == .good
? WordeckTheme.primaryForeground.opacity(0.85)
: WordeckTheme.mutedForeground
}
private func triggerHaptic(for rating: Rating) {
#if canImport(UIKit)
let style: UIImpactFeedbackGenerator.FeedbackStyle =
rating == .easy ? .heavy : .medium
UIImpactFeedbackGenerator(style: style).impactOccurred()
#endif
}
}