-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds support for NIP-28 #178
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the contribution! I left a few comments around supporting properties of each kind in a first-class way, and using a Builder
protocol instead of continuing with the soon-to-be-deprecated EventCreating
protocol.
Also, there seems to be a few SwiftLint errors that should be addressed.
} | ||
|
||
public extension EventCreating { | ||
func createChannelEvent(withContent content: String, signedBy keypair: Keypair) throws -> CreateChannelEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm in the process of deprecating the EventCreating
protocol actually, and moving towards a Builder
pattern. The former is too rigid for building Nostr events, whereas the latter gives us a lot of flexibility.
See https://github.com/nostr-sdk/nostr-sdk-ios/pull/175/files#diff-c87e105b65b1d7c9512acf2ee8c7e3fc7582648811025c959a37b36d97403b67R170 as an example.
Also, it would be better to have an API where the developer can specify the basic channel metadata (name, about, picture and relays) rather than needing to enter in raw JSON in content
.
https://github.com/nostr-protocol/nips/blob/master/28.md#kind-40-create-channel
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, @tyiu, thanks for your time and looking over my commit!
Yes, I am on board with the later. The interface in this PR needs further definition and polish.
I created this PR because I am using your SDK with my project (SwiftUI/Swift 5). Thanks for putting it together!
So far, I was able to implement and successfully field test Encrypted Message, Group Chat and Key Management features.
As far as, my PR goes I will clean it up soon for the linter. I am thinking doing it in parallel while I’m working on replies for my chat application.
I don't quite understand what you mean by ‘deprecating EventCreating protocol’. I do use it a lot but I did find it somewhat rigid.
I also think there needs to be some better way to manage subscriptions. I do have them on about 5 Views on my app. Yikes...! 🦟
Regards!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I say deprecating the EventCreating
protocol in favor of the Builder
protocol, I mean it would look something like this when creating this event:
try CreateChannelEvent.Builder()
.channelMetadata(channelMetadata, merging: rawChannelMetadata)
.build(signedBy: keypair)
Take a look at what we do here for kind 0, as an example:
nostr-sdk-ios/Sources/NostrSDK/Events/MetadataEvent.swift
Lines 161 to 192 in 8b40eb9
public extension MetadataEvent { | |
/// Builder of ``MetadataEvent``. | |
final class Builder: NostrEvent.Builder<MetadataEvent>, CustomEmojiBuilding { | |
public init() { | |
super.init(kind: .metadata) | |
} | |
/// Sets the user metadata by merging ``UserMetadata`` with a dictionary of raw metadata. | |
/// | |
/// - Parameters: | |
/// - userMetadata: The ``UserMetadata`` to set. | |
/// - rawUserMetadata: The dictionary of raw metadata to set that can contain fields unknown to any implemented NIPs. | |
/// | |
/// > Note: If `rawUserMetadata` has fields that conflict with `userMetadata`, `userMetadata` fields take precedence. | |
public final func userMetadata(_ userMetadata: UserMetadata, merging rawUserMetadata: [String: Any] = [:]) throws -> Self { | |
let userMetadataAsData = try JSONEncoder().encode(userMetadata) | |
let allUserMetadataAsData: Data | |
if rawUserMetadata.isEmpty { | |
allUserMetadataAsData = userMetadataAsData | |
} else { | |
var userMetadataAsDictionary = try JSONSerialization.jsonObject(with: userMetadataAsData, options: []) as? [String: Any] ?? [:] | |
userMetadataAsDictionary.merge(rawUserMetadata) { (current, _) in current } | |
allUserMetadataAsData = try JSONSerialization.data(withJSONObject: userMetadataAsDictionary, options: .sortedKeys) | |
} | |
let allUserMetadataAsString = String(decoding: allUserMetadataAsData, as: UTF8.self) | |
content(allUserMetadataAsString) | |
return self | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@masterial Turns out the SwiftLint errors were on the main branch, due to how CI pulls in the latest SwiftLint version and it had some breaking changes in what it validates. I just fixed the issue on the main branch so the lint errors should go away after you pull it into your branch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merged, thanks for the heads up, @tyiu!
I plan to address other issues you've highlighted very soon
} | ||
|
||
public extension EventCreating { | ||
func createChannelMessageEvent(withContent content: String, eventId: String, relayUrl: String, signedBy keypair: Keypair) throws -> CreateChannelMessageEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. Again, look at how TextNoteEvent's Builder handles NIP-10 marker event tags. Maybe you could refactor or at least follow what we do in that code.
NIP-28 says that root or reply event tags are supported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I will take care of this when I am working on replies in my app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI I'm working on NIP-17 - Private Direct Messages which requires threading as well, so I ended up refactoring the logic for building events with NIP-10 tags. It hasn't been merged yet -- still working on my branch but it'll be ready soon. Hope you haven't gotten to it yet, don't want to make your changes redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! Thank you so much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merged: #186
|
||
/// Create a public chat channel. | ||
/// See [NIP-28](https://github.com/nostr-protocol/nips/blob/master/28.md#kind-40-create-channel). | ||
public class CreateChannelEvent: NostrEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to expose the basic channel metadata (name, about, picture and relays) properties.
Take a look at https://github.com/nostr-sdk/nostr-sdk-ios/blob/main/Sources/NostrSDK/Events/MetadataEvent.swift as it also does JSON encoding and decoding for the kind 0 event.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I make a MetadataChannel class here or what?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a separate file, you could create a ChannelMetadata
struct with the properties. Then, you could create a ChannelMetadataInterpreting
protocol that decodes the JSON.
Kinds 40 CreateChannelEvent
and kind 41 SetChannelMetadataEvent
could extend from ChannelMetadataInterpreting
.
Something like this, it'll be pretty similar to what we do with kind 0:
public struct ChannelMetadata: Codable {
public let name: String?
public let about: String?
...
enum CodingKeys: String, CodingKey {
...
}
}
public protocol ChannelMetadataInterpreting: NostrEvent {}
public extension ChannelMetadataInterpreting {
public var channelMetadata: ChannelMetadata {
guard let data = content.data(using: .utf8) else {
return nil
}
return try? JSONDecoder().decode(ChannelMetadata.self, from: data)
}
}
|
||
/// User no longer wants to see a certain message. | ||
/// See [NIP-28](https://github.com/nostr-protocol/nips/blob/master/28.md#kind-43-hide-message). | ||
public class HideChannelMessageEvent: NostrEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here for the reason
metadata in the content field.
} | ||
|
||
public extension EventCreating { | ||
func hideChannelMessageEvent(withContent content: String, signedBy keypair: Keypair) throws -> HideChannelMessageEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
|
||
/// User no longer wants to see messages from another user. | ||
/// See [NIP-28](https://github.com/nostr-protocol/nips/blob/master/28.md#kind-44-mute-user). | ||
public class MuteChannelUserEvent: NostrEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here about reason
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, implementing this feature soon too.
} | ||
|
||
public extension EventCreating { | ||
func createMuteChannelUserEvent(withContent content: String, signedBy keypair: Keypair) throws -> MuteChannelUserEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
|
||
/// Update a channel's public metadata. | ||
/// See [NIP-28](https://github.com/nostr-protocol/nips/blob/master/28.md#kind-41-set-channel-metadata). | ||
public class SetChannelMetadataEvent: NostrEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here about exposing the basic metadata fields as properties on this class.
} | ||
|
||
public extension EventCreating { | ||
func setChannelMetadataEvent(withContent content: String, signedBy keypair: Keypair) throws -> SetChannelMetadataEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here about the builder pattern and exposing an API to set the metadata fields instead of the raw content field.
* Fix SwiftLint errors from latest SwiftLint release (nostr-sdk#180) * Fix SwiftLint errors from latest SwiftLint release * Fix GitHub action workflows to trigger on only pull_request * Fix GitHub workflows to also run on main branch (nostr-sdk#181) * Add Swift 6.0 to unit tests (nostr-sdk#179) --------- Co-authored-by: Terry Yiu <[email protected]>
NIP-28 Public Chat