-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathnostr-sub.ts
149 lines (138 loc) · 3.77 KB
/
nostr-sub.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import * as nip19 from 'nostr-tools/nip19'
import ndk from './ndk.ts'
import {NDKSubscription, NDKEvent} from '@nostr-dev-kit/ndk'
type HTMXInternalAPI = {
getInternalData(elt: HTMLElement): HTMXElementData
getAttributeValue(elt: Element, attr: string): string | undefined
withExtensions(elt: HTMLElement, fn: (ext: any) => void): void
makeSettleInfo(elt: HTMLElement): SettleInfo
makeFragment(s: string): HTMLElement
oobSwap(arg: string, elt: Element, si: SettleInfo): void
settleImmediately(tasks: any[]): void
}
type SettleInfo = {
tasks: any[]
}
type HTMXElementData = {
sub: NDKSubscription
}
let element: HTMLElement
let api: HTMXInternalAPI
// @ts-ignore
const htmx: any = window.htmx
htmx.defineExtension('nostr-sub', {
init(apiRef: HTMXInternalAPI) {
api = apiRef
},
onEvent(name: string, evt: CustomEvent) {
switch (name) {
case 'htmx:trigger': {
subscribe(evt.target as HTMLElement)
break
}
case 'htmx:beforeCleanupElement': {
console.log('canceling subscription')
const data = api.getInternalData(element)
data.sub.stop()
break
}
}
}
})
function subscribe(element: HTMLElement) {
const data = api.getInternalData(element)
let filter: any = {}
try {
filter = JSON.parse(api.getAttributeValue(element, 'nostr-filter') || '{}')
} catch (err) {
filter = {}
}
if (element instanceof HTMLFormElement) {
;['since', 'until'].forEach(f => {
if (element[f]) {
filter[f] = parseInt(element[f].value, 10)
}
})
;['author', 'kind', 'id'].forEach(f => {
if (element[f]) {
const fs = f + 's'
const current = filter[fs] || []
const fieldValue = processValue(element[f].value)
if (fieldValue) {
current.push(fieldValue)
filter[fs] = current
}
}
})
;['authors', 'kinds', 'ids'].forEach(fs => {
if (element[fs]) {
const current = filter[fs] || []
const fieldValues = element[fs].value
.split(',')
.map(processValue)
.filter((v: any) => v)
if (fieldValues.length) {
current.push(...fieldValues)
filter[fs] = current
}
}
})
}
function processValue(v: string | null): string | null {
if (!v) return null
v = v.trim()
try {
const {type, data} = nip19.decode(v)
switch (type) {
case 'npub':
v = data as string
break
case 'nprofile':
v = (data as nip19.ProfilePointer).pubkey
break
case 'nevent':
v = (data as nip19.EventPointer).id
break
case 'note':
v = data as string
break
default:
v = null
break
}
} catch (err) {
/* -- */
}
if (v && v.match(/^[a-f0-9]{64}$/)) return v
return null
}
console.log('triggering subscription', filter)
data.sub = ndk.subscribe(filter, {closeOnEose: false})
data.sub.start()
data.sub.on(
'event',
(response: NDKEvent) => {
response.ndk = ndk
let raw: any = response.rawEvent()
raw.author = response.author
let html = JSON.stringify(raw)
api.withExtensions(element, function (extension) {
html = extension.transformResponse(html, null, element)
})
const settleInfo = api.makeSettleInfo(element)
const fragment = api.makeFragment(html)
if (fragment.children.length) {
const children = Array.from(fragment.children)
for (let i = 0; i < children.length; i++) {
api.oobSwap(
api.getAttributeValue(children[i], 'hx-swap-oob') || 'true',
children[i],
settleInfo
)
}
}
api.settleImmediately(settleInfo.tasks)
},
5000
)
}