diff --git a/bun.lockb b/bun.lockb index be51bbb..36ac7c3 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 22865da..c0cc5de 100644 --- a/package.json +++ b/package.json @@ -15,17 +15,17 @@ "@iconify/svelte": "^4.0.2", "@sveltejs/adapter-node": "^5.2.9", "@sveltejs/adapter-auto": "^3.3.1", - "@sveltejs/kit": "^2.8.5", + "@sveltejs/kit": "^2.9.0", "@sveltejs/vite-plugin-svelte": "^4.0.2", "prettier": "^3.4.1", "prettier-plugin-svelte": "^3.3.2", - "svelte": "^5.2.10", + "svelte": "^5.2.11", "svelte-check": "^4.1.0", "typescript": "^5.7.2", "vite": "^5.4.11" }, "dependencies": { - "@arx/utils": "git+ssh://git@git.arx-ccn.com:222/Arx/ts-utils", + "@arx/utils": "git+ssh://git@git.arx-ccn.com:222/Arx/ts-utils#03163e9f4a07b011a28a8f97c90852ecfc806ddd", "@gandlaf21/bc-ur": "^1.1.12", "@nostr-dev-kit/ndk": "^2.10.7", "@nostr-dev-kit/ndk-cache-dexie": "^2.5.8", diff --git a/src/components/NostrIdentifier.svelte b/src/components/NostrIdentifier.svelte index 6380e0c..8b7c5b8 100644 --- a/src/components/NostrIdentifier.svelte +++ b/src/components/NostrIdentifier.svelte @@ -111,7 +111,7 @@ {/if} - {#if npub !== displayString && !emailAddress} + {#if npub !== displayString} {displayString} diff --git a/src/lib/random.ts b/src/lib/random.ts new file mode 100644 index 0000000..9da85df --- /dev/null +++ b/src/lib/random.ts @@ -0,0 +1,244 @@ +export const adjectives = [ + // Colors + 'crimson', + 'azure', + 'golden', + 'silver', + 'emerald', + 'violet', + 'cobalt', + 'scarlet', + 'obsidian', + 'jade', + 'amber', + 'coral', + 'indigo', + 'sapphire', + 'ruby', + 'onyx', + // Elements + 'frost', + 'flame', + 'storm', + 'thunder', + 'crystal', + 'shadow', + 'lunar', + 'solar', + 'plasma', + 'terra', + 'aether', + 'void', + 'cosmic', + 'astral', + 'nebula', + 'nova', + // Power + 'mega', + 'ultra', + 'hyper', + 'super', + 'prime', + 'apex', + 'elite', + 'omega', + 'alpha', + 'delta', + 'sigma', + 'gamma', + 'beta', + 'epsilon', + 'zeta', + 'theta', + // Tech + 'cyber', + 'techno', + 'digital', + 'binary', + 'neural', + 'crypto', + 'matrix', + 'vector', + 'quantum', + 'nano', + 'laser', + 'cyber', + 'data', + 'pixel', + 'sonic', + 'hyper', + // Mystical + 'mystic', + 'arcane', + 'ethereal', + 'divine', + 'phantom', + 'spirit', + 'ancient', + 'chaos', + 'astral', + 'eldritch', + 'occult', + 'mythic', + 'sacred', + 'cursed', + 'blessed', + 'doom', + // Nature + 'savage', + 'primal', + 'feral', + 'wild', + 'fierce', + 'rapid', + 'swift', + 'silent', + 'deadly', + 'stealth', + 'shadow', + 'night', + 'dark', + 'light', + 'bright', + 'dawn', + // Epic + 'epic', + 'legendary', + 'mythic', + 'eternal', + 'immortal', + 'infinite', + 'supreme', + 'ultimate', + 'grand', + 'mighty', + 'noble', + 'royal', + 'heroic', + 'valor', + 'glory', + 'honor' +]; + + +export const animals = [ + // Classic Predators + 'wolf', + 'tiger', + 'lion', + 'eagle', + 'hawk', + 'bear', + 'panther', + 'falcon', + 'jaguar', + 'leopard', + 'lynx', + 'cobra', + 'viper', + 'python', + 'raptor', + 'shark', + // Mythical Dragons + 'dragon', + 'wyvern', + 'drake', + 'wyrm', + 'hydra', + 'basilisk', + 'tiamat', + 'ryuu', + 'fafnir', + 'bahamut', + 'ryu', + 'draco', + 'naga', + 'ouroboros', + 'lindworm', + 'lung', + // Fantasy + 'phoenix', + 'griffin', + 'unicorn', + 'pegasus', + 'chimera', + 'manticore', + 'sphinx', + 'kraken', + 'behemoth', + 'leviathan', + 'titan', + 'giant', + 'colossus', + 'golem', + 'gargoyle', + 'djinn', + // Norse + 'fenrir', + 'jormungandr', + 'sleipnir', + 'valkyrie', + 'einherjar', + 'huginn', + 'muninn', + 'garmr', + 'nidhogg', + 'ratatoskr', + 'hraesvelgr', + 'gullinkambi', + 'eikthyrnir', + 'duneyrr', + 'dvalinn', + 'dainn', + // Eastern + 'kitsune', + 'kirin', + 'byakko', + 'suzaku', + 'genbu', + 'seiryu', + 'oni', + 'tengu', + 'raiju', + 'baku', + 'nekomata', + 'tanuki', + 'kappa', + 'tsukumogami', + 'yokai', + 'orochi', + // Ancient + 'cyclops', + 'minotaur', + 'cerberus', + 'scylla', + 'typhon', + 'charybdis', + 'medusa', + 'harpy', + 'siren', + 'gorgon', + 'centaur', + 'satyr', + 'triton', + 'echidna', + 'lamia', + 'sphinx', + // Cosmic + 'nova', + 'pulsar', + 'quasar', + 'nebula', + 'vortex', + 'cosmic', + 'astral', + 'celestial', + 'starborn', + 'solaris', + 'lunaris', + 'eclipse', + 'meteor', + 'comet', + 'galaxy', + 'cosmos' +]; diff --git a/src/lib/stores.svelte.ts b/src/lib/stores.svelte.ts index 780a47b..d1614f3 100644 --- a/src/lib/stores.svelte.ts +++ b/src/lib/stores.svelte.ts @@ -9,18 +9,18 @@ const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'npub-email-cache' }); const nip07signer = new NDKNip07Signer(); export const _ndk = new NDKSvelte({ - explicitRelayUrls: [ - 'wss://relay.primal.net', - 'wss://relay.damus.io', - 'wss://relay.nostr.band', - 'wss://offchain.pub', - 'wss://relay.snort.social' - ], - autoConnectUserRelays: true, - relayAuthDefaultPolicy: async (r) => true, - enableOutboxModel: true, - // signer: nip07signer, - cacheAdapter: dexieAdapter + explicitRelayUrls: [ + 'wss://relay.primal.net', + 'wss://relay.damus.io', + 'wss://relay.nostr.band', + 'wss://offchain.pub', + 'wss://relay.snort.social' + ], + autoConnectUserRelays: false, + relayAuthDefaultPolicy: async (r) => true, + enableOutboxModel: true, + // signer: nip07signer, + cacheAdapter: dexieAdapter }); if (browser && localStorage.getItem('useNip07')) _ndk.signer = nip07signer; @@ -33,42 +33,42 @@ if (browser && _ndk.activeUser) activeUser.set(_ndk.activeUser); export const validSortOptions = ['stamps', 'date', 'sender', 'subject']; export const baseHue = writable( - browser ? parseInt(localStorage.getItem('baseHue')) || 200 : 200 + browser ? parseInt(localStorage.getItem('baseHue')) || 200 : 200 ); export const groupByStamps = writable( - browser ? localStorage.getItem('groupByStamps') === 'true' : false + browser ? localStorage.getItem('groupByStamps') === 'true' : false ); export const sortBy = writable(browser ? localStorage.getItem('sortBy') || 'date' : 'date'); export const dateFormat = writable( - browser ? localStorage.getItem('dateFormat') || 'y-m-d' : 'y-m-d' + browser ? localStorage.getItem('dateFormat') || 'y-m-d' : 'y-m-d' ); export const timeFormat = writable( - browser ? localStorage.getItem('timeFormat') || 'h:m:s' : 'h:m:s' + browser ? localStorage.getItem('timeFormat') || 'h:m:s' : 'h:m:s' ); export let pageTitle = writable('loading'); export let pageIcon = writable(''); if (browser) { - baseHue.subscribe((value) => { - document.documentElement.style.setProperty('--base-hue', value.toString()); - localStorage.setItem('baseHue', value.toString()); - }); + baseHue.subscribe((value) => { + document.documentElement.style.setProperty('--base-hue', value.toString()); + localStorage.setItem('baseHue', value.toString()); + }); - groupByStamps.subscribe((value) => { - localStorage.setItem('groupByStamps', value.toString()); - }); + groupByStamps.subscribe((value) => { + localStorage.setItem('groupByStamps', value.toString()); + }); - sortBy.subscribe((value) => { - if (!validSortOptions.includes(value)) value = 'date'; - localStorage.setItem('sortBy', value); - }); + sortBy.subscribe((value) => { + if (!validSortOptions.includes(value)) value = 'date'; + localStorage.setItem('sortBy', value); + }); - dateFormat.subscribe((value) => { - localStorage.setItem('dateFormat', value); - }); + dateFormat.subscribe((value) => { + localStorage.setItem('dateFormat', value); + }); - timeFormat.subscribe((value) => { - localStorage.setItem('timeFormat', value); - }); + timeFormat.subscribe((value) => { + localStorage.setItem('timeFormat', value); + }); } diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte index c2d8e1a..163abd4 100644 --- a/src/routes/settings/+page.svelte +++ b/src/routes/settings/+page.svelte @@ -3,182 +3,22 @@ activeUser, baseHue, groupByStamps, - ndk, pageIcon, pageTitle, sortBy, validSortOptions } from '$lib/stores.svelte'; - import ColorPicker from '../../components/ColorPicker.svelte'; - import SettingsLine from '../../components/SettingsLine.svelte'; - import Checkbox from '../../components/Checkbox.svelte'; - import Select from '../../components/Select.svelte'; - import DateFormatBuilder from '../../components/DateFormatBuilder.svelte'; import { onMount } from 'svelte'; - import * as nip98 from 'nostr-tools/nip98'; - import { NDKEvent, type NostrEvent } from '@nostr-dev-kit/ndk'; - import { PUBLIC_API_BASE_URL } from '$env/static/public'; - import TimeCountdown from '../../components/TimeCountdown.svelte'; - import { TokenInfoWithMailSubscriptionDuration } from '@arx/utils'; - - let subscribed = $state(false); - let hasUnlimitedSubscription = $state(false); - let subscriptionTill = $state(new Date(0)); - let aliases = $state([]); - let newAlias = $state(''); - - let cashuTokenForBuy = $state(''); - let tokenInfo = $state(); - let tokenInfoError = $state(''); - let buyTimeForNpub = $state(''); - - $effect(() => { - try { - tokenInfo = new TokenInfoWithMailSubscriptionDuration(cashuTokenForBuy); - tokenInfoError = ''; - } catch (e) { - tokenInfoError = (e as Error).message; - } - }); - - async function buyTime() { - if (!tokenInfo) return; - if (tokenInfoError) return; - if (!buyTimeForNpub) return; - try { - const response = await fetch(PUBLIC_API_BASE_URL + '/addTime/' + buyTimeForNpub, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - tokenString: cashuTokenForBuy - }) - }); - if (!response.ok) throw new Error(await response.json().then(r => r.message)); - reloadSubscriptionStatus(); - } catch (e) { - alert(e); - } - } - - async function reloadSubscriptionStatus() { - const subscriptionStatusURL = PUBLIC_API_BASE_URL + '/subscription/' + $ndk.activeUser.npub; - const subscriptionStatus = await fetch(subscriptionStatusURL).then(r => r.json<{ - subscribed: boolean, - subscribedUntil: number | null - }>()); - if (subscriptionStatus.subscribed) { - subscribed = true; - if (subscriptionStatus.subscribedUntil == null) - hasUnlimitedSubscription = true; - else { - subscriptionTill = new Date(subscriptionStatus.subscribedUntil * 1000); - } - } - } - - async function reloadAliases() { - const aliasesURL = PUBLIC_API_BASE_URL + '/aliases/' + $ndk.activeUser.npub; - const auth = await nip98.getToken(aliasesURL, 'post', async (e: NostrEvent) => { - const event = new NDKEvent($ndk, e); - await event.sign(); - return event.rawEvent(); - }, - true - ); - aliases = await fetch(aliasesURL, { - headers: { - Authorization: auth - } - }).then(r => r.json()); - } - - async function addAlias() { - const addAliasURL = PUBLIC_API_BASE_URL + '/addAlias'; - const auth = await nip98.getToken(addAliasURL, 'post', async (e: NostrEvent) => { - const event = new NDKEvent($ndk, e); - await event.sign(); - return event.rawEvent(); - }, - true - ); - await fetch(addAliasURL, { - method: 'POST', - headers: { - Authorization: auth, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - alias: newAlias - }) - }).then(r => r.json()).then(console.log); - newAlias = ''; - await reloadAliases(); - alert('Alias added'); - } - - function randomAlias() { - const adjectives = [ - // Colors - 'crimson', 'azure', 'golden', 'silver', 'emerald', 'violet', 'cobalt', 'scarlet', - 'obsidian', 'jade', 'amber', 'coral', 'indigo', 'sapphire', 'ruby', 'onyx', - // Elements - 'frost', 'flame', 'storm', 'thunder', 'crystal', 'shadow', 'lunar', 'solar', - 'plasma', 'terra', 'aether', 'void', 'cosmic', 'astral', 'nebula', 'nova', - // Power - 'mega', 'ultra', 'hyper', 'super', 'prime', 'apex', 'elite', 'omega', - 'alpha', 'delta', 'sigma', 'gamma', 'beta', 'epsilon', 'zeta', 'theta', - // Tech - 'cyber', 'techno', 'digital', 'binary', 'neural', 'crypto', 'matrix', 'vector', - 'quantum', 'nano', 'laser', 'cyber', 'data', 'pixel', 'sonic', 'hyper', - // Mystical - 'mystic', 'arcane', 'ethereal', 'divine', 'phantom', 'spirit', 'ancient', 'chaos', - 'astral', 'eldritch', 'occult', 'mythic', 'sacred', 'cursed', 'blessed', 'doom', - // Nature - 'savage', 'primal', 'feral', 'wild', 'fierce', 'rapid', 'swift', 'silent', - 'deadly', 'stealth', 'shadow', 'night', 'dark', 'light', 'bright', 'dawn', - // Epic - 'epic', 'legendary', 'mythic', 'eternal', 'immortal', 'infinite', 'supreme', 'ultimate', - 'grand', 'mighty', 'noble', 'royal', 'heroic', 'valor', 'glory', 'honor' - ]; - - const animals = [ - // Classic Predators - 'wolf', 'tiger', 'lion', 'eagle', 'hawk', 'bear', 'panther', 'falcon', - 'jaguar', 'leopard', 'lynx', 'cobra', 'viper', 'python', 'raptor', 'shark', - // Mythical Dragons - 'dragon', 'wyvern', 'drake', 'wyrm', 'hydra', 'basilisk', 'tiamat', 'ryuu', - 'fafnir', 'bahamut', 'ryu', 'draco', 'naga', 'ouroboros', 'lindworm', 'lung', - // Fantasy - 'phoenix', 'griffin', 'unicorn', 'pegasus', 'chimera', 'manticore', 'sphinx', 'kraken', - 'behemoth', 'leviathan', 'titan', 'giant', 'colossus', 'golem', 'gargoyle', 'djinn', - // Norse - 'fenrir', 'jormungandr', 'sleipnir', 'valkyrie', 'einherjar', 'huginn', 'muninn', 'garmr', - 'nidhogg', 'ratatoskr', 'hraesvelgr', 'gullinkambi', 'eikthyrnir', 'duneyrr', 'dvalinn', 'dainn', - // Eastern - 'kitsune', 'kirin', 'byakko', 'suzaku', 'genbu', 'seiryu', 'oni', 'tengu', - 'raiju', 'baku', 'nekomata', 'tanuki', 'kappa', 'tsukumogami', 'yokai', 'orochi', - // Ancient - 'cyclops', 'minotaur', 'cerberus', 'scylla', 'typhon', 'charybdis', 'medusa', 'harpy', - 'siren', 'gorgon', 'centaur', 'satyr', 'triton', 'echidna', 'lamia', 'sphinx', - // Cosmic - 'nova', 'pulsar', 'quasar', 'nebula', 'vortex', 'cosmic', 'astral', 'celestial', - 'starborn', 'solaris', 'lunaris', 'eclipse', 'meteor', 'comet', 'galaxy', 'cosmos' - ]; - - const adjective = adjectives[Math.floor(Math.random() * adjectives.length)]; - const animal = animals[Math.floor(Math.random() * animals.length)]; - const digits = Math.floor(Math.random() * 10000).toString().padStart(4, '0'); - - newAlias = `${adjective}-${animal}${digits}`; - } + import Checkbox from '../../components/Checkbox.svelte'; + import ColorPicker from '../../components/ColorPicker.svelte'; + import DateFormatBuilder from '../../components/DateFormatBuilder.svelte'; + import Select from '../../components/Select.svelte'; + import SettingsLine from './SettingsLine.svelte'; + import SubscriptionSettings from './SubscriptionSettings.svelte'; onMount(async () => { $pageTitle = 'Settings'; $pageIcon = 'si:settings-cute-line'; - await reloadAliases(); - await reloadSubscriptionStatus(); }); @@ -187,78 +27,19 @@ -

- npub.email offers email aliases that connect to your nostr account, converting incoming emails into Letters.
- These aliases can also serve as your nip 05 identifier. -

- -

Pricing:

- -
    -
  • 210 sats per day
  • -
  • Minimum purchase: 21 sats (2.4 hours)
  • -
  • Flexible duration: Purchase any length of time you need
  • -
- -

- Purchase time blocks to activate your email alias service for yourself or gift them to another user. Once the time - expires, you'll need to purchase additional time to continue using the service. Note: emails received while the - service is inactive will not be processed. -

- - - {#if hasUnlimitedSubscription} -

You are officially awesome!

-

- The Arx team has granted you an unlimited subscription to npub.email for your valuable contributions to Arx, - nostr or bitcoin.
- Keep up your great work and thank you! -

- {:else if subscribed && subscriptionTill.getTime() > 1000} - Your subscription will end in:
- - {:else} - You are not currently subscribed to npub.email - {/if} -
- - - {#if cashuTokenForBuy !== ''} - {#if tokenInfoError} -

{tokenInfoError}

- {:else} -

{tokenInfo.amount} sats

- - - {/if} - {/if} - - -

Buy for:

- - - -
- - {#if subscribed} - - {#each aliases as alias} - {alias}@npub.email - {:else} -

No aliases yet

- {/each} - - - - -
- {/if} +
- ({ + value: o, + label: o.charAt(0).toUpperCase() + o.slice(1).toLowerCase() + }))} + /> diff --git a/src/routes/settings/AliasSettings.svelte b/src/routes/settings/AliasSettings.svelte new file mode 100644 index 0000000..58e911e --- /dev/null +++ b/src/routes/settings/AliasSettings.svelte @@ -0,0 +1,124 @@ + + + + + + {#each aliases as alias} + + + + + {:else} + + {/each} + +
+ {alias}@npub.email + + deleteAlias(alias)} + /> +
No aliases yet
+ + + + +
diff --git a/src/routes/settings/BuyTime.svelte b/src/routes/settings/BuyTime.svelte new file mode 100644 index 0000000..4035784 --- /dev/null +++ b/src/routes/settings/BuyTime.svelte @@ -0,0 +1,62 @@ + + +{#if cashuTokenForBuy !== ''} + {#if tokenInfoError} +

{tokenInfoError}

+ {:else if tokenInfo} +

{tokenInfo.amount} sats

+ + + {/if} +{/if} + + +

Buy for:

+ + + diff --git a/src/components/SettingsLine.svelte b/src/routes/settings/SettingsLine.svelte similarity index 100% rename from src/components/SettingsLine.svelte rename to src/routes/settings/SettingsLine.svelte diff --git a/src/routes/settings/SubscriptionSettings.svelte b/src/routes/settings/SubscriptionSettings.svelte new file mode 100644 index 0000000..fc6809c --- /dev/null +++ b/src/routes/settings/SubscriptionSettings.svelte @@ -0,0 +1,71 @@ + + +

+ npub.email offers email aliases that connect to your nostr account, converting incoming emails + into Letters.
+ These aliases can also serve as your nip 05 identifier. +

+ +

Pricing:

+ +
    +
  • 210 sats per day
  • +
  • Minimum purchase: 21 sats (2.4 hours)
  • +
  • Flexible duration: Purchase any length of time you need
  • +
+ +

+ Purchase time blocks to activate your email alias service for yourself or gift them to another + user. Once the time expires, you'll need to purchase additional time to continue using the + service. Note: emails received while the service is inactive will not be processed. +

+ + + {#if hasUnlimitedSubscription} +

You are officially awesome!

+

+ The Arx team has granted you an unlimited subscription to npub.email for your valuable + contributions to Arx, nostr or bitcoin.
+ Keep up your great work and thank you! +

+ {:else if subscribed && subscriptionTill.getTime() > 1000} + Your subscription will end in:
+ + {:else} + You are not currently subscribed to npub.email + {/if} +
+ + + + + +{#if subscribed} + +{/if}