Compare commits
4 commits
c556d28587
...
07df85efe6
| Author | SHA1 | Date | |
|---|---|---|---|
| 07df85efe6 | |||
| 297b05e8e5 | |||
| c5ba4a31be | |||
| 43356f5dfb |
5 changed files with 108 additions and 61 deletions
|
|
@ -59,7 +59,7 @@
|
||||||
"light-bolt11-decoder": "^3.2.0",
|
"light-bolt11-decoder": "^3.2.0",
|
||||||
"lucide-vue-next": "^0.474.0",
|
"lucide-vue-next": "^0.474.0",
|
||||||
"ngeohash": "^0.6.3",
|
"ngeohash": "^0.6.3",
|
||||||
"nostr-tools": "2.15.0",
|
"nostr-tools": "^2.23.3",
|
||||||
"pinia": "^2.3.1",
|
"pinia": "^2.3.1",
|
||||||
"qr-scanner": "^1.4.2",
|
"qr-scanner": "^1.4.2",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
|
|
|
||||||
88
pnpm-lock.yaml
generated
88
pnpm-lock.yaml
generated
|
|
@ -57,8 +57,8 @@ importers:
|
||||||
specifier: ^0.6.3
|
specifier: ^0.6.3
|
||||||
version: 0.6.3
|
version: 0.6.3
|
||||||
nostr-tools:
|
nostr-tools:
|
||||||
specifier: 2.15.0
|
specifier: ^2.23.3
|
||||||
version: 2.15.0(typescript@5.6.3)
|
version: 2.23.5(typescript@5.6.3)
|
||||||
pinia:
|
pinia:
|
||||||
specifier: ^2.3.1
|
specifier: ^2.3.1
|
||||||
version: 2.3.1(typescript@5.6.3)(vue@3.5.34(typescript@5.6.3))
|
version: 2.3.1(typescript@5.6.3)(vue@3.5.34(typescript@5.6.3))
|
||||||
|
|
@ -1247,22 +1247,17 @@ packages:
|
||||||
resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==}
|
resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==}
|
||||||
engines: {node: '>= 12.13.0'}
|
engines: {node: '>= 12.13.0'}
|
||||||
|
|
||||||
'@noble/ciphers@0.5.3':
|
'@noble/ciphers@2.1.1':
|
||||||
resolution: {integrity: sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==}
|
resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==}
|
||||||
|
engines: {node: '>= 20.19.0'}
|
||||||
|
|
||||||
'@noble/curves@1.1.0':
|
'@noble/curves@2.0.1':
|
||||||
resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==}
|
resolution: {integrity: sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==}
|
||||||
|
engines: {node: '>= 20.19.0'}
|
||||||
|
|
||||||
'@noble/curves@1.2.0':
|
'@noble/hashes@2.0.1':
|
||||||
resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==}
|
resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==}
|
||||||
|
engines: {node: '>= 20.19.0'}
|
||||||
'@noble/hashes@1.3.1':
|
|
||||||
resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==}
|
|
||||||
engines: {node: '>= 16'}
|
|
||||||
|
|
||||||
'@noble/hashes@1.3.2':
|
|
||||||
resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==}
|
|
||||||
engines: {node: '>= 16'}
|
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
|
|
@ -1478,11 +1473,14 @@ packages:
|
||||||
'@scure/base@1.1.1':
|
'@scure/base@1.1.1':
|
||||||
resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==}
|
resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==}
|
||||||
|
|
||||||
'@scure/bip32@1.3.1':
|
'@scure/base@2.0.0':
|
||||||
resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==}
|
resolution: {integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==}
|
||||||
|
|
||||||
'@scure/bip39@1.2.1':
|
'@scure/bip32@2.0.1':
|
||||||
resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==}
|
resolution: {integrity: sha512-4Md1NI5BzoVP+bhyJaY3K6yMesEFzNS1sE/cP+9nuvE7p/b0kx9XbpDHHFl8dHtufcbdHRUUQdRqLIPHN/s7yA==}
|
||||||
|
|
||||||
|
'@scure/bip39@2.0.1':
|
||||||
|
resolution: {integrity: sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg==}
|
||||||
|
|
||||||
'@sindresorhus/is@4.6.0':
|
'@sindresorhus/is@4.6.0':
|
||||||
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
||||||
|
|
@ -3535,8 +3533,8 @@ packages:
|
||||||
resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
|
resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
nostr-tools@2.15.0:
|
nostr-tools@2.23.5:
|
||||||
resolution: {integrity: sha512-Jj/+UFbu3JbTAWP4ipPFNuyD4W5eVRBNAP+kmnoRCYp3bLmTrlQ0Qhs5O1xSQJTFpjdZqoS0zZOUKdxUdjc+pw==}
|
resolution: {integrity: sha512-Fa7ZlUdjfUW1P4E7H3yBexhOHYi18XNyvd2n7eNHkYR085xADX6Y8V8Vm7nT/XQajaFOBrptXmVIGkJ2E4vfVw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '>=5.0.0'
|
typescript: '>=5.0.0'
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
|
|
@ -6204,19 +6202,13 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
|
|
||||||
'@noble/ciphers@0.5.3': {}
|
'@noble/ciphers@2.1.1': {}
|
||||||
|
|
||||||
'@noble/curves@1.1.0':
|
'@noble/curves@2.0.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.3.1
|
'@noble/hashes': 2.0.1
|
||||||
|
|
||||||
'@noble/curves@1.2.0':
|
'@noble/hashes@2.0.1': {}
|
||||||
dependencies:
|
|
||||||
'@noble/hashes': 1.3.2
|
|
||||||
|
|
||||||
'@noble/hashes@1.3.1': {}
|
|
||||||
|
|
||||||
'@noble/hashes@1.3.2': {}
|
|
||||||
|
|
||||||
'@nodelib/fs.scandir@2.1.5':
|
'@nodelib/fs.scandir@2.1.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -6362,16 +6354,18 @@ snapshots:
|
||||||
|
|
||||||
'@scure/base@1.1.1': {}
|
'@scure/base@1.1.1': {}
|
||||||
|
|
||||||
'@scure/bip32@1.3.1':
|
'@scure/base@2.0.0': {}
|
||||||
dependencies:
|
|
||||||
'@noble/curves': 1.1.0
|
|
||||||
'@noble/hashes': 1.3.1
|
|
||||||
'@scure/base': 1.1.1
|
|
||||||
|
|
||||||
'@scure/bip39@1.2.1':
|
'@scure/bip32@2.0.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/hashes': 1.3.1
|
'@noble/curves': 2.0.1
|
||||||
'@scure/base': 1.1.1
|
'@noble/hashes': 2.0.1
|
||||||
|
'@scure/base': 2.0.0
|
||||||
|
|
||||||
|
'@scure/bip39@2.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@noble/hashes': 2.0.1
|
||||||
|
'@scure/base': 2.0.0
|
||||||
|
|
||||||
'@sindresorhus/is@4.6.0': {}
|
'@sindresorhus/is@4.6.0': {}
|
||||||
|
|
||||||
|
|
@ -8539,14 +8533,14 @@ snapshots:
|
||||||
|
|
||||||
normalize-url@6.1.0: {}
|
normalize-url@6.1.0: {}
|
||||||
|
|
||||||
nostr-tools@2.15.0(typescript@5.6.3):
|
nostr-tools@2.23.5(typescript@5.6.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/ciphers': 0.5.3
|
'@noble/ciphers': 2.1.1
|
||||||
'@noble/curves': 1.2.0
|
'@noble/curves': 2.0.1
|
||||||
'@noble/hashes': 1.3.1
|
'@noble/hashes': 2.0.1
|
||||||
'@scure/base': 1.1.1
|
'@scure/base': 2.0.0
|
||||||
'@scure/bip32': 1.3.1
|
'@scure/bip32': 2.0.1
|
||||||
'@scure/bip39': 1.2.1
|
'@scure/bip39': 2.0.1
|
||||||
nostr-wasm: 0.1.0
|
nostr-wasm: 0.1.0
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.6.3
|
typescript: 5.6.3
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { toast } from 'vue-sonner'
|
||||||
import { CalendarDays, Map, Heart, Search, Plus } from 'lucide-vue-next'
|
import { CalendarDays, Map, Heart, Search, Plus } from 'lucide-vue-next'
|
||||||
import AppShell from '@/components/layout/AppShell.vue'
|
import AppShell from '@/components/layout/AppShell.vue'
|
||||||
import type { BottomTab } from '@/components/layout/BottomNav.vue'
|
import type { BottomTab } from '@/components/layout/BottomNav.vue'
|
||||||
|
|
@ -15,6 +16,7 @@ import type { CreateEventRequest } from '@/modules/activities/types/ticket'
|
||||||
import CreateEventDialog from '@/modules/activities/components/CreateEventDialog.vue'
|
import CreateEventDialog from '@/modules/activities/components/CreateEventDialog.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { isAuthenticated, currentUser } = useAuth()
|
const { isAuthenticated, currentUser } = useAuth()
|
||||||
const activitiesStore = useActivitiesStore()
|
const activitiesStore = useActivitiesStore()
|
||||||
|
|
@ -25,9 +27,9 @@ const { isAdmin, autoApprove } = useApprovalState()
|
||||||
const { loadOwnEvents } = useActivities()
|
const { loadOwnEvents } = useActivities()
|
||||||
|
|
||||||
// Settings dropped — theme/lang/currency now live in the shared profile sheet.
|
// Settings dropped — theme/lang/currency now live in the shared profile sheet.
|
||||||
// Create lives in the bottom nav (auth-gated): activity creation is a deliberate
|
// Create lives in the bottom nav: when logged out, tapping it shows an
|
||||||
// act, surfacing it as a tab keeps it one tap away when authed and out of the
|
// auth-prompt toast (mirroring BookmarkButton/RSVPButton) instead of
|
||||||
// way when not. Per-app placement deliberation tracked at #53.
|
// opening the dialog. Per-app placement deliberation tracked at #53.
|
||||||
const tabs = computed<BottomTab[]>(() => [
|
const tabs = computed<BottomTab[]>(() => [
|
||||||
{ name: t('activities.nav.feed'), icon: Search, path: '/activities' },
|
{ name: t('activities.nav.feed'), icon: Search, path: '/activities' },
|
||||||
{ name: t('activities.nav.calendar'), icon: CalendarDays, path: '/activities/calendar' },
|
{ name: t('activities.nav.calendar'), icon: CalendarDays, path: '/activities/calendar' },
|
||||||
|
|
@ -35,6 +37,15 @@ const tabs = computed<BottomTab[]>(() => [
|
||||||
name: t('activities.createNew'),
|
name: t('activities.createNew'),
|
||||||
icon: Plus,
|
icon: Plus,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
if (!isAuthenticated.value) {
|
||||||
|
toast.info('Log in to create an activity', {
|
||||||
|
action: {
|
||||||
|
label: 'Log in',
|
||||||
|
onClick: () => router.push('/login'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
// Defensively clear any lingering edit selection so the Create
|
// Defensively clear any lingering edit selection so the Create
|
||||||
// tap always opens in Create mode regardless of a prior Edit.
|
// tap always opens in Create mode regardless of a prior Edit.
|
||||||
activitiesStore.editingEvent = null
|
activitiesStore.editingEvent = null
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,28 @@ function foldDateTime(date: string, time: string): string {
|
||||||
return time ? `${date}T${time}` : date
|
return time ? `${date}T${time}` : date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stamp the form's wall-clock datetime with the user's local UTC offset
|
||||||
|
// before sending it to the LNbits events backend. Without this, the
|
||||||
|
// backend's `_to_unix` (nostr_publisher.py) treats a naive ISO string
|
||||||
|
// as UTC, so e.g. "08:00" entered in CEST gets stored as 08:00 UTC and
|
||||||
|
// the NIP-52 `start` tag is off by the user's offset on the relay
|
||||||
|
// — the detail page then renders it +offset (08:00 → 10:00 in CEST).
|
||||||
|
// Preserving the user's intended wall-clock means stamping it here.
|
||||||
|
// Date-only values (no "T") pass through unchanged.
|
||||||
|
function withLocalTzOffset(value: string): string {
|
||||||
|
if (!value || !value.includes('T')) return value
|
||||||
|
// The form's "YYYY-MM-DDTHH:MM" is parsed by JS Date as local time;
|
||||||
|
// getTimezoneOffset() returns minutes west of UTC, so negate it.
|
||||||
|
const offMin = -new Date(value).getTimezoneOffset()
|
||||||
|
const sign = offMin >= 0 ? '+' : '-'
|
||||||
|
const abs = Math.abs(offMin)
|
||||||
|
const hh = String(Math.floor(abs / 60)).padStart(2, '0')
|
||||||
|
const mm = String(abs % 60).padStart(2, '0')
|
||||||
|
// Include `:00` seconds for compatibility with older Python
|
||||||
|
// `datetime.fromisoformat` (pre-3.11 won't accept "HH:MM+HH:MM").
|
||||||
|
return `${value}:00${sign}${hh}:${mm}`
|
||||||
|
}
|
||||||
|
|
||||||
const formSchema = toTypedSchema(
|
const formSchema = toTypedSchema(
|
||||||
z
|
z
|
||||||
.object({
|
.object({
|
||||||
|
|
@ -138,8 +160,11 @@ interface BannerImage extends UploadedImage {
|
||||||
}
|
}
|
||||||
const bannerImages = ref<BannerImage[]>([])
|
const bannerImages = ref<BannerImage[]>([])
|
||||||
|
|
||||||
// Inverse of foldDateTime: split a stored "YYYY-MM-DD[THH:MM]" back
|
// Inverse of foldDateTime: split a stored "YYYY-MM-DD[THH:MM[:SS][±HH:MM]]"
|
||||||
// into separate date + time pieces for the form inputs.
|
// back into separate date + time pieces for the form inputs. The
|
||||||
|
// time slice trims to "HH:MM" so any seconds + offset suffix added by
|
||||||
|
// withLocalTzOffset on submit drops cleanly — the user sees the same
|
||||||
|
// wall-clock they originally entered when re-editing.
|
||||||
function splitDateTime(value: string | null | undefined): { date: string; time: string } {
|
function splitDateTime(value: string | null | undefined): { date: string; time: string } {
|
||||||
if (!value) return { date: '', time: '' }
|
if (!value) return { date: '', time: '' }
|
||||||
const [date, time = ''] = value.split('T')
|
const [date, time = ''] = value.split('T')
|
||||||
|
|
@ -267,9 +292,8 @@ const onSubmit = form.handleSubmit(async (formValues) => {
|
||||||
try {
|
try {
|
||||||
const eventData: CreateEventRequest = {
|
const eventData: CreateEventRequest = {
|
||||||
name: formValues.name,
|
name: formValues.name,
|
||||||
event_start_date: foldDateTime(
|
event_start_date: withLocalTzOffset(
|
||||||
formValues.event_start_date,
|
foldDateTime(formValues.event_start_date, formValues.event_start_time)
|
||||||
formValues.event_start_time
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
if (!isEditMode.value) {
|
if (!isEditMode.value) {
|
||||||
|
|
@ -281,9 +305,8 @@ const onSubmit = form.handleSubmit(async (formValues) => {
|
||||||
// Optional fields — only include if provided
|
// Optional fields — only include if provided
|
||||||
if (formValues.info) eventData.info = formValues.info
|
if (formValues.info) eventData.info = formValues.info
|
||||||
if (formValues.event_end_date) {
|
if (formValues.event_end_date) {
|
||||||
eventData.event_end_date = foldDateTime(
|
eventData.event_end_date = withLocalTzOffset(
|
||||||
formValues.event_end_date,
|
foldDateTime(formValues.event_end_date, formValues.event_end_time)
|
||||||
formValues.event_end_time
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (formValues.location) eventData.location = formValues.location
|
if (formValues.location) eventData.location = formValues.location
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { SimplePool, type Filter, type Event, type Relay } from 'nostr-tools'
|
import { SimplePool, type Filter, type Event, type Relay } from 'nostr-tools'
|
||||||
|
import type { SubscribeManyParams, SubCloser } from 'nostr-tools/abstract-pool'
|
||||||
import { BaseService } from '@/core/base/BaseService'
|
import { BaseService } from '@/core/base/BaseService'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
|
@ -438,7 +439,7 @@ export class RelayHub extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recreate the subscription
|
// Recreate the subscription
|
||||||
const subscription = this.pool.subscribeMany(availableRelays, config.filters, {
|
const subscription = this.poolSubscribe(availableRelays, config.filters, {
|
||||||
onevent: (event: Event) => {
|
onevent: (event: Event) => {
|
||||||
config.onEvent?.(event)
|
config.onEvent?.(event)
|
||||||
this.emit('event', { subscriptionId: id, event, relay: 'unknown' })
|
this.emit('event', { subscriptionId: id, event, relay: 'unknown' })
|
||||||
|
|
@ -482,7 +483,7 @@ export class RelayHub extends BaseService {
|
||||||
|
|
||||||
|
|
||||||
// Create subscription using the pool
|
// Create subscription using the pool
|
||||||
const subscription = this.pool.subscribeMany(availableRelays, config.filters, {
|
const subscription = this.poolSubscribe(availableRelays, config.filters, {
|
||||||
onevent: (event: Event) => {
|
onevent: (event: Event) => {
|
||||||
config.onEvent?.(event)
|
config.onEvent?.(event)
|
||||||
this.emit('event', { subscriptionId: config.id, event, relay: 'unknown' })
|
this.emit('event', { subscriptionId: config.id, event, relay: 'unknown' })
|
||||||
|
|
@ -550,6 +551,24 @@ export class RelayHub extends BaseService {
|
||||||
return { success: successful, total }
|
return { success: successful, total }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nostr-tools 2.23+ deprecated the Filter[] form on pool.subscribeMany; route
|
||||||
|
// single-filter through pool.subscribe and multi-filter through subscribeMap
|
||||||
|
// so a single REQ-per-relay still carries every filter.
|
||||||
|
private poolSubscribe(
|
||||||
|
relays: string[],
|
||||||
|
filters: Filter[],
|
||||||
|
params: SubscribeManyParams
|
||||||
|
): SubCloser {
|
||||||
|
if (filters.length === 0) {
|
||||||
|
throw new Error('Cannot subscribe with empty filters')
|
||||||
|
}
|
||||||
|
if (filters.length === 1) {
|
||||||
|
return this.pool.subscribe(relays, filters[0], params)
|
||||||
|
}
|
||||||
|
const requests = relays.flatMap(url => filters.map(filter => ({ url, filter })))
|
||||||
|
return this.pool.subscribeMap(requests, params)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query events from relays (one-time fetch)
|
* Query events from relays (one-time fetch)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue