Feature Request: Send text instead of files

This commit is contained in:
Robin Linus 2016-01-02 14:23:51 +01:00
parent ce61dc7c60
commit 0e008a5f31
9 changed files with 288 additions and 111 deletions

View file

@ -1,14 +1,16 @@
<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-sharing/file-input-behavior.html"> <link rel="import" href="../file-sharing/file-input-behavior.html">
<link rel="import" href="../text-sharing/text-input-behavior.html">
<dom-module id="buddy-avatar"> <dom-module id="buddy-avatar">
<template> <template>
<style> <style>
:host { :host {
display: block; display: block;
@apply(--layout-vertical); @apply(--layout-vertical);
@apply(--layout-center); @apply(--layout-center-center);
width: 120px; width: 120px;
height: 152px; height: 124px;
cursor: pointer;
} }
paper-icon-button { paper-icon-button {
@ -29,6 +31,8 @@
.paper-font-subhead { .paper-font-subhead {
text-align: center; text-align: center;
line-height: 22px;
margin-top: 2px;
} }
.paper-font-body1 { .paper-font-body1 {
@ -36,7 +40,7 @@
width: 100%; width: 100%;
font-size: 13px; font-size: 13px;
color: grey; color: grey;
margin-top: 2px; line-height: 13px;
} }
:host, :host,
@ -46,7 +50,6 @@
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
margin-top: 4px;
} }
:host([only1]) { :host([only1]) {
@ -55,16 +58,26 @@
@apply(--layout-center-center); @apply(--layout-center-center);
cursor: pointer; cursor: pointer;
} }
.container {
@apply(--layout-vertical);
@apply(--layout-center);
height: 112px;
padding-top: 16px;
display: block;
}
</style> </style>
<paper-icon-button icon="{{_displayIcon}}"></paper-icon-button> <div class="container">
<div class="paper-font-subhead">{{_displayName}}</div> <paper-icon-button icon="{{_displayIcon}}"></paper-icon-button>
<div class="paper-font-body1">{{status}}</div> <div class="paper-font-subhead">{{_displayName}}</div>
<div class="paper-font-body1">{{status}}</div>
</div>
</template> </template>
<script> <script>
'use strict'; 'use strict';
Polymer({ Polymer({
is: 'buddy-avatar', is: 'buddy-avatar',
behaviors: [Chat.FileInputBehavior], behaviors: [Chat.FileInputBehavior, Chat.TextInputBehavior],
properties: { properties: {
contact: Object, contact: Object,
_displayName: { _displayName: {

View file

@ -28,10 +28,6 @@
@apply(--layout-wrap); @apply(--layout-wrap);
} }
.buddy {
cursor: pointer;
}
.explanation { .explanation {
@apply(--paper-font-headline); @apply(--paper-font-headline);
color: #4285f4; color: #4285f4;
@ -60,10 +56,37 @@
} }
} }
@media all and (max-height: 440px) {
.buddies {
padding-top: 48px;
@apply(--layout-self-start);
}
}
.explanation2 {
display: none;
}
@media all and (min-height: 640px) {
.explanation2 {
display: block;
position: absolute;
top: 128px;
width: 296px;
margin-left: -148px;
left: 50%;
@apply(--paper-font-title);
color: #7baaf7;
text-align: center;
}
}
</style> </style>
<div class="explanation2" hidden$="{{!buddies.0}}">
Tap to send File.<br>Long Press to send Text.
</div>
<div class="buddies"> <div class="buddies">
<template is="dom-repeat" items="{{buddies}}"> <template is="dom-repeat" items="{{buddies}}">
<buddy-avatar on-file-selected="_fileSelected" only$="{{!buddies.1}}" contact="{{item}}" class="buddy"></buddy-avatar> <buddy-avatar on-file-selected="_fileSelected" only$="{{!buddies.1}}" contact="{{item}}"></buddy-avatar>
</template> </template>
</div> </div>
<div hidden$="{{buddies.0}}" class="explanation"> <div hidden$="{{buddies.0}}" class="explanation">

View file

@ -23,13 +23,14 @@ Chat.FileButtonBehaviorImpl = {
var files = this.fileInput.files; var files = this.fileInput.files;
this.notifyFilesSelection(files); this.notifyFilesSelection(files);
}.bind(this); }.bind(this);
}, this.addEventListener('click', function(e) {
listeners: { var button = e.which || e.button;
'tap': '_openDialog' if (button !== 1) {
}, return;
_openDialog: function() { }
this.fileInput.value = null; this.fileInput.value = null;
this.fileInput.click(); this.fileInput.click();
}.bind(this), false);
} }
}; };
Chat.FileButtonBehavior = [Chat.FileButtonBehaviorImpl, Chat.FileSelectionBehavior]; Chat.FileButtonBehavior = [Chat.FileButtonBehaviorImpl, Chat.FileSelectionBehavior];

View file

@ -43,6 +43,9 @@ Chat.FileTransferProtocol = {
case 'buddies': case 'buddies':
this._onBuddies(msg); this._onBuddies(msg);
break; break;
case 'text':
this._onTextReceived(msg);
break;
} }
}, },
sendFile: function(peerId, file) { sendFile: function(peerId, file) {
@ -135,6 +138,19 @@ Chat.FileTransferProtocol = {
}, },
_onBuddies: function(msg) { _onBuddies: function(msg) {
this.set('buddies', msg.buddies); this.set('buddies', msg.buddies);
},
sendText: function(toPeer, text) {
console.log('FTP send text:', text, 'To:', toPeer);
this.connectToPeer(toPeer, function() {
this._sendSystemEvent(toPeer, {
type: 'text',
text: text
});
}.bind(this));
},
_onTextReceived: function(msg) {
this.fire('text-received', msg);
} }
}; };
</script> </script>

View file

@ -108,7 +108,7 @@
s.on('error', function(err) { s.on('error', function(err) {
console.log(err); console.log(err);
if (err.message.indexOf('Connection is not open') > -1) { if (err.message.indexOf('Connection is not open') > -1) {
console.err('Handle this error!!'); console.error('Handle this error!!', err);
} }
}); });

View file

@ -0,0 +1,36 @@
<script>
'use strict';
(function(document) {
var copyTextarea = document.createElement('textarea');
copyTextarea.setAttribute('id', 'clipboard-textarea');
var style = copyTextarea.style;
style.position = 'absolute';
style.top = '-10000px';
document.body.appendChild(copyTextarea);
window.Chat.ClipboardBehavior = {
copyToClipboard: function(content) {
copyTextarea.value = content;
var range = document.createRange();
range.selectNode(copyTextarea);
window.getSelection().addRange(range);
try {
// Now that we've selected the anchor text, execute the copy command
var successful = document.execCommand('copy');
if (successful) {
app.displayToast('Copied text to clipboard. Paste it where you want!');
} else {
console.log('failed to copy to clipboard', successful);
}
} catch (err) {
console.log('Oops, unable to copy', err);
}
// Remove the selections - NOTE: Should use
// removeRange(range) when it is supported
window.getSelection().removeAllRanges();
}
};
}(document));
</script>

File diff suppressed because one or more lines are too long

View file

@ -1,34 +1,59 @@
<link rel="import" href="text-input-dialog.html">
<script> <script>
'use strict'; 'use strict';
window.Chat = window.Chat || {}; window.Chat = window.Chat || {};
Chat.TextInputBehavior = { (function() {
get textInput() { var textInput = Polymer.Base.create('text-input-dialog');
var textInput = Polymer.dom(this).querySelector('.textInput'); textInput.className = 'textInput';
if (!textInput) { document.body.appendChild(textInput);
textInput = document.createElement('input'); Chat.TextInputBehavior = {
textInput.type = 'file'; properties: {
textInput.multiple = 'true'; contact: Object,
textInput.className = 'textInput'; },
textInput.style.position = 'fixed'; get textInput() {
textInput.style.top = '-10000px'; var textInput = Polymer.dom(document).querySelector('.textInput');
textInput.style.left = '-10000px'; return textInput;
textInput.style.opacity = 0; },
Polymer.dom(this).appendChild(textInput); openTextDialog: function() {
this.textInput.open(this.contact);
},
listeners: {
'contextmenu': '_handleContextMenu',
'down': '_handleDown',
'up': '_handleUp',
},
_handleContextMenu: function(ev) {
ev.preventDefault();
ev.stopPropagation();
ev.cancelBubble = true;
this.cancelAsync(this.pressTimer);
this.openTextDialog();
return false;
},
_handleUp: function(e) {
this.cancelAsync(this.pressTimer);
},
_handleDown: function(ev) {
this.pressTimer = this.async(function() {
this.openTextDialog();
ev.preventDefault();
ev.stopPropagation();
ev.cancelBubble = true;
return false;
}, 1100);
},
attached: function() {
// this.addEventListener('mousedown', function(e) {
// clearTimeout(this.pressTimer);
// }.bind(this), false);
// this.addEventListener('mousup', function(e) {
// this.pressTimer = window.setTimeout(function() {
// this.openTextDialog();
// }, 1500);
// }.bind(this), false);
} }
return textInput; };
}, }());
attached: function() {
this.textInput.onchange = function() {
var files = this.textInput.files;
this.notifyFilesSelection(files);
}.bind(this);
},
listeners: {
'tap': '_openDialog'
},
_openDialog: function() {
this.textInput.value = null;
this.textInput.click();
}
};
</script> </script>

View file

@ -4,7 +4,10 @@
<link rel="import" href="../../bower_components/neon-animation/animations/fade-out-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/iron-pages/iron-pages.html">
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html"> <link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
<link rel="import" href="../../bower_components/paper-spinner/paper-textarea.html"> <link rel="import" href="../../bower_components/paper-input/paper-textarea.html">
<link rel="import" href="linkify.html">
<link rel="import" href="clipboard-behavior.html">
<link rel="import" href="../sound-notification/sound-notification-behavior.html">
<dom-module id="text-input-dialog"> <dom-module id="text-input-dialog">
<template> <template>
<style> <style>
@ -12,92 +15,147 @@
display: block; display: block;
} }
#dialog, #sendDialog,
#download { #receiveDialog {
width: 300px; width: 324px;
z-index: 101; z-index: 101;
max-height: 320px;
overflow: hidden;
margin: 16px;
} }
.filename { @media all and (max-height: 600px) {
#sendDialog {
padding-top: 24px;
top:0px !important;
}
}
#receivedText {
word-break: break-all; word-break: break-all;
word-break: break-word; word-break: break-word;
} }
paper-textarea { paper-textarea {
height: 200px; max-height: 200px;
width: calc(100% - 48px);
overflow-x: hidden;
overflow-y: auto;
}
#receivedText {
max-height: 200px;
overflow: hidden;
width: calc(100% - 48px);
text-overflow: ellipsis;
-webkit-line-clamp: 9;
clamp: 9;
} }
</style> </style>
<paper-dialog id="dialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal> <paper-dialog id="sendDialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal>
<h2>Send Text</h2> <h2>Send Text</h2>
<paper-textarea label="Enter Text"></paper-textarea> <paper-textarea id="textInput" label="Enter Text" value="{{textToSend}}" autofocus></paper-textarea>
<div class="buttons">
<paper-button dialog-dismiss on-tap="_decline">Discard</paper-button>
<paper-button dialog-confirm on-tap="_accept" autofocus>Send</paper-button>
</div>
</paper-dialog>
<paper-dialog id="download" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal>
<h2>Text Received</h2>
<p>Open File or Right Click and "Save as"...</p>
<div class="buttons"> <div class="buttons">
<paper-button dialog-dismiss>Discard</paper-button> <paper-button dialog-dismiss>Discard</paper-button>
<a href="{{dataUri}}" target="_blank"> <paper-button dialog-dismiss on-tap="_send">Send</paper-button>
<paper-button dialog-confirm autofocus>Open File</paper-button> </div>
</paper-dialog>
<paper-dialog id="receiveDialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal>
<h2>Text Received</h2>
<div>
<div id="receivedText">
</div>
</div>
<div class="buttons">
<paper-button dialog-dismiss>Discard</paper-button>
<a href="tel:{{tel}}" hidden$="{{!tel}}">
<paper-button dialog-dismiss>Call</paper-button>
</a> </a>
<paper-button on-tap="_copy" autofocus>Copy</paper-button>
</div> </div>
</paper-dialog> </paper-dialog>
</template> </template>
<script> <script>
'use strict'; 'use strict';
(function() { (function() {
/*
*
* /^\+?[0-9x]*$/ is the first usuful Text sent via Snapdrop 2015/1/2 5:30
*
*/
var phoneNumbers = /^\+?[0-9x/ ]*$/;
Polymer({ Polymer({
is: 'text-input-dialog', is: 'text-input-dialog',
open: function() { behaviors: [Chat.ClipboardBehavior,Chat.SoundNotificationBehavior],
this.$.dialog.open(); properties: {
textToSend: {
type: String
},
receivedText: {
type: String
},
contact: {
type: Object
},
tel: {
computed: '_isPhoneNumber(receivedText)',
value: false
}
},
open: function(contact) {
this.contact = contact;
this.$.sendDialog.open();
}, },
attached: function() { attached: function() {
// this.async(function() { this.async(function() {
// app.conn.addEventListener('file-offer', function(e) { app.conn.addEventListener('text-received', function(e) {
// this.file = e.detail; var receivedText = e.detail.text;
// this.$.dialog.open(); this.receivedText = receivedText;
// }.bind(this), false); this.$.receivedText.textContent = receivedText;
// app.conn.addEventListener('file-received', function(e) { window.linkifyElement(this.$.receivedText, {}, document);
// this._fileReceived(e.detail); this.$.receiveDialog.open();
// }.bind(this), false); this.playSound();
// app.conn.addEventListener('file-declined', function(e) { }.bind(this), false);
// app.displayToast('User declined file ' + e.detail.name); }, 200);
// }.bind(this), false);
// app.conn.addEventListener('upload-complete', function(e) { this.$.textInput.addEventListener('keypress', function(e) {
// app.displayToast('User received file ' + e.detail.name); if (e.which === 13 || e.charCode === 13) {
// }.bind(this), false); var key;
// app.conn.addEventListener('upload-error', function(e) { var isShift;
// app.displayToast('The other device did not respond. Please try again.'); if (window.event) {
// }.bind(this), false); key = window.event.keyCode;
// }, 200); isShift = !!window.event.shiftKey; // typecast to boolean
} else {
key = e.which;
isShift = !!e.shiftKey;
}
if (!isShift) {
e.preventDefault();
e.stopPropagation();
this._send();
}
}
}.bind(this), false);
}, },
_fileReceived: function(file) { _send: function() {
this.downloadURI(file); this.$.sendDialog.close();
app.conn.sendText(this.contact.peerId, this.textToSend);
}, },
_decline: function() { _copy: function() {
app.conn.decline(this.file); this.copyToClipboard(this.receivedText);
this.$.receiveDialog.close();
console.log('text copied', this.receivedText);
}, },
_accept: function() { _isPhoneNumber: function(text) {
app.conn.accept(this.file); if (!text || text.length < 5 || text.length > 100) {
}, return false;
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();
} }
} if (phoneNumbers.test(text)) {
return text;
}
},
}); });
}()); }());
</script> </script>