diff --git a/app/Livewire/Nostr/Start.php b/app/Livewire/Nostr/Start.php index f015155d..afbce9ec 100644 --- a/app/Livewire/Nostr/Start.php +++ b/app/Livewire/Nostr/Start.php @@ -2,10 +2,18 @@ namespace App\Livewire\Nostr; +use App\Models\User; use Livewire\Component; class Start extends Component { + public ?User $user = null; + + public function setUser($value) + { + $this->user = User::query()->with(['meetups'])->where('nostr', $value['npub'])->first(); + } + public function render() { return view('livewire.nostr.start'); diff --git a/app/Models/User.php b/app/Models/User.php index 64839703..49af964a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -14,13 +14,11 @@ use ParagonIE\CipherSweet\BlindIndex; use ParagonIE\CipherSweet\EncryptedRow; use ParagonIE\CipherSweet\JsonFieldMap; use QCod\Gamify\Gamify; -use Spatie\Comments\Models\Concerns\InteractsWithComments; -use Spatie\Comments\Models\Concerns\Interfaces\CanComment; use Spatie\LaravelCipherSweet\Concerns\UsesCipherSweet; use Spatie\LaravelCipherSweet\Contracts\CipherSweetEncrypted; use Spatie\Permission\Traits\HasRoles; -class User extends Authenticatable implements MustVerifyEmail, CanComment, CipherSweetEncrypted +class User extends Authenticatable implements MustVerifyEmail, CipherSweetEncrypted { use UsesCipherSweet; use HasApiTokens; @@ -30,7 +28,6 @@ class User extends Authenticatable implements MustVerifyEmail, CanComment, Ciphe use Notifiable; use TwoFactorAuthenticatable; use HasRoles; - use InteractsWithComments; use Gamify; protected $guarded = []; diff --git a/package.json b/package.json index 5a7cfdd6..68e40c54 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@alpinejs/focus": "^3.11.1", "@alpinejs/intersect": "^3.11.1", "@nostr-dev-kit/ndk": "^0.8.20-1", + "@nostr-dev-kit/ndk-cache-dexie": "^2.0.5", "@tailwindcss/forms": "^0.5.2", "@tailwindcss/line-clamp": "^0.4.2", "@tailwindcss/typography": "^0.5.0", diff --git a/resources/js/app.js b/resources/js/app.js index bae9ec36..0ac961cd 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,18 +1,21 @@ -import './components' -import './bootstrap' +import "./components" +import {Alpine, Livewire} from '../../vendor/livewire/livewire/dist/livewire.esm'; +import {NDKNip07Signer} from "@nostr-dev-kit/ndk"; +import NDKCacheAdapterDexie from "@nostr-dev-kit/ndk-cache-dexie"; +import nostrStart from "./nostr/nostrStart"; -import Alpine from 'alpinejs' -import collapse from '@alpinejs/collapse' -import intersect from '@alpinejs/intersect' -import focus from '@alpinejs/focus' -import NDK, { NDKNip07Signer, NDKEvent } from "@nostr-dev-kit/ndk" +Alpine.store('ndk', { + // nostr ndk + ndk: null, + // signer + nip07signer: new NDKNip07Signer(), + // dexie cache adapter + dexieAdapter: new NDKCacheAdapterDexie({dbName: 'einundzwanzigNostrDB', expirationTime: 60 * 60 * 24 * 7}), + // current nostr user + user: null, + // hours ago + explicitRelayUrls: [], +}); +Alpine.data('nostrStart', nostrStart); -window.Alpine = Alpine -window.NDK = NDK -window.NDKNip07Signer = NDKNip07Signer -window.NDKEvent = NDKEvent - -Alpine.plugin(collapse) -Alpine.plugin(intersect) -Alpine.plugin(focus) -Alpine.start() +Livewire.start(); diff --git a/resources/js/nostr/ndk/excplicitRelays.js b/resources/js/nostr/ndk/excplicitRelays.js new file mode 100644 index 00000000..aed79a29 --- /dev/null +++ b/resources/js/nostr/ndk/excplicitRelays.js @@ -0,0 +1,7 @@ +export default [ + 'wss://nostr.wine', + 'wss://nos.lol', + 'wss://nostr-pub.wellorder.net', + 'wss://nostr.cercatrova.me', + 'wss://nostr.mutinywallet.com', +]; diff --git a/resources/js/nostr/ndk/instance.js b/resources/js/nostr/ndk/instance.js new file mode 100644 index 00000000..1a2cbcca --- /dev/null +++ b/resources/js/nostr/ndk/instance.js @@ -0,0 +1,79 @@ +import excplicitRelays from "./excplicitRelays.js"; +import NDK from "@nostr-dev-kit/ndk"; + +export const ndkInstance = (Alpine) => ({ + async init() { + try { + const urls = excplicitRelays.map((relay) => { + if (relay.startsWith('ws')) { + return relay.replace('ws', 'http'); + } + if (relay.startsWith('wss')) { + return relay.replace('wss', 'https'); + } + }); + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort('timeout'), 5000); + + const requests = urls.map((url) => + fetch(url, { + headers: {Accept: 'application/nostr+json'}, + signal: controller.signal, + }) + ); + const responses = await Promise.all(requests); + const errors = responses.filter((response) => !response.ok); + + if (errors.length > 0) { + throw errors.map((response) => Error(response.statusText)); + } + + let verifiedRelays = responses.map((res) => { + if (res.url.startsWith('http')) { + return res.url.replace('http', 'ws'); + } + if (res.url.startsWith('https')) { + return res.url.replace('https', 'wss'); + } + }); + + // clear timeout + clearTimeout(timeoutId); + + console.log('##### verifiedRelays #####', verifiedRelays); + Alpine.$store.ndk.explicitRelayUrls = verifiedRelays; + + const instance = new NDK({ + explicitRelayUrls: Alpine.$store.ndk.explicitRelayUrls, + signer: Alpine.$store.ndk.nip07signer, + cacheAdapter: Alpine.$store.ndk.dexieAdapter, + outboxRelayUrls: ["wss://nostr.einundzwanzig.space",], + enableOutboxModel: true, + }); + + try { + await instance.connect(10000); + } catch (error) { + throw new Error('NDK instance init failed: ', error); + } + + // store NDK instance in store + Alpine.$store.ndk.ndk = instance; + + // init nip07 signer and fetch profile + await Alpine.$store.ndk.nip07signer.user().then(async (user) => { + if (!!user.npub) { + Alpine.$store.ndk.user = Alpine.$store.ndk.ndk.getUser({ + npub: user.npub, + }); + await Alpine.$store.ndk.user.fetchProfile(); + } + }).catch((error) => { + console.log('##### nip07 signer error #####', error); + }); + } catch (e) { + console.log(e); + } + } +}); diff --git a/resources/js/nostr/nostrStart.js b/resources/js/nostr/nostrStart.js new file mode 100644 index 00000000..2f364c54 --- /dev/null +++ b/resources/js/nostr/nostrStart.js @@ -0,0 +1,19 @@ +import {ndkInstance} from "./ndk/instance.js"; + +export default (livewireComponent) => ({ + + + + async init() { + + await ndkInstance(this).init(); + + console.log(this.$store.ndk.user); + + if (this.$store.ndk.user) { + this.$wire.setUser(this.$store.ndk.user); + } + + }, + +}); diff --git a/resources/views/components/layouts/app.blade.php b/resources/views/components/layouts/app.blade.php index 78f15a14..8c04cda2 100644 --- a/resources/views/components/layouts/app.blade.php +++ b/resources/views/components/layouts/app.blade.php @@ -15,20 +15,18 @@ @googlefonts {{-- Scripts --}} - + @wireUiScripts @vite(['resources/css/app.css', 'resources/js/app.js']) {{-- Styles --}} - @livewireStyles @include('components.layouts.styles') - - +{{----}} {{ $slot }} @stack('modals') -@livewireScripts +@livewireScriptConfig diff --git a/resources/views/components/layouts/guest.blade.php b/resources/views/components/layouts/guest.blade.php index 78f15a14..8c04cda2 100644 --- a/resources/views/components/layouts/guest.blade.php +++ b/resources/views/components/layouts/guest.blade.php @@ -15,20 +15,18 @@ @googlefonts {{-- Scripts --}} - + @wireUiScripts @vite(['resources/css/app.css', 'resources/js/app.js']) {{-- Styles --}} - @livewireStyles @include('components.layouts.styles') - - +{{----}} {{ $slot }} @stack('modals') -@livewireScripts +@livewireScriptConfig diff --git a/resources/views/livewire/nostr/start.blade.php b/resources/views/livewire/nostr/start.blade.php index b2b426d8..18fdfd53 100644 --- a/resources/views/livewire/nostr/start.blade.php +++ b/resources/views/livewire/nostr/start.blade.php @@ -1,3 +1,12 @@ -
- {{-- Because she competes with no one, no one can compete with her. --}} +
+ +
+ @if($user) + @foreach($user->meetups as $meetup) +
+ {{ $meetup->name }} +
+ @endforeach + @endif +
diff --git a/yarn.lock b/yarn.lock index ca06d50e..7c97d02c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -496,6 +496,13 @@ __metadata: languageName: node linkType: hard +"@noble/ciphers@npm:0.2.0": + version: 0.2.0 + resolution: "@noble/ciphers@npm:0.2.0" + checksum: fa6c14b66865cee83ee779a4ca4c8b39a6397425fae3f805d1766a2daf887d950d3771a68bb95e89cc3afbd595a219b44e8529aa4aaaad0ea5c030c1fb589ef4 + languageName: node + linkType: hard + "@noble/curves@npm:1.1.0, @noble/curves@npm:~1.1.0": version: 1.1.0 resolution: "@noble/curves@npm:1.1.0" @@ -546,6 +553,38 @@ __metadata: languageName: node linkType: hard +"@nostr-dev-kit/ndk-cache-dexie@npm:^2.0.5": + version: 2.0.5 + resolution: "@nostr-dev-kit/ndk-cache-dexie@npm:2.0.5" + dependencies: + "@nostr-dev-kit/ndk": 2.0.5 + debug: ^4.3.4 + dexie: ^3.2.4 + nostr-tools: ^1.14.0 + typescript-lru-cache: ^2.0.0 + checksum: 5c0d97dd39ad4b9cc67fd164caa493bbba79de09d210b3e7cc22eb21241eb2682891210f82e1ac5f35d57a5ef39e105e84422171937b54630fbc6c6336ab5ef4 + languageName: node + linkType: hard + +"@nostr-dev-kit/ndk@npm:2.0.5": + version: 2.0.5 + resolution: "@nostr-dev-kit/ndk@npm:2.0.5" + dependencies: + "@noble/hashes": ^1.3.1 + "@noble/secp256k1": ^2.0.0 + "@scure/base": ^1.1.1 + debug: ^4.3.4 + eventemitter3: ^5.0.1 + light-bolt11-decoder: ^3.0.0 + node-fetch: ^3.3.1 + nostr-tools: ^1.15.0 + typescript-lru-cache: ^2.0.0 + utf8-buffer: ^1.0.0 + websocket-polyfill: ^0.0.3 + checksum: 5f2e74fa132757530b84a0a6892d5252fde6e3f4e27b5f32abadda6b59d7915356a7e3448b658fdacc94911981a39a5f343aacede7ee326c90f1f6cb0a84a81c + languageName: node + linkType: hard + "@nostr-dev-kit/ndk@npm:^0.8.20-1": version: 0.8.20 resolution: "@nostr-dev-kit/ndk@npm:0.8.20" @@ -1833,6 +1872,13 @@ __metadata: languageName: node linkType: hard +"dexie@npm:^3.2.4": + version: 3.2.4 + resolution: "dexie@npm:3.2.4" + checksum: 4e5294a954118b6862c864b8c3970904a1733daebcd919488624520696411e2e81ed1bceeac3634c5c15a21e37ce4b8502ed41c4edfbc5ba3f5925c34d56497b + languageName: node + linkType: hard + "didyoumean@npm:^1.2.2": version: 1.2.2 resolution: "didyoumean@npm:1.2.2" @@ -4192,6 +4238,25 @@ __metadata: languageName: node linkType: hard +"nostr-tools@npm:^1.15.0": + version: 1.17.0 + resolution: "nostr-tools@npm:1.17.0" + dependencies: + "@noble/ciphers": 0.2.0 + "@noble/curves": 1.1.0 + "@noble/hashes": 1.3.1 + "@scure/base": 1.1.1 + "@scure/bip32": 1.3.1 + "@scure/bip39": 1.2.1 + peerDependencies: + typescript: ">=5.0.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 55cb4f7fd447a653fcc4f073f2cc6bea458e75ff5c40ab31baa2a8aae1e0ef2fabb840c08fa53a8fd5037dba7673c7361bb41fef60be5411525ceaa5ea46a1ca + languageName: node + linkType: hard + "npm-run-all@npm:^4.1.5": version: 4.1.5 resolution: "npm-run-all@npm:4.1.5" @@ -4920,6 +4985,7 @@ __metadata: "@alpinejs/focus": ^3.11.1 "@alpinejs/intersect": ^3.11.1 "@nostr-dev-kit/ndk": ^0.8.20-1 + "@nostr-dev-kit/ndk-cache-dexie": ^2.0.5 "@tailwindcss/forms": ^0.5.2 "@tailwindcss/line-clamp": ^0.4.2 "@tailwindcss/typography": ^0.5.0 @@ -5668,6 +5734,13 @@ __metadata: languageName: node linkType: hard +"typescript-lru-cache@npm:^2.0.0": + version: 2.0.0 + resolution: "typescript-lru-cache@npm:2.0.0" + checksum: 50948bb62cfe928f0baf4dcc09634cc2456520c0abf2ad726483039371eb523ee6b9c4f45210704f3c1fb30dc6ae5e5ffb27127a34d626848db9d80bcb3057d1 + languageName: node + linkType: hard + "typescript@npm:^5.1.6": version: 5.1.6 resolution: "typescript@npm:5.1.6"