From bd1f8c3ed21d38ca3007e986b31c1143ff8b503c Mon Sep 17 00:00:00 2001 From: Julian Steinwachs Date: Tue, 12 Jan 2016 14:17:50 +0100 Subject: [PATCH] edit profile and avatar --- docker/Dockerfile | 36 +++ docker/Dockerfile~ | 36 +++ docker/settings.json | 375 ++++++++++++++++++++++++++ jsx/common/FollowButton.js | 76 ++++++ jsx/other/ImportAccountModalButton.js | 92 +++++++ jsx/profile/EditAvatarModalButton.js | 145 ++++++++++ jsx/profile/EditProfileModalButton.js | 124 +++++++++ 7 files changed, 884 insertions(+) create mode 100644 docker/Dockerfile create mode 100644 docker/Dockerfile~ create mode 100644 docker/settings.json create mode 100644 jsx/common/FollowButton.js create mode 100644 jsx/other/ImportAccountModalButton.js create mode 100644 jsx/profile/EditAvatarModalButton.js create mode 100644 jsx/profile/EditProfileModalButton.js diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..586f513 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,36 @@ +# +# Dockerfile for building Twister peer-to-peer micro-blogging +# + +FROM ubuntu:14.04 + +# Install twister-core + +RUN apt-get update +RUN apt-get install -y git autoconf libtool build-essential libboost-all-dev libssl-dev libdb++-dev libminiupnpc-dev && apt-get clean +RUN git clone https://github.com/miguelfreitas/twister-core.git +RUN cd twister-core && \ + ./bootstrap.sh && \ + make + +RUN mkdir ~/.twister +RUN echo -e "rpcuser=user\nrpcpassword=pwd\nhtmldir=~/twister-react" > ~/.twister/twister.conf +RUN chmod 600 ~/.twister/twister.conf + +RUN git clone https://github.com/Tschaul/twister-react.git +RUN git clone https://github.com/Tschaul/twister-lib-js.git +RUN git clone https://github.com/digital-dreamer/twister-proxy.git + +RUN apt-get install -y nodejs nodejs-legacy npm + +RUN npm install -g browserify react-tools + +RUN cd twister-lib-js \ npm install + +RUN cd twister-react \ npm install \ npm run pull-lib-and-build + +RUN cd twister-proxy \ npm install + +COPY settings.json twister-proxy/ + +CMD cd twister-core \ ./twisterd & \ cd ../twister-proxy \ node twister-proxy.js & diff --git a/docker/Dockerfile~ b/docker/Dockerfile~ new file mode 100644 index 0000000..d67b0f8 --- /dev/null +++ b/docker/Dockerfile~ @@ -0,0 +1,36 @@ +# +# Dockerfile for building Twister peer-to-peer micro-blogging +# + +FROM ubuntu:14.04 + +# Install twister-core + +RUN apt-get update +RUN apt-get install -y git autoconf libtool build-essential libboost-all-dev libssl-dev libdb++-dev libminiupnpc-dev && apt-get clean +RUN git clone https://github.com/miguelfreitas/twister-core.git +RUN cd twister-core && \ + ./bootstrap.sh && \ + make + +RUN mkdir ~/.twister +RUN echo -e "rpcuser=user\nrpcpassword=pwd\nhtmldir=~/twister-react" > ~/.twister/twister.conf +RUN chmod 600 ~/.twister/twister.conf + +RUN git clone https://github.com/Tschaul/twister-react.git +RUN git clone https://github.com/Tschaul/twister-lib-js.git +RUN git clone https://github.com/digital-dreamer/twister-proxy.git + +RUN apt-get install -y nodejs nodejs-legacy npm + +RUN npm install -g browserify react-tools + +RUN cd twister-lib-js \ npm install + +RUN cd twister-react \ npm install \ npm run pull-lib-and-build + +RUN cd twister-proxy \ npm install + +COPY settings.json twister-proxy/ + + diff --git a/docker/settings.json b/docker/settings.json new file mode 100644 index 0000000..9eb116a --- /dev/null +++ b/docker/settings.json @@ -0,0 +1,375 @@ +{ + "Server": + { + "ssl_key_file": "insert/path/to/your/server-key-file", + "ssl_certificate_file": "insert/path/to/your/ssl-certificate", + "enable_https": false, + + "https_port": 443, + "http_port": 8080 + }, + + "RPC": + { + "host": "localhost", + "port": 28332, + "user": "user", + "password": "pwd" + }, + + "CallLimits": + [ + { + "name": "getbestblockhash", + "maxPerMinute": null, + "maxPerMinutePerIP": null + }, + { + "name": "getinfo", + "maxPerMinute": 999, + "maxPerMinutePerIP": 999 + }, + { + "name": "listwalletusers", + "maxPerMinute": null, + "maxPerMinutePerIP": null + }, + { + "name": "getblock", + "maxPerMinute": null, + "maxPerMinutePerIP": null + }, + { + "name": "dhtget", + "maxPerMinute": 999, + "maxPerMinutePerIP": 999 + }, + { + "name": "listusernamespartial", + "maxPerMinute": 999, + "maxPerMinutePerIP": 999 + }, + { + "name": "gettrendinghashtags", + "maxPerMinute": 999, + "maxPerMinutePerIP": 999 + }, + { + "name": "stop", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getblockcount", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getconnectioncount", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getpeerinfo", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "addnode", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "adddnsseed", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getaddednodeinfo", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getdifficulty", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getgenerate", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "setgenerate", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "gethashespersec", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getmininginfo", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "createwalletuser", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "backupwallet", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "walletpassphrase", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "walletpassphrasechange", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "walletlock", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "encryptwallet", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getrawmempool", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getblockhash", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "gettransaction", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "listtransactions", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "signmessage", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "verifymessage", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getwork", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getblocktemplate", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "submitblock", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "listsinceblock", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "dumpprivkey", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "dumppubkey", + "maxPerMinute": 999, + "maxPerMinutePerIP": 999 + }, + { + "name": "testvector", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "dumpwallet", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "importprivkey", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "importwallet", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getrawtransaction", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "createrawtransaction", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "decoderawtransaction", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "sendrawtransaction", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "sendnewusertransaction", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "verifychain", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getlastsoftcheckpoint", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "dhtput", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "dhtputraw", + "maxPerMinute": 999, + "maxPerMinutePerIP": 999 + }, + { + "name": "newpostmsg", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "newpostraw", + "maxPerMinute": 999, + "maxPerMinutePerIP": 999 + }, + { + "name": "newdirectmsg", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "newrtmsg", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getposts", + "maxPerMinute": 999, + "maxPerMinutePerIP": 999 + }, + { + "name": "getdirectmsgs", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getmentions", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "setspammsg", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getspammsg", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "follow", + "maxPerMinute": 999, + "maxPerMinutePerIP": 999 + }, + { + "name": "unfollow", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getfollowing", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getlasthave", + "maxPerMinute": 999, + "maxPerMinutePerIP": 999 + }, + { + "name": "getnumpieces", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "rescandirectmsgs", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "recheckusertorrent", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "getspamposts", + "maxPerMinute": 999, + "maxPerMinutePerIP": 999 + }, + { + "name": "torrentstatus", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + }, + { + "name": "search", + "maxPerMinute": 0, + "maxPerMinutePerIP": null + } + ], + + "LogAsAttackThreshold": + { + "callsOverLimits": 30, + "invalidRequests": 30, + "forbiddenCalls": 30 + } +} diff --git a/jsx/common/FollowButton.js b/jsx/common/FollowButton.js new file mode 100644 index 0000000..cbb3714 --- /dev/null +++ b/jsx/common/FollowButton.js @@ -0,0 +1,76 @@ + +var ReactBootstrap = require('react-bootstrap') + , OverlayMixin = ReactBootstrap.OverlayMixin + , 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'); + +module.exports = FollowButton = React.createClass({ + mixins: [SafeStateChangeMixin], + getInitialState: function () { + return { + hasLoaded: false, + isCurrentlyFollowing: false + }; + }, + handleClick: function (e) { + + thisComponent = this; + + e.preventDefault(); + + if(this.state.hasLoaded){ + + var methodName = thisComponent.state.isCurrentlyFollowing ? "unfollow" : "follow"; + var newValForState = !thisComponent.state.isCurrentlyFollowing; + var eventName = thisComponent.state.isCurrentlyFollowing ? "unfollowbyuser" : "followbyuser"; + + Twister.getAccount(thisComponent.props.activeAccount)[methodName]( + thisComponent.props.username, + function(following){ + + thisComponent.setStateSafe({isCurrentlyFollowing:newValForState}); + + Twister.getAccount(thisComponent.props.activeAccount).activateTorrent(thisComponent.props.username) + + }); + + } + + return; + }, + componentDidMount: function () { + + thisComponent = this; + + Twister.getUser(thisComponent.props.activeAccount).doFollowings(function(followings){ + if(followings.map(function(fol){ + return fol.getUsername(); + }).indexOf(thisComponent.props.username)<0){ + thisComponent.setStateSafe({isCurrentlyFollowing: false, hasLoaded: true}); + }else{ + thisComponent.setStateSafe({isCurrentlyFollowing: true, hasLoaded: true}); + } + }) + }, + render: function() { + + if(!this.state.hasLoaded || this.props.activeAccount==this.props.username){ + return ( + + ) + } + + var methodName = this.state.isCurrentlyFollowing ? "Unfollow" : "Follow"; + + return ( + + ); + } +}); \ No newline at end of file diff --git a/jsx/other/ImportAccountModalButton.js b/jsx/other/ImportAccountModalButton.js new file mode 100644 index 0000000..d5f1e1b --- /dev/null +++ b/jsx/other/ImportAccountModalButton.js @@ -0,0 +1,92 @@ + +var ReactBootstrap = require('react-bootstrap') + , OverlayMixin = ReactBootstrap.OverlayMixin + , 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 = ImportAccountModalButton = React.createClass({ + mixins: [OverlayMixin], + getInitialState: function () { + return { + isModalOpen: false, + privkey: "", + username: "", + }; + }, + handlePrivkeyChange: function(e) { + this.setState({privkey: e.target.value}); + }, + handleUsernameChange: function(e) { + this.setState({username: e.target.value}); + }, + handleToggle: function () { + this.setState({ + isModalOpen: !this.state.isModalOpen + }); + }, + handleImportAccount: function (e) { + + e.preventDefault(); + + var newprivkey = this.state.privkey; + var newusername = this.state.username; + + Twister.importClientSideAccount(newusername,newprivkey,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() { + + + return ( + + ); + }, + renderOverlay: function() { + + if (!this.state.isModalOpen) { + return ; + } + + return ( + } onRequestHide={this.handleToggle}> +
+
+ + + +
+
+
+ ); + + } +}); \ No newline at end of file diff --git a/jsx/profile/EditAvatarModalButton.js b/jsx/profile/EditAvatarModalButton.js new file mode 100644 index 0000000..33db699 --- /dev/null +++ b/jsx/profile/EditAvatarModalButton.js @@ -0,0 +1,145 @@ + + + + +var ReactBootstrap = require('react-bootstrap') + , OverlayMixin = ReactBootstrap.OverlayMixin + , 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 = EditAvatarModalButton = React.createClass({ + mixins: [OverlayMixin,SafeStateChangeMixin], + getInitialState: function () { + return { + isModalOpen: false, + avatar: this.props.avatar + }; + }, + handleAvatarChange: function(event) { + + selectedFile=event.target.files[0]; + + var thisComponent = this; + + var targetWidth = 64; + + var dataUrl = ""; + var reader = new FileReader(); + reader.onloadend = function () { + dataUrl = reader.result; + + var sourceImage = new Image(); + + sourceImage.onload = function () { + // Create a canvas with the desired dimensions + var canvas = document.createElement("canvas"); + + var imWidth = sourceImage.width; + var imHeight = sourceImage.height; + + var sx = 0; + var sy = 0; + + sourceWidth = imWidth; + + if (imWidth > imHeight) { + sx = (imWidth - imHeight) / 2; + sourceWidth = imHeight; + } else { + sy = (imHeight - imWidth) / 2; + } + + canvas.width = targetWidth; + canvas.height = targetWidth; + + // Scale and draw the source image to the canvas + canvas.getContext("2d").drawImage(sourceImage, sx, sy, sourceWidth, sourceWidth, 0, 0, targetWidth, targetWidth); + + var imgURL = undefined; + for (var quality = 1.0; (!imgURL || imgURL.length > 4096) && quality > 0.1; quality -= 0.05) { + imgURL = canvas.toDataURL('image/jpeg', quality); + } + + thisComponent.setStateSafe({avatar: imgURL}); + }; + sourceImage.src = dataUrl; + + }; + reader.readAsDataURL(selectedFile); + }, + handleToggle: function () { + this.setState({ + isModalOpen: !this.state.isModalOpen + }); + }, + handleAvatarEdit: function (e) { + + e.preventDefault(); + + var newavatar = this.state.avatar; + + if(newavatar == "img/genericPerson.png") newavatar = ""; + + var thisComponent = this; + + + Twister.getAccount(this.props.activeAccount).updateAvatar(newavatar,function(avatar){ + + console.log(avatar._data); + + var event = new CustomEvent('avatarupdatebyuser',{detail: avatar}); + //alert("scrolled to bottom") + window.dispatchEvent(event); + + }); + + this.handleToggle(); + + return; + }, + render: function() { + + if(this.props.activeAccount!=this.props.username){ + return ( + + ) + } + + return ( + + ); + }, + renderOverlay: function() { + + if (!this.state.isModalOpen) { + return ; + } + + return ( + } onRequestHide={this.handleToggle}> +
+
+ + + +
+
+
+ ); + + } +}); \ No newline at end of file diff --git a/jsx/profile/EditProfileModalButton.js b/jsx/profile/EditProfileModalButton.js new file mode 100644 index 0000000..b8ce180 --- /dev/null +++ b/jsx/profile/EditProfileModalButton.js @@ -0,0 +1,124 @@ + +var ReactBootstrap = require('react-bootstrap') + , OverlayMixin = ReactBootstrap.OverlayMixin + , 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 = EditProfileModalButton = React.createClass({ + mixins: [OverlayMixin], + getInitialState: function () { + return { + isModalOpen: false, + fullname: this.props.fullname, + location: this.props.location, + bio: this.props.bio, + url: this.props.url + }; + }, + handleFullnameChange: function(e) { + this.setState({fullname: e.target.value}); + }, + handleLocationChange: function(e) { + this.setState({location: e.target.value}); + }, + handleBioChange: function(e) { + this.setState({bio: e.target.value}); + }, + handleUrlChange: function(e) { + this.setState({url: e.target.value}); + }, + handleToggle: function () { + this.setState({ + isModalOpen: !this.state.isModalOpen + }); + }, + handleProfileEdit: function (e) { + + e.preventDefault(); + + var newProfileFields = { + fullname: this.state.fullname, + location: this.state.location, + bio: this.state.bio, + url: this.state.url, + }; + + + Twister.getAccount(this.props.activeAccount).updateProfileFields(newProfileFields,function(profile){ + + console.log(profile._data); + + var event = new CustomEvent('profileupdatebyuser',{detail: profile}); + //alert("scrolled to bottom") + window.dispatchEvent(event); + + }); + + this.handleToggle(); + + return; + }, + render: function() { + + if(this.props.activeAccount!=this.props.username){ + return ( + + ) + } + + return ( + + ); + }, + renderOverlay: function() { + + if (!this.state.isModalOpen) { + return ; + } + + return ( + } onRequestHide={this.handleToggle}> +
+
+ + + + + +
+
+
+ ); + + } +}); \ No newline at end of file