web_lib

Common web application libraries
git clone https://radroots.dev/git/web_lib.git
Log | Files | Refs | LICENSE

commit b724635e74628c6c0d0a9a1f262b1257b7d3a4c8
parent c5b88e683eedfd9aa8de78fec8be1d8c317324ec
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Sat, 26 Apr 2025 23:45:28 +0000

utils-nostr: add `@radroots/radroots-common-bindings` and edit classified event tags utils

Diffstat:
Mutils-nostr/package.json | 1+
Mutils-nostr/src/lib/tags.ts | 61++++++++++++++++++++++++++-----------------------------------
Mutils-nostr/src/lib/types.ts | 76+++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mutils-nostr/src/services/events/lib.ts | 3+--
Mutils-nostr/src/services/events/types.ts | 3+--
Mutils-nostr/src/types.ts | 2++
Mutils-nostr/src/util.ts | 9+++++++--
7 files changed, 83 insertions(+), 72 deletions(-)

diff --git a/utils-nostr/package.json b/utils-nostr/package.json @@ -29,6 +29,7 @@ "@noble/curves": "^1.6.0", "@noble/hashes": "^1.4.0", "@nostr-dev-kit/ndk": "^2.11.0", + "@radroots/radroots-common-bindings": "workspace:*", "@radroots/util": "workspace:*", "nostr-geotags": "workspace:*", "nostr-tools": "^2.10.4", diff --git a/utils-nostr/src/lib/tags.ts b/utils-nostr/src/lib/tags.ts @@ -1,4 +1,4 @@ -import { INostrClassified, INostrJobRequest, NostrEventTagClient, NostrEventTagLocation, NostrEventTagMediaUpload, NostrEventTagPrice, NostrEventTagPriceTier, NostrEventTagQuantity, type INostrFollow, type NostrEventTag, type NostrEventTags } from "$root"; +import { INostrClassified, INostrJobRequest, NostrEventTagClient, NostrEventTagLocation, NostrEventTagMediaUpload, NostrEventTagPrice, NostrEventTagPriceDiscount, NostrEventTagQuantity, type INostrFollow, type NostrEventTag, type NostrEventTags } from "$root"; import { ngeotags, type InputData as NostrGeotagsInputData } from "nostr-geotags"; export const tag_client = (opts: NostrEventTagClient, d_tag?: string): NostrEventTag => { @@ -20,16 +20,21 @@ export const tags_follow_list = (list: INostrFollow[]): NostrEventTags => { export const tag_classified_quantity = (opts: NostrEventTagQuantity): NostrEventTag => { const tag = [`quantity`, opts.amt, opts.unit]; if (opts.label) tag.push(opts.label); - return tag; + return tag.map(i => i.toLowerCase()); }; -export const tag_classified_price_tier = (tier: NostrEventTagPriceTier, price_key: string): NostrEventTag => { - const tag = [`price-tier`, tier.type, tier.value, price_key, tier.qty_min.toString()]; - return tag; + +export const tag_classified_price_discount = (discount: NostrEventTagPriceDiscount): NostrEventTag => { + const tag = [`price-discount-${Object.keys(discount)[0]}`]; + if (`mass` in discount) tag.push(...Object.values(discount.mass)); + else if (`quantity` in discount) tag.push(...Object.values(discount.quantity)); + else if (`subtotal` in discount) tag.push(...Object.values(discount.subtotal)); + else if (`total` in discount) tag.push(...Object.values(discount.total)); + return tag.map(i => i.toLowerCase()); }; -export const tag_classified_price = (tag_price: NostrEventTagPrice, tag_qty: NostrEventTagQuantity, quantity_key: string): NostrEventTag => { - const tag = [`price`, tag_price.amt, tag_price.currency, tag_qty.amt, tag_qty.unit, quantity_key]; - return tag; +export const tag_classified_price = (price: NostrEventTagPrice): NostrEventTag => { + const tag = [`price`, price.amt, price.currency, price.qty_amt, price.qty_unit, price.qty_key]; + return tag.map(i => i.toLowerCase()); }; export const tag_classified_image = (opts: NostrEventTagMediaUpload): NostrEventTag => { @@ -48,43 +53,29 @@ export const tag_classified_location = (opts: NostrEventTagLocation): NostrEvent }; export const tags_classified_location_geotags = (opts: NostrEventTagLocation): NostrEventTags => { - return ngeotags( - { - lat: opts.lat, - lon: opts.lng, - city: opts.city, - regionName: opts.region, - countryName: opts.country, - countryCode: opts.country_code - } satisfies NostrGeotagsInputData, - { - geohash: true, - gps: true, - city: true, - iso31662: true, - }); + const { lat, lng: lon, city, region: regionName, country: countryName, country_code: countryCode } = opts; + return ngeotags({ lat, lon, city, regionName, countryName, countryCode } satisfies NostrGeotagsInputData, { geohash: true, gps: true, city: true, iso31662: true }); }; - export const tags_classified = (opts: INostrClassified): NostrEventTags => { - const { d_tag, listing, quantities, location } = opts; + const { d_tag, listing, quantities, prices } = opts; const tags: NostrEventTags = [[`d`, d_tag]]; if (opts.client) tags.push(tag_client(opts.client, opts.d_tag)); for (const [k, v] of Object.entries(listing)) if (v) tags.push([k, v]); for (const quantity of quantities) { - const quantity_key = `${quantity.amt}-${quantity.unit}${quantity.label ? `-${quantity.label}` : ``}`.toLowerCase(); tags.push(tag_classified_quantity(quantity)); - for (const price of quantity.prices) { - const price_key = `${price.amt}-${price.currency}-${quantity.amt}-${quantity.unit}`.toLowerCase(); - tags.push(tag_classified_price(price, quantity, quantity_key)); - for (const tier of price.tiers || []) { - tags.push(tag_classified_price_tier(tier, price_key)); - } - } } - tags.push(tag_classified_location(location)); + for (const price of prices) { + tags.push(tag_classified_price(price)); + } + for (const discount of opts.discounts || []) { + tags.push(tag_classified_price_discount(discount)); + } + if (opts.location) { + tags.push(tag_classified_location(opts.location)); + //tags.push(...tags_classified_location_geotags(opts.location)); + } if (opts.images) for (const image_tags of opts.images) tags.push(tag_classified_image(image_tags)); - tags.push(...tags_classified_location_geotags(location)); return tags; }; diff --git a/utils-nostr/src/lib/types.ts b/utils-nostr/src/lib/types.ts @@ -1,3 +1,4 @@ +import { ListingOrder } from "@radroots/radroots-common-bindings"; import { type EventTemplate as NostrToolsEventTemplate } from "nostr-tools"; export type INostrMetadata = { @@ -23,51 +24,64 @@ export type NostrEventTagQuantity = { amt: string; unit: string; label?: string; - prices: NostrEventTagPrice[]; }; -export type NostrEventTagPriceTier = { - type: string; - value: string; - qty_min: number; -} +export type NostrEventTagPriceDiscount = ( + { + quantity: { + ref_quantity: string; + threshold: string; + value: string; + currency: string; + } + } | + { + mass: { + unit: string; + threshold: string; + threshold_unit: string; + value: string; + currency: string; + } + } | + { + subtotal: { + threshold: string; + currency: string; + value: string; + measure: string; + } + } | + { + total: { + total_min: string; + value: string; + measure: string; + } + } +); + export type NostrEventTagPrice = { amt: string; currency: string; - tiers?: NostrEventTagPriceTier[]; + qty_amt: string; + qty_unit: string; + qty_key: string; }; export type INostrClassified = { d_tag: string; listing: NostrEventTagListing; quantities: NostrEventTagQuantity[]; - location: NostrEventTagLocation; + prices: NostrEventTagPrice[]; + discounts?: NostrEventTagPriceDiscount[]; + location?: NostrEventTagLocation; images?: NostrEventTagMediaUpload[]; client?: NostrEventTagClient; }; export type NostrJobRequestMassUnit = 'g' | 'kg' | 'lb'; -export type INostrJobRequestOrderQuantity = { - amount: number; - unit: string; - count: number; - mass_g: number; - label: string; -}; - -export type INostrJobRequestOrderPrice = { - amount: number; - currency: string; - quantity_amount: number; - quantity_unit: NostrJobRequestMassUnit; -}; - -export type INostrJobRequestOrder = { - price: INostrJobRequestOrderPrice; - quantity: INostrJobRequestOrderQuantity; -} - export type INostrJobRequestInput = { tags?: string[]; } & ({ @@ -75,7 +89,7 @@ export type INostrJobRequestInput = { id: string; relay: string; marker?: ({ - order: INostrJobRequestOrder; + order: ListingOrder; }); } }) @@ -108,8 +122,8 @@ export type NostrEventTagLocation = { region_code?: string; country?: string; country_code?: string; - lat: number; - lng: number; + lat?: number; + lng?: number; geohash: string; }; diff --git a/utils-nostr/src/services/events/lib.ts b/utils-nostr/src/services/events/lib.ts @@ -1,6 +1,5 @@ -import { INostrClassified, INostrEventEventSign, INostrEventService, INostrEventServiceEventResolve, INostrEventServiceNeventEncode, INostrFollow, INostrMetadata, lib_nostr_event_sign, lib_nostr_event_sign_attest, lib_nostr_event_verify, lib_nostr_event_verify_serialized, lib_nostr_nevent_encode, ndk_event, ndk_event_classified, ndk_event_follows } from "$root"; +import { err_msg, ErrorMessage, INostrClassified, INostrEventEventSign, INostrEventService, INostrEventServiceEventResolve, INostrEventServiceNeventEncode, INostrFollow, INostrMetadata, lib_nostr_event_sign, lib_nostr_event_sign_attest, lib_nostr_event_verify, lib_nostr_event_verify_serialized, lib_nostr_nevent_encode, ndk_event, ndk_event_classified, ndk_event_follows } from "$root"; import NDK, { NDKKind, NDKUser, type NDKEvent } from "@nostr-dev-kit/ndk"; -import { err_msg, ErrorMessage } from "@radroots/util"; import { type NostrEvent as NostrToolsEvent } from "nostr-tools"; export class NostrEventService implements INostrEventService { diff --git a/utils-nostr/src/services/events/types.ts b/utils-nostr/src/services/events/types.ts @@ -1,6 +1,5 @@ -import type { INostrClassified, INostrEventEventSign, INostrFollow, INostrMetadata } from "$root"; +import type { INostrClassified, INostrEventEventSign, INostrFollow, INostrMetadata, ResolveError } from "$root"; import NDK, { NDKEvent } from "@nostr-dev-kit/ndk"; -import { type ResolveError } from "@radroots/util"; import { type NostrEvent as NostrToolsEvent } from "nostr-tools"; export type INostrEventServiceNeventEncode = { diff --git a/utils-nostr/src/types.ts b/utils-nostr/src/types.ts @@ -1,2 +1,4 @@ export type NostrEventTag = string[]; export type NostrEventTags = NostrEventTag[]; +export type ErrorMessage<T extends string> = { err: T }; +export type ResolveError<T> = T | ErrorMessage<string>; diff --git a/utils-nostr/src/util.ts b/utils-nostr/src/util.ts @@ -1,5 +1,10 @@ +import { type ErrorMessage } from "$root"; import { v4 } from "uuid"; export const time_now_ms = (): number => Math.floor(new Date().getTime() / 1000); -export const uuidv4 = (): string => v4(); -\ No newline at end of file +export const uuidv4 = (): string => v4(); + +export const err_msg = <T extends string>(err: T): ErrorMessage<T> => { + return { err }; +}; +\ No newline at end of file