diff --git a/apps/portal/src/actions.js b/apps/portal/src/actions.js index 6d35cd2074..df96cd95e8 100644 --- a/apps/portal/src/actions.js +++ b/apps/portal/src/actions.js @@ -501,6 +501,22 @@ async function oneClickSubscribe({data: {siteUrl}, state}) { return {}; } +function trackRecommendationClicked({data: {recommendationId}, api}) { + api.recommendations.trackClicked({ + recommendationId + }); + + return {}; +} + +async function trackRecommendationSubscribed({data: {recommendationId}, api}) { + api.recommendations.trackSubscribed({ + recommendationId + }); + + return {}; +} + const Actions = { togglePopup, openPopup, @@ -524,7 +540,9 @@ const Actions = { updateNewsletterPreference, showPopupNotification, removeEmailFromSuppressionList, - oneClickSubscribe + oneClickSubscribe, + trackRecommendationClicked, + trackRecommendationSubscribed }; /** Handle actions in the App, returns updated state */ diff --git a/apps/portal/src/components/pages/RecommendationsPage.js b/apps/portal/src/components/pages/RecommendationsPage.js index 18093ffc50..5b58c3f691 100644 --- a/apps/portal/src/components/pages/RecommendationsPage.js +++ b/apps/portal/src/components/pages/RecommendationsPage.js @@ -74,7 +74,7 @@ export const RecommendationsPageStyles = ` .gh-portal-recommendation-item .gh-portal-list-detail:hover .gh-portal-recommendation-arrow-icon { opacity: 0.8; } - + .gh-portal-recommendation-subscribed { display: flex; align-items: center; @@ -155,6 +155,7 @@ const RecommendationItem = (recommendation) => { const {title, url, reason, favicon, one_click_subscribe: oneClickSubscribe, featured_image: featuredImage} = recommendation; const allowOneClickSubscribe = member && oneClickSubscribe; const [subscribed, setSubscribed] = useState(false); + const [clicked, setClicked] = useState(false); const [loading, setLoading] = useState(false); const outboundLinkTagging = site.outbound_link_tagging ?? false; @@ -179,7 +180,12 @@ const RecommendationItem = (recommendation) => { const visitHandler = useCallback(() => { // Open url in a new tab openTab(refUrl); - }, [refUrl]); + + if (!clicked) { + onAction('trackRecommendationClicked', {recommendationId: recommendation.id}); + setClicked(true); + } + }, [refUrl, recommendation.id, clicked]); const oneClickSubscribeHandler = useCallback(async () => { try { @@ -188,6 +194,10 @@ const RecommendationItem = (recommendation) => { siteUrl: url, throwErrors: true }); + if (!clicked) { + onAction('trackRecommendationSubscribed', {recommendationId: recommendation.id}); + setClicked(true); + } setSubscribed(true); } catch (_) { // Open portal signup page @@ -195,9 +205,14 @@ const RecommendationItem = (recommendation) => { // Trigger a visit openTab(signupUrl); + + if (!clicked) { + onAction('trackRecommendationClicked', {recommendationId: recommendation.id}); + setClicked(true); + } } setLoading(false); - }, [setSubscribed, url, refUrl]); + }, [setSubscribed, url, refUrl, recommendation.id, clicked]); const clickHandler = useCallback((e) => { if (loading) { diff --git a/apps/portal/src/utils/api.js b/apps/portal/src/utils/api.js index 346d3657f8..50c9b5d49b 100644 --- a/apps/portal/src/utils/api.js +++ b/apps/portal/src/utils/api.js @@ -163,6 +163,18 @@ function setupGhostApi({siteUrl = window.location.origin, apiUrl, apiKey}) { } }; + api.recommendations = { + trackClicked({recommendationId}) { + let url = endpointFor({type: 'members', resource: 'recommendations/' + recommendationId + '/clicked'}); + navigator.sendBeacon(url); + }, + + trackSubscribed({recommendationId}) { + let url = endpointFor({type: 'members', resource: 'recommendations/' + recommendationId + '/subscribed'}); + navigator.sendBeacon(url); + } + }; + api.member = { identity() { const url = endpointFor({type: 'members', resource: 'session'});