Implemented session data for audio

This commit is contained in:
Pax1601
2024-12-03 18:54:30 +01:00
parent 5e40d7abf1
commit f17ee42d63
18 changed files with 212 additions and 208 deletions

View File

@@ -10,10 +10,10 @@ export function OlFrequencyInput(props: { value: number; className?: string; onC
flex gap-2
`}>
<OlNumberInput
min={1}
min={0}
max={400}
onChange={(e) => {
let newValue = Math.max(Math.min(Number(e.target.value), 400), 1) * 1000000;
let newValue = Math.max(Math.min(Number(e.target.value), 400), 0) * 1000000;
let decimalPart = frequency - Math.floor(frequency / 1000000) * 1000000;
frequency = newValue + decimalPart;
props.onChange(frequency);

View File

@@ -3,7 +3,10 @@ import React from "react";
export function OlLabelToggle(props: { toggled: boolean | undefined; leftLabel: string; rightLabel: string; onClick: () => void }) {
return (
<button
onClick={props.onClick}
onClick={(e) => {
e.stopPropagation();
props.onClick();
}}
className={`
relative flex h-10 w-[120px] flex-none cursor-pointer select-none
flex-row content-center justify-between rounded-md border px-1 py-[5px]

View File

@@ -21,7 +21,10 @@ export function OlNumberInput(props: {
<div className="relative flex max-w-[8rem] items-center">
<button
type="button"
onClick={props.onDecrease}
onClick={(e) => {
e.stopPropagation();
props.onDecrease();
}}
className={`
h-10 rounded-s-lg bg-gray-100 p-3
dark:bg-gray-700 dark:hover:bg-gray-600 dark:focus:ring-blue-700
@@ -45,6 +48,7 @@ export function OlNumberInput(props: {
<input
type="text"
onChange={props.onChange}
onClick={(e) => e.stopPropagation()}
min={props.min}
max={props.max}
className={`
@@ -59,7 +63,10 @@ export function OlNumberInput(props: {
/>
<button
type="button"
onClick={props.onIncrease}
onClick={(e) => {
e.stopPropagation();
props.onIncrease();
}}
className={`
h-10 rounded-e-lg bg-gray-100 p-3
dark:bg-gray-700 dark:hover:bg-gray-600 dark:focus:ring-blue-500

View File

@@ -30,13 +30,14 @@ export function OlStateButton(props: {
textColor = "#243141";
}
const opacity = (hover && !props.checked) ? "AA" : "FF";
const opacity = hover && !props.checked ? "AA" : "FF";
return (
<>
<button
ref={buttonRef}
onClick={() => {
onClick={(e) => {
e.stopPropagation();
props.onClick();
props.onClick ?? setHover(false);
}}

View File

@@ -2,10 +2,16 @@ import React from "react";
export function OlToggle(props: { toggled: boolean | undefined; onClick: () => void }) {
return (
<div className="inline-flex cursor-pointer items-center" onClick={props.onClick}>
<div
className="inline-flex cursor-pointer items-center"
onClick={(e) => {
e.stopPropagation();
props.onClick();
}}
>
<button className="peer sr-only" />
<div
data-toggled={props.toggled === true? 'true': props.toggled === undefined? 'undefined': 'false'}
data-toggled={props.toggled === true ? "true" : props.toggled === undefined ? "undefined" : "false"}
className={`
peer relative h-7 w-14 rounded-full bg-gray-200
after:absolute after:start-[4px] after:top-0.5 after:h-6 after:w-6

View File

@@ -1,129 +0,0 @@
import React, { useEffect, useState } from "react";
import { Modal } from "./components/modal";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowRight, faCheck, faUpload } from "@fortawesome/free-solid-svg-icons";
import { getApp } from "../../olympusapp";
import { OlympusState } from "../../constants/constants";
import { SessionDataLoadedEvent } from "../../events";
export function FileSourceLoadPrompt(props: { open: boolean }) {
const [files, setFiles] = useState([] as { filename: string; volume: number }[]);
const [loaded, setLoaded] = useState([] as boolean[]);
useEffect(() => {
SessionDataLoadedEvent.on((sessionData) => {
if (getApp().getState() === OlympusState.LOAD_FILES) return; // TODO don't like this, is hacky Should avoid reading state directly
if (sessionData.fileSources) {
setFiles([...sessionData.fileSources]);
setLoaded(
sessionData.fileSources.map((file) => {
return false;
})
);
}
});
}, []);
return (
<Modal
open={props.open}
className={`
inline-flex h-fit max-h-[800px] w-[600px] overflow-y-auto scroll-smooth
bg-white p-10
dark:bg-olympus-800
max-md:h-full max-md:max-h-full max-md:w-full max-md:rounded-none
max-md:border-none
`}
>
<div className="flex h-full w-full flex-col gap-12">
<div className={`flex flex-col items-start gap-2`}>
<span
className={`
text-gray-800 text-md
dark:text-white
`}
>
Please, select the files for the following audio sources
</span>
<span
className={`
text-gray-800 text-md
dark:text-gray-500
`}
>
Browsers can't automatically load files from your computer, therefore you must click on the following buttons to select the original files for each
audio file source.
</span>
<span
className={`
text-gray-800 text-md
dark:text-gray-500
`}
>
If you don't want to reload your audio sources, press "Skip".
</span>
<div className="mt-4 w-full">
{files.map((fileData, idx) => {
return (
<div
className={`flex w-full content-center justify-between gap-4`}
>
<span className={`my-auto truncate text-white`}>{fileData.filename}</span>
<button
type="button"
disabled={loaded[idx] === true || (idx > 0 && loaded[idx - 1] == false)}
data-disabled={loaded[idx] === true || (idx > 0 && loaded[idx - 1] == false)}
onClick={() => {
var input = document.createElement("input");
input.type = "file";
input.click();
input.onchange = (e: Event) => {
let target = e.target as HTMLInputElement;
if (target && target.files) {
var file = target.files[0];
getApp().getAudioManager().addFileSource(file).setVolume(fileData.volume);
loaded[idx] = true;
setLoaded([...loaded]);
if (idx === loaded.length - 1) getApp().setState(OlympusState.IDLE);
}
};
}}
className={`
mb-2 me-2 ml-auto flex cursor-pointer content-center
items-center gap-2 rounded-sm bg-blue-600 px-5 py-2.5
text-sm font-medium text-white
data-[disabled="true"]:bg-blue-800
focus:outline-none focus:ring-4 focus:ring-blue-800
hover:bg-blue-700
`}
>
{loaded[idx] ? "Loaded" : "Load"}
<FontAwesomeIcon className={`my-auto`} icon={loaded[idx] ? faCheck : faUpload} />
</button>
</div>
);
})}
</div>
</div>
<div className="flex">
<button
type="button"
onClick={() => {getApp().setState(OlympusState.IDLE)}}
className={`
mb-2 me-2 ml-auto flex content-center items-center gap-2
rounded-sm border-[1px] bg-blue-700 px-5 py-2.5 text-sm
font-medium text-white
dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400
dark:hover:bg-gray-700 dark:focus:ring-blue-800
focus:outline-none focus:ring-4 focus:ring-blue-300
hover:bg-blue-800
`}
>
Skip
<FontAwesomeIcon className={`my-auto`} icon={faArrowRight} />
</button>
</div>
</div>
</Modal>
);
}

View File

@@ -202,18 +202,7 @@ export function AudioMenu(props: { open: boolean; onClose: () => void; children?
focus:outline-none focus:ring-4 focus:ring-blue-300
hover:bg-blue-800
`}
onClick={() => {
var input = document.createElement("input");
input.type = "file";
input.click();
input.onchange = (e: Event) => {
let target = e.target as HTMLInputElement;
if (target && target.files) {
var file = target.files[0];
getApp().getAudioManager().addFileSource(file);
}
};
}}
onClick={() => getApp().getAudioManager().addFileSource()}
>
Add audio source
</button>

View File

@@ -18,11 +18,14 @@ export const RadioSinkPanel = forwardRef((props: { radio: RadioSink; shortcutKey
<div
data-receiving={props.radio.getReceiving()}
className={`
box-border flex flex-col content-center justify-between gap-2 rounded-md
border-2 border-transparent bg-olympus-200/30 px-4 py-3
box-border flex cursor-pointer flex-col content-center justify-between
gap-2 rounded-md border-2 border-transparent bg-olympus-200/30 px-4 py-3
data-[receiving='true']:border-white
`}
ref={ref}
onClick={() => {
setExpanded(!expanded);
}}
>
<div className="flex content-center justify-between gap-2">
<div

View File

@@ -8,6 +8,7 @@ import { OlRangeSlider } from "../../components/olrangeslider";
import { FileSource } from "../../../audio/filesource";
import { MicrophoneSource } from "../../../audio/microphonesource";
import { TextToSpeechSource } from "../../../audio/texttospeechsource";
import { FaUpload } from "react-icons/fa";
export const AudioSourcePanel = forwardRef((props: { source: AudioSource; onExpanded: () => void }, ref: ForwardedRef<HTMLDivElement>) => {
const [meterLevel, setMeterLevel] = useState(0);
@@ -48,7 +49,47 @@ export const AudioSourcePanel = forwardRef((props: { source: AudioSource; onExpa
/>
</div>
<div className="flex w-full overflow-hidden">
<span className={`my-auto truncate`}>{props.source.getName()}</span>
<div className={`my-auto w-full truncate`}>
{props.source.getName() === "" ? (
props.source instanceof FileSource ? (
<div
className="flex w-full content-center justify-between"
>
<span className={`my-auto text-red-500`}>No file selected</span>
<button
type="button"
onClick={() => {
var input = document.createElement("input");
input.type = "file";
input.click();
input.onchange = (e: Event) => {
let target = e.target as HTMLInputElement;
if (target && target.files) {
var file = target.files[0];
(props.source as FileSource).setFile(file)
}
};
}}
className={`
flex cursor-pointer content-center items-center gap-2
rounded-sm bg-blue-600 px-5 py-2.5 text-sm font-medium
text-white
data-[disabled="true"]:bg-blue-800
focus:outline-none focus:ring-4 focus:ring-blue-800
hover:bg-blue-700
`}
>
<FaUpload className={`my-auto`} />
</button>
</div>
) : (
"No name"
)
) : (
props.source.getName()
)}
</div>
</div>
{!(props.source instanceof MicrophoneSource) && !(props.source instanceof TextToSpeechSource) && (
<div
@@ -68,22 +109,22 @@ export const AudioSourcePanel = forwardRef((props: { source: AudioSource; onExpa
<>
{(props.source instanceof FileSource || props.source instanceof TextToSpeechSource) && (
<div className="flex flex-col gap-2 rounded-md bg-olympus-400 p-2">
{props.source instanceof TextToSpeechSource &&
<input
className={`
block h-10 w-full border-[2px] bg-gray-50 py-2.5 text-center
text-sm text-gray-900
dark:border-gray-700 dark:bg-olympus-600 dark:text-white
dark:placeholder-gray-400 dark:focus:border-blue-700
dark:focus:ring-blue-700
focus:border-blue-700 focus:ring-blue-500
`}
value={text}
onChange={(ev) => {
setText(ev.target.value);
}}
></input>
}
{props.source instanceof TextToSpeechSource && (
<input
className={`
block h-10 w-full border-[2px] bg-gray-50 py-2.5 text-center
text-sm text-gray-900
dark:border-gray-700 dark:bg-olympus-600 dark:text-white
dark:placeholder-gray-400 dark:focus:border-blue-700
dark:focus:ring-blue-700
focus:border-blue-700 focus:ring-blue-500
`}
value={text}
onChange={(ev) => {
setText(ev.target.value);
}}
></input>
)}
<div className="flex gap-4">
<OlStateButton
checked={false}
@@ -97,7 +138,8 @@ export const AudioSourcePanel = forwardRef((props: { source: AudioSource; onExpa
<OlRangeSlider
value={props.source.getDuration() > 0 ? (props.source.getCurrentPosition() / props.source.getDuration()) * 100 : 0}
onChange={(ev) => {
if (props.source instanceof FileSource || props.source instanceof TextToSpeechSource) props.source.setCurrentPosition(parseFloat(ev.currentTarget.value));
if (props.source instanceof FileSource || props.source instanceof TextToSpeechSource)
props.source.setCurrentPosition(parseFloat(ev.currentTarget.value));
}}
className="my-auto"
/>
@@ -124,9 +166,10 @@ export const AudioSourcePanel = forwardRef((props: { source: AudioSource; onExpa
flex-row border-gray-500
`}
>
<div style={{ minWidth: `${meterLevel * 100}%` }} className={`
rounded-full bg-gray-200
`}></div>
<div
style={{ minWidth: `${meterLevel * 100}%` }}
className={`rounded-full bg-gray-200`}
></div>
</div>
<OlRangeSlider
value={props.source.getVolume() * 100}

View File

@@ -11,7 +11,6 @@ import { MapHiddenTypes, MapOptions } from "../types/types";
import { NO_SUBSTATE, OlympusState, OlympusSubState, OptionsSubstate, SpawnSubState, UnitControlSubState } from "../constants/constants";
import { getApp, setupApp } from "../olympusapp";
import { LoginModal } from "./modals/loginmodal";
import { FileSourceLoadPrompt } from "./modals/filesourceloadprompt";
import { MiniMapPanel } from "./panels/minimappanel";
import { UnitControlBar } from "./panels/unitcontrolbar";
@@ -70,8 +69,7 @@ export function UI() {
<LoginModal open={appState === OlympusState.LOGIN} />
<ProtectionPromptModal open={appState === OlympusState.UNIT_CONTROL && appSubState == UnitControlSubState.PROTECTION} />
<KeybindModal open={appState === OlympusState.OPTIONS && appSubState === OptionsSubstate.KEYBIND} />
<FileSourceLoadPrompt open={appState === OlympusState.LOAD_FILES}/>
<div id="map-container" className="z-0 h-full w-screen" />
<MainMenu open={appState === OlympusState.MAIN_MENU} onClose={() => getApp().setState(OlympusState.IDLE)} />