Skip to content
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

✨ [Bento] Implement bento-app-banner #37127

Merged
merged 76 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
40967a8
feature(bento-add-banner): initial template generation
Nov 26, 2021
11546ec
feature(bento-add-banner): created simple storybook
Nov 26, 2021
8b277b4
feature(bento-add-banner): copied CSS from classic
Nov 29, 2021
35cd935
feature(bento-add-banner): added many "stubs" for standalone services
Nov 30, 2021
ec80aec
feature(bento-add-banner): ported IOS and Android app logic
Nov 30, 2021
c39b1aa
feature(bento-add-banner): added platform-aware logic, and dismiss bu…
Dec 1, 2021
27b0109
feature(bento-add-banner): added UI unit tests
Dec 3, 2021
03a1283
feature(bento-add-banner): added IOS tests
Dec 3, 2021
cb7164e
feature(bento-add-banner): moved sources into `component` folder
Dec 3, 2021
32a16f8
feature(bento-add-banner): added unit tests for Android
Dec 3, 2021
65d672b
feature(bento-add-banner): mapped the `dismiss-button-aria-label` att…
Dec 3, 2021
98b4636
feature(bento-add-banner): added minor notes to the stories
Dec 6, 2021
2ff6959
feature(bento-add-banner): added unit tests for amp-app-banner
Dec 6, 2021
5ca3b39
feature(bento-add-banner): updated validator
Dec 6, 2021
265ee5d
feature(bento-add-banner): extracted services to common location
Dec 6, 2021
15df7b6
feature(bento-add-banner): added README for services
Dec 6, 2021
3811141
Merge branch 'ampproject:main' into bento-app-banner
scottrippey Dec 6, 2021
b9252bb
feature(bento-add-banner): updated type defs
Dec 6, 2021
d320cd4
feature(bento-add-banner): updated formatting
Dec 6, 2021
8155335
feature(bento-add-banner): lint fix
Dec 6, 2021
db040fd
feature(bento-add-banner): lint ignores
Dec 6, 2021
a6aacb4
feature(bento-add-banner): lint fixes
Dec 7, 2021
98c445b
feature(bento-add-banner): type fixes
Dec 7, 2021
e3aeb30
feature(bento-add-banner): updated z-index file
Dec 7, 2021
ccaf030
feature(bento-add-banner): updated validator template
Dec 8, 2021
0e44f88
feature(bento-add-banner): simplified code with `Array.find`
Dec 8, 2021
ddd479e
feature(bento-add-banner): parse meta content using `Array.reduce`
Dec 8, 2021
c01a07c
feature(bento-add-banner): added more tests for android
Dec 8, 2021
859b083
feature(bento-add-banner): added more tests for ios
Dec 8, 2021
bf93576
feature(bento-add-banner): improved tests for BentoAppBanner
Dec 8, 2021
b0356ad
feature(bento-add-banner): added tests for Dismiss logic
Dec 8, 2021
07f7934
feature(bento-add-banner): added more iOS tests
Dec 8, 2021
d09f081
feature(bento-add-banner): tiny lint fix
Dec 8, 2021
9fc8f22
feature(bento-add-banner): improved tests for web-component mode, and…
Dec 8, 2021
4af33ef
feature(bento-add-banner): removed unnecessary `waitFor`
Dec 8, 2021
caad34c
feature(bento-add-banner): lint fix
Dec 9, 2021
686dee6
Merge remote-tracking branch 'ampproject/main' into bento-app-banner
Dec 9, 2021
576f2df
Merge branch 'bento-app-banner' into bento-app-banner-updated
Dec 9, 2021
4c527b3
feature(bento-add-banner): added whitespace in tests
Dec 9, 2021
c781da4
feature(bento-add-banner): ensure tests are not dependent on timers
Dec 9, 2021
52ba3a1
feature(bento-add-banner): removed obsolete `latestVersion`
Dec 13, 2021
bdea4a1
feature(bento-add-banner): use `Object.fromEntries` as suggested
Dec 13, 2021
de6fe75
feature(bento-add-banner): removed unused CSS
Dec 13, 2021
35b8044
feature(bento-add-banner): use `WindowInterface.getTop` to improve un…
Dec 14, 2021
64bd507
Merge remote-tracking branch 'ampproject/main' into bento-app-banner
Dec 14, 2021
1b4dd90
feature(bento-add-banner): extracted querySelectorInSlot to core
Dec 14, 2021
6a71948
feature(bento-add-banner): removed useless services, use object-synta…
Dec 14, 2021
da14d12
feature(bento-add-banner): renamed services to utils
Dec 14, 2021
2561781
feature(bento-add-banner): removed redundant win variable
Dec 14, 2021
0db16e1
feature(bento-add-banner): extracted parsing logic into separate func…
Dec 14, 2021
91ef49c
feature(bento-add-banner): renamed file to `docInfo`
Dec 14, 2021
790528e
feature(bento-add-banner): improved safety of accessing localStorage
Dec 14, 2021
c91fde0
feature(bento-add-banner): ensure proper default value is returned
Dec 14, 2021
209a271
feature(bento-add-banner): removed unnecessary async
Dec 15, 2021
d5bcaa6
feature(bento-add-banner): ensure localStorage still works if `key` c…
Dec 16, 2021
f7da558
feature(bento-add-banner): flip ternary for readability
Dec 16, 2021
3c5e62d
feature(bento-add-banner): added `self` for fetch
Dec 16, 2021
5351eec
feature(bento-add-banner): updated generated z-index file
Dec 16, 2021
3fb943d
Merge remote-tracking branch 'ampproject/main' into bento-app-banner
Jan 24, 2022
a1bf63a
feature(bento-app-banner): ensure XHR rejects invalid status codes
Jan 24, 2022
530864b
feature(bento-app-banner): removed dependencies on `/src/url`
Jan 25, 2022
cdf32f0
feature(bento-app-banner): lint sample code
Jan 25, 2022
b48b4c0
feature(bento-app-banner): minor code improvements
Jan 25, 2022
2cf9006
feature(bento-app-banner): minor code improvements
Jan 25, 2022
f5fc300
feature(bento-app-banner): minor code improvements
Jan 25, 2022
360336c
feature(bento-app-banner): allow localStorage from custom hook
Jan 25, 2022
b147ae5
feature(bento-app-banner): include optional `init` param for requests
Jan 26, 2022
5ee4776
Merge remote-tracking branch 'ampproject/main' into bento-app-banner
Jan 26, 2022
e389a67
feature(bento-app-banner): improved logging
Jan 26, 2022
b023e6d
feature(bento-app-banner): only expose `logger[info|warn|error]`
Jan 26, 2022
290a187
feature(bento-app-banner): warn when localStorage.setItem fails
Jan 26, 2022
bf54b4b
feature(bento-app-banner): ensure logger can be stubbed and tested
Jan 27, 2022
d1473ad
feature(bento-app-banner): moved `INVALID_PROTOCOLS` to proper location
Jan 27, 2022
716fc3e
feature(bento-app-banner): ensure logger is globally disabled
Jan 27, 2022
55c40e4
feature(bento-app-banner): ensure component inherits from AmpPreactBa…
Jan 27, 2022
bce7b5a
Update src/preact/utils/docInfo.js
scottrippey Jan 28, 2022
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
8 changes: 8 additions & 0 deletions build-system/compile/bundles.config.extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@
"hasCss": true
}
},
{
"name": "amp-app-banner",
"version": "1.0",
"options": {
"hasCss": true,
"bento": true
}
},
{
"name": "amp-audio",
"version": "0.1",
Expand Down
1 change: 1 addition & 0 deletions build-system/test-configs/forbidden-terms.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ const forbiddenTermsGlobal = {
'extensions/amp-web-push/0.1/amp-web-push-helper-frame.js',
'extensions/amp-web-push/0.1/amp-web-push-permission-dialog.js',
'src/experiments/index.js',
'src/preact/hooks/useLocalStorage.js',
'src/service/cid-impl.js',
'src/service/standard-actions-impl.js',
'src/service/storage-impl.js',
Expand Down
4 changes: 4 additions & 0 deletions css/Z_INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,12 @@
| `amp-mega-menu` | 1000 | [extensions/amp-mega-menu/0.1/amp-mega-menu.css](/extensions/amp-mega-menu/0.1/amp-mega-menu.css) |
| `amp-user-notification` | 1000 | [extensions/amp-user-notification/0.1/amp-user-notification.css](/extensions/amp-user-notification/0.1/amp-user-notification.css) |
| `i-amphtml-app-banner-top-padding` | 15 | [extensions/amp-app-banner/0.1/amp-app-banner.css](/extensions/amp-app-banner/0.1/amp-app-banner.css) |
| `bannerPadding` | 15 | [extensions/amp-app-banner/1.0/component/component.jss.js](/extensions/amp-app-banner/1.0/component/component.jss.js) |
| `.amp-app-banner-dismiss-button` | 14 | [extensions/amp-app-banner/0.1/amp-app-banner.css](/extensions/amp-app-banner/0.1/amp-app-banner.css) |
| `dismiss` | 14 | [extensions/amp-app-banner/1.0/component/component.jss.js](/extensions/amp-app-banner/1.0/component/component.jss.js) |
| `amp-app-banner` | 13 | [extensions/amp-app-banner/0.1/amp-app-banner.css](/extensions/amp-app-banner/0.1/amp-app-banner.css) |
| `amp-app-banner` | 13 | [extensions/amp-app-banner/1.0/amp-app-banner.css](/extensions/amp-app-banner/1.0/amp-app-banner.css) |
| `banner` | 13 | [extensions/amp-app-banner/1.0/component/component.jss.js](/extensions/amp-app-banner/1.0/component/component.jss.js) |
| `amp-sticky-ad-top-padding` | 12 | [extensions/amp-sticky-ad/1.0/amp-sticky-ad.css](/extensions/amp-sticky-ad/1.0/amp-sticky-ad.css) |
| `style` | 11 | [extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js](/extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js) |
| `amp-sticky-ad` | 11 | [extensions/amp-sticky-ad/1.0/amp-sticky-ad.css](/extensions/amp-sticky-ad/1.0/amp-sticky-ad.css) |
Expand Down
2 changes: 1 addition & 1 deletion examples/visual-tests/article.amp/article.amp.html
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@
<script async custom-element="amp-app-banner" src="https://cdn.ampproject.org/v0/amp-app-banner-0.1.js" data-amp-report-test="amp-app-banner.js"></script>
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<meta name="apple-itunes-app" content="app-id=828256236, app-argument=medium://p/cb7f223fad86">
<link rel="manifest" href="medium-manifest.json">
<link rel="manifest" href="../../medium-manifest.json">
</head>
<body>
<amp-sidebar id="sidebar" layout="nodisplay">
Expand Down
45 changes: 27 additions & 18 deletions extensions/amp-app-banner/0.1/amp-app-banner.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,12 +295,7 @@ export class AmpIosAppBanner extends AbstractAppBanner {
* @private
*/
parseIosMetaContent_(metaContent) {
const parts = metaContent.replace(/\s/, '').split(',');
const config = {};
parts.forEach((part) => {
const keyValuePair = part.split('=');
config[keyValuePair[0]] = keyValuePair[1];
});
const config = this.parseKeyValues(metaContent);

const appId = config['app-id'];
const openUrl = config['app-argument'];
Expand Down Expand Up @@ -328,6 +323,22 @@ export class AmpIosAppBanner extends AbstractAppBanner {
installAppUrl
);
}

/**
* Parses a string like "key1=value1,key2=value2" into { key1: "value1", key2: "value2" }
* @param {string} metaContent
* @return {*}
*/
parseKeyValues(metaContent) {
return metaContent
.replace(/\s/, '')
.split(',')
.reduce((result, keyValue) => {
const [key, value] = keyValue.split('=');
result[key] = value;
return result;
}, {});
}
}

/**
Expand Down Expand Up @@ -471,18 +482,16 @@ export class AmpAndroidAppBanner extends AbstractAppBanner {
return;
}

for (let i = 0; i < apps.length; i++) {
const app = apps[i];
if (app['platform'] == 'play') {
const installAppUrl = `https://play.google.com/store/apps/details?id=${app['id']}`;
const openInAppUrl = this.getAndroidIntentForUrl_(app['id']);
this.setupOpenButton_(
dev().assertElement(this.openButton_),
openInAppUrl,
installAppUrl
);
return;
}
const playApp = apps.find((a) => a['platform'] === 'play');
if (playApp) {
const installAppUrl = `https://play.google.com/store/apps/details?id=${playApp['id']}`;
const openInAppUrl = this.getAndroidIntentForUrl_(playApp['id']);
this.setupOpenButton_(
dev().assertElement(this.openButton_),
openInAppUrl,
installAppUrl
);
return;
scottrippey marked this conversation as resolved.
Show resolved Hide resolved
}

user().warn(
Expand Down
11 changes: 11 additions & 0 deletions extensions/amp-app-banner/1.0/amp-app-banner.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
amp-app-banner {
position: fixed !important;
bottom: 0 !important;
left: 0;
width: 100%;
max-height: 100px !important;
box-sizing: border-box;
background: #fff;
z-index: 13;
box-shadow: 0 0 5px 0 rgba(0,0,0, 0.2) !important;
}
28 changes: 28 additions & 0 deletions extensions/amp-app-banner/1.0/amp-app-banner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {isExperimentOn} from '#experiments';

import {AmpPreactBaseElement, setSuperClass} from '#preact/amp-base-element';

import {userAssert} from '#utils/log';

import {BaseElement} from './base-element';

import {CSS} from '../../../build/amp-app-banner-1.0.css';

/** @const {string} */
const TAG = 'amp-app-banner';

class AmpAppBanner extends setSuperClass(BaseElement, AmpPreactBaseElement) {
/** @override */
isLayoutSupported(layout) {
userAssert(
isExperimentOn(this.win, 'bento') ||
isExperimentOn(this.win, 'bento-app-banner'),
'expected global "bento" or specific "bento-app-banner" experiment to be enabled'
);
return super.isLayoutSupported(layout);
}
}

AMP.extension(TAG, '1.0', (AMP) => {
AMP.registerElement(TAG, AmpAppBanner, CSS);
});
25 changes: 25 additions & 0 deletions extensions/amp-app-banner/1.0/base-element.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {PreactBaseElement} from '#preact/base-element';

import {BentoAppBanner} from './component/component';
import {CSS as COMPONENT_CSS} from './component/component.jss';

export class BaseElement extends PreactBaseElement {}

/** @override */
BaseElement['Component'] = BentoAppBanner;

/** @override */
BaseElement['props'] = {
'children': {passthrough: true},
'dismissButtonAriaLabel': {attr: 'dismiss-button-aria-label'},
'id': {attr: 'id'},
scottrippey marked this conversation as resolved.
Show resolved Hide resolved
};

/** @override */
BaseElement['layoutSizeDefined'] = true;

/** @override */
BaseElement['usesShadowDom'] = true;

/** @override */
BaseElement['shadowCss'] = COMPONENT_CSS;
101 changes: 101 additions & 0 deletions extensions/amp-app-banner/1.0/component/android.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {WindowInterface} from '#core/window/interface';

import {logger} from '#preact/logger';
import {docInfo} from '#preact/utils/docInfo';
import {platformUtils} from '#preact/utils/platform';
import {urlUtils} from '#preact/utils/url';
import {xhrUtils} from '#preact/utils/xhr';

import {openWindowDialog} from '../../../../src/open-window-dialog';

const OPEN_LINK_TIMEOUT = 1500;

/**
* @return {{openOrInstall: function(): void, promise: Promise<Response>}|null}
*/
export function getAndroidAppInfo() {
// We want to fallback to browser builtin mechanism when possible.
const canShowBuiltinBanner =
platformUtils.isAndroid() && platformUtils.isChrome();

if (canShowBuiltinBanner) {
logger.info(
'BENTO-APP-BANNER',
'Not rendering bento-app-banner:',
'Browser supports builtin banners.'
);
return null;
}

const manifestLink = self.document.head.querySelector(
'link[rel=manifest],link[rel=origin-manifest]'
);

const missingDataSources = !manifestLink;
if (missingDataSources) {
return null;
}

const manifestHref = manifestLink.getAttribute('href');

urlUtils.assertHttpsUrl(manifestHref, undefined, 'manifest href');

const promise = xhrUtils.fetchJson(manifestHref).then(parseManifest);
return {
promise,
openOrInstall: () => {
return promise.then((manifest) => {
if (!manifest) {
return;
}
const {installAppUrl, openInAppUrl} = manifest;
setTimeout(() => {
WindowInterface.getTop(window).location.assign(installAppUrl);
}, OPEN_LINK_TIMEOUT);
openWindowDialog(window, openInAppUrl, '_top');
});
},
};
}

/**
* @param {object} manifestJson
* @return {{installAppUrl: string, openInAppUrl: string}|null}
*/
function parseManifest(manifestJson) {
const apps = manifestJson['related_applications'];
if (!apps) {
logger.error(
'BENTO-APP-BANNER',
'Invalid manifest:',
'related_applications is missing from manifest.json file'
);
return null;
}

const playApp = apps.find((a) => a['platform'] === 'play');
if (!playApp) {
logger.error(
'BENTO-APP-BANNER',
'Invalid manifest:',
'Could not find a platform=play app in manifest'
);
return null;
}

const installAppUrl = `https://play.google.com/store/apps/details?id=${playApp['id']}`;
const openInAppUrl = getAndroidIntentForUrl(playApp['id']);
return {installAppUrl, openInAppUrl};
}

/**
* @param {string} appId
* @return {string}
*/
function getAndroidIntentForUrl(appId) {
const parsedUrl = urlUtils.parse(docInfo.canonicalUrl);
const cleanProtocol = parsedUrl.protocol.replace(':', '');
const {host, pathname} = parsedUrl;

return `android-app://${appId}/${cleanProtocol}/${host}${pathname}`;
}
Loading