Compare commits
No commits in common. "81f60a8c3866a755b83815e40e789cec7ba598c5" and "36a1cad9774d793e29d39a6cea58e2acc4ee3905" have entirely different histories.
81f60a8c38
...
36a1cad977
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"cSpell.words": ["Nostr"]
|
|
||||||
}
|
|
127
package-lock.json
generated
127
package-lock.json
generated
@ -29,15 +29,13 @@
|
|||||||
"gatsby-source-filesystem": "^3.3.0",
|
"gatsby-source-filesystem": "^3.3.0",
|
||||||
"gatsby-transformer-remark": "^4.0.0",
|
"gatsby-transformer-remark": "^4.0.0",
|
||||||
"gatsby-transformer-sharp": "^3.3.0",
|
"gatsby-transformer-sharp": "^3.3.0",
|
||||||
"nostr-tools": "^2.7.0",
|
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-icons": "^4.2.0",
|
"react-icons": "^4.2.0",
|
||||||
"react-share": "^4.4.0",
|
"react-share": "^4.4.0",
|
||||||
"react-use-flexsearch": "^0.1.1",
|
"react-use-flexsearch": "^0.1.1",
|
||||||
"styled-components": "^5.2.3",
|
"styled-components": "^5.2.3"
|
||||||
"tseep": "^1.2.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@popperjs/core": "^2.9.2",
|
"@popperjs/core": "^2.9.2",
|
||||||
@ -3116,47 +3114,6 @@
|
|||||||
"eslint-scope": "5.1.1"
|
"eslint-scope": "5.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@noble/ciphers": {
|
|
||||||
"version": "0.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz",
|
|
||||||
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@noble/curves": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@noble/hashes": "1.3.2"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@noble/curves/node_modules/@noble/hashes": {
|
|
||||||
"version": "1.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
|
|
||||||
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@noble/hashes": {
|
|
||||||
"version": "1.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
|
||||||
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@ -3258,53 +3215,6 @@
|
|||||||
"url": "https://opencollective.com/popperjs"
|
"url": "https://opencollective.com/popperjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@scure/base": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@scure/bip32": {
|
|
||||||
"version": "1.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
|
|
||||||
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
|
||||||
"dependencies": {
|
|
||||||
"@noble/curves": "~1.1.0",
|
|
||||||
"@noble/hashes": "~1.3.1",
|
|
||||||
"@scure/base": "~1.1.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@scure/bip32/node_modules/@noble/curves": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@noble/hashes": "1.3.1"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@scure/bip39": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
|
|
||||||
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
|
||||||
"dependencies": {
|
|
||||||
"@noble/hashes": "~1.3.0",
|
|
||||||
"@scure/base": "~1.1.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://paulmillr.com/funding/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sideway/address": {
|
"node_modules/@sideway/address": {
|
||||||
"version": "4.1.5",
|
"version": "4.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
|
||||||
@ -15944,36 +15854,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nostr-tools": {
|
|
||||||
"version": "2.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.7.0.tgz",
|
|
||||||
"integrity": "sha512-jJoL2J1CBiKDxaXZww27nY/Wsuxzx7AULxmGKFce4sskDu1tohNyfnzYQ8BvDyvkstU8kNZUAXPL32tre33uig==",
|
|
||||||
"dependencies": {
|
|
||||||
"@noble/ciphers": "^0.5.1",
|
|
||||||
"@noble/curves": "1.2.0",
|
|
||||||
"@noble/hashes": "1.3.1",
|
|
||||||
"@scure/base": "1.1.1",
|
|
||||||
"@scure/bip32": "1.3.1",
|
|
||||||
"@scure/bip39": "1.2.1"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"nostr-wasm": "v0.1.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": ">=5.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"typescript": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/nostr-wasm": {
|
|
||||||
"version": "0.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz",
|
|
||||||
"integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"node_modules/not": {
|
"node_modules/not": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/not/-/not-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/not/-/not-0.1.0.tgz",
|
||||||
@ -21150,11 +21030,6 @@
|
|||||||
"json5": "lib/cli.js"
|
"json5": "lib/cli.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tseep": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tseep/-/tseep-1.2.1.tgz",
|
|
||||||
"integrity": "sha512-VFnsNcPGC4qFJ1nxbIPSjTmtRZOhlqLmtwRqtLVos8mbRHki8HO9cy9Z1e89EiWyxFmq6LBviI9TQjijxw/mEw=="
|
|
||||||
},
|
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
@ -38,15 +38,13 @@
|
|||||||
"gatsby-source-filesystem": "^3.3.0",
|
"gatsby-source-filesystem": "^3.3.0",
|
||||||
"gatsby-transformer-remark": "^4.0.0",
|
"gatsby-transformer-remark": "^4.0.0",
|
||||||
"gatsby-transformer-sharp": "^3.3.0",
|
"gatsby-transformer-sharp": "^3.3.0",
|
||||||
"nostr-tools": "^2.7.0",
|
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-icons": "^4.2.0",
|
"react-icons": "^4.2.0",
|
||||||
"react-share": "^4.4.0",
|
"react-share": "^4.4.0",
|
||||||
"react-use-flexsearch": "^0.1.1",
|
"react-use-flexsearch": "^0.1.1",
|
||||||
"styled-components": "^5.2.3",
|
"styled-components": "^5.2.3"
|
||||||
"tseep": "^1.2.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@popperjs/core": "^2.9.2",
|
"@popperjs/core": "^2.9.2",
|
||||||
|
@ -1,205 +0,0 @@
|
|||||||
import {
|
|
||||||
Event,
|
|
||||||
EventTemplate,
|
|
||||||
SimplePool,
|
|
||||||
UnsignedEvent,
|
|
||||||
finalizeEvent,
|
|
||||||
nip04,
|
|
||||||
nip19,
|
|
||||||
verifyEvent,
|
|
||||||
generateSecretKey,
|
|
||||||
getPublicKey
|
|
||||||
} from 'nostr-tools'
|
|
||||||
import { EventEmitter } from 'tseep'
|
|
||||||
import { SignedEvent, Keys } from '../types'
|
|
||||||
|
|
||||||
export class NostrController extends EventEmitter {
|
|
||||||
private static instance: NostrController
|
|
||||||
|
|
||||||
private generatedKeys: Keys | undefined
|
|
||||||
|
|
||||||
private constructor() {
|
|
||||||
super()
|
|
||||||
|
|
||||||
this.generatedKeys = this.generateKeys()
|
|
||||||
}
|
|
||||||
|
|
||||||
public static getInstance(): NostrController {
|
|
||||||
if (!NostrController.instance) {
|
|
||||||
NostrController.instance = new NostrController()
|
|
||||||
}
|
|
||||||
return NostrController.instance
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function will publish provided event to the provided relays
|
|
||||||
*/
|
|
||||||
publishEvent = async (event: Event, relays: string[]) => {
|
|
||||||
const simplePool = new SimplePool()
|
|
||||||
const promises = simplePool.publish(relays, event)
|
|
||||||
|
|
||||||
const results = await Promise.allSettled(promises)
|
|
||||||
|
|
||||||
const publishedRelays: string[] = []
|
|
||||||
|
|
||||||
results.forEach((result, index) => {
|
|
||||||
if (result.status === 'fulfilled') publishedRelays.push(relays[index])
|
|
||||||
})
|
|
||||||
|
|
||||||
if (publishedRelays.length === 0) {
|
|
||||||
const failedPublishes: any[] = []
|
|
||||||
const fallbackRejectionReason =
|
|
||||||
'Attempt to publish an event has been rejected with unknown reason.'
|
|
||||||
|
|
||||||
results.forEach((res, index) => {
|
|
||||||
if (res.status === 'rejected') {
|
|
||||||
failedPublishes.push({
|
|
||||||
relay: relays[index],
|
|
||||||
error: res.reason
|
|
||||||
? res.reason.message || fallbackRejectionReason
|
|
||||||
: fallbackRejectionReason
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
throw failedPublishes
|
|
||||||
}
|
|
||||||
|
|
||||||
return publishedRelays
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signs an event with private key (if it is present in local storage) or
|
|
||||||
* with browser extension (if it is present) or
|
|
||||||
* with nSecBunker instance.
|
|
||||||
* @param event - unsigned nostr event.
|
|
||||||
* @returns - a promised that is resolved with signed nostr event.
|
|
||||||
*/
|
|
||||||
signEvent = async (
|
|
||||||
event: UnsignedEvent | EventTemplate
|
|
||||||
): Promise<SignedEvent> => {
|
|
||||||
if (!this.generatedKeys) {
|
|
||||||
throw new Error(`Private & public key pair is not found.`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { private: nsec } = this.generatedKeys
|
|
||||||
const privateKey = nip19.decode(nsec).data as Uint8Array
|
|
||||||
|
|
||||||
const signedEvent = finalizeEvent(event, privateKey)
|
|
||||||
|
|
||||||
this.verifySignedEvent(signedEvent)
|
|
||||||
|
|
||||||
return Promise.resolve(signedEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
nip04Encrypt = async (receiver: string, content: string) => {
|
|
||||||
if (!this.generatedKeys) {
|
|
||||||
throw new Error(`Private & public key pair is not found.`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { private: nsec } = this.generatedKeys
|
|
||||||
const privateKey = nip19.decode(nsec).data as Uint8Array
|
|
||||||
|
|
||||||
const encrypted = await nip04.encrypt(privateKey, receiver, content)
|
|
||||||
|
|
||||||
return encrypted
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a Direct Message (DM) to a recipient, encrypting the content and handling authentication.
|
|
||||||
* @param fileUrl The URL of the encrypted zip file to be included in the DM.
|
|
||||||
* @param encryptionKey The encryption key used to decrypt the zip file to be included in the DM.
|
|
||||||
* @param pubkey The public key of the recipient.
|
|
||||||
* @param isSigner Boolean indicating whether the recipient is a signer or viewer.
|
|
||||||
* @param setAuthUrl Function to set the authentication URL in the component state.
|
|
||||||
*/
|
|
||||||
sendDM = async (pubkey: string, message: string) => {
|
|
||||||
// Set up timeout promise to handle encryption timeout
|
|
||||||
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
reject(new Error('Timeout occurred'))
|
|
||||||
}, 60000) // Timeout duration = 60 seconds
|
|
||||||
})
|
|
||||||
|
|
||||||
// Encrypt the DM content, with timeout
|
|
||||||
const encrypted = await Promise.race([
|
|
||||||
this.nip04Encrypt(this.npubToHex(pubkey) as string, message),
|
|
||||||
timeoutPromise
|
|
||||||
])
|
|
||||||
|
|
||||||
// Return if encryption failed
|
|
||||||
if (!encrypted) throw new Error('Message was not encrypted.')
|
|
||||||
|
|
||||||
// Construct event metadata for the DM
|
|
||||||
const event: EventTemplate = {
|
|
||||||
kind: 4, // DM event type
|
|
||||||
content: encrypted, // Encrypted DM content
|
|
||||||
created_at: Math.floor(Date.now() / 1000), // Current timestamp
|
|
||||||
tags: [['p', this.npubToHex(pubkey) as string]] // Tag with recipient's public key
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign the DM event
|
|
||||||
const signedEvent = await this.signEvent(event)
|
|
||||||
|
|
||||||
// Return if event signing failed
|
|
||||||
if (!signedEvent) throw new Error('Message was not signed.')
|
|
||||||
|
|
||||||
// These relay will be used to send a DM. Recipient has to read from these relays to receive a DM.
|
|
||||||
const relays = [
|
|
||||||
'wss://relay.damus.io/',
|
|
||||||
'wss://nos.lol/',
|
|
||||||
'wss://relay.snort.social'
|
|
||||||
]
|
|
||||||
|
|
||||||
// Publish the signed DM event to the recipient's read relays
|
|
||||||
return await this.publishEvent(signedEvent, relays)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param hexKey hex private or public key
|
|
||||||
* @returns whether or not is key valid
|
|
||||||
*/
|
|
||||||
validateHex = (hexKey: string) => {
|
|
||||||
return hexKey.match(/^[a-f0-9]{64}$/)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NPUB provided - it will convert NPUB to HEX
|
|
||||||
* HEX provided - it will return HEX
|
|
||||||
*
|
|
||||||
* @param pubKey in NPUB, HEX format
|
|
||||||
* @returns HEX format
|
|
||||||
*/
|
|
||||||
npubToHex = (pubKey: string): string | null => {
|
|
||||||
// If key is NPUB
|
|
||||||
if (pubKey.startsWith('npub1')) {
|
|
||||||
try {
|
|
||||||
return nip19.decode(pubKey).data as string
|
|
||||||
} catch (error) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// valid hex key
|
|
||||||
if (this.validateHex(pubKey)) return pubKey
|
|
||||||
|
|
||||||
// Not a valid hex key
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
generateKeys = (): Keys => {
|
|
||||||
const nsec = generateSecretKey()
|
|
||||||
|
|
||||||
return { private: nip19.nsecEncode(nsec), public: getPublicKey(nsec) }
|
|
||||||
}
|
|
||||||
|
|
||||||
verifySignedEvent = (event: SignedEvent) => {
|
|
||||||
const isGood = verifyEvent(event)
|
|
||||||
|
|
||||||
if (!isGood) {
|
|
||||||
throw new Error(
|
|
||||||
'Signed event did not pass verification. Check sig, id and pubkey.'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export * from './NostrController'
|
|
@ -1,5 +1,5 @@
|
|||||||
import { PageProps, Link, graphql } from 'gatsby'
|
import { PageProps, Link, graphql } from 'gatsby'
|
||||||
import React, { useState } from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import Layout from '../components/layout'
|
import Layout from '../components/layout'
|
||||||
import Seo from '../components/seo'
|
import Seo from '../components/seo'
|
||||||
@ -20,7 +20,6 @@ import {
|
|||||||
import contactBg from '../images/contact_bg.jpg'
|
import contactBg from '../images/contact_bg.jpg'
|
||||||
|
|
||||||
import '../styledComponents/contact.css'
|
import '../styledComponents/contact.css'
|
||||||
import { NostrController } from '../controllers'
|
|
||||||
|
|
||||||
type DataProps = {
|
type DataProps = {
|
||||||
site: {
|
site: {
|
||||||
@ -33,17 +32,6 @@ type DataProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Contact: React.FC<PageProps<DataProps>> = ({ data, location }) => {
|
const Contact: React.FC<PageProps<DataProps>> = ({ data, location }) => {
|
||||||
const nostrController = NostrController.getInstance()
|
|
||||||
|
|
||||||
const [name, setName] = useState<string>()
|
|
||||||
const [email, setEmail] = useState<string>()
|
|
||||||
const [subject, setSubject] = useState<string>()
|
|
||||||
const [message, setMessage] = useState<string>()
|
|
||||||
const [notification, setNotification] = useState<string>()
|
|
||||||
|
|
||||||
const getBorderStyle = (value: string | undefined) =>
|
|
||||||
value === undefined ? {} : value ? {} : { border: '1px solid red' }
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
location={location}
|
location={location}
|
||||||
@ -64,35 +52,8 @@ const Contact: React.FC<PageProps<DataProps>> = ({ data, location }) => {
|
|||||||
|
|
||||||
<form
|
<form
|
||||||
className="kwes-form"
|
className="kwes-form"
|
||||||
onSubmit={async (evt) => {
|
method="POST"
|
||||||
evt.preventDefault()
|
action="https://kwes.io/api/foreign/forms/mxKuyK4lxZWnG2WNH3ga"
|
||||||
|
|
||||||
if (name && email && subject && message) {
|
|
||||||
const res = await nostrController
|
|
||||||
.sendDM(
|
|
||||||
'npub1dc0000002dtkw7et06sztc9nvk79r6yju8gk69sr88rgrg0e8cvsnptgyv',
|
|
||||||
`Name: ${name}
|
|
||||||
Email: ${email}
|
|
||||||
Subject: ${subject}
|
|
||||||
Message: ${message}`
|
|
||||||
)
|
|
||||||
.catch((err) => {
|
|
||||||
setNotification(
|
|
||||||
`Something went wrong. Please check the console for more information. Please try one more time.`
|
|
||||||
)
|
|
||||||
|
|
||||||
console.log(`Sending message error: `, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (res && res.length) {
|
|
||||||
setNotification(`Message sent. We'll contact you shortly.`)
|
|
||||||
} else {
|
|
||||||
setNotification(
|
|
||||||
`Something went wrong. Please try one more time.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<StyledLabel htmlFor="name" className="form-label">
|
<StyledLabel htmlFor="name" className="form-label">
|
||||||
@ -104,11 +65,6 @@ Message: ${message}`
|
|||||||
id="name"
|
id="name"
|
||||||
name="name"
|
name="name"
|
||||||
rules="required|max:50"
|
rules="required|max:50"
|
||||||
onChange={(evt) => {
|
|
||||||
setName(evt.target.value)
|
|
||||||
setNotification(undefined)
|
|
||||||
}}
|
|
||||||
style={getBorderStyle(name)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
@ -122,11 +78,6 @@ Message: ${message}`
|
|||||||
name="email"
|
name="email"
|
||||||
rules="required|email"
|
rules="required|email"
|
||||||
aria-describedby="emailHelp"
|
aria-describedby="emailHelp"
|
||||||
onChange={(evt) => {
|
|
||||||
setEmail(evt.target.value)
|
|
||||||
setNotification(undefined)
|
|
||||||
}}
|
|
||||||
style={getBorderStyle(email)}
|
|
||||||
/>
|
/>
|
||||||
<div id="emailHelp" className="form-text">
|
<div id="emailHelp" className="form-text">
|
||||||
We'll never share your email with anyone else.
|
We'll never share your email with anyone else.
|
||||||
@ -142,11 +93,6 @@ Message: ${message}`
|
|||||||
id="subject"
|
id="subject"
|
||||||
name="subject"
|
name="subject"
|
||||||
rules="required|max:50"
|
rules="required|max:50"
|
||||||
onChange={(evt) => {
|
|
||||||
setSubject(evt.target.value)
|
|
||||||
setNotification(undefined)
|
|
||||||
}}
|
|
||||||
style={getBorderStyle(subject)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
@ -159,18 +105,12 @@ Message: ${message}`
|
|||||||
name="message"
|
name="message"
|
||||||
rows="5"
|
rows="5"
|
||||||
rules="required|max:200"
|
rules="required|max:200"
|
||||||
onChange={(evt) => {
|
|
||||||
setMessage(evt.target.value)
|
|
||||||
setNotification(undefined)
|
|
||||||
}}
|
|
||||||
style={getBorderStyle(message)}
|
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<SolidButton theme="dark">Submit</SolidButton>
|
<SolidButton theme="dark">Submit</SolidButton>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{notification && <span>{notification}</span>}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
<ContactBackground src={contactBg} info="Book a Demo" />
|
<ContactBackground src={contactBg} info="Book a Demo" />
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export * from './nostr'
|
|
@ -1,14 +0,0 @@
|
|||||||
export interface SignedEvent {
|
|
||||||
kind: number
|
|
||||||
tags: string[][]
|
|
||||||
content: string
|
|
||||||
created_at: number
|
|
||||||
pubkey: string
|
|
||||||
id: string
|
|
||||||
sig: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Keys {
|
|
||||||
private: string
|
|
||||||
public: string
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user