Skip to content

Commit 97bb388

Browse files
authored
Merge pull request #16 from Borumer/polish-home-request
Polish home response handling, implement task sharing, and refactor
2 parents c47ee95 + 8ac4e7d commit 97bb388

27 files changed

Lines changed: 4563 additions & 1386 deletions

components/CreateJottingButton/createJottingButton.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import * as Requests from "../../libs/Datastore/requests";
99
*/
1010
export default function CreateJottingButton({
1111
jotType,
12-
jots,
13-
setJots,
1412
requestFunc = "createJotting",
1513
requestArg1 = jotType,
1614
}) {

components/Jotting/jottingDetails.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import jotting from "./jotting.module.css";
22
import FetchError from "../FetchError/fetchError";
3-
import CircularProgress from "../CircularProgress/circularProgress";
3+
import ProgressSpinner from "../ProgressSpinner/progressSpinner";
44
import { getBody, updateBody } from "../../libs/Datastore/requests";
55
import { useEffect, useState } from "react";
66
import {
@@ -67,5 +67,5 @@ export default function JottingDetails({ jottingInfo, jotType }) {
6767
};
6868
}, [body]);
6969

70-
return body != null ? bodyEl : <CircularProgress />;
70+
return body != null ? bodyEl : <ProgressSpinner />;
7171
}
Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
import JottingOptionsBar from "./JottingOptionsBar";
22
import Image from "next/image";
3+
import ShareButton from "../ShareButton/shareButton";
34

45
/**
56
* The JottingOptionsBar plus specific Note options
67
* @param {object} props Information about the note
78
*/
89
export default function NoteOptionsBar(props) {
9-
const handleShareClick = e => {
10-
const [showShareMenu, setShowShareMenu] = props.showShareMenuState;
11-
setShowShareMenu(!showShareMenu);
12-
};
13-
1410
return (
1511
<JottingOptionsBar jotType="note" {...props}>
16-
<button onClick={handleShareClick}>
17-
<Image height={35} width={55} src="/images/share.png" alt="share icon" title="Share jotting" />
18-
</button>
12+
<ShareButton showShareMenuState={props.showShareMenuState} />
1913
</JottingOptionsBar>
2014
);
2115
}

components/Jotting/subtaskList.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useEffect, useState } from "react";
22
import { useRecurringRequest } from "../../libs/Datastore/requestHelpers";
33
import { deleteTask, getSubtasks } from "../../libs/Datastore/requests";
4-
import CircularProgress from "../CircularProgress/circularProgress";
4+
import ProgressSpinner from "../ProgressSpinner/progressSpinner";
55
import FetchError from "../FetchError/fetchError";
66
import RemoveableListItem from "../RemoveableListItem/removeableListItem";
77
import StyledCheckbox from "../StyledCheckbox/styledCheckbox";
@@ -34,7 +34,7 @@ export default function SubtaskList({ id, subtasksState }) {
3434
newList.splice(i, 1); // Remove the element at the current index
3535
setSubtasks(null); // Needed for state to actually update
3636
setSubtasks(newList);
37-
return;
37+
break;
3838
}
3939
}
4040
})
@@ -64,5 +64,5 @@ export default function SubtaskList({ id, subtasksState }) {
6464
</ul>
6565
);
6666
else if (subtasks != null) return <FetchError itemName="subtasks" />;
67-
else return <CircularProgress />;
67+
else return <ProgressSpinner />;
6868
}

components/Jotting/task.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import SubtaskList from "./subtaskList";
66
import CreateJottingButton from "../CreateJottingButton/createJottingButton";
77
import { useState } from "react";
88
import StyledCheckbox from "../StyledCheckbox/styledCheckbox";
9+
import ShareButton from "../ShareButton/shareButton";
910

1011
export default function Task(task) {
1112
return (
1213
<Jotting jotType="task" {...task}>
1314
<JottingOptionsBar jotType="task" {...task}>
15+
<ShareButton showShareMenuState={task.showShareMenuState} />
1416
<div>
1517
<StyledCheckbox
1618
id={task.id}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import NoteControl from "./noteControl";
2+
import TaskControl from "./taskControl";
3+
import NotesControl from "./notesControl";
4+
import TasksControl from "./tasksControl";
5+
import { useEffect, useState } from "react";
6+
import { getJottings, getSharedJottings } from "../../libs/Datastore/requests";
7+
import { useInterval } from "../../libs/delay";
8+
import { compareArrays } from "../../libs/arrayExtensions";
9+
10+
export default function JottingsControl(props) {
11+
const [notes, setNotes] = useState(null);
12+
const [tasks, setTasks] = useState(null);
13+
14+
const getJotsToShow = (response, jotType) => {
15+
if (
16+
response[1].status === "rejected" &&
17+
response[0].status === "fulfilled"
18+
)
19+
return response[0].value[jotType];
20+
else if (
21+
response[1].status === "fulfilled" &&
22+
response[0].status === "fulfilled"
23+
)
24+
return [...response[0].value[jotType], ...response[1].value[jotType]];
25+
else if (
26+
response[0].status === "rejected" &&
27+
response[1].status === "fulfilled"
28+
)
29+
return response[1].value[jotType];
30+
31+
return -1;
32+
};
33+
34+
const makeJottingsRequests = async (interval) => {
35+
const ownAbortController = new AbortController();
36+
const sharedAbortController = new AbortController();
37+
38+
if (notes === -1 || tasks === -1) {
39+
console.info("Interval cleared due to errors");
40+
clearInterval(interval);
41+
}
42+
43+
try {
44+
const response = Promise.allSettled([
45+
getJottings(ownAbortController),
46+
getSharedJottings(sharedAbortController),
47+
]);
48+
49+
console.info("Requests to owned and shared jottings started");
50+
console.debug("Response", await response);
51+
52+
const notesToShow = getJotsToShow(await response, "notes");
53+
const tasksToShow = getJotsToShow(await response, "tasks");
54+
55+
console.info("Response received, data computed");
56+
console.debug("notesToShow", notesToShow);
57+
console.debug("tasksToShow", tasksToShow);
58+
59+
const notesToShowIsNewData =
60+
notes == null || compareArrays(notes, notesToShow);
61+
62+
if (notesToShowIsNewData) {
63+
setNotes(notesToShow);
64+
console.info("UI updated");
65+
}
66+
if (tasksToShow != tasks) {
67+
setTasks(tasksToShow);
68+
}
69+
} catch (e) {
70+
console.error("Request Error:", e);
71+
}
72+
};
73+
74+
// componentDidMount() - Load the jottings and recurringly update them with requests
75+
const updateData = useInterval(() => {
76+
console.group("Interval Cycle");
77+
makeJottingsRequests(updateData).then(() =>
78+
console.groupEnd("Interval Cycle")
79+
);
80+
}, 10000);
81+
82+
return (
83+
<>
84+
<NotesControl notesState={[notes, setNotes]} />
85+
<TasksControl tasksState={[tasks, setTasks]} />
86+
87+
<NoteControl notes={notes} />
88+
<TaskControl tasks={tasks} />
89+
</>
90+
);
91+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
.ownNoteList {
2+
grid-column: 1 / 3;
3+
}
4+
5+
.ownTaskList {
6+
grid-column: 3 / 7;
7+
}
8+
9+
.ownNoteList, .ownTaskList {
10+
grid-row: 2 / 6;
11+
margin: 1em;
12+
}
13+
14+
.fullJotting {
15+
display: grid;
16+
grid-row: 2 / 5;
17+
grid-column-end: -1;
18+
justify-self: flex-start;
19+
overflow: auto;
20+
grid-template-columns: subgrid;
21+
}
22+
23+
.taskControl {
24+
grid-column-start: 1;
25+
}
26+
27+
.noteControl {
28+
grid-column-start: 2;
29+
}
30+
31+
.jottingContent {
32+
grid-row: 1 / 2;
33+
grid-column: 1 / -3;
34+
}
35+
36+
.noteControl, .taskControl {
37+
position: relative;
38+
}
39+
40+
@media only screen and (max-width: 830px) {
41+
.noteControl, .fullJotting, .jottingContent {
42+
grid-column: 1 / -1;
43+
justify-self: stretch;
44+
}
45+
46+
.taskControl {
47+
grid-row: 7 / -1;
48+
}
49+
50+
.ownNoteList {
51+
grid-column: 1 / 3;
52+
}
53+
54+
.ownTaskList {
55+
grid-column: 3 / 5;
56+
}
57+
}
58+
59+
@media only screen and (max-width: 600px) {
60+
.ownNoteList, .ownTaskList {
61+
grid-column: 1 / -1;
62+
}
63+
64+
.ownTaskList {
65+
grid-row: 6;
66+
}
67+
}
68+
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import Note from "../Jotting/note";
2+
import ShareMenu from "../ShareMenu/shareMenu";
3+
import { useRef, useState, useEffect } from "react";
4+
import jottingsControl from "./jottingsControl.module.css";
5+
import { useRouter } from "next/router";
6+
import { useOutsideAlerter, useEscapeAlerter } from "../../libs/view";
7+
import UrlService from "../../libs/UrlService";
8+
9+
export default function NoteControl({ notes }) {
10+
const router = useRouter();
11+
const [showShareMenu, setShowShareMenu] = useState(false);
12+
13+
const ref = useRef(null);
14+
15+
// Escape the jot popup when Escape is pressed or the user clicks outside this component
16+
useOutsideAlerter(ref, router);
17+
useEscapeAlerter(router);
18+
19+
const urlService = new UrlService(router);
20+
const showNote =
21+
notes &&
22+
((router.query.type &&
23+
router.query.type == "note" &&
24+
router.query.id) ||
25+
urlService.queryHasJottingInfo("note"));
26+
27+
useEffect(() => {
28+
urlService.setQueryToJottingInfo("note");
29+
}, [notes]);
30+
31+
if (showNote && router.query.type == "note") {
32+
return (
33+
<article ref={showShareMenu ? ref : null} className={`${jottingsControl.fullJotting} ${jottingsControl.noteControl}`}>
34+
<div
35+
ref={showShareMenu ? null : ref}
36+
className={jottingsControl.jottingContent}
37+
>
38+
<Note
39+
note={router.query}
40+
showShareMenuState={[showShareMenu, setShowShareMenu]}
41+
/>
42+
</div>
43+
44+
{showShareMenu && router.query.type == "note" ? (
45+
<ShareMenu jotType="note" setShowShareMenu={setShowShareMenu} />
46+
) : (
47+
""
48+
)}
49+
</article>
50+
);
51+
}
52+
53+
return null;
54+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import ProgressSpinner from "../ProgressSpinner/progressSpinner";
2+
import CreateNoteButton from "../CreateJottingButton/createNoteButton";
3+
import NoteList from "../JottingList/noteList";
4+
import jottingsControl from "./jottingsControl.module.css";
5+
6+
/**
7+
* Control for Notes heading,
8+
* list for view user notes, and
9+
* button to create note
10+
* @param { { notesState: [notes, setNotes] } } props
11+
* @param props.notesState The array returned from useState for the notes state
12+
* @param props.notesState[0] The value of notes
13+
* @param props.notesState[1] The Dispatch to set a new value to the notes state
14+
*/
15+
export default function NotesControl({notesState}) {
16+
const [notes, setNotes] = notesState;
17+
18+
return (
19+
<article className={jottingsControl.ownNoteList}>
20+
<h1>Notes</h1>
21+
{notes ? <NoteList notes={notes} /> : <ProgressSpinner />}
22+
<CreateNoteButton jots={notes} />
23+
</article>
24+
);
25+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { useRouter } from "next/router";
2+
import { useRef, useState } from "react";
3+
import UrlService from "../../libs/UrlService";
4+
import { useEscapeAlerter, useOutsideAlerter } from "../../libs/view";
5+
import Task from "../Jotting/task";
6+
import ShareMenu from "../ShareMenu/shareMenu";
7+
import jottingsControl from "./jottingsControl.module.css";
8+
9+
export default function TaskControl({ tasks }) {
10+
const router = useRouter();
11+
const urlService = new UrlService(router);
12+
const ref = useRef(null);
13+
const [showShareMenu, setShowShareMenu] = useState(false);
14+
15+
useOutsideAlerter(ref, router);
16+
17+
// Escape the jot popup when Escape is pressed
18+
useEscapeAlerter(router);
19+
20+
urlService.setQueryToJottingInfo("task");
21+
22+
return tasks &&
23+
((router.query.type &&
24+
router.query.type == "task" &&
25+
router.query.id) ||
26+
urlService.queryHasJottingInfo("task")) ? (
27+
<article
28+
ref={ref}
29+
className={`${jottingsControl.fullJotting} ${jottingsControl.taskControl}`}
30+
>
31+
<div
32+
ref={showShareMenu ? null : ref}
33+
className={jottingsControl.jottingContent}
34+
>
35+
<Task
36+
showShareMenuState={[showShareMenu, setShowShareMenu]}
37+
{...tasks.find((item) => item.id == router.query.id)}
38+
/>
39+
</div>
40+
41+
{showShareMenu && router.query.type == "task" ? (
42+
<ShareMenu jotType="task" setShowShareMenu={setShowShareMenu} />
43+
) : (
44+
""
45+
)}
46+
</article>
47+
) : (
48+
""
49+
);
50+
}

0 commit comments

Comments
 (0)