Skip to content

Commit

Permalink
Add summary and image tags to NIP-52 calendar events
Browse files Browse the repository at this point in the history
  • Loading branch information
tyiu committed Aug 4, 2024
1 parent e18ebda commit ad38266
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

public protocol CalendarEventInterpreting: NostrEvent, CalendarEventParticipantInterpreting, HashtagInterpreting, ParameterizedReplaceableEvent, ReferenceTagInterpreting, TitleTagInterpreting {}
public protocol CalendarEventInterpreting: NostrEvent, CalendarEventParticipantInterpreting, HashtagInterpreting, ImageTagInterpreting, ParameterizedReplaceableEvent, ReferenceTagInterpreting, SummaryTagInterpreting, TitleTagInterpreting {}
public extension CalendarEventInterpreting {
/// The locations of the calendar event. e.g. address, GPS coordinates, meeting room name, link to video call.
var locations: [String] {
Expand Down
18 changes: 14 additions & 4 deletions Sources/NostrSDK/Events/Calendars/DateBasedCalendarEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public extension EventCreating {
/// - Parameters:
/// - identifier: A unique identifier for the calendar event. Can be reused in the future for replacing the calendar event. If an identifier is not provided, a ``UUID`` string is used.
/// - title: The title of the calendar event.
/// - summary: A brief summary of the calendar event.
/// - imageURL: A ``URL`` pointing to an image to be shown along with the title.
/// - description: A detailed description of the calendar event.
/// - startDate: An inclusive start date. Must be less than end, if it exists. If there are any components other than year, month,
/// - endDate: An exclusive end date. If omitted, the calendar event ends on the same date as start.
Expand All @@ -66,8 +68,8 @@ public extension EventCreating {
/// - Returns: The signed ``DateBasedCalendarEvent``.
///
/// See [NIP-52](https://github.com/nostr-protocol/nips/blob/master/52.md).
func dateBasedCalendarEvent(withIdentifier identifier: String = UUID().uuidString, title: String, description: String = "", startDate: TimeOmittedDate, endDate: TimeOmittedDate? = nil, locations: [String]? = nil, geohash: String? = nil, participants: [CalendarEventParticipant]? = nil, hashtags: [String]? = nil, references: [URL]? = nil, signedBy keypair: Keypair) throws -> DateBasedCalendarEvent {
func dateBasedCalendarEvent(withIdentifier identifier: String = UUID().uuidString, title: String, summary: String? = nil, imageURL: URL? = nil, description: String = "", startDate: TimeOmittedDate, endDate: TimeOmittedDate? = nil, locations: [String]? = nil, geohash: String? = nil, participants: [CalendarEventParticipant]? = nil, hashtags: [String]? = nil, references: [URL]? = nil, signedBy keypair: Keypair) throws -> DateBasedCalendarEvent {

var tags: [Tag] = []

// If the end date is omitted, the calendar event ends on the same date as the start date.
Expand All @@ -79,15 +81,23 @@ public extension EventCreating {

tags.append(Tag(name: "end", value: endDate.dateString))
}

// Re-arrange tags so that it's easier to read with the identifier and name appearing first in the list of tags,
// and the end date being placed next to the start date.
tags = [
Tag(name: .identifier, value: identifier),
Tag(name: .title, value: title),
Tag(name: "start", value: startDate.dateString)
] + tags


if let summary {
tags.append(Tag(name: .summary, value: summary))
}

if let imageURL {
tags.append(Tag(name: .image, value: imageURL.absoluteString))
}

if let locations, !locations.isEmpty {
tags += locations.map { Tag(name: "location", value: $0) }
}
Expand Down
18 changes: 14 additions & 4 deletions Sources/NostrSDK/Events/Calendars/TimeBasedCalendarEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public extension EventCreating {
/// - Parameters:
/// - identifier: A unique identifier for the calendar event. Can be reused in the future for replacing the calendar event. If an identifier is not provided, a ``UUID`` string is used.
/// - title: The title of the calendar event.
/// - summary: A brief summary of the calendar event.
/// - imageURL: A ``URL`` pointing to an image to be shown along with the title.
/// - description: A detailed description of the calendar event.
/// - startTimestamp: An inclusive start timestamp.
/// - endTimestamp: An exclusive end timestamp. If omitted, the calendar event ends instantaneously.
Expand All @@ -84,22 +86,30 @@ public extension EventCreating {
/// - Returns: The signed ``TimeBasedCalendarEvent``.
///
/// See [NIP-52](https://github.com/nostr-protocol/nips/blob/master/52.md).
func timeBasedCalendarEvent(withIdentifier identifier: String = UUID().uuidString, title: String, description: String = "", startTimestamp: Date, endTimestamp: Date? = nil, startTimeZone: TimeZone? = nil, endTimeZone: TimeZone? = nil, locations: [String]? = nil, geohash: String? = nil, participants: [CalendarEventParticipant]? = nil, hashtags: [String]? = nil, references: [URL]? = nil, signedBy keypair: Keypair) throws -> TimeBasedCalendarEvent {
func timeBasedCalendarEvent(withIdentifier identifier: String = UUID().uuidString, title: String, summary: String? = nil, imageURL: URL? = nil, description: String = "", startTimestamp: Date, endTimestamp: Date? = nil, startTimeZone: TimeZone? = nil, endTimeZone: TimeZone? = nil, locations: [String]? = nil, geohash: String? = nil, participants: [CalendarEventParticipant]? = nil, hashtags: [String]? = nil, references: [URL]? = nil, signedBy keypair: Keypair) throws -> TimeBasedCalendarEvent {

// If the end timestamp is omitted, the calendar event ends instantaneously.
if let endTimestamp {
// The start timestamp must occur before the end timestamp, if it exists.
guard startTimestamp < endTimestamp else {
throw EventCreatingError.invalidInput
}
}

var tags: [Tag] = [
Tag(name: .identifier, value: identifier),
Tag(name: .title, value: title),
Tag(name: "start", value: String(Int64(startTimestamp.timeIntervalSince1970)))
]


if let summary {
tags.append(Tag(name: .summary, value: summary))
}

if let imageURL {
tags.append(Tag(name: .image, value: imageURL.absoluteString))
}

if let endTimestamp {
tags.append(Tag(name: "end", value: String(Int64(endTimestamp.timeIntervalSince1970))))
}
Expand Down
15 changes: 1 addition & 14 deletions Sources/NostrSDK/Events/LongformContentEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Foundation
/// * MUST NOT support adding HTML to Markdown.
///
/// > Note: [NIP-23 Specification](https://github.com/nostr-protocol/nips/blob/master/23.md)
public final class LongformContentEvent: NostrEvent, HashtagInterpreting, ParameterizedReplaceableEvent, TitleTagInterpreting {
public final class LongformContentEvent: NostrEvent, HashtagInterpreting, ImageTagInterpreting, ParameterizedReplaceableEvent, SummaryTagInterpreting, TitleTagInterpreting {
public required init(from decoder: Decoder) throws {
try super.init(from: decoder)
}
Expand All @@ -36,19 +36,6 @@ public final class LongformContentEvent: NostrEvent, HashtagInterpreting, Parame
}
return Date(timeIntervalSince1970: unixSeconds)
}

/// A summary of the content.
var summary: String? {
firstValueForTagName(.summary)
}

/// A URL pointing to an image to be shown along with the title.
var imageURL: URL? {
guard let imageURLString = firstValueForTagName(.image) else {
return nil
}
return URL(string: imageURLString)
}
}

public extension EventCreating {
Expand Down
19 changes: 19 additions & 0 deletions Sources/NostrSDK/Events/Tags/ImageTagInterpreting.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// ImageTagInterpreting.swift
//
//
// Created by Terry Yiu on 8/4/24.
//

import Foundation

public protocol ImageTagInterpreting: NostrEvent {}
public extension ImageTagInterpreting {
/// The image of the event.
var imageURL: URL? {
guard let imageURLString = firstValueForTagName(.image) else {
return nil
}
return URL(string: imageURLString)
}
}
16 changes: 16 additions & 0 deletions Sources/NostrSDK/Events/Tags/SummaryTagInterpreting.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// SummaryTagInterpreting.swift
//
//
// Created by Terry Yiu on 8/4/24.
//

import Foundation

public protocol SummaryTagInterpreting: NostrEvent {}
public extension SummaryTagInterpreting {
/// The summary of the content.
var summary: String? {
firstValueForTagName(.summary)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ final class DateBasedCalendarEventTests: XCTestCase, EventCreating, EventVerifyi
func testCreateDateBasedCalendarEvent() throws {
let identifier = "nostrica-12345"
let title = "Nostrica"
let description = "First Nostr unconference"
let summary = "First Nostr unconference summary"
let imageString = "https://nostrsdk.com/image.png"
let description = "First Nostr unconference description"

let startDate = try XCTUnwrap(TimeOmittedDate(year: 2023, month: 3, day: 19))
let endDate = try XCTUnwrap(TimeOmittedDate(year: 2023, month: 3, day: 21))
Expand All @@ -34,6 +36,8 @@ final class DateBasedCalendarEventTests: XCTestCase, EventCreating, EventVerifyi
let dateBasedCalendarEvent = try dateBasedCalendarEvent(
withIdentifier: identifier,
title: title,
summary: summary,
imageURL: URL(string: imageString),
description: description,
startDate: startDate,
endDate: endDate,
Expand All @@ -47,6 +51,8 @@ final class DateBasedCalendarEventTests: XCTestCase, EventCreating, EventVerifyi

XCTAssertEqual(dateBasedCalendarEvent.identifier, identifier)
XCTAssertEqual(dateBasedCalendarEvent.title, title)
XCTAssertEqual(dateBasedCalendarEvent.summary, summary)
XCTAssertEqual(dateBasedCalendarEvent.imageURL?.absoluteString, imageString)
XCTAssertEqual(dateBasedCalendarEvent.content, description)
XCTAssertEqual(dateBasedCalendarEvent.startDate, startDate)
XCTAssertEqual(dateBasedCalendarEvent.endDate, endDate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ final class TimeBasedCalendarEventTests: XCTestCase, EventCreating, EventVerifyi
func testCreateTimeBasedCalendarEvent() throws {
let identifier = "flight-from-new-york-jfk-to-san-jose-costa-rica-sjo-12345"
let title = "Flight from New York (JFK) to San José, Costa Rica (SJO)"
let description = "Flight to Nostrica"
let summary = "Flight to Nostrica summary."
let imageString = "https://nostrsdk.com/image.png"
let description = "Flight to Nostrica description."

let startTimeZone = TimeZone(identifier: "America/New_York")
let startComponents = DateComponents(calendar: Calendar(identifier: .iso8601), timeZone: startTimeZone, year: 2023, month: 3, day: 17, hour: 8, minute: 15)
Expand All @@ -40,6 +42,8 @@ final class TimeBasedCalendarEventTests: XCTestCase, EventCreating, EventVerifyi
let timeBasedCalendarEvent = try timeBasedCalendarEvent(
withIdentifier: identifier,
title: title,
summary: summary,
imageURL: URL(string: imageString),
description: description,
startTimestamp: startTimestamp,
endTimestamp: endTimestamp,
Expand All @@ -55,6 +59,8 @@ final class TimeBasedCalendarEventTests: XCTestCase, EventCreating, EventVerifyi

XCTAssertEqual(timeBasedCalendarEvent.identifier, identifier)
XCTAssertEqual(timeBasedCalendarEvent.title, title)
XCTAssertEqual(timeBasedCalendarEvent.summary, summary)
XCTAssertEqual(timeBasedCalendarEvent.imageURL?.absoluteString, imageString)
XCTAssertEqual(timeBasedCalendarEvent.content, description)
XCTAssertEqual(timeBasedCalendarEvent.startTimestamp, startTimestamp)
XCTAssertEqual(timeBasedCalendarEvent.endTimestamp, endTimestamp)
Expand Down

0 comments on commit ad38266

Please sign in to comment.