From c68d8c7335f81e38d1b98e6edbb2a584c4279ad9 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Fri, 11 Oct 2024 16:51:08 -0400 Subject: [PATCH 1/3] #1602: Welcome to Your Feed tip --- Nos.xcodeproj/project.pbxproj | 8 ++++ Nos/Assets/Localization/Localizable.xcstrings | 22 +++++++++ Nos/NosApp.swift | 2 + Nos/Views/Home/HomeFeedView.swift | 48 ++++++++++++------- Nos/Views/Home/WelcomeToFeedTip.swift | 12 +++++ Nos/Views/Modifiers/InlineTipViewStyle.swift | 33 +++++++++++++ .../Modifiers/LinearGradient+Planetary.swift | 20 +++++--- 7 files changed, 120 insertions(+), 25 deletions(-) create mode 100644 Nos/Views/Home/WelcomeToFeedTip.swift create mode 100644 Nos/Views/Modifiers/InlineTipViewStyle.swift diff --git a/Nos.xcodeproj/project.pbxproj b/Nos.xcodeproj/project.pbxproj index b53eef0f5..38012c848 100644 --- a/Nos.xcodeproj/project.pbxproj +++ b/Nos.xcodeproj/project.pbxproj @@ -99,6 +99,8 @@ 03C49AC22C938DE100502321 /* SoupOpenGraphParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C49AC12C938DE100502321 /* SoupOpenGraphParser.swift */; }; 03C49AC32C938DE100502321 /* SoupOpenGraphParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C49AC12C938DE100502321 /* SoupOpenGraphParser.swift */; }; 03C68C522C94D8400037DC59 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 03C68C512C94D8400037DC59 /* SwiftSoup */; }; + 03C7E7922CB9C0B30054624C /* WelcomeToFeedTip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C7E7912CB9C0AF0054624C /* WelcomeToFeedTip.swift */; }; + 03C7E7982CB9C16C0054624C /* InlineTipViewStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C7E7972CB9C1600054624C /* InlineTipViewStyle.swift */; }; 03C8B4962C6D065900A07CCD /* ImageViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03C8B4952C6D065900A07CCD /* ImageViewer.swift */; }; 03D1B4282C3C1A5D001778CD /* NostrIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D1B4272C3C1A5D001778CD /* NostrIdentifier.swift */; }; 03D1B4292C3C1AC9001778CD /* NostrIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D1B4272C3C1A5D001778CD /* NostrIdentifier.swift */; }; @@ -660,6 +662,8 @@ 03B4E6AB2C125D13006E5F59 /* FileStorageUploadResponseJSONTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileStorageUploadResponseJSONTests.swift; sourceTree = ""; }; 03B4E6AD2C125D61006E5F59 /* FileStorageUploadResponseJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileStorageUploadResponseJSON.swift; sourceTree = ""; }; 03C49AC12C938DE100502321 /* SoupOpenGraphParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoupOpenGraphParser.swift; sourceTree = ""; }; + 03C7E7912CB9C0AF0054624C /* WelcomeToFeedTip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeToFeedTip.swift; sourceTree = ""; }; + 03C7E7972CB9C1600054624C /* InlineTipViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineTipViewStyle.swift; sourceTree = ""; }; 03C8B4952C6D065900A07CCD /* ImageViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewer.swift; sourceTree = ""; }; 03D1B4272C3C1A5D001778CD /* NostrIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrIdentifier.swift; sourceTree = ""; }; 03D1B42B2C3C1B0D001778CD /* TLVElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TLVElement.swift; sourceTree = ""; }; @@ -1649,6 +1653,7 @@ children = ( C9DEBFD8298941000078B43A /* HomeFeedView.swift */, 5BE281C92AE2CCEB00880466 /* HomeTab.swift */, + 03C7E7912CB9C0AF0054624C /* WelcomeToFeedTip.swift */, ); path = Home; sourceTree = ""; @@ -1747,6 +1752,7 @@ C9CFF6D02AB241EB00D4B368 /* Modifiers */ = { isa = PBXGroup; children = ( + 03C7E7972CB9C1600054624C /* InlineTipViewStyle.swift */, 5B0D99022A94090A0039F0C5 /* DoubleTapToPopModifier.swift */, C95D68A8299E709800429F86 /* LinearGradient+Planetary.swift */, C95D68A4299E6E1E00429F86 /* PlaceholderModifier.swift */, @@ -2443,6 +2449,7 @@ 030FECAB2CB5E0B900820014 /* BuildYourNetworkView.swift in Sources */, A3B943CF299AE00100A15A08 /* Keychain.swift in Sources */, C9671D73298DB94C00EE7E12 /* Data+Encoding.swift in Sources */, + 03C7E7922CB9C0B30054624C /* WelcomeToFeedTip.swift in Sources */, C9646EA129B7A22C007239A4 /* Analytics.swift in Sources */, C9A6C74B2AD866A7001F9500 /* UNSSuccessView.swift in Sources */, 5045540D2C81E10C0044ECAE /* EditableAvatarView.swift in Sources */, @@ -2466,6 +2473,7 @@ 3F43C47629A9625700E896A0 /* AuthorReference+CoreDataClass.swift in Sources */, C9A6C7492AD86271001F9500 /* UNSNewNameView.swift in Sources */, 5B6136382C2F408E00ADD9C3 /* RepliesLabel.swift in Sources */, + 03C7E7982CB9C16C0054624C /* InlineTipViewStyle.swift in Sources */, C9ADB13D29929B540075E7F8 /* Bech32.swift in Sources */, C95D68A1299E6D3E00429F86 /* BioView.swift in Sources */, 5BE281CA2AE2CCEB00880466 /* HomeTab.swift in Sources */, diff --git a/Nos/Assets/Localization/Localizable.xcstrings b/Nos/Assets/Localization/Localizable.xcstrings index 19f002181..df1704e33 100644 --- a/Nos/Assets/Localization/Localizable.xcstrings +++ b/Nos/Assets/Localization/Localizable.xcstrings @@ -12966,6 +12966,17 @@ } } }, + "nosDoesNotUseAlgorithms" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nos does not use algorithms to populate your feed. Notes appear in reverse chronological order based on publish time and date." + } + } + } + }, "nosIsOpenSource" : { "extractionState" : "manual", "localizations" : { @@ -21665,6 +21676,17 @@ } } }, + "welcomeToYourFeed" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Welcome to your feed!" + } + } + } + }, "xNewFollowers" : { "extractionState" : "manual", "localizations" : { diff --git a/Nos/NosApp.swift b/Nos/NosApp.swift index 188a98189..8dee730ca 100644 --- a/Nos/NosApp.swift +++ b/Nos/NosApp.swift @@ -1,6 +1,7 @@ import SwiftUI import Logger import Dependencies +import TipKit @main struct NosApp: App { @@ -22,6 +23,7 @@ struct NosApp: App { // https://github.com/planetary-social/nos/issues/1064 UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = .systemBlue persistenceController.scheduleBackgroundCleanupTask() + try? Tips.configure() } var body: some Scene { diff --git a/Nos/Views/Home/HomeFeedView.swift b/Nos/Views/Home/HomeFeedView.swift index 84ef084cc..3768adf2c 100644 --- a/Nos/Views/Home/HomeFeedView.swift +++ b/Nos/Views/Home/HomeFeedView.swift @@ -2,6 +2,7 @@ import SwiftUI import CoreData import Combine import Dependencies +import TipKit struct HomeFeedView: View { @@ -21,6 +22,9 @@ struct HomeFeedView: View { static let staticLoadTime: TimeInterval = 2 let user: Author + + /// A tip to display at the top of the feed. + let welcomeTip = WelcomeToFeedTip() @State private var showRelayPicker = false @State private var selectedRelay: Relay? @@ -63,25 +67,33 @@ struct HomeFeedView: View { var body: some View { ZStack { - PagedNoteListView( - refreshController: $refreshController, - databaseFilter: homeFeedFetchRequest, - relayFilter: homeFeedFilter, - relay: selectedRelay, - managedObjectContext: viewContext, - tab: .home, - header: { - EmptyView() - }, - emptyPlaceholder: { - VStack { - Text(.localizable.noEvents) - .padding() + VStack(spacing: 8) { + TipView(welcomeTip) + .padding(.top, 20) + .padding(.horizontal, 16) + .tipBackground(LinearGradient.horizontalAccentReversed) + .tipViewStyle(.inline) + + PagedNoteListView( + refreshController: $refreshController, + databaseFilter: homeFeedFetchRequest, + relayFilter: homeFeedFilter, + relay: selectedRelay, + managedObjectContext: viewContext, + tab: .home, + header: { + EmptyView() + }, + emptyPlaceholder: { + VStack { + Text(.localizable.noEvents) + .padding() + } + .frame(minHeight: 300) } - .frame(minHeight: 300) - } - ) - .padding(0) + ) + .padding(0) + } NewNotesButton(fetchRequest: FetchRequest(fetchRequest: newNotesRequest)) { refreshController.startRefresh = true diff --git a/Nos/Views/Home/WelcomeToFeedTip.swift b/Nos/Views/Home/WelcomeToFeedTip.swift new file mode 100644 index 000000000..4f1cb02ac --- /dev/null +++ b/Nos/Views/Home/WelcomeToFeedTip.swift @@ -0,0 +1,12 @@ +import TipKit + +/// A tip that's displayed on the Feed view, also known as the Home view. +struct WelcomeToFeedTip: Tip { + var title: Text { + Text("welcomeToYourFeed") + } + + var message: Text? { + Text("nosDoesNotUseAlgorithms") + } +} diff --git a/Nos/Views/Modifiers/InlineTipViewStyle.swift b/Nos/Views/Modifiers/InlineTipViewStyle.swift new file mode 100644 index 000000000..2dd952af8 --- /dev/null +++ b/Nos/Views/Modifiers/InlineTipViewStyle.swift @@ -0,0 +1,33 @@ +import TipKit + +/// An inline tip view style with a styled title, message, and close button. +struct InlineTipViewStyle: TipViewStyle { + func makeBody(configuration: TipViewStyle.Configuration) -> some View { + VStack(alignment: .leading, spacing: 10) { + HStack(alignment: .top) { + configuration.title + .font(.title2.bold()) + .foregroundStyle(Color.primaryTxt) + .shadow(color: .lightShadow, radius: 2, x: 0, y: 1) + Spacer() + Button { + configuration.tip.invalidate(reason: .tipClosed) + } label: { + Image(systemName: "xmark").scaledToFit() + .foregroundStyle(Color.primaryTxt) + .font(.system(size: 20).bold()) + } + } + + configuration.message + .foregroundStyle(Color.primaryTxt) + .shadow(color: .lightShadow, radius: 2, x: 0, y: 1) + } + .padding(.vertical, 20) + .padding(.horizontal, 16) + } +} + +extension TipViewStyle where Self == InlineTipViewStyle { + static var inline: Self { Self() } +} diff --git a/Nos/Views/Modifiers/LinearGradient+Planetary.swift b/Nos/Views/Modifiers/LinearGradient+Planetary.swift index 38f103382..ce94a9d43 100644 --- a/Nos/Views/Modifiers/LinearGradient+Planetary.swift +++ b/Nos/Views/Modifiers/LinearGradient+Planetary.swift @@ -3,19 +3,25 @@ import SwiftUI extension LinearGradient { public static let horizontalAccent = LinearGradient( - colors: [ Color.actionPrimaryGradientTop, Color.actionPrimaryGradientBottom], + colors: [.actionPrimaryGradientTop, .actionPrimaryGradientBottom], + startPoint: .leading, + endPoint: .trailing + ) + + public static let horizontalAccentReversed = LinearGradient( + colors: [.actionPrimaryGradientBottom, .actionPrimaryGradientTop], startPoint: .leading, endPoint: .trailing ) public static let diagonalAccent = LinearGradient( - colors: [ Color.actionPrimaryGradientTop, Color.actionPrimaryGradientBottom], + colors: [.actionPrimaryGradientTop, .actionPrimaryGradientBottom], startPoint: .topLeading, endPoint: .bottomTrailing ) public static let diagonalAccent2 = LinearGradient( - colors: [ Color.actionPrimaryGradientTop, Color.actionPrimaryGradientBottom], + colors: [.actionPrimaryGradientTop, .actionPrimaryGradientBottom], startPoint: .bottomLeading, endPoint: .topTrailing ) @@ -39,7 +45,7 @@ extension LinearGradient { ) public static let cardGradient = LinearGradient( - colors: [Color.cardBgTop, Color.cardBgBottom], + colors: [.cardBgTop, .cardBgBottom], startPoint: .top, endPoint: .bottom ) @@ -51,19 +57,19 @@ extension LinearGradient { ) public static let gold = LinearGradient( - colors: [Color.goldBgGradientTop, Color.goldBgGradientBottom], + colors: [.goldBgGradientTop, .goldBgGradientBottom], startPoint: .top, endPoint: .bottom ) public static let nip05 = LinearGradient( - colors: [Color.nip05BgGradientTop, Color.nip05BgGradientBottom], + colors: [.nip05BgGradientTop, .nip05BgGradientBottom], startPoint: .top, endPoint: .bottom ) public static let bio = LinearGradient( - colors: [Color.bioBgGradientTop, Color.bioBgGradientBottom], + colors: [.bioBgGradientTop, .bioBgGradientBottom], startPoint: .top, endPoint: .bottom ) From d71a114a9ec571eedcb4621505c9a82d2d7d6df4 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Fri, 11 Oct 2024 17:25:28 -0400 Subject: [PATCH 2/3] update CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 691f42f0c..9d7388d35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Release Notes +- Added a tip to the Feed to welcome first-time users and explain how the Feed works. [#1602](https://github.com/planetary-social/nos/issues/1602) + ## [0.2.2] - 2024-10-11Z ### Release Notes From 32779c9b3cd1c9da00fb266788cb9272c45d3e0c Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Tue, 15 Oct 2024 18:05:08 -0400 Subject: [PATCH 3/3] add readability padding to tip view --- Nos/Views/Home/HomeFeedView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Nos/Views/Home/HomeFeedView.swift b/Nos/Views/Home/HomeFeedView.swift index 3768adf2c..1a435739f 100644 --- a/Nos/Views/Home/HomeFeedView.swift +++ b/Nos/Views/Home/HomeFeedView.swift @@ -71,6 +71,7 @@ struct HomeFeedView: View { TipView(welcomeTip) .padding(.top, 20) .padding(.horizontal, 16) + .readabilityPadding() .tipBackground(LinearGradient.horizontalAccentReversed) .tipViewStyle(.inline)