diff options
author | silverwind <me@silverwind.io> | 2019-02-28 23:10:08 +0100 |
---|---|---|
committer | zeripath <art27@cantab.net> | 2019-02-28 22:10:08 +0000 |
commit | 8e266c31933343437d8edef3d42f80b50e7e6279 (patch) | |
tree | b0d424d50c6158b8b8b7bf0f896afb9a9a8e34fd | |
parent | 4a2e92bcd1060ba0c124732690fb2f51c7f9d79d (diff) | |
download | gitea-8e266c31933343437d8edef3d42f80b50e7e6279.tar.gz gitea-8e266c31933343437d8edef3d42f80b50e7e6279.zip |
UI: Fix race in update issue labels and assignees (#6194)
Fix #6191
* fix issue update race condition
* fix similar race same race when clearing assignee
* always load promise polyfill
* replace es6-promise with promise-polyfill
* move promise-polyfill to <head>
-rw-r--r-- | public/js/index.js | 57 | ||||
-rw-r--r-- | public/vendor/librejs.html | 6 | ||||
-rw-r--r-- | public/vendor/plugins/es6-promise/es6-promise.auto.min.js | 1 | ||||
-rw-r--r-- | public/vendor/plugins/promise-polyfill/polyfill.min.js | 1 | ||||
-rw-r--r-- | templates/base/footer.tmpl | 1 | ||||
-rw-r--r-- | templates/base/head.tmpl | 1 |
6 files changed, 35 insertions, 32 deletions
diff --git a/public/js/index.js b/public/js/index.js index c86ccd4cf0..a34904abba 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -216,17 +216,19 @@ function initBranchSelector() { }); } -function updateIssuesMeta(url, action, issueIds, elementId, afterSuccess) { - $.ajax({ - type: "POST", - url: url, - data: { - "_csrf": csrf, - "action": action, - "issue_ids": issueIds, - "id": elementId - }, - success: afterSuccess +function updateIssuesMeta(url, action, issueIds, elementId) { + return new Promise(function(resolve) { + $.ajax({ + type: "POST", + url: url, + data: { + "_csrf": csrf, + "action": action, + "issue_ids": issueIds, + "id": elementId + }, + success: resolve + }) }) } @@ -348,6 +350,10 @@ function uploadFile(file, callback) { xhr.send(formData); } +function reload() { + window.location.reload(); +} + function initImagePaste(target) { target.each(function(i, field) { field.addEventListener('paste', function(event){ @@ -385,18 +391,20 @@ function initCommentForm() { $('.' + selector).dropdown('setting', 'onHide', function(){ hasLabelUpdateAction = $listMenu.data('action') == 'update'; // Update the var if (hasLabelUpdateAction) { + var promises = []; for (var elementId in labels) { if (labels.hasOwnProperty(elementId)) { var label = labels[elementId]; - updateIssuesMeta( + var promise = updateIssuesMeta( label["update-url"], label["action"], label["issue-id"], elementId ); + promises.push(promise); } } - location.reload(); + Promise.all(promises).then(reload); } }); @@ -479,8 +487,7 @@ function initCommentForm() { "clear", $listMenu.data('issue-id'), "" - ); - $listMenu.data('action', 'update'); // Update to reload the page when we updated items + ).then(reload); } $(this).parent().find('.item').each(function () { @@ -518,9 +525,8 @@ function initCommentForm() { $menu.data('update-url'), "", $menu.data('issue-id'), - $(this).data('id'), - function() { location.reload(); } - ); + $(this).data('id') + ).then(reload); } switch (input_id) { case '#milestone_id': @@ -545,9 +551,8 @@ function initCommentForm() { $menu.data('update-url'), "", $menu.data('issue-id'), - $(this).data('id'), - function() { location.reload(); } - ); + $(this).data('id') + ).then(reload); } $list.find('.selected').html(''); @@ -801,7 +806,7 @@ function initRepository() { function (data) { $editInput.val(data.title); $issueTitle.text(data.title); - location.reload(); + reload(); }); return false; }); @@ -1786,7 +1791,7 @@ function u2fRegistered(resp) { data: JSON.stringify(resp), contentType: "application/json; charset=utf-8", success: function(){ - window.location.reload(); + reload(); }, fail: function (xhr, textStatus) { u2fError(1); @@ -2073,9 +2078,7 @@ $(document).ready(function () { return this.dataset.issueId; }).get().join(); var url = this.dataset.url - updateIssuesMeta(url, action, issueIDs, elementId, function() { - location.reload(); - }); + updateIssuesMeta(url, action, issueIDs, elementId).then(reload); }); buttonsClickOnEnter(); @@ -2912,7 +2915,7 @@ function updateDeadline(deadlineString) { contentType: 'application/json', type: 'POST', success: function () { - window.location.reload(); + reload(); }, error: function () { $('#deadline-loader').removeClass('loading'); diff --git a/public/vendor/librejs.html b/public/vendor/librejs.html index 18c5aca935..1472849661 100644 --- a/public/vendor/librejs.html +++ b/public/vendor/librejs.html @@ -146,9 +146,9 @@ <td><a href="https://github.com/moment/moment/archive/2.22.2.tar.gz">0.4.1.tar.gz</a></td> </tr> <tr> - <td><a href="./plugins/es6-promise/">es6-promise</a></td> - <td><a href="https://github.com/stefanpenner/es6-promise/blob/master/LICENSE">MIT</a></td> - <td><a href="https://github.com/stefanpenner/es6-promise/archive/v4.2.6.tar.gz">4.2.6.tar.gz</a></td> + <td><a href="./plugins/promise-polyfill/">promise-polyfill</a></td> + <td><a href="https://github.com/taylorhakes/promise-polyfill/blob/master/LICENSE">MIT</a></td> + <td><a href="https://github.com/taylorhakes/promise-polyfill/archive/8.1.0.tar.gz">8.1.0.tar.gz</a></td> </tr> </tbody> </table> diff --git a/public/vendor/plugins/es6-promise/es6-promise.auto.min.js b/public/vendor/plugins/es6-promise/es6-promise.auto.min.js deleted file mode 100644 index 77596bdaf8..0000000000 --- a/public/vendor/plugins/es6-promise/es6-promise.auto.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.ES6Promise=e()}(this,function(){"use strict";function t(t){var e=typeof t;return null!==t&&("object"===e||"function"===e)}function e(t){return"function"==typeof t}function n(t){B=t}function r(t){G=t}function o(){return function(){return process.nextTick(a)}}function i(){return"undefined"!=typeof z?function(){z(a)}:c()}function s(){var t=0,e=new J(a),n=document.createTextNode("");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function u(){var t=new MessageChannel;return t.port1.onmessage=a,function(){return t.port2.postMessage(0)}}function c(){var t=setTimeout;return function(){return t(a,1)}}function a(){for(var t=0;t<W;t+=2){var e=V[t],n=V[t+1];e(n),V[t]=void 0,V[t+1]=void 0}W=0}function f(){try{var t=Function("return this")().require("vertx");return z=t.runOnLoop||t.runOnContext,i()}catch(e){return c()}}function l(t,e){var n=this,r=new this.constructor(p);void 0===r[Z]&&O(r);var o=n._state;if(o){var i=arguments[o-1];G(function(){return P(o,r,i,n._result)})}else E(n,r,t,e);return r}function h(t){var e=this;if(t&&"object"==typeof t&&t.constructor===e)return t;var n=new e(p);return g(n,t),n}function p(){}function v(){return new TypeError("You cannot resolve a promise with itself")}function d(){return new TypeError("A promises callback cannot return that same promise.")}function _(t){try{return t.then}catch(e){return nt.error=e,nt}}function y(t,e,n,r){try{t.call(e,n,r)}catch(o){return o}}function m(t,e,n){G(function(t){var r=!1,o=y(n,e,function(n){r||(r=!0,e!==n?g(t,n):S(t,n))},function(e){r||(r=!0,j(t,e))},"Settle: "+(t._label||" unknown promise"));!r&&o&&(r=!0,j(t,o))},t)}function b(t,e){e._state===tt?S(t,e._result):e._state===et?j(t,e._result):E(e,void 0,function(e){return g(t,e)},function(e){return j(t,e)})}function w(t,n,r){n.constructor===t.constructor&&r===l&&n.constructor.resolve===h?b(t,n):r===nt?(j(t,nt.error),nt.error=null):void 0===r?S(t,n):e(r)?m(t,n,r):S(t,n)}function g(e,n){e===n?j(e,v()):t(n)?w(e,n,_(n)):S(e,n)}function A(t){t._onerror&&t._onerror(t._result),T(t)}function S(t,e){t._state===$&&(t._result=e,t._state=tt,0!==t._subscribers.length&&G(T,t))}function j(t,e){t._state===$&&(t._state=et,t._result=e,G(A,t))}function E(t,e,n,r){var o=t._subscribers,i=o.length;t._onerror=null,o[i]=e,o[i+tt]=n,o[i+et]=r,0===i&&t._state&&G(T,t)}function T(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r=void 0,o=void 0,i=t._result,s=0;s<e.length;s+=3)r=e[s],o=e[s+n],r?P(n,r,o,i):o(i);t._subscribers.length=0}}function M(t,e){try{return t(e)}catch(n){return nt.error=n,nt}}function P(t,n,r,o){var i=e(r),s=void 0,u=void 0,c=void 0,a=void 0;if(i){if(s=M(r,o),s===nt?(a=!0,u=s.error,s.error=null):c=!0,n===s)return void j(n,d())}else s=o,c=!0;n._state!==$||(i&&c?g(n,s):a?j(n,u):t===tt?S(n,s):t===et&&j(n,s))}function x(t,e){try{e(function(e){g(t,e)},function(e){j(t,e)})}catch(n){j(t,n)}}function C(){return rt++}function O(t){t[Z]=rt++,t._state=void 0,t._result=void 0,t._subscribers=[]}function k(){return new Error("Array Methods must be provided an Array")}function F(t){return new ot(this,t).promise}function Y(t){var e=this;return new e(U(t)?function(n,r){for(var o=t.length,i=0;i<o;i++)e.resolve(t[i]).then(n,r)}:function(t,e){return e(new TypeError("You must pass an array to race."))})}function q(t){var e=this,n=new e(p);return j(n,t),n}function D(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function K(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function L(){var t=void 0;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(e){throw new Error("polyfill failed because global object is unavailable in this environment")}var n=t.Promise;if(n){var r=null;try{r=Object.prototype.toString.call(n.resolve())}catch(e){}if("[object Promise]"===r&&!n.cast)return}t.Promise=it}var N=void 0;N=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)};var U=N,W=0,z=void 0,B=void 0,G=function(t,e){V[W]=t,V[W+1]=e,W+=2,2===W&&(B?B(a):X())},H="undefined"!=typeof window?window:void 0,I=H||{},J=I.MutationObserver||I.WebKitMutationObserver,Q="undefined"==typeof self&&"undefined"!=typeof process&&"[object process]"==={}.toString.call(process),R="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,V=new Array(1e3),X=void 0;X=Q?o():J?s():R?u():void 0===H&&"function"==typeof require?f():c();var Z=Math.random().toString(36).substring(2),$=void 0,tt=1,et=2,nt={error:null},rt=0,ot=function(){function t(t,e){this._instanceConstructor=t,this.promise=new t(p),this.promise[Z]||O(this.promise),U(e)?(this.length=e.length,this._remaining=e.length,this._result=new Array(this.length),0===this.length?S(this.promise,this._result):(this.length=this.length||0,this._enumerate(e),0===this._remaining&&S(this.promise,this._result))):j(this.promise,k())}return t.prototype._enumerate=function(t){for(var e=0;this._state===$&&e<t.length;e++)this._eachEntry(t[e],e)},t.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===h){var o=_(t);if(o===l&&t._state!==$)this._settledAt(t._state,e,t._result);else if("function"!=typeof o)this._remaining--,this._result[e]=t;else if(n===it){var i=new n(p);w(i,t,o),this._willSettleAt(i,e)}else this._willSettleAt(new n(function(e){return e(t)}),e)}else this._willSettleAt(r(t),e)},t.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===$&&(this._remaining--,t===et?j(r,n):this._result[e]=n),0===this._remaining&&S(r,this._result)},t.prototype._willSettleAt=function(t,e){var n=this;E(t,void 0,function(t){return n._settledAt(tt,e,t)},function(t){return n._settledAt(et,e,t)})},t}(),it=function(){function t(e){this[Z]=C(),this._result=this._state=void 0,this._subscribers=[],p!==e&&("function"!=typeof e&&D(),this instanceof t?x(this,e):K())}return t.prototype["catch"]=function(t){return this.then(null,t)},t.prototype["finally"]=function(t){var n=this,r=n.constructor;return e(t)?n.then(function(e){return r.resolve(t()).then(function(){return e})},function(e){return r.resolve(t()).then(function(){throw e})}):n.then(t,t)},t}();return it.prototype.then=l,it.all=F,it.race=Y,it.resolve=h,it.reject=q,it._setScheduler=n,it._setAsap=r,it._asap=G,it.polyfill=L,it.Promise=it,it.polyfill(),it}); diff --git a/public/vendor/plugins/promise-polyfill/polyfill.min.js b/public/vendor/plugins/promise-polyfill/polyfill.min.js new file mode 100644 index 0000000000..425c164d04 --- /dev/null +++ b/public/vendor/plugins/promise-polyfill/polyfill.min.js @@ -0,0 +1 @@ +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n():"function"==typeof define&&define.amd?define(n):n()}(0,function(){"use strict";function e(e){var n=this.constructor;return this.then(function(t){return n.resolve(e()).then(function(){return t})},function(t){return n.resolve(e()).then(function(){return n.reject(t)})})}function n(){}function t(e){if(!(this instanceof t))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=undefined,this._deferreds=[],u(e,this)}function o(e,n){for(;3===e._state;)e=e._value;0!==e._state?(e._handled=!0,t._immediateFn(function(){var t=1===e._state?n.onFulfilled:n.onRejected;if(null!==t){var o;try{o=t(e._value)}catch(f){return void i(n.promise,f)}r(n.promise,o)}else(1===e._state?r:i)(n.promise,e._value)})):e._deferreds.push(n)}function r(e,n){try{if(n===e)throw new TypeError("A promise cannot be resolved with itself.");if(n&&("object"==typeof n||"function"==typeof n)){var o=n.then;if(n instanceof t)return e._state=3,e._value=n,void f(e);if("function"==typeof o)return void u(function(e,n){return function(){e.apply(n,arguments)}}(o,n),e)}e._state=1,e._value=n,f(e)}catch(r){i(e,r)}}function i(e,n){e._state=2,e._value=n,f(e)}function f(e){2===e._state&&0===e._deferreds.length&&t._immediateFn(function(){e._handled||t._unhandledRejectionFn(e._value)});for(var n=0,r=e._deferreds.length;r>n;n++)o(e,e._deferreds[n]);e._deferreds=null}function u(e,n){var t=!1;try{e(function(e){t||(t=!0,r(n,e))},function(e){t||(t=!0,i(n,e))})}catch(o){if(t)return;t=!0,i(n,o)}}var c=setTimeout;t.prototype["catch"]=function(e){return this.then(null,e)},t.prototype.then=function(e,t){var r=new this.constructor(n);return o(this,new function(e,n,t){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof n?n:null,this.promise=t}(e,t,r)),r},t.prototype["finally"]=e,t.all=function(e){return new t(function(n,t){function o(e,f){try{if(f&&("object"==typeof f||"function"==typeof f)){var u=f.then;if("function"==typeof u)return void u.call(f,function(n){o(e,n)},t)}r[e]=f,0==--i&&n(r)}catch(c){t(c)}}if(!e||"undefined"==typeof e.length)throw new TypeError("Promise.all accepts an array");var r=Array.prototype.slice.call(e);if(0===r.length)return n([]);for(var i=r.length,f=0;r.length>f;f++)o(f,r[f])})},t.resolve=function(e){return e&&"object"==typeof e&&e.constructor===t?e:new t(function(n){n(e)})},t.reject=function(e){return new t(function(n,t){t(e)})},t.race=function(e){return new t(function(n,t){for(var o=0,r=e.length;r>o;o++)e[o].then(n,t)})},t._immediateFn="function"==typeof setImmediate&&function(e){setImmediate(e)}||function(e){c(e,0)},t._unhandledRejectionFn=function(e){void 0!==console&&console&&console.warn("Possible Unhandled Promise Rejection:",e)};var l=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if("undefined"!=typeof global)return global;throw Error("unable to locate global object")}();"Promise"in l?l.Promise.prototype["finally"]||(l.Promise.prototype["finally"]=e):l.Promise=t}); diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index 98c9871f20..2481b2187c 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -122,7 +122,6 @@ <script src="{{AppSubUrl}}/vendor/plugins/semantic/semantic.min.js"></script> <script src="{{AppSubUrl}}/js/index.js?v={{MD5 AppVer}}"></script> {{if .EnableHeatmap}} - <script src="{{AppSubUrl}}/vendor/plugins/es6-promise/es6-promise.auto.min.js" charset="utf-8"></script> <script src="{{AppSubUrl}}/vendor/plugins/moment/moment.min.js" charset="utf-8"></script> <script src="{{AppSubUrl}}/vendor/plugins/vue-calendar-heatmap/vue-calendar-heatmap.browser.js" charset="utf-8"></script> <script type="text/javascript"> diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index b6afe94bb4..0a8d3bd9b5 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -123,6 +123,7 @@ {{end}} <style class="list-search-style"></style> + <script src="{{AppSubUrl}}/vendor/plugins/promise-polyfill/polyfill.min.js"></script> <script src="{{AppSubUrl}}/vendor/plugins/cssrelpreload/loadCSS.min.js"></script> <script src="{{AppSubUrl}}/vendor/plugins/cssrelpreload/cssrelpreload.min.js"></script> {{if .PageIsUserProfile}} |