feat(analytics): track received tips and messages, tip origin, display name, and token symbols - #629
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the receive-side analytics we were missing, plus a few adjacent gaps found along the way. Spec covers both platforms; this is the iOS half — the Android half is code-payments/code-android-app#1304.
What's new
Received tips and messages (people counters)
Three Mixpanel people properties, incremented as inbound messages land:
Tips Received— +1 per inbound tipped Cash messageTips Received Value— + the USD-normalised value of that tipMessages Received— +1 per inbound message of any type (a tip bumps both)People properties are cumulative and unreversible, so double-counting is permanent. The watermark is the conversation's newest stored message id, read before the write: only messages above it are credited, and it only moves forward. A cold catch-up into a conversation the client holds nothing for seeds the store and counts nothing, so this PR is prospective-only — existing history is not retro-counted here. Backfilling it lands in a follow-up PR, on top of these counters.
Both delivery paths are hooked. Live deliveries count in
persist(event:)(.newMessagesand.chatEvents); gap-filled and reconnect deliveries count insidecatchUp's delta-batch closure, which writes through its own path and never routes throughpersist(event:)— hooking only one would miss every reconnect.catchUpcaptures a single run-start baseline so batch 2 of a backfill can't count what batch 1 just seeded.resyncAfterReset,loadMessages, and older-paging are deliberately not hooked: they're history loads, and retro-counting them piecemeal is the follow-up PR's job, not the ingestion path's.Chat cash arrives in the sender's native currency. If no exchange rate is cached for that currency we still increment the count but skip the value — an understated total is recoverable later, a wrong one is not.
Received events
Tip Received(Chat Type,Fiat,Currency,Mint,Token Symbol,Quarks) andMessage Received(Chat Type), emitted one per inbound message as the self read pointer advances, over the half-open interval(previousPointer, newPointer]. The two are mutually exclusive — a tip emits onlyTip Received.OriginonSent TipExisting
Sent Tipnow carriesOrigin:TipcardorChat, so subsequent payments from the money button inside a tip chat are distinguishable from the first tip off the tip card.Display name events
Display Name SetandDisplay Name Updated, both carryingSource(Onboarding,My Account,Tip Card Setup). Set-vs-updated is decided by whether a prior name existed, not by which screen you came from — someone who skips onboarding and names themselves from the tip card still getsSet.Token Symbolalongside every mintEvery event that carries
Mintnow also carriesToken Symbol, andPayment MintgainsPayment Token Symbol. Resolution happens centrally inAnalytics.trackvia a resolver installed at login and backed by the session's stored mint metadata; when a mint isn't cached the property is omitted entirely rather than sent empty.Hex identity
No change here — iOS already identifies to Mixpanel with lowercase hex. Android moved to match in its half.
Notes for review
Three deliberate deviations from the spec's iOS notes:
ConversationController.markReadrather thanConversationStore.advanceSelfReadPointer— the store lives inFlipcashCore, which can't reference the app target'sAnalytics.markReadcaptures the read pointer before the RPC and reports the crossed window only after a successful advance, so a failedmarkReadreports nothing and the next attempt retries the same window.Database.newestMessageID(conversationID:), not a new column. iOS has no migration framework — bumpingSQLiteVersionwipes the local store — and the newest-id read gives the same monotonic-max semantics with zero schema change..catchUpdelivery into an empty conversation seeds only and counts nothing; a.livedelivery with no watermark still counts. Existing history stays uncounted until the backfill PR.The receive-side concern lives in its own
ConversationReceiptReporterwith every dependency injected as a closure, so the counters and events are testable without touching Mixpanel.