UNPKG

ferment

Version:

Peer-to-peer audio publishing and streaming application. Like SoundCloud but decentralized. A mashup of ssb, webtorrent and electron.

231 lines (205 loc) 6.61 kB
var h = require('./lib/h') var Value = require('@mmckegg/mutant/value') var computed = require('@mmckegg/mutant/computed') var when = require('@mmckegg/mutant/when') var send = require('@mmckegg/mutant/send') var electron = require('electron') var Player = require('./widgets/player') var onceTrue = require('./lib/once-true') var watch = require('@mmckegg/mutant/watch') var MutantMap = require('@mmckegg/mutant/map') var LatestUpdate = require('./lib/latest-update') var sanitizeFileName = require('sanitize-filename') var views = { discoveryFeed: require('./views/discovery-feed'), followingFeed: require('./views/following-feed'), profile: require('./views/profile'), post: require('./views/audio-post') } module.exports = function (client, config) { var api = require('./api')(client, config) var background = require('./models/background-remote')(config) var currentView = Value(['discoveryFeed']) var backHistory = [] var forwardHistory = [] var canGoForward = Value(false) var canGoBack = Value(false) var latestUpdate = LatestUpdate() window.latestUpdate = latestUpdate var actions = { openEditProfileWindow, openJoinPubWindow, editPost, saveFile (item, cb) { electron.remote.dialog.showSaveDialog(electron.remote.getCurrentWindow(), { title: 'Export Audio File', defaultPath: sanitizeFileName(`${item.author.displayName()} - ${item.title()}.mp3`), filters: [ {name: 'MP3', extensions: ['mp3']} ] }, function (path) { if (path) { background.exportFile(item.audioSrc(), path, cb) } else { cb && cb(false) } }) }, repost (item) { }, viewProfile (id) { actions.setView('profile', id) }, viewPost (id) { actions.setView('post', id) }, setView: function (view, ...args) { var newView = [view, ...args] if (!isSame(newView, currentView())) { canGoForward.set(false) canGoBack.set(true) forwardHistory.length = 0 backHistory.push([currentView(), mainElement.scrollTop]) currentView.set(newView) mainElement.scrollTop = 0 } } } background.stats(x => console.log(x)) var profile = api.getOwnProfile() var discoveryFeed = api.getDiscoveryFeed() var suggestedProfiles = api.getSuggestedProfiles(15) var following = MutantMap(api.rankProfileIds(profile.following), id => api.getProfile(id)) var followingFeed = api.getFollowingFeed() // hold these open watch(suggestedProfiles) watch(discoveryFeed) watch(followingFeed) var context = { config, api, background, actions, discoveryFeed, followingFeed, suggestedProfiles, following, profile } var player = context.player = Player(context) var rootElement = computed(currentView, (data) => { if (Array.isArray(data) && views[data[0]]) { return views[data[0]](context, ...data.slice(1)) } }) var mainElement = h('div.main', [ rootElement ]) onceTrue(api.profilesLoaded, (value) => { // make sure we're connected to the pub that invited us if (!profile.displayName()) { // prompt use to set up profile the first time they open app openEditProfileWindow() } }) electron.ipcRenderer.on('playPause', function () { player.togglePlay() }) electron.ipcRenderer.on('nextTrack', function () { player.playNext() }) electron.ipcRenderer.on('previousTrack', function () { player.playPrevious() }) return h('MainWindow', { classList: [ '-' + process.platform ] }, [ h('div.top', [ h('span.history', [ h('a', { 'ev-click': goBack, classList: [ when(canGoBack, '-active') ] }, '<'), h('a', { 'ev-click': goForward, classList: [ when(canGoForward, '-active') ] }, '>') ]), h('span.nav', [ h('a', { 'ev-click': send(actions.setView, 'discoveryFeed'), classList: [ computed(currentView, (x) => x[0] === 'discoveryFeed' ? '-selected' : null) ] }, 'Discovery'), h('a', { 'ev-click': send(actions.setView, 'followingFeed'), classList: [ computed(currentView, (x) => x[0] === 'followingFeed' ? '-selected' : null) ] }, 'Following') ]), h('span.appTitle', ['Ferment']), h('span', [ h('a', { 'ev-click': send(actions.viewProfile, api.id), title: 'Your Profile', classList: [ computed(currentView, (x) => x[0] === 'profile' && x[1] === api.id ? '-selected' : null) ] }, '😀'), h('a -profile', {href: '#', 'ev-click': openEditProfileWindow}, ['Edit Profile']), h('a -add', {href: '#', 'ev-click': openAddWindow}, ['+ Add Audio']) ]) ]), when(latestUpdate, h('div.info', [ h('a.message -update', { href: 'https://github.com/mmckegg/ferment/releases' }, [ h('strong', ['🎉 Ferment ', latestUpdate, ' has been released.']), ' Click here for more info!' ]) ]) ), mainElement, h('div.bottom', [ player.audioElement ]) ]) // scoped function goBack () { if (backHistory.length) { canGoForward.set(true) forwardHistory.push([currentView(), mainElement.scrollTop]) var item = backHistory.pop() currentView.set(item[0]) setTimeout(() => { mainElement.scrollTop = item[1] }, 50) canGoBack.set(backHistory.length > 0) } } function goForward () { if (forwardHistory.length) { backHistory.push([currentView(), mainElement.scrollTop]) var item = forwardHistory.pop() currentView.set(item[0]) setTimeout(() => { mainElement.scrollTop = item[1] }, 50) canGoForward.set(forwardHistory.length > 0) canGoBack.set(true) } } function openEditProfileWindow () { electron.ipcRenderer.send('open-edit-profile-window', { profile: profile.byMe(), id: api.id }) } function openJoinPubWindow () { electron.ipcRenderer.send('open-join-pub-window') } } function editPost (opts) { if (opts.id && opts.item.type === 'ferment/audio') { electron.ipcRenderer.send('open-add-window', opts) } } function openAddWindow (opts) { electron.ipcRenderer.send('open-add-window') } function isSame (a, b) { if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) { for (var i = 0; i < a.length; i++) { if (a[i] !== b[i]) { return false } } return true } else if (a === b) { return true } }