Tone3000 dialog (waiting for iframe fix)

This commit is contained in:
Robin E. R. Davies
2026-01-23 21:47:31 -05:00
parent f26e3bf102
commit f0c3995a8f
4 changed files with 275 additions and 261 deletions
+26
View File
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tone3000 Download Received</title>
</head>
<body>
<script>
// Parse URL parameters
const urlParams = new URLSearchParams(window.location.search);
const toneUrl = urlParams.get('tone_url');
// Send the data back to the opener window
if (window.opener) {
window.opener.postMessage({
type: 'tone3000Download',
toneUrl: toneUrl
}, '*');
}
// Close the window after sending the data
window.close();
</script>
</body>
</html>
+22
View File
@@ -0,0 +1,22 @@
<html>
<head>
<title>Test Tone3000 Select</title>
<script>
function onPageLoad() {
console.log("Test Tone3000 Select Page Loaded");
let iframe = document.getElementById("tone3000SelectIframe");
iframe.src = "https://www.tone3000.com/api/v1/select?app_id=pipedal_app&redirect_url=http://localhost:5173/public/handleTone3000download.html";
}
</script>
</head>
<body onload="onPageLoad()">
<h1>Test Tone3000 Select Page</h1>
<p>This is a test page for Tone3000 select functionality.</p>
<iframe
style="width: 680px; height: 400px; border: 2px solid red;"
id="tone3000SelectIframe"
style="width:100%; height:600px; border:none;">
</body>
</html>
+138 -134
View File
@@ -21,7 +21,7 @@
import React from 'react';
import { createStyles } from './WithStyles';
// import Tone3000Dialog from './Tone3000Dialog';
import Tone3000Dialog from './Tone3000Dialog';
import TextInfoDialog from './TextInfoDialog';
import Tone3000HelpDialog from './Tone3000HelpDialog';
@@ -172,11 +172,11 @@ export interface FilePropertyDialogState {
previousSelection: string;
multiSelect: boolean,
selectedFiles: string[],
//openTone3000Dialog: boolean,
openTone3000Dialog: boolean,
openTone3000Help: boolean,
openGuitarMlHelp: boolean,
textFileName? : string;
textFileName?: string;
};
@@ -249,7 +249,7 @@ export default withStyles(
previousSelection: this.props.selectedFile,
multiSelect: false,
selectedFiles: [],
//openTone3000Dialog: false,
openTone3000Dialog: false,
openTone3000Help: false,
openGuitarMlHelp: false
};
@@ -701,15 +701,14 @@ export default withStyles(
}
this.openSelectedFile();
}
handleApply(fileProperty: UiFileProperty, selectedItem: string)
{
handleApply(fileProperty: UiFileProperty, selectedItem: string) {
if (this.state.previousSelection == selectedItem) {
return;
}
if (!this.isLicenseFile(selectedItem) && !this.isFolderArtwork(selectedItem)) {
this.props.onApply(fileProperty, selectedItem);
}
this.setState({previousSelection: selectedItem});
this.setState({ previousSelection: selectedItem });
}
@@ -1069,13 +1068,13 @@ export default withStyles(
() => { this.handleAutoScrollTick() }, AUTOSCROLL_TICK_DELAY);
}
// handleTone3000Dialog(e: React.MouseEvent<HTMLButtonElement>) {
// e.stopPropagation();
// e.preventDefault();
handleTone3000Dialog(e: React.MouseEvent<HTMLButtonElement>) {
e.stopPropagation();
e.preventDefault();
// this.setState({ openTone3000Dialog: true });
this.setState({ openTone3000Dialog: true });
// }
}
handleGuitarMlHelp(e: React.MouseEvent<HTMLButtonElement>) {
@@ -1519,125 +1518,126 @@ export default withStyles(
{(!this.state.reordering && !this.state.multiSelect) && (
<>
<DialogActions style={{ justifyContent: "stretch", width: "100%" }}>
<div style={{display: "flex", flexFlow: "column nowrap", width: "100%", alignItems: "stretch", }}>
{isToobNamModelFile && (
<div style={{
display: "flex", flexFlow: "row nowrap", justifyContent: "center",
alignItems: "center", width: "100%"
}}>
<Typography variant="body2" >
Download model files from <LinkEx
href="https://www.tone3000.com/search" target="_blank">TONE3000</LinkEx>
</Typography>
<IconButtonEx tooltip="Help"
onClick={(e) => { this.handleTone3000Help(e); }} aria-label="help" edge="end" color="inherit" style={{ opacity: 0.6, marginLeft: 8 }}
>
<HelpOutlineIcon />
</IconButtonEx>
</div>
)}
{isToobMLModelFile && (
<div style={{
display: "flex", flexFlow: "row nowrap", justifyContent: "center", width: "100%",
alignItems: "center"
}}>
<Typography variant="body2" >
Download model files from <LinkEx
href="https://github.com/GuitarML/ToneLibrary/releases/download/v1.0/Proteus_Tone_Packs.zip" target="_blank">GuitarML</LinkEx>
</Typography>
<IconButtonEx tooltip="Help"
onClick={(e) => { this.handleGuitarMlHelp(e); }} aria-label="help" edge="end" color="inherit" style={{ opacity: 0.6, marginLeft: 8 }}
>
<HelpOutlineIcon />
</IconButtonEx>
</div>
)}
{this.state.windowWidth > 500 ? (
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "column nowrap" }}>
<div style={{ flex: "1 1 100%", display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
<IconButtonEx style={{ visibility: (this.state.hasSelection ? "visible" : "hidden") }} aria-label="delete" component="label" color="primary"
tooltip="Delete selected file or folder"
disabled={!this.state.hasSelection || this.state.selectedFile === "" || protectedItem}
onClick={() => this.handleDelete()} >
<OldDeleteIcon fontSize='small' />
<div style={{ display: "flex", flexFlow: "column nowrap", width: "100%", alignItems: "stretch", }}>
{isToobNamModelFile && (
<div style={{
display: "flex", flexFlow: "row nowrap", justifyContent: "center",
alignItems: "center", width: "100%"
}}>
<Button
onClick={(e) => { this.handleTone3000Dialog(e); }}
>
Download model files from Tone3000
</Button>
<IconButtonEx tooltip="Help"
onClick={(e) => { this.handleTone3000Help(e); }} aria-label="help" edge="end" color="inherit" style={{ opacity: 0.6, marginLeft: 8 }}
>
<HelpOutlineIcon />
</IconButtonEx>
<ButtonEx tooltip="Upload file" style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileUploadIcon />}
onClick={() => { this.setState({ openUploadFileDialog: true }) }} disabled={protectedDirectory}
>
<div>Upload</div>
</ButtonEx>
<ButtonEx tooltip="Download file" style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileDownloadIcon />}
onClick={() => { this.handleDownloadFile(); }} disabled={protectedDirectory || !this.state.hasFileSelection}
>
<div>Download</div>
</ButtonEx>
<div style={{ flex: "1 1 auto" }}>&nbsp;</div>
<Button variant="dialogSecondary" onClick={() => {
this.handleApply(this.props.fileProperty, this.state.initialSelection);
this.props.onCancel();
}} aria-label="cancel">
Cancel
</Button>
<Button variant="dialogPrimary" style={{ flex: "0 0 auto" }}
onClick={() => { this.openSelectedFile(); }}
disabled={(!canSelectFile)
} aria-label="select"
>
{okButtonText}
</Button>
</div>
</div>
) : (
<div style={{ width: "100%" }}>
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
<IconButtonEx tooltip="Delete" style={{ visibility: (this.state.hasSelection ? "visible" : "hidden") }} aria-label="delete" component="label" color="primary"
disabled={!this.state.hasSelection || this.state.selectedFile === "" || protectedItem}
onClick={() => this.handleDelete()} >
<OldDeleteIcon fontSize='small' />
)}
{isToobMLModelFile && (
<div style={{
display: "flex", flexFlow: "row nowrap", justifyContent: "center", width: "100%",
alignItems: "center"
}}>
<Typography variant="body2" >
Download model files from <LinkEx
href="https://github.com/GuitarML/ToneLibrary/releases/download/v1.0/Proteus_Tone_Packs.zip" target="_blank">GuitarML</LinkEx>
</Typography>
<IconButtonEx tooltip="Help"
onClick={(e) => { this.handleGuitarMlHelp(e); }} aria-label="help" edge="end" color="inherit" style={{ opacity: 0.6, marginLeft: 8 }}
>
<HelpOutlineIcon />
</IconButtonEx>
<Button style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileUploadIcon />}
onClick={() => { this.setState({ openUploadFileDialog: true }) }} disabled={protectedDirectory}
>
<div>Upload</div>
</Button>
<Button style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileDownloadIcon />}
onClick={() => { this.handleDownloadFile(); }} disabled={protectedDirectory || !this.state.hasFileSelection}
>
<div>Download</div>
</Button>
<div style={{ flex: "1 1 auto" }}>&nbsp;</div>
</div>
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
<div style={{ flex: "1 1 auto" }}>&nbsp;</div>
<Button variant="dialogSecondary" onClick={() => { this.props.onCancel(); }} aria-label="cancel">
Cancel
</Button>
<Button variant="dialogPrimary" style={{ flex: "0 0 auto" }}
onClick={() => { this.openSelectedFile(); }}
)}
disabled={!canSelectFile}
aria-label="select"
>
{okButtonText}
</Button>
{this.state.windowWidth > 500 ? (
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "column nowrap" }}>
<div style={{ flex: "1 1 100%", display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
<IconButtonEx style={{ visibility: (this.state.hasSelection ? "visible" : "hidden") }} aria-label="delete" component="label" color="primary"
tooltip="Delete selected file or folder"
disabled={!this.state.hasSelection || this.state.selectedFile === "" || protectedItem}
onClick={() => this.handleDelete()} >
<OldDeleteIcon fontSize='small' />
</IconButtonEx>
<ButtonEx tooltip="Upload file" style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileUploadIcon />}
onClick={() => { this.setState({ openUploadFileDialog: true }) }} disabled={protectedDirectory}
>
<div>Upload</div>
</ButtonEx>
<ButtonEx tooltip="Download file" style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileDownloadIcon />}
onClick={() => { this.handleDownloadFile(); }} disabled={protectedDirectory || !this.state.hasFileSelection}
>
<div>Download</div>
</ButtonEx>
<div style={{ flex: "1 1 auto" }}>&nbsp;</div>
<Button variant="dialogSecondary" onClick={() => {
this.handleApply(this.props.fileProperty, this.state.initialSelection);
this.props.onCancel();
}} aria-label="cancel">
Cancel
</Button>
<Button variant="dialogPrimary" style={{ flex: "0 0 auto" }}
onClick={() => { this.openSelectedFile(); }}
disabled={(!canSelectFile)
} aria-label="select"
>
{okButtonText}
</Button>
</div>
</div>
</div>
)}
) : (
<div style={{ width: "100%" }}>
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
<IconButtonEx tooltip="Delete" style={{ visibility: (this.state.hasSelection ? "visible" : "hidden") }} aria-label="delete" component="label" color="primary"
disabled={!this.state.hasSelection || this.state.selectedFile === "" || protectedItem}
onClick={() => this.handleDelete()} >
<OldDeleteIcon fontSize='small' />
</IconButtonEx>
<Button style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileUploadIcon />}
onClick={() => { this.setState({ openUploadFileDialog: true }) }} disabled={protectedDirectory}
>
<div>Upload</div>
</Button>
<Button style={{ flex: "0 0 auto" }} aria-label="upload" variant="text" startIcon={<FileDownloadIcon />}
onClick={() => { this.handleDownloadFile(); }} disabled={protectedDirectory || !this.state.hasFileSelection}
>
<div>Download</div>
</Button>
<div style={{ flex: "1 1 auto" }}>&nbsp;</div>
</div>
<div style={{ display: "flex", width: "100%", alignItems: "center", flexFlow: "row nowrap" }}>
<div style={{ flex: "1 1 auto" }}>&nbsp;</div>
<Button variant="dialogSecondary" onClick={() => { this.props.onCancel(); }} aria-label="cancel">
Cancel
</Button>
<Button variant="dialogPrimary" style={{ flex: "0 0 auto" }}
onClick={() => { this.openSelectedFile(); }}
disabled={!canSelectFile}
aria-label="select"
>
{okButtonText}
</Button>
</div>
</div>
)}
</div>
</DialogActions>
</>
@@ -1742,12 +1742,17 @@ export default withStyles(
)
)
}
{/* {this.state.openTone3000Dialog && (
{this.state.openTone3000Dialog && (
<Tone3000Dialog
open={this.state.openTone3000Dialog}
onClose={() => this.setState({ openTone3000Dialog: false })}
onCancel={() => this.setState({ openTone3000Dialog: false })}
onDownload={(toneUrl: string) => {
alert("Donload tone url: " + toneUrl);
this.setState({ openTone3000Dialog: false });
}
}
/>
)} */}
)}
{this.state.openTone3000Help && (
<Tone3000HelpDialog
open={this.state.openTone3000Help}
@@ -1761,22 +1766,21 @@ export default withStyles(
/>
)}
{this.state.textFileName !== undefined && (
<TextInfoDialog open={true}
<TextInfoDialog open={true}
title={pathFileNameOnly(this.state.textFileName)}
fileName={this.state.textFileName} onClose={() => this.setState({ textFileName: undefined })}
/>
fileName={this.state.textFileName} onClose={() => this.setState({ textFileName: undefined })}
/>
)}
</DialogEx>
);
}
isLicenseFile(fileName: string) {
let extension = pathExtension(fileName);
if (extension === ".txt" || extension === ".md"){
if (extension === ".txt" || extension === ".md") {
return true;
}
}
let fileNameOnly = pathFileNameOnly(fileName);
if (fileNameOnly.toUpperCase() === "LICENSE" || fileNameOnly.toUpperCase() === "README")
{
if (fileNameOnly.toUpperCase() === "LICENSE" || fileNameOnly.toUpperCase() === "README") {
return true;
}
return false;
+89 -127
View File
@@ -1,15 +1,10 @@
import React from 'react';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import Button from '@mui/material/Button';
import AppBar from '@mui/material/AppBar';
import Typography from '@mui/material/Typography';
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
import DialogEx from './DialogEx';
import Link from '@mui/material/Link';
import Toolbar from '@mui/material/Toolbar';
import IconButtonEx from './IconButtonEx';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
@@ -19,142 +14,109 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
export interface Tone3000DialogProps {
open: boolean;
onClose: () => void;
onCancel: () => void;
onDownload: (toneUrl: string) => void;
}
interface Tone3000AuthState {
authToken: string;
refreshToken: string;
expiry: number;
};
// function getAuthToken(): Tone3000AuthState | null {
// let authToken = window.localStorage.getItem("tone300_auth_token");
// if (authToken === null) {
// return null;
// }
// let refreshToken = window.localStorage.getItem("tone3000_refresh_token");
// if (refreshToken === null) {
// return null;
// }
// let expiry = window.localStorage.getTime("tone3000_auth_expiry");
// return { authToken: authToken, refreshToken: refreshToken, expiry: expiry };
// }
// function setAuthToken(authToken: string, refreshToken: string, expirySeconds: number) {
// window.localStorage.setItem("tone3000_auth_token", authToken);
// window.localStorage.setItem("tone3000_refresh_token", refreshToken);
// window.localStorage.setItem("tone3000_auth_expiry", String(Date.now() + (expirySeconds * 1000)));
// }
// enum AuthState {
// OnBoarding,
// TokenExpired,
// Authenticated
// }
function Tone3000Dialog(props: Tone3000DialogProps) {
const { open, onClose } = props;
const model: PiPedalModel = PiPedalModelFactory.getInstance();
let [openAuthDialog, setOpenAuthDialog] = React.useState(!model.hasTone3000Auth.get());
React.useEffect(()=> {
let authListener = (value: boolean) => {
setOpenAuthDialog(!value);
}
model.hasTone3000Auth.addOnChangedHandler(authListener);
return () => {
model.hasTone3000Auth.removeOnChangedHandler(authListener);
}
});
const { onCancel,onDownload } = props;
function RedirectUrl() {
let baseUrl = new URL(window.location.href);
let result = "http://" + baseUrl.hostname + ":" + baseUrl.port + "/";
let result = "http://" + baseUrl.hostname + ":" + baseUrl.port + "/public/handleTone3000download.html";
return result;
}
function AuthUrl() {
//return "https://www.tone3000.com/api/v1/auth?redirect_url=" + encodeURIComponent(RedirectUrl()) + "&opt_only=true";
return "https://www.tone3000.com/api/v1/auth?redirect_url=" + encodeURIComponent(RedirectUrl()) + "&otp_only=true";
const appId = 'pipedal_app';
const redirectUrl = RedirectUrl();
let selectUrl = `https://www.tone3000.com/api/v1/select?app_id=${appId}&redirect_url=${redirectUrl}`;
const model: PiPedalModel = PiPedalModelFactory.getInstance();
if (model) {
}
React.useEffect(()=> {
let messagehandler = (event: MessageEvent) => {
// Handle messages received from the iframe
if (event.origin !== "https://www.tone3000.com") {
return;
}
const data = event.data;
if (data.type === "tone3000_selection") {
onDownload(data.toneUrl);
}
}
window.addEventListener("message", messagehandler);
return () => {
window.removeEventListener("message", messagehandler);
}
});
return (
<DialogEx
tag="tone3000"
fullScreen={true}
open={open}
onEnterKey={() => { onClose(); }}
onClose={() => { onClose(); }}
aria-labelledby="tone3000-dialog-title"
aria-describedby="tone3000-dialog-description"
>
<DialogTitle id="tone3000-dialog-title">
<Toolbar style={{ padding: 0 }}>
<IconButtonEx
tooltip="Close"
edge="start"
color="inherit"
aria-label="cancel"
style={{ opacity: 0.6 }}
onClick={() => { onClose(); }}
>
<ArrowBackIcon style={{ width: 24, height: 24 }} />
</IconButtonEx>
<DialogEx open={props.open} onClose={()=>{ onCancel()}} fullScreen={true} tag="tone3000Dlg" onEnterKey={()=>{}} >
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
TONE3000 Models
</Typography>
</Toolbar>
</DialogTitle>
<DialogContent>
<Typography variant="body1">
This is a placeholder for the Tone 3000 dialog.
</Typography>
</DialogContent>
<DialogActions>
<Button onClick={onClose} color="primary">
Close
</Button>
</DialogActions>
{
openAuthDialog && (
<Dialog
fullScreen={true}
open={openAuthDialog}>
<DialogTitle id="tone3000-auth-dialog-title">
<Toolbar style={{ padding: 0 }}>
<IconButtonEx
tooltip="Close"
edge="start"
color="inherit"
aria-label="cancel"
style={{ opacity: 0.6 }}
onClick={() => { onClose(); }}
<div style={{display: "flex", flexDirection: "column", width: "100%", height: "100%"}}>
<div style={{ flex: "0 0 auto" }}>
<AppBar style={{
position: 'relative',
top: 0, left: 0
}} >
<Toolbar>
<IconButtonEx tooltip="Back" edge="start" color="inherit" onClick={()=> onCancel()}
aria-label="back"
>
<ArrowBackIcon style={{ width: 24, height: 24 }} />
<ArrowBackIcon />
</IconButtonEx>
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
TONE3000 Authorization
<Typography noWrap variant="h6"
sx={{ marginLeft: 2, flex: 1 }}
>
Tone3000 Direct Download
</Typography>
</Toolbar>
</DialogTitle>
<DialogContent>
<div style={{ display: "flex", flexFlow: "row nowrap", alignItems: "start" }}>
<div style={{ flex: "1 1 1px" }} />
<div style={{
maxWidth: 500, marginLeft: "auto", marginRight: "auto",
display: "flex", flexFlow: "column nowrap", gap: 16, alignItems: "start",
}}>
<Typography variant="body1" component="div" display="block" >
The <Link href="https://www.tone3000.com" target="_blank">TONE3000 website</Link> provides an
online library of models for use with TooB Neural Amp Modeller. You can download
models from the TONE3000 website onto your local machine and then upload them to
PiPedal; or, more conveniently, you can browse the TONE3000 model database directly, and download models directly
to the PiPedal server from the TONE3000 model database in a single step.
</Typography>
<Typography variant="body1" component="div" display="block" >
In order to access the TONE3000 database, you must first obtain an access token from the
TONE3000 website. Clicking on the button will take you to an external website in
order to complete the authorization process.
</Typography>
<Button variant="contained" color="primary" style={{ alignSelf: "end", marginRight: 32 }}
onClick={() => {
window.open(AuthUrl(), "_blank");
}}
>
Continue to TONE3000
</Button>
<Typography variant="body2" component="div" display="block" style={{ marginTop: 32 }}>
Privacy statement: PiPedal will only have access to your authorization token
which does not contain personally identifying information, and which is stored locally on
your PiPedal server. Please refer to
the <Link href="https://www.tone3000.com/privacy" target="_blank">TONE3000 privacy policy</Link> for
information on how your data is used by TONE3000.
</Typography>
</div>
<div style={{ flex: "2 2 1px" }} />
</div>
</DialogContent>
</Dialog>
)}
</AppBar>
</div>
<iframe style={{flex: "1 1 1px", border: "none"}}
src={ selectUrl}
>
</iframe>
</div>
</DialogEx>
);