function AuthenticationHelp (el) { delegate.on(el, 'button', 'click', window.close) githubCurrentUser.verify(function (err, verified, username) { if (err) { var errorWrapperEl = el.querySelector('#error-wrapper') var errorEl = el.querySelector('#error') errorWrapperEl.style.display = 'block' errorEl.innerHTML = err } var usernameEl = el.querySelector('#username') var validKeyEl = el.querySelector('#valid-key') usernameEl.innerHTML = username || 'unknown' if (typeof verified !== 'undefined') { validKeyEl.innerHTML = verified } else { validKeyEl.innerHTML = '' } }) }
function App (el) { var self = this if (!(self instanceof App)) return new App(el) var swarm = window.swarm = Swarm() var verify user.verify(function (err, verified, username) { if (err) return console.error(err.message || err) if (verified) { self.data.username = username swarm.username = username verify = ghsign.verifier(username) } else { self.data.username = '******' + catNames.random() + ')' } }) var channelsFound = {} swarm.log.ready(function () { var usersFound = {} var verifiers = {} channelsFound.friends = { id: 0, name: 'friends', active: true, messages: [] } self.data.channels.push(channelsFound.friends) self.data.messages = channelsFound.friends.messages var logStream = swarm.log.createReadStream({ live: true, since: Math.max(swarm.log.changes - 500, 0) }) logStream.pipe(through(function (entry, _, next) { var basicMessage = JSON.parse(entry.value) var userVerify = verifiers[basicMessage.username] if (!userVerify && basicMessage.sig) userVerify = verifiers[basicMessage.username] = ghsign.verifier(basicMessage.username) if (userVerify && basicMessage.sig) { var msg = Buffer.concat([ new Buffer(basicMessage.username), new Buffer(basicMessage.channel ? basicMessage.channel: ''), new Buffer(basicMessage.text), new Buffer(basicMessage.timestamp.toString()) ]) return userVerify(msg, new Buffer(basicMessage.sig, 'base64'), function (err, valid) { basicMessage.valid = valid; next(null, basicMessage); }); } next(null, basicMessage); })).pipe(through(function (basicMessage, _, next) { var message = richMessage(basicMessage) var channelName = message.channel || 'friends' var channel = channelsFound[channelName] if (!channel) { channel = channelsFound[channelName] = { id: self.data.channels.length, name: channelName, active: false, messages: [] } self.data.channels.push(channel) } var anon = /Anonymous/i.test(message.username) message.avatar = anon ? 'static/Icon.png' : 'https://github.com/' + message.username + '.png' message.timeago = moment(message.timestamp).fromNow() if (self.data.username && !currentWindow.isFocused()) { console.log(message.rawText) if (message.rawText.indexOf(self.data.username) > -1) { new Notification('Mentioned in #' + channel.name, { // eslint-disable-line body: message.username + ': ' + message.rawText.slice(0, 20) }) self.setBadge() } } channel.messages.push(message) if (!message.anon && message.valid && !usersFound[message.username]) { usersFound[message.username] = true self.data.users.push({ name: message.username, avatar: message.avatar }) } if (!message.anon && !message.valid) { message.username = '******' + message.username } self.views.messages.scrollToBottom() next(); })) }) swarm.on('peer', function (p) { self.data.peers++ eos(p, function () { self.data.peers-- }) }) // The mock data model self.data = { peers: 0, username: '******' + catNames.random() + ')', channels: [], messages: [], users: [] } // View instances used in our App self.views = { channels: new Channels(self), composer: new Composer(self), messages: new Messages(self), users: new Users(self), peers: new Peers(self), status: new Status(self) } // Initial DOM tree render var tree = self.render() var rootNode = createElement(tree) el.appendChild(rootNode) // Main render loop raf(function tick () { var newTree = self.render() var patches = diff(tree, newTree) rootNode = patch(rootNode, patches) tree = newTree raf(tick) }) self.on('selectChannel', function (selectedChannel) { self.data.channels.forEach(function (channel) { channel.active = (selectedChannel === channel) if (channel.active) self.data.messages = channel.messages }) self.views.messages.scrollToBottom() }) self.on('sendMessage', function (text) { text = text.trim() if (text.length === 0) return var activeChannel = self.data.channels.reduce(function (a, b) { return a && a.active ? a : b }, null) swarm.send({ username: self.data.username, channel: activeChannel && activeChannel.name, text: text, timestamp: Date.now() }) }) self.on('addChannel', function (channelName) { if (!channelsFound[channelName]) { var channel = channelsFound[channelName] = { name: channelName, id: self.data.channels.length, active: false, messages: [] } self.data.channels.push(channel) } }) // Update friendly "timeago" time string (once per minute) setInterval(function () { self.data.messages.forEach(function (message) { message.timeago = moment(message.timestamp).fromNow() }) }, 60 * 1000) }
function App (el) { var self = this if (!(self instanceof App)) return new App(el) var db = levelup('./friendsdb', {db: leveldown}) db.channels = subleveldown(db, 'channels', {valueEncoding: 'json'}) // Open links in user's default browser delegate.on(el, 'a', 'click', function (e) { var href = e.target.getAttribute('href') if (/^https?:/.test(href)) { e.preventDefault() shell.openExternal(href) } }) // The mock data model self.data = { peers: 0, username: '******' + catNames.random() + ')', channels: [], messages: [], users: [] } var swarm = window.swarm = Swarm(subleveldown(db, 'swarm')) githubCurrentUser.verify(function (err, verified, username) { if (err) return console.error(err.message || err) if (verified) { self.data.username = username swarm.username = username ghsign.verifier(username) } }) var channelsFound = {} var usersFound = {} var verifiers = {} swarm.process(function (entry, cb) { var basicMessage = JSON.parse(entry.value) var userVerify = verifiers[basicMessage.username] var onverify = function () { var message = richMessage(basicMessage) var channelName = message.channel || 'friends' var channel = channelsFound[channelName] if (!channel) { channel = channelsFound[channelName] = { id: self.data.channels.length, name: channelName, active: false, peers: 0, messages: [] } self.data.channels.push(channel) self.data.activeChannel = channel } var anon = /Anonymous/i.test(message.username) message.avatar = anon ? 'static/Icon.png' : 'https://github.com/' + message.username + '.png' message.timeago = util.timeago(message.timestamp) if (self.data.username && !currentWindow.isFocused()) { console.log(message.rawText) if (message.rawText.indexOf(self.data.username) > -1) { new Notification('Mentioned in #' + channel.name, { // eslint-disable-line body: message.username + ': ' + message.rawText.slice(0, 20) }) self.setBadge() } } channel.messages.push(message) if (!message.anon && message.valid && !usersFound[message.username]) { usersFound[message.username] = true self.data.users.push({ name: message.username, avatar: message.avatar }) } if (!message.anon && !message.valid) { message.username = '******' + message.username } self.views.messages.scrollToBottom() cb() } if (!userVerify && basicMessage.sig) userVerify = verifiers[basicMessage.username] = ghsign.verifier(basicMessage.username) if (userVerify && basicMessage.sig) { var msg = Buffer.concat([ new Buffer(basicMessage.username), new Buffer(basicMessage.channel ? basicMessage.channel : ''), new Buffer(basicMessage.text), new Buffer(basicMessage.timestamp.toString()) ]) userVerify(msg, new Buffer(basicMessage.sig, 'base64'), function (err, valid) { if (err) basicMessage.valid = false basicMessage.valid = valid onverify() }) return } onverify() }) swarm.on('peer', function (p, channel) { var ch = channelsFound[channel] if (ch) ch.peers++ self.data.peers++ eos(p, function () { if (ch) ch.peers-- self.data.peers-- }) }) channelsFound.friends = { id: 0, name: 'friends', active: true, peers: 0, messages: [] } self.data.channels.push(channelsFound.friends) self.data.messages = channelsFound.friends.messages self.data.activeChannel = channelsFound.friends // View instances used in our App self.views = { channels: new Channels(self), composer: new Composer(self), messages: new Messages(self), users: new Users(self), peers: new Peers(self), status: new Status(self) } // Initial DOM tree render var tree = self.render() var rootNode = createElement(tree) el.appendChild(rootNode) // Main render loop raf(function tick () { var newTree = self.render() var patches = diff(tree, newTree) rootNode = patch(rootNode, patches) tree = newTree raf(tick) }) self.on('selectChannel', function (selectedChannel) { self.data.channels.forEach(function (channel) { channel.active = (selectedChannel === channel) if (channel.active) { self.data.messages = channel.messages self.data.activeChannel = channel if (channel.name !== 'friends') db.channels.put(channel.name, {name: channel.name, id: channel.id}) } }) self.views.messages.scrollToBottom() }) self.on('sendMessage', function (text) { text = text.trim() if (text.length === 0) return swarm.send({ username: self.data.username, channel: self.data.activeChannel && self.data.activeChannel.name, text: text, timestamp: Date.now() }) }) self.on('addChannel', function (channelName) { if (channelName[0] === '#') channelName = channelName.substring(1) if (!channelsFound[channelName]) { var channel = channelsFound[channelName] = { name: channelName, id: self.data.channels.length, peers: 0, active: false, messages: [] } self.data.channels.push(channel) swarm.addChannel(channelName) db.channels.put(channelName, {name: channelName, id: self.data.channels.length}) } }) self.on('leaveChannel', function (channelName) { if (channelName === 'friends') return // can't leave friends for now db.channels.del(channelName, function () { var channel = channelsFound[channelName] if (!channel) return var i = self.data.channels.indexOf(channel) if (i > -1) self.data.channels.splice(i, 1) delete channelsFound[channelName] swarm.removeChannel(channelName) self.emit('selectChannel', channelsFound.friends) }) }) // Update friendly "timeago" time string (once per minute) setInterval(function () { self.data.activeChannel.messages.forEach(function (message) { message.timeago = util.timeago(message.timestamp) }) }, 60 * 1000) db.channels.createValueStream() .on('data', function (data) { data.messages = [] data.peers = 0 self.data.channels.push(data) channelsFound[data.name] = data swarm.addChannel(data.name) }) }
function App (el) { var self = this if (!(self instanceof App)) return new App(el) self._notifications = 0 var db = levelup('./friendsdb', {db: leveldown}) db.channels = subleveldown(db, 'channels', {valueEncoding: 'json'}) // Open links in user's default browser delegate.on(el, 'a', 'click', function (e) { var href = e.target.getAttribute('href') if (/^https?:/.test(href)) { e.preventDefault() shell.openExternal(href) } else if (/^#/.test(href)) { self.emit('addChannel', href) } }) // The mock data model self.data = { peers: 0, username: '******' + catNames.random() + ')', channels: [], messages: [], users: [], activeChannel: null } var swarm = window.swarm = Swarm(subleveldown(db, 'swarm'), {maxPeers: 20}) var channelsFound = {} var usersFound = {} var changesOffsets = {} // join default channel swarm.addChannel('friends') githubCurrentUser.verify(function (err, verified, username) { if (err || !verified) self.showGitHelp() if (err) return console.error(err.message || err) if (verified) { self.data.username = username swarm.username = username // Re-create rich messages after we know our username, since we can now do // highlights correctly. self.data.messages = self.data.messages.map(function (message) { return richMessage(message, self.data.username) }) render() } }) // clear notifications on focus. TODO: only clear notifications in current channel when we have that currentWindow.on('focus', function () { self.setBadge(false) }) swarm.process(function (basicMessage, cb) { var message = richMessage(basicMessage, self.data.username) var channelName = message.channel || 'friends' var channel = channelsFound[channelName] if (!channel) { channel = channelsFound[channelName] = { id: self.data.channels.length, name: channelName, active: false, peers: 0, messages: [] } self.data.channels.push(channel) self.data.activeChannel = channel } if (!changesOffsets[channel.name]) changesOffsets[channel.name] = swarm.changes(channel.name) if (self.data.username && !currentWindow.isFocused()) { if (message.text.indexOf(self.data.username) > -1) { new Notification('Mentioned in #' + channel.name, { // eslint-disable-line body: message.username + ': ' + message.text.slice(0, 20) }) self.setBadge() } } var lastMessage = channel.messages[channel.messages.length - 1] if (lastMessage && lastMessage.username === message.username) { // Last message came from same user, so merge into the last message message = richMessage.mergeMessages(lastMessage, message) } else { channel.messages.push(message) } if (!message.anon && message.valid && !usersFound[message.username]) { usersFound[message.username] = true self.data.users[message.username] = { avatar: message.avatar, blocked: false } // Add user names to available autocompletes self.views.composer.autocompletes.push(message.username) } if (!message.anon && !message.valid) { message.username = '******' + message.username } if (changesOffsets[channel.name] <= basicMessage.change) { render() self.views.messages.scrollToBottom() } cb() }) swarm.on('peer', function (p, channel) { var ch = channelsFound[channel] if (ch) ch.peers++ self.data.peers++ render() eos(p, function () { if (ch) ch.peers-- self.data.peers-- render() }) }) channelsFound.friends = { id: 0, name: 'friends', active: true, peers: 0, messages: [] } self.data.channels.push(channelsFound.friends) self.data.messages = channelsFound.friends.messages self.data.activeChannel = channelsFound.friends // View instances used in our App self.views = { channels: new Channels(self), composer: new Composer(self), messages: new Messages(self), status: new Status(self), users: new Users(self) } // Initial DOM tree render var tree = self.render() var rootNode = createElement(tree) el.appendChild(rootNode) function render () { var newTree = self.render() var patches = diff(tree, newTree) rootNode = patch(rootNode, patches) tree = newTree } self.on('render', render) self.on('selectChannel', function (channelName) { self.data.channels.forEach(function (channel) { channel.active = (channelName === channel.name) if (channel.active) { self.data.messages = channel.messages self.data.activeChannel = channel if (channel.name !== 'friends') db.channels.put(channel.name, {name: channel.name, id: channel.id}) } }) render() self.views.composer.focus() self.views.messages.scrollToBottom() }) self.on('sendMessage', function (text) { text = text.trim() if (text.length === 0) return swarm.send({ username: self.data.username, channel: self.data.activeChannel && self.data.activeChannel.name, text: text, timestamp: Date.now() }) }) self.on('executeCommand', function (commandStr) { var words = commandStr.split(' ') var command = words[0].substring(1, words[0].length).toLowerCase() switch (command) { case 'join': words.shift() var channel = words.join(' ') self.emit('addChannel', channel) break case 'wc': case 'part': case 'leave': self.emit('leaveChannel', self.data.activeChannel.name) break default: console.log('Unrecognized /command: ' + command) self.emit('sendMessage', commandStr) break } }) self.on('addChannel', function (channelName) { if (channelName[0] === '#') channelName = channelName.substring(1) if (channelName.length === 0) return if (!channelsFound[channelName]) { var channel = channelsFound[channelName] = { name: channelName, id: self.data.channels.length, peers: 0, active: false, messages: [] } self.data.channels.push(channel) swarm.addChannel(channelName) db.channels.put(channelName, { name: channelName, id: self.data.channels.length }) } self.emit('selectChannel', channelName) }) self.on('leaveChannel', function (channelName) { if (channelName === 'friends') return // can't leave friends for now db.channels.del(channelName, function () { var channel = channelsFound[channelName] if (!channel) return var i = self.data.channels.indexOf(channel) if (i > -1) self.data.channels.splice(i, 1) delete channelsFound[channelName] swarm.removeChannel(channelName) self.emit('selectChannel', 'friends') render() }) }) self.on('toggleBlockUser', function (username) { var user = self.data.users[username] if (user) user.blocked = !user.blocked render() self.views.messages.scrollToBottom(true) }) self.on('resizeComposer', function (height) { self.views.messages.notifyComposerHeight(height) render() }) // Update friendly "timeago" time string (once per minute) setInterval(function () { self.data.activeChannel.messages.forEach(function (message) { message.timeago = util.timeago(message.timestamp) }) }, 60 * 1000) db.channels.createValueStream() .on('data', function (data) { data.messages = [] data.peers = 0 self.data.channels.push(data) channelsFound[data.name] = data swarm.addChannel(data.name) }) .on('end', function () { render() }) }