diff --git a/.env.example b/.env.example index 2a2a258..9eff04f 100644 --- a/.env.example +++ b/.env.example @@ -7,3 +7,4 @@ domain-c=localhost # domain-ccc.com domain-c-background=bg-red-100 port=8080 # port to listen on google-client-id= # google client id +facebook-app-id= # facebook app id diff --git a/app.js b/app.js index e1e13ba..47f3893 100644 --- a/app.js +++ b/app.js @@ -34,6 +34,7 @@ app.use((req, res, next) => { res.locals.domainB = process.env['domain-b']; res.locals.domainC = process.env['domain-c']; res.locals.googleClientId = process.env['google-client-id']; + res.locals.facebookAppId = process.env['facebook-app-id']; res.locals.port = process.env.port; res.locals.isPortPresent = req.get('host').includes(':'); res.locals.currentDomain = req.get( 'host' ); @@ -54,7 +55,7 @@ app.use((req, res, next) => { }); // Mount routes for different demo types -const demoTypes = ['chips', 'related-websites-sets', 'private-state-tokens', 'fedcm']; +const demoTypes = ['chips', 'related-websites-sets', 'private-state-tokens', 'fedcm', 'storage-access-api']; demoTypes.forEach(demoType => { const demoRoutes = require(`./src/demos/${demoType}/routes`); app.use(`/${demoType}`, demoRoutes); // Mount the routes on a path specific to the demo type @@ -70,7 +71,8 @@ const scenarios = [ 'personalization', 'personalization-localstorage', 'gsi', - 'social-media' + 'social-media', + 'social-media-comments' ]; scenarios.forEach(scenario => { const scenarioRoutes = require(`./src/scenarios/${scenario}/routes`); diff --git a/public/assets/js/main.js b/public/assets/js/main.js index 8bf4245..9683f0c 100644 --- a/public/assets/js/main.js +++ b/public/assets/js/main.js @@ -4,8 +4,12 @@ document.addEventListener('DOMContentLoaded', function() { if (isIframe) { const mainHeader = document.querySelector('.main-header'); const mainFooter = document.querySelector('.main-footer'); + const internalPageHeader = document.querySelector('.internal-page-header'); + const themeContainer = document.getElementById('theme-container'); mainHeader?.remove(); - mainFooter?.remove(); + mainFooter?.remove(); + internalPageHeader?.remove('hidden'); + themeContainer?.classList.add('h-screen'); } }); \ No newline at end of file diff --git a/public/assets/styles/style.css b/public/assets/styles/style.css index f9da133..e6a0ebc 100644 --- a/public/assets/styles/style.css +++ b/public/assets/styles/style.css @@ -667,14 +667,6 @@ video { position: relative; } -.right-8 { - right: 2rem; -} - -.top-8 { - top: 2rem; -} - .z-50 { z-index: 50; } @@ -833,8 +825,8 @@ video { appearance: none; } -.grid-cols-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); } .flex-col { @@ -861,24 +853,12 @@ video { gap: 1.5rem; } -.space-x-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(0.5rem * var(--tw-space-x-reverse)); - margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); -} - .space-x-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(1rem * var(--tw-space-x-reverse)); margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); } -.space-y-2 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); -} - .space-y-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); @@ -1005,11 +985,6 @@ video { text-align: center; } -.text-2xl { - font-size: 1.5rem; - line-height: 2rem; -} - .text-3xl { font-size: 1.875rem; line-height: 2.25rem; @@ -1158,6 +1133,14 @@ video { } @media (min-width: 768px) { + .md\:w-\[60rem\] { + width: 60rem; + } + + .md\:max-w-full { + max-width: 100%; + } + .md\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } diff --git a/src/common/common-scenarios.ejs b/src/common/common-scenarios.ejs deleted file mode 100644 index 7220aa9..0000000 --- a/src/common/common-scenarios.ejs +++ /dev/null @@ -1,3 +0,0 @@ -
-

To learn more about the CUJs, accessAnalysis Scenarios page

-
\ No newline at end of file diff --git a/src/common/index.ejs b/src/common/index.ejs index d7b5399..c12e5d1 100644 --- a/src/common/index.ejs +++ b/src/common/index.ejs @@ -9,17 +9,19 @@ <% } %>
-
+
<%= renderCard('Analytics Tracking', '🔎', '/analytics') %> <%= renderCard('Embedded Content', '📽️', '/embedded-video') %> - <%= renderCard('E-Commerce', '🛒', '/ecommerce') %> + <%= renderCard('Shopping Cart', '🛒', '/ecommerce') %> <%= renderCard('Personalization', '🎨', '/personalization') %> <%= renderCard('Personalization with localStorage', '🖼️', '/personalization-localstorage') %> <%= renderCard('Single Sign-On', '🔐', '/single-sign-on') %> <%= renderCard('Payment Gateway', '💳', '/payment-gateway') %> - <%= renderCard('CHIPS', '🍪', '/chips') %> <%= renderCard('Legacy GSI', '🔐', '/gsi') %> - <%= renderCard('Facebook', '👍', '/social-media') %> + <%= renderCard('Facebook Like', '👍', '/social-media') %> + <%= renderCard('Facebook Comments', '💬', '/social-media-comments') %> + <%= renderCard('CHIPS', '🍪', '/chips') %> + <%= renderCard('Storage Access API', '🗃️', '/storage-access-api') %>
diff --git a/src/common/internal-page/header.ejs b/src/common/internal-page/header.ejs index 1d2b964..f814f82 100644 --- a/src/common/internal-page/header.ejs +++ b/src/common/internal-page/header.ejs @@ -7,7 +7,7 @@ %>
<% if (title && displayHeader) { %> -
+

<%= title %>

<% } %> diff --git a/src/demos/storage-access-api/index.ejs b/src/demos/storage-access-api/index.ejs new file mode 100644 index 0000000..5604b62 --- /dev/null +++ b/src/demos/storage-access-api/index.ejs @@ -0,0 +1,5 @@ +<%- include(commonPath + '/header.ejs') %> + <%- include(commonPath + '/internal-page/header.ejs') %> + + <%- include(commonPath + '/internal-page/footer.ejs') %> +<%- include(commonPath + '/footer.ejs') %> diff --git a/src/demos/storage-access-api/personalization.ejs b/src/demos/storage-access-api/personalization.ejs new file mode 100644 index 0000000..91f9c0a --- /dev/null +++ b/src/demos/storage-access-api/personalization.ejs @@ -0,0 +1,103 @@ +document.addEventListener('DOMContentLoaded', () => { + const baseURL = '<%= protocol %>://<%= domainC %><% if (isPortPresent) { %>:<%= port %><% } %>/personalization'; + const pageContainer = document.getElementById('theme-container'); + const themeSwitcher = document.getElementById('dark-mode-switch'); + const errorMessages = document.getElementById('status-message'); + const loadButton = document.getElementById('load-button'); + const toggleContainer = document.querySelector('.dark-mode-toggle'); + const isIframe = window.self !== window.top; + const containerClass = isIframe ? 'h-screen flex items-center justify-center' : 'flex items-center justify-center'; + let hasStorageAccess = false; + + document.hasStorageAccess().then(result => { + hasStorageAccess = result; + if ( hasStorageAccess ) { + updateUserPreference(); + } + }) + + async function updateUserPreference() { + console.log('hasStorageAccess', hasStorageAccess); + if ( hasStorageAccess ) { + fetchAndApplyTheme(); + } else { + try { + await document.requestStorageAccess(); + toggleContainer.classList.remove('hidden'); + loadButton.classList.add('hidden'); + fetchAndApplyTheme(); + } catch (error) { + console.error('There has been a problem with your fetch operation:', error); + errorMessages.textContent = `Error: ${error}`; + } + } + } + + function fetchAndApplyTheme() { + fetch(`${baseURL}/get-personalization`, { + method: 'GET', + credentials: 'include' + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + errorMessages.textContent = `Network response was not ok ${response.status} - ${response.statusText}`; + } + return response.json(); + }) + .then(data => { + const theme = data.theme; + pageContainer.className = `${containerClass} ${data.theme}` + console.log('theme', theme); + if (theme === 'dark') { + themeSwitcher.checked = true; + } + }) + .catch(error => { + console.error('There has been a problem with your fetch operation:', error); + errorMessages.textContent = `Error: ${error.message}`; + }); + } + + function fetchSetPersonalization() { + fetch( `${baseURL}/set-personalization`, { + method: 'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ theme: themeSwitcher?.checked ? 'dark' : 'light' }) + }) + .then(response => response.json()) + .then(data => { + pageContainer.className = `${containerClass} ${data.theme}`; + }); + } + + async function toggleTheme() { + hasStorageAccess = await document.hasStorageAccess(); + + if( hasStorageAccess ) { + fetchSetPersonalization(); + } else { + try { + await document.requestStorageAccess(); + if ( await document.hasStorageAccess() ) { + fetchSetPersonalization(); + } else { + console.error('User denied storage access'); + errorMessages.textContent = 'User denied storage access'; + } + } catch (error) { + console.error('Error:', error); + errorMessages.textContent = `The request to storage access API was denied because the user never interacted with the top-level site context or the permission wasn't grant by the user`; + } + } + } + + window.toggleTheme = toggleTheme; + if (isIframe && !hasStorageAccess) { + console.log('In iframe'); + toggleContainer.classList.add('hidden'); + loadButton.classList.remove('hidden'); + loadButton.addEventListener('click', updateUserPreference); + } +}); diff --git a/src/demos/storage-access-api/routes.js b/src/demos/storage-access-api/routes.js new file mode 100644 index 0000000..f0dad07 --- /dev/null +++ b/src/demos/storage-access-api/routes.js @@ -0,0 +1,49 @@ +const express = require('express'); +const path = require('path'); +const router = express.Router(); + +router.get('/', (req, res) => { + const currentDomain = req.get('host'); + const template = currentDomain === res.locals.domainC ? 'theme-selection' : 'index'; + res.render(path.join(__dirname,template), { + title: 'Storage Access API' + }); +}); + +router.get('/theme-selection', (req, res) => { + res.render(path.join(__dirname,'theme-selection'), { + title: 'Storage Access API' + }); +}); + + +router.get( '/get-personalization', ( req, res ) => { + const currentTheme = req.cookies.theme || 'light'; + res.json( { theme: currentTheme }); +}); + +router.post( '/set-personalization', ( req, res ) => { + const { theme } = req.body; + + if (!theme) { + res.status(400).send({ message: 'Invalid request' }); + + } + + res.cookie('theme', theme, { + domain: res.locals.domainC, + maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days + httpOnly: true, + sameSite: "none", + secure: true + }); + res.status(200).send({ message: 'Success', theme : theme}); +}); + +// Serve the personalization.js file to the site +router.get('/personalization.js', (req, res) => { + res.set('Content-Type', 'application/javascript'); + res.render(path.join(__dirname,'personalization')); +}); + +module.exports = router; diff --git a/src/demos/storage-access-api/theme-selection.ejs b/src/demos/storage-access-api/theme-selection.ejs new file mode 100644 index 0000000..e4f024f --- /dev/null +++ b/src/demos/storage-access-api/theme-selection.ejs @@ -0,0 +1,22 @@ +<%- include(commonPath + '/header.ejs') %> +
+ <%- include(commonPath + '/internal-page/header.ejs', {containerType: 'sm'}) %> +
+ +
+ + +
+ <%- include(commonPath + '/internal-page/footer.ejs') %> +
+ + + +<%- include(commonPath + '/footer.ejs') %> diff --git a/src/scenarios/ecommerce/routes.js b/src/scenarios/ecommerce/routes.js index 4eb9524..d725f52 100644 --- a/src/scenarios/ecommerce/routes.js +++ b/src/scenarios/ecommerce/routes.js @@ -14,7 +14,7 @@ router.use((req, res, next) => { router.get('/', (req, res) => { // Render the index view (homepage) res.render(path.join(__dirname,'index'), { - title: 'E-commerce' + title: 'Shopping Cart' }); }); diff --git a/src/scenarios/personalization-localstorage/routes.js b/src/scenarios/personalization-localstorage/routes.js index 983db1f..551c863 100644 --- a/src/scenarios/personalization-localstorage/routes.js +++ b/src/scenarios/personalization-localstorage/routes.js @@ -3,7 +3,9 @@ const path = require('path'); const router = express.Router(); router.get('/', (req, res) => { - res.render(path.join(__dirname,'index'), { + const currentDomain = req.get('host'); + const pageTemplate = currentDomain === res.locals.domainC ? 'theme-selection' : 'index'; + res.render(path.join(__dirname, pageTemplate), { title: 'Personalization with localStorage' }); }); diff --git a/src/scenarios/personalization-localstorage/theme-selection.ejs b/src/scenarios/personalization-localstorage/theme-selection.ejs index 87a0e37..9fea8c0 100644 --- a/src/scenarios/personalization-localstorage/theme-selection.ejs +++ b/src/scenarios/personalization-localstorage/theme-selection.ejs @@ -1,6 +1,6 @@ <%- include(commonPath + '/header.ejs') %> -
- <%- include(commonPath + '/internal-page/header.ejs', {showHeader: false, containerType: 'sm'}) %> +
+ <%- include(commonPath + '/internal-page/header.ejs', { containerType: 'sm'}) %>