Lots of small improvements, websockets fallback

This commit is contained in:
Robin Linus 2015-12-23 13:57:13 +01:00
parent e5eab64c6b
commit 22be7c5cb9
35 changed files with 2672 additions and 916 deletions

View file

@ -1,97 +1,100 @@
<link rel="import" href="../../../bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="../../../bower_components/paper-styles/paper-styles.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="../../bower_components/paper-styles/paper-styles.html">
<link rel="import" href="../file-sharing/file-input.html">
<link rel="import" href="user-avatar.html">
<link rel="import" href="personal-avatar.html">
<dom-module id="buddy-finder">
<template>
<style>
:host {
display: block;
background-color: white;
background-color: transparent;
@apply(--layout-fit);
@apply(--layout-horizontal);
@apply(--layout-center-center);
overflow: hidden;
}
.paper-font-display1 {
color: black;
text-align: center;
margin-bottom: 16px;
display: none;
position: relative;
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
margin: 0;
}
.buddies {
z-index: 1;
@apply(--layout-horizontal);
@apply(--layout-center-center);
@apply(--layout-wrap);
}
.buddy {
cursor: pointer;
}
.circles {
position: absolute;
bottom: -50px;
left: 50%;
width: 1140px;
margin-left: -570px;
height: 700px;
transform-origin: 570px 570px;
animation: grow 1.5s ease-out;
fill: transparent;
}
.me {
position: absolute;
bottom: 30px;
bottom: 24px;
left: 50%;
margin-left: -60px;
margin-left: -180px;
}
.explanation {
@apply(--paper-font-headline);
color: #4285f4;
text-align: center;
}
</style>
<div class="paper-font-display1">People near by</div>
<div class="buddies">
<template is="dom-repeat" items="{{buddies}}">
<file-input on-file-selected="_fileDropped">
<user-avatar contact="{{item.peerId}}" class="buddy"></user-avatar>
<file-input on-file-selected="_fileSelected">
<user-avatar contact="{{item}}" class="buddy"></user-avatar>
</file-input>
</template>
</div>
<user-avatar contact="{{me}}" class="me"></user-avatar>
<svg class="circles" viewBox="-0.5 -0.5 1140 700">
<circle class="circle" cx="570" cy="570" r="120" stroke="rgba(160,160,160,.15)"></circle>
<circle class="circle" cx="570" cy="570" r="210" stroke="rgba(160,160,160,.2)"></circle>
<circle class="circle" cx="570" cy="570" r="300" stroke="rgba(160,160,160,.3)"></circle>
<circle class="circle" cx="570" cy="570" r="390" stroke="rgba(160,160,160,.35)"></circle>
<circle class="circle" cx="570" cy="570" r="480" stroke="rgba(160,160,160,.4)"></circle>
<circle class="circle" cx="570" cy="570" r="570" stroke="rgba(160,160,160,.43)"></circle>
</svg>
<iron-ajax id="ajax" auto url="https://yawim.com/findbuddies/{{me}}" handle-as="json" last-response="{{buddies}}"></iron-ajax>
<div hidden$="{{buddies.length}}" class="explanation">
Open this page on another device
<wbr>to share files.
</div>
<personal-avatar class="me"></personal-avatar>
<!-- <iron-ajax id="ajax" auto url="https://yawim.com/findbuddies/{{me}}" handle-as="json" last-response="{{buddies}}"></iron-ajax> -->
</template>
<script>
'use strict';
Polymer({
is: 'buddy-finder',
properties: {
buddies: Array,
buddies: {
type: Array,
value: []
},
me: {
type: String,
}
},
attached: function() {
//Ask server every second for changes
setInterval(function() {
this.$.ajax.generateRequest();
}.bind(this), 1000);
var ajax = this.$.ajax;
function request() {
//ajax.generateRequest();
}
var intervalId = setInterval(request, 1000);
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
clearInterval(intervalId);
intervalId = 0;
} else {
if (!intervalId) {
intervalId = setInterval(request, 1000);
}
}
});
},
_fileDropped: function(e) {
_fileSelected: function(e) {
var peerId = e.model.item.peerId;
var file = e.detail;
app.p2p.connectToPeer(peerId, function() {
app.p2p.sendFile(peerId, file);
});
console.log('Send:', file);
console.log('To:', peerId);
app.p2p.sendFile(peerId, file);
}
});
</script>

View file

@ -0,0 +1,41 @@
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../styles/icons.html">
<dom-module id="personal-avatar">
<template>
<style>
:host {
@apply(--layout-vertical);
@apply(--layout-center);
width: 360px;
}
iron-icon {
width: 80px;
height: 80px;
color: #4285f4;
}
.paper-font-body1 {
font-size: 13px;
margin-top: 6px;
}
.discover {
color: #4285f4;
}
</style>
<iron-icon icon="chat:wifi-tethering"></iron-icon>
<div class="paper-font-body1">
SnapDrop lets you share instantly with people near by.
</div>
<div class="paper-font-body1 discover">
Allow me to be discovered by: Everyone in this network.
</div>
</template>
<script>
'use strict';
Polymer({
is: 'personal-avatar'
});
</script>
</dom-module>

View file

@ -1,4 +1,4 @@
<link rel="import" href="../contact-item/anonymous-contact-behavior.html">
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
<dom-module id="user-avatar">
<template>
<style>
@ -9,35 +9,121 @@
width: 120px;
height: 120px;
}
.avatar {
paper-icon-button {
display: inline-block;
width: 52px;
height: 52px;
width: 64px !important;
height: 64px !important;
border-radius: 50%;
overflow: hidden;
background: #ccc;
@apply(--shadow-elevation-2dp);
padding: 12px;
margin-bottom: 4px;
background-color: #4285f4;
color: white;
}
.paper-font-subhead{
text-align: center;
:host:hover paper-icon-button {
transform: scale(1.05);
}
.paper-font-subhead {
text-align: center;
}
.paper-font-body1 {
text-align: center;
width: 100%;
font-size: 13px;
color: grey;
margin-top: 2px;
}
:host,
.paper-font-subhead,
.paper-font-body1 {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
margin: 4px;
}
</style>
<div class="avatar" id="avatar" item-icon></div>
<paper-icon-button icon="{{_displayIcon}}"></paper-icon-button>
<div class="paper-font-subhead">{{_displayName}}</div>
<div class="paper-font-body1">{{status}}</div>
</template>
<script>
'use strict';
Polymer({
is: 'user-avatar',
behaviors:[Chat.AnonymousContactBehavior],
observers:['_computeBackgroundImg(contact.*)'],
_computeBackgroundImg:function(){
console.log('avatar changed');
var avatar = this.anonymousAccount(this.contact).avatar;
var style = this.$.avatar.style;
style.backgroundImage='url('+avatar.url+')';
style.backgroundPosition=avatar.left+'px '+avatar.top+'px';
properties: {
contact: Object,
_displayName: {
computed: '_computeDisplayName(contact)'
},
_displayIcon: {
computed: '_computeDisplayIcon(contact)'
},
status: {
type: String,
value: ''
}
},
_computeDisplayName: function(contact) {
contact = contact.name;
if (contact.model) {
return contact.os + ' ' + contact.model;
}
contact.os = contact.os.replace('Mac OS', 'Mac');
return contact.os + ' ' + contact.browser;
},
_computeDisplayIcon: function(contact) {
contact = contact.name;
if (contact.type === 'mobile') {
return 'chat:phone-iphone';
}
if (contact.type === 'tablet') {
return 'chat:tablet-mac';
}
return 'chat:desktop-mac';
},
attached: function() {
this.async(function() {
app.p2p.addEventListener('file-offered', function(e) {
if (e.detail.to === this.contact.peerId) {
this.status = 'Waiting to accept...';
}
}.bind(this), false);
app.p2p.addEventListener('upload-started', function(e) {
if (e.detail.to === this.contact.peerId) {
this.status = 'Uploading...';
}
}.bind(this), false);
app.p2p.addEventListener('download-started', function(e) {
if (e.detail.from === this.contact.peerId) {
this.status = 'Downloading...';
}
}.bind(this), false);
app.p2p.addEventListener('upload-complete', function(e) {
if (e.detail.from === this.contact.peerId) {
this.status = '';
}
}.bind(this), false);
app.p2p.addEventListener('download-complete', function(e) {
if (e.detail.from === this.contact.peerId) {
this.status = '';
}
}.bind(this), false);
app.p2p.addEventListener('file-declined', function(e) {
if (e.detail.from === this.contact.peerId) {
this.status = '';
}
}.bind(this), false);
app.p2p.addEventListener('upload-error', function(e) {
this.status = '';
}.bind(this), false);
}, 200);
}
});
</script>

View file

@ -1,337 +0,0 @@
<script>
'use strict';
window.Chat = window.Chat || {};
var djb2Code = function(str) {
var hash = 5381;
for (var i = 0; i < str.length; i++) {
var character = str.charCodeAt(i);
hash = ((hash << 5) + hash) + character; /* hash * 33 + c */
}
return hash > 0 ? hash : -hash;
};
var animals = [
'Adelie',
'Penguin',
'Akita',
'Bulldog',
'Ant',
'Fox',
'Hare',
'Wolf',
'Terrier',
'Avocet',
'Baboon',
'Camel',
'Badger',
'Barb',
'Basenji',
'Basking',
'Bat',
'Beagle',
'Bear',
'Collie',
'Beaver',
'Beetle',
'Bichon',
'Bird',
'Birman',
'Bison',
'Bobcat',
'Bombay',
'Bongo',
'Bonobo',
'Booby',
'Boykin',
'Budgie',
'Buffalo',
'Burmese',
'Fish',
'Caiman',
'Lizard',
'Canaan',
'Caracal',
'Cat',
'Catfish',
'Cesky',
'Fousek',
'Chamois',
'Cheetah',
'Chicken',
'Chinook',
'Cichlid',
'Leopard',
'Clumber',
'Coati',
'Coral',
'Tamarin',
'Cougar',
'Cow',
'Coyote',
'Crab',
'Macaque',
'Crane',
'Cuscus',
'Frog',
'Deer',
'Bracke',
'Dhole',
'Dingo',
'Discus',
'Dodo',
'Dog',
'Dogo',
'Dolphin',
'Donkey',
'Drever',
'Duck',
'Dugong',
'Dunker',
'Dusky',
'Eagle',
'Earwig ',
'Gorilla',
'Echidna',
'Emu',
'Falcon',
'Fennec',
'Ferret',
'Spitz',
'Fly',
'Fossa',
'Gecko',
'Gerbil',
'Gharial',
'Gibbon',
'Giraffe',
'Goat',
'Oriole',
'Goose',
'Gopher',
'Grouse',
'Guppy',
'Shark',
'Hamster',
'Harrier',
'Heron',
'Horse',
'Human',
'Hyena',
'Ibis',
'Iguana',
'Impala',
'Indri',
'Insect',
'Setter',
'Jackal',
'Jaguar',
'Kakapo',
'Kiwi',
'Koala',
'Lemming',
'Lemur',
'Liger',
'Lion',
'Llama',
'Lobster',
'Owl',
'Lynx',
'Mayfly',
'Meerkat',
'Molly',
'Mongrel',
'Monkey',
'Moorhen',
'Moose',
'Mouse',
'Mule',
'Numbat',
'Ocelot',
'Octopus',
'Okapi',
'Opossum',
'Ostrich',
'Otter',
'Oyster',
'Panther',
'Parrot',
'Peacock',
'Pelican',
'Persian',
'Pig',
'Piranha',
'Pointer',
'Poodle',
'Possum',
'Prawn',
'Puffin',
'Pug',
'Puma',
'Pygmy',
'Quail',
'Quetzal',
'Quokka',
'Quoll',
'Rabbit',
'Raccoon',
'Ragdoll',
'Rat',
'Robin',
'Saola',
'Seal',
'Serval',
'Sheep',
'Shrimp',
'Siamese',
'Skunk',
'Sloth',
'Snail',
'Snake',
'Somali',
'Sparrow',
'Dogfish',
'Sponge',
'Squid',
'Stoat',
'Swan',
'Tang',
'Tapir',
'Tarsier',
'Termite',
'Tetra',
'Tiffany',
'Tiger',
'Toucan',
'Tuatara',
'Turkey',
'Uakari',
'Uguisu',
'Vulture',
'Wallaby',
'Walrus',
'Warthog',
'Wasp',
'Weasel',
'Whippet',
'Wombat',
'Wrasse',
'Yak',
'Yorkie',
'Zebra',
'Zebu',
'Zonkey',
'Zorse'
];
var bb = [
'Walter White',
'Skyler White',
'Jesse Pinkman',
'Hank Schrader',
'Marie Schrader',
'Walter White, Jr.',
'Saul Goodman',
'Gustavo Fring',
'Mike Ehrmantraut',
'Lydia Rodarte-Quayle',
'Todd Alquist',
'Steven Gomez',
'Detectives Kalanchoe & Munn',
'George Merkert',
'Sac Ramey',
'Tim Roberts',
'Maximino Arciniega',
'Gale Boetticher',
'Duane Chow',
'Ron Forenall',
'Barry Goodman',
'Tyrus Kitt',
'Chris Mara',
'Dennis Markowski',
'Victor',
'Dan Wachsberger',
'Don Eladio Vuente',
'Juan Bolsa',
'Hector Salamanca',
'Tuco Salamanca',
'Leonel Salamanca',
'Marco Salamanca',
'Gonzo',
'Emilio Koyama',
'Krazy-8 Molina',
'Jack Welker',
'Andrea Cantillo',
'Brock Cantillo',
'Jane Margolis',
'Brandon Mayhew',
'Combo Ortega',
'Skinny Pete',
'Adam Pinkman',
'Mrs. Pinkman',
'Jake Pinkman',
'Wendy',
'Huell Babineaux',
'Ed',
'Francesca',
'Patrick Kuby',
'Hugo Archuleta',
'Ted Beneke',
'Clovis',
'Louis Corbett',
'Dr. Delcavoli',
'Lawson',
'Donald Margolis',
'Carmen Molina',
'Old Joe',
'Pamela',
'Gretchen Schwartz',
'Elliott Schwartz',
'Drew Sharp',
'Spooge',
'Holly White',
'Bogdan Wolynetz'
];
Chat.AnonymousContactBehavior = {
properties: {
contact: {
type: Object,
notify: true
},
_displayName: {
computed: '_computeDisplayName(contact)'
}
},
_computeDisplayName: function(contact) {
if (contact === undefined || contact === null) {
return 'connecting...';
}
if (contact === 'error' || contact === 'invite') {
return '';
}
if (!contact.name) {
return this.anonymousAccount(contact).name;
}
return contact.name;
},
get names() {
return bb;
},
anonymousAccount: function(contact) {
if (contact && !contact.name) {
var peer = contact.peer || contact;
var hash = djb2Code(peer);
var i = hash % this.names.length;
var name = this.names[i];
var marginTop = i % 2;
var marginLeft = Math.floor(i / 2) % 5;
return {
name: name,
peer: peer,
avatar: {
url: 'images/avatars.jpg',
left: -14 + 80 * marginLeft,
top: -19 + 95 * marginTop
}
};
}
}
};
</script>

View file

@ -1,15 +1,15 @@
<link rel="import" href="../bower_components/platinum-sw/platinum-sw-cache.html">
<link rel="import" href="../bower_components/platinum-sw/platinum-sw-register.html">
<link rel="import" href="../bower_components/paper-toast/paper-toast.html">
<link rel="import" href="../bower_components/paper-progress/paper-progress.html">
<!-- Configure your routes here -->
<!-- Configure your routes here
<link rel="import" href="routing.html">
-->
<!-- Add your elements here -->
<link rel="import" href="../styles/app-theme.html">
<link rel="import" href="../styles/shared-styles.html">
<link rel="import" href="buddy-finder/buddy-finder.html">
<link rel="import" href="p2p-network/p2p-network.html">
<link rel="import" href="p2p-network/connection-wrapper.html">
<link rel="import" href="file-sharing/file-receiver.html">

View file

@ -8,6 +8,7 @@ Chat.FileButtonBehaviorImpl = {
if (!fileInput) {
fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.multiple = 'true';
fileInput.className = 'fileInput';
fileInput.style.position = 'fixed';
fileInput.style.top = '-10000px';

View file

@ -1,4 +1,4 @@
<link rel="import" href="../../../bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="file-button-behavior.html">
<dom-module id="file-button">
<template>

View file

@ -25,7 +25,7 @@ Chat.FileDropBehaviorImpl = {
dropZone.addEventListener('drop', function(event) {
event.stopPropagation();
event.preventDefault();
//call dragend
dragEnd();
@ -36,5 +36,13 @@ Chat.FileDropBehaviorImpl = {
});
}
};
document.body.addEventListener('dragover', function(e) {
e.stopPropagation();
e.preventDefault();
}, false);
document.body.addEventListener('drop', function(event) {
event.stopPropagation();
event.preventDefault();
});
Chat.FileDropBehavior = [Chat.FileDropBehaviorImpl, Chat.FileSelectionBehavior];
</script>

View file

@ -2,51 +2,94 @@
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../bower_components/neon-animation/animations/scale-up-animation.html">
<link rel="import" href="../../bower_components/neon-animation/animations/fade-out-animation.html">
<link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
<dom-module id="file-receiver">
<template>
<style>
:host {
display: block;
position: fixed;
z-index: 100;
}
#dialog,
#download {
width: 300px;
z-index: 101;
}
b {
word-break: break-word;
}
</style>
<paper-dialog id="dialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdro>
<h2>File Received</h2>
<p>You received file {{file.name}}</p>
<paper-dialog id="dialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal>
<h2>Download File</h2>
<p><b>{{file.name}}</b></p>
<div class="buttons">
<paper-button dialog-dismiss>Dismiss</paper-button>
<paper-button dialog-confirm on-tap="_download">Download</paper-button>
<paper-button dialog-dismiss on-tap="_decline">Discard</paper-button>
<paper-button dialog-confirm on-tap="_accept" autofocus>Download</paper-button>
</div>
</paper-dialog>
<paper-dialog id="download" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal>
<h2>File Received</h2>
<p>Right Click and "Save as"...</p>
<div class="buttons">
<paper-button dialog-dismiss>Discard</paper-button>
<a href="{{dataUri}}" target="_blank">
<paper-button dialog-confirm autofocus>Download</paper-button>
</a>
</div>
</paper-dialog>
</template>
<script>
'use strict';
Polymer({
is: 'file-receiver',
attached: function() {
this.async(function() {
app.p2p.addEventListener('file-received', function(e) {
this.fileReceived(e.detail);
}.bind(this), false);
},200);
},
fileReceived: function(file) {
this.set('file', file);
this.$.dialog.open();
},
_download: function() {
var link = document.createElement('a');
link.download = this.file.name;
// Construct the uri
var uri = this.file.dataURI;
link.href = uri;
document.body.appendChild(link);
link.click();
// Cleanup the DOM
document.body.removeChild(link);
//delete link;
}
});
(function() {
Polymer({
is: 'file-receiver',
attached: function() {
this.async(function() {
app.p2p.addEventListener('file-offer', function(e) {
this.file = e.detail;
this.$.dialog.open();
}.bind(this), false);
app.p2p.addEventListener('file-received', function(e) {
this._fileReceived(e.detail);
}.bind(this), false);
app.p2p.addEventListener('file-declined', function(e) {
app.displayToast('User declined file ' + e.detail.name);
}.bind(this), false);
app.p2p.addEventListener('upload-complete', function(e) {
app.displayToast('User received file ' + e.detail.name);
}.bind(this), false);
app.p2p.addEventListener('upload-error', function(e) {
app.displayToast('The other device did not respond. Please try again.');
}.bind(this), false);
}, 200);
},
_fileReceived: function(file) {
this.downloadURI(file);
},
_decline: function() {
app.p2p.decline(this.file);
},
_accept: function() {
app.p2p.accept(this.file);
},
downloadURI: function(file) {
var link = document.createElement('a');
var uri = (window.URL || window.webkitURL).createObjectURL(file.blob);
if (typeof link.download !== 'undefined') {
//download attribute is supported
link.href = uri;
link.download = file.name || 'blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else {
this.dataUri = uri;
this.$.download.open();
}
}
});
}());
</script>
</dom-module>

File diff suppressed because one or more lines are too long

View file

@ -3,22 +3,16 @@
window.Chat = window.Chat || {};
Chat.FileSelectionBehavior = {
notifyFilesSelection: function(files) {
if(!files){
if (!files) {
console.log('no files selected...');
return;
}
for (var i = 0; i < files.length; i++) {
var file = files[i];
var reader = new FileReader();
reader.onload = function(e2) {
// finished reading file data.
console.log('file dropped');
this.fire('file-selected', {
dataURI: e2.target.result,
name: file.name
});
}.bind(this);
reader.readAsDataURL(file); // start reading the file data.
this.fire('file-selected', {
file: file,
name: file.name
});
}
}
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,59 @@
<link rel="import" href="p2p-network.html">
<link rel="import" href="web-socket.html">
<dom-module id="connection-wrapper">
<template>
<p2p-network id="p2p" me="{{me}}"></p2p-network>
<web-socket id="ws" me="{{me}}"></web-socket>
</template>
<script>
'use strict';
(function() {
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
var webRTCSupported = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection || window.webkitRTCPeerConnection;
function rtcConnectionSupported(peerId) {
return webRTCSupported && (peerId.indexOf('rtc_') === 0);
}
Polymer({
is: 'connection-wrapper',
properties: {
me: {
notify: true,
value: (webRTCSupported ? 'rtc_' : 'ws_') + guid()
}
},
behaviors: [Chat.FileTransferProtocol],
_sendFile: function(toPeer, file) {
if (!rtcConnectionSupported(toPeer)) {
this.$.ws._sendFile(toPeer, file);
} else {
this.$.p2p._sendFile(toPeer, file);
}
},
_sendSystemEvent: function(toPeer, event) {
console.log('system event', toPeer, event);
if (!rtcConnectionSupported(toPeer)) {
this.$.ws._sendSystemEvent(toPeer, event);
} else {
this.$.p2p._sendSystemEvent(toPeer, event);
}
},
connectToPeer: function(toPeer, callback) {
if (!rtcConnectionSupported(toPeer)) {
callback();
} else {
this.$.p2p.connectToPeer(toPeer,callback);
}
},
});
})();
</script>
</dom-module>

View file

@ -0,0 +1,137 @@
<script>
'use strict';
window.Chat = window.Chat || {};
Chat.FileTransferProtocol = {
properties: {
loading: {
type: Boolean,
notify: true,
value: false,
observer: '_loadingChanged'
},
buddies: {
notify: true
}
},
listeners: {
'system-event': '_onSystemMsg',
'file-received': '_onFileReceived',
},
_onSystemMsg: function(event) {
var msg = event.detail;
console.log('FTP received sysMsg:', msg);
switch (msg.type) {
case 'offer':
this._onOffered(msg);
break;
case 'decline':
this._onDeclined(msg);
break;
case 'accept':
this._onAccepted(msg);
break;
case 'transfer':
this._onTransfer(msg);
break;
case 'received':
this._onReceived(msg);
break;
case 'buddies':
this._onBuddies(msg);
break;
}
},
sendFile: function(peerId, file) {
this.set('loading', true);
this.fileToSend = file;
this.fire('file-offered', {
to: peerId
});
this.connectToPeer(peerId, function() {
this._offer(peerId, file);
}.bind(this));
//set 15sec timeout
this._timeoutTimer = this.async(function() {
this._onError();
}, 15000);
},
_offer: function(toPeer, file) {
console.log('FTP offer file:', file, 'To:', toPeer);
this._sendSystemEvent(toPeer, {
type: 'offer',
name: file.name
});
},
_onOffered: function(offer) {
console.log('FTP offered file:', offer.name, 'From:', offer.from);
this.fire('file-offer', {
from: offer.from,
name: offer.name
});
},
decline: function(offer) {
this._sendSystemEvent(offer.from, {
type: 'decline',
name: offer.name
});
},
_onDeclined: function(offer) {
this.cancelAsync(this._timeoutTimer);
delete this.fileToSend;
this.set('loading', false);
this.fire('file-declined', offer);
},
accept: function(offer) {
this._sendSystemEvent(offer.from, {
type: 'accept',
name: offer.name
});
this.fire('download-started', {
from: offer.from
});
},
_onAccepted: function(offer) {
this.cancelAsync(this._timeoutTimer);
this._sendSystemEvent(offer.from, {
type: 'transfer',
name: offer.name
});
this.fire('upload-started', {
to: offer.from
});
this._sendFile(offer.from, this.fileToSend);
},
_onTransfer: function() {
this.loading = true;
},
_onFileReceived: function(event) {
var file = event.detail;
this.loading = false;
this._sendSystemEvent(file.from, {
type: 'received',
name: file.name
});
this.fire('download-complete', {
from: file.from
});
console.log('FTP received:', file);
},
_onReceived: function(offer) {
this.loading = false;
this.fire('upload-complete', offer);
},
_onError: function() {
this.loading = false;
this.fire('upload-error');
},
_loadingChanged: function(loading) {
window.anim(loading);
},
_onBuddies: function(msg) {
this.set('buddies', msg.buddies);
}
};
</script>

View file

@ -1,4 +1,5 @@
<script src="../../../bower_components/peerjs/peer.min.js"></script>
<script src="../../bower_components/peerjs/peer.min.js"></script>
<link rel="import" href="file-transfer-protocol.html">
<dom-module id="p2p-network">
<template>
</template>
@ -30,7 +31,7 @@
path: 'peerjs',
secure: true
};
this._peer = new Peer(options);
this._peer = new Peer(this.me,options);
this._peer.on('open', function(id) {
console.log('My peer ID is: ' + id);
this.set('me', id);
@ -65,12 +66,22 @@
if (c.label === 'file') {
c.on('data', function(data) {
console.log('received!', data);
console.log(data);
var dataView = new Uint8Array(data.file);
var dataBlob = new Blob([dataView]);
this.fire('file-received', {
peer: peer,
dataURI: data.dataURI,
from: peer,
blob: dataBlob,
name: data.name,
});
}.bind(this));
}
if (c.label === 'system') {
c.on('data', function(data) {
data.from = peer;
this.fire('system-event', data);
}.bind(this));
}
},
@ -78,15 +89,32 @@
function request(requestedPeer, callback) {
return function() {
//system messages channel
var s = this._peer.connect(requestedPeer, {
label: 'system'
});
s.on('open', function() {
this.connect(s);
if (callback) {
callback();
}
}.bind(this));
s.on('error', function(err) {
console.log(err);
if (err.message.indexOf('Connection is not open') > -1) {
console.err('Handle this error!!');
}
});
//files channel
var f = this._peer.connect(requestedPeer, {
label: 'file',
reliable: true
});
f.on('open', function() {
this.connect(f);
if (callback) {
callback();
}
}.bind(this));
f.on('error', function(err) {
console.log(err);
@ -98,7 +126,6 @@
callback();
return;
}
this.set('loading', true);
if (this._peerOpen) {
request(requestedPeer, callback).bind(this)();
} else {
@ -107,7 +134,7 @@
};
}()),
sendFile: function(peerId, file) {
_sendFile: function(peerId, file) {
var conns = this._peer.connections[peerId];
if (conns) {
conns.forEach(function(conn) {
@ -115,7 +142,17 @@
conn.send(file);
console.log('file send');
}
});
}.bind(this));
}
},
_sendSystemEvent: function(peerId, msg) {
var conns = this._peer.connections[peerId];
if (conns) {
conns.forEach(function(conn) {
if (conn.label === 'system') {
conn.send(msg);
}
}.bind(this));
}
}
});

View file

@ -0,0 +1,82 @@
<link rel="import" href="binaryjs.html">
<dom-module id="web-socket">
<template>
<style>
:host {
display: block;
}
</style>
</template>
<script>
'use strict';
Polymer({
is: 'web-socket',
attached: function() {
this.init();
},
init: function() {
var websocketUrl = (window.location.protocol === 'https:' ? 'wss://' : 'ws://') + document.location.hostname + ':9001';
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);
}
}.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));
this.client.on('open', function(e) {
console.log(e);
this.client.send({}, {
handshake: this.me
});
}.bind(this));
this.client.on('error', function(e) {
console.log(e);
});
this.client.on('close', function(e) {
console.log(e);
//try to reconnect after 3s
this.async(this.init, 3000);
}.bind(this));
},
_sendFile: function(toPeer, file) {
console.log('send file!', file);
this.client.send(file.file, {
name: file.file.name,
type: file.file.type,
toPeer: toPeer
});
},
connectToPeer: function(peer, callback) {
callback();
},
_sendSystemEvent: function(toPeer, event) {
console.log('system event', toPeer, event);
event.isSystemEvent = true;
this.client.send(event, {
toPeer: toPeer
});
}
});
</script>
</dom-module>