DEV: Introduce declarative hide-application-footer helper (#23088)

Previously, we had a `showFooter` boolean on the application controller which would be set true/false in various routes by different routes/controllers. A global `routeWillChange` hook would set it `false` before every route transition, and the destination route/controller would have to set it `true` for the footer to show correctly.

This commit replaces that with a new 'declarative' system. Instead of having to set the value true/false manually, UIs which need the footer to be hidden can simply include the `{{hide-application-footer}}` helper in their template when needed. The helper/service will automatically keep track of all the current invocations of that helper, and only show the footer when there are 0 invocations.

This significantly simplifies things, and removes the need for many observers and controller injections, both of which are considered 'code smells' in modern Ember applications.
This commit is contained in:
David Taylor 2023-08-17 12:47:08 +01:00 committed by GitHub
parent 8b3eca056b
commit 5a99243629
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
97 changed files with 189 additions and 376 deletions

View File

@ -16,10 +16,11 @@ module.exports = {
"directory-table-header-title",
"loading-spinner",
"directory-item-label",
"hide-application-footer",
],
},
"no-implicit-this": {
allow: ["loading-spinner"],
allow: ["loading-spinner", "hide-application-footer"],
},
"require-mandatory-role-attributes": false,
"require-media-caption": false,

View File

@ -9,7 +9,6 @@ export default class AdminRoute extends DiscourseRoute {
activate() {
this.controllerFor("application").setProperties({
showTop: false,
showFooter: false,
});
}

View File

@ -1,3 +1,4 @@
{{hide-application-footer}}
<AdminWrapper @class="container">
<div class="row">
<div class="full-width">

View File

@ -3,6 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators";
import discourseDebounce from "discourse-common/lib/debounce";
import { inject as service } from "@ember/service";
import { action } from "@ember/object";
import deprecated from "discourse-common/lib/deprecated";
const HIDE_SIDEBAR_KEY = "sidebar-hidden";
@ -10,8 +11,8 @@ export default Controller.extend({
queryParams: [{ navigationMenuQueryParamOverride: "navigation_menu" }],
showTop: true,
showFooter: false,
router: service(),
footer: service(),
showSidebar: false,
navigationMenuQueryParamOverride: null,
sidebarDisabledRouteOverride: false,
@ -22,6 +23,18 @@ export default Controller.extend({
this.showSidebar = this.calculateShowSidebar();
},
get showFooter() {
return this.footer.showFooter;
},
set showFooter(value) {
deprecated(
"showFooter state is now stored in the `footer` service, and should be controlled by adding the {{hide-application-footer}} helper to an Ember template.",
{ id: "discourse.application-show-footer" }
);
this.footer.showFooter = value;
},
@discourseComputed
canSignUp() {
return (

View File

@ -1,6 +1,6 @@
import EmberObject, { action } from "@ember/object";
import Controller, { inject as controller } from "@ember/controller";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import discourseComputed from "discourse-common/utils/decorators";
import Badge from "discourse/models/badge";
import I18n from "I18n";
import UserBadge from "discourse/models/user-badge";
@ -63,11 +63,6 @@ export default Controller.extend({
return !!user && grantCount > 1;
},
@observes("canLoadMore")
_showFooter() {
this.set("application.showFooter", !this.canLoadMore);
},
@action
loadMore() {
if (!this.canLoadMore) {

View File

@ -1,5 +1,5 @@
import { inject as service } from "@ember/service";
import { alias, equal, not } from "@ember/object/computed";
import { alias, equal } from "@ember/object/computed";
import Controller, { inject as controller } from "@ember/controller";
import { action } from "@ember/object";
import Category from "discourse/models/category";
@ -8,29 +8,24 @@ import DiscourseURL from "discourse/lib/url";
export default class DiscoveryController extends Controller {
@service router;
@controller("discovery/topics") discoveryTopics;
@controller("navigation/category") navigationCategory;
@controller application;
@equal("router.currentRouteName", "discovery.categories")
viewingCategoriesList;
@alias("navigationCategory.category") category;
@alias("navigationCategory.noSubcategories") noSubcategories;
@not("discoveryTopics.model.canLoadMore") loadedAllItems;
loading = false;
@action
loadingBegan() {
this.set("loading", true);
this.set("application.showFooter", false);
}
@action
loadingComplete() {
this.set("loading", false);
this.set("application.showFooter", this.loadedAllItems);
}
showMoreUrl(period) {

View File

@ -41,19 +41,6 @@ export default class TopicsController extends DiscoveryController.extend(
@equal("period", "weekly") weekly;
@equal("period", "daily") daily;
// Remove these loading actions which are defined in `DiscoveryController`
// We want them to bubble in DiscoveryTopicsController
@action
loadingBegan() {
this.set("application.showFooter", false);
return true;
}
@action
loadingComplete() {
this.set("application.showFooter", this.loadedAllItems);
return true;
}
@discourseComputed("model.filter", "model.topics.length")
showDismissRead(filter, topicsLength) {
return this._isFilterPage(filter, "unread") && topicsLength > 0;

View File

@ -245,11 +245,6 @@ export default Controller.extend({
);
},
@observes("loading")
_showFooter() {
this.set("application.showFooter", !this.loading);
},
@discourseComputed("resultCount", "noSortQ")
resultCountLabel(count, term) {
const plus = count % 50 === 0 ? "+" : "";
@ -437,7 +432,6 @@ export default Controller.extend({
},
_afterTransition() {
this._showFooter();
if (Object.keys(this.model).length === 0) {
this.reset();
}

View File

@ -1,6 +1,5 @@
import Controller, { inject as controller } from "@ember/controller";
import { fmt } from "discourse/lib/computed";
import { observes } from "discourse-common/utils/decorators";
export default Controller.extend({
group: controller(),
@ -41,9 +40,4 @@ export default Controller.extend({
}
},
},
@observes("canLoadMore")
_showFooter() {
this.set("application.showFooter", !this.canLoadMore);
},
});

View File

@ -1,4 +1,4 @@
import Controller, { inject as controller } from "@ember/controller";
import Controller from "@ember/controller";
import discourseComputed, {
debounce,
observes,
@ -9,8 +9,6 @@ import { gt } from "@ember/object/computed";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default Controller.extend({
application: controller(),
queryParams: ["order", "asc", "filter"],
order: "",
@ -24,6 +22,10 @@ export default Controller.extend({
bulkSelection: null,
get canLoadMore() {
return this.get("model.members")?.length >= this.get("model.user_count");
},
@observes("filterInput")
filterInputChanged() {
this._setFilter();
@ -44,18 +46,13 @@ export default Controller.extend({
return;
}
if (!refresh && this.model.members.length >= this.model.user_count) {
this.set("application.showFooter", true);
if (!refresh && !this.canLoadMore) {
return;
}
this.set("loading", true);
this.model.reloadMembers(this.memberParams, refresh).finally(() => {
this.setProperties({
"application.showFooter":
this.model.members.length >= this.model.user_count,
loading: false,
});
this.set("loading", false);
if (this.refresh) {
this.set("bulkSelection", []);

View File

@ -42,11 +42,6 @@ export default Controller.extend({
});
},
@observes("model.all_loaded")
_showFooter() {
this.set("application.showFooter", this.get("model.all_loaded"));
},
reset() {
this.setProperties({
offset: 0,

View File

@ -18,6 +18,10 @@ export default Controller.extend({
loading: false,
get canLoadMore() {
return this.get("model.requesters")?.length < this.get("model.user_count");
},
@observes("filterInput")
filterInputChanged() {
this._setFilter();
@ -43,17 +47,12 @@ export default Controller.extend({
return;
}
if (!refresh && model.requesters.length >= model.user_count) {
this.set("application.showFooter", true);
if (!refresh && !this.canLoadMore) {
return;
}
this.set("loading", true);
model.findRequesters(this.memberParams, refresh).finally(() => {
this.set(
"application.showFooter",
model.requesters.length >= model.user_count
);
this.set("loading", false);
});
},

View File

@ -1,4 +1,4 @@
import Controller, { inject as controller } from "@ember/controller";
import Controller from "@ember/controller";
import I18n from "I18n";
import { INPUT_DELAY } from "discourse-common/config/environment";
import { action } from "@ember/object";
@ -8,7 +8,6 @@ import { inject as service } from "@ember/service";
export default Controller.extend({
router: service(),
application: controller(),
queryParams: ["order", "asc", "filter", "type"],
order: null,
asc: null,
@ -37,10 +36,6 @@ export default Controller.extend({
.findAll("group", params)
.then((groups) => {
this.set("groups", groups);
if (groups.canLoadMore) {
this.set("application.showFooter", !groups.canLoadMore);
}
})
.finally(() => this.set("isLoading", false));
},

View File

@ -1,6 +1,5 @@
import DiscoverySortableController from "discourse/controllers/discovery-sortable";
import { inject as controller } from "@ember/controller";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import discourseComputed from "discourse-common/utils/decorators";
import BulkTopicSelection from "discourse/mixins/bulk-topic-selection";
import DismissTopics from "discourse/mixins/dismiss-topics";
import I18n from "I18n";
@ -19,7 +18,6 @@ export default DiscoverySortableController.extend(
DismissTopics,
{
application: controller(),
dialog: service(),
router: service(),
currentUser: service(),
@ -79,11 +77,6 @@ export default DiscoverySortableController.extend(
});
},
@observes("list.canLoadMore")
_showFooter() {
this.set("application.showFooter", !this.list?.canLoadMore);
},
@discourseComputed("navMode", "list.topics.length", "loading")
footerMessage(navMode, listTopicsLength, loading) {
if (loading) {

View File

@ -1,5 +1,5 @@
import Category from "discourse/models/category";
import Controller, { inject as controller } from "@ember/controller";
import Controller from "@ember/controller";
import DiscourseURL, { userPath } from "discourse/lib/url";
import { alias, and, not, or } from "@ember/object/computed";
import discourseComputed, {
@ -57,7 +57,6 @@ export function registerCustomPostMessageCallback(type, callback) {
export default Controller.extend(bufferedProperty("model"), {
composer: service(),
application: controller(),
dialog: service(),
documentTitle: service(),
screenTrack: service(),
@ -1806,12 +1805,4 @@ export default Controller.extend(bufferedProperty("model"), {
}
}
},
@observes("model.postStream.loaded", "model.postStream.loadedAllPosts")
_showFooter() {
const showFooter =
this.get("model.postStream.loaded") &&
this.get("model.postStream.loadedAllPosts");
this.set("application.showFooter", showFooter);
},
});

View File

@ -3,32 +3,15 @@ import I18n from "I18n";
import { alias } from "@ember/object/computed";
import { exportUserArchive } from "discourse/lib/export-csv";
import { inject as service } from "@ember/service";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import discourseComputed from "discourse-common/utils/decorators";
export default Controller.extend({
dialog: service(),
application: controller(),
user: controller(),
userActionType: null,
canDownloadPosts: alias("user.viewingSelf"),
@observes("userActionType", "model.stream.itemsLoaded")
_showFooter() {
let showFooter;
if (this.userActionType) {
const stat = (this.get("model.stats") || []).find(
(s) => s.action_type === this.userActionType
);
showFooter = stat && stat.count <= this.get("model.stream.itemsLoaded");
} else {
showFooter =
this.get("model.statsCountNonPM") <=
this.get("model.stream.itemsLoaded");
}
this.set("application.showFooter", showFooter);
},
@discourseComputed("currentUser.draft_count")
draftLabel(count) {
return count > 0

View File

@ -1,7 +1,7 @@
import Controller, { inject as controller } from "@ember/controller";
import Controller from "@ember/controller";
import getURL from "discourse-common/lib/get-url";
import { iconHTML } from "discourse-common/lib/icon-library";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax";
import I18n from "I18n";
import { htmlSafe } from "@ember/template";
@ -10,15 +10,9 @@ import DismissNotificationConfirmationModal from "discourse/components/modal/dis
export default Controller.extend({
modal: service(),
application: controller(),
queryParams: ["filter"],
filter: "all",
@observes("model.canLoadMore")
_showFooter() {
this.set("application.showFooter", !this.get("model.canLoadMore"));
},
@discourseComputed("filter")
isFiltered() {
return this.filter && this.filter !== "all";

View File

@ -1,11 +1,3 @@
import Controller, { inject as controller } from "@ember/controller";
import { observes } from "discourse-common/utils/decorators";
import Controller from "@ember/controller";
export default Controller.extend({
application: controller(),
@observes("model.canLoadMore")
_showFooter() {
this.set("application.showFooter", !this.get("model.canLoadMore"));
},
});
export default class UserPostsController extends Controller {}

View File

@ -1,5 +1,5 @@
import Controller, { inject as controller } from "@ember/controller";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import Controller from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators";
import { reads } from "@ember/object/computed";
import BulkTopicSelection from "discourse/mixins/bulk-topic-selection";
import { action } from "@ember/object";
@ -12,8 +12,6 @@ import {
// Lists of topics on a user's page.
export default Controller.extend(BulkTopicSelection, {
application: controller(),
hideCategory: false,
showPosters: false,
channel: null,
@ -25,11 +23,6 @@ export default Controller.extend(BulkTopicSelection, {
return topicsLength === 0 && incomingCount === 0;
},
@observes("model.canLoadMore")
_showFooter() {
this.set("application.showFooter", !this.get("model.canLoadMore"));
},
@discourseComputed("filter", "model.topics.length")
showResetNew(filter, hasTopics) {
return filter === NEW_FILTER && hasTopics;

View File

@ -1,14 +1,12 @@
import Controller, { inject as controller } from "@ember/controller";
import Controller from "@ember/controller";
import Group from "discourse/models/group";
import { action } from "@ember/object";
import discourseDebounce from "discourse-common/lib/debounce";
import showModal from "discourse/lib/show-modal";
import { and, equal } from "@ember/object/computed";
import { longDate } from "discourse/lib/formatter";
import { observes } from "discourse-common/utils/decorators";
export default Controller.extend({
application: controller(),
queryParams: [
"period",
"order",
@ -115,11 +113,6 @@ export default Controller.extend({
this.loadUsers();
},
@observes("model.canLoadMore")
_showFooter() {
this.set("application.showFooter", !this.get("model.canLoadMore"));
},
@action
loadMore() {
this.model.loadMore();

View File

@ -0,0 +1,13 @@
import Helper from "@ember/component/helper";
import { inject as service } from "@ember/service";
export default class HideApplicationFooter extends Helper {
@service footer;
constructor() {
super(...arguments);
this.footer.registerHider(this);
}
compute() {}
}

View File

@ -1,13 +0,0 @@
export default {
initialize(owner) {
const router = owner.lookup("router:main");
const application = owner.lookup("controller:application");
// only take care of hiding the footer here
// controllers MUST take care of displaying it
router.on("routeWillChange", () => {
application.set("showFooter", false);
return true;
});
},
};

View File

@ -50,11 +50,30 @@ export default RestModel.extend({
return this.findItems();
},
@discourseComputed("baseUrl", "filterParam", "actingUsername")
nextFindUrl() {
let findUrl = this.baseUrl;
if (this.filterParam) {
findUrl += `&filter=${this.filterParam}`;
}
if (this.actingUsername) {
findUrl += `&acting_username=${this.actingUsername}`;
}
return findUrl;
},
@discourseComputed("loaded", "content.[]")
noContent(loaded, content) {
return loaded && content.length === 0;
},
@discourseComputed("nextFindUrl", "lastLoadedUrl")
canLoadMore() {
return this.nextFindUrl !== this.lastLoadedUrl;
},
remove(userAction) {
// 1) remove the user action from the child groups
this.content.forEach((ua) => {
@ -77,21 +96,13 @@ export default RestModel.extend({
},
findItems() {
let findUrl = this.baseUrl;
if (this.filterParam) {
findUrl += `&filter=${this.filterParam}`;
}
if (this.actingUsername) {
findUrl += `&acting_username=${this.actingUsername}`;
}
// Don't load the same stream twice. We're probably at the end.
const lastLoadedUrl = this.lastLoadedUrl;
if (lastLoadedUrl === findUrl) {
if (!this.canLoadMore) {
// Don't load the same stream twice. We're probably at the end.
return Promise.resolve();
}
const findUrl = this.nextFindUrl;
if (this.loading) {
return Promise.resolve();
}

View File

@ -1,7 +1,6 @@
import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
import { ajax } from "discourse/lib/ajax";
import { action } from "@ember/object";
export default DiscourseRoute.extend({
model() {
@ -37,10 +36,4 @@ export default DiscourseRoute.extend({
titleToken() {
return I18n.t("about.simple_title");
},
@action
didTransition() {
this.controllerFor("application").set("showFooter", true);
return true;
},
});

View File

@ -2,8 +2,6 @@ import Badge from "discourse/models/badge";
import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
import PreloadStore from "discourse/lib/preload-store";
import { scrollTop } from "discourse/mixins/scroll-top";
import { action } from "@ember/object";
export default DiscourseRoute.extend({
model() {
@ -19,11 +17,4 @@ export default DiscourseRoute.extend({
titleToken() {
return I18n.t("badges.title");
},
@action
didTransition() {
this.controllerFor("application").set("showFooter", true);
scrollTop();
return true;
},
});

View File

@ -2,9 +2,7 @@ import Badge from "discourse/models/badge";
import DiscourseRoute from "discourse/routes/discourse";
import PreloadStore from "discourse/lib/preload-store";
import UserBadge from "discourse/models/user-badge";
import { scrollTop } from "discourse/mixins/scroll-top";
import { hash } from "rsvp";
import { action } from "@ember/object";
export default DiscourseRoute.extend({
queryParams: {
@ -64,11 +62,4 @@ export default DiscourseRoute.extend({
controller.set("userBadges", this.userBadgesGrant);
controller.set("userBadgesAll", this.userBadgesAll);
},
@action
didTransition() {
this.controllerFor("badges/show")._showFooter();
scrollTop();
return true;
},
});

View File

@ -24,12 +24,6 @@ export default (inboxType, path, filter) => {
];
},
@action
didTransition() {
this.controllerFor("user-topics-list")._showFooter();
return true;
},
model() {
const topicListFilter =
"topics/" + path + "/" + this.modelFor("user").get("username_lower");

View File

@ -5,19 +5,10 @@ import { getOwner } from "discourse-common/lib/get-owner";
import deprecated from "discourse-common/lib/deprecated";
const DiscourseRoute = Route.extend({
showFooter: false,
willTransition() {
seenUser();
},
activate() {
this._super(...arguments);
if (this.showFooter) {
this.controllerFor("application").set("showFooter", true);
}
},
_refreshTitleOnce() {
this.send("_collectTitleTokens", []);
},

View File

@ -7,7 +7,6 @@ import TopicList from "discourse/models/topic-list";
import { ajax } from "discourse/lib/ajax";
import { defaultHomepage } from "discourse/lib/utilities";
import { hash } from "rsvp";
import { next } from "@ember/runloop";
import showModal from "discourse/lib/show-modal";
import Session from "discourse/models/session";
import { inject as service } from "@ember/service";
@ -153,10 +152,4 @@ export default class DiscoveryCategoriesRoute extends DiscourseRoute {
reorderCategories() {
showModal("reorder-categories");
}
@action
didTransition() {
next(() => this.controllerFor("application").set("showFooter", true));
return true;
}
}

View File

@ -1,14 +1,7 @@
import DiscourseRoute from "discourse/routes/discourse";
import { action } from "@ember/object";
export default DiscourseRoute.extend({
serialize() {
return "";
},
@action
didTransition() {
this.controllerFor("application").set("showFooter", true);
return true;
},
});

View File

@ -3,7 +3,6 @@ import { inject as service } from "@ember/service";
import DiscourseURL from "discourse/lib/url";
import StaticPage from "discourse/models/static-page";
import I18n from "I18n";
import { action } from "@ember/object";
export default class FaqRoute extends DiscourseRoute {
@service siteSettings;
@ -30,10 +29,4 @@ export default class FaqRoute extends DiscourseRoute {
titleToken() {
return I18n.t(this.pageId);
}
@action
didTransition() {
this.controllerFor("application").set("showFooter", true);
return true;
}
}

View File

@ -24,7 +24,6 @@ export function buildGroupPage(type) {
type,
canLoadMore: !loadedAll,
});
this.controllerFor("application").set("showFooter", loadedAll);
},
@action

View File

@ -2,8 +2,6 @@ import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
export default DiscourseRoute.extend({
showFooter: true,
titleToken() {
return I18n.t(`groups.topics`);
},

View File

@ -2,8 +2,6 @@ import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
export default DiscourseRoute.extend({
showFooter: true,
titleToken() {
return I18n.t("groups.manage.categories.title");
},

View File

@ -4,7 +4,6 @@ import { inject as service } from "@ember/service";
export default DiscourseRoute.extend({
router: service(),
showFooter: true,
beforeModel() {
// cannot configure IMAP without SMTP being enabled

View File

@ -3,7 +3,6 @@ import { inject as service } from "@ember/service";
export default DiscourseRoute.extend({
router: service(),
showFooter: true,
beforeModel() {
this.router.transitionTo("group.manage.profile");

View File

@ -2,8 +2,6 @@ import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
export default DiscourseRoute.extend({
showFooter: true,
titleToken() {
return I18n.t("groups.manage.interaction.title");
},

View File

@ -2,8 +2,6 @@ import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
export default DiscourseRoute.extend({
showFooter: true,
titleToken() {
return I18n.t("groups.manage.membership.title");
},

View File

@ -2,8 +2,6 @@ import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
export default DiscourseRoute.extend({
showFooter: true,
titleToken() {
return I18n.t("groups.manage.profile.title");
},

View File

@ -2,8 +2,6 @@ import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
export default DiscourseRoute.extend({
showFooter: true,
titleToken() {
return I18n.t("groups.manage.tags.title");
},

View File

@ -4,7 +4,6 @@ import { inject as service } from "@ember/service";
export default DiscourseRoute.extend({
router: service(),
showFooter: true,
titleToken() {
return I18n.t("groups.manage.title");

View File

@ -6,7 +6,6 @@ import { inject as service } from "@ember/service";
export default DiscourseRoute.extend({
router: service(),
showFooter: true,
titleToken() {
return I18n.t("groups.permissions.title");

View File

@ -5,7 +5,6 @@ import { inject as service } from "@ember/service";
export default DiscourseRoute.extend({
router: service(),
showFooter: true,
titleToken() {
return I18n.t("admin.groups.new.title");

View File

@ -5,8 +5,6 @@ import { action } from "@ember/object";
import I18n from "I18n";
export default RestrictedUserRoute.extend({
showFooter: true,
model() {
const user = this.modelFor("user");
if (this.siteSettings.enable_badges) {

View File

@ -1,5 +1,3 @@
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
showFooter: true,
});
export default RestrictedUserRoute.extend({});

View File

@ -1,5 +1,3 @@
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
showFooter: true,
});
export default RestrictedUserRoute.extend({});

View File

@ -1,8 +1,6 @@
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
showFooter: true,
model() {
return this.modelFor("user");
},

View File

@ -1,5 +1,3 @@
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
showFooter: true,
});
export default RestrictedUserRoute.extend({});

View File

@ -3,7 +3,6 @@ import { inject as service } from "@ember/service";
export default RestrictedUserRoute.extend({
router: service(),
showFooter: true,
redirect() {
this.router.transitionTo("preferences.account");

View File

@ -2,8 +2,6 @@ import RestrictedUserRoute from "discourse/routes/restricted-user";
import { currentThemeId } from "discourse/lib/theme-selector";
export default RestrictedUserRoute.extend({
showFooter: true,
setupController(controller, user) {
controller.setProperties({
model: user,

View File

@ -2,8 +2,6 @@ import RestrictedUserRoute from "discourse/routes/restricted-user";
import Category from "discourse/models/category";
export default RestrictedUserRoute.extend({
showFooter: true,
setupController(controller, user) {
const props = {
model: user,

View File

@ -1,5 +1,3 @@
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
showFooter: true,
});
export default RestrictedUserRoute.extend({});

View File

@ -1,7 +1,6 @@
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
showFooter: true,
setupController(controller, model) {
controller.set("model", model);
},

View File

@ -6,8 +6,6 @@ export default RestrictedUserRoute.extend({
currentUser: service(),
siteSettings: service(),
showFooter: true,
model() {
return this.modelFor("user");
},

View File

@ -1,5 +1,3 @@
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
showFooter: true,
});
export default RestrictedUserRoute.extend({});

View File

@ -1,5 +1,3 @@
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
showFooter: true,
});
export default RestrictedUserRoute.extend({});

View File

@ -1,5 +1,3 @@
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
showFooter: true,
});
export default RestrictedUserRoute.extend({});

View File

@ -1,5 +1,3 @@
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
showFooter: true,
});
export default RestrictedUserRoute.extend({});

View File

@ -3,7 +3,6 @@ import { inject as service } from "@ember/service";
import DiscourseURL from "discourse/lib/url";
import StaticPage from "discourse/models/static-page";
import I18n from "I18n";
import { action } from "@ember/object";
export default class PrivacyRoute extends DiscourseRoute {
@service siteSettings;
@ -27,10 +26,4 @@ export default class PrivacyRoute extends DiscourseRoute {
titleToken() {
return I18n.t("privacy");
}
@action
didTransition() {
this.controllerFor("application").set("showFooter", true);
return true;
}
}

View File

@ -1,8 +1,6 @@
import DiscourseRoute from "discourse/routes/discourse";
export default DiscourseRoute.extend({
showFooter: true,
model(params) {
return this.store.find("tagGroup", params.id);
},

View File

@ -4,7 +4,6 @@ import { inject as service } from "@ember/service";
export default DiscourseRoute.extend({
router: service(),
showFooter: true,
beforeModel() {
if (!this.siteSettings.tagging_enabled) {

View File

@ -2,8 +2,6 @@ import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
export default DiscourseRoute.extend({
showFooter: true,
model() {
return this.store.findAll("tagGroup");
},

View File

@ -260,12 +260,6 @@ export default DiscourseRoute.extend({
resetParams.call(this, skipParams);
},
@action
didTransition() {
this.controllerFor("tag.show")._showFooter();
return true;
},
_controllerTags(controller) {
return [controller.get("model.id"), ...makeArray(controller.additionalTags)]
.filter(Boolean)

View File

@ -38,12 +38,6 @@ export default DiscourseRoute.extend({
});
},
@action
didTransition() {
this.controllerFor("application").set("showFooter", true);
return true;
},
@action
showTagGroups() {
this.router.transitionTo("tagGroups");

View File

@ -248,7 +248,6 @@ const TopicRoute = DiscourseRoute.extend({
@action
didTransition() {
const controller = this.controllerFor("topic");
controller._showFooter();
const topicId = controller.get("model.id");
setTopicId(topicId);
return true;

View File

@ -3,7 +3,6 @@ import { inject as service } from "@ember/service";
import DiscourseURL from "discourse/lib/url";
import StaticPage from "discourse/models/static-page";
import I18n from "I18n";
import { action } from "@ember/object";
export default class TosRoute extends DiscourseRoute {
@service siteSettings;
@ -27,10 +26,4 @@ export default class TosRoute extends DiscourseRoute {
titleToken() {
return I18n.t("tos");
}
@action
didTransition() {
this.controllerFor("application").set("showFooter", true);
return true;
}
}

View File

@ -52,12 +52,6 @@ export default DiscourseRoute.extend({
return I18n.t("user_action_groups.3");
},
@action
didTransition() {
this.controllerFor("user-activity")._showFooter();
return true;
},
@action
triggerRefresh() {
this.refresh();

View File

@ -1,6 +1,5 @@
import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
import { action } from "@ember/object";
export default DiscourseRoute.extend({
templateName: "user/stream",
@ -35,10 +34,4 @@ export default DiscourseRoute.extend({
titleToken() {
return I18n.t("user_action_groups.15");
},
@action
didTransition() {
this.controllerFor("user-activity")._showFooter();
return true;
},
});

View File

@ -2,7 +2,6 @@ import UserAction from "discourse/models/user-action";
import UserActivityStreamRoute from "discourse/routes/user-activity-stream";
import { iconHTML } from "discourse-common/lib/icon-library";
import I18n from "I18n";
import { action } from "@ember/object";
import { htmlSafe } from "@ember/template";
export default UserActivityStreamRoute.extend({
@ -28,10 +27,4 @@ export default UserActivityStreamRoute.extend({
titleToken() {
return I18n.t("user_action_groups.1");
},
@action
didTransition() {
this.controllerFor("application").set("showFooter", true);
return true;
},
});

View File

@ -1,7 +1,6 @@
import UserAction from "discourse/models/user-action";
import UserActivityStreamRoute from "discourse/routes/user-activity-stream";
import I18n from "I18n";
import { action } from "@ember/object";
import { htmlSafe } from "@ember/template";
import getURL from "discourse-common/lib/get-url";
@ -32,10 +31,4 @@ export default UserActivityStreamRoute.extend({
titleToken() {
return I18n.t("user_action_groups.5");
},
@action
didTransition() {
this.controllerFor("application").set("showFooter", true);
return true;
},
});

View File

@ -1,6 +1,5 @@
import DiscourseRoute from "discourse/routes/discourse";
import ViewingActionType from "discourse/mixins/viewing-action-type";
import { action } from "@ember/object";
import I18n from "I18n";
export default DiscourseRoute.extend(ViewingActionType, {
@ -37,10 +36,4 @@ export default DiscourseRoute.extend(ViewingActionType, {
const body = "";
return { title, body };
},
@action
didTransition() {
this.controllerFor("user-activity")._showFooter();
return true;
},
});

View File

@ -1,7 +1,6 @@
import DiscourseRoute from "discourse/routes/discourse";
import UserBadge from "discourse/models/user-badge";
import ViewingActionType from "discourse/mixins/viewing-action-type";
import { action } from "@ember/object";
import I18n from "I18n";
export default DiscourseRoute.extend(ViewingActionType, {
@ -22,10 +21,4 @@ export default DiscourseRoute.extend(ViewingActionType, {
titleToken() {
return I18n.t("badges.title");
},
@action
didTransition() {
this.controllerFor("application").set("showFooter", true);
return true;
},
});

View File

@ -1,7 +1,6 @@
import DiscourseRoute from "discourse/routes/discourse";
import { emojiUnescape } from "discourse/lib/text";
import { escapeExpression } from "discourse/lib/utilities";
import { action } from "@ember/object";
export default class UserDeletedPosts extends DiscourseRoute {
templateName = "user/posts";
@ -26,10 +25,4 @@ export default class UserDeletedPosts extends DiscourseRoute {
}
});
}
@action
didTransition() {
this.controller._showFooter();
return true;
}
}

View File

@ -1,18 +1,11 @@
import DiscourseRoute from "discourse/routes/discourse";
import ViewingActionType from "discourse/mixins/viewing-action-type";
import { action } from "@ember/object";
import I18n from "I18n";
export default DiscourseRoute.extend(ViewingActionType, {
controllerName: "user-notifications",
queryParams: { filter: { refreshModel: true } },
@action
didTransition() {
this.controllerFor("user-notifications")._showFooter();
return true;
},
model(params) {
const username = this.modelFor("user").get("username");

View File

@ -2,8 +2,6 @@ import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
export default DiscourseRoute.extend({
showFooter: true,
model() {
const user = this.modelFor("user");
if (user.get("profile_hidden")) {

View File

@ -3,7 +3,6 @@ import I18n from "I18n";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { Promise } from "rsvp";
import { action } from "@ember/object";
export default DiscourseRoute.extend({
queryParams: {
@ -60,10 +59,4 @@ export default DiscourseRoute.extend({
controller.loadUsers(model.params),
]);
},
@action
didTransition() {
this.controllerFor("users")._showFooter();
return true;
},
});

View File

@ -0,0 +1,28 @@
import Service from "@ember/service";
import { disableImplicitInjections } from "discourse/lib/implicit-injections";
import { tracked } from "@glimmer/tracking";
import { registerDestructor } from "@ember/destroyable";
import { TrackedSet } from "@ember-compat/tracked-built-ins";
@disableImplicitInjections
export default class FooterService extends Service {
#hiders = new TrackedSet();
@tracked _showFooterOverride = null;
get showFooter() {
return this._showFooterOverride ?? this.#hiders.size === 0;
}
set showFooter(value) {
if (value === true) {
this._showFooterOverride = null;
} else {
this._showFooterOverride = value;
}
}
registerHider(destroyable) {
this.#hiders.add(destroyable);
registerDestructor(destroyable, () => this.#hiders.delete(destroyable));
}
}

View File

@ -1,3 +1,7 @@
{{#if this.canLoadMore}}
{{hide-application-footer}}
{{/if}}
<div class="container show-badge {{this.model.slug}}">
<h1>
<LinkTo @route="badges.index">{{i18n "badges.title"}}</LinkTo>

View File

@ -24,6 +24,10 @@
<ConditionalLoadingSpinner @condition={{this.showLoadingSpinner}} />
{{#if this.loading}}
{{hide-application-footer}}
{{/if}}
<span>
<PluginOutlet @name="discovery-above" @connectorTagName="div" />
</span>

View File

@ -1,3 +1,7 @@
{{#if (or this.loading this.model.canLoadMore)}}
{{hide-application-footer}}
{{/if}}
{{#if this.redirectedReason}}
<div class="alert alert-info">{{this.redirectedReason}}</div>
{{/if}}

View File

@ -1,3 +1,7 @@
{{#if this.loading}}
{{hide-application-footer}}
{{/if}}
<DSection @pageClass="search" @class="search-container">
<ScrollTracker
@name="full-page-search"

View File

@ -1,3 +1,7 @@
{{#if this.canLoadMore}}
{{hide-application-footer}}
{{/if}}
<LoadMore @selector=".user-stream-item" @action={{action "loadMore"}}>
<div class="user-stream">
{{#each this.model as |post|}}

View File

@ -1,3 +1,7 @@
{{#if (or this.loading this.canLoadMore)}}
{{hide-application-footer}}
{{/if}}
<section class="user-content">
<div class="group-members-actions">

View File

@ -1,3 +1,7 @@
{{#if (or this.loading this.canLoadMore)}}
{{hide-application-footer}}
{{/if}}
<section class="user-content">
<div class="group-members-actions">

View File

@ -1,3 +1,7 @@
{{#unless this.model.all_loaded}}
{{hide-application-footer}}
{{/unless}}
{{#if this.model.logs}}
<div class="group-manage-logs-controls">
<GroupManageLogsFilter

View File

@ -1,3 +1,7 @@
{{#if (or this.loading this.groups.canLoadMore)}}
{{hide-application-footer}}
{{/if}}
<PluginOutlet @name="before-groups-index-container" @connectorTagName="div" />
<DSection @pageClass="groups">

View File

@ -1 +1,2 @@
{{loading-spinner}}
{{loading-spinner}}
{{hide-application-footer}}

View File

@ -1,3 +1,7 @@
{{#if (or this.loading this.model.canLoadMore)}}
{{hide-application-footer}}
{{/if}}
<TopicDismissButtons
@position="top"
@selectedTopics={{this.selected}}
@ -101,5 +105,7 @@
}}
{{/if}}
</FooterMessage>
{{else}}
{{hide-application-footer}}
{{/if}}
</footer>

View File

@ -1,3 +1,7 @@
{{#if this.list.canLoadMore}}
{{hide-application-footer}}
{{/if}}
<DSection
@tagName=""
@pageClass="tags"

View File

@ -1,3 +1,9 @@
{{#let this.model.postStream as |postStream|}}
{{#unless (and postStream.loaded postStream.loadedAllPosts)}}
{{hide-application-footer}}
{{/unless}}
{{/let}}
<DiscourseTopic
@multiSelect={{this.multiSelect}}
@enteredAt={{this.enteredAt}}

View File

@ -1,3 +1,7 @@
{{#if this.model.canLoadMore}}
{{hide-application-footer}}
{{/if}}
{{#if this.noContent}}
<EmptyState
@title={{this.model.emptyState.title}}

View File

@ -1,3 +1,7 @@
{{#if this.model.canLoadMore}}
{{hide-application-footer}}
{{/if}}
<DSection @pageClass="user-notifications" />
<div class="user-navigation user-navigation-secondary">

View File

@ -1 +1,5 @@
{{#if this.model.canLoadMore}}
{{hide-application-footer}}
{{/if}}
<UserStream @stream={{this.model}} />

View File

@ -1,3 +1,7 @@
{{#if (or this.loading this.model.stream.canLoadMore)}}
{{hide-application-footer}}
{{/if}}
{{#if this.model.stream.noContent}}
<EmptyState
@title={{this.model.emptyState.title}}

View File

@ -1,3 +1,7 @@
{{#if this.model.canLoadMore}}
{{hide-application-footer}}
{{/if}}
<DSection @pageClass="users">
<LoadMore
@selector=".directory-table .directory-table__cell"

View File

@ -14,7 +14,6 @@ export default Route.extend(DisableSidebar, {
this.controllerFor("application").setProperties({
showTop: false,
showFooter: false,
showSiteHeader: false,
});
},

View File

@ -1,3 +1,4 @@
{{hide-application-footer}}
<div id="wizard-main">
{{#if this.showCanvas}}
<WizardCanvas />