diff --git a/.env.development b/.env.development index 93fa24a..7e21354 100644 --- a/.env.development +++ b/.env.development @@ -1 +1,2 @@ -VITE_API_URL=http://localhost:8000 \ No newline at end of file +VITE_API_URL=http://localhost:8000 +VITE_APP_LOG=true \ No newline at end of file diff --git a/.env.production b/.env.production index 42318bd..db7eee5 100644 --- a/.env.production +++ b/.env.production @@ -1 +1,2 @@ -VITE_API_URL=https://uvm015.dm.informatik.tu-darmstadt.de/api \ No newline at end of file +VITE_API_URL=https://uvm015.dm.informatik.tu-darmstadt.de/api +VITE_APP_LOG=false \ No newline at end of file diff --git a/src/components/DocBaseOverview/DocBaseOverview.tsx b/src/components/DocBaseOverview/DocBaseOverview.tsx index 8b9e757..085d4b9 100644 --- a/src/components/DocBaseOverview/DocBaseOverview.tsx +++ b/src/components/DocBaseOverview/DocBaseOverview.tsx @@ -34,7 +34,6 @@ function DocBaseOverview({ organizationProp }: Props) { }, []); const loadDocBases = (orgID: number) => { - console.log('loadDocBases: ' + orgID); if (orgID === -1) { setDocBases([]); return; diff --git a/src/components/LoadingScreen/LoadingScreen.scss b/src/components/LoadingScreen/LoadingScreen.scss index 4890fa4..4681a63 100644 --- a/src/components/LoadingScreen/LoadingScreen.scss +++ b/src/components/LoadingScreen/LoadingScreen.scss @@ -40,7 +40,7 @@ bottom: 20px; right: 20px; width: 500px; - height: 300px; + height: 350px; z-index: 9999; border: 2px solid var(--text-color); box-shadow: 0px 0px 10px 0px var(--text-color); @@ -59,8 +59,11 @@ align-items: center; width: 100%; height: 100%; - left: 0; + left: 50%; top: 0; + text-align: center; + width: 90%; + transform: translateX(-50%); } .screenBtn { diff --git a/src/components/LoadingScreen/LoadingScreen.tsx b/src/components/LoadingScreen/LoadingScreen.tsx index 8261d95..4cacbfb 100644 --- a/src/components/LoadingScreen/LoadingScreen.tsx +++ b/src/components/LoadingScreen/LoadingScreen.tsx @@ -38,7 +38,13 @@ function LoadingScreen({ heading, info = '', id }: Props) {

{id}

-

{info}

+

+ {info} +

diff --git a/src/providers/AudioProvider.tsx b/src/providers/AudioProvider.tsx new file mode 100644 index 0000000..e03b729 --- /dev/null +++ b/src/providers/AudioProvider.tsx @@ -0,0 +1,97 @@ +import React, { ReactNode } from 'react'; +import { useStoreInLS } from './StorageProvider'; + +// eslint-disable-next-line react-refresh/only-export-components +export enum MyAudio { + BING = '/src/audio/bing.mp3', +} + +const AudioContext = React.createContext({ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + playAudio: (_audio: MyAudio) => {}, + toggleAudio: () => {}, + isAudioEnabled: (): boolean => { + return false; + }, +}); + +// eslint-disable-next-line react-refresh/only-export-components +export function usePlayAudio() { + const context = React.useContext(AudioContext); + if (!context) { + throw new Error('usePlayAudio must be used within a AudioProvider'); + } + return context.playAudio; +} + +// eslint-disable-next-line react-refresh/only-export-components +export function useToggleAudio() { + const context = React.useContext(AudioContext); + if (!context) { + throw new Error('useToggleAudio must be used within a AudioProvider'); + } + return context.toggleAudio; +} + +// eslint-disable-next-line react-refresh/only-export-components +export function useIsAudioEnabled() { + const context = React.useContext(AudioContext); + if (!context) { + throw new Error( + 'useIsAudioEnabled must be used within a AudioProvider' + ); + } + return context.isAudioEnabled; +} + +interface Props { + children: ReactNode; +} + +/** + * A provider that plays audio + */ +export function AudioProvider({ children }: Props) { + const [enabled, setEnabled] = React.useState(false); + + const storeInLS = useStoreInLS(); + + React.useEffect(() => { + if (localStorage.getItem('audio-allowed') === 'true') { + setEnabled(true); + } + }, []); + + const play = (audio: MyAudio) => { + if (!enabled) { + return; + } + const a = new Audio(audio); + a.addEventListener('canplaythrough', () => { + /* the audio is now playable; play it if permissions allow */ + a.play(); + }); + a.play(); + }; + + const toggle = () => { + storeInLS('audio-allowed', JSON.stringify(!enabled)); + setEnabled(!enabled); + }; + + const isEnabled = () => { + return enabled; + }; + + return ( + + {children} + + ); +} diff --git a/src/providers/DocBaseTaskProvider.tsx b/src/providers/DocBaseTaskProvider.tsx index 50528fa..3eadada 100644 --- a/src/providers/DocBaseTaskProvider.tsx +++ b/src/providers/DocBaseTaskProvider.tsx @@ -2,9 +2,14 @@ import React, { ReactNode } from 'react'; import DocbaseViewer from '../components/DocbaseViewer/DocbaseViewer'; import DocBase from '../types/DocBase'; -import { useSetLoadingScreen } from './LoadingScreenProvider'; +import { + useSetLoadingScreen, + useSetLoadingScreenLock, +} from './LoadingScreenProvider'; import APIService from '../utils/ApiService'; import { useShowNotification } from './NotificationProvider'; +import Logger from '../utils/Logger'; +import { MyAudio, usePlayAudio } from './AudioProvider'; const DocBaseTaskContext = React.createContext({ isDocbaseTaskRunning: (): boolean => { @@ -48,7 +53,9 @@ interface Props { */ export function DocBaseTaskProvider({ children }: Props) { const setLoadingScreen = useSetLoadingScreen(); + const setLoadingScreenLock = useSetLoadingScreenLock(); const showNotification = useShowNotification(); + const playAudio = usePlayAudio(); const [isRunning, setIsRunning] = React.useState(false); const [docBase, setDocBase] = React.useState( @@ -67,13 +74,15 @@ export function DocBaseTaskProvider({ children }: Props) { 'Please wait...', taskId ); + setLoadingScreenLock(true); setIsRunning(true); const updateInterval = setInterval(() => { // eslint-disable-next-line @typescript-eslint/no-explicit-any APIService.getTaskStatus(taskId).then((res): any => { - console.log(res); + Logger.log(res); if (res == undefined || res.state === 'FAILURE') { + setLoadingScreenLock(false); setLoadingScreen(false); showNotification( 'Error', @@ -87,11 +96,14 @@ export function DocBaseTaskProvider({ children }: Props) { } if (res.state === 'SUCCESS') { + setLoadingScreenLock(false); setLoadingScreen(false); + // play sound + playAudio(MyAudio.BING); + const docBase = new DocBase(basename, attList); for (const nugget of res.meta.document_base_to_ui.msg .nuggets) { - console.log(nugget); try { docBase.addNugget( nugget.document.name, @@ -132,7 +144,8 @@ export function DocBaseTaskProvider({ children }: Props) { true, 'Creating Docbase ' + basename + '...', info, - taskId + taskId, + true ); }); }, 1000); diff --git a/src/providers/LoadingScreenProvider.tsx b/src/providers/LoadingScreenProvider.tsx index 1f27c02..9dfb860 100644 --- a/src/providers/LoadingScreenProvider.tsx +++ b/src/providers/LoadingScreenProvider.tsx @@ -7,8 +7,10 @@ const LoadingScreenContext = React.createContext({ _loading: boolean, _heading = 'Loading...', _info = 'Please wait', - _id = '' + _id = '', + _force = false ) => {}, + setLoadingScreenLock: (_lock: boolean) => {}, }); // eslint-disable-next-line react-refresh/only-export-components @@ -22,6 +24,17 @@ export function useSetLoadingScreen() { return context.setLoadingScreen; } +// eslint-disable-next-line react-refresh/only-export-components +export function useSetLoadingScreenLock() { + const context = React.useContext(LoadingScreenContext); + if (!context) { + throw new Error( + 'useSetLoadingScreenLock must be used within a LoadingScreenProvider' + ); + } + return context.setLoadingScreenLock; +} + interface Props { children: ReactNode; } @@ -31,6 +44,7 @@ interface Props { */ export function LoadingScreenProvider({ children }: Props) { const [loading, setLoading] = React.useState(false); + const [lock, setLock] = React.useState(false); const [heading, setHeading] = React.useState(''); const [info, setInfo] = React.useState(''); const [id, setId] = React.useState(''); @@ -39,18 +53,25 @@ export function LoadingScreenProvider({ children }: Props) { loading: boolean, heading = 'Loading...', info = 'Please wait', - id = '' + id = '', + force = false ) => { + if (lock && !force) return; setLoading(loading); setHeading(heading); setInfo(info); setId(id); }; + const setLoadingScreenLock = (lock: boolean) => { + setLock(lock); + }; + return ( {loading && } diff --git a/src/providers/Providers.tsx b/src/providers/Providers.tsx index 59384e7..55c135d 100644 --- a/src/providers/Providers.tsx +++ b/src/providers/Providers.tsx @@ -6,6 +6,7 @@ import { ThemeProvider } from './ThemeProvider'; import { UserProvider } from './UserProvider'; import { LoadingScreenProvider } from './LoadingScreenProvider'; import { DocBaseTaskProvider } from './DocBaseTaskProvider'; +import { AudioProvider } from './AudioProvider'; interface Props { children: ReactNode; @@ -20,13 +21,15 @@ export function Providers({ children }: Props) { - - - - {children} - - - + + + + + {children} + + + + diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts new file mode 100644 index 0000000..39f1027 --- /dev/null +++ b/src/utils/Logger.ts @@ -0,0 +1,32 @@ +/** + * A logger class to log messages to the console only if the environment variable VITE_APP_LOG is set to true + */ +class Logger { + /** + * Log a message to the console + * @param message The message to log + */ + public static log(message: unknown): void { + if (import.meta.env.VITE_APP_LOG === 'false') return; + console.log(message); + } + + /** + * Log a error to the console + * @param message The message to log + */ + public static error(message: unknown): void { + if (import.meta.env.VITE_APP_LOG === 'false') return; + console.error(message); + } + /** + * Log a error to the console + * @param message The message to log + */ + public static warn(message: unknown): void { + if (import.meta.env.VITE_APP_LOG === 'false') return; + console.warn(message); + } +} + +export default Logger; diff --git a/src/views/Settings/Settings.tsx b/src/views/Settings/Settings.tsx index b149462..d03192d 100644 --- a/src/views/Settings/Settings.tsx +++ b/src/views/Settings/Settings.tsx @@ -1,4 +1,8 @@ import Navbar from '../../components/Navbar/Navbar'; +import { + useIsAudioEnabled, + useToggleAudio, +} from '../../providers/AudioProvider'; import { useAcceptCookie, useCookieAllowed, @@ -18,6 +22,9 @@ function Settings() { const isDarkMode = useIsDarkTheme(); const toggleTheme = useToggleTheme(); + const isSoundEnabled = useIsAudioEnabled(); + const toggleSound = useToggleAudio(); + return (
@@ -41,6 +48,11 @@ function Settings() { earance + + {isDarkMode + ? 'Dark Mode is enabled' + : 'Light Mode is enabled'} + +

+ Sound +

+ {isSoundEnabled() ? 'Sound is On' : 'Sound is Off'} +

Storage