Add donate button and improve ux

This commit is contained in:
JKamsker 2024-02-19 17:54:45 +01:00
parent 10db1f235a
commit 0e636aa9ef

View file

@ -1,82 +1,78 @@
import React, { useState } from 'react' import React, { useState } from "react";
import { withStyles } from '@material-ui/core/styles' import { withStyles } from "@material-ui/core/styles";
import Avatar from '@material-ui/core/Avatar' import Avatar from "@material-ui/core/Avatar";
import Button from '@material-ui/core/Button' import Button from "@material-ui/core/Button";
import CssBaseline from '@material-ui/core/CssBaseline' import CssBaseline from "@material-ui/core/CssBaseline";
import TextField from '@material-ui/core/TextField' import TextField from "@material-ui/core/TextField";
import Link from '@material-ui/core/Link' import Link from "@material-ui/core/Link";
import Box from '@material-ui/core/Box' import Box from "@material-ui/core/Box";
import LockOutlinedIcon from '@material-ui/icons/LockOutlined' import LockOutlinedIcon from "@material-ui/icons/LockOutlined";
import Typography from '@material-ui/core/Typography' import Typography from "@material-ui/core/Typography";
import Container from '@material-ui/core/Container' import Container from "@material-ui/core/Container";
import Dropzone from 'react-dropzone' import Dropzone from "react-dropzone";
import IconButton from '@material-ui/core/IconButton' import IconButton from "@material-ui/core/IconButton";
import FileCopyOutlined from '@material-ui/icons/FileCopyOutlined' import FileCopyOutlined from "@material-ui/icons/FileCopyOutlined";
import PublishOutlined from '@material-ui/icons/PublishOutlined' import PublishOutlined from "@material-ui/icons/PublishOutlined";
import { FilePicker } from '../src/Components' import { FilePicker } from "../src/Components";
import { CopyToClipboard } from 'react-copy-to-clipboard' import { CopyToClipboard } from "react-copy-to-clipboard";
import ControlledAccordions from './ControlledAccordions' import ControlledAccordions from "./ControlledAccordions";
import OnlineConverter from './OnlineConverter' import OnlineConverter from "./OnlineConverter";
import 'react-notifications-component/dist/theme.css' import "react-notifications-component/dist/theme.css";
import ReactNotification from 'react-notifications-component' import ReactNotification from "react-notifications-component";
import { store } from 'react-notifications-component' import { store } from "react-notifications-component";
import AaxHashAlgorithm from './Utils/AaxHashAlgorithm' import AaxHashAlgorithm from "./Utils/AaxHashAlgorithm";
import {
GoogleReCaptchaProvider,
withGoogleReCaptcha,
} from 'react-google-recaptcha-v3'
import { GoogleReCaptchaProvider, withGoogleReCaptcha } from "react-google-recaptcha-v3";
const useStyles = (theme) => ({ const useStyles = (theme) => ({
paper: { paper: {
marginTop: theme.spacing(8), marginTop: theme.spacing(8),
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
alignItems: 'center', alignItems: "center",
}, },
avatar: { avatar: {
margin: theme.spacing(1), margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main, backgroundColor: theme.palette.secondary.main,
}, },
form: { form: {
width: '100%', // Fix IE 11 issue. width: "100%", // Fix IE 11 issue.
marginTop: theme.spacing(1), marginTop: theme.spacing(1),
}, },
//Accordeon //Accordeon
heading: { heading: {
fontSize: theme.typography.pxToRem(15), fontSize: theme.typography.pxToRem(15),
flexBasis: '33.33%', flexBasis: "33.33%",
flexShrink: 0, flexShrink: 0,
}, },
secondaryHeading: { secondaryHeading: {
fontSize: theme.typography.pxToRem(15), fontSize: theme.typography.pxToRem(15),
color: theme.palette.text.secondary, color: theme.palette.text.secondary,
}, },
}) });
class ChecksumResolver extends React.Component { class ChecksumResolver extends React.Component {
constructor(props) { constructor(props) {
super(props) super(props);
this.state = { this.state = {
checksum: '', checksum: "",
fileName: 'input.aax', fileName: "input.aax",
activationBytes: '', activationBytes: "",
} };
} }
componentDidMount() { componentDidMount() {
let path = window.location.pathname; let path = window.location.pathname;
let checksumMatch = window.location.pathname.match(/([a-fA-F0-9]{40})/) let checksumMatch = window.location.pathname.match(/([a-fA-F0-9]{40})/);
if(!checksumMatch) return; if (!checksumMatch) return;
let checksum = checksumMatch[1]; let checksum = checksumMatch[1];
// this.setState({}); // this.setState({});
this.setChecksum(checksum); this.setChecksum(checksum);
@ -90,151 +86,168 @@ class ChecksumResolver extends React.Component {
DarkerDisabledTextField = withStyles({ DarkerDisabledTextField = withStyles({
root: { root: {
marginRight: 8, marginRight: 8,
'& .MuiInputBase-root.Mui-disabled': { "& .MuiInputBase-root.Mui-disabled": {
color: 'rgba(0, 0, 0, 0.6)', color: "rgba(0, 0, 0, 0.6)",
}, },
}, },
})(TextField) })(TextField);
Copyright = function () { Copyright = function () {
return ( return (
<Typography variant="body2" color="textSecondary" align="center"> <Typography variant="body2" color="textSecondary" align="center">
{'Copyright © '} {"Copyright © "}
<Link color="inherit" href="https://audible-tools.github.io/"> <Link color="inherit" href="https://audible-tools.github.io/">
audible-tools audible-tools
</Link>{' '} </Link>{" "}
{new Date().getFullYear()} {new Date().getFullYear()}
{'. V 0.3'} {". V 0.3"}
</Typography> </Typography>
) );
} };
setChecksum = (value) => { setChecksum = (value) => {
if (value.length > 40) { if (value.length > 40) {
return return;
} }
this.setState({ checksum: value }) this.setState({ checksum: value });
} };
isChecksumValid = () => { isChecksumValid = () => {
const { checksum } = this.state const { checksum } = this.state;
const regex = RegExp('[a-f0-9]{40}') const regex = RegExp("[a-f0-9]{40}");
const testResults = regex.test(checksum) const testResults = regex.test(checksum);
return testResults return testResults;
} };
isInputInvalid = () => { isInputInvalid = () => {
const { checksum } = this.state const { checksum } = this.state;
if (!checksum || checksum === '') { if (!checksum || checksum === "") {
return false return false;
} }
return !this.isChecksumValid() return !this.isChecksumValid();
} };
addNotification = function (text, success = true) { addNotification = function (text, success = true) {
store.addNotification({ store.addNotification({
message: text, message: text,
type: success ? 'success' : 'danger', type: success ? "success" : "danger",
// type: "danger", // type: "danger",
insert: 'bottom-left', insert: "bottom-left",
container: 'top-full', container: "top-full",
animationIn: ['animate__animated', 'animate__fadeIn'], animationIn: ["animate__animated", "animate__fadeIn"],
animationOut: ['animate__animated', 'animate__fadeOut'], animationOut: ["animate__animated", "animate__fadeOut"],
dismiss: { dismiss: {
duration: 3000, duration: 3000,
onScreen: false, onScreen: false,
}, },
}) });
} };
requestActivationBytes = async (checksumX) => { requestActivationBytes = async (checksumX) => {
const checksum = checksumX ? checksumX : this.state.checksum const checksum = checksumX ? checksumX : this.state.checksum;
window.history.pushState('page2', 'Title', '/' + checksum); window.history.pushState("page2", "Title", "/" + checksum);
let executeRecaptcha = this.props.googleReCaptchaProps.executeRecaptcha
while(!executeRecaptcha) { let executeRecaptcha = this.props.googleReCaptchaProps.executeRecaptcha;
console.log('Recaptcha has not been loaded')
executeRecaptcha = this.props.googleReCaptchaProps?.executeRecaptcha while (!executeRecaptcha) {
await new Promise(r => setTimeout(r, 100)); console.log("Recaptcha has not been loaded");
executeRecaptcha = this.props.googleReCaptchaProps?.executeRecaptcha;
await new Promise((r) => setTimeout(r, 100));
} }
const token = await executeRecaptcha('homepage') const token = await executeRecaptcha("homepage");
console.log(`XToken: ${token}`) console.log(`XToken: ${token}`);
try { try {
let request = await fetch( let request = await fetch(
`${process.env.REACT_APP_APISERVER}/api/v2/activation/${checksum}`, `${process.env.REACT_APP_APISERVER}/api/v2/activation/${checksum}`,
{ {
headers: new Headers({ 'x-captcha-result': token }), headers: new Headers({ "x-captcha-result": token }),
}, }
) );
let result = await request.json() let result = await request.json();
const { success, activationBytes } = result const { success, activationBytes } = result;
if (success !== true) { if (success !== true) {
this.setState({ activationBytes: 'UNKNOWN' }) this.setState({ activationBytes: "UNKNOWN" });
this.addNotification( this.addNotification(
'An error occured while resolving the activation bytes, please check your inputs', "An error occured while resolving the activation bytes, please check your inputs",
false, false
) );
return return;
} }
if (success === true) { if (success === true) {
const calculatedChecksum = await AaxHashAlgorithm.CalculateChecksum( const calculatedChecksum = await AaxHashAlgorithm.CalculateChecksum(
activationBytes, activationBytes
) );
if (calculatedChecksum == checksum) { if (calculatedChecksum == checksum) {
this.setState({ activationBytes: activationBytes }) this.setState({ activationBytes: activationBytes });
this.addNotification('Successfully resolved the activation bytes') this.addNotification("Successfully resolved the activation bytes");
return return;
} }
this.setState({ activationBytes: 'API ERROR' }) this.setState({ activationBytes: "API ERROR" });
this.addNotification( this.addNotification(
'An unexpected error occured while resolving the activation bytes, please try again', "An unexpected error occured while resolving the activation bytes, please try again",
false, false
) );
} }
} catch (error) { } catch (error) {
this.setState({ activationBytes: error }) this.setState({ activationBytes: error });
this.addNotification( this.addNotification(
'An error occured while resolving the activation bytes, please check your inputs', "An error occured while resolving the activation bytes, please check your inputs",
false, false
) );
} }
} };
buf2hex(buffer) { buf2hex(buffer) {
// buffer is an ArrayBuffer // buffer is an ArrayBuffer
return Array.prototype.map return Array.prototype.map
.call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2)) .call(new Uint8Array(buffer), (x) => ("00" + x.toString(16)).slice(-2))
.join('') .join("");
} }
acceptFiles = async (files) => { acceptFiles = async (files) => {
const file = files[0] const file = files[0];
await this.acceptFile(file) await this.acceptFile(file);
} };
acceptFile = async (file) => { acceptFile = async (file) => {
// if (!file.name.toLowerCase().endsWith(".aax")) { if (!file.name.toLowerCase().endsWith(".aax")) {
// alert('FileType not supported!'); // alert('FileType not supported!');
// return; // return;
// } // notify user that the file type is not supported
// this.addNotification("FileType not supported!", false);
this.setState({ fileName: file.name, file: file }) // notify user that the file type is not supported with alert, ask if they want to continue
const slic = file.slice(653, 653 + 20)
const results = this.buf2hex(await slic.arrayBuffer()) // Message if aaxc: "Only .aax files are supported, you have provided a .aaxc file. Renaming the file won't work, please provide a .aax file. You can download the .aax file from the Audible website. (eg: https://www.audible.de/library/titles)"
this.setChecksum(results)
this.requestActivationBytes() const fileExtension = file.name.includes(".") ? file.name.split(".").pop() : "unknown";
} const message = file.name.toLowerCase().endsWith(".aaxc")
? "Only .aax files are supported, you have provided a .aaxc file. Renaming the file won't work, please provide a .aax file. You can download the .aax file from the Audible website. (eg: https://www.audible.de/library/titles) Do you want to continue anyway?"
: `.${fileExtension} files are not supported! Do you want to continue anyway?`;
const response = window.confirm(message);
if (!response) {
return;
}
}
this.setState({ fileName: file.name, file: file });
const slic = file.slice(653, 653 + 20);
const results = this.buf2hex(await slic.arrayBuffer());
this.setChecksum(results);
this.requestActivationBytes();
};
render() { render() {
const { classes } = this.props const { classes } = this.props;
const { checksum, activationBytes, fileName, file } = this.state const { checksum, activationBytes, fileName, file } = this.state;
// const id = this.props.match.params.id; // const id = this.props.match.params.id;
// let { id } = useParams(); // let { id } = useParams();
// console.log("IDDDDDD"+ id); // console.log("IDDDDDD"+ id);
@ -254,8 +267,8 @@ class ChecksumResolver extends React.Component {
<Dropzone <Dropzone
noClick noClick
onDrop={(acceptedFiles) => { onDrop={(acceptedFiles) => {
console.log(acceptedFiles) console.log(acceptedFiles);
this.acceptFiles(acceptedFiles) this.acceptFiles(acceptedFiles);
}} }}
> >
{({ getRootProps, getInputProps }) => ( {({ getRootProps, getInputProps }) => (
@ -280,7 +293,7 @@ class ChecksumResolver extends React.Component {
readOnly: false, readOnly: false,
endAdornment: ( endAdornment: (
<FilePicker <FilePicker
extensions={['aax', 'AAX']} extensions={["aax", "AAX"]}
maxSize={99999} maxSize={99999}
onChange={this.acceptFile} onChange={this.acceptFile}
onError={() => {}} onError={() => {}}
@ -297,16 +310,38 @@ class ChecksumResolver extends React.Component {
)} )}
</Dropzone> </Dropzone>
<Button <div style={{ display: "flex", justifyContent: "space-between" }}>
fullWidth <Button
variant="contained" fullWidth
onClick={() => { variant="contained"
this.requestActivationBytes() onClick={() => {
}} this.requestActivationBytes();
disabled={!this.isChecksumValid()} }}
> disabled={!this.isChecksumValid()}
Request Activation Bytes >
</Button> Request Activation Bytes
</Button>
{/* Paypal donate Button redirects to https://www.paypal.com/paypalme/jdawg1337*/}
<Button
style={{ marginLeft: 8, minWidth: 137 }}
variant="contained"
onClick={() => {
// window.location.href = "https://www.paypal.com/paypalme/jdawg1337";
// open in new tab
window.open("https://www.paypal.com/paypalme/jdawg1337", "_blank");
}}
>
{/* paypal icon */}
<a style={{ paddingLeft: 8, height: "100%" }}>Donate</a>
<img
style={{ marginLeft: -10, width: 50 }}
// src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/PP_logo_h_100x26.png"
src="https://pngimg.com/uploads/paypal/paypal_PNG7.png"
alt="PayPal"
/>
</Button>
</div>
<this.DarkerDisabledTextField <this.DarkerDisabledTextField
value={activationBytes} value={activationBytes}
@ -315,7 +350,7 @@ class ChecksumResolver extends React.Component {
margin="normal" margin="normal"
fullWidth fullWidth
id="activationBytes" id="activationBytes"
label={activationBytes ? '' : 'Activation Bytes'} label={activationBytes ? "" : "Activation Bytes"}
name="activationBytes" name="activationBytes"
autoComplete="activationBytes" autoComplete="activationBytes"
aria-readonly aria-readonly
@ -341,9 +376,8 @@ class ChecksumResolver extends React.Component {
<this.Copyright /> <this.Copyright />
</Box> </Box>
</Container> </Container>
) );
} }
} }
export default withGoogleReCaptcha(withStyles(useStyles)(ChecksumResolver)) export default withGoogleReCaptcha(withStyles(useStyles)(ChecksumResolver));