~avery/public-inbox

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
3 2

[PATCH crankshaft v2 1/3] Integrate plugins into gamepadui

Details
Message ID
<20220901165745.197501-1-ugzuzg@gmail.com>
DKIM signature
missing
Download raw message
Patch: +46 -62
---
 .../menu-manager/deck/menu-injector-deck.css  | 13 +---
 .../menu-manager/deck/menu-injector-deck.tsx  | 70 +++++--------------
 injected/src/smm.ts                           | 18 +++++
 injected/src/tab-observer.ts                  |  5 ++
 injected/src/types/global.d.ts                |  2 +
 5 files changed, 46 insertions(+), 62 deletions(-)

diff --git a/injected/src/menu-manager/deck/menu-injector-deck.css b/injected/src/menu-manager/deck/menu-injector-deck.css
index 4f78a23..2621f37 100644
--- a/injected/src/menu-manager/deck/menu-injector-deck.css
+++ b/injected/src/menu-manager/deck/menu-injector-deck.css
@@ -1,21 +1,12 @@
[data-smm-menu-page-container] {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: auto;
  z-index: 999;

  width: 100%;
  height: calc(100% - 40px);
  height: 100%;
  padding-top: calc(0px + var(--basicui-header-height));

  display: flex;
  overflow: auto;

  background-color: #23262e;
  opacity: 0;

  pointer-events: none;
}

[data-smm-menu-page-container] > ul:first-child {
diff --git a/injected/src/menu-manager/deck/menu-injector-deck.tsx b/injected/src/menu-manager/deck/menu-injector-deck.tsx
index 67b7497..f6949a9 100644
--- a/injected/src/menu-manager/deck/menu-injector-deck.tsx
+++ b/injected/src/menu-manager/deck/menu-injector-deck.tsx
@@ -34,6 +34,7 @@ export class MenuInjectorDeck implements MenuInjector {
    this.createPageContainer();
    this.addPluginsMenuItem();
    this.listenToClickEvents();
    this.listenToNavigationChanges();
  }

  private injectMenuStyles() {
@@ -56,21 +57,24 @@ export class MenuInjectorDeck implements MenuInjector {
        {this.menuPage}
      </div>
    );

    document
      .querySelector<HTMLDivElement>(DECK_SELECTORS.mainNavMenu)
      ?.appendChild(this.pageContainer);
  }

  private listenToClickEvents() {
    this.smm.IPC.on<{ id: string }>(
      'csMenuItemClicked',
      async ({ data: { id: _id } }) => {
        this.openPluginsPage();
        window.coolClass.Navigate('/blank/cs-plugins');
        window.coolClass.CloseSideMenus();
      }
    );
  }

  private listenToNavigationChanges() {
    this.smm.addEventListener('switchToPlugins', () => {
      this.openPluginsPage();
    });
  }

  private addPluginsMenuItem() {
    window.csMenuItems = [
      {
@@ -82,23 +86,11 @@ export class MenuInjectorDeck implements MenuInjector {
  }

  private openPluginsPage() {
    window.csMenuActiveItem = 'plugins';

    // Close menu
    window.coolClass.OpenSideMenu();
    if (this.pageContainer.isConnected) return;

    // Make sure we're on a page where we can show the plugin page
    // (we'll navigate back when the page is closed)
    if (
      document.querySelector(DECK_SELECTORS.topLevelTransitionSwitch)?.children
        ?.length === 0
    ) {
      window.coolClass.NavigateToLibraryTab();
      this.enteredWithNavigate = true;
    }
    window.csMenuActiveItem = 'plugins';

    this.showPageContainer();

    this.menuListGamepad = new GamepadHandler({
      smm: this.smm,
      root: this.menuList,
@@ -110,44 +102,20 @@ export class MenuInjectorDeck implements MenuInjector {
    });
  }

  private async closePluginsPage() {
    // Fade out the plugin page before removing it
    const animation = await this.pageContainer.animate([{ opacity: 0 }], {
      duration: 300,
      fill: 'forwards',
    }).finished;
    this.hidePageContainer();
    animation.cancel();

  private async closePluginsPage(forward = false) {
    // Clear active menu item
    window.csMenuActiveItem = undefined;
    window.csMenuUpdate?.();

    if (this.enteredWithNavigate) {
      window.coolClass.NavigateBackOrOpenMenu();
    }
  }
    deleteAll('[data-smm-menu-page-container]');

  private showPageContainer() {
    this.pageContainer.style.opacity = '1';
    this.pageContainer.style.pointerEvents = 'all';
    const header = document.querySelector<HTMLDivElement>(
      DECK_SELECTORS.header
    );
    if (header) {
      header.style.display = 'none';
    }
    if (!forward) window.coolClass.NavigateBackOrOpenMenu();
  }

  private hidePageContainer() {
    this.pageContainer.style.opacity = '0';
    this.pageContainer.style.pointerEvents = 'none';
    const header = document.querySelector<HTMLDivElement>(
      DECK_SELECTORS.header
    );
    if (header) {
      header.style.display = 'flex';
    }
  private showPageContainer() {
    document
      .querySelector<HTMLDivElement>(DECK_SELECTORS.topLevelTransitionSwitch)
      ?.appendChild(this.pageContainer);
  }

  createMenuItem({ id, label, render }: MenuItem) {
@@ -181,7 +149,7 @@ export class MenuInjectorDeck implements MenuInjector {
  closeActivePage() {
    this.activePluginGamepad?.cleanup();
    this.menuListGamepad?.cleanup();
    this.closePluginsPage();
    this.closePluginsPage(true);
  }

  private openPluginPage(render: MenuItem['render']) {
diff --git a/injected/src/smm.ts b/injected/src/smm.ts
index a375f9b..2689405 100644
--- a/injected/src/smm.ts
+++ b/injected/src/smm.ts
@@ -29,6 +29,7 @@ type SMMEventType =
  | typeof eventTypeSwitchToCollections
  | typeof eventTypeSwitchToAppDetails
  | typeof eventTypeSwitchToAppProperties
  | typeof eventTypeSwitchToPlugins
  | typeof eventTypeLockScreenOpened
  | typeof eventTypeLockScreenClosed;

@@ -38,6 +39,7 @@ type SMMEvent =
  | EventSwitchToCollections
  | EventSwitchToAppDetails
  | EventSwitchToAppProperties
  | EventSwitchToPlugins
  | EventLockScreenOpened
  | EventLockScreenClosed;

@@ -77,6 +79,13 @@ class EventSwitchToAppProperties extends CustomEvent<AppPropsApp> {
  }
}

const eventTypeSwitchToPlugins = 'switchToPlugins' as const;
class EventSwitchToPlugins extends CustomEvent<void> {
  constructor() {
    super(eventTypeSwitchToPlugins);
  }
}

const eventTypeLockScreenOpened = 'lockScreenOpened' as const;
class EventLockScreenOpened extends CustomEvent<void> {
  constructor() {
@@ -275,6 +284,15 @@ export class SMM extends EventTarget {
    this.dispatchEvent(new EventSwitchToAppProperties(app));
  }

  /**
   * @internal
   */
  switchToPlugins() {
    info('Switched to plugins');

    this.dispatchEvent(new EventSwitchToPlugins());
  }

  /**
   * @internal
   */
diff --git a/injected/src/tab-observer.ts b/injected/src/tab-observer.ts
index c66c3eb..d8ea9ee 100644
--- a/injected/src/tab-observer.ts
+++ b/injected/src/tab-observer.ts
@@ -81,6 +81,11 @@ export const createTabObserver = (smm: SMM, mainLibraryEl: HTMLElement) => {
      }
    }

    if (location.pathname === '/routes/blank/cs-plugins') {
      smm.switchToPlugins();
      return;
    }

    smm.switchToUnknownPage();
  });

diff --git a/injected/src/types/global.d.ts b/injected/src/types/global.d.ts
index 0b0c628..f082cbf 100644
--- a/injected/src/types/global.d.ts
+++ b/injected/src/types/global.d.ts
@@ -68,9 +68,11 @@ declare global {
      // other/none = close menu
      OpenSideMenu: (menu?: number) => void;
      ToggleSideMenu: (menu?: number) => void;
      CloseSideMenus: () => void;
      // Currently open menu (same number values as above)
      m_eOpenSideMenu?: number;

      Navigate: (target: string) => void;
      NavigateToLibraryTab: () => void;
      NavigateBackOrOpenMenu: () => void;

-- 
2.37.3

[PATCH crankshaft v2 2/3] Restyle plugins page to match Steam Deck

Details
Message ID
<20220901165745.197501-2-ugzuzg@gmail.com>
In-Reply-To
<20220901165745.197501-1-ugzuzg@gmail.com> (view parent)
DKIM signature
missing
Download raw message
Patch: +217 -40
---
 injected/src/gamepad/gamepad.ts               |  16 +-
 .../menu-manager/deck/menu-injector-deck.css  | 138 +++++++++++++++---
 .../menu-manager/deck/menu-injector-deck.tsx  |  98 +++++++++++--
 injected/src/ui/buttons.tsx                   |   2 +-
 injected/src/ui/index.ts                      |   3 +-
 5 files changed, 217 insertions(+), 40 deletions(-)

diff --git a/injected/src/gamepad/gamepad.ts b/injected/src/gamepad/gamepad.ts
index a7577c5..fbd2b67 100644
--- a/injected/src/gamepad/gamepad.ts
+++ b/injected/src/gamepad/gamepad.ts
@@ -79,23 +79,27 @@ export class GamepadHandler {
  async cleanup() {
    await this.smm.ButtonInterceptors.removeAfter(gamepadRoot(this.id));
    await this.smm.ButtonInterceptors.removeInterceptor(gamepadRoot(this.id));
    this.root
      .querySelectorAll('.cs-gp-focus')
      .forEach((node) => node.classList.remove('cs-gp-focus'));
    this.root.querySelectorAll<HTMLElement>('.cs-gp-focus').forEach((node) => {
      node.classList.remove('cs-gp-focus');
      node.blur();
    });
  }

  updateFocused(newFocusPath: string) {
    const curFocus = this.tree[this.focusPath];
    if (curFocus) {
      curFocus.el.classList.remove('cs-gp-focus');
      curFocus.el.blur();
    }

    document
      .querySelectorAll('.cs-gp-focus')
      .forEach((node) => node.classList.remove('cs-gp-focus'));
    document.querySelectorAll<HTMLElement>('.cs-gp-focus').forEach((node) => {
      node.classList.remove('cs-gp-focus');
      node.blur();
    });

    const newFocusEl = this.tree[newFocusPath].el;
    newFocusEl.classList.add('cs-gp-focus');
    newFocusEl.focus();

    this.focusPath = newFocusPath;

diff --git a/injected/src/menu-manager/deck/menu-injector-deck.css b/injected/src/menu-manager/deck/menu-injector-deck.css
index 2621f37..c2f0150 100644
--- a/injected/src/menu-manager/deck/menu-injector-deck.css
+++ b/injected/src/menu-manager/deck/menu-injector-deck.css
@@ -9,47 +9,110 @@
  background-color: #23262e;
}

[data-smm-menu-page-container] > ul:first-child {
  flex: 1 0 auto;
[data-smm-menu-page-container].animate-in {
  transition-property: opacity, transform;
  transition-duration: 400ms;
  transition-timing-function: cubic-bezier(0, 0, 0.1, 1);
  transition-delay: 100ms;
}
[data-smm-menu-page-container].animate-out {
  transition-property: opacity, transform;
  transition-duration: 100ms;
  transition-timing-function: cubic-bezier(0.6, 0, 1, 1);
}
[data-smm-menu-page-container].animate-in-end,
[data-smm-menu-page-container].animate-out-start {
  opacity: 1;
  transform: scale(1);
}
[data-smm-menu-page-container].animate-out-end,
[data-smm-menu-page-container].animate-in-start {
  opacity: 0;
  transform: scale(0.9);
}

  max-width: 200px;
  height: 100%;
[data-smm-menu-page-container] h1 {
  font-size: 22px !important;
}

[data-smm-menu-page-container] h2 {
  font-size: 16px;
}

[data-smm-menu-page-container] > ul:first-child {
  min-width: 240px;
  max-width: 40%;
  height: calc(100% - 16px * 2);

  margin: 0;
  padding: 0;
  padding: 16px 0;

  list-style: none;
  background-color: rgba(255, 255, 255, 2%);

  background: #23262e;
  border-right: 1px solid #0e141b;

  flex: 1;
  display: flex;
  flex-direction: column;
  position: relative;
  overflow-x: hidden;
}

[data-smm-menu-page-container] > ul:first-child > li {
  cursor: pointer;
  border: solid 1px transparent;

  transition: all 200ms;
}

[data-smm-menu-page-container] > ul:first-child > li.cs-gp-focus {
  outline: none;
  border-color: white;
}

[data-smm-menu-page-container]
  > ul:first-child
  > li.cs-gp-focus
  > .smm-menu-item-button {
  transform: scale(1.1);
  background: #3d4450;
}

.smm-menu-item-button {
  width: 100%;

  padding: 8px 24px;
  border: none;
  background: none;

  background-color: rgba(255, 255, 255, 2%);
  color: rgba(255, 255, 255, 90%);
  font-weight: normal;
  font-size: 16px;
  border: none;
  font-style: normal;
  line-height: 20px;
  text-align: left;
  text-decoration: none;
  text-indent: 0;
  text-shadow: none;
  text-transform: none;
  letter-spacing: 0px;
  color: #fff;
  padding: 10px calc(12px + 1.4vw);
  color: #b8bcbf;
  cursor: pointer;

  transition: all 150ms;
  display: flex;
  flex-direction: row;
  transform: scale(1) rotateX(1deg);
  line-height: 22px;
  scroll-margin: 2.5em 0;
  transition-property: transform, background-color;
  transition-duration: 0.32s, 0s;
  transition-timing-function: cubic-bezier(0.17, 0.45, 0.14, 0.83);
  transform-origin: 12% 50%;
  animation-timing-function: cubic-bezier(0.17, 0.45, 0.14, 0.83);
  animation-duration: 0.5s;
  animation-fill-mode: forwards;
  transform: scale(1) rotateX(1deg);
}

.smm-menu-item-button:hover {
  background-color: rgba(255, 255, 255, 4%);
  background-color: #23262e;
}

.smm-menu-item-button.active {
@@ -60,5 +123,46 @@
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: #23262e;
  background: #0e141b;
  position: relative;
}

[data-smm-plugin-page] {
  position: absolute;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

[data-smm-plugin-page].animate-out-top,
[data-smm-plugin-page].animate-out-bottom {
  transition-property: opacity, transform;
  transition-duration: 80ms;
  transition-timing-function: cubic-bezier(0.6, 0, 1, 1);
}
[data-smm-plugin-page].animate-in-top,
[data-smm-plugin-page].animate-in-bottom {
  transition-property: opacity, transform;
  transition-duration: 320ms;
  transition-timing-function: cubic-bezier(0, 0, 0.1, 1);
  transition-delay: 80ms;
}

[data-smm-plugin-page].animate-out-top-start,
[data-smm-plugin-page].animate-out-bottom-start,
[data-smm-plugin-page].animate-in-top-end,
[data-smm-plugin-page].animate-in-bottom-end {
  opacity: 1;
  transform: translateY(0);
}
[data-smm-plugin-page].animate-out-bottom-end,
[data-smm-plugin-page].animate-in-top-start {
  opacity: 0;
  transform: translateY(-8%);
}
[data-smm-plugin-page].animate-out-top-end,
[data-smm-plugin-page].animate-in-bottom-start {
  opacity: 0;
  transform: translateY(8%);
}
diff --git a/injected/src/menu-manager/deck/menu-injector-deck.tsx b/injected/src/menu-manager/deck/menu-injector-deck.tsx
index f6949a9..f8a82a8 100644
--- a/injected/src/menu-manager/deck/menu-injector-deck.tsx
+++ b/injected/src/menu-manager/deck/menu-injector-deck.tsx
@@ -8,6 +8,21 @@ import styles from './menu-injector-deck.css';

// @use-dom-chef

const animate = (
  node: HTMLElement,
  animationName: string,
  duration: number
) => {
  node.classList.add(animationName, `${animationName}-start`);
  setTimeout(() => {
    node.classList.remove(`${animationName}-start`);
    node.classList.add(`${animationName}-end`);
  }, 0);
  setTimeout(() => {
    node.classList.remove(animationName, `${animationName}-end`);
  }, duration);
};

export class MenuInjectorDeck implements MenuInjector {
  private readonly smm: SMM;
  private readonly menuManager: MenuManager;
@@ -18,17 +33,21 @@ export class MenuInjectorDeck implements MenuInjector {
  // List of plugins in page
  private menuList!: HTMLUListElement;
  private menuItemNodes: Record<string, HTMLLIElement>;
  private menuItems: MenuItem[];
  // Root to render plugin contents
  private menuPage!: HTMLDivElement;

  private menuListGamepad?: GamepadHandler;
  private activePluginGamepad?: GamepadHandler;
  private activePluginId?: string;
  private activePluginEl?: HTMLDivElement;

  constructor(smm: SMM, menuManager: MenuManager) {
    this.smm = smm;
    this.menuManager = menuManager;
    this.enteredWithNavigate = false;
    this.menuItemNodes = {};
    this.menuItems = [];

    this.injectMenuStyles();
    this.createPageContainer();
@@ -103,11 +122,13 @@ export class MenuInjectorDeck implements MenuInjector {
  }

  private async closePluginsPage(forward = false) {
    this.activePluginId = undefined;
    this.activePluginEl = undefined;
    // Clear active menu item
    window.csMenuActiveItem = undefined;
    window.csMenuUpdate?.();

    deleteAll('[data-smm-menu-page-container]');
    this.hidePageContainer();

    if (!forward) window.coolClass.NavigateBackOrOpenMenu();
  }
@@ -116,11 +137,19 @@ export class MenuInjectorDeck implements MenuInjector {
    document
      .querySelector<HTMLDivElement>(DECK_SELECTORS.topLevelTransitionSwitch)
      ?.appendChild(this.pageContainer);
    animate(this.pageContainer, 'animate-in', 600);
  }

  createMenuItem({ id, label, render }: MenuItem) {
  private hidePageContainer() {
    animate(this.pageContainer, 'animate-out', 100);
    setTimeout(() => deleteAll('[data-smm-menu-page-container]'), 100);
  }

  createMenuItem(menuItem: MenuItem) {
    const { id, label, render } = menuItem;
    const newMenuItem = dcCreateElement<HTMLLIElement>(
      <li
        tabIndex={0}
        smm-menu-item={id}
        data-cs-gp-in-group="root"
        data-cs-gp-item={id}
@@ -128,7 +157,10 @@ export class MenuInjectorDeck implements MenuInjector {
          Object.values(this.menuItemNodes).length === 0 ? 'true' : 'false'
        }
        onClick={() => {
          this.openPluginPage(render);
          this.openPluginPage(id, render, true);
        }}
        onFocus={() => {
          this.openPluginPage(id, render);
        }}
      >
        <button className="smm-menu-item-button" data-smm-menu-item-button={id}>
@@ -139,11 +171,13 @@ export class MenuInjectorDeck implements MenuInjector {

    this.menuItemNodes[id] = newMenuItem;
    this.menuList.appendChild(newMenuItem);
    this.menuItems.push(menuItem);
  }

  removeMenuItem(id: string) {
    this.menuItemNodes[id]?.remove();
    delete this.menuItemNodes[id];
    this.menuItems = this.menuItems.filter((item) => item.id !== id);
  }

  closeActivePage() {
@@ -152,17 +186,51 @@ export class MenuInjectorDeck implements MenuInjector {
    this.closePluginsPage(true);
  }

  private openPluginPage(render: MenuItem['render']) {
    [...this.menuPage.children].forEach((node) => node.remove());
    render(this.smm, this.menuPage);
    this.activePluginGamepad = new GamepadHandler({
      smm: this.smm,
      root: this.menuPage,
      rootExitCallback: () => {
        this.activePluginGamepad?.cleanup();
        [...this.menuPage.children].forEach((node) => node.remove());
        this.menuListGamepad?.updateFocused(this.menuListGamepad.focusPath);
      },
    });
  private openPluginPage(
    id: string,
    render: MenuItem['render'],
    navigateIntoView = false
  ) {
    if (this.activePluginId !== id || !this.activePluginEl) {
      const oldIndex = this.menuItems.findIndex(
        (item) => item.id === this.activePluginId
      );
      const newIndex = this.menuItems.findIndex((item) => item.id === id);
      const animationDirection = newIndex > oldIndex ? 'bottom' : 'top';

      ([...this.menuPage.children] as HTMLElement[]).forEach(
        (node: HTMLElement) => {
          animate(node, `animate-out-${animationDirection}`, 80);
          setTimeout(() => {
            // reset scroll after out animation is over
            this.menuPage.scrollTop = 0;
            node.remove();
          }, 80);
        }
      );

      const shouldAnimate = this.activePluginId != null;
      this.activePluginId = id;

      this.activePluginEl = dcCreateElement<HTMLDivElement>(
        <div data-smm-plugin-page />
      );
      render(this.smm, this.activePluginEl);
      this.menuPage.appendChild(this.activePluginEl);

      if (shouldAnimate)
        animate(this.activePluginEl, `animate-in-${animationDirection}`, 500);
    }

    if (navigateIntoView) {
      this.activePluginGamepad = new GamepadHandler({
        smm: this.smm,
        root: this.activePluginEl,
        rootExitCallback: () => {
          this.activePluginGamepad?.cleanup();
          this.menuListGamepad?.updateFocused(this.menuListGamepad.focusPath);
        },
      });
    }
  }
}
diff --git a/injected/src/ui/buttons.tsx b/injected/src/ui/buttons.tsx
index 6103d97..dfd89d5 100644
--- a/injected/src/ui/buttons.tsx
+++ b/injected/src/ui/buttons.tsx
@@ -25,7 +25,7 @@ const buttonStyles = `
	}

	.cs-button:focus {
		outline: none;
		outline: white solid 2px;
	}

	.cs-button:focus-visible {
diff --git a/injected/src/ui/index.ts b/injected/src/ui/index.ts
index 0eb5849..1eb9864 100644
--- a/injected/src/ui/index.ts
+++ b/injected/src/ui/index.ts
@@ -9,7 +9,8 @@ export const registerCustomElements = async () => {
  // TODO: load these styles from a .css file
  // Had an issue with esbuild loading the text from the file
  let styleSheetContents = `
.cs-gp-focus {
.cs-gp-focus,
.cs-gp-focus:focus {
  outline: solid 2px white;
}
  `;
-- 
2.37.3

[PATCH crankshaft v2 3/3] Add types for events and callbacks

Details
Message ID
<20220901165745.197501-3-ugzuzg@gmail.com>
In-Reply-To
<20220901165745.197501-1-ugzuzg@gmail.com> (view parent)
DKIM signature
missing
Download raw message
Patch: +82 -61
smm.addEventListener can now automatically determine the event arg type based on the string supplied to it.
---
 .../menu-manager/deck/menu-injector-deck.tsx  |  12 +-
 injected/src/smm.ts                           | 124 ++++++++++--------
 injected/src/tab-observer.ts                  |   7 +-
 3 files changed, 82 insertions(+), 61 deletions(-)

diff --git a/injected/src/menu-manager/deck/menu-injector-deck.tsx b/injected/src/menu-manager/deck/menu-injector-deck.tsx
index f8a82a8..0cdef73 100644
--- a/injected/src/menu-manager/deck/menu-injector-deck.tsx
+++ b/injected/src/menu-manager/deck/menu-injector-deck.tsx
@@ -1,7 +1,7 @@
import { dcCreateElement } from '../../dom-chef';
import { GamepadHandler } from '../../gamepad';
import { DECK_SELECTORS } from '../../selectors';
import { SMM } from '../../smm';
import { eventTypeSwitchToLocation, SMM } from '../../smm';
import { deleteAll } from '../../util';
import { MenuInjector, MenuItem, MenuManager } from '../menu-manager';
import styles from './menu-injector-deck.css';
@@ -89,8 +89,12 @@ export class MenuInjectorDeck implements MenuInjector {
  }

  private listenToNavigationChanges() {
    this.smm.addEventListener('switchToPlugins', () => {
      this.openPluginsPage();
    this.smm.addEventListener(eventTypeSwitchToLocation, (e) => {
      if (e.detail.pathname === '/routes/blank/cs-plugins') {
        this.openPluginsPage();
      } else {
        this.closeActivePage();
      }
    });
  }

@@ -122,6 +126,8 @@ export class MenuInjectorDeck implements MenuInjector {
  }

  private async closePluginsPage(forward = false) {
    if (!this.pageContainer.isConnected) return;

    this.activePluginId = undefined;
    this.activePluginEl = undefined;
    // Clear active menu item
diff --git a/injected/src/smm.ts b/injected/src/smm.ts
index 2689405..32d255a 100644
--- a/injected/src/smm.ts
+++ b/injected/src/smm.ts
@@ -19,52 +19,28 @@ import { info } from './util';

type PluginId = string;

type AddEventListenerArgs = Parameters<EventTarget['addEventListener']>;

// TODO: there's probably a better way to do these EventTarget types

type SMMEventType =
  | typeof eventTypeSwitchToUnknownPage
  | typeof eventTypeSwitchToHome
  | typeof eventTypeSwitchToCollections
  | typeof eventTypeSwitchToAppDetails
  | typeof eventTypeSwitchToAppProperties
  | typeof eventTypeSwitchToPlugins
  | typeof eventTypeLockScreenOpened
  | typeof eventTypeLockScreenClosed;

type SMMEvent =
  | EventSwitchToUnknownPage
  | EventSwitchToHome
  | EventSwitchToCollections
  | EventSwitchToAppDetails
  | EventSwitchToAppProperties
  | EventSwitchToPlugins
  | EventLockScreenOpened
  | EventLockScreenClosed;

const eventTypeSwitchToUnknownPage = 'switchToUnknownPage' as const;
export const eventTypeSwitchToUnknownPage = 'switchToUnknownPage' as const;
class EventSwitchToUnknownPage extends CustomEvent<void> {
  constructor() {
    super(eventTypeSwitchToUnknownPage);
  }
}

const eventTypeSwitchToHome = 'switchToHome' as const;
export const eventTypeSwitchToHome = 'switchToHome' as const;
class EventSwitchToHome extends CustomEvent<void> {
  constructor() {
    super(eventTypeSwitchToHome);
  }
}

const eventTypeSwitchToCollections = 'switchToCollections' as const;
export const eventTypeSwitchToCollections = 'switchToCollections' as const;
class EventSwitchToCollections extends CustomEvent<void> {
  constructor() {
    super(eventTypeSwitchToCollections);
  }
}

const eventTypeSwitchToAppDetails = 'switchToAppDetails' as const;
export const eventTypeSwitchToAppDetails = 'switchToAppDetails' as const;
type eventDetailsSwitchToAppDetails = { appId: string; appName: string };
class EventSwitchToAppDetails extends CustomEvent<eventDetailsSwitchToAppDetails> {
  constructor(detail: eventDetailsSwitchToAppDetails) {
@@ -72,34 +48,79 @@ class EventSwitchToAppDetails extends CustomEvent<eventDetailsSwitchToAppDetails
  }
}

const eventTypeSwitchToAppProperties = 'switchToAppProperties' as const;
export const eventTypeSwitchToAppProperties = 'switchToAppProperties' as const;
class EventSwitchToAppProperties extends CustomEvent<AppPropsApp> {
  constructor(detail: AppPropsApp) {
    super(eventTypeSwitchToAppProperties, { detail });
  }
}

const eventTypeSwitchToPlugins = 'switchToPlugins' as const;
class EventSwitchToPlugins extends CustomEvent<void> {
  constructor() {
    super(eventTypeSwitchToPlugins);
export const eventTypeSwitchToLocation = 'switchToLocation' as const;
type eventDetailsSwitchToLocation = Location;
class EventSwitchToLocation extends CustomEvent<eventDetailsSwitchToLocation> {
  constructor(detail: eventDetailsSwitchToLocation) {
    super(eventTypeSwitchToLocation, { detail });
  }
}

const eventTypeLockScreenOpened = 'lockScreenOpened' as const;
export const eventTypeLockScreenOpened = 'lockScreenOpened' as const;
class EventLockScreenOpened extends CustomEvent<void> {
  constructor() {
    super(eventTypeLockScreenOpened);
  }
}

const eventTypeLockScreenClosed = 'lockScreenClosed' as const;
export const eventTypeLockScreenClosed = 'lockScreenClosed' as const;
class EventLockScreenClosed extends CustomEvent<void> {
  constructor() {
    super(eventTypeLockScreenClosed);
  }
}

interface EventListenerObjectFor<T extends Event> {
  handleEvent(evt: T): void;
}
interface EventListenerFor<T extends Event> {
  (evt: T): void;
}
type EventListenerOrEventListenerObjectFor<T extends Event> =
  | EventListenerFor<T>
  | EventListenerObjectFor<T>;

type SMMEventGroup<T extends string, E extends Event> = {
  type: T;
  event: E;
};

type SMMEvent =
  | SMMEventGroup<typeof eventTypeSwitchToUnknownPage, EventSwitchToUnknownPage>
  | SMMEventGroup<typeof eventTypeSwitchToHome, EventSwitchToHome>
  | SMMEventGroup<typeof eventTypeSwitchToCollections, EventSwitchToCollections>
  | SMMEventGroup<typeof eventTypeSwitchToAppDetails, EventSwitchToAppDetails>
  | SMMEventGroup<
      typeof eventTypeSwitchToAppProperties,
      EventSwitchToAppProperties
    >
  | SMMEventGroup<typeof eventTypeSwitchToLocation, EventSwitchToLocation>
  | SMMEventGroup<typeof eventTypeLockScreenOpened, EventLockScreenOpened>
  | SMMEventGroup<typeof eventTypeLockScreenClosed, EventLockScreenClosed>;

type MapToCallbacks<U extends SMMEvent> = U extends any
  ? {
      type: U['type'];
      callback: EventListenerOrEventListenerObjectFor<U['event']> | null;
      options: Parameters<EventTarget['addEventListener']>[2];
    }
  : never;
type SMMEventCallback = MapToCallbacks<SMMEvent>;

type SMMCallbackMap = {
  [P in SMMEventCallback['type']]: Extract<
    SMMEventCallback,
    { type: P }
  >['callback'];
};

/**
 * @public
 */
@@ -141,14 +162,7 @@ export class SMM extends EventTarget {
  // that plugin.
  private currentPlugin?: string;
  // Events attached by plugins
  private attachedEvents: Record<
    PluginId,
    {
      type: AddEventListenerArgs[0];
      callback: AddEventListenerArgs[1];
      options: AddEventListenerArgs[2];
    }[]
  >;
  private attachedEvents: Record<PluginId, SMMEventCallback[]>;

  constructor(entry: Entry) {
    super();
@@ -287,10 +301,10 @@ export class SMM extends EventTarget {
  /**
   * @internal
   */
  switchToPlugins() {
    info('Switched to plugins');
  switchToLocation(loc: Location) {
    info('Switched to location');

    this.dispatchEvent(new EventSwitchToPlugins());
    this.dispatchEvent(new EventSwitchToLocation(loc));
  }

  /**
@@ -383,15 +397,15 @@ export class SMM extends EventTarget {

    if (this.attachedEvents[pluginId]) {
      for (const { type, callback, options } of this.attachedEvents[pluginId]) {
        this.removeEventListener(type, callback, options);
        this.removeEventListener(type, callback as EventListener, options);
      }
      delete this.attachedEvents[pluginId];
    }
  }

  addEventListener(
    type: SMMEventType,
    callback: EventListenerOrEventListenerObject | null,
  addEventListener<K extends keyof SMMCallbackMap>(
    type: K,
    callback: SMMCallbackMap[K],
    options?: boolean | AddEventListenerOptions
  ): void {
    if (this.currentPlugin) {
@@ -399,13 +413,17 @@ export class SMM extends EventTarget {
        this.attachedEvents[this.currentPlugin] = [];
      }

      this.attachedEvents[this.currentPlugin].push({ type, callback, options });
      this.attachedEvents[this.currentPlugin].push({
        type,
        callback,
        options,
      } as SMMEventCallback);
    }

    super.addEventListener(type, callback, options);
    super.addEventListener(type, callback as EventListener, options);
  }

  dispatchEvent(event: SMMEvent): boolean {
  dispatchEvent(event: SMMEvent['event']): boolean {
    return super.dispatchEvent(event);
  }

diff --git a/injected/src/tab-observer.ts b/injected/src/tab-observer.ts
index d8ea9ee..d9bfad9 100644
--- a/injected/src/tab-observer.ts
+++ b/injected/src/tab-observer.ts
@@ -14,6 +14,8 @@ export const createTabObserver = (smm: SMM, mainLibraryEl: HTMLElement) => {
  }

  const observer = new MutationObserver((_mutationsList) => {
    smm.switchToLocation({ ...location });

    if (window.smmUIMode === 'deck') {
      if (document.querySelector(DECK_SELECTORS.lockScreenContainer)) {
        if (!smm.onLockScreen) {
@@ -81,11 +83,6 @@ export const createTabObserver = (smm: SMM, mainLibraryEl: HTMLElement) => {
      }
    }

    if (location.pathname === '/routes/blank/cs-plugins') {
      smm.switchToPlugins();
      return;
    }

    smm.switchToUnknownPage();
  });

-- 
2.37.3

[crankshaft/patches] build failed

builds.sr.ht <builds@sr.ht>
Details
Message ID
<CML843YE6NHI.0IG76UZCMVVY@cirno>
In-Reply-To
<20220901165745.197501-3-ugzuzg@gmail.com> (view parent)
DKIM signature
missing
Download raw message
crankshaft/patches: FAILED in 1m36s

[Integrate plugins into gamepadui][0] v2 from [Jarasłaŭ Viktorčyk][1]

[0]: https://lists.sr.ht/~avery/public-inbox/patches/35075
[1]: ugzuzg@gmail.com

✗ #836525 FAILED  crankshaft/patches/mirror.yml https://builds.sr.ht/~avery/job/836525
✓ #836524 SUCCESS crankshaft/patches/linux.yml  https://builds.sr.ht/~avery/job/836524
Reply to thread Export thread (mbox)