From 12942e8ee9f65fefb050e8daa14c62d5ebd8e9e0 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 11 Jun 2020 15:47:41 +0200 Subject: [PATCH] feat: amp --- src/core/build/createWebpackConfig.ts | 10 ++++ src/core/build/paths.ts | 2 + src/core/client/embed/Coral.ts | 4 +- src/core/client/embed/StreamEmbed.ts | 3 +- src/core/client/embed/amp.html | 26 +++++++++++ .../client/embed/decorators/withAutoHeight.ts | 12 ++++- src/core/client/embed/index.html | 7 +-- src/core/client/embed/story.html | 7 +-- src/core/client/embed/storyAMP.html | 34 ++++++++++++++ src/core/client/embed/storyButton.html | 7 +-- src/core/client/stream/App/TabBar.tsx | 9 +++- src/core/client/stream/index.html | 7 ++- src/core/server/app/middleware/csp/tenant.ts | 2 +- src/core/server/app/router/client.ts | 46 ++++++++++++++++--- src/core/server/app/views/amp.html | 25 ++++++++++ 15 files changed, 178 insertions(+), 23 deletions(-) create mode 100644 src/core/client/embed/amp.html create mode 100644 src/core/client/embed/storyAMP.html create mode 100644 src/core/server/app/views/amp.html diff --git a/src/core/build/createWebpackConfig.ts b/src/core/build/createWebpackConfig.ts index b4d431dfb..110c3f057 100644 --- a/src/core/build/createWebpackConfig.ts +++ b/src/core/build/createWebpackConfig.ts @@ -718,6 +718,16 @@ export default function createWebpackConfig( filename: "storyButton.html", template: paths.appEmbedStoryButtonHTML, inject: "head", + }), + new HtmlWebpackPlugin({ + filename: "amp.html", + template: paths.appEmbedAMPHTML, + inject: "head", + }), + new HtmlWebpackPlugin({ + filename: "storyAMP.html", + template: paths.appEmbedStoryAMPHTML, + inject: false, }) ), ...ifBuild( diff --git a/src/core/build/paths.ts b/src/core/build/paths.ts index dfbe3d789..5e7e51376 100644 --- a/src/core/build/paths.ts +++ b/src/core/build/paths.ts @@ -53,6 +53,8 @@ export default { appEmbedHTML: resolveSrc("core/client/embed/index.html"), appEmbedStoryHTML: resolveSrc("core/client/embed/story.html"), appEmbedStoryButtonHTML: resolveSrc("core/client/embed/storyButton.html"), + appEmbedAMPHTML: resolveSrc("core/client/embed/amp.html"), + appEmbedStoryAMPHTML: resolveSrc("core/client/embed/storyAMP.html"), appDistStatic: resolveApp("dist/static"), appPublic: resolveApp("public"), diff --git a/src/core/client/embed/Coral.ts b/src/core/client/embed/Coral.ts index c713bac47..150b8fb3b 100644 --- a/src/core/client/embed/Coral.ts +++ b/src/core/client/embed/Coral.ts @@ -18,6 +18,7 @@ export interface Config { enableDeprecatedEvents?: boolean; /** Allow setting className of body tag inside iframe */ bodyClassName?: string; + amp?: boolean; } export function createStreamEmbed(config: Config): StreamEmbed { @@ -32,7 +33,7 @@ export function createStreamEmbed(config: Config): StreamEmbed { return create({ title: "Coral Embed Stream", storyID: config.storyID || query.storyID, - storyURL: config.storyURL || resolveStoryURL(), + storyURL: config.storyURL || query.storyURL || resolveStoryURL(), commentID: config.commentID || query.commentID, id: config.id || "coral-embed-stream", rootURL: config.rootURL || getLocationOrigin(), @@ -41,5 +42,6 @@ export function createStreamEmbed(config: Config): StreamEmbed { accessToken: config.accessToken, bodyClassName: config.bodyClassName, enableDeprecatedEvents: config.enableDeprecatedEvents, + amp: config.amp, }); } diff --git a/src/core/client/embed/StreamEmbed.ts b/src/core/client/embed/StreamEmbed.ts index 3229eb4ae..8efb50b6e 100644 --- a/src/core/client/embed/StreamEmbed.ts +++ b/src/core/client/embed/StreamEmbed.ts @@ -35,6 +35,7 @@ export interface StreamEmbedConfig { accessToken?: string; bodyClassName?: string; enableDeprecatedEvents?: boolean; + amp?: boolean; } export class StreamEmbed { @@ -137,7 +138,7 @@ export class StreamEmbed { const streamDecorators: ReadonlyArray = [ withIOSSafariWidthWorkaround, - withAutoHeight, + withAutoHeight(Boolean(this.config.amp)), withClickEvent, withSetCommentID, withEventEmitter( diff --git a/src/core/client/embed/amp.html b/src/core/client/embed/amp.html new file mode 100644 index 000000000..6dfe2c1d5 --- /dev/null +++ b/src/core/client/embed/amp.html @@ -0,0 +1,26 @@ + + + + Coral – AMP Embed Stream + + + + + + +
+ + + diff --git a/src/core/client/embed/decorators/withAutoHeight.ts b/src/core/client/embed/decorators/withAutoHeight.ts index c9ab0d81f..f96eb3e18 100644 --- a/src/core/client/embed/decorators/withAutoHeight.ts +++ b/src/core/client/embed/decorators/withAutoHeight.ts @@ -1,12 +1,22 @@ import { Decorator } from "./types"; -const withAutoHeight: Decorator = (pym) => { +const withAutoHeight: (amp: boolean) => Decorator = (amp) => (pym) => { // Resize parent iframe height when child height changes let cachedHeight: string; pym.onMessage("height", (height: string) => { if (height !== cachedHeight) { pym.iframe.style.height = `${height}px`; cachedHeight = height; + if (amp) { + window.parent.postMessage( + { + sentinel: "amp", + type: "embed-size", + height: Number.parseInt(height, 10) > 100 ? height : 100, + }, + "*" + ); + } } }); }; diff --git a/src/core/client/embed/index.html b/src/core/client/embed/index.html index 32077e928..3140509e1 100644 --- a/src/core/client/embed/index.html +++ b/src/core/client/embed/index.html @@ -1,7 +1,7 @@ - Coral 5.0 – Embed Stream + Coral – Embed Stream @@ -16,9 +16,10 @@

Admin | Story | - Story With Button + Story With Button | + AMP

-

Coral 5.0 – Embed Stream

+

Coral – Embed Stream

+ + Coral AMP + + + + + + +

+ Admin | Default | Story | + Story With Button +

+

Coral – AMP

+ +
+
Read more
+
+ + diff --git a/src/core/client/embed/storyButton.html b/src/core/client/embed/storyButton.html index 428a8ec3e..79d7fae9e 100644 --- a/src/core/client/embed/storyButton.html +++ b/src/core/client/embed/storyButton.html @@ -1,7 +1,7 @@ - Coral 5.0 – Embed Stream – Story with Button + Coral – Embed Stream – Story with Button @@ -16,9 +16,10 @@

Admin | Default | - Story + Story | + AMP

-

Coral 5.0 – Story with Button

+

Coral – Story with Button

 

diff --git a/src/core/client/stream/App/TabBar.tsx b/src/core/client/stream/App/TabBar.tsx index 967bab723..168993170 100644 --- a/src/core/client/stream/App/TabBar.tsx +++ b/src/core/client/stream/App/TabBar.tsx @@ -6,7 +6,7 @@ import { GQLSTORY_MODE } from "coral-framework/schema"; import CLASSES from "coral-stream/classes"; import { Icon, MatchMedia, Tab, TabBar } from "coral-ui/components"; -type TabValue = "COMMENTS" | "PROFILE" | "%future added value"; +type TabValue = "COMMENTS" | "PROFILE" | "CONFIGURE" | "%future added value"; export interface Props { activeTab: TabValue; @@ -52,7 +52,12 @@ const AppTabBar: FunctionComponent = (props) => { )} {props.showConfigureTab && ( - + {(matches) => matches ? ( diff --git a/src/core/client/stream/index.html b/src/core/client/stream/index.html index 79adf6c91..d2d6d9c61 100644 --- a/src/core/client/stream/index.html +++ b/src/core/client/stream/index.html @@ -10,7 +10,12 @@
diff --git a/src/core/server/app/middleware/csp/tenant.ts b/src/core/server/app/middleware/csp/tenant.ts index a29be6137..1d1ecc8ab 100644 --- a/src/core/server/app/middleware/csp/tenant.ts +++ b/src/core/server/app/middleware/csp/tenant.ts @@ -99,7 +99,7 @@ function generateContentSecurityPolicy(allowedOrigins: string[]) { // Only the domains that are allowed by the tenant may embed Coral. directives.frameAncestors = - allowedOrigins.length > 0 ? allowedOrigins : ["'none'"]; + allowedOrigins.length > 0 ? ["'self'", ...allowedOrigins] : ["'none'"]; // Build the directive. const directive = builder({ directives }); diff --git a/src/core/server/app/router/client.ts b/src/core/server/app/router/client.ts index 8fd725758..14a1da447 100644 --- a/src/core/server/app/router/client.ts +++ b/src/core/server/app/router/client.ts @@ -17,6 +17,11 @@ import Entrypoints, { Entrypoint } from "../helpers/entrypoints"; export interface ClientTargetHandlerOptions { defaultLocale: LanguageCode; + /** + * viewTemplate is the html template to use. + */ + viewTemplate?: string; + /** * mongo is used when trying to infer a site from the request. */ @@ -73,6 +78,7 @@ const clientHandler = ({ entrypoint, enableCustomCSS, defaultLocale, + viewTemplate = "client", }: ClientTargetHandlerOptions): RequestHandler => (req, res, next) => { // Provide configuration to the frontend in the HTML. const config = { @@ -86,7 +92,7 @@ const clientHandler = ({ } res.render( - "client", + viewTemplate, { staticURI, entrypoint, enableCustomCSS, locale, config }, (err, html) => { if (err) { @@ -104,10 +110,7 @@ const clientHandler = ({ ); }; -export function mountClientRoutes( - router: Router, - { staticURI, tenantCache, defaultLocale, mongo }: MountClientRouteOptions -) { +function loadEntrypoints(manifestFile: string) { // TODO: (wyattjoh) figure out a better way of referencing paths. // Load the entrypoint manifest. const manifest = path.join( @@ -119,9 +122,17 @@ export function mountClientRoutes( "..", "dist", "static", - "asset-manifest.json" + manifestFile ); - const entrypoints = Entrypoints.fromFile(manifest); + return Entrypoints.fromFile(manifest); +} + +export function mountClientRoutes( + router: Router, + { staticURI, tenantCache, defaultLocale, mongo }: MountClientRouteOptions +) { + const manifest = "asset-manifest.json"; + const entrypoints = loadEntrypoints(manifest); if (!entrypoints) { logger.error( { manifest }, @@ -129,6 +140,17 @@ export function mountClientRoutes( ); return; } + + const embedManifest = "embed-asset-manifest.json"; + const embedEntrypoints = loadEntrypoints(embedManifest); + if (!embedEntrypoints) { + logger.error( + { manifest: embedManifest }, + "could not load the generated manifest, client routes will remain un-mounted" + ); + return; + } + // Tenant identification middleware. router.use( tenantMiddleware({ @@ -138,6 +160,16 @@ export function mountClientRoutes( ); // Add the embed targets. + router.use( + "/embed/stream/amp", + createClientTargetRouter({ + staticURI, + entrypoint: embedEntrypoints.get("main"), + defaultLocale, + mongo, + viewTemplate: "amp", + }) + ); router.use( "/embed/stream", createClientTargetRouter({ diff --git a/src/core/server/app/views/amp.html b/src/core/server/app/views/amp.html new file mode 100644 index 000000000..b1e30b8a4 --- /dev/null +++ b/src/core/server/app/views/amp.html @@ -0,0 +1,25 @@ +{% import "macros.html" as macros %} +{% extends "templates/base.html" %} + +{% block title %}Coral AMP{% endblock %} + +{% block css %} + +{% endblock %} + +{% block body %} +
+ {% if entrypoint.js %} + {% for asset in entrypoint.js %} + {{ macros.js(asset.src, asset.integrity, staticURI) }} + {% endfor %} + {% endif %} + +{% endblock %}