big change in how streams are changed, and lots of bugfixes

This commit is contained in:
Hannes Mannerheim 2015-11-23 00:24:33 +01:00
parent 7b6acc3a64
commit 072c411ac2
8 changed files with 690 additions and 529 deletions

View File

@ -442,6 +442,7 @@ class QvitterPlugin extends Plugin {
function onNoticeSimpleStatusArray($notice, &$twitter_status, $scoped) function onNoticeSimpleStatusArray($notice, &$twitter_status, $scoped)
{ {
// groups // groups
$notice_groups = $notice->getGroups(); $notice_groups = $notice->getGroups();
$group_addressees = false; $group_addressees = false;
@ -450,13 +451,6 @@ 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(array_key_exists('repeated', $twitter_status) && $twitter_status['repeated'] === true) {
$repeated = Notice::pkeyGet(array('profile_id' => $scoped->id,
'repeat_of' => $notice->id));
$twitter_status['repeated_id'] = $repeated->id;
}
// more metadata about attachments // more metadata about attachments
// get all attachments first, and put all the extra meta data in an array // get all attachments first, and put all the extra meta data in an array
@ -541,6 +535,7 @@ class QvitterPlugin extends Plugin {
$twitter_status['in_reply_to_profileurl'] = null; $twitter_status['in_reply_to_profileurl'] = null;
} }
// fave number // fave number
$faves = Fave::byNotice($notice); $faves = Fave::byNotice($notice);
$favenum = count($faves); $favenum = count($faves);
@ -550,7 +545,9 @@ class QvitterPlugin extends Plugin {
$repeats = $notice->repeatStream(); $repeats = $notice->repeatStream();
$repeatnum=0; $repeatnum=0;
while ($repeats->fetch()) { while ($repeats->fetch()) {
$repeatnum++; if($repeats->verb == ActivityVerb::SHARE) { // i.e. not deleted repeats
$repeatnum++;
}
} }
$twitter_status['repeat_num'] = $repeatnum; $twitter_status['repeat_num'] = $repeatnum;
@ -724,17 +721,6 @@ class QvitterPlugin extends Plugin {
assert($notice->id > 0); // since we removed tests below assert($notice->id > 0); // since we removed tests below
// don't add notifications for activity type notices
if($notice->source == 'activity' || $notice->object_type == 'activity' || $notice->object_type == 'http://activitystrea.ms/schema/1.0/activity') {
return true;
}
// mark reply/mention-notifications as read if we're replying to a notice we're notified about
if($notice->reply_to) {
self::markNotificationAsSeen($notice->reply_to,$notice->profile_id,'mention');
self::markNotificationAsSeen($notice->reply_to,$notice->profile_id,'reply');
}
// repeats // repeats
if ($notice->isRepeat()) { if ($notice->isRepeat()) {
$repeated_notice = Notice::getKV('id', $notice->repeat_of); $repeated_notice = Notice::getKV('id', $notice->repeat_of);
@ -744,44 +730,57 @@ class QvitterPlugin extends Plugin {
// mark reply/mention-notifications as read if we're repeating to a notice we're notified about // mark reply/mention-notifications as read if we're repeating to a notice we're notified about
self::markNotificationAsSeen($repeated_notice->id,$notice->profile_id,'mention'); self::markNotificationAsSeen($repeated_notice->id,$notice->profile_id,'mention');
self::markNotificationAsSeen($repeated_notice->id,$notice->profile_id,'reply'); self::markNotificationAsSeen($repeated_notice->id,$notice->profile_id,'reply');
// (no other notifications repeats)
return true;
} }
} }
// replies and mentions (no notifications for these if this is a repeat) // don't add notifications for activity type notices
else { if($notice->source == 'activity' || $notice->object_type == 'activity' || $notice->object_type == 'http://activitystrea.ms/schema/1.0/activity') {
$reply_notification_to = false; return true;
// check for reply to insert in notifications
if($notice->reply_to) {
try {
$replyauthor = $notice->getParent()->getProfile();
$reply_notification_to = $replyauthor->id;
$this->insertNotification($replyauthor->id, $notice->profile_id, 'reply', $notice->id);
//} catch (NoParentNoticeException $e) { // TODO: catch this when everyone runs latest GNU social!
// This is not a reply to something (has no parent)
} catch (NoResultException $e) {
// Parent author's profile not found! Complain louder?
common_log(LOG_ERR, "Parent notice's author not found: ".$e->getMessage());
}
} }
// check for mentions to insert in notifications // mark reply/mention-notifications as read if we're replying to a notice we're notified about
$mentions = $notice->getReplies(); if($notice->reply_to) {
$sender = Profile::getKV($notice->profile_id); self::markNotificationAsSeen($notice->reply_to,$notice->profile_id,'mention');
$all_mentioned_user_ids = array(); self::markNotificationAsSeen($notice->reply_to,$notice->profile_id,'reply');
foreach ($mentions as $mentioned) { }
// no duplicate mentions
if(in_array($mentioned, $all_mentioned_user_ids)) {
continue;
}
$all_mentioned_user_ids[] = $mentioned;
// only notify if mentioned user is not already notified for reply // replies and mentions
if($reply_notification_to != $mentioned) { $reply_notification_to = false;
$this->insertNotification($mentioned, $notice->profile_id, 'mention', $notice->id); // check for reply to insert in notifications
} if($notice->reply_to) {
try {
$replyauthor = $notice->getParent()->getProfile();
$reply_notification_to = $replyauthor->id;
$this->insertNotification($replyauthor->id, $notice->profile_id, 'reply', $notice->id);
//} catch (NoParentNoticeException $e) { // TODO: catch this when everyone runs latest GNU social!
// This is not a reply to something (has no parent)
} catch (NoResultException $e) {
// Parent author's profile not found! Complain louder?
common_log(LOG_ERR, "Parent notice's author not found: ".$e->getMessage());
}
}
// check for mentions to insert in notifications
$mentions = $notice->getReplies();
$sender = Profile::getKV($notice->profile_id);
$all_mentioned_user_ids = array();
foreach ($mentions as $mentioned) {
// no duplicate mentions
if(in_array($mentioned, $all_mentioned_user_ids)) {
continue;
} }
} $all_mentioned_user_ids[] = $mentioned;
// only notify if mentioned user is not already notified for reply
if($reply_notification_to != $mentioned) {
$this->insertNotification($mentioned, $notice->profile_id, 'mention', $notice->id);
}
}
return true; return true;
} }

View File

@ -88,7 +88,7 @@ class ApiFavsAndRepeatsAction extends ApiPrivateAuthAction
// notifications tied to this notice and the current profile as read // notifications tied to this notice and the current profile as read
if($this->auth_user) { if($this->auth_user) {
QvitterPlugin::markNotificationAsSeen($this->notice_id,$this->auth_user->id,'mention'); QvitterPlugin::markNotificationAsSeen($this->notice_id,$this->auth_user->id,'mention');
QvitterPlugin::markNotificationAsSeen($this->notice_id,$this->auth_user->id,'reply'); QvitterPlugin::markNotificationAsSeen($this->notice_id,$this->auth_user->id,'reply');
} }
@ -128,6 +128,7 @@ class ApiFavsAndRepeatsAction extends ApiPrivateAuthAction
$notice->selectAdd('profile_id'); $notice->selectAdd('profile_id');
$notice->selectAdd('created'); $notice->selectAdd('created');
$notice->repeat_of = $this->original->id; $notice->repeat_of = $this->original->id;
$notice->verb = ActivityVerb::SHARE;
$notice->orderBy('created, id'); // NB: asc! $notice->orderBy('created, id'); // NB: asc!
if (!is_null($this->cnt)) { if (!is_null($this->cnt)) {
$notice->limit(0, $this->cnt); $notice->limit(0, $this->cnt);

View File

@ -106,7 +106,7 @@ class ApiQvitterAllFollowingAction extends ApiBareAuthAction
$this_group[3] = false; $this_group[3] = false;
} }
$this->groups_stripped[$user_group->id] = $this_group; $this->groups_stripped[$user_group->id] = $this_group;
} }
return true; return true;
} }
@ -169,11 +169,12 @@ class ApiQvitterAllFollowingAction extends ApiBareAuthAction
$this->since_id, $this->since_id,
$this->max_id $this->max_id
); );
while ($group->fetch()) { if(!empty($group)) {
$groups[] = clone($group); while ($group->fetch()) {
$groups[] = clone($group);
}
} }
return $groups; return $groups;
} }

View File

@ -1405,6 +1405,7 @@ body.rtl #footer-spinner-container {
-webkit-hyphens: auto; -webkit-hyphens: auto;
-moz-hyphens: auto; -moz-hyphens: auto;
hyphens: auto; hyphens: auto;
word-wrap: break-word;
} }
.tooltip-caret { .tooltip-caret {
z-index: 10000; z-index: 10000;
@ -4165,6 +4166,15 @@ button.shorten:after {
top: 20px; top: 20px;
} }
#feed-header-inner .loader {
position: absolute;
right:4px;
top:3px;
left:auto;
margin-left:0;
opacity:0.5;
}
.reload-stream { .reload-stream {
display: block; display: block;
position: absolute; position: absolute;

View File

@ -176,6 +176,7 @@ function getFromAPI(stream, actionOnSuccess) {
data = convertEmptyObjectToEmptyArray(data); data = convertEmptyObjectToEmptyArray(data);
data = iterateRecursiveReplaceHtmlSpecialChars(data); data = iterateRecursiveReplaceHtmlSpecialChars(data);
searchForUserDataToCache(data); searchForUserDataToCache(data);
searchForUpdatedNoticeData(data);
actionOnSuccess(data, userArray, request, url); actionOnSuccess(data, userArray, request, url);
}, },
@ -256,7 +257,7 @@ function getAllFollowsMembershipsAndBlocks(callback) {
if(data.blocks) { if(data.blocks) {
window.allBlocking = data.blocks; window.allBlocking = data.blocks;
} }
if(typeof callback == 'function') { if(typeof callback == 'function') {
callback(); callback();
@ -547,42 +548,6 @@ function postActionToAPI(action, actionOnSuccess) {
} }
/* ·
·
· Delete requeet
·
· @param this_stream_item: jQuery object for stream-item
· @param this_action: JQuery object for the requeet-button
· @param my_rq_id: the id for the requeet
·
· · · · · · · · · */
function unRequeet(this_stream_item, this_action, my_rq_id) {
this_action.children('.with-icn').removeClass('done');
this_action.find('.with-icn b').html(window.sL.requeetVerb);
this_stream_item.removeClass('requeeted');
// post unrequeet
postActionToAPI('statuses/destroy/' + my_rq_id + '.json', function(data) {
if(data) {
remove_spinner();
this_stream_item.removeAttr('data-requeeted-by-me-id');
this_stream_item.children('.queet').children('.context').find('.requeet-text').children('a[data-user-id="' + window.loggedIn.id + '"]').remove();
if(this_stream_item.children('.queet').children('.context').find('.requeet-text').children('a').length<1) {
this_stream_item.children('.queet').children('.context').remove();
}
getFavsAndRequeetsForQueet(this_stream_item, this_stream_item.attr('data-quitter-id'));
}
else {
remove_spinner();
this_action.children('.with-icn').addClass('done');
this_action.find('.with-icn b').html(window.sL.requeetedVerb);
this_stream_item.addClass('requeeted');
}
});
}
/* · /* ·
· ·

View File

@ -619,22 +619,20 @@ function groupProfileCard(groupAlias) {
function setNewCurrentStream(streamObject,setLocation,fallbackId,actionOnSuccess) { function setNewCurrentStream(streamObject,setLocation,fallbackId,actionOnSuccess) {
if(!streamObject && !streamObject.stream) { if(!streamObject || !streamObject.stream) {
console.log('invalid streamObject, no stream to set!'); console.log('invalid streamObject, no stream to set!');
return; return;
} }
// update the cache for the old stream
rememberStreamStateInLocalStorage();
// remove any old error messages // remove any old error messages
$('.error-message').remove(); $('.error-message').remove();
// remember state of old stream (including profile card)
rememberStreamStateInLocalStorage();
// halt interval that checks for new queets // halt interval that checks for new queets
window.clearInterval(checkForNewQueetsInterval); window.clearInterval(checkForNewQueetsInterval);
display_spinner();
// scroll to top // scroll to top
$(window).scrollTop(0); $(window).scrollTop(0);
$('body').addClass('androidFix').scrollTop(0).removeClass('androidFix'); $('body').addClass('androidFix').scrollTop(0).removeClass('androidFix');
@ -646,14 +644,19 @@ function setNewCurrentStream(streamObject,setLocation,fallbackId,actionOnSuccess
$('#feed-body').removeAttr('data-search-page-number'); $('#feed-body').removeAttr('data-search-page-number');
$('#feed-body').removeAttr('data-end-reached'); $('#feed-body').removeAttr('data-end-reached');
// hide new queets bar and reload stream button
$('#new-queets-bar-container').addClass('hidden');
$('.reload-stream').hide();
display_spinner('#feed-header-inner');
// are we just reloading? // are we just reloading?
var weAreReloading = false; var weAreReloading = false;
if(typeof window.currentStreamObject != 'undefined' && window.currentStreamObject.name == streamObject.name) { if(typeof window.currentStreamObject != 'undefined' && window.currentStreamObject.stream == streamObject.stream) {
weAreReloading = true; weAreReloading = true;
} }
// remember the most recent stream // remember the most recent stream object
window.currentStream = streamObject.stream;
window.currentStreamObject = streamObject; window.currentStreamObject = streamObject;
// set the new streams header // set the new streams header
@ -669,6 +672,10 @@ function setNewCurrentStream(streamObject,setLocation,fallbackId,actionOnSuccess
$('#feed-header-inner h2').append('<div id="stream-menu-cog" data-tooltip="' + window.sL.timelineOptions + '"></div>'); $('#feed-header-inner h2').append('<div id="stream-menu-cog" data-tooltip="' + window.sL.timelineOptions + '"></div>');
} }
// subtle animation to show somethings happening
$('#feed-header-inner h2').css('opacity','0.2');
$('#feed-header-inner h2').animate({opacity:'1'},1000);
// if we're just reloading, we dont need to: // if we're just reloading, we dont need to:
// (1) check if we have a cached version of this stream // (1) check if we have a cached version of this stream
// (2) remove the stream if we don't // (2) remove the stream if we don't
@ -689,11 +696,18 @@ function setNewCurrentStream(streamObject,setLocation,fallbackId,actionOnSuccess
if(haveOldStreamState) { if(haveOldStreamState) {
$('.profile-card,.hover-card,.hover-card-caret').remove(); $('.profile-card,.hover-card,.hover-card-caret').remove();
$('#feed').before(haveOldStreamState.card); $('#feed').before(haveOldStreamState.card);
$('#feed-body').html(haveOldStreamState.feed);
// subtle animation to show somethings happening var oldStreamState = $('<div/>').html(haveOldStreamState.feed);
$('#feed-header-inner h2').css('opacity','0.2');
$('#feed-header-inner h2').animate({opacity:'1'},1000); // if the cached items has data-quitter-id-in-stream attributes, sort them before adding them
if(oldStreamState.children('.stream-item[data-quitter-id-in-stream]').length>0) {
oldStreamState.sortDivsByAttrDesc('data-quitter-id-in-stream');
$('#feed-body').html('');
oldStreamState.children('.stream-item[data-quitter-id-in-stream]').appendTo('#feed-body');
}
else {
$('#feed-body').html(haveOldStreamState.feed);
}
// set location bar from stream // set location bar from stream
if(setLocation) { if(setLocation) {
@ -717,11 +731,12 @@ function setNewCurrentStream(streamObject,setLocation,fallbackId,actionOnSuccess
$('.profile-card,.hover-card,.hover-card-caret').remove(); $('.profile-card,.hover-card,.hover-card-caret').remove();
$('#feed').css('opacity',0); $('#feed').css('opacity',0);
$('#feed-body').html(''); $('#feed-body').html('');
remove_spinner(); display_spinner(); // display spinner in page header instead feed header
} }
// (3) change design immediately to either cached design or logged in user's // (3) change design immediately to either cached design or logged in user's
if(typeof window.oldStreamsDesigns[theUserOrGroupThisStreamBelongsTo(window.currentStream)] != 'undefined') { if(typeof window.oldStreamsDesigns[window.currentStreamObject.nickname] != 'undefined') {
changeDesign(window.oldStreamsDesigns[theUserOrGroupThisStreamBelongsTo(window.currentStream)]); changeDesign(window.oldStreamsDesigns[window.currentStreamObject.nickname]);
} }
else { else {
changeDesign({backgroundimage:window.loggedIn.background_image, backgroundcolor:window.loggedIn.backgroundcolor, linkcolor:window.loggedIn.linkcolor}); changeDesign({backgroundimage:window.loggedIn.background_image, backgroundcolor:window.loggedIn.backgroundcolor, linkcolor:window.loggedIn.linkcolor});
@ -732,7 +747,7 @@ function setNewCurrentStream(streamObject,setLocation,fallbackId,actionOnSuccess
getFromAPI(streamObject.stream, function(queet_data, userArray, error, url){ getFromAPI(streamObject.stream, function(queet_data, userArray, error, url){
// while waiting for this data user might have changed stream, so only proceed if current stream still is this one // while waiting for this data user might have changed stream, so only proceed if current stream still is this one
if(window.currentStream != streamObject.stream) { if(window.currentStreamObject.stream != streamObject.stream) {
console.log('stream has changed, aborting'); console.log('stream has changed, aborting');
return; return;
} }
@ -801,7 +816,9 @@ function setNewCurrentStream(streamObject,setLocation,fallbackId,actionOnSuccess
else if(error.status == 410 && streamObject.name == 'notice') { else if(error.status == 410 && streamObject.name == 'notice') {
showErrorMessage(window.sL.ERRORnoticeRemoved); showErrorMessage(window.sL.ERRORnoticeRemoved);
} }
else if(error.status == 0) { else if(error.status == 0
|| (error.status == 200 && error.responseText == 'An error occurred.')
) {
showErrorMessage(window.sL.ERRORnoContactWithServer + ' (' + replaceHtmlSpecialChars(error.statusText) + ')'); showErrorMessage(window.sL.ERRORnoContactWithServer + ' (' + replaceHtmlSpecialChars(error.statusText) + ')');
} }
else { else {
@ -851,13 +868,48 @@ function setNewCurrentStream(streamObject,setLocation,fallbackId,actionOnSuccess
addStreamToHistoryMenuAndMarkAsCurrent(streamObject); addStreamToHistoryMenuAndMarkAsCurrent(streamObject);
remove_spinner(); remove_spinner();
$('#feed-body').html(''); // empty feed body
$('#new-queets-bar').parent().addClass('hidden'); document.title = window.siteTitle; // hide new queets bar if it's visible there // some streams, e.g. /statuses/show/1234.json is not enclosed in an array, make sure it is
addToFeed(queet_data, false,'visible'); // add stream items to feed element if(!$.isArray(queet_data)) {
$('#feed').animate({opacity:'1'},150); // fade in queet_data = [queet_data];
}
// empty feed-body if this is a
// (1) notice page
// (2) if we got an empty result
// (3) it's not a stream of notices or notifications
if(window.currentStreamObject.name == 'notice'
|| queet_data.length==0
|| (window.currentStreamObject.type != 'notices' && window.currentStreamObject.type != 'notifications')) {
$('#feed-body').html('');
}
// if the last item in the stream doesn't exists in the feed-body, we can't
// just prepend the new items, since it will create a gap in the middle of the
// feed. in that case we just empty the body and start from scratch
else if($('#feed-body').children('.stream-item[data-quitter-id-in-stream="' + queet_data.slice(-1)[0].id + '"]').length == 0) {
$('#feed-body').html('');
}
// if the stream is slow to load, the user might have expanded a notice, or scrolled down
// and started reading. in that case we add the new items _hidden_
if($('#feed-body').children('.stream-item.expanded').length>0 || $(window).scrollTop() > 50) {
addToFeed(queet_data, false,'hidden');
maybeShowTheNewQueetsBar();
}
else {
addToFeed(queet_data, false,'visible');
}
// fade in if we need too
if(parseInt($('#feed').css('opacity'),10) == '0') {
$('#feed').animate({opacity:'1'},150);
}
$('.reload-stream').show(); $('.reload-stream').show();
$('body').removeClass('loading-older');$('body').removeClass('loading-newer'); $('body').removeClass('loading-older');$('body').removeClass('loading-newer');
$('html,body').scrollTop(0); // scroll to top
// maybe do something // maybe do something
if(typeof actionOnSuccess == 'function') { if(typeof actionOnSuccess == 'function') {
@ -1028,36 +1080,8 @@ function expand_queet(q,doScrolling) {
q.addClass('expanded'); q.addClass('expanded');
q.prev().addClass('next-expanded'); q.prev().addClass('next-expanded');
// if shortened queet, get full text // get full html, if shortened
if(q.children('.queet').find('span.attachment.more').length>0 && q.data('attachments') != 'undefined') { getFullUnshortenedHtmlForQueet(q);
// get full html for queet, first try localstorage cache
var cacheData = localStorageObjectCache_GET('fullQueetHtml',qid);
if(cacheData) {
q.children('.queet').find('.queet-text').html(cacheData);
q.children('.queet').outerHTML(detectRTL(q.children('.queet').outerHTML()));
}
else {
var attachmentId = q.children('.queet').find('span.attachment.more').attr('data-attachment-id');
// the url to the text/html attachment is in an array in an attribute
$.each(q.data('attachments'), function(k,attachment) {
if(attachment.id == attachmentId) {
$.get(attachment.url,function(data){
if(data) {
// get body and store in localStorage
var bodyHtml = $('<html/>').html(data).find('body').html();
localStorageObjectCache_STORE('fullQueetHtml',qid,bodyHtml);
q.children('.queet').find('.queet-text').html($.trim(bodyHtml));
q.children('.queet').outerHTML(detectRTL(q.children('.queet').outerHTML()));
}
});
return false;
}
});
}
}
// 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'));
@ -1152,17 +1176,7 @@ function expand_queet(q,doScrolling) {
// 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') {
q.children('.queet').append(replyFormHtml(q,qid)); q.children('.queet').append(replyFormHtml(q,qid));
maybePrefillQueetBoxWithCachedText(q.children('.queet').find('.queet-box'));
// if we have cached text, expand the reply form and add that
var queetBox = q.children('.queet').find('.queet-box');
var cachedText = decodeURIComponent(queetBox.attr('data-cached-text'));
var cachedTextText = $('<div/>').html(cachedText).text();
if(cachedText != 'undefined') {
queetBox.click();
queetBox.html(cachedText);
setSelectionRange(queetBox[0], cachedTextText.length, cachedTextText.length);
queetBox.trigger('input');
}
} }
} }
} }
@ -1189,15 +1203,22 @@ function cleanUpAfterCollapseQueet(q) {
/* · /* ·
· ·
· Get a queet box, mainly for popups · Get a popup queet box
· ·
· @return the html for the queet box · @return the html for the queet box
· ·
· · · · · · · · · */ · · · · · · · · · */
function queetBoxHtml() { function queetBoxPopUpHtml() {
// if we have cached text in localstorage
var data = localStorageObjectCache_GET('queetBoxInput','pop-up-queet-box');
if(data) {
var cachedText = encodeURIComponent(data);
}
var startText = encodeURIComponent(window.sL.compose); var startText = encodeURIComponent(window.sL.compose);
return '<div class="inline-reply-queetbox"><div class="queet-box queet-box-syntax" data-start-text="' + startText + '">' + decodeURIComponent(startText) + '</div><div class="syntax-middle"></div><div class="syntax-two" contenteditable="true"></div><div class="mentions-suggestions"></div><div class="queet-toolbar toolbar-reply"><div class="queet-box-extras"><button data-tooltip="' + window.sL.tooltipAttachImage + '" class="upload-image"></button><button data-tooltip="' + window.sL.tooltipShortenUrls + '" class="shorten disabled">URL</button></div><div class="queet-button"><span class="queet-counter"></span><button>' + window.sL.queetVerb + '</button></div></div></div>'; return '<div class="inline-reply-queetbox"><div id="pop-up-queet-box" class="queet-box queet-box-syntax" data-start-text="' + startText + '" data-cached-text="' + cachedText + '">' + decodeURIComponent(startText) + '</div><div class="syntax-middle"></div><div class="syntax-two" contenteditable="true"></div><div class="mentions-suggestions"></div><div class="queet-toolbar toolbar-reply"><div class="queet-box-extras"><button data-tooltip="' + window.sL.tooltipAttachImage + '" class="upload-image"></button><button data-tooltip="' + window.sL.tooltipShortenUrls + '" class="shorten disabled">URL</button></div><div class="queet-button"><span class="queet-counter"></span><button>' + window.sL.queetVerb + '</button></div></div></div>';
} }
@ -1341,28 +1362,14 @@ function showConversation(q, qid, data, offsetScroll) {
// note: first we add the full conversation, but hidden // note: first we add the full conversation, but hidden
if(obj.id != qid) { if(obj.id != qid) {
var queetTime = parseTwitterDate(obj.created_at); var queetTime = parseTwitterDate(obj.created_at);
var queetHtml = buildQueetHtml(obj, obj.id, 'hidden-conversation', false, true);
if(obj.source == 'activity') {
// because we had an xss issue, the obj.statusnet_html of qvitter-deleted-activity-notices can contain unwanted html, so we escape..
obj.statusnet_html = replaceHtmlSpecialChars(obj.statusnet_html);
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>';
// detect rtl
queetHtml = detectRTL(queetHtml);
}
else {
var queetHtml = buildQueetHtml(obj, obj.id, 'conversation hidden-conversation', false, true);
}
if(q.hasClass('expanded')) { // add queet to conversation only if still expanded if(q.hasClass('expanded')) { // add queet to conversation only if still expanded
// replace already existing queets' html // replace already existing queets' html
if(q.children('#conversation-stream-item-' + obj.id).length > 0) { if(q.children('.stream-item.conversation[data-quitter-id="' + obj.id + '"]').length > 0) {
var streamItemInnerHtml = $('<div/>').append(queetHtml).find('.stream-item').html(); var streamItemInnerHtml = $('<div/>').append(queetHtml).find('.stream-item').html();
q.children('#conversation-stream-item-' + obj.id).html(streamItemInnerHtml); q.children('.stream-item.conversation[data-quitter-id="' + obj.id + '"]').html(streamItemInnerHtml);
} }
else if(before_or_after == 'before') { else if(before_or_after == 'before') {
q.children('.queet').before(queetHtml); q.children('.queet').before(queetHtml);
@ -1412,13 +1419,13 @@ function findAndMarkLastVisibleInConversation(streamItem) {
/* · /* ·
· ·
· Recursive walker functions to view onlt reyplies to replies, not full conversation · Recursive walker functions to view only reyplies to replies, not full conversation
· ·
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
function findInReplyToStatusAndShow(q, 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 = q.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 = q.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.css('opacity','1'); reply_found.css('opacity','1');
@ -1490,30 +1497,22 @@ function checkForHiddenConversationQueets(q, qid) {
· ·
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
function addToFeed(feed, after, extraClasses, isReply) { function addToFeed(feed, after, extraClasses) {
// some streams, e.g. /statuses/show/1234.json is not enclosed in an array, make sure it is // some streams, e.g. /statuses/show/1234.json is not enclosed in an array, make sure it is
if(!$.isArray(feed)) { if(!$.isArray(feed)) {
feed = [feed]; feed = [feed];
} }
var addedToTopOfFeedBodyNum = 0;
$.each(feed.reverse(), function (key,obj) { $.each(feed.reverse(), function (key,obj) {
var extraClassesThisRun = extraClasses; var extraClassesThisRun = extraClasses;
// is this a temp-post-placeholder? // if this is the notifications feed
var isTempPost = false; if(window.currentStreamObject.name == 'notifications') {
if(after) {
if(after.indexOf('stream-item-temp-post') > -1) {
isTempPost = true;
}
}
// if this is the notifications feed, but not if it is a reply
if(window.currentStream.substring(0,35) == 'qvitter/statuses/notifications.json'
&& !isReply) {
// don't show any notices with object_type "activity" // don't show any notices with object_type "activity"
if(typeof obj.notice != 'undefined' && obj.notice !== null && obj.notice.is_activity === true) { if(typeof obj.notice != 'undefined' && obj.notice !== null && obj.notice.is_activity === true) {
@ -1526,8 +1525,10 @@ function addToFeed(feed, after, extraClasses, isReply) {
obj.from_profile.description = obj.from_profile.description || ''; obj.from_profile.description = obj.from_profile.description || '';
var notificationTime = parseTwitterDate(obj.created_at); var notificationTime = parseTwitterDate(obj.created_at);
var notSeenHtml = '';
if(obj.is_seen == '0') { if(obj.is_seen == '0') {
extraClassesThisRun = extraClassesThisRun + ' not-seen' extraClassesThisRun += ' not-seen'
notSeenHtml = '<div class="not-seen-disc"></div>';
} }
// external // external
@ -1541,6 +1542,7 @@ function addToFeed(feed, after, extraClasses, isReply) {
var noticeTime = parseTwitterDate(obj.notice.created_at); var noticeTime = parseTwitterDate(obj.notice.created_at);
var notificationHtml = '<div data-quitter-id-in-stream="' + obj.id + '" id="stream-item-n-' + obj.id + '" class="stream-item ' + extraClassesThisRun + ' notification like">\ var notificationHtml = '<div data-quitter-id-in-stream="' + obj.id + '" id="stream-item-n-' + obj.id + '" class="stream-item ' + extraClassesThisRun + ' notification like">\
<div class="queet">\ <div class="queet">\
' + notSeenHtml + '\
<div class="dogear"></div>\ <div class="dogear"></div>\
' + ostatusHtml + '\ ' + ostatusHtml + '\
<div class="queet-content">\ <div class="queet-content">\
@ -1570,9 +1572,10 @@ function addToFeed(feed, after, extraClasses, isReply) {
var noticeTime = parseTwitterDate(obj.notice.created_at); var noticeTime = parseTwitterDate(obj.notice.created_at);
var notificationHtml = '<div data-quitter-id-in-stream="' + obj.id + '" id="stream-item-n-' + obj.id + '" class="stream-item ' + extraClassesThisRun + ' notification repeat">\ var notificationHtml = '<div data-quitter-id-in-stream="' + obj.id + '" id="stream-item-n-' + obj.id + '" class="stream-item ' + extraClassesThisRun + ' notification repeat">\
<div class="queet">\ <div class="queet">\
' + notSeenHtml + '\
<div class="dogear"></div>\
' + ostatusHtml + '\
<div class="queet-content">\ <div class="queet-content">\
<div class="dogear"></div>\
' + ostatusHtml + '\
<div class="stream-item-header">\ <div class="stream-item-header">\
<a class="account-group" href="' + obj.from_profile.statusnet_profile_url + '">\ <a class="account-group" href="' + obj.from_profile.statusnet_profile_url + '">\
<img class="avatar" src="' + obj.from_profile.profile_image_url + '" />\ <img class="avatar" src="' + obj.from_profile.profile_image_url + '" />\
@ -1610,24 +1613,13 @@ function addToFeed(feed, after, extraClasses, isReply) {
} }
else { else {
$('#feed-body').prepend(notificationHtml); $('#feed-body').prepend(notificationHtml);
addedToTopOfFeedBodyNum++;
} }
// add not seen notification circle
$.each($('.notification.not-seen .queet'),function(){
if($(this).children('.not-seen').length<1) {
$(this).prepend('<div class="not-seen-disc"></div>');
}
});
} }
} }
// if this is a user feed // if this is a user feed
else if((window.currentStream.substring(0,21) == 'statuses/friends.json' else if(window.currentStreamObject.type == 'users') {
|| window.currentStream.substring(0,18) == 'statuses/followers'
|| window.currentStream.substring(0,28) == 'statusnet/groups/membership/'
|| window.currentStream.substring(0,24) == 'statusnet/groups/admins/')
&& isTempPost === false // not if we're posting queet
) {
// only if not user is already in stream // only if not user is already in stream
if($('#stream-item-' + obj.id).length == 0) { if($('#stream-item-' + obj.id).length == 0) {
@ -1672,14 +1664,13 @@ function addToFeed(feed, after, extraClasses, isReply) {
} }
else { else {
$('#feed-body').prepend(userHtml); $('#feed-body').prepend(userHtml);
addedToTopOfFeedBodyNum++;
} }
} }
} }
// if this is a list of groups // if this is a list of groups
else if(window.currentStream.substring(0,26) == 'statusnet/groups/list.json' else if(window.currentStreamObject.type == 'groups') {
&& isTempPost === false // not if we're posting queet
) {
// only if not group is already in stream // only if not group is already in stream
if($('#stream-item-' + obj.id).length == 0) { if($('#stream-item-' + obj.id).length == 0) {
@ -1713,41 +1704,19 @@ function addToFeed(feed, after, extraClasses, isReply) {
} }
else { else {
$('#feed-body').prepend(groupHtml); $('#feed-body').prepend(groupHtml);
addedToTopOfFeedBodyNum++;
} }
} }
} }
// if this is a retweet // if this is a retweet
else if(typeof obj.retweeted_status != 'undefined') { // (note the difference between "the repeat-notice" and "the repeated notice")
// but the unrepeat delete activity notices have retweeted_status added to them, so check this is not a delete notice
// don't show any notices with object_type "activity" else if(typeof obj.retweeted_status != 'undefined'
if(typeof obj.retweeted_status.is_activity != 'undefined' && obj.retweeted_status.is_activity === true) { && (typeof obj.qvitter_delete_notice == 'undefined' || obj.qvitter_delete_notice === false)) {
return true;
}
// retweeted object already exist in feed
if($('#q-' + obj.retweeted_status.id).length > 0) {
// only if not already shown and not mine
if($('#requeet-' + obj.id).length == 0 && obj.user.statusnet_profile_url != $('#user-profile-link').children('a').attr('href')) {
// if requeeted before
if($('#q-' + obj.retweeted_status.id + ' > .context').find('.requeet-text').length > 0) {
// if users rt not already added
if($('#q-' + obj.retweeted_status.id + ' > .context').find('.requeet-text').find('a[data-user-id="' + obj.user.id + '"]').length==0) {
$('#q-' + obj.retweeted_status.id + ' > .context').find('.requeet-text').children('a').last().after('<a data-user-id="' + obj.user.id + '" href="' + obj.user.statusnet_profile_url + '"> <b>' + obj.user.name + '</b></a>');
}
}
// if no context requeets
else {
var requeetHtml = '<a data-user-id="' + obj.user.id + '" href="' + obj.user.statusnet_profile_url + '"> <b>' + obj.user.name + '</b></a>';
$('#q-' + obj.retweeted_status.id).prepend('<div class="context" id="requeet-' + obj.id + '"><span class="with-icn"><i class="badge-requeeted" data-tooltip="' + parseTwitterDate(obj.created_at) + '"></i><span class="requeet-text"> ' + window.sL.requeetedBy.replace('{requeeted-by}',requeetHtml) + '</span></span></div>');
}
}
}
// retweeted object don't exist in feed
else {
// if repeat-notice doesn't already exist in feed
if($('#stream-item-' + obj.id).length == 0) {
var queetHtml = buildQueetHtml(obj.retweeted_status, obj.id, extraClassesThisRun, obj); var queetHtml = buildQueetHtml(obj.retweeted_status, obj.id, extraClassesThisRun, obj);
if(after) { if(after) {
@ -1755,113 +1724,45 @@ function addToFeed(feed, after, extraClasses, isReply) {
} }
else { else {
$('#feed-body').prepend(queetHtml); $('#feed-body').prepend(queetHtml);
addedToTopOfFeedBodyNum++;
} }
} }
} }
// ordinary tweet // ordinary tweet
else { else {
// if this is a special qvitter-delete-notice activity notice it means we try to hide
// the deleted notice from our stream
// the uri is in the obj.text var, between the double curly brackets
if(typeof obj.qvitter_delete_notice != 'undefined' && obj.qvitter_delete_notice == true) {
var uriToHide = obj.text.substring(obj.text.indexOf('{{')+2,obj.text.indexOf('}}'));
var streamItemToHide = $('.stream-item[data-uri="' + uriToHide + '"]');
streamItemToHide.animate({opacity:'0.2'},1000,'linear',function(){
$(this).css('height',$(this).height() + 'px');
$(this).animate({height:'0px'},500,'linear',function(){
$(this).remove();
});
});
}
// only if not already exist // only if not already exist
if($('#q-' + obj.id).length == 0) { if($('#stream-item-' + obj.id).length == 0) {
// activity get special design var queetHtml = buildQueetHtml(obj, obj.id, extraClassesThisRun);
if(obj.source == 'activity' || obj.is_activity === true) {
// because we had an xss issue, the obj.statusnet_html of qvitter-deleted-activity-notices can contain unwanted html, so we escape..
obj.statusnet_html = replaceHtmlSpecialChars(obj.statusnet_html);
var queetTime = parseTwitterDate(obj.created_at);
var queetHtml = '<div id="stream-item-' + obj.id + '" class="stream-item activity ' + extraClassesThisRun + '" data-quitter-id="' + obj.id + '" data-conversation-id="' + obj.statusnet_conversation_id + '" data-quitter-id-in-stream="' + obj.id + '"><div class="queet" id="q-' + obj.id + '"><div class="queet-content"><div class="stream-item-header"><small class="created-at" data-created-at="' + obj.created_at + '"><a href="' + window.siteInstanceURL + 'notice/' + obj.id + '">' + queetTime + '</a></small></div><div class="queet-text">' + $.trim(obj.statusnet_html) + '</div></div></div></div>';
// detect rtl
queetHtml = detectRTL(queetHtml);
if(after) {
$('#' + after).after(queetHtml);
}
else {
$('#feed-body').prepend(queetHtml);
}
if(after) {
$('#' + after).after(queetHtml);
} }
else { else {
$('#feed-body').prepend(queetHtml);
addedToTopOfFeedBodyNum++;
// if this is my queet, remove any temp-queets // if this is a single notice, we expand it
if(typeof obj.user != 'undefined') { if(window.currentStreamObject.name == 'notice') {
if(obj.user.screen_name == $('#user-screen-name').html()) { expand_queet($('#stream-item-' + obj.id));
if($('.temp-post').length > 0) {
$('.temp-post').each(function (){
// remove temp duplicate
$(this).css('display','none');
// we do this so this queet gets added after correct temp-queet in expanded conversations
if($(this).find('.queet-text').text() == obj.text) {
after = $(this).attr('id');
}
// but don't hide my new queet
extraClassesThisRun = 'visible';
});
}
}
} }
var queetHtml = buildQueetHtml(obj, obj.id, extraClassesThisRun);
if(after) {
if($('#' + after).hasClass('conversation')) { // if this is a reply, give stream item some conversation formatting
if($('#conversation-q-' + obj.id).length == 0) { // only if it's not already there
$('#' + after).after(queetHtml.replace('id="stream-item','id="conversation-stream-item').replace('class="stream-item','class="stream-item conversation').replace('id="q','id="conversation-q'));
$('#' + after).remove();
}
}
else {
$('#' + after).after(queetHtml);
}
}
else {
$('#feed-body').prepend(queetHtml);
// if this is a single notice, we expand it
if(window.currentStream.substring(0,14) == 'statuses/show/') {
expand_queet($('#stream-item-' + obj.id));
}
}
// fadeout any posting-popups
setTimeout(function(){
$('#popup-sending').fadeOut(1000, function(){
$('#popup-sending').remove();
});
},100);
} }
} }
} }
convertAttachmentMoreHref();
}); });
convertAttachmentMoreHref();
// if we've added stuff to the top of feed-body, we update our stream cache
if(addedToTopOfFeedBodyNum>0) {
rememberStreamStateInLocalStorage();
}
$('.stream-selection').removeAttr('data-current-user-stream-name'); // don't remeber user feeds $('.stream-selection').removeAttr('data-current-user-stream-name'); // don't remeber user feeds
} }
/* · /* ·
· ·
· Build HTML for a queet from an object · Build HTML for a queet from an object
@ -1871,20 +1772,38 @@ function addToFeed(feed, after, extraClasses, isReply) {
· ·
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
function buildQueetHtml(obj, idInStream, extraClassesThisRun, requeeted_by, isConversation) { function buildQueetHtml(obj, idInStream, extraClasses, requeeted_by, isConversation) {
// if we've blocked this user, but it has slipped through anyway // if we've blocked this user, but it has slipped through anyway
var blockingTooltip = ''; var blockingTooltip = '';
if(typeof window.allBlocking != 'undefined') { if(typeof window.allBlocking != 'undefined') {
$.each(window.allBlocking,function(){ $.each(window.allBlocking,function(){
if(this == obj.user.id){ if(this == obj.user.id){
extraClassesThisRun = extraClassesThisRun + ' profile-blocked-by-me'; extraClasses += ' profile-blocked-by-me';
blockingTooltip = ' data-tooltip="' + window.sL.thisIsANoticeFromABlockedUser + '"'; blockingTooltip = ' data-tooltip="' + window.sL.thisIsANoticeFromABlockedUser + '"';
return false; // break return false; // break
} }
}); });
} }
// deleted?
if(window.knownDeletedNotices[obj.uri]) {
}
// unrepeated?
if(typeof requeeted_by != 'undefined' && requeeted_by !== false) {
if(window.knownDeletedNotices[requeeted_by.uri]) {
}
}
// activity? (hidden with css)
if(obj.source == 'activity' || obj.is_activity === true) {
extraClasses += ' activity';
// because we had an xss issue with activities, the obj.statusnet_html of qvitter-deleted-activity-notices can contain unwanted html, so we escape, they are hidden anyway
obj.statusnet_html = replaceHtmlSpecialChars(obj.statusnet_html);
}
// if we have the full html for a truncated notice cached in localstorage, we use that // if we have the full html for a truncated notice cached in localstorage, we use that
var cacheData = localStorageObjectCache_GET('fullQueetHtml',obj.id); var cacheData = localStorageObjectCache_GET('fullQueetHtml',obj.id);
@ -1902,6 +1821,7 @@ function buildQueetHtml(obj, idInStream, extraClassesThisRun, requeeted_by, isCo
var idPrepend = ''; var idPrepend = '';
if(typeof isConversation != 'undefined' && isConversation === true) { if(typeof isConversation != 'undefined' && isConversation === true) {
var idPrepend = 'conversation-'; var idPrepend = 'conversation-';
extraClasses += ' conversation'
} }
// is this mine? // is this mine?
@ -1917,19 +1837,17 @@ function buildQueetHtml(obj, idInStream, extraClassesThisRun, requeeted_by, isCo
} }
// requeet html // requeet html
var requeetedClass = '';
if(obj.repeated) { 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>'; var requeetHtml = '<li class="action-rt-container"><a class="with-icn done"><span class="icon sm-rt" title="' + window.sL.requeetedVerb + '"></span></a></li>';
var requeetedClass = 'requeeted'; extraClasses += ' requeeted';
} }
else { 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>'; 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 // favorite html
var favoritedClass = '';
if(obj.favorited) { if(obj.favorited) {
var favoriteHtml = '<a class="with-icn done"><span class="icon sm-fav" title="' + window.sL.favoritedVerb + '"></span></a>'; var favoriteHtml = '<a class="with-icn done"><span class="icon sm-fav" title="' + window.sL.favoritedVerb + '"></span></a>';
favoritedClass = 'favorited'; extraClasses += ' favorited';
} }
else { else {
var favoriteHtml = '<a class="with-icn"><span class="icon sm-fav" title="' + window.sL.favoriteVerb + '"></span></a>'; var favoriteHtml = '<a class="with-icn"><span class="icon sm-fav" title="' + window.sL.favoriteVerb + '"></span></a>';
@ -2018,25 +1936,40 @@ function buildQueetHtml(obj, idInStream, extraClassesThisRun, requeeted_by, isCo
}); });
} }
// requeets // requeets get's a context element and a identifying class
// uri used is the repeate-notice's uri for repeats, not the repetED notice's uri (necessary if someone deletes a repeat)
var URItoUse = obj.uri;
var requeetHtml = ''; var requeetHtml = '';
if(typeof requeeted_by != 'undefined' && requeeted_by !== false) { 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" data-tooltip="' + parseTwitterDate(requeeted_by.created_at) + '"></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" data-tooltip="' + parseTwitterDate(requeeted_by.created_at) + '"></i><span class="requeet-text"> ' + window.sL.requeetedBy.replace('{requeeted-by}',requeetedByHtml) + '</span></span></div>';
var URItoUse = requeeted_by.uri;
extraClasses += ' is-requeet';
} }
// the URI for delete activity notices are the same as the notice that is to be deleted
// so we make the URI for the (hidden) actitity notice unique, otherwise we might remove
// the activity notice from DOM when we remove the deleted notice
if(typeof obj.qvitter_delete_notice != 'undefined' && obj.qvitter_delete_notice == true) {
URItoUse += '-activity-notice';
}
if(typeof requeeted_by != 'undefined' && requeeted_by !== false) {
}
// external // external
var ostatusHtml = ''; var ostatusHtml = '';
if(obj.is_local === false) { if(obj.is_local === false) {
ostatusHtml = '<a target="_blank" data-tooltip="' + window.sL.goToOriginalNotice + '" class="ostatus-link" href="' + obj.external_url + '"></a>'; ostatusHtml = '<a target="_blank" data-tooltip="' + window.sL.goToOriginalNotice + '" class="ostatus-link" href="' + obj.external_url + '"></a>';
} }
var queetTime = parseTwitterDate(obj.created_at); var queetTime = parseTwitterDate(obj.created_at);
var queetHtml = '<div \ var queetHtml = '<div \
id="' + idPrepend + 'stream-item-' + obj.id + '" \ id="' + idPrepend + 'stream-item-' + idInStream + '" \
data-uri="' + obj.uri + '" \ data-uri="' + URItoUse + '" \
class="stream-item ' + extraClassesThisRun + ' ' + requeetedClass + ' ' + favoritedClass + '" \ class="stream-item ' + extraClasses + '" \
data-attachments=\'' + JSON.stringify(obj.attachments) + '\'\ data-attachments=\'' + JSON.stringify(obj.attachments) + '\'\
data-source="' + escape(obj.source) + '" \ data-source="' + escape(obj.source) + '" \
data-quitter-id="' + obj.id + '" \ data-quitter-id="' + obj.id + '" \
@ -2045,7 +1978,7 @@ function buildQueetHtml(obj, idInStream, extraClassesThisRun, requeeted_by, isCo
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 + '"\
' + requeetedByMe + '>\ ' + requeetedByMe + '>\
<div class="queet" id="' + idPrepend + 'q-' + obj.id + '"' + blockingTooltip + '>\ <div class="queet" id="' + idPrepend + 'q-' + idInStream + '"' + blockingTooltip + '>\
' + requeetHtml + '\ ' + requeetHtml + '\
' + ostatusHtml + '\ ' + ostatusHtml + '\
<div class="queet-content">\ <div class="queet-content">\

View File

@ -51,35 +51,37 @@
function localStorageObjectCache_STORE(name, unique_id, object) { function localStorageObjectCache_STORE(name, unique_id, object) {
if(localStorageIsEnabled()) { if(localStorageIsEnabled() === false) {
return false;
}
name = localStorageMaybeAppendIdToKey(name);
if(object === false || object === null || object.length < 1) {
// false or an empty object means we remove this entry
if(typeof localStorage[name + '-' + unique_id] != 'undefined' && localStorage[name + '-' + unique_id] !== null) {
delete localStorage[name + '-' + unique_id];
}
return false;
}
var dataToSave = {};
dataToSave.modified = Date.now();
dataToSave.cdata = LZString.compressToUTF16(JSON.stringify(object));
try {
localStorage.setItem(name + '-' + unique_id, JSON.stringify(dataToSave));
}
catch (e) {
if (e.name == 'QUOTA_EXCEEDED_ERR' || e.name == 'NS_ERROR_DOM_QUOTA_REACHED' || e.name == 'QuotaExceededError' || e.name == 'W3CException_DOM_QUOTA_EXCEEDED_ERR') {
removeOldestLocalStorageEntries(function(){
localStorageObjectCache_STORE(name, unique_id, object);
});
if(object === false || object === null || object.length < 1) {
// false or an empty object means we remove this entry
if(typeof localStorage[name + '-' + unique_id] != 'undefined' && localStorage[name + '-' + unique_id] !== null) {
delete localStorage[name + '-' + unique_id];
}
} }
else { else {
console.log('could not store in localStorage, unknown error');
var dataToSave = {};
dataToSave.modified = Date.now();
dataToSave.cdata = LZString.compressToUTF16(JSON.stringify(object));
try {
localStorage.setItem(name + '-' + unique_id, JSON.stringify(dataToSave));
}
catch (e) {
if (e.name == 'QUOTA_EXCEEDED_ERR' || e.name == 'NS_ERROR_DOM_QUOTA_REACHED' || e.name == 'QuotaExceededError' || e.name == 'W3CException_DOM_QUOTA_EXCEEDED_ERR') {
removeOldestLocalStorageEntries(function(){
localStorageObjectCache_STORE(name, unique_id, object);
});
}
else {
console.log('could not store in localStorage, unknown error');
}
}
} }
} }
} }
@ -131,38 +133,61 @@ function removeOldestLocalStorageEntries(callback) {
· · · · · · · · · */ · · · · · · · · · */
function localStorageObjectCache_GET(name, unique_id) { function localStorageObjectCache_GET(name, unique_id) {
if(localStorageIsEnabled()) {
if(typeof localStorage[name + '-' + unique_id] != 'undefined' && localStorage[name + '-' + unique_id] !== null) { if(localStorageIsEnabled() === false) {
try { return false;
var parsedObject = JSON.parse(localStorage[name + '-' + unique_id]); }
}
catch(e) { name = localStorageMaybeAppendIdToKey(name);
return false;
} if(typeof localStorage[name + '-' + unique_id] == 'undefined' || localStorage[name + '-' + unique_id] === null) {
if(typeof parsedObject.modified == 'undefined' || parsedObject.modified === null) { return false;
// invalid or old localstorage object found, check the whole localstorage! }
checkLocalStorage();
return false; try {
} var parsedObject = JSON.parse(localStorage[name + '-' + unique_id]);
else { }
try { catch(e) {
var decompressedAndParsed = JSON.parse(LZString.decompressFromUTF16(parsedObject.cdata)); return false;
return decompressedAndParsed; }
}
catch(e) { if(typeof parsedObject.modified == 'undefined' || parsedObject.modified === null) {
return false; // invalid or old localstorage object found, check the whole localstorage!
} checkLocalStorage();
} return false;
}
else {
try {
var decompressedAndParsed = JSON.parse(LZString.decompressFromUTF16(parsedObject.cdata));
return decompressedAndParsed;
} }
else { catch(e) {
return false; return false;
} }
} }
}
// to the following data types we add the logged in user's user id,
// since they contain user specific data (0 for logged out)
// selectedLanguage is handled differently, since we want to be able to
// access the logged out user's data if we're logged in
function localStorageMaybeAppendIdToKey(name) {
if(jQuery.inArray(name, ['browsingHistory', 'conversation', 'queetBoxInput', 'streamState']) !== -1) {
if(window.loggedIn) {
return name + '-' + window.loggedIn.id;
}
else {
return name + '-0' ;
}
}
else { else {
return false; return name;
} }
} }
function checkLocalStorage() { function checkLocalStorage() {
if(localStorageIsEnabled() === false) { if(localStorageIsEnabled() === false) {
@ -246,6 +271,60 @@ function checkLocalStorage() {
} }
/* ·
·
· Checks if localstorage is availible
·
· We can't just do if(typeof localStorage.selectedLanguage != 'undefined')
· because firefox with cookies disabled then freaks out and stops executing js completely
·
· · · · · · · · · */
function localStorageIsEnabled() {
var mod = 'test';
try {
localStorage.setItem(mod, mod);
localStorage.removeItem(mod);
return true;
}
catch(e) {
if (e.name == 'QUOTA_EXCEEDED_ERR' || e.name == 'NS_ERROR_DOM_QUOTA_REACHED' || e.name == 'QuotaExceededError' || e.name == 'W3CException_DOM_QUOTA_EXCEEDED_ERR') {
removeOldestLocalStorageEntries(function(){
localStorageIsEnabled();
});
}
else {
return false;
}
}
}
/* ·
·
· Check for hidden items and show the new queets bar if there are any
·
· · · · · · · · · */
function maybeShowTheNewQueetsBar() {
var new_queets_num = $('#feed-body').find('.stream-item.hidden:not(.activity)').length;
if(new_queets_num > 0) {
$('#new-queets-bar').parent().removeClass('hidden');
// bar label
if(new_queets_num == 1) { var q_txt = window.sL.newQueet; }
else { var q_txt = window.sL.newQueets; }
if(window.currentStreamObject.name == 'notifications') {
if(new_queets_num == 1) { var q_txt = window.sL.newNotification; }
else { var q_txt = window.sL.newNotifications; }
}
$('#new-queets-bar').html(q_txt.replace('{new-notice-count}',new_queets_num));
}
}
/* · /* ·
· ·
@ -568,10 +647,132 @@ function searchForUserDataToCache(obj) {
} }
} }
/* ·
·
· Iterates recursively through an API response in search for updated notice data
· If we find a "repeated" key we assume the parent is a notice object (chosen arbitrary)
·
· · · · · · · · · · · · · */
window.knownDeletedNotices = new Object();
function searchForUpdatedNoticeData(obj) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
searchForUpdatedNoticeData(obj[property]);
}
else if(typeof obj[property] == 'boolean' && property == 'repeated') {
var streamItemFoundInFeed = $('.stream-item[data-conversation-id][data-quitter-id="' + obj.id + '"]'); // data-conversation-id identifies it as a notice, not a user or something
// if this is a special qvitter-delete-notice activity notice it means we try to hide
// the deleted notice from our stream
// the uri is in the obj.text var, between the double curly brackets
if(typeof obj.qvitter_delete_notice != 'undefined' && obj.qvitter_delete_notice == true) {
var uriToHide = obj.text.substring(obj.text.indexOf('{{')+2,obj.text.indexOf('}}'));
window.knownDeletedNotices[uriToHide] = true;
var streamItemToHide = $('.stream-item[data-uri="' + uriToHide + '"]');
slideUpAndRemoveStreamItem(streamItemToHide);
}
// ordinary notices
else if(streamItemFoundInFeed.length>0) {
var queetFoundInFeed = streamItemFoundInFeed.children('.queet');
var queetID = streamItemFoundInFeed.attr('data-quitter-id');
// console.log(obj);
// sometimes activity notices don't get the is_activity flag set to true
// maybe because they were in the process of being saved when
// we first got them
if(obj.is_activity) {
streamItemFoundInFeed.addClass('activity');
}
// update the avatar row if the queet is expanded and the numbers are not the same
if(streamItemFoundInFeed.hasClass('expanded')) {
var oldFavNum = parseInt(queetFoundInFeed.find('.action-fav-num').text(),10);
var oldRQNum = parseInt(queetFoundInFeed.find('.action-rq-num').text(),10);
if(oldFavNum != obj.fave_num || oldRQNum != obj.repeat_num) {
getFavsAndRequeetsForQueet(streamItemFoundInFeed, queetID);
}
}
// avatar may have changed
if(typeof obj.user != 'undefined'
&& typeof obj.user.profile_image_url_profile_size != 'undefined'
&& queetFoundInFeed.find('img.avatar').src != obj.user.profile_image_url_profile_size) {
queetFoundInFeed.find('img.avatar').attr('src',obj.user.profile_image_url_profile_size);
}
// name may have changed
if(typeof obj.user != 'undefined'
&& typeof obj.user.name != 'undefined'
&& queetFoundInFeed.find('strong.name').html() != obj.user.name) {
queetFoundInFeed.find('strong.name').html(obj.user.name);
}
// set favorite data
queetFoundInFeed.find('.action-fav-num').attr('data-fav-num',obj.fave_num);
queetFoundInFeed.find('.action-fav-num').html(obj.fave_num);
if(obj.favorited) {
streamItemFoundInFeed.addClass('favorited');
queetFoundInFeed.find('.action-fav-container').children('.with-icn').addClass('done');
queetFoundInFeed.find('.action-fav-container').find('.icon.sm-fav').attr('data-tooltip',window.sL.favoritedVerb);
}
else {
streamItemFoundInFeed.removeClass('favorited');
queetFoundInFeed.find('.action-fav-container').children('.with-icn').removeClass('done');
queetFoundInFeed.find('.action-fav-container').find('.icon.sm-fav').attr('data-tooltip',window.sL.favoriteVerb);
}
// set repeat data
queetFoundInFeed.find('.action-rq-num').attr('data-rq-num',obj.repeat_num);
queetFoundInFeed.find('.action-rq-num').html(obj.repeat_num);
if(obj.repeated) {
streamItemFoundInFeed.addClass('requeeted');
queetFoundInFeed.find('.action-rt-container').children('.with-icn').addClass('done');
queetFoundInFeed.find('.action-rt-container').find('.icon.sm-rt').attr('data-tooltip',window.sL.requeetedVerb);
streamItemFoundInFeed.attr('data-requeeted-by-me-id',obj.repeated_id);
}
else {
streamItemFoundInFeed.removeClass('requeeted');
queetFoundInFeed.find('.action-rt-container').children('.with-icn').removeClass('done');
queetFoundInFeed.find('.action-rt-container').find('.icon.sm-rt').attr('data-tooltip',window.sL.requeetVerb);
streamItemFoundInFeed.removeAttr('data-requeeted-by-me-id');
}
}
}
}
}
}
/* · /* ·
· ·
· Store the current stream's state (html) in localStorage · Removes a stream item from the feed gracefully
·
· · · · · · · · · */
function slideUpAndRemoveStreamItem(streamItem,callback) {
if(streamItem.length>0) {
streamItem.animate({opacity:'0.2'},1000,'linear',function(){
$(this).css('height',$(this).height() + 'px');
$(this).animate({height:'0px'},500,'linear',function(){
$(this).remove();
if(typeof callback == 'function') {
callback();
}
});
});
}
}
/* ·
·
· Store the current stream's state (html) in localStorage (if we're logged in)
· ·
· · · · · · · · · */ · · · · · · · · · */
@ -580,17 +781,25 @@ function rememberStreamStateInLocalStorage() {
if(typeof window.currentStreamObject != 'undefined') { if(typeof window.currentStreamObject != 'undefined') {
// dont store open conversations, and only store profile card and the top 20 stream-items // dont store open conversations, and only store profile card and the top 20 stream-items
var firstTwentyHTML = ''; var firstTwentyVisibleHTML = '';
$.each($('#feed-body').children('.stream-item').slice(0,20),function(){ var i = 0;
firstTwentyHTML += $(this).outerHTML(); $.each($('#feed-body').children('.stream-item'),function(k,streamItem){
firstTwentyVisibleHTML += $(streamItem).outerHTML();
if(!$(streamItem).hasClass('activity')) {
i++;
}
if(i>20) {
return false;
}
}); });
var feed = $('<div/>').append(firstTwentyHTML); var feed = $('<div/>').append(firstTwentyVisibleHTML);
feed.find('.view-more-container-top').remove(); feed.find('.view-more-container-top').remove();
feed.find('.view-more-container-bottom').remove(); feed.find('.view-more-container-bottom').remove();
feed.find('.stream-item.conversation').remove(); feed.find('.stream-item.conversation').remove();
feed.find('.expanded-content').remove(); feed.find('.expanded-content').remove();
feed.find('.inline-reply-queetbox').remove(); feed.find('.inline-reply-queetbox').remove();
feed.find('.stream-item').removeClass('expanded').removeClass('next-expanded'); feed.find('.not-seen-disc').remove();
feed.find('.stream-item').removeClass('expanded').removeClass('next-expanded').removeClass('hidden').addClass('visible');
var feedHtml = feed.html(); var feedHtml = feed.html();
var profileCardHtml = $('#feed').siblings('.profile-card').outerHTML(); var profileCardHtml = $('#feed').siblings('.profile-card').outerHTML();
var streamData = { var streamData = {
@ -603,6 +812,47 @@ function rememberStreamStateInLocalStorage() {
} }
/* ·
·
· Gets the full unshortened HTML for a queet
·
· · · · · · · · · */
function getFullUnshortenedHtmlForQueet(streamItem) {
var queet = streamItem.children('.queet');
var queetId = streamItem.attr('data-quitter-id');
var attachmentMore = queet.find('span.attachment.more');
// only if actually shortened
if(attachmentMore.length>0 && streamItem.data('attachments') != 'undefined') {
// first try localstorage cache
var cacheData = localStorageObjectCache_GET('fullQueetHtml',queetId);
if(cacheData) {
queet.find('.queet-text').html(cacheData);
queet.outerHTML(detectRTL(queet.outerHTML()));
}
// then try static html file attachment, that we should have in an array in a data-attachments attribute
else {
var attachmentId = attachmentMore.attr('data-attachment-id');
$.each(streamItem.data('attachments'), function(k,attachment) {
if(attachment.id == attachmentId) {
$.get(attachment.url,function(data){
if(data) {
// get body and store in localStorage
var bodyHtml = $('<html/>').html(data).find('body').html();
localStorageObjectCache_STORE('fullQueetHtml',queetId,bodyHtml);
queet.find('.queet-text').html($.trim(bodyHtml));
queet.outerHTML(detectRTL(queet.outerHTML()));
}
});
return false;
}
});
}
}
}
/* · /* ·
· ·
· Appends a user to the array containing the mentions suggestions to show when typing a notice · Appends a user to the array containing the mentions suggestions to show when typing a notice
@ -736,27 +986,6 @@ function replaceHtmlSpecialChars(text) {
return text.replace(/[&<>"']/g, function(m) { return map[m]; }); return text.replace(/[&<>"']/g, function(m) { return map[m]; });
} }
/* ·
·
· Checks if localstorage is availible
·
· We can't just do if(typeof localStorage.selectedLanguage != 'undefined')
· because firefox with cookies disabled then freaks out and stops executing js completely
·
· · · · · · · · · */
function localStorageIsEnabled() {
var mod = 'test';
try {
localStorage.setItem(mod, mod);
localStorage.removeItem(mod);
return true;
}
catch(e) {
return false;
}
}
/* · /* ·
· ·
@ -873,7 +1102,7 @@ function changeDesign(obj) {
// if we're logged out and this is the front page, we use the default design // if we're logged out and this is the front page, we use the default design
if(!window.loggedIn && if(!window.loggedIn &&
(window.currentStream == 'statuses/public_timeline.json' || window.currentStream == 'statuses/public_and_external_timeline.json')) { (window.currentStreamObject.name == 'public timeline' || window.currentStreamObject.name == 'public and external timeline')) {
obj.backgroundimage = window.fullUrlToThisQvitterApp + window.siteBackground; obj.backgroundimage = window.fullUrlToThisQvitterApp + window.siteBackground;
obj.backgroundcolor = window.defaultBackgroundColor; obj.backgroundcolor = window.defaultBackgroundColor;
obj.linkcolor = window.defaultLinkColor; obj.linkcolor = window.defaultLinkColor;
@ -886,8 +1115,8 @@ function changeDesign(obj) {
} }
// remember the design for this stream // remember the design for this stream
if(typeof window.oldStreamsDesigns[theUserOrGroupThisStreamBelongsTo(window.currentStream)] == 'undefined') { if(typeof window.oldStreamsDesigns[window.currentStreamObject.nickname] == 'undefined') {
window.oldStreamsDesigns[theUserOrGroupThisStreamBelongsTo(window.currentStream)] = new Object(); window.oldStreamsDesigns[window.currentStreamObject.nickname] = new Object();
} }
// change design elements // change design elements
@ -898,21 +1127,21 @@ function changeDesign(obj) {
else if(obj.backgroundimage.length > 4) { else if(obj.backgroundimage.length > 4) {
$('body').css('background-image','url(\'' + obj.backgroundimage + '\')'); $('body').css('background-image','url(\'' + obj.backgroundimage + '\')');
} }
window.oldStreamsDesigns[theUserOrGroupThisStreamBelongsTo(window.currentStream)].backgroundimage = obj.backgroundimage; window.oldStreamsDesigns[window.currentStreamObject.nickname].backgroundimage = obj.backgroundimage;
} }
if(typeof obj.backgroundcolor != 'undefined') { if(typeof obj.backgroundcolor != 'undefined') {
if(obj.backgroundcolor === false || obj.backgroundcolor == '') { if(obj.backgroundcolor === false || obj.backgroundcolor == '') {
obj.backgroundcolor = window.defaultBackgroundColor; obj.backgroundcolor = window.defaultBackgroundColor;
} }
changeBackgroundColor(obj.backgroundcolor); changeBackgroundColor(obj.backgroundcolor);
window.oldStreamsDesigns[theUserOrGroupThisStreamBelongsTo(window.currentStream)].backgroundcolor = obj.backgroundcolor; window.oldStreamsDesigns[window.currentStreamObject.nickname].backgroundcolor = obj.backgroundcolor;
} }
if(typeof obj.linkcolor != 'undefined') { if(typeof obj.linkcolor != 'undefined') {
if(obj.linkcolor === false || obj.linkcolor == '') { if(obj.linkcolor === false || obj.linkcolor == '') {
obj.linkcolor = window.defaultLinkColor; obj.linkcolor = window.defaultLinkColor;
} }
changeLinkColor(obj.linkcolor); changeLinkColor(obj.linkcolor);
window.oldStreamsDesigns[theUserOrGroupThisStreamBelongsTo(window.currentStream)].linkcolor = obj.linkcolor; window.oldStreamsDesigns[window.currentStreamObject.nickname].linkcolor = obj.linkcolor;
} }
} }
@ -1031,7 +1260,7 @@ function detectRTL(s) {
$streamItem.find('.queet-text').find('.h-card.mention').prepend('@'); $streamItem.find('.queet-text').find('.h-card.mention').prepend('@');
$streamItem.find('.queet-text').find('.h-card.group').prepend('!'); $streamItem.find('.queet-text').find('.h-card.group').prepend('!');
$streamItem.find('.queet-text').find('.vcard .fn.nickname:not(.group)').prepend('@'); // very old style $streamItem.find('.queet-text').find('.vcard .fn.nickname:not(.group)').prepend('@'); // very old style
$streamItem.find('.queet-text').find('.vcard .nickname.mention:not(.fn)').prepend('@'); // old style $streamItem.find('.queet-text').find('.vcard .nickname.mention:not(.fn)').prepend('@'); // old style
$streamItem.find('.queet-text').find('.vcard .nickname.group').prepend('!'); // old style $streamItem.find('.queet-text').find('.vcard .nickname.group').prepend('!'); // old style
$streamItem.find('.queet-text').find('a[rel="tag"]').prepend('#'); $streamItem.find('.queet-text').find('a[rel="tag"]').prepend('#');
} }
@ -1401,6 +1630,26 @@ function countCharsInQueetBox(src,trgt,btn) {
} }
/* ·
·
· Prefill the queet box with cached text, if there is any in an attribute
·
· @param queetBox: jQuery object for the queet box
·
· · · · · · · · · · · · · */
function maybePrefillQueetBoxWithCachedText(queetBox) {
var cachedText = decodeURIComponent(queetBox.attr('data-cached-text'));
var cachedTextText = $('<div/>').html(cachedText).text();
if(cachedText != 'undefined' && cachedText != 'false') {
queetBox.click();
queetBox.html(cachedText);
setSelectionRange(queetBox[0], cachedTextText.length, cachedTextText.length);
queetBox.trigger('input');
}
}
/* · /* ·
· ·
· Remember my scroll position · Remember my scroll position
@ -1511,6 +1760,17 @@ jQuery.fn.outerHTML = function(s) {
/* ·
·
· Sort divs by attribute descending
·
· · · · · · · · · · · · · */
jQuery.fn.sortDivsByAttrDesc = function sortDivsByAttrDesc(attr) {
$("> div", this[0]).sort(dec_sort).appendTo(this[0]);
function dec_sort(a, b){ return parseInt($(b).attr(attr),10) > parseInt($(a).attr(attr),10) ? 1 : -1; }
}
/* · /* ·
· ·
@ -1661,39 +1921,9 @@ function shortenUrlsInBox(shortenButton) {
} }
}); });
}); });
}
/* ·
·
· Return the user screen name that this stream belongs to. last resort just return the stream
·
· · · · · · · · · · · · · */
function theUserOrGroupThisStreamBelongsTo(stream) {
// if screen_name is given as get-var, use that
if(stream.indexOf('screen_name=')>-1) {
var thisUsersScreenName = stream.substring(stream.indexOf('screen_name=')+12);
if(thisUsersScreenName.indexOf('&=')>-1) {
thisUsersScreenName = thisUsersScreenName.substring(0,stream.indexOf('&'));
}
return thisUsersScreenName;
}
// groups
else if(stream.indexOf('statusnet/groups/timeline/')>-1
|| stream.indexOf('statusnet/groups/membership/')>-1
|| stream.indexOf('statusnet/groups/admins/')>-1) {
var groupName = '!' + stream.substring(stream.lastIndexOf('/')+1, stream.indexOf('.json'));
return groupName;
}
// otherwise, and if we're logged in, we assume this is my stream
else if (window.loggedIn){
return window.loggedIn.screen_name;
}
else {
return stream;
}
} }
/* · /* ·
· ·
· Youtube embed link from youtube url · Youtube embed link from youtube url

View File

@ -875,15 +875,8 @@ function doLogin(streamObjectToSet) {
// we might have cached text for the queet box // we might have cached text for the queet box
// (we need to get the mentions suggestions and cache the syntax highlighting before doing this) // (we need to get the mentions suggestions and cache the syntax highlighting before doing this)
var cachedQueetBoxData = localStorageObjectCache_GET('queetBoxInput','queet-box'); $('#queet-box').attr('data-cached-text',encodeURIComponent(localStorageObjectCache_GET('queetBoxInput','queet-box')));
var cachedQueetBoxDataText = $('<div/>').html(cachedQueetBoxData).text(); maybePrefillQueetBoxWithCachedText($('#queet-box'));
if(cachedQueetBoxData) {
queetBox = $('#queet-box');
queetBox.click();
queetBox.html(cachedQueetBoxData);
setSelectionRange(queetBox[0], cachedQueetBoxDataText.length, cachedQueetBoxDataText.length);
queetBox.trigger('input');
}
}); });
// load history // load history
@ -1618,11 +1611,11 @@ $(window).scroll(function() {
if($(window).scrollTop() + $(window).height() > $(document).height() - 1000) { if($(window).scrollTop() + $(window).height() > $(document).height() - 1000) {
// not if we're already loading or if no stream is set yet // not if we're already loading or if no stream is set yet
if(!$('body').hasClass('loading-older') && typeof window.currentStream != "undefined" && $('#feed-body').attr('data-end-reached') != 'true') { if(!$('body').hasClass('loading-older') && typeof window.currentStreamObject != "undefined" && $('#feed-body').attr('data-end-reached') != 'true') {
$('body').addClass('loading-older'); $('body').addClass('loading-older');
// remove loading class in 10 seconds, i.e. try again if failed to load within 10 s // remove loading class in 10 seconds, i.e. try again if failed to load within 10 s
if(window.currentStream.substring(0,6) != 'search') { if(window.currentStreamObject.name != 'search') {
setTimeout(function(){$('body').removeClass('loading-older');},10000); setTimeout(function(){$('body').removeClass('loading-older');},10000);
} }
@ -1638,15 +1631,15 @@ $(window).scroll(function() {
var searchPage=2; var searchPage=2;
} }
var nextPage = searchPage+1; var nextPage = searchPage+1;
var getVars = qOrAmp(window.currentStream) + 'rpp=20&page=' + searchPage; // search uses 'rpp' var and others 'count' for paging, though we can add rrp to others aswell without any problem var getVars = qOrAmp(window.currentStreamObject.stream) + 'rpp=20&page=' + searchPage; // search uses 'rpp' var and others 'count' for paging, though we can add rrp to others aswell without any problem
} }
// normal streams // normal streams
else { else {
var getVars = qOrAmp(window.currentStream) + 'max_id=' + ($('#feed-body').children('.stream-item').last().attr('data-quitter-id-in-stream')); var getVars = qOrAmp(window.currentStreamObject.stream) + 'max_id=' + ($('#feed-body').children('.stream-item').last().attr('data-quitter-id-in-stream'));
} }
display_spinner('#footer-spinner-container'); display_spinner('#footer-spinner-container');
getFromAPI(window.currentStream + getVars,function(data){ getFromAPI(window.currentStreamObject.stream + getVars,function(data){
// if data returned an empty array, we have probably reached the bottom // if data returned an empty array, we have probably reached the bottom
if(data.length == 0) { if(data.length == 0) {
@ -1699,41 +1692,24 @@ function checkForNewQueets() {
// only if logged in and only for notice or notification streams // only if logged in and only for notice or notification streams
if(window.loggedIn && (window.currentStreamObject.type == 'notices' || window.currentStreamObject.type == 'notifications')) { if(window.loggedIn && (window.currentStreamObject.type == 'notices' || window.currentStreamObject.type == 'notifications')) {
var lastId = $('#feed-body').children('.stream-item').not('.temp-post').attr('data-quitter-id-in-stream'); var lastId = $('#feed-body').children('.stream-item').not('.temp-post').not('.posted-from-form').attr('data-quitter-id-in-stream');
var addThisStream = window.currentStream; var addThisStream = window.currentStreamObject.stream;
var timeNow = new Date().getTime(); var timeNow = new Date().getTime();
getFromAPI(addThisStream + qOrAmp(window.currentStream) + 'since_id=' + lastId,function(data){ getFromAPI(addThisStream + qOrAmp(window.currentStreamObject.stream) + 'since_id=' + lastId,function(data){
if(data) { if(data) {
$('body').removeClass('loading-newer'); $('body').removeClass('loading-newer');
if(addThisStream == window.currentStream) { if(addThisStream == window.currentStreamObject.stream) {
addToFeed(data, false, 'hidden'); addToFeed(data, false, 'hidden');
// if we have hidden items, show new-queets-bar // say hello to the api if this is notifications stream, to
var new_queets_num = $('#feed-body').find('.stream-item.hidden:not(.activity)').length; // get correct unread notifcation count
if(new_queets_num > 0) { if(window.currentStreamObject.name == 'notifications') {
helloAPI();
$('#new-queets-bar').parent().removeClass('hidden');
// bar label
if(new_queets_num == 1) { var q_txt = window.sL.newQueet; }
else { var q_txt = window.sL.newQueets; }
if(window.currentStreamObject.name == 'notifications') {
if(new_queets_num == 1) { var q_txt = window.sL.newNotification; }
else { var q_txt = window.sL.newNotifications; }
}
$('#new-queets-bar').html(q_txt.replace('{new-notice-count}',new_queets_num));
// say hello to the api if this is notifications stream, to
// get correct unread notifcation count
if(window.currentStreamObject.name == 'notifications') {
helloAPI();
}
// cache the now updated stream
rememberStreamStateInLocalStorage();
} }
// if we have hidden items, show new-queets-bar
maybeShowTheNewQueetsBar()
} }
} }
}); });
@ -2138,9 +2114,9 @@ $('body').on('click','.action-rt-container .icon:not(.is-mine)',function(){
getFavsAndRequeetsForQueet(this_stream_item, this_stream_item.attr('data-quitter-id')); getFavsAndRequeetsForQueet(this_stream_item, this_stream_item.attr('data-quitter-id'));
// mark all instances of this notice as repeated // 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="' + data.retweeted_status.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="' + data.retweeted_status.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'); $('.stream-item[data-quitter-id="' + data.retweeted_status.id + '"]').children('.queet').find('.action-rt-container').children('.with-icn').addClass('done');
} }
else { else {
// error // error
@ -2154,13 +2130,34 @@ $('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();
var my_rq_id = this_stream_item.attr('data-requeeted-by-me-id'); var myRequeetID = this_stream_item.attr('data-requeeted-by-me-id');
unRequeet(this_stream_item, this_action, my_rq_id);
// mark all instances of this notice as non-repeated // display button as unrepeated
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').removeClass('requeeted'); this_action.children('.with-icn').removeClass('done');
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').removeAttr('data-requeeted-by-me-id'); this_action.find('.with-icn b').html(window.sL.requeetVerb);
$('.stream-item[data-quitter-id="' + this_stream_item.attr('data-quitter-id') + '"]').children('.queet').find('.action-rt-container').children('.with-icn').removeClass('done'); this_stream_item.removeClass('requeeted');
// post unrequeet
postActionToAPI('statuses/destroy/' + myRequeetID + '.json', function(data) {
if(data) {
// remove my repeat-notice from the feed, if it's there
slideUpAndRemoveStreamItem($('.stream-item[data-quitter-id-in-stream="' + myRequeetID + '"]'));
// 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');
getFavsAndRequeetsForQueet(this_stream_item, this_stream_item.attr('data-quitter-id'));
remove_spinner();
}
else {
remove_spinner();
this_action.children('.with-icn').addClass('done');
this_action.find('.with-icn b').html(window.sL.requeetedVerb);
this_stream_item.addClass('requeeted');
}
});
} }
}); });
@ -2285,12 +2282,13 @@ $('body').on('click','.action-reply-container',function(){
popUpAction('popup-reply-' + this_stream_item_id, window.sL.replyTo + ' ' + this_stream_item.children('.queet').find('.screen-name').html(),replyFormHtml(this_stream_item,this_stream_item_id),queetHtmlWithoutFooter); popUpAction('popup-reply-' + this_stream_item_id, window.sL.replyTo + ' ' + this_stream_item.children('.queet').find('.screen-name').html(),replyFormHtml(this_stream_item,this_stream_item_id),queetHtmlWithoutFooter);
// fix the width of the queet box, otherwise the syntax highlighting break // fix the width of the queet box, otherwise the syntax highlighting break
var queetBoxWidth = $('#popup-reply-' + this_stream_item_id).find('.modal-body').find('.inline-reply-queetbox').width()-20; var queetBox = $('#popup-reply-' + this_stream_item_id).find('.modal-body').find('.inline-reply-queetbox');
$('#popup-reply-' + this_stream_item_id).find('.modal-body').find('.queet-box-syntax').width(queetBoxWidth); var queetBoxWidth = queetBox.width()-20;
$('#popup-reply-' + this_stream_item_id).find('.modal-body').find('.syntax-middle').width(queetBoxWidth); queetBox.children('.queet-box-syntax, .syntax-middle, .syntax-two').width(queetBoxWidth);
$('#popup-reply-' + this_stream_item_id).find('.modal-body').find('.syntax-two').width(queetBoxWidth);
$('#popup-reply-' + this_stream_item_id).find('.modal-body').find('.queet-box').trigger('click'); // expand $('#popup-reply-' + this_stream_item_id).find('.modal-body').find('.queet-box').trigger('click'); // expand
maybePrefillQueetBoxWithCachedText(queetBox.children('.queet-box'));
}); });
@ -2301,12 +2299,13 @@ $('body').on('click','.action-reply-container',function(){
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
$('body').on('click','#top-compose',function(){ $('body').on('click','#top-compose',function(){
popUpAction('popup-compose', window.sL.compose,queetBoxHtml(),false); popUpAction('popup-compose', window.sL.compose,queetBoxPopUpHtml(),false);
var queetBoxWidth = $('#popup-compose').find('.inline-reply-queetbox').width()-20; var queetBoxWidth = $('#popup-compose').find('.inline-reply-queetbox').width()-20;
$('#popup-compose').find('.queet-box-syntax').width(queetBoxWidth); $('#popup-compose').find('.queet-box-syntax').width(queetBoxWidth);
$('#popup-compose').find('.syntax-middle').width(queetBoxWidth); $('#popup-compose').find('.syntax-middle').width(queetBoxWidth);
$('#popup-compose').find('.syntax-two').width(queetBoxWidth); $('#popup-compose').find('.syntax-two').width(queetBoxWidth);
$('#popup-compose').find('.queet-box').trigger('click'); $('#popup-compose').find('.queet-box').trigger('click');
maybePrefillQueetBoxWithCachedText($('#popup-compose').find('.queet-box'));
}); });
@ -2392,18 +2391,22 @@ $('body').on('click', '.queet-toolbar button',function () {
$('.modal-container').remove(); $('.modal-container').remove();
// try to find a queet to add the temp queet to: // try to find a queet to add the temp queet to:
var tempQueetInsertedInConversation = false;
// if the queet is in conversation, add it to parent's conversation // if the queet is in conversation, add it to parent's conversation
if($('.stream-item.replying-to').length > 0 && $('.stream-item.replying-to').hasClass('conversation')) { if($('.stream-item.replying-to').length > 0 && $('.stream-item.replying-to').hasClass('conversation')) {
$('.stream-item.replying-to').parent().append(queetHtml); var insertedTempQueet = $(queetHtml).appendTo($('.stream-item.replying-to').parent());
tempQueetInsertedInConversation = true;
} }
// if the queet is expanded, add it to its conversation // if the queet is expanded, add it to its conversation
else if($('.stream-item.replying-to').length > 0 && $('.stream-item.replying-to').hasClass('expanded')) { else if($('.stream-item.replying-to').length > 0 && $('.stream-item.replying-to').hasClass('expanded')) {
$('.stream-item.replying-to').append(queetHtml); var insertedTempQueet = $(queetHtml).appendTo($('.stream-item.replying-to'));
tempQueetInsertedInConversation = true;
} }
// maybe the replying-to class is missing but we still have a suiting place to add it // maybe the replying-to class is missing but we still have a suiting place to add it
else if($('.stream-item.expanded[data-quitter-id="' + in_reply_to_status_id + '"]').length > 0) { else if($('.stream-item.expanded[data-quitter-id="' + in_reply_to_status_id + '"]').length > 0) {
$('.stream-item.expanded[data-quitter-id="' + in_reply_to_status_id + '"]').append(queetHtml); var insertedTempQueet = $(queetHtml).appendTo($('.stream-item.expanded[data-quitter-id="' + in_reply_to_status_id + '"]'));
tempQueetInsertedInConversation = true;
} }
// if we can't find a proper place, add it to top and remove conversation class // if we can't find a proper place, add it to top and remove conversation class
// if this is either 1) our home/all feed, 2) our user timeline or 3) whole site or 4) whole network // if this is either 1) our home/all feed, 2) our user timeline or 3) whole site or 4) whole network
@ -2411,11 +2414,14 @@ $('body').on('click', '.queet-toolbar button',function () {
|| window.currentStreamObject.name == 'my profile' || window.currentStreamObject.name == 'my profile'
|| window.currentStreamObject.name == 'public timeline' || window.currentStreamObject.name == 'public timeline'
|| window.currentStreamObject.name == 'public and external timeline') { || window.currentStreamObject.name == 'public and external timeline') {
$('#feed-body').prepend(queetHtml.replace('class="stream-item conversation','class="stream-item')); var insertedTempQueet = $(queetHtml).prependTo('#feed-body');
insertedTempQueet.removeClass('conversation');
} }
// don't add it to the current stream, open a popup instead, without conversation class // don't add it to the current stream, open a popup instead, without conversation class
else { else {
popUpAction('popup-sending', '',queetHtml.replace('class="stream-item conversation','class="stream-item'),false); popUpAction('popup-sending','','',false);
var insertedTempQueet = $(queetHtml).prependTo($('#popup-sending').find('.modal-body'));
insertedTempQueet.removeClass('conversation');
} }
// maybe post queet in groups // maybe post queet in groups
@ -2443,13 +2449,9 @@ $('body').on('click', '.queet-toolbar button',function () {
// post queet // post queet
postQueetToAPI(queetText, in_reply_to_status_id, postToGroups, function(data){ if(data) { postQueetToAPI(queetText, in_reply_to_status_id, postToGroups, function(data){ if(data) {
// show real queet // show real queet and remove temp queet
var new_queet = Array(); var insertedRealQueet = $(buildQueetHtml(data, data.id, 'visible posted-from-form', false, tempQueetInsertedInConversation)).insertAfter(insertedTempQueet);
new_queet[0] = data; insertedTempQueet.remove();
addToFeed(new_queet,tempPostId,'visible', true);
// remove temp queet
$('#' + tempPostId).remove();
// clear queetbox input cache // clear queetbox input cache
localStorageObjectCache_STORE('queetBoxInput',queetBox.attr('id'),false); localStorageObjectCache_STORE('queetBoxInput',queetBox.attr('id'),false);
@ -2457,6 +2459,13 @@ $('body').on('click', '.queet-toolbar button',function () {
// queet count // queet count
$('#user-queets strong').html(parseInt($('#user-queets strong').html(),10)+1); $('#user-queets strong').html(parseInt($('#user-queets strong').html(),10)+1);
// fadeout any posting-popups
setTimeout(function(){
$('#popup-sending').fadeOut(1000, function(){
$('#popup-sending').remove();
});
},100);
}}); }});
} }
}); });
@ -2514,7 +2523,6 @@ $('body').on('click','button.shorten',function () {
· ·
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
$('body').on('click','.reload-stream',function () { $('body').on('click','.reload-stream',function () {
$('.reload-stream').hide();
setNewCurrentStream(URLtoStreamRouter(window.location.href),false,false,false); setNewCurrentStream(URLtoStreamRouter(window.location.href),false,false,false);
}); });
@ -2972,15 +2980,29 @@ $('body').on('keyup', 'div.queet-box-syntax', function(e) {
· · · · · · · · · · · · · */ · · · · · · · · · · · · · */
$('body').on('keyup', 'div.queet-box-syntax', function(e) { $('body').on('keyup', 'div.queet-box-syntax', function(e) {
var thisId = $(this).attr('id');
var thisText = $.trim($(this).text());
// keep in global var to avoid doing all these operations every keystroke
if(typeof window.queetBoxCurrentlyActive == 'undefined'
|| window.queetBoxCurrentlyActive.id != thisId) {
window.queetBoxCurrentlyActive = {
id: thisId,
startText: $.trim($('<div/>').append(decodeURIComponent($(this).attr('data-start-text'))).text()),
repliesText: $.trim($('<div/>').append(decodeURIComponent($(this).attr('data-replies-text'))).text())
};
}
// remove from cache if empty, or same as default text // remove from cache if empty, or same as default text
if($.trim($(this).text()) == '' if(thisText == ''
|| $.trim($(this).text()) == window.sL.compose || thisText == window.sL.compose
|| $.trim($(this).text()) == $.trim(decodeURIComponent($(this).attr('data-start-text'))) || thisText == window.queetBoxCurrentlyActive.startText
|| $.trim($(this).text()) == $.trim(decodeURIComponent($(this).attr('data-replies-text')))) { || thisText == window.queetBoxCurrentlyActive.repliesText) {
localStorageObjectCache_STORE('queetBoxInput',$(this).attr('id'),false); localStorageObjectCache_STORE('queetBoxInput',thisId,false);
} }
else { else {
localStorageObjectCache_STORE('queetBoxInput',$(this).attr('id'),$(this).html()); localStorageObjectCache_STORE('queetBoxInput',thisId,$(this).html());
} }
}); });
@ -3122,8 +3144,8 @@ $('body').on('click','.view-more-container-top', function(){
$('body').on('click','.show-full-conversation',function(){ $('body').on('click','.show-full-conversation',function(){
var this_q = $(this).closest('.queet'); var this_q = $(this).closest('.queet');
var this_qid = $(this).closest('.stream-item:not(.conversation)').attr('data-quitter-id'); var thisStreamItem = this_q.parent();
var thisStreamItem = $('#stream-item-' + $(this).attr('data-stream-item-id')); var this_qid = thisStreamItem.attr('data-quitter-id');
rememberMyScrollPos(this_q,this_qid); rememberMyScrollPos(this_q,this_qid);