caching of profile cards in window object. also: open popups for local users

This commit is contained in:
Hannes Mannerheim 2015-06-05 02:17:33 +02:00
parent a017ad1940
commit d749713ef3
5 changed files with 340 additions and 43 deletions

View File

@ -1858,22 +1858,30 @@ body.rtl .view-more-container-bottom { direction:rtl; }
#popup-external-profile .profile-card, #popup-external-profile .profile-card,
#popup-external-profile .profile-card .profile-banner-footer { #popup-external-profile .profile-card .profile-banner-footer,
#popup-local-profile .profile-card,
#popup-local-profile .profile-card .profile-banner-footer {
border-radius:0; border-radius:0;
} }
#popup-external-profile ul.queet-actions { #popup-external-profile ul.queet-actions,
#popup-local-profile ul.queet-actions {
display:none; display:none;
} }
#popup-external-profile .queet, #popup-external-profile .queet,
#popup-external-profile .queet-content, #popup-external-profile .queet-content,
#popup-external-profile .queet-text { #popup-external-profile .queet-text
#popup-local-profile .queet,
#popup-local-profile .queet-content,
#popup-local-profile .queet-text {
cursor:auto; cursor:auto;
border-bottom:0 none; border-bottom:0 none;
} }
#popup-external-profile .stream-item .stream-item-header .name:before { #popup-external-profile .stream-item .stream-item-header .name:before,
#popup-local-profile .stream-item .stream-item-header .name:before {
left:10px; left:10px;
} }
#popup-external-profile .go-to-external-profile { #popup-external-profile .go-to-external-profile,
#popup-local-profile .go-to-local-profile {
font-weight:bold; font-weight:bold;
} }
@ -3507,6 +3515,12 @@ button.shorten:after {
top: 20px; top: 20px;
} }
#popup-external-profile-spinner .loader,
#popup-local-profile-spinner .loader {
position:absolute;
top:150px;
}
.reload-stream { .reload-stream {
display: block; display: block;
position: absolute; position: absolute;

View File

@ -147,18 +147,18 @@ function getFromAPI(stream, actionOnSuccess) {
displayOrHideUnreadNotifications(request.getResponseHeader('Qvitter-Notifications')); displayOrHideUnreadNotifications(request.getResponseHeader('Qvitter-Notifications'));
// profile card from user array // profile card from user array, also cache it
if(request.getResponseHeader('Qvitter-User-Array') !== null) { if(request.getResponseHeader('Qvitter-User-Array') !== null) {
addProfileCardToDOM( var userArray = iterateRecursiveReplaceHtmlSpecialChars($.parseJSON(request.getResponseHeader('Qvitter-User-Array')));
buildProfileCard( userArrayCacheStore(userArray);
iterateRecursiveReplaceHtmlSpecialChars( addProfileCardToDOM(buildProfileCard(userArray));
$.parseJSON(
request.getResponseHeader('Qvitter-User-Array')))));
} }
data = convertEmptyObjectToEmptyArray(data); data = convertEmptyObjectToEmptyArray(data);
data = iterateRecursiveReplaceHtmlSpecialChars(data); data = iterateRecursiveReplaceHtmlSpecialChars(data);
searchForUserDataToCache(data);
actionOnSuccess(data); actionOnSuccess(data);
}, },

View File

@ -277,9 +277,8 @@ function buildExternalProfileCard(data) {
emptyWebpage = ' empty'; emptyWebpage = ' empty';
} }
var serverUrl = data.statusnet_profile_url.replace('/' + data.screen_name,''); var serverUrl = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname(data.statusnet_profile_url, data.screen_name);
var userApiUrl = serverUrl + '/api/statuses/user_timeline.json?screen_name=' + data.screen_name; data.screenNameWithServer = '@' + data.screen_name + '@' + serverUrl;
data.screenNameWithServer = '@' + data.screen_name + '@' + serverUrl.replace('http://','').replace('https://','');
var followButton = '<div class="user-actions"><button' + followLocalIdHtml + ' data-follow-user="' + data.statusnet_profile_url + '" type="button" class="qvitter-follow-button ' + followingClass + '"><span class="button-text follow-text"><i class="follow"></i>' + window.sL.userFollow + '</span><span class="button-text following-text">' + window.sL.userFollowing + '</span><span class="button-text unfollow-text">' + window.sL.userUnfollow + '</span></button></div>'; var followButton = '<div class="user-actions"><button' + followLocalIdHtml + ' data-follow-user="' + data.statusnet_profile_url + '" type="button" class="qvitter-follow-button ' + followingClass + '"><span class="button-text follow-text"><i class="follow"></i>' + window.sL.userFollow + '</span><span class="button-text following-text">' + window.sL.userFollowing + '</span><span class="button-text unfollow-text">' + window.sL.userUnfollow + '</span></button></div>';
data.profileCard = '\ data.profileCard = '\
@ -343,6 +342,50 @@ function addProfileCardToDOM(data) {
} }
/* ·
·
· Open external profile card in popup
·
· @param data: an object with a user array
·
· · · · · · · · · */
function openExternalProfileInPopup(data) {
var data = buildExternalProfileCard(data);
// preview latest notice
var noticeHtml = '';
if(typeof data.status != 'undefined') {
data.status.user = data;
var noticeHtml = buildQueetHtml(data.status);
}
popUpAction('popup-external-profile', data.screenNameWithServer,data.profileCard + noticeHtml,'<a class="go-to-external-profile" href="' + data.statusnet_profile_url + '">' + window.sL.goToExternalProfile + '</a>');
}
/* ·
·
· Open local profile card in popup
·
· @param data: an object with a user array
·
· · · · · · · · · */
function openLocalProfileInPopup(data) {
var data = buildProfileCard(data);
// preview latest notice
var noticeHtml = '';
if(typeof data.status != 'undefined') {
data.status.user = data;
var noticeHtml = buildQueetHtml(data.status);
}
popUpAction('popup-local-profile', '@' + data.screen_name, data.profileCardHtml + '<div class="clearfix"></div>' + noticeHtml,'<a class="go-to-local-profile" href="' + data.statusnet_profile_url + '">' + window.sL.goToExternalProfile + '</a>');
}
/* · /* ·

View File

@ -216,6 +216,185 @@ function checkLocalStorage() {
console.log(corrected + ' entries corrected, ' + deleted + ' entries deleted'); console.log(corrected + ' entries corrected, ' + deleted + ' entries deleted');
} }
/* ·
·
· User array cache
·
· Stored in window.userArrayCache as instance_url/nickname
· with protocol (http:// or https://) trimmed off, e.g. "quitter.se/hannes2peer"
·
· · · · · · · · · */
window.userArrayCache = new Object();
function userArrayCacheStore(data) {
if(typeof data == 'undefined') {
return false;
}
// if we are passed a data object with both local and external data, use external data as key
if(typeof data.local != 'undefined'
&& typeof data.local.statusnet_profile_url != 'undefined'
&& typeof data.external != 'undefined'
&& typeof data.external.statusnet_profile_url != 'undefined') {
var instanceUrlWithoutProtocol = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname(data.external.statusnet_profile_url, data.external.screen_name);
var key = instanceUrlWithoutProtocol + '/' + data.external.screen_name;
var dataToStore = data;
}
// we can also get either local...
else if(typeof data.local != 'undefined' && typeof data.local.statusnet_profile_url != 'undefined' ) {
var instanceUrlWithoutProtocol = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname(data.local.statusnet_profile_url, data.external.screen_name);
var key = instanceUrlWithoutProtocol + '/' + data.external.screen_name;
data.external = false;
var dataToStore = data;
}
// ...or external...
else if(typeof data.external != 'undefined' && typeof data.external.statusnet_profile_url != 'undefined' ) {
var instanceUrlWithoutProtocol = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname(data.external.statusnet_profile_url, data.external.screen_name);
var key = instanceUrlWithoutProtocol + '/' + data.external.screen_name;
data.local = false;
var dataToStore = data;
}
// ...or an unspecified data object, in which case we check the avatar urls to see if it's local or external
else if (typeof data.statusnet_profile_url != 'undefined') {
var instanceUrlWithoutProtocol = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname(data.statusnet_profile_url, data.screen_name);
var key = instanceUrlWithoutProtocol + '/' + data.screen_name;
var dataProfileImageUrlWithoutProtocol = removeProtocolFromUrl(data.profile_image_url);
var siteInstanceURLWithoutProtocol = removeProtocolFromUrl(window.siteInstanceURL);
// local
if(dataProfileImageUrlWithoutProtocol.substring(0,siteInstanceURLWithoutProtocol.length) == siteInstanceURLWithoutProtocol){
var dataToStore = {local:data,external:false};
}
// external
else {
var dataToStore = {external:data,local:false};
}
}
else {
return false;
}
// store
if(typeof window.userArrayCache[key] == 'undefined') {
window.userArrayCache[key] = dataToStore;
}
else {
if(dataToStore.local) {
// keep old status if newer data doesn't have any
if(typeof dataToStore.local.status == 'undefined' && typeof window.userArrayCache[key].local.status != 'undefined') {
dataToStore.local.status = window.userArrayCache[key].local.status;
}
window.userArrayCache[key].local = dataToStore.local;
}
if(dataToStore.external) {
window.userArrayCache[key].external = dataToStore.external;
}
}
}
function userArrayCacheGetByLocalNickname(localNickname) {
if(typeof window.userArrayCache[window.siteRootDomain + '/' + localNickname] != 'undefined') {
return window.userArrayCache[window.siteRootDomain + '/' + localNickname];
}
else {
return false;
}
}
function userArrayCacheGetByProfileUrlAndNickname(profileUrl, nickname) {
var guessedInstanceUrl = guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname(profileUrl, nickname);
if(typeof window.userArrayCache[guessedInstanceUrl + '/' + nickname] == 'undefined') {
return false;
}
else {
return window.userArrayCache[guessedInstanceUrl + '/' + nickname];
}
}
/* ·
·
· Guess instance's base installation url without protocol from a profile url
·
· · · · · · · · · */
function guessInstanceUrlWithoutProtocolFromProfileUrlAndNickname(profileUrl, nickname) {
// remove protocol
var guessedInstanceUrl = removeProtocolFromUrl(profileUrl)
// user/id-style profile urls
if(guessedInstanceUrl.indexOf('/user/') > -1 &&
$.isNumeric(guessedInstanceUrl.substring(guessedInstanceUrl.lastIndexOf('/user/')+6))) {
guessedInstanceUrl = guessedInstanceUrl.substring(0,guessedInstanceUrl.lastIndexOf('/user/'));
}
// nickname-style profile urls
else if(guessedInstanceUrl.substring(guessedInstanceUrl.lastIndexOf('/')+1) == nickname) {
guessedInstanceUrl = guessedInstanceUrl.substring(0,guessedInstanceUrl.lastIndexOf('/'));
}
// remove trailing "index.php" if the instance doesn't use mod_rewrite
if(guessedInstanceUrl.substring(guessedInstanceUrl.lastIndexOf('/')) == '/index.php') {
guessedInstanceUrl = guessedInstanceUrl.substring(0,guessedInstanceUrl.lastIndexOf('/'));
}
// there was a bug once that made some instances have multiple /:s in their url,
// so make sure there's no trailing /:s
while (guessedInstanceUrl.slice(-1) == '/') {
guessedInstanceUrl = guessedInstanceUrl.slice(0,-1);
}
return guessedInstanceUrl;
}
/* ·
·
· Remove the protocol (e.g. "http://") from an URL
·
· · · · · · · · · */
function removeProtocolFromUrl(url) {
if(url.indexOf('://') == -1) {
return url;
}
return url.substring(url.indexOf('://')+3);
}
/* ·
·
· Iterates recursively through an API response in search for user data to cache
· If we find a "statusnet_profile_url" key we assume the parent is a user array/object
·
· · · · · · · · · · · · · */
function searchForUserDataToCache(obj) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
searchForUserDataToCache(obj[property]);
}
else if(typeof obj[property] == 'string' && property == 'statusnet_profile_url') {
userArrayCacheStore(obj);
}
}
}
}
/* · /* ·
· ·
· Display unread notifications · Display unread notifications
@ -284,10 +463,8 @@ function iterateRecursiveReplaceHtmlSpecialChars(obj) {
if (typeof obj[property] == "object") { if (typeof obj[property] == "object") {
iterateRecursiveReplaceHtmlSpecialChars(obj[property]); iterateRecursiveReplaceHtmlSpecialChars(obj[property]);
} }
else { else if(typeof obj[property] == 'string' && property != 'statusnet_html' && property != 'source') {
if(typeof obj[property] == 'string' && property != 'statusnet_html' && property != 'source') { obj[property] = replaceHtmlSpecialChars(obj[property]);
obj[property] = replaceHtmlSpecialChars(obj[property]);
}
} }
} }
} }

View File

@ -935,6 +935,11 @@ $('body').on('click','a', function(e) {
if(!!$(this).attr('donthijack') || $(this).attr('donthijack') == '') { if(!!$(this).attr('donthijack') || $(this).attr('donthijack') == '') {
return; return;
} }
// if we're clicking something in a profile card popup, close it!
if($(this).closest('#popup-local-profile, #popup-external-profile').length>0) {
$('.modal-container').remove();
}
// all links opens in new tab // all links opens in new tab
$(this).attr('target','_blank'); $(this).attr('target','_blank');
@ -969,8 +974,18 @@ $('body').on('click','a', function(e) {
setNewCurrentStream('favorites.json',function(){},true); setNewCurrentStream('favorites.json',function(){},true);
} }
// profiles // profiles
else if ((/^[a-zA-Z0-9]+$/.test($(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/','')))) { else if ((/^[a-zA-Z0-9]+$/.test($(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/','')))
var linkNickname = $(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/',''); || (/^[0-9]+$/.test($(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/user/','')))) {
if($(this).attr('href').indexOf('/user/') > -1) {
var linkNickname = $(this).text().toLowerCase();
if(linkNickname.substring(0,1) == '@') {
linkNickname = linkNickname.substring(1);
}
}
else {
var linkNickname = $(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/','');
}
// don't hijack /groups-url // don't hijack /groups-url
if(linkNickname == 'groups') { if(linkNickname == 'groups') {
@ -978,17 +993,52 @@ $('body').on('click','a', function(e) {
} }
e.preventDefault(); e.preventDefault();
if($(this).parent().attr('id') == 'user-profile-link') { // logged in user
// logged in user
if($(this).parent().attr('id') == 'user-profile-link'
|| linkNickname == window.loggedIn.screen_name) {
setNewCurrentStream('statuses/user_timeline.json?screen_name=' + window.loggedIn.screen_name,function(){},true); setNewCurrentStream('statuses/user_timeline.json?screen_name=' + window.loggedIn.screen_name,function(){},true);
} }
else { // any user // when in local profile popups
setNewCurrentStream('statuses/user_timeline.json?screen_name=' + linkNickname,function(){},true); else if($(this).closest('#popup-local-profile').length>0) {
setNewCurrentStream('statuses/user_timeline.json?screen_name=' + linkNickname,function(){},true);
}
// any local user, not in popups > open popup
else {
$(this).addClass('local-profile-clicked');
popUpAction('popup-local-profile', '','<div id="popup-local-profile-spinner" style="height:300px;"></div>',false);
display_spinner('#popup-local-profile-spinner');
// try getting from cache, to display immediately
if($(this).hasClass('account-group')) {
var localNickname = $(this).children('.screen-name').text().toLowerCase();
}
else {
var localNickname = $(this).text().toLowerCase();
}
if(localNickname.substring(0,1) == '@') {
localNickname = localNickname.substring(1);
}
var cachedUserArray = userArrayCacheGetByProfileUrlAndNickname($(this).attr('href'), localNickname);
if(cachedUserArray && cachedUserArray.local) {
openLocalProfileInPopup(cachedUserArray.local);
remove_spinner();
$('.local-profile-clicked').removeClass('local-profile-clicked');
}
// but always query the server also
getFromAPI('users/show.json?id=' + localNickname,function(data){
if(data) {
openLocalProfileInPopup(data);
remove_spinner();
$('.local-profile-clicked').removeClass('local-profile-clicked');
}
});
} }
} }
else if((/^[0-9]+$/.test($(this).attr('href').replace('http://','').replace('https://','').replace(window.siteRootDomain + '/user/','')))) {
e.preventDefault();
setNewCurrentStream('statuses/user_timeline.json?screen_name=' + $(this).text().toLowerCase(),function(){},true);
}
// tags // tags
else if ($(this).attr('href').indexOf(window.siteRootDomain + '/tag/')>-1) { else if ($(this).attr('href').indexOf(window.siteRootDomain + '/tag/')>-1) {
e.preventDefault(); e.preventDefault();
@ -1036,30 +1086,43 @@ $('body').on('click','a', function(e) {
|| ($(this).closest('.stream-item').hasClass('activity') && $(this).attr('href').indexOf('/group/')==-1)) // or if it's a activity notice but not a group link || ($(this).closest('.stream-item').hasClass('activity') && $(this).attr('href').indexOf('/group/')==-1)) // or if it's a activity notice but not a group link
&& typeof window.loggedIn.screen_name != 'undefined') { // if logged in && typeof window.loggedIn.screen_name != 'undefined') { // if logged in
e.preventDefault(); e.preventDefault();
display_spinner();
$(this).addClass('external-profile-clicked'); $(this).addClass('external-profile-clicked');
popUpAction('popup-external-profile', '','<div id="popup-external-profile-spinner" style="height:300px;"></div>',false);
display_spinner('#popup-external-profile-spinner');
// try getting from cache, to display immediately
if($(this).hasClass('account-group')) {
var externalNickname = $(this).children('.screen-name').text();
}
else {
var externalNickname = $(this).text();
}
if(externalNickname.substring(0,1) == '@') {
externalNickname = externalNickname.substring(1);
}
var cachedUserArray = userArrayCacheGetByProfileUrlAndNickname($(this).attr('href'), externalNickname);
if(cachedUserArray && cachedUserArray.external) {
openExternalProfileInPopup(cachedUserArray);
remove_spinner();
$('.external-profile-clicked').removeClass('external-profile-clicked');
}
// but always query the server also
getFromAPI('qvitter/external_user_show.json?profileurl=' + encodeURIComponent($(this).attr('href')),function(data){ getFromAPI('qvitter/external_user_show.json?profileurl=' + encodeURIComponent($(this).attr('href')),function(data){
if(data && data.external !== null) { if(data && data.external !== null) {
var data = buildExternalProfileCard(data); openExternalProfileInPopup(data);
// preview latest notice
var noticeHtml = '';
if(typeof data.status != 'undefined') {
data.status.user = data;
var noticeHtml = buildQueetHtml(data.status);
}
popUpAction('popup-external-profile', data.screenNameWithServer,data.profileCard + noticeHtml,'<a class="go-to-external-profile" href="' + data.statusnet_profile_url + '">' + window.sL.goToExternalProfile + '</a>');
remove_spinner(); remove_spinner();
$('a').removeClass('external-profile-clicked');
$('.external-profile-clicked').removeClass('external-profile-clicked');
} }
// if external lookup failed, trigger click again. // if external lookup failed, and we don't have a cached profile card, trigger click again.
// it will not be hijacked since we don't remove the external-profile-clicked class here // it will not be hijacked since we don't remove the external-profile-clicked class here
else { else if($('#popup-external-profile-spinner').length > 0){
remove_spinner(); $('.modal-container').remove();
$('.external-profile-clicked')[0].click(); $('.external-profile-clicked')[0].click();
$('.external-profile-clicked').removeClass('external-profile-clicked'); $('.external-profile-clicked').removeClass('external-profile-clicked');
} }