window.nostr.js 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc.yaml +1 -1
- package/README.md +15 -0
- package/dist/index.html +1 -1
- package/dist/window.nostr.js +13 -5
- package/package.json +1 -1
- package/src/App.svelte +282 -137
- package/src/Spinner.svelte +3 -3
- package/src/app.css +1 -0
- package/tailwind.config.js +10 -1
package/src/App.svelte
CHANGED
|
@@ -25,14 +25,23 @@
|
|
|
25
25
|
import Spinner from './Spinner.svelte'
|
|
26
26
|
|
|
27
27
|
const mobileMode = mediaQueryStore('only screen and (max-width: 640px)')
|
|
28
|
+
const localStorageKeys = {
|
|
29
|
+
ORIGIN: 'wnj:origin',
|
|
30
|
+
CLIENT_SECRET: 'wnj:clientSecret',
|
|
31
|
+
Y_POS: 'wnj:ypos',
|
|
32
|
+
CALLBACK_TOKEN: 'wnj:callbackToken',
|
|
33
|
+
BUNKER_POINTER: 'wnj:bunkerPointer'
|
|
34
|
+
}
|
|
28
35
|
|
|
29
36
|
let myself: HTMLDivElement
|
|
30
37
|
export let accent: string
|
|
31
38
|
export let position: 'top' | 'bottom' = 'top'
|
|
32
39
|
$: origin = $mobileMode
|
|
33
40
|
? 'bottom'
|
|
34
|
-
: (localStorage.getItem(
|
|
35
|
-
|
|
41
|
+
: (localStorage.getItem(localStorageKeys.ORIGIN) as
|
|
42
|
+
| 'top'
|
|
43
|
+
| 'bottom'
|
|
44
|
+
| null) || position
|
|
36
45
|
|
|
37
46
|
const win = window as any
|
|
38
47
|
const pool = new SimplePool()
|
|
@@ -42,23 +51,32 @@
|
|
|
42
51
|
let nameInputValue: string
|
|
43
52
|
let chosenProvider: BunkerProfile | undefined
|
|
44
53
|
let clientSecret: Uint8Array
|
|
45
|
-
const local = localStorage.getItem(
|
|
54
|
+
const local = localStorage.getItem(localStorageKeys.CLIENT_SECRET)
|
|
46
55
|
if (local) {
|
|
47
56
|
clientSecret = hexToBytes(local)
|
|
48
57
|
} else {
|
|
49
58
|
clientSecret = generateSecretKey()
|
|
50
|
-
localStorage.setItem(
|
|
59
|
+
localStorage.setItem(
|
|
60
|
+
localStorageKeys.CLIENT_SECRET,
|
|
61
|
+
bytesToHex(clientSecret)
|
|
62
|
+
)
|
|
51
63
|
}
|
|
52
64
|
|
|
53
65
|
let state: 'opened' | 'closed' | 'justopened' | 'justclosed' = 'closed'
|
|
54
66
|
let bunkerPointer: BunkerPointer | null
|
|
55
67
|
let resolveBunker: (_: BunkerSigner) => void
|
|
68
|
+
let rejectBunker: (_: string) => void
|
|
56
69
|
let bunker: Promise<BunkerSigner>
|
|
57
70
|
let connecting: boolean
|
|
58
|
-
let
|
|
59
|
-
let
|
|
71
|
+
let connected: boolean
|
|
72
|
+
let showAuth: string | null = null
|
|
73
|
+
let showLogin: string | null = null
|
|
74
|
+
let takingTooLong = false
|
|
60
75
|
let creating: boolean
|
|
61
|
-
let
|
|
76
|
+
let awaitingCreation: boolean
|
|
77
|
+
let errorMessage: string
|
|
78
|
+
let showInfo = false
|
|
79
|
+
let identity: null | {
|
|
62
80
|
pubkey: string
|
|
63
81
|
npub: string
|
|
64
82
|
name?: string
|
|
@@ -67,12 +85,16 @@
|
|
|
67
85
|
}
|
|
68
86
|
let metadataSub: SubCloser | null
|
|
69
87
|
let providers: BunkerProfile[] = []
|
|
88
|
+
const connectBunkerError =
|
|
89
|
+
'We could not connect to a NIP-46 bunker with that url, are you sure it is set up correctly?'
|
|
90
|
+
const connectNip05Error =
|
|
91
|
+
'We were not able to connect using this address. For it to work it has to come from a NIP-46 provider.'
|
|
70
92
|
|
|
71
93
|
const BASE_YPOS = 20
|
|
72
94
|
export let right = 20
|
|
73
95
|
$: ypos = $mobileMode
|
|
74
96
|
? BASE_YPOS
|
|
75
|
-
: parseInt(localStorage.getItem(
|
|
97
|
+
: parseInt(localStorage.getItem(localStorageKeys.Y_POS) || '0') || BASE_YPOS
|
|
76
98
|
let dragStarted = false
|
|
77
99
|
let hasMoved = false
|
|
78
100
|
let insidePosition: number
|
|
@@ -82,10 +104,10 @@
|
|
|
82
104
|
$: opened = state === 'justopened' || state === 'opened'
|
|
83
105
|
|
|
84
106
|
$: movingStyle = hasMoved
|
|
85
|
-
? '
|
|
107
|
+
? 'cursor-grabbing outline-dashed outline-' +
|
|
86
108
|
accent +
|
|
87
|
-
'-500
|
|
88
|
-
: '
|
|
109
|
+
'-500 outline-1 outline-offset-4'
|
|
110
|
+
: 'outline-none'
|
|
89
111
|
|
|
90
112
|
$: bunkerInputValueIsGood =
|
|
91
113
|
bunkerInputValue &&
|
|
@@ -95,10 +117,18 @@
|
|
|
95
117
|
const bunkerSignerParams: BunkerSignerParams = {
|
|
96
118
|
pool,
|
|
97
119
|
onauth(url: string) {
|
|
98
|
-
|
|
120
|
+
if (creating) {
|
|
121
|
+
showAuth = url
|
|
122
|
+
} else {
|
|
123
|
+
showLogin = url
|
|
124
|
+
}
|
|
99
125
|
}
|
|
100
126
|
}
|
|
101
127
|
|
|
128
|
+
function openAuthURLPopup(url: string | null): Window | null {
|
|
129
|
+
return window.open(url!, 'window.nostr', `width=600,height=800,popup=yes`)
|
|
130
|
+
}
|
|
131
|
+
|
|
102
132
|
const delayedUpdateState = debounce(() => {
|
|
103
133
|
switch (state) {
|
|
104
134
|
case 'justopened':
|
|
@@ -120,31 +150,40 @@
|
|
|
120
150
|
delayedUpdateState()
|
|
121
151
|
}
|
|
122
152
|
|
|
153
|
+
function connectOrOpen() {
|
|
154
|
+
if (bunkerPointer && !connected) {
|
|
155
|
+
connect()
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
open()
|
|
159
|
+
}
|
|
160
|
+
|
|
123
161
|
reset()
|
|
124
162
|
|
|
125
163
|
let windowNostr = {
|
|
126
164
|
isWnj: true,
|
|
127
165
|
async getPublicKey(): Promise<string> {
|
|
166
|
+
if (bunkerPointer) return bunkerPointer.pubkey
|
|
128
167
|
if (!connecting && !connected) open()
|
|
129
168
|
return (await bunker).bp.pubkey
|
|
130
169
|
},
|
|
131
170
|
async signEvent(event: NostrEvent): Promise<VerifiedEvent> {
|
|
132
|
-
if (!connecting && !connected)
|
|
171
|
+
if (!connecting && !connected) connectOrOpen()
|
|
133
172
|
return (await bunker).signEvent(event)
|
|
134
173
|
},
|
|
135
174
|
async getRelays(): Promise<{
|
|
136
175
|
[url: string]: {read: boolean; write: boolean}
|
|
137
176
|
}> {
|
|
138
|
-
if (!connecting && !connected)
|
|
177
|
+
if (!connecting && !connected) connectOrOpen()
|
|
139
178
|
return (await bunker).getRelays()
|
|
140
179
|
},
|
|
141
180
|
nip04: {
|
|
142
181
|
async encrypt(pubkey: string, plaintext: string): Promise<string> {
|
|
143
|
-
if (!connecting && !connected)
|
|
182
|
+
if (!connecting && !connected) connectOrOpen()
|
|
144
183
|
return (await bunker).nip04Encrypt(pubkey, plaintext)
|
|
145
184
|
},
|
|
146
185
|
async decrypt(pubkey: string, ciphertext: string): Promise<string> {
|
|
147
|
-
if (!connecting && !connected)
|
|
186
|
+
if (!connecting && !connected) connectOrOpen()
|
|
148
187
|
return (await bunker).nip04Decrypt(pubkey, ciphertext)
|
|
149
188
|
}
|
|
150
189
|
}
|
|
@@ -153,22 +192,28 @@
|
|
|
153
192
|
function reset() {
|
|
154
193
|
close()
|
|
155
194
|
bunkerPointer = null
|
|
156
|
-
bunker = new Promise(resolve => {
|
|
195
|
+
bunker = new Promise((resolve, reject) => {
|
|
157
196
|
resolveBunker = resolve
|
|
197
|
+
rejectBunker = reject
|
|
158
198
|
})
|
|
199
|
+
identity = null
|
|
159
200
|
connecting = false
|
|
201
|
+
takingTooLong = false
|
|
160
202
|
creating = false
|
|
161
|
-
connected =
|
|
203
|
+
connected = false
|
|
162
204
|
metadataSub = null
|
|
205
|
+
errorMessage = ''
|
|
163
206
|
}
|
|
164
207
|
|
|
165
208
|
onMount(() => {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
209
|
+
if (!bunkerPointer) {
|
|
210
|
+
let data = localStorage.getItem(localStorageKeys.BUNKER_POINTER)
|
|
211
|
+
if (data) {
|
|
212
|
+
bunkerPointer = JSON.parse(data)
|
|
213
|
+
// we have a pointer, which means we can get the public key right away
|
|
214
|
+
// but we will only try to connect when any other method is called on window.nostr
|
|
215
|
+
identify()
|
|
216
|
+
}
|
|
172
217
|
}
|
|
173
218
|
|
|
174
219
|
if (win.nostr && !win.nostr.isWnj) {
|
|
@@ -214,27 +259,54 @@
|
|
|
214
259
|
function handleCloseModal(ev: MouseEvent) {
|
|
215
260
|
close()
|
|
216
261
|
creating = false
|
|
262
|
+
showAuth = null
|
|
263
|
+
showLogin = null
|
|
264
|
+
ev.stopPropagation()
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function handleShowInfo(ev: MouseEvent) {
|
|
268
|
+
showInfo = true
|
|
269
|
+
ev.stopPropagation()
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function handleCloseInfo(ev: MouseEvent) {
|
|
273
|
+
showInfo = false
|
|
217
274
|
ev.stopPropagation()
|
|
218
275
|
}
|
|
219
276
|
|
|
220
277
|
async function handleConnect(ev: SubmitEvent) {
|
|
221
278
|
ev.preventDefault()
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
279
|
+
try {
|
|
280
|
+
bunkerPointer = await parseBunkerInput(bunkerInput.value)
|
|
281
|
+
if (!bunkerPointer) {
|
|
282
|
+
if (bunkerInput.value.match(BUNKER_REGEX)) {
|
|
283
|
+
errorMessage = connectBunkerError
|
|
284
|
+
} else {
|
|
285
|
+
errorMessage = connectNip05Error
|
|
286
|
+
}
|
|
287
|
+
return
|
|
288
|
+
}
|
|
229
289
|
|
|
230
|
-
|
|
231
|
-
|
|
290
|
+
bunkerInput.setCustomValidity('')
|
|
291
|
+
errorMessage = ''
|
|
292
|
+
await connect()
|
|
293
|
+
|
|
294
|
+
// since we are connecting right now after the user has typed stuff
|
|
295
|
+
// wait until the connection has succeeded before loading user data
|
|
296
|
+
identify()
|
|
297
|
+
} catch (error) {
|
|
298
|
+
if (bunkerInput.value.match(BUNKER_REGEX)) {
|
|
299
|
+
errorMessage = connectBunkerError
|
|
300
|
+
} else {
|
|
301
|
+
errorMessage = connectNip05Error
|
|
302
|
+
}
|
|
303
|
+
connecting = false
|
|
304
|
+
}
|
|
232
305
|
}
|
|
233
306
|
|
|
234
307
|
async function handleDisconnect(ev: MouseEvent) {
|
|
235
308
|
ev.preventDefault()
|
|
236
|
-
localStorage.removeItem(
|
|
237
|
-
if (win.isWnj) delete win.nostr
|
|
309
|
+
localStorage.removeItem(localStorageKeys.BUNKER_POINTER)
|
|
238
310
|
reset()
|
|
239
311
|
}
|
|
240
312
|
|
|
@@ -259,16 +331,20 @@
|
|
|
259
331
|
ev.preventDefault()
|
|
260
332
|
if (!chosenProvider) return
|
|
261
333
|
|
|
334
|
+
awaitingCreation = true
|
|
262
335
|
let bunker = await createAccount(
|
|
263
336
|
chosenProvider,
|
|
264
337
|
bunkerSignerParams,
|
|
265
338
|
nameInput.value,
|
|
266
339
|
chosenProvider.domain
|
|
267
340
|
)
|
|
341
|
+
awaitingCreation = false
|
|
268
342
|
|
|
269
343
|
open()
|
|
270
344
|
creating = false
|
|
271
345
|
|
|
346
|
+
bunkerPointer = bunker.bp
|
|
347
|
+
identify()
|
|
272
348
|
connect(bunker)
|
|
273
349
|
}
|
|
274
350
|
|
|
@@ -283,64 +359,74 @@
|
|
|
283
359
|
}, 500)
|
|
284
360
|
|
|
285
361
|
function handleAbortConnection() {
|
|
286
|
-
|
|
362
|
+
takingTooLong = false
|
|
287
363
|
connecting = false
|
|
288
|
-
|
|
289
|
-
|
|
364
|
+
rejectBunker('connection aborted')
|
|
365
|
+
reset()
|
|
290
366
|
}
|
|
291
367
|
|
|
292
|
-
async function connect(
|
|
293
|
-
|
|
368
|
+
async function connect(b: BunkerSigner | undefined = undefined) {
|
|
369
|
+
b = b || new BunkerSigner(clientSecret, bunkerPointer!, bunkerSignerParams)
|
|
370
|
+
connecting = true
|
|
294
371
|
|
|
295
|
-
|
|
296
|
-
|
|
372
|
+
let connectionTimeout = setTimeout(() => {
|
|
373
|
+
takingTooLong = true
|
|
297
374
|
opened = true
|
|
375
|
+
}, 5000)
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
await b.connect()
|
|
379
|
+
connected = true
|
|
380
|
+
localStorage.setItem(
|
|
381
|
+
localStorageKeys.BUNKER_POINTER,
|
|
382
|
+
JSON.stringify(bunkerPointer)
|
|
383
|
+
)
|
|
384
|
+
close()
|
|
385
|
+
resolveBunker(b)
|
|
386
|
+
} catch (err: any) {
|
|
387
|
+
rejectBunker(err?.message || String(err))
|
|
388
|
+
} finally {
|
|
389
|
+
// this still gets executed even if we return above
|
|
298
390
|
clearTimeout(connectionTimeout)
|
|
391
|
+
connecting = false
|
|
392
|
+
takingTooLong = false
|
|
393
|
+
showAuth = null
|
|
394
|
+
showLogin = null
|
|
299
395
|
}
|
|
396
|
+
}
|
|
300
397
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
await bunker.connect()
|
|
305
|
-
|
|
306
|
-
clearTimeout(connectionTimeout)
|
|
307
|
-
bunkerPointer = bunker.bp
|
|
398
|
+
function identify(onFirstMetadata: (() => void) | null = null) {
|
|
399
|
+
let pubkey = bunkerPointer!.pubkey
|
|
308
400
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
npub: npubEncode(bunker.bp.pubkey),
|
|
401
|
+
identity = {
|
|
402
|
+
pubkey: pubkey,
|
|
403
|
+
npub: npubEncode(pubkey),
|
|
313
404
|
event: null
|
|
314
405
|
}
|
|
315
406
|
|
|
316
|
-
localStorage.setItem('wnj:bunkerPointer', JSON.stringify(bunkerPointer))
|
|
317
|
-
|
|
318
|
-
// load metadata
|
|
319
407
|
metadataSub = pool.subscribeMany(
|
|
320
408
|
[
|
|
321
409
|
'wss://purplepag.es',
|
|
322
410
|
'wss://relay.snort.social',
|
|
323
411
|
'wss://relay.nos.social'
|
|
324
412
|
],
|
|
325
|
-
[{kinds: [0], authors: [
|
|
413
|
+
[{kinds: [0], authors: [pubkey]}],
|
|
326
414
|
{
|
|
327
415
|
onevent(evt) {
|
|
328
|
-
if ((
|
|
416
|
+
if ((identity!.event?.created_at || 0) >= evt.created_at) return
|
|
329
417
|
try {
|
|
330
418
|
let {name, picture} = JSON.parse(evt.content)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
419
|
+
identity!.event = evt
|
|
420
|
+
identity!.name = name
|
|
421
|
+
identity!.picture = picture
|
|
422
|
+
onFirstMetadata?.()
|
|
423
|
+
onFirstMetadata = null
|
|
334
424
|
} catch (err) {
|
|
335
425
|
/***/
|
|
336
426
|
}
|
|
337
427
|
}
|
|
338
428
|
}
|
|
339
429
|
)
|
|
340
|
-
connecting = false
|
|
341
|
-
longConnecting = false
|
|
342
|
-
close()
|
|
343
|
-
resolveBunker(bunker)
|
|
344
430
|
}
|
|
345
431
|
|
|
346
432
|
function handleMouseDown(ev: MouseEvent) {
|
|
@@ -396,8 +482,8 @@
|
|
|
396
482
|
ypos = BASE_YPOS
|
|
397
483
|
}
|
|
398
484
|
|
|
399
|
-
localStorage.setItem(
|
|
400
|
-
localStorage.setItem(
|
|
485
|
+
localStorage.setItem(localStorageKeys.ORIGIN, origin)
|
|
486
|
+
localStorage.setItem(localStorageKeys.Y_POS, ypos.toString())
|
|
401
487
|
}
|
|
402
488
|
}
|
|
403
489
|
</script>
|
|
@@ -410,8 +496,8 @@
|
|
|
410
496
|
|
|
411
497
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
412
498
|
<div
|
|
413
|
-
class="
|
|
414
|
-
class:
|
|
499
|
+
class="draggable animate-fadein font-sans text-white"
|
|
500
|
+
class:cursor-pointer={!identity && !opened}
|
|
415
501
|
style="position: fixed; {opened && $mobileMode
|
|
416
502
|
? 'width: 100%;'
|
|
417
503
|
: ''}; right: {opened && $mobileMode
|
|
@@ -425,29 +511,29 @@
|
|
|
425
511
|
<!-- Close status ################### -->
|
|
426
512
|
{#if !opened}
|
|
427
513
|
<div
|
|
428
|
-
class="
|
|
514
|
+
class="rounded px-4 py-2 shadow-[0_0px_10px_0px_rgba(0,0,0,0.3)] transition-all duration-200 bg-{accent}-700 hover:bg-{accent}-800 {movingStyle}"
|
|
429
515
|
>
|
|
430
516
|
<!-- Connecting view ################### -->
|
|
431
517
|
{#if connecting}
|
|
432
|
-
<div class="
|
|
518
|
+
<div class="flex items-center">
|
|
433
519
|
Connecting to bunker
|
|
434
520
|
<Spinner />
|
|
435
521
|
</div>
|
|
436
|
-
{:else if !
|
|
522
|
+
{:else if !identity}
|
|
437
523
|
Connect with Nostr
|
|
438
524
|
{:else}
|
|
439
|
-
<div class="
|
|
440
|
-
{#if
|
|
525
|
+
<div class="flex items-center">
|
|
526
|
+
{#if identity.picture}
|
|
441
527
|
<img
|
|
442
|
-
src={
|
|
528
|
+
src={identity.picture}
|
|
443
529
|
alt=""
|
|
444
|
-
class="
|
|
530
|
+
class="mr-2 h-5 w-5 rounded-full"
|
|
445
531
|
/>
|
|
446
532
|
{:else}
|
|
447
533
|
☉
|
|
448
534
|
{/if}
|
|
449
|
-
{
|
|
450
|
-
|
|
535
|
+
{identity.name ||
|
|
536
|
+
identity.npub.slice(0, 7) + '…' + identity.npub.slice(-4)}
|
|
451
537
|
</div>
|
|
452
538
|
{/if}
|
|
453
539
|
</div>
|
|
@@ -455,77 +541,148 @@
|
|
|
455
541
|
<!-- Open status ################### -->
|
|
456
542
|
{:else}
|
|
457
543
|
<div
|
|
458
|
-
class="
|
|
544
|
+
class="animate-show rounded-md bg-gradient-to-b px-8 py-8 shadow-[0_0px_30px_0px_rgba(0,0,0,0.6)] transition-all sm:w-96 from-{accent}-900 to-{accent}-700 {movingStyle}"
|
|
459
545
|
>
|
|
460
546
|
<button
|
|
461
|
-
on:click={handleCloseModal}
|
|
462
|
-
class="
|
|
547
|
+
on:click={showInfo ? handleCloseInfo : handleCloseModal}
|
|
548
|
+
class="absolute right-2 top-0 cursor-pointer bg-transparent text-3xl text-{accent}-600"
|
|
463
549
|
>⤫</button
|
|
464
550
|
>
|
|
465
|
-
|
|
466
|
-
{#if
|
|
467
|
-
<
|
|
468
|
-
|
|
469
|
-
|
|
551
|
+
|
|
552
|
+
{#if !showInfo && !showAuth && !showLogin}
|
|
553
|
+
<button
|
|
554
|
+
on:click={handleShowInfo}
|
|
555
|
+
class="absolute bottom-1 right-3 cursor-pointer bg-transparent text-xl text-{accent}-600"
|
|
556
|
+
>?</button
|
|
557
|
+
>
|
|
558
|
+
{/if}
|
|
559
|
+
|
|
560
|
+
{#if showAuth}
|
|
561
|
+
<div class="m-auto w-full">
|
|
562
|
+
<div class="text-center text-lg">Create a Nostr account</div>
|
|
563
|
+
<div class="mt-4 text-center text-sm leading-4">
|
|
564
|
+
Now you a new window will bring you to <strong>{new URL(showAuth).host}</strong> where the account creation will take place. If nothing happens check that if your browser is blocking popups, pleaase.<br/>
|
|
565
|
+
After that you will be returned to this page.
|
|
566
|
+
</div>
|
|
567
|
+
<button
|
|
568
|
+
class="mt-4 block w-full cursor-pointer rounded border-0 px-2 py-1 text-lg text-white disabled:cursor-default disabled:bg-neutral-400 disabled:text-neutral-200 bg-{accent}-900 hover:bg-{accent}-950"
|
|
569
|
+
on:click={() => openAuthURLPopup(showAuth)}>
|
|
570
|
+
Start account creation »
|
|
571
|
+
</button>
|
|
572
|
+
</div>
|
|
573
|
+
|
|
574
|
+
{:else if showLogin}
|
|
575
|
+
<div class="m-auto w-full">
|
|
576
|
+
<div class="text-center text-lg">Login into a Nostr account</div>
|
|
577
|
+
<div class="mt-4 text-center text-sm leading-4">
|
|
578
|
+
Now you a new window will bring you to <strong>{new URL(showLogin).host}</strong> where you can login and approve the permissions. If nothing happens check that if your browser is blocking popups, pleaase.<br/>
|
|
579
|
+
After that you will be returned to this page.
|
|
580
|
+
</div>
|
|
581
|
+
<button
|
|
582
|
+
class="mt-4 block w-full cursor-pointer rounded border-0 px-2 py-1 text-lg text-white disabled:cursor-default disabled:bg-neutral-400 disabled:text-neutral-200 bg-{accent}-900 hover:bg-{accent}-950"
|
|
583
|
+
on:click={() => openAuthURLPopup(showLogin)}>
|
|
584
|
+
Login now »
|
|
585
|
+
</button>
|
|
586
|
+
</div>
|
|
587
|
+
|
|
588
|
+
<!-- Show info ################### -->
|
|
589
|
+
{:else if showInfo}
|
|
590
|
+
<div class="text-center text-lg">What is that?</div>
|
|
591
|
+
<div class="text-base leading-5">
|
|
592
|
+
<p class="mb mt-4">
|
|
593
|
+
This widget is created with <i>window.nostr.js</i>, a small script
|
|
594
|
+
you can drop in any page that already uses NIP-07 and make it also
|
|
595
|
+
work with NIP-46 automatically when the user doesn't have an
|
|
596
|
+
extension installed.
|
|
597
|
+
<br />
|
|
598
|
+
It adds a small floating button on the side of the window that users
|
|
599
|
+
can use to create Nostr accuonts or connect to their NIP-46 bunkers.
|
|
600
|
+
</p>
|
|
601
|
+
<p class="mt-4">
|
|
602
|
+
This tool is opensource, get the code from the <a
|
|
603
|
+
target="_blank"
|
|
604
|
+
class="underline"
|
|
605
|
+
href="https://github.com/fiatjaf/window.nostr.js"
|
|
606
|
+
>project's page</a
|
|
607
|
+
>.
|
|
608
|
+
</p>
|
|
609
|
+
<p class="mt-4">
|
|
610
|
+
You don't know what Nostr is?
|
|
611
|
+
<a target="_blank" class="underline" href="https://www.nostr.com"
|
|
612
|
+
>Learn more</a
|
|
613
|
+
>.
|
|
614
|
+
</p>
|
|
615
|
+
</div>
|
|
616
|
+
|
|
617
|
+
<!-- Create account view ################### -->
|
|
618
|
+
{:else if creating}
|
|
619
|
+
<div class="text-center text-lg">Create a Nostr account</div>
|
|
620
|
+
<form class="mb-1 mt-4" on:submit={handleCreate}>
|
|
621
|
+
<div class="flex flex-row">
|
|
470
622
|
<!-- svelte-ignore a11y-autofocus -->
|
|
471
623
|
<input
|
|
472
|
-
class="
|
|
624
|
+
class="box-border w-40 rounded px-2 py-1 text-lg text-neutral-800 outline-none"
|
|
473
625
|
placeholder="bob"
|
|
474
626
|
bind:this={nameInput}
|
|
475
627
|
bind:value={nameInputValue}
|
|
476
628
|
on:input={checkNameInput}
|
|
477
629
|
autofocus
|
|
630
|
+
autocapitalize="none"
|
|
478
631
|
/>
|
|
479
|
-
<div class="
|
|
632
|
+
<div class="mx-2 text-2xl">@</div>
|
|
480
633
|
<select
|
|
481
|
-
class="
|
|
634
|
+
class="box-border w-full rounded px-2 py-1 text-lg text-neutral-800 outline-none"
|
|
482
635
|
bind:value={chosenProvider}
|
|
483
636
|
>
|
|
484
637
|
{#each providers as prov}
|
|
485
638
|
<option
|
|
486
639
|
label={prov.domain}
|
|
487
640
|
value={prov}
|
|
488
|
-
class="
|
|
641
|
+
class="px-2 py-1 text-lg"
|
|
489
642
|
/>
|
|
490
643
|
{/each}
|
|
491
644
|
</select>
|
|
492
645
|
</div>
|
|
493
|
-
<div class="tw-text-sm tw-text-center tw-mt-4 tw-leading-4">
|
|
494
|
-
A window from the selected provider will pop up to finalize the
|
|
495
|
-
creation; if it doesn't display check if the browser is blocking it
|
|
496
|
-
</div>
|
|
497
646
|
<button
|
|
498
|
-
class="
|
|
499
|
-
disabled={!chosenProvider || !nameInputValue}
|
|
647
|
+
class="mt-4 block w-full cursor-pointer rounded border-0 px-2 py-1 text-lg text-white disabled:cursor-default disabled:bg-neutral-400 disabled:text-neutral-200 bg-{accent}-900 hover:bg-{accent}-950"
|
|
648
|
+
disabled={!chosenProvider || !nameInputValue || awaitingCreation}
|
|
500
649
|
>
|
|
501
|
-
|
|
650
|
+
Continue »
|
|
502
651
|
</button>
|
|
503
652
|
</form>
|
|
504
|
-
<div class="
|
|
653
|
+
<div class="mt-6 text-center text-sm leading-3">
|
|
505
654
|
Do you already have a Nostr address?<br />
|
|
506
655
|
<button
|
|
507
|
-
class="
|
|
656
|
+
class="cursor-pointer border-0 bg-transparent text-sm text-white underline"
|
|
508
657
|
on:click={handleOpenLogin}>Login now</button
|
|
509
658
|
>
|
|
510
659
|
</div>
|
|
511
660
|
|
|
512
661
|
<!-- Login view ################### -->
|
|
513
|
-
{:else if !
|
|
514
|
-
<div class="
|
|
662
|
+
{:else if !identity}
|
|
663
|
+
<div class="text-center text-lg">
|
|
515
664
|
How do you want to connect to Nostr?
|
|
516
665
|
</div>
|
|
517
|
-
<form class="
|
|
666
|
+
<form class="mb-1 mt-4 flex flex-col" on:submit={handleConnect}>
|
|
518
667
|
<!-- svelte-ignore a11y-autofocus -->
|
|
519
668
|
<input
|
|
520
|
-
class="
|
|
669
|
+
class="box-border w-full rounded px-2 py-1 text-lg text-neutral-800 outline-none"
|
|
521
670
|
placeholder="user@provider or bunker://..."
|
|
522
671
|
bind:this={bunkerInput}
|
|
523
672
|
bind:value={bunkerInputValue}
|
|
524
673
|
autofocus
|
|
525
674
|
disabled={connecting}
|
|
675
|
+
autocapitalize="none"
|
|
526
676
|
/>
|
|
677
|
+
{#if errorMessage}
|
|
678
|
+
<div
|
|
679
|
+
class="my-2 rounded bg-yellow-100 p-2 text-center text-sm leading-4 text-red-400"
|
|
680
|
+
>
|
|
681
|
+
{errorMessage}
|
|
682
|
+
</div>
|
|
683
|
+
{/if}
|
|
527
684
|
<button
|
|
528
|
-
class="
|
|
685
|
+
class="mt-4 flex w-full cursor-pointer items-center justify-center rounded border-0 px-2 py-1 text-lg text-white disabled:cursor-default disabled:bg-neutral-400 disabled:text-neutral-200 bg-{accent}-900 hover:bg-{accent}-950"
|
|
529
686
|
disabled={!bunkerInputValueIsGood || connecting}
|
|
530
687
|
>
|
|
531
688
|
{#if connecting}
|
|
@@ -535,63 +692,61 @@
|
|
|
535
692
|
Connect »
|
|
536
693
|
{/if}
|
|
537
694
|
</button>
|
|
538
|
-
{#if connecting &&
|
|
539
|
-
<div class="
|
|
695
|
+
{#if connecting && takingTooLong}
|
|
696
|
+
<div class="mt-6 text-center text-sm leading-3">
|
|
540
697
|
Waiting too much?
|
|
541
698
|
<button
|
|
542
|
-
class="
|
|
699
|
+
class="cursor-pointer border-0 bg-transparent text-sm text-white underline"
|
|
543
700
|
on:click={handleAbortConnection}>Cancel the connection</button
|
|
544
701
|
>
|
|
545
702
|
</div>
|
|
546
703
|
{/if}
|
|
547
704
|
</form>
|
|
548
705
|
{#if !connecting}
|
|
549
|
-
<div class="
|
|
706
|
+
<div class="mt-6 text-center text-sm leading-3">
|
|
550
707
|
Do you need a Nostr account?<br />
|
|
551
708
|
<button
|
|
552
|
-
class="
|
|
709
|
+
class="cursor-pointer border-0 bg-transparent text-sm text-white underline"
|
|
553
710
|
on:click={handleOpenCreate}>Sign up now</button
|
|
554
711
|
>
|
|
555
712
|
</div>
|
|
556
713
|
{/if}
|
|
557
714
|
|
|
558
715
|
<!-- Connected view ################### -->
|
|
559
|
-
{:else
|
|
560
|
-
<div class="
|
|
561
|
-
<div class="
|
|
716
|
+
{:else}
|
|
717
|
+
<div class="text-center">
|
|
718
|
+
<div class="mb-4 text-sm">You are connected to Nostr as</div>
|
|
562
719
|
<a
|
|
563
720
|
target="_blank"
|
|
564
|
-
href={'https://nosta.me/' +
|
|
565
|
-
class="
|
|
721
|
+
href={'https://nosta.me/' + identity.npub}
|
|
722
|
+
class="group text-white no-underline"
|
|
566
723
|
>
|
|
567
|
-
{#if
|
|
568
|
-
<div
|
|
569
|
-
|
|
570
|
-
>
|
|
571
|
-
{#if connected.picture}
|
|
724
|
+
{#if identity.picture || identity.name}
|
|
725
|
+
<div class="mb-2 flex items-center justify-center gap-2">
|
|
726
|
+
{#if identity.picture}
|
|
572
727
|
<img
|
|
573
|
-
src={
|
|
728
|
+
src={identity.picture}
|
|
574
729
|
alt=""
|
|
575
|
-
class="
|
|
730
|
+
class="h-10 w-10 rounded-full border-2 border-solid border-transparent group-hover:border-{accent}-100"
|
|
576
731
|
/>
|
|
577
732
|
{/if}
|
|
578
|
-
{#if
|
|
733
|
+
{#if identity.name}
|
|
579
734
|
<div
|
|
580
|
-
class="
|
|
735
|
+
class="text-3xl decoration-2 underline-offset-4 group-hover:underline"
|
|
581
736
|
>
|
|
582
|
-
{
|
|
737
|
+
{identity.name}
|
|
583
738
|
</div>
|
|
584
739
|
{/if}
|
|
585
740
|
</div>
|
|
586
741
|
{/if}
|
|
587
|
-
<div class="
|
|
742
|
+
<div class="block break-all">{identity.npub}</div>
|
|
588
743
|
</a>
|
|
589
744
|
</div>
|
|
590
745
|
<button
|
|
591
|
-
class="
|
|
746
|
+
class="my-2 mt-6 block w-full cursor-pointer rounded border-0 px-2 py-1 text-lg text-white bg-{accent}-900 hover:bg-{accent}-950"
|
|
592
747
|
on:click={handleDisconnect}>Disconnect</button
|
|
593
748
|
>
|
|
594
|
-
<div class="
|
|
749
|
+
<div class="mt-6 block break-all text-center text-sm">
|
|
595
750
|
This webpage is using the public key:<br />
|
|
596
751
|
{getPublicKey(clientSecret)}
|
|
597
752
|
</div>
|
|
@@ -599,13 +754,3 @@
|
|
|
599
754
|
</div>
|
|
600
755
|
{/if}
|
|
601
756
|
</div>
|
|
602
|
-
|
|
603
|
-
<!-- hack to preload tailwind colors:
|
|
604
|
-
tw-bg-cyan-700 tw-bg-cyan-800 tw-bg-cyan-900 tw-bg-cyan-950 tw-text-cyan-950 tw-outline-cyan-500
|
|
605
|
-
tw-bg-green-700 tw-bg-green-800 tw-bg-green-900 tw-bg-green-950 tw-text-green-950 tw-outline-green-500
|
|
606
|
-
tw-bg-purple-700 tw-bg-purple-800 tw-bg-purple-900 tw-bg-purple-950 tw-text-purple-950 tw-outline-purple-500
|
|
607
|
-
tw-bg-red-700 tw-bg-red-800 tw-bg-red-900 tw-bg-red-950 tw-text-red-950 tw-outline-red-500
|
|
608
|
-
tw-bg-orange-700 tw-bg-orange-800 tw-bg-orange-900 tw-bg-orange-950 tw-text-orange-950 tw-outline-orange-500
|
|
609
|
-
tw-bg-neutral-700 tw-bg-neutral-800 tw-bg-neutral-900 tw-bg-neutral-950 tw-text-neutral-950 tw-outline-neutral-500
|
|
610
|
-
tw-bg-stone-700 tw-bg-stone-800 tw-bg-stone-900 tw-bg-stone-950 tw-text-stone-950 tw-outline-stone-500
|
|
611
|
-
-->
|