Skip to content

Commit

Permalink
Merge pull request #1748 from planetary-social/refactor-profile-view-…
Browse files Browse the repository at this point in the history
…onappear

Refactor ProfileView.onAppear code
  • Loading branch information
mplorentz authored Jan 31, 2025
2 parents 3869c8d + e2e5c31 commit 938b4a7
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 73 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved naming of a couple list-related classes.
- Track TestFlight vs AppStore installations in Posthog. [#130](https://github.com/verse-pbc/issues/issues/130)
- Track breadcrumbs in Sentry for all analytics events. [#125](https://github.com/verse-pbc/issues/issues/125)
- Refactored the way the ProfileView downloads data and logs analytics events. [#1748](https://github.com/planetary-social/nos/pull/1748)

## [1.1] - 2025-01-03Z

Expand Down
31 changes: 17 additions & 14 deletions Nos.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@
C98298332ADD7F9A0096C5B5 /* DeepLinkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C98298322ADD7F9A0096C5B5 /* DeepLinkService.swift */; };
C98298342ADD7F9A0096C5B5 /* DeepLinkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C98298322ADD7F9A0096C5B5 /* DeepLinkService.swift */; };
C98651102B0BD49200597B68 /* PagedNoteListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C986510F2B0BD49200597B68 /* PagedNoteListView.swift */; };
C987153D2D4D198200EA2F56 /* OnTabAppearModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C987153C2D4D198200EA2F56 /* OnTabAppearModifier.swift */; };
C987F81729BA4C6A00B44E7A /* BigActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C987F81629BA4C6900B44E7A /* BigActionButton.swift */; };
C987F81A29BA4D0E00B44E7A /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C987F81929BA4D0E00B44E7A /* ActionButton.swift */; };
C987F81D29BA6D9A00B44E7A /* ProfileTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = C987F81C29BA6D9A00B44E7A /* ProfileTab.swift */; };
Expand Down Expand Up @@ -951,6 +952,7 @@
C98298312ADD7EDB0096C5B5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
C98298322ADD7F9A0096C5B5 /* DeepLinkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkService.swift; sourceTree = "<group>"; };
C986510F2B0BD49200597B68 /* PagedNoteListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagedNoteListView.swift; sourceTree = "<group>"; };
C987153C2D4D198200EA2F56 /* OnTabAppearModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnTabAppearModifier.swift; sourceTree = "<group>"; };
C987F81629BA4C6900B44E7A /* BigActionButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BigActionButton.swift; sourceTree = "<group>"; };
C987F81929BA4D0E00B44E7A /* ActionButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = "<group>"; };
C987F81C29BA6D9A00B44E7A /* ProfileTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileTab.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1868,12 +1870,13 @@
03C7E7A12CB9CD0B0054624C /* PointDownEmojiTipViewStyle.swift */,
C9A25B3C29F174D200B39534 /* ReadabilityPadding.swift */,
C9E37E0E2A1E7C32003D4B0A /* ReportMenuModifier.swift */,
C987153C2D4D198200EA2F56 /* OnTabAppearModifier.swift */,
C9A0DAE629C69FA000466635 /* Text+Gradient.swift */,
04C9D7262CBF09C200EAAD4D /* TextField+PlaceHolderStyle.swift */,
C9DC6CB92C1739AD00E1CFB3 /* View+HandleURLsInRouter.swift */,
50089A162C98678600834588 /* View+ListRowGradientBackground.swift */,
C93EC2FC29C3785C0012EE2A /* View+RoundedCorner.swift */,
50DE6B1A2C6B88FE0065665D /* View+StyledBorder.swift */,
04C9D7262CBF09C200EAAD4D /* TextField+PlaceHolderStyle.swift */,
);
path = Modifiers;
sourceTree = "<group>";
Expand Down Expand Up @@ -2261,7 +2264,7 @@
C9B737702AB24D5F00398BE7 /* XCRemoteSwiftPackageReference "SwiftGenPlugin" */,
C91565BF2B2368FA0068EECA /* XCRemoteSwiftPackageReference "ViewInspector" */,
3AD3185B2B294E6200026B07 /* XCRemoteSwiftPackageReference "xcstrings-tool-plugin" */,
C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1.swift" */,
C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1" */,
C9FD35112BCED5A6008F8D95 /* XCRemoteSwiftPackageReference "nostr-sdk-ios" */,
03C49ABE2C938A9C00502321 /* XCRemoteSwiftPackageReference "SwiftSoup" */,
039389212CA4985C00698978 /* XCRemoteSwiftPackageReference "SDWebImageWebPCoder" */,
Expand Down Expand Up @@ -2575,7 +2578,6 @@
C98CA9042B14FA3D00929141 /* PagedRelaySubscription.swift in Sources */,
5B0D99032A94090A0039F0C5 /* DoubleTapToPopModifier.swift in Sources */,
030024192CC00DFC0073ED56 /* SplashScreenView.swift in Sources */,
50CBD8102D3A8B7000BF8A0B /* ListCircle.swift in Sources */,
0357299B2BE415E5005FEE85 /* ContentWarningController.swift in Sources */,
5BFF66B42A58853D00AA79DD /* PublishedEventsView.swift in Sources */,
03D1B42C2C3C1B0D001778CD /* TLVElement.swift in Sources */,
Expand Down Expand Up @@ -2626,6 +2628,7 @@
65BD8DC22BDAF2C300802039 /* DiscoverTab.swift in Sources */,
65BD8DC32BDAF2C300802039 /* FeaturedAuthorCategory.swift in Sources */,
C93F045E2B9B7A7000AD5872 /* ReplyPreview.swift in Sources */,
C987153D2D4D198200EA2F56 /* OnTabAppearModifier.swift in Sources */,
C97465312A3B89140031226F /* AuthorLabel.swift in Sources */,
C9C547592A4F1D8C006B0741 /* NosNotification+CoreDataClass.swift in Sources */,
030AE4292BE3D63C004DEE02 /* FeaturedAuthor.swift in Sources */,
Expand Down Expand Up @@ -2928,11 +2931,11 @@
/* Begin PBXTargetDependency section */
3AD3185D2B294E9000026B07 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = 3AD3185C2B294E9000026B07 /* plugin:XCStringsToolPlugin */;
productRef = 3AD3185C2B294E9000026B07 /* XCStringsToolPlugin */;
};
3AEABEF32B2BF806001BC933 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = 3AEABEF22B2BF806001BC933 /* plugin:XCStringsToolPlugin */;
productRef = 3AEABEF22B2BF806001BC933 /* XCStringsToolPlugin */;
};
C90862C229E9804B00C35A71 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
Expand All @@ -2941,11 +2944,11 @@
};
C9A6C7442AD83F7A001F9500 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = C9A6C7432AD83F7A001F9500 /* plugin:SwiftGenPlugin */;
productRef = C9A6C7432AD83F7A001F9500 /* SwiftGenPlugin */;
};
C9D573402AB24A3700E06BB4 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = C9D5733F2AB24A3700E06BB4 /* plugin:SwiftGenPlugin */;
productRef = C9D5733F2AB24A3700E06BB4 /* SwiftGenPlugin */;
};
C9DEBFE6298941020078B43A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
Expand Down Expand Up @@ -3819,7 +3822,7 @@
minimumVersion = 4.0.0;
};
};
C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1.swift" */ = {
C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/GigaBitcoin/secp256k1.swift";
requirement = {
Expand Down Expand Up @@ -3858,12 +3861,12 @@
package = 03C49ABE2C938A9C00502321 /* XCRemoteSwiftPackageReference "SwiftSoup" */;
productName = SwiftSoup;
};
3AD3185C2B294E9000026B07 /* plugin:XCStringsToolPlugin */ = {
3AD3185C2B294E9000026B07 /* XCStringsToolPlugin */ = {
isa = XCSwiftPackageProductDependency;
package = 3AD3185B2B294E6200026B07 /* XCRemoteSwiftPackageReference "xcstrings-tool-plugin" */;
productName = "plugin:XCStringsToolPlugin";
};
3AEABEF22B2BF806001BC933 /* plugin:XCStringsToolPlugin */ = {
3AEABEF22B2BF806001BC933 /* XCStringsToolPlugin */ = {
isa = XCSwiftPackageProductDependency;
package = 3AD3185B2B294E6200026B07 /* XCRemoteSwiftPackageReference "xcstrings-tool-plugin" */;
productName = "plugin:XCStringsToolPlugin";
Expand Down Expand Up @@ -3942,7 +3945,7 @@
package = C99DBF7C2A9E81CF00F7068F /* XCRemoteSwiftPackageReference "SDWebImageSwiftUI" */;
productName = SDWebImageSwiftUI;
};
C9A6C7432AD83F7A001F9500 /* plugin:SwiftGenPlugin */ = {
C9A6C7432AD83F7A001F9500 /* SwiftGenPlugin */ = {
isa = XCSwiftPackageProductDependency;
package = C9B737702AB24D5F00398BE7 /* XCRemoteSwiftPackageReference "SwiftGenPlugin" */;
productName = "plugin:SwiftGenPlugin";
Expand All @@ -3962,7 +3965,7 @@
package = C9B71DBC2A8E9BAD0031ED9F /* XCRemoteSwiftPackageReference "sentry-cocoa" */;
productName = Sentry;
};
C9D5733F2AB24A3700E06BB4 /* plugin:SwiftGenPlugin */ = {
C9D5733F2AB24A3700E06BB4 /* SwiftGenPlugin */ = {
isa = XCSwiftPackageProductDependency;
package = C9C8450C2AB249DB00654BC1 /* XCRemoteSwiftPackageReference "SwiftGenPlugin" */;
productName = "plugin:SwiftGenPlugin";
Expand All @@ -3974,12 +3977,12 @@
};
C9FD34F52BCEC89C008F8D95 /* secp256k1 */ = {
isa = XCSwiftPackageProductDependency;
package = C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1.swift" */;
package = C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1" */;
productName = secp256k1;
};
C9FD34F72BCEC8B5008F8D95 /* secp256k1 */ = {
isa = XCSwiftPackageProductDependency;
package = C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1.swift" */;
package = C9FD34F42BCEC89C008F8D95 /* XCRemoteSwiftPackageReference "secp256k1" */;
productName = secp256k1;
};
C9FD35122BCED5A6008F8D95 /* NostrSDK */ = {
Expand Down
2 changes: 2 additions & 0 deletions Nos/Router.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Dependencies
@Dependency(\.persistenceController) private var persistenceController
@Dependency(\.relayService) private var relayService
@Dependency(\.crashReporting) private var crashReporting
@Dependency(\.analytics) private var analytics

/// The `NavigationPath` of the tab (or side menu) the user currently has open.
/// This has to be a two-way binding, but really the only things that should be modifying it are the `Router`
Expand Down Expand Up @@ -104,6 +105,7 @@ import Dependencies

/// Pushes a profile view for the given author.
func push(_ author: Author) {
analytics.showedProfile()
push(.author(author.hexadecimalPublicKey))
}

Expand Down
10 changes: 7 additions & 3 deletions Nos/Service/Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,29 @@ class Analytics {
}

func showedHome() {
track("Home Tab Tapped")
track("Home Tab Opened")
}

func showedDiscover() {
track("Discover Tab Tapped")
track("Discover Tab Opened")
}

func showedNoteComposer() {
track("New Note Tapped")
}

func showedNotifications() {
track("Notifications Tab Tapped")
track("Notifications Tab Opened")
}

func showedProfile() {
track("Profile View Opened")
}

func showedProfileTab() {
track("Profile Tab Opened")
}

func showedThread() {
track("Thread View Opened")
}
Expand Down
2 changes: 1 addition & 1 deletion Nos/Views/AppView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ struct AppView: View {
.badge(pushNotificationService.badgeCount)

if let author = currentUser.author {
ProfileTab(author: author, path: $router.profilePath)
ProfileTab(author: author)
.tabItem {
VStack {
let text = Text("profileTitle")
Expand Down
16 changes: 2 additions & 14 deletions Nos/Views/Discover/DiscoverTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ struct DiscoverTab: View {

@State var columns: Int = 0

@State private var isVisible = false

@State private var searchController = SearchController()

@FocusState private var isSearching: Bool
Expand Down Expand Up @@ -56,18 +54,8 @@ struct DiscoverTab: View {
}
.background(Color.appBg)
.animation(.easeInOut, value: columns)
.onAppear {
if router.selectedTab == .discover {
isVisible = true
}
}
.onDisappear {
isVisible = false
}
.onChange(of: isVisible) {
if isVisible {
analytics.showedDiscover()
}
.onTabAppear(.discover) {
analytics.showedDiscover()
}
.nosNavigationBar("discover")
.toolbarBackground(.visible, for: .navigationBar)
Expand Down
17 changes: 4 additions & 13 deletions Nos/Views/Home/HomeFeedView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ struct HomeFeedView: View {

@Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject private var router: Router
@ObservationIgnored @Dependency(\.analytics) private var analytics
@Dependency(\.analytics) private var analytics
@ObserveInjection var inject

@State private var refreshController = RefreshController(lastRefreshDate: Date.now + Self.staticLoadTime)
@State private var isVisible = false
@State private var feedController: FeedController

/// When set to true this will display a fullscreen progress wheel for a set amount of time to give us a chance
Expand Down Expand Up @@ -188,17 +187,9 @@ struct HomeFeedView: View {
.navigationBarTitle("", displayMode: .inline)
.padding(.top, 1)
.environment(feedController)
.onAppear {
if router.selectedTab == .home {
isVisible = true
}
}
.onDisappear { isVisible = false }
.onChange(of: isVisible) {
if isVisible {
analytics.showedHome()
GoToFeedTip.viewedFeed.sendDonation()
}
.onTabAppear(.home) {
analytics.showedHome()
GoToFeedTip.viewedFeed.sendDonation()
}
.onChange(of: shouldNavigateToListsOnAppear) {
if shouldNavigateToListsOnAppear {
Expand Down
46 changes: 46 additions & 0 deletions Nos/Views/Modifiers/OnTabAppearModifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import SwiftUI

/// A view modifier that helps track when a tab becomes visible or invisible in a TabView.
struct OnTabAppearModifier: ViewModifier {
@EnvironmentObject private var router: Router
let tab: AppDestination
let onAppear: (() async -> Void)?
let onDisappear: (() async -> Void)?

@State private var isVisible = false

func body(content: Content) -> some View {
content
.onAppear {
if router.selectedTab == tab {
isVisible = true
}
}
.onDisappear { isVisible = false }
.onChange(of: isVisible) {
if isVisible {
Task { await onAppear?() }
} else {
Task { await onDisappear?() }
}
}
}
}

extension View {
/// Executes an action when a specific tab becomes visible
/// - Parameters:
/// - tab: The tab to monitor for visibility
/// - action: The action to perform when the tab becomes visible
func onTabAppear(_ tab: AppDestination, perform action: @escaping () async -> Void) -> some View {
modifier(OnTabAppearModifier(tab: tab, onAppear: action, onDisappear: nil))
}

/// Executes an action when a specific tab is navigated away from
/// - Parameters:
/// - tab: The tab to monitor for visibility
/// - action: The action to perform when the tab becomes invisible
func onTabDisappear(_ tab: AppDestination, perform action: @escaping () async -> Void) -> some View {
modifier(OnTabAppearModifier(tab: tab, onAppear: nil, onDisappear: action))
}
}
24 changes: 7 additions & 17 deletions Nos/Views/Notifications/NotificationsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,25 +94,15 @@ struct NotificationsView: View {
.refreshable {
await subscribeToNewEvents()
}
.onAppear {
if router.selectedTab == .notifications {
isVisible = true
}
.onTabAppear(.notifications) {
pushNotificationService.requestNotificationPermissionsFromUser()
analytics.showedNotifications()
await subscribeToNewEvents()
await markAllNotificationsRead()
}
.onDisappear {
isVisible = false
}
.onChange(of: isVisible) {
Task { await markAllNotificationsRead() }
if isVisible {
analytics.showedNotifications()
Task {
await subscribeToNewEvents()
}
} else {
Task { await cancelSubscriptions() }
}
.onTabDisappear(.notifications) {
await cancelSubscriptions()
await markAllNotificationsRead()
}
.doubleTapToPop(tab: .notifications) { proxy in
if let firstEvent = events.first {
Expand Down
10 changes: 7 additions & 3 deletions Nos/Views/Profile/ProfileTab.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import Combine
import SwiftUI
import Dependencies

/// A version of the ProfileView that is displayed in the main tab bar
struct ProfileTab: View {

@Environment(CurrentUser.self) var currentUser
@EnvironmentObject private var router: Router
@Dependency(\.analytics) private var analytics
@ObservedObject var author: Author

@Binding var path: NavigationPath

var body: some View {
NosNavigationStack(path: $path) {
NosNavigationStack(path: $router.profilePath) {
ProfileView(author: author, addDoubleTapToPop: true)
.navigationBarItems(leading: SideMenuButton())
.onTabAppear(.profile) {
analytics.showedProfileTab()
}
}
}
}
10 changes: 2 additions & 8 deletions Nos/Views/Profile/ProfileView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,8 @@ struct ProfileView: View {
}
)
.alert(unwrapping: $alert)
.onAppear {
Task {
await downloadAuthorData()
}
analytics.showedProfile()
}
.onDisappear {
relaySubscriptions.removeAll()
.task {
await downloadAuthorData()
}
}

Expand Down

0 comments on commit 938b4a7

Please sign in to comment.