applesauce-actions 0.0.0-next-20251205152544 → 0.0.0-next-20251220152312

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.
@@ -1,35 +1,51 @@
1
+ import { User } from "applesauce-common/casts/user";
2
+ import { EventFactory, EventSigner } from "applesauce-core/event-factory";
3
+ import { EventModels, IEventStoreActions, IEventStoreRead, IEventStoreStreams, IEventSubscriptions } from "applesauce-core/event-store";
4
+ import { EventTemplate, NostrEvent, UnsignedEvent } from "applesauce-core/helpers/event";
1
5
  import { Observable } from "rxjs";
2
- import { NostrEvent } from "applesauce-core/helpers/event";
3
- import { EventFactory } from "applesauce-core";
4
- import { IEventStoreActions, IEventStoreRead } from "applesauce-core";
5
6
  /** A callback used to tell the upstream app to publish an event */
6
- export type PublishMethod = (event: NostrEvent) => void | Promise<void>;
7
+ export type PublishMethod = (event: NostrEvent, relays?: string[]) => void | Promise<any> | Observable<any>;
8
+ type UpstreamPool = PublishMethod | {
9
+ publish: PublishMethod;
10
+ };
7
11
  /** The context that is passed to actions for them to use to preform actions */
8
12
  export type ActionContext = {
9
13
  /** The event store to load events from */
10
- events: IEventStoreRead;
14
+ events: IEventStoreRead & IEventStoreStreams & IEventSubscriptions & EventModels;
11
15
  /** The pubkey of the signer in the event factory */
12
16
  self: string;
17
+ /** The {@link User} cast that is signing the events */
18
+ user: User;
19
+ /** The event signer used to sign events */
20
+ signer?: EventSigner;
13
21
  /** The event factory used to build and modify events */
14
22
  factory: EventFactory;
23
+ /** Sign an event using the event factory */
24
+ sign: (draft: EventTemplate | UnsignedEvent) => Promise<NostrEvent>;
25
+ /** The method to publish events to an optional list of relays */
26
+ publish: (event: NostrEvent | NostrEvent[], relays?: string[]) => Promise<void>;
27
+ /** Run a sub-action within the current action context and return the events */
28
+ run: <Args extends Array<any>>(builder: ActionBuilder<Args>, ...args: Args) => Promise<void>;
15
29
  };
16
30
  /** An action that can be run in a context to preform an action */
17
- export type Action = (ctx: ActionContext) => Observable<NostrEvent> | AsyncGenerator<NostrEvent> | Generator<NostrEvent>;
18
- export type ActionConstructor<Args extends Array<any>> = (...args: Args) => Action;
31
+ export type Action = (context: ActionContext) => Promise<void>;
32
+ /** A function that takes arguments and returns an action */
33
+ export type ActionBuilder<Args extends Array<any>> = (...args: Args) => Action;
19
34
  /** The main class that runs actions */
20
35
  export declare class ActionHub {
21
- events: IEventStoreRead & IEventStoreActions;
36
+ events: IEventStoreRead & IEventStoreStreams & IEventSubscriptions & IEventStoreActions & EventModels;
22
37
  factory: EventFactory;
23
- publish?: PublishMethod | undefined;
38
+ private publishMethod?;
24
39
  /** Whether to save all events created by actions to the event store */
25
40
  saveToStore: boolean;
26
- constructor(events: IEventStoreRead & IEventStoreActions, factory: EventFactory, publish?: PublishMethod | undefined);
41
+ constructor(events: IEventStoreRead & IEventStoreStreams & IEventSubscriptions & IEventStoreActions & EventModels, factory: EventFactory, publishMethod?: UpstreamPool | undefined);
27
42
  protected context: ActionContext | undefined;
28
43
  protected getContext(): Promise<ActionContext>;
29
- /** Runs an action in a ActionContext and converts the result to an Observable */
30
- static runAction(ctx: ActionContext, action: Action): Observable<NostrEvent>;
44
+ /** Internal method for publishing events to relays */
45
+ publish(event: NostrEvent | NostrEvent[], relays?: string[]): Promise<void>;
31
46
  /** Run an action and publish events using the publish method */
32
- run<Args extends Array<any>>(Action: ActionConstructor<Args>, ...args: Args): Promise<void>;
47
+ run<Args extends Array<any>>(builder: ActionBuilder<Args>, ...args: Args): Promise<void>;
33
48
  /** Run an action without publishing the events */
34
- exec<Args extends Array<any>>(Action: ActionConstructor<Args>, ...args: Args): Observable<NostrEvent>;
49
+ exec<Args extends Array<any>>(builder: ActionBuilder<Args>, ...args: Args): Observable<NostrEvent>;
35
50
  }
51
+ export {};
@@ -1,15 +1,16 @@
1
- import { from, isObservable, lastValueFrom, switchMap, tap, toArray } from "rxjs";
1
+ import { castUser } from "applesauce-common/casts/user";
2
+ import { from, identity, isObservable, lastValueFrom, Observable, switchMap, tap } from "rxjs";
2
3
  /** The main class that runs actions */
3
4
  export class ActionHub {
4
5
  events;
5
6
  factory;
6
- publish;
7
+ publishMethod;
7
8
  /** Whether to save all events created by actions to the event store */
8
9
  saveToStore = true;
9
- constructor(events, factory, publish) {
10
+ constructor(events, factory, publishMethod) {
10
11
  this.events = events;
11
12
  this.factory = factory;
12
- this.publish = publish;
13
+ this.publishMethod = publishMethod;
13
14
  }
14
15
  context = undefined;
15
16
  async getContext() {
@@ -19,33 +20,70 @@ export class ActionHub {
19
20
  if (!this.factory.context.signer)
20
21
  throw new Error("Missing signer");
21
22
  const self = await this.factory.context.signer.getPublicKey();
22
- this.context = { self, events: this.events, factory: this.factory };
23
+ const user = castUser(self, this.events);
24
+ this.context = {
25
+ self,
26
+ user,
27
+ events: this.events,
28
+ signer: this.factory.context.signer,
29
+ factory: this.factory,
30
+ publish: this.publish.bind(this),
31
+ run: this.run.bind(this),
32
+ sign: this.factory.sign.bind(this.factory),
33
+ };
23
34
  return this.context;
24
35
  }
25
36
  }
26
- /** Runs an action in a ActionContext and converts the result to an Observable */
27
- static runAction(ctx, action) {
28
- const result = action(ctx);
29
- if (isObservable(result))
30
- return result;
31
- else
32
- return from(result);
37
+ /** Internal method for publishing events to relays */
38
+ async publish(event, relays) {
39
+ if (!this.publishMethod)
40
+ throw new Error("Missing publish method, use ActionHub.exec");
41
+ // Unwrap array of events to publish
42
+ if (Array.isArray(event)) {
43
+ await Promise.all(event.map((e) => this.publish(e, relays)));
44
+ return;
45
+ }
46
+ if (this.publishMethod) {
47
+ let result;
48
+ if ("publish" in this.publishMethod)
49
+ result = this.publishMethod.publish(event, relays);
50
+ else if (typeof this.publishMethod === "function")
51
+ result = this.publishMethod(event, relays);
52
+ else
53
+ throw new Error("Invalid publish method");
54
+ if (isObservable(result)) {
55
+ await lastValueFrom(result);
56
+ }
57
+ else if (result instanceof Promise) {
58
+ await result;
59
+ }
60
+ // Optionally save the event to the store
61
+ if (this.saveToStore)
62
+ this.events.add(event);
63
+ }
33
64
  }
34
65
  /** Run an action and publish events using the publish method */
35
- async run(Action, ...args) {
36
- if (!this.publish)
37
- throw new Error("Missing publish method, use ActionHub.exec");
66
+ async run(builder, ...args) {
38
67
  // wait for action to complete and group events
39
- const events = await lastValueFrom(this.exec(Action, ...args).pipe(toArray()));
40
- // publish events
41
- for (const event of events)
42
- await this.publish(event);
68
+ const context = await this.getContext();
69
+ await builder(...args)(context);
43
70
  }
44
71
  /** Run an action without publishing the events */
45
- exec(Action, ...args) {
46
- return from(this.getContext()).pipe(switchMap((ctx) => {
47
- const action = Action(...args);
48
- return ActionHub.runAction(ctx, action);
49
- }), tap((event) => this.saveToStore && this.events.add(event)));
72
+ exec(builder, ...args) {
73
+ return from(this.getContext()).pipe(
74
+ // Run the action
75
+ switchMap((ctx) => {
76
+ return new Observable((subscriber) => {
77
+ const context = {
78
+ ...ctx,
79
+ publish: async (event) => Array.isArray(event) ? event.forEach((e) => subscriber.next(e)) : subscriber.next(event),
80
+ run: (builder, ...args) => builder(...args)(context),
81
+ };
82
+ builder(...args)(context).then(() => subscriber.complete(), (err) => subscriber.error(err));
83
+ });
84
+ }),
85
+ // NOTE: its necessary to add a tap() here because we are overwriting the publish method above
86
+ // Optionally save all events to the store
87
+ this.saveToStore ? tap((event) => this.events.add(event)) : identity);
50
88
  }
51
89
  }
@@ -2,12 +2,12 @@ import { AppDataBlueprint } from "applesauce-common/blueprints/app-data";
2
2
  import { APP_DATA_KIND, getAppDataEncryption } from "applesauce-common/helpers/app-data";
3
3
  import * as AppData from "applesauce-common/operations/app-data";
4
4
  export function UpdateAppData(identifier, data) {
5
- return async function* ({ self, factory, events }) {
5
+ return async ({ self, factory, events, user, publish, sign }) => {
6
6
  const event = events.getReplaceable(APP_DATA_KIND, self, identifier);
7
7
  const encryption = !!event && getAppDataEncryption(event);
8
- const draft = event
9
- ? await factory.modify(event, AppData.setContent(data, encryption))
10
- : await factory.create(AppDataBlueprint, identifier, data, encryption);
11
- yield await factory.sign(draft);
8
+ const signed = event
9
+ ? await factory.modify(event, AppData.setContent(data, encryption)).then(sign)
10
+ : await factory.create(AppDataBlueprint, identifier, data, encryption).then(sign);
11
+ await publish(signed, await user.outboxes$.$first(1000, undefined));
12
12
  };
13
13
  }
@@ -3,8 +3,3 @@ import { Action } from "../action-hub.js";
3
3
  export declare function AddBlockedRelay(relay: string | string[], hidden?: boolean): Action;
4
4
  /** An action that removes a relay from the 10006 blocked relays event */
5
5
  export declare function RemoveBlockedRelay(relay: string | string[], hidden?: boolean): Action;
6
- /** Creates a new blocked relays event */
7
- export declare function NewBlockedRelays(relays?: string[] | {
8
- public?: string[];
9
- hidden?: string[];
10
- }): Action;
@@ -1,48 +1,40 @@
1
+ import { firstValueFrom } from "applesauce-core";
1
2
  import { kinds } from "applesauce-core/helpers/event";
2
3
  import { modifyHiddenTags, modifyPublicTags } from "applesauce-core/operations";
3
4
  import { addRelayTag, removeRelayTag } from "applesauce-core/operations/tag/relay";
4
- function getBlockedRelaysEvent(events, self) {
5
- const event = events.getReplaceable(kinds.BlockedRelaysList, self);
6
- if (!event)
7
- throw new Error("Can't find blocked relays event");
8
- return event;
5
+ import { of, timeout } from "rxjs";
6
+ // Action to generally modify the blocked relays event
7
+ function ModifyBlockedRelaysEvent(operations) {
8
+ return async ({ events, factory, user, publish, sign }) => {
9
+ const [event, outboxes] = await Promise.all([
10
+ firstValueFrom(events.replaceable(kinds.BlockedRelaysList, user.pubkey).pipe(timeout({ first: 1000, with: () => of(undefined) }))),
11
+ user.outboxes$.$first(1000, undefined),
12
+ ]);
13
+ // Modify or build new event
14
+ const signed = event
15
+ ? await factory.modify(event, ...operations).then(sign)
16
+ : await factory.build({ kind: kinds.BlockedRelaysList }, ...operations).then(sign);
17
+ // Publish the event to the user's outboxes
18
+ await publish(signed, outboxes);
19
+ };
9
20
  }
10
21
  /** An action that adds a relay to the 10006 blocked relays event */
11
22
  export function AddBlockedRelay(relay, hidden = false) {
12
- return async function* ({ events, factory, self }) {
13
- const blocked = getBlockedRelaysEvent(events, self);
14
- const operation = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
15
- const draft = await factory.modifyTags(blocked, hidden ? { hidden: operation } : operation);
16
- yield await factory.sign(draft);
23
+ return async ({ run }) => {
24
+ const tagOperations = Array.isArray(relay)
25
+ ? relay.map((r) => addRelayTag(r))
26
+ : [addRelayTag(relay)];
27
+ const operation = hidden ? modifyHiddenTags(...tagOperations) : modifyPublicTags(...tagOperations);
28
+ await run(ModifyBlockedRelaysEvent, [operation]);
17
29
  };
18
30
  }
19
31
  /** An action that removes a relay from the 10006 blocked relays event */
20
32
  export function RemoveBlockedRelay(relay, hidden = false) {
21
- return async function* ({ events, factory, self }) {
22
- const blocked = getBlockedRelaysEvent(events, self);
23
- const operation = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
24
- const draft = await factory.modifyTags(blocked, hidden ? { hidden: operation } : operation);
25
- yield await factory.sign(draft);
26
- };
27
- }
28
- /** Creates a new blocked relays event */
29
- export function NewBlockedRelays(relays) {
30
- return async function* ({ events, factory, self }) {
31
- const blocked = events.getReplaceable(kinds.BlockedRelaysList, self);
32
- if (blocked)
33
- throw new Error("Blocked relays event already exists");
34
- let publicOperations = [];
35
- let hiddenOperations = [];
36
- if (Array.isArray(relays)) {
37
- publicOperations.push(...relays.map((r) => addRelayTag(r)));
38
- }
39
- else {
40
- if (relays?.public)
41
- publicOperations.push(...(relays?.public ?? []).map((r) => addRelayTag(r)));
42
- if (relays?.hidden)
43
- hiddenOperations.push(...(relays?.hidden ?? []).map((r) => addRelayTag(r)));
44
- }
45
- const draft = await factory.build({ kind: kinds.BlockedRelaysList }, publicOperations.length ? modifyPublicTags(...publicOperations) : undefined, hiddenOperations.length ? modifyHiddenTags(...hiddenOperations) : undefined);
46
- yield await factory.sign(draft);
33
+ return async ({ run }) => {
34
+ const tagOperations = Array.isArray(relay)
35
+ ? relay.map((r) => removeRelayTag(r))
36
+ : [removeRelayTag(relay)];
37
+ const operation = hidden ? modifyHiddenTags(...tagOperations) : modifyPublicTags(...tagOperations);
38
+ await run(ModifyBlockedRelaysEvent, [operation]);
47
39
  };
48
40
  }
@@ -1,51 +1,44 @@
1
1
  import { BLOSSOM_SERVER_LIST_KIND } from "applesauce-common/helpers/blossom";
2
2
  import { addBlossomServer, removeBlossomServer } from "applesauce-common/operations/blossom";
3
- /** An action that adds a server to the Blossom servers event */
4
- export function AddBlossomServer(server) {
5
- return async function* ({ events, factory, self }) {
6
- const servers = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
7
- const operations = Array.isArray(server) ? server.map((s) => addBlossomServer(s)) : [addBlossomServer(server)];
3
+ // Action to modify or create a new Blossom servers event
4
+ function ModifyBlossomServersEvent(operations) {
5
+ return async ({ factory, user, publish, sign }) => {
6
+ const [event, outboxes] = await Promise.all([
7
+ user.replaceable(BLOSSOM_SERVER_LIST_KIND).$first(1000, undefined),
8
+ user.outboxes$.$first(1000, undefined),
9
+ ]);
8
10
  // Modify or build new event
9
- const draft = servers
10
- ? await factory.modify(servers, ...operations)
11
- : await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations);
12
- yield await factory.sign(draft);
11
+ const signed = event
12
+ ? await factory.modify(event, ...operations).then(sign)
13
+ : await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations).then(sign);
14
+ // Publish the event to the user's outboxes
15
+ await publish(signed, outboxes);
13
16
  };
14
17
  }
18
+ /** An action that adds a server to the Blossom servers event */
19
+ export function AddBlossomServer(server) {
20
+ return ModifyBlossomServersEvent(Array.isArray(server) ? server.map((s) => addBlossomServer(s)) : [addBlossomServer(server)]);
21
+ }
15
22
  /** An action that removes a server from the Blossom servers event */
16
23
  export function RemoveBlossomServer(server) {
17
- return async function* ({ events, factory, self }) {
18
- const servers = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
19
- const operations = Array.isArray(server)
20
- ? server.map((s) => removeBlossomServer(s))
21
- : [removeBlossomServer(server)];
22
- // Modify or build new event
23
- const draft = servers
24
- ? await factory.modify(servers, ...operations)
25
- : await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations);
26
- yield await factory.sign(draft);
27
- };
24
+ return ModifyBlossomServersEvent(Array.isArray(server) ? server.map((s) => removeBlossomServer(s)) : [removeBlossomServer(server)]);
25
+ }
26
+ // Small event operation to prepend a tag to the events tags
27
+ function prependTag(tag) {
28
+ return (draft) => ({ ...draft, tags: [tag, ...draft.tags] });
28
29
  }
29
30
  /** Makes a specific Blossom server the default server (move it to the top of the list) */
30
31
  export function SetDefaultBlossomServer(server) {
31
- return async function* ({ events, factory, self }) {
32
- const servers = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
33
- const prependTag = (tag) => (draft) => ({ ...draft, tags: [tag, ...draft.tags] });
34
- const operations = [removeBlossomServer(server), prependTag(["server", String(server)])];
35
- const draft = servers
36
- ? await factory.modify(servers, ...operations)
37
- : await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations);
38
- yield await factory.sign(draft);
39
- };
32
+ return ModifyBlossomServersEvent([removeBlossomServer(server), prependTag(["server", String(server)])]);
40
33
  }
41
34
  /** Creates a new Blossom servers event */
42
35
  export function NewBlossomServers(servers) {
43
- return async function* ({ events, factory, self }) {
36
+ return async ({ events, factory, self, user, publish, sign }) => {
44
37
  const existing = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
45
38
  if (existing)
46
39
  throw new Error("Blossom servers event already exists");
47
40
  const operations = servers ? servers.map((s) => addBlossomServer(s)) : [];
48
- const draft = await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations);
49
- yield await factory.sign(draft);
41
+ const signed = await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations).then(sign);
42
+ await publish(signed, await user.outboxes$.$first(1000, undefined));
50
43
  };
51
44
  }
@@ -1,3 +1,4 @@
1
+ import { AddressPointer, EventPointer } from "applesauce-core/helpers";
1
2
  import { NostrEvent } from "applesauce-core/helpers/event";
2
3
  import { Action } from "../action-hub.js";
3
4
  /**
@@ -6,14 +7,14 @@ import { Action } from "../action-hub.js";
6
7
  * @param identifier the "d" tag of the bookmark set or `undefined` for the default bookmark list
7
8
  * @param hidden set to true to add to hidden bookmarks
8
9
  */
9
- export declare function BookmarkEvent(event: NostrEvent, identifier?: string | NostrEvent, hidden?: boolean): Action;
10
+ export declare function BookmarkEvent(event: NostrEvent | EventPointer | AddressPointer, identifier?: string | NostrEvent, hidden?: boolean): Action;
10
11
  /**
11
12
  * An action that removes a note or article from the bookmark list or bookmark set
12
13
  * @param event the event to remove from bookmarks
13
14
  * @param identifier the "d" tag of the bookmark set or `undefined` for the default bookmark list
14
15
  * @param hidden set to true to remove from hidden bookmarks
15
16
  */
16
- export declare function UnbookmarkEvent(event: NostrEvent, identifier?: string | NostrEvent, hidden?: boolean): Action;
17
+ export declare function UnbookmarkEvent(event: NostrEvent | EventPointer | AddressPointer, identifier?: string | NostrEvent, hidden?: boolean): Action;
17
18
  /** An action that creates a new bookmark list for a user */
18
19
  export declare function CreateBookmarkList(bookmarks?: NostrEvent[]): Action;
19
20
  /** An action that creates a new bookmark set for a user */
@@ -1,40 +1,55 @@
1
1
  import * as List from "applesauce-common/operations/list";
2
2
  import { addEventBookmarkTag, removeEventBookmarkTag } from "applesauce-common/operations/tag/bookmarks";
3
- import { kinds } from "applesauce-core/helpers/event";
3
+ import { getReplaceableIdentifier, kinds } from "applesauce-core/helpers/event";
4
4
  import { modifyHiddenTags, modifyPublicTags } from "applesauce-core/operations";
5
+ function ModifyBookmarkSetEvent(operations, set, hidden = false) {
6
+ const identifier = typeof set === "string" ? set : getReplaceableIdentifier(set);
7
+ return async ({ factory, user, publish, sign }) => {
8
+ const [event, outboxes] = await Promise.all([
9
+ user.replaceable(kinds.Bookmarksets, identifier).$first(1000, undefined),
10
+ user.outboxes$.$first(1000, undefined),
11
+ ]);
12
+ const operation = hidden ? modifyHiddenTags(...operations) : modifyPublicTags(...operations);
13
+ // Modify or build new event
14
+ const signed = event
15
+ ? await factory.modify(event, operation).then(sign)
16
+ : await factory.build({ kind: kinds.Bookmarksets }, operation).then(sign);
17
+ // Publish the event to the user's outboxes
18
+ await publish(signed, outboxes);
19
+ };
20
+ }
21
+ function ModifyBookmarkListEvent(operations, hidden = false) {
22
+ return async ({ factory, user, publish, sign }) => {
23
+ const [event, outboxes] = await Promise.all([
24
+ user.replaceable(kinds.BookmarkList).$first(1000, undefined),
25
+ user.outboxes$.$first(1000, undefined),
26
+ ]);
27
+ const operation = hidden ? modifyHiddenTags(...operations) : modifyPublicTags(...operations);
28
+ // Modify or build new event
29
+ const signed = event
30
+ ? await factory.modify(event, operation).then(sign)
31
+ : await factory.build({ kind: kinds.BookmarkList }, operation).then(sign);
32
+ // Publish the event to the user's outboxes
33
+ await publish(signed, outboxes);
34
+ };
35
+ }
5
36
  /**
6
37
  * An action that adds a note or article to the bookmark list or a bookmark set
7
38
  * @param event the event to bookmark
8
39
  * @param identifier the "d" tag of the bookmark set or `undefined` for the default bookmark list
9
40
  * @param hidden set to true to add to hidden bookmarks
10
41
  */
11
- export function BookmarkEvent(event, identifier, hidden = false) {
12
- return async function* ({ events, factory, self }) {
13
- let draft;
14
- const operation = addEventBookmarkTag(event);
15
- if (typeof identifier === "string" || identifier?.kind === kinds.Bookmarksets) {
16
- const list = typeof identifier === "string" ? events.getReplaceable(kinds.Bookmarksets, self, identifier) : identifier;
17
- if (list && list.kind !== kinds.Bookmarksets)
18
- throw new Error("Event is not a bookmark set");
19
- // Modify or build new event bookmark set
20
- draft = list
21
- ? await factory.modifyTags(list, hidden ? { hidden: operation } : operation)
22
- : await factory.build({ kind: kinds.Bookmarksets }, hidden ? modifyHiddenTags(operation) : modifyPublicTags(operation));
23
- }
24
- else if (identifier === undefined || identifier?.kind === kinds.BookmarkList) {
25
- const list = identifier ? identifier : events.getReplaceable(kinds.BookmarkList, self);
26
- if (list && list.kind !== kinds.BookmarkList)
27
- throw new Error("Event is not a bookmark list");
28
- // Modify or build new event bookmark list
29
- draft = list
30
- ? await factory.modifyTags(list, hidden ? { hidden: operation } : operation)
31
- : await factory.build({ kind: kinds.BookmarkList }, hidden ? modifyHiddenTags(operation) : modifyPublicTags(operation));
32
- }
33
- else {
34
- throw new Error(`Event kind ${identifier.kind} is not a bookmark list or bookmark set`);
35
- }
36
- yield await factory.sign(draft);
37
- };
42
+ export function BookmarkEvent(event, identifier, hidden) {
43
+ const operation = addEventBookmarkTag(event);
44
+ if (typeof identifier === "string" || identifier?.kind === kinds.Bookmarksets) {
45
+ return ModifyBookmarkSetEvent([operation], identifier, hidden);
46
+ }
47
+ else if (identifier === undefined || identifier?.kind === kinds.BookmarkList) {
48
+ return ModifyBookmarkListEvent([operation], hidden);
49
+ }
50
+ else {
51
+ throw new Error(`Event kind ${identifier.kind} is not a bookmark list or bookmark set`);
52
+ }
38
53
  }
39
54
  /**
40
55
  * An action that removes a note or article from the bookmark list or bookmark set
@@ -42,42 +57,36 @@ export function BookmarkEvent(event, identifier, hidden = false) {
42
57
  * @param identifier the "d" tag of the bookmark set or `undefined` for the default bookmark list
43
58
  * @param hidden set to true to remove from hidden bookmarks
44
59
  */
45
- export function UnbookmarkEvent(event, identifier, hidden = false) {
46
- return async function* ({ events, factory, self }) {
47
- let list;
48
- const operation = removeEventBookmarkTag(event);
49
- if (typeof identifier === "string" || identifier?.kind === kinds.Bookmarksets) {
50
- list = typeof identifier === "string" ? events.getReplaceable(kinds.Bookmarksets, self, identifier) : identifier;
51
- }
52
- else if (identifier === undefined || identifier?.kind === kinds.BookmarkList) {
53
- list = identifier ? identifier : events.getReplaceable(kinds.BookmarkList, self);
54
- if (!list)
55
- return;
56
- }
57
- else {
58
- throw new Error(`Event kind ${identifier.kind} is not a bookmark list or bookmark set`);
59
- }
60
- // If no list is found, return
61
- if (!list)
62
- return;
63
- const draft = await factory.modifyTags(list, hidden ? { hidden: operation } : operation);
64
- yield await factory.sign(draft);
65
- };
60
+ export function UnbookmarkEvent(event, identifier, hidden) {
61
+ const operation = removeEventBookmarkTag(event);
62
+ if (typeof identifier === "string" || identifier?.kind === kinds.Bookmarksets) {
63
+ return ModifyBookmarkSetEvent([operation], identifier, hidden);
64
+ }
65
+ else if (identifier === undefined || identifier?.kind === kinds.BookmarkList) {
66
+ return ModifyBookmarkListEvent([operation], hidden);
67
+ }
68
+ else {
69
+ throw new Error(`Event kind ${identifier.kind} is not a bookmark list or bookmark set`);
70
+ }
66
71
  }
67
72
  /** An action that creates a new bookmark list for a user */
68
73
  export function CreateBookmarkList(bookmarks) {
69
- return async function* ({ events, factory, self }) {
74
+ return async ({ events, factory, self, user, publish, sign }) => {
70
75
  const existing = events.getReplaceable(kinds.BookmarkList, self);
71
76
  if (existing)
72
77
  throw new Error("Bookmark list already exists");
73
- const draft = await factory.build({ kind: kinds.BookmarkList }, bookmarks ? modifyPublicTags(...bookmarks.map(addEventBookmarkTag)) : undefined);
74
- yield await factory.sign(draft);
78
+ const signed = await factory
79
+ .build({ kind: kinds.BookmarkList }, bookmarks ? modifyPublicTags(...bookmarks.map(addEventBookmarkTag)) : undefined)
80
+ .then(sign);
81
+ await publish(signed, await user.outboxes$.$first(1000, undefined));
75
82
  };
76
83
  }
77
84
  /** An action that creates a new bookmark set for a user */
78
85
  export function CreateBookmarkSet(title, description, additional) {
79
- return async function* ({ factory }) {
80
- const draft = await factory.build({ kind: kinds.BookmarkList }, List.setTitle(title), List.setDescription(description), additional.image ? List.setImage(additional.image) : undefined, additional.public ? modifyPublicTags(...additional.public.map(addEventBookmarkTag)) : undefined, additional.hidden ? modifyHiddenTags(...additional.hidden.map(addEventBookmarkTag)) : undefined);
81
- yield await factory.sign(draft);
86
+ return async ({ factory, user, publish, sign }) => {
87
+ const signed = await factory
88
+ .build({ kind: kinds.BookmarkList }, List.setTitle(title), List.setDescription(description), additional.image ? List.setImage(additional.image) : undefined, additional.public ? modifyPublicTags(...additional.public.map(addEventBookmarkTag)) : undefined, additional.hidden ? modifyHiddenTags(...additional.hidden.map(addEventBookmarkTag)) : undefined)
89
+ .then(sign);
90
+ await publish(signed, await user.outboxes$.$first(1000, undefined));
82
91
  };
83
92
  }
@@ -1,6 +1,9 @@
1
+ import { AddressPointer } from "applesauce-core/helpers";
1
2
  import { NostrEvent } from "applesauce-core/helpers/event";
2
3
  import { Action } from "../action-hub.js";
3
4
  /** Adds a calendar event to a calendar */
4
- export declare function AddEventToCalendar(calendar: NostrEvent, event: NostrEvent): Action;
5
+ export declare function AddEventToCalendar(calendar: NostrEvent | AddressPointer, event: NostrEvent | AddressPointer): Action;
5
6
  /** Removes a calendar event from a calendar */
6
- export declare function RemoveEventFromCalendar(calendar: NostrEvent, event: NostrEvent): Action;
7
+ export declare function RemoveEventFromCalendar(calendar: NostrEvent | AddressPointer, event: NostrEvent | AddressPointer): Action;
8
+ /** Creates a new calendar event with title and events */
9
+ export declare function CreateCalendar(title: string, events?: (NostrEvent | AddressPointer)[]): Action;
@@ -1,25 +1,39 @@
1
1
  import { DATE_BASED_CALENDAR_EVENT_KIND, TIME_BASED_CALENDAR_EVENT_KIND, } from "applesauce-common/helpers/calendar-event";
2
- import { kinds } from "applesauce-core/helpers/event";
3
2
  import * as Calendar from "applesauce-common/operations/calendar";
3
+ import { isEvent, kinds } from "applesauce-core/helpers/event";
4
+ import { firstValueFrom, of, timeout } from "rxjs";
5
+ function ModifyCalendarEvent(calendar, operations) {
6
+ return async ({ factory, user, publish, events, sign }) => {
7
+ const [event, outboxes] = await Promise.all([
8
+ isEvent(calendar)
9
+ ? Promise.resolve(calendar)
10
+ : firstValueFrom(events.replaceable(kinds.Calendar, user.pubkey).pipe(timeout({ first: 1000, with: () => of(undefined) }))),
11
+ user.outboxes$.$first(1000, undefined),
12
+ ]);
13
+ if (!event)
14
+ throw new Error("Calendar event not found");
15
+ const signed = await factory.modify(event, ...operations).then(sign);
16
+ await publish(signed, outboxes);
17
+ };
18
+ }
4
19
  /** Adds a calendar event to a calendar */
5
20
  export function AddEventToCalendar(calendar, event) {
6
- if (calendar.kind !== kinds.Calendar)
7
- throw new Error("Calendar is not a calendar event");
8
21
  if (event.kind !== DATE_BASED_CALENDAR_EVENT_KIND && event.kind !== TIME_BASED_CALENDAR_EVENT_KIND)
9
22
  throw new Error("Event is not a calendar event");
10
- return async function* ({ factory }) {
11
- const draft = await factory.modify(calendar, Calendar.addEvent(event));
12
- return await factory.sign(draft);
13
- };
23
+ return ModifyCalendarEvent(calendar, [Calendar.addEvent(event)]);
14
24
  }
15
25
  /** Removes a calendar event from a calendar */
16
26
  export function RemoveEventFromCalendar(calendar, event) {
17
- if (calendar.kind !== kinds.Calendar)
18
- throw new Error("Calendar is not a calendar event");
19
27
  if (event.kind !== DATE_BASED_CALENDAR_EVENT_KIND && event.kind !== TIME_BASED_CALENDAR_EVENT_KIND)
20
28
  throw new Error("Event is not a calendar event");
21
- return async function* ({ factory }) {
22
- const draft = await factory.modify(calendar, Calendar.removeEvent(event));
23
- return await factory.sign(draft);
29
+ return ModifyCalendarEvent(calendar, [Calendar.removeEvent(event)]);
30
+ }
31
+ /** Creates a new calendar event with title and events */
32
+ export function CreateCalendar(title, events) {
33
+ return async ({ factory, sign, publish, user }) => {
34
+ const event = await factory
35
+ .build({ kind: kinds.Calendar }, Calendar.setTitle(title), ...(events?.map((event) => Calendar.addEvent(event)) ?? []))
36
+ .then(sign);
37
+ await publish(event, await user.outboxes$.$first(1000, undefined));
24
38
  };
25
39
  }
@@ -1,8 +1,8 @@
1
1
  import { ProfilePointer } from "applesauce-core/helpers/pointers";
2
2
  import { Action } from "../action-hub.js";
3
3
  /** An action that adds a pubkey to a users contacts event */
4
- export declare function FollowUser(pubkey: string, relay?: string, hidden?: boolean): Action;
4
+ export declare function FollowUser(user: string | ProfilePointer): Action;
5
5
  /** An action that removes a pubkey from a users contacts event */
6
- export declare function UnfollowUser(user: string | ProfilePointer, hidden?: boolean): Action;
6
+ export declare function UnfollowUser(user: string | ProfilePointer): Action;
7
7
  /** An action that creates a new kind 3 contacts lists, throws if a contact list already exists */
8
8
  export declare function NewContacts(pubkeys?: (string | ProfilePointer)[]): Action;