Bottom Tab Bar Visibility Adjustment
Nucleic-Session: 747527BF-AEE9-4939-BE70-A14F1F3BBBA8 Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
@@ -144,6 +144,10 @@ struct NewChatAffordance: ViewModifier {
|
|||||||
var project: WireProject? = nil
|
var project: WireProject? = nil
|
||||||
/// Owned by the caller so it can also open the composer programmatically (iPad ⌘N on Home).
|
/// Owned by the caller so it can also open the composer programmatically (iPad ⌘N on Home).
|
||||||
@Binding var isPresented: Bool
|
@Binding var isPresented: Bool
|
||||||
|
/// Prompt text and its focus live here so the scrim tap and auto-focus can coordinate with the
|
||||||
|
/// composer (whether it's empty, whether the keyboard is up).
|
||||||
|
@State private var draft = ""
|
||||||
|
@FocusState private var draftFocused: Bool
|
||||||
|
|
||||||
/// The button only makes sense when this device can control a Mac and there's a project to
|
/// The button only makes sense when this device can control a Mac and there's a project to
|
||||||
/// start a chat in; otherwise the composer it opens would be inert.
|
/// start a chat in; otherwise the composer it opens would be inert.
|
||||||
@@ -151,6 +155,8 @@ struct NewChatAffordance: ViewModifier {
|
|||||||
store.canControl && (project != nil || !store.dashboard.projects.isEmpty)
|
store.canControl && (project != nil || !store.dashboard.projects.isEmpty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var isEmpty: Bool { draft.trimmingCharacters(in: .whitespaces).isEmpty }
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
content
|
content
|
||||||
// The "+" disc — hidden while the composer is up so it doesn't sit over the bar.
|
// The "+" disc — hidden while the composer is up so it doesn't sit over the bar.
|
||||||
@@ -175,7 +181,7 @@ struct NewChatAffordance: ViewModifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The composer itself, floating up from the bottom over a light scrim — no modal pane,
|
// The composer itself, floating up from the bottom over a light scrim — no modal pane,
|
||||||
// no nav chrome, just the entry box. Tapping the scrim dismisses it.
|
// no nav chrome, just the entry box.
|
||||||
.overlay {
|
.overlay {
|
||||||
if isPresented {
|
if isPresented {
|
||||||
ZStack(alignment: .bottom) {
|
ZStack(alignment: .bottom) {
|
||||||
@@ -183,8 +189,14 @@ struct NewChatAffordance: ViewModifier {
|
|||||||
.fill(.black.opacity(0.2))
|
.fill(.black.opacity(0.2))
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
.onTapGesture { isPresented = false }
|
// Tapping away drops the keyboard; with nothing typed there's nothing to
|
||||||
StartChatComposer(onStart: { isPresented = false }, lockedProject: project)
|
// keep the bar around for, so close it. A draft in progress stays open
|
||||||
|
// (keyboard down) so a stray tap doesn't discard it.
|
||||||
|
.onTapGesture {
|
||||||
|
if isEmpty { isPresented = false } else { draftFocused = false }
|
||||||
|
}
|
||||||
|
StartChatComposer(draft: $draft, focus: $draftFocused,
|
||||||
|
onStart: { isPresented = false }, lockedProject: project)
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
.padding(.bottom, 8)
|
.padding(.bottom, 8)
|
||||||
.transition(.move(edge: .bottom).combined(with: .opacity))
|
.transition(.move(edge: .bottom).combined(with: .opacity))
|
||||||
@@ -192,6 +204,20 @@ struct NewChatAffordance: ViewModifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.animation(.spring(response: 0.35, dampingFraction: 0.86), value: isPresented)
|
.animation(.spring(response: 0.35, dampingFraction: 0.86), value: isPresented)
|
||||||
|
.onChange(of: isPresented) { _, open in
|
||||||
|
if open {
|
||||||
|
draft = ""
|
||||||
|
// Focus once the bar has sprung into place, so the keyboard rises against a
|
||||||
|
// settled layout — otherwise its avoidance inset is computed mid-animation and
|
||||||
|
// the keyboard lands overlapping the bar until the next re-layout.
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
|
||||||
|
if isPresented { draftFocused = true }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
draftFocused = false
|
||||||
|
draft = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user