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
3 changes: 2 additions & 1 deletion src/app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export default async function Page({ params }: PageProps) {

// Always use ArticleLayout for consistency, even for purchased content
const hideNewsletter = !!(content?.commerce?.requiresEmail && !isSubscribed)
const isAuthenticated = !!session?.user?.email

return (
<>
Expand All @@ -105,7 +106,7 @@ export default async function Page({ params }: PageProps) {
{React.createElement(MdxContent)}
</div>
) : (
renderPaywalledContent(MdxContent, content, hasPurchased, isSubscribed)
renderPaywalledContent(MdxContent, content, hasPurchased, isSubscribed, isAuthenticated)
)}
</ArticleLayout>
</>
Expand Down
3 changes: 2 additions & 1 deletion src/app/videos/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export default async function VideoSlugPage({ params }: PageProps) {

// Always use ArticleLayout for consistency, even for purchased content
const hideNewsletter = !!(content?.commerce?.requiresEmail && !isSubscribed)
const isAuthenticated = !!session?.user?.email

return (
<>
Expand All @@ -99,7 +100,7 @@ export default async function VideoSlugPage({ params }: PageProps) {
{React.createElement(MdxContent)}
</div>
) : (
renderPaywalledContent(MdxContent, content, hasPurchased, isSubscribed)
renderPaywalledContent(MdxContent, content, hasPurchased, isSubscribed, isAuthenticated)
)}
</ArticleLayout>
</>
Expand Down
6 changes: 4 additions & 2 deletions src/components/ArticleContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface ArticleContentProps {
content: Content
requiresEmail?: boolean
isSubscribed?: boolean
isAuthenticated?: boolean
}

export default function ArticleContent({
Expand All @@ -30,14 +31,15 @@ export default function ArticleContent({
buttonText,
content,
requiresEmail = false,
isSubscribed = false
isSubscribed = false,
isAuthenticated = false
}: ArticleContentProps) {
if (!content.slug) {
console.warn('ArticleContent: content.slug is missing, rendering full content')
return <>{children}</>
}

if (showFullContent || (requiresEmail && isSubscribed)) {
if (showFullContent || (requiresEmail && isSubscribed && isAuthenticated)) {
return <>{children}</>
}

Expand Down
14 changes: 14 additions & 0 deletions src/components/Newsletter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,20 @@ export default function Newsletter({ title, body, successMessage, onSubscribe =

// Update the form UI to show the user their subscription was successful
setSuccess(true);

// Trigger magic link sign-in via email to complete authentication flow
try {
await fetch('/api/auto-login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: data.email, sessionId: 'newsletter' })
});
// Redirect to built-in sign-in page with email prefilled
const params = new URLSearchParams({ email: data.email }).toString();
window.location.href = `/auth/login?${params}`;
} catch (e) {
console.error('Failed to trigger email sign-in after subscribe', e);
}
} catch (e) {
console.error("Newsletter submission error:", e);
setError(true);
Expand Down
12 changes: 10 additions & 2 deletions src/lib/content-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ function _processContentMetadata(contentType: string, directorySlug: string, raw
: (processedMetadata.title as any)?.default || 'Untitled';


// Normalize commerce.requiresEmail: allow string flag values like "sign in required"
if (processedMetadata.commerce && typeof (processedMetadata.commerce as any).requiresEmail === 'string') {
// Any non-empty string will be treated as true to enable the gate
(processedMetadata.commerce as any).requiresEmail = true as any;
}

const content: Content = {
_id: contentId,
slug: finalContentSlug, // Full URL path slug
Expand Down Expand Up @@ -459,13 +465,14 @@ export function renderPaywalledContent(
MdxContent: React.ComponentType,
content: Content, // Use the processed Content type
hasPurchased: boolean,
isSubscribed: boolean
isSubscribed: boolean,
isAuthenticated: boolean
) {
// Determine if we should show the full content
const showFullContent =
(!content.commerce?.isPaid && !content.commerce?.requiresEmail) ||
hasPurchased ||
(content.commerce?.requiresEmail && isSubscribed);
(content.commerce?.requiresEmail && isSubscribed && isAuthenticated);

// Get default paywall text based on content type
const defaultText = getDefaultPaywallText(content.type);
Expand Down Expand Up @@ -501,6 +508,7 @@ export function renderPaywalledContent(
buttonText: content.commerce?.buttonText || defaultText.buttonText,
requiresEmail: content.commerce?.requiresEmail,
isSubscribed: isSubscribed,
isAuthenticated: isAuthenticated,
// Pass content object itself if ArticleContent needs more data
content: content,
}
Expand Down