-
-
Notifications
You must be signed in to change notification settings - Fork 411
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Scaffold Knock integration * Misc fixes, regen lockfile * Collab on Notifications (#388) * update env vars * update notif provider * add secret key for server sdk * create feed component * make provider client --------- Co-authored-by: Jeff Everhart <jeffeverhart383@gmail.com> * Extract notifications into new package * Update feed.tsx * Move provider to package * Scaffold doc * Update env.ts * change key and stub docs (#393) * use public api key for feed * stub out docs * add link to keys docs * Fix keys * Add notifications feed to app * Update notifications.mdx * Temporary shadcn/ui patch shadcn-ui/ui#6334 --------- Co-authored-by: Jeff Everhart <jeffeverhart383@gmail.com>
- Loading branch information
1 parent
749b103
commit 0e9560e
Showing
22 changed files
with
923 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
--- | ||
title: Notifications | ||
description: In-app notifications for your users. | ||
--- | ||
|
||
import { Authors } from '/snippets/authors.mdx'; | ||
|
||
<Authors data={[{ | ||
user: { | ||
name: 'Hayden Bleasel', | ||
id: 'haydenbleasel', | ||
}, | ||
company: { | ||
name: 'next-forge', | ||
id: 'next-forge', | ||
}, | ||
}, { | ||
user: { | ||
name: 'Jeff Everhart', | ||
id: 'j_everhart383', | ||
}, | ||
company: { | ||
name: 'Knock', | ||
id: 'knock', | ||
}, | ||
}]} /> | ||
|
||
next-forge offers a notifications package that allows you to send in-app notifications to your users. By default, it uses [Knock](https://knock.app/), a cross-channel notification platform that supports in-app, email, SMS, push, and chat notifications. Knock allows you to centralize your notification logic and templates in one place and [orchestrate complex workflows](https://docs.knock.app/designing-workflows/overview) with things like branching, batching, throttling, delays, and conditional sending. | ||
|
||
## Setup | ||
|
||
To use the notifications package, you need to add the required environment variables to your project, as specified in the `packages/notifications/keys.ts` file. | ||
|
||
## In-app notifications feed | ||
|
||
To render an in-app notifications feed, import the `NotificationsTrigger` component from the `@repo/notifications` package and use it in your app. We've already added this to the sidebar in the example app: | ||
|
||
```tsx apps/app/app/(authenticated)/components/sidebar.tsx | ||
import { NotificationsTrigger } from '@repo/notifications/components/trigger'; | ||
|
||
<NotificationsTrigger> | ||
<Button variant="ghost" size="icon" className="shrink-0"> | ||
<BellIcon size={16} className="text-muted-foreground" /> | ||
</Button> | ||
</NotificationsTrigger> | ||
``` | ||
|
||
Pressing the button will open the in-app notifications feed, which displays all of the notifications for the current user. | ||
|
||
## Send a notification | ||
|
||
Knock sends notifications using workflows. To send an in-app notification, create a new [workflow](https://docs.knock.app/concepts/workflows) in the Knock dashboard that uses the [`in-app` channel provider](https://docs.knock.app/integrations/in-app/knock) and create a corresponding message template. | ||
|
||
Then you can [trigger that workflow](https://docs.knock.app/send-notifications/triggering-workflows) for a particular user in your app, passing in the necessary data to populate the message template: | ||
|
||
```tsx notify.ts | ||
import { notifications } from '@repo/notifications'; | ||
|
||
await notifications.workflows.trigger('workflow-key', { | ||
recipients: [{ | ||
id: 'user-id', | ||
email: 'user-email', | ||
}], | ||
data: { | ||
message: 'Hello, world!', | ||
}, | ||
}); | ||
``` | ||
|
||
## Multi-channel notifications | ||
|
||
Using Knock, you can add additional channel providers to your workflow to send notifications via email, SMS, push, or chat. To do this, create a new [channel provider](https://docs.knock.app/integrations) in the Knock dashboard, follow any configuration instructions for that provider, and add it to your workflow as a channel step. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
'use client'; | ||
|
||
import { KnockFeedProvider, KnockProvider } from '@knocklabs/react'; | ||
import type { ReactNode } from 'react'; | ||
import { keys } from '../keys'; | ||
|
||
const knockApiKey = keys().NEXT_PUBLIC_KNOCK_API_KEY; | ||
const knockFeedChannelId = keys().NEXT_PUBLIC_KNOCK_FEED_CHANNEL_ID; | ||
|
||
type NotificationsProviderProps = { | ||
children: ReactNode; | ||
userId: string; | ||
}; | ||
|
||
export const NotificationsProvider = ({ | ||
children, | ||
userId, | ||
}: NotificationsProviderProps) => { | ||
if (!knockApiKey || !knockFeedChannelId) { | ||
return children; | ||
} | ||
|
||
return ( | ||
<KnockProvider apiKey={knockApiKey} userId={userId}> | ||
<KnockFeedProvider feedId={knockFeedChannelId}> | ||
{children} | ||
</KnockFeedProvider> | ||
</KnockProvider> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
'use client'; | ||
|
||
import { NotificationFeedPopover } from '@knocklabs/react'; | ||
import { useRef, useState } from 'react'; | ||
import type { ReactElement, RefObject } from 'react'; | ||
import { keys } from '../keys'; | ||
|
||
// Required CSS import, unless you're overriding the styling | ||
import '@knocklabs/react/dist/index.css'; | ||
|
||
type NotificationsTriggerProperties = { | ||
children: ReactElement; | ||
}; | ||
|
||
export const NotificationsTrigger = ({ | ||
children, | ||
}: NotificationsTriggerProperties) => { | ||
const [isVisible, setIsVisible] = useState(false); | ||
const notifButtonRef = useRef<HTMLDivElement>(null); | ||
|
||
if (!keys().NEXT_PUBLIC_KNOCK_API_KEY) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<> | ||
{/* biome-ignore lint/nursery/noStaticElementInteractions: "avoid nested buttons" */} | ||
<div | ||
onClick={() => setIsVisible(!isVisible)} | ||
onKeyDown={(e) => { | ||
if (e.key === 'Enter' || e.key === ' ') { | ||
setIsVisible(!isVisible); | ||
} | ||
}} | ||
ref={notifButtonRef} | ||
> | ||
{children} | ||
</div> | ||
{notifButtonRef.current && ( | ||
<NotificationFeedPopover | ||
buttonRef={notifButtonRef as RefObject<HTMLElement>} | ||
isVisible={isVisible} | ||
onClose={() => setIsVisible(false)} | ||
/> | ||
)} | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { Knock } from '@knocklabs/node'; | ||
import { keys } from './keys'; | ||
|
||
const key = keys().KNOCK_SECRET_API_KEY; | ||
|
||
export const notifications = new Knock(key); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { createEnv } from '@t3-oss/env-nextjs'; | ||
import { z } from 'zod'; | ||
|
||
export const keys = () => | ||
createEnv({ | ||
server: { | ||
KNOCK_SECRET_API_KEY: z.string().min(1).optional(), | ||
}, | ||
client: { | ||
NEXT_PUBLIC_KNOCK_API_KEY: z.string().min(1).optional(), | ||
NEXT_PUBLIC_KNOCK_FEED_CHANNEL_ID: z.string().min(1).optional(), | ||
}, | ||
runtimeEnv: { | ||
NEXT_PUBLIC_KNOCK_API_KEY: process.env.NEXT_PUBLIC_KNOCK_API_KEY, | ||
NEXT_PUBLIC_KNOCK_FEED_CHANNEL_ID: | ||
process.env.NEXT_PUBLIC_KNOCK_FEED_CHANNEL_ID, | ||
KNOCK_SECRET_API_KEY: process.env.KNOCK_SECRET_API_KEY, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "@repo/notifications", | ||
"version": "0.0.0", | ||
"private": true, | ||
"scripts": { | ||
"clean": "git clean -xdf .cache .turbo dist node_modules", | ||
"typecheck": "tsc --noEmit --emitDeclarationOnly false" | ||
}, | ||
"dependencies": { | ||
"@knocklabs/node": "^0.6.13", | ||
"@knocklabs/react": "^0.2.29", | ||
"@t3-oss/env-nextjs": "^0.11.1", | ||
"react": "^19.0.0", | ||
"zod": "^3.24.1" | ||
}, | ||
"devDependencies": { | ||
"@repo/typescript-config": "workspace:*", | ||
"typescript": "^5.7.2", | ||
"@types/node": "22.10.5", | ||
"@types/react": "19.0.2", | ||
"@types/react-dom": "^19.0.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"extends": "@repo/typescript-config/nextjs.json", | ||
"compilerOptions": { | ||
"baseUrl": "." | ||
}, | ||
"include": ["**/*.ts", "**/*.tsx"], | ||
"exclude": ["node_modules"] | ||
} |
Oops, something went wrong.