This repository has been archived on 2023-03-18. You can view files and clone it, but cannot push or open issues or pull requests.
osr-discourse-src/app/assets/javascripts/discourse/app/controllers/exception.js
Kane York c72bf1d732 FEATURE: Improvement to history stack handling on server errors
The exception page is shown before Ember can actually figure out what the final destination URL we're going to is.
This means that the new page is not present in the history stack, so if we attempt to use the history stack to go back, we will actually navigate back by two steps.
By instead forcing a navigation to the current URL, we achieve the goal of going "back" with no history mucking.

Unfortunately, the actual URL that was attempted is not available. Additionally, this only works for the on-screen back button and not the browser back.

Additionally, several modernizations of the exception page code were made.
2021-06-21 11:09:23 -07:00

156 lines
4.2 KiB
JavaScript

import { alias, equal, gte, none } from "@ember/object/computed";
import discourseComputed, { on } from "discourse-common/utils/decorators";
import DiscourseURL from "discourse/lib/url";
import Controller from "@ember/controller";
import I18n from "I18n";
import { schedule } from "@ember/runloop";
const ButtonBackBright = {
classes: "btn-primary",
action: "back",
key: "errors.buttons.back",
},
ButtonBackDim = {
classes: "",
action: "back",
key: "errors.buttons.back",
},
ButtonTryAgain = {
classes: "btn-primary",
action: "tryLoading",
key: "errors.buttons.again",
icon: "sync",
},
ButtonLoadPage = {
classes: "btn-primary",
action: "tryLoading",
key: "errors.buttons.fixed",
};
// The controller for the nice error page
export default Controller.extend({
thrown: null,
lastTransition: null,
@discourseComputed("thrown")
isNetwork(thrown) {
// never made it on the wire
if (thrown && thrown.readyState === 0) {
return true;
}
// timed out
if (thrown && thrown.jqTextStatus === "timeout") {
return true;
}
return false;
},
isNotFound: equal("thrown.status", 404),
isForbidden: equal("thrown.status", 403),
isServer: gte("thrown.status", 500),
isUnknown: none("isNetwork", "isServer"),
// Handling for the detailed_404 setting (which actually creates 403s)
errorHtml: alias("thrown.responseJSON.extras.html"),
// TODO
// make ajax requests to /srv/status with exponential backoff
// if one succeeds, set networkFixed to true, which puts a "Fixed!" message on the page
networkFixed: false,
loading: false,
@on("init")
_init() {
this.set("loading", false);
},
@discourseComputed("isNetwork", "thrown.status", "thrown")
reason(isNetwork, thrownStatus, thrown) {
if (isNetwork) {
return I18n.t("errors.reasons.network");
} else if (thrownStatus >= 500) {
return I18n.t("errors.reasons.server");
} else if (thrownStatus === 404) {
return I18n.t("errors.reasons.not_found");
} else if (thrownStatus === 403) {
return I18n.t("errors.reasons.forbidden");
} else if (thrown === null) {
return I18n.t("errors.reasons.unknown");
} else {
// TODO
return I18n.t("errors.reasons.unknown");
}
},
requestUrl: alias("thrown.requestedUrl"),
@discourseComputed(
"networkFixed",
"isNetwork",
"thrown.status",
"thrown.statusText",
"thrown"
)
desc(networkFixed, isNetwork, thrownStatus, thrownStatusText, thrown) {
if (networkFixed) {
return I18n.t("errors.desc.network_fixed");
} else if (isNetwork) {
return I18n.t("errors.desc.network");
} else if (thrownStatus === 404) {
return I18n.t("errors.desc.not_found");
} else if (thrownStatus === 403) {
return I18n.t("errors.desc.forbidden");
} else if (thrownStatus >= 500) {
return I18n.t("errors.desc.server", {
status: thrownStatus + " " + thrownStatusText,
});
} else if (thrown === null) {
return I18n.t("errors.desc.unknown");
} else {
// TODO
return I18n.t("errors.desc.unknown");
}
},
@discourseComputed("networkFixed", "isNetwork", "lastTransition")
enabledButtons(networkFixed, isNetwork, lastTransition) {
if (networkFixed) {
return [ButtonLoadPage];
} else if (isNetwork) {
return [ButtonBackDim, ButtonTryAgain];
} else if (!lastTransition) {
return [ButtonBackBright];
} else {
return [ButtonBackBright, ButtonTryAgain];
}
},
actions: {
back() {
// Strip off subfolder
const currentURL = DiscourseURL.router.location.getURL();
if (this.lastTransition && currentURL !== "/exception") {
this.lastTransition.abort();
this.setProperties({ lastTransition: null, thrown: null });
// Can't use routeTo because it handles navigation to the same page
DiscourseURL.handleURL(currentURL);
} else {
window.history.back();
}
},
tryLoading() {
this.set("loading", true);
schedule("afterRender", () => {
const transition = this.lastTransition;
this.setProperties({ lastTransition: null, thrown: null });
transition.retry();
this.set("loading", false);
});
},
},
});