Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
bce2433
use component api
ianmacartney Nov 4, 2025
ba60af1
improve configs & fix bundler errors
ianmacartney Nov 4, 2025
1e3d107
update test config
ianmacartney Nov 4, 2025
f7d98cc
package scripts
ianmacartney Nov 4, 2025
84788ad
Standardize prettier config and format
ianmacartney Nov 4, 2025
7eba27c
lint
ianmacartney Nov 4, 2025
fb9a133
update codegen
ianmacartney Nov 6, 2025
bec4c1e
simple tsconfig
ianmacartney Nov 6, 2025
f89d630
fancy new codegen
ianmacartney Nov 6, 2025
8589633
changelog
ianmacartney Nov 6, 2025
c3c8102
github test workflow
ianmacartney Nov 6, 2025
defd7b7
0.3.0
ianmacartney Nov 6, 2025
93f63f2
contributing
ianmacartney Nov 6, 2025
401ad7a
update module resolution
ianmacartney Nov 7, 2025
547986f
remove attw
ianmacartney Nov 7, 2025
74731ca
add ./convex.config.js entrypoint
ianmacartney Nov 7, 2025
d50af0f
install chokidar-cli
ianmacartney Nov 7, 2025
9335d57
fix some type errors
ianmacartney Nov 7, 2025
1ff0367
move expo into subdir
ianmacartney Nov 7, 2025
f714693
drop react and expo deps
ianmacartney Nov 7, 2025
49b0c65
create expo app & install convex
ianmacartney Nov 7, 2025
65eca28
readme
ianmacartney Nov 7, 2025
c196077
npx expo install expo-notifications expo-device expo-constants
ianmacartney Nov 7, 2025
d386a7e
add App.tsx from https://docs.expo.dev/push-notifications/push-notifi…
ianmacartney Nov 7, 2025
0262772
add Demo.tsx with convex
ianmacartney Nov 7, 2025
2362bec
install convex
ianmacartney Nov 7, 2025
5fb5667
add convex and app to layout
ianmacartney Nov 7, 2025
7442016
contributing setup
ianmacartney Nov 8, 2025
e78e256
add eas for example project
ianmacartney Nov 8, 2025
03c99de
import convex from package
ianmacartney Nov 8, 2025
111d93f
safer check on token
ianmacartney Nov 8, 2025
538ef6c
use expo-54
ianmacartney Nov 8, 2025
b1cf884
install example in ci
ianmacartney Nov 8, 2025
d54d768
improve app for web
ianmacartney Nov 8, 2025
1ba3fef
handle symlink to parent
ianmacartney Nov 8, 2025
023b2d2
simplify expo code
ianmacartney Nov 8, 2025
7152823
scripts
ianmacartney Nov 8, 2025
e7b02fa
contributing
ianmacartney Nov 8, 2025
9fc0e8f
document hack for setup.test
ianmacartney Nov 8, 2025
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
22 changes: 14 additions & 8 deletions .expo/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
> Why do I have a folder named ".expo" in my project?
The ".expo" folder is created when an Expo project is started using "expo start" command.
> What do the files contain?
- "devices.json": contains information about devices that have recently opened this project. This is used to populate the "Development sessions" list in your development builds.
- "settings.json": contains the server configuration that is used to serve the application manifest.
> Should I commit the ".expo" folder?
No, you should not share the ".expo" folder. It does not contain any information that is relevant for other developers working on the project, it is specific to your machine.
Upon project creation, the ".expo" folder is already added to your ".gitignore" file.
> Why do I have a folder named ".expo" in my project? The ".expo" folder is
> created when an Expo project is started using "expo start" command. What do
> the files contain?

- "devices.json": contains information about devices that have recently opened
this project. This is used to populate the "Development sessions" list in your
development builds.
- "settings.json": contains the server configuration that is used to serve the
application manifest.
> Should I commit the ".expo" folder? No, you should not share the ".expo"
> folder. It does not contain any information that is relevant for other
> developers working on the project, it is specific to your machine. Upon
> project creation, the ".expo" folder is already added to your ".gitignore"
> file.
41 changes: 41 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Test and lint
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

on:
push:
branches: [main]
pull_request:
branches: ["**"]

jobs:
check:
name: Test and lint
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5

- name: Node setup
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
cache-dependency-path: |
package.json
example/package.json
node-version: "20.x"
cache: "npm"

- name: Install and build
run: |
npm i
npm run build
cd example && npm install
- name: Publish package for testing branch
run: npx pkg-pr-new publish || echo "Have you set up pkg-pr-new for this repo?"
- name: Test
run: |
npm run test
npm run typecheck
npm run lint
8 changes: 3 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ dist-ssr
explorations
node_modules
.eslintcache
# components are libraries!
package-lock.json

# this is a package-json-redirect stub dir, see https://github.com/andrewbranch/example-subpath-exports-ts-compat?tab=readme-ov-file
frontend/package.json
**/.expo
**/expo-env.d.ts
# npm pack output
*.tgz
*.tsbuildinfo
.claude
4 changes: 4 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"trailingComma": "all",
"proseWrap": "always"
}
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changelog

## 0.3.0

- Adds /test and /\_generated/component.js entrypoints
- Drops commonjs support
- Improves source mapping for generated files
- Changes to a statically generated component API
15 changes: 11 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# Claude Development Notes

## Codegen Command

After making changes to component functions, run codegen with:

```bash
cd example/ && npm run dev -- --once
```
Expand All @@ -10,18 +12,23 @@ cd example/ && npm run dev -- --once

When adding new functions to this Convex component:

1. **Client function** (`src/client/index.ts`):
- Add method to `PushNotifications` class that calls `ctx.runMutation(this.component.public.functionName, { ...args, logLevel: this.config.logLevel })`
1. **Client function** (`src/client/index.ts`):

- Add method to `PushNotifications` class that calls
`ctx.runMutation(this.component.public.functionName, { ...args, logLevel: this.config.logLevel })`
- Follow existing patterns for argument types and return types

2. **Component function** (`src/component/public.ts`):

- Define args schema using `v.object()`
- Export mutation/query with proper return type
- Call `ensureCoordinator(ctx)` after processing for coordination

3. **Batch functions**:
- Use existing handler functions (like `sendPushNotificationHandler`) in loops

- Use existing handler functions (like `sendPushNotificationHandler`) in
loops
- Call `ensureCoordinator` once after all processing
- Return array of results matching individual function return types

4. **Always run codegen** after changes to regenerate types
4. **Always run codegen** after changes to regenerate types
48 changes: 48 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Developing guide

## Running locally

```sh
npm run setup
npm run dev
```

Setup should:

1. Install dependencies in the root and example directories
1. Build the component
1. Create a Convex project if not already connected, and deploy to it once.
1. Set the `EXPO_PUBLIC_CONVEX_URL` environment variable for the example expo
app (needs to be set in the example expo app's `.env.local` file)

## Testing

```sh
npm run clean
npm run build
npm run typecheck
npm run lint
npm run test
```

## Deploying

### Building a one-off package

```sh
npm run clean
npm ci
npm pack
```

### Deploying a new version

```sh
npm run release
```

or for alpha release:

```sh
npm run alpha
```
46 changes: 29 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

<!-- START: Include on https://convex.dev/components -->

This is a Convex component that integrates with [Expo's push notification API](https://docs.expo.dev/push-notifications/overview/)
to allow sending mobile push notifications to users of your app. It will batch calls to Expo's API and handle retrying delivery.
This is a Convex component that integrates with
[Expo's push notification API](https://docs.expo.dev/push-notifications/overview/)
to allow sending mobile push notifications to users of your app. It will batch
calls to Expo's API and handle retrying delivery.

<details>
<summary>Demo GIF</summary>
Expand Down Expand Up @@ -51,11 +53,12 @@ export const sendPushNotification = mutation({

## Pre-requisite: Convex

You'll need an existing Convex project to use the component.
Convex is a hosted backend platform, including a database, serverless functions,
and a ton more you can learn about [here](https://docs.convex.dev/get-started).
You'll need an existing Convex project to use the component. Convex is a hosted
backend platform, including a database, serverless functions, and a ton more you
can learn about [here](https://docs.convex.dev/get-started).

Run `npm create convex` or follow any of the [quickstarts](https://docs.convex.dev/home) to set one up.
Run `npm create convex` or follow any of the
[quickstarts](https://docs.convex.dev/home) to set one up.

## Installation

Expand All @@ -65,12 +68,13 @@ Install the component package:
npm i @convex-dev/expo-push-notifications
```

Create a `convex.config.ts` file in your app's `convex/` folder and install the component by calling `use`:
Create a `convex.config.ts` file in your app's `convex/` folder and install the
component by calling `use`:

```ts
// convex/convex.config.ts
import { defineApp } from "convex/server";
import pushNotifications from "@convex-dev/expo-push-notifications/convex.config";
import pushNotifications from "@convex-dev/expo-push-notifications/convex.config.js";

const app = defineApp();
app.use(pushNotifications);
Expand All @@ -88,21 +92,24 @@ import { PushNotifications } from "@convex-dev/expo-push-notifications";
const pushNotifications = new PushNotifications(components.pushNotifications);
```

It takes in an optional type parameter (defaulting to `Id<"users">`) for the type to use as a unique identifier for push notification recipients:
It takes in an optional type parameter (defaulting to `Id<"users">`) for the
type to use as a unique identifier for push notification recipients:

```ts
import { PushNotifications } from "@convex-dev/expo-push-notifications";

export type Email = string & { __isEmail: true };

const pushNotifications = new PushNotifications<Email>(
components.pushNotifications
components.pushNotifications,
);
```

## Registering a user for push notifications

Get a user's push notification token following the Expo documentation [here](https://docs.expo.dev/push-notifications/push-notifications-setup/#registering-for-push-notifications), and record it using a Convex mutation:
Get a user's push notification token following the Expo documentation
[here](https://docs.expo.dev/push-notifications/push-notifications-setup/#registering-for-push-notifications),
and record it using a Convex mutation:

```ts
// convex/example.ts
Expand All @@ -118,9 +125,11 @@ export const recordPushNotificationToken = mutation({
});
```

You can pause and resume push notification sending for a user using the `pausePushNotifications` and `resumePushNotifications` methods.
You can pause and resume push notification sending for a user using the
`pausePushNotifications` and `resumePushNotifications` methods.

To determine if a user has a token and their pause status, you can use `getStatusForUser`.
To determine if a user has a token and their pause status, you can use
`getStatusForUser`.

## Send notifications

Expand All @@ -139,21 +148,24 @@ export const sendPushNotification = mutation({
});
```

You can use the ID returned from `sendPushNotifications` to query the status of the notification using `getNotification`.
Using this in a query allows you to subscribe to the status of a notification.
You can use the ID returned from `sendPushNotifications` to query the status of
the notification using `getNotification`. Using this in a query allows you to
subscribe to the status of a notification.

You can also view all notifications for a user with `getNotificationsForUser`.

## Troubleshooting

To add more logging, provide `PushNotifications` with a `logLevel` in the constructor:
To add more logging, provide `PushNotifications` with a `logLevel` in the
constructor:

```ts
const pushNotifications = new PushNotifications(components.pushNotifications, {
logLevel: "DEBUG",
});
```

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

<!-- END: Include on https://convex.dev/components -->
9 changes: 0 additions & 9 deletions commonjs.json

This file was deleted.

7 changes: 7 additions & 0 deletions convex.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "./node_modules/convex/schemas/convex.schema.json",
"functions": "example/convex",
"codegen": {
"legacyComponentApi": false
}
}
34 changes: 25 additions & 9 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,41 @@ import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";

export default [
{ files: ["src/**/*.{js,mjs,cjs,ts,tsx}"] },
{
ignores: [
"dist/**",
"eslint.config.js",
"*.config.{js,mjs,ts}",
"**/_generated/",
"node10stubs.mjs",
"example/.expo/**",
"example/**/*.config.{js,mjs,ts}",
"example/expo-env.d.ts",
],
},
{
files: [
"src/**/*.{js,mjs,cjs,ts,tsx}",
"example/convex/*.{js,mjs,cjs,ts,tsx}",
],
languageOptions: {
globals: globals.worker,
parser: tseslint.parser,

parserOptions: {
project: true,
tsconfigRootDir: ".",
project: ["./tsconfig.json", "./example/convex/tsconfig.json"],
tsconfigRootDir: import.meta.dirname,
},
},
},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
// Convex code - Worker environment
{
files: ["src/**/*.{ts,tsx}", "example/convex/**/*.{ts,tsx}"],
languageOptions: {
globals: globals.worker,
},
rules: {
"@typescript-eslint/no-floating-promises": "error",
"eslint-comments/no-unused-disable": "off",

// allow (_arg: number) => {} and const _foo = 1;
"@typescript-eslint/no-explicit-any": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
Expand All @@ -39,6 +46,15 @@ export default [
varsIgnorePattern: "^_",
},
],
"no-unused-expressions": "off",
"@typescript-eslint/no-unused-expressions": [
"error",
{
allowShortCircuit: true,
allowTernary: true,
allowTaggedTemplates: true,
},
],
},
},
];
9 changes: 0 additions & 9 deletions esm.json

This file was deleted.

2 changes: 2 additions & 0 deletions example/.env.local.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Get this from ../../.env.local 'CONVEX_URL'
EXPO_PUBLIC_CONVEX_URL=https://your-animal-123.convex.cloud
Loading
Loading