Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up StreamDeck module #10865

Merged
merged 8 commits into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export default (env: Env) => {
optimization: {
// We always want the chunk name, otherwise it's just numbers
// chunkIds: 'named',
// Extract the runtime into a separate chunk
// Extract the runtime into a separate chunk.
runtimeChunk: 'single',
splitChunks: {
chunks(chunk) {
Expand Down
15 changes: 2 additions & 13 deletions src/app/item-actions/ItemAccessoryButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DimItem } from 'app/inventory/item-types';
import { ItemActionsModel } from 'app/item-popup/item-popup-actions';
import OpenOnStreamDeckButton from 'app/stream-deck/OpenOnStreamDeckButton/OpenOnStreamDeckButton';
import { streamDeckEnabledSelector } from 'app/stream-deck/selectors';
import { lazy, Suspense } from 'react';
import { useSelector } from 'react-redux';
import {
CompareActionButton,
Expand All @@ -13,13 +13,6 @@ import {
TagActionButton,
} from './ActionButtons';

const OpenOnStreamDeckButton = lazy(
() =>
import(
/* webpackChunkName: "send-to-stream-deck-button" */ 'app/stream-deck/OpenOnStreamDeckButton/OpenOnStreamDeckButton'
),
);

/**
* "Accessory" buttons like tagging, locking, comparing, adding to loadout. Displayed separately on mobile, but together with the move actions on desktop.
*/
Expand Down Expand Up @@ -58,11 +51,7 @@ export default function ItemAccessoryButtons({
{actionsModel.infusable && (
<InfuseActionButton item={item} label={showLabel} actionModel={actionsModel} />
)}
{streamDeckEnabled && (
<Suspense>
<OpenOnStreamDeckButton label={showLabel} item={item} />
</Suspense>
)}
{streamDeckEnabled && <OpenOnStreamDeckButton label={showLabel} item={item} />}
</>
);
}
4 changes: 1 addition & 3 deletions src/app/loadout/LoadoutsRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ export default memo(function LoadoutRow({
const streamDeckDeepLink = $featureFlags.elgatoStreamDeck
? // eslint-disable-next-line
useStreamDeckSelection({
type: 'loadout',
loadout,
store,
options: { type: 'loadout' as const, loadout, store },
equippable,
})
: undefined;
Expand Down
6 changes: 4 additions & 2 deletions src/app/loadout/ingame/InGameLoadoutStrip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,11 @@ function InGameLoadoutTile({
const streamDeckDeepLink = $featureFlags.elgatoStreamDeck
? // eslint-disable-next-line
useStreamDeckSelection({
type: 'in-game-loadout',
options: {
type: 'in-game-loadout' as const,
loadout: gameLoadout,
},
equippable: true,
loadout: gameLoadout,
})
: undefined;

Expand Down
16 changes: 3 additions & 13 deletions src/app/shell/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { accountRoute } from 'app/routes';
import { SearchFilterRef } from 'app/search/SearchBar';
import DimApiWarningBanner from 'app/storage/DimApiWarningBanner';
import { useThunkDispatch } from 'app/store/thunk-dispatch';
import StreamDeckButton from 'app/stream-deck/StreamDeckButton/StreamDeckButton';
import { streamDeckEnabledSelector } from 'app/stream-deck/selectors';
import { isiOSBrowser } from 'app/utils/browsers';
import { compact } from 'app/utils/collections';
Expand All @@ -18,7 +19,7 @@ import { infoLog } from 'app/utils/log';
import clsx from 'clsx';
import logo from 'images/logo-type-right-light.svg';
import { AnimatePresence, Spring, Variants, motion } from 'motion/react';
import React, { Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
import { Link, NavLink, useLocation } from 'react-router';
import { useSubscription } from 'use-subscription';
Expand Down Expand Up @@ -53,13 +54,6 @@ const menuAnimateVariants: Variants = {
};
const menuAnimateTransition: Spring = { type: 'spring', duration: 0.3, bounce: 0 };

const StreamDeckButton = lazy(
() =>
import(
/* webpackChunkName: "stream-deck-button" */ 'app/stream-deck/StreamDeckButton/StreamDeckButton'
),
);

// TODO: finally time to hack apart the header styles!

export default function Header() {
Expand Down Expand Up @@ -373,11 +367,7 @@ export default function Header() {
<SearchFilter onClear={hideSearch} ref={searchFilter} />
</span>
)}
{streamDeckEnabled && (
<Suspense>
<StreamDeckButton />
</Suspense>
)}
{streamDeckEnabled && <StreamDeckButton />}
<RefreshButton className={styles.menuItem} />
{!isPhonePortrait && (
<Link className={styles.menuItem} to="/settings" title={t('Settings.Settings')}>
Expand Down
7 changes: 4 additions & 3 deletions src/app/store/observerMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ export interface StoreObserver<T> {
*/
getObserved: (rootState: RootState) => T;
/**
* Runs the side effect providing both the previous and current version of the derrived state.
* Runs the side effect providing both the previous and current version of the derived state.
* When the `runInitially` flag is true, previous will be undefined on first run.
*/
sideEffect: (states: { previous: T | undefined; current: T }) => void;
sideEffect: (states: { previous: T | undefined; current: T; rootState: RootState }) => void;
}

/**
Expand Down Expand Up @@ -70,6 +70,7 @@ export function observerMiddleware<D extends Dispatch>(
storeObserver.sideEffect({
previous: undefined,
current: storeObserver.getObserved(api.getState()),
rootState: api.getState(),
});
}

Expand Down Expand Up @@ -97,7 +98,7 @@ export function observerMiddleware<D extends Dispatch>(
const current = observer.getObserved(currentRootState);
const equals = observer.equals || Object.is;
if (!equals(previous, current)) {
observer.sideEffect({ previous, current });
observer.sideEffect({ previous, current, rootState: currentRootState });
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@ import { DimItem } from 'app/inventory/item-types';
import ActionButton from 'app/item-actions/ActionButton';
import { BucketHashes } from 'data/d2/generated-enums';
import streamDeckIcon from 'images/streamDeck.svg';
import { useMemo } from 'react';
import { useStreamDeckSelection } from '../stream-deck';
import styles from './OpenOnStreamDeckButton.m.scss';

export default function OpenOnStreamDeckButton({ item, label }: { item: DimItem; label: boolean }) {
const options = useMemo(
() => ({
type: 'item' as const,
item,
isSubClass: item.bucket.hash === BucketHashes.Subclass,
}),
[item],
);

const deepLink = useStreamDeckSelection({
type: 'item',
item,
isSubClass: item.bucket.hash === BucketHashes.Subclass,
options,
equippable: !item.notransfer,
});

Expand Down
59 changes: 28 additions & 31 deletions src/app/stream-deck/StreamDeckButton/StreamDeckButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,23 @@ import { PressTip } from 'app/dim-ui/PressTip';
import { t } from 'app/i18next-t';
import { AppIcon, banIcon } from 'app/shell/icons';
import { useThunkDispatch } from 'app/store/thunk-dispatch';
import { EventBus } from 'app/utils/observable';
import streamDeckIcon from 'images/streamDeck.svg';
import { useEffect, useMemo, useState } from 'react';
import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { streamDeckSelector } from '../selectors';
import { streamDeckAuthorizationInit } from '../util/authorization';
import { STREAM_DECK_MINIMUM_VERSION, checkStreamDeckVersion } from '../util/version';
import styles from './StreamDeckButton.m.scss';

const version$ = new EventBus<undefined>();

const usePluginVersion = () => {
const [version, setVersion] = useState<string | undefined>(undefined);

useEffect(() => {
version$.subscribe(() =>
fetch('http://localhost:9120/version', {
mode: 'cors',
})
.then((response) => response.text())
.then((text) => setVersion(text))
.catch(() => setVersion(undefined)),
);
version$.next(undefined);
}, []);
return version;
};

interface StreamDeckTooltipProps {
function StreamDeckTooltip({
version,
error,
needSetup,
}: {
version?: string;
error?: boolean;
needSetup?: boolean;
}

function StreamDeckTooltip({ version, error, needSetup }: StreamDeckTooltipProps) {
}) {
return (
<div>
<div className={styles.tooltipTitle}>{t('StreamDeck.Tooltip.Title')}</div>
Expand Down Expand Up @@ -73,18 +55,35 @@ function StreamDeckTooltip({ version, error, needSetup }: StreamDeckTooltipProps
);
}

function StreamDeckButton() {
export default function StreamDeckButton() {
const { connected, auth } = useSelector(streamDeckSelector);
const version = usePluginVersion();
const error = useMemo(() => !checkStreamDeckVersion(version), [version]);
const [version, setVersion] = useState<string | undefined>(undefined);

const updateVersion = async () => {
try {
const resp = await fetch('http://localhost:9120/version', {
mode: 'cors',
});
const text = await resp.text();
setVersion(text);
} catch {
setVersion(undefined);
}
};

useEffect(() => {
updateVersion();
}, []);

const error = !checkStreamDeckVersion(version);
const needSetup = auth === undefined;
const dispatch = useThunkDispatch();

return (
<PressTip tooltip={<StreamDeckTooltip version={version} error={error} needSetup={needSetup} />}>
<button
onClick={() => {
version$.next(undefined);
updateVersion();
needSetup && dispatch(streamDeckAuthorizationInit());
}}
type="button"
Expand All @@ -103,5 +102,3 @@ function StreamDeckButton() {
</PressTip>
);
}

export default StreamDeckButton;
10 changes: 5 additions & 5 deletions src/app/stream-deck/async-module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// async module
import { currentStoreSelector } from 'app/inventory/selectors';
import { observe, unobserve } from 'app/store/observerMiddleware';
import store from 'app/store/store';
import { ThunkResult } from 'app/store/types';
import { RootState, ThunkResult } from 'app/store/types';
import { streamDeckConnected, streamDeckDisconnected } from 'app/stream-deck/actions';
import { SendToStreamDeckArgs, StreamDeckMessage } from 'app/stream-deck/interfaces';
import { handleStreamDeckMessage } from 'app/stream-deck/msg-handlers';
import packager from 'app/stream-deck/util/packager';
import useSelection from './useStreamDeckSelection';

const STREAM_DECK_FARMING_OBSERVER_ID = 'stream-deck-farming-observer';

Expand All @@ -25,8 +25,7 @@ export async function sendToStreamDeck(msg: SendToStreamDeckArgs) {
}

// collect and send data to the stream deck
function refreshStreamDeck() {
const state = store.getState();
function refreshStreamDeck(state: RootState) {
if (websocket.readyState === WebSocket.OPEN) {
const store = currentStoreSelector(state);
store &&
Expand Down Expand Up @@ -75,7 +74,7 @@ function registerObservers(): ThunkResult {
id: STREAM_DECK_INVENTORY_OBSERVER_ID,
runInitially: true,
getObserved: (rootState) => rootState.inventory,
sideEffect: refreshStreamDeck,
sideEffect: ({ rootState }) => refreshStreamDeck(rootState),
}),
);
};
Expand Down Expand Up @@ -153,4 +152,5 @@ function start(): ThunkResult {
export default {
start,
stop,
useSelection,
};
5 changes: 0 additions & 5 deletions src/app/stream-deck/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ export interface MaxPowerAction {
export interface PullItemAction {
action: 'pullItem';
itemId: string;
/**
* @deprecated to be removed in future plugin update
* @see type
*/
equip: boolean;
type: 'equip' | 'pull' | 'vault';
}

Expand Down
3 changes: 2 additions & 1 deletion src/app/stream-deck/msg-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ function pullItemHandler({ msg, state, store }: HandlerArgs<PullItemAction>): Th
const allItems = allItemsSelector(state);
const [item] = allItems.filter((it) => it.index.startsWith(msg.itemId));
const targetStore = msg.type === 'vault' ? vaultSelector(state) : store;
const shouldEquip = msg.type === 'equip' || msg.equip;
const shouldEquip = msg.type === 'equip';
if (targetStore) {
await dispatch(moveItemTo(item, targetStore, shouldEquip, item.amount));
}
Expand Down Expand Up @@ -235,6 +235,7 @@ export function handleStreamDeckMessage(msg: StreamDeckMessage, token: string):
});
throw new Error(!msg.token ? 'missing-token' : 'invalid-token');
}

if (store) {
// handle stream deck actions
const handler = handlers[msg.action] as (args: HandlerArgs<StreamDeckMessage>) => ThunkResult;
Expand Down
4 changes: 0 additions & 4 deletions src/app/stream-deck/stream-deck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,10 @@ const lazyLoaded: LazyStreamDeck = {};
// lazy load the stream deck module when needed
export const lazyLoadStreamDeck = async () => {
const core = await import(/* webpackChunkName: "streamdeck" */ './async-module');
const useStreamDeckSelection = await import(
/* webpackChunkName: "streamdeck-selection" */ './useStreamDeckSelection'
);
// load only once
if (!lazyLoaded.start) {
Object.assign(lazyLoaded, {
...core.default,
...useStreamDeckSelection.default,
});
}
};
Expand Down
Loading
Loading