applesauce-core 2.3.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/event-store/event-set.js +5 -3
- package/dist/event-store/event-store.d.ts +54 -24
- package/dist/event-store/event-store.js +139 -47
- package/dist/event-store/interface.d.ts +36 -18
- package/dist/helpers/calendar-event.d.ts +36 -0
- package/dist/helpers/calendar-event.js +110 -0
- package/dist/helpers/calendar-rsvp.d.ts +15 -0
- package/dist/helpers/calendar-rsvp.js +38 -0
- package/dist/helpers/calendar.d.ts +6 -0
- package/dist/helpers/calendar.js +11 -0
- package/dist/helpers/encrypted-content.d.ts +18 -10
- package/dist/helpers/encrypted-content.js +11 -3
- package/dist/helpers/event-cache.d.ts +15 -0
- package/dist/helpers/event-cache.js +32 -0
- package/dist/helpers/event.d.ts +1 -4
- package/dist/helpers/event.js +2 -10
- package/dist/helpers/expiration.js +1 -2
- package/dist/helpers/hidden-content.d.ts +3 -3
- package/dist/helpers/hidden-content.js +2 -2
- package/dist/helpers/hidden-tags.d.ts +5 -10
- package/dist/helpers/hidden-tags.js +3 -3
- package/dist/helpers/highlight.d.ts +45 -0
- package/dist/helpers/highlight.js +76 -0
- package/dist/helpers/index.d.ts +8 -0
- package/dist/helpers/index.js +8 -0
- package/dist/helpers/pointers.js +1 -1
- package/dist/helpers/poll.d.ts +46 -0
- package/dist/helpers/poll.js +78 -0
- package/dist/helpers/stream-chat.d.ts +4 -0
- package/dist/helpers/stream-chat.js +9 -0
- package/dist/helpers/stream.d.ts +31 -0
- package/dist/helpers/stream.js +81 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.js +1 -0
- package/dist/models/blossom.d.ts +2 -1
- package/dist/models/blossom.js +4 -2
- package/dist/models/calendar.d.ts +6 -0
- package/dist/models/calendar.js +15 -0
- package/dist/models/common.d.ts +14 -10
- package/dist/models/common.js +47 -76
- package/dist/models/contacts.d.ts +1 -1
- package/dist/models/contacts.js +4 -2
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +1 -0
- package/dist/models/mailboxes.d.ts +2 -1
- package/dist/models/mailboxes.js +4 -2
- package/dist/models/mutes.d.ts +3 -2
- package/dist/models/mutes.js +4 -2
- package/dist/models/profile.d.ts +2 -1
- package/dist/models/profile.js +4 -2
- package/package.json +1 -1
- package/dist/event-store/common.d.ts +0 -1
- package/dist/event-store/common.js +0 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { getOrComputeCachedValue } from "./cache.js";
|
|
2
|
+
import { getTagValue } from "./event-tags.js";
|
|
3
|
+
// NIP-88 Poll kinds
|
|
4
|
+
export const POLL_KIND = 1068;
|
|
5
|
+
export const POLL_RESPONSE_KIND = 1018;
|
|
6
|
+
// Cache symbols
|
|
7
|
+
export const PollOptionsSymbol = Symbol.for("poll-options");
|
|
8
|
+
/**
|
|
9
|
+
* Get the poll question/label from a poll event
|
|
10
|
+
* Returns the content field which contains the poll question
|
|
11
|
+
*/
|
|
12
|
+
export function getPollQuestion(event) {
|
|
13
|
+
return event.content;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get the poll options from a poll event
|
|
17
|
+
* Returns array of options with id and label
|
|
18
|
+
*/
|
|
19
|
+
export function getPollOptions(event) {
|
|
20
|
+
return getOrComputeCachedValue(event, PollOptionsSymbol, () => {
|
|
21
|
+
return event.tags
|
|
22
|
+
.filter((tag) => tag[0] === "option" && tag.length >= 3)
|
|
23
|
+
.map((tag) => ({
|
|
24
|
+
id: tag[1],
|
|
25
|
+
label: tag[2],
|
|
26
|
+
}));
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get the relays specified for poll responses (from 'relay' tags)
|
|
31
|
+
* Returns undefined if no relays are specified
|
|
32
|
+
*/
|
|
33
|
+
export function getPollRelays(event) {
|
|
34
|
+
return event.tags.filter((tag) => tag[0] === "relay" && tag.length >= 2).map((tag) => tag[1]);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get the poll type from a poll event (from 'polltype' tag)
|
|
38
|
+
* Returns "singlechoice" or "multiplechoice", defaults to "singlechoice"
|
|
39
|
+
*/
|
|
40
|
+
export function getPollType(event) {
|
|
41
|
+
const type = getTagValue(event, "polltype");
|
|
42
|
+
return type === "multiplechoice" || type === "singlechoice" ? type : "singlechoice";
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the poll expiration timestamp (from 'endsAt' tag)
|
|
46
|
+
* Returns undefined if no expiration is set
|
|
47
|
+
*/
|
|
48
|
+
export function getPollEndsAt(event) {
|
|
49
|
+
const endsAt = getTagValue(event, "endsAt");
|
|
50
|
+
return endsAt ? parseInt(endsAt, 10) : undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get the poll ID that a response is referencing (from 'e' tag)
|
|
54
|
+
* Returns undefined if no poll reference is found
|
|
55
|
+
*/
|
|
56
|
+
export function getPollResponsePollId(event) {
|
|
57
|
+
return getTagValue(event, "e");
|
|
58
|
+
}
|
|
59
|
+
/** Get the selected option IDs from a poll response event (from 'response' tags) */
|
|
60
|
+
export function getPollResponseOptions(event) {
|
|
61
|
+
return event.tags.filter((tag) => tag[0] === "response" && tag.length >= 2).map((tag) => tag[1]);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Gets the options that a user has voted for in a poll event
|
|
65
|
+
* Returns undefined if the response is not valid
|
|
66
|
+
*/
|
|
67
|
+
export function getPollResponseVotes(poll, response) {
|
|
68
|
+
if (poll.id !== getPollResponsePollId(response))
|
|
69
|
+
return;
|
|
70
|
+
const pollOptions = getPollOptions(poll);
|
|
71
|
+
const responseOptions = getPollResponseOptions(response);
|
|
72
|
+
const votes = responseOptions.filter((opts) => pollOptions.some((option) => option.id === opts));
|
|
73
|
+
const type = getPollType(poll);
|
|
74
|
+
// If its a single choice poll, return the first vote
|
|
75
|
+
if (type === "singlechoice")
|
|
76
|
+
return votes.length === 1 ? [votes[0]] : undefined;
|
|
77
|
+
return Array.from(new Set(votes));
|
|
78
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getAddressPointerFromATag } from "./pointers.js";
|
|
2
|
+
import { isATag } from "./tags.js";
|
|
3
|
+
/** Returns the pointer to the stream chat message stream */
|
|
4
|
+
export function getStreamChatMessageStream(message) {
|
|
5
|
+
const tag = message.tags.find(isATag);
|
|
6
|
+
if (!tag)
|
|
7
|
+
return undefined;
|
|
8
|
+
return getAddressPointerFromATag(tag);
|
|
9
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { NostrEvent } from "nostr-tools";
|
|
2
|
+
import { EventPointer, ProfilePointer } from "nostr-tools/nip19";
|
|
3
|
+
export type StreamStatus = "live" | "ended" | "planned";
|
|
4
|
+
export type StreamRole = "host" | "participant" | "speaker";
|
|
5
|
+
export declare function getStreamTitle(stream: NostrEvent): string | undefined;
|
|
6
|
+
export declare function getStreamSummary(stream: NostrEvent): string | undefined;
|
|
7
|
+
export declare function getStreamImage(stream: NostrEvent): string | undefined;
|
|
8
|
+
/** Returns the status of the stream, defaults to ended if the stream is older than 2 weeks */
|
|
9
|
+
export declare function getStreamStatus(stream: NostrEvent): StreamStatus;
|
|
10
|
+
/** Returns the pubkey of the host of the stream */
|
|
11
|
+
export declare function getStreamHost(stream: NostrEvent): ProfilePointer;
|
|
12
|
+
/** Returns the participants of a stream */
|
|
13
|
+
export declare function getStreamParticipants(stream: NostrEvent): (ProfilePointer & {
|
|
14
|
+
role: StreamRole;
|
|
15
|
+
})[];
|
|
16
|
+
export declare function getStreamGoalPointer(stream: NostrEvent): EventPointer | undefined;
|
|
17
|
+
/** Gets all the streaming urls for a stream */
|
|
18
|
+
export declare function getStreamStreamingURLs(stream: NostrEvent): string[];
|
|
19
|
+
export declare function getStreamRecording(stream: NostrEvent): string | undefined;
|
|
20
|
+
/** Gets the relays for a stream */
|
|
21
|
+
export declare function getStreamRelays(stream: NostrEvent): string[] | undefined;
|
|
22
|
+
/** Gets the stream start time if it has one */
|
|
23
|
+
export declare function getStreamStartTime(stream: NostrEvent): number | undefined;
|
|
24
|
+
/** Gets the stream end time if it has one */
|
|
25
|
+
export declare function getStreamEndTime(stream: NostrEvent): number | undefined;
|
|
26
|
+
/** Returns the current number of participants in the stream */
|
|
27
|
+
export declare function getStreamViewers(stream: NostrEvent): number | undefined;
|
|
28
|
+
/** Returns the maximum number of participants in the stream */
|
|
29
|
+
export declare function getStreamMaxViewers(stream: NostrEvent): number | undefined;
|
|
30
|
+
/** Returns the hashtags for a stream */
|
|
31
|
+
export declare function getStreamHashtags(stream: NostrEvent): string[];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { getTagValue } from "./event-tags.js";
|
|
2
|
+
import { addRelayHintsToPointer, getEventPointerFromETag, getProfilePointerFromPTag } from "./pointers.js";
|
|
3
|
+
import { mergeRelaySets } from "./relays.js";
|
|
4
|
+
import { isPTag } from "./tags.js";
|
|
5
|
+
import { unixNow } from "./time.js";
|
|
6
|
+
export function getStreamTitle(stream) {
|
|
7
|
+
return getTagValue(stream, "title");
|
|
8
|
+
}
|
|
9
|
+
export function getStreamSummary(stream) {
|
|
10
|
+
return getTagValue(stream, "summary");
|
|
11
|
+
}
|
|
12
|
+
export function getStreamImage(stream) {
|
|
13
|
+
return getTagValue(stream, "image");
|
|
14
|
+
}
|
|
15
|
+
const TWO_WEEKS = 60 * 60 * 24 * 14;
|
|
16
|
+
/** Returns the status of the stream, defaults to ended if the stream is older than 2 weeks */
|
|
17
|
+
export function getStreamStatus(stream) {
|
|
18
|
+
if (stream.created_at < unixNow() - TWO_WEEKS)
|
|
19
|
+
return "ended";
|
|
20
|
+
else
|
|
21
|
+
return getTagValue(stream, "status") || "ended";
|
|
22
|
+
}
|
|
23
|
+
/** Returns the pubkey of the host of the stream */
|
|
24
|
+
export function getStreamHost(stream) {
|
|
25
|
+
let host = undefined;
|
|
26
|
+
for (const tag of stream.tags) {
|
|
27
|
+
if (isPTag(tag) && (!host || (tag[3] && tag[3].toLowerCase() === "host"))) {
|
|
28
|
+
host = getProfilePointerFromPTag(tag);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return host || { pubkey: stream.pubkey };
|
|
32
|
+
}
|
|
33
|
+
/** Returns the participants of a stream */
|
|
34
|
+
export function getStreamParticipants(stream) {
|
|
35
|
+
return stream.tags
|
|
36
|
+
.filter((t) => isPTag(t) && t[3])
|
|
37
|
+
.map((t) => ({ ...getProfilePointerFromPTag(t), role: t[3].toLowerCase() }));
|
|
38
|
+
}
|
|
39
|
+
export function getStreamGoalPointer(stream) {
|
|
40
|
+
const goalTag = stream.tags.find((t) => t[0] === "goal");
|
|
41
|
+
return goalTag && addRelayHintsToPointer(getEventPointerFromETag(goalTag), getStreamRelays(stream));
|
|
42
|
+
}
|
|
43
|
+
/** Gets all the streaming urls for a stream */
|
|
44
|
+
export function getStreamStreamingURLs(stream) {
|
|
45
|
+
return stream.tags.filter((t) => t[0] === "streaming").map((t) => t[1]);
|
|
46
|
+
}
|
|
47
|
+
export function getStreamRecording(stream) {
|
|
48
|
+
return getTagValue(stream, "recording");
|
|
49
|
+
}
|
|
50
|
+
/** Gets the relays for a stream */
|
|
51
|
+
export function getStreamRelays(stream) {
|
|
52
|
+
for (const tag of stream.tags) {
|
|
53
|
+
if (tag[0] === "relays")
|
|
54
|
+
return mergeRelaySets(tag.slice(1));
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
/** Gets the stream start time if it has one */
|
|
59
|
+
export function getStreamStartTime(stream) {
|
|
60
|
+
const str = getTagValue(stream, "starts");
|
|
61
|
+
return str ? parseInt(str) : undefined;
|
|
62
|
+
}
|
|
63
|
+
/** Gets the stream end time if it has one */
|
|
64
|
+
export function getStreamEndTime(stream) {
|
|
65
|
+
const str = getTagValue(stream, "ends");
|
|
66
|
+
return str ? parseInt(str) : getStreamStatus(stream) === "ended" ? stream.created_at : undefined;
|
|
67
|
+
}
|
|
68
|
+
/** Returns the current number of participants in the stream */
|
|
69
|
+
export function getStreamViewers(stream) {
|
|
70
|
+
const viewers = getTagValue(stream, "current_participants");
|
|
71
|
+
return viewers ? parseInt(viewers) : undefined;
|
|
72
|
+
}
|
|
73
|
+
/** Returns the maximum number of participants in the stream */
|
|
74
|
+
export function getStreamMaxViewers(stream) {
|
|
75
|
+
const viewers = getTagValue(stream, "total_participants");
|
|
76
|
+
return viewers ? parseInt(viewers) : undefined;
|
|
77
|
+
}
|
|
78
|
+
/** Returns the hashtags for a stream */
|
|
79
|
+
export function getStreamHashtags(stream) {
|
|
80
|
+
return stream.tags.filter((t) => t[0] === "t").map((t) => t[1]);
|
|
81
|
+
}
|
package/dist/logger.d.ts
CHANGED
package/dist/logger.js
CHANGED
package/dist/models/blossom.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ProfilePointer } from "nostr-tools/nip19";
|
|
1
2
|
import { Model } from "../event-store/interface.js";
|
|
2
3
|
/** A model that returns a users blossom servers */
|
|
3
|
-
export declare function UserBlossomServersModel(
|
|
4
|
+
export declare function UserBlossomServersModel(user: string | ProfilePointer): Model<URL[]>;
|
package/dist/models/blossom.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { map } from "rxjs/operators";
|
|
2
2
|
import { BLOSSOM_SERVER_LIST_KIND, getBlossomServersFromList } from "../helpers/blossom.js";
|
|
3
3
|
/** A model that returns a users blossom servers */
|
|
4
|
-
export function UserBlossomServersModel(
|
|
4
|
+
export function UserBlossomServersModel(user) {
|
|
5
|
+
if (typeof user === "string")
|
|
6
|
+
user = { pubkey: user };
|
|
5
7
|
return (store) => store
|
|
6
|
-
.replaceable(BLOSSOM_SERVER_LIST_KIND, pubkey)
|
|
8
|
+
.replaceable({ kind: BLOSSOM_SERVER_LIST_KIND, pubkey: user.pubkey, relays: user.relays })
|
|
7
9
|
.pipe(map((event) => (event ? getBlossomServersFromList(event) : [])));
|
|
8
10
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { NostrEvent } from "nostr-tools";
|
|
2
|
+
import { Model } from "../event-store/interface.js";
|
|
3
|
+
/** A model that gets all the events for a calendar */
|
|
4
|
+
export declare function CalendarEventsModel(calendar: NostrEvent): Model<NostrEvent[]>;
|
|
5
|
+
/** A model that gets all the RSVPs for a calendar event */
|
|
6
|
+
export declare function CalendarEventRSVPsModel(event: NostrEvent): Model<NostrEvent[]>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { combineLatest } from "rxjs";
|
|
2
|
+
import { CALENDAR_EVENT_RSVP_KIND } from "../helpers/calendar-rsvp.js";
|
|
3
|
+
import { getCalendarAddressPointers } from "../helpers/calendar.js";
|
|
4
|
+
import { getReplaceableAddress } from "../helpers/index.js";
|
|
5
|
+
import { defined } from "../observable/defined.js";
|
|
6
|
+
/** A model that gets all the events for a calendar */
|
|
7
|
+
export function CalendarEventsModel(calendar) {
|
|
8
|
+
return (events) => combineLatest(getCalendarAddressPointers(calendar).map((p) => events.replaceable(p.kind, p.pubkey, p.identifier).pipe(defined())));
|
|
9
|
+
}
|
|
10
|
+
/** A model that gets all the RSVPs for a calendar event */
|
|
11
|
+
export function CalendarEventRSVPsModel(event) {
|
|
12
|
+
return (events) => {
|
|
13
|
+
return events.timeline({ kinds: [CALENDAR_EVENT_RSVP_KIND], "#a": [getReplaceableAddress(event)] });
|
|
14
|
+
};
|
|
15
|
+
}
|
package/dist/models/common.d.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { Filter, NostrEvent } from "nostr-tools";
|
|
2
|
+
import { AddressPointer, EventPointer } from "nostr-tools/nip19";
|
|
2
3
|
import { Model } from "../event-store/interface.js";
|
|
4
|
+
import { AddressPointerWithoutD } from "../helpers/index.js";
|
|
3
5
|
/** A model that returns a single event or undefined when its removed */
|
|
4
|
-
export declare function EventModel(
|
|
6
|
+
export declare function EventModel(pointer: string | EventPointer): Model<NostrEvent | undefined>;
|
|
5
7
|
/** A model that returns the latest version of a replaceable event or undefined if its removed */
|
|
6
|
-
export declare function ReplaceableModel(
|
|
8
|
+
export declare function ReplaceableModel(pointer: AddressPointer | AddressPointerWithoutD): Model<NostrEvent | undefined>;
|
|
7
9
|
/** A model that returns an array of sorted events matching the filters */
|
|
8
10
|
export declare function TimelineModel(filters: Filter | Filter[], includeOldVersion?: boolean): Model<NostrEvent[]>;
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
11
|
+
/**
|
|
12
|
+
* A model that returns a multiple events in a map
|
|
13
|
+
* @deprecated use multiple {@link EventModel} instead
|
|
14
|
+
*/
|
|
15
|
+
export declare function EventsModel(ids: string[]): Model<Record<string, NostrEvent | undefined>>;
|
|
16
|
+
/**
|
|
17
|
+
* A model that returns a directory of events by their UID
|
|
18
|
+
* @deprecated use multiple {@link ReplaceableModel} instead
|
|
19
|
+
*/
|
|
20
|
+
export declare function ReplaceableSetModel(pointers: (AddressPointer | AddressPointerWithoutD)[]): Model<Record<string, NostrEvent | undefined>>;
|
package/dist/models/common.js
CHANGED
|
@@ -1,40 +1,60 @@
|
|
|
1
|
-
import { defer, distinctUntilChanged, EMPTY, endWith, filter, finalize, from, map, merge, mergeWith, of, repeat, scan, takeUntil, tap, } from "rxjs";
|
|
1
|
+
import { combineLatest, defer, distinctUntilChanged, EMPTY, endWith, filter, finalize, from, map, merge, mergeWith, of, repeat, scan, takeUntil, tap, } from "rxjs";
|
|
2
|
+
import { insertEventIntoDescendingList } from "nostr-tools/utils";
|
|
2
3
|
import { createReplaceableAddress, getEventUID, getReplaceableIdentifier, isReplaceable, matchFilters, } from "../helpers/index.js";
|
|
3
4
|
import { claimEvents } from "../observable/claim-events.js";
|
|
4
5
|
import { claimLatest } from "../observable/claim-latest.js";
|
|
5
|
-
import { insertEventIntoDescendingList } from "nostr-tools/utils";
|
|
6
6
|
import { withImmediateValueOrDefault } from "../observable/with-immediate-value.js";
|
|
7
7
|
/** A model that returns a single event or undefined when its removed */
|
|
8
|
-
export function EventModel(
|
|
8
|
+
export function EventModel(pointer) {
|
|
9
|
+
if (typeof pointer === "string")
|
|
10
|
+
pointer = { id: pointer };
|
|
9
11
|
return (events) => merge(
|
|
10
12
|
// get current event and ignore if there is none
|
|
11
13
|
defer(() => {
|
|
12
|
-
let event = events.getEvent(id);
|
|
13
|
-
|
|
14
|
+
let event = events.getEvent(pointer.id);
|
|
15
|
+
if (event)
|
|
16
|
+
return of(event);
|
|
17
|
+
// If there is a loader, use it to get the event
|
|
18
|
+
if (!events.eventLoader)
|
|
19
|
+
return EMPTY;
|
|
20
|
+
return from(events.eventLoader(pointer)).pipe(filter((e) => !!e));
|
|
14
21
|
}),
|
|
15
|
-
//
|
|
16
|
-
events.insert$.pipe(filter((e) => e.id === id)),
|
|
17
|
-
// subscribe to updates
|
|
18
|
-
events.updated(id),
|
|
22
|
+
// Listen for new events
|
|
23
|
+
events.insert$.pipe(filter((e) => e.id === pointer.id)),
|
|
19
24
|
// emit undefined when deleted
|
|
20
|
-
events.removed(id).pipe(endWith(undefined))).pipe(
|
|
25
|
+
events.removed(pointer.id).pipe(endWith(undefined))).pipe(
|
|
21
26
|
// claim all events
|
|
22
27
|
claimLatest(events),
|
|
28
|
+
// ignore duplicate events
|
|
29
|
+
distinctUntilChanged((a, b) => a?.id === b?.id),
|
|
23
30
|
// always emit undefined so the observable is synchronous
|
|
24
31
|
withImmediateValueOrDefault(undefined));
|
|
25
32
|
}
|
|
26
33
|
/** A model that returns the latest version of a replaceable event or undefined if its removed */
|
|
27
|
-
export function ReplaceableModel(
|
|
34
|
+
export function ReplaceableModel(pointer) {
|
|
28
35
|
return (events) => {
|
|
29
36
|
let current = undefined;
|
|
30
37
|
return merge(
|
|
31
38
|
// lazily get current event
|
|
32
39
|
defer(() => {
|
|
33
|
-
let event = events.getReplaceable(kind, pubkey,
|
|
34
|
-
|
|
40
|
+
let event = events.getReplaceable(pointer.kind, pointer.pubkey, pointer.identifier);
|
|
41
|
+
if (event)
|
|
42
|
+
return of(event);
|
|
43
|
+
else if (pointer.identifier !== undefined) {
|
|
44
|
+
if (!events.addressableLoader)
|
|
45
|
+
return EMPTY;
|
|
46
|
+
return from(events.addressableLoader(pointer)).pipe(filter((e) => !!e));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
if (!events.replaceableLoader)
|
|
50
|
+
return EMPTY;
|
|
51
|
+
return from(events.replaceableLoader(pointer)).pipe(filter((e) => !!e));
|
|
52
|
+
}
|
|
35
53
|
}),
|
|
36
54
|
// subscribe to new events
|
|
37
|
-
events.insert$.pipe(filter((e) => e.pubkey == pubkey &&
|
|
55
|
+
events.insert$.pipe(filter((e) => e.pubkey == pointer.pubkey &&
|
|
56
|
+
e.kind === pointer.kind &&
|
|
57
|
+
(pointer.identifier !== undefined ? getReplaceableIdentifier(e) === pointer.identifier : true)))).pipe(
|
|
38
58
|
// only update if event is newer
|
|
39
59
|
distinctUntilChanged((prev, event) => {
|
|
40
60
|
// are the events the same? i.e. is the prev event older
|
|
@@ -108,69 +128,20 @@ export function TimelineModel(filters, includeOldVersion) {
|
|
|
108
128
|
finalize(() => seen.clear()));
|
|
109
129
|
};
|
|
110
130
|
}
|
|
111
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* A model that returns a multiple events in a map
|
|
133
|
+
* @deprecated use multiple {@link EventModel} instead
|
|
134
|
+
*/
|
|
112
135
|
export function EventsModel(ids) {
|
|
113
|
-
return (events) =>
|
|
114
|
-
// lazily get existing events
|
|
115
|
-
defer(() => from(ids.map((id) => events.getEvent(id)))),
|
|
116
|
-
// subscribe to new events
|
|
117
|
-
events.insert$.pipe(filter((e) => ids.includes(e.id))),
|
|
118
|
-
// subscribe to updates
|
|
119
|
-
events.update$.pipe(filter((e) => ids.includes(e.id)))).pipe(
|
|
120
|
-
// ignore empty messages
|
|
121
|
-
filter((e) => !!e),
|
|
122
|
-
// claim all events until cleanup
|
|
123
|
-
claimEvents(events),
|
|
124
|
-
// watch for removed events
|
|
125
|
-
mergeWith(events.remove$.pipe(filter((e) => ids.includes(e.id)), map((e) => e.id))),
|
|
126
|
-
// merge all events into a directory
|
|
127
|
-
scan((dir, event) => {
|
|
128
|
-
if (typeof event === "string") {
|
|
129
|
-
// delete event by id
|
|
130
|
-
const clone = { ...dir };
|
|
131
|
-
delete clone[event];
|
|
132
|
-
return clone;
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
// add even to directory
|
|
136
|
-
return { ...dir, [event.id]: event };
|
|
137
|
-
}
|
|
138
|
-
}, {}));
|
|
136
|
+
return (events) => combineLatest(Object.fromEntries(ids.map((id) => [id, events.model(EventModel, { id })])));
|
|
139
137
|
}
|
|
140
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* A model that returns a directory of events by their UID
|
|
140
|
+
* @deprecated use multiple {@link ReplaceableModel} instead
|
|
141
|
+
*/
|
|
141
142
|
export function ReplaceableSetModel(pointers) {
|
|
142
|
-
return (events) =>
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
defer(() => from(pointers.map((p) => events.getReplaceable(p.kind, p.pubkey, p.identifier)))),
|
|
147
|
-
// subscribe to new events
|
|
148
|
-
events.insert$.pipe(filter((e) => isReplaceable(e.kind) && uids.has(getEventUID(e))))).pipe(
|
|
149
|
-
// filter out undefined
|
|
150
|
-
filter((e) => !!e),
|
|
151
|
-
// claim all events
|
|
152
|
-
claimEvents(events),
|
|
153
|
-
// convert events to add commands
|
|
154
|
-
map((e) => ["add", e]),
|
|
155
|
-
// watch for removed events
|
|
156
|
-
mergeWith(events.remove$.pipe(filter((e) => isReplaceable(e.kind) && uids.has(getEventUID(e))), map((e) => ["remove", e]))),
|
|
157
|
-
// reduce events into directory
|
|
158
|
-
scan((dir, [action, event]) => {
|
|
159
|
-
const uid = getEventUID(event);
|
|
160
|
-
if (action === "add") {
|
|
161
|
-
// add event to dir if its newer
|
|
162
|
-
if (!dir[uid] || dir[uid].created_at < event.created_at)
|
|
163
|
-
return { ...dir, [uid]: event };
|
|
164
|
-
}
|
|
165
|
-
else if (action === "remove" && dir[uid] === event) {
|
|
166
|
-
// remove event from dir
|
|
167
|
-
let newDir = { ...dir };
|
|
168
|
-
delete newDir[uid];
|
|
169
|
-
return newDir;
|
|
170
|
-
}
|
|
171
|
-
return dir;
|
|
172
|
-
}, {}),
|
|
173
|
-
// ignore changes that do not modify the directory
|
|
174
|
-
distinctUntilChanged());
|
|
175
|
-
};
|
|
143
|
+
return (events) => combineLatest(Object.fromEntries(pointers.map((pointer) => [
|
|
144
|
+
createReplaceableAddress(pointer.kind, pointer.pubkey, pointer.identifier),
|
|
145
|
+
events.model(ReplaceableModel, pointer),
|
|
146
|
+
])));
|
|
176
147
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ProfilePointer } from "nostr-tools/nip19";
|
|
2
2
|
import { Model } from "../event-store/interface.js";
|
|
3
3
|
/** A model that returns all contacts for a user */
|
|
4
|
-
export declare function ContactsModel(
|
|
4
|
+
export declare function ContactsModel(user: string | ProfilePointer): Model<ProfilePointer[]>;
|
|
5
5
|
/** A model that returns all public contacts for a user */
|
|
6
6
|
export declare function PublicContactsModel(pubkey: string): Model<ProfilePointer[] | undefined>;
|
|
7
7
|
/** A model that returns all hidden contacts for a user */
|
package/dist/models/contacts.js
CHANGED
|
@@ -3,8 +3,10 @@ import { map } from "rxjs/operators";
|
|
|
3
3
|
import { getContacts, getHiddenContacts, getPublicContacts } from "../helpers/contacts.js";
|
|
4
4
|
import { watchEventUpdates } from "../observable/index.js";
|
|
5
5
|
/** A model that returns all contacts for a user */
|
|
6
|
-
export function ContactsModel(
|
|
7
|
-
|
|
6
|
+
export function ContactsModel(user) {
|
|
7
|
+
if (typeof user === "string")
|
|
8
|
+
user = { pubkey: user };
|
|
9
|
+
return (events) => events.replaceable({ kind: kinds.Contacts, pubkey: user.pubkey, relays: user.relays }).pipe(
|
|
8
10
|
// listen for event updates (hidden tags unlocked)
|
|
9
11
|
watchEventUpdates(events),
|
|
10
12
|
// Get all contacts
|
package/dist/models/index.d.ts
CHANGED
package/dist/models/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { ProfilePointer } from "nostr-tools/nip19";
|
|
1
2
|
import { Model } from "../event-store/interface.js";
|
|
2
3
|
/** A model that gets and parses the inbox and outbox relays for a pubkey */
|
|
3
|
-
export declare function MailboxesModel(
|
|
4
|
+
export declare function MailboxesModel(user: string | ProfilePointer): Model<{
|
|
4
5
|
inboxes: string[];
|
|
5
6
|
outboxes: string[];
|
|
6
7
|
} | undefined>;
|
package/dist/models/mailboxes.js
CHANGED
|
@@ -2,8 +2,10 @@ import { kinds } from "nostr-tools";
|
|
|
2
2
|
import { map } from "rxjs/operators";
|
|
3
3
|
import { getInboxes, getOutboxes } from "../helpers/mailboxes.js";
|
|
4
4
|
/** A model that gets and parses the inbox and outbox relays for a pubkey */
|
|
5
|
-
export function MailboxesModel(
|
|
6
|
-
|
|
5
|
+
export function MailboxesModel(user) {
|
|
6
|
+
if (typeof user === "string")
|
|
7
|
+
user = { pubkey: user };
|
|
8
|
+
return (events) => events.replaceable({ kind: kinds.RelayList, pubkey: user.pubkey, relays: user.relays }).pipe(map((event) => event && {
|
|
7
9
|
inboxes: getInboxes(event),
|
|
8
10
|
outboxes: getOutboxes(event),
|
|
9
11
|
}));
|
package/dist/models/mutes.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ProfilePointer } from "nostr-tools/nip19";
|
|
2
2
|
import { Model } from "../event-store/interface.js";
|
|
3
|
+
import { Mutes } from "../helpers/mutes.js";
|
|
3
4
|
/** A model that returns all a users muted things */
|
|
4
|
-
export declare function MuteModel(
|
|
5
|
+
export declare function MuteModel(user: string | ProfilePointer): Model<Mutes | undefined>;
|
|
5
6
|
/** A model that returns all a users public muted things */
|
|
6
7
|
export declare function PublicMuteModel(pubkey: string): Model<Mutes | undefined>;
|
|
7
8
|
/** A model that returns all a users hidden muted things */
|
package/dist/models/mutes.js
CHANGED
|
@@ -3,8 +3,10 @@ import { map } from "rxjs/operators";
|
|
|
3
3
|
import { getHiddenMutedThings, getMutedThings, getPublicMutedThings } from "../helpers/mutes.js";
|
|
4
4
|
import { watchEventUpdates } from "../observable/watch-event-updates.js";
|
|
5
5
|
/** A model that returns all a users muted things */
|
|
6
|
-
export function MuteModel(
|
|
7
|
-
|
|
6
|
+
export function MuteModel(user) {
|
|
7
|
+
if (typeof user === "string")
|
|
8
|
+
user = { pubkey: user };
|
|
9
|
+
return (events) => events.replaceable({ kind: kinds.Mutelist, pubkey: user.pubkey, relays: user.relays }).pipe(
|
|
8
10
|
// listen for event updates (hidden tags unlocked)
|
|
9
11
|
watchEventUpdates(events),
|
|
10
12
|
// Get all muted things
|
package/dist/models/profile.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Model } from "../event-store/interface.js";
|
|
2
2
|
import { ProfileContent } from "../helpers/profile.js";
|
|
3
|
+
import { ProfilePointer } from "nostr-tools/nip19";
|
|
3
4
|
/** A model that gets and parses the kind 0 metadata for a pubkey */
|
|
4
|
-
export declare function ProfileModel(
|
|
5
|
+
export declare function ProfileModel(user: string | ProfilePointer): Model<ProfileContent | undefined>;
|
package/dist/models/profile.js
CHANGED
|
@@ -3,8 +3,10 @@ import { filter, map } from "rxjs/operators";
|
|
|
3
3
|
import { getProfileContent, isValidProfile } from "../helpers/profile.js";
|
|
4
4
|
import { withImmediateValueOrDefault } from "../observable/with-immediate-value.js";
|
|
5
5
|
/** A model that gets and parses the kind 0 metadata for a pubkey */
|
|
6
|
-
export function ProfileModel(
|
|
7
|
-
|
|
6
|
+
export function ProfileModel(user) {
|
|
7
|
+
if (typeof user === "string")
|
|
8
|
+
user = { pubkey: user };
|
|
9
|
+
return (events) => events.replaceable({ kind: kinds.Metadata, pubkey: user.pubkey, relays: user.relays }).pipe(
|
|
8
10
|
// Filter out invalid profile events
|
|
9
11
|
filter(isValidProfile),
|
|
10
12
|
// Parse the profile event into a ProfileContent
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|