conversation, repeat and fav caching, new api for getting favs&repeats in same request, etc

This commit is contained in:
Hannes Mannerheim 2015-02-25 21:16:24 +01:00
parent af1635825d
commit 1e36c997f0
6 changed files with 458 additions and 331 deletions

View File

@ -117,7 +117,9 @@ class QvitterPlugin extends Plugin {
public function onRouterInitialized($m) public function onRouterInitialized($m)
{ {
$m->connect('api/qvitter.json', array('action' => 'qvitterapi')); $m->connect('api/qvitter/favs_and_repeats/:notice_id.json',
array('action' => 'ApiFavsAndRepeats'),
array('notice_id' => '[0-9]+'));
$m->connect('api/statuses/public_and_external_timeline.:format', $m->connect('api/statuses/public_and_external_timeline.:format',
array('action' => 'ApiTimelinePublicAndExternal', array('action' => 'ApiTimelinePublicAndExternal',
'format' => '(xml|json|rss|atom|as)')); 'format' => '(xml|json|rss|atom|as)'));
@ -467,7 +469,7 @@ class QvitterPlugin extends Plugin {
* @return boolean hook return * @return boolean hook return
*/ */
function onNoticeSimpleStatusArray($notice, &$twitter_status) function onNoticeSimpleStatusArray($notice, &$twitter_status, $scoped)
{ {
// groups // groups
@ -478,7 +480,12 @@ class QvitterPlugin extends Plugin {
} }
$twitter_status['statusnet_in_groups'] = $group_addressees; $twitter_status['statusnet_in_groups'] = $group_addressees;
// include the repeat-id, which we need when unrepeating later
if($twitter_status['repeated'] === true) {
$repeated = Notice::pkeyGet(array('profile_id' => $scoped->id,
'repeat_of' => $notice->id));
$twitter_status['repeated_id'] = $repeated->id;
}
// thumb urls // thumb urls

View File

@ -0,0 +1,167 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Returns both favs and repeats for a notice
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Ouputs information for a user, specified by ID or screen name.
* The user's most recent status will be returned inline.
*/
class ApiFavsAndRepeatsAction extends ApiPrivateAuthAction
{
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->notice_id = $this->trimmed('notice_id');
$this->original = Notice::getKV('id', $this->notice_id);
if (empty($this->original)) {
// TRANS: Client error displayed trying to display redents of a non-exiting notice.
$this->clientError(_('No such notice.'),
400, $this->format);
return false;
}
$cnt = $this->trimmed('count');
if (empty($cnt) || !is_integer($cnt)) {
$this->cnt = 100;
} else {
$this->cnt = min((int)$cnt, self::MAXCOUNT);
}
return true;
}
/**
* Handle the request
*
* Check the format and show the user info
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
// favs
$fave = new Fave();
$fave->selectAdd();
$fave->selectAdd('user_id');
$fave->selectAdd('modified');
$fave->notice_id = $this->original->id;
$fave->orderBy('modified');
if (!is_null($this->cnt)) {
$fave->limit(0, $this->cnt);
}
$fav_ids = $fave->fetchAll('user_id', 'modified');
// get nickname and profile image
$fav_ids_with_profile_data = array();
$i=0;
foreach($fav_ids as $id=>$time) {
$profile = Profile::getKV('id', $id);
$fav_ids_with_profile_data[$i]['user_id'] = $id;
$fav_ids_with_profile_data[$i]['nickname'] = $profile->nickname;
$fav_ids_with_profile_data[$i]['fullname'] = $profile->fullname;
$fav_ids_with_profile_data[$i]['profileurl'] = $profile->profileurl;
$fav_ids_with_profile_data[$i]['time'] = strtotime($time);
$profile = new Profile();
$profile->id = $id;
$avatarurl = $profile->avatarUrl(48);
$fav_ids_with_profile_data[$i]['avatarurl'] = $avatarurl;
$i++;
}
// repeats
$notice = new Notice();
$notice->selectAdd(); // clears it
$notice->selectAdd('profile_id');
$notice->selectAdd('created');
$notice->repeat_of = $this->original->id;
$notice->orderBy('created, id'); // NB: asc!
if (!is_null($this->cnt)) {
$notice->limit(0, $this->cnt);
}
$repeat_ids = $notice->fetchAll('profile_id','created');
// get nickname and profile image
$repeat_ids_with_profile_data = array();
$i=0;
foreach($repeat_ids as $id=>$time) {
$profile = Profile::getKV('id', $id);
$repeat_ids_with_profile_data[$i]['user_id'] = $id;
$repeat_ids_with_profile_data[$i]['nickname'] = $profile->nickname;
$repeat_ids_with_profile_data[$i]['fullname'] = $profile->fullname;
$repeat_ids_with_profile_data[$i]['profileurl'] = $profile->profileurl;
$repeat_ids_with_profile_data[$i]['time'] = strtotime($time);
$profile = new Profile();
$profile->id = $id;
$avatarurl = $profile->avatarUrl(48);
$repeat_ids_with_profile_data[$i]['avatarurl'] = $avatarurl;
$i++;
}
$favs_and_repeats = array('favs'=>$fav_ids_with_profile_data,'repeats'=>$repeat_ids_with_profile_data);
$this->initDocument('json');
$this->showJsonObjects($favs_and_repeats);
$this->endDocument('json');
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

View File

@ -340,6 +340,7 @@ function unRequeet(this_stream_item, this_action, my_rq_id) {
if(this_stream_item.children('.queet').children('.context').find('.requeet-text').children('a').length<1) { if(this_stream_item.children('.queet').children('.context').find('.requeet-text').children('a').length<1) {
this_stream_item.children('.queet').children('.context').remove(); this_stream_item.children('.queet').children('.context').remove();
} }
getFavsAndRequeetsForQueet(this_stream_item, this_stream_item.attr('data-quitter-id'));
} }
else { else {
remove_spinner(); remove_spinner();
@ -356,23 +357,39 @@ function unRequeet(this_stream_item, this_action, my_rq_id) {
· ·
· Gets favs or requeets for a queet from api · Gets favs or requeets for a queet from api
· ·
· @param apiaction: i.e. 'favs' or 'requeets' · @param q: stream item object
· @param qid: the queet id · @param qid: the queet id
· @param actionOnSuccess: callback function
· ·
· · · · · · · · · */ · · · · · · · · · */
function getFavsOrRequeetsForQueet(apiaction,qid,actionOnSuccess) { function getFavsAndRequeetsForQueet(q,qid) {
if(apiaction=="requeets") { apiaction="retweets"; } // we might mix this up...
$.ajax({ url: window.apiRoot + "statuses/" + apiaction + "/" + qid + ".json?t=" + timeNow(), // get immediately from localstorage cache
if(localStorageIsEnabled()) {
if(typeof localStorage['favsAndRequeets-' + qid] != 'undefined' && localStorage['favsAndRequeets-' + qid] !== null) {
showFavsAndRequeetsInQueet(q, JSON.parse(localStorage['favsAndRequeets-' + qid]));
}
}
$.ajax({ url: window.apiRoot + "qvitter/favs_and_repeats/" + qid + ".json?t=" + timeNow(),
type: "GET", type: "GET",
dataType: 'json', dataType: 'json',
success: function(data) { success: function(data) {
if(data.length > 0) { if(data.favs.length > 0 || data.repeats.length > 0) {
actionOnSuccess(data); if(localStorageIsEnabled()) { localStorage['favsAndRequeets-' + qid] = JSON.stringify(data);} // cache response
if(q.hasClass('expanded') && !q.hasClass('collapsing')) {
showFavsAndRequeetsInQueet(q,data);
}
} }
else { else {
actionOnSuccess(false); // remove from cache and DOM if all favs and repeats are deleted
if(localStorageIsEnabled()) {
if(typeof localStorage['favsAndRequeets-' + qid] != 'undefined' && localStorage['favsAndRequeets-' + qid] !== null) {
delete localStorage['favsAndRequeets-' + qid];
}
}
q.children('.queet').find('.stats').remove();
} }
}, },
error: function(data) { error: function(data) {

View File

@ -36,66 +36,90 @@
/* · /* ·
· ·
· Show favs or requeets in queet element · Show favs and requeets in queet element
· ·
· @param q: queet jQuery object · @param q: queet jQuery object
· @param users: object with users that has faved or requeeted · @param data: object with users that has faved and requeeted
· @param type: fav or requeet
· ·
· · · · · · · · · */ · · · · · · · · · */
function showFavsOrRequeetsInQueet(q,users,type) { function showFavsAndRequeetsInQueet(q,data) {
// label maybe plural // remove any existing stats container and add a new empty one
if(users.length == 1) { if(q.children('.queet').find('ul.stats').length > 0) {
if(type=='favs') { var label = window.sL.favoriteNoun; } q.children('.queet').find('ul.stats').remove();
else if(type=='requeets') { var label = window.sL.requeetNoun; }
}
else if(users.length > 1) {
if(type=='favs') { var label = window.sL.favoritesNoun; }
else if(type=='requeets') { var label = window.sL.requeetsNoun; }
} }
var html = '<li class="x-count"><a>' + label + ' </a><strong>' + users.length + '</strong></li>'; q.children('.queet').find('.queet-stats-container').prepend('<ul class="stats"><li class="avatar-row"></li></ul>');
// add fav and rq-container if it doesn't exist // add favs
if(!q.find('ul.stats').length > 0) { if(data.favs.length > 0) {
q.find('.queet-stats-container').prepend('<ul class="stats"><li class="avatar-row"></li></ul>');
if(data.favs.length == 1) {
var favLabel = window.sL.favoriteNoun;
}
else if(data.favs.length > 1) {
var favLabel = window.sL.favoritesNoun;
} }
// requeets always first if(q.children('.queet').find('.fav-count').length>0) {
if(type=='favs') { q.children('.queet').find('.fav-count').children('strong').html(data.favs.length);
q.find('li.avatar-row').before(html.replace('x-count','fav-count')); }
else {
q.children('.queet').find('li.avatar-row').before('<li class="fav-count"><a>' + favLabel + ' </a><strong>' + data.favs.length + '</strong></li>');
} }
else if(type=='requeets') {
q.find('ul.stats').prepend(html.replace('x-count','rq-count'));
} }
// show max seven avatars // add repeats
var avatarnum = q.find('.avatar').length; if(data.repeats.length > 0) {
if(avatarnum < 7) {
$.each(users,function(){
// api return little different objects for fav and rq if(data.repeats.length == 1) {
var userObj = new Object(); var repeatsLabel = window.sL.requeetNoun;
if(type=='favs') {
userObj = this;
} }
else if(type=='requeets') { else if(data.repeats.length > 1) {
userObj.fullname = this.user.name; var repeatsLabel = window.sL.requeetsNoun;
userObj.user_id = this.user.id;
userObj.profileurl = this.user.statusnet_profile_url;
userObj.avatarurl = this.user.profile_image_url;
} }
// no dupes if(q.children('.queet').find('.rq-count').length>0) {
if(q.children('.queet').find('#av-' + userObj.user_id).length==0) { q.children('.queet').find('.rq-count').children('strong').html(data.repeats.length);
q.find('.avatar-row').append('<a title="' + userObj.fullname + '" data-user-id="' + userObj.user_id + '" href="' + userObj.profileurl + '"><img alt="' + userObj.fullname + '" src="' + userObj.avatarurl + '" class="avatar size24" id="av-' + userObj.user_id + '"></a>');
avatarnum++;
} }
return (avatarnum < 8); else {
q.children('.queet').find('li.avatar-row').before('<li class="rq-count"><a>' + repeatsLabel + ' </a><strong>' + data.repeats.length + '</strong></li>');
}
}
// merge favs and repeats objects by user_id (removes duplicate users)
var favsAndRepeats = {};
$.each(data.repeats,function(){
favsAndRepeats[this.user_id] = this;
}); });
} $.each(data.favs,function(){
favsAndRepeats[this.user_id] = this;
});
// make an object with time the key
var favsAndRepeatsByTime = {};
$.each(favsAndRepeats,function(){
favsAndRepeatsByTime[this.time] = this;
});
// create an array with times and sort it
var timeSorted = [];
$.each(favsAndRepeats,function(){
timeSorted.push(this.time);
});
timeSorted.sort();
// display avatars in chronological order, max 7
var avatarnum = 1;
$.each(timeSorted,function(){
q.children('.queet').find('.avatar-row').append('<a title="' + favsAndRepeatsByTime[this].fullname + '" data-user-id="' + favsAndRepeatsByTime[this].user_id + '" href="' + favsAndRepeatsByTime[this].profileurl + '"><img alt="' + favsAndRepeatsByTime[this].fullname + '" src="' + favsAndRepeatsByTime[this].avatarurl + '" class="avatar size24" id="av-' + favsAndRepeatsByTime[this].user_id + '"></a>');
return (avatarnum < 8);
avatarnum++;
});
} }
@ -895,16 +919,13 @@ function expand_queet(q,doScrolling) {
} }
else if(!q.hasClass('collapsing')) { else if(!q.hasClass('collapsing')) {
rememberMyScrollPos(q,qid,-8);
// not for acitivity or notifications // not for acitivity or notifications
if(!q.hasClass('activity') && !q.hasClass('repeat') && !q.hasClass('like') && !q.hasClass('follow')) { if(!q.hasClass('activity') && !q.hasClass('repeat') && !q.hasClass('like') && !q.hasClass('follow')) {
q.addClass('expanded'); q.addClass('expanded');
q.prev().addClass('next-expanded'); q.prev().addClass('next-expanded');
// if shortened queet, get full text (not if external) // if shortened queet, get full text
if(!q.hasClass('external-conversation')) {
if(q.children('.queet').find('span.attachment.more').length>0) { if(q.children('.queet').find('span.attachment.more').length>0) {
var attachmentId = q.children('.queet').find('span.attachment.more').attr('data-attachment-id'); var attachmentId = q.children('.queet').find('span.attachment.more').attr('data-attachment-id');
getFromAPI("attachment/" + attachmentId + ".json",function(data){ getFromAPI("attachment/" + attachmentId + ".json",function(data){
@ -914,7 +935,6 @@ function expand_queet(q,doScrolling) {
} }
}); });
} }
}
// add expanded container // add expanded container
var longdate = parseTwitterLongDate(q.find('.created-at').attr('data-created-at')); var longdate = parseTwitterLongDate(q.find('.created-at').attr('data-created-at'));
@ -997,33 +1017,18 @@ function expand_queet(q,doScrolling) {
} }
}); });
// get favs and rq:s (not if external) (and only if logged in because API demands that)
if(!q.hasClass('external-conversation') && window.loggedIn) {
// get and show favs // get and show favs and repeats
getFavsOrRequeetsForQueet('favs',qid,function(favs){ getFavsAndRequeetsForQueet(q, qid);
if(favs) {
if(q.hasClass('expanded') && !q.hasClass('collapsing')) {
showFavsOrRequeetsInQueet(q,favs,'favs');
}
}
});
// get and show requeets
getFavsOrRequeetsForQueet('retweets',qid,function(requeets){
if(requeets) {
if(q.hasClass('expanded') && !q.hasClass('collapsing')) {
showFavsOrRequeetsInQueet(q,requeets,'requeets');
}
}
});
}
// show conversation and reply form (but not if already in conversation) // show conversation and reply form (but not if already in conversation)
if(!q.hasClass('conversation')) { if(!q.hasClass('conversation')) {
// show conversation // show conversation (wait for css to animate the margin 50ms)
showConversation(qid); setTimeout(function(){
getConversation(q, qid);
},50);
// show inline reply form if logged in // show inline reply form if logged in
if(typeof window.loggedIn.screen_name != 'undefined') { if(typeof window.loggedIn.screen_name != 'undefined') {
@ -1175,26 +1180,57 @@ function convertNewGNUSocialURItoURL(obj) {
/* · /* ·
· ·
· Show conversation · Get and show conversation
· ·
· This function has grown into a monster, needs fixing · This function has grown into a monster, needs fixing
· ·
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
function showConversation(qid) { function getConversation(q, qid) {
// get conversation // check if we have a conversation for this notice cached in localstorage
if(localStorageIsEnabled()) {
if(typeof localStorage['conversation-' + qid] != 'undefined' && localStorage['conversation-' + qid] !== null) {
showConversation(q, qid, JSON.parse(localStorage['conversation-' + qid]));
}
// if we have a conversation for the notice that this notice is a reply to,
// we assume it's the same conversation, and use that
else if(q.attr('data-in-reply-to-status-id') !== null
&& q.attr('data-in-reply-to-status-id') != 'null'
&& typeof localStorage['conversation-' + q.attr('data-in-reply-to-status-id')] != 'undefined'
&& localStorage['conversation-' + q.attr('data-in-reply-to-status-id')] !== null) {
showConversation(q, qid, JSON.parse(localStorage['conversation-' + q.attr('data-in-reply-to-status-id')]));
}
}
// always get most recent conversation from server
getFromAPI('statusnet/conversation/' + $('#stream-item-' + qid).attr('data-conversation-id') + '.json?count=100', function(data){ if(data) { getFromAPI('statusnet/conversation/' + $('#stream-item-' + qid).attr('data-conversation-id') + '.json?count=100', function(data){ if(data) {
if(data && !$('#stream-item-' + qid).hasClass('collapsing')){ // cache in localstorage
// we have local conversation if(localStorageIsEnabled()) {
localStorage['conversation-' + qid] = JSON.stringify(data);
}
showConversation(q, qid,data);
}});
}
function showConversation(q, qid, data) {
rememberMyScrollPos(q.children('.queet'),qid,0);
if(data && !q.hasClass('collapsing')){
if(data.length>1) { if(data.length>1) {
var before_or_after = 'after'; var before_or_after = 'before';
$.each(data, function (key,obj) { $.each(data.reverse(), function (key,obj) {
// switch to append after original queet // switch to append after original queet
if(obj.id == qid) { if(obj.id == qid) {
before_or_after = 'before'; before_or_after = 'after';
} }
// don't add clicked queet to DOM, but all else // don't add clicked queet to DOM, but all else
@ -1202,84 +1238,34 @@ function showConversation(qid) {
if(obj.id != qid) { if(obj.id != qid) {
var queetTime = parseTwitterDate(obj.created_at); var queetTime = parseTwitterDate(obj.created_at);
// we don't want to print 'null', someone might have that username!
var in_reply_to_screen_name = '';
if(obj.in_reply_to_screen_name != null) {
in_reply_to_screen_name = obj.in_reply_to_screen_name;
}
// is this mine?
var isThisMine = 'not-mine';
if(obj.user.id == window.myUserID) {
var isThisMine = 'is-mine';
}
// requeet or delete html
var requeetedClass = '';
if(obj.repeated) {
var requeetHtml = '<li class="action-rt-container"><a class="with-icn done"><span class="icon sm-rt" title="' + window.sL.requeetedVerb + '"></span></a></li>';
requeetedClass = 'requeeted';
}
else {
var requeetHtml = '<li class="action-rt-container"><a class="with-icn"><span class="icon sm-rt ' + isThisMine + '" title="' + window.sL.requeetVerb + '"></span></a></li>';
}
// favorite html
var favoritedClass = '';
if(obj.favorited) {
var favoriteHtml = '<a class="with-icn done"><span class="icon sm-fav" title="' + window.sL.favoritedVerb + '"></span></a>';
favoritedClass = 'favorited';
}
else {
var favoriteHtml = '<a class="with-icn"><span class="icon sm-fav" title="' + window.sL.favoriteVerb + '"></span></a>';
}
// actions only for logged in users
var queetActions = '';
if(typeof window.loggedIn.screen_name != 'undefined') {
queetActions = '<ul class="queet-actions"><li class="action-reply-container"><a class="with-icn"><span class="icon sm-reply" title="' + window.sL.replyVerb + '"></span></a></li>' + requeetHtml + '<li class="action-fav-container">' + favoriteHtml + '</li><li class="action-ellipsis-container"><a class="with-icn"><span class="icon sm-ellipsis" title="' + window.sL.ellipsisMore + '"></span></a></li></ul>';
}
// reply-to html
var reply_to_html = '';
if(obj.in_reply_to_screen_name !== null && obj.in_reply_to_profileurl !== null && obj.in_reply_to_screen_name != obj.user.screen_name) {
reply_to_html = '<span class="reply-to"><a class="h-card mention" href="' + obj.in_reply_to_profileurl + '">@' + obj.in_reply_to_screen_name + '</a></span> ';
}
// in-groups html
var in_groups_html = '';
if(obj.statusnet_in_groups !== false && typeof obj.statusnet_in_groups != 'undefined') {
$.each(obj.statusnet_in_groups,function(){
in_groups_html = '<span class="in-groups"><a class="h-card group" href="' + obj.statusnet_in_groups.url + '">!' + obj.statusnet_in_groups.nickname + '</a></span>';
});
}
// external
var ostatusHtml = '';
if(obj.is_local === false) {
ostatusHtml = '<a target="_blank" title="' + window.sL.goToOriginalNotice + '" class="ostatus-link" href="' + obj.external_url + '"></a>';
}
obj = convertNewGNUSocialURItoURL(obj);
if(obj.source == 'activity') { if(obj.source == 'activity') {
var queetHtml = '<div id="conversation-stream-item-' + obj.id + '" class="stream-item conversation activity hidden-conversation" data-source="' + escape(obj.source) + '" data-quitter-id="' + obj.id + '" data-quitter-id-in-stream="' + obj.id + '"><div class="queet" id="conversation-q-' + obj.id + '"><div class="queet-content"><div class="stream-item-header"><small class="created-at" data-created-at="' + obj.created_at + '"><a>' + queetTime + '</a></small></div><div class="queet-text">' + $.trim(obj.statusnet_html) + '</div></div></div></div>'; var queetHtml = '<div id="conversation-stream-item-' + obj.id + '" class="stream-item conversation activity hidden-conversation" data-source="' + escape(obj.source) + '" data-quitter-id="' + obj.id + '" data-quitter-id-in-stream="' + obj.id + '"><div class="queet" id="conversation-q-' + obj.id + '"><div class="queet-content"><div class="stream-item-header"><small class="created-at" data-created-at="' + obj.created_at + '"><a>' + queetTime + '</a></small></div><div class="queet-text">' + $.trim(obj.statusnet_html) + '</div></div></div></div>';
} }
else { else {
var queetHtml = '<div id="conversation-stream-item-' + obj.id + '" class="stream-item conversation hidden-conversation ' + requeetedClass + ' ' + favoritedClass + '" data-source="' + escape(obj.source) + '" data-quitter-id="' + obj.id + '" data-conversation-id="' + obj.statusnet_conversation_id + '" data-quitter-id-in-stream="' + obj.id + '" data-in-reply-to-screen-name="' + in_reply_to_screen_name + '" data-in-reply-to-status-id="' + obj.in_reply_to_status_id + '"><div class="queet" id="conversation-q-' + obj.id + '">' + ostatusHtml + '<div class="queet-content"><div class="stream-item-header"><a class="account-group" href="' + obj.user.statusnet_profile_url + '"><img class="avatar" src="' + obj.user.profile_image_url_profile_size + '" /><strong class="name" data-user-id="' + obj.user.id + '">' + obj.user.name + '</strong> <span class="screen-name">@' + obj.user.screen_name + '</span></a> <i class="addressees">' + reply_to_html + in_groups_html + '</i><small class="created-at" data-created-at="' + obj.created_at + '"><a href="' + obj.uri + '">' + queetTime + '</a></small></div><div class="queet-text">' + $.trim(obj.statusnet_html) + '</div><div class="stream-item-footer">' + queetActions + '</div></div></div></div>'; var queetHtml = buildQueetHtml(obj, obj.id, 'conversation hidden-conversation', false, true);
} }
// detect rtl // detect rtl
queetHtml = detectRTL(queetHtml); queetHtml = detectRTL(queetHtml);
if($('#stream-item-' + qid).hasClass('expanded')) { // add queet to conversation only if still expanded if(q.hasClass('expanded')) { // add queet to conversation only if still expanded
if(before_or_after == 'before') {
$('#stream-item-' + qid).prepend(queetHtml); // replace already existing queets' html
if(q.children('#conversation-stream-item-' + obj.id).length > 0) {
var streamItemInnerHtml = $('<div/>').append(queetHtml).find('.stream-item').html();
q.children('#conversation-stream-item-' + obj.id).html(streamItemInnerHtml);
}
else if(before_or_after == 'before') {
q.children('.queet').before(queetHtml);
} }
else { else {
$('#q-' + qid).after(queetHtml); if(q.children('.queet').nextAll('.conversation').length < 1) {
q.children('.queet').after(queetHtml);
}
else {
q.children('.queet').nextAll('.conversation').last().after(queetHtml);
}
} }
} }
@ -1287,91 +1273,18 @@ function showConversation(qid) {
convertAttachmentMoreHref(); convertAttachmentMoreHref();
}); });
} }
// try to get conversation from external instance
else if(typeof data[0] != 'undefined') {
if(data[0].source == 'ostatus') {
var external_status_url = data[0].uri.replace('notice','api/statuses/show') + '.json';
var external_base_url = data[0].uri.substring(0,data[0].uri.indexOf('/notice/'));
$.ajax({ url: external_status_url, type: "GET", dataType: "jsonp", success: function(data) { // try to get conversation id fro notice
var external_id = data.id;
if(typeof data.statusnet_conversation_id == 'undefined') {
console.log(data);remove_spinner();
}
else { // proceed if we got a conversation_id
$.ajax({ url: external_base_url + '/api/statusnet/conversation/' + data.statusnet_conversation_id + ".json?count=100", type: "GET", dataType: "jsonp", success: function(data) {
var before_or_after = 'after';
data = iterateRecursiveReplaceHtmlSpecialChars(data);
$.each(data, function (key,obj) {
// switch to append after original queet
if(obj.id == external_id) {
before_or_after = 'before';
$('#stream-item-' + qid).attr('data-in-reply-to-status-id',obj.in_reply_to_status_id);
}
else {
var queetTime = parseTwitterDate(obj.created_at);
// we don't want to print 'null', someone might have that username!
var in_reply_to_screen_name = '';
if(obj.in_reply_to_screen_name != null) {
in_reply_to_screen_name = obj.in_reply_to_screen_name;
}
// reply-to html
var reply_to_html = '';
if(obj.in_reply_to_screen_name != null && obj.in_reply_to_screen_name != obj.user.screen_name) {
reply_to_html = '<span class="reply-to">@' + obj.in_reply_to_screen_name + '</span> ';
}
obj = convertNewGNUSocialURItoURL(obj);
if(obj.source == 'activity') {
var queetHtml = '<div id="conversation-stream-item-' + obj.id + '" class="stream-item conversation activity hidden-conversation external-conversation" data-source="' + escape(obj.source) + '" data-quitter-id="' + obj.id + '" data-quitter-id-in-stream="' + obj.id + '"><div class="queet" id="conversation-q-' + obj.id + '"><div class="queet-content"><div class="stream-item-header"><small class="created-at" data-created-at="' + obj.created_at + '"><a href="' + external_base_url + '/notice/' + obj.id + '">' + queetTime + '</a></small></div><div class="queet-text">' + $.trim(obj.statusnet_html) + '</div></div></div></div>';
}
else {
var queetHtml = '<div id="conversation-stream-item-' + obj.id + '" class="stream-item conversation hidden-conversation external-conversation" data-external-base-url="' + escape(external_base_url) + '" data-source="' + escape(obj.source) + '" data-quitter-id="' + obj.id + '" data-conversation-id="' + obj.statusnet_conversation_id + '" data-quitter-id-in-stream="' + obj.id + '" data-in-reply-to-screen-name="' + in_reply_to_screen_name + '" data-in-reply-to-status-id="' + obj.in_reply_to_status_id + '"><div class="queet" id="conversation-q-' + obj.id + '"><div class="queet-content"><div class="stream-item-header"><a class="account-group" href="' + obj.user.statusnet_profile_url + '"><img class="avatar" src="' + obj.user.profile_image_url_profile_size + '" /><strong class="name" data-user-id="' + obj.user.id + '">' + obj.user.name + '</strong> <span class="screen-name">@' + obj.user.screen_name + '</span></a>' + reply_to_html + '<small class="created-at" data-created-at="' + obj.created_at + '"><a href="' + external_base_url + '/notice/' + obj.id + '">' + queetTime + '</a></small></div><div class="queet-text">' + $.trim(obj.statusnet_html) + '</div><div class="stream-item-footer"><ul class="queet-actions"><li class="action-reply-container">&nbsp;</li></ul></div></div></div></div>';
}
// detect rtl
queetHtml = detectRTL(queetHtml);
if($('#stream-item-' + qid).hasClass('expanded')) { // add queet to conversation only if still expanded
if(before_or_after == 'before') {
$('#stream-item-' + qid).prepend(queetHtml);
}
else {
$('#q-' + qid).after(queetHtml);
}
}
}
remove_spinner();
convertAttachmentMoreHref();
});
findInReplyToStatusAndShow(qid,$('#stream-item-' + qid).attr('data-in-reply-to-status-id'),true,false);
backToMyScrollPos($('#q-' + qid),qid,false);
}, error: function(data) { console.log(data);remove_spinner(); }});
}
}, error: function(data) { console.log(data);remove_spinner(); }});
}
// no conversation
else {
remove_spinner();
}
}
else { else {
remove_spinner(); remove_spinner();
} }
// loop trough this stream items conversation and show the "strict" line of replies // loop trough this stream items conversation and show the "strict" line of replies
findInReplyToStatusAndShow(qid,$('#stream-item-' + qid).attr('data-in-reply-to-status-id'),true,false); findInReplyToStatusAndShow(q,qid,q.attr('data-in-reply-to-status-id'),true,false);
backToMyScrollPos($('#q-' + qid),qid,false); backToMyScrollPos(q.children('.queet'),qid,false);
findAndMarkLastVisibleInConversation($('#stream-item-' + qid)); findAndMarkLastVisibleInConversation(q);
} }
else { else {
remove_spinner(); remove_spinner();
} }
}});
} }
@ -1394,66 +1307,70 @@ function findAndMarkLastVisibleInConversation(streamItem) {
· ·
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
function findInReplyToStatusAndShow(qid,reply,only_first,onlyINreplyto) { function findInReplyToStatusAndShow(q, qid,reply,only_first,onlyINreplyto) {
var reply_found = $('#stream-item-' + qid).find('.stream-item[data-quitter-id="' + reply + '"]'); var reply_found = $('#stream-item-' + qid).find('.stream-item[data-quitter-id="' + reply + '"]');
var reply_found_reply_to = $('#stream-item-' + qid).find('.stream-item[data-quitter-id="' + reply_found.attr('data-in-reply-to-status-id') + '"]'); var reply_found_reply_to = $('#stream-item-' + qid).find('.stream-item[data-quitter-id="' + reply_found.attr('data-in-reply-to-status-id') + '"]');
if(reply_found.length>0) { if(reply_found.length>0) {
reply_found.removeClass('hidden-conversation'); reply_found.removeClass('hidden-conversation');
//reply_found.animate({opacity:'1'},500);
reply_found.css('opacity','1'); reply_found.css('opacity','1');
if(only_first && reply_found_reply_to.length>0) { if(only_first && reply_found_reply_to.length>0) {
if(q.children('.view-more-container-top').length < 1) {
reply_found.before('<div class="view-more-container-top" data-trace-from="' + reply + '"><a>' + window.sL.viewMoreInConvBefore + '</a></div>'); reply_found.before('<div class="view-more-container-top" data-trace-from="' + reply + '"><a>' + window.sL.viewMoreInConvBefore + '</a></div>');
findReplyToStatusAndShow(qid,qid,true); }
findReplyToStatusAndShow(q, qid,qid,true);
} }
else { else {
findInReplyToStatusAndShow(qid,reply_found.attr('data-in-reply-to-status-id'),false,onlyINreplyto); findInReplyToStatusAndShow(q, qid,reply_found.attr('data-in-reply-to-status-id'),false,onlyINreplyto);
} }
} }
else if(!onlyINreplyto) { else if(!onlyINreplyto) {
findReplyToStatusAndShow(qid,qid,true); findReplyToStatusAndShow(q, qid,qid,true);
} }
else { else {
checkForHiddenConversationQueets(qid); checkForHiddenConversationQueets(q, qid);
} }
} }
// recursive function to find the replies to a status // recursive function to find the replies to a status
function findReplyToStatusAndShow(qid,this_id,only_first) { function findReplyToStatusAndShow(q, qid,this_id,only_first) {
var reply_found = $('#stream-item-' + qid).find('.stream-item[data-in-reply-to-status-id="' + this_id + '"]');
var reply_founds_reply = $('#stream-item-' + qid).find('.stream-item[data-in-reply-to-status-id="' + reply_found.attr('data-quitter-id') + '"]'); var replies_found = q.find('.stream-item[data-in-reply-to-status-id="' + this_id + '"]');
if(reply_found.length>0) {
reply_found.removeClass('hidden-conversation'); $.each(replies_found, function(k,reply_found){
// reply_found.animate({opacity:'1'},0,function(){
// if(!only_first) { var reply_founds_reply = q.find('.stream-item[data-in-reply-to-status-id="' + $(reply_found).attr('data-quitter-id') + '"]');
// findReplyToStatusAndShow(qid,$(this).attr('data-quitter-id'),false);
// } $(reply_found).removeClass('hidden-conversation');
// }); $(reply_found).css('opacity','1');
reply_found.css('opacity','1');
if(!only_first) { if(!only_first) {
findReplyToStatusAndShow(qid,$(this).attr('data-quitter-id'),false); findReplyToStatusAndShow(q, qid,$(this).attr('data-quitter-id'),false);
} }
if(only_first && reply_founds_reply.length>0) { if(only_first && reply_founds_reply.length>0) {
reply_found.last().after('<div class="view-more-container-bottom" data-replies-after="' + qid + '"><a>' + window.sL.viewMoreInConvAfter + '</a></div>'); if(q.children('.view-more-container-bottom').length < 1) {
q.append('<div class="view-more-container-bottom" data-replies-after="' + qid + '"><a>' + window.sL.viewMoreInConvAfter + '</a></div>');
} }
} }
checkForHiddenConversationQueets(qid);
});
checkForHiddenConversationQueets(q, qid);
} }
// helper function for the above recursive functions // helper function for the above recursive functions
function checkForHiddenConversationQueets(qid) { function checkForHiddenConversationQueets(q, qid) {
// here we check if there are any remaining hidden queets in conversation, if there are, we put a "show full conversation"-link // here we check if there are any remaining hidden queets in conversation, if there are, we put a "show full conversation"-link
if($('#stream-item-' + qid).find('.hidden-conversation').length>0) { if(q.find('.hidden-conversation').length>0) {
if($('#stream-item-' + qid).children('.queet').find('.show-full-conversation').length == 0) { if(q.children('.queet').find('.show-full-conversation').length == 0) {
$('#stream-item-' + qid).children('.queet').find('.stream-item-footer').append('<span class="show-full-conversation" data-stream-item-id="' + qid + '">' + window.sL.expandFullConversation + '</span>'); q.children('.queet').find('.stream-item-footer').append('<span class="show-full-conversation" data-stream-item-id="' + qid + '">' + window.sL.expandFullConversation + '</span>');
// if this is a single notice, we show conversation // if this is a single notice, we show conversation
if(window.currentStream.substring(0,14) == 'statuses/show/') { if(window.currentStream.substring(0,14) == 'statuses/show/') {
$('#stream-item-' + qid).children('.queet').find('.show-full-conversation').trigger('click'); q.children('.queet').find('.show-full-conversation').trigger('click');
} }
} }
} }
else { else {
$('#stream-item-' + qid).children('.queet').find('.show-full-conversation').remove(); q.children('.queet').find('.show-full-conversation').remove();
} }
} }
@ -1767,7 +1684,7 @@ function addToFeed(feed, after, extraClasses, isReply) {
· ·
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
function buildQueetHtml(obj, idInStream, extraClassesThisRun, requeeted_by) { function buildQueetHtml(obj, idInStream, extraClassesThisRun, requeeted_by, isConversation) {
// we don't want to print 'null' in in_reply_to_screen_name-attribute, someone might have that username! // we don't want to print 'null' in in_reply_to_screen_name-attribute, someone might have that username!
var in_reply_to_screen_name = ''; var in_reply_to_screen_name = '';
@ -1775,12 +1692,24 @@ function buildQueetHtml(obj, idInStream, extraClassesThisRun, requeeted_by) {
in_reply_to_screen_name = obj.in_reply_to_screen_name; in_reply_to_screen_name = obj.in_reply_to_screen_name;
} }
// conversations has some slightly different id's
var idPrepend = '';
if(typeof isConversation != 'undefined' && isConversation === true) {
var idPrepend = 'conversation-';
}
// is this mine? // is this mine?
var isThisMine = 'not-mine'; var isThisMine = 'not-mine';
if(obj.user.id == window.myUserID) { if(obj.user.id == window.myUserID) {
var isThisMine = 'is-mine'; var isThisMine = 'is-mine';
} }
// requeeted by me?
var requeetedByMe = '';
if(obj.repeated_id) {
requeetedByMe = ' data-requeeted-by-me-id="' + obj.repeated_id + '" ';
}
// requeet html // requeet html
var requeetedClass = ''; var requeetedClass = '';
if(obj.repeated) { if(obj.repeated) {
@ -1854,7 +1783,7 @@ function buildQueetHtml(obj, idInStream, extraClassesThisRun, requeeted_by) {
// requeets // requeets
var requeetHtml = ''; var requeetHtml = '';
if(typeof requeeted_by != 'undefined') { if(typeof requeeted_by != 'undefined' && requeeted_by !== false) {
var requeetedByHtml = '<a data-user-id="' + requeeted_by.user.id + '" href="' + requeeted_by.user.statusnet_profile_url + '"> <b>' + requeeted_by.user.name + '</b></a>'; var requeetedByHtml = '<a data-user-id="' + requeeted_by.user.id + '" href="' + requeeted_by.user.statusnet_profile_url + '"> <b>' + requeeted_by.user.name + '</b></a>';
requeetHtml = '<div class="context" id="requeet-' + requeeted_by.id + '"><span class="with-icn"><i class="badge-requeeted"></i><span class="requeet-text"> ' + window.sL.requeetedBy.replace('{requeeted-by}',requeetedByHtml) + '</span></span></div>'; requeetHtml = '<div class="context" id="requeet-' + requeeted_by.id + '"><span class="with-icn"><i class="badge-requeeted"></i><span class="requeet-text"> ' + window.sL.requeetedBy.replace('{requeeted-by}',requeetedByHtml) + '</span></span></div>';
} }
@ -1869,15 +1798,16 @@ function buildQueetHtml(obj, idInStream, extraClassesThisRun, requeeted_by) {
var queetTime = parseTwitterDate(obj.created_at); var queetTime = parseTwitterDate(obj.created_at);
var queetHtml = '<div \ var queetHtml = '<div \
id="stream-item-' + obj.id + '" \ id="' + idPrepend + 'stream-item-' + obj.id + '" \
class="stream-item ' + extraClassesThisRun + ' ' + requeetedClass + ' ' + favoritedClass + '" \ class="stream-item ' + extraClassesThisRun + ' ' + requeetedClass + ' ' + favoritedClass + '" \
data-source="' + escape(obj.source) + '" \ data-source="' + escape(obj.source) + '" \
data-quitter-id="' + obj.id + '" \ data-quitter-id="' + obj.id + '" \
data-conversation-id="' + obj.statusnet_conversation_id + '" \ data-conversation-id="' + obj.statusnet_conversation_id + '" \
data-quitter-id-in-stream="' + idInStream + '" \ data-quitter-id-in-stream="' + idInStream + '" \
data-in-reply-to-screen-name="' + in_reply_to_screen_name + '" \ data-in-reply-to-screen-name="' + in_reply_to_screen_name + '" \
data-in-reply-to-status-id="' + obj.in_reply_to_status_id + '">\ data-in-reply-to-status-id="' + obj.in_reply_to_status_id + '"\
<div class="queet" id="q-' + obj.id + '">\ ' + requeetedByMe + '>\
<div class="queet" id="' + idPrepend + 'q-' + obj.id + '">\
' + requeetHtml + '\ ' + requeetHtml + '\
' + attachment_html + '\ ' + attachment_html + '\
' + ostatusHtml + '\ ' + ostatusHtml + '\

View File

@ -1466,6 +1466,12 @@ $('body').on('click','.action-rt-container .icon:not(.is-mine)',function(){
if(data) { if(data) {
// success // success
this_stream_item.attr('data-requeeted-by-me-id',data.id); this_stream_item.attr('data-requeeted-by-me-id',data.id);
getFavsAndRequeetsForQueet(this_stream_item, this_stream_item.attr('data-quitter-id'));
// mark all instances of this notice as repeated
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').addClass('requeeted');
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').attr('data-requeeted-by-me-id',data.id);
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').children('.queet').find('.action-rt-container').children('.with-icn').addClass('done');
} }
else { else {
// error // error
@ -1479,23 +1485,13 @@ $('body').on('click','.action-rt-container .icon:not(.is-mine)',function(){
else if(this_action.children('.with-icn').hasClass('done')) { else if(this_action.children('.with-icn').hasClass('done')) {
display_spinner(); display_spinner();
// if we don't have the id od the repeat stored in DOM, we need to look it up
// (might be a problem if there's more than 100 repeats)
if(typeof this_stream_item.attr('data-requeeted-by-me-id') == 'undefined') {
getFavsOrRequeetsForQueet('requeets',this_stream_item.attr('data-quitter-id'),function(data) {
$.each(data,function(key,obj){
if(window.myUserID == obj.user.id) {
var my_rq_id = obj.id;
unRequeet(this_stream_item, this_action, my_rq_id);
}
});
});
}
// if we have the id stored in DOM
else {
var my_rq_id = this_stream_item.attr('data-requeeted-by-me-id'); var my_rq_id = this_stream_item.attr('data-requeeted-by-me-id');
unRequeet(this_stream_item, this_action, my_rq_id); unRequeet(this_stream_item, this_action, my_rq_id);
}
// mark all instances of this notice as non-repeated
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').removeClass('requeeted');
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').removeAttr('data-requeeted-by-me-id');
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').children('.queet').find('.action-rt-container').children('.with-icn').removeClass('done');
} }
}); });
@ -1530,6 +1526,11 @@ $('body').on('click','.action-fav-container',function(){
postActionToAPI('favorites/create/' + this_stream_item.attr('data-quitter-id') + '.json', function(data) { postActionToAPI('favorites/create/' + this_stream_item.attr('data-quitter-id') + '.json', function(data) {
if(data) { if(data) {
// success // success
getFavsAndRequeetsForQueet(this_stream_item, this_stream_item.attr('data-quitter-id'));
// mark all instances of this notice as favorited
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').addClass('favorited');
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').children('.queet').find('.action-fav-container').children('.with-icn').addClass('done');
} }
else { else {
// error // error
@ -1551,6 +1552,11 @@ $('body').on('click','.action-fav-container',function(){
if(data) { if(data) {
// success // success
remove_spinner(); remove_spinner();
getFavsAndRequeetsForQueet(this_stream_item, this_stream_item.attr('data-quitter-id'));
// mark all instances of this notice as non-favorited
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').removeClass('favorited');
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').children('.queet').find('.action-fav-container').children('.with-icn').removeClass('done');
} }
else { else {
// error // error
@ -2122,7 +2128,7 @@ $('body').on('keyup', 'div.queet-box-syntax', function(e) {
$('body').on('click','.view-more-container-bottom', function(){ $('body').on('click','.view-more-container-bottom', function(){
var thisParentStreamItem = $(this).parent('.stream-item'); var thisParentStreamItem = $(this).parent('.stream-item');
findReplyToStatusAndShow($(this).parent('.stream-item').attr('data-quitter-id'),$(this).attr('data-replies-after')); findReplyToStatusAndShow(thisParentStreamItem, thisParentStreamItem.attr('data-quitter-id'),$(this).attr('data-replies-after'));
$(this).remove(); $(this).remove();
findAndMarkLastVisibleInConversation(thisParentStreamItem); findAndMarkLastVisibleInConversation(thisParentStreamItem);
}); });
@ -2134,14 +2140,14 @@ $('body').on('click','.view-more-container-top', function(){
rememberMyScrollPos(queet,'moretop' + this_qid); rememberMyScrollPos(queet,'moretop' + this_qid);
findInReplyToStatusAndShow($(this).parent('.stream-item').attr('data-quitter-id'),$(this).attr('data-trace-from'),false,true); findInReplyToStatusAndShow(thisParentStreamItem, thisParentStreamItem.attr('data-quitter-id'),$(this).attr('data-trace-from'),false,true);
$(this).remove(); $(this).remove();
backToMyScrollPos(queet,'moretop' + this_qid,false); backToMyScrollPos(queet,'moretop' + this_qid,false);
// remove the "show full conversation" link if nothing more to show // remove the "show full conversation" link if nothing more to show
if($(this).parent('.stream-item').find('.hidden-conversation').length == 0) { if(thisParentStreamItem.find('.hidden-conversation').length == 0) {
$(this).parent('.stream-item').children('.queet').find('.show-full-conversation').remove(); thisParentStreamItem.children('.queet').find('.show-full-conversation').remove();
} }
findAndMarkLastVisibleInConversation(thisParentStreamItem); findAndMarkLastVisibleInConversation(thisParentStreamItem);
}); });