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
182 changes: 182 additions & 0 deletions POC-GUEST-PREVIEW-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# Temporary Guest My News POC

This POC implements a temporary signed-in preview experience for guest users to test the hypothesis that allowing non-registered users to build a temporary My News experience will increase BBC account registrations.

## Features Implemented

### 1. Enhanced Save Button for Guests
- **Location**: `src/app/components/SaveArticleButton/SaveArticleButtonGuestWithPreview/`
- **Features**:
- Hover tooltip on save button with info: "Save this article for later. It will appear in your My News page."
- Save/unsave functionality using localStorage
- Confirmation notification after saving with link to My News
- Visual feedback showing saved state

### 2. Temporary Article Storage
- **Location**: `src/app/hooks/useTemporarySavedArticles/`
- **Features**:
- Stores articles in browser localStorage
- 2-day expiry period
- Auto-cleanup on expiry
- Tracks save timestamp and article metadata

### 3. Temporary My News Page
- **Location**: `ws-nextjs-app/pages/[service]/my-news/MyNewsPage/MyNewsPageTemporary/`
- **Features**:
- Displays saved articles in a grid
- Prominent banner explaining temporary nature
- Shows countdown to expiry
- Prompts for registration/sign-in with call-to-action buttons
- Integrates with existing AccountActionButtons component

### 4. Integration Points
- **Article Pages**: Save button now uses `enableGuestPreview` prop to activate POC
- **My News Page**: Automatically shows temporary view when user has saved articles but isn't signed in
- **Smooth transition**: When user signs in, they see their temporary saves migrate to permanent storage

## Usage

### Enable on Article Pages
The POC is enabled by default on article pages. The `SaveArticleButton` component accepts an `enableGuestPreview` prop:

```tsx
<SaveArticleButton
saveArticlePageData={extractSaveArticleProps(articlePageData)}
enableGuestPreview={true}
/>
```

### User Flow

1. **Article with Save Button**
- Guest user sees "Save for later" button
- Hovering shows tooltip explaining the feature

2. **Clicking Save**
- Article is saved to localStorage
- Confirmation notification appears with "View My News" link

3. **Visiting My News Page**
- Shows temporary My News page with:
- Banner: "This is a temporary page available for 2 days"
- Expiry countdown
- Sign in / Register buttons
- Grid of saved articles

4. **Registration/Sign-in**
- User clicks register or sign in
- Completes account creation
- Returns to My News with personalized experience
- (Future enhancement: migrate temporary saves to permanent account)

## Technical Details

### Data Storage
- **Key**: `bbc_temp_saved_articles`
- **Expiry Key**: `bbc_temp_saved_articles_expiry`
- **Format**: JSON array of article objects
- **Lifecycle**: 2 days from first save

### Article Data Structure
```typescript
{
id: string;
title: string;
link: string;
imageUrl?: string;
imageAlt?: string;
promoImage?: string;
type: string;
description: string;
savedAt: number;
}
```

### Components Reused
- `SaveButton` - Base save button component
- `CurationGrid` - Displays article grid
- `AccountActionButtons` - Sign in/register buttons
- `AccountSignInModal` - Modal for guest save (original flow)
- `Heading`, `Text`, `CallToActionLink` - UI primitives

## Files Created/Modified

### New Files
- `src/app/hooks/useTemporarySavedArticles/index.ts`
- `src/app/components/SaveArticleButton/SaveArticleButtonGuestWithPreview/index.tsx`
- `src/app/components/SaveArticleButton/SaveArticleButtonGuestWithPreview/SaveButtonTooltip.tsx`
- `src/app/components/SaveArticleButton/SaveArticleButtonGuestWithPreview/SaveArticleConfirmation.tsx`
- `ws-nextjs-app/pages/[service]/my-news/MyNewsPage/MyNewsPageTemporary/index.tsx`

### Modified Files
- `src/app/components/SaveArticleButton/index.tsx` - Added `enableGuestPreview` prop
- `ws-nextjs-app/pages/[service]/my-news/MyNewsPage/index.tsx` - Added conditional rendering for temporary view
- `src/app/pages/ArticlePage/ArticlePage.tsx` - Enabled guest preview on article pages

## Future Enhancements

1. **Migration on Sign-in**: - Automatic transfer of temporary saves to permanent UAS storage
2. **Analytics**: Track conversion rates from temporary saves to registrations
3. **Recommendations**: Show personalized recommendations based on temporary saves
4. **Persistence Warning**: Show warning before expiry (e.g., "1 hour left")
5. **Cross-device**: Sync temporary saves using anonymous token
6. **Topic/Place Following**: Extend to topics and places, not just articles

## ✨ Automatic Migration to Permanent Storage

### Overview
When a guest user with temporary saved articles signs in or registers, their articles are **automatically migrated** to permanent UAS storage with zero user action required!

### New Components for Migration

**Migration Hook** (`useTemporarySavesMigration`):
- `src/app/hooks/useTemporarySavesMigration/index.ts`
- Automatically triggers on sign-in
- Migrates all temporary articles to UAS
- Clears localStorage after success

**Success Banner** (`MigrationSuccessBanner`):
- `ws-nextjs-app/pages/[service]/my-news/MyNewsPage/MigrationSuccessBanner/index.tsx`
- Green success banner
- Auto-hides after 10 seconds

### Migration Flow

```
1. Guest saves articles → localStorage
2. Signs in/registers → Account created
3. Returns to My News → Migration triggers automatically
4. Loading state (1-2 seconds) → Migrating articles to UAS
5. Success banner appears → "Your articles have been saved!"
6. Permanent My News → All articles now in UAS
```

### What Gets Migrated

- Article ID, title, and link
- Promo image and alt text
- Service context
- All metadata needed for display

### Error Handling

- Individual failures don't stop migration
- Successful articles still saved
- Errors logged to console
- User sees successfully migrated content

[copilot]

## Testing

This POC does not include test suites . For manual testing:

1. Open any article page as a guest user
2. Hover over the save button to see tooltip
3. Click save and verify confirmation appears
4. Navigate to My News page
5. Verify temporary banner and articles display
6. Wait 2 days or manually clear localStorage to test expiry
7. Sign in to test transition (saves will not migrate in POC)

[copilot]
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { use, useEffect } from 'react';
import { ServiceContext } from '#contexts/ServiceContext';
import Text from '#app/components/Text';
import CallToActionLink from '#app/components/CallToActionLink';
import { Close } from '#app/components/icons';
import { css } from '@emotion/react';

const styles = {
overlay: css({
position: 'absolute',
top: '100%',
left: '0',
marginTop: '0.75rem',
backgroundColor: '#fff',
border: '1px solid #ccc',
borderRadius: '4px',
padding: '1rem',
minWidth: '280px',
maxWidth: '350px',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
zIndex: 1000,
animation: 'fadeIn 0.3s ease-out',
'@keyframes fadeIn': {
from: {
opacity: 0,
transform: 'translateY(-10px)',
},
to: {
opacity: 1,
transform: 'translateY(0)',
},
},
}),
header: css({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'flex-start',
marginBottom: '0.5rem',
}),
closeButton: css({
background: 'none',
border: 'none',
padding: '0.25rem',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
'&:hover': {
opacity: 0.7,
},
}),
content: css({
marginTop: '0.5rem',
}),
link: css({
display: 'inline',
}),
};

interface SaveArticleConfirmationProps {
onClose: () => void;
}

const SaveArticleConfirmation = ({ onClose }: SaveArticleConfirmationProps) => {
const { service } = use(ServiceContext);

const confirmationText = 'Saved to your temporary personal page';
const linkTextBefore = 'Find it in';
const linkTextMyNews = 'My News';
const linkTextAfter =
'during this temporary preview. Sign in to keep it for future visits';

const myNewsPath = `/${service}/my-news`;

useEffect(() => {
const timer = setTimeout(() => {
onClose();
}, 10000);

return () => clearTimeout(timer);
}, [onClose]);

return (
<div css={styles.overlay} role="alert">
<div css={styles.header}>
<Text size="pica" fontVariant="sansBold">
{confirmationText}
</Text>
<button
type="button"
onClick={onClose}
css={styles.closeButton}
aria-label="Close"
>
<Close width="16" height="16" />
</button>
</div>
<div css={styles.content}>
<Text size="longPrimer">
{linkTextBefore}{' '}
<CallToActionLink
url={myNewsPath}
css={styles.link}
eventTrackingData={{
componentName: 'save-article-confirmation-link',
}}
>
<CallToActionLink.Text shouldUnderlineOnHoverFocus>
{linkTextMyNews}
</CallToActionLink.Text>
</CallToActionLink>{' '}
{linkTextAfter}
</Text>
</div>
</div>
);
};

export default SaveArticleConfirmation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { css } from '@emotion/react';
import { use } from 'react';
import Text from '#app/components/Text';
import CallToActionLink from '#app/components/CallToActionLink';
import { ServiceContext } from '#app/contexts/ServiceContext';

interface SaveButtonTooltipProps {
isSaved?: boolean;
}

const styles = {
tooltip: css({
position: 'absolute',
bottom: '100%',
left: '50%',
transform: 'translateX(-50%)',
marginBottom: '0.5rem',
padding: '0.75rem 1rem',
backgroundColor: '#fff',
color: '#222',
border: '1px solid #ccc',
borderRadius: '4px',
whiteSpace: 'normal',
maxWidth: '280px',
width: 'max-content',
minWidth: '200px',
zIndex: 1000,
boxShadow: '0 2px 12px rgba(0,0,0,0.15)',
textAlign: 'center',
'&::after': {
content: '""',
position: 'absolute',
top: '100%',
left: '50%',
marginLeft: '-8px',
borderWidth: '8px',
borderStyle: 'solid',
borderColor: '#fff transparent transparent transparent',
},
'&::before': {
content: '""',
position: 'absolute',
top: '100%',
left: '50%',
marginLeft: '-9px',
borderWidth: '9px',
borderStyle: 'solid',
borderColor: '#ccc transparent transparent transparent',
zIndex: -1,
},
}),
link: css({
display: 'inline',
}),
};

const SaveButtonTooltip = ({ isSaved = false }: SaveButtonTooltipProps) => {
const { service } = use(ServiceContext);
const myNewsPath = `/${service}/my-news`;

const unsavedText =
'Want to read this later? Save this article and find it in your temporary My News page.';

Check failure on line 63 in src/app/components/SaveArticleButton/SaveArticleButtonGuestWithPreview/SaveButtonTooltip.tsx

View workflow job for this annotation

GitHub Actions / build (22.x)

Delete `··`
const savedTextBefore = 'This article is saved temporarily in your';
const savedTextLink = 'My News page';
const savedTextAfter = '.';

return (
<div css={styles.tooltip} role="tooltip">
<Text size="brevier" fontVariant="sansRegular" css={{ color: '#222' }}>
{isSaved ? (
<>
{savedTextBefore}{' '}
<CallToActionLink url={myNewsPath} css={styles.link}>
<CallToActionLink.Text shouldUnderlineOnHoverFocus>
{savedTextLink}
</CallToActionLink.Text>
</CallToActionLink>
{savedTextAfter}
</>
) : (
unsavedText
)}
</Text>
</div>
);
};

export default SaveButtonTooltip;
Loading
Loading