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.
156 lines
4.2 KiB
JavaScript
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);
|
|
});
|
|
},
|
|
},
|
|
});
|