Halo heisst ab hier Flaro — die Marke stand im Weg
Der Upload zu App Store Connect scheiterte am Namen. „Halo" ist eine eingetragene Marke von Microsoft; im App Store heisst zwar keine App exakt so, aber reservierte und markenrechtlich belegte Namen tauchen in keiner Suche auf. Der einzige verlässliche Test war die Eingabe dort, und die ging daneben. **Der Name wurde diesmal vorher geprüft.** Zwanzig Kandidaten gegen den deutschen und amerikanischen App Store, auf exakte Treffer und auf ähnliche Namen. „Kavo" wäre dieselbe Wand gewesen — exakt belegt in beiden Stores, in Deutschland von KaVo Dental GmbH. „Flare" ist mit 42 ähnlichen Apps je Store der am dichtesten besetzte Name der ganzen Runde. **Flaro** ist in beiden frei und liest sich als eigenes Wort, nicht als Flare mit Tippfehler. Das bleibt eine Store-Suche und keine Markenrecherche — reservierte Namen ohne veröffentlichte App sieht sie nicht. Aber es ist mehr Prüfung, als beim letzten Mal stattfand. --- 2160 Textvorkommen in 273 Dateien, 49 Pfade. Betroffen ist diesmal auch der Server: `Flaro.Api`, `Flaro.Core`, `Flaro.Data`, `Flaro.Media`, `Flaro.Metadata`, `FlaroContext`, `FlaroPaths`, `flaro.db`, die Variablen `FLARO_DATA`, `FLARO_CACHE`, `FLARO_TRANSCODE`, `FLARO_WRITE_SIDECARS`, und das Image. **Vor dem Ersetzen geprüft**, ob „halo" irgendwo als Teil eines anderen Wortes steckt — und ob es im TMDB-Token vorkommt. Tat es nicht; ein blindes Ersetzen hätte den sonst zerlegt. `docs/superpowers/specs/` bleibt wie beim letzten Mal unangetastet: datierte Entwurfsdokumente sind ein Protokoll getroffener Entscheidungen. Das Server-Dokument hat stattdessen einen Nachtrag bekommen, damit es niemanden in die Irre führt. **Die Bundle-ID wandert mit**, zum zweiten Mal an einem Tag: `com.scarriffleservices.flaro`. Wer die vorige Fassung installiert hat, hat jetzt zwei Karteileichen auf dem Gerät — beide löschen, dann ist Ruhe. 308 Server-Tests grün, 368 im Swift-Paket, null Warnungen. iOS und tvOS bauen, das Image baut, und ein frischer Container läuft durch Einrichtung und Anmeldung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f98b995050
commit
a7cf6c5def
@@ -0,0 +1,138 @@
|
||||
import SwiftUI
|
||||
import FlaroDesign
|
||||
|
||||
/// Wer angemeldet ist — unten in der Seitenleiste.
|
||||
///
|
||||
/// Der Platz ist nicht zufällig: dort sucht man sein Konto, weil es in jeder
|
||||
/// App der letzte Eintrag ist. Und von dort führt der Weg in die
|
||||
/// Einstellungen, statt dass sie als gleichrangiger Eintrag zwischen den
|
||||
/// Mediatheken stehen.
|
||||
///
|
||||
/// **Eine Karte, die über der Zeile aufgeht**, kein Systemmenü: mit denselben
|
||||
/// Abständen und derselben Schrift wie der Rest der App, statt der grauen
|
||||
/// Standardliste.
|
||||
///
|
||||
/// Sie hängt als Popover an der Zeile und wächst nicht in der Leiste selbst.
|
||||
/// Das war der erste Versuch, und er ging schief: die Leiste unten hat eine
|
||||
/// feste Höhe, die Karte wuchs nach unten aus dem Bild heraus, und die Zeile
|
||||
/// mit dem Namen verschwand dabei.
|
||||
///
|
||||
/// Auf dem iPhone gibt es diese Leiste nicht; dort haben die Einstellungen
|
||||
/// ihren eigenen Eintrag.
|
||||
struct AccountBar: View {
|
||||
@Binding var selection: SidebarDestination
|
||||
|
||||
@Environment(FlaroSession.self) private var session
|
||||
@State private var isExpanded = false
|
||||
@State private var showsSignOutConfirmation = false
|
||||
|
||||
private var title: String {
|
||||
session.accountName ?? session.serverName
|
||||
}
|
||||
|
||||
/// Der Server steht nur dann darunter, wenn oben schon der Kontoname
|
||||
/// steht — sonst stünde zweimal dasselbe.
|
||||
private var subtitle: String? {
|
||||
session.accountName == nil ? nil : session.serverName
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
accountRow
|
||||
.popover(isPresented: $isExpanded, arrowEdge: .bottom) {
|
||||
VStack(spacing: 0) {
|
||||
action("account.settings", symbol: "gearshape") {
|
||||
isExpanded = false
|
||||
selection = .settings
|
||||
}
|
||||
separator
|
||||
action(
|
||||
"action.signOut",
|
||||
symbol: "rectangle.portrait.and.arrow.right",
|
||||
isDestructive: true
|
||||
) {
|
||||
isExpanded = false
|
||||
showsSignOutConfirmation = true
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 240)
|
||||
.background(Color.flaroSurface)
|
||||
// Auf dem iPhone gäbe es diese Leiste gar nicht; die Angabe
|
||||
// steht trotzdem da, damit aus dem Popover dort kein
|
||||
// bildschirmfüllendes Sheet wird.
|
||||
.presentationCompactAdaptation(.popover)
|
||||
}
|
||||
.confirmationDialog(
|
||||
Text("settings.signOutConfirm"),
|
||||
isPresented: $showsSignOutConfirmation,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("action.signOut", role: .destructive) { session.signOut() }
|
||||
Button("action.cancel", role: .cancel) {}
|
||||
} message: {
|
||||
Text("settings.signOutExplanation")
|
||||
}
|
||||
}
|
||||
|
||||
private var accountRow: some View {
|
||||
Button {
|
||||
isExpanded.toggle()
|
||||
} label: {
|
||||
HStack(spacing: FlaroSpacing.snug) {
|
||||
AccountAvatar(name: title)
|
||||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(title)
|
||||
.font(.flaroCardTitle)
|
||||
.foregroundStyle(Color.flaroTextPrimary)
|
||||
.lineLimit(1)
|
||||
|
||||
if let subtitle {
|
||||
Text(subtitle)
|
||||
.font(.flaroCaption)
|
||||
.foregroundStyle(Color.flaroTextMuted)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
// Ohne Vorrang staucht SwiftUI den Text vor dem Zwischenraum:
|
||||
// in einer schmalen Leiste stand vom Namen nur noch „S…" da.
|
||||
.layoutPriority(1)
|
||||
|
||||
Spacer(minLength: 0)
|
||||
|
||||
// Zeigt nach oben: dorthin geht die Karte auf.
|
||||
Image(systemName: "chevron.up")
|
||||
.font(.caption2.weight(.semibold))
|
||||
.foregroundStyle(Color.flaroTextMuted)
|
||||
}
|
||||
.padding(.horizontal, FlaroSpacing.regular)
|
||||
.padding(.vertical, FlaroSpacing.snug)
|
||||
.contentShape(.rect)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel(Text(title))
|
||||
.accessibilityHint(Text("account.hint"))
|
||||
}
|
||||
|
||||
private func action(
|
||||
_ title: LocalizedStringKey,
|
||||
symbol: String,
|
||||
isDestructive: Bool = false,
|
||||
perform: @escaping () -> Void
|
||||
) -> some View {
|
||||
Button(action: perform) {
|
||||
Label(title, systemImage: symbol)
|
||||
.font(.flaroBody)
|
||||
.foregroundStyle(isDestructive ? Color.flaroDestructive : Color.flaroTextPrimary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, FlaroSpacing.regular)
|
||||
.padding(.vertical, FlaroSpacing.regular)
|
||||
.contentShape(.rect)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
private var separator: some View {
|
||||
Divider().overlay(Color.flaroSeparator)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user