From ae5ccea547ae3de3c5c445b34119a9319a11009f Mon Sep 17 00:00:00 2001 From: Hannes Mannerheim Date: Fri, 18 Sep 2015 13:42:19 +0200 Subject: [PATCH] better support for links to profiles that has changed nickname after the link was created --- js/ajax-functions.js | 12 ++--- js/dom-functions.js | 104 +++++++++++++++++++++++++++++-------------- js/qvitter.js | 51 ++++++++++++--------- js/stream-router.js | 2 +- 4 files changed, 106 insertions(+), 63 deletions(-) diff --git a/js/ajax-functions.js b/js/ajax-functions.js index 92c32d6..2bbefcb 100644 --- a/js/ajax-functions.js +++ b/js/ajax-functions.js @@ -135,12 +135,6 @@ function getFromAPI(stream, actionOnSuccess) { statusCode: { 401:function() { location.reload(); // we may have been logged out in another tab, reload page - }, - 404:function() { - // redirect to frontpage when trying to access non-existing users - if(stream.indexOf('statuses/user_timeline.json?screen_name=') > -1) { - window.location.replace(window.siteInstanceURL); - } } }, success: function(data, textStatus, request) { @@ -178,15 +172,17 @@ function getFromAPI(stream, actionOnSuccess) { /* · · - · Get user nickname from user id + · Get user nickname by user id · · @param id: local user id · @param callback: function to invoke when done · · · · · · · · · · · · · · */ -function getUserIdFromNicknameFromAPI(id, callback) { +function getNicknameByUserIdFromAPI(id, callback) { + display_spinner(); getFromAPI('users/show.json?id=' + id, function(data){ + remove_spinner(); if(data && typeof data.screen_name != 'undefined') { callback(data.screen_name); } diff --git a/js/dom-functions.js b/js/dom-functions.js index eab1501..bbff81c 100644 --- a/js/dom-functions.js +++ b/js/dom-functions.js @@ -483,11 +483,14 @@ function groupProfileCard(groupAlias) { · Change stream · · @param streamObject: object returned by pathToStreamRouter() + · @param setLocation: whether we should update the browsers location bar when we set the new stream + · @param fallbackId: if we fail to get the stream, it can be due to a bad/changed user/group nickname, + · in that case this parameter can contain a user/group id that we can use to retrieve the correct nickname · @param actionOnSuccess: callback function on success · · · · · · · · · · */ -function setNewCurrentStream(streamObject,setLocation,actionOnSuccess) { +function setNewCurrentStream(streamObject,setLocation,fallbackId,actionOnSuccess) { if(!streamObject && !streamObject.stream) { console.log('invalid streamObject, no stream to set!'); @@ -497,11 +500,6 @@ function setNewCurrentStream(streamObject,setLocation,actionOnSuccess) { // remember state of old stream (including profile card) window.oldStreams[window.currentStream] = $('#feed').siblings('.profile-card').outerHTML() + $('#feed').outerHTML(); - // set location bar from stream - if(setLocation) { - setUrlFromStream(streamObject); - } - // halt interval that checks for new queets window.clearInterval(checkForNewQueetsInterval); @@ -540,6 +538,11 @@ function setNewCurrentStream(streamObject,setLocation,actionOnSuccess) { $('#feed-header-inner h2').css('opacity','0.2'); $('#feed-header-inner h2').animate({opacity:'1'},1000); + // set location bar from stream + if(setLocation) { + setUrlFromStream(streamObject); + } + // also mark this stream as the current stream immediately, if a saved copy exists addStreamToHistoryMenuAndMarkAsCurrent(streamObject); } @@ -563,41 +566,74 @@ function setNewCurrentStream(streamObject,setLocation,actionOnSuccess) { // get stream getFromAPI(streamObject.stream, function(queet_data, userArray){ - if(queet_data) { - // 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) { - // profile card from user array - if(userArray) { - addProfileCardToDOM(buildProfileCard(userArray)); + // 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) { + console.log('stream has changed, aborting'); + return; + } + + // if we have a fallbackId and a userArray, and the userArray's is is not equal to + // the fallackId, this is the wrong stream! we need to re-invoke setNewCurrentStream() + // with the correct and up-to-date nickname (maybe best not to send a fallbackId here not + // to risk getting into an infinite loop caused by bad data) + // also, we do the same thing if getting the stream fails, but we have a fallback id + if((userArray && fallbackId && userArray.id != fallbackId) + || (queet_data === false && fallbackId)) { + getNicknameByUserIdFromAPI(fallbackId,function(nickname) { + if(nickname) { + setNewCurrentStream(pathToStreamRouter(nickname),true,false,actionOnSuccess); } - - // show group profile card if this is a group stream - if(streamObject.name == 'group notice stream' - || streamObject.name == 'group member list' - || streamObject.name == 'group admin list') { - groupProfileCard(streamObject.nickname); + else { + // redirect to front page if everything fails + setNewCurrentStream(pathToStreamRouter('/'),true,false,actionOnSuccess); } + }); + } - // start checking for new queets again - window.clearInterval(checkForNewQueetsInterval); - checkForNewQueetsInterval=window.setInterval(function(){checkForNewQueets()},window.timeBetweenPolling); + // getting stream failed, and we don't have a fallback id, redirect to front page + else if(queet_data === false) { + setNewCurrentStream(pathToStreamRouter('/'),true,false,actionOnSuccess); + } - // add this stream to the history menu - addStreamToHistoryMenuAndMarkAsCurrent(streamObject); + // everything seems fine, show the new stream + else if(queet_data) { - remove_spinner(); - $('#feed-body').html(''); // empty feed only now so the scrollers don't flicker on and off - $('#new-queets-bar').parent().addClass('hidden'); document.title = window.siteTitle; // hide new queets bar if it's visible there - addToFeed(queet_data, false,'visible'); // add stream items to feed element - $('#feed').animate({opacity:'1'},150); // fade in - $('body').removeClass('loading-older');$('body').removeClass('loading-newer'); - $('html,body').scrollTop(0); // scroll to top + // set location bar from stream + if(setLocation) { + setUrlFromStream(streamObject); + } - // maybe do something - if(typeof actionOnSuccess == 'function') { - actionOnSuccess(); - } + // profile card from user array + if(userArray) { + addProfileCardToDOM(buildProfileCard(userArray)); + } + + // show group profile card if this is a group stream + if(streamObject.name == 'group notice stream' + || streamObject.name == 'group member list' + || streamObject.name == 'group admin list') { + groupProfileCard(streamObject.nickname); + } + + // start checking for new queets again + window.clearInterval(checkForNewQueetsInterval); + checkForNewQueetsInterval=window.setInterval(function(){checkForNewQueets()},window.timeBetweenPolling); + + // add this stream to the history menu + addStreamToHistoryMenuAndMarkAsCurrent(streamObject); + + remove_spinner(); + $('#feed-body').html(''); // empty feed only now so the scrollers don't flicker on and off + $('#new-queets-bar').parent().addClass('hidden'); document.title = window.siteTitle; // hide new queets bar if it's visible there + addToFeed(queet_data, false,'visible'); // add stream items to feed element + $('#feed').animate({opacity:'1'},150); // fade in + $('body').removeClass('loading-older');$('body').removeClass('loading-newer'); + $('html,body').scrollTop(0); // scroll to top + + // maybe do something + if(typeof actionOnSuccess == 'function') { + actionOnSuccess(); } } }); diff --git a/js/qvitter.js b/js/qvitter.js index c3c7af2..3ef65b4 100644 --- a/js/qvitter.js +++ b/js/qvitter.js @@ -56,7 +56,7 @@ window.loggedIn = iterateRecursiveReplaceHtmlSpecialChars(window.loggedIn); window.onpopstate = function(event) { if(event && event.state) { display_spinner(); - setNewCurrentStream(pathToStreamRouter(event.state.strm),false,function(){ + setNewCurrentStream(pathToStreamRouter(event.state.strm),false,false,function(){ remove_spinner(); }); } @@ -764,7 +764,7 @@ function proceedToSetLanguageAndLogin(data){ else { display_spinner(); window.currentStream = ''; // force reload stream - setNewCurrentStream(getStreamFromUrl(),true,function(){ + setNewCurrentStream(getStreamFromUrl(),true,false,function(){ logoutWithoutReload(false); remove_spinner(); }); @@ -878,7 +878,7 @@ function doLogin(streamObjectToSet) { // set stream window.currentStream = ''; // always reload stream on login - setNewCurrentStream(streamObjectToSet,true,function(){ + setNewCurrentStream(streamObjectToSet,true,false,function(){ $('.language-dropdown').css('display','none'); $('#user-header').animate({opacity:'1'},800); $('#user-body').animate({opacity:'1'},800); @@ -1200,7 +1200,7 @@ $('#user-header').on('click',function(e){ if($(e.target).is('#mini-edit-profile-button')) { return; } - setNewCurrentStream(pathToStreamRouter(window.loggedIn.screen_name),true); + setNewCurrentStream(pathToStreamRouter(window.loggedIn.screen_name),true,false); }); @@ -1215,7 +1215,7 @@ $('#search-query').on('keyup',function(e) { if(e.keyCode==13) { showSearchStream $('button.icon.nav-search').on('click',function(e) { showSearchStream();}); // on click on magnifying glass function showSearchStream() { var path = 'search/notice?q=' + encodeURIComponent(replaceHtmlSpecialChars($('#search-query').val())); - setNewCurrentStream(pathToStreamRouter(path),true); + setNewCurrentStream(pathToStreamRouter(path),true,false); } @@ -1272,31 +1272,42 @@ $('body').on('click','a', function(e) { if(streamObject && streamObject.stream) { e.preventDefault(); - // if this is a user/{id} type link + // if this is a user/{id} type link we want to find the nickname before setting a new stream + // the main reason is that we want to update the browsers location bar and the .stream-selecton + // links as fast as we can. we rather not wait for the server response if(streamObject.name == 'profile by id') { - // see if we have the nickname in cache - var nickname = userArrayCacheGetUserNicknameById(streamObject.id); - if(nickname) { - setNewCurrentStream(pathToStreamRouter(nickname),true); + // pathToStreamRouter() might have found a cached nickname + if(streamObject.nickname) { + setNewCurrentStream(pathToStreamRouter(streamObject.nickname),true,streamObject.id); } - // if we don't have it in cache we query the server for it - // (we _could_ just try the nickname in the link html, but the user can have changed nickname) + // otherwise we might follow the user and thereby already know its nickname + else if (typeof window.following != 'undefined' && typeof window.following[streamObject.id] != 'undefined') { + setNewCurrentStream(pathToStreamRouter(window.following[streamObject.id].username),true,streamObject.id); + } + // if the text() of the clicked element looks like a user nickname, use that (but send id to setNewCurrentStream() in case this is bad data) + else if(/^@[a-zA-Z0-9]+$/.test($(e.target).text()) || /^[a-zA-Z0-9]+$/.test($(e.target).text())) { + var nickname = $(e.target).text(); + if(nickname.indexOf('@') == 0) { + nickname = nickname.substring(1); // remove any starting @ + } + setNewCurrentStream(pathToStreamRouter(nickname),true,streamObject.id); + } + // if we can't figure out or guess a nickname, query the server for it else { - display_spinner(); - getUserIdFromNicknameFromAPI(streamObject.id,function(nickname) { + getNicknameByUserIdFromAPI(streamObject.id,function(nickname) { if(nickname) { - setNewCurrentStream(pathToStreamRouter(nickname),true); + setNewCurrentStream(pathToStreamRouter(nickname),true,false); } else { - remove_spinner(); - alert('could not find local user with id ' + streamObject.id); + alert('user not found'); } }); } } + // all other links that qvitter can handle else { - setNewCurrentStream(streamObject,true); + setNewCurrentStream(streamObject,true,false); } } } @@ -2300,7 +2311,7 @@ $('body').on('click','button.shorten',function () { · · · · · · · · · · · · · */ $('body').on('click','.reload-stream',function () { $('.reload-stream').hide(); - setNewCurrentStream(URLtoStreamRouter(window.location.href),false,function(){ + setNewCurrentStream(URLtoStreamRouter(window.location.href),false,false,function(){ $('.reload-stream').show(); }); }); @@ -3501,7 +3512,7 @@ $('body').on('click','#mini-edit-profile-button, #edit-profile-header-link, .hov $('#page-container > .profile-card .edit-profile-button').trigger('click'); } else { - setNewCurrentStream(pathToStreamRouter(window.loggedIn.screen_name), true, function(){ + setNewCurrentStream(pathToStreamRouter(window.loggedIn.screen_name), true, false, function(){ $('#page-container > .profile-card .edit-profile-button').trigger('click'); }); } diff --git a/js/stream-router.js b/js/stream-router.js index 6935417..291499b 100644 --- a/js/stream-router.js +++ b/js/stream-router.js @@ -217,7 +217,7 @@ function pathToStreamRouter(path) { return streamObject; } - // group/{id }/id + // group/{id}/id if(pathSplit.length == 3 && pathSplit[0] == 'group' && /^[0-9]+$/.test(pathSplit[1]) && pathSplit[2] == 'id') { streamObject.name = 'group notice stream by id'; streamObject.id = pathSplit[1];