diff --git a/Nos/Models/CoreData/Author+CoreDataClass.swift b/Nos/Models/CoreData/Author+CoreDataClass.swift index a18e9afc6..f1aac4d4c 100644 --- a/Nos/Models/CoreData/Author+CoreDataClass.swift +++ b/Nos/Models/CoreData/Author+CoreDataClass.swift @@ -366,7 +366,6 @@ import Logger func add(relay: Relay) { relays.insert(relay) - print("Adding \(relay.address ?? "") to \(hexadecimalPublicKey ?? "")") } @MainActor func mute(viewContext context: NSManagedObjectContext) async throws { diff --git a/Nos/Models/CoreData/Event+CoreDataClass.swift b/Nos/Models/CoreData/Event+CoreDataClass.swift index 0cb63a628..ec4ae7821 100644 --- a/Nos/Models/CoreData/Event+CoreDataClass.swift +++ b/Nos/Models/CoreData/Event+CoreDataClass.swift @@ -365,8 +365,17 @@ public class Event: NosManagedObject, VerifiableEvent { } @nonobjc public class func event(by identifier: String, seenOn relay: Relay) -> NSFetchRequest { + guard let relayAddress = relay.address else { + return Event.emptyRequest() + } + let fetchRequest = NSFetchRequest(entityName: "Event") - fetchRequest.predicate = NSPredicate(format: "identifier = %@ AND ANY seenOnRelays = %@", identifier, relay) + fetchRequest.predicate = NSPredicate( + format: "identifier = %@ AND ANY seenOnRelays.address = %@", + identifier, + relayAddress + ) + fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Relay.createdAt, ascending: true)] fetchRequest.fetchLimit = 1 return fetchRequest } diff --git a/Nos/Models/CoreData/Relay+CoreDataClass.swift b/Nos/Models/CoreData/Relay+CoreDataClass.swift index adb9dca53..e795ed9a2 100644 --- a/Nos/Models/CoreData/Relay+CoreDataClass.swift +++ b/Nos/Models/CoreData/Relay+CoreDataClass.swift @@ -57,6 +57,7 @@ public class Relay: NosManagedObject { @nonobjc public class func relay(by address: String) -> NSFetchRequest { let fetchRequest = NSFetchRequest(entityName: "Relay") fetchRequest.predicate = NSPredicate(format: "address = %@", address) + fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Relay.createdAt, ascending: true)] fetchRequest.fetchLimit = 1 return fetchRequest } @@ -116,7 +117,7 @@ public class Relay: NosManagedObject { metadataFetchedAt = Date.now } - convenience init(context: NSManagedObjectContext, address: String, author: Author? = nil) throws { + private convenience init(context: NSManagedObjectContext, address: String, author: Author? = nil) throws { guard let addressURL = URL(string: address), addressURL.scheme == "wss" else { throw RelayError.invalidAddress diff --git a/Nos/Service/CurrentUser.swift b/Nos/Service/CurrentUser.swift index 7e0c80b92..d90318828 100644 --- a/Nos/Service/CurrentUser.swift +++ b/Nos/Service/CurrentUser.swift @@ -154,11 +154,8 @@ enum CurrentUserError: Error { // Recommended Relays for new user for address in Relay.recommended { - _ = try? Relay( - context: viewContext, - address: address, - author: author - ) + let relay = try? Relay.findOrCreate(by: address, context: viewContext) + relay?.addToAuthors(author) } try viewContext.save() diff --git a/Nos/Views/Discover/DiscoverTab.swift b/Nos/Views/Discover/DiscoverTab.swift index f4e3aa777..aa484332a 100644 --- a/Nos/Views/Discover/DiscoverTab.swift +++ b/Nos/Views/Discover/DiscoverTab.swift @@ -130,7 +130,8 @@ struct DiscoverTab_Previews: PreviewProvider { static func createRelayData(in context: NSManagedObjectContext, user: Author) { let addresses = ["wss://nostr.band", "wss://nos.social", "wss://a.long.domain.name.to.see.what.happens"] addresses.forEach { - _ = try? Relay(context: previewContext, address: $0, author: user) + let relay = try? Relay.findOrCreate(by: $0, context: previewContext) + relay?.addToAuthors(user) } try? previewContext.save() diff --git a/Nos/Views/Onboarding/OnboardingLoginView.swift b/Nos/Views/Onboarding/OnboardingLoginView.swift index 6eddcefbf..b82a18fbb 100644 --- a/Nos/Views/Onboarding/OnboardingLoginView.swift +++ b/Nos/Views/Onboarding/OnboardingLoginView.swift @@ -19,11 +19,7 @@ struct OnboardingLoginView: View { for address in Relay.allKnown { do { - let relay = try Relay( - context: viewContext, - address: address, - author: currentUser.author - ) + let relay = try Relay.findOrCreate(by: address, context: viewContext) currentUser.onboardingRelays.append(relay) } catch { Log.error(error.localizedDescription) diff --git a/Nos/Views/RelayDetailView.swift b/Nos/Views/RelayDetailView.swift index 2834a7b83..6bfb3b223 100644 --- a/Nos/Views/RelayDetailView.swift +++ b/Nos/Views/RelayDetailView.swift @@ -82,7 +82,7 @@ struct RelayDetailView_Previews: PreviewProvider { static var previewContext = PersistenceController.preview.container.viewContext static var relay: Relay { do { - return try Relay(context: previewContext, address: "wss://example.com") + return try Relay.findOrCreate(by: "wss://example.com", context: previewContext) } catch { return Relay(context: previewContext) } diff --git a/Nos/Views/RelayPicker.swift b/Nos/Views/RelayPicker.swift index 1ea36f53f..421550065 100644 --- a/Nos/Views/RelayPicker.swift +++ b/Nos/Views/RelayPicker.swift @@ -116,9 +116,10 @@ struct RelayPicker_Previews: PreviewProvider { static func createTestData(in context: NSManagedObjectContext, user: Author) { let addresses = ["wss://nostr.com", "wss://nos.social", "wss://alongdomainnametoseewhathappens.com"] - addresses.forEach { + addresses.forEach { address in do { - _ = try Relay(context: previewContext, address: $0, author: user) + let relay = try? Relay.findOrCreate(by: address, context: previewContext) + relay?.addToAuthors(user) } catch { print(error) } diff --git a/NosTests/Model/EventTests.swift b/NosTests/Model/EventTests.swift index f2a2a84ea..f81f428c6 100644 --- a/NosTests/Model/EventTests.swift +++ b/NosTests/Model/EventTests.swift @@ -408,6 +408,52 @@ final class EventTests: CoreDataTestCase { XCTAssertEqual(testEvent.repostedNote()?.identifier, nil) } + + // MARK: - Fetch requests + + func test_eventByIdentifierSeenOnRelay_givenAlreadySeen() throws { + // Arrange + let eventID = "foo" + let event = try Event.findOrCreateStubBy(id: eventID, context: testContext) + let relay = try Relay.findOrCreate(by: "wss://relay.nos.social", context: testContext) + event.addToSeenOnRelays(relay) + try testContext.saveIfNeeded() + + // Act + let events = try testContext.fetch(Event.event(by: eventID, seenOn: relay)) + + // Assert + XCTAssertEqual(events.count, 1) + XCTAssertEqual(events.first, event) + } + + func test_eventByIdentifierSeenOnRelay_givenNotSeen() throws { + // Arrange + let eventID = "foo" + let event = try Event.findOrCreateStubBy(id: eventID, context: testContext) + let relay = try Relay.findOrCreate(by: "wss://relay.nos.social", context: testContext) + + // Act + let events = try testContext.fetch(Event.event(by: eventID, seenOn: relay)) + + // Assert + XCTAssertEqual(events.count, 0) + } + + func test_eventByIdentifierSeenOnRelay_givenSeenOnAnother() throws { + // Arrange + let eventID = "foo" + let event = try Event.findOrCreateStubBy(id: eventID, context: testContext) + let relayOne = try Relay.findOrCreate(by: "wss://relay.nos.social", context: testContext) + event.addToSeenOnRelays(relayOne) + let relayTwo = try Relay.findOrCreate(by: "wss://other.relay.com", context: testContext) + + // Act + let events = try testContext.fetch(Event.event(by: eventID, seenOn: relayTwo)) + + // Assert + XCTAssertEqual(events.count, 0) + } // MARK: - Helpers