Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,15 @@ const pushNotifications = new PushNotifications(components.pushNotifications, {
});
```

If you have an Expo access token, pass it as `expoAccessToken` so requests to
the Expo push API include the `Authorization: Bearer <token>` header:

```ts
const pushNotifications = new PushNotifications(components.pushNotifications, {
expoAccessToken: process.env.EXPO_ACCESS_TOKEN,
});
```

The push notification sender can be shutdown gracefully, and then restarted
using the `shutdown` and `restart` methods.

Expand Down
16 changes: 15 additions & 1 deletion src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@ import type { ComponentApi } from "../component/_generated/component.js";
export class PushNotifications<UserType extends string = GenericId<"users">> {
private config: {
logLevel: LogLevel;
expoAccessToken?: string;
};
constructor(
public component: ComponentApi,
config?: {
logLevel?: LogLevel;
expoAccessToken?: string;
},
) {
this.component = component;
this.config = {
...(config ?? {}),
logLevel: config?.logLevel ?? "ERROR",
expoAccessToken: config?.expoAccessToken,
};
}

Expand All @@ -47,6 +49,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
return ctx.runMutation(this.component.public.recordPushNotificationToken, {
...args,
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}

Expand All @@ -59,6 +62,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
return ctx.runMutation(this.component.public.removePushNotificationToken, {
...args,
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}

Expand All @@ -69,6 +73,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
return ctx.runQuery(this.component.public.getStatusForUser, {
...args,
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}

Expand Down Expand Up @@ -97,6 +102,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
return ctx.runMutation(this.component.public.sendPushNotification, {
...args,
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}

Expand All @@ -120,6 +126,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
return ctx.runMutation(this.component.public.sendPushNotificationBatch, {
...args,
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}

Expand All @@ -131,6 +138,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
return ctx.runQuery(this.component.public.getNotification, {
...args,
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}

Expand All @@ -144,6 +152,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
return ctx.runQuery(this.component.public.getNotificationsForUser, {
...args,
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}

Expand All @@ -154,6 +163,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
return ctx.runMutation(this.component.public.deleteNotificationsForUser, {
...args,
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}

Expand All @@ -167,6 +177,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
return ctx.runMutation(this.component.public.pauseNotificationsForUser, {
...args,
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}

Expand All @@ -180,6 +191,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
return ctx.runMutation(this.component.public.unpauseNotificationsForUser, {
...args,
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}

Expand All @@ -195,6 +207,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
shutdown(ctx: RunMutationCtx) {
return ctx.runMutation(this.component.public.shutdown, {
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}

Expand All @@ -208,6 +221,7 @@ export class PushNotifications<UserType extends string = GenericId<"users">> {
restart(ctx: RunMutationCtx): Promise<boolean> {
return ctx.runMutation(this.component.public.restart, {
logLevel: this.config.logLevel,
expoAccessToken: this.config.expoAccessToken,
});
}
}
Expand Down
44 changes: 36 additions & 8 deletions src/component/_generated/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,22 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
deleteNotificationsForUser: FunctionReference<
"mutation",
"internal",
{ logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR"; userId: string },
{
expoAccessToken?: string;
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
userId: string;
},
null,
Name
>;
getNotification: FunctionReference<
"query",
"internal",
{ id: string; logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR" },
{
expoAccessToken?: string;
id: string;
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
},
null | {
_contentAvailable?: boolean;
_creationTime: number;
Expand Down Expand Up @@ -71,6 +79,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
"query",
"internal",
{
expoAccessToken?: string;
limit?: number;
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
userId: string;
Expand Down Expand Up @@ -111,21 +120,30 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
getStatusForUser: FunctionReference<
"query",
"internal",
{ logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR"; userId: string },
{
expoAccessToken?: string;
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
userId: string;
},
{ hasToken: boolean; paused: boolean },
Name
>;
pauseNotificationsForUser: FunctionReference<
"mutation",
"internal",
{ logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR"; userId: string },
{
expoAccessToken?: string;
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
userId: string;
},
null,
Name
>;
recordPushNotificationToken: FunctionReference<
"mutation",
"internal",
{
expoAccessToken?: string;
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
pushToken: string;
userId: string;
Expand All @@ -136,14 +154,18 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
removePushNotificationToken: FunctionReference<
"mutation",
"internal",
{ logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR"; userId: string },
{
expoAccessToken?: string;
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
userId: string;
},
null,
Name
>;
restart: FunctionReference<
"mutation",
"internal",
{ logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR" },
{ expoAccessToken?: string; logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR" },
boolean,
Name
>;
Expand All @@ -152,6 +174,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
"internal",
{
allowUnregisteredTokens?: boolean;
expoAccessToken?: string;
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
notification: {
_contentAvailable?: boolean;
Expand Down Expand Up @@ -183,6 +206,7 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
"internal",
{
allowUnregisteredTokens?: boolean;
expoAccessToken?: string;
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
notifications: Array<{
notification: {
Expand Down Expand Up @@ -214,14 +238,18 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
shutdown: FunctionReference<
"mutation",
"internal",
{ logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR" },
{ expoAccessToken?: string; logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR" },
{ data?: any; message: string },
Name
>;
unpauseNotificationsForUser: FunctionReference<
"mutation",
"internal",
{ logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR"; userId: string },
{
expoAccessToken?: string;
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
userId: string;
},
null,
Name
>;
Expand Down
63 changes: 51 additions & 12 deletions src/component/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,87 @@ import {
customMutation,
customQuery,
} from "convex-helpers/server/customFunctions";
import { v } from "convex/values";
import * as VanillaConvex from "./_generated/server.js";
import { Logger, logLevelValidator } from "../logging/index.js";

const expoAccessTokenValidator = v.optional(v.string());

export const query = customQuery(VanillaConvex.query, {
args: { logLevel: logLevelValidator },
args: { logLevel: logLevelValidator, expoAccessToken: expoAccessTokenValidator },
input: async (ctx, args) => {
return { ctx: { logger: new Logger(args.logLevel) }, args: {} };
return {
ctx: {
logger: new Logger(args.logLevel),
expoAccessToken: args.expoAccessToken,
},
args: {},
};
},
});

export const mutation = customMutation(VanillaConvex.mutation, {
args: { logLevel: logLevelValidator },
args: { logLevel: logLevelValidator, expoAccessToken: expoAccessTokenValidator },
input: async (ctx, args) => {
return { ctx: { logger: new Logger(args.logLevel) }, args: {} };
return {
ctx: {
logger: new Logger(args.logLevel),
expoAccessToken: args.expoAccessToken,
},
args: {},
};
},
});

export const action = customAction(VanillaConvex.action, {
args: { logLevel: logLevelValidator },
args: { logLevel: logLevelValidator, expoAccessToken: expoAccessTokenValidator },
input: async (ctx, args) => {
return { ctx: { logger: new Logger(args.logLevel) }, args: {} };
return {
ctx: {
logger: new Logger(args.logLevel),
expoAccessToken: args.expoAccessToken,
},
args: {},
};
},
});

export const internalQuery = customQuery(VanillaConvex.internalQuery, {
args: { logLevel: logLevelValidator },
args: { logLevel: logLevelValidator, expoAccessToken: expoAccessTokenValidator },
input: async (ctx, args) => {
return { ctx: { logger: new Logger(args.logLevel) }, args: {} };
return {
ctx: {
logger: new Logger(args.logLevel),
expoAccessToken: args.expoAccessToken,
},
args: {},
};
},
});

export const internalMutation = customMutation(VanillaConvex.internalMutation, {
args: { logLevel: logLevelValidator },
args: { logLevel: logLevelValidator, expoAccessToken: expoAccessTokenValidator },
input: async (ctx, args) => {
return { ctx: { logger: new Logger(args.logLevel) }, args: {} };
return {
ctx: {
logger: new Logger(args.logLevel),
expoAccessToken: args.expoAccessToken,
},
args: {},
};
},
});

export const internalAction = customAction(VanillaConvex.internalAction, {
args: { logLevel: logLevelValidator },
args: { logLevel: logLevelValidator, expoAccessToken: expoAccessTokenValidator },
input: async (ctx, args) => {
return { ctx: { logger: new Logger(args.logLevel) }, args: {} };
return {
ctx: {
logger: new Logger(args.logLevel),
expoAccessToken: args.expoAccessToken,
},
args: {},
};
},
});

Expand Down
1 change: 1 addition & 0 deletions src/component/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export async function ensureCoordinator(ctx: MutationCtx) {
internal.internal.coordinateSendingPushNotifications,
{
logLevel: ctx.logger.level,
expoAccessToken: ctx.expoAccessToken,
},
);
const coordinatorId = await ctx.db.insert("senderCoordinator", {
Expand Down
Loading
Loading