Handling situation where React Router may not be available for a Tag Label.

This commit is contained in:
Roland 2023-05-03 12:46:57 -06:00
parent 0920383963
commit 2cbc6e00e0
No known key found for this signature in database
GPG key ID: 9D6DBD3ACF11AB93
8 changed files with 56 additions and 33 deletions

View file

@ -10,16 +10,16 @@ import {MoreRoute} from "./components/Apps";
import {InventoryRoute} from "./components/Inventory";
import {ArchiveRoute} from "./components/Archive";
import {FilesRoute} from "./components/Files";
import {useStatusInterval} from "./hooks/customHooks";
import {StatusProvider} from "./hooks/customHooks";
import {MapRoute} from "./components/Map";
import {MediaContextProvider, mediaStyles, StatusContext, ThemeContext} from "./contexts/contexts";
import {Header, ThemeWrapper} from "./components/Theme";
import {Header, ThemeProvider} from "./components/Theme";
import {Dashboard} from "./Dashboard";
import {Donate} from "./components/Donate";
import {useEventsInterval} from "./Events";
import {SemanticToastContainer} from "react-semantic-toasts-2";
import {FilePreviewWrapper} from "./components/FilePreview";
import {TagsContext, useTagsInterval} from "./Tags";
import {FilePreviewProvider} from "./components/FilePreview";
import {TagsProvider} from "./Tags";
function PageNotFound() {
const {t} = useContext(ThemeContext);
@ -76,24 +76,21 @@ const router = createBrowserRouter(createRoutesFromElements(<Route
</Route>));
export default function App() {
const statusValue = useStatusInterval();
const tagsValue = useTagsInterval();
useEventsInterval();
return <ThemeWrapper>
<TagsContext.Provider value={tagsValue}>
<FilePreviewWrapper>
return <ThemeProvider>
<TagsProvider>
<FilePreviewProvider>
{/* Context and style to handle switching between mobile/computer. */}
<style>{mediaStyles}</style>
{/* Toasts can be on any page. */}
<SemanticToastContainer position='top-right'/>
<MediaContextProvider>
<StatusContext.Provider value={statusValue}>
<StatusProvider>
<RouterProvider router={router}/>
</StatusContext.Provider>
</StatusProvider>
</MediaContextProvider>
</FilePreviewWrapper>
</TagsContext.Provider>
</ThemeWrapper>
</FilePreviewProvider>
</TagsProvider>
</ThemeProvider>
}

View file

@ -32,7 +32,7 @@ import _ from "lodash";
import {HexColorPicker} from "react-colorful";
import {useRecurringTimeout} from "./hooks/customHooks";
import {Media, ThemeContext} from "./contexts/contexts";
import {Link} from "react-router-dom";
import {Link, useNavigate} from "react-router-dom";
export const TagsContext = React.createContext({
NameToTagLabel: null,
@ -104,16 +104,30 @@ export function useTags() {
</Label.Group>
}
const TagLabelLink = ({name, props}) => {
const to = `/?tag=${name}`;
const style = {marginLeft: '0.3em', marginRight: '0.3em'};
try {
// We prefer to use Link to avoid reloading the page, check if React Router is available, so we can use it.
useNavigate();
return <Link to={to} style={style}>
<NameToTagLabel name={name} {...props}/>
</Link>
} catch {
// React Router is not available, use anchor.
return <a href={to} style={style}>
<NameToTagLabel name={name} {...props}/>
</a>
}
}
const TagsLinkGroup = ({tagNames, ...props}) => {
if (!tagNames || tagNames.length === 0) {
return <React.Fragment/>;
}
return <Label.Group tag>
{tagNames.map(i =>
<Link key={i} to={`/?tag=${i}`} style={{marginLeft: '0.3em', marginRight: '0.3em'}}>
<NameToTagLabel name={i} {...props}/>
</Link>
)}
{tagNames.map(i => <TagLabelLink key={i} name={i} props={props}/>)}
</Label.Group>
}
@ -227,7 +241,7 @@ function EditTagsModal() {
onClose={localOnClose}
>
<ModalHeader>Edit Tags</ModalHeader>
<div className={`scrolling content ${inverted ? 'inverted' : ''}`} id='editModalContent'>
<div className={`content scrolling ${inverted}`} id='editModalContent'>
<Label.Group tag>
<Label size='large' style={{backgroundColor: tagColor, color: textColor}}>
{tagName || 'Example Tag'}
@ -459,3 +473,11 @@ export const TagsDropdown = ({value = [], onChange, ...props}) => {
{...props}
/>
}
export const TagsProvider = (props) => {
const tagsValue = useTagsInterval();
return <TagsContext.Provider value={tagsValue}>
{props.children}
</TagsContext.Provider>
}

View file

@ -440,7 +440,7 @@ function ChannelRow({channel}) {
const {inverted} = React.useContext(ThemeContext);
const editTo = `/videos/channel/${channel.id}/edit`;
const buttonClass = `ui button secondary ${inverted ? 'inverted' : ''}`;
const buttonClass = `ui button secondary ${inverted}`;
return <TableRow>
<TableCell>

View file

@ -1,11 +1,7 @@
import React from "react";
import _ from "lodash";
import {
Grid,
Header as SHeader,
Image,
} from "semantic-ui-react";
import {TagsSelector} from "../Tags";
import {Grid, Header as SHeader, Image,} from "semantic-ui-react";
import {TagsProvider, TagsSelector} from "../Tags";
import {Media} from "../contexts/contexts";
import {encodeMediaPath} from "./Common";
import {addTag, fetchFile, removeTag} from "../api";
@ -152,7 +148,7 @@ export const FilePreviewContext = React.createContext({
const MAXIMUM_TEXT_SIZE = 5_000_000;
export function FilePreviewWrapper({children}) {
export function FilePreviewProvider({children}) {
const [previewFile, setPreviewFile] = React.useState(null);
const [previewModal, setPreviewModal] = React.useState(null);
const [callbacks, setCallbacks] = React.useState(null);

View file

@ -38,7 +38,7 @@ export const defaultTheme = lightTheme;
export const systemTheme = 'system';
export const themeSessionKey = 'color-scheme';
export function ThemeWrapper({children, ...props}) {
export function ThemeProvider({children, ...props}) {
if (!_.isEmpty(props)) {
console.log(props);
console.error('ThemeWrapper does not support props!');

View file

@ -183,7 +183,7 @@ function VideoPage({videoFile, prevFile, nextFile, fetchVideo, ...props}) {
>
<source src={videoUrl} type="video/mp4"/>
{/* Only WebVTT captions can be displayed. */}
{captionUrls.map(i => <track kind="captions" label="English" src={i} srcLang="en" default/>)}
{captionUrls.map(i => <track key={i} kind="captions" label="English" src={i} srcLang="en" default/>)}
</video>
<Container style={{marginTop: '1em'}}>

View file

@ -717,6 +717,14 @@ export const useStatusInterval = () => {
return {status, fetchStatus};
}
export const StatusProvider = (props) => {
const value = useStatusInterval();
return <StatusContext.Provider value={value}>
{props.children}
</StatusContext.Provider>
}
export const useVideoStatistics = () => {
const [statistics, setStatistics] = useState({});

View file

@ -201,7 +201,7 @@ def download_channel(id_: int):
@optional_session
def search_channels_by_directory(name: str, limit: int = 5, session: Session = None) -> List[Channel]:
channels = session.query(Channel) \
.filter(Channel.directory.ilike(f'%{name}%')) \
.filter(Channel.name.ilike(f'%{name}%')) \
.order_by(asc(Channel.name)) \
.limit(limit) \
.all()