From 4bcab7e282b856e23400abaf73505ca16ba5b304 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Tue, 6 Oct 2026 18:45:06 -0400 Subject: [PATCH] feat(analytics): catalogue the profile refresh events Adds a Profile domain: Settings Viewed, Edit Profile Viewed, Field Saved, Bio Moderation Failed, Menu Action, Pinned Button Tapped and Card Shown, with ProfileField, ProfileMenuAction and ProfilePinnedAction lists. Adds PROFILE to AddMoneySource. No call sites are wired yet. --- .../SharedCoreKit/AnalyticsEvents.swift | 4 + libs/analytics-events/EVENTS.md | 85 +++++++++++++++++++ libs/analytics-events/events.toml | 73 ++++++++++++++++ .../flipcash/analytics/AddMoneyEventsTest.kt | 2 +- .../flipcash/analytics/ProfileEventsTest.kt | 52 ++++++++++++ 5 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 libs/analytics-events/src/commonTest/kotlin/com/flipcash/analytics/ProfileEventsTest.kt diff --git a/kmp/shared-core/spm/Sources/SharedCoreKit/AnalyticsEvents.swift b/kmp/shared-core/spm/Sources/SharedCoreKit/AnalyticsEvents.swift index b406d34184..cf3c54da3a 100644 --- a/kmp/shared-core/spm/Sources/SharedCoreKit/AnalyticsEvents.swift +++ b/kmp/shared-core/spm/Sources/SharedCoreKit/AnalyticsEvents.swift @@ -26,6 +26,9 @@ public typealias GroupInviteMethod = SharedCore.GroupInviteMethod public typealias GroupInviteSource = SharedCore.GroupInviteSource public typealias GroupAccess = SharedCore.GroupAccess public typealias GroupGateFunding = SharedCore.GroupGateFunding +public typealias ProfileField = SharedCore.ProfileField +public typealias ProfileMenuAction = SharedCore.ProfileMenuAction +public typealias ProfilePinnedAction = SharedCore.ProfilePinnedAction public typealias AnalyticsButton = SharedCore.Button public typealias AnalyticsAmount = SharedCore.Amount public typealias PeopleCounter = SharedCore.PeopleCounter @@ -45,6 +48,7 @@ public typealias ErrorModalEvents = SharedCore.ErrorModalEvents public typealias AccountEvents = SharedCore.AccountEvents public typealias ButtonEvents = SharedCore.ButtonEvents public typealias GroupEvents = SharedCore.GroupEvents +public typealias ProfileEvents = SharedCore.ProfileEvents /// A property value as the iOS sender needs it. Mixpanel is not a dependency of this /// package, so the app maps these to `MixpanelType` itself. diff --git a/libs/analytics-events/EVENTS.md b/libs/analytics-events/EVENTS.md index 3a31fc9d72..ef6246508b 100644 --- a/libs/analytics-events/EVENTS.md +++ b/libs/analytics-events/EVENTS.md @@ -506,6 +506,56 @@ No properties. No properties. +## Profile + +### Profile: Settings Viewed + +The Settings screen opened. + +No properties. + +### Profile: Edit Profile Viewed + +The Edit Profile screen opened. + +No properties. + +### Profile: Field Saved + +A profile field was saved. + +| Property | Type | Optional | Values | +|---|---|---|---| +| Field | list | no | [ProfileField](#profilefield) | + +### Profile: Bio Moderation Failed + +A bio was rejected by moderation. + +No properties. + +### Profile: Menu Action + +An action was picked from another user's profile menu. + +| Property | Type | Optional | Values | +|---|---|---|---| +| Action | list | no | [ProfileMenuAction](#profilemenuaction) | + +### Profile: Pinned Button Tapped + +The pinned button on another user's profile was tapped. + +| Property | Type | Optional | Values | +|---|---|---|---| +| Action | list | no | [ProfilePinnedAction](#profilepinnedaction) | + +### Profile: Card Shown + +A profile card was shown. + +No properties. + ## Button ### Button: {button} @@ -574,6 +624,7 @@ The `Source` property on Add Money: Opened: where the user opened Add Money from | Chat | `CHAT` | | | Scanner | `SCANNER` | | | Balance | `BALANCE` | | +| Profile | `PROFILE` | | ### AddMoneyMethod @@ -704,6 +755,40 @@ The `Source` property on the Display Name events: the screen the name was set on | My Account | `MY_ACCOUNT` | | | Tip Card Setup | `TIP_CARD_SETUP` | | +### ProfileField + +The `Field` property on Profile: Field Saved: the profile field that was saved. + +| Value sent | Name in code | Drift | +|---|---|---| +| Name | `NAME` | | +| Username | `USERNAME` | | +| Photo | `PHOTO` | | +| Cover | `COVER` | | +| Bio | `BIO` | | +| Minimum | `MINIMUM` | | + +### ProfileMenuAction + +The `Action` property on Profile: Menu Action: the action picked from another user's profile menu. + +| Value sent | Name in code | Drift | +|---|---|---| +| Mute | `MUTE` | | +| Report | `REPORT` | | +| Block | `BLOCK` | | +| Unblock | `UNBLOCK` | | + +### ProfilePinnedAction + +The `Action` property on Profile: Pinned Button Tapped: what the profile's pinned button does. + +| Value sent | Name in code | Drift | +|---|---|---| +| Start Chatting | `START_CHATTING` | | +| Open Chat | `OPEN_CHAT` | | +| Unblock | `UNBLOCK` | | + ### Button The button tapped, sent in the event name `Button: {button}`. diff --git a/libs/analytics-events/events.toml b/libs/analytics-events/events.toml index 7eeca8bc8d..ca66e47d48 100644 --- a/libs/analytics-events/events.toml +++ b/libs/analytics-events/events.toml @@ -90,6 +90,7 @@ USERNAME_SHORTFALL = "Username Shortfall" CHAT = "Chat" SCANNER = "Scanner" BALANCE = "Balance" +PROFILE = "Profile" [lists.AddMoneyMethod] description = "The `Method` property on the Add Money events: how the user adds money." @@ -181,6 +182,31 @@ ONBOARDING = "Onboarding" MY_ACCOUNT = "My Account" TIP_CARD_SETUP = "Tip Card Setup" +[lists.ProfileField] +description = "The `Field` property on Profile: Field Saved: the profile field that was saved." +[lists.ProfileField.values] +NAME = "Name" +USERNAME = "Username" +PHOTO = "Photo" +COVER = "Cover" +BIO = "Bio" +MINIMUM = "Minimum" + +[lists.ProfileMenuAction] +description = "The `Action` property on Profile: Menu Action: the action picked from another user's profile menu." +[lists.ProfileMenuAction.values] +MUTE = "Mute" +REPORT = "Report" +BLOCK = "Block" +UNBLOCK = "Unblock" + +[lists.ProfilePinnedAction] +description = "The `Action` property on Profile: Pinned Button Tapped: what the profile's pinned button does." +[lists.ProfilePinnedAction.values] +START_CHATTING = "Start Chatting" +OPEN_CHAT = "Open Chat" +UNBLOCK = "Unblock" + [lists.Button] description = "The button tapped, sent in the event name `Button: {button}`." [lists.Button.values] @@ -684,6 +710,53 @@ name = "Linked Phone Number" domain = "Account" name = "Complete Onboarding" +# Profile + +[[event]] +domain = "Profile" +name = "Profile: Settings Viewed" +builder = "settingsViewed" +description = "The Settings screen opened." + +[[event]] +domain = "Profile" +name = "Profile: Edit Profile Viewed" +builder = "editProfileViewed" +description = "The Edit Profile screen opened." + +[[event]] +domain = "Profile" +name = "Profile: Field Saved" +builder = "fieldSaved" +properties = [{ name = "Field", type = "ProfileField" }] +description = "A profile field was saved." + +[[event]] +domain = "Profile" +name = "Profile: Bio Moderation Failed" +builder = "bioModerationFailed" +description = "A bio was rejected by moderation." + +[[event]] +domain = "Profile" +name = "Profile: Menu Action" +builder = "menuAction" +properties = [{ name = "Action", type = "ProfileMenuAction" }] +description = "An action was picked from another user's profile menu." + +[[event]] +domain = "Profile" +name = "Profile: Pinned Button Tapped" +builder = "pinnedButtonTapped" +properties = [{ name = "Action", type = "ProfilePinnedAction" }] +description = "The pinned button on another user's profile was tapped." + +[[event]] +domain = "Profile" +name = "Profile: Card Shown" +builder = "cardShown" +description = "A profile card was shown." + # Buttons [[event]] diff --git a/libs/analytics-events/src/commonTest/kotlin/com/flipcash/analytics/AddMoneyEventsTest.kt b/libs/analytics-events/src/commonTest/kotlin/com/flipcash/analytics/AddMoneyEventsTest.kt index b4f7bae783..6a54b8080a 100644 --- a/libs/analytics-events/src/commonTest/kotlin/com/flipcash/analytics/AddMoneyEventsTest.kt +++ b/libs/analytics-events/src/commonTest/kotlin/com/flipcash/analytics/AddMoneyEventsTest.kt @@ -17,7 +17,7 @@ class AddMoneyEventsTest { @Test fun openedCarriesEverySourceByItsWireString() { assertEquals( - listOf("Menu", "Give Shortfall", "Buy Shortfall", "Username Shortfall", "Chat", "Scanner", "Balance"), + listOf("Menu", "Give Shortfall", "Buy Shortfall", "Username Shortfall", "Chat", "Scanner", "Balance", "Profile"), AddMoneySource.entries.map { val event = AddMoneyEvents.opened(it) assertEquals("Add Money: Opened", event.name) diff --git a/libs/analytics-events/src/commonTest/kotlin/com/flipcash/analytics/ProfileEventsTest.kt b/libs/analytics-events/src/commonTest/kotlin/com/flipcash/analytics/ProfileEventsTest.kt new file mode 100644 index 0000000000..5f256fee16 --- /dev/null +++ b/libs/analytics-events/src/commonTest/kotlin/com/flipcash/analytics/ProfileEventsTest.kt @@ -0,0 +1,52 @@ +package com.flipcash.analytics + +import com.flipcash.analytics.events.ProfileEvents +import kotlin.test.Test +import kotlin.test.assertEquals + +class ProfileEventsTest { + + @Test + fun screenEventsHaveNoProperties() { + assertEquals(AnalyticsEvent("Profile: Settings Viewed", emptyMap()), ProfileEvents.settingsViewed()) + assertEquals(AnalyticsEvent("Profile: Edit Profile Viewed", emptyMap()), ProfileEvents.editProfileViewed()) + assertEquals(AnalyticsEvent("Profile: Bio Moderation Failed", emptyMap()), ProfileEvents.bioModerationFailed()) + assertEquals(AnalyticsEvent("Profile: Card Shown", emptyMap()), ProfileEvents.cardShown()) + } + + @Test + fun fieldSavedCarriesEveryFieldByItsWireString() { + assertEquals( + listOf("Name", "Username", "Photo", "Cover", "Bio", "Minimum"), + ProfileField.entries.map { + val event = ProfileEvents.fieldSaved(it) + assertEquals("Profile: Field Saved", event.name) + (event.properties.getValue("Field") as PropertyValue.Text).value + }, + ) + } + + @Test + fun menuActionCarriesEveryActionByItsWireString() { + assertEquals( + listOf("Mute", "Report", "Block", "Unblock"), + ProfileMenuAction.entries.map { + val event = ProfileEvents.menuAction(it) + assertEquals("Profile: Menu Action", event.name) + (event.properties.getValue("Action") as PropertyValue.Text).value + }, + ) + } + + @Test + fun pinnedButtonTappedCarriesEveryActionByItsWireString() { + assertEquals( + listOf("Start Chatting", "Open Chat", "Unblock"), + ProfilePinnedAction.entries.map { + val event = ProfileEvents.pinnedButtonTapped(it) + assertEquals("Profile: Pinned Button Tapped", event.name) + (event.properties.getValue("Action") as PropertyValue.Text).value + }, + ) + } +}