feat: page update notifications (#2074)

* feat: watchers notification and email preferences

* fix: email copy

* digests

* clean up

* fix

* clean up

* move backlinks queue-up to history processor

* fix

* fix keys

* feat: group notifications

* filter

* adjust email digest window
This commit is contained in:
Philip Okugbe
2026-03-31 16:03:59 +01:00
committed by GitHub
parent c180d0e487
commit 879aa2c3d8
39 changed files with 983 additions and 73 deletions
@@ -0,0 +1,76 @@
import { Link, Section, Text } from '@react-email/components';
import * as React from 'react';
import { content, link, paragraph } from '../css/styles';
import { getGreetingName, MailBody } from '../partials/partials';
interface PageUpdate {
title: string;
url: string;
updatedBy: string[];
}
interface Props {
userName: string;
pageUpdates: PageUpdate[];
totalUpdates: number;
}
export const PageUpdateDigestEmail = ({
userName,
pageUpdates,
totalUpdates,
}: Props) => {
return (
<MailBody>
<Section style={content}>
<Text style={paragraph}>
Hi {getGreetingName(userName)},
</Text>
<Text style={paragraph}>
There {totalUpdates === 1 ? 'has' : 'have'} been{' '}
<strong>
{totalUpdates} update{totalUpdates === 1 ? '' : 's'}
</strong>{' '}
since your last update.
</Text>
{pageUpdates.map((page, i) => (
<Section key={i} style={pageCard}>
<Text style={pageTitle}>
<Link href={page.url} style={link}>
{page.title}
</Link>
</Text>
{page.updatedBy.length > 0 && (
<Text style={updatedByText}>
Edited by {page.updatedBy.join(', ')}
</Text>
)}
</Section>
))}
</Section>
</MailBody>
);
};
const pageCard = {
borderLeft: '3px solid #e8e5ef',
paddingLeft: '12px',
marginBottom: '12px',
};
const pageTitle = {
...paragraph,
margin: '0 0 2px 0',
fontSize: 14,
fontWeight: 'bold' as const,
};
const updatedByText = {
...paragraph,
margin: '0',
fontSize: 13,
color: '#666',
};
export default PageUpdateDigestEmail;
@@ -0,0 +1,36 @@
import { Link, Section, Text } from '@react-email/components';
import * as React from 'react';
import { content, link, paragraph } from '../css/styles';
import { EmailButton, getGreetingName, MailBody } from '../partials/partials';
interface Props {
userName: string;
actorName: string;
pageTitle: string;
pageUrl: string;
}
export const PageUpdateEmail = ({
userName,
actorName,
pageTitle,
pageUrl,
}: Props) => {
return (
<MailBody>
<Section style={content}>
<Text style={paragraph}>Hi {getGreetingName(userName)},</Text>
<Text style={paragraph}>
<strong>{actorName}</strong> updated{' '}
<Link href={pageUrl} style={link}>
<strong>{pageTitle}</strong>
</Link>
.
</Text>
</Section>
<EmailButton href={pageUrl}>View page</EmailButton>
</MailBody>
);
};
export default PageUpdateEmail;