From a324c9156041cde6a94f0f3ff281827ffb5d8dfb Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Sat, 9 Nov 2024 15:57:28 -0500 Subject: [PATCH] dev: add quick window to see what groups a user is in I need this for development/debugging purposes. I figure I may as well start developing the system dialog we'll eventually have to create rather than make a temporary debugging element. This window could eventually become a full-featured group manager, and can easily be moved to a settings tab. --- src/gui/src/UI/UIElement.js | 17 ++++ src/gui/src/extensions/groups-manager.js | 105 +++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/gui/src/extensions/groups-manager.js diff --git a/src/gui/src/UI/UIElement.js b/src/gui/src/UI/UIElement.js index 5220622c3..ccd09c9a3 100644 --- a/src/gui/src/UI/UIElement.js +++ b/src/gui/src/UI/UIElement.js @@ -5,6 +5,21 @@ import UIWindow from "./UIWindow.js"; export default def(class UIElement extends AdvancedBase { static ID = 'ui.UIElement'; static TAG_NAME = 'div'; + + /** + * Default behavior of UIWindow with no options creates a + * transparent rectangle at the bottom of the window. These + * default options will be used to prevent that behavior. + */ + static DEFAULT_WINDOW_OPTIONS = { + height: 'auto', + body_css: { + width: 'initial', + 'background-color': 'rgb(245 247 249)', + 'backdrop-filter': 'blur(3px)', + padding: '20px', + }, + }; // === START :: Helpful convenience library === static el = (...a) => { @@ -73,9 +88,11 @@ export default def(class UIElement extends AdvancedBase { super(); this.windowOptions = { + ...(this.constructor.DEFAULT_WINDOW_OPTIONS ?? {}), ...(this.constructor.WINDOW_OPTIONS ?? {}), ...(windowOptions ?? {}), }; + this.tagName = tagName ?? this.constructor.TAG_NAME; this.css = css ?? this.constructor.CSS; this.values = { diff --git a/src/gui/src/extensions/groups-manager.js b/src/gui/src/extensions/groups-manager.js new file mode 100644 index 000000000..1c9439a5b --- /dev/null +++ b/src/gui/src/extensions/groups-manager.js @@ -0,0 +1,105 @@ +const UIElement = use('ui.UIElement'); +const Collector = use('util.Collector'); + +const el = UIElement.el; + +class UIGroupsManager extends UIElement { + static CSS = ` + .alpha-warning { + background-color: #f8d7da; + color: #721c24; + padding: 10px; + margin-bottom: 20px; + border: 1px solid #f5c6cb; + border-radius: 4px; + } + + .group { + display: flex; + align-items: center; + padding: 10px; + border: 1px solid #ccc; + border-radius: 4px; + margin-bottom: 10px; + } + + .group-name { + font-size: 18px; + font-weight: bold; + } + + .group-name::before { + content: '👥'; + margin-right: 10px; + } + `; + async make ({ root }) { + const experimental_ui_notice = el('div.alpha-warning', { + text: `This feature is under development.` + }); + root.appendChild(experimental_ui_notice); + + // TODO: we shouldn't have to construct this every time; + // maybe GUI itself can provide an instance of Collector + this.collector = new Collector({ + antiCSRF: window.services?.get?.('anti-csrf'), + origin: window.api_origin, + authToken: puter.authToken, + }); + + const groups = await this.collector.get('/group/list'); + const groups_el = el('div', groups.in_groups.map(group => { + let title, color = '#FFF'; + if ( group.metadata ) { + title = group.metadata.title; + color = group.metadata.color; + } + + if ( ! title ) { + title = group.uid; + } + + const group_el = el('div.group', [ + el('div.group-name', { + text: title, + }), + ]); + + if ( color ) { + group_el.style.backgroundColor = color; + } + + return group_el; + })); + root.appendChild(groups_el); + } +} + +$(window).on('ctxmenu-will-open', event => { + if ( ! event.detail.options?.id === 'user-options-menu' ) return; + + const newMenuItems = [ + { + id: 'groups-manager', + html: 'Groups Manager', + action: () => { + const groupsManager = new UIGroupsManager(); + groupsManager.open_as_window(); + } + } + ]; + + const items = event.detail.options.items; + + const insertBeforeIndex = 1 + + items.findIndex(item => item.id === 'task_manager'); + + if ( insertBeforeIndex === -1 ) { + event.detail.options.items = [...items, ...newMenuItems]; + return; + } + + const firstHalf = items.slice(0, insertBeforeIndex); + const secondHalf = items.slice(insertBeforeIndex); + event.detail.options.items = [...firstHalf, ...newMenuItems, ...secondHalf]; +});