Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions Flipcash/Core/Navigation/AppRouter+Destination.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,14 @@ extension AppRouter {
/// The signed-in user's own tipcard, pushed from the Tips list.
case tipcard
/// A tip DM conversation, pushed onto the `.tips` stack — from the
/// Tips list, a completed tip, or a tip-DM push notification.
/// Tips list or a tip-DM push notification.
case tipConversation(ConversationID)
/// Same screen as `tipConversation` but opens with the message field
/// focused and the keyboard up. Modelled as a sibling case rather than
/// an associated-value flag (matching `currencyInfoForDeposit`) so the
/// trace shows "post-tip, keyboard up" distinctly, and so the ordinary
/// tip-list / push-notification opens stay keyboard-closed untouched.
case tipConversationWithKeyboard(ConversationID)

// Conversation flow
/// A DM conversation, pushed onto the `.send` stack — from the Chats
Expand All @@ -78,7 +84,8 @@ extension AppRouter {
.settingsAppSettings, .settingsBetaFlags, .settingsAccountSelection,
.settingsApplicationLogs, .accessKey, .withdraw:
return .settings
case .profileName, .profilePhoto, .tipcard, .tipConversation:
case .profileName, .profilePhoto, .tipcard,
.tipConversation, .tipConversationWithKeyboard:
return .tips
case .dmConversation:
return .send
Expand Down Expand Up @@ -115,6 +122,7 @@ extension AppRouter {
case .profilePhoto: "profilePhoto"
case .tipcard: "tipcard"
case .tipConversation: "tipConversation"
case .tipConversationWithKeyboard: "tipConversationWithKeyboard"
case .dmConversation: "dmConversation"
}
}
Expand All @@ -134,7 +142,8 @@ extension AppRouter {
return conversationID.description
case .dmConversation(.contact(let contact)):
return contact.contactId
case .tipConversation(let conversationID):
case .tipConversation(let conversationID),
.tipConversationWithKeyboard(let conversationID):
return conversationID.description
case .discoverCurrencies, .currencyCreationSummary, .currencyCreationWizard,
.usdcDepositEducation, .usdcDepositAddress,
Expand Down
6 changes: 6 additions & 0 deletions Flipcash/Core/Navigation/AppRouter+DestinationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ struct DestinationView: View {
ConversationScreen(context: .existing(conversationID))
.id(conversationID)

case .tipConversationWithKeyboard(let conversationID):
// Post-tip open: same screen, but focus the field so the keyboard
// comes up. `.id` forces fresh view identity per conversation.
ConversationScreen(context: .existing(conversationID), openKeyboard: true)
.id(conversationID)

// MARK: - Conversation flow

case .dmConversation(let context):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ struct ChatScreenRepresentable: UIViewControllerRepresentable {
let onSendCash: () -> Void
let conversationController: ConversationController
let barModel: ConversationBarModel
/// Focus the composer when the bar first appears (post-tip open). One-shot:
/// the composer requests focus in its `.task`, which runs once on appear.
let focusOnAppear: Bool
/// Whether this is a tip DM — the send button then stays minimized.
let isTipDm: Bool

Expand Down Expand Up @@ -99,6 +102,7 @@ struct ChatScreenRepresentable: UIViewControllerRepresentable {
symbol: symbol,
onSendCash: onSendCash,
model: barModel,
focusOnAppear: focusOnAppear
isTipDm: isTipDm
)
.environment(conversationController)
Expand Down
21 changes: 19 additions & 2 deletions Flipcash/Core/Screens/Conversation/ConversationBottomBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ struct ConversationBottomBar: View {
let symbol: String
let onSendCash: () -> Void
let model: ConversationBarModel
/// Focus the composer on first appear (post-tip open); false everywhere else.
var focusOnAppear: Bool = false
/// 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
Expand All @@ -62,8 +64,12 @@ struct ConversationBottomBar: View {
)
}
if chatExists {
ConversationComposer(conversationID: conversationID, model: model)
.transition(.opacity)
ConversationComposer(
conversationID: conversationID,
model: model,
focusOnAppear: focusOnAppear
)
.transition(.opacity)
}
}
.padding(.horizontal, 12)
Expand All @@ -84,6 +90,8 @@ struct ConversationComposer: View {

let conversationID: ConversationID?
@Bindable var model: ConversationBarModel
/// Raise the keyboard on open (post-tip chats only). One-shot via `.task`.
var focusOnAppear: Bool = false

@Environment(ConversationController.self) private var conversationController
@FocusState private var isFocused: Bool
Expand Down Expand Up @@ -125,6 +133,15 @@ struct ConversationComposer: View {

return field
.glassBackground(cornerRadius: BarMetrics.cornerRadius)
// Post-tip open: raise the keyboard once the composer appears. Requested
// after a short delay so the push transition has settled — focus asked
// mid-transition is dropped and the keyboard never rises. `.task` runs
// once on appear and is cancelled on disappear.
.task {
guard focusOnAppear else { return }
try? await Task.delay(milliseconds: 350)
isFocused = true
}
// Focus is the single source of `isComposing` — the button morph and the
// screen's interactive-dismiss gate both key off it. Losing focus
// (keyboard swiped down) ends composing.
Expand Down
5 changes: 5 additions & 0 deletions Flipcash/Core/Screens/Conversation/ConversationScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ struct ConversationScreen: View {

let context: ConversationContext

/// Focus the message field on open so the keyboard comes up. Set only by the
/// post-tip navigation; every other entry point opens keyboard-closed.
var openKeyboard: Bool = false

@Environment(ConversationController.self) private var conversationController
@Environment(ContactSyncController.self) private var contactSyncController
@Environment(AppRouter.self) private var router
Expand Down Expand Up @@ -168,6 +172,7 @@ struct ConversationScreen: View {
onSendCash: sendCash,
conversationController: conversationController,
barModel: barModel,
focusOnAppear: openKeyboard
isTipDm: tipCounterpart != nil
)
.ignoresSafeArea(.keyboard)
Expand Down
4 changes: 3 additions & 1 deletion Flipcash/Core/Screens/Main/Tips/TipFlow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,9 @@ final class TipFlow {
let chatID = ConversationID.tipDm(between: session.userID, and: recipient.userID)
Task { [router] in
try? await Task.delay(milliseconds: 600)
router.navigate(to: .tipConversation(chatID))
// Open the post-tip chat with the keyboard up — the tip-specific
// variant focuses the composer; ordinary opens stay closed.
router.navigate(to: .tipConversationWithKeyboard(chatID))
}
}
}
Expand Down