Fix autocomplete, create new timer
This commit is contained in:
parent
ea279b7295
commit
a862ad4d3b
49
client/package-lock.json
generated
49
client/package-lock.json
generated
@ -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",
|
||||||
|
@ -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"
|
||||||
|
@ -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">
|
||||||
×
|
×
|
||||||
</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>
|
||||||
|
@ -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) {
|
||||||
|
47
client/src/styles/mention.ts
Normal file
47
client/src/styles/mention.ts
Normal 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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
12
client/src/styles/modal.ts
Normal file
12
client/src/styles/modal.ts
Normal 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",
|
||||||
|
},
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user