diff --git a/Nos.xcodeproj/project.pbxproj b/Nos.xcodeproj/project.pbxproj index 369a9fad8..3258f155e 100644 --- a/Nos.xcodeproj/project.pbxproj +++ b/Nos.xcodeproj/project.pbxproj @@ -226,6 +226,8 @@ 50DE6B1B2C6B88FE0065665D /* View+StyledBorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DE6B1A2C6B88FE0065665D /* View+StyledBorder.swift */; }; 50E2EB722C86175900D4B360 /* NSRegularExpression+Replacement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E2EB712C86175900D4B360 /* NSRegularExpression+Replacement.swift */; }; 50E2EB7B2C8617C800D4B360 /* NSRegularExpression+Replacement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E2EB712C86175900D4B360 /* NSRegularExpression+Replacement.swift */; }; + 50EA86D42D28150F001E62CC /* FeedSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EA86D32D28150D001E62CC /* FeedSource.swift */; }; + 50EA86D52D28150F001E62CC /* FeedSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EA86D32D28150D001E62CC /* FeedSource.swift */; }; 50F695072C6392C4000E4C74 /* zap_receipt.json in Resources */ = {isa = PBXBuildFile; fileRef = 50F695062C6392C4000E4C74 /* zap_receipt.json */; }; 5B098DBC2BDAF6CB00500A1B /* NoteParserTests+NIP08.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B098DBB2BDAF6CB00500A1B /* NoteParserTests+NIP08.swift */; }; 5B098DC62BDAF73500500A1B /* AttributedString+Links.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B098DC52BDAF73500500A1B /* AttributedString+Links.swift */; }; @@ -791,6 +793,7 @@ 5095330A2C625B5D00E0BACA /* zap_request_no_amount.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = zap_request_no_amount.json; sourceTree = ""; }; 50DE6B1A2C6B88FE0065665D /* View+StyledBorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+StyledBorder.swift"; sourceTree = ""; }; 50E2EB712C86175900D4B360 /* NSRegularExpression+Replacement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+Replacement.swift"; sourceTree = ""; }; + 50EA86D32D28150D001E62CC /* FeedSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedSource.swift; sourceTree = ""; }; 50F695062C6392C4000E4C74 /* zap_receipt.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = zap_receipt.json; sourceTree = ""; }; 5B098DBB2BDAF6CB00500A1B /* NoteParserTests+NIP08.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NoteParserTests+NIP08.swift"; sourceTree = ""; }; 5B098DC52BDAF73500500A1B /* AttributedString+Links.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Links.swift"; sourceTree = ""; }; @@ -1966,6 +1969,7 @@ 03FE3F782C87A9D900D25810 /* EventError.swift */, 0365CD862C4016A200622A1A /* EventKind.swift */, C9EE3E622A053910008A7491 /* ExpirationTimeOption.swift */, + 50EA86D32D28150D001E62CC /* FeedSource.swift */, C93CA0C229AE3A1E00921183 /* JSONEvent.swift */, 50089A002C9712EF00834588 /* JSONEvent+Kinds.swift */, 5B503F612A291A1A0098805A /* JSONRelayMetadata.swift */, @@ -2417,6 +2421,7 @@ C9CE5B142A0172CF008E198C /* WebView.swift in Sources */, CD4908D429B92941007443DB /* ReportABugMailView.swift in Sources */, 0314D5AC2C7D31060002E7F4 /* MediaService.swift in Sources */, + 50EA86D52D28150F001E62CC /* FeedSource.swift in Sources */, 5022FBCF2D242C850012FF4B /* NosSegmentedPicker.swift in Sources */, 5B7C93B02B6AD52400410ABE /* CreateUsernameWizard.swift in Sources */, 030036852C5D39DD002C71F5 /* RefreshController.swift in Sources */, @@ -2722,6 +2727,7 @@ C9EE3E642A053910008A7491 /* ExpirationTimeOption.swift in Sources */, 504454712C90728E00251A7E /* Event+Fetching.swift in Sources */, 03FFCA7C2D07721100D6F0F1 /* AuthorListError.swift in Sources */, + 50EA86D42D28150F001E62CC /* FeedSource.swift in Sources */, 65D066AA2BD55E160011C5CD /* DirectMessageWrapper.swift in Sources */, C973AB5E2A323167002AED16 /* Event+CoreDataProperties.swift in Sources */, C9F64D8D29ED840700563F2B /* Zipper.swift in Sources */, diff --git a/Nos/Controller/FeedController.swift b/Nos/Controller/FeedController.swift index d3ea272ee..7e4c3b284 100644 --- a/Nos/Controller/FeedController.swift +++ b/Nos/Controller/FeedController.swift @@ -3,74 +3,6 @@ import CoreData import Dependencies import SwiftUI -/// The source to be used for a feed of notes. -enum FeedSource: RawRepresentable, Hashable, Equatable { - case following - case relay(String, String?) - case list(String, String?) - - var displayName: String { - switch self { - case .following: String(localized: "following") - case .relay(let name, _), .list(let name, _): name - } - } - - var description: String? { - switch self { - case .following: nil - case .relay(_, let description), .list(_, let description): description - } - } - - static func == (lhs: FeedSource, rhs: FeedSource) -> Bool { - switch (lhs, rhs) { - case (.following, .following): true - case (.relay(let name1, _), .relay(let name2, _)): name1 == name2 - case (.list(let name1, _), .list(let name2, _)): name1 == name2 - default: false - } - } - - // Note: RawRepresentable conformance is required for use of @AppStorage for persistence. - var rawValue: String { - switch self { - case .following: - "following" - case .relay(let host, let description): - "relay:|\(host):|\(description ?? "")" - case .list(let name, let description): - "list:|\(name):|\(description ?? "")" - } - } - - init?(rawValue: String) { - let components = rawValue.split(separator: ":|").map { String($0) } - guard let caseName = components.first else { - return nil - } - - switch caseName { - case "following": - self = .following - case "relay": - guard components.count >= 2 else { - return nil - } - let description = components.count >= 3 ? components[2] : "" - self = .relay(components[1], description) - case "list": - guard components.count >= 2 else { - return nil - } - let description = components.count >= 3 ? components[2] : "" - self = .list(components[1], description) - default: - return nil - } - } -} - @Observable @MainActor final class FeedController { @ObservationIgnored @Dependency(\.persistenceController) private var persistenceController @@ -190,7 +122,7 @@ enum FeedSource: RawRepresentable, Hashable, Equatable { var relayItems = [FeedToggleRow.Item]() for list in lists { - let source = FeedSource.list(list.title ?? "??", nil) + let source = FeedSource.list(name: list.title ?? "??", description: nil) if list.isFeedEnabled { enabledSources.append(source) @@ -200,7 +132,7 @@ enum FeedSource: RawRepresentable, Hashable, Equatable { } for relay in relays { - let source = FeedSource.relay(relay.host ?? "", relay.relayDescription) + let source = FeedSource.relay(host: relay.host ?? "", description: relay.relayDescription) if relay.isFeedEnabled { enabledSources.append(source) diff --git a/Nos/Models/FeedSource.swift b/Nos/Models/FeedSource.swift new file mode 100644 index 000000000..99cf74f50 --- /dev/null +++ b/Nos/Models/FeedSource.swift @@ -0,0 +1,67 @@ +/// The source to be used for a feed of notes. +enum FeedSource: RawRepresentable, Hashable, Equatable { + case following + case relay(host: String, description: String?) + case list(name: String, description: String?) + + var displayName: String { + switch self { + case .following: String(localized: "following") + case .relay(let name, _), .list(let name, _): name + } + } + + var description: String? { + switch self { + case .following: nil + case .relay(_, let description), .list(_, let description): description + } + } + + static func == (lhs: FeedSource, rhs: FeedSource) -> Bool { + switch (lhs, rhs) { + case (.following, .following): true + case (.relay(let name1, _), .relay(let name2, _)): name1 == name2 + case (.list(let name1, _), .list(let name2, _)): name1 == name2 + default: false + } + } + + // Note: RawRepresentable conformance is required for use of @AppStorage for persistence. + var rawValue: String { + switch self { + case .following: + "following" + case .relay(let host, let description): + "relay:|\(host):|\(description ?? "")" + case .list(let name, let description): + "list:|\(name):|\(description ?? "")" + } + } + + init?(rawValue: String) { + let components = rawValue.split(separator: ":|").map { String($0) } + guard let caseName = components.first else { + return nil + } + + switch caseName { + case "following": + self = .following + case "relay": + guard components.count >= 2 else { + return nil + } + let description = components.count >= 3 ? components[2] : "" + self = .relay(host: components[1], description: description) + case "list": + guard components.count >= 2 else { + return nil + } + let description = components.count >= 3 ? components[2] : "" + self = .list(name: components[1], description: description) + default: + return nil + } + } +} diff --git a/Nos/Service/CurrentUser.swift b/Nos/Service/CurrentUser.swift index aa20654c4..ceca6c364 100644 --- a/Nos/Service/CurrentUser.swift +++ b/Nos/Service/CurrentUser.swift @@ -200,7 +200,7 @@ import Dependencies let latestReceivedEvent = try? viewContext.fetch(Event.lastReceived(for: author)).first let importantEventsFilter = Filter( authorKeys: [key], - kinds: [.mute, .delete, .report, .contactList, .zapRequest], + kinds: [.mute, .delete, .report, .contactList, .zapRequest, .followSet], limit: 100, since: latestReceivedEvent?.receivedAt, keepSubscriptionOpen: true diff --git a/Nos/Views/Home/HomeFeedView.swift b/Nos/Views/Home/HomeFeedView.swift index a79e151ca..0facd3223 100644 --- a/Nos/Views/Home/HomeFeedView.swift +++ b/Nos/Views/Home/HomeFeedView.swift @@ -110,6 +110,7 @@ struct HomeFeedView: View { NewNotesButton(fetchRequest: FetchRequest(fetchRequest: newNotesRequest)) { refreshController.startRefresh = true } + .padding(.top, 44) if showTimedLoadingIndicator { FullscreenProgressView(