Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
45 changes: 45 additions & 0 deletions example/streaming/podlets/content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Podlet from '@podium/podlet';
import express from 'express';

const podlet = new Podlet({
name: 'content',
version: Date.now().toString(),
pathname: '/',
});

podlet.css({ value: 'http://localhost:6103/css' });

const app = express();

app.use(podlet.middleware());

app.get('/manifest.json', (req, res) => {
res.send(podlet);
});

app.get('/css', (req, res) => {
res.set('Content-Type', 'text/css');
res.send(`
.content {
border: 1px solid black;
border-radius: 5px;
width: 100%;
padding: 20px;
margin: 0;
margin-bottom: 20px;
box-sizing: border-box;
}
`);
});

app.get('/', (req, res) => {
res.send(`
<section class="content">
main content goes here
</section>
`);
Comment on lines +45 to +56

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The layout examples have some inline documentation explaining the different parts. It would be nice to have something similar for at least one of the podlets, or in a separate README.

If it's covered in the podlet-repo, perhaps link to docs there?

});

app.listen(6103, () => {
console.log(`content podlet server running at http://localhost:6103`);
});
45 changes: 45 additions & 0 deletions example/streaming/podlets/footer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Podlet from '@podium/podlet';
import express from 'express';

const podlet = new Podlet({
name: 'footer',
version: Date.now().toString(),
pathname: '/',
});

podlet.css({ value: 'http://localhost:6104/css' });

const app = express();

app.use(podlet.middleware());

app.get('/manifest.json', (req, res) => {
res.send(podlet);
});

app.get('/css', (req, res) => {
res.set('Content-Type', 'text/css');
res.send(`
footer {
border: 1px solid black;
border-radius: 5px;
width: 100%;
padding: 20px;
margin: 0;
margin-bottom: 20px;
box-sizing: border-box;
}
`);
});

app.get('/', (req, res) => {
res.send(`
<footer>
footer content
</footer>
`);
});

app.listen(6104, () => {
console.log(`footer podlet server running at http://localhost:6104`);
});
50 changes: 50 additions & 0 deletions example/streaming/podlets/header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Podlet from '@podium/podlet';
import express from 'express';

const podlet = new Podlet({
name: 'header',
version: Date.now().toString(),
pathname: '/',
});

podlet.css({ value: 'http://localhost:6101/css' });

const app = express();

app.use(podlet.middleware());

app.get('/manifest.json', (req, res) => {
res.send(podlet);
});

app.get('/css', (req, res) => {
res.set('Content-Type', 'text/css');
res.send(`
header {
border: 1px solid black;
border-radius: 5px;
width: 100%;
padding: 20px;
margin: 0;
margin-bottom: 20px;
box-sizing: border-box;
}
header h1 {
text-align: center;
margin: 0;
padding: 0;
}
`);
});

app.get('/', (req, res) => {
res.send(`
<header>
<h1>Header</h1>
</header>
`);
});

app.listen(6101, () => {
console.log(`header podlet server running at http://localhost:6101`);
});
62 changes: 62 additions & 0 deletions example/streaming/podlets/menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Podlet from '@podium/podlet';
import express from 'express';

const podlet = new Podlet({
name: 'menu',
version: Date.now().toString(),
pathname: '/',
});

podlet.css({ value: 'http://localhost:6102/css' });

const app = express();

app.use(podlet.middleware());

app.get('/manifest.json', (req, res) => {
res.send(podlet);
});

app.get('/css', (req, res) => {
res.set('Content-Type', 'text/css');
res.send(`
menu {
border: 1px solid black;
border-radius: 5px;
width: 100%;
padding: 10px;
margin: 0;
margin-bottom: 20px;
box-sizing: border-box;
}
menu ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
justify-content: space-evenly;
align-items: center;
}
menu ul li {
margin: 0;
padding: 0;
}
`);
});

app.get('/', (req, res) => {
res.send(`
<menu>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/things">Things</a></li>
<li><a href="/stuff">Stuff</a></li>
</ul>
</menu>
`);
});

app.listen(6102, () => {
console.log(`menu podlet server running at http://localhost:6102`);
});
169 changes: 169 additions & 0 deletions example/streaming/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import express from 'express';
import Layout from '../../lib/layout.js';
import { template } from './views/template.js';

const layout = new Layout({
pathname: '/foo',
logger: console,
name: 'demo',
});

// use our custom streaming template
layout.view(template);

const content = layout.client.register({
name: 'content',
uri: 'http://localhost:6103/manifest.json',
});

const header = layout.client.register({
name: 'header',
uri: 'http://localhost:6101/manifest.json',
});

const menu = layout.client.register({
name: 'menu',
uri: 'http://localhost:6102/manifest.json',
});

const footer = layout.client.register({
name: 'footer',
uri: 'http://localhost:6104/manifest.json',
});

layout.css({ value: '/css' });

const app = express();

app.use(layout.pathname(), layout.middleware());

app.get('/foo/css', (req, res) => {
res.set('Content-Type', 'text/css');
res.send(`
@keyframes pulse {
0% {
background-color: #e0e0e0;
}
50% {
background-color: #f0f0f0;
}
100% {
background-color: #e0e0e0;
}
}
.skeleton {
width: 100%;
background-color: #e0e0e0;
border-radius: 5px;
animation: pulse 1.5s infinite ease-in-out;
margin: 0;
margin-bottom: 20px;
box-sizing: border-box;
}
.skeleton.header {
height:79px;
}
.skeleton.menu {
height:40px;
}
.skeleton.content {
height:60px;
}
.skeleton.footer {
height:60px;
}
`);
});

app.get(layout.pathname(), async (req, res) => {
const incoming = res.locals.podium;

incoming.view = {
title: 'Example streaming application',
};

const headerFetch = header.fetch(incoming);
const menuFetch = menu.fetch(incoming);
const contentFetch = content.fetch(incoming);
const footerFetch = footer.fetch(incoming);

incoming.hints.on('complete', async ({ js, css }) => {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once we have hints in from the podlets, we can set up streaming

// set the assets on httpincoming so that they are available in the document template
incoming.js = [...incoming.js, ...js];
incoming.css = [...incoming.css, ...css];

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is pretty awkward but works for now at least


// set up the stream which will send the document template head
const stream = res.podiumStream();

// stream in the document body with slot placeholders for podlets
stream.send(`
<template shadowrootmode="open">
<link href="/foo/css" type="text/css" rel="stylesheet">
<div class="container">
<div>
<div>
<slot name="header"><div class="skeleton header"></div></slot>
</div>
</div>
<div>
<div>
<slot name="menu"><div class="skeleton menu"></div></slot>
</div>
<div>
<slot name="content"><div class="skeleton content"></div></slot>
</div>
</div>
<div>
<div>
<slot name="footer"><div class="skeleton footer"></div></slot>
</div>
</div>
</div>
</template>
`);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case we are sending a DSD template with slots and placeholder skeleton html so that we can replace these by streaming in podlet content later.


// fake 1 second delay
await new Promise((res) => setTimeout(res, 1000));

// stream in podlet content when available...
headerFetch.then((content) => {
stream.send(`<div slot="header">${content}</div>`);
});

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we decorate the podlet content in divs with slot attributes to indicate where they should be placed into the document.


await new Promise((res) => setTimeout(res, 1000));

menuFetch.then((content) => {
stream.send(`<div slot="menu">${content}</div>`);
});

await new Promise((res) => setTimeout(res, 1000));

contentFetch.then((content) => {
stream.send(`<div slot="content">${content}</div>`);
});

await new Promise((res) => setTimeout(res, 1000));

footerFetch.then((content) => {
stream.send(`<div slot="footer">${content}</div>`);
});

// close out the dom and the stream
await Promise.all([headerFetch, menuFetch, contentFetch, footerFetch]);
stream.done();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indicate that we are finished streaming content so that the html template can finish things off

});
});

app.use(`${layout.pathname()}/assets`, express.static('assets'));

// eslint-disable-next-line no-unused-vars
app.use((error, req, res, next) => {
console.error(error);
res.status(500).send(
'<html><body><h1>Internal server error</h1></body></html>',
);
});

app.listen(6123, () => {
console.log(`layout server running at http://localhost:6123`);
});
Loading