DEV: Update DiscourseRoute and ApplicationRoute to native class syntax (#28594)

Changes made using the ember-native-class-codemod, plus some manual tweaks

Also ensures our implicit injections applied to the prototype immediately. Without this, they will only be applied on the next `.extend()` call, which is now later than the first native-class extension.
This commit is contained in:
David Taylor 2024-08-28 13:05:06 +01:00 committed by GitHub
parent b092ccbdc5
commit 2bc30bd7b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 213 additions and 199 deletions

View File

@ -46,6 +46,7 @@ function setInjections(target, injections) {
extension[key] = implicitInjectionShim(lookupName, key);
}
EmberObject.reopen.call(target, extension);
target.proto();
}
let alreadyRegistered = false;

View File

@ -21,44 +21,38 @@ import getURL from "discourse-common/lib/get-url";
import I18n from "discourse-i18n";
import NotActivatedModal from "../components/modal/not-activated";
function unlessStrictlyReadOnly(method, message) {
return function () {
if (this.site.isReadOnly && !this.site.isStaffWritesOnly) {
this.dialog.alert(message);
} else {
this[method]();
}
};
function isStrictlyReadonly(site) {
return site.isReadOnly && !site.isStaffWritesOnly;
}
const ApplicationRoute = DiscourseRoute.extend({
siteTitle: setting("title"),
shortSiteDescription: setting("short_site_description"),
export default class ApplicationRoute extends DiscourseRoute {
@service clientErrorHandler;
@service composer;
@service currentUser;
@service dialog;
@service documentTitle;
@service historyStore;
@service loadingSlider;
@service login;
@service modal;
@service router;
@service site;
@service siteSettings;
@service restrictedRouting;
clientErrorHandler: service(),
composer: service(),
currentUser: service(),
dialog: service(),
documentTitle: service(),
historyStore: service(),
loadingSlider: service(),
login: service(),
modal: service(),
router: service(),
site: service(),
siteSettings: service(),
restrictedRouting: service(),
@setting("title") siteTitle;
@setting("short_site_description") shortSiteDescription;
get isOnlyOneExternalLoginMethod() {
return (
!this.siteSettings.enable_local_logins &&
this.externalLoginMethods.length === 1
);
},
}
get externalLoginMethods() {
return findAll();
},
}
@action
loading(transition) {
@ -67,7 +61,7 @@ const ApplicationRoute = DiscourseRoute.extend({
this.loadingSlider.transitionEnded();
});
return false;
},
}
@action
willTransition(transition) {
@ -85,192 +79,215 @@ const ApplicationRoute = DiscourseRoute.extend({
}
return true;
},
}
@action
willResolveModel(transition) {
this.historyStore.willResolveModel(transition);
return true;
},
}
actions: {
toggleMobileView() {
mobile.toggleMobileView();
},
@action
toggleMobileView() {
mobile.toggleMobileView();
}
toggleSidebar() {
this.controllerFor("application").send("toggleSidebar");
},
@action
toggleSidebar() {
this.controllerFor("application").send("toggleSidebar");
}
logout: unlessStrictlyReadOnly(
"_handleLogout",
I18n.t("read_only_mode.logout_disabled")
),
@action
logout() {
if (isStrictlyReadonly(this.site)) {
this.dialog.alert(I18n.t("read_only_mode.logout_disabled"));
return;
}
this._handleLogout();
}
_collectTitleTokens(tokens) {
tokens.push(this.siteTitle);
if (
(window.location.pathname === getURL("/") ||
window.location.pathname === getURL("/login")) &&
this.shortSiteDescription !== ""
) {
tokens.push(this.shortSiteDescription);
@action
_collectTitleTokens(tokens) {
tokens.push(this.siteTitle);
if (
(window.location.pathname === getURL("/") ||
window.location.pathname === getURL("/login")) &&
this.shortSiteDescription !== ""
) {
tokens.push(this.shortSiteDescription);
}
this.documentTitle.setTitle(tokens.join(" - "));
}
@action
composePrivateMessage(user, post) {
const recipients = user ? user.get("username") : "";
const reply = post
? `${window.location.protocol}//${window.location.host}${post.url}`
: null;
const title = post
? I18n.t("composer.reference_topic_title", {
title: post.topic.title,
})
: null;
// used only once, one less dependency
return this.composer.open({
action: Composer.PRIVATE_MESSAGE,
recipients,
archetypeId: "private_message",
draftKey: Composer.NEW_PRIVATE_MESSAGE_KEY,
draftSequence: 0,
reply,
title,
});
}
@action
error(err, transition) {
const xhrOrErr = err.jqXHR ? err.jqXHR : err;
const exceptionController = this.controllerFor("exception");
let shouldBubble = false;
const themeOrPluginSource = identifySource(err);
if (!(xhrOrErr instanceof RouteException)) {
shouldBubble = true;
// eslint-disable-next-line no-console
console.error(
...[consolePrefix(err, themeOrPluginSource), xhrOrErr].filter(Boolean)
);
if (xhrOrErr && xhrOrErr.status === 404) {
return this.router.transitionTo("exception-unknown");
}
this.documentTitle.setTitle(tokens.join(" - "));
},
composePrivateMessage(user, post) {
const recipients = user ? user.get("username") : "";
const reply = post
? `${window.location.protocol}//${window.location.host}${post.url}`
: null;
const title = post
? I18n.t("composer.reference_topic_title", {
title: post.topic.title,
})
: null;
// used only once, one less dependency
return this.composer.open({
action: Composer.PRIVATE_MESSAGE,
recipients,
archetypeId: "private_message",
draftKey: Composer.NEW_PRIVATE_MESSAGE_KEY,
draftSequence: 0,
reply,
title,
});
},
error(err, transition) {
const xhrOrErr = err.jqXHR ? err.jqXHR : err;
const exceptionController = this.controllerFor("exception");
let shouldBubble = false;
const themeOrPluginSource = identifySource(err);
if (!(xhrOrErr instanceof RouteException)) {
shouldBubble = true;
// eslint-disable-next-line no-console
console.error(
...[consolePrefix(err, themeOrPluginSource), xhrOrErr].filter(Boolean)
if (themeOrPluginSource) {
this.clientErrorHandler.displayErrorNotice(
"Error loading route",
themeOrPluginSource
);
if (xhrOrErr && xhrOrErr.status === 404) {
return this.router.transitionTo("exception-unknown");
}
if (themeOrPluginSource) {
this.clientErrorHandler.displayErrorNotice(
"Error loading route",
themeOrPluginSource
);
}
}
}
exceptionController.setProperties({
lastTransition: transition,
thrown: xhrOrErr,
});
exceptionController.setProperties({
lastTransition: transition,
thrown: xhrOrErr,
});
if (transition.intent.url) {
if (transition.method === "replace") {
DiscourseURL.replaceState(transition.intent.url);
} else {
DiscourseURL.pushState(transition.intent.url);
}
}
this.intermediateTransitionTo("exception");
return shouldBubble;
},
showLogin: unlessStrictlyReadOnly(
"handleShowLogin",
I18n.t("read_only_mode.login_disabled")
),
showCreateAccount(createAccountProps = {}) {
if (this.site.isReadOnly) {
this.dialog.alert(I18n.t("read_only_mode.login_disabled"));
if (transition.intent.url) {
if (transition.method === "replace") {
DiscourseURL.replaceState(transition.intent.url);
} else {
this.handleShowCreateAccount(createAccountProps);
DiscourseURL.pushState(transition.intent.url);
}
},
}
showForgotPassword() {
this.modal.show(ForgotPassword);
},
this.intermediateTransitionTo("exception");
return shouldBubble;
}
showNotActivated(props) {
this.modal.show(NotActivatedModal, { model: props });
},
@action
showLogin() {
if (isStrictlyReadonly(this.site)) {
this.dialog.alert(I18n.t("read_only_mode.login_disabled"));
return;
}
this.handleShowLogin();
}
showUploadSelector() {
document.getElementById("file-uploader").click();
},
@action
showCreateAccount(createAccountProps = {}) {
if (this.site.isReadOnly) {
this.dialog.alert(I18n.t("read_only_mode.login_disabled"));
} else {
this.handleShowCreateAccount(createAccountProps);
}
}
showKeyboardShortcutsHelp() {
this.modal.show(KeyboardShortcutsHelp);
},
@action
showForgotPassword() {
this.modal.show(ForgotPassword);
}
// Close the current modal, and destroy its state.
closeModal(initiatedBy) {
return this.modal.close(initiatedBy);
},
@action
showNotActivated(props) {
this.modal.show(NotActivatedModal, { model: props });
}
/**
@action
showUploadSelector() {
document.getElementById("file-uploader").click();
}
@action
showKeyboardShortcutsHelp() {
this.modal.show(KeyboardShortcutsHelp);
}
// Close the current modal, and destroy its state.
@action
closeModal(initiatedBy) {
return this.modal.close(initiatedBy);
}
/**
Hide the modal, but keep it with all its state so that it can be shown again later.
This is useful if you want to prompt for confirmation. hideModal, ask "Are you sure?",
user clicks "No", reopenModal. If user clicks "Yes", be sure to call closeModal.
**/
hideModal() {
return this.modal.hide();
},
@action
hideModal() {
return this.modal.hide();
}
reopenModal() {
return this.modal.reopen();
},
@action
reopenModal() {
return this.modal.reopen();
}
editCategory(category) {
DiscourseURL.routeTo(`/c/${Category.slugFor(category)}/edit`);
},
@action
editCategory(category) {
DiscourseURL.routeTo(`/c/${Category.slugFor(category)}/edit`);
}
checkEmail(user) {
user.checkEmail();
},
@action
checkEmail(user) {
user.checkEmail();
}
createNewTopicViaParams(title, body, categoryId, tags) {
deprecated(
"createNewTopicViaParam on the application route is deprecated. Use the composer service instead",
{ id: "discourse.createNewTopicViaParams" }
);
getOwnerWithFallback(this).lookup("service:composer").openNewTopic({
title,
body,
categoryId,
tags,
});
},
@action
createNewTopicViaParams(title, body, categoryId, tags) {
deprecated(
"createNewTopicViaParam on the application route is deprecated. Use the composer service instead",
{ id: "discourse.createNewTopicViaParams" }
);
getOwnerWithFallback(this).lookup("service:composer").openNewTopic({
title,
body,
categoryId,
tags,
});
}
createNewMessageViaParams({
recipients = "",
topicTitle = "",
topicBody = "",
hasGroups = false,
} = {}) {
deprecated(
"createNewMessageViaParams on the application route is deprecated. Use the composer service instead",
{ id: "discourse.createNewMessageViaParams" }
);
getOwnerWithFallback(this).lookup("service:composer").openNewMessage({
recipients,
title: topicTitle,
body: topicBody,
hasGroups,
});
},
},
@action
createNewMessageViaParams({
recipients = "",
topicTitle = "",
topicBody = "",
hasGroups = false,
} = {}) {
deprecated(
"createNewMessageViaParams on the application route is deprecated. Use the composer service instead",
{ id: "discourse.createNewMessageViaParams" }
);
getOwnerWithFallback(this).lookup("service:composer").openNewMessage({
recipients,
title: topicTitle,
body: topicBody,
hasGroups,
});
}
handleShowLogin() {
if (this.siteSettings.enable_discourse_connect) {
@ -291,7 +308,7 @@ const ApplicationRoute = DiscourseRoute.extend({
});
}
}
},
}
handleShowCreateAccount(createAccountProps) {
if (this.siteSettings.enable_discourse_connect) {
@ -307,7 +324,7 @@ const ApplicationRoute = DiscourseRoute.extend({
this.modal.show(CreateAccount, { model: createAccountProps });
}
}
},
}
_handleLogout() {
if (this.currentUser) {
@ -315,7 +332,5 @@ const ApplicationRoute = DiscourseRoute.extend({
.destroySession()
.then((response) => logout({ redirect: response["redirect_url"] }));
}
},
});
export default ApplicationRoute;
}
}

View File

@ -6,16 +6,16 @@ import { seenUser } from "discourse/lib/user-presence";
import deprecated from "discourse-common/lib/deprecated";
import { getOwnerWithFallback } from "discourse-common/lib/get-owner";
const DiscourseRoute = Route.extend({
router: service(),
export default class DiscourseRoute extends Route {
@service router;
willTransition() {
seenUser();
},
}
_refreshTitleOnce() {
this.send("_collectTitleTokens", []);
},
}
@action
_collectTitleTokens(tokens) {
@ -31,19 +31,19 @@ const DiscourseRoute = Route.extend({
}
}
return true;
},
}
@action
refreshTitle() {
once(this, this._refreshTitleOnce);
},
}
redirectIfLoginRequired() {
const app = this.controllerFor("application");
if (app.get("loginRequired")) {
this.router.replaceWith("login");
}
},
}
openTopicDraft() {
deprecated(
@ -55,7 +55,7 @@ const DiscourseRoute = Route.extend({
.lookup("service:composer")
.openNewTopic({ preferDraft: true });
}
},
}
isCurrentUser(user) {
if (!this.currentUser) {
@ -63,7 +63,5 @@ const DiscourseRoute = Route.extend({
}
return user.id === this.currentUser.id;
},
});
export default DiscourseRoute;
}
}