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
7 changes: 5 additions & 2 deletions Flipcash/Core/Controllers/ConversationController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,9 @@ final class ConversationController {
}

private func deliver(clientMessageID: UUID, text: String, to conversationID: ConversationID) async -> Bool {
// Captured before the send so success and failure report the same
// conversation kind; an unresolved conversation reports as Unknown.
let chatType = conversation(withID: conversationID)?.type
do {
let message = try await messaging.sendMessage(owner: owner, conversationID: conversationID, text: text, clientMessageID: clientMessageID)
// Persist the row carrying its client id (the server echoes none) so the DB keeps the send's
Expand All @@ -892,7 +895,7 @@ final class ConversationController {
store.advanceLastActivity(to: message.date, in: conversationID)
refreshFeedPreview(for: conversationID)
persistConversation(conversationID)
Analytics.track(event: Analytics.ConversationEvent.sentMessage)
Analytics.sentMessage(chatType: chatType)
return true
} catch {
store.markPending(clientMessageID: clientMessageID, status: .failed, in: conversationID)
Expand All @@ -901,7 +904,7 @@ final class ConversationController {
"error": "\(error)",
])
ErrorReporting.captureError(error, reason: "Failed to send conversation message")
Analytics.track(event: Analytics.ConversationEvent.sentMessage, error: error)
Analytics.sentMessage(chatType: chatType, error: error)
return false
}
}
Expand Down
1 change: 1 addition & 0 deletions Flipcash/Core/Screens/Main/ScanViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class ScanViewModel {
case .cash(let payload):
didScanCash(payload)
case .tip(let payload):
Analytics.track(event: Analytics.TipCardEvent.scanned)
tipFlow.begin(userID: payload.userID)
}
}
Expand Down
3 changes: 3 additions & 0 deletions Flipcash/Core/Screens/Main/Tips/TipFlow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ final class TipFlow {
}

private func present(_ recipient: TipRecipient) {
// The card is resolved and about to show, whether reached from a scan or
// a deep link — the second step of the Scanned → Presented → Sent Tip funnel.
Analytics.track(event: Analytics.TipCardEvent.presented)
selection = nil
customAmount = nil
submission = SendAmountViewModel(
Expand Down
9 changes: 7 additions & 2 deletions Flipcash/Core/Screens/Send/SendAmountViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,17 +195,22 @@ final class SendAmountViewModel {
return .failed
}

// A payment into a tip DM reports as a tip; a contact DM is a plain
// cash send. This covers both the scanned-tipcard flow and the
// Send Cash action inside a tip thread, since both submit here.
let transferEvent: Analytics.TransferEvent = if case .tip = target { .sentTip } else { .sentCash }

do {
try await sender.send(
amount: amountToSend,
verifiedState: pinnedState,
to: recipient,
chat: chatPaymentMetadata()
)
Analytics.transfer(event: .sentCash, exchangedFiat: amountToSend, grabTime: nil, successful: true, error: nil)
Analytics.transfer(event: transferEvent, exchangedFiat: amountToSend, grabTime: nil, successful: true, error: nil)
return .success
} catch {
Analytics.transfer(event: .sentCash, exchangedFiat: amountToSend, grabTime: nil, successful: false, error: error)
Analytics.transfer(event: transferEvent, exchangedFiat: amountToSend, grabTime: nil, successful: false, error: error)
showSendError()
return .failed
}
Expand Down
35 changes: 35 additions & 0 deletions Flipcash/Utilities/Events.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ extension Analytics {
enum TransferEvent: String, AnalyticsEvent {
case withdrawal = "Withdrawal"
case sentCash = "Sent Cash"
case sentTip = "Sent Tip"
case sendCashLink = "Send Cash Link"
case receiveCashLink = "Receive Cash Link"
case grabBill = "Grab Bill"
Expand All @@ -61,6 +62,14 @@ extension Analytics {
case sentMessage = "Sent Message"
}

/// The scanned-tipcard funnel: a tipcard is scanned, resolves and is
/// presented, then a tip is sent (`TransferEvent.sentTip`). Names are
/// shared verbatim with Android.
enum TipCardEvent: String, AnalyticsEvent {
case scanned = "Tip Card Scanned"
case presented = "Tip Card Presented"
}

enum PhoneEvent: String, AnalyticsEvent {
case entered = "Entered Phone Number"
case verified = "Verified Phone Number"
Expand Down Expand Up @@ -212,6 +221,31 @@ extension Analytics {
}
}

// MARK: - Conversation -

extension Analytics {
/// A chat message send. `Chat Type` mirrors Android — Tip / Contact /
/// Unknown (a conversation not resolved locally yet).
static func sentMessage(chatType: ConversationType?, error: Error? = nil) {
track(
event: ConversationEvent.sentMessage,
properties: [.chatType: chatType.analyticsValue],
error: error
)
}
}

private extension Optional where Wrapped == ConversationType {
/// The `Chat Type` property value, shared verbatim with Android.
var analyticsValue: String {
switch self {
case .contactDm: "Contact"
case .tipDm: "Tip"
case .none: "Unknown"
}
}
}

// MARK: - Add Money -

extension Analytics {
Expand Down Expand Up @@ -377,6 +411,7 @@ extension Analytics {
case fx = "Exchange Rate"

case type = "Type"
case chatType = "Chat Type"
case error = "Error"
case url = "URL"

Expand Down