Merge remote-tracking branch 'origin/master' into mstratford-duck-when-honking

This commit is contained in:
Matthew Stratford 2020-11-02 19:33:06 +00:00
commit e63535658b
No known key found for this signature in database
GPG key ID: 9E53C8B3F0B57395
6 changed files with 88 additions and 39 deletions

View file

@ -125,6 +125,10 @@ class Player extends ((PlayerEmitter as unknown) as { new (): EventEmitter }) {
}
}
getVolume() {
return this.volume;
}
setVolume(val: number) {
this.volume = val;
this._applyVolume();

View file

@ -509,7 +509,12 @@ export const load = (
const itsChannel = getState()
.showplan.plan!.filter((x) => x.channel === item.channel)
.sort((x, y) => x.weight - y.weight);
const itsIndex = itsChannel.indexOf(item);
// Sadly, we can't just do .indexOf() item directly,
// since the player's idea of an item may be changed over it's lifecycle (setting played,intro/cue/outro etc.)
// Therefore we'll find the updated item from the plan and match that.
const itsIndex = itsChannel.findIndex(
(x) => itemId(x) === itemId(item)
);
if (itsIndex > -1 && itsIndex !== itsChannel.length - 1) {
dispatch(load(player, itsChannel[itsIndex + 1]));
}
@ -596,14 +601,12 @@ export const stop = (player: number): AppThunk => (dispatch, getState) => {
let cueTime = 0;
console.log(Math.round(playerInstance.currentTime));
if (
state.loadedItem &&
"cue" in state.loadedItem &&
Math.round(playerInstance.currentTime) !== Math.round(state.loadedItem.cue)
) {
cueTime = state.loadedItem.cue;
console.log(cueTime);
}
playerInstance.stop();
@ -677,7 +680,14 @@ export const setVolume = (
const state = getState().mixer.players[player];
const currentLevel = state.volume;
const currentGain = state.gain;
let currentGain = state.gain;
// If we can, use the engine's 'real' volume gain.
// This helps when we've interupted a previous fade, so the state gain won't be correct.
if (typeof audioEngine.players[player] !== "undefined") {
currentGain = audioEngine.players[player]!.getVolume();
}
const volumeTween = new Between(currentLevel, uiLevel)
.time(FADE_TIME_SECONDS * 1000)
.on("update", (val: number) => {

View file

@ -14,18 +14,19 @@ export function ImporterModal(props: ImporterProps) {
// Add support for closing the modal when the importer wants to reload the show plan.
// There is a similar listener in showplanner/index.tsx to actually reload the show plan.
useEffect(() => {
window.addEventListener(
"message",
(event) => {
if (!event.origin.includes("ury.org.uk")) {
return;
}
if (event.data === "reload_showplan") {
props.close();
}
},
false
);
function reloadListener(event: MessageEvent) {
if (!event.origin.includes("ury.org.uk")) {
return;
}
if (event.data === "reload_showplan") {
props.close();
}
}
window.addEventListener("message", reloadListener);
return () => {
window.removeEventListener("message", reloadListener);
};
});
return (
<Modal isOpen={props.isOpen} onRequestClose={props.close}>

View file

@ -67,6 +67,8 @@ const setTrackIntro = (
player: number
): AppThunk => async (dispatch, getState) => {
try {
// Api only deals with whole seconds.
secs = Math.round(secs);
dispatch(MixerState.setLoadedItemIntro(player, secs));
if (getState().settings.saveShowPlanChanges) {
await api.setTrackIntro(track.trackid, secs);
@ -84,6 +86,8 @@ const setTrackOutro = (
player: number
): AppThunk => async (dispatch, getState) => {
try {
// Api only deals with whole seconds.
secs = Math.round(secs);
dispatch(MixerState.setLoadedItemOutro(player, secs));
if (getState().settings.saveShowPlanChanges) {
await api.setTrackOutro(track.trackid, secs);
@ -101,6 +105,8 @@ const setTrackCue = (
player: number
): AppThunk => async (dispatch, getState) => {
try {
// Api only deals with whole seconds.
secs = Math.round(secs);
dispatch(MixerState.setLoadedItemCue(player, secs));
if (getState().settings.saveShowPlanChanges) {
await api.setTimeslotItemCue(item.timeslotitemid, secs);
@ -192,9 +198,17 @@ function TimingButtons({ id }: { id: number }) {
}
export function Player({ id }: { id: number }) {
// Define time remaining (secs) when the play icon should flash.
const SECS_REMAINING_WARNING = 20;
// We want to force update the selector when we pass the SECS_REMAINING_WARNING barrier.
const playerState = useSelector(
(state: RootState) => state.mixer.players[id],
(a, b) =>
!(
a.timeRemaining <= SECS_REMAINING_WARNING &&
b.timeRemaining > SECS_REMAINING_WARNING
) &&
shallowEqual(
omit(a, "timeCurrent", "timeRemaining"),
omit(b, "timeCurrent", "timeRemaining")
@ -222,11 +236,15 @@ export function Player({ id }: { id: number }) {
}
};
var duration: number = 0;
let channelDuration = 0;
let channelUnplayed = 0;
const plan = useSelector((state: RootState) => state.showplan.plan);
plan?.forEach((pItem) => {
if (pItem.channel === id) {
duration += HHMMTosec(pItem.length);
channelDuration += HHMMTosec(pItem.length);
if (!pItem.played) {
channelUnplayed += HHMMTosec(pItem.length);
}
}
});
@ -239,6 +257,14 @@ export function Player({ id }: { id: number }) {
}
>
<div className="card text-center">
<div className="d-inline mx-1">
<span className="float-left">
Total: {secToHHMM(channelDuration)}
</span>
<span className="float-right">
Unplayed: {secToHHMM(channelUnplayed)}
</span>
</div>
<div className="row m-0 p-1 card-header channelButtons hover-menu">
<span className="hover-label">Channel Controls</span>
<button
@ -278,7 +304,6 @@ export function Player({ id }: { id: number }) {
<FaRedo />
&nbsp; Repeat {playerState.repeat}
</button>
<div>Total Time: {secToHHMM(duration)}</div>
</div>
{proMode && <ProModeButtons channel={id} />}
<div className="card-body p-0">
@ -319,7 +344,7 @@ export function Player({ id }: { id: number }) {
onClick={() => dispatch(MixerState.play(id))}
className={
playerState.state === "playing"
? playerState.timeRemaining <= 15
? playerState.timeRemaining <= SECS_REMAINING_WARNING
? "sp-state-playing sp-ending-soon"
: "sp-state-playing"
: ""

View file

@ -20,8 +20,14 @@ export default function ProModeButtons({ channel }: { channel: number }) {
<>
<div className="row m-0 p-1 card-header channelButtons proMode hover-menu">
<span className="hover-label">Pro Mode&trade;</span>
<button className="mr-1 btn btn-warning" title="Trim">
<FaTachometerAlt onClick={() => setActiveButton("trim")} />
<button
className="mr-1 btn btn-warning"
title="Trim"
onClick={() =>
setActiveButton(activeButton === "trim" ? null : "trim")
}
>
<FaTachometerAlt />
</button>
<button
className={
@ -46,9 +52,10 @@ export default function ProModeButtons({ channel }: { channel: number }) {
max={12}
step={0.2}
value={trimVal.toFixed(1)}
onChange={(e) =>
dispatch(setChannelTrim(channel, parseFloat(e.target.value)))
}
onChange={(e) => {
dispatch(setChannelTrim(channel, parseFloat(e.target.value)));
e.target.blur(); // Stop dragging from disabling the keyboard triggers.
}}
/>
<strong className="mt-2">{trimVal} dB</strong>
</>

View file

@ -389,20 +389,22 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
// Add support for reloading the show plan from the iFrames.
// There is a similar listener in showplanner/ImporterModal.tsx to handle closing the iframe.
useEffect(() => {
window.addEventListener(
"message",
(event) => {
if (!event.origin.includes("ury.org.uk")) {
return;
}
if (event.data === "reload_showplan") {
session.currentTimeslot !== null &&
dispatch(getShowplan(session.currentTimeslot.timeslot_id));
}
},
false
);
});
function reloadListener(event: MessageEvent) {
if (!event.origin.includes("ury.org.uk")) {
return;
}
if (event.data === "reload_showplan") {
session.currentTimeslot !== null &&
dispatch(getShowplan(session.currentTimeslot.timeslot_id));
}
}
window.addEventListener("message", reloadListener);
return () => {
window.removeEventListener("message", reloadListener);
};
}, [dispatch, session.currentTimeslot]);
if (showplan === null) {
return (
<LoadingDialogue