Browse Source

export key

master
Julian Steinwachs 8 years ago
parent
commit
8d2bc300ff
  1. 1187
      build/app-bundle.js
  2. 60863
      build/twister-lib.js
  3. BIN
      fonts/glyphicons-halflings-regular.woff2
  4. BIN
      fonts/passwordsP2P.pdf
  5. 197
      jsx/common/PostComposer.js
  6. 152
      jsx/common/PostContentHelper.js
  7. 6
      jsx/other/Accounts.js
  8. 7
      jsx/other/Conversation.js
  9. 171
      jsx/other/ExportAccountModalButton.js
  10. 120
      jsx/other/GenerateAccountModalButton.js
  11. 1
      package.json

1187
build/app-bundle.js

File diff suppressed because it is too large Load Diff

60863
build/twister-lib.js

File diff suppressed because one or more lines are too long

BIN
fonts/glyphicons-halflings-regular.woff2

Binary file not shown.

BIN
fonts/passwordsP2P.pdf

Binary file not shown.

197
jsx/common/PostComposer.js

@ -0,0 +1,197 @@ @@ -0,0 +1,197 @@
var React = require('react');
var ContentEditable = require('react-wysiwyg');
var PostContentHelper = require('../common/PostContentHelper.js');
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js');
var ReactBootstrap = require('react-bootstrap')
, Button = ReactBootstrap.Button
, DropdownButton = ReactBootstrap.DropdownButton
, MenuItem = ReactBootstrap.MenuItem
, ButtonGroup = ReactBootstrap.ButtonGroup
, OverlayTrigger = ReactBootstrap.OverlayTrigger
, Popover = ReactBootstrap.Popover
, Glyphicon = ReactBootstrap.Glyphicon
, Grid = ReactBootstrap.Grid
, Col = ReactBootstrap.Col
, Row = ReactBootstrap.Row
var escape = document.createElement('textarea')
function escapeHTML(html) {
escape.textContent = html;
return escape.innerHTML;
}
module.exports = PostComposer = React.createClass({
mixins:[SafeStateChangeMixin],
getInitialState: function(){
var editing = false
var defaultValue = ''
return {
html: defaultValue,
editing: true,
placeholder: true,
maxLength: 140,
totalLength: defaultValue.length,
queryMention: false,
text: defaultValue
}
},
componentDidMount: function () {
// Gives the window a callback to call before the next repaint.
window.requestAnimationFrame(this.checkCursor)
},
checkCursor: function (timestamp) {
var self = this
var selection = window.getSelection()
if (this.state.editing && selection.focusNode) {
var node = selection
.getRangeAt(0)
.commonAncestorContainer
.parentNode
if (node.className === 'show-dropdown') {
// you could use the node to determine its position,
// and show the dropdown inline, too.
this.setStateSafe({ queryMention : node.textContent })
} else if (this.state.queryMention) {
this.setStateSafe({ queryMention: false })
}
} else if (this.state.queryMention) {
this.setStateSafe({ queryMention: false })
}
window.requestAnimationFrame(self.checkCursor)
},
render: function(){
var isValid = (this.state.maxLength >= this.state.totalLength)
&& (this.state.totalLength > 0)
return (
<div>
<div>{this.state.error}</div>
<ContentEditable
ref='editable'
tagName='div'
html={this.state.html}
placeholder={this.state.placeholder}
placeholderText='write'
onKeyPress={this.onKeyPress}
preventStyling
noLinebreaks
onChange={this.onChange}
editing={this.state.editing}
style={{"outline": "none"}}
/>
<Row>
<Col xs={9} md={9}>
</Col>
<Col xs={1} md={1}>
<Button disabled id="content-length">
{this.state.maxLength - this.state.totalLength}
</Button>
</Col>
<Col xs={2} md={2}>
<Button disabled={!isValid} onClick={this.handleSubmit}>
Twist
</Button>
</Col>
</Row>
</div>
);
/*<div>
Show autocomplete? {this.state.queryMention ? 'Yes ' + this.state.queryMention : 'No'}
</div>*/
},
handleSubmit: function(){
this.props.onSubmit(this.state.text);
},
autofocus: function () {
if (this.state.editing) {
this.refs.editable.autofocus()
}
},
onChange: function(text, setPlaceholder) {
// in order to render the updated html,
// you need to pass it as a prop to contentEditable.
// This gives you increased flexibility.
if (setPlaceholder) {
this.setState({
placeholder: true,
html: '',
totalLength: 0,
text: ''
})
} else {
var copy = text.slice(0, this.state.maxLength)
var parsedContent = PostContentHelper.parseContent(copy);
//console.log(copy,parsedContent);
var output = "";
parsedContent.map(function(item,index){
//console.log(item.raw)
switch(item.type) {
case "mention":
output+=('<a class="text-muted" href="#/profile/"'+item.raw.substr(1)+'">'+item.raw+'</a>');
break;
case "hashtag":
output+=('<a class="text-muted" href="#/hashtag/"'+item.raw.substr(1)+'">'+item.raw+'</a>');
break;
case "url":
output+=('<a class="text-primary" href="'+item.raw+'" target="_blank">'+item.raw+'</a>');
break;
case "email":
output+=('<span class="text-primary">'+item.raw+'</span>');
break;
default:
output+=('<span>'+item.raw+'</span>');
}
});
// text overflow
if (text.length > this.state.maxLength) {
var overflow = '<span style="text-decoration: line-through;">' +
text.slice(this.state.maxLength) +
'</span>'
output = output + overflow
}
this.setState({
placeholder: false,
html: output,
totalLength: text.length,
text: copy
})
}
},
enableEditing: function(){
var editing = !this.state.editing
// set your contenteditable field into editing mode.
this.setState({ editing: editing });
if (editing) {
this.refs.editable.autofocus()
this.refs.editable.setCursorToEnd()
}
}
});

152
jsx/common/PostContentHelper.js

@ -0,0 +1,152 @@ @@ -0,0 +1,152 @@
module.exports = {
extractUsername: function(s) {
var username = "";
for( var i = 0; i < s.length; i++ ) {
var c = s.charCodeAt(i);
if( (c >= 'a'.charCodeAt(0) && c <= 'z'.charCodeAt(0)) ||
(c >= 'A'.charCodeAt(0) && c <= 'Z'.charCodeAt(0)) ||
(c >= '0'.charCodeAt(0) && c <= '9'.charCodeAt(0)) ||
c == '_'.charCodeAt(0) ) {
username += s[i];
} else {
break;
}
}
return username;
},
extractHashtag: function(s) {
var hashtag = "";
s = this.reverseHtmlEntities(s);
for( var i = 0; i < s.length; i++ ) {
if( " \n\t.,:/?!;'\"()[]{}*#".indexOf(s[i]) < 0 ) {
hashtag += s[i];
} else {
break;
}
}
return hashtag;
},
escapeHtmlEntities: function(str) {
return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
},
reverseHtmlEntities: function(str) {
return str
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&apos;/g, "'")
.replace(/&amp;/g, '&');
},
parseContent: function( msg ) {
//return [{type:"text",raw:msg}];
var output = [];
var tmp;
var match = null;
var index;
var strUrlRegexp = "http[s]?://";
var strEmailRegexp = "\\S+@\\S+\\.\\S+";
var strSplitCounterR = "\\(\\d{1,2}\\/\\d{1,2}\\)$";
var reAll = new RegExp("(?:^|[ \\n\\t.,:\\/?!])(#|@|" + strUrlRegexp + "|" + strEmailRegexp + "|" + strSplitCounterR + ")");
var reHttp = new RegExp(strUrlRegexp);
var reEmail = new RegExp(strEmailRegexp);
var reSplitCounter = new RegExp(strSplitCounterR);
//msg = this.escapeHtmlEntities(msg);
while( msg != undefined && msg.length ) {
match = reAll.exec(msg);
if( match ) {
index = (match[0] === match[1]) ? match.index : match.index + 1;
if( match[1] == "@" ) {
output.push({type:"text",raw:(msg.substr(0, index))});
tmp = msg.substr(index+1);
var username = this.extractUsername(tmp);
if( username.length ) {
output.push({type:"mention",raw:"@"+username});
}else{
output.push({type:"text",raw:"@"});
}
msg = tmp.substr(String(username).length);
continue;
}
if( reHttp.exec(match[1]) ) {
output.push({type:"text",raw:(msg.substr(0, index))});
tmp = msg.substring(index);
//console.log(tmp)
var space = tmp.search(/\s/);
//console.log(space)
var url;
if( space != -1 ) url = tmp.substring(0,space); else url = tmp;
if( url.length ) {
output.push({type:"url",raw:url});
}
msg = tmp.substr(String(url).length);
continue;
}
if( reEmail.exec(match[1]) ) {
output.push({type:"text",raw:(msg.substr(0, index))});
tmp = msg.substring(index);
var space = tmp.search(/\s/);
var email;
if( space != -1 ) email = tmp.substring(0,space); else email = tmp;
if( email.length ) {
output.push({type:"email",raw:email});
}
msg = tmp.substr(String(email).length);
continue;
}
if( match[1] == "#" ) {
output.push({type:"text",raw:(msg.substr(0, index))});
tmp = msg.substr(index+1);
var hashtag = this.extractHashtag(tmp);
if( hashtag.length ) {
// var hashtag_lc='';
// for( var i = 0; i < hashtag.length; i++ ) {
// var c = hashtag[i];
// hashtag_lc += (c >= 'A' && c <= 'Z') ? c.toLowerCase() : c;
// }
output.push({type:"hashtag",raw:"#"+hashtag});
}else{
output.push({type:"text",raw:"@"});
}
msg = tmp.substr(String(hashtag).length);
continue;
}
/*if (reSplitCounter.exec(match[1])) {
output.append({type:"text",raw:(msg.substr(0, index))});
tmp = msg.substring(index);
if( tmp.length ) {
var splitCounter = $('<span class="splited-post-counter"></span>');
splitCounter.text(tmp);
output.append(splitCounter);
msg = "";
continue;
}
msg = tmp.substr(String(hashtag).length);
continue;
}*/
}
output.push({type:"text",raw:this.reverseHtmlEntities(msg)});
msg = "";
}
return output;
}
}

6
jsx/other/Accounts.js

@ -5,6 +5,7 @@ var EventListenerMixin = require('../common/EventListenerMixin.js'); @@ -5,6 +5,7 @@ var EventListenerMixin = require('../common/EventListenerMixin.js');
var AppSettingsMixin = require('../common/AppSettingsMixin.js');
var ImportAccountModalButton = require('../other/ImportAccountModalButton.js');
var ExportAccountModalButton = require('../other/ExportAccountModalButton.js');
var GenerateAccountModalButton = require('../other/GenerateAccountModalButton.js');
var ReactBootstrap = require('react-bootstrap')
@ -40,7 +41,10 @@ module.exports = Accounts = React.createClass({ @@ -40,7 +41,10 @@ module.exports = Accounts = React.createClass({
return (
<div key={"miniprofile:"+acc.name}>
<MiniProfile username={acc.name} pollIntervalProfile={thisComponent.props.pollIntervalProfile}/>
<p>{acc.status}</p>
<p>
{acc.status}
<ExportAccountModalButton username={acc.name}/>
</p>
</div>
);
})}

7
jsx/other/Conversation.js

@ -24,13 +24,10 @@ module.exports = Conversation = React.createClass({ @@ -24,13 +24,10 @@ module.exports = Conversation = React.createClass({
SafeStateChangeMixin,
EventListenerMixin('newpostbyuser')
],
contextTypes: {
router: React.PropTypes.func
},
getInitialState: function() {
return {
username: this.context.router.getCurrentParams().username,
postid: parseInt(this.context.router.getCurrentParams().postid),
username: this.props.params.username,
postid: parseInt(this.props.params.postid),
data: [],
postIdentifiers: {},
loading: true

171
jsx/other/ExportAccountModalButton.js

@ -0,0 +1,171 @@ @@ -0,0 +1,171 @@
var ReactBootstrap = require('react-bootstrap')
, Button = ReactBootstrap.Button
, ButtonGroup = ReactBootstrap.ButtonGroup
, Glyphicon = ReactBootstrap.Glyphicon
, Modal = ReactBootstrap.Modal
, Input = ReactBootstrap.Input
var React = require('react');
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js');
var SetIntervalMixin = require("../common/SetIntervalMixin.js");
module.exports = ExportAccountModalButton = React.createClass({
mixins: [
SafeStateChangeMixin
],
getInitialState: function () {
return {
isModalOpen: false,
useEncryption: true,
passphrase1: "",
passphrase2: "",
setupComplete: false,
encryptionInProgess: false,
encryptionComplete: false,
encryptedKey: "",
};
},
handleUseEncryptionChange: function(e) {
this.setState({
useEncryption: e.target.checked,
encryptionInProgess: false,
encryptionComplete: false,
});
},
handlePassphrase1Change: function(e) {
this.setState({
passphrase1: e.target.value,
encryptionInProgess: false,
encryptionComplete: false,
});
},
handlePassphrase2Change: function(e) {
this.setState({
passphrase2: e.target.value,
encryptionInProgess: false,
encryptionComplete: false,
});
},
handleToggle: function () {
this.setState({
isModalOpen: !this.state.isModalOpen
});
},
handleEncryption: function (e) {
e.preventDefault();
var thisComponent = this;
if(this.state.useEncryption){
var passphrase = this.state.passphrase1;
thisComponent.setStateSafe({encryptionInProgess: true});
Twister.getAccount(this.props.username).encryptPrivateKey(passphrase,
function(encryptedKey){
thisComponent.setStateSafe({encryptedKey: encryptedKey, encryptionComplete: true, encryptionInProgess: false});
});
}else{
var wif = Twister.getAccount(this.props.username).getPrivateKey();
thisComponent.setStateSafe({encryptedKey: wif, encryptionComplete: true, encryptionInProgess: false});
}
return;
},
render: function() {
var belowForm = (
<p/>
);
if(this.state.encryptionInProgess){
belowForm = (
<p>Encryption in progress. Sorry for the lag.</p>
)
}
if(this.state.encryptionComplete){
if(this.state.useEncryption){
var formattedBody = "Your encrypted key for Twister: "
+this.state.encryptedKey
+"\n\nAdvice: Print this email and note down your username and passphrase on the same piece of paper.";
var subject = "Encrypted Twister Private Key";
var mailToLink = "mailto:?body=" + encodeURIComponent(formattedBody) + "&subject=" + encodeURIComponent(subject);
belowForm = (
<p>
{"Your encrypted key: "+this.state.encryptedKey}
<Button href={mailToLink}>Send via Email</Button>
</p>
)
}else{
belowForm = (
<p>
{"Your private key: "+this.state.encryptedKey}
</p>
)
}
}
var submitButton = (
<Input type='submit' value='Encrypt Private Key' disabled={this.state.passphrase1!=this.state.passphrase2}/>
)
if(!this.state.useEncryption){
submitButton = (
<Input type='submit' value='Reveal Private Key'/>
);
}
return (
<Button onClick={this.handleToggle}>
Export Account
<Modal show={this.state.isModalOpen} bsStyle='primary' onHide={this.handleToggle}>
<Modal.Header>
<Glyphicon glyph='export'/>
</Modal.Header>
<Modal.Body>
<form onSubmit={this.handleEncryption}>
<Input
type='checkbox'
label='Use Encrpytion (highly recommended)'
checked={this.state.useEncryption}
onChange={this.handleUseEncryptionChange}
/>
<Input
type='password'
label='Passphrase'
value={this.state.passphrase1}
onChange={this.handlePassphrase1Change}
disabled={!this.state.useEncryption}
/>
<Input
type='password'
label='Repeat Passphrase'
value={this.state.passphrase2}
onChange={this.handlePassphrase2Change}
disabled={!this.state.useEncryption}
/>
{submitButton}
</form>
{belowForm}
</Modal.Body>
</Modal>
</Button>
);
}
});

120
jsx/other/GenerateAccountModalButton.js

@ -0,0 +1,120 @@ @@ -0,0 +1,120 @@
var ReactBootstrap = require('react-bootstrap')
, Button = ReactBootstrap.Button
, ButtonGroup = ReactBootstrap.ButtonGroup
, Glyphicon = ReactBootstrap.Glyphicon
, Modal = ReactBootstrap.Modal
, Input = ReactBootstrap.Input
var React = require('react');
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js');
var SetIntervalMixin = require("../common/SetIntervalMixin.js");
module.exports = GenerateAccountModalButton = React.createClass({
mixins: [SafeStateChangeMixin],
getInitialState: function () {
return {
isModalOpen: false,
username: "",
checkedUsername: "",
available: false,
};
},
handleUsernameChange: function(e) {
this.setState({username: e.target.value});
var thisComponent = this;
if(e.target.value.length){
Twister.checkUsernameAvailable(e.target.value,function(result){
thisComponent.setStateSafe({
checkedUsername: e.target.value,
available: result
});
})
}
},
handleToggle: function () {
this.setState({
isModalOpen: !this.state.isModalOpen
});
},
handleGenerateAccount: function (e) {
e.preventDefault();
var newusername = this.state.username;
Twister.generateClientSideAccount(newusername,function(newaccount){
console.log(newaccount._name);
var event = new CustomEvent('newaccountbyuser',{detail: newaccount});
//alert("scrolled to bottom")
window.dispatchEvent(event);
})
this.handleToggle();
return;
},
render: function() {
var showAvailable = (
<span/>
);
if(this.state.username.length==0){
showAvailable = (
<p>try a username</p>
);
}else{
if((this.state.username==this.state.checkedUsername)){
if(this.state.available){
showAvailable = (
<p>{this.state.username + " is available"}</p>
)
}else{
showAvailable = (
<p>{this.state.username + " is allready taken"}</p>
)
}
}else{
showAvailable = (
<p>checking ...</p>
);
}
}
return (
<Button onClick={this.handleToggle}>
Generate Account
<Modal show={this.state.isModalOpen} bsStyle='primary' onHide={this.handleToggle}>
<Modal.Header>
<Glyphicon glyph='certificate'/>
</Modal.Header>
<Modal.Body>
<form onSubmit={this.handleGenerateAccount}>
<Input
type='text'
label='Username'
value={this.state.username}
onChange={this.handleUsernameChange}
/>
{showAvailable}
<Input type='submit' value='Generate Account' data-dismiss="modal" />
</form>
</Modal.Body>
</Modal>
</Button>
);
}
});

1
package.json

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
},
"scripts": {
"test": "jsx jsx build-buffer && node test/PosContentTest.js",
"watch": "watch \"npm run pull-lib-and-build\" jsx",
"build": "jsx jsx build-buffer && browserify build-buffer/App.js -o build/app-bundle.js",
"pull-lib-and-build": "cd ../twister-lib-js && npm run bundle && cd ../twister-react && cp ../twister-lib-js/twister-lib.js build && jsx jsx build-buffer && browserify build-buffer/App.js -o build/app-bundle.js"
},

Loading…
Cancel
Save