diff --git a/Flipcash/Core/Screens/Conversation/ChatScreenRepresentable.swift b/Flipcash/Core/Screens/Conversation/ChatScreenRepresentable.swift index 7b016222..be74d43e 100644 --- a/Flipcash/Core/Screens/Conversation/ChatScreenRepresentable.swift +++ b/Flipcash/Core/Screens/Conversation/ChatScreenRepresentable.swift @@ -39,6 +39,8 @@ struct ChatScreenRepresentable: UIViewControllerRepresentable { let onSendCash: () -> Void let conversationController: ConversationController let barModel: ConversationBarModel + /// Whether this is a tip DM — the send button then stays minimized. + let isTipDm: Bool func makeUIViewController(context: Context) -> ChatScreenViewController { let barHost = UIHostingController(rootView: bar(coordinator: context.coordinator)) @@ -96,7 +98,8 @@ struct ChatScreenRepresentable: UIViewControllerRepresentable { conversationID: conversationID, symbol: symbol, onSendCash: onSendCash, - model: barModel + model: barModel, + isTipDm: isTipDm ) .environment(conversationController) .modifier(MeasuredBarHeight { coordinator.screen?.setBarHeight($0) }) diff --git a/Flipcash/Core/Screens/Conversation/ConversationBottomBar.swift b/Flipcash/Core/Screens/Conversation/ConversationBottomBar.swift index 7d5702aa..ae546b3d 100644 --- a/Flipcash/Core/Screens/Conversation/ConversationBottomBar.swift +++ b/Flipcash/Core/Screens/Conversation/ConversationBottomBar.swift @@ -46,6 +46,9 @@ struct ConversationBottomBar: View { let symbol: String let onSendCash: () -> Void let model: ConversationBarModel + /// Tip chats always show the compact symbol-only send button; ordinary + /// chats expand to "Send €" at rest and collapse only while composing. + var isTipDm: Bool = false var body: some View { let content = HStack(alignment: .bottom, spacing: 10) { @@ -54,6 +57,7 @@ struct ConversationBottomBar: View { symbol: symbol, composing: model.isComposing, standalone: !chatExists, + alwaysMinimized: isTipDm, action: onSendCash ) } @@ -170,8 +174,9 @@ private struct BarGradientBackground: ViewModifier { } /// The Send Cash button, rendered as a white "Send €" pill at rest and a -/// compact glass "€" square while composing. Alone in the bar it takes the -/// standard filled-button size; beside the composer it's field-sized. +/// compact glass "€" square while composing (or always, in a tip chat). Alone +/// in the bar it takes the standard filled-button size; beside the composer +/// it's field-sized. // One persistent view: the morph animates its properties (prefix text, fill, // width, color) in lockstep — splitting the two states into separate views // would crossfade instead of morphing. @@ -182,8 +187,15 @@ struct SendCashMorphButton: View { /// Whether the button is the bar's only control (no chat yet): it spans /// the bar at the standard filled-button size instead of field-sized. let standalone: Bool + /// Forces the compact symbol-only presentation regardless of composing. + /// Tip chats always show it minimized; ordinary chats expand at rest. + var alwaysMinimized: Bool = false let action: () -> Void + /// The compact glass "€" presentation: while composing, or always in a tip + /// chat. The whole morph (label, fill, width, color) keys off this. + private var minimized: Bool { composing || alwaysMinimized } + private var height: CGFloat { standalone ? Metrics.buttonHeight : BarMetrics.contentHeight } @@ -195,7 +207,7 @@ struct SendCashMorphButton: View { var body: some View { Button(action: action) { HStack(spacing: 4) { - if !composing { + if !minimized { Text("Send") .font(.appTextMedium) .transition(.opacity) @@ -203,16 +215,16 @@ struct SendCashMorphButton: View { Text(symbol) // Same persistent Text — .interpolate animates the glyph // between sizes; swapping views would crossfade. - .font(composing ? .appTextXL : .appTextMedium) + .font(minimized ? .appTextXL : .appTextMedium) .contentTransition(.interpolate) } - .foregroundStyle(composing ? Color.textMain : Color.textAction) + .foregroundStyle(minimized ? Color.textMain : Color.textAction) // The label must never reflow to "Se…" mid-morph; overflow is // clipped by the shape instead. .fixedSize() - .padding(.horizontal, composing ? 0 : 20) + .padding(.horizontal, minimized ? 0 : 20) .frame(minWidth: BarMetrics.contentHeight) - .frame(maxWidth: standalone && !composing ? .infinity : nil) + .frame(maxWidth: standalone && !minimized ? .infinity : nil) .frame(height: height) } .buttonStyle(.plain) @@ -221,7 +233,7 @@ struct SendCashMorphButton: View { .background { RoundedRectangle(cornerRadius: cornerRadius) .fill(Color.action) - .opacity(composing ? 0 : 1) + .opacity(minimized ? 0 : 1) } .glassBackground(cornerRadius: cornerRadius) .clipShape(RoundedRectangle(cornerRadius: cornerRadius)) diff --git a/Flipcash/Core/Screens/Conversation/ConversationScreen.swift b/Flipcash/Core/Screens/Conversation/ConversationScreen.swift index ce38f0de..71486a19 100644 --- a/Flipcash/Core/Screens/Conversation/ConversationScreen.swift +++ b/Flipcash/Core/Screens/Conversation/ConversationScreen.swift @@ -167,7 +167,8 @@ struct ConversationScreen: View { symbol: ratesController.balanceCurrency.compactSymbol, onSendCash: sendCash, conversationController: conversationController, - barModel: barModel + barModel: barModel, + isTipDm: tipCounterpart != nil ) .ignoresSafeArea(.keyboard) // Extend the transcript under the navigation bar so content scrolls beneath it — that's