ferment
Version:
Peer-to-peer audio publishing and streaming application. Like SoundCloud but decentralized. A mashup of ssb, webtorrent and electron.
153 lines (143 loc) • 4.83 kB
JavaScript
var h = require('../lib/h')
var send = require('@mmckegg/mutant/send')
var computed = require('@mmckegg/mutant/computed')
var when = require('@mmckegg/mutant/when')
var AudioOverview = require('./audio-overview')
var contextMenu = require('../lib/context-menu')
var magnet = require('magnet-uri')
var colorHash = require('../lib/color-hash')
var humanTime = require('human-time')
var TorrentStatusWidget = require('./torrent-status')
module.exports = function (context, post) {
var player = context.player || {}
var infoHash = computed(post.audioSrc, (src) => magnet.decode(src).infoHash)
var likesCount = computed(post.likes, x => x.length)
var repostsCount = computed(post.reposters, (list) => list.length)
var reposted = computed([context.profile && context.profile.posts, post.id], (posts, id) => posts && posts.includes(id))
var liked = computed([context.profile && context.profile.likes, post.id], (likes, id) => likes && likes.includes(id))
var isOwner = context.profile && context.profile.id === post.author.id
var color = colorHash.hex(post.id)
var url = computed(post.artworkSrc, context.api.getBlobUrl)
return h('AudioPost', {
'ev-contextmenu': contextMenu.bind(null, context, post),
'data-info-hash': infoHash,
'data-duration': post.duration,
classList: [
computed(post.state, (s) => `-${s}`)
]
}, [
h('div.artwork', {
style: {
'background-image': computed(url, (src) => src ? `url("${src}")` : ''),
'background-color': color,
'cursor': 'pointer'
},
'ev-click': send(context.actions.viewPost, post.id)
}),
h('div.main', [
h('div.title', [
h('a.play', { 'ev-click': send(player.togglePlay, post), href: '#' }),
h('header', [
h('a.feedTitle', {
href: '#', 'ev-click': send(context.actions.viewProfile, post.author.id)
}, [post.author.displayName]),
h('a.title', {
href: '#', 'ev-click': send(context.actions.viewPost, post.id)
}, [post.title])
]),
h('div.timestamp', [
humanTime(Date.now() / 1000 - post.timestamp() / 1000)
])
]),
h('div.display', {
hooks: [
SetPositionHook(context, post)
]
}, [
AudioOverview(post.overview, 600, 100),
h('div.progress', {
style: {
width: computed([post.position, post.duration], (pos, dur) => Math.round(pos / dur * 1000) / 10 + '%')
}
}),
when(post.position, h('span.position', computed(post.position, formatTime))),
h('span.duration', computed(post.duration, formatTime))
]),
h('div.options', [
h('a.like', {
href: '#',
'ev-click': send(toggleLike, { liked, context, post }),
classList: [
when(liked, '-active')
]
}, [
'💚 ', when(likesCount, likesCount, 'Like')
]),
when(isOwner,
h('a.repost -disabled', [
'📡 ', when(repostsCount, repostsCount, 'Repost')
]),
h('a.repost', {
href: '#',
'ev-click': send(toggleRepost, { reposted, context, post }),
classList: [
when(reposted, '-active')
]
}, [
'📡 ', when(repostsCount, repostsCount, 'Repost')
])
),
when(isOwner,
h('a.edit', { href: '#', 'ev-click': edit }, '✨ Edit')
),
h('a.save', { href: '#', 'ev-click': send(context.actions.saveFile, post) }, '💾 Save'),
TorrentStatusWidget(context, infoHash)
])
])
])
function edit () {
context.actions.editPost({
id: post.id,
item: post()
})
}
}
function toggleRepost (opts) {
if (opts.reposted()) {
opts.context.api.unrepost(opts.post.id)
} else {
opts.context.api.repost(opts.post.id)
}
}
function toggleLike (opts) {
if (opts.liked()) {
opts.context.api.unlike(opts.post.id)
} else {
opts.context.api.like(opts.post.id)
}
}
function SetPositionHook (context, item) {
return function (element) {
element.onmousemove = element.onmousedown = function (ev) {
if (ev.buttons && ev.button === 0) {
var box = ev.currentTarget.getBoundingClientRect()
var x = ev.clientX - box.left
if (x < 5) {
x = 0
}
setPosition(x / box.width * item.duration())
}
}
}
function setPosition (position) {
if (context.player.currentItem.get() === item) {
context.player.audioElement.currentTime = position
}
item.position.set(position)
}
}
function formatTime (value) {
var minutes = Math.floor(value / 60)
var seconds = Math.floor(value % 60)
return minutes + ':' + ('0' + seconds).slice(-2)
}