Files
threshold-ui/ui/desktop/app/controllers/application.js
T
Lisbet Alvarez b1b49edb39 Refactor hds side nav to hds app side nav and header (#3032)
* refactor: 💡 use hds app header and app side nav

* style: 💄 fix dark-mode colors for side nav and app header

* style: 💄 cleanup

* style: 💄 simplify selector

* refactor: 💡 move util (param finder) to core addon

* refactor: 💡 move scope picker to core addon

* refactor: 💡 use hds header and app side nav in ui/desktop

* refactor: 💡 clean up ui/desktop files

* refactor: 💡 remove uneeded header components

* refactor: 💡 remove uneeded global layout

* Add missing copyright headers

* style: 💄 adjust foreground and background colors to fix contras

* style: 💄 remove uneeded stylelint disables

* style: 💄 fix dark mode styling in desktop ui

* refactor: 💡 readd user-select style

* test: 💍 fix tests + e2e tests for admin and desktop UIs

* refactor: 💡 clean up uneeded main elements and fix color link

* Add missing copyright headers

* fix: 🐛 e2e test selectors for ui/desktop

* style: 💄 revert link color change

* refactor: 💡 remove hds override

* fix: 🐛 scope dropdown for desktop

* fix: 🐛 max-height to desktop scope dropdown

* Add missing copyright headers

* chore: 🤖 update lockfile due to webpack update
2026-02-25 13:55:36 -08:00

186 lines
4.6 KiB
JavaScript

/**
* Copyright IBM Corp. 2021, 2026
* SPDX-License-Identifier: BUSL-1.1
*/
import Controller from '@ember/controller';
import { service } from '@ember/service';
import { getOwner } from '@ember/application';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { defaultValidator } from 'ember-a11y-refocus';
import { paramValueFinder } from 'core/utils/param-value-finder';
export default class ApplicationController extends Controller {
// =services
@service clusterUrl;
@service flashMessages;
@service ipc;
@service session;
@service('browser/window') window;
@service router;
@service scope;
@service intl;
// =attributes
@tracked isLoggingOut = false;
@tracked isAppQuitting = false;
removeOnAppQuitListener;
constructor() {
super(...arguments);
// Listen for when user attempts to quit app
// Setup removeOnAppQuitListener to destroy the listener afterwards
this.removeOnAppQuitListener = this.window.electron?.onAppQuit(() => {
this.isAppQuitting = true;
});
}
/**
* Returns display name and icon for current scope.
* @type {object}
*/
get currentScope() {
if (this.scope.org.isOrg) {
return { name: this.scope.org.displayName, icon: 'org' };
} else {
return { name: this.intl.t('titles.global'), icon: 'globe' };
}
}
/**
* Shows side navigation only for routes nested under a scope
* and if user has been authenticated.
* @type {boolean}
*/
get showSideNav() {
return (
this.router.currentRouteName.startsWith('scopes.scope') &&
this.session.isAuthenticated
);
}
// =actions
/**
* Stop all active/pending target sessions
* Logout or close app
*/
@action
async confirmCloseSessions() {
await this.ipc.invoke('stopAll');
if (this.isAppQuitting) {
this.isAppQuitting = false;
this.close();
} else {
// this.session.invalidate() comes from Ember Simple Auth BaseSessionService
this.session.invalidate();
}
this.isLoggingOut = false;
}
/**
* Only renders the signout modal if target sessions are running
*/
@action
async showModalOrLogout() {
const hasRunningSessions = await this.ipc.invoke('hasRunningSessions');
if (hasRunningSessions) {
this.isLoggingOut = true;
} else {
this.session.invalidate();
}
}
/**
* Disconnects from clusterUrl and invalidates session, thereby resetting
* the client and reloading to the onboarding clusterUrl screen.
*/
@action
disconnect() {
this.clusterUrl.resetClusterUrl();
this.session.invalidate();
}
@action
minimize() {
this.ipc.invoke('minimizeWindow');
}
@action
toggleFullScreen() {
this.ipc.invoke('toggleFullscreenWindow');
}
@action
close() {
this.ipc.invoke('closeWindow');
}
/**
* Applies the specified color theme to the root ember element.
* @param {string} theme - "light", "dark", or nullish (system default)
*/
@action
toggleTheme(theme) {
const rootElementSelector = getOwner(this).rootElement;
const rootEl = getOwner(this)
.lookup('service:-document')
.querySelector(rootElementSelector);
this.session.set('data.theme', theme);
switch (theme) {
case 'light':
rootEl.classList.add('rose-theme-light');
rootEl.classList.remove('rose-theme-dark');
break;
case 'dark':
rootEl.classList.add('rose-theme-dark');
rootEl.classList.remove('rose-theme-light');
break;
default:
rootEl.classList.remove('rose-theme-dark');
rootEl.classList.remove('rose-theme-light');
}
}
@action
cancel() {
this.isLoggingOut = false;
this.isAppQuitting = false;
}
willDestroy() {
super.willDestroy(...arguments);
this.removeOnAppQuitListener?.();
}
/**
* Add custom route change validation to prevent refocus when
* user is attempting to search, filter, or sort.
* @param {object} transition
* @returns {boolean}
*/
customRouteChangeValidator(transition) {
if (!transition.to || !transition.from) {
return true;
}
if (transition.to.name === transition.from.name) {
const toParams = paramValueFinder(
transition.to.localName,
transition.to.parent,
);
const fromParams = paramValueFinder(
transition.from.localName,
transition.from.parent,
);
// Return false to prevent refocus when routes have equivalent dynamic segments (params).
return JSON.stringify(toParams) !== JSON.stringify(fromParams);
}
return defaultValidator(transition);
}
}