Prettified Code!
This commit is contained in:
parent
d72e4ec4d4
commit
bcfd4004d7
30 changed files with 308 additions and 321 deletions
82
src/App.css
82
src/App.css
|
@ -25,8 +25,8 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
margin-left: .5%;
|
||||
margin-right: .5%;
|
||||
margin-left: 0.5%;
|
||||
margin-right: 0.5%;
|
||||
}
|
||||
|
||||
.sp {
|
||||
|
@ -42,16 +42,15 @@
|
|||
.sp-col {
|
||||
display: flex;
|
||||
overflow-y: scroll;
|
||||
margin-left: .2vw;
|
||||
margin-right: .2vw;
|
||||
padding: .2vw;
|
||||
margin-left: 0.2vw;
|
||||
margin-right: 0.2vw;
|
||||
padding: 0.2vw;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
flex-grow: 1;
|
||||
|
||||
}
|
||||
|
||||
.sp-main-col{
|
||||
.sp-main-col {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
|
@ -66,7 +65,6 @@
|
|||
.sidebar-toggle {
|
||||
flex-grow: 0;
|
||||
width: auto;
|
||||
|
||||
}
|
||||
.sidebar-toggle .btn {
|
||||
writing-mode: vertical-rl;
|
||||
|
@ -109,7 +107,7 @@
|
|||
}
|
||||
|
||||
.sp-track .aux {
|
||||
background-color: #07F;
|
||||
background-color: #07f;
|
||||
}
|
||||
|
||||
.sp-track .ghost {
|
||||
|
@ -120,7 +118,9 @@
|
|||
background-color: #10c998 !important;
|
||||
}
|
||||
|
||||
html, body, #root {
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
|
@ -142,10 +142,10 @@ html, body, #root {
|
|||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mediaButtons button{
|
||||
.mediaButtons button {
|
||||
width: 32%;
|
||||
border-style: none;
|
||||
margin: .5%;
|
||||
margin: 0.5%;
|
||||
padding: 2%;
|
||||
}
|
||||
|
||||
|
@ -161,11 +161,10 @@ html, body, #root {
|
|||
background-color: #db2c2c;
|
||||
}
|
||||
|
||||
button{
|
||||
button {
|
||||
background-color: rgb(199, 199, 199);
|
||||
}
|
||||
|
||||
|
||||
/*.player div {
|
||||
overflow-x: hidden;
|
||||
}*/
|
||||
|
@ -219,19 +218,20 @@ button{
|
|||
display: block;
|
||||
z-index: 5;
|
||||
/* padding: 1px; */
|
||||
|
||||
}
|
||||
.waveform .current, .waveform .remaining, .waveform .length {
|
||||
|
||||
.waveform .current,
|
||||
.waveform .remaining,
|
||||
.waveform .length {
|
||||
font-weight: 800;
|
||||
|
||||
}
|
||||
.waveform .remaining, .waveform .outro {
|
||||
.waveform .remaining,
|
||||
.waveform .outro {
|
||||
right: 0;
|
||||
position: absolute;
|
||||
|
||||
}
|
||||
.waveform .intro, .waveform .outro, .waveform .length {
|
||||
.waveform .intro,
|
||||
.waveform .outro,
|
||||
.waveform .length {
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ button{
|
|||
height: 6vh !important;
|
||||
}
|
||||
.waveform .loading {
|
||||
background: #CCCCFF;
|
||||
background: #ccccff;
|
||||
}
|
||||
|
||||
.bypass-click {
|
||||
|
@ -261,30 +261,42 @@ button{
|
|||
|
||||
/* Flash class and keyframe animation */
|
||||
.sp-ending-soon {
|
||||
-webkit-animation: green-flash steps(1) 0.5s infinite;
|
||||
animation: green-flash steps(1) 0.5s infinite;
|
||||
-webkit-animation: green-flash steps(1) 0.5s infinite;
|
||||
animation: green-flash steps(1) 0.5s infinite;
|
||||
}
|
||||
@-webkit-keyframes green-flash {
|
||||
0% { }
|
||||
50% { background-color: rgb(199, 255, 199); }
|
||||
0% {
|
||||
}
|
||||
50% {
|
||||
background-color: rgb(199, 255, 199);
|
||||
}
|
||||
}
|
||||
@keyframes green-flash {
|
||||
0% { }
|
||||
50% { background-color: rgb(199, 255, 199); }
|
||||
0% {
|
||||
}
|
||||
50% {
|
||||
background-color: rgb(199, 255, 199);
|
||||
}
|
||||
}
|
||||
|
||||
/* Flash class and keyframe animation */
|
||||
.sp-muted-player {
|
||||
-webkit-animation: red-flash steps(1) 0.5s infinite;
|
||||
animation: red-flash steps(1) 0.5s infinite;
|
||||
-webkit-animation: red-flash steps(1) 0.5s infinite;
|
||||
animation: red-flash steps(1) 0.5s infinite;
|
||||
}
|
||||
@-webkit-keyframes red-flash {
|
||||
0% { }
|
||||
50% { background-color: rgb(255, 199, 199); }
|
||||
0% {
|
||||
}
|
||||
50% {
|
||||
background-color: rgb(255, 199, 199);
|
||||
}
|
||||
}
|
||||
@keyframes red-flash {
|
||||
0% { }
|
||||
50% { background-color: rgb(255, 199, 199); }
|
||||
0% {
|
||||
}
|
||||
50% {
|
||||
background-color: rgb(255, 199, 199);
|
||||
}
|
||||
}
|
||||
|
||||
.ReactModal__Overlay {
|
||||
|
@ -300,4 +312,4 @@ button{
|
|||
z-index: 9000;
|
||||
pointer-events: none;
|
||||
box-shadow: inset 0 0 3px 6px red;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ const App: React.FC = () => {
|
|||
currentUser,
|
||||
userLoading,
|
||||
currentTimeslot,
|
||||
timeslotLoading
|
||||
timeslotLoading,
|
||||
} = useSelector((state: RootState) => state.session);
|
||||
|
||||
if (
|
||||
|
@ -60,8 +60,8 @@ const App: React.FC = () => {
|
|||
type="text"
|
||||
placeholder="enter a timeslot id"
|
||||
value={inputVal}
|
||||
onChange={e => setInputVal(e.target.value)}
|
||||
onKeyPress={e => enterKeyCont(e.key)}
|
||||
onChange={(e) => setInputVal(e.target.value)}
|
||||
onKeyPress={(e) => enterKeyCont(e.key)}
|
||||
autoFocus
|
||||
/>
|
||||
<button onClick={cont}>Continue</button>
|
||||
|
|
28
src/api.ts
28
src/api.ts
|
@ -15,7 +15,7 @@ export async function apiRequest(
|
|||
let req = null;
|
||||
if (method === "GET") {
|
||||
req = fetch(url + qs.stringify(params, { addQueryPrefix: true }), {
|
||||
credentials: need_auth ? "include" : "omit"
|
||||
credentials: need_auth ? "include" : "omit",
|
||||
});
|
||||
} else {
|
||||
const body = JSON.stringify(params);
|
||||
|
@ -24,9 +24,9 @@ export async function apiRequest(
|
|||
method,
|
||||
body,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=UTF-8"
|
||||
"Content-Type": "application/json; charset=UTF-8",
|
||||
},
|
||||
credentials: need_auth ? "include" : "omit"
|
||||
credentials: need_auth ? "include" : "omit",
|
||||
});
|
||||
}
|
||||
return await req;
|
||||
|
@ -123,8 +123,8 @@ export function getShowplan(showId: number): Promise<Showplan> {
|
|||
`/timeslot/${showId.toString(10)}/showplan`,
|
||||
"GET",
|
||||
{}
|
||||
).then(res => {
|
||||
return Object.keys(res).map(x => res[x]);
|
||||
).then((res) => {
|
||||
return Object.keys(res).map((x) => res[x]);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -136,11 +136,11 @@ function wrapPromise<T, TArgs>(factory: (...args: TArgs[]) => Promise<T>) {
|
|||
read(...args: TArgs[]) {
|
||||
if (!(suspender instanceof Promise)) {
|
||||
suspender = factory(...args).then(
|
||||
r => {
|
||||
(r) => {
|
||||
status = "success";
|
||||
result = r;
|
||||
},
|
||||
e => {
|
||||
(e) => {
|
||||
status = "error";
|
||||
result = e;
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ function wrapPromise<T, TArgs>(factory: (...args: TArgs[]) => Promise<T>) {
|
|||
} else {
|
||||
throw new Error("Can't happen.");
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ export function searchForTracks(
|
|||
artist,
|
||||
title,
|
||||
limit: 100,
|
||||
digitised: true
|
||||
digitised: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -206,8 +206,8 @@ export function getAuxPlaylists(): Promise<Array<ManagedPlaylist>> {
|
|||
|
||||
export function loadAuxLibrary(libraryId: string): Promise<AuxItem[]> {
|
||||
return apiRequest(MYRADIO_NON_API_BASE + "/NIPSWeb/load_aux_lib", "GET", {
|
||||
libraryid: libraryId
|
||||
}).then(res => res.json());
|
||||
libraryid: libraryId,
|
||||
}).then((res) => res.json());
|
||||
}
|
||||
|
||||
export type UpdateOp =
|
||||
|
@ -242,7 +242,7 @@ export function updateShowplan(
|
|||
ops: UpdateOp[]
|
||||
): Promise<OpResult[]> {
|
||||
return myradioApiRequest(`/timeslot/${timeslotid}/updateshowplan`, "PUT", {
|
||||
set: ops
|
||||
set: ops,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -255,7 +255,7 @@ export interface Timeslot {
|
|||
|
||||
export function getCurrentApiTimeslot(): Promise<Timeslot> {
|
||||
return myradioApiRequest(`/timeslot/userselectedtimeslot`, "GET", {}).then(
|
||||
res => {
|
||||
(res) => {
|
||||
return res;
|
||||
}
|
||||
);
|
||||
|
@ -270,7 +270,7 @@ export interface User {
|
|||
}
|
||||
|
||||
export function getCurrentApiUser(): Promise<User> {
|
||||
return myradioApiRequest(`/user/currentuser`, "GET", {}).then(res => {
|
||||
return myradioApiRequest(`/user/currentuser`, "GET", {}).then((res) => {
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ export class RecordingStreamer extends Streamer {
|
|||
super();
|
||||
this.recorder = new MediaRecorder(stream);
|
||||
this.chunks = [];
|
||||
this.recorder.ondataavailable = e => {
|
||||
this.recorder.ondataavailable = (e) => {
|
||||
this.chunks.push(e.data);
|
||||
};
|
||||
this.recorder.onstart = () => {
|
||||
|
@ -17,7 +17,7 @@ export class RecordingStreamer extends Streamer {
|
|||
this.recorder.onstop = () => {
|
||||
this.onStateChange("NOT_CONNECTED");
|
||||
const finalData = new Blob(this.chunks, {
|
||||
type: "audio/ogg; codecs=opus"
|
||||
type: "audio/ogg; codecs=opus",
|
||||
});
|
||||
const url = URL.createObjectURL(finalData);
|
||||
|
||||
|
@ -26,7 +26,7 @@ export class RecordingStreamer extends Streamer {
|
|||
a.download = "recorded.ogg";
|
||||
a.click();
|
||||
};
|
||||
this.recorder.onerror = e => {
|
||||
this.recorder.onerror = (e) => {
|
||||
console.error(e.error);
|
||||
this.onStateChange("CONNECTION_LOST");
|
||||
};
|
||||
|
|
|
@ -32,7 +32,7 @@ export class WebRTCStreamer extends Streamer {
|
|||
this.pc = new RTCPeerConnection({
|
||||
iceServers: [
|
||||
{
|
||||
urls: ["stun:eu-turn4.xirsys.com"]
|
||||
urls: ["stun:eu-turn4.xirsys.com"],
|
||||
},
|
||||
{
|
||||
username:
|
||||
|
@ -44,12 +44,12 @@ export class WebRTCStreamer extends Streamer {
|
|||
"turn:eu-turn4.xirsys.com:80?transport=tcp",
|
||||
"turn:eu-turn4.xirsys.com:3478?transport=tcp",
|
||||
"turns:eu-turn4.xirsys.com:443?transport=tcp",
|
||||
"turns:eu-turn4.xirsys.com:5349?transport=tcp"
|
||||
]
|
||||
}
|
||||
]
|
||||
"turns:eu-turn4.xirsys.com:5349?transport=tcp",
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
this.pc.oniceconnectionstatechange = e => {
|
||||
this.pc.oniceconnectionstatechange = (e) => {
|
||||
if (!this.pc) {
|
||||
throw new Error(
|
||||
"Received ICEConnectionStateChange but PC was null?????"
|
||||
|
@ -58,16 +58,13 @@ export class WebRTCStreamer extends Streamer {
|
|||
console.log("ICE Connection state change: " + this.pc.iceConnectionState);
|
||||
this.onStateChange(this.mapStateToConnectionState());
|
||||
};
|
||||
this.stream.getAudioTracks().forEach(track => this.pc!.addTrack(track));
|
||||
this.stream.getAudioTracks().forEach((track) => this.pc!.addTrack(track));
|
||||
|
||||
this.addConnectionStateListener(state => {
|
||||
this.addConnectionStateListener((state) => {
|
||||
if (state === "CONNECTED") {
|
||||
this.newsInterval = later.setInterval(
|
||||
this.doTheNews,
|
||||
later.parse
|
||||
.recur()
|
||||
.on(59)
|
||||
.minute()
|
||||
later.parse.recur().on(59).minute()
|
||||
);
|
||||
} else if (state === "CONNECTION_LOST" || state === "NOT_CONNECTED") {
|
||||
this.newsInterval?.clear();
|
||||
|
@ -76,11 +73,11 @@ export class WebRTCStreamer extends Streamer {
|
|||
|
||||
console.log("PC created");
|
||||
this.ws = new WebSocket(process.env.REACT_APP_WS_URL!);
|
||||
this.ws.onopen = e => {
|
||||
this.ws.onopen = (e) => {
|
||||
console.log("WS open");
|
||||
this.onStateChange(this.mapStateToConnectionState());
|
||||
};
|
||||
this.ws.onclose = e => {
|
||||
this.ws.onclose = (e) => {
|
||||
console.log("WS close");
|
||||
this.onStateChange(this.mapStateToConnectionState());
|
||||
};
|
||||
|
@ -110,30 +107,14 @@ export class WebRTCStreamer extends Streamer {
|
|||
// Sanity check
|
||||
const now = new Date();
|
||||
if (now.getSeconds() < 45) {
|
||||
later.setTimeout(
|
||||
async () => {
|
||||
await MixerState.playNewsIntro();
|
||||
},
|
||||
later.parse
|
||||
.recur()
|
||||
.on(59)
|
||||
.minute()
|
||||
.on(45)
|
||||
.second()
|
||||
);
|
||||
later.setTimeout(async () => {
|
||||
await MixerState.playNewsIntro();
|
||||
}, later.parse.recur().on(59).minute().on(45).second());
|
||||
}
|
||||
if (now.getMinutes() <= 1 && now.getSeconds() < 55) {
|
||||
later.setTimeout(
|
||||
async () => {
|
||||
await MixerState.playNewsEnd();
|
||||
},
|
||||
later.parse
|
||||
.recur()
|
||||
.on(1)
|
||||
.minute()
|
||||
.on(55)
|
||||
.second()
|
||||
);
|
||||
later.setTimeout(async () => {
|
||||
await MixerState.playNewsEnd();
|
||||
}, later.parse.recur().on(1).minute().on(55).second());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +138,7 @@ export class WebRTCStreamer extends Streamer {
|
|||
// Do some fun SDP fuckery to get better quality
|
||||
const parsed = SdpTransform.parse(offer.sdp!);
|
||||
console.log("Old SDP", parsed);
|
||||
parsed.media.forEach(track => {
|
||||
parsed.media.forEach((track) => {
|
||||
let opusIndex = 0;
|
||||
for (let i = 0; i < track.rtp.length; i++) {
|
||||
if (track.rtp[i].codec === "opus") {
|
||||
|
@ -165,9 +146,9 @@ export class WebRTCStreamer extends Streamer {
|
|||
}
|
||||
// TODO: maybe delete non-Opus candidates?
|
||||
}
|
||||
track.fmtp[opusIndex].config += `; maxaveragebitrate=${192 *
|
||||
2 *
|
||||
1024}; stereo=1; sprop-stereo=1 ; cbr=1`;
|
||||
track.fmtp[opusIndex].config += `; maxaveragebitrate=${
|
||||
192 * 2 * 1024
|
||||
}; stereo=1; sprop-stereo=1 ; cbr=1`;
|
||||
});
|
||||
|
||||
offer.sdp = SdpTransform.write(parsed);
|
||||
|
@ -179,7 +160,7 @@ export class WebRTCStreamer extends Streamer {
|
|||
JSON.stringify({
|
||||
kind: "OFFER",
|
||||
type: this.pc.localDescription!.type,
|
||||
sdp: this.pc.localDescription!.sdp
|
||||
sdp: this.pc.localDescription!.sdp,
|
||||
})
|
||||
);
|
||||
this.state = "OFFER";
|
||||
|
@ -190,7 +171,7 @@ export class WebRTCStreamer extends Streamer {
|
|||
}
|
||||
const answer = new RTCSessionDescription({
|
||||
type: data.type,
|
||||
sdp: data.sdp
|
||||
sdp: data.sdp,
|
||||
});
|
||||
await this.pc.setRemoteDescription(answer);
|
||||
this.state = "ANSWER";
|
||||
|
@ -226,7 +207,7 @@ export class WebRTCStreamer extends Streamer {
|
|||
|
||||
// TODO: supporting trickle ICE would be nICE
|
||||
waitForIceCandidates() {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
if (!this.pc) {
|
||||
throw new Error(
|
||||
"Tried to gather ICE Candidates with a null PeerConnection!"
|
||||
|
|
|
@ -45,7 +45,7 @@ const broadcastState = createSlice({
|
|||
autoNewsEnd: true,
|
||||
liveForThePurposesOfTracklisting: false,
|
||||
connectionState: "NOT_CONNECTED",
|
||||
recordingState: "NOT_CONNECTED"
|
||||
recordingState: "NOT_CONNECTED",
|
||||
} as BroadcastState,
|
||||
reducers: {
|
||||
changeSetting<K extends keyof BroadcastState>(
|
||||
|
@ -76,8 +76,8 @@ const broadcastState = createSlice({
|
|||
},
|
||||
setRecordingState(state, action: PayloadAction<ConnectionStateEnum>) {
|
||||
state.recordingState = action.payload;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default broadcastState.reducer;
|
||||
|
@ -85,7 +85,7 @@ export default broadcastState.reducer;
|
|||
export const {
|
||||
toggleTracklisting,
|
||||
setTracklisting,
|
||||
setWsID
|
||||
setWsID,
|
||||
} = broadcastState.actions;
|
||||
|
||||
export interface TrackListItem {
|
||||
|
@ -107,7 +107,7 @@ export const registerForShow = (): AppThunk => async (dispatch, getState) => {
|
|||
NavbarState.showAlert({
|
||||
color: "warning",
|
||||
content: "You are not WebStudio Trained and cannot go live.",
|
||||
closure: 7000
|
||||
closure: 7000,
|
||||
})
|
||||
);
|
||||
return;
|
||||
|
@ -139,7 +139,7 @@ export const registerForShow = (): AppThunk => async (dispatch, getState) => {
|
|||
NavbarState.showAlert({
|
||||
content: e.message,
|
||||
color: "danger",
|
||||
closure: 10000
|
||||
closure: 10000,
|
||||
})
|
||||
);
|
||||
if (streamer) {
|
||||
|
@ -168,7 +168,7 @@ export const cancelTimeslot = (): AppThunk => async (dispatch, getState) => {
|
|||
NavbarState.showAlert({
|
||||
content: e.message,
|
||||
color: "danger",
|
||||
closure: 10000
|
||||
closure: 10000,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
|
@ -203,7 +203,7 @@ export function sendBroadcastRegister(
|
|||
memberid: memberid,
|
||||
timeslotid: timeslotid,
|
||||
sourceid: sourceid,
|
||||
wsid: wsID
|
||||
wsid: wsID,
|
||||
} as any;
|
||||
return broadcastApiRequest("/registerTimeslot", "POST", payload);
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ export function sendBroadcastCancel(
|
|||
connid: number | null
|
||||
): Promise<string | null> {
|
||||
return broadcastApiRequest("/cancelTimeslot", "POST", {
|
||||
connid: connid
|
||||
connid: connid,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,7 @@ export function sendBroadcastChange(
|
|||
sourceid: sourceid,
|
||||
beginning: beginning,
|
||||
middle: middle,
|
||||
end: end
|
||||
end: end,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -286,7 +286,7 @@ export function sendTracklistStart(trackid: number): Promise<TrackListItem> {
|
|||
return myradioApiRequest("/tracklistItem", "POST", {
|
||||
trackid: trackid,
|
||||
sourceid: "w",
|
||||
state: "c"
|
||||
state: "c",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -296,14 +296,14 @@ export const goOnAir = (): AppThunk => async (dispatch, getState) => {
|
|||
NavbarState.showAlert({
|
||||
color: "warning",
|
||||
content: "You are not WebStudio Trained and cannot go live.",
|
||||
closure: 7000
|
||||
closure: 7000,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
console.log("starting streamer.");
|
||||
streamer = new WebRTCStreamer(MixerState.destination.stream, dispatch);
|
||||
streamer.addConnectionStateListener(state => {
|
||||
streamer.addConnectionStateListener((state) => {
|
||||
dispatch(broadcastState.actions.setConnectionState(state));
|
||||
if (state === "CONNECTION_LOST") {
|
||||
// un-register if we drop, let the user manually reconnect
|
||||
|
@ -316,7 +316,7 @@ export const goOnAir = (): AppThunk => async (dispatch, getState) => {
|
|||
await streamer.start();
|
||||
};
|
||||
|
||||
export const stopStreaming = (): AppThunk => async dispatch => {
|
||||
export const stopStreaming = (): AppThunk => async (dispatch) => {
|
||||
if (streamer) {
|
||||
await streamer.stop();
|
||||
streamer = null;
|
||||
|
@ -327,15 +327,15 @@ export const stopStreaming = (): AppThunk => async dispatch => {
|
|||
|
||||
let recorder: RecordingStreamer;
|
||||
|
||||
export const startRecording = (): AppThunk => async dispatch => {
|
||||
export const startRecording = (): AppThunk => async (dispatch) => {
|
||||
recorder = new RecordingStreamer(MixerState.destination.stream);
|
||||
recorder.addConnectionStateListener(state => {
|
||||
recorder.addConnectionStateListener((state) => {
|
||||
dispatch(broadcastState.actions.setRecordingState(state));
|
||||
});
|
||||
await recorder.start();
|
||||
};
|
||||
|
||||
export const stopRecording = (): AppThunk => async dispatch => {
|
||||
export const stopRecording = (): AppThunk => async (dispatch) => {
|
||||
if (recorder) {
|
||||
await recorder.stop();
|
||||
} else {
|
||||
|
|
|
@ -20,6 +20,6 @@ export abstract class Streamer {
|
|||
}
|
||||
|
||||
protected onStateChange(state: ConnectionStateEnum) {
|
||||
this.csListeners.forEach(l => l(state));
|
||||
this.csListeners.forEach((l) => l(state));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ export async function createLoudnessMeasurement(
|
|||
await input.context.audioWorklet.addModule(workletUrl);
|
||||
const processor = new AudioWorkletNode(input.context, "loudness-processor", {
|
||||
numberOfInputs: 1,
|
||||
numberOfOutputs: 0
|
||||
numberOfOutputs: 0,
|
||||
});
|
||||
processor.port.onmessage = evt => {
|
||||
processor.port.onmessage = (evt) => {
|
||||
callback(evt.data);
|
||||
};
|
||||
input.connect(processor);
|
||||
|
|
|
@ -34,7 +34,7 @@ class DBFSPeakProcessor extends AudioWorkletProcessor {
|
|||
}
|
||||
this.port.postMessage({
|
||||
peak,
|
||||
loudness: -Infinity // TODO
|
||||
loudness: -Infinity, // TODO
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ class PPMPeakProcessor extends AudioWorkletProcessor {
|
|||
this.calcIntermediate(inputs[0]);
|
||||
this.port.postMessage({
|
||||
peak: this.convert(this.intermediateValue[0]),
|
||||
loudness: this.intermediateValue[0]
|
||||
loudness: this.intermediateValue[0],
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import {
|
|||
createSlice,
|
||||
PayloadAction,
|
||||
Dispatch,
|
||||
Middleware
|
||||
Middleware,
|
||||
} from "@reduxjs/toolkit";
|
||||
import fetchProgress, { FetchProgressData } from "fetch-progress";
|
||||
import Between from "between.js";
|
||||
|
@ -127,7 +127,7 @@ const mixerState = createSlice({
|
|||
autoAdvance: true,
|
||||
repeat: "none",
|
||||
tracklistItemID: -1,
|
||||
loadError: false
|
||||
loadError: false,
|
||||
},
|
||||
{
|
||||
loadedItem: null,
|
||||
|
@ -144,7 +144,7 @@ const mixerState = createSlice({
|
|||
autoAdvance: true,
|
||||
repeat: "none",
|
||||
tracklistItemID: -1,
|
||||
loadError: false
|
||||
loadError: false,
|
||||
},
|
||||
{
|
||||
loadedItem: null,
|
||||
|
@ -161,8 +161,8 @@ const mixerState = createSlice({
|
|||
autoAdvance: true,
|
||||
repeat: "none",
|
||||
tracklistItemID: -1,
|
||||
loadError: false
|
||||
}
|
||||
loadError: false,
|
||||
},
|
||||
],
|
||||
mic: {
|
||||
open: false,
|
||||
|
@ -171,8 +171,8 @@ const mixerState = createSlice({
|
|||
baseGain: 1,
|
||||
openError: null,
|
||||
id: "None",
|
||||
calibration: false
|
||||
}
|
||||
calibration: false,
|
||||
},
|
||||
} as MixerState,
|
||||
reducers: {
|
||||
loadItem(
|
||||
|
@ -254,7 +254,7 @@ const mixerState = createSlice({
|
|||
state.players[action.payload.player].timeRemaining = timeRemaining;
|
||||
},
|
||||
updateTimeEndingAt(state) {
|
||||
state.players.forEach(player => {
|
||||
state.players.forEach((player) => {
|
||||
let date = new Date();
|
||||
date.setSeconds(date.getSeconds() + player.timeRemaining);
|
||||
player.timeEndingAt = date.toLocaleString("en-GB").split(" ")[1];
|
||||
|
@ -323,8 +323,8 @@ const mixerState = createSlice({
|
|||
},
|
||||
stopMicCalibration(state) {
|
||||
state.mic.calibration = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default mixerState.reducer;
|
||||
|
@ -384,8 +384,8 @@ export const load = (
|
|||
backend: "MediaElementWebAudio",
|
||||
responsive: true,
|
||||
xhr: {
|
||||
credentials: "include"
|
||||
} as any
|
||||
credentials: "include",
|
||||
} as any,
|
||||
});
|
||||
|
||||
wavesurfer.on("ready", () => {
|
||||
|
@ -393,13 +393,13 @@ export const load = (
|
|||
dispatch(
|
||||
mixerState.actions.setTimeLength({
|
||||
player,
|
||||
time: wavesurfer.getDuration()
|
||||
time: wavesurfer.getDuration(),
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
mixerState.actions.setTimeCurrent({
|
||||
player,
|
||||
time: 0
|
||||
time: 0,
|
||||
})
|
||||
);
|
||||
dispatch(updateTimeEnding());
|
||||
|
@ -415,7 +415,7 @@ export const load = (
|
|||
dispatch(
|
||||
mixerState.actions.setPlayerState({
|
||||
player,
|
||||
state: wavesurfer.getCurrentTime() === 0 ? "stopped" : "paused"
|
||||
state: wavesurfer.getCurrentTime() === 0 ? "stopped" : "paused",
|
||||
})
|
||||
);
|
||||
});
|
||||
|
@ -423,7 +423,7 @@ export const load = (
|
|||
dispatch(
|
||||
mixerState.actions.setTimeCurrent({
|
||||
player,
|
||||
time: wavesurfer.getCurrentTime()
|
||||
time: wavesurfer.getCurrentTime(),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
@ -439,7 +439,7 @@ export const load = (
|
|||
if ("channel" in item) {
|
||||
// it's not in the CML/libraries "column"
|
||||
const itsChannel = getState().showplan.plan!.filter(
|
||||
x => x.channel === item.channel
|
||||
(x) => x.channel === item.channel
|
||||
);
|
||||
const itsIndex = itsChannel.indexOf(item);
|
||||
if (itsIndex === itsChannel.length - 1) {
|
||||
|
@ -450,7 +450,7 @@ export const load = (
|
|||
if ("channel" in item) {
|
||||
// it's not in the CML/libraries "column"
|
||||
const itsChannel = getState().showplan.plan!.filter(
|
||||
x => x.channel === item.channel
|
||||
(x) => x.channel === item.channel
|
||||
);
|
||||
const itsIndex = itsChannel.indexOf(item);
|
||||
if (itsIndex > -1 && itsIndex !== itsChannel.length - 1) {
|
||||
|
@ -469,7 +469,7 @@ export const load = (
|
|||
dispatch(
|
||||
mixerState.actions.setTimeCurrent({
|
||||
player,
|
||||
time: wavesurfer.getCurrentTime()
|
||||
time: wavesurfer.getCurrentTime(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -479,7 +479,7 @@ export const load = (
|
|||
const signal = loadAbortControllers[player].signal; // hang on to the signal, even if its controller gets replaced
|
||||
const result = await fetch(url, {
|
||||
credentials: "include",
|
||||
signal
|
||||
signal,
|
||||
}).then(
|
||||
fetchProgress({
|
||||
// implement onProgress method
|
||||
|
@ -490,7 +490,7 @@ export const load = (
|
|||
mixerState.actions.itemLoadPercentage({ player, percent })
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
const rawData = await result.arrayBuffer();
|
||||
|
@ -524,17 +524,11 @@ export const load = (
|
|||
}
|
||||
};
|
||||
|
||||
export const updateTimeEnding = (): AppThunk => async dispatch => {
|
||||
export const updateTimeEnding = (): AppThunk => async (dispatch) => {
|
||||
if (!timerInterval) {
|
||||
timerInterval = later.setInterval(
|
||||
() => {
|
||||
dispatch(mixerState.actions.updateTimeEndingAt());
|
||||
},
|
||||
later.parse
|
||||
.recur()
|
||||
.every(1)
|
||||
.second()
|
||||
);
|
||||
timerInterval = later.setInterval(() => {
|
||||
dispatch(mixerState.actions.updateTimeEndingAt());
|
||||
}, later.parse.recur().every(1).second());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -606,11 +600,11 @@ export const stop = (player: number): AppThunk => (dispatch, getState) => {
|
|||
export const {
|
||||
toggleAutoAdvance,
|
||||
togglePlayOnLoad,
|
||||
toggleRepeat
|
||||
toggleRepeat,
|
||||
} = mixerState.actions;
|
||||
|
||||
export const redrawWavesurfers = (): AppThunk => () => {
|
||||
wavesurfers.forEach(function(item) {
|
||||
wavesurfers.forEach(function (item) {
|
||||
item.drawBuffer();
|
||||
});
|
||||
};
|
||||
|
@ -648,7 +642,7 @@ export const setVolume = (
|
|||
// If we've just hit the button/key to go to the same value as that fade,
|
||||
// stop it and immediately cut to the target value.
|
||||
// Otherwise, stop id and start a new fade.
|
||||
playerGainTweens[player].tweens.forEach(tween => tween.pause());
|
||||
playerGainTweens[player].tweens.forEach((tween) => tween.pause());
|
||||
if (playerGainTweens[player].target === level) {
|
||||
delete playerGainTweens[player];
|
||||
dispatch(mixerState.actions.setPlayerVolume({ player, volume: uiLevel }));
|
||||
|
@ -682,7 +676,7 @@ export const setVolume = (
|
|||
|
||||
playerGainTweens[player] = {
|
||||
target: level,
|
||||
tweens: [volumeTween, gainTween]
|
||||
tweens: [volumeTween, gainTween],
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -712,8 +706,8 @@ export const openMicrophone = (micID: string): AppThunk => async (
|
|||
echoCancellation: false,
|
||||
autoGainControl: false,
|
||||
noiseSuppression: false,
|
||||
latency: 0.01
|
||||
}
|
||||
latency: 0.01,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof DOMException) {
|
||||
|
@ -758,9 +752,9 @@ export const openMicrophone = (micID: string): AppThunk => async (
|
|||
}
|
||||
};
|
||||
|
||||
export const setMicVolume = (
|
||||
level: MicVolumePresetEnum
|
||||
): AppThunk => dispatch => {
|
||||
export const setMicVolume = (level: MicVolumePresetEnum): AppThunk => (
|
||||
dispatch
|
||||
) => {
|
||||
// no tween fuckery here, just cut the level
|
||||
const levelVal = level === "full" ? 1 : 0;
|
||||
// actually, that's a lie - if we're turning it off we delay it a little to compensate for
|
||||
|
@ -832,11 +826,9 @@ export const stopMicCalibration = (): AppThunk => (dispatch, getState) => {
|
|||
dispatch(mixerState.actions.stopMicCalibration());
|
||||
};
|
||||
|
||||
export const mixerMiddleware: Middleware<
|
||||
{},
|
||||
RootState,
|
||||
Dispatch<any>
|
||||
> = store => next => action => {
|
||||
export const mixerMiddleware: Middleware<{}, RootState, Dispatch<any>> = (
|
||||
store
|
||||
) => (next) => (action) => {
|
||||
const oldState = store.getState().mixer;
|
||||
const result = next(action);
|
||||
const newState = store.getState().mixer;
|
||||
|
@ -863,7 +855,7 @@ export const mixerKeyboardShortcutsMiddleware: Middleware<
|
|||
{},
|
||||
RootState,
|
||||
Dispatch<any>
|
||||
> = store => {
|
||||
> = (store) => {
|
||||
Keys("q", () => {
|
||||
store.dispatch(play(0));
|
||||
});
|
||||
|
@ -925,5 +917,5 @@ export const mixerKeyboardShortcutsMiddleware: Middleware<
|
|||
store.dispatch(setMicVolume(state.volume === 1 ? "off" : "full"));
|
||||
});
|
||||
|
||||
return next => action => next(action);
|
||||
return (next) => (action) => next(action);
|
||||
};
|
||||
|
|
|
@ -2,44 +2,45 @@
|
|||
* Taken from http://stackoverflow.com/a/36289507/995325 */
|
||||
@media (max-width: 991px) {
|
||||
.navbar-header {
|
||||
float: none;
|
||||
float: none;
|
||||
}
|
||||
.navbar-left, .navbar-right {
|
||||
float: none !important;
|
||||
.navbar-left,
|
||||
.navbar-right {
|
||||
float: none !important;
|
||||
}
|
||||
.navbar-toggle {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
.navbar-collapse {
|
||||
border-top: 1px solid transparent;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
border-top: 1px solid transparent;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.navbar-fixed-top {
|
||||
top: 0;
|
||||
border-width: 0 0 1px;
|
||||
top: 0;
|
||||
border-width: 0 0 1px;
|
||||
}
|
||||
.navbar-collapse.collapse {
|
||||
display: none !important;
|
||||
display: none !important;
|
||||
}
|
||||
.navbar-nav {
|
||||
float: none !important;
|
||||
margin-top: 7.5px;
|
||||
float: none !important;
|
||||
margin-top: 7.5px;
|
||||
}
|
||||
.navbar-nav > li {
|
||||
float: none;
|
||||
float: none;
|
||||
}
|
||||
.navbar-nav > li > a {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.collapse.in {
|
||||
display: block !important;
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
/** MyRadio bootstrap navbar overides taken from http://work.smarchal.com/twbscolor/css/2D425F2D425Fffffffe1e1e11 **/
|
||||
.navbar-ury {
|
||||
background-color: #2D333C;
|
||||
border-color: #2D425F;
|
||||
background-color: #2d333c;
|
||||
border-color: #2d425f;
|
||||
border: none;
|
||||
height: 60px;
|
||||
max-height: 7vh;
|
||||
|
@ -47,9 +48,8 @@
|
|||
.navbar-ury .navbar-brand {
|
||||
color: #ffffff;
|
||||
padding: 10px 15px; /** added to center the logo **/
|
||||
line-height: 30px; /** added to center the logo **/
|
||||
line-height: 30px; /** added to center the logo **/
|
||||
margin-right: 0;
|
||||
|
||||
}
|
||||
.navbar-ury .navbar-brand img {
|
||||
max-height: 100%;
|
||||
|
@ -90,10 +90,10 @@
|
|||
.navbar-ury .navbar-nav > li > a:hover,
|
||||
.navbar-ury .navbar-nav > li > a:focus {
|
||||
color: #e1e1e1;
|
||||
background-color: #2D425F;
|
||||
background-color: #2d425f;
|
||||
}
|
||||
.navbar-ury .navbar-nav > li > .dropdown-menu {
|
||||
background-color: #2D333C;
|
||||
background-color: #2d333c;
|
||||
border-top: none;
|
||||
}
|
||||
.navbar-ury .navbar-nav > li > .dropdown-menu > a {
|
||||
|
@ -102,35 +102,35 @@
|
|||
.navbar-ury .navbar-nav > li > .dropdown-menu > a:hover,
|
||||
.navbar-ury .navbar-nav > li > .dropdown-menu > a:focus {
|
||||
color: #e1e1e1;
|
||||
background-color: #2D425F;
|
||||
background-color: #2d425f;
|
||||
}
|
||||
.navbar-ury .navbar-nav > li > .dropdown-menu > .divider {
|
||||
background-color: #2D425F;
|
||||
background-color: #2d425f;
|
||||
}
|
||||
.navbar-ury .navbar-nav .open .dropdown-menu > .active > a,
|
||||
.navbar-ury .navbar-nav .open .dropdown-menu > .active > a:hover,
|
||||
.navbar-ury .navbar-nav .open .dropdown-menu > .active > a:focus {
|
||||
color: #e1e1e1;
|
||||
background-color: #2D333C;
|
||||
background-color: #2d333c;
|
||||
}
|
||||
.navbar-ury .navbar-nav > .active > a,
|
||||
.navbar-ury .navbar-nav > .active > a:hover,
|
||||
.navbar-ury .navbar-nav > .active > a:focus {
|
||||
color: #e1e1e1;
|
||||
background-color: #2D333C;
|
||||
background-color: #2d333c;
|
||||
}
|
||||
.navbar-ury .navbar-nav > .open > a,
|
||||
.navbar-ury .navbar-nav > .open > a:hover,
|
||||
.navbar-ury .navbar-nav > .open > a:focus {
|
||||
color: #e1e1e1;
|
||||
background-color: #2D425F !important;
|
||||
background-color: #2d425f !important;
|
||||
}
|
||||
.navbar-ury .navbar-toggle {
|
||||
border-color: #2D425F;
|
||||
border-color: #2d425f;
|
||||
}
|
||||
.navbar-ury .navbar-toggle:hover,
|
||||
.navbar-ury .navbar-toggle:focus {
|
||||
background-color: #2D425F;
|
||||
background-color: #2d425f;
|
||||
}
|
||||
.navbar-ury .navbar-toggle .icon-bar {
|
||||
background-color: #ffffff;
|
||||
|
@ -147,7 +147,7 @@
|
|||
}
|
||||
.navbar-ury .navbar-brand:hover {
|
||||
color: #e1e1e1;
|
||||
background-color: #2D425F !important;
|
||||
background-color: #2d425f !important;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
|
@ -162,22 +162,22 @@
|
|||
.navbar-ury .navbar-nav .open .dropdown-menu > .active > a:hover,
|
||||
.navbar-ury .navbar-nav .open .dropdown-menu > .active > a:focus {
|
||||
color: #e1e1e1;
|
||||
background-color: #2D425F;
|
||||
background-color: #2d425f;
|
||||
}
|
||||
}
|
||||
|
||||
.alertbar {
|
||||
position: fixed !important;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
min-height: 38.5px;
|
||||
z-index: 99999;
|
||||
transform: translateY(-50px);
|
||||
transition: 400ms transform;
|
||||
&.visible {
|
||||
transform: translateY(0);
|
||||
}
|
||||
position: fixed !important;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
min-height: 38.5px;
|
||||
z-index: 99999;
|
||||
transform: translateY(-50px);
|
||||
transition: 400ms transform;
|
||||
&.visible {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
#timelord {
|
||||
|
@ -194,4 +194,4 @@
|
|||
#timelord .time {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ interface NavbarState {
|
|||
const navbarState = createSlice({
|
||||
name: "navbar",
|
||||
initialState: {
|
||||
currentAlert: null
|
||||
currentAlert: null,
|
||||
} as NavbarState,
|
||||
reducers: {
|
||||
showAlert(state, action: PayloadAction<Alert>) {
|
||||
|
@ -29,8 +29,8 @@ const navbarState = createSlice({
|
|||
},
|
||||
closeAlert(state) {
|
||||
state.currentAlert = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default navbarState.reducer;
|
||||
|
|
|
@ -4,7 +4,7 @@ import logo from "../assets/images/webstudio.svg";
|
|||
import {
|
||||
MYRADIO_BASE_URL,
|
||||
MYRADIO_NON_API_BASE,
|
||||
BROADCAST_API_BASE_URL
|
||||
BROADCAST_API_BASE_URL,
|
||||
} from "../api";
|
||||
|
||||
export function AboutTab() {
|
||||
|
|
|
@ -22,7 +22,7 @@ export function AdvancedTab() {
|
|||
className="form-control"
|
||||
id="broadcastSourceSelect"
|
||||
value={broadcastState.sourceID}
|
||||
onChange={e =>
|
||||
onChange={(e) =>
|
||||
dispatch(
|
||||
changeBroadcastSetting("sourceID", parseInt(e.target.value))
|
||||
)
|
||||
|
@ -39,7 +39,7 @@ export function AdvancedTab() {
|
|||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={broadcastState.autoNewsBeginning}
|
||||
onChange={e =>
|
||||
onChange={(e) =>
|
||||
dispatch(
|
||||
changeBroadcastSetting("autoNewsBeginning", e.target.checked)
|
||||
)
|
||||
|
@ -52,7 +52,7 @@ export function AdvancedTab() {
|
|||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={broadcastState.autoNewsMiddle}
|
||||
onChange={e =>
|
||||
onChange={(e) =>
|
||||
dispatch(changeBroadcastSetting("autoNewsMiddle", e.target.checked))
|
||||
}
|
||||
/>
|
||||
|
@ -65,7 +65,7 @@ export function AdvancedTab() {
|
|||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={broadcastState.autoNewsEnd}
|
||||
onChange={e =>
|
||||
onChange={(e) =>
|
||||
dispatch(changeBroadcastSetting("autoNewsEnd", e.target.checked))
|
||||
}
|
||||
/>
|
||||
|
@ -81,12 +81,12 @@ export function AdvancedTab() {
|
|||
className="form-control"
|
||||
id="broadcastSourceSelect"
|
||||
value={settings.tracklist}
|
||||
onChange={e =>
|
||||
onChange={(e) =>
|
||||
dispatch(
|
||||
changeSetting({
|
||||
key: "tracklist",
|
||||
// @ts-ignore
|
||||
val: e.target.value
|
||||
val: e.target.value,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -101,11 +101,11 @@ export function AdvancedTab() {
|
|||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={settings.showDebugInfo}
|
||||
onChange={e =>
|
||||
onChange={(e) =>
|
||||
dispatch(
|
||||
changeSetting({
|
||||
key: "showDebugInfo",
|
||||
val: e.target.checked
|
||||
val: e.target.checked,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -119,11 +119,11 @@ export function AdvancedTab() {
|
|||
className="form-check-input"
|
||||
type="checkbox"
|
||||
checked={settings.enableRecording}
|
||||
onChange={e =>
|
||||
onChange={(e) =>
|
||||
dispatch(
|
||||
changeSetting({
|
||||
key: "enableRecording",
|
||||
val: e.target.checked
|
||||
val: e.target.checked,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ type MicErrorEnum =
|
|||
|
||||
function reduceToInputs(devices: MediaDeviceInfo[]) {
|
||||
var temp: MediaDeviceInfo[] = [];
|
||||
devices.forEach(device => {
|
||||
devices.forEach((device) => {
|
||||
if (device.kind === "audioinput") {
|
||||
temp.push(device);
|
||||
}
|
||||
|
@ -91,11 +91,11 @@ export function MicTab() {
|
|||
className="form-control"
|
||||
style={{ width: "100%" }}
|
||||
value={nextMicSource}
|
||||
onChange={e => setMicSource(e.target.value)}
|
||||
onChange={(e) => setMicSource(e.target.value)}
|
||||
disabled={micList === null}
|
||||
>
|
||||
<option value={"$NONE"} disabled label="Choose a microphone" />
|
||||
{(micList || []).map(function(e, i) {
|
||||
{(micList || []).map(function (e, i) {
|
||||
return (
|
||||
<option value={e.deviceId} key={i}>
|
||||
{e.label !== "" ? e.label : e.deviceId}
|
||||
|
@ -138,7 +138,7 @@ export function MicTab() {
|
|||
max={3}
|
||||
step={0.05}
|
||||
value={state.baseGain}
|
||||
onChange={e =>
|
||||
onChange={(e) =>
|
||||
dispatch(MixerState.setMicBaseGain(parseFloat(e.target.value)))
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -34,15 +34,15 @@ export function StatsTab() {
|
|||
}
|
||||
return (
|
||||
<>
|
||||
{Array.from(stats).map(stat => (
|
||||
{Array.from(stats).map((stat) => (
|
||||
<div key={stat[1].id}>
|
||||
<h2>Report: {stat[1].type}</h2>
|
||||
<div>
|
||||
<strong>ID:</strong> {stat[1].id}
|
||||
</div>
|
||||
{Object.keys(stat[1])
|
||||
.filter(x => x !== "id" && x !== "type" && x !== "timestamp")
|
||||
.map(key => (
|
||||
.filter((x) => x !== "id" && x !== "type" && x !== "timestamp")
|
||||
.map((key) => (
|
||||
<div key={key}>
|
||||
<strong>{key}</strong>: {stat[1][key]}
|
||||
</div>
|
||||
|
|
|
@ -11,7 +11,7 @@ const settingsState = createSlice({
|
|||
initialState: {
|
||||
showDebugInfo: false,
|
||||
enableRecording: false,
|
||||
tracklist: "while_live"
|
||||
tracklist: "while_live",
|
||||
} as Settings,
|
||||
reducers: {
|
||||
changeSetting<K extends keyof Settings>(
|
||||
|
@ -19,8 +19,8 @@ const settingsState = createSlice({
|
|||
action: PayloadAction<{ key: K; val: Settings[K] }>
|
||||
) {
|
||||
state[action.payload.key] = action.payload.val;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default settingsState.reducer;
|
||||
|
|
|
@ -2,7 +2,7 @@ import {
|
|||
createSlice,
|
||||
PayloadAction,
|
||||
Middleware,
|
||||
Dispatch
|
||||
Dispatch,
|
||||
} from "@reduxjs/toolkit";
|
||||
import { RootState } from "../rootReducer";
|
||||
|
||||
|
@ -14,7 +14,7 @@ const optionsMenuState = createSlice({
|
|||
name: "optionsMenu",
|
||||
initialState: {
|
||||
open: false,
|
||||
currentTab: "mic" as OptionsTabIDsEnum
|
||||
currentTab: "mic" as OptionsTabIDsEnum,
|
||||
},
|
||||
reducers: {
|
||||
open(state) {
|
||||
|
@ -29,19 +29,17 @@ const optionsMenuState = createSlice({
|
|||
},
|
||||
changeTab(state, action: PayloadAction<OptionsTabIDsEnum>) {
|
||||
state.currentTab = action.payload;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default optionsMenuState.reducer;
|
||||
|
||||
export const { open, openToTab, close, changeTab } = optionsMenuState.actions;
|
||||
|
||||
export const tabSyncMiddleware: Middleware<
|
||||
{},
|
||||
RootState,
|
||||
Dispatch
|
||||
> = store => next => action => {
|
||||
export const tabSyncMiddleware: Middleware<{}, RootState, Dispatch> = (
|
||||
store
|
||||
) => (next) => (action) => {
|
||||
const oldState = store.getState();
|
||||
const result = next(action);
|
||||
const newState = store.getState();
|
||||
|
|
|
@ -19,7 +19,7 @@ const rootReducer = combineReducers({
|
|||
session: sessionReducer,
|
||||
navbar: NavbarReducer,
|
||||
optionsMenu: OptionsMenuReducer,
|
||||
settings: SettingsState
|
||||
settings: SettingsState,
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof rootReducer>;
|
||||
|
@ -28,7 +28,7 @@ const persistenceConfig: PersistConfig<RootState> = {
|
|||
key: "root",
|
||||
storage: webStorage,
|
||||
whitelist: ["settings"],
|
||||
stateReconciler: autoMergeLevel2
|
||||
stateReconciler: autoMergeLevel2,
|
||||
};
|
||||
|
||||
const persistedReducer = persistReducer(persistenceConfig, rootReducer);
|
||||
|
|
|
@ -68,7 +68,7 @@ export function register(config?: Config) {
|
|||
function registerValidSW(swUrl: string, config?: Config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
.then((registration) => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
|
@ -85,7 +85,7 @@ function registerValidSW(swUrl: string, config?: Config) {
|
|||
color: "info",
|
||||
closure: 10000,
|
||||
content:
|
||||
"A new version of WebStudio is available! Please close and reopen all WebStudio tabs to use it."
|
||||
"A new version of WebStudio is available! Please close and reopen all WebStudio tabs to use it.",
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -108,7 +108,7 @@ function registerValidSW(swUrl: string, config?: Config) {
|
|||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
console.error("Error during service worker registration:", error);
|
||||
});
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ function registerValidSW(swUrl: string, config?: Config) {
|
|||
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (
|
||||
|
@ -124,7 +124,7 @@ function checkValidServiceWorker(swUrl: string, config?: Config) {
|
|||
(contentType != null && contentType.indexOf("javascript") === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
|
@ -140,7 +140,7 @@ function checkValidServiceWorker(swUrl: string, config?: Config) {
|
|||
color: "secondary",
|
||||
closure: 5000,
|
||||
content:
|
||||
"No internet connection available. WebStudio is running in offline mode."
|
||||
"No internet connection available. WebStudio is running in offline mode.",
|
||||
})
|
||||
);
|
||||
});
|
||||
|
@ -148,7 +148,7 @@ function checkValidServiceWorker(swUrl: string, config?: Config) {
|
|||
|
||||
export function unregister() {
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { MYRADIO_NON_API_BASE } from "../api";
|
|||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { RootState } from "../rootReducer";
|
||||
|
||||
const SessionHandler: React.FC = function() {
|
||||
const SessionHandler: React.FC = function () {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -38,7 +38,7 @@ const SessionHandler: React.FC = function() {
|
|||
userLoading,
|
||||
userLoadError,
|
||||
timeslotLoading,
|
||||
timeslotLoadError
|
||||
timeslotLoadError,
|
||||
} = useSelector((state: RootState) => state.session);
|
||||
|
||||
var redirect_url = encodeURIComponent(window.location.toString());
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
getCurrentApiUser,
|
||||
Timeslot,
|
||||
getCurrentApiTimeslot,
|
||||
doesCurrentUserHavePermission
|
||||
doesCurrentUserHavePermission,
|
||||
} from "../api";
|
||||
|
||||
import raygun from "raygun4js";
|
||||
|
@ -31,7 +31,7 @@ const sessionState = createSlice({
|
|||
userLoading: false,
|
||||
userLoadError: null,
|
||||
timeslotLoading: false,
|
||||
timeslotLoadError: null
|
||||
timeslotLoadError: null,
|
||||
} as sessionState,
|
||||
reducers: {
|
||||
setCurrentUser(
|
||||
|
@ -67,8 +67,8 @@ const sessionState = createSlice({
|
|||
},
|
||||
getState(state) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default sessionState.reducer;
|
||||
|
@ -77,17 +77,17 @@ export const getCurrentUser = (): AppThunk => async (dispatch, getState) => {
|
|||
return getState().session.currentUser;
|
||||
};
|
||||
|
||||
export const getUser = (): AppThunk => async dispatch => {
|
||||
export const getUser = (): AppThunk => async (dispatch) => {
|
||||
dispatch(sessionState.actions.getUserStarting());
|
||||
try {
|
||||
const [user, canBroadcast] = await Promise.all([
|
||||
getCurrentApiUser(),
|
||||
doesCurrentUserHavePermission(BROADCAST_PERMISSION_ID)
|
||||
doesCurrentUserHavePermission(BROADCAST_PERMISSION_ID),
|
||||
]);
|
||||
raygun("setUser", {
|
||||
identifier: user.memberid.toString(10),
|
||||
firstName: user.fname,
|
||||
fullName: user.fname + " " + user.sname
|
||||
fullName: user.fname + " " + user.sname,
|
||||
});
|
||||
dispatch(sessionState.actions.setCurrentUser({ user, canBroadcast }));
|
||||
} catch (e) {
|
||||
|
@ -96,7 +96,7 @@ export const getUser = (): AppThunk => async dispatch => {
|
|||
}
|
||||
};
|
||||
|
||||
export const getTimeslot = (): AppThunk => async dispatch => {
|
||||
export const getTimeslot = (): AppThunk => async (dispatch) => {
|
||||
dispatch(sessionState.actions.getTimeslotStarting());
|
||||
try {
|
||||
const timeslot = await getCurrentApiTimeslot();
|
||||
|
|
|
@ -13,7 +13,7 @@ export const TS_ITEM_MENU_ID = "SongMenu";
|
|||
export const Item = memo(function Item({
|
||||
item: x,
|
||||
index,
|
||||
column
|
||||
column,
|
||||
}: {
|
||||
item: PlanItem | Track | AuxItem;
|
||||
index: number;
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Button } from "reactstrap";
|
|||
|
||||
export function PisModal({
|
||||
close,
|
||||
isOpen
|
||||
isOpen,
|
||||
}: {
|
||||
close: () => any;
|
||||
isOpen: boolean;
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
FaRedo,
|
||||
FaPlay,
|
||||
FaPause,
|
||||
FaStop
|
||||
FaStop,
|
||||
} from "react-icons/fa";
|
||||
import { RootState } from "../rootReducer";
|
||||
import * as MixerState from "../mixer/state";
|
||||
|
@ -168,7 +168,7 @@ export function Player({ id }: { id: number }) {
|
|||
style={
|
||||
playerState.loading !== -1
|
||||
? {
|
||||
width: playerState.loading * 100 + "%"
|
||||
width: playerState.loading * 100 + "%",
|
||||
}
|
||||
: {}
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ export function Player({ id }: { id: number }) {
|
|||
width:
|
||||
(USE_REAL_GAIN_VALUE ? playerState.gain : playerState.volume) *
|
||||
100 +
|
||||
"%"
|
||||
"%",
|
||||
}}
|
||||
></div>
|
||||
<button onClick={() => dispatch(MixerState.setVolume(id, "off"))}>
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
Droppable,
|
||||
DragDropContext,
|
||||
DropResult,
|
||||
ResponderProvided
|
||||
ResponderProvided,
|
||||
} from "react-beautiful-dnd";
|
||||
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
|
@ -21,7 +21,7 @@ import {
|
|||
moveItem,
|
||||
addItem,
|
||||
removeItem,
|
||||
getPlaylists
|
||||
getPlaylists,
|
||||
} from "./state";
|
||||
|
||||
import * as MixerState from "../mixer/state";
|
||||
|
@ -31,7 +31,7 @@ import {
|
|||
CentralMusicLibrary,
|
||||
CML_CACHE,
|
||||
AuxLibrary,
|
||||
AUX_CACHE
|
||||
AUX_CACHE,
|
||||
} from "./libraries";
|
||||
import { Player } from "./Player";
|
||||
|
||||
|
@ -54,7 +54,7 @@ function Column({ id, data }: { id: number; data: PlanItem[] }) {
|
|||
{typeof data[id] === "undefined"
|
||||
? null
|
||||
: data
|
||||
.filter(x => x.channel === id)
|
||||
.filter((x) => x.channel === id)
|
||||
.sort((a, b) => a.weight - b.weight)
|
||||
.map((x, index) => (
|
||||
<Item
|
||||
|
@ -91,20 +91,20 @@ function LibraryColumn() {
|
|||
className="form-control"
|
||||
style={{ width: "100%" }}
|
||||
value={sauce}
|
||||
onChange={e => setSauce(e.target.value)}
|
||||
onChange={(e) => setSauce(e.target.value)}
|
||||
>
|
||||
<option value={"None"} disabled>
|
||||
Choose a library
|
||||
</option>
|
||||
<option value={"CentralMusicLibrary"}>Central Music Library</option>
|
||||
<option disabled>Personal Resources</option>
|
||||
{userPlaylists.map(playlist => (
|
||||
{userPlaylists.map((playlist) => (
|
||||
<option key={playlist.managedid} value={playlist.managedid}>
|
||||
{playlist.title}
|
||||
</option>
|
||||
))}
|
||||
<option disabled>Shared Resources</option>
|
||||
{auxPlaylists.map(playlist => (
|
||||
{auxPlaylists.map((playlist) => (
|
||||
<option
|
||||
key={"aux-" + playlist.managedid}
|
||||
value={"aux-" + playlist.managedid}
|
||||
|
@ -140,7 +140,7 @@ function MicControl() {
|
|||
<div
|
||||
className="sp-mixer-buttons-backdrop"
|
||||
style={{
|
||||
width: state.volume * 100 + "%"
|
||||
width: state.volume * 100 + "%",
|
||||
}}
|
||||
></div>
|
||||
<button onClick={() => dispatch(MixerState.setMicVolume("off"))}>
|
||||
|
@ -171,13 +171,15 @@ function incrReducer(state: number, action: any) {
|
|||
return state + 1;
|
||||
}
|
||||
|
||||
const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
||||
const Showplanner: React.FC<{ timeslotId: number }> = function ({
|
||||
timeslotId,
|
||||
}) {
|
||||
const {
|
||||
plan: showplan,
|
||||
planLoadError,
|
||||
planLoading,
|
||||
planSaveError,
|
||||
planSaving
|
||||
planSaving,
|
||||
} = useSelector((state: RootState) => state.showplan);
|
||||
const session = useSelector((state: RootState) => state.session);
|
||||
|
||||
|
@ -189,7 +191,7 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
|||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useBeforeunload(event => event.preventDefault());
|
||||
useBeforeunload((event) => event.preventDefault());
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getShowplan(timeslotId));
|
||||
|
@ -200,7 +202,7 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
|||
if (element) {
|
||||
element.classList.toggle("active");
|
||||
}
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
dispatch(MixerState.redrawWavesurfers());
|
||||
}, 500);
|
||||
}
|
||||
|
@ -224,7 +226,7 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
|||
timeslotitemid: "I" + insertIndex,
|
||||
channel: parseInt(result.destination.droppableId, 10),
|
||||
weight: result.destination.index,
|
||||
...data
|
||||
...data,
|
||||
};
|
||||
dispatch(addItem(timeslotId, newItem));
|
||||
increment(null);
|
||||
|
@ -238,7 +240,7 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
|||
channel: parseInt(result.destination.droppableId, 10),
|
||||
weight: result.destination.index,
|
||||
clean: true,
|
||||
...data
|
||||
...data,
|
||||
} as any;
|
||||
dispatch(addItem(timeslotId, newItem));
|
||||
increment(null);
|
||||
|
@ -247,7 +249,7 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
|||
dispatch(
|
||||
moveItem(timeslotId, result.draggableId, [
|
||||
parseInt(result.destination.droppableId, 10),
|
||||
result.destination.index
|
||||
result.destination.index,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
|
|
@ -29,13 +29,13 @@ export function CentralMusicLibrary() {
|
|||
}
|
||||
setItems([]);
|
||||
setState("searching");
|
||||
searchForTracks(artist, track).then(tracks => {
|
||||
searchForTracks(artist, track).then((tracks) => {
|
||||
if (tracks.length === 0) {
|
||||
setState("no-results");
|
||||
} else {
|
||||
setState("results");
|
||||
}
|
||||
tracks.forEach(track => {
|
||||
tracks.forEach((track) => {
|
||||
const id = itemId(track);
|
||||
if (!(id in CML_CACHE)) {
|
||||
CML_CACHE[id] = track;
|
||||
|
@ -51,14 +51,14 @@ export function CentralMusicLibrary() {
|
|||
type="text"
|
||||
placeholder="Filter by track..."
|
||||
value={track}
|
||||
onChange={e => setTrack(e.target.value)}
|
||||
onChange={(e) => setTrack(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
className="form-control"
|
||||
type="text"
|
||||
placeholder="Filter by artist..."
|
||||
value={artist}
|
||||
onChange={e => setArtist(e.target.value)}
|
||||
onChange={(e) => setArtist(e.target.value)}
|
||||
/>
|
||||
<span
|
||||
className={
|
||||
|
@ -100,7 +100,7 @@ export function AuxLibrary({ libraryId }: { libraryId: string }) {
|
|||
useEffect(() => {
|
||||
async function load() {
|
||||
const libItems = await loadAuxLibrary(libraryId);
|
||||
libItems.forEach(item => {
|
||||
libItems.forEach((item) => {
|
||||
const id = itemId(item);
|
||||
if (!(id in AUX_CACHE)) {
|
||||
AUX_CACHE[id] = item;
|
||||
|
@ -117,14 +117,14 @@ export function AuxLibrary({ libraryId }: { libraryId: string }) {
|
|||
type="text"
|
||||
placeholder="Filter..."
|
||||
value={title}
|
||||
onChange={e => setTitle(e.target.value)}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
/>
|
||||
<Droppable droppableId="$AUX">
|
||||
{(provided, snapshot) => (
|
||||
<div ref={provided.innerRef} {...provided.droppableProps}>
|
||||
{items
|
||||
.filter(
|
||||
its =>
|
||||
(its) =>
|
||||
its.title
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
|
|
|
@ -53,7 +53,7 @@ const initialState: ShowplanState = {
|
|||
planSaving: false,
|
||||
planSaveError: null,
|
||||
auxPlaylists: [],
|
||||
userPlaylists: []
|
||||
userPlaylists: [],
|
||||
};
|
||||
|
||||
const showplan = createSlice({
|
||||
|
@ -84,11 +84,11 @@ const showplan = createSlice({
|
|||
return;
|
||||
}
|
||||
console.log("Applying op sequence", action.payload);
|
||||
action.payload.forEach(op => {
|
||||
action.payload.forEach((op) => {
|
||||
switch (op.op) {
|
||||
case "MoveItem":
|
||||
const item = state.plan!.find(
|
||||
x => itemId(x) === op.timeslotitemid
|
||||
(x) => itemId(x) === op.timeslotitemid
|
||||
)!;
|
||||
item.channel = op.channel;
|
||||
item.weight = op.weight;
|
||||
|
@ -98,7 +98,7 @@ const showplan = createSlice({
|
|||
break;
|
||||
case "RemoveItem":
|
||||
const idx = state.plan!.findIndex(
|
||||
x => itemId(x) === op.timeslotitemid
|
||||
(x) => itemId(x) === op.timeslotitemid
|
||||
);
|
||||
if (idx < 0) {
|
||||
throw new Error();
|
||||
|
@ -121,7 +121,7 @@ const showplan = createSlice({
|
|||
action: PayloadAction<{ ghostId: string; newItemData: TimeslotItem }>
|
||||
) {
|
||||
const idx = state.plan!.findIndex(
|
||||
x => itemId(x) === action.payload.ghostId
|
||||
(x) => itemId(x) === action.payload.ghostId
|
||||
);
|
||||
if (idx < 0) {
|
||||
throw new Error();
|
||||
|
@ -133,8 +133,8 @@ const showplan = createSlice({
|
|||
},
|
||||
addAuxPlaylists(state, action: PayloadAction<api.ManagedPlaylist[]>) {
|
||||
state.auxPlaylists = state.auxPlaylists.concat(action.payload);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default showplan.reducer;
|
||||
|
@ -146,7 +146,7 @@ export const moveItem = (
|
|||
): AppThunk => async (dispatch, getState) => {
|
||||
// Make a copy of the plan, because we are about to engage in FUCKERY.
|
||||
const plan = cloneDeep(getState().showplan.plan!);
|
||||
const itemToMove = plan.find(x => itemId(x) === itemid)!;
|
||||
const itemToMove = plan.find((x) => itemId(x) === itemid)!;
|
||||
if (itemToMove.channel === to[0] && itemToMove.weight === to[1]) {
|
||||
return;
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ export const moveItem = (
|
|||
if (oldChannel === newChannel) {
|
||||
// Moving around in the same channel
|
||||
const itemChan = plan
|
||||
.filter(x => x.channel === oldChannel)
|
||||
.filter((x) => x.channel === oldChannel)
|
||||
.sort((a, b) => a.weight - b.weight);
|
||||
if (oldWeight < newWeight) {
|
||||
// moved the item down (incremented) - everything in between needs decrementing
|
||||
|
@ -196,7 +196,7 @@ export const moveItem = (
|
|||
// First, decrement everything between the old weight and the end of the old channel
|
||||
// (inclusive of old weight, because we've removed the item)
|
||||
const oldChannelData = plan
|
||||
.filter(x => x.channel === oldChannel)
|
||||
.filter((x) => x.channel === oldChannel)
|
||||
.sort((a, b) => a.weight - b.weight);
|
||||
for (let i = oldWeight; i < oldChannelData.length; i++) {
|
||||
const movingItem = oldChannelData[i];
|
||||
|
@ -207,7 +207,7 @@ export const moveItem = (
|
|||
// Then, increment everything between the new weight and the end of the new channel
|
||||
// (again, inclusive)
|
||||
const newChannelData = plan
|
||||
.filter(x => x.channel === newChannel)
|
||||
.filter((x) => x.channel === newChannel)
|
||||
.sort((a, b) => a.weight - b.weight);
|
||||
for (let i = newWeight; i < newChannelData.length; i++) {
|
||||
const movingItem = newChannelData[i];
|
||||
|
@ -219,27 +219,27 @@ export const moveItem = (
|
|||
const ops: api.UpdateOp[] = [];
|
||||
console.log("Inc, dec:", inc, dec);
|
||||
|
||||
inc.forEach(id => {
|
||||
const item = plan.find(x => itemId(x) === id)!;
|
||||
inc.forEach((id) => {
|
||||
const item = plan.find((x) => itemId(x) === id)!;
|
||||
ops.push({
|
||||
op: "MoveItem",
|
||||
timeslotitemid: itemId(item),
|
||||
oldchannel: item.channel,
|
||||
oldweight: item.weight - 1,
|
||||
channel: item.channel,
|
||||
weight: item.weight
|
||||
weight: item.weight,
|
||||
});
|
||||
});
|
||||
|
||||
dec.forEach(id => {
|
||||
const item = plan.find(x => itemId(x) === id)!;
|
||||
dec.forEach((id) => {
|
||||
const item = plan.find((x) => itemId(x) === id)!;
|
||||
ops.push({
|
||||
op: "MoveItem",
|
||||
timeslotitemid: itemId(item),
|
||||
oldchannel: item.channel,
|
||||
oldweight: item.weight + 1,
|
||||
channel: item.channel,
|
||||
weight: item.weight
|
||||
weight: item.weight,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -250,7 +250,7 @@ export const moveItem = (
|
|||
oldchannel: oldChannel,
|
||||
oldweight: oldWeight,
|
||||
channel: newChannel,
|
||||
weight: newWeight
|
||||
weight: newWeight,
|
||||
});
|
||||
|
||||
dispatch(showplan.actions.applyOps(ops));
|
||||
|
@ -272,7 +272,7 @@ export const addItem = (
|
|||
// This is basically a simplified version of the second case above
|
||||
// Before we add the new item to the plan, we increment everything below it
|
||||
const planColumn = plan
|
||||
.filter(x => x.channel === newItemData.channel)
|
||||
.filter((x) => x.channel === newItemData.channel)
|
||||
.sort((a, b) => a.weight - b.weight);
|
||||
for (let i = newItemData.weight; i < planColumn.length; i++) {
|
||||
const item = planColumn[i];
|
||||
|
@ -282,7 +282,7 @@ export const addItem = (
|
|||
oldchannel: item.channel,
|
||||
oldweight: item.weight,
|
||||
channel: item.channel,
|
||||
weight: item.weight + 1
|
||||
weight: item.weight + 1,
|
||||
});
|
||||
item.weight += 1;
|
||||
}
|
||||
|
@ -343,9 +343,9 @@ export const removeItem = (
|
|||
): AppThunk => async (dispatch, getState) => {
|
||||
// This is a simplified version of the second case of moveItem
|
||||
const plan = cloneDeep(getState().showplan.plan!);
|
||||
const item = plan.find(x => itemId(x) === itemid)!;
|
||||
const item = plan.find((x) => itemId(x) === itemid)!;
|
||||
const planColumn = plan
|
||||
.filter(x => x.channel === item.channel)
|
||||
.filter((x) => x.channel === item.channel)
|
||||
.sort((a, b) => a.weight - b.weight);
|
||||
|
||||
const ops: api.UpdateOp[] = [];
|
||||
|
@ -353,7 +353,7 @@ export const removeItem = (
|
|||
op: "RemoveItem",
|
||||
timeslotitemid: itemid,
|
||||
channel: item.channel,
|
||||
weight: item.weight
|
||||
weight: item.weight,
|
||||
});
|
||||
planColumn.splice(planColumn.indexOf(item), 1);
|
||||
for (let i = item.weight; i < planColumn.length; i++) {
|
||||
|
@ -364,7 +364,7 @@ export const removeItem = (
|
|||
oldchannel: movingItem.channel,
|
||||
oldweight: movingItem.weight,
|
||||
channel: movingItem.channel,
|
||||
weight: movingItem.weight - 1
|
||||
weight: movingItem.weight - 1,
|
||||
});
|
||||
movingItem.weight -= 1;
|
||||
}
|
||||
|
@ -377,7 +377,9 @@ export const removeItem = (
|
|||
dispatch(showplan.actions.applyOps(ops));
|
||||
};
|
||||
|
||||
export const getShowplan = (timeslotId: number): AppThunk => async dispatch => {
|
||||
export const getShowplan = (timeslotId: number): AppThunk => async (
|
||||
dispatch
|
||||
) => {
|
||||
dispatch(showplan.actions.getShowplanStarting());
|
||||
try {
|
||||
const plan = await api.getShowplan(timeslotId);
|
||||
|
@ -405,7 +407,7 @@ export const getShowplan = (timeslotId: number): AppThunk => async dispatch => {
|
|||
oldchannel: colIndex,
|
||||
channel: colIndex,
|
||||
oldweight: item.weight,
|
||||
weight: itemIndex
|
||||
weight: itemIndex,
|
||||
});
|
||||
plan[colIndex][itemIndex].weight = itemIndex;
|
||||
}
|
||||
|
@ -432,7 +434,7 @@ export const getShowplan = (timeslotId: number): AppThunk => async dispatch => {
|
|||
}
|
||||
};
|
||||
|
||||
export const getPlaylists = (): AppThunk => async dispatch => {
|
||||
export const getPlaylists = (): AppThunk => async (dispatch) => {
|
||||
try {
|
||||
const userPlaylists = await api.getUserPlaylists();
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { configureStore, Action, getDefaultMiddleware } from "@reduxjs/toolkit";
|
|||
import { ThunkAction } from "redux-thunk";
|
||||
import {
|
||||
mixerMiddleware,
|
||||
mixerKeyboardShortcutsMiddleware
|
||||
mixerKeyboardShortcutsMiddleware,
|
||||
} from "./mixer/state";
|
||||
import { tabSyncMiddleware } from "./optionsMenu/state";
|
||||
import { persistStore } from "redux-persist";
|
||||
|
@ -14,8 +14,8 @@ const store = configureStore({
|
|||
mixerMiddleware,
|
||||
mixerKeyboardShortcutsMiddleware,
|
||||
tabSyncMiddleware,
|
||||
...getDefaultMiddleware()
|
||||
]
|
||||
...getDefaultMiddleware(),
|
||||
],
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV === "development" && module.hot) {
|
||||
|
|
Loading…
Reference in a new issue