mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-20 12:48:00 +00:00
feat(repo): add quick repository switcher to repo header (#38188)
Add a GitHub-style quick repo switcher: a caret next to the owner/repo breadcrumb opens a dropdown that lists and searches the current owner's repositories and navigates to the selected one. The current repository is marked with a check, and private/fork repos show an icon. Also, fix various bugs in fomtantic dropdown remote query ## Screenshots <img width="505" height="198" alt="image" src="https://github.com/user-attachments/assets/9f673d1b-fe60-41f0-b9e2-b00dc43720b5" /> Fixes #38187 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -75,6 +75,8 @@ $.api = $.fn.api = function(parameters) {
|
||||
data,
|
||||
requestStartTime,
|
||||
|
||||
cachedResponses = {},
|
||||
|
||||
// standard module
|
||||
element = this,
|
||||
context = $context[0],
|
||||
@@ -141,34 +143,16 @@ $.api = $.fn.api = function(parameters) {
|
||||
|
||||
read: {
|
||||
cachedResponse: function(url) {
|
||||
var
|
||||
response
|
||||
;
|
||||
if(window.Storage === undefined) {
|
||||
module.error(error.noStorage);
|
||||
return;
|
||||
}
|
||||
response = sessionStorage.getItem(url);
|
||||
module.debug('Using cached response', url, response);
|
||||
response = module.decode.json(response);
|
||||
return response;
|
||||
const cacheItem = cachedResponses[url];
|
||||
if (!cacheItem) return null;
|
||||
return module.decode.json(cacheItem.respText);
|
||||
}
|
||||
},
|
||||
write: {
|
||||
cachedResponse: function(url, response) {
|
||||
if(response && response === '') {
|
||||
module.debug('Response empty, not caching', response);
|
||||
return;
|
||||
}
|
||||
if(window.Storage === undefined) {
|
||||
module.error(error.noStorage);
|
||||
return;
|
||||
}
|
||||
if( $.isPlainObject(response) ) {
|
||||
response = JSON.stringify(response);
|
||||
}
|
||||
sessionStorage.setItem(url, response);
|
||||
module.verbose('Storing cached response for url', url, response);
|
||||
if(!response) return;
|
||||
if($.isPlainObject(response) || $.isArray(response)) response = JSON.stringify(response);
|
||||
cachedResponses[url] = {cacheTime: Date.now(), respText: response};
|
||||
}
|
||||
},
|
||||
|
||||
@@ -243,10 +227,12 @@ $.api = $.fn.api = function(parameters) {
|
||||
|
||||
module.debug('Querying URL', ajaxSettings.url);
|
||||
module.verbose('Using AJAX settings', ajaxSettings);
|
||||
if(settings.cache === 'local' && module.read.cachedResponse(url)) {
|
||||
|
||||
const cachedObj = module.read.cachedResponse(url);
|
||||
if(settings.cache === 'local' && cachedObj) {
|
||||
module.debug('Response returned from local cache');
|
||||
module.request = module.create.request();
|
||||
module.request.resolveWith(context, [ module.read.cachedResponse(url) ]);
|
||||
module.request.resolveWith(context, [ cachedObj ]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -537,13 +523,13 @@ $.api = $.fn.api = function(parameters) {
|
||||
}
|
||||
},
|
||||
request: {
|
||||
done: function(response, xhr) {
|
||||
done: function(response) {
|
||||
module.debug('Successful API Response', response);
|
||||
if(settings.cache === 'local' && url) {
|
||||
module.write.cachedResponse(url, response);
|
||||
module.debug('Saving server response locally', module.cache);
|
||||
}
|
||||
settings.onSuccess.call(context, response, $module, xhr);
|
||||
settings.onSuccess.call(context, response, $module);
|
||||
},
|
||||
complete: function(firstParameter, secondParameter) {
|
||||
var
|
||||
@@ -1081,7 +1067,7 @@ $.api.settings = {
|
||||
throttle : 0,
|
||||
|
||||
// whether to throttle first request or only repeated
|
||||
throttleFirstRequest : true,
|
||||
throttleFirstRequest : false,
|
||||
|
||||
// standard ajax settings
|
||||
method : 'get',
|
||||
@@ -1110,7 +1096,7 @@ $.api.settings = {
|
||||
// response was successful, if JSON passed validation
|
||||
onSuccess : function(response, $module) {},
|
||||
|
||||
// request finished without aborting
|
||||
// request completed, either success or error/failure/abort: jQuery.ajax().always()
|
||||
onComplete : function(response, $module) {},
|
||||
|
||||
// failed JSON success test
|
||||
|
||||
@@ -431,7 +431,12 @@ $.fn.dropdown = function(parameters) {
|
||||
module.refresh();
|
||||
},
|
||||
menu: function(values) {
|
||||
$menu.html( templates.menu(values, fields,settings.preserveHTML,settings.className));
|
||||
const $menuItems = $(templates.menu(values, fields,settings.preserveHTML,settings.className));
|
||||
$menuItems.attr('data-item-dynamic', '');
|
||||
$menu.find('[data-item-dynamic]').remove();
|
||||
$menu.append(...$menuItems);
|
||||
settings.onMenuUpdated();
|
||||
|
||||
$item = $menu.find(selector.item);
|
||||
$divider = settings.hideDividers ? $item.parent().children(selector.divider) : $();
|
||||
},
|
||||
@@ -515,8 +520,10 @@ $.fn.dropdown = function(parameters) {
|
||||
? callback
|
||||
: function(){}
|
||||
;
|
||||
if(!module.can.show() && module.is.remote()) {
|
||||
const dataKeyRemoteQueried = 'remote-queried';
|
||||
if(module.is.remote() && !$module.data(dataKeyRemoteQueried)) {
|
||||
module.debug('No API results retrieved, searching before show');
|
||||
$module.data(dataKeyRemoteQueried, true)
|
||||
module.queryRemote(module.get.query(), module.show);
|
||||
}
|
||||
if( module.can.show() && !module.is.active() ) {
|
||||
@@ -793,6 +800,9 @@ $.fn.dropdown = function(parameters) {
|
||||
},
|
||||
|
||||
queryRemote: function(query, callback) {
|
||||
const dataKeyRemoteQuerying = 'remote-querying';
|
||||
if ($module.data(dataKeyRemoteQuerying) === query) return;
|
||||
$module.data(dataKeyRemoteQuerying, query);
|
||||
var
|
||||
apiSettings = {
|
||||
errorDuration : false,
|
||||
@@ -801,6 +811,11 @@ $.fn.dropdown = function(parameters) {
|
||||
urlData : {
|
||||
query: query
|
||||
},
|
||||
onComplete: function() {
|
||||
if ($module.data(dataKeyRemoteQuerying) === query) {
|
||||
$module.removeData(dataKeyRemoteQuerying);
|
||||
}
|
||||
},
|
||||
onError: function() {
|
||||
module.add.message(message.serverError);
|
||||
callback();
|
||||
@@ -3930,7 +3945,8 @@ $.fn.dropdown.settings = {
|
||||
minCharacters : 0, // Minimum characters required to trigger API call
|
||||
|
||||
filterRemoteData : false, // Whether API results should be filtered after being returned for query term
|
||||
saveRemoteData : true, // Whether remote name/value pairs should be stored in sessionStorage to allow remote data to be restored on page refresh
|
||||
saveRemoteData : false, // Whether remote name/value pairs should be stored in sessionStorage to allow remote data to be restored on page refresh
|
||||
// saveRemoteData is a wrong design and buggy, don't use it.
|
||||
|
||||
throttle : 200, // How long to wait after last user input to search remotely
|
||||
|
||||
@@ -3996,6 +4012,7 @@ $.fn.dropdown.settings = {
|
||||
onLabelCreate : function(value, text) { return $(this); },
|
||||
onLabelRemove : function(value) { return true; },
|
||||
onNoResults : function(searchTerm) { return true; },
|
||||
onMenuUpdated : function(){},
|
||||
onShow : function(){},
|
||||
onHide : function(){},
|
||||
|
||||
@@ -4222,6 +4239,8 @@ $.fn.dropdown.settings.templates = {
|
||||
if(option[fields.divider]){
|
||||
html += '<div class="'+className.divider+'"></div>';
|
||||
}
|
||||
} else if( itemType === 'html' ) {
|
||||
html += option.html;
|
||||
}
|
||||
});
|
||||
return html;
|
||||
|
||||
Reference in New Issue
Block a user