Highlights
This patch adds highlights (NIP-84) to Damus. Kind 9802 are handled by all the necessary models. We show highlighted events, longform events, and url references. Url references also leverage text fragments to take the user to the highlighted text. Testing —— iPhone 15 Pro Max (17.0) Dark Mode: https://v.nostr.build/oM6DW.mp4 iPhone 15 Pro Max (17.0) Light Mode: https://v.nostr.build/BRrmP.mp4 iPhone SE (3rd generation) (16.4) Light Mode: https://v.nostr.build/6GzKa.mp4 —— Closes: https://github.com/damus-io/damus/issues/2172 Closes: https://github.com/damus-io/damus/issues/1772 Closes: https://github.com/damus-io/damus/issues/1773 Closes: https://github.com/damus-io/damus/issues/2173 Closes: https://github.com/damus-io/damus/issues/2175 Changelog-Added: Highlights (NIP-84) PATCH CHANGELOG: V1 -> V2: addressed review comments highlights are now truncated and highlight label shown in Thread view V2 -> V3: handle case where highlight context is smaller than the highlight content Signed-off-by: ericholguin <ericholguin@apache.org>
This commit is contained in:
committed by
Daniel D’Aquino
parent
23c3130a82
commit
6376c61bad
@@ -406,6 +406,11 @@
|
|||||||
5C7389B12B6EFA7100781E0A /* ProxyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B02B6EFA7100781E0A /* ProxyView.swift */; };
|
5C7389B12B6EFA7100781E0A /* ProxyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B02B6EFA7100781E0A /* ProxyView.swift */; };
|
||||||
5C7389B72B9E692E00781E0A /* MutinyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B62B9E692E00781E0A /* MutinyButton.swift */; };
|
5C7389B72B9E692E00781E0A /* MutinyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B62B9E692E00781E0A /* MutinyButton.swift */; };
|
||||||
5C7389B92B9E69ED00781E0A /* MutinyGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B82B9E69ED00781E0A /* MutinyGradient.swift */; };
|
5C7389B92B9E69ED00781E0A /* MutinyGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B82B9E69ED00781E0A /* MutinyGradient.swift */; };
|
||||||
|
5CC8529D2BD741CD0039FFC5 /* HighlightEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC8529C2BD741CD0039FFC5 /* HighlightEvent.swift */; };
|
||||||
|
5CC8529F2BD744F60039FFC5 /* HighlightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC8529E2BD744F60039FFC5 /* HighlightView.swift */; };
|
||||||
|
5CC852A22BDED9B90039FFC5 /* HighlightDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC852A12BDED9B90039FFC5 /* HighlightDescription.swift */; };
|
||||||
|
5CC852A42BDF3CA10039FFC5 /* HighlightLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC852A32BDF3CA10039FFC5 /* HighlightLink.swift */; };
|
||||||
|
5CC852A62BE00F180039FFC5 /* HighlightEventRef.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC852A52BE00F180039FFC5 /* HighlightEventRef.swift */; };
|
||||||
5CC868DD2AA29B3200FB22BA /* NeutralButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC868DC2AA29B3200FB22BA /* NeutralButtonStyle.swift */; };
|
5CC868DD2AA29B3200FB22BA /* NeutralButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC868DC2AA29B3200FB22BA /* NeutralButtonStyle.swift */; };
|
||||||
5CF2DCCC2AA3AF0B00984B8D /* RelayPicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF2DCCB2AA3AF0B00984B8D /* RelayPicView.swift */; };
|
5CF2DCCC2AA3AF0B00984B8D /* RelayPicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF2DCCB2AA3AF0B00984B8D /* RelayPicView.swift */; };
|
||||||
5CF2DCCE2AABE1A500984B8D /* DamusLightGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF2DCCD2AABE1A500984B8D /* DamusLightGradient.swift */; };
|
5CF2DCCE2AABE1A500984B8D /* DamusLightGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF2DCCD2AABE1A500984B8D /* DamusLightGradient.swift */; };
|
||||||
@@ -1341,6 +1346,11 @@
|
|||||||
5C7389B02B6EFA7100781E0A /* ProxyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyView.swift; sourceTree = "<group>"; };
|
5C7389B02B6EFA7100781E0A /* ProxyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyView.swift; sourceTree = "<group>"; };
|
||||||
5C7389B62B9E692E00781E0A /* MutinyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutinyButton.swift; sourceTree = "<group>"; };
|
5C7389B62B9E692E00781E0A /* MutinyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutinyButton.swift; sourceTree = "<group>"; };
|
||||||
5C7389B82B9E69ED00781E0A /* MutinyGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutinyGradient.swift; sourceTree = "<group>"; };
|
5C7389B82B9E69ED00781E0A /* MutinyGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutinyGradient.swift; sourceTree = "<group>"; };
|
||||||
|
5CC8529C2BD741CD0039FFC5 /* HighlightEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightEvent.swift; sourceTree = "<group>"; };
|
||||||
|
5CC8529E2BD744F60039FFC5 /* HighlightView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightView.swift; sourceTree = "<group>"; };
|
||||||
|
5CC852A12BDED9B90039FFC5 /* HighlightDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightDescription.swift; sourceTree = "<group>"; };
|
||||||
|
5CC852A32BDF3CA10039FFC5 /* HighlightLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightLink.swift; sourceTree = "<group>"; };
|
||||||
|
5CC852A52BE00F180039FFC5 /* HighlightEventRef.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightEventRef.swift; sourceTree = "<group>"; };
|
||||||
5CC868DC2AA29B3200FB22BA /* NeutralButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeutralButtonStyle.swift; sourceTree = "<group>"; };
|
5CC868DC2AA29B3200FB22BA /* NeutralButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeutralButtonStyle.swift; sourceTree = "<group>"; };
|
||||||
5CF2DCCB2AA3AF0B00984B8D /* RelayPicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayPicView.swift; sourceTree = "<group>"; };
|
5CF2DCCB2AA3AF0B00984B8D /* RelayPicView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayPicView.swift; sourceTree = "<group>"; };
|
||||||
5CF2DCCD2AABE1A500984B8D /* DamusLightGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusLightGradient.swift; sourceTree = "<group>"; };
|
5CF2DCCD2AABE1A500984B8D /* DamusLightGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusLightGradient.swift; sourceTree = "<group>"; };
|
||||||
@@ -1668,6 +1678,8 @@
|
|||||||
B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */,
|
B5C60C1F2B530D5100C5ECA7 /* MuteItem.swift */,
|
||||||
B533694D2B66D791008A805E /* MutelistManager.swift */,
|
B533694D2B66D791008A805E /* MutelistManager.swift */,
|
||||||
D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */,
|
D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */,
|
||||||
|
D7C28E3A2BBB4D0000EE459F /* VideoCache.swift */,
|
||||||
|
5CC8529C2BD741CD0039FFC5 /* HighlightEvent.swift */,
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2402,6 +2414,7 @@
|
|||||||
4CC7AAEE297F11B300430951 /* Events */ = {
|
4CC7AAEE297F11B300430951 /* Events */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
5CC852A02BDED9970039FFC5 /* Highlight */,
|
||||||
4CA927682A290F8F0098A105 /* Components */,
|
4CA927682A290F8F0098A105 /* Components */,
|
||||||
4CC7AAEF297F11C700430951 /* SelectedEventView.swift */,
|
4CC7AAEF297F11C700430951 /* SelectedEventView.swift */,
|
||||||
4CC7AAF5297F1A6A00430951 /* EventBody.swift */,
|
4CC7AAF5297F1A6A00430951 /* EventBody.swift */,
|
||||||
@@ -2699,6 +2712,17 @@
|
|||||||
path = Images;
|
path = Images;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
5CC852A02BDED9970039FFC5 /* Highlight */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5CC8529E2BD744F60039FFC5 /* HighlightView.swift */,
|
||||||
|
5CC852A12BDED9B90039FFC5 /* HighlightDescription.swift */,
|
||||||
|
5CC852A32BDF3CA10039FFC5 /* HighlightLink.swift */,
|
||||||
|
5CC852A52BE00F180039FFC5 /* HighlightEventRef.swift */,
|
||||||
|
);
|
||||||
|
path = Highlight;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
7C0F392D29B57C8F0039859C /* Extensions */ = {
|
7C0F392D29B57C8F0039859C /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -3183,6 +3207,7 @@
|
|||||||
4C32B94D2A9AD44700DC3548 /* Offset.swift in Sources */,
|
4C32B94D2A9AD44700DC3548 /* Offset.swift in Sources */,
|
||||||
4C633350283D40E500B1C9C3 /* HomeModel.swift in Sources */,
|
4C633350283D40E500B1C9C3 /* HomeModel.swift in Sources */,
|
||||||
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */,
|
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */,
|
||||||
|
5CC852A42BDF3CA10039FFC5 /* HighlightLink.swift in Sources */,
|
||||||
4C32B9552A9AD44700DC3548 /* ByteBuffer.swift in Sources */,
|
4C32B9552A9AD44700DC3548 /* ByteBuffer.swift in Sources */,
|
||||||
4C32B95B2A9AD44700DC3548 /* NativeObject.swift in Sources */,
|
4C32B95B2A9AD44700DC3548 /* NativeObject.swift in Sources */,
|
||||||
3AB72AB9298ECF30004BB58C /* Translator.swift in Sources */,
|
3AB72AB9298ECF30004BB58C /* Translator.swift in Sources */,
|
||||||
@@ -3322,6 +3347,7 @@
|
|||||||
4C3AC7A12835A81400E1F516 /* SetupView.swift in Sources */,
|
4C3AC7A12835A81400E1F516 /* SetupView.swift in Sources */,
|
||||||
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */,
|
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */,
|
||||||
4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */,
|
4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */,
|
||||||
|
5CC852A22BDED9B90039FFC5 /* HighlightDescription.swift in Sources */,
|
||||||
4C94D6432BA5AEFE00C26EFF /* QuoteRepostsView.swift in Sources */,
|
4C94D6432BA5AEFE00C26EFF /* QuoteRepostsView.swift in Sources */,
|
||||||
D7EDED332B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */,
|
D7EDED332B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */,
|
||||||
4CA352AE2A76C1AC003BB08B /* FollowedNotify.swift in Sources */,
|
4CA352AE2A76C1AC003BB08B /* FollowedNotify.swift in Sources */,
|
||||||
@@ -3365,7 +3391,9 @@
|
|||||||
4C2859602A12A2BE004746F7 /* SupporterBadge.swift in Sources */,
|
4C2859602A12A2BE004746F7 /* SupporterBadge.swift in Sources */,
|
||||||
4C1A9A2A29DDF54400516EAC /* DamusVideoPlayer.swift in Sources */,
|
4C1A9A2A29DDF54400516EAC /* DamusVideoPlayer.swift in Sources */,
|
||||||
4CA352A22A76AEC5003BB08B /* LikedNotify.swift in Sources */,
|
4CA352A22A76AEC5003BB08B /* LikedNotify.swift in Sources */,
|
||||||
|
5CC8529F2BD744F60039FFC5 /* HighlightView.swift in Sources */,
|
||||||
BA37598D2ABCCE500018D73B /* PhotoCaptureProcessor.swift in Sources */,
|
BA37598D2ABCCE500018D73B /* PhotoCaptureProcessor.swift in Sources */,
|
||||||
|
5CC8529D2BD741CD0039FFC5 /* HighlightEvent.swift in Sources */,
|
||||||
4C9146FD2A2A87C200DDEA40 /* wasm.c in Sources */,
|
4C9146FD2A2A87C200DDEA40 /* wasm.c in Sources */,
|
||||||
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
||||||
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
|
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
|
||||||
@@ -3477,6 +3505,7 @@
|
|||||||
B51C1CEB2B55A60A00E312A9 /* MuteDurationMenu.swift in Sources */,
|
B51C1CEB2B55A60A00E312A9 /* MuteDurationMenu.swift in Sources */,
|
||||||
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
|
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
|
||||||
4C32B9512A9AD44700DC3548 /* FlatbuffersErrors.swift in Sources */,
|
4C32B9512A9AD44700DC3548 /* FlatbuffersErrors.swift in Sources */,
|
||||||
|
5CC852A62BE00F180039FFC5 /* HighlightEventRef.swift in Sources */,
|
||||||
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */,
|
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */,
|
||||||
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
|
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
|
||||||
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
|
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0xF2",
|
||||||
|
"green" : "0xD8",
|
||||||
|
"red" : "0xF4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x45",
|
||||||
|
"green" : "0x17",
|
||||||
|
"red" : "0x47"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ class DamusColors {
|
|||||||
static let green = Color("DamusGreen")
|
static let green = Color("DamusGreen")
|
||||||
static let purple = Color("DamusPurple")
|
static let purple = Color("DamusPurple")
|
||||||
static let deepPurple = Color("DamusDeepPurple")
|
static let deepPurple = Color("DamusDeepPurple")
|
||||||
|
static let highlight = Color("DamusHighlight")
|
||||||
static let blue = Color("DamusBlue")
|
static let blue = Color("DamusBlue")
|
||||||
static let bitcoin = Color("Bitcoin")
|
static let bitcoin = Color("Bitcoin")
|
||||||
static let success = Color("DamusSuccessPrimary")
|
static let success = Color("DamusSuccessPrimary")
|
||||||
|
|||||||
34
damus/Models/HighlightEvent.swift
Normal file
34
damus/Models/HighlightEvent.swift
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// HighlightEvent.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by eric on 4/22/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct HighlightEvent {
|
||||||
|
let event: NostrEvent
|
||||||
|
|
||||||
|
var event_ref: String? = nil
|
||||||
|
var url_ref: URL? = nil
|
||||||
|
var context: String? = nil
|
||||||
|
|
||||||
|
static func parse(from ev: NostrEvent) -> HighlightEvent {
|
||||||
|
var highlight = HighlightEvent(event: ev)
|
||||||
|
|
||||||
|
for tag in ev.tags {
|
||||||
|
guard tag.count >= 2 else { continue }
|
||||||
|
switch tag[0].string() {
|
||||||
|
case "e": highlight.event_ref = tag[1].string()
|
||||||
|
case "a": highlight.event_ref = tag[1].string()
|
||||||
|
case "r": highlight.url_ref = URL(string: tag[1].string())
|
||||||
|
case "context": highlight.context = tag[1].string()
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return highlight
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -184,7 +184,7 @@ class HomeModel: ContactsDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch kind {
|
switch kind {
|
||||||
case .chat, .longform, .text:
|
case .chat, .longform, .text, .highlight:
|
||||||
handle_text_event(sub_id: sub_id, ev)
|
handle_text_event(sub_id: sub_id, ev)
|
||||||
case .contacts:
|
case .contacts:
|
||||||
handle_contact_event(sub_id: sub_id, relay_id: relay_id, ev: ev)
|
handle_contact_event(sub_id: sub_id, relay_id: relay_id, ev: ev)
|
||||||
@@ -586,7 +586,7 @@ class HomeModel: ContactsDelegate {
|
|||||||
func subscribe_to_home_filters(friends fs: [Pubkey]? = nil, relay_id: RelayURL? = nil) {
|
func subscribe_to_home_filters(friends fs: [Pubkey]? = nil, relay_id: RelayURL? = nil) {
|
||||||
// TODO: separate likes?
|
// TODO: separate likes?
|
||||||
var home_filter_kinds: [NostrKind] = [
|
var home_filter_kinds: [NostrKind] = [
|
||||||
.text, .longform, .boost
|
.text, .longform, .boost, .highlight
|
||||||
]
|
]
|
||||||
if !damus_state.settings.onlyzaps_mode {
|
if !damus_state.settings.onlyzaps_mode {
|
||||||
home_filter_kinds.append(.like)
|
home_filter_kinds.append(.like)
|
||||||
|
|||||||
@@ -60,11 +60,11 @@ class ProfileModel: ObservableObject, Equatable {
|
|||||||
damus.pool.unsubscribe(sub_id: sub_id)
|
damus.pool.unsubscribe(sub_id: sub_id)
|
||||||
damus.pool.unsubscribe(sub_id: prof_subid)
|
damus.pool.unsubscribe(sub_id: prof_subid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func subscribe() {
|
func subscribe() {
|
||||||
var text_filter = NostrFilter(kinds: [.text, .longform])
|
var text_filter = NostrFilter(kinds: [.text, .longform, .highlight])
|
||||||
var profile_filter = NostrFilter(kinds: [.contacts, .metadata, .boost])
|
var profile_filter = NostrFilter(kinds: [.contacts, .metadata, .boost])
|
||||||
|
|
||||||
profile_filter.authors = [pubkey]
|
profile_filter.authors = [pubkey]
|
||||||
|
|
||||||
text_filter.authors = [pubkey]
|
text_filter.authors = [pubkey]
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class SearchModel: ObservableObject {
|
|||||||
func subscribe() {
|
func subscribe() {
|
||||||
// since 1 month
|
// since 1 month
|
||||||
search.limit = self.limit
|
search.limit = self.limit
|
||||||
search.kinds = [.text, .like, .longform]
|
search.kinds = [.text, .like, .longform, .highlight]
|
||||||
|
|
||||||
//likes_filter.ids = ref_events.referenced_ids!
|
//likes_filter.ids = ref_events.referenced_ids!
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ enum NostrKind: UInt32, Codable {
|
|||||||
case longform = 30023
|
case longform = 30023
|
||||||
case zap = 9735
|
case zap = 9735
|
||||||
case zap_request = 9734
|
case zap_request = 9734
|
||||||
|
case highlight = 9802
|
||||||
case nwc_request = 23194
|
case nwc_request = 23194
|
||||||
case nwc_response = 23195
|
case nwc_response = 23195
|
||||||
case http_auth = 27235
|
case http_auth = 27235
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ struct EventView: View {
|
|||||||
}
|
}
|
||||||
} else if event.known_kind == .longform {
|
} else if event.known_kind == .longform {
|
||||||
LongformPreview(state: damus, ev: event, options: options)
|
LongformPreview(state: damus, ev: event, options: options)
|
||||||
|
} else if event.known_kind == .highlight {
|
||||||
|
HighlightView(state: damus, event: event, options: options)
|
||||||
} else {
|
} else {
|
||||||
TextEvent(damus: damus, event: event, pubkey: pubkey, options: options)
|
TextEvent(damus: damus, event: event, pubkey: pubkey, options: options)
|
||||||
//.padding([.top], 6)
|
//.padding([.top], 6)
|
||||||
|
|||||||
@@ -16,7 +16,15 @@ struct ReplyPart: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
if let reply_ref = event.thread_reply()?.reply {
|
if let reply_ref = event.thread_reply()?.reply {
|
||||||
ReplyDescription(event: event, replying_to: events.lookup(reply_ref.note_id), ndb: ndb)
|
let replying_to = events.lookup(reply_ref.note_id)
|
||||||
|
if event.known_kind != .highlight {
|
||||||
|
ReplyDescription(event: event, replying_to: replying_to, ndb: ndb)
|
||||||
|
} else if event.known_kind == .highlight {
|
||||||
|
HighlightDescription(event: event, highlighted_event: replying_to, ndb: ndb)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ struct EventBody: View {
|
|||||||
if !options.contains(.truncate_content) {
|
if !options.contains(.truncate_content) {
|
||||||
note_content
|
note_content
|
||||||
}
|
}
|
||||||
|
} else if event.known_kind == .highlight {
|
||||||
|
HighlightBodyView(state: damus_state, ev: event, options: options)
|
||||||
} else {
|
} else {
|
||||||
note_content
|
note_content
|
||||||
}
|
}
|
||||||
|
|||||||
54
damus/Views/Events/Highlight/HighlightDescription.swift
Normal file
54
damus/Views/Events/Highlight/HighlightDescription.swift
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// HighlightDescription.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by eric on 4/28/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// Modified from Reply Description
|
||||||
|
struct HighlightDescription: View {
|
||||||
|
let event: NostrEvent
|
||||||
|
let highlighted_event: NostrEvent?
|
||||||
|
let ndb: Ndb
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
(Text(Image(systemName: "highlighter")) + Text(verbatim: " \(highlight_desc(ndb: ndb, event: event, highlighted_event: highlighted_event))"))
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HighlightDescription_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
HighlightDescription(event: test_note, highlighted_event: test_note, ndb: test_damus_state.ndb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func highlight_desc(ndb: Ndb, event: NostrEvent, highlighted_event: NostrEvent?, locale: Locale = Locale.current) -> String {
|
||||||
|
let desc = make_reply_description(event, replying_to: highlighted_event)
|
||||||
|
let pubkeys = desc.pubkeys
|
||||||
|
|
||||||
|
let bundle = bundleForLocale(locale: locale)
|
||||||
|
|
||||||
|
if desc.pubkeys.count == 0 {
|
||||||
|
return NSLocalizedString("Highlighted", bundle: bundle, comment: "Label to indicate that the user is highlighting their own post.")
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let profile_txn = NdbTxn(ndb: ndb) else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
let names: [String] = pubkeys.map { pk in
|
||||||
|
let prof = ndb.lookup_profile_with_txn(pk, txn: profile_txn)
|
||||||
|
|
||||||
|
return Profile.displayName(profile: prof?.profile, pubkey: pk).username.truncate(maxLength: 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
let uniqueNames = NSOrderedSet(array: names).array as! [String]
|
||||||
|
|
||||||
|
return String(format: NSLocalizedString("Highlighted %@", bundle: bundle, comment: "Label to indicate that the user is highlighting 1 user."), locale: locale, uniqueNames[0])
|
||||||
|
}
|
||||||
92
damus/Views/Events/Highlight/HighlightEventRef.swift
Normal file
92
damus/Views/Events/Highlight/HighlightEventRef.swift
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
//
|
||||||
|
// HighlightEventRef.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by eric on 4/29/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
|
struct HighlightEventRef: View {
|
||||||
|
let damus_state: DamusState
|
||||||
|
let event_ref: NoteId
|
||||||
|
|
||||||
|
init(damus_state: DamusState, event_ref: NoteId) {
|
||||||
|
self.damus_state = damus_state
|
||||||
|
self.event_ref = event_ref
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FailedImage: View {
|
||||||
|
var body: some View {
|
||||||
|
Image("markdown")
|
||||||
|
.resizable()
|
||||||
|
.foregroundColor(DamusColors.neutral6)
|
||||||
|
.background(DamusColors.neutral3)
|
||||||
|
.frame(width: 35, height: 35)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
.overlay(RoundedRectangle(cornerRadius: 10).stroke(.gray.opacity(0.5), lineWidth: 0.5))
|
||||||
|
.scaledToFit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
EventLoaderView(damus_state: damus_state, event_id: event_ref) { event in
|
||||||
|
EventMutingContainerView(damus_state: damus_state, event: event) {
|
||||||
|
if event.known_kind == .longform {
|
||||||
|
HStack(alignment: .top, spacing: 10) {
|
||||||
|
let longform_event = LongformEvent.parse(from: event)
|
||||||
|
if let url = longform_event.image {
|
||||||
|
KFAnimatedImage(url)
|
||||||
|
.callbackQueue(.dispatch(.global(qos:.background)))
|
||||||
|
.backgroundDecode(true)
|
||||||
|
.imageContext(.note, disable_animation: true)
|
||||||
|
.image_fade(duration: 0.25)
|
||||||
|
.cancelOnDisappear(true)
|
||||||
|
.configure { view in
|
||||||
|
view.framePreloadCount = 3
|
||||||
|
}
|
||||||
|
.background {
|
||||||
|
FailedImage()
|
||||||
|
}
|
||||||
|
.frame(width: 35, height: 35)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
.overlay(RoundedRectangle(cornerRadius: 10).stroke(.gray.opacity(0.5), lineWidth: 0.5))
|
||||||
|
.scaledToFit()
|
||||||
|
} else {
|
||||||
|
FailedImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
|
Text(longform_event.title ?? "Untitled")
|
||||||
|
.font(.system(size: 14, weight: .bold))
|
||||||
|
.lineLimit(1)
|
||||||
|
|
||||||
|
let profile_txn = damus_state.profiles.lookup(id: longform_event.event.pubkey, txn_name: "highlight-profile")
|
||||||
|
let profile = profile_txn?.unsafeUnownedValue
|
||||||
|
|
||||||
|
if let display_name = profile?.display_name {
|
||||||
|
Text(display_name)
|
||||||
|
.font(.system(size: 12))
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
} else if let name = profile?.name {
|
||||||
|
Text(name)
|
||||||
|
.font(.system(size: 12))
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding([.leading, .vertical], 7)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.cornerRadius(10)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 10)
|
||||||
|
.stroke(DamusColors.neutral3, lineWidth: 2)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
101
damus/Views/Events/Highlight/HighlightLink.swift
Normal file
101
damus/Views/Events/Highlight/HighlightLink.swift
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
//
|
||||||
|
// HighlightLink.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by eric on 4/28/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
|
struct HighlightLink: View {
|
||||||
|
let state: DamusState
|
||||||
|
let url: URL
|
||||||
|
let content: String
|
||||||
|
@Environment(\.openURL) var openURL
|
||||||
|
|
||||||
|
func text_fragment_url() -> URL? {
|
||||||
|
let fragmentDirective = "#:~:"
|
||||||
|
let textDirective = "text="
|
||||||
|
let separator = ","
|
||||||
|
var text = ""
|
||||||
|
|
||||||
|
let components = content.components(separatedBy: " ")
|
||||||
|
if components.count <= 10 {
|
||||||
|
text = content
|
||||||
|
} else {
|
||||||
|
let textStart = Array(components.prefix(5)).joined(separator: " ")
|
||||||
|
let textEnd = Array(components.suffix(2)).joined(separator: " ")
|
||||||
|
text = textStart + separator + textEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
let url_with_fragments = url.absoluteString + fragmentDirective + textDirective + text
|
||||||
|
return URL(string: url_with_fragments)
|
||||||
|
}
|
||||||
|
|
||||||
|
func get_url_icon() -> URL? {
|
||||||
|
var icon = URL(string: url.absoluteString + "/favicon.ico")
|
||||||
|
if let url_host = url.host() {
|
||||||
|
icon = URL(string: "https://" + url_host + "/favicon.ico")
|
||||||
|
}
|
||||||
|
return icon
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button(action: {
|
||||||
|
openURL(text_fragment_url() ?? url)
|
||||||
|
}, label: {
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
if let url = get_url_icon() {
|
||||||
|
KFAnimatedImage(url)
|
||||||
|
.imageContext(.pfp, disable_animation: true)
|
||||||
|
.cancelOnDisappear(true)
|
||||||
|
.configure { view in
|
||||||
|
view.framePreloadCount = 3
|
||||||
|
}
|
||||||
|
.placeholder { _ in
|
||||||
|
Image("link")
|
||||||
|
.resizable()
|
||||||
|
.padding(5)
|
||||||
|
.foregroundColor(DamusColors.neutral6)
|
||||||
|
.background(DamusColors.adaptableWhite)
|
||||||
|
}
|
||||||
|
.frame(width: 35, height: 35)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
.scaledToFit()
|
||||||
|
} else {
|
||||||
|
Image("link")
|
||||||
|
.resizable()
|
||||||
|
.padding(5)
|
||||||
|
.foregroundColor(DamusColors.neutral6)
|
||||||
|
.background(DamusColors.adaptableWhite)
|
||||||
|
.frame(width: 35, height: 35)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(url.absoluteString)
|
||||||
|
.font(eventviewsize_to_font(.normal, font_size: state.settings.font_size))
|
||||||
|
.foregroundColor(DamusColors.adaptableBlack)
|
||||||
|
.truncationMode(.tail)
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
|
.padding([.leading, .vertical], 7)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.background(DamusColors.neutral3)
|
||||||
|
.cornerRadius(10)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 10)
|
||||||
|
.stroke(DamusColors.neutral3, lineWidth: 2)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HighlightLink_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let url = URL(string: "https://damus.io")!
|
||||||
|
VStack {
|
||||||
|
HighlightLink(state: test_damus_state, url: url, content: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
192
damus/Views/Events/Highlight/HighlightView.swift
Normal file
192
damus/Views/Events/Highlight/HighlightView.swift
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
//
|
||||||
|
// HighlightView.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by eric on 4/22/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
|
struct HighlightTruncatedText: View {
|
||||||
|
let attributedString: AttributedString
|
||||||
|
let maxChars: Int
|
||||||
|
|
||||||
|
init(attributedString: AttributedString, maxChars: Int = 360) {
|
||||||
|
self.attributedString = attributedString
|
||||||
|
self.maxChars = maxChars
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
|
||||||
|
let truncatedAttributedString: AttributedString? = attributedString.truncateOrNil(maxLength: maxChars)
|
||||||
|
|
||||||
|
if let truncatedAttributedString {
|
||||||
|
Text(truncatedAttributedString)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
} else {
|
||||||
|
Text(attributedString)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if truncatedAttributedString != nil {
|
||||||
|
Spacer()
|
||||||
|
Button(NSLocalizedString("Show more", comment: "Button to show entire note.")) { }
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HighlightBodyView: View {
|
||||||
|
let state: DamusState
|
||||||
|
let event: HighlightEvent
|
||||||
|
let options: EventViewOptions
|
||||||
|
|
||||||
|
init(state: DamusState, ev: HighlightEvent, options: EventViewOptions) {
|
||||||
|
self.state = state
|
||||||
|
self.event = ev
|
||||||
|
self.options = options
|
||||||
|
}
|
||||||
|
|
||||||
|
init(state: DamusState, ev: NostrEvent, options: EventViewOptions) {
|
||||||
|
self.state = state
|
||||||
|
self.event = HighlightEvent.parse(from: ev)
|
||||||
|
self.options = options
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Group {
|
||||||
|
if options.contains(.wide) {
|
||||||
|
Main.padding(.horizontal)
|
||||||
|
} else {
|
||||||
|
Main
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var truncate: Bool {
|
||||||
|
return options.contains(.truncate_content)
|
||||||
|
}
|
||||||
|
|
||||||
|
var truncate_very_short: Bool {
|
||||||
|
return options.contains(.truncate_content_very_short)
|
||||||
|
}
|
||||||
|
|
||||||
|
func truncatedText(attributedString: AttributedString) -> some View {
|
||||||
|
Group {
|
||||||
|
if truncate_very_short {
|
||||||
|
HighlightTruncatedText(attributedString: attributedString, maxChars: 140)
|
||||||
|
.font(eventviewsize_to_font(.normal, font_size: state.settings.font_size))
|
||||||
|
}
|
||||||
|
else if truncate {
|
||||||
|
HighlightTruncatedText(attributedString: attributedString)
|
||||||
|
.font(eventviewsize_to_font(.normal, font_size: state.settings.font_size))
|
||||||
|
} else {
|
||||||
|
Text(attributedString)
|
||||||
|
.font(eventviewsize_to_font(.normal, font_size: state.settings.font_size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var Main: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
|
HStack {
|
||||||
|
var attributedString: AttributedString {
|
||||||
|
var attributedString: AttributedString = ""
|
||||||
|
if let context = event.context {
|
||||||
|
if context.count < event.event.content.count {
|
||||||
|
attributedString = AttributedString(event.event.content)
|
||||||
|
} else {
|
||||||
|
attributedString = AttributedString(context)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attributedString = AttributedString(event.event.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let range = attributedString.range(of: event.event.content) {
|
||||||
|
attributedString[range].backgroundColor = DamusColors.highlight
|
||||||
|
}
|
||||||
|
return attributedString
|
||||||
|
}
|
||||||
|
|
||||||
|
truncatedText(attributedString: attributedString)
|
||||||
|
.lineSpacing(5)
|
||||||
|
.padding(10)
|
||||||
|
}
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 25).fill(DamusColors.highlight).frame(width: 4),
|
||||||
|
alignment: .leading
|
||||||
|
)
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
|
||||||
|
if let url = event.url_ref {
|
||||||
|
HighlightLink(state: state, url: url, content: event.event.content)
|
||||||
|
} else {
|
||||||
|
if let evRef = event.event_ref {
|
||||||
|
if let eventHex = hex_decode_id(evRef) {
|
||||||
|
HighlightEventRef(damus_state: state, event_ref: NoteId(eventHex))
|
||||||
|
.padding(.top, 5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HighlightView: View {
|
||||||
|
let state: DamusState
|
||||||
|
let event: HighlightEvent
|
||||||
|
let options: EventViewOptions
|
||||||
|
|
||||||
|
init(state: DamusState, event: NostrEvent, options: EventViewOptions) {
|
||||||
|
self.state = state
|
||||||
|
self.event = HighlightEvent.parse(from: event)
|
||||||
|
self.options = options.union(.no_mentions)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
EventShell(state: state, event: event.event, options: options) {
|
||||||
|
HighlightBodyView(state: state, ev: event, options: options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HighlightView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
|
||||||
|
let content = "Nostr, a decentralized and open social network protocol. Without ads, toxic algorithms, or censorship"
|
||||||
|
let context = "Damus is built on Nostr, a decentralized and open social network protocol. Without ads, toxic algorithms, or censorship, Damus gives you access to the social network that a truly free and healthy society needs — and deserves."
|
||||||
|
|
||||||
|
let test_highlight_event = HighlightEvent.parse(from: NostrEvent(
|
||||||
|
content: content,
|
||||||
|
keypair: test_keypair,
|
||||||
|
kind: NostrKind.highlight.rawValue,
|
||||||
|
tags: [
|
||||||
|
["context", context],
|
||||||
|
["r", "https://damus.io"],
|
||||||
|
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"],
|
||||||
|
])!
|
||||||
|
)
|
||||||
|
|
||||||
|
let test_highlight_event2 = HighlightEvent.parse(from: NostrEvent(
|
||||||
|
content: content,
|
||||||
|
keypair: test_keypair,
|
||||||
|
kind: NostrKind.highlight.rawValue,
|
||||||
|
tags: [
|
||||||
|
["context", context],
|
||||||
|
["e", "36017b098859d62e1dbd802290d59c9de9f18bb0ca00ba4b875c2930dd5891ae"],
|
||||||
|
["p", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"],
|
||||||
|
])!
|
||||||
|
)
|
||||||
|
VStack {
|
||||||
|
HighlightView(state: test_damus_state, event: test_highlight_event.event, options: [])
|
||||||
|
|
||||||
|
HighlightView(state: test_damus_state, event: test_highlight_event2.event, options: [.wide])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,7 +41,16 @@ struct SelectedEventView: View {
|
|||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
|
||||||
if let reply_ref = event.thread_reply()?.reply {
|
if let reply_ref = event.thread_reply()?.reply {
|
||||||
ReplyDescription(event: event, replying_to: damus.events.lookup(reply_ref.note_id), ndb: damus.ndb)
|
let replying_to = damus.events.lookup(reply_ref.note_id)
|
||||||
|
if event.known_kind == .highlight {
|
||||||
|
HighlightDescription(event: event, highlighted_event: replying_to, ndb: damus.ndb)
|
||||||
|
.padding(.horizontal)
|
||||||
|
} else {
|
||||||
|
ReplyDescription(event: event, replying_to: replying_to, ndb: damus.ndb)
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
} else if event.known_kind == .highlight {
|
||||||
|
HighlightDescription(event: event, highlighted_event: nil, ndb: damus.ndb)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ extension NdbNote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func get_cached_inner_event(cache: EventCache) -> NdbNote? {
|
func get_cached_inner_event(cache: EventCache) -> NdbNote? {
|
||||||
guard self.known_kind == .boost else {
|
guard self.known_kind == .boost || self.known_kind == .highlight else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ class NdbNote: Encodable, Equatable, Hashable {
|
|||||||
// Extension to make NdbNote compatible with NostrEvent's original API
|
// Extension to make NdbNote compatible with NostrEvent's original API
|
||||||
extension NdbNote {
|
extension NdbNote {
|
||||||
var is_textlike: Bool {
|
var is_textlike: Bool {
|
||||||
return kind == 1 || kind == 42 || kind == 30023
|
return kind == 1 || kind == 42 || kind == 30023 || kind == 9802
|
||||||
}
|
}
|
||||||
|
|
||||||
var is_quote_repost: NoteId? {
|
var is_quote_repost: NoteId? {
|
||||||
|
|||||||
Reference in New Issue
Block a user