damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit b3d9ee3fc096aae23e02cbfd6f1e8d4b8fd28323
parent e65219ee3e942ab3ab359ed8a2d59c36919ba665
Author: Terry Yiu <git@tyiu.xyz>
Date:   Wed,  4 Jun 2025 00:14:34 -0400

Add tip in threads to inform users what trusted network means

Changelog-Added: Added tip in threads to inform users what trusted network means
Signed-off-by: Terry Yiu <git@tyiu.xyz>

Diffstat:
Mdamus.xcodeproj/project.pbxproj | 16++++++++++++++++
Mdamus/ContentView.swift | 19+++++++++++++++++++
Mdamus/Models/UserSettingsStore.swift | 3+++
Mdamus/Util/Log.swift | 1+
Mdamus/Views/Chat/ChatroomThreadView.swift | 11+++++++++++
Mdamus/Views/Notifications/NotificationsView.swift | 8+++++++-
Mdamus/Views/Settings/DeveloperSettingsView.swift | 5+++++
Adamus/Views/Tips/TrustedNetworkRepliesTip.swift | 26++++++++++++++++++++++++++
8 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -27,6 +27,9 @@ 3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */; }; 3A4647CF2A413ADC00386AD8 /* CondensedProfilePicturesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4647CE2A413ADC00386AD8 /* CondensedProfilePicturesView.swift */; }; 3A48E7B029DFBE9D006E787E /* MutedThreadsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A48E7AF29DFBE9D006E787E /* MutedThreadsManager.swift */; }; + 3A515C502DF4E100002D3B34 /* TrustedNetworkRepliesTip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A515C4F2DF4E100002D3B34 /* TrustedNetworkRepliesTip.swift */; }; + 3A515C512DF4E100002D3B34 /* TrustedNetworkRepliesTip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A515C4F2DF4E100002D3B34 /* TrustedNetworkRepliesTip.swift */; }; + 3A515C522DF4E100002D3B34 /* TrustedNetworkRepliesTip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A515C4F2DF4E100002D3B34 /* TrustedNetworkRepliesTip.swift */; }; 3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8CC6CB2A2CFEF900940F5F /* StringUtil.swift */; }; 3A92C0FE2DE16E9800CEEBAC /* FaviconCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A92C0FD2DE16E9800CEEBAC /* FaviconCache.swift */; }; 3A92C0FF2DE16E9800CEEBAC /* FaviconCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A92C0FD2DE16E9800CEEBAC /* FaviconCache.swift */; }; @@ -1870,6 +1873,7 @@ 3A47CB782BDA05A200728A7C /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; }; 3A47CB792BDA05A200728A7C /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fi; path = fi.lproj/Localizable.stringsdict; sourceTree = "<group>"; }; 3A48E7AF29DFBE9D006E787E /* MutedThreadsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutedThreadsManager.swift; sourceTree = "<group>"; }; + 3A515C4F2DF4E100002D3B34 /* TrustedNetworkRepliesTip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrustedNetworkRepliesTip.swift; sourceTree = "<group>"; }; 3A5C4575296A879E0032D398 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "es-419"; path = "es-419.lproj/Localizable.stringsdict"; sourceTree = "<group>"; }; 3A5CAE1D298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; 3A5CAE1E298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Localizable.strings"; sourceTree = "<group>"; }; @@ -2748,6 +2752,14 @@ path = "Empty Views"; sourceTree = "<group>"; }; + 3A515C4E2DF4E0E6002D3B34 /* Tips */ = { + isa = PBXGroup; + children = ( + 3A515C4F2DF4E100002D3B34 /* TrustedNetworkRepliesTip.swift */, + ); + path = Tips; + sourceTree = "<group>"; + }; 3AA24800297E3DAE0090C62D /* Reposts */ = { isa = PBXGroup; children = ( @@ -3245,6 +3257,7 @@ 4C190F232A547D1700027FD5 /* NostrScript */, 4C7D095A2A098C5C00943473 /* Wallet */, 4C8D1A6D29F31E4100ACDF75 /* Buttons */, + 3A515C4E2DF4E0E6002D3B34 /* Tips */, 4C1A9A2829DDF53B00516EAC /* Video */, 4C1A9A1B29DDCF8B00516EAC /* Settings */, 4CFF8F6129CC9A80008DB934 /* Images */, @@ -4766,6 +4779,7 @@ D7100C5A2B76FD5100C59298 /* LogoView.swift in Sources */, 4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */, 4C3AC79F2833115300E1F516 /* FollowButtonView.swift in Sources */, + 3A515C502DF4E100002D3B34 /* TrustedNetworkRepliesTip.swift in Sources */, D7CB5D3B2B112FBB00AD4105 /* NotificationFormatter.swift in Sources */, 4C4E137B2A76D5FB00BDD832 /* MuteThreadNotify.swift in Sources */, 4CC7AAE7297EFA7B00430951 /* Zap.swift in Sources */, @@ -5416,6 +5430,7 @@ 82D6FB9D2CD99F7900C925F4 /* ZapButtonModel.swift in Sources */, 5C09FD142DF283D700823661 /* FollowPackModel.swift in Sources */, 82D6FB9E2CD99F7900C925F4 /* ContentFilters.swift in Sources */, + 3A515C512DF4E100002D3B34 /* TrustedNetworkRepliesTip.swift in Sources */, 82D6FB9F2CD99F7900C925F4 /* DamusCacheManager.swift in Sources */, 82D6FBA02CD99F7900C925F4 /* NotificationsManager.swift in Sources */, D755B28E2D3E7D8800BBEEFA /* NIP37Draft.swift in Sources */, @@ -5671,6 +5686,7 @@ D73E5E2B2C6A97F4007EB227 /* PostNotify.swift in Sources */, D73E5E2C2C6A97F4007EB227 /* PresentSheetNotify.swift in Sources */, D73E5E2D2C6A97F4007EB227 /* ProfileUpdatedNotify.swift in Sources */, + 3A515C522DF4E100002D3B34 /* TrustedNetworkRepliesTip.swift in Sources */, D73E5E2E2C6A97F4007EB227 /* ReportNotify.swift in Sources */, D73E5E2F2C6A97F4007EB227 /* ScrollToTopNotify.swift in Sources */, D73E5E302C6A97F4007EB227 /* SwitchedTimelineNotify.swift in Sources */, diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -10,6 +10,10 @@ import AVKit import MediaPlayer import EmojiPicker +#if canImport(TipKit) +import TipKit +#endif + struct ZapSheet { let target: ZapTarget let lnurl: String @@ -705,6 +709,21 @@ struct ContentView: View { damus_state.nostrNetwork.pool.register_handler(sub_id: sub_id, handler: home.handle_event) damus_state.nostrNetwork.connect() + + if #available(iOS 17, *) { + if damus_state.settings.developer_mode && damus_state.settings.reset_tips_on_launch { + do { + try Tips.resetDatastore() + } catch { + Log.error("Failed to reset tips datastore: %s", for: .tips, error.localizedDescription) + } + } + do { + try Tips.configure() + } catch { + Log.error("Failed to configure tips: %s", for: .tips, error.localizedDescription) + } + } } func music_changed(_ state: MusicState) { diff --git a/damus/Models/UserSettingsStore.swift b/damus/Models/UserSettingsStore.swift @@ -131,6 +131,9 @@ class UserSettingsStore: ObservableObject { @Setting(key: "show_trusted_replies_first", default_value: true) var show_trusted_replies_first: Bool + @Setting(key: "reset_tips_on_launch", default_value: false) + var reset_tips_on_launch: Bool + @Setting(key: "hide_nsfw_tagged_content", default_value: false) var hide_nsfw_tagged_content: Bool diff --git a/damus/Util/Log.swift b/damus/Util/Log.swift @@ -21,6 +21,7 @@ enum LogCategory: String { case damus_purple case image_uploading case video_coordination + case tips } /// Damus structured logger diff --git a/damus/Views/Chat/ChatroomThreadView.swift b/damus/Views/Chat/ChatroomThreadView.swift @@ -7,6 +7,7 @@ import SwiftUI import SwipeActions +import TipKit struct ChatroomThreadView: View { @Environment(\.dismiss) var dismiss @@ -159,6 +160,12 @@ struct ChatroomThreadView: View { // MARK: - Children view - outside trusted network if !untrusted_events.isEmpty { + if #available(iOS 17, *) { + TipView(TrustedNetworkRepliesTip.shared, arrowEdge: .bottom) + .padding(.top, 10) + .padding(.horizontal) + } + VStack(alignment: .leading, spacing: 0) { // Track this section's position Color.clear @@ -184,6 +191,10 @@ struct ChatroomThreadView: View { withAnimation { untrusted_network_expanded.toggle() + if #available(iOS 17, *) { + TrustedNetworkRepliesTip.shared.invalidate(reason: .actionPerformed) + } + scroll_to_event(scroller: scroller, id: ChatroomThreadView.untrusted_network_section_id, delay: 0.1, animate: true, anchor: ChatroomThreadView.sticky_header_adjusted_anchor) } }) { diff --git a/damus/Views/Notifications/NotificationsView.swift b/damus/Views/Notifications/NotificationsView.swift @@ -7,6 +7,10 @@ import SwiftUI +#if canImport(TipKit) +import TipKit +#endif + class NotificationFilter: ObservableObject, Equatable { @Published var state: NotificationFilterState @Published var friend_filter: FriendFilter @@ -75,7 +79,9 @@ struct NotificationsView: View { @StateObject var filter = NotificationFilter() @SceneStorage("NotificationsView.filter_state") var filter_state: NotificationFilterState = .all @Binding var subtitle: String? - + + @State var showTip: Bool = true + @Environment(\.colorScheme) var colorScheme var body: some View { diff --git a/damus/Views/Settings/DeveloperSettingsView.swift b/damus/Views/Settings/DeveloperSettingsView.swift @@ -96,6 +96,11 @@ struct DeveloperSettingsView: View { Toggle(NSLocalizedString("Enable experimental Purple In-app purchase support", comment: "Developer mode setting to enable experimental Purple In-app purchase support."), isOn: $settings.enable_experimental_purple_iap_support) .toggleStyle(.switch) + + if #available(iOS 17, *) { + Toggle(NSLocalizedString("Reset tips on launch", comment: "Developer mode setting to reset tips upon app first launch. Tips are visual contextual hints that highlight new, interesting, or unused features users have not discovered yet."), isOn: $settings.reset_tips_on_launch) + .toggleStyle(.switch) + } } } } diff --git a/damus/Views/Tips/TrustedNetworkRepliesTip.swift b/damus/Views/Tips/TrustedNetworkRepliesTip.swift @@ -0,0 +1,26 @@ +// +// TrustedNetworkRepliesTip.swift +// damus +// +// Created by Terry Yiu on 6/7/25. +// + +import Foundation +import TipKit + +@available(iOS 17, *) +struct TrustedNetworkRepliesTip: Tip { + static let shared = TrustedNetworkRepliesTip() + + var title: Text { + Text("Toggle visibility of replies from outside your trusted network", comment: "Title of tip that informs users what trusted network means and that they can toggle the visibility of threaded replies from outside their trusted network.") + } + + var message: Text? { + Text("Your trusted network is comprised of profiles you follow and profiles that they follow.", comment: "Description of the tip that informs users what trusted network means.") + } + + var image: Image? { + Image(systemName: "network.badge.shield.half.filled") + } +}