-
Notifications
You must be signed in to change notification settings - Fork 29
jingram/subscription win back banner: Adds Subscription Win-Back Banner #2014
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jsoningram
wants to merge
15
commits into
main
Choose a base branch
from
jingram/subscription-win-back-banner
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
52a83f7
Move utility functions to shared file and update unit tests
jsoningram c72ff36
Messaging support for the subscription win-back banner
jsoningram adb03e7
Create win-back banner component and supporting files
jsoningram 4c17529
Wire up mock transport
jsoningram aa0d03a
Generate types
jsoningram 6a5ddf0
Wire up integration test for win-back banner on NTP
jsoningram b3f48cf
Update button styles
jsoningram 4088c1e
Update new-tab readme
jsoningram deba1e0
Add entry point for Subscription Win-back banner
jsoningram bfe6623
Resolve Unknown property `composes`
jsoningram b474e6b
Resolve Unexpected unknown property "composes" property-no-unknown
jsoningram 8841f60
Fix unit test issue
jsoningram 44cdc7f
Patch: Update test-int-snapshots-update in injected/package.json
jsoningram 2016065
Use accentBrand colors for MacOS button
jsoningram ef8193c
Update test to pass the correct query param value
jsoningram File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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
14 changes: 14 additions & 0 deletions
14
special-pages/pages/new-tab/app/entry-points/subscriptionWinBackBanner.js
This file contains hidden or 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,14 @@ | ||
import { h } from 'preact'; | ||
import { Centered } from '../components/Layout.js'; | ||
import { SubscriptionWinBackBannerConsumer } from '../subscription-winback-banner/components/SubscriptionWinBackBanner.js'; | ||
import { SubscriptionWinBackBannerProvider } from '../subscription-winback-banner/SubscriptionWinBackBannerProvider.js'; | ||
|
||
export function factory() { | ||
return ( | ||
<Centered data-entry-point="subscriptionWinBackBanner"> | ||
<SubscriptionWinBackBannerProvider> | ||
<SubscriptionWinBackBannerConsumer /> | ||
</SubscriptionWinBackBannerProvider> | ||
</Centered> | ||
); | ||
} |
This file contains hidden or 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
30 changes: 0 additions & 30 deletions
30
special-pages/pages/new-tab/app/freemium-pir-banner/freemiumPIRBanner.utils.js
This file was deleted.
Oops, something went wrong.
This file contains hidden or 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
96 changes: 96 additions & 0 deletions
96
...-pages/pages/new-tab/app/subscription-winback-banner/SubscriptionWinBackBannerProvider.js
This file contains hidden or 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,96 @@ | ||
import { createContext, h } from 'preact'; | ||
import { useCallback, useEffect, useReducer, useRef } from 'preact/hooks'; | ||
import { useMessaging } from '../types.js'; | ||
import { SubscriptionWinBackBannerService } from './subscriptionWinBackBanner.service.js'; | ||
import { reducer, useDataSubscription, useInitialData } from '../service.hooks.js'; | ||
|
||
/** | ||
* @typedef {import('../../types/new-tab.js').SubscriptionWinBackBannerData} SubscriptionWinBackBannerData | ||
* @typedef {import('../service.hooks.js').State<SubscriptionWinBackBannerData, undefined>} State | ||
* @typedef {import('../service.hooks.js').Events<SubscriptionWinBackBannerData, undefined>} Events | ||
*/ | ||
|
||
/** | ||
* These are the values exposed to consumers. | ||
*/ | ||
export const SubscriptionWinBackBannerContext = createContext({ | ||
/** @type {State} */ | ||
state: { status: 'idle', data: null, config: null }, | ||
/** @type {(id: string) => void} */ | ||
dismiss: (id) => { | ||
throw new Error('must implement dismiss' + id); | ||
}, | ||
/** @type {(id: string) => void} */ | ||
action: (id) => { | ||
throw new Error('must implement action' + id); | ||
}, | ||
}); | ||
|
||
export const SubscriptionWinBackBannerDispatchContext = createContext(/** @type {import("preact/hooks").Dispatch<Events>} */ ({})); | ||
|
||
/** | ||
* A data provider that will use `SubscriptionWinBackBannerService` to fetch data, subscribe | ||
* to updates and modify state. | ||
* | ||
* @param {Object} props | ||
* @param {import("preact").ComponentChild} props.children | ||
*/ | ||
export function SubscriptionWinBackBannerProvider(props) { | ||
const initial = /** @type {State} */ ({ | ||
status: 'idle', | ||
data: null, | ||
config: null, | ||
}); | ||
|
||
// const [state, dispatch] = useReducer(withLog('SubscriptionWinBackBannerProvider', reducer), initial) | ||
const [state, dispatch] = useReducer(reducer, initial); | ||
|
||
// create an instance of `SubscriptionWinBackBannerService` for the lifespan of this component. | ||
const service = useService(); | ||
|
||
// get initial data | ||
useInitialData({ dispatch, service }); | ||
|
||
// subscribe to data updates | ||
useDataSubscription({ dispatch, service }); | ||
|
||
// todo(valerie): implement onDismiss in the service | ||
const dismiss = useCallback( | ||
(id) => { | ||
console.log('onDismiss'); | ||
service.current?.dismiss(id); | ||
}, | ||
[service], | ||
); | ||
|
||
const action = useCallback( | ||
(id) => { | ||
service.current?.action(id); | ||
}, | ||
[service], | ||
); | ||
|
||
return ( | ||
<SubscriptionWinBackBannerContext.Provider value={{ state, dismiss, action }}> | ||
<SubscriptionWinBackBannerDispatchContext.Provider value={dispatch}> | ||
{props.children} | ||
</SubscriptionWinBackBannerDispatchContext.Provider> | ||
</SubscriptionWinBackBannerContext.Provider> | ||
); | ||
} | ||
|
||
/** | ||
* @return {import("preact").RefObject<SubscriptionWinBackBannerService>} | ||
*/ | ||
export function useService() { | ||
const service = useRef(/** @type {SubscriptionWinBackBannerService|null} */ (null)); | ||
const ntp = useMessaging(); | ||
useEffect(() => { | ||
const stats = new SubscriptionWinBackBannerService(ntp); | ||
service.current = stats; | ||
return () => { | ||
stats.destroy(); | ||
}; | ||
}, [ntp]); | ||
return service; | ||
} |
18 changes: 18 additions & 0 deletions
18
.../new-tab/app/subscription-winback-banner/components/SubscriptionWinBackBanner.examples.js
This file contains hidden or 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,18 @@ | ||
import { h } from 'preact'; | ||
import { noop } from '../../utils.js'; | ||
import { SubscriptionWinBackBanner } from './SubscriptionWinBackBanner.js'; | ||
import { subscriptionWinBackBannerDataExamples } from '../mocks/subscriptionWinBackBanner.data.js'; | ||
|
||
/** @type {Record<string, {factory: () => import("preact").ComponentChild}>} */ | ||
|
||
export const subscriptionWinBackBannerExamples = { | ||
'subscriptionWinBackBanner.winback_last_day': { | ||
factory: () => ( | ||
<SubscriptionWinBackBanner | ||
message={subscriptionWinBackBannerDataExamples.winback_last_day.content} | ||
dismiss={noop('winBackOffer_dismiss')} | ||
action={noop('winBackOffer_action')} | ||
/> | ||
), | ||
}, | ||
}; |
47 changes: 47 additions & 0 deletions
47
...ges/pages/new-tab/app/subscription-winback-banner/components/SubscriptionWinBackBanner.js
This file contains hidden or 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,47 @@ | ||
import cn from 'classnames'; | ||
import { h } from 'preact'; | ||
import { Button } from '../../../../../shared/components/Button/Button'; | ||
import { DismissButton } from '../../components/DismissButton'; | ||
import styles from './SubscriptionWinBackBanner.module.css'; | ||
import { SubscriptionWinBackBannerContext } from '../SubscriptionWinBackBannerProvider'; | ||
import { useContext } from 'preact/hooks'; | ||
import { convertMarkdownToHTMLForStrongTags } from '../../../../../shared/utils'; | ||
|
||
/** | ||
* @typedef { import("../../../types/new-tab").SubscriptionWinBackBannerMessage} SubscriptionWinBackBannerMessage | ||
* @param {object} props | ||
* @param {SubscriptionWinBackBannerMessage} props.message | ||
* @param {(id: string) => void} props.dismiss | ||
* @param {(id: string) => void} props.action | ||
*/ | ||
export function SubscriptionWinBackBanner({ message, action, dismiss }) { | ||
const processedMessageDescription = convertMarkdownToHTMLForStrongTags(message.descriptionText); | ||
return ( | ||
<div id={message.id} class={cn(styles.root, styles.icon)}> | ||
<span class={styles.iconBlock}> | ||
<img aria-hidden="true" src={`./icons/Subscription-Clock-96.svg`} alt="" /> | ||
</span> | ||
<div class={styles.content}> | ||
{message.titleText && <h2 class={styles.title}>{message.titleText}</h2>} | ||
<p class={styles.description} dangerouslySetInnerHTML={{ __html: processedMessageDescription }} /> | ||
</div> | ||
{message.messageType === 'big_single_action' && message?.actionText && action && ( | ||
<div class={styles.btnBlock}> | ||
<Button size="md" variant="accent" onClick={() => action(message.id)}> | ||
{message.actionText} | ||
</Button> | ||
</div> | ||
)} | ||
{message.id && dismiss && <DismissButton className={styles.dismissBtn} onClick={() => dismiss(message.id)} />} | ||
</div> | ||
); | ||
} | ||
|
||
export function SubscriptionWinBackBannerConsumer() { | ||
const { state, action, dismiss } = useContext(SubscriptionWinBackBannerContext); | ||
|
||
if (state.status === 'ready' && state.data.content) { | ||
return <SubscriptionWinBackBanner message={state.data.content} action={action} dismiss={dismiss} />; | ||
} | ||
return null; | ||
} |
41 changes: 41 additions & 0 deletions
41
...s/new-tab/app/subscription-winback-banner/components/SubscriptionWinBackBanner.module.css
This file contains hidden or 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,41 @@ | ||
/** | ||
* Using CSS Modules 'composes' to import styles from FreemiumPIRBanner. | ||
* This avoids code duplication and keeps both components in sync. | ||
* See: https://github.com/css-modules/css-modules/blob/master/docs/composition.md | ||
*/ | ||
|
||
.root { | ||
composes: root from '../../freemium-pir-banner/components/FreemiumPIRBanner.module.css'; | ||
} | ||
|
||
.iconBlock { | ||
composes: iconBlock from '../../freemium-pir-banner/components/FreemiumPIRBanner.module.css'; | ||
} | ||
|
||
.content { | ||
composes: content from '../../freemium-pir-banner/components/FreemiumPIRBanner.module.css'; | ||
} | ||
|
||
.title { | ||
composes: title from '../../freemium-pir-banner/components/FreemiumPIRBanner.module.css'; | ||
} | ||
|
||
.description { | ||
composes: description from '../../freemium-pir-banner/components/FreemiumPIRBanner.module.css'; | ||
} | ||
|
||
.btnBlock { | ||
composes: btnBlock from '../../freemium-pir-banner/components/FreemiumPIRBanner.module.css'; | ||
} | ||
|
||
.btnRow { | ||
composes: btnRow from '../../freemium-pir-banner/components/FreemiumPIRBanner.module.css'; | ||
} | ||
|
||
.dismissBtn { | ||
composes: dismissBtn from '../../freemium-pir-banner/components/FreemiumPIRBanner.module.css'; | ||
} | ||
|
||
.icon { | ||
composes: icon from '../../freemium-pir-banner/components/FreemiumPIRBanner.module.css'; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very cool! today i learned. 🙌