Fix front-end warnings boy-scout.

pull/31/head
Sander Vocke 5 years ago
parent c87d995a09
commit 8844b1f390
  1. 6
      client/src/api.ts
  2. 8
      client/src/components/MainWindow.tsx
  3. 2
      client/src/components/appbar/AppBar.tsx
  4. 30
      client/src/components/querybuilder/QBLeafElem.tsx
  5. 10
      client/src/components/querybuilder/QBNodeElem.tsx
  6. 12
      client/src/components/querybuilder/QBSelectWithRequest.tsx
  7. 2
      client/src/components/tables/ResultsTable.tsx
  8. 21
      client/src/components/windows/album/AlbumWindow.tsx
  9. 23
      client/src/components/windows/artist/ArtistWindow.tsx
  10. 3
      client/src/components/windows/login/LoginWindow.tsx
  11. 4
      client/src/components/windows/manage_tags/ManageTagMenu.tsx
  12. 12
      client/src/components/windows/manage_tags/ManageTagsWindow.tsx
  13. 4
      client/src/components/windows/manage_tags/NewTagMenu.tsx
  14. 6
      client/src/components/windows/manage_tags/TagChange.tsx
  15. 37
      client/src/components/windows/query/QueryWindow.tsx
  16. 6
      client/src/components/windows/register/RegisterWindow.tsx
  17. 15
      client/src/components/windows/song/SongWindow.tsx
  18. 25
      client/src/components/windows/tag/TagWindow.tsx
  19. 12
      client/src/lib/query/Query.tsx

@ -331,17 +331,17 @@ export interface RegisterUserResponse { }
export function checkPassword(password: string): boolean {
const result = (password.length < 32) &&
(password.length >= 8) &&
/^[\x00-\x7F]*$/.test(password) && // is ASCII
password.split("").every(char => char.charCodeAt(0) <= 127) && // is ASCII
(/[a-z]/g.test(password)) && // has lowercase
(/[A-Z]/g.test(password)) && // has uppercase
(/[0-9]/g.test(password)) && // has number
(/[!@#\$%\^&\*\(\)_\+/]/g.test(password)) // has special character;
(/[!@#$%^&*()_+/]/g.test(password)) // has special character;
console.log("Password check for ", password, ": ", result);
return result;
}
export function checkEmail(email: string): boolean {
const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
const re = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
const result = re.test(String(email).toLowerCase());
console.log("Email check for ", email, ": ", result);
return result;

@ -1,19 +1,17 @@
import React, { useReducer, Reducer, useContext } from 'react';
import React from 'react';
import { ThemeProvider, CssBaseline, createMuiTheme } from '@material-ui/core';
import { grey } from '@material-ui/core/colors';
import AppBar, { AppBarTab } from './appbar/AppBar';
import QueryWindow, { QueryWindowReducer } from './windows/query/QueryWindow';
import { newWindowState, newWindowReducer, WindowType } from './windows/Windows';
import QueryWindow from './windows/query/QueryWindow';
import ArtistWindow from './windows/artist/ArtistWindow';
import AlbumWindow from './windows/album/AlbumWindow';
import TagWindow from './windows/tag/TagWindow';
import SongWindow from './windows/song/SongWindow';
import ManageTagsWindow from './windows/manage_tags/ManageTagsWindow';
import { BrowserRouter, Switch, Route, useParams, Redirect } from 'react-router-dom';
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom';
import LoginWindow from './windows/login/LoginWindow';
import { useAuth } from '../lib/useAuth';
import RegisterWindow from './windows/register/RegisterWindow';
var _ = require('lodash');
const darkTheme = createMuiTheme({
palette: {

@ -1,10 +1,8 @@
import React from 'react';
import { AppBar as MuiAppBar, Box, Tab as MuiTab, Tabs, IconButton, Typography, Menu, MenuItem } from '@material-ui/core';
import CloseIcon from '@material-ui/icons/Close';
import SearchIcon from '@material-ui/icons/Search';
import LocalOfferIcon from '@material-ui/icons/LocalOffer';
import { Link, useHistory } from 'react-router-dom';
import { WindowType } from '../windows/Windows';
import { useAuth } from '../../lib/useAuth';
export enum AppBarTab {

@ -110,56 +110,56 @@ export function QBLeafElem(props: IProps) {
</Box>
: undefined;
if (e.a == QueryLeafBy.ArtistName &&
e.leafOp == QueryLeafOp.Equals &&
if (e.a === QueryLeafBy.ArtistName &&
e.leafOp === QueryLeafOp.Equals &&
typeof e.b == "string") {
return <QBQueryElemArtistEquals
{...props}
extraElements={extraElements}
/>
} else if (e.a == QueryLeafBy.ArtistName &&
e.leafOp == QueryLeafOp.Like &&
} else if (e.a === QueryLeafBy.ArtistName &&
e.leafOp === QueryLeafOp.Like &&
typeof e.b == "string") {
return <QBQueryElemArtistLike
{...props}
extraElements={extraElements}
/>
} else if (e.a == QueryLeafBy.AlbumName &&
e.leafOp == QueryLeafOp.Equals &&
} else if (e.a === QueryLeafBy.AlbumName &&
e.leafOp === QueryLeafOp.Equals &&
typeof e.b == "string") {
return <QBQueryElemAlbumEquals
{...props}
extraElements={extraElements}
/>
} else if (e.a == QueryLeafBy.AlbumName &&
e.leafOp == QueryLeafOp.Like &&
} else if (e.a === QueryLeafBy.AlbumName &&
e.leafOp === QueryLeafOp.Like &&
typeof e.b == "string") {
return <QBQueryElemAlbumLike
{...props}
extraElements={extraElements}
/>
} if (e.a == QueryLeafBy.SongTitle &&
e.leafOp == QueryLeafOp.Equals &&
} if (e.a === QueryLeafBy.SongTitle &&
e.leafOp === QueryLeafOp.Equals &&
typeof e.b == "string") {
return <QBQueryElemTitleEquals
{...props}
extraElements={extraElements}
/>
} else if (e.a == QueryLeafBy.SongTitle &&
e.leafOp == QueryLeafOp.Like &&
} else if (e.a === QueryLeafBy.SongTitle &&
e.leafOp === QueryLeafOp.Like &&
typeof e.b == "string") {
return <QBQueryElemTitleLike
{...props}
extraElements={extraElements}
/>
} else if (e.a == QueryLeafBy.TagInfo &&
e.leafOp == QueryLeafOp.Equals &&
} else if (e.a === QueryLeafBy.TagInfo &&
e.leafOp === QueryLeafOp.Equals &&
isTagQueryInfo(e.b)) {
return <QBQueryElemTagEquals
{...props}
extraElements={extraElements}
/>
}else if (e.leafOp == QueryLeafOp.Placeholder) {
}else if (e.leafOp === QueryLeafOp.Placeholder) {
return <QBPlaceholder
onReplace={props.onReplace}
requestFunctions={props.requestFunctions}

@ -1,10 +1,8 @@
import React from 'react';
import QBOrBlock from './QBOrBlock';
import QBAndBlock from './QBAndBlock';
import { QueryNodeElem, QueryNodeOp, QueryElem, isNodeElem, simplify } from '../../lib/query/Query';
import { QBLeafElem } from './QBLeafElem';
import { QueryNodeElem, QueryNodeOp, QueryElem, simplify } from '../../lib/query/Query';
import { QBQueryElem } from './QBQueryElem';
import { O_APPEND } from 'constants';
import { Requests } from './QueryBuilder';
export interface NodeProps {
@ -37,11 +35,11 @@ export function QBNodeElem(props: NodeProps) {
/>
});
if (e.nodeOp == QueryNodeOp.And) {
if (e.nodeOp === QueryNodeOp.And) {
return <QBAndBlock>{children}</QBAndBlock>
} else if (e.nodeOp == QueryNodeOp.Or) {
} else if (e.nodeOp === QueryNodeOp.Or) {
return <QBOrBlock>{children}</QBOrBlock>
}
throw "Unsupported node element";
throw new Error("Unsupported node element");
}

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import CircularProgress from '@material-ui/core/CircularProgress';
@ -26,26 +26,26 @@ export default function QBSelectWithRequest(props: IProps & any) {
const loading: boolean = !options || options.forInput !== input;
const updateOptions = (forInput: string, options: any[]) => {
const updateOptions = useCallback((forInput: string, options: any[]) => {
if (forInput === input) {
setOptions({
forInput: forInput,
options: options,
});
}
}
}, [setOptions, input]);
const startRequest = (_input: string) => {
const startRequest = useCallback((_input: string) => {
setInput(_input);
(async () => {
const newOptions = await getNewOptions(_input);
updateOptions(_input, newOptions);
})();
};
}, [setInput, getNewOptions, updateOptions]);
useEffect(() => {
startRequest(input);
}, [input]);
}, [input, startRequest]);
const onInputChange = (e: any, val: any, reason: any) => {
if (reason === 'reset') {

@ -50,10 +50,8 @@ export default function SongTable(props: {
const artistNames = props.songGetters.getArtistNames(song);
const artist = stringifyList(artistNames);
const mainArtistId = props.songGetters.getArtistIds(song)[0];
const mainArtistName = artistNames[0];
const albumNames = props.songGetters.getAlbumNames(song);
const album = stringifyList(albumNames);
const mainAlbumName = albumNames[0];
const mainAlbumId = props.songGetters.getAlbumIds(song)[0];
const songId = props.songGetters.getId(song);
const tagIds = props.songGetters.getTagIds(song);

@ -12,7 +12,6 @@ import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import { queryAlbums, querySongs } from '../../../lib/backend/queries';
import { songGetters } from '../../../lib/songGetters';
import { useParams } from 'react-router';
var _ = require('lodash');
export type AlbumMetadata = serverApi.AlbumDetails;
export type AlbumMetadataChanges = serverApi.ModifyAlbumRequest;
@ -76,40 +75,40 @@ export function AlbumWindowControlled(props: {
state: AlbumWindowState,
dispatch: (action: any) => void,
}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;
let { id: albumId, metadata, pendingChanges, songsOnAlbum } = props.state;
let { dispatch } = props;
// Effect to get the album's metadata.
useEffect(() => {
getAlbumMetadata(props.state.id)
getAlbumMetadata(albumId)
.then((m: AlbumMetadata) => {
props.dispatch({
dispatch({
type: AlbumWindowStateActions.SetMetadata,
value: m
});
})
}, [metadata?.name]);
}, [albumId, dispatch]);
// Effect to get the album's songs.
useEffect(() => {
if (props.state.songsOnAlbum) { return; }
if (songsOnAlbum) { return; }
(async () => {
const songs = await querySongs({
query: {
a: QueryLeafBy.AlbumId,
b: props.state.id,
b: albumId,
leafOp: QueryLeafOp.Equals,
},
offset: 0,
limit: -1,
});
props.dispatch({
dispatch({
type: AlbumWindowStateActions.SetSongs,
value: songs,
});
})();
}, [props.state.songsOnAlbum]);
}, [songsOnAlbum, albumId, dispatch]);
const [editingName, setEditingName] = useState<string | null>(null);
const name = <Typography variant="h4"><EditableText
@ -132,7 +131,7 @@ export function AlbumWindowControlled(props: {
const storeLinks = metadata?.storeLinks && metadata?.storeLinks.map((link: string) => {
const store = whichStore(link);
return store && <a
href={link} target="_blank"
href={link} target="_blank" rel="noopener noreferrer"
>
<IconButton><StoreLinkIcon
whichStore={store}

@ -1,5 +1,5 @@
import React, { useEffect, useState, useReducer } from 'react';
import { Box, Typography, IconButton, Button, CircularProgress } from '@material-ui/core';
import { Box, Typography, IconButton, CircularProgress } from '@material-ui/core';
import PersonIcon from '@material-ui/icons/Person';
import * as serverApi from '../../../api';
import { WindowState } from '../Windows';
@ -12,7 +12,6 @@ import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import { queryArtists, querySongs } from '../../../lib/backend/queries';
import { songGetters } from '../../../lib/songGetters';
import { useParams } from 'react-router';
var _ = require('lodash');
export type ArtistMetadata = serverApi.ArtistDetails;
export type ArtistMetadataChanges = serverApi.ModifyArtistRequest;
@ -81,40 +80,40 @@ export function ArtistWindowControlled(props: {
state: ArtistWindowState,
dispatch: (action: any) => void,
}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;
let { metadata, id: artistId, pendingChanges, songsByArtist } = props.state;
let { dispatch } = props;
// Effect to get the artist's metadata.
useEffect(() => {
getArtistMetadata(props.state.id)
getArtistMetadata(artistId)
.then((m: ArtistMetadata) => {
props.dispatch({
dispatch({
type: ArtistWindowStateActions.SetMetadata,
value: m
});
})
}, [metadata?.name]);
}, [artistId, dispatch]);
// Effect to get the artist's songs.
useEffect(() => {
if (props.state.songsByArtist) { return; }
if (songsByArtist) { return; }
(async () => {
const songs = await querySongs({
query: {
a: QueryLeafBy.ArtistId,
b: props.state.id,
b: artistId,
leafOp: QueryLeafOp.Equals,
},
offset: 0,
limit: -1,
});
props.dispatch({
dispatch({
type: ArtistWindowStateActions.SetSongs,
value: songs,
});
})();
}, [props.state.songsByArtist]);
}, [songsByArtist, dispatch, artistId]);
const [editingName, setEditingName] = useState<string | null>(null);
const name = <Typography variant="h4"><EditableText
@ -137,7 +136,7 @@ export function ArtistWindowControlled(props: {
const storeLinks = metadata?.storeLinks && metadata?.storeLinks.map((link: string) => {
const store = whichStore(link);
return store && <a
href={link} target="_blank"
href={link} target="_blank" rel="noopener noreferrer"
>
<IconButton><StoreLinkIcon
whichStore={store}

@ -1,10 +1,9 @@
import React, { useState, useReducer } from 'react';
import React, { useReducer } from 'react';
import { WindowState } from "../Windows";
import { Box, Paper, Typography, TextField, Button } from "@material-ui/core";
import { useHistory, useLocation } from 'react-router';
import { useAuth, Auth } from '../../../lib/useAuth';
import Alert from '@material-ui/lab/Alert';
import { Link } from 'react-router-dom';
export enum LoginStatus {
NoneSubmitted = 0,

@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Menu, MenuItem, TextField, Input } from '@material-ui/core';
import React from 'react';
import { Menu, MenuItem } from '@material-ui/core';
import NestedMenuItem from "material-ui-nested-menu-item";
import MenuEditText from '../../common/MenuEditText';

@ -1,5 +1,5 @@
import React, { useEffect, useState, ReactFragment, useReducer } from 'react';
import { WindowState, newWindowReducer, WindowType } from '../Windows';
import { WindowState } from '../Windows';
import { Box, Typography, Chip, IconButton, useTheme, Button } from '@material-ui/core';
import LoyaltyIcon from '@material-ui/icons/Loyalty';
import ArrowRightIcon from '@material-ui/icons/ArrowRight';
@ -9,8 +9,6 @@ import ControlTagChanges, { TagChange, TagChangeType, submitTagChanges } from '.
import { queryTags } from '../../../lib/backend/queries';
import NewTagMenu from './NewTagMenu';
import { v4 as genUuid } from 'uuid';
import LocalOfferIcon from '@material-ui/icons/LocalOffer';
import { songGetters } from '../../../lib/songGetters';
import Alert from '@material-ui/lab/Alert';
import { useHistory } from 'react-router';
var _ = require('lodash');
@ -355,6 +353,8 @@ export function ManageTagsWindowControlled(props: {
dispatch: (action: any) => void,
}) {
const [newTagMenuPos, setNewTagMenuPos] = React.useState<null | number[]>(null);
let { fetchedTags } = props.state;
let { dispatch } = props;
const onOpenNewTagMenu = (e: any) => {
setNewTagMenuPos([e.clientX, e.clientY])
@ -364,19 +364,19 @@ export function ManageTagsWindowControlled(props: {
};
useEffect(() => {
if (props.state.fetchedTags !== null) {
if (fetchedTags !== null) {
return;
}
(async () => {
const allTags = await getAllTags();
// We have the tags in list form. Now, we want to organize
// them hierarchically by giving each tag a "children" prop.
props.dispatch({
dispatch({
type: ManageTagsWindowActions.SetFetchedTags,
value: allTags,
});
})();
}, [props.state.fetchedTags]);
}, [fetchedTags, dispatch]);
const tagsWithChanges = annotateTagsWithChanges(props.state.fetchedTags || {}, props.state.pendingChanges || [])
const changedTags = organiseTags(

@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Menu, MenuItem, TextField, Input } from '@material-ui/core';
import React from 'react';
import { Menu } from '@material-ui/core';
import NestedMenuItem from "material-ui-nested-menu-item";
import MenuEditText from '../../common/MenuEditText';

@ -1,7 +1,5 @@
import React, { useState, useEffect } from 'react';
import React from 'react';
import { Typography, Chip, CircularProgress, Box, Paper } from '@material-ui/core';
import { queryTags } from '../../../lib/backend/queries';
import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import DiscardChangesButton from '../../common/DiscardChangesButton';
import SubmitChangesButton from '../../common/SubmitChangesButton';
import { createTag, modifyTag, deleteTag, mergeTag } from '../../../lib/backend/tags';
@ -31,7 +29,7 @@ export async function submitTagChanges(changes: TagChange[]) {
var id_lookup: Record<string, number> = {}
const getId = (id_string: string) => {
return (Number(id_string) === NaN) ?
return (isNaN(Number(id_string))) ?
id_lookup[id_string] : Number(id_string);
}

@ -1,24 +1,13 @@
import React, { useEffect, useReducer } from 'react';
import { createMuiTheme, Box, LinearProgress } from '@material-ui/core';
import { QueryElem, toApiQuery, QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import React, { useEffect, useReducer, useCallback } from 'react';
import { Box, LinearProgress } from '@material-ui/core';
import { QueryElem, QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import QueryBuilder from '../../querybuilder/QueryBuilder';
import * as serverApi from '../../../api';
import SongTable from '../../tables/ResultsTable';
import { songGetters } from '../../../lib/songGetters';
import { queryArtists, querySongs, queryAlbums, queryTags } from '../../../lib/backend/queries';
import { grey } from '@material-ui/core/colors';
import { WindowState } from '../Windows';
var _ = require('lodash');
const darkTheme = createMuiTheme({
palette: {
type: 'dark',
primary: {
main: grey[100],
}
},
});
export interface ResultsForQuery {
for: QueryElem,
results: any[],
@ -112,23 +101,23 @@ export function QueryWindowControlled(props: {
state: QueryWindowState,
dispatch: (action: any) => void,
}) {
let query = props.state.query;
let editing = props.state.editingQuery;
let resultsFor = props.state.resultsForQuery;
let { query, editingQuery: editing, resultsForQuery: resultsFor } = props.state;
let { dispatch } = props;
let setQuery = (q: QueryElem | null) => {
props.dispatch({ type: QueryWindowStateActions.SetQuery, value: q });
}
let setEditingQuery = (e: boolean) => {
props.dispatch({ type: QueryWindowStateActions.SetEditingQuery, value: e });
}
let setResultsForQuery = (r: ResultsForQuery | null) => {
props.dispatch({ type: QueryWindowStateActions.SetResultsForQuery, value: r });
}
let setResultsForQuery = useCallback((r: ResultsForQuery | null) => {
dispatch({ type: QueryWindowStateActions.SetResultsForQuery, value: r });
}, [ dispatch ]);
const loading = query && (!resultsFor || !_.isEqual(resultsFor.for, query));
const showResults = (query && resultsFor && query == resultsFor.for) ? resultsFor.results : [];
const showResults = (query && resultsFor && query === resultsFor.for) ? resultsFor.results : [];
const doQuery = async (_query: QueryElem) => {
const doQuery = useCallback(async (_query: QueryElem) => {
const songs = await querySongs({
query: _query,
offset: 0,
@ -141,7 +130,7 @@ export function QueryWindowControlled(props: {
results: songs,
})
}
}
}, [query, setResultsForQuery]);
useEffect(() => {
if (query) {
@ -149,7 +138,7 @@ export function QueryWindowControlled(props: {
} else {
setResultsForQuery(null);
}
}, [query]);
}, [query, doQuery, setResultsForQuery]);
return <Box width="100%" justifyContent="center" display="flex" flexWrap="wrap">
<Box

@ -1,7 +1,7 @@
import React, { useState, useReducer } from 'react';
import React, { useReducer } from 'react';
import { WindowState } from "../Windows";
import { Box, Paper, Typography, TextField, Button } from "@material-ui/core";
import { useHistory, useLocation } from 'react-router';
import { useHistory } from 'react-router';
import { useAuth, Auth } from '../../../lib/useAuth';
import Alert from '@material-ui/lab/Alert';
import { Link } from 'react-router-dom';
@ -50,9 +50,7 @@ export function RegisterWindowControlled(props: {
dispatch: (action: any) => void,
}) {
let history: any = useHistory();
let location: any = useLocation();
let auth: Auth = useAuth();
let { from } = location.state || { from: { pathname: "/" } };
const onSubmit = (event: any) => {
event.preventDefault();

@ -1,5 +1,5 @@
import React, { useEffect, useState, useReducer } from 'react';
import { Box, Typography, IconButton, Button, CircularProgress } from '@material-ui/core';
import { Box, Typography, IconButton, CircularProgress } from '@material-ui/core';
import AudiotrackIcon from '@material-ui/icons/Audiotrack';
import PersonIcon from '@material-ui/icons/Person';
import AlbumIcon from '@material-ui/icons/Album';
@ -13,7 +13,6 @@ import SubmitChangesButton from '../../common/SubmitChangesButton';
import { saveSongChanges } from '../../../lib/saveChanges';
import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import { querySongs } from '../../../lib/backend/queries';
import { songGetters } from '../../../lib/songGetters';
import { useParams } from 'react-router';
export type SongMetadata = serverApi.SongDetails;
@ -71,18 +70,18 @@ export function SongWindowControlled(props: {
state: SongWindowState,
dispatch: (action: any) => void,
}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;
let { pendingChanges, metadata, id: songId } = props.state;
let { dispatch } = props;
useEffect(() => {
getSongMetadata(props.state.id)
getSongMetadata(songId)
.then((m: SongMetadata) => {
props.dispatch({
dispatch({
type: SongWindowStateActions.SetMetadata,
value: m
});
})
}, [metadata?.title]);
}, [songId, dispatch]);
const [editingTitle, setEditingTitle] = useState<string | null>(null);
const title = <Typography variant="h4"><EditableText
@ -117,7 +116,7 @@ export function SongWindowControlled(props: {
const storeLinks = metadata?.storeLinks && metadata?.storeLinks.map((link: string) => {
const store = whichStore(link);
return store && <a
href={link} target="_blank"
href={link} target="_blank" rel="noopener noreferrer"
>
<IconButton><StoreLinkIcon
whichStore={store}

@ -12,7 +12,6 @@ import { queryTags, querySongs } from '../../../lib/backend/queries';
import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
import { songGetters } from '../../../lib/songGetters';
import { useParams } from 'react-router';
var _ = require('lodash');
export interface FullTagMetadata extends serverApi.TagDetails {
fullName: string[],
@ -78,7 +77,7 @@ export async function getTagMetadata(id: number) {
export default function TagWindow(props: {}) {
const { id } = useParams();
const [state, dispatch] = useReducer(TagWindowReducer,{
const [state, dispatch] = useReducer(TagWindowReducer, {
id: id,
metadata: null,
pendingChanges: null,
@ -95,38 +94,40 @@ export function TagWindowControlled(props: {
}) {
let metadata = props.state.metadata;
let pendingChanges = props.state.pendingChanges;
let { id: tagId, songsWithTag } = props.state;
let dispatch = props.dispatch;
// Effect to get the tag's metadata.
useEffect(() => {
getTagMetadata(props.state.id)
getTagMetadata(tagId)
.then((m: TagMetadata) => {
props.dispatch({
dispatch({
type: TagWindowStateActions.SetMetadata,
value: m
});
})
}, [metadata?.name]);
}, [tagId, dispatch]);
// Effect to get the tag's songs.
useEffect(() => {
if (props.state.songsWithTag) { return; }
if (songsWithTag) { return; }
(async () => {
const songs = await querySongs({
query: {
a: QueryLeafBy.TagId,
b: props.state.id,
b: tagId,
leafOp: QueryLeafOp.Equals,
},
offset: 0,
limit: -1,
});
props.dispatch({
dispatch({
type: TagWindowStateActions.SetSongs,
value: songs,
});
});
})();
}, [props.state.songsWithTag]);
}, [songsWithTag, tagId, dispatch]);
const [editingName, setEditingName] = useState<string | null>(null);
const name = <Typography variant="h4"><EditableText
@ -147,7 +148,7 @@ export function TagWindowControlled(props: {
/></Typography>
const fullName = <Box display="flex" alignItems="center">
{metadata?.fullName.map((n: string, i: number) => {
if (metadata?.fullName && i == metadata?.fullName.length - 1) {
if (metadata?.fullName && i === metadata?.fullName.length - 1) {
return name;
} else if (i >= (metadata?.fullName.length || 0) - 1) {
return undefined;
@ -160,7 +161,7 @@ export function TagWindowControlled(props: {
const storeLinks = metadata?.storeLinks && metadata?.storeLinks.map((link: string) => {
const store = whichStore(link);
return store && <a
href={link} target="_blank"
href={link} target="_blank" rel="noopener noreferrer"
>
<IconButton><StoreLinkIcon
whichStore={store}

@ -104,11 +104,11 @@ export function addPlaceholders(
return newBlock;
}
} else if (isLeafElem(q) &&
q.leafOp != QueryLeafOp.Placeholder &&
q.leafOp !== QueryLeafOp.Placeholder &&
inNode !== null) {
return { operands: [q, makePlaceholder()], nodeOp: otherOp[inNode] };
} else if (isLeafElem(q) &&
q.leafOp != QueryLeafOp.Placeholder &&
q.leafOp !== QueryLeafOp.Placeholder &&
inNode === null) {
return {
operands: [
@ -127,7 +127,7 @@ export function removePlaceholders(q: QueryElem | null): QueryElem | null {
var newOperands: QueryElem[] = [];
q.operands.forEach((op: any) => {
if (isLeafElem(op) && op.leafOp == QueryLeafOp.Placeholder) {
if (isLeafElem(op) && op.leafOp === QueryLeafOp.Placeholder) {
return;
}
const newOp = removePlaceholders(op);
@ -136,14 +136,14 @@ export function removePlaceholders(q: QueryElem | null): QueryElem | null {
}
})
if (newOperands.length == 0) {
if (newOperands.length === 0) {
return null;
}
if (newOperands.length == 1) {
if (newOperands.length === 1) {
return newOperands[0];
}
return { operands: newOperands, nodeOp: q.nodeOp };
} else if (q && isLeafElem(q) && q.leafOp == QueryLeafOp.Placeholder) {
} else if (q && isLeafElem(q) && q.leafOp === QueryLeafOp.Placeholder) {
return null;
}

Loading…
Cancel
Save