mirror of
https://github.com/twisterarmy/twister-react.git
synced 2025-01-12 16:07:55 +00:00
post content is now parsed to generate links to urls and profiles
This commit is contained in:
parent
1345e50bf1
commit
9afd7f871b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build-buffer
|
File diff suppressed because it is too large
Load Diff
@ -31151,6 +31151,44 @@ Twister.trimCache = function (timestamp) {
|
||||
|
||||
}
|
||||
|
||||
Twister._activeQueryIds = {};
|
||||
|
||||
Twister.raiseQueryId = function (id) {
|
||||
|
||||
if (id) {
|
||||
if(!Twister._activeQueryIds[id]){
|
||||
Twister._activeQueryIds[id]={func:null,count:1};
|
||||
}else{
|
||||
Twister._activeQueryIds[id].count++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Twister.bumpQueryId = function (id) {
|
||||
|
||||
if (id) {
|
||||
Twister._activeQueryIds[id].count--;
|
||||
if (Twister._activeQueryIds[id].count==0) {
|
||||
if (Twister._activeQueryIds[id].func) {
|
||||
Twister._activeQueryIds[id].func();
|
||||
}
|
||||
delete Twister._activeQueryIds[id];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Twister.onQueryComplete = function (id, cbfunc){
|
||||
|
||||
if(!Twister._activeQueryIds[id]){
|
||||
Twister._activeQueryIds[id]={func:cbfunc,count:0};
|
||||
}else{
|
||||
Twister._activeQueryIds[id].func=cbfunc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Twister;
|
||||
|
||||
},{"./ServerWallet/TwisterAccount.js":139,"./TwisterHashtag.js":145,"./TwisterPromotedPosts.js":149,"./TwisterResource.js":152,"./TwisterUser.js":155}],143:[function(require,module,exports){
|
||||
@ -32594,6 +32632,36 @@ TwisterResource.prototype.inCache = function () {
|
||||
return (this._lastUpdate>0);
|
||||
}
|
||||
|
||||
TwisterResource.prototype._wrapPromise = function (context,handler,cbfunc,querySettings) {
|
||||
|
||||
if ( typeof cbfunc != "function" ) {
|
||||
|
||||
querySettings = cbfunc;
|
||||
|
||||
cbfunc = null;
|
||||
|
||||
}
|
||||
|
||||
if (!querySettings){ querySettings = {}; }
|
||||
|
||||
|
||||
if (querySettings["errorfunc"]) {
|
||||
var errorfuncFromQuerySettings = querySettings["errorfunc"];
|
||||
} else {
|
||||
var errorfuncFromQuerySettings = null;
|
||||
}
|
||||
delete querySettings["errorfunc"];
|
||||
|
||||
return new Promise ( function ( resolve, reject ) {
|
||||
|
||||
querySettings["errorfunc"]=reject;
|
||||
|
||||
handler.call(context,resolve,querySettings);
|
||||
|
||||
} ).then(cbfunc,errorfuncFromQuerySettings);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether cached resource is outdated and invokes an update if needed. Calls cbfunc on the resource when done.
|
||||
* @function
|
||||
@ -32602,6 +32670,8 @@ TwisterResource.prototype.inCache = function () {
|
||||
*/
|
||||
TwisterResource.prototype._checkQueryAndDo = function (cbfunc,querySettings) {
|
||||
|
||||
|
||||
|
||||
if (querySettings===undefined) {querySettings={};}
|
||||
//else {console.log(querySettings)}
|
||||
|
||||
@ -32611,8 +32681,9 @@ TwisterResource.prototype._checkQueryAndDo = function (cbfunc,querySettings) {
|
||||
|
||||
if (!thisResource._updateInProgress) {
|
||||
|
||||
thisResource._activeQuerySettings = querySettings;
|
||||
thisResource._activeQuerySettings = JSON.parse(JSON.stringify(querySettings));
|
||||
thisResource._updateInProgress = true;
|
||||
Twister.raiseQueryId(thisResource._activeQuerySettings["queryId"]);
|
||||
|
||||
var outdatedTimestamp = 0;
|
||||
|
||||
@ -32624,17 +32695,19 @@ TwisterResource.prototype._checkQueryAndDo = function (cbfunc,querySettings) {
|
||||
|
||||
thisResource._log("resource present in cache");
|
||||
|
||||
Twister.bumpQueryId(thisResource._activeQuerySettings["queryId"]);
|
||||
thisResource._activeQuerySettings = {};
|
||||
thisResource._updateInProgress = false;
|
||||
|
||||
} else {
|
||||
|
||||
thisResource._log("resource not in cache. querying");
|
||||
|
||||
thisResource._queryAndDo(function(newresource){
|
||||
|
||||
thisResource._do(cbfunc);
|
||||
|
||||
thisResource._log("resource not in cache. querying");
|
||||
|
||||
Twister.bumpQueryId(thisResource._activeQuerySettings["queryId"]);
|
||||
thisResource._activeQuerySettings = {};
|
||||
thisResource._updateInProgress = false;
|
||||
|
||||
@ -32654,6 +32727,7 @@ TwisterResource.prototype._checkQueryAndDo = function (cbfunc,querySettings) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -32709,9 +32783,10 @@ TwisterResource.prototype.setQuerySettings = function (settings) {
|
||||
TwisterResource.prototype._handleError = function (error) {
|
||||
|
||||
this._updateInProgress = false;
|
||||
|
||||
this.getQuerySetting("errorfunc").call(this,error);
|
||||
|
||||
Twister.bumpQueryId(this._activeQuerySettings["queryId"]);
|
||||
this._activeQuerySettings={};
|
||||
|
||||
}
|
||||
|
||||
TwisterResource.prototype._log = function (log) {
|
||||
@ -32763,18 +32838,45 @@ TwisterResource.prototype.RPC = function (method, params, resultFunc, errorFunc)
|
||||
}, function(error, response, body) {
|
||||
|
||||
if (error) {
|
||||
|
||||
error.message = "Host not reachable (http error).";
|
||||
|
||||
thisResource._handleError(error)
|
||||
|
||||
thisResource._handleError({
|
||||
message: "Host not reachable.",
|
||||
data: error.code,
|
||||
code: 32090
|
||||
})
|
||||
|
||||
} else {
|
||||
var res = JSON.parse(body);
|
||||
if (res.error) {
|
||||
|
||||
if (response.statusCode<200 || response.statusCode>299) {
|
||||
|
||||
thisResource._handleError({
|
||||
message: "Request was not processed successfully (http error: "+response.statusCode+").",
|
||||
data: response.statusCode,
|
||||
code: 32091
|
||||
})
|
||||
|
||||
} else {
|
||||
|
||||
try {
|
||||
|
||||
var res = JSON.parse(body);
|
||||
|
||||
if (res.error) {
|
||||
thisResource._handleError(res.error);
|
||||
} else {
|
||||
} else {
|
||||
resultFunc(res.result);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
thisResource._handleError({
|
||||
message: "An error occurred while parsing the JSON response body.",
|
||||
code: 32092
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -33405,6 +33507,8 @@ var TwisterFollowings = require('./TwisterFollowings.js');
|
||||
var TwisterPubKey = require('./TwisterPubKey.js');
|
||||
var TwisterStream = require('./TwisterStream.js');
|
||||
var TwisterMentions = require('./TwisterMentions.js');
|
||||
var TwisterResource = require('./TwisterResource.js');
|
||||
var inherits = require('inherits');
|
||||
|
||||
/**
|
||||
* Describes a user in {@ Twister}. Allows for accessing all public onformation about this user.
|
||||
@ -33428,6 +33532,8 @@ function TwisterUser(name,scope) {
|
||||
|
||||
}
|
||||
|
||||
inherits(TwisterUser,TwisterResource);
|
||||
|
||||
module.exports = TwisterUser;
|
||||
|
||||
TwisterUser.prototype.trim = function () {
|
||||
@ -33505,7 +33611,11 @@ TwisterUser.prototype._doPubKey = function (cbfunc, querySettings) {
|
||||
}
|
||||
|
||||
TwisterUser.prototype.doProfile = function (cbfunc, querySettings) {
|
||||
this._profile._checkQueryAndDo(cbfunc, querySettings);
|
||||
return this._wrapPromise(
|
||||
this._profile,
|
||||
this._profile._checkQueryAndDo,
|
||||
cbfunc,
|
||||
querySettings);
|
||||
};
|
||||
|
||||
TwisterUser.prototype.getProfile = function () {
|
||||
@ -33513,7 +33623,11 @@ TwisterUser.prototype.getProfile = function () {
|
||||
};
|
||||
|
||||
TwisterUser.prototype.doAvatar = function (cbfunc, querySettings) {
|
||||
this._avatar._checkQueryAndDo(cbfunc, querySettings);
|
||||
return this._wrapPromise(
|
||||
this._avatar,
|
||||
this._avatar._checkQueryAndDo,
|
||||
cbfunc,
|
||||
querySettings);
|
||||
};
|
||||
|
||||
TwisterUser.prototype.getAvatar = function () {
|
||||
@ -33521,7 +33635,11 @@ TwisterUser.prototype.getAvatar = function () {
|
||||
};
|
||||
|
||||
TwisterUser.prototype.doFollowings = function (cbfunc, querySettings) {
|
||||
this._followings._checkQueryAndDo(cbfunc, querySettings);
|
||||
return this._wrapPromise(
|
||||
this._followings,
|
||||
this._followings._checkQueryAndDo,
|
||||
cbfunc,
|
||||
querySettings);
|
||||
};
|
||||
|
||||
TwisterUser.prototype.getFollowings = function () {
|
||||
@ -33529,11 +33647,25 @@ TwisterUser.prototype.getFollowings = function () {
|
||||
};
|
||||
|
||||
TwisterUser.prototype.doStatus = function (cbfunc, querySettings) {
|
||||
this._stream._checkQueryAndDo(cbfunc, querySettings);
|
||||
return this._wrapPromise(
|
||||
this._stream,
|
||||
this._stream._checkQueryAndDo,
|
||||
cbfunc,
|
||||
querySettings);
|
||||
};
|
||||
|
||||
TwisterUser.prototype.doPost = function (id, cbfunc, querySettings) {
|
||||
this._stream._doPost(id, cbfunc, querySettings);
|
||||
|
||||
var thisStream = this._stream;
|
||||
|
||||
return this._wrapPromise(
|
||||
thisStream,
|
||||
function(cb,qs){
|
||||
thisStream._doPost(id, cb, qs);
|
||||
},
|
||||
cbfunc,
|
||||
querySettings);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -33547,7 +33679,11 @@ TwisterUser.prototype.getPost = function (id) {
|
||||
|
||||
TwisterUser.prototype.doMentions = function (cbfunc, querySettings) {
|
||||
|
||||
this._mentions._checkQueryAndDo(cbfunc);
|
||||
return this._wrapPromise(
|
||||
this._mentions,
|
||||
this._mentions._checkQueryAndDo,
|
||||
cbfunc,
|
||||
querySettings);
|
||||
|
||||
}
|
||||
|
||||
@ -33560,7 +33696,7 @@ TwisterUser.prototype.doLatestPostsUntil = function (cbfunc, querySettings) {
|
||||
this._stream._doUntil(cbfunc, querySettings);
|
||||
|
||||
}
|
||||
},{"./TwisterAvatar.js":143,"./TwisterFollowings.js":144,"./TwisterMentions.js":146,"./TwisterProfile.js":148,"./TwisterPubKey.js":150,"./TwisterStream.js":154}],156:[function(require,module,exports){
|
||||
},{"./TwisterAvatar.js":143,"./TwisterFollowings.js":144,"./TwisterMentions.js":146,"./TwisterProfile.js":148,"./TwisterPubKey.js":150,"./TwisterResource.js":152,"./TwisterStream.js":154,"inherits":52}],156:[function(require,module,exports){
|
||||
|
||||
},{}],157:[function(require,module,exports){
|
||||
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
|
||||
|
16
css/main.css
16
css/main.css
@ -64,3 +64,19 @@ body.modal-open {
|
||||
opacity: 0.01;
|
||||
}
|
||||
|
||||
.mention {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.hashtag {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.url {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.email {
|
||||
color: coral;
|
||||
}
|
||||
|
||||
|
@ -197,7 +197,7 @@ initializeApp = function () {
|
||||
|
||||
Twister.deserializeCache(JSON.parse(localStorage.getItem("twister-cache")));
|
||||
|
||||
Twister.setup({logfunc: function(log){console.log(log)}})
|
||||
//Twister.setup({logfunc: function(log){console.log(log)}})
|
||||
|
||||
var accounts = Twister.getAccounts();
|
||||
|
||||
|
@ -12,6 +12,7 @@ var React = require('react');
|
||||
|
||||
var SetIntervalMixin = require("../common/SetIntervalMixin.js");
|
||||
var SafeStateChangeMixin = require('../common/SafeStateChangeMixin.js');
|
||||
var PostContent = require('../common/PostContent.js');
|
||||
|
||||
module.exports = Post = React.createClass({
|
||||
mixins: [SetIntervalMixin,SafeStateChangeMixin],
|
||||
@ -104,7 +105,7 @@ module.exports = Post = React.createClass({
|
||||
</Col>
|
||||
<Col xs={9} md={9}>
|
||||
<strong>{this.state.fullname}</strong>
|
||||
{post.getContent()}
|
||||
<PostContent content={post.getContent()}/>
|
||||
</Col>
|
||||
<Col xs={1} md={1} className="fullytight text-align-right">{this.state.timeAgo}</Col>
|
||||
</Row>
|
||||
|
205
jsx/common/PostContent.js
Normal file
205
jsx/common/PostContent.js
Normal file
@ -0,0 +1,205 @@
|
||||
|
||||
var React = require('react');
|
||||
|
||||
module.exports = Post = React.createClass({
|
||||
|
||||
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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
},
|
||||
reverseHtmlEntities: function(str) {
|
||||
return str
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/&/g, '&');
|
||||
},
|
||||
parseContent: function( 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});
|
||||
}
|
||||
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);
|
||||
var space = tmp.search(/[ \n\t]/);
|
||||
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(/[ \n\t]/);
|
||||
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});
|
||||
|
||||
}
|
||||
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:(msg.substr(0, index))});
|
||||
msg = "";
|
||||
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
},
|
||||
render: function() {
|
||||
|
||||
var parsedContent = this.parseContent(this.props.content);
|
||||
|
||||
//console.log(parsedContent)
|
||||
|
||||
var ret = parsedContent.map(function(item,index){
|
||||
//console.log(item.raw)
|
||||
switch(item.type) {
|
||||
case "mention":
|
||||
return (
|
||||
<a key={index} className="text-muted" href={"#/profile/"+item.raw.substr(1)}>{item.raw}</a>
|
||||
)
|
||||
case "hashtag":
|
||||
return (
|
||||
<span key={index} className="text-muted">{item.raw}</span>
|
||||
)
|
||||
case "url":
|
||||
return (
|
||||
<a key={index} className="text-primary" href={item.raw} target="_blank">{item.raw}</a>
|
||||
)
|
||||
case "email":
|
||||
return (
|
||||
<span key={index} className="text-primary">{item.raw}</span>
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<span key={index}>{item.raw}</span>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
//console.log(ret);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ret}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
<div className="post-avatar">
|
||||
<img src={this.state.avatar}/>
|
||||
</div>
|
||||
<div className="post-bulk">
|
||||
<div className="post-username">
|
||||
<span className="post-fullname">{this.state.fullname} </span>
|
||||
@{post.username} - {post.id}
|
||||
|
||||
</div>
|
||||
<div className="post-timestamp">{post.timestamp}</div>
|
||||
<div className="post-content">{post.content}</div>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
*/
|
5
tests/PostContentTest.js
Normal file
5
tests/PostContentTest.js
Normal file
@ -0,0 +1,5 @@
|
||||
var PostContent = require("../jsx/common/PostContent.js");
|
||||
|
||||
var sut = new PostContent();
|
||||
|
||||
console.log(sut.parseContent("asdas julian.steinwachs@fau.de fgh"));
|
Loading…
Reference in New Issue
Block a user