snapdrop

A Progressive Web App for local file sharing
git clone http://git.hanabi.in/repos/snapdrop.git
Log | Files | Refs | README | LICENSE

commit e756a3fd0c6eba5727a772716529bc2cdd76293c
parent 1bce467a7ce48d32aff53350c6482ab411efea66
Author: Robin Linus <robin_woll@capira.de>
Date:   Wed, 30 Dec 2015 18:07:37 +0100

Squashed commit of the following:

commit 5b7ac6f0f56f3888c01049f08b6b47dbeb3bcfb0
Author: Robin Linus <robin_woll@capira.de>
Date:   Wed Dec 30 18:06:48 2015 +0100

    Clean up about and social links

commit 9c7da37d1e8f58f1c45626289fbab336fc982a0f
Author: Robin Linus <robin_woll@capira.de>
Date:   Wed Dec 30 18:06:35 2015 +0100

    Change Slogan

commit fcea5cfb5c6928acabce44caacc1d75fafdab447
Author: Robin Linus <robin_woll@capira.de>
Date:   Wed Dec 30 18:06:22 2015 +0100

    Add shorturl

commit f09e9e42c30aa7b26df2a5fb00bec653f3ad68e1
Author: Robin Linus <robin_woll@capira.de>
Date:   Wed Dec 30 16:56:55 2015 +0100

    initial

Diffstat:
Mapp/elements/buddy-finder/buddy-finder.html | 14++++++++++++--
Mapp/elements/buddy-finder/personal-avatar.html | 2+-
Mapp/elements/elements.html | 2+-
Aapp/elements/invitation-link/invitation-link-behavior.html | 45+++++++++++++++++++++++++++++++++++++++++++++
Aapp/elements/invitation-link/invitation-link.html | 29+++++++++++++++++++++++++++++
Mapp/elements/p2p-network/p2p-network.html | 90+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Aapp/elements/p2p-network/tab-active.html | 25+++++++++++++++++++++++++
Mapp/elements/p2p-network/web-socket.html | 95++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Aapp/elements/x-cards/about-page.html | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dapp/elements/x-cards/x-card.html | 163-------------------------------------------------------------------------------
Mapp/elements/x-cards/x-cards.html | 30------------------------------
Mapp/index.html | 4++--
12 files changed, 430 insertions(+), 280 deletions(-)

diff --git a/app/elements/buddy-finder/buddy-finder.html b/app/elements/buddy-finder/buddy-finder.html @@ -41,9 +41,18 @@ .explanation { @apply(--paper-font-headline); - color: #4285f4; + color: #4E4E4E; text-align: center; } + + .short { + @apply(--paper-font-body1); + color: #333; + } + + .url { + color: #4285f4; + } </style> <div class="buddies"> <template is="dom-repeat" items="{{buddies}}"> @@ -53,7 +62,8 @@ </template> </div> <div hidden$="{{buddies.0}}" class="explanation"> - Open this page on other devices<br> to send files. + Open <span class="url">dropc.at</span> on other devices + <br> to send files. </div> <personal-avatar class="me"></personal-avatar> </template> diff --git a/app/elements/buddy-finder/personal-avatar.html b/app/elements/buddy-finder/personal-avatar.html @@ -26,7 +26,7 @@ </style> <iron-icon icon="chat:wifi-tethering"></iron-icon> <div class="paper-font-body1"> - Snapdrop lets you share instantly with people near by. + The easiest way to transfer files across devices. </div> <div class="paper-font-body1 discover"> Allow me to be discovered by: Everyone in this network. diff --git a/app/elements/elements.html b/app/elements/elements.html @@ -8,7 +8,7 @@ --> <!-- Add your elements here --> <link rel="import" href="../styles/app-theme.html"> -<link rel="import" href="x-cards/x-card.html"> +<link rel="import" href="x-cards/about-page.html"> <link rel="import" href="x-cards/x-cards.html"> <link rel="import" href="buddy-finder/buddy-finder.html"> <link rel="import" href="p2p-network/connection-wrapper.html"> diff --git a/app/elements/invitation-link/invitation-link-behavior.html b/app/elements/invitation-link/invitation-link-behavior.html @@ -0,0 +1,45 @@ +<script> +'use strict'; +window.Chat = window.Chat || {}; +Chat.InvitationLinkBehavior = { + properties: { + contact: { + type: String + } + }, + _copy: function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + Polymer.Base.create('textarea'); + var copyTextarea = this.textarea; + copyTextarea.value = this.link; + copyTextarea.select(); + try { + var successful = document.execCommand('copy'); + if (successful) { + app.displayToast('Copied invitation link to clipboard. Share it to send files to friends!'); + } + } catch (err) { + console.log('Oops, unable to copy', err); + } + copyTextarea.blur(); + }, + get link() { + return 'http://' + window.location.host + '/' + this.contact; + }, + get textarea() { + var textarea = document.querySelector('#copytextarea'); + if (!textarea) { + textarea = Polymer.Base.create('textarea'); + textarea.id = 'copytextarea'; + var style = textarea.style; + style.position = 'absolute'; + style.top = '-10000px'; + document.body.appendChild(textarea); + } + return textarea; + } +}; +</script> diff --git a/app/elements/invitation-link/invitation-link.html b/app/elements/invitation-link/invitation-link.html @@ -0,0 +1,29 @@ +<link rel="import" href="invitation-link-behavior.html"> +<link rel="import" href="../../bower_components/paper-tooltip/paper-tooltip.html"> +<dom-module id="invitation-link"> + <template> + <style> + :host { + display: block; + position: absolute; + top: 16px; + left: 16px; + z-index: 3; + } + </style> + <paper-icon-button icon="chat:share" on-tap="_copy" id="btn"></paper-icon-button> + <paper-tooltip + for="btn" + position="bottom" + offset="14"> + Get an Invitation Link to send files accross different networks. + </paper-tooltip> + </template> + <script> + 'use strict'; + Polymer({ + is: 'invitation-link', + behaviors: [Chat.InvitationLinkBehavior] + }); + </script> +</dom-module> diff --git a/app/elements/p2p-network/p2p-network.html b/app/elements/p2p-network/p2p-network.html @@ -23,47 +23,49 @@ }.bind(this); }, initialize: function() { - var options; - if (window.debug) { - options = { - host: window.location.hostname, - port: 3002, - path: 'peerjs' - }; - } else { - options = { - host: 'snapdrop.net', - port: 443, - path: 'peerjs', - secure: true - }; - } - this._peer = new Peer(this.me, options); - this._peer.on('open', function(id) { - console.log('My peer ID is: ' + id); - this.set('me', id); - this._peerOpen = true; - this._initCallbacks.forEach(function(cb) { - cb(); - }); - }.bind(this)); - - this._peer.on('connection', this.connect.bind(this)); - this._peer.on('error', function(err) { - console.error(err); - //ugly hack to find out error type - if (err.message.indexOf('Could not connect to peer') > -1) { - delete this._connectedPeers[this.peer]; - this.set('peer', 'error'); - return; - } - if (err.message.indexOf('Lost connection to server') > -1) { - this._peer.destroy(); - this.set('me', this.me); - this.async(this._initialize, 3000); - return; + if (window.isActive) { + clearInterval(this.reconnectTimer); + this.reconnectTimer = undefined; + var options; + if (window.debug) { + options = { + host: window.location.hostname, + port: 3002, + path: 'peerjs' + }; + } else { + options = { + host: 'snapdrop.net', + port: 443, + path: 'peerjs', + secure: true + }; } - }.bind(this)); + this._peer = new Peer(this.me, options); + this._peer.on('open', function(id) { + console.log('My peer ID is: ' + id); + this.set('me', id); + this._peerOpen = true; + this._initCallbacks.forEach(function(cb) { + cb(); + }); + }.bind(this)); + + this._peer.on('connection', this.connect.bind(this)); + this._peer.on('error', function(err) { + console.error(err); + //ugly hack to find out error type + if (err.message.indexOf('Could not connect to peer') > -1) { + delete this._connectedPeers[this.peer]; + return; + } + if (err.message.indexOf('Lost connection to server') > -1) { + this._peer.destroy(); + this._reconnect(); + return; + } + }.bind(this)); + } }, connect: function(c) { @@ -145,7 +147,7 @@ conns.forEach(function(conn) { if (conn.label === 'file') { conn.send(file); - console.log('file send via WebRTC'); + console.log('send file via WebRTC'); } }.bind(this)); } @@ -159,6 +161,12 @@ } }.bind(this)); } + }, + _reconnect: function(e) { + //try to reconnect after 3s + if (!this.reconnectTimer) { + this.reconnectTimer = setInterval(this.initialize.bind(this), 3000); + } } }); </script> diff --git a/app/elements/p2p-network/tab-active.html b/app/elements/p2p-network/tab-active.html @@ -0,0 +1,25 @@ +<script> +'use strict'; +var vis = (function() { + var stateKey, + eventKey, + keys = { + hidden: "visibilitychange", + webkitHidden: "webkitvisibilitychange", + mozHidden: "mozvisibilitychange", + msHidden: "msvisibilitychange" + }; + for (stateKey in keys) { + if (stateKey in document) { + eventKey = keys[stateKey]; + break; + } + } + return function(c) { + if (c) { + document.addEventListener(eventKey, c); + } + return !document[stateKey]; + }; +})(); +</script> diff --git a/app/elements/p2p-network/web-socket.html b/app/elements/p2p-network/web-socket.html @@ -9,55 +9,68 @@ </template> <script> 'use strict'; + window.isActive = true; + + window.onfocus = function() { + window.isActive = true; + }; + + window.onblur = function() { + window.isActive = false; + }; + Polymer({ is: 'web-socket', attached: function() { this.init(); }, init: function() { - var websocketUrl = (window.debug ? 'ws://' + window.location.hostname + ':3002' : 'wss://snapdrop.net') + '/binary'; - this.client = new BinaryClient(websocketUrl); - this.client.on('stream', function(stream, meta) { - // collect stream data - var parts = []; - stream.on('data', function(data) { - //console.log('part received', meta, data); - if (data.isSystemEvent) { - if (meta) { - data.from = meta.from; + if (window.isActive) { + clearInterval(this.reconnectTimer); + this.reconnectTimer = undefined; + var websocketUrl = (window.debug ? 'ws://' + window.location.hostname + ':3002' : 'wss://snapdrop.net') + '/binary'; + this.client = new BinaryClient(websocketUrl); + this.client.on('stream', function(stream, meta) { + // collect stream data + var parts = []; + stream.on('data', function(data) { + //console.log('part received', meta, data); + if (data.isSystemEvent) { + if (meta) { + data.from = meta.from; + } + this.fire('system-event', data); + } else { + parts.push(data); } - this.fire('system-event', data); - } else { - parts.push(data); - } + }.bind(this)); + // when finished, set it as the background image + stream.on('end', function() { + var blob = new Blob(parts, { + type: meta.type + }); + console.log('file received', blob, meta); + this.fire('file-received', { + blob: blob, + name: meta.name, + from: meta.from + }); + }.bind(this)); }.bind(this)); - // when finished, set it as the background image - stream.on('end', function() { - var blob = new Blob(parts, { - type: meta.type - }); - console.log('file received', blob, meta); - this.fire('file-received', { - blob: blob, - name: meta.name, - from: meta.from + this.client.on('open', function(e) { + console.log(e); + this.client.send({}, { + serverMsg: 'rtc-support', + rtc: window.webRTCSupported }); }.bind(this)); - }.bind(this)); - this.client.on('open', function(e) { - this.cancelAsync(this.reconnectTimer); - console.log(e); - this.client.send({}, { - serverMsg: 'rtc-support', - rtc: window.webRTCSupported - }); - }.bind(this)); - this.client.on('error', function(e) { - this._reconnect(e); - }.bind(this)); - this.client.on('close', function(e) { - this._reconnect(e); - }.bind(this)); + this.client.on('error', function(e) { + this._reconnect(e); + }.bind(this)); + this.client.on('close', function(e) { + this._reconnect(e); + }.bind(this)); + } }, _sendFile: function(toPeer, file) { console.log('send file via WebSocket', file); @@ -80,7 +93,9 @@ _reconnect: function(e) { console.log('disconnected', e); //try to reconnect after 3s - this.reconnectTimer = this.async(this.init, 3000); + if (!this.reconnectTimer) { + this.reconnectTimer = setInterval(this.init.bind(this), 3000); + } } }); </script> diff --git a/app/elements/x-cards/about-page.html b/app/elements/x-cards/about-page.html @@ -0,0 +1,211 @@ +<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout.html"> +<link rel="import" href="../../bower_components/neon-animation/neon-shared-element-animatable-behavior.html"> +<link rel="import" href="../../bower_components/neon-animation/neon-animations.html"> +<link rel="import" href="../../bower_components/paper-styles/paper-styles-classes.html"> +<link rel="import" href="../../bower_components/iron-icon/iron-icon.html"> +<dom-module id="about-page"> + <template> + <style> + :host { + display: block; + overflow: hidden; + color: white; + z-index: 3 + } + + #placeholder { + opacity: 0; + background-color: #4285f4; + @apply(--layout-fit); + } + /* paper-icon-button { + position: absolute; + top: 16px; + right: 16px; + z-index: 2; + }*/ + + #container { + @apply(--layout-fit); + @apply(--layout-vertical); + @apply(--layout-center-center); + background-color: #4285f4; + padding: 64px 32px 64px 32px; + box-sizing: border-box; + } + + .logo { + width: 80px; + height: 80px; + } + + .slogan { + text-align: center; + } + + .paper-font-headline { + margin-bottom: 8px; + font-size: 32px; + } + + a { + text-decoration: none; + color: white; + } + + .center { + @apply(--layout-vertical); + @apply(--layout-center-center); + } + + #footer { + position: absolute; + left: 50%; + margin-left: -160px; + width: 320px; + bottom: 24px; + text-align: center; + } + + a { + display: inline-block; + text-align: center; + } + + .donate-icon { + padding-top: 32px; + width: 32px; + height: 32px; + } + + .paper-font-subhead { + font-size: 20px; + } + + a.paper-font-subhead { + padding-top: 32px; + } + + .share { + position: absolute; + top: 16px; + right: 16px; + z-index: 10; + } + + #btn, + .share a { + color: #52524F; + } + + .share a { + text-decoration: none; + padding-left: 8px; + padding-right: 8px; + } + + .share a:hover, + #btn:hover { + color: white; + } + </style> + <div id="placeholder"></div> + <div id="container"> + <div class="share"> + <a href="https://twitter.com/snapdrop42" target="_blank"> + <iron-icon icon="chat:twitter"></iron-icon> + <paper-tooltip for="" position="bottom" offset="14"> + Contact us on Twitter! + </paper-tooltip> + </a> + <a href="https://www.facebook.com/snapdrop.net/" target="_blank"> + <iron-icon icon="chat:facebook"></iron-icon> + <paper-tooltip for="" position="bottom" offset="14"> + Contact us on Facebook! + </paper-tooltip> + </a> + <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FDAHZJH3228D6" target="_blank"> + <iron-icon icon="chat:local-cafe"></iron-icon> + <paper-tooltip for="" position="bottom" offset="14"> + You like Snapdrop? + <br> Buy me a cup of coffee! + </paper-tooltip> + </a> + <a href="https://github.com/capira12/snapdrop" target="_blank" class="github"> + <iron-icon icon="chat:github"></iron-icon> + <paper-tooltip for="" position="bottom" offset="14"> + Get involved! + </paper-tooltip> + </a> + <paper-icon-button id="btn" icon="chat:close" on-tap="_switch"></paper-icon-button> + </div> + <div class="center"> + <iron-icon icon="chat:wifi-tethering" class="logo"></iron-icon> + <div class="paper-font-headline">Snapdrop</div> + <div class="slogan">The easiest way to transfer files across devices.</div> + <a class="paper-font-subhead" href="https://github.com/capira12/snapdrop/blob/master/faq.md" target="_blank">Frequently Asked Questions</a> + </div> + <span id="footer">Built with &#9829; by <a href="https://twitter.com/capira42" target="_blank">Robin Linus</a></span> + </div> + </template> +</dom-module> +<script> +(function() { + Polymer({ + is: 'about-page', + behaviors: [ + Polymer.NeonSharedElementAnimatableBehavior + ], + properties: { + animationConfig: { + value: function() { + return { + 'entry': [{ + name: 'ripple-animation', + id: 'ripple', + toPage: this + }, { + name: 'fade-out-animation', + node: this.$.placeholder, + timing: { + delay: 250 + } + }, { + name: 'fade-in-animation', + node: this.$.container, + timing: { + delay: 50 + } + }], + 'exit': [{ + name: 'opaque-animation', + node: this.$.placeholder + }, { + name: 'fade-out-animation', + node: this.$.container, + timing: { + duration: 0 + } + }, { + name: 'reverse-ripple-animation', + id: 'reverse-ripple', + fromPage: this + }] + }; + } + }, + sharedElements: { + value: function() { + return { + 'ripple': this.$.placeholder, + 'reverse-ripple': this.$.placeholder + }; + } + } + }, + _switch: function() { + document.querySelector('#pages').select(0); + } + }); +})(); +</script> diff --git a/app/elements/x-cards/x-card.html b/app/elements/x-cards/x-card.html @@ -1,163 +0,0 @@ -<link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout.html"> -<link rel="import" href="../../bower_components/neon-animation/neon-shared-element-animatable-behavior.html"> -<link rel="import" href="../../bower_components/neon-animation/neon-animations.html"> -<link rel="import" href="../../bower_components/paper-styles/paper-styles-classes.html"> -<link rel="import" href="../../bower_components/iron-icon/iron-icon.html"> -<dom-module id="x-card"> - <template> - <style> - :host { - display: block; - overflow: hidden; - color: white; - z-index: 3 - } - - #placeholder { - opacity: 0; - background-color: #4285f4; - @apply(--layout-fit); - } - - paper-icon-button { - position: absolute; - top: 16px; - right: 16px; - z-index: 2; - } - - #container { - @apply(--layout-fit); - @apply(--layout-vertical); - @apply(--layout-center-center); - background-color: #4285f4; - padding: 64px 32px 64px 32px; - box-sizing: border-box; - } - - - .logo { - width: 80px; - height: 80px; - } - - .slogan{ - text-align: center; - } - - .paper-font-headline { - margin-bottom: 8px; - font-size: 32px; - } - - a { - text-decoration: none; - color: white; - } - - .center { - @apply(--layout-vertical); - @apply(--layout-center-center); - } - - #footer { - position: absolute; - left: 50%; - margin-left: -160px; - width: 320px; - bottom: 24px; - text-align: center; - } - - a { - display: inline-block; - text-align: center; - } - - .donate-icon { - padding-top: 32px; - width: 32px; - height: 32px; - } - - .paper-font-subhead { - font-size: 20px; - } - - a.paper-font-subhead { - padding-top: 32px; - } - </style> - <paper-icon-button id="btn" icon="chat:close" on-tap="_switch"></paper-icon-button> - <div id="placeholder"></div> - <div id="container"> - <div class="center"> - <iron-icon icon="chat:wifi-tethering" class="logo"></iron-icon> - <div class="paper-font-headline">Snapdrop</div> - <div class="slogan">The easiest way to transfer files accross devices.</div> - <a class="paper-font-subhead" href="https://github.com/capira12/snapdrop/blob/master/faq.md" target="_blank">Frequently Asked Questions</a> - </div> - <span id="footer">Built with &#9829; by <a href="https://twitter.com/snapdrop42" target="_blank">Robin Linus</a></span> - </div> - </template> -</dom-module> -<script> -(function() { - Polymer({ - is: 'x-card', - behaviors: [ - Polymer.NeonSharedElementAnimatableBehavior - ], - properties: { - animationConfig: { - value: function() { - return { - 'entry': [{ - name: 'ripple-animation', - id: 'ripple', - toPage: this - }, { - name: 'fade-out-animation', - node: this.$.placeholder, - timing: { - delay: 250 - } - }, { - name: 'fade-in-animation', - node: this.$.container, - timing: { - delay: 50 - } - }], - 'exit': [{ - name: 'opaque-animation', - node: this.$.placeholder - }, { - name: 'fade-out-animation', - node: this.$.container, - timing: { - duration: 0 - } - }, { - name: 'reverse-ripple-animation', - id: 'reverse-ripple', - fromPage: this - }] - }; - } - }, - sharedElements: { - value: function() { - return { - 'ripple': this.$.placeholder, - 'reverse-ripple': this.$.placeholder - }; - } - } - }, - _switch: function() { - document.querySelector('#pages').select(0); - } - }); -})(); -</script> diff --git a/app/elements/x-cards/x-cards.html b/app/elements/x-cards/x-cards.html @@ -47,40 +47,10 @@ color: #4285f4; } - @media all and (max-height: 640px) { - .github { - display: none; - } - } </style> <div id="placeholder"></div> <div id="container"> <div class="share"> - <a href="https://twitter.com/snapdrop42" target="_blank"> - <iron-icon icon="chat:twitter"></iron-icon> - <paper-tooltip for="" position="bottom" offset="14"> - Contact us on Twitter! - </paper-tooltip> - </a> - <a href="https://www.facebook.com/snapdrop.net/" target="_blank"> - <iron-icon icon="chat:facebook"></iron-icon> - <paper-tooltip for="" position="bottom" offset="14"> - Like us on Facebook! - </paper-tooltip> - </a> - <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FDAHZJH3228D6" target="_blank"> - <iron-icon icon="chat:local-cafe"></iron-icon> - <paper-tooltip for="" position="bottom" offset="14"> - You like Snapdrop? - <br> Buy me a cup of coffee! - </paper-tooltip> - </a> - <a href="https://github.com/capira12/snapdrop" target="_blank" class="github"> - <iron-icon icon="chat:github" ></iron-icon> - <paper-tooltip for="" position="bottom" offset="14"> - Get involved! - </paper-tooltip> - </a> <paper-icon-button id="btn" icon="chat:info-outline" on-tap="_switch"></paper-icon-button> </div> <content select="div"></content> diff --git a/app/index.html b/app/index.html @@ -56,8 +56,8 @@ <buddy-finder me="{{me}}" active$="{{loading}}" buddies="{{buddies}}"></buddy-finder> </div> </x-cards> - <x-card on-switch="_showApp"> - </x-card> + <about-page on-switch="_showApp"> + </about-page> </neon-animated-pages> <file-receiver></file-receiver> <paper-toast id="toast" duration="6000">