diff --git a/package.json b/package.json index 05b3d27..fafda2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webstudio", - "version": "1.5.3", + "version": "1.5.4", "private": true, "dependencies": { "@babel/core": "7.6.0", diff --git a/src/App.scss b/src/App.scss index 1af0ac2..d056570 100644 --- a/src/App.scss +++ b/src/App.scss @@ -42,6 +42,10 @@ $number-of-channels: 3; font-size: 0.92em; } + .navbar.hover-menu .hover-label { + display: none; + } + .hover-menu { .btn { padding-top: 0.1em; @@ -57,7 +61,6 @@ $number-of-channels: 3; } &:not(:hover) { - overflow: hidden; height: 15px; > *:not(.hover-label) { diff --git a/src/api.ts b/src/api.ts index d38c398..206f9ee 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,4 +1,6 @@ import qs from "qs"; +import { getUserError, setCurrentUser } from "./session/state"; +import store from "./store"; export const MYRADIO_NON_API_BASE = process.env.REACT_APP_MYRADIO_NONAPI_BASE!; export const MYRADIO_BASE_URL = process.env.REACT_APP_MYRADIO_BASE!; @@ -42,8 +44,14 @@ export async function myradioApiRequest( if (json.status === "OK") { return json.payload; } else { - console.error(json.payload); - throw new ApiException(json.payload); + if (res.status === 401) { + // We've logged out! Oh no! + store.dispatch(setCurrentUser({ user: null, canBroadcast: false })); + store.dispatch(getUserError("User is no longer logged in.")); + } else { + console.error(json.payload); + throw new ApiException(json.payload); + } } } diff --git a/src/broadcast/state.ts b/src/broadcast/state.ts index ed86b3c..d078ea2 100644 --- a/src/broadcast/state.ts +++ b/src/broadcast/state.ts @@ -144,7 +144,16 @@ export const registerForShow = (): AppThunk => async (dispatch, getState) => { }) ); if (streamer) { - await streamer.stop("ApiException " + e.message); + // We're connected, + var remain_connected = getState().settings.allowStreamingOnReject; + if (remain_connected) { + console.log( + "StateServer refused registration. Due to setting, staying connected.", + e + ); + } else { + await streamer.stop("ApiException " + e.message); + } } } else { // let raygun handle it diff --git a/src/optionsMenu/AdvancedTab.tsx b/src/optionsMenu/AdvancedTab.tsx index 69cb9f6..a77f58a 100644 --- a/src/optionsMenu/AdvancedTab.tsx +++ b/src/optionsMenu/AdvancedTab.tsx @@ -305,6 +305,25 @@ export function AdvancedTab() { /> +
+ + dispatch( + changeSetting({ + key: "allowStreamingOnReject", + val: e.target.checked, + }) + ) + } + /> + +
async (dispatch, getState) => { return getState().session.currentUser; }; @@ -91,7 +93,7 @@ export const getUser = (): AppThunk => async (dispatch) => { }); dispatch(sessionState.actions.setCurrentUser({ user, canBroadcast })); } catch (e) { - console.log("failed to get user. " + e.toString()); + console.log("Failed to get user. " + e.toString()); dispatch(sessionState.actions.getUserError(e.toString())); } }; @@ -102,7 +104,7 @@ export const getTimeslot = (): AppThunk => async (dispatch) => { const timeslot = await getCurrentApiTimeslot(); dispatch(sessionState.actions.getTimeslotSuccess(timeslot)); } catch (e) { - console.log("failed to get selected timeslot. " + e.toString()); + console.log("Failed to get selected timeslot. " + e.toString()); dispatch(sessionState.actions.getTimeslotError(e.toString())); } }; diff --git a/src/showplanner/index.tsx b/src/showplanner/index.tsx index 6733da1..c15522e 100644 --- a/src/showplanner/index.tsx +++ b/src/showplanner/index.tsx @@ -347,17 +347,20 @@ export function LoadingDialogue({

{subtitle}

- {error !== null && ( - <> -

- Failed! Please tell Computing Team that something - broke. -

-

- {error} -

- - )} + {error !== null && + (error === "Error: No valid authentication data provided." ? ( +

Redirecting you to MyRadio, please wait...

+ ) : ( + <> +

+ Failed! Please tell Computing Team that + something broke. +

+

+ {error} +

+ + ))}
); diff --git a/src/showplanner/libraries.tsx b/src/showplanner/libraries.tsx index 930755f..82938db 100644 --- a/src/showplanner/libraries.tsx +++ b/src/showplanner/libraries.tsx @@ -173,38 +173,42 @@ export function LibraryColumn() { } export function CentralMusicLibrary() { - const [track, setTrack] = useState(""); - const [artist, setArtist] = useState(""); - const debouncedTrack = useDebounce(track, 1000); - const debouncedArtist = useDebounce(artist, 1000); - const [items, setItems] = useState([]); + const [trackSearchTerm, setTrackSearchTerm] = useState(""); + const [artistSearchTerm, setArtistSearchTerm] = useState(""); + const debouncedTrackSearchTerm = useDebounce(trackSearchTerm, 1000); + const debouncedArtistSearchTerm = useDebounce(artistSearchTerm, 1000); + const [tracks, setTracks] = useState([]); - const [state, setState] = useState("not-searching"); + const [searchingState, setSearchingState] = useState( + "not-searching" + ); useEffect(() => { - if (debouncedTrack === "" && debouncedArtist === "") { - setItems([]); - setState("not-searching"); + if (debouncedTrackSearchTerm === "" && debouncedArtistSearchTerm === "") { + setTracks([]); + setSearchingState("not-searching"); return; } - setItems([]); - setState("searching"); - searchForTracks(artist, track).then((tracks) => { - tracks.forEach((track) => { - const id = itemId(track); - if (!(id in CML_CACHE)) { - CML_CACHE[id] = track; + setTracks([]); + setSearchingState("searching"); + searchForTracks(debouncedArtistSearchTerm, debouncedTrackSearchTerm).then( + (tracks) => { + tracks.forEach((track) => { + const id = itemId(track); + if (!(id in CML_CACHE)) { + CML_CACHE[id] = track; + } + }); + setTracks(tracks); + if (tracks.length === 0) { + setSearchingState("no-results"); + } else { + setSearchingState("results"); + ReactTooltip.rebuild(); // Update tooltips so they appear. } - }); - setItems(tracks); - if (tracks.length === 0) { - setState("no-results"); - } else { - setState("results"); - ReactTooltip.rebuild(); // Update tooltips so they appear. } - }); - }, [debouncedTrack, debouncedArtist, artist, track]); + ); + }, [debouncedTrackSearchTerm, debouncedArtistSearchTerm]); return (
@@ -212,19 +216,19 @@ export function CentralMusicLibrary() { className="form-control form-control-sm" type="text" placeholder="Filter by track..." - value={track} - onChange={(e) => setTrack(e.target.value)} + value={trackSearchTerm} + onChange={(e) => setTrackSearchTerm(e.target.value)} /> setArtist(e.target.value)} + value={artistSearchTerm} + onChange={(e) => setArtistSearchTerm(e.target.value)} />
- + {(provided, snapshot) => (
- {items.map((item, index) => ( + {tracks.map((item, index) => ( ([]); - const [state, setState] = useState("not-searching"); + const [searchingState, setSearchingState] = useState( + "not-searching" + ); useEffect(() => { async function load() { setItems([]); - setState("searching"); + setSearchingState("searching"); const libItems = await loadPlaylistLibrary(libraryId); libItems.forEach((item) => { const id = itemId(item); @@ -270,9 +276,9 @@ export function ManagedPlaylistLibrary({ libraryId }: { libraryId: string }) { }); setItems(libItems); if (libItems.length === 0) { - setState("no-results"); + setSearchingState("no-results"); } else { - setState("results"); + setSearchingState("results"); ReactTooltip.rebuild(); // Update tooltips so they appear. } } @@ -285,19 +291,19 @@ export function ManagedPlaylistLibrary({ libraryId }: { libraryId: string }) { className="form-control form-control-sm" type="text" placeholder="Filter by track..." - value={track} - onChange={(e) => setTrack(e.target.value)} + value={trackSearchTerm} + onChange={(e) => setTrackSearchTerm(e.target.value)} /> setArtist(e.target.value)} + value={artistSearchTerm} + onChange={(e) => setArtistSearchTerm(e.target.value)} />
- + {(provided, snapshot) => (
-1 + .indexOf(debouncedTrackSearchTerm.toLowerCase()) > -1 ) .filter( (its) => its.artist .toString() .toLowerCase() - .indexOf(debouncedArtist.toLowerCase()) > -1 + .indexOf(debouncedArtistSearchTerm.toLowerCase()) > -1 ) .map((item, index) => ( ([]); - const [state, setState] = useState("not-searching"); + const [searchingState, setSearchingState] = useState( + "not-searching" + ); useEffect(() => { async function load() { setItems([]); - setState("searching"); + setSearchingState("searching"); const libItems = await loadAuxLibrary(libraryId); libItems.forEach((item) => { const id = itemId(item); @@ -359,9 +367,9 @@ export function AuxLibrary({ libraryId }: { libraryId: string }) { setItems(libItems); ReactTooltip.rebuild(); // Update tooltips so they appear. if (libItems.length === 0) { - setState("no-results"); + setSearchingState("no-results"); } else { - setState("results"); + setSearchingState("results"); } } load(); @@ -378,7 +386,7 @@ export function AuxLibrary({ libraryId }: { libraryId: string }) { />
- + {(provided, snapshot) => (
- {state === "not-searching" && } - {state === "searching" && } - {state === "no-results" && } + {searchingState === "not-searching" && } + {searchingState === "searching" && ( + + )} + {searchingState === "no-results" && }
- {state === "not-searching" + {searchingState === "not-searching" ? "Enter a search term." - : state === "searching" + : searchingState === "searching" ? "Searching..." - : state === "no-results" + : searchingState === "no-results" ? "No results." : ""}