mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Refactor and transform "Application update", "Preferences" and "Application menu" into Features (#6437)
* Move some code for application update to feature directory The rest of the code could not be moved yet because of work-in-progress refactorings for OCP compliance. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce helper to get a global override for a function-injectable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract global override-files from bloated getDiForUnitTesting Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Move some more code for application update to feature directory Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce competition for top bar items to achieve OCP-compliance Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for opening context menu using the new competition Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for navigating to home as OCP Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for navigating to back as OCP Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for navigating to forward as OCP Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for application update as OCP Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Format code to make ongoing refactoring a bit easier Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Add missing unit tests for top bar extendability using extension API This makes ongoing refactoring easier. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Replace implementation for old top-bar items for losing competition Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract top menu item for window controls as OCP Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce reusable component for spacing between other components Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce helper component to render list of React elements Features: - Placeholder for empty list - Separators between items - No boilerplate for "key" prop in React Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix double-clicking and dragging of window from top bar Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update snapshots Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix stuff broken in rebase Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Update snapshots after rebase Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make fake time have a default value for "now" Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Migrate some application menu items to injectables Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate separators of application menu in single file Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove duplication from separators in application menu Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract some operation system actions from application menu as injectables Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract menu item for quitting application as injectable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce way to type narrow a string property Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make the di's for unit testing able to auto-register also named exports Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make global override less strict to simplify setup of many unit tests There's a better solution for this in the horizon, as this overridden thing is better faked than stubbed. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify a test Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce way to create hierarchical composites from a flat array Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Implement hierarchy of application menu items using "many-root" composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Migrate more application menu items to injectables Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Simplify hierarchy of application menu items using "single-root" composite Also solve composed typing of application menu by using Discriminated Unions of TypeScript, see: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update snapshot Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify creation of composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make composite able to have custom handler for missing parent ids This will be useful next for application menu items, where a missing parent id cannot be fatal. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Defend against self-referencing composites Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Reintroduce non-fatal handling of orphan application menu items Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make faked, yet weak, typing a bit stronger Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify getting of composite paths Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make registrator for application menu items support all known scenarios Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Add logging for unrecognizable application menu item types Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Rename id of menu item to keep it discoverable by existing extensions Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate code to check for updates closer to feature Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce reusable horizontal line Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for preferences as a Feature Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for terminal preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for editor preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for proxy preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for telemetry preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for application preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move code related to helm chart preferences under related Feature in preparation for competition Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for kubernetes preference tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for application preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for editor preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for kubernetes preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for proxy preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add missing observer to make sure component updates Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for telemetry preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for terminal preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak UI for preferences Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove dead code Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak more UI of preferences Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Revert "Introduce reusable horizontal line" This reverts commit 4d8c147fe0f1a14bd884f73cf345e7d3a28b954a. Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove dead code Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce utility to find exactly one item from array Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for preferences navigation Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move code under a Feature Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove usage of old code Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove dead code Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add extensions tab group to preferences Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce way to find out if composite has a descendant This will serve eg. hiding of empty preference tab groups. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make tab groups and tabs in preferences not render when there is no content Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove code made redundant with hiding of preference tabs without content Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Tweak UI for preference navigation tab groups Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce test helper to abstract discovery of HTML elements Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Kill dead code Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate discovery of HTML elements in some tests Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update snapshots Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt application builder to changes in preference navigation Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt test setup to changes in preference navigation Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove uninteresting technical tests that are covered by behavioural ones Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate discovery of HTML elements in a test Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove test ID made redundant by consolidating discovery of HTML elements Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove duplication from preference pages Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix import Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make queries in element discovery return matching attribute values for easier testing Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make element discovery able to do nested discovery Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make element discovery able to discover without value for attribute Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Implement registrator for preference items Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update snapshots Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make styling less brittle by not relying on static HTML-element structures with CSS-rules Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Add todo Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove "group" from preference types, as it is exact replica of "item" Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Update snapshots Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Replace usages of react-component factory with actual components for simplicity Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Start considering application preferences as default tab Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate naming Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make sense in horizontal lines Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Clean public interface of a normalize composite Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate name of function Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate directory structure of composite Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move utility functions to common Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Rename a preference item type to better communicate intent in UI Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move shared UI component to more common place Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate sizing of horizontal line to t-shirts Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move application update related preferences under application update feature Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make winner of competition to use original route for preferences Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make HTML element discovery require less parameters Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract "composable-responsibilities" for Discriminable, Labelable, Orderable, and Showable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Update snapshot Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove dead code Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move code under a feature Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate navigating to preferences Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move code under a feature Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Relocate code under a sub-feature Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add TODO Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove dead code Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix merge conflicts Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update snapshots after rebase Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix import path after rebase Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove duplication from exhaustiveness checks for discriminating unions Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add TODO Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update link to a more recent article Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify static Showability of a PreferenceItem Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Move general function to general directory Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract responsibility of "separability" Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make separator in Map-component know left and right item Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove additional separators when separated items are not shown for having no content Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Update snapshots Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix lint error Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt integration test to recent changes Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Adapt more integration tests to recent changes Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make composite not care about in formatting of ids Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Adapt application builder and tests to array-like paths over string-like paths Array-like paths do not have weakness for special characters as part of id, such as ".". Also note: the error messaging for clicking of application menu in application builder is a bit worse now I think, but the simplification of the test code is worth it in this case IMHO. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make composite not care about formatting of ids Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Adapt application builder and tests to array-like paths over string-like paths Array-like paths do not have weakness for special characters as part of id, such as ".". Also note: the error messaging for clicking of application menu in application builder is a bit worse now I think, but the simplification of the test code is worth it in this case IMHO. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate output of get-composite-path to match find-composite's input Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make global overrides for functions log args of the call for devability Also make the thrown error suggest how to fix the problem. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make attempts to log error throw in unit tests Errors cannot be allowed to happen without a unit test explicitly causing it. Errors cannot be allowed to happen without author of unit test knowing it. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make composite not know about how children are transformed Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove some duplication from tests of composite Also make the thrown error suggest how to fix the problem. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make composite unit test an unrealistic test scenario about undefined ids Also make the thrown error suggest how to fix the problem. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Kill dead code Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify unit tests for composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate tests to now point-free composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify "Showable" Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make non-shown application menu items not break composite structure This was made apparent by adding related unit tests for all known environments. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify usages of Orderable and Showable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Extract being maybe Showable as explicit composable responsibility Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make more showables maybe showable Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate logic for application menu for Windows to be based on composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Start using named export for composite Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Rename type for accuracy Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove unnecessary type and value Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make composable responsibilities readonly to nudge towards immutability Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Remove a bit of duplication to create TS-constants Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make a comment and test name make more sense Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify fallthrough in a switch/case Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Replace inline styles with proper CSS Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Switch to correct type to indicate "object which might not contain a property" Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify overriding of platform in a unit test Also make typing of platforms more strict, and remove some magic strings. Also add a TODO for further OCP-ification. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Consolidate some "maybe-types" and arguments using them Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Update snapshots Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
1 parent
38feab813c
commit
37eb236948
418 files changed
+39647
-27024
No files matched your search
-60
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import electronUpdaterInjectable from "../../electron-app/features/electron-updater.injectable";
|
||||
import type { UpdateChannel } from "../../../common/application-update/update-channels";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import type { UpdateCheckResult } from "electron-updater";
|
||||
|
||||
export type CheckForUpdatesResult = {
|
||||
updateWasDiscovered: false;
|
||||
} | {
|
||||
updateWasDiscovered: true;
|
||||
version: string;
|
||||
};
|
||||
|
||||
export type CheckForPlatformUpdates = (updateChannel: UpdateChannel, opts: { allowDowngrade: boolean }) => Promise<CheckForUpdatesResult>;
|
||||
|
||||
const checkForPlatformUpdatesInjectable = getInjectable({
|
||||
id: "check-for-platform-updates",
|
||||
|
||||
instantiate: (di): CheckForPlatformUpdates => {
|
||||
const electronUpdater = di.inject(electronUpdaterInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
return async (updateChannel, { allowDowngrade }) => {
|
||||
electronUpdater.channel = updateChannel.id;
|
||||
electronUpdater.autoDownload = false;
|
||||
electronUpdater.allowDowngrade = allowDowngrade;
|
||||
|
||||
let result: UpdateCheckResult;
|
||||
|
||||
try {
|
||||
result = await electronUpdater.checkForUpdates();
|
||||
} catch (error) {
|
||||
logger.error("[UPDATE-APP/CHECK-FOR-UPDATES]", error);
|
||||
|
||||
return {
|
||||
updateWasDiscovered: false,
|
||||
};
|
||||
}
|
||||
|
||||
const { updateInfo, cancellationToken } = result;
|
||||
|
||||
if (!cancellationToken) {
|
||||
return {
|
||||
updateWasDiscovered: false,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
updateWasDiscovered: true,
|
||||
version: updateInfo.version,
|
||||
};
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default checkForPlatformUpdatesInjectable;
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import electronUpdaterInjectable from "../../electron-app/features/electron-updater.injectable";
|
||||
import type { AsyncFnMock } from "@async-fn/jest";
|
||||
import asyncFn from "@async-fn/jest";
|
||||
import type { AppUpdater, UpdateCheckResult } from "electron-updater";
|
||||
import type { CheckForPlatformUpdates } from "./check-for-platform-updates.injectable";
|
||||
import checkForPlatformUpdatesInjectable from "./check-for-platform-updates.injectable";
|
||||
import type { UpdateChannel, ReleaseChannel } from "../../../common/application-update/update-channels";
|
||||
import { getPromiseStatus } from "../../../common/test-utils/get-promise-status";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import type { Logger } from "../../../common/logger";
|
||||
|
||||
describe("check-for-platform-updates", () => {
|
||||
let checkForPlatformUpdates: CheckForPlatformUpdates;
|
||||
let electronUpdaterFake: AppUpdater;
|
||||
let checkForUpdatesMock: AsyncFnMock<() => UpdateCheckResult>;
|
||||
let logErrorMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting();
|
||||
|
||||
checkForUpdatesMock = asyncFn();
|
||||
|
||||
electronUpdaterFake = {
|
||||
channel: undefined,
|
||||
autoDownload: undefined,
|
||||
allowDowngrade: undefined,
|
||||
|
||||
checkForUpdates: checkForUpdatesMock,
|
||||
} as unknown as AppUpdater;
|
||||
|
||||
di.override(electronUpdaterInjectable, () => electronUpdaterFake);
|
||||
|
||||
logErrorMock = jest.fn();
|
||||
|
||||
di.override(loggerInjectable, () => ({ error: logErrorMock }) as unknown as Logger);
|
||||
|
||||
checkForPlatformUpdates = di.inject(checkForPlatformUpdatesInjectable);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let actualPromise: Promise<any>;
|
||||
|
||||
beforeEach(() => {
|
||||
const testUpdateChannel: UpdateChannel = {
|
||||
id: "some-update-channel" as ReleaseChannel,
|
||||
label: "Some update channel",
|
||||
moreStableUpdateChannel: null,
|
||||
};
|
||||
|
||||
actualPromise = checkForPlatformUpdates(testUpdateChannel, { allowDowngrade: true });
|
||||
});
|
||||
|
||||
it("sets update channel", () => {
|
||||
expect(electronUpdaterFake.channel).toBe("some-update-channel");
|
||||
});
|
||||
|
||||
it("sets flag for allowing downgrade", () => {
|
||||
expect(electronUpdaterFake.allowDowngrade).toBe(true);
|
||||
});
|
||||
|
||||
it("disables auto downloading for being controlled", () => {
|
||||
expect(electronUpdaterFake.autoDownload).toBe(false);
|
||||
});
|
||||
|
||||
it("checks for updates", () => {
|
||||
expect(checkForUpdatesMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not resolve yet", async () => {
|
||||
const promiseStatus = await getPromiseStatus(actualPromise);
|
||||
|
||||
expect(promiseStatus.fulfilled).toBe(false);
|
||||
});
|
||||
|
||||
it("when checking for updates resolves with update, resolves with the discovered update", async () => {
|
||||
await checkForUpdatesMock.resolve({
|
||||
updateInfo: {
|
||||
version: "some-version",
|
||||
},
|
||||
|
||||
cancellationToken: "some-cancellation-token",
|
||||
} as unknown as UpdateCheckResult);
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual({ updateWasDiscovered: true, version: "some-version" });
|
||||
});
|
||||
|
||||
it("when checking for updates resolves without update, resolves with update not being discovered", async () => {
|
||||
await checkForUpdatesMock.resolve({
|
||||
updateInfo: {
|
||||
version: "some-version-that-matches-to-current-installed-version",
|
||||
},
|
||||
|
||||
cancellationToken: null,
|
||||
} as unknown as UpdateCheckResult);
|
||||
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual({ updateWasDiscovered: false });
|
||||
});
|
||||
|
||||
describe("when checking for updates rejects", () => {
|
||||
let errorStub: Error;
|
||||
|
||||
beforeEach(() => {
|
||||
errorStub = new Error("Some error");
|
||||
|
||||
checkForUpdatesMock.reject(errorStub);
|
||||
});
|
||||
|
||||
it("logs errors", () => {
|
||||
expect(logErrorMock).toHaveBeenCalledWith("[UPDATE-APP/CHECK-FOR-UPDATES]", errorStub);
|
||||
});
|
||||
|
||||
it("resolves with update not being discovered", async () => {
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual({ updateWasDiscovered: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import updatingIsEnabledInjectable from "./updating-is-enabled.injectable";
|
||||
import { trayMenuItemInjectionToken } from "../tray/tray-menu-item/tray-menu-item-injection-token";
|
||||
import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.injectable";
|
||||
import discoveredUpdateVersionInjectable from "../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
||||
import updateIsBeingDownloadedInjectable from "../../common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable";
|
||||
import updatesAreBeingDiscoveredInjectable from "../../common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable";
|
||||
import progressOfUpdateDownloadInjectable from "../../common/application-update/progress-of-update-download/progress-of-update-download.injectable";
|
||||
import assert from "assert";
|
||||
import processCheckingForUpdatesInjectable from "./check-for-updates/process-checking-for-updates.injectable";
|
||||
import { withErrorSuppression } from "../../common/utils/with-error-suppression/with-error-suppression";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import withErrorLoggingInjectable from "../../common/utils/with-error-logging/with-error-logging.injectable";
|
||||
|
||||
const checkForUpdatesTrayItemInjectable = getInjectable({
|
||||
id: "check-for-updates-tray-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
||||
const updatingIsEnabled = di.inject(updatingIsEnabledInjectable);
|
||||
const progressOfUpdateDownload = di.inject(progressOfUpdateDownloadInjectable);
|
||||
const discoveredVersionState = di.inject(discoveredUpdateVersionInjectable);
|
||||
const downloadingUpdateState = di.inject(updateIsBeingDownloadedInjectable);
|
||||
const checkingForUpdatesState = di.inject(updatesAreBeingDiscoveredInjectable);
|
||||
const processCheckingForUpdates = di.inject(processCheckingForUpdatesInjectable);
|
||||
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
|
||||
|
||||
return {
|
||||
id: "check-for-updates",
|
||||
parentId: null,
|
||||
orderNumber: 30,
|
||||
|
||||
label: computed(() => {
|
||||
if (downloadingUpdateState.value.get()) {
|
||||
const discoveredVersion = discoveredVersionState.value.get();
|
||||
|
||||
assert(discoveredVersion);
|
||||
|
||||
const roundedPercentage = Math.round(progressOfUpdateDownload.value.get().percentage);
|
||||
|
||||
return `Downloading update ${discoveredVersion.version} (${roundedPercentage}%)...`;
|
||||
}
|
||||
|
||||
if (checkingForUpdatesState.value.get()) {
|
||||
return "Checking for updates...";
|
||||
}
|
||||
|
||||
return "Check for updates";
|
||||
}),
|
||||
|
||||
enabled: computed(() => !checkingForUpdatesState.value.get() && !downloadingUpdateState.value.get()),
|
||||
|
||||
visible: computed(() => updatingIsEnabled),
|
||||
|
||||
click: pipeline(
|
||||
async () => {
|
||||
const { updateIsReadyToBeInstalled } = await processCheckingForUpdates("tray");
|
||||
|
||||
if (updateIsReadyToBeInstalled) {
|
||||
await showApplicationWindow();
|
||||
}
|
||||
},
|
||||
|
||||
withErrorLoggingFor(() => "[TRAY]: Checking for updates failed."),
|
||||
|
||||
// TODO: Find out how to improve typing so that instead of
|
||||
// x => withErrorSuppression(x) there could only be withErrorSuppression
|
||||
(x) => withErrorSuppression(x),
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: trayMenuItemInjectionToken,
|
||||
});
|
||||
|
||||
export default checkForUpdatesTrayItemInjectable;
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { UpdateChannel } from "../../../common/application-update/update-channels";
|
||||
import checkForPlatformUpdatesInjectable from "../check-for-platform-updates/check-for-platform-updates.injectable";
|
||||
import updateCanBeDowngradedInjectable from "./update-can-be-downgraded.injectable";
|
||||
|
||||
export type CheckForUpdatesFromChannelResult = {
|
||||
updateWasDiscovered: false;
|
||||
} | {
|
||||
updateWasDiscovered: true;
|
||||
version: string;
|
||||
actualUpdateChannel: UpdateChannel;
|
||||
};
|
||||
|
||||
const checkForUpdatesStartingFromChannelInjectable = getInjectable({
|
||||
id: "check-for-updates-starting-from-channel",
|
||||
|
||||
instantiate: (di) => {
|
||||
const checkForPlatformUpdates = di.inject(checkForPlatformUpdatesInjectable);
|
||||
const updateCanBeDowngraded = di.inject(updateCanBeDowngradedInjectable);
|
||||
|
||||
const _recursiveCheck = async (
|
||||
updateChannel: UpdateChannel,
|
||||
): Promise<CheckForUpdatesFromChannelResult> => {
|
||||
const result = await checkForPlatformUpdates(updateChannel, {
|
||||
allowDowngrade: updateCanBeDowngraded.get(),
|
||||
});
|
||||
|
||||
if (result.updateWasDiscovered) {
|
||||
return {
|
||||
updateWasDiscovered: true,
|
||||
version: result.version,
|
||||
actualUpdateChannel: updateChannel,
|
||||
};
|
||||
}
|
||||
|
||||
if (updateChannel.moreStableUpdateChannel) {
|
||||
return await _recursiveCheck(updateChannel.moreStableUpdateChannel);
|
||||
}
|
||||
|
||||
return { updateWasDiscovered: false };
|
||||
};
|
||||
|
||||
return _recursiveCheck;
|
||||
},
|
||||
});
|
||||
|
||||
export default checkForUpdatesStartingFromChannelInjectable;
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import selectedUpdateChannelInjectable from "../../../common/application-update/selected-update-channel/selected-update-channel.injectable";
|
||||
import updatesAreBeingDiscoveredInjectable from "../../../common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable";
|
||||
import discoveredUpdateVersionInjectable from "../../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
||||
import { runInAction } from "mobx";
|
||||
import downloadUpdateInjectable from "../download-update/download-update.injectable";
|
||||
import checkForUpdatesStartingFromChannelInjectable from "./check-for-updates-starting-from-channel.injectable";
|
||||
import emitEventInjectable from "../../../common/app-event-bus/emit-event.injectable";
|
||||
import { getCurrentDateTime } from "../../../common/utils/date/get-current-date-time";
|
||||
|
||||
const processCheckingForUpdatesInjectable = getInjectable({
|
||||
id: "process-checking-for-updates",
|
||||
|
||||
instantiate: (di) => {
|
||||
const downloadUpdate = di.inject(downloadUpdateInjectable);
|
||||
const selectedUpdateChannel = di.inject(selectedUpdateChannelInjectable);
|
||||
const checkingForUpdatesState = di.inject(updatesAreBeingDiscoveredInjectable);
|
||||
const discoveredVersionState = di.inject(discoveredUpdateVersionInjectable);
|
||||
const checkForUpdatesStartingFromChannel = di.inject(checkForUpdatesStartingFromChannelInjectable);
|
||||
const emitEvent = di.inject(emitEventInjectable);
|
||||
|
||||
return async (source: string) => {
|
||||
emitEvent({
|
||||
name: "app",
|
||||
action: "checking-for-updates",
|
||||
params: { currentDateTime: getCurrentDateTime(), source },
|
||||
});
|
||||
|
||||
runInAction(() => {
|
||||
checkingForUpdatesState.set(true);
|
||||
});
|
||||
|
||||
const result = await checkForUpdatesStartingFromChannel(selectedUpdateChannel.value.get());
|
||||
|
||||
if (!result.updateWasDiscovered) {
|
||||
runInAction(() => {
|
||||
discoveredVersionState.set(null);
|
||||
checkingForUpdatesState.set(false);
|
||||
});
|
||||
|
||||
return { updateIsReadyToBeInstalled: false };
|
||||
}
|
||||
|
||||
const { version, actualUpdateChannel } = result;
|
||||
|
||||
const previouslyDiscovered = discoveredVersionState.value.get();
|
||||
|
||||
if (version === previouslyDiscovered?.version) {
|
||||
runInAction(() => {
|
||||
checkingForUpdatesState.set(false);
|
||||
});
|
||||
|
||||
return { updateIsReadyToBeInstalled: true };
|
||||
}
|
||||
|
||||
emitEvent({
|
||||
name: "app",
|
||||
action: "update-was-discovered",
|
||||
params: { version, currentDateTime: getCurrentDateTime() },
|
||||
});
|
||||
|
||||
runInAction(() => {
|
||||
discoveredVersionState.set({
|
||||
version,
|
||||
updateChannel: actualUpdateChannel,
|
||||
});
|
||||
|
||||
checkingForUpdatesState.set(false);
|
||||
});
|
||||
|
||||
const { downloadWasSuccessful } = await downloadUpdate();
|
||||
|
||||
return { updateIsReadyToBeInstalled: downloadWasSuccessful };
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default processCheckingForUpdatesInjectable;
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import selectedUpdateChannelInjectable from "../../../common/application-update/selected-update-channel/selected-update-channel.injectable";
|
||||
import releaseChannelInjectable from "../../../common/vars/release-channel.injectable";
|
||||
|
||||
const updateCanBeDowngradedInjectable = getInjectable({
|
||||
id: "update-can-be-downgraded",
|
||||
|
||||
instantiate: (di) => {
|
||||
const selectedUpdateChannel = di.inject(selectedUpdateChannelInjectable);
|
||||
const releaseChannel = di.inject(releaseChannelInjectable);
|
||||
|
||||
return computed(() => (
|
||||
selectedUpdateChannel.value.get().id === "latest"
|
||||
&& releaseChannel.get() !== "latest"
|
||||
));
|
||||
},
|
||||
});
|
||||
|
||||
export default updateCanBeDowngradedInjectable;
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import electronUpdaterInjectable from "../../electron-app/features/electron-updater.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import type { ProgressInfo } from "electron-updater";
|
||||
import type { ProgressOfDownload } from "../../../common/application-update/progress-of-update-download/progress-of-update-download.injectable";
|
||||
|
||||
export type DownloadPlatformUpdate = (
|
||||
onDownloadProgress: (arg: ProgressOfDownload) => void
|
||||
) => Promise<{ downloadWasSuccessful: boolean }>;
|
||||
|
||||
const downloadPlatformUpdateInjectable = getInjectable({
|
||||
id: "download-platform-update",
|
||||
|
||||
instantiate: (di): DownloadPlatformUpdate => {
|
||||
const electronUpdater = di.inject(electronUpdaterInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
return async (onDownloadProgress) => {
|
||||
onDownloadProgress({ percentage: 0 });
|
||||
|
||||
const updateDownloadProgress = ({ percent: percentage }: ProgressInfo) =>
|
||||
onDownloadProgress({ percentage });
|
||||
|
||||
electronUpdater.on("download-progress", updateDownloadProgress);
|
||||
|
||||
try {
|
||||
await electronUpdater.downloadUpdate();
|
||||
} catch(error) {
|
||||
logger.error("[UPDATE-APP/DOWNLOAD]", error);
|
||||
|
||||
return { downloadWasSuccessful: false };
|
||||
} finally {
|
||||
electronUpdater.off("download-progress", updateDownloadProgress);
|
||||
}
|
||||
|
||||
return { downloadWasSuccessful: true };
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default downloadPlatformUpdateInjectable;
|
||||
-155
@@ -1,155 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import electronUpdaterInjectable from "../../electron-app/features/electron-updater.injectable";
|
||||
import type { DownloadPlatformUpdate } from "./download-platform-update.injectable";
|
||||
import downloadPlatformUpdateInjectable from "./download-platform-update.injectable";
|
||||
import type { AppUpdater } from "electron-updater";
|
||||
import type { AsyncFnMock } from "@async-fn/jest";
|
||||
import asyncFn from "@async-fn/jest";
|
||||
import { getPromiseStatus } from "../../../common/test-utils/get-promise-status";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import type { Logger } from "../../../common/logger";
|
||||
|
||||
describe("download-platform-update", () => {
|
||||
let downloadPlatformUpdate: DownloadPlatformUpdate;
|
||||
let downloadUpdateMock: AsyncFnMock<() => void>;
|
||||
let electronUpdaterFake: AppUpdater;
|
||||
let electronUpdaterOnMock: jest.Mock;
|
||||
let electronUpdaterOffMock: jest.Mock;
|
||||
let di: DiContainer;
|
||||
let logErrorMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting();
|
||||
|
||||
downloadUpdateMock = asyncFn();
|
||||
electronUpdaterOnMock = jest.fn();
|
||||
electronUpdaterOffMock = jest.fn();
|
||||
|
||||
electronUpdaterFake = {
|
||||
channel: undefined,
|
||||
autoDownload: undefined,
|
||||
|
||||
on: electronUpdaterOnMock,
|
||||
off: electronUpdaterOffMock,
|
||||
|
||||
downloadUpdate: downloadUpdateMock,
|
||||
} as unknown as AppUpdater;
|
||||
|
||||
di.override(electronUpdaterInjectable, () => electronUpdaterFake);
|
||||
|
||||
logErrorMock = jest.fn();
|
||||
di.override(loggerInjectable, () => ({ error: logErrorMock }) as unknown as Logger);
|
||||
|
||||
downloadPlatformUpdate = di.inject(downloadPlatformUpdateInjectable);
|
||||
});
|
||||
|
||||
describe("when called", () => {
|
||||
let actualPromise: Promise<{ downloadWasSuccessful: boolean }>;
|
||||
let onDownloadProgressMock: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
onDownloadProgressMock = jest.fn();
|
||||
|
||||
actualPromise = downloadPlatformUpdate(onDownloadProgressMock);
|
||||
});
|
||||
|
||||
it("calls for downloading of update", () => {
|
||||
expect(downloadUpdateMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not resolve yet", async () => {
|
||||
const promiseStatus = await getPromiseStatus(actualPromise);
|
||||
|
||||
expect(promiseStatus.fulfilled).toBe(false);
|
||||
});
|
||||
|
||||
it("starts progress of download from 0", () => {
|
||||
expect(onDownloadProgressMock).toHaveBeenCalledWith({ percentage: 0 });
|
||||
});
|
||||
|
||||
describe("when downloading progresses", () => {
|
||||
beforeEach(() => {
|
||||
onDownloadProgressMock.mockClear();
|
||||
|
||||
const [, callback] = electronUpdaterOnMock.mock.calls.find(
|
||||
([event]) => event === "download-progress",
|
||||
);
|
||||
|
||||
callback({
|
||||
percent: 42,
|
||||
total: 0,
|
||||
delta: 0,
|
||||
transferred: 0,
|
||||
bytesPerSecond: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("updates progress of the download", () => {
|
||||
expect(onDownloadProgressMock).toHaveBeenCalledWith({ percentage: 42 });
|
||||
});
|
||||
|
||||
describe("when downloading resolves", () => {
|
||||
beforeEach(async () => {
|
||||
onDownloadProgressMock.mockClear();
|
||||
|
||||
await downloadUpdateMock.resolve();
|
||||
});
|
||||
|
||||
it("resolves with success", async () => {
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual({ downloadWasSuccessful: true });
|
||||
});
|
||||
|
||||
it("does not reset progress of download yet", () => {
|
||||
expect(onDownloadProgressMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("stops watching for download progress", () => {
|
||||
expect(electronUpdaterOffMock).toHaveBeenCalledWith(
|
||||
"download-progress",
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("when starting download again, resets progress of download", () => {
|
||||
downloadPlatformUpdate(onDownloadProgressMock);
|
||||
|
||||
expect(onDownloadProgressMock).toHaveBeenCalledWith({ percentage: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
describe("when downloading rejects", () => {
|
||||
let errorStub: Error;
|
||||
|
||||
beforeEach(() => {
|
||||
errorStub = new Error("Some error");
|
||||
|
||||
downloadUpdateMock.reject(errorStub);
|
||||
});
|
||||
|
||||
it("logs error", () => {
|
||||
expect(logErrorMock).toHaveBeenCalledWith("[UPDATE-APP/DOWNLOAD]", errorStub);
|
||||
});
|
||||
|
||||
it("stops watching for download progress", () => {
|
||||
expect(electronUpdaterOffMock).toHaveBeenCalledWith(
|
||||
"download-progress",
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves with failure", async () => {
|
||||
const actual = await actualPromise;
|
||||
|
||||
expect(actual).toEqual({ downloadWasSuccessful: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import downloadPlatformUpdateInjectable from "../download-platform-update/download-platform-update.injectable";
|
||||
import updateIsBeingDownloadedInjectable from "../../../common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable";
|
||||
import discoveredUpdateVersionInjectable from "../../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
||||
import { action, runInAction } from "mobx";
|
||||
import type { ProgressOfDownload } from "../../../common/application-update/progress-of-update-download/progress-of-update-download.injectable";
|
||||
import progressOfUpdateDownloadInjectable from "../../../common/application-update/progress-of-update-download/progress-of-update-download.injectable";
|
||||
import { getCurrentDateTime } from "../../../common/utils/date/get-current-date-time";
|
||||
import updateDownloadedDateTimeInjectable from "../../../common/application-update/update-downloaded-date-time/update-downloaded-date-time.injectable";
|
||||
|
||||
const downloadUpdateInjectable = getInjectable({
|
||||
id: "download-update",
|
||||
|
||||
instantiate: (di) => {
|
||||
const downloadPlatformUpdate = di.inject(downloadPlatformUpdateInjectable);
|
||||
const downloadingUpdateState = di.inject(updateIsBeingDownloadedInjectable);
|
||||
const discoveredVersionState = di.inject(discoveredUpdateVersionInjectable);
|
||||
const progressOfUpdateDownload = di.inject(progressOfUpdateDownloadInjectable);
|
||||
const updateDownloadedDate = di.inject(updateDownloadedDateTimeInjectable);
|
||||
|
||||
const updateDownloadProgress = action((progressOfDownload: ProgressOfDownload) => {
|
||||
progressOfUpdateDownload.set(progressOfDownload);
|
||||
});
|
||||
|
||||
return async () => {
|
||||
runInAction(() => {
|
||||
progressOfUpdateDownload.set({ percentage: 0 });
|
||||
downloadingUpdateState.set(true);
|
||||
});
|
||||
|
||||
const { downloadWasSuccessful } = await downloadPlatformUpdate(
|
||||
updateDownloadProgress,
|
||||
);
|
||||
|
||||
runInAction(() => {
|
||||
if (!downloadWasSuccessful) {
|
||||
progressOfUpdateDownload.set({ percentage: 0, failed: "Download of update failed" });
|
||||
discoveredVersionState.set(null);
|
||||
}
|
||||
|
||||
const currentDateTime = getCurrentDateTime();
|
||||
|
||||
updateDownloadedDate.set(currentDateTime);
|
||||
|
||||
downloadingUpdateState.set(false);
|
||||
});
|
||||
|
||||
return { downloadWasSuccessful };
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default downloadUpdateInjectable;
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { afterApplicationIsLoadedInjectionToken } from "../start-main-application/runnable-tokens/after-application-is-loaded-injection-token";
|
||||
import emitEventInjectable from "../../common/app-event-bus/emit-event.injectable";
|
||||
import { getCurrentDateTime } from "../../common/utils/date/get-current-date-time";
|
||||
import buildVersionInjectable from "../vars/build-version/build-version.injectable";
|
||||
|
||||
const emitCurrentVersionToAnalyticsInjectable = getInjectable({
|
||||
id: "emit-current-version-to-analytics",
|
||||
|
||||
instantiate: (di) => {
|
||||
const emitEvent = di.inject(emitEventInjectable);
|
||||
const buildVersion = di.inject(buildVersionInjectable);
|
||||
|
||||
return {
|
||||
id: "emit-current-version-to-analytics",
|
||||
run: () => {
|
||||
emitEvent({
|
||||
name: "app",
|
||||
action: "current-version",
|
||||
|
||||
params: {
|
||||
version: buildVersion.get(),
|
||||
currentDateTime: getCurrentDateTime(),
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: afterApplicationIsLoadedInjectionToken,
|
||||
});
|
||||
|
||||
export default emitCurrentVersionToAnalyticsInjectable;
|
||||
@@ -1,54 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import { trayMenuItemInjectionToken } from "../tray/tray-menu-item/tray-menu-item-injection-token";
|
||||
import discoveredUpdateVersionInjectable from "../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
||||
import { withErrorSuppression } from "../../common/utils/with-error-suppression/with-error-suppression";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import withErrorLoggingInjectable from "../../common/utils/with-error-logging/with-error-logging.injectable";
|
||||
import quitAndInstallUpdateInjectable from "./quit-and-install-update.injectable";
|
||||
import updateIsReadyToBeInstalledInjectable from "./update-is-ready-to-be-installed.injectable";
|
||||
|
||||
const installApplicationUpdateTrayItemInjectable = getInjectable({
|
||||
id: "install-update-tray-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const quitAndInstallUpdate = di.inject(quitAndInstallUpdateInjectable);
|
||||
const discoveredVersionState = di.inject(discoveredUpdateVersionInjectable);
|
||||
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
|
||||
const updateIsReadyToBeInstalled = di.inject(updateIsReadyToBeInstalledInjectable);
|
||||
|
||||
return {
|
||||
id: "install-update",
|
||||
parentId: null,
|
||||
orderNumber: 50,
|
||||
|
||||
label: computed(() => {
|
||||
const versionToBeInstalled = discoveredVersionState.value.get()?.version;
|
||||
|
||||
return `Install update ${versionToBeInstalled}`;
|
||||
}),
|
||||
|
||||
enabled: computed(() => true),
|
||||
|
||||
visible: updateIsReadyToBeInstalled,
|
||||
|
||||
click: pipeline(
|
||||
quitAndInstallUpdate,
|
||||
|
||||
withErrorLoggingFor(() => "[TRAY]: Update installation failed."),
|
||||
|
||||
// TODO: Find out how to improve typing so that instead of
|
||||
// x => withErrorSuppression(x) there could only be withErrorSuppression
|
||||
(x) => withErrorSuppression(x),
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: trayMenuItemInjectionToken,
|
||||
});
|
||||
|
||||
export default installApplicationUpdateTrayItemInjectable;
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
|
||||
import processCheckingForUpdatesInjectable from "../check-for-updates/process-checking-for-updates.injectable";
|
||||
import withOrphanPromiseInjectable from "../../../common/utils/with-orphan-promise/with-orphan-promise.injectable";
|
||||
|
||||
const periodicalCheckForUpdatesInjectable = getInjectable({
|
||||
id: "periodical-check-for-updates",
|
||||
|
||||
instantiate: (di) => {
|
||||
const withOrphanPromise = di.inject(withOrphanPromiseInjectable);
|
||||
const processCheckingForUpdates = withOrphanPromise(di.inject(processCheckingForUpdatesInjectable));
|
||||
|
||||
return getStartableStoppable("periodical-check-for-updates", () => {
|
||||
const TWO_HOURS = 1000 * 60 * 60 * 2;
|
||||
|
||||
processCheckingForUpdates("periodic");
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
|
||||
processCheckingForUpdates("periodic");
|
||||
}, TWO_HOURS);
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
causesSideEffects: true,
|
||||
});
|
||||
|
||||
export default periodicalCheckForUpdatesInjectable;
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import periodicalCheckForUpdatesInjectable from "./periodical-check-for-updates.injectable";
|
||||
import updatingIsEnabledInjectable from "../updating-is-enabled.injectable";
|
||||
import { afterApplicationIsLoadedInjectionToken } from "../../start-main-application/runnable-tokens/after-application-is-loaded-injection-token";
|
||||
|
||||
const startCheckingForUpdatesInjectable = getInjectable({
|
||||
id: "start-checking-for-updates",
|
||||
|
||||
instantiate: (di) => {
|
||||
const periodicalCheckForUpdates = di.inject(periodicalCheckForUpdatesInjectable);
|
||||
const updatingIsEnabled = di.inject(updatingIsEnabledInjectable);
|
||||
|
||||
return {
|
||||
id: "start-checking-for-updates",
|
||||
run: async () => {
|
||||
if (updatingIsEnabled && !periodicalCheckForUpdates.started) {
|
||||
await periodicalCheckForUpdates.start();
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: afterApplicationIsLoadedInjectionToken,
|
||||
});
|
||||
|
||||
export default startCheckingForUpdatesInjectable;
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import periodicalCheckForUpdatesInjectable from "./periodical-check-for-updates.injectable";
|
||||
import { beforeQuitOfBackEndInjectionToken } from "../../start-main-application/runnable-tokens/before-quit-of-back-end-injection-token";
|
||||
|
||||
const stopCheckingForUpdatesInjectable = getInjectable({
|
||||
id: "stop-checking-for-updates",
|
||||
|
||||
instantiate: (di) => {
|
||||
const periodicalCheckForUpdates = di.inject(periodicalCheckForUpdatesInjectable);
|
||||
|
||||
return {
|
||||
id: "stop-checking-for-updates",
|
||||
run: async () => {
|
||||
if (periodicalCheckForUpdates.started) {
|
||||
await periodicalCheckForUpdates.stop();
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeQuitOfBackEndInjectionToken,
|
||||
});
|
||||
|
||||
export default stopCheckingForUpdatesInjectable;
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import applicationInformationInjectable from "../../common/vars/application-information.injectable";
|
||||
|
||||
const publishIsConfiguredInjectable = getInjectable({
|
||||
id: "publish-is-configured",
|
||||
instantiate: (di) => Boolean(di.inject(applicationInformationInjectable).build.publish?.length),
|
||||
});
|
||||
|
||||
export default publishIsConfiguredInjectable;
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import electronQuitAndInstallUpdateInjectable from "../electron-app/features/electron-quit-and-install-update.injectable";
|
||||
import { getCurrentDateTime } from "../../common/utils/date/get-current-date-time";
|
||||
import emitEventInjectable from "../../common/app-event-bus/emit-event.injectable";
|
||||
import discoveredUpdateVersionInjectable from "../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
||||
|
||||
const quitAndInstallUpdateInjectable = getInjectable({
|
||||
id: "quit-and-install-update",
|
||||
|
||||
instantiate: (di) => {
|
||||
const electronQuitAndInstallUpdate = di.inject(
|
||||
electronQuitAndInstallUpdateInjectable,
|
||||
);
|
||||
|
||||
const emitEvent = di.inject(emitEventInjectable);
|
||||
const discoveredUpdateVersion = di.inject(discoveredUpdateVersionInjectable);
|
||||
|
||||
return () => {
|
||||
const discoveredVersion = discoveredUpdateVersion.value.get();
|
||||
|
||||
if (!discoveredVersion) {
|
||||
throw new Error("Tried to install update but no update was discovered.");
|
||||
}
|
||||
|
||||
emitEvent({
|
||||
name: "app",
|
||||
action: "start-installing-update",
|
||||
|
||||
params: {
|
||||
version: discoveredVersion.version,
|
||||
updateChannel: discoveredVersion.updateChannel.id,
|
||||
currentDateTime: getCurrentDateTime(),
|
||||
},
|
||||
});
|
||||
|
||||
electronQuitAndInstallUpdate();
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default quitAndInstallUpdateInjectable;
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { restartAndInstallUpdateChannel } from "../../../common/application-update/restart-and-install-update-channel";
|
||||
import { getMessageChannelListenerInjectable } from "../../../common/utils/channel/message-channel-listener-injection-token";
|
||||
import quitAndInstallUpdateInjectable from "../quit-and-install-update.injectable";
|
||||
|
||||
const restartAndInstallUpdateListenerInjectable = getMessageChannelListenerInjectable({
|
||||
id: "restart",
|
||||
channel: restartAndInstallUpdateChannel,
|
||||
handler: (di) => di.inject(quitAndInstallUpdateInjectable),
|
||||
});
|
||||
|
||||
export default restartAndInstallUpdateListenerInjectable;
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import getTrayIconPathInjectable from "../../tray/menu-icon/get-tray-icon-path.injectable";
|
||||
import { trayIconInjectionToken } from "../../tray/menu-icon/tray-icon-injection-token";
|
||||
import updatesAreBeingDiscoveredInjectable from "../../../common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable";
|
||||
import updateIsBeingDownloadedInjectable from "../../../common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable";
|
||||
|
||||
const checkingForUpdatesTrayIconInjectable = getInjectable({
|
||||
id: "checking-for-updates-tray-icon",
|
||||
|
||||
instantiate: (di) => {
|
||||
const getTrayIconPath = di.inject(getTrayIconPathInjectable);
|
||||
const updatesAreBeingDiscovered = di.inject(updatesAreBeingDiscoveredInjectable);
|
||||
const updateIsBeingDownloaded = di.inject(updateIsBeingDownloadedInjectable);
|
||||
|
||||
return {
|
||||
iconPath: getTrayIconPath("checking-for-updates"),
|
||||
priority: 1,
|
||||
shouldBeShown: computed(
|
||||
() =>
|
||||
updatesAreBeingDiscovered.value.get() ||
|
||||
updateIsBeingDownloaded.value.get(),
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: trayIconInjectionToken,
|
||||
});
|
||||
|
||||
export default checkingForUpdatesTrayIconInjectable;
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import getTrayIconPathInjectable from "../../tray/menu-icon/get-tray-icon-path.injectable";
|
||||
import { trayIconInjectionToken } from "../../tray/menu-icon/tray-icon-injection-token";
|
||||
import updateIsReadyToBeInstalledInjectable from "../update-is-ready-to-be-installed.injectable";
|
||||
|
||||
const updateIsReadyToBeInstalledTrayIconInjectable = getInjectable({
|
||||
id: "update-is-ready-to-be-installed-tray-icon",
|
||||
|
||||
instantiate: (di) => {
|
||||
const getTrayIconPath = di.inject(getTrayIconPathInjectable);
|
||||
const updateIsReadyToBeInstalled = di.inject(updateIsReadyToBeInstalledInjectable);
|
||||
|
||||
return {
|
||||
iconPath: getTrayIconPath("update-available"),
|
||||
priority: 1,
|
||||
shouldBeShown: updateIsReadyToBeInstalled,
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: trayIconInjectionToken,
|
||||
});
|
||||
|
||||
export default updateIsReadyToBeInstalledTrayIconInjectable;
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import discoveredUpdateVersionInjectable from "../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
||||
import updateIsBeingDownloadedInjectable from "../../common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable";
|
||||
|
||||
const updateIsReadyToBeInstalledInjectable = getInjectable({
|
||||
id: "update-is-ready-to-be-installed",
|
||||
|
||||
instantiate: (di) => {
|
||||
const discoveredUpdateVersion = di.inject(discoveredUpdateVersionInjectable);
|
||||
const updateIsBeingDownloaded = di.inject(updateIsBeingDownloadedInjectable);
|
||||
|
||||
return computed(
|
||||
() =>
|
||||
!!discoveredUpdateVersion.value.get() &&
|
||||
!updateIsBeingDownloaded.value.get(),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default updateIsReadyToBeInstalledInjectable;
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import electronUpdaterIsActiveInjectable from "../electron-app/features/electron-updater-is-active.injectable";
|
||||
import publishIsConfiguredInjectable from "./publish-is-configured.injectable";
|
||||
|
||||
const updatingIsEnabledInjectable = getInjectable({
|
||||
id: "updating-is-enabled",
|
||||
|
||||
instantiate: (di) => {
|
||||
const electronUpdaterIsActive = di.inject(electronUpdaterIsActiveInjectable);
|
||||
const publishIsConfigured = di.inject(publishIsConfiguredInjectable);
|
||||
|
||||
return electronUpdaterIsActive && publishIsConfigured;
|
||||
},
|
||||
});
|
||||
|
||||
export default updatingIsEnabledInjectable;
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
import watchIfUpdateShouldHappenOnQuitInjectable from "./watch-if-update-should-happen-on-quit.injectable";
|
||||
|
||||
const startWatchingIfUpdateShouldHappenOnQuitInjectable = getInjectable({
|
||||
id: "start-watching-if-update-should-happen-on-quit",
|
||||
|
||||
instantiate: (di) => {
|
||||
const watchIfUpdateShouldHappenOnQuit = di.inject(watchIfUpdateShouldHappenOnQuitInjectable);
|
||||
|
||||
return {
|
||||
id: "start-watching-if-update-should-happen-on-quit",
|
||||
run: () => {
|
||||
watchIfUpdateShouldHappenOnQuit.start();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default startWatchingIfUpdateShouldHappenOnQuitInjectable;
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import watchIfUpdateShouldHappenOnQuitInjectable from "./watch-if-update-should-happen-on-quit.injectable";
|
||||
import { beforeQuitOfBackEndInjectionToken } from "../../start-main-application/runnable-tokens/before-quit-of-back-end-injection-token";
|
||||
|
||||
const stopWatchingIfUpdateShouldHappenOnQuitInjectable = getInjectable({
|
||||
id: "stop-watching-if-update-should-happen-on-quit",
|
||||
|
||||
instantiate: (di) => {
|
||||
const watchIfUpdateShouldHappenOnQuit = di.inject(watchIfUpdateShouldHappenOnQuitInjectable);
|
||||
|
||||
return {
|
||||
id: "stop-watching-if-update-should-happen-on-quit",
|
||||
run: () => {
|
||||
watchIfUpdateShouldHappenOnQuit.stop();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeQuitOfBackEndInjectionToken,
|
||||
});
|
||||
|
||||
export default stopWatchingIfUpdateShouldHappenOnQuitInjectable;
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { autorun } from "mobx";
|
||||
import { getStartableStoppable } from "../../../common/utils/get-startable-stoppable";
|
||||
import setUpdateOnQuitInjectable from "../../electron-app/features/set-update-on-quit.injectable";
|
||||
import selectedUpdateChannelInjectable from "../../../common/application-update/selected-update-channel/selected-update-channel.injectable";
|
||||
import type { ReleaseChannel, UpdateChannel } from "../../../common/application-update/update-channels";
|
||||
import discoveredUpdateVersionInjectable from "../../../common/application-update/discovered-update-version/discovered-update-version.injectable";
|
||||
|
||||
const watchIfUpdateShouldHappenOnQuitInjectable = getInjectable({
|
||||
id: "watch-if-update-should-happen-on-quit",
|
||||
|
||||
instantiate: (di) => {
|
||||
const setUpdateOnQuit = di.inject(setUpdateOnQuitInjectable);
|
||||
const selectedUpdateChannel = di.inject(selectedUpdateChannelInjectable);
|
||||
const discoveredVersionState = di.inject(discoveredUpdateVersionInjectable);
|
||||
|
||||
return getStartableStoppable("watch-if-update-should-happen-on-quit", () =>
|
||||
autorun(() => {
|
||||
const sufficientlyStableUpdateChannels = getSufficientlyStableUpdateChannels(selectedUpdateChannel.value.get());
|
||||
const updateIsDiscoveredFromChannel = discoveredVersionState.value.get()?.updateChannel;
|
||||
|
||||
setUpdateOnQuit((
|
||||
updateIsDiscoveredFromChannel
|
||||
? sufficientlyStableUpdateChannels.includes(updateIsDiscoveredFromChannel.id)
|
||||
: false
|
||||
));
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const getSufficientlyStableUpdateChannels = (updateChannel: UpdateChannel): ReleaseChannel[] => {
|
||||
if (!updateChannel.moreStableUpdateChannel) {
|
||||
return [updateChannel.id];
|
||||
}
|
||||
|
||||
return [
|
||||
updateChannel.id,
|
||||
...getSufficientlyStableUpdateChannels(updateChannel.moreStableUpdateChannel),
|
||||
];
|
||||
};
|
||||
|
||||
export default watchIfUpdateShouldHappenOnQuitInjectable;
|
||||
+3
-3
@@ -6,12 +6,12 @@ import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { setupIpcMainHandlers } from "./setup-ipc-main-handlers";
|
||||
import loggerInjectable from "../../../../common/logger.injectable";
|
||||
import clusterManagerInjectable from "../../../cluster/manager.injectable";
|
||||
import applicationMenuItemsInjectable from "../../../menu/application-menu-items.injectable";
|
||||
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
import operatingSystemThemeInjectable from "../../../theme/operating-system-theme.injectable";
|
||||
import catalogEntityRegistryInjectable from "../../../catalog/entity-registry.injectable";
|
||||
import askUserForFilePathsInjectable from "../../../ipc/ask-user-for-file-paths.injectable";
|
||||
import applicationMenuItemCompositeInjectable from "../../../../features/application-menu/main/application-menu-item-composite.injectable";
|
||||
|
||||
const setupIpcMainHandlersInjectable = getInjectable({
|
||||
id: "setup-ipc-main-handlers",
|
||||
@@ -19,7 +19,7 @@ const setupIpcMainHandlersInjectable = getInjectable({
|
||||
instantiate: (di) => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const clusterManager = di.inject(clusterManagerInjectable);
|
||||
const applicationMenuItems = di.inject(applicationMenuItemsInjectable);
|
||||
const applicationMenuItemComposite = di.inject(applicationMenuItemCompositeInjectable);
|
||||
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
const clusterStore = di.inject(clusterStoreInjectable);
|
||||
const operatingSystemTheme = di.inject(operatingSystemThemeInjectable);
|
||||
@@ -31,7 +31,7 @@ const setupIpcMainHandlersInjectable = getInjectable({
|
||||
logger.debug("[APP-MAIN] initializing ipc main handlers");
|
||||
|
||||
setupIpcMainHandlers({
|
||||
applicationMenuItems,
|
||||
applicationMenuItemComposite,
|
||||
clusterManager,
|
||||
catalogEntityRegistry,
|
||||
clusterStore,
|
||||
|
||||
@@ -15,16 +15,19 @@ import { pushCatalogToRenderer } from "../../../catalog-pusher";
|
||||
import type { ClusterManager } from "../../../cluster/manager";
|
||||
import { ResourceApplier } from "../../../resource-applier";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import type { MenuItemOpts } from "../../../menu/application-menu-items.injectable";
|
||||
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window";
|
||||
import { handleWindowAction, onLocationChange } from "../../../ipc/window";
|
||||
import { openFilePickingDialogChannel } from "../../../../common/ipc/dialog";
|
||||
import { getNativeThemeChannel } from "../../../../common/ipc/native-theme";
|
||||
import type { Theme } from "../../../theme/operating-system-theme-state.injectable";
|
||||
import type { AskUserForFilePaths } from "../../../ipc/ask-user-for-file-paths.injectable";
|
||||
import type { ApplicationMenuItemTypes } from "../../../../features/application-menu/main/menu-items/application-menu-item-injection-token";
|
||||
import type { Composite } from "../../../../common/utils/composite/get-composite/get-composite";
|
||||
import { getApplicationMenuTemplate } from "../../../../features/application-menu/main/populate-application-menu.injectable";
|
||||
import type { MenuItemRoot } from "../../../../features/application-menu/main/application-menu-item-composite.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
applicationMenuItems: IComputedValue<MenuItemOpts[]>;
|
||||
applicationMenuItemComposite: IComputedValue<Composite<ApplicationMenuItemTypes | MenuItemRoot>>;
|
||||
clusterManager: ClusterManager;
|
||||
catalogEntityRegistry: CatalogEntityRegistry;
|
||||
clusterStore: ClusterStore;
|
||||
@@ -33,7 +36,7 @@ interface Dependencies {
|
||||
}
|
||||
|
||||
export const setupIpcMainHandlers = ({
|
||||
applicationMenuItems,
|
||||
applicationMenuItemComposite,
|
||||
clusterManager,
|
||||
catalogEntityRegistry,
|
||||
clusterStore,
|
||||
@@ -124,9 +127,8 @@ export const setupIpcMainHandlers = ({
|
||||
ipcMainHandle(broadcastMainChannel, (event, channel, ...args) => broadcastMessage(channel, ...args));
|
||||
|
||||
ipcMainOn(windowOpenAppMenuAsContextMenuChannel, async (event) => {
|
||||
const appMenu = applicationMenuItems.get();
|
||||
|
||||
const menu = Menu.buildFromTemplate(appMenu);
|
||||
const electronTemplate = getApplicationMenuTemplate(applicationMenuItemComposite.get());
|
||||
const menu = Menu.buildFromTemplate(electronTemplate);
|
||||
|
||||
menu.popup({
|
||||
...BrowserWindow.fromWebContents(event.sender),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { kebabCase, noop, chunk } from "lodash/fp";
|
||||
import type { DiContainer, Injectable } from "@ogre-tools/injectable";
|
||||
import { createContainer } from "@ogre-tools/injectable";
|
||||
import { createContainer, isInjectable } from "@ogre-tools/injectable";
|
||||
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||
import writeJsonFileInjectable from "../common/fs/write-json-file.injectable";
|
||||
import readJsonFileInjectable from "../common/fs/read-json-file.injectable";
|
||||
@@ -31,7 +31,6 @@ import setupLensProxyInjectable from "./start-main-application/runnables/setup-l
|
||||
import setupShellInjectable from "./start-main-application/runnables/setup-shell.injectable";
|
||||
import setupSyncingOfWeblinksInjectable from "./start-main-application/runnables/setup-syncing-of-weblinks.injectable";
|
||||
import stopServicesAndExitAppInjectable from "./stop-services-and-exit-app.injectable";
|
||||
import applicationMenuInjectable from "./menu/application-menu.injectable";
|
||||
import isDevelopmentInjectable from "../common/vars/is-development.injectable";
|
||||
import setupSystemCaInjectable from "./start-main-application/runnables/setup-system-ca.injectable";
|
||||
import setupDeepLinkingInjectable from "./electron-app/runnables/setup-deep-linking.injectable";
|
||||
@@ -59,15 +58,11 @@ import syncThemeFromOperatingSystemInjectable from "./electron-app/features/sync
|
||||
import platformInjectable from "../common/vars/platform.injectable";
|
||||
import electronQuitAndInstallUpdateInjectable from "./electron-app/features/electron-quit-and-install-update.injectable";
|
||||
import electronUpdaterIsActiveInjectable from "./electron-app/features/electron-updater-is-active.injectable";
|
||||
import publishIsConfiguredInjectable from "./application-update/publish-is-configured.injectable";
|
||||
import checkForPlatformUpdatesInjectable from "./application-update/check-for-platform-updates/check-for-platform-updates.injectable";
|
||||
import baseBundledBinariesDirectoryInjectable from "../common/vars/base-bundled-binaries-dir.injectable";
|
||||
import setUpdateOnQuitInjectable from "./electron-app/features/set-update-on-quit.injectable";
|
||||
import downloadPlatformUpdateInjectable from "./application-update/download-platform-update/download-platform-update.injectable";
|
||||
import startCatalogSyncInjectable from "./catalog-sync-to-renderer/start-catalog-sync.injectable";
|
||||
import startKubeConfigSyncInjectable from "./start-main-application/runnables/kube-config-sync/start-kube-config-sync.injectable";
|
||||
import getRandomIdInjectable from "../common/utils/get-random-id.injectable";
|
||||
import periodicalCheckForUpdatesInjectable from "./application-update/periodical-check-for-updates/periodical-check-for-updates.injectable";
|
||||
import execFileInjectable from "../common/fs/exec-file.injectable";
|
||||
import normalizedPlatformArchitectureInjectable from "../common/vars/normalized-platform-architecture.injectable";
|
||||
import getHelmChartVersionsInjectable from "./helm/helm-service/get-helm-chart-versions.injectable";
|
||||
@@ -100,8 +95,12 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
|
||||
|
||||
di.preventSideEffects();
|
||||
|
||||
const injectables: Injectable<any, any, any>[] = (global as any).mainInjectablePaths.map(
|
||||
(filePath: string) => require(filePath).default,
|
||||
const injectables: Injectable<any, any, any>[] = (
|
||||
global as any
|
||||
).mainInjectablePaths.flatMap((filePath: string) =>
|
||||
Object.values(require(filePath)).filter(
|
||||
(maybeInjectable: any) => isInjectable(maybeInjectable),
|
||||
),
|
||||
);
|
||||
|
||||
runInAction(() => {
|
||||
@@ -152,10 +151,6 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
|
||||
di.override(stopServicesAndExitAppInjectable, () => () => {});
|
||||
di.override(lensResourcesDirInjectable, () => "/irrelevant");
|
||||
|
||||
di.override(applicationMenuInjectable, () => ({ start: () => {}, stop: () => {} }));
|
||||
|
||||
di.override(periodicalCheckForUpdatesInjectable, () => ({ start: () => {}, stop: () => {}, started: false }));
|
||||
|
||||
overrideFunctionalInjectables(di, [
|
||||
getHelmChartVersionsInjectable,
|
||||
getHelmChartValuesInjectable,
|
||||
@@ -252,20 +247,14 @@ const overrideElectronFeatures = (di: DiContainer) => {
|
||||
di.override(syncThemeFromOperatingSystemInjectable, () => ({ start: () => {}, stop: () => {} }));
|
||||
di.override(electronQuitAndInstallUpdateInjectable, () => () => {});
|
||||
di.override(setUpdateOnQuitInjectable, () => () => {});
|
||||
di.override(downloadPlatformUpdateInjectable, () => async () => ({ downloadWasSuccessful: true }));
|
||||
di.override(focusApplicationInjectable, () => () => {});
|
||||
|
||||
di.override(checkForPlatformUpdatesInjectable, () => () => {
|
||||
throw new Error("Tried to check for platform updates without explicit override.");
|
||||
});
|
||||
|
||||
di.override(
|
||||
getElectronAppPathInjectable,
|
||||
() => (name: string) => `/some-electron-app-path-for-${kebabCase(name)}`,
|
||||
);
|
||||
|
||||
di.override(setElectronAppPathInjectable, () => () => {});
|
||||
di.override(publishIsConfiguredInjectable, () => false);
|
||||
di.override(electronUpdaterIsActiveInjectable, () => false);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,333 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { docsUrl, supportUrl } from "../../common/vars";
|
||||
import { broadcastMessage } from "../../common/ipc";
|
||||
import type { MenuItemConstructorOptions } from "electron";
|
||||
import { webContents } from "electron";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import electronMenuItemsInjectable from "./electron-menu-items.injectable";
|
||||
import updatingIsEnabledInjectable from "../application-update/updating-is-enabled.injectable";
|
||||
import navigateToPreferencesInjectable from "../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable";
|
||||
import navigateToExtensionsInjectable from "../../common/front-end-routing/routes/extensions/navigate-to-extensions.injectable";
|
||||
import navigateToCatalogInjectable from "../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
||||
import navigateToWelcomeInjectable from "../../common/front-end-routing/routes/welcome/navigate-to-welcome.injectable";
|
||||
import navigateToAddClusterInjectable from "../../common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable";
|
||||
import stopServicesAndExitAppInjectable from "../stop-services-and-exit-app.injectable";
|
||||
import isMacInjectable from "../../common/vars/is-mac.injectable";
|
||||
import { computed } from "mobx";
|
||||
import showAboutInjectable from "./show-about.injectable";
|
||||
import reloadCurrentApplicationWindowInjectable from "../start-main-application/lens-window/reload-current-application-window.injectable";
|
||||
import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.injectable";
|
||||
import processCheckingForUpdatesInjectable from "../application-update/check-for-updates/process-checking-for-updates.injectable";
|
||||
import openLinkInBrowserInjectable from "../../common/utils/open-link-in-browser.injectable";
|
||||
import appNameInjectable from "../../common/vars/app-name.injectable";
|
||||
import productNameInjectable from "../../common/vars/product-name.injectable";
|
||||
|
||||
function ignoreIf(check: boolean, menuItems: MenuItemOpts[]) {
|
||||
return check ? [] : menuItems;
|
||||
}
|
||||
|
||||
export interface MenuItemOpts extends MenuItemConstructorOptions {
|
||||
submenu?: MenuItemConstructorOptions[];
|
||||
}
|
||||
|
||||
const applicationMenuItemsInjectable = getInjectable({
|
||||
id: "application-menu-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const appName = di.inject(appNameInjectable);
|
||||
const productName = di.inject(productNameInjectable);
|
||||
const isMac = di.inject(isMacInjectable);
|
||||
const updatingIsEnabled = di.inject(updatingIsEnabledInjectable);
|
||||
const electronMenuItems = di.inject(electronMenuItemsInjectable);
|
||||
const showAbout = di.inject(showAboutInjectable);
|
||||
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
|
||||
const reloadApplicationWindow = di.inject(reloadCurrentApplicationWindowInjectable);
|
||||
const navigateToPreferences = di.inject(navigateToPreferencesInjectable);
|
||||
const navigateToExtensions = di.inject(navigateToExtensionsInjectable);
|
||||
const navigateToCatalog = di.inject(navigateToCatalogInjectable);
|
||||
const navigateToWelcome = di.inject(navigateToWelcomeInjectable);
|
||||
const navigateToAddCluster = di.inject(navigateToAddClusterInjectable);
|
||||
const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable);
|
||||
const processCheckingForUpdates = di.inject(processCheckingForUpdatesInjectable);
|
||||
const openLinkInBrowser = di.inject(openLinkInBrowserInjectable);
|
||||
|
||||
logger.info(`[MENU]: autoUpdateEnabled=${updatingIsEnabled}`);
|
||||
|
||||
return computed((): MenuItemOpts[] => {
|
||||
const macAppMenu: MenuItemOpts = {
|
||||
label: appName,
|
||||
id: "root",
|
||||
submenu: [
|
||||
{
|
||||
label: `About ${productName}`,
|
||||
id: "about",
|
||||
click() {
|
||||
showAbout();
|
||||
},
|
||||
},
|
||||
...ignoreIf(!updatingIsEnabled, [
|
||||
{
|
||||
id: "check-for-updates",
|
||||
label: "Check for updates",
|
||||
click() {
|
||||
processCheckingForUpdates("application-menu").then(() => showApplicationWindow());
|
||||
},
|
||||
},
|
||||
]),
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Preferences",
|
||||
accelerator: "CmdOrCtrl+,",
|
||||
id: "preferences",
|
||||
click() {
|
||||
navigateToPreferences();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Extensions",
|
||||
accelerator: "CmdOrCtrl+Shift+E",
|
||||
id: "extensions",
|
||||
click() {
|
||||
navigateToExtensions();
|
||||
},
|
||||
},
|
||||
{ type: "separator" },
|
||||
{ role: "services" },
|
||||
{ type: "separator" },
|
||||
{ role: "hide" },
|
||||
{ role: "hideOthers" },
|
||||
{ role: "unhide" },
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Quit",
|
||||
accelerator: "Cmd+Q",
|
||||
id: "quit",
|
||||
click() {
|
||||
stopServicesAndExitApp();
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const fileMenu: MenuItemOpts = {
|
||||
label: "File",
|
||||
id: "file",
|
||||
submenu: [
|
||||
{
|
||||
label: "Add Cluster",
|
||||
accelerator: "CmdOrCtrl+Shift+A",
|
||||
id: "add-cluster",
|
||||
click() {
|
||||
navigateToAddCluster();
|
||||
},
|
||||
},
|
||||
...ignoreIf(isMac, [
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Preferences",
|
||||
id: "preferences",
|
||||
accelerator: "Ctrl+,",
|
||||
click() {
|
||||
navigateToPreferences();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Extensions",
|
||||
accelerator: "Ctrl+Shift+E",
|
||||
click() {
|
||||
navigateToExtensions();
|
||||
},
|
||||
},
|
||||
]),
|
||||
{ type: "separator" },
|
||||
...ignoreIf(!isMac, [
|
||||
{
|
||||
role: "close",
|
||||
label: "Close Window",
|
||||
accelerator: "Shift+Cmd+W",
|
||||
},
|
||||
]),
|
||||
...ignoreIf(isMac, [
|
||||
{
|
||||
label: "Exit",
|
||||
accelerator: "Alt+F4",
|
||||
id: "quit",
|
||||
click() {
|
||||
stopServicesAndExitApp();
|
||||
},
|
||||
},
|
||||
]),
|
||||
],
|
||||
};
|
||||
const editMenu: MenuItemOpts = {
|
||||
label: "Edit",
|
||||
id: "edit",
|
||||
submenu: [
|
||||
{ role: "undo" },
|
||||
{ role: "redo" },
|
||||
{ type: "separator" },
|
||||
{ role: "cut" },
|
||||
{ role: "copy" },
|
||||
{ role: "paste" },
|
||||
{ role: "delete" },
|
||||
{ type: "separator" },
|
||||
{ role: "selectAll" },
|
||||
],
|
||||
};
|
||||
const viewMenu: MenuItemOpts = {
|
||||
label: "View",
|
||||
id: "view",
|
||||
submenu: [
|
||||
{
|
||||
label: "Catalog",
|
||||
accelerator: "Shift+CmdOrCtrl+C",
|
||||
id: "catalog",
|
||||
click() {
|
||||
navigateToCatalog();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Command Palette...",
|
||||
accelerator: "Shift+CmdOrCtrl+P",
|
||||
id: "command-palette",
|
||||
click(_m, _b, event) {
|
||||
/**
|
||||
* Don't broadcast unless it was triggered by menu iteration so that
|
||||
* there aren't double events in renderer
|
||||
*
|
||||
* NOTE: this `?` is required because of a bug in playwright. https://github.com/microsoft/playwright/issues/10554
|
||||
*/
|
||||
if (!event?.triggeredByAccelerator) {
|
||||
broadcastMessage("command-palette:open");
|
||||
}
|
||||
},
|
||||
},
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Back",
|
||||
accelerator: "CmdOrCtrl+[",
|
||||
id: "go-back",
|
||||
click() {
|
||||
webContents
|
||||
.getAllWebContents()
|
||||
.filter((wc) => wc.getType() === "window")
|
||||
.forEach((wc) => wc.goBack());
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Forward",
|
||||
accelerator: "CmdOrCtrl+]",
|
||||
id: "go-forward",
|
||||
click() {
|
||||
webContents
|
||||
.getAllWebContents()
|
||||
.filter((wc) => wc.getType() === "window")
|
||||
.forEach((wc) => wc.goForward());
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Reload",
|
||||
accelerator: "CmdOrCtrl+R",
|
||||
id: "reload",
|
||||
click() {
|
||||
reloadApplicationWindow();
|
||||
},
|
||||
},
|
||||
{ role: "toggleDevTools" },
|
||||
{ type: "separator" },
|
||||
{ role: "resetZoom" },
|
||||
{ role: "zoomIn" },
|
||||
{ role: "zoomOut" },
|
||||
{ type: "separator" },
|
||||
{ role: "togglefullscreen" },
|
||||
],
|
||||
};
|
||||
const helpMenu: MenuItemOpts = {
|
||||
role: "help",
|
||||
id: "help",
|
||||
submenu: [
|
||||
{
|
||||
label: "Welcome",
|
||||
id: "welcome",
|
||||
click() {
|
||||
navigateToWelcome();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Documentation",
|
||||
id: "documentation",
|
||||
click: async () => {
|
||||
openLinkInBrowser(docsUrl).catch((error) => {
|
||||
logger.error("[MENU]: failed to open browser", { error });
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Support",
|
||||
id: "support",
|
||||
click: async () => {
|
||||
openLinkInBrowser(supportUrl).catch((error) => {
|
||||
logger.error("[MENU]: failed to open browser", { error });
|
||||
});
|
||||
},
|
||||
},
|
||||
...ignoreIf(isMac, [
|
||||
{
|
||||
label: `About ${productName}`,
|
||||
id: "about",
|
||||
click() {
|
||||
showAbout();
|
||||
},
|
||||
},
|
||||
...ignoreIf(!updatingIsEnabled, [
|
||||
{
|
||||
label: "Check for updates",
|
||||
click() {
|
||||
processCheckingForUpdates("periodic").then(() =>
|
||||
showApplicationWindow(),
|
||||
);
|
||||
},
|
||||
},
|
||||
]),
|
||||
]),
|
||||
],
|
||||
};
|
||||
// Prepare menu items order
|
||||
const appMenu = new Map([
|
||||
["mac", macAppMenu],
|
||||
["file", fileMenu],
|
||||
["edit", editMenu],
|
||||
["view", viewMenu],
|
||||
["help", helpMenu],
|
||||
]);
|
||||
|
||||
// Modify menu from extensions-api
|
||||
for (const menuItem of electronMenuItems.get()) {
|
||||
const parentMenu = appMenu.get(menuItem.parentId);
|
||||
|
||||
if (!parentMenu) {
|
||||
logger.error(
|
||||
`[MENU]: cannot register menu item for parentId=${menuItem.parentId}, parent item doesn't exist`,
|
||||
{ menuItem },
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
(parentMenu.submenu ??= []).push(menuItem);
|
||||
}
|
||||
|
||||
if (!isMac) {
|
||||
appMenu.delete("mac");
|
||||
}
|
||||
|
||||
return [...appMenu.values()];
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default applicationMenuItemsInjectable;
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { autorun } from "mobx";
|
||||
import { buildMenu } from "./menu";
|
||||
import applicationMenuItemsInjectable from "./application-menu-items.injectable";
|
||||
import { getStartableStoppable } from "../../common/utils/get-startable-stoppable";
|
||||
|
||||
const applicationMenuInjectable = getInjectable({
|
||||
id: "application-menu",
|
||||
|
||||
instantiate: (di) => {
|
||||
const applicationMenuItems = di.inject(applicationMenuItemsInjectable);
|
||||
|
||||
return getStartableStoppable("build-of-application-menu", () =>
|
||||
autorun(() => buildMenu(applicationMenuItems.get()), {
|
||||
delay: 100,
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default applicationMenuInjectable;
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import mainExtensionsInjectable from "../../extensions/main-extensions.injectable";
|
||||
|
||||
const electronMenuItemsInjectable = getInjectable({
|
||||
id: "electron-menu-items",
|
||||
|
||||
instantiate: (di) => {
|
||||
const extensions = di.inject(mainExtensionsInjectable);
|
||||
|
||||
return computed(() =>
|
||||
extensions.get().flatMap((extension) => extension.appMenus));
|
||||
},
|
||||
});
|
||||
|
||||
export default electronMenuItemsInjectable;
|
||||
@@ -1,116 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import { LensMainExtension } from "../../extensions/lens-main-extension";
|
||||
import electronMenuItemsInjectable from "./electron-menu-items.injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { computed, ObservableMap, runInAction } from "mobx";
|
||||
import type { MenuRegistration } from "./menu-registration";
|
||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||
import mainExtensionsInjectable from "../../extensions/main-extensions.injectable";
|
||||
|
||||
describe("electron-menu-items", () => {
|
||||
let di: DiContainer;
|
||||
let electronMenuItems: IComputedValue<MenuRegistration[]>;
|
||||
let extensionsStub: ObservableMap<string, LensMainExtension>;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
extensionsStub = new ObservableMap();
|
||||
|
||||
di.override(
|
||||
mainExtensionsInjectable,
|
||||
() => computed(() => [...extensionsStub.values()]),
|
||||
);
|
||||
|
||||
electronMenuItems = di.inject(electronMenuItemsInjectable);
|
||||
});
|
||||
|
||||
it("does not have any items yet", () => {
|
||||
expect(electronMenuItems.get()).toHaveLength(0);
|
||||
});
|
||||
|
||||
describe("when extension is enabled", () => {
|
||||
beforeEach(() => {
|
||||
const someExtension = new SomeTestExtension({
|
||||
id: "some-extension-id",
|
||||
appMenus: [{ parentId: "some-parent-id-from-first-extension" }],
|
||||
});
|
||||
|
||||
runInAction(() => {
|
||||
extensionsStub.set("some-extension-id", someExtension);
|
||||
});
|
||||
});
|
||||
|
||||
it("has menu items", () => {
|
||||
expect(electronMenuItems.get()).toEqual([
|
||||
{
|
||||
parentId: "some-parent-id-from-first-extension",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("when disabling extension, does not have menu items", () => {
|
||||
extensionsStub.delete("some-extension-id");
|
||||
|
||||
expect(electronMenuItems.get()).toHaveLength(0);
|
||||
});
|
||||
|
||||
describe("when other extension is enabled", () => {
|
||||
beforeEach(() => {
|
||||
const someOtherExtension = new SomeTestExtension({
|
||||
id: "some-extension-id",
|
||||
appMenus: [{ parentId: "some-parent-id-from-second-extension" }],
|
||||
});
|
||||
|
||||
extensionsStub.set("some-other-extension-id", someOtherExtension);
|
||||
});
|
||||
|
||||
it("has menu items for both extensions", () => {
|
||||
expect(electronMenuItems.get()).toEqual([
|
||||
{
|
||||
parentId: "some-parent-id-from-first-extension",
|
||||
},
|
||||
|
||||
{
|
||||
parentId: "some-parent-id-from-second-extension",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("when extension is disabled, still returns menu items for extensions that are enabled", () => {
|
||||
runInAction(() => {
|
||||
extensionsStub.delete("some-other-extension-id");
|
||||
});
|
||||
|
||||
expect(electronMenuItems.get()).toEqual([
|
||||
{
|
||||
parentId: "some-parent-id-from-first-extension",
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class SomeTestExtension extends LensMainExtension {
|
||||
constructor({ id, appMenus }: {
|
||||
id: string;
|
||||
appMenus: MenuRegistration[];
|
||||
}) {
|
||||
super({
|
||||
id,
|
||||
absolutePath: "irrelevant",
|
||||
isBundled: false,
|
||||
isCompatible: false,
|
||||
isEnabled: false,
|
||||
manifest: { name: id, version: "some-version", engines: { lens: "^5.5.0" }},
|
||||
manifestPath: "irrelevant",
|
||||
});
|
||||
|
||||
this.appMenus = appMenus;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { MenuItemConstructorOptions } from "electron";
|
||||
|
||||
export interface MenuRegistration extends MenuItemConstructorOptions {
|
||||
parentId: string;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { Menu } from "electron";
|
||||
import type { MenuItemOpts } from "./application-menu-items.injectable";
|
||||
|
||||
export type MenuTopId = "mac" | "file" | "edit" | "view" | "help";
|
||||
|
||||
export function buildMenu(applicationMenuItems: MenuItemOpts[]) {
|
||||
Menu.setApplicationMenu(
|
||||
Menu.buildFromTemplate(applicationMenuItems),
|
||||
);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import showMessagePopupInjectable from "../electron-app/features/show-message-popup.injectable";
|
||||
import isWindowsInjectable from "../../common/vars/is-windows.injectable";
|
||||
import appNameInjectable from "../../common/vars/app-name.injectable";
|
||||
import productNameInjectable from "../../common/vars/product-name.injectable";
|
||||
import buildVersionInjectable from "../vars/build-version/build-version.injectable";
|
||||
import extensionApiVersionInjectable from "../../common/vars/extension-api-version.injectable";
|
||||
import applicationCopyrightInjectable from "../../common/vars/application-copyright.injectable";
|
||||
|
||||
const showAboutInjectable = getInjectable({
|
||||
id: "show-about",
|
||||
|
||||
instantiate: (di) => {
|
||||
const buildVersion = di.inject(buildVersionInjectable);
|
||||
const extensionApiVersion = di.inject(extensionApiVersionInjectable);
|
||||
const showMessagePopup = di.inject(showMessagePopupInjectable);
|
||||
const isWindows = di.inject(isWindowsInjectable);
|
||||
const appName = di.inject(appNameInjectable);
|
||||
const productName = di.inject(productNameInjectable);
|
||||
const applicationCopyright = di.inject(applicationCopyrightInjectable);
|
||||
|
||||
return () => {
|
||||
const appInfo = [
|
||||
`${appName}: ${buildVersion.get()}`,
|
||||
`Extension API: ${extensionApiVersion}`,
|
||||
`Electron: ${process.versions.electron}`,
|
||||
`Chrome: ${process.versions.chrome}`,
|
||||
`Node: ${process.versions.node}`,
|
||||
applicationCopyright,
|
||||
];
|
||||
|
||||
showMessagePopup(
|
||||
`${isWindows ? " ".repeat(2) : ""}${appName}`,
|
||||
productName,
|
||||
appInfo.join("\r\n"),
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default showAboutInjectable;
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import applicationMenuInjectable from "./application-menu.injectable";
|
||||
import { onLoadOfApplicationInjectionToken } from "../start-main-application/runnable-tokens/on-load-of-application-injection-token";
|
||||
|
||||
const startApplicationMenuInjectable = getInjectable({
|
||||
id: "start-application-menu",
|
||||
|
||||
instantiate: (di) => {
|
||||
const applicationMenu = di.inject(
|
||||
applicationMenuInjectable,
|
||||
);
|
||||
|
||||
return {
|
||||
id: "start-application-menu",
|
||||
run: async () => {
|
||||
await applicationMenu.start();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: onLoadOfApplicationInjectionToken,
|
||||
});
|
||||
|
||||
export default startApplicationMenuInjectable;
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import applicationMenuInjectable from "./application-menu.injectable";
|
||||
import { beforeQuitOfBackEndInjectionToken } from "../start-main-application/runnable-tokens/before-quit-of-back-end-injection-token";
|
||||
|
||||
const stopApplicationMenuInjectable = getInjectable({
|
||||
id: "stop-application-menu",
|
||||
|
||||
instantiate: (di) => {
|
||||
const applicationMenu = di.inject(
|
||||
applicationMenuInjectable,
|
||||
);
|
||||
|
||||
return {
|
||||
id: "stop-application-menu",
|
||||
run: async () => {
|
||||
await applicationMenu.stop();
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: beforeQuitOfBackEndInjectionToken,
|
||||
});
|
||||
|
||||
export default stopApplicationMenuInjectable;
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import showApplicationWindowInjectable from "../../../start-main-application/lens-window/show-application-window.injectable";
|
||||
import showAboutInjectable from "../../../menu/show-about.injectable";
|
||||
import showAboutInjectable from "../../../../features/application-menu/main/menu-items/special-menu-for-mac-application/show-about-application/show-about.injectable";
|
||||
import { trayMenuItemInjectionToken } from "../tray-menu-item-injection-token";
|
||||
import { computed } from "mobx";
|
||||
import withErrorLoggingInjectable from "../../../../common/utils/with-error-logging/with-error-logging.injectable";
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { trayMenuItemInjectionToken } from "../tray-menu-item-injection-token";
|
||||
import navigateToPreferencesInjectable from "../../../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable";
|
||||
import { computed } from "mobx";
|
||||
import { withErrorSuppression } from "../../../../common/utils/with-error-suppression/with-error-suppression";
|
||||
import { pipeline } from "@ogre-tools/fp";
|
||||
import withErrorLoggingInjectable from "../../../../common/utils/with-error-logging/with-error-logging.injectable";
|
||||
|
||||
const openPreferencesTrayItemInjectable = getInjectable({
|
||||
id: "open-preferences-tray-item",
|
||||
|
||||
instantiate: (di) => {
|
||||
const navigateToPreferences = di.inject(navigateToPreferencesInjectable);
|
||||
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
|
||||
|
||||
return {
|
||||
id: "open-preferences",
|
||||
parentId: null,
|
||||
label: computed(() => "Preferences"),
|
||||
orderNumber: 20,
|
||||
enabled: computed(() => true),
|
||||
visible: computed(() => true),
|
||||
|
||||
click: pipeline(
|
||||
navigateToPreferences,
|
||||
|
||||
withErrorLoggingFor(() => "[TRAY]: Opening of preferences failed."),
|
||||
|
||||
// TODO: Find out how to improve typing so that instead of
|
||||
// x => withErrorSuppression(x) there could only be withErrorSuppression
|
||||
(x) => withErrorSuppression(x),
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: trayMenuItemInjectionToken,
|
||||
});
|
||||
|
||||
export default openPreferencesTrayItemInjectable;
|
||||
@@ -3,9 +3,9 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import defaultUpdateChannelInjectable from "../../../common/application-update/selected-update-channel/default-update-channel.injectable";
|
||||
import { beforeApplicationIsLoadingInjectionToken } from "../../start-main-application/runnable-tokens/before-application-is-loading-injection-token";
|
||||
import initReleaseChannelInjectable from "../release-channel/init.injectable";
|
||||
import defaultUpdateChannelInjectable from "../../../features/application-update/common/selected-update-channel/default-update-channel.injectable";
|
||||
|
||||
const initDefaultUpdateChannelInjectable = getInjectable({
|
||||
id: "init-default-update-channel",
|
||||
|
||||
Reference in new issue
Block a user