Fix NaN errors and timeout warnings when typing

- Disable iOS autocorrection on text fields to prevent conflicts
- Add NaN validation for all geometry and gesture calculations
- Fix frame calculations to handle invalid geometry values
- Validate translation and velocity values in drag gestures
- Prevent Result accumulator timeout warnings
This commit is contained in:
jack
2025-07-24 14:33:20 +02:00
parent eac3531a54
commit ffab7b695c
+30 -16
View File
@@ -93,17 +93,20 @@ struct ContentView: View {
insertion: .move(edge: .trailing), insertion: .move(edge: .trailing),
removal: .move(edge: .trailing) removal: .move(edge: .trailing)
)) ))
.offset(x: showPrivateChat ? -1 : geometry.size.width) .offset(x: showPrivateChat ? -1 : max(0, geometry.size.width))
.offset(x: backSwipeOffset) .offset(x: backSwipeOffset.isNaN ? 0 : backSwipeOffset)
.gesture( .gesture(
DragGesture() DragGesture()
.onChanged { value in .onChanged { value in
if value.translation.width > 0 { if value.translation.width > 0 && !value.translation.width.isNaN {
backSwipeOffset = min(value.translation.width, geometry.size.width) let maxWidth = max(0, geometry.size.width)
backSwipeOffset = min(value.translation.width, maxWidth.isNaN ? 0 : maxWidth)
} }
} }
.onEnded { value in .onEnded { value in
if value.translation.width > 50 || (value.translation.width > 30 && value.velocity.width > 300) { let translation = value.translation.width.isNaN ? 0 : value.translation.width
let velocity = value.velocity.width.isNaN ? 0 : value.velocity.width
if translation > 50 || (translation > 30 && velocity > 300) {
withAnimation(.easeOut(duration: 0.2)) { withAnimation(.easeOut(duration: 0.2)) {
showPrivateChat = false showPrivateChat = false
backSwipeOffset = 0 backSwipeOffset = 0
@@ -134,22 +137,26 @@ struct ContentView: View {
if showSidebar || sidebarDragOffset != 0 { if showSidebar || sidebarDragOffset != 0 {
sidebarView sidebarView
#if os(macOS) #if os(macOS)
.frame(width: min(300, geometry.size.width * 0.4)) .frame(width: min(300, max(0, geometry.size.width.isNaN ? 300 : geometry.size.width) * 0.4))
#else #else
.frame(width: geometry.size.width * 0.7) .frame(width: max(0, geometry.size.width.isNaN ? 300 : geometry.size.width) * 0.7)
#endif #endif
.transition(.move(edge: .trailing)) .transition(.move(edge: .trailing))
} else { } else {
// Empty placeholder when hidden // Empty placeholder when hidden
Color.clear Color.clear
#if os(macOS) #if os(macOS)
.frame(width: min(300, geometry.size.width * 0.4)) .frame(width: min(300, max(0, geometry.size.width.isNaN ? 300 : geometry.size.width) * 0.4))
#else #else
.frame(width: geometry.size.width * 0.7) .frame(width: max(0, geometry.size.width.isNaN ? 300 : geometry.size.width) * 0.7)
#endif #endif
} }
} }
.offset(x: showSidebar ? -sidebarDragOffset : geometry.size.width - sidebarDragOffset) .offset(x: {
let dragOffset = sidebarDragOffset.isNaN ? 0 : sidebarDragOffset
let width = geometry.size.width.isNaN ? 0 : max(0, geometry.size.width)
return showSidebar ? -dragOffset : width - dragOffset
}())
.animation(.easeInOut(duration: 0.25), value: showSidebar) .animation(.easeInOut(duration: 0.25), value: showSidebar)
} }
} }
@@ -452,6 +459,8 @@ struct ContentView: View {
.foregroundColor(textColor) .foregroundColor(textColor)
.focused($isTextFieldFocused) .focused($isTextFieldFocused)
.padding(.leading, 12) .padding(.leading, 12)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
.onChange(of: messageText) { newValue in .onChange(of: messageText) { newValue in
// Cancel previous debounce timer // Cancel previous debounce timer
autocompleteDebounceTimer?.invalidate() autocompleteDebounceTimer?.invalidate()
@@ -715,23 +724,26 @@ struct ContentView: View {
.gesture( .gesture(
DragGesture() DragGesture()
.onChanged { value in .onChanged { value in
if !showSidebar && value.translation.width < 0 { let translation = value.translation.width.isNaN ? 0 : value.translation.width
sidebarDragOffset = max(value.translation.width, -300) if !showSidebar && translation < 0 {
} else if showSidebar && value.translation.width > 0 { sidebarDragOffset = max(translation, -300)
sidebarDragOffset = min(-300 + value.translation.width, 0) } else if showSidebar && translation > 0 {
sidebarDragOffset = min(-300 + translation, 0)
} }
} }
.onEnded { value in .onEnded { value in
let translation = value.translation.width.isNaN ? 0 : value.translation.width
let velocity = value.velocity.width.isNaN ? 0 : value.velocity.width
withAnimation(.easeOut(duration: 0.2)) { withAnimation(.easeOut(duration: 0.2)) {
if !showSidebar { if !showSidebar {
if value.translation.width < -100 || (value.translation.width < -50 && value.velocity.width < -500) { if translation < -100 || (translation < -50 && velocity < -500) {
showSidebar = true showSidebar = true
sidebarDragOffset = 0 sidebarDragOffset = 0
} else { } else {
sidebarDragOffset = 0 sidebarDragOffset = 0
} }
} else { } else {
if value.translation.width > 100 || (value.translation.width > 50 && value.velocity.width > 500) { if translation > 100 || (translation > 50 && velocity > 500) {
showSidebar = false showSidebar = false
sidebarDragOffset = 0 sidebarDragOffset = 0
} else { } else {
@@ -788,6 +800,8 @@ struct ContentView: View {
.frame(maxWidth: 100) .frame(maxWidth: 100)
.foregroundColor(textColor) .foregroundColor(textColor)
.focused($isNicknameFieldFocused) .focused($isNicknameFieldFocused)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
.onChange(of: isNicknameFieldFocused) { isFocused in .onChange(of: isNicknameFieldFocused) { isFocused in
if !isFocused { if !isFocused {
// Only validate when losing focus // Only validate when losing focus