Use bundled Font Awesome instead of CDN

This commit is contained in:
Marks Polakovs 2020-04-17 17:37:01 +02:00
parent 7c8eff5520
commit 94ffe8dd74
9 changed files with 210 additions and 178 deletions

View file

@ -78,6 +78,7 @@
"react-dnd": "^9.4.0",
"react-dnd-html5-backend": "^9.4.0",
"react-dom": "^16.13.1",
"react-icons": "^3.9.0",
"react-live-clock": "^4.0.5",
"react-modal": "^3.11.2",
"react-redux": "^7.1.3",

View file

@ -41,7 +41,6 @@
-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://kit.fontawesome.com/255e917109.js" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"

View file

@ -95,16 +95,23 @@
background-color: #78acf1;
}
.sp-track .circle {
height: 16px;
width: 16px;
display: inline-block;
border-radius: 50%;
}
.sp-track .central {
color: green;
background-color: green;
}
.sp-track .aux {
color: #07F;
background-color: #07F;
}
.sp-track .ghost {
color: gray;
background-color: gray;
}
.sp-track-active {

View file

@ -2,6 +2,8 @@ import React, { useRef, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import Clock from "react-live-clock";
import { FaClock, FaUser } from "react-icons/fa";
import { RootState } from "../rootReducer";
import * as BroadcastState from "../broadcast/state";
@ -112,7 +114,8 @@ export function NavBar() {
aria-haspopup="true"
aria-expanded="false"
>
<span className="fa fa-clock-o"></span>&nbsp;
<FaClock />
&nbsp;
{sessionState.currentTimeslot &&
sessionState.currentTimeslot.start_time}
</a>
@ -142,7 +145,8 @@ export function NavBar() {
aria-haspopup="true"
aria-expanded="false"
>
<i className="fa fa-user-o"></i>&nbsp;
<FaUser />
&nbsp;
{sessionState.currentUser?.fname} {sessionState.currentUser?.sname}
</a>
<div className="dropdown-menu" aria-labelledby="dropdown07">

View file

@ -6,6 +6,7 @@ import { RootState } from "../rootReducer";
import * as MixerState from "../mixer/state";
import { Draggable } from "react-beautiful-dnd";
import { FaCircle } from "react-icons/fa";
import { ContextMenuTrigger } from "react-contextmenu";
export const TS_ITEM_MENU_ID = "SongMenu";
@ -13,7 +14,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;
@ -24,11 +25,14 @@ export const Item = memo(function Item({
const isReal = "timeslotitemid" in x;
const isGhost = "ghostid" in x;
const playerState = useSelector(
(state: RootState) => column > -1 ? state.mixer.players[column] : undefined
const playerState = useSelector((state: RootState) =>
column > -1 ? state.mixer.players[column] : undefined
);
const isLoaded = playerState && playerState.loadedItem !== null && itemId(playerState.loadedItem) === id;
const isLoaded =
playerState &&
playerState.loadedItem !== null &&
itemId(playerState.loadedItem) === id;
const showDebug = useSelector(
(state: RootState) => state.settings.showDebugInfo
@ -41,7 +45,11 @@ export const Item = memo(function Item({
}
return (
<Draggable draggableId={id} index={index} isDragDisabled={isGhost || isLoaded}>
<Draggable
draggableId={id}
index={index}
isDragDisabled={isGhost || isLoaded}
>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
@ -62,7 +70,8 @@ export const Item = memo(function Item({
id={isReal ? TS_ITEM_MENU_ID : ""}
collect={() => ({ id })}
>
<i className={"fa fa-circle " + x.type}></i>&nbsp;
<span className={"circle " + x.type} />
&nbsp;
{x.title.toString()}
{"artist" in x && " - " + x.artist}
<small

View file

@ -1,5 +1,13 @@
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import {
FaLevelDownAlt,
FaPlayCircle,
FaRedo,
FaPlay,
FaPause,
FaStop
} from "react-icons/fa";
import { RootState } from "../rootReducer";
import * as MixerState from "../mixer/state";
import { secToHHMM } from "../lib/utils";
@ -28,9 +36,12 @@ export function Player({ id }: { id: number }) {
? "btn-primary"
: "btn-outline-secondary") + " btn btn-sm col-4 sp-play-on-load"
}
onClick={() => dispatch(MixerState.toggleAutoAdvance({player: id}))}
onClick={() =>
dispatch(MixerState.toggleAutoAdvance({ player: id }))
}
>
<i className="fa fa-level-down-alt"></i>&nbsp; Auto Advance
<FaLevelDownAlt />
&nbsp; Auto Advance
</button>
<button
className={
@ -38,9 +49,12 @@ export function Player({ id }: { id: number }) {
? "btn-primary"
: "btn-outline-secondary") + " btn btn-sm col-4 sp-play-on-load"
}
onClick={() => dispatch(MixerState.togglePlayOnLoad({ player: id }))}
onClick={() =>
dispatch(MixerState.togglePlayOnLoad({ player: id }))
}
>
<i className="far fa-play-circle"></i>&nbsp; Play on Load
<FaPlayCircle />
&nbsp; Play on Load
</button>
<button
className={
@ -50,7 +64,8 @@ export function Player({ id }: { id: number }) {
}
onClick={() => dispatch(MixerState.toggleRepeat({ player: id }))}
>
<i className="fa fa-redo"></i>&nbsp; Repeat {playerState.repeat}
<FaRedo />
&nbsp; Repeat {playerState.repeat}
</button>
</div>
<div className="card-body p-0">
@ -97,7 +112,7 @@ export function Player({ id }: { id: number }) {
: ""
}
>
<i className="fas fa-play"></i>
<FaPlay />
</button>
<button
onClick={() => dispatch(MixerState.pause(id))}
@ -105,7 +120,7 @@ export function Player({ id }: { id: number }) {
playerState.state === "paused" ? "sp-state-paused" : ""
}
>
<i className="fas fa-pause"></i>
<FaPause />
</button>
<button
onClick={() => dispatch(MixerState.stop(id))}
@ -113,7 +128,7 @@ export function Player({ id }: { id: number }) {
playerState.state === "stopped" ? "sp-state-stopped" : ""
}
>
<i className="fas fa-stop"></i>
<FaStop />
</button>
</div>
</div>
@ -141,11 +156,19 @@ export function Player({ id }: { id: number }) {
- in
</span>
)}
<div className={"m-0 graph" + ((playerState.loading !== -1) ? " loading" : "")} id={"waveform-" + id}
style={(playerState.loading !== -1) ? {
width: (playerState.loading*100) + "%"} : {}}
>
</div>
<div
className={
"m-0 graph" + (playerState.loading !== -1 ? " loading" : "")
}
id={"waveform-" + id}
style={
playerState.loading !== -1
? {
width: playerState.loading * 100 + "%"
}
: {}
}
></div>
</div>
</div>

View file

@ -1,12 +1,7 @@
import React, { useState, useReducer, useEffect, memo } from "react";
import { ContextMenu, MenuItem } from "react-contextmenu";
import { useBeforeunload } from "react-beforeunload";
import {
MYRADIO_NON_API_BASE,
getUserPlaylists,
getAuxPlaylists,
ManagedPlaylist
} from "../api";
import { FaCaretSquareDown, FaAlignJustify } from "react-icons/fa";
import { TimeslotItem } from "../api";
@ -44,7 +39,7 @@ import { Player, USE_REAL_GAIN_VALUE } from "./Player";
import { CombinedNavAlertBar } from "../navbar";
import { OptionsMenu } from "../optionsMenu";
import { WelcomeModal } from "./WelcomeModal";
import {PisModal} from "./PISModal";
import { PisModal } from "./PISModal";
function Column({ id, data }: { id: number; data: PlanItem[] }) {
return (
@ -127,7 +122,7 @@ function LibraryColumn() {
<span
className={sauce === "None" ? "mt-5 text-center text-muted" : "d-none"}
>
<i className="far fa-2x fa-caret-square-down"></i>
<FaCaretSquareDown />
<br />
Select a library to search.
</span>
@ -303,7 +298,8 @@ const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
className="btn btn-outline-dark btn-sm mb-0"
onClick={() => toggleSidebar()}
>
<i className="fas fa-align-justify mb-2"></i>Toggle Sidebar
<FaAlignJustify />
Toggle Sidebar
</span>
</div>
<div id="sidebar" className="sp-main-col">

View file

@ -3,160 +3,146 @@ import useDebounce from "../lib/useDebounce";
import { Track, searchForTracks, loadAuxLibrary, AuxItem } from "../api";
import { itemId } from "./state";
import { Droppable } from "react-beautiful-dnd";
import { FaCog, FaSearch, FaTimesCircle } from "react-icons/fa";
import { Item } from "./Item";
export const CML_CACHE: { [recordid_trackid: string]: Track } = {};
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<Track[]>([]);
const [track, setTrack] = useState("");
const [artist, setArtist] = useState("");
const debouncedTrack = useDebounce(track, 1000);
const debouncedArtist = useDebounce(artist, 1000);
const [items, setItems] = useState<Track[]>([]);
type searchingStateEnum =
| "searching"
| "not-searching"
| "results"
| "no-results";
const [state, setState] = useState<searchingStateEnum>("not-searching");
useEffect(() => {
if (debouncedTrack === "" && debouncedArtist === "") {
setItems([]);
setState("not-searching");
return;
}
setItems([]);
setState("searching");
searchForTracks(artist, track).then(tracks => {
if (tracks.length === 0) {
setState("no-results");
} else {
setState("results");
}
tracks.forEach(track => {
const id = itemId(track);
if (!(id in CML_CACHE)) {
CML_CACHE[id] = track;
}
});
setItems(tracks);
});
}, [debouncedTrack, debouncedArtist]);
return (
<>
<input
className="form-control"
type="text"
placeholder="Filter by track..."
value={track}
onChange={e => setTrack(e.target.value)}
/>
<input
className="form-control"
type="text"
placeholder="Filter by artist..."
value={artist}
onChange={e => setArtist(e.target.value)}
/>
<span
className={
state !== "results"
? "mt-5 text-center text-muted"
: "d-none"
}
>
<i
className={
"fa fa-2x " +
(state === "not-searching"
? "fa-search"
: state === "searching"
? "fa-cog fa-spin"
: state === "no-results"
? "fa-times-circle"
: "d-none")
}
></i>
<br />
{state === "not-searching"
? "Enter a search term."
: state === "searching"
? "Searching..."
: state === "no-results"
? "No results."
: ""}
</span>
<Droppable droppableId="$CML">
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{items.map((item, index) => (
<Item
key={itemId(item)}
item={item}
index={index}
column={-1}
/>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</>
);
type searchingStateEnum =
| "searching"
| "not-searching"
| "results"
| "no-results";
const [state, setState] = useState<searchingStateEnum>("not-searching");
useEffect(() => {
if (debouncedTrack === "" && debouncedArtist === "") {
setItems([]);
setState("not-searching");
return;
}
setItems([]);
setState("searching");
searchForTracks(artist, track).then(tracks => {
if (tracks.length === 0) {
setState("no-results");
} else {
setState("results");
}
tracks.forEach(track => {
const id = itemId(track);
if (!(id in CML_CACHE)) {
CML_CACHE[id] = track;
}
});
setItems(tracks);
});
}, [debouncedTrack, debouncedArtist]);
return (
<>
<input
className="form-control"
type="text"
placeholder="Filter by track..."
value={track}
onChange={e => setTrack(e.target.value)}
/>
<input
className="form-control"
type="text"
placeholder="Filter by artist..."
value={artist}
onChange={e => setArtist(e.target.value)}
/>
<span
className={
state !== "results" ? "mt-5 text-center text-muted" : "d-none"
}
>
{state === "not-searching" && <FaSearch />}
{state === "searching" && <FaCog className="fa-spin" />}
{state === "no-results" && <FaTimesCircle />}
<br />
{state === "not-searching"
? "Enter a search term."
: state === "searching"
? "Searching..."
: state === "no-results"
? "No results."
: ""}
</span>
<Droppable droppableId="$CML">
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{items.map((item, index) => (
<Item key={itemId(item)} item={item} index={index} column={-1} />
))}
{provided.placeholder}
</div>
)}
</Droppable>
</>
);
}
export const AUX_CACHE: { [auxid: string]: AuxItem } = {};
export function AuxLibrary({ libraryId }: { libraryId: string }) {
const [title, setTitle] = useState("");
const debouncedTitle = useDebounce(title, 1000);
const [items, setItems] = useState<AuxItem[]>([]);
const [title, setTitle] = useState("");
const debouncedTitle = useDebounce(title, 1000);
const [items, setItems] = useState<AuxItem[]>([]);
useEffect(() => {
async function load() {
const libItems = await loadAuxLibrary(libraryId);
libItems.forEach(item => {
const id = itemId(item);
if (!(id in AUX_CACHE)) {
AUX_CACHE[id] = item;
}
});
setItems(libItems);
}
load();
}, [libraryId]);
return (
<>
<input
className="form-control"
type="text"
placeholder="Filter..."
value={title}
onChange={e => setTitle(e.target.value)}
/>
<Droppable droppableId="$AUX">
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{items
.filter(
its =>
its.title.toString()
.toLowerCase()
.indexOf(its.title.toString().toLowerCase()) > -1
)
.map((item, index) => (
<Item
key={itemId(item)}
item={item}
index={index}
column={-1}
/>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</>
);
useEffect(() => {
async function load() {
const libItems = await loadAuxLibrary(libraryId);
libItems.forEach(item => {
const id = itemId(item);
if (!(id in AUX_CACHE)) {
AUX_CACHE[id] = item;
}
});
setItems(libItems);
}
load();
}, [libraryId]);
return (
<>
<input
className="form-control"
type="text"
placeholder="Filter..."
value={title}
onChange={e => setTitle(e.target.value)}
/>
<Droppable droppableId="$AUX">
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{items
.filter(
its =>
its.title
.toString()
.toLowerCase()
.indexOf(its.title.toString().toLowerCase()) > -1
)
.map((item, index) => (
<Item
key={itemId(item)}
item={item}
index={index}
column={-1}
/>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</>
);
}

View file

@ -8863,6 +8863,13 @@ react-error-overlay@^6.0.3:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA==
react-icons@^3.9.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-3.9.0.tgz#89a00f20a0e02e6bfd899977eaf46eb4624239d5"
integrity sha512-gKbYKR+4QsD3PmIHLAM9TDDpnaTsr3XZeK1NTAb6WQQ+gxEdJ0xuCgLq0pxXdS7Utg2AIpcVhM1ut/jlDhcyNg==
dependencies:
camelcase "^5.0.0"
react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"