|
|
|
@ -17,6 +17,8 @@ interface BuffBarProps {
|
|
|
|
buffCharges: Partial<Record<BuffType, BuffChargeState>>;
|
|
|
|
buffCharges: Partial<Record<BuffType, BuffChargeState>>;
|
|
|
|
/** When true, UI max charge labels use subscriber caps (×2). */
|
|
|
|
/** When true, UI max charge labels use subscriber caps (×2). */
|
|
|
|
subscriptionActive?: boolean;
|
|
|
|
subscriptionActive?: boolean;
|
|
|
|
|
|
|
|
/** When true (e.g. hero dead), buff taps do nothing and refill flow is blocked. */
|
|
|
|
|
|
|
|
buffsLocked?: boolean;
|
|
|
|
nowMs: number;
|
|
|
|
nowMs: number;
|
|
|
|
onActivate: (type: BuffType) => void;
|
|
|
|
onActivate: (type: BuffType) => void;
|
|
|
|
/** Called when a buff refill purchase returns an updated hero */
|
|
|
|
/** Called when a buff refill purchase returns an updated hero */
|
|
|
|
@ -31,6 +33,7 @@ interface BuffButtonProps {
|
|
|
|
onActivate: () => void;
|
|
|
|
onActivate: () => void;
|
|
|
|
onRefill?: (type: BuffType) => void;
|
|
|
|
onRefill?: (type: BuffType) => void;
|
|
|
|
nowMs: number;
|
|
|
|
nowMs: number;
|
|
|
|
|
|
|
|
buffsLocked?: boolean;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---- Tooltip ----
|
|
|
|
// ---- Tooltip ----
|
|
|
|
@ -166,7 +169,7 @@ const buttonBase: CSSProperties = {
|
|
|
|
WebkitTapHighlightColor: 'transparent',
|
|
|
|
WebkitTapHighlightColor: 'transparent',
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function BuffButton({ buff, meta, charge, maxCharges, onActivate, onRefill, nowMs }: BuffButtonProps) {
|
|
|
|
function BuffButton({ buff, meta, charge, maxCharges, onActivate, onRefill, nowMs, buffsLocked }: BuffButtonProps) {
|
|
|
|
const tr = useT();
|
|
|
|
const tr = useT();
|
|
|
|
const [pressed, setPressed] = useState(false);
|
|
|
|
const [pressed, setPressed] = useState(false);
|
|
|
|
const [showTooltip, setShowTooltip] = useState(false);
|
|
|
|
const [showTooltip, setShowTooltip] = useState(false);
|
|
|
|
@ -189,7 +192,7 @@ function BuffButton({ buff, meta, charge, maxCharges, onActivate, onRefill, nowM
|
|
|
|
const remaining = charge?.remaining;
|
|
|
|
const remaining = charge?.remaining;
|
|
|
|
const hasChargeData = remaining != null;
|
|
|
|
const hasChargeData = remaining != null;
|
|
|
|
const isOutOfCharges = hasChargeData && remaining === 0;
|
|
|
|
const isOutOfCharges = hasChargeData && remaining === 0;
|
|
|
|
const isDisabled = isOnCooldown || (isOutOfCharges && !isActive);
|
|
|
|
const isDisabled = isOnCooldown || (isOutOfCharges && !isActive) || !!buffsLocked;
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
return () => {
|
|
|
|
return () => {
|
|
|
|
@ -198,6 +201,13 @@ function BuffButton({ buff, meta, charge, maxCharges, onActivate, onRefill, nowM
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
|
|
if (buffsLocked) {
|
|
|
|
|
|
|
|
setShowTooltip(false);
|
|
|
|
|
|
|
|
setShowRefillConfirm(false);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}, [buffsLocked]);
|
|
|
|
|
|
|
|
|
|
|
|
const openTooltip = (): void => {
|
|
|
|
const openTooltip = (): void => {
|
|
|
|
setShowTooltip(true);
|
|
|
|
setShowTooltip(true);
|
|
|
|
if (autoHideTimer.current) clearTimeout(autoHideTimer.current);
|
|
|
|
if (autoHideTimer.current) clearTimeout(autoHideTimer.current);
|
|
|
|
@ -210,6 +220,7 @@ function BuffButton({ buff, meta, charge, maxCharges, onActivate, onRefill, nowM
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleTouchStart = (): void => {
|
|
|
|
const handleTouchStart = (): void => {
|
|
|
|
|
|
|
|
if (buffsLocked) return;
|
|
|
|
didLongPress.current = false;
|
|
|
|
didLongPress.current = false;
|
|
|
|
longPressTimer.current = setTimeout(() => {
|
|
|
|
longPressTimer.current = setTimeout(() => {
|
|
|
|
didLongPress.current = true;
|
|
|
|
didLongPress.current = true;
|
|
|
|
@ -225,6 +236,7 @@ function BuffButton({ buff, meta, charge, maxCharges, onActivate, onRefill, nowM
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleClick = (): void => {
|
|
|
|
const handleClick = (): void => {
|
|
|
|
|
|
|
|
if (buffsLocked) return;
|
|
|
|
if (didLongPress.current) {
|
|
|
|
if (didLongPress.current) {
|
|
|
|
didLongPress.current = false;
|
|
|
|
didLongPress.current = false;
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
@ -242,7 +254,11 @@ function BuffButton({ buff, meta, charge, maxCharges, onActivate, onRefill, nowM
|
|
|
|
onActivate();
|
|
|
|
onActivate();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const dimmedFaceOpacity = isDisabled ? (isOutOfCharges && !isOnCooldown ? 0.3 : 0.55) : 1;
|
|
|
|
const dimmedFaceOpacity = buffsLocked
|
|
|
|
|
|
|
|
? 0.4
|
|
|
|
|
|
|
|
: isDisabled
|
|
|
|
|
|
|
|
? (isOutOfCharges && !isOnCooldown ? 0.3 : 0.55)
|
|
|
|
|
|
|
|
: 1;
|
|
|
|
|
|
|
|
|
|
|
|
const hitStyle: CSSProperties = {
|
|
|
|
const hitStyle: CSSProperties = {
|
|
|
|
position: 'relative',
|
|
|
|
position: 'relative',
|
|
|
|
@ -251,7 +267,8 @@ function BuffButton({ buff, meta, charge, maxCharges, onActivate, onRefill, nowM
|
|
|
|
padding: 0,
|
|
|
|
padding: 0,
|
|
|
|
border: 'none',
|
|
|
|
border: 'none',
|
|
|
|
background: 'transparent',
|
|
|
|
background: 'transparent',
|
|
|
|
cursor: isDisabled ? 'not-allowed' : 'pointer',
|
|
|
|
pointerEvents: buffsLocked ? 'none' : 'auto',
|
|
|
|
|
|
|
|
cursor: buffsLocked || isDisabled ? 'not-allowed' : 'pointer',
|
|
|
|
transform: pressed ? 'scale(0.94)' : 'scale(1)',
|
|
|
|
transform: pressed ? 'scale(0.94)' : 'scale(1)',
|
|
|
|
transition: 'transform 80ms ease',
|
|
|
|
transition: 'transform 80ms ease',
|
|
|
|
};
|
|
|
|
};
|
|
|
|
@ -282,8 +299,8 @@ function BuffButton({ buff, meta, charge, maxCharges, onActivate, onRefill, nowM
|
|
|
|
onTouchStart={handleTouchStart}
|
|
|
|
onTouchStart={handleTouchStart}
|
|
|
|
onTouchEnd={handleTouchEnd}
|
|
|
|
onTouchEnd={handleTouchEnd}
|
|
|
|
onTouchCancel={handleTouchEnd}
|
|
|
|
onTouchCancel={handleTouchEnd}
|
|
|
|
onMouseEnter={openTooltip}
|
|
|
|
onMouseEnter={buffsLocked ? undefined : openTooltip}
|
|
|
|
onMouseLeave={closeTooltip}
|
|
|
|
onMouseLeave={buffsLocked ? undefined : closeTooltip}
|
|
|
|
aria-disabled={isDisabled}
|
|
|
|
aria-disabled={isDisabled}
|
|
|
|
aria-label={`${meta.label}: ${meta.desc}`}
|
|
|
|
aria-label={`${meta.label}: ${meta.desc}`}
|
|
|
|
>
|
|
|
|
>
|
|
|
|
@ -515,7 +532,16 @@ function getBuffEntry(
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function BuffBar({ buffs, cooldownEndsAt, buffCharges, subscriptionActive, nowMs, onActivate, onHeroUpdated }: BuffBarProps) {
|
|
|
|
export function BuffBar({
|
|
|
|
|
|
|
|
buffs,
|
|
|
|
|
|
|
|
cooldownEndsAt,
|
|
|
|
|
|
|
|
buffCharges,
|
|
|
|
|
|
|
|
subscriptionActive,
|
|
|
|
|
|
|
|
buffsLocked,
|
|
|
|
|
|
|
|
nowMs,
|
|
|
|
|
|
|
|
onActivate,
|
|
|
|
|
|
|
|
onHeroUpdated,
|
|
|
|
|
|
|
|
}: BuffBarProps) {
|
|
|
|
const handleActivate = useCallback(
|
|
|
|
const handleActivate = useCallback(
|
|
|
|
(type: BuffType) => () => onActivate(type),
|
|
|
|
(type: BuffType) => () => onActivate(type),
|
|
|
|
[onActivate],
|
|
|
|
[onActivate],
|
|
|
|
@ -552,6 +578,7 @@ export function BuffBar({ buffs, cooldownEndsAt, buffCharges, subscriptionActive
|
|
|
|
onActivate={handleActivate(type)}
|
|
|
|
onActivate={handleActivate(type)}
|
|
|
|
onRefill={handleRefill}
|
|
|
|
onRefill={handleRefill}
|
|
|
|
nowMs={nowMs}
|
|
|
|
nowMs={nowMs}
|
|
|
|
|
|
|
|
buffsLocked={buffsLocked}
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
})}
|
|
|
|
|