forked from Kieran/void.cat
Standardize button with VoidButton
This commit is contained in:
parent
3d8dba9eae
commit
20d32ad6d3
@ -4,6 +4,7 @@ import {PaywallServices} from "./Const";
|
|||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import {LightningPaywall} from "./LightningPaywall";
|
import {LightningPaywall} from "./LightningPaywall";
|
||||||
import {useApi} from "./Api";
|
import {useApi} from "./Api";
|
||||||
|
import {VoidButton} from "./VoidButton";
|
||||||
|
|
||||||
export function FilePaywall(props) {
|
export function FilePaywall(props) {
|
||||||
const {Api} = useApi();
|
const {Api} = useApi();
|
||||||
@ -14,15 +15,11 @@ export function FilePaywall(props) {
|
|||||||
|
|
||||||
const [order, setOrder] = useState();
|
const [order, setOrder] = useState();
|
||||||
|
|
||||||
async function fetchOrder(e) {
|
async function fetchOrder() {
|
||||||
if(e.target.classList.contains("disabled")) return;
|
|
||||||
e.target.classList.add("disabled");
|
|
||||||
|
|
||||||
let req = await Api.createOrder(file.id);
|
let req = await Api.createOrder(file.id);
|
||||||
if (req.ok && req.status === 200) {
|
if (req.ok && req.status === 200) {
|
||||||
setOrder(await req.json());
|
setOrder(await req.json());
|
||||||
}
|
}
|
||||||
e.target.classList.remove("disabled");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
@ -42,7 +39,7 @@ export function FilePaywall(props) {
|
|||||||
<h3>
|
<h3>
|
||||||
You must pay {FormatCurrency(pw.cost.amount, pw.cost.currency)} to view this file.
|
You must pay {FormatCurrency(pw.cost.amount, pw.cost.currency)} to view this file.
|
||||||
</h3>
|
</h3>
|
||||||
<div className="btn" onClick={fetchOrder}>Pay</div>
|
<VoidButton onClick={fetchOrder}>Pay</VoidButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import {Fragment, useEffect, useState} from "react";
|
import {Fragment} from "react";
|
||||||
import FeatherIcon from "feather-icons-react";
|
import FeatherIcon from "feather-icons-react";
|
||||||
import {FormatBytes} from "./Util";
|
import {FormatBytes} from "./Util";
|
||||||
|
|
||||||
import "./GlobalStats.css";
|
import "./GlobalStats.css";
|
||||||
import {useApi} from "./Api";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {useSelector} from "react-redux";
|
import {useSelector} from "react-redux";
|
||||||
|
|
||||||
export function GlobalStats(props) {
|
export function GlobalStats() {
|
||||||
const {Api} = useApi();
|
|
||||||
let stats = useSelector(state => state.info.stats);
|
let stats = useSelector(state => state.info.stats);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -3,8 +3,8 @@ import {useDispatch, useSelector} from "react-redux";
|
|||||||
import {setAuth} from "./LoginState";
|
import {setAuth} from "./LoginState";
|
||||||
import {useApi} from "./Api";
|
import {useApi} from "./Api";
|
||||||
import "./Login.css";
|
import "./Login.css";
|
||||||
import {btnDisable, btnEnable} from "./Util";
|
|
||||||
import HCaptcha from "@hcaptcha/react-hcaptcha";
|
import HCaptcha from "@hcaptcha/react-hcaptcha";
|
||||||
|
import {VoidButton} from "./VoidButton";
|
||||||
|
|
||||||
export function Login() {
|
export function Login() {
|
||||||
const {Api} = useApi();
|
const {Api} = useApi();
|
||||||
@ -15,8 +15,7 @@ export function Login() {
|
|||||||
const captchaKey = useSelector(state => state.info.stats.captchaSiteKey);
|
const captchaKey = useSelector(state => state.info.stats.captchaSiteKey);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
async function login(e, fnLogin) {
|
async function login(fnLogin) {
|
||||||
if(!btnDisable(e.target)) return;
|
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
let req = await fnLogin(username, password, captchaResponse);
|
let req = await fnLogin(username, password, captchaResponse);
|
||||||
@ -28,8 +27,6 @@ export function Login() {
|
|||||||
setError(rsp.error);
|
setError(rsp.error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
btnEnable(e.target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -42,8 +39,8 @@ export function Login() {
|
|||||||
<dd><input type="password" onChange={(e) => setPassword(e.target.value)}/></dd>
|
<dd><input type="password" onChange={(e) => setPassword(e.target.value)}/></dd>
|
||||||
</dl>
|
</dl>
|
||||||
{captchaKey ? <HCaptcha sitekey={captchaKey} onVerify={setCaptchaResponse}/> : null}
|
{captchaKey ? <HCaptcha sitekey={captchaKey} onVerify={setCaptchaResponse}/> : null}
|
||||||
<div className="btn" onClick={(e) => login(e, Api.login)}>Login</div>
|
<VoidButton onClick={() => login(Api.login)}>Login</VoidButton>
|
||||||
<div className="btn" onClick={(e) => login(e, Api.register)}>Register</div>
|
<VoidButton onClick={() => login(Api.register)}>Register</VoidButton>
|
||||||
{error ? <div className="error-msg">{error}</div> : null}
|
{error ? <div className="error-msg">{error}</div> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
import FeatherIcon from "feather-icons-react";
|
import FeatherIcon from "feather-icons-react";
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import {btnDisable, btnEnable} from "./Util";
|
import {VoidButton} from "./VoidButton";
|
||||||
|
|
||||||
export function NoPaywallConfig(props) {
|
export function NoPaywallConfig(props) {
|
||||||
const [saveStatus, setSaveStatus] = useState();
|
const [saveStatus, setSaveStatus] = useState();
|
||||||
const privateFile = props.privateFile;
|
const privateFile = props.privateFile;
|
||||||
const onSaveConfig = props.onSaveConfig;
|
const onSaveConfig = props.onSaveConfig;
|
||||||
|
|
||||||
async function saveConfig(e) {
|
async function saveConfig() {
|
||||||
if(!btnDisable(e.target)) return;
|
|
||||||
|
|
||||||
let cfg = {
|
let cfg = {
|
||||||
editSecret: privateFile.metadata.editSecret
|
editSecret: privateFile.metadata.editSecret
|
||||||
};
|
};
|
||||||
@ -17,12 +15,11 @@ export function NoPaywallConfig(props) {
|
|||||||
if (typeof onSaveConfig === "function") {
|
if (typeof onSaveConfig === "function") {
|
||||||
setSaveStatus(await onSaveConfig(cfg));
|
setSaveStatus(await onSaveConfig(cfg));
|
||||||
}
|
}
|
||||||
btnEnable(e.target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="btn" onClick={saveConfig}>Save</div>
|
<VoidButton onClick={saveConfig}>Save</VoidButton>
|
||||||
{saveStatus ? <FeatherIcon icon={saveStatus === true ? "check-circle" : "alert-circle"}/> : null}
|
{saveStatus ? <FeatherIcon icon={saveStatus === true ? "check-circle" : "alert-circle"}/> : null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -6,10 +6,11 @@ import "./Profile.css";
|
|||||||
import {useDispatch, useSelector} from "react-redux";
|
import {useDispatch, useSelector} from "react-redux";
|
||||||
import {logout, setProfile as setGlobalProfile} from "./LoginState";
|
import {logout, setProfile as setGlobalProfile} from "./LoginState";
|
||||||
import {DigestAlgo} from "./FileUpload";
|
import {DigestAlgo} from "./FileUpload";
|
||||||
import {btnDisable, btnEnable, buf2hex, hasFlag} from "./Util";
|
import {buf2hex, hasFlag} from "./Util";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import FeatherIcon from "feather-icons-react";
|
import FeatherIcon from "feather-icons-react";
|
||||||
import {FileList} from "./FileList";
|
import {FileList} from "./FileList";
|
||||||
|
import {VoidButton} from "./VoidButton";
|
||||||
|
|
||||||
export function Profile() {
|
export function Profile() {
|
||||||
const [profile, setProfile] = useState();
|
const [profile, setProfile] = useState();
|
||||||
@ -93,8 +94,6 @@ export function Profile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveUser(e) {
|
async function saveUser(e) {
|
||||||
if (!btnDisable(e.target)) return;
|
|
||||||
|
|
||||||
let r = await Api.updateUser({
|
let r = await Api.updateUser({
|
||||||
id: profile.id,
|
id: profile.id,
|
||||||
avatar: profile.avatar,
|
avatar: profile.avatar,
|
||||||
@ -106,19 +105,15 @@ export function Profile() {
|
|||||||
dispatch(setGlobalProfile(profile));
|
dispatch(setGlobalProfile(profile));
|
||||||
setSaved(true);
|
setSaved(true);
|
||||||
}
|
}
|
||||||
btnEnable(e.target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitCode(e) {
|
async function submitCode(e) {
|
||||||
if (!btnDisable(e.target)) return;
|
|
||||||
|
|
||||||
let r = await Api.submitVerifyCode(profile.id, emailCode);
|
let r = await Api.submitVerifyCode(profile.id, emailCode);
|
||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
await loadProfile();
|
await loadProfile();
|
||||||
} else {
|
} else {
|
||||||
setEmailCodeError("Invalid or expired code.");
|
setEmailCodeError("Invalid or expired code.");
|
||||||
}
|
}
|
||||||
btnEnable(e.target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendNewCode() {
|
async function sendNewCode() {
|
||||||
@ -138,8 +133,8 @@ export function Profile() {
|
|||||||
<br/>
|
<br/>
|
||||||
<input type="text" placeholder="Verification code" value={emailCode}
|
<input type="text" placeholder="Verification code" value={emailCode}
|
||||||
onChange={(e) => setEmailCode(e.target.value)}/>
|
onChange={(e) => setEmailCode(e.target.value)}/>
|
||||||
<div className="btn" onClick={submitCode}>Submit</div>
|
<VoidButton onClick={submitCode}>Submit</VoidButton>
|
||||||
<div className="btn" onClick={() => dispatch(logout())}>Logout</div>
|
<VoidButton onClick={() => dispatch(logout())}>Logout</VoidButton>
|
||||||
<br/>
|
<br/>
|
||||||
{emailCodeError ? <b>{emailCodeError}</b> : null}
|
{emailCodeError ? <b>{emailCodeError}</b> : null}
|
||||||
{emailCodeError && !newCodeSent ? <a onClick={sendNewCode}>Send verfication email</a> : null}
|
{emailCodeError && !newCodeSent ? <a onClick={sendNewCode}>Send verfication email</a> : null}
|
||||||
@ -165,13 +160,13 @@ export function Profile() {
|
|||||||
</dl>
|
</dl>
|
||||||
<div className="flex flex-center">
|
<div className="flex flex-center">
|
||||||
<div>
|
<div>
|
||||||
<div className="btn" onClick={saveUser}>Save</div>
|
<VoidButton onClick={saveUser}>Save</VoidButton>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{saved ? <FeatherIcon icon="check-circle"/> : null}
|
{saved ? <FeatherIcon icon="check-circle"/> : null}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="btn" onClick={() => dispatch(logout())}>Logout</div>
|
<VoidButton onClick={() => dispatch(logout())}>Logout</VoidButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import FeatherIcon from "feather-icons-react";
|
import FeatherIcon from "feather-icons-react";
|
||||||
import {PaywallCurrencies} from "./Const";
|
import {PaywallCurrencies} from "./Const";
|
||||||
import {btnDisable, btnEnable} from "./Util";
|
import {VoidButton} from "./VoidButton";
|
||||||
|
|
||||||
export function StrikePaywallConfig(props) {
|
export function StrikePaywallConfig(props) {
|
||||||
const file = props.file;
|
const file = props.file;
|
||||||
@ -16,8 +16,6 @@ export function StrikePaywallConfig(props) {
|
|||||||
const [saveStatus, setSaveStatus] = useState();
|
const [saveStatus, setSaveStatus] = useState();
|
||||||
|
|
||||||
async function saveStrikeConfig(e) {
|
async function saveStrikeConfig(e) {
|
||||||
if(!btnDisable(e.target)) return;
|
|
||||||
|
|
||||||
let cfg = {
|
let cfg = {
|
||||||
editSecret,
|
editSecret,
|
||||||
strike: {
|
strike: {
|
||||||
@ -37,7 +35,6 @@ export function StrikePaywallConfig(props) {
|
|||||||
setSaveStatus(false);
|
setSaveStatus(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
btnEnable(e.target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -57,7 +54,7 @@ export function StrikePaywallConfig(props) {
|
|||||||
<dt>Price:</dt>
|
<dt>Price:</dt>
|
||||||
<dd><input type="number" value={price} onChange={(e) => setPrice(parseFloat(e.target.value))}/></dd>
|
<dd><input type="number" value={price} onChange={(e) => setPrice(parseFloat(e.target.value))}/></dd>
|
||||||
</dl>
|
</dl>
|
||||||
<div className="btn" onClick={saveStrikeConfig}>Save</div>
|
<VoidButton onClick={saveStrikeConfig}>Save</VoidButton>
|
||||||
{saveStatus ? <FeatherIcon icon="check-circle"/> : null}
|
{saveStatus ? <FeatherIcon icon="check-circle"/> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -80,13 +80,3 @@ export function FormatCurrency(value, currency) {
|
|||||||
export function hasFlag(value, flag) {
|
export function hasFlag(value, flag) {
|
||||||
return (value & flag) === flag;
|
return (value & flag) === flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function btnDisable(btn){
|
|
||||||
if(btn.classList.contains("disabled")) return false;
|
|
||||||
btn.classList.add("disabled");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function btnEnable(btn){
|
|
||||||
btn.classList.remove("disabled");
|
|
||||||
}
|
|
18
VoidCat/spa/src/VoidButton.js
Normal file
18
VoidCat/spa/src/VoidButton.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export function VoidButton(props) {
|
||||||
|
async function handleClick(e) {
|
||||||
|
if (e.target.classList.contains("disabled")) return;
|
||||||
|
e.target.classList.add("disabled");
|
||||||
|
|
||||||
|
let fn = props.onClick;
|
||||||
|
if (typeof fn === "function") {
|
||||||
|
let ret = fn(e);
|
||||||
|
if (typeof ret === "object" && typeof ret.then === "function") {
|
||||||
|
await ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e.target.classList.remove("disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="btn" onClick={handleClick}>{props.children}</div>;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user