Fix autocomplete, create new timer

This commit is contained in:
Elizabeth Hunt 2023-04-04 14:07:05 -06:00
parent ea279b7295
commit a862ad4d3b
Signed by: simponic
GPG Key ID: 52B3774857EB24B1
6 changed files with 176 additions and 23 deletions

View File

@ -11,6 +11,7 @@
"chota": "^0.9.2", "chota": "^0.9.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-mentions": "^4.4.7",
"react-modal": "^3.16.1", "react-modal": "^3.16.1",
"react-router-dom": "^6.10.0", "react-router-dom": "^6.10.0",
"socket.io-client": "^4.6.1" "socket.io-client": "^4.6.1"
@ -330,6 +331,14 @@
"@babel/core": "^7.0.0-0" "@babel/core": "^7.0.0-0"
} }
}, },
"node_modules/@babel/runtime": {
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz",
"integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==",
"dependencies": {
"regenerator-runtime": "^0.13.2"
}
},
"node_modules/@babel/template": { "node_modules/@babel/template": {
"version": "7.20.7", "version": "7.20.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
@ -1109,6 +1118,14 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/is-core-module": { "node_modules/is-core-module": {
"version": "2.11.0", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
@ -1298,6 +1315,21 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
}, },
"node_modules/react-mentions": {
"version": "4.4.7",
"resolved": "https://registry.npmjs.org/react-mentions/-/react-mentions-4.4.7.tgz",
"integrity": "sha512-VNriu2h/uOB+RS0mwZgPG2Vf+UtdDvRh5zbXa2TNc1WqacKuNDgTdhlbo9LEOZRBxRzIeTUYQmYJ7p9M9rDHqQ==",
"dependencies": {
"@babel/runtime": "7.4.5",
"invariant": "^2.2.4",
"prop-types": "^15.5.8",
"substyle": "^9.1.0"
},
"peerDependencies": {
"react": ">=16.8.3",
"react-dom": ">=16.8.3"
}
},
"node_modules/react-modal": { "node_modules/react-modal": {
"version": "3.16.1", "version": "3.16.1",
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz", "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz",
@ -1355,6 +1387,11 @@
"react-dom": ">=16.8" "react-dom": ">=16.8"
} }
}, },
"node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.1", "version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
@ -1440,6 +1477,18 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/substyle": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/substyle/-/substyle-9.4.1.tgz",
"integrity": "sha512-VOngeq/W1/UkxiGzeqVvDbGDPM8XgUyJVWjrqeh+GgKqspEPiLYndK+XRcsKUHM5Muz/++1ctJ1QCF/OqRiKWA==",
"dependencies": {
"@babel/runtime": "^7.3.4",
"invariant": "^2.2.4"
},
"peerDependencies": {
"react": ">=16.8.3"
}
},
"node_modules/supports-color": { "node_modules/supports-color": {
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",

View File

@ -12,6 +12,7 @@
"chota": "^0.9.2", "chota": "^0.9.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-mentions": "^4.4.7",
"react-modal": "^3.16.1", "react-modal": "^3.16.1",
"react-router-dom": "^6.10.0", "react-router-dom": "^6.10.0",
"socket.io-client": "^4.6.1" "socket.io-client": "^4.6.1"

View File

@ -1,36 +1,55 @@
import Modal from "react-modal"; import Modal from "react-modal";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Mention, MentionsInput } from "react-mentions";
import { useAuthContext } from "../context/authContext"; import { useAuthContext } from "../context/authContext";
import mentionStyles from "../styles/mention";
const customStyles = { import modalStyles from "../styles/modal";
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
width: "40vw",
maxWidth: "800px",
},
};
Modal.setAppElement("#root"); Modal.setAppElement("#root");
export default function TimerHeader({ friends, selected, onSelect }) { export default function TimerHeader({ friends, selected, onSelect }) {
const [modalOpen, setModalOpen] = useState(false); const [modalOpen, setModalOpen] = useState(false);
const [newTimerName, setNewTimerName] = useState("");
const [errors, setErrors] = useState([]);
const { friendName, setSignedIn } = useAuthContext(); const { friendName, setSignedIn } = useAuthContext();
const logout = () => { const logout = () => {
fetch("/api/auth/logout").then(() => setSignedIn(false)); fetch("/api/auth/logout").then(() => setSignedIn(false));
}; };
const createTimer = (e) => {
e.preventDefault();
fetch("/api/timers", {
method: "POST",
body: JSON.stringify({
name: newTimerName.replaceAll(
/\[@[\w\d\s]+\]\((\@<\d+>)\)/g,
(_match, atId) => atId
),
}),
headers: {
"Content-Type": "application/json",
},
})
.then((r) => r.json())
.then((r) => {
if (r.message) {
setErrors([r.message]);
return;
}
setNewTimerName("");
setErrors([]);
setModalOpen(false);
});
};
return ( return (
<> <>
<Modal <Modal
isOpen={modalOpen} isOpen={modalOpen}
onRequestClose={() => setModalOpen(false)} onRequestClose={() => setModalOpen(false)}
style={customStyles} style={modalStyles}
> >
<div id="createTimerModal"> <div id="createTimerModal">
<div> <div>
@ -42,13 +61,35 @@ export default function TimerHeader({ friends, selected, onSelect }) {
marginBottom: "1rem", marginBottom: "1rem",
}} }}
> >
<span>New Timer</span> <h4 style={{ margin: "none" }}>New Timer</h4>
<a onClick={() => setModalOpen(false)} className="button outline"> <a onClick={() => setModalOpen(false)} className="button outline">
&times; &times;
</a> </a>
</div> </div>
<div> <div>
<form> <form onSubmit={createTimer}>
<MentionsInput
style={mentionStyles}
value={newTimerName}
onChange={(e) => setNewTimerName(e.target.value)}
>
<Mention
data={friends.map(({ id, name }) => ({
id: `@<${id}>`,
display: `@${name}`,
}))}
/>
</MentionsInput>
{errors.length ? (
errors.map((error, i) => (
<div key={i} className="text-error">
{error}
</div>
))
) : (
<></>
)}
<button type="submit">Add</button> <button type="submit">Add</button>
</form> </form>
</div> </div>

View File

@ -31,9 +31,11 @@ export default function Login() {
const getTokenFormSubmission = async (e) => { const getTokenFormSubmission = async (e) => {
e.preventDefault(); e.preventDefault();
const { error, token } = await requestTokenSubmit(e.target.name.value); const { error, message, token } = await requestTokenSubmit(
if (error) { e.target.name.value
setErrors([error]); );
if (error && message) {
setErrors([message]);
return; return;
} }
setErrors([]); setErrors([]);
@ -42,9 +44,8 @@ export default function Login() {
const signTokenFormSubmission = async (e) => { const signTokenFormSubmission = async (e) => {
e.preventDefault(); e.preventDefault();
const { error, token, expiration, friend } = await submitSignedToken( const { error, message, token, expiration, friend } =
e.target.signature.value await submitSignedToken(e.target.signature.value);
);
if (token) { if (token) {
setSignedIn(true); setSignedIn(true);
@ -55,7 +56,9 @@ export default function Login() {
return; return;
} }
setErrors([error]); if (error & message) {
setErrors([message]);
}
}; };
if (signedIn) { if (signedIn) {

View File

@ -0,0 +1,47 @@
export default {
control: {
backgroundColor: "#fff",
fontSize: 16,
// fontWeight: 'normal',
},
"&multiLine": {
control: {
fontFamily: "monospace",
minHeight: 63,
},
highlighter: {
padding: 9,
border: "1px solid transparent",
},
input: {
padding: 9,
border: "1px solid silver",
},
},
"&singleLine": {
display: "inline-block",
width: 180,
highlighter: {
padding: 1,
border: "2px inset transparent",
},
input: {
padding: 1,
border: "2px inset",
},
},
suggestions: {
list: {
backgroundColor: "white",
border: "1px solid rgba(0,0,0,0.15)",
fontSize: 16,
},
item: {
padding: "5px 15px",
borderBottom: "1px solid rgba(0,0,0,0.15)",
"&focused": {
backgroundColor: "#cee4e5",
},
},
},
};

View File

@ -0,0 +1,12 @@
export default {
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
width: "40vw",
maxWidth: "800px",
},
};