crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

commit 767d29a66d4fd810346eadc87cf5b3f9d1cb1e49
parent 33a3e34f8cfee4d2ddc553a855b0d3bfaa8e0cb0
Author: Michael Camilleri <[email protected]>
Date:   Thu, 11 Jun 2026 12:41:00 +0900

Add profile name to Settings view

Diffstat:
MCrossmate/Views/SettingsView.swift | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+), 0 deletions(-)

diff --git a/Crossmate/Views/SettingsView.swift b/Crossmate/Views/SettingsView.swift @@ -16,6 +16,22 @@ struct SettingsView: View { @Bindable var preferences = preferences NavigationStack { Form { + Section { + NavigationLink { + ProfileNameEditView() + } label: { + HStack { + Text("Name") + Spacer() + Text(preferences.name) + .foregroundStyle(.secondary) + .lineLimit(1) + } + } + } header: { + Text("Profile") + } + Section("External Source") { Picker("Publisher", selection: $externalSource) { Text("None").tag(nil as ExternalSource?) @@ -162,3 +178,63 @@ struct SettingsView: View { } } +private struct ProfileNameEditView: View { + @Environment(PlayerPreferences.self) private var preferences + @State private var nameDraft = "" + @FocusState private var isNameFocused: Bool + + var body: some View { + Form { + Section { + HStack { + TextField("Name", text: $nameDraft) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + .focused($isNameFocused) + .onSubmit(commitName) + + if !nameDraft.isEmpty { + Button { + nameDraft = "" + isNameFocused = true + } label: { + Image(systemName: "xmark.circle.fill") + .foregroundStyle(.secondary) + } + .buttonStyle(.plain) + .accessibilityLabel("Clear Name") + } + } + } footer: { + Text("This is the name other players will see.") + } + } + .navigationTitle("Name") + .navigationBarTitleDisplayMode(.inline) + .onAppear { + nameDraft = preferences.name + } + .onChange(of: isNameFocused) { _, isFocused in + if !isFocused { + commitName() + } + } + .onDisappear { + commitName() + } + } + + private var trimmedName: String { + nameDraft.trimmingCharacters(in: .whitespacesAndNewlines) + } + + private var canCommitName: Bool { + !trimmedName.isEmpty && trimmedName != preferences.name + } + + private func commitName() { + guard canCommitName else { return } + preferences.name = trimmedName + nameDraft = trimmedName + } +}