Skip to content
This repository has been archived by the owner on May 24, 2021. It is now read-only.

Commit

Permalink
Merge pull request #509 from alexander-heimbuch/feature/loading-indic…
Browse files Browse the repository at this point in the history
…ator

Feature/loading indicator
  • Loading branch information
alexander-heimbuch authored Feb 16, 2018
2 parents ac66486 + e68e707 commit b94b25c
Show file tree
Hide file tree
Showing 15 changed files with 465 additions and 170 deletions.
Binary file modified design.sketch
Binary file not shown.
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@
"lint": "npm run lint:standard",
"commit": "git-cz",
"deploy:gh-pages": "scripts/deploy-ghpages.sh dist",
"deploy:version": "scripts/deploy-version.sh",
"deploy:latest": "scripts/deploy-version.sh latest",
"deploy:cdn": "rsync -rvt --chmod=D2755,F644 dist/ podlove@rsync.keycdn.com:applications/web-player/",
"deploy:npm": "npm publish --access public",
"deploy:release": "scripts/deploy-release.sh",
Expand All @@ -55,9 +53,9 @@
"@podlove/html5-audio-driver": "0.7.1",
"babel-polyfill": "6.26.0",
"binary-search": "1.3.3",
"bluebird": "3.5.1",
"clipboard": "1.7.1",
"color": "3.0.0",
"data.task": "^3.1.1",
"detect-browser": "2.0.0",
"hashcode": "1.0.3",
"iframe-resizer": "3.5.16",
Expand Down Expand Up @@ -97,6 +95,7 @@
"cross-env": "5.1.3",
"css-loader": "0.28.9",
"cz-conventional-changelog": "2.1.0",
"eslint": "4.17.0",
"eslint-plugin-html": "4.0.2",
"extract-text-webpack-plugin": "3.0.2",
"file-loader": "1.1.6",
Expand Down
16 changes: 7 additions & 9 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
# Podlove Web Player

[![Greenkeeper badge](https://badges.greenkeeper.io/podlove/podlove-web-player.svg)](https://greenkeeper.io/)

[![Latest Version](https://img.shields.io/github/release/podlove/podlove-web-player.svg)](https://github.com/podlove/podlove-web-player/releases)
[![Code Climate](https://codeclimate.com/github/podlove/podlove-web-player/badges/gpa.svg)](https://codeclimate.com/github/podlove/podlove-web-player)
[![Build Status](https://img.shields.io/circleci/project/github/podlove/podlove-web-player/development.svg)](https://circleci.com/gh/podlove/podlove-web-player)
[![Coverage](https://img.shields.io/codecov/c/github/podlove/podlove-web-player/feature/coverage.svg)](https://codecov.io/gh/podlove/podlove-web-player/branch/development)
[![Coverage](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/feross/standard)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fpodlove%2Fpodlove-web-player.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fpodlove%2Fpodlove-web-player?ref=badge_shield)
[![npm version](https://badge.fury.io/js/%40podlove%2Fpodlove-web-player.svg?style=flat-square)](https://badge.fury.io/js/%40podlove%2Fpodlove-web-player)
[![Greenkeeper badge](https://badges.greenkeeper.io/podlove/podlove-web-player.svg?style=flat-square)](https://greenkeeper.io/)
[![Build Status](https://img.shields.io/circleci/project/github/podlove/podlove-web-player/development.svg?style=flat-square)](https://circleci.com/gh/podlove/podlove-web-player)
[![Coverage](https://img.shields.io/codecov/c/github/podlove/podlove-web-player/feature/coverage.svg?style=flat-square)](https://codecov.io/gh/podlove/podlove-web-player/branch/development)
[![Coverage](https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square)](http://commitizen.github.io/cz-cli/)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fpodlove%2Fpodlove-web-player.svg?type=shield&style=flat-square)](https://app.fossa.io/projects/git%2Bgithub.com%2Fpodlove%2Fpodlove-web-player?ref=badge_shield)

![Preview](screenshot.jpg)

Expand Down
3 changes: 0 additions & 3 deletions scripts/version.js

This file was deleted.

10 changes: 7 additions & 3 deletions src/effects/transcripts/active.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@ const buildIndex = (duration = 0, data = []) => {
})

return time => {
if (time) {
return timeIndex.search(time)
let result

try {
result = timeIndex.search(time)
} catch (e) {
result = []
}

return []
return result
}
}

Expand Down
159 changes: 39 additions & 120 deletions src/embed/embed.js
Original file line number Diff line number Diff line change
@@ -1,135 +1,54 @@
/* globals BASE */

import 'babel-polyfill'
import { get, head, isString } from 'lodash'
import Bluebird from 'bluebird'

import { findNode, createNode, appendNode, tag } from 'utils/dom'
import requestConfig from 'utils/request'
import { urlParameters } from 'utils/url'

import { get, compose } from 'lodash'
import { iframeResizer } from 'iframe-resizer'
// eslint-disable-next-line
import iframeResizerContentWindow from 'raw-loader!iframe-resizer/js/iframeResizer.contentWindow.js'

// Player renderer
const playerSandbox = anchor => {
const frame = createNode('iframe')

frame.setAttribute('width', anchor.offsetWidth)

frame.setAttribute('min-width', '100%')
frame.setAttribute('seamless', '')
frame.setAttribute('scrolling', 'no')
frame.setAttribute('frameborder', '0')

// Reset the width on viewport resize
window.addEventListener('resize', () => {
frame.setAttribute('width', anchor.offsetWidth)
})

appendNode(anchor, frame)
return frame
}

const injectPlayer = (sandbox, player) => new Bluebird(resolve => {
const sandboxDoc = get(sandbox, ['contentWindow', 'document'])

const documentLoaded = () => {
if (sandboxDoc.readyState === 'complete') {
return resolve(sandbox)
}

return setTimeout(documentLoaded, 150)
}

sandboxDoc.open()
sandboxDoc.write('<!DOCTYPE html>')
sandboxDoc.write('<html>')
sandboxDoc.write('<head><meta charset="utf-8" /></head>')
sandboxDoc.write(player)
sandboxDoc.close()

return documentLoaded()
})

const getPodloveStore = sandbox =>
get(sandbox, ['contentWindow', 'PODLOVE_STORE', 'store'])
import { findNode, tag } from 'utils/dom'
import requestConfig from 'utils/request'
import { sandbox, sandboxWindow } from 'utils/sandbox'

const preloader = sandbox => ({
init: () => {
sandbox.style.opacity = 0
// maximum width player
sandbox.style['max-width'] = '768px'
sandbox.style.transition = 'all 500ms'
},
done: () => {
sandbox.style.opacity = 1
sandbox.style.height = 'auto'
}
})
import loader from './loader'

const renderPlayer = anchor => player => {
const sandbox = playerSandbox(anchor)
const loader = preloader(sandbox)
const player = config => [
// Config
tag('script', `window.PODLOVE = ${JSON.stringify(config)}`),

loader.init()
// Loader
loader(config),

return injectPlayer(sandbox, player)
.then(sandbox => {
iframeResizer({
checkOrigin: false,
log: false
}, sandbox)
// Entry
tag('PodlovePlayer'),

loader.done()
})
.return(sandbox)
.then(getPodloveStore)
}
// Bundles
tag('link', '', {rel: 'stylesheet', href: `${get(config.reference, 'base', BASE)}/style.css`}),
tag('script', '', {type: 'text/javascript', src: `${get(config.reference, 'base', BASE)}/vendor.js`}),
tag('script', '', {type: 'text/javascript', src: `${get(config.reference, 'base', BASE)}/window.js`}),

const getConfig = (episode) =>
Bluebird.resolve(episode)
// If the config is a string, lets assume that this will point to the remote config json
.then(config => isString(config) ? requestConfig(config) : config)
// iFrameResizer
tag('script', iframeResizerContentWindow)
].join('')

const dispatchUrlParameters = store => {
store.dispatch({
type: 'SET_URL_PARAMS',
payload: urlParameters
})
const resizer = sandbox => {
iframeResizer({
checkOrigin: false,
log: false
}, sandbox)

return store
return sandbox
}

// Config Node
const configNode = (config = {}) => tag('script', `window.PODLOVE = ${JSON.stringify(config)}`)

// Player Logic
const styleBundle = config => tag('link', '', {rel: 'stylesheet', href: `${get(config.reference, 'base', BASE)}/style.css`})
const vendorBundle = config => tag('script', '', {type: 'text/javascript', src: `${get(config.reference, 'base', BASE)}/vendor.js`})
const appBundle = config => tag('script', '', {type: 'text/javascript', src: `${get(config.reference, 'base', BASE)}/window.js`})

// Dynamic resizer
const dynamicResizer = tag('script', iframeResizerContentWindow)

// Transclusion point
const playerEntry = tag('PodlovePlayer')

// Bootstrap
window.podlovePlayer = (selector, episode) => {
const anchor = typeof selector === 'string' ? head(findNode(selector)) : selector

return getConfig(episode)
.then(config => Bluebird.all([
playerEntry,
configNode(config),
styleBundle(config),
vendorBundle(config),
appBundle(config),
dynamicResizer
]))
.then(result => result.join(''))
.then(renderPlayer(anchor))
.then(dispatchUrlParameters)
}
const sandboxFromSelector = compose(sandbox, findNode)

window.podlovePlayer = (selector, episode) =>
requestConfig(episode)
.then(player)
.then(sandboxFromSelector(selector))
.then(resizer)
.then(sandboxWindow(['PODLOVE_STORE', 'store']))
.catch(() => {
console.group(`Can't load player with config`)
console.error('selector', selector)
console.error('config', episode)
console.groupEnd()
})
35 changes: 35 additions & 0 deletions src/embed/loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import color from 'color'
import { get } from 'lodash'

import { tag } from 'utils/dom'
import css from 'css-loader!autoprefixer-loader!sass-loader!../styles/_loader.scss'

const style = tag('style', css.toString())

const dom = ({ theme }) => {
const light = '#fff'
const dark = '#000'

const main = get(theme, 'main', '#2B8AC6')
const luminosity = color(main).luminosity()

const highlight = get(theme, 'highlight', luminosity < 0.25 ? light : dark)

return `<div class="loader" id="loader" style="background: ${main}">
<div class="dot bounce1" style="background: ${highlight}"></div>
<div class="dot bounce2" style="background: ${highlight}"></div>
<div class="dot bounce3" style="background: ${highlight}"></div>
</div>`
}

const script = tag('script', `
var loader = document.getElementById('loader')
window.addEventListener('load', function() {
loader.className += ' done'
setTimeout(loader.remove, 300)
})
`)

export default config => ([style, dom(config), script].join(''))
56 changes: 56 additions & 0 deletions src/styles/_loader.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
.loader {
width: 100%;
height: 100%;
position: fixed;
left: 0;
top: 0;
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
opacity: 1;
transition: opacity linear 300ms;

.dot {
width: 20px;
height: 20px;
margin: 3px;
border-radius: 100%;
display: inline-block;
animation: loader 1.4s ease-in-out 0s infinite both;
}

.bounce1 {
animation-delay: -0.32s;
}

.bounce2 {
animation-delay: -0.16s;
}

&.done {
opacity: 0;
}
}

@-webkit-keyframes loader {
0%,
80%,
100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
}

@keyframes loader {
0%,
80%,
100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
}
34 changes: 24 additions & 10 deletions src/utils/dom.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { curry, compose, uniq, concat, join, filter } from 'lodash/fp'
import { curry, compose, uniq, concat, join, filter, head, identity } from 'lodash/fp'

export const findNode = selector => document.querySelectorAll(selector)
export const findNode = selector => typeof selector === 'string' ? head(document.querySelectorAll(selector)) : selector
export const createNode = tag => document.createElement(tag)
export const appendNode = curry((node, child) => node.appendChild(child))
export const appendNode = curry((node, child) => {
node.appendChild(child)

return child
})

export const tag = curry((tag, value = '', attributes = {}) => {
let attr = Object.keys(attributes).map(attribute => ` ${attribute}="${attributes[attribute]}"`)
Expand All @@ -11,24 +15,34 @@ export const tag = curry((tag, value = '', attributes = {}) => {
return `<${tag}${attr}>${value}</${tag}>`
})

export const setStyles = (attrs = {}) => el => {
export const setStyles = curry((attrs = {}, el) => {
Object.keys(attrs).forEach(property => {
el.style[property] = attrs[property]
})
}

export const getClasses = el => el.className.split(' ')
return el
})

export const getClasses = compose(filter(identity), el => el.className.split(' '))

export const hasOverflow = el => el.scrollWidth > el.clientWidth

export const addClasses = (...classes) => el => {
export const addClasses = curry((classes, el) => {
el.className = compose(join(' '), uniq, concat(classes), getClasses)(el)

return el
}
})

export const removeClasses = (...classes) => el => {
export const removeClasses = curry((classes, el) => {
el.className = compose(join(' '), filter(className => !~classes.indexOf(className)), getClasses)(el)

return el
}
})

export const setAttributes = curry((attrs = {}, el) => {
Object.keys(attrs).forEach(property => {
el.setAttribute(property, attrs[property])
})

return el
})
Loading

0 comments on commit b94b25c

Please sign in to comment.