Skip to content

Commit

Permalink
Merge pull request #16 from Borumer/polish-home-request
Browse files Browse the repository at this point in the history
Polish home response handling, implement task sharing, and refactor
  • Loading branch information
varunsingh87 authored May 2, 2021
2 parents c47ee95 + 8ac4e7d commit 97bb388
Show file tree
Hide file tree
Showing 27 changed files with 4,563 additions and 1,386 deletions.
2 changes: 0 additions & 2 deletions components/CreateJottingButton/createJottingButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import * as Requests from "../../libs/Datastore/requests";
*/
export default function CreateJottingButton({
jotType,
jots,
setJots,
requestFunc = "createJotting",
requestArg1 = jotType,
}) {
Expand Down
4 changes: 2 additions & 2 deletions components/Jotting/jottingDetails.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import jotting from "./jotting.module.css";
import FetchError from "../FetchError/fetchError";
import CircularProgress from "../CircularProgress/circularProgress";
import ProgressSpinner from "../ProgressSpinner/progressSpinner";
import { getBody, updateBody } from "../../libs/Datastore/requests";
import { useEffect, useState } from "react";
import {
Expand Down Expand Up @@ -67,5 +67,5 @@ export default function JottingDetails({ jottingInfo, jotType }) {
};
}, [body]);

return body != null ? bodyEl : <CircularProgress />;
return body != null ? bodyEl : <ProgressSpinner />;
}
10 changes: 2 additions & 8 deletions components/Jotting/noteOptionsBar.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import JottingOptionsBar from "./JottingOptionsBar";
import Image from "next/image";
import ShareButton from "../ShareButton/shareButton";

/**
* The JottingOptionsBar plus specific Note options
* @param {object} props Information about the note
*/
export default function NoteOptionsBar(props) {
const handleShareClick = e => {
const [showShareMenu, setShowShareMenu] = props.showShareMenuState;
setShowShareMenu(!showShareMenu);
};

return (
<JottingOptionsBar jotType="note" {...props}>
<button onClick={handleShareClick}>
<Image height={35} width={55} src="/images/share.png" alt="share icon" title="Share jotting" />
</button>
<ShareButton showShareMenuState={props.showShareMenuState} />
</JottingOptionsBar>
);
}
6 changes: 3 additions & 3 deletions components/Jotting/subtaskList.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
import { useRecurringRequest } from "../../libs/Datastore/requestHelpers";
import { deleteTask, getSubtasks } from "../../libs/Datastore/requests";
import CircularProgress from "../CircularProgress/circularProgress";
import ProgressSpinner from "../ProgressSpinner/progressSpinner";
import FetchError from "../FetchError/fetchError";
import RemoveableListItem from "../RemoveableListItem/removeableListItem";
import StyledCheckbox from "../StyledCheckbox/styledCheckbox";
Expand Down Expand Up @@ -34,7 +34,7 @@ export default function SubtaskList({ id, subtasksState }) {
newList.splice(i, 1); // Remove the element at the current index
setSubtasks(null); // Needed for state to actually update
setSubtasks(newList);
return;
break;
}
}
})
Expand Down Expand Up @@ -64,5 +64,5 @@ export default function SubtaskList({ id, subtasksState }) {
</ul>
);
else if (subtasks != null) return <FetchError itemName="subtasks" />;
else return <CircularProgress />;
else return <ProgressSpinner />;
}
2 changes: 2 additions & 0 deletions components/Jotting/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import SubtaskList from "./subtaskList";
import CreateJottingButton from "../CreateJottingButton/createJottingButton";
import { useState } from "react";
import StyledCheckbox from "../StyledCheckbox/styledCheckbox";
import ShareButton from "../ShareButton/shareButton";

export default function Task(task) {
return (
<Jotting jotType="task" {...task}>
<JottingOptionsBar jotType="task" {...task}>
<ShareButton showShareMenuState={task.showShareMenuState} />
<div>
<StyledCheckbox
id={task.id}
Expand Down
91 changes: 91 additions & 0 deletions components/JottingsControls/jottingsControl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import NoteControl from "./noteControl";
import TaskControl from "./taskControl";
import NotesControl from "./notesControl";
import TasksControl from "./tasksControl";
import { useEffect, useState } from "react";
import { getJottings, getSharedJottings } from "../../libs/Datastore/requests";
import { useInterval } from "../../libs/delay";
import { compareArrays } from "../../libs/arrayExtensions";

export default function JottingsControl(props) {
const [notes, setNotes] = useState(null);
const [tasks, setTasks] = useState(null);

const getJotsToShow = (response, jotType) => {
if (
response[1].status === "rejected" &&
response[0].status === "fulfilled"
)
return response[0].value[jotType];
else if (
response[1].status === "fulfilled" &&
response[0].status === "fulfilled"
)
return [...response[0].value[jotType], ...response[1].value[jotType]];
else if (
response[0].status === "rejected" &&
response[1].status === "fulfilled"
)
return response[1].value[jotType];

return -1;
};

const makeJottingsRequests = async (interval) => {
const ownAbortController = new AbortController();
const sharedAbortController = new AbortController();

if (notes === -1 || tasks === -1) {
console.info("Interval cleared due to errors");
clearInterval(interval);
}

try {
const response = Promise.allSettled([
getJottings(ownAbortController),
getSharedJottings(sharedAbortController),
]);

console.info("Requests to owned and shared jottings started");
console.debug("Response", await response);

const notesToShow = getJotsToShow(await response, "notes");
const tasksToShow = getJotsToShow(await response, "tasks");

console.info("Response received, data computed");
console.debug("notesToShow", notesToShow);
console.debug("tasksToShow", tasksToShow);

const notesToShowIsNewData =
notes == null || compareArrays(notes, notesToShow);

if (notesToShowIsNewData) {
setNotes(notesToShow);
console.info("UI updated");
}
if (tasksToShow != tasks) {
setTasks(tasksToShow);
}
} catch (e) {
console.error("Request Error:", e);
}
};

// componentDidMount() - Load the jottings and recurringly update them with requests
const updateData = useInterval(() => {
console.group("Interval Cycle");
makeJottingsRequests(updateData).then(() =>
console.groupEnd("Interval Cycle")
);
}, 10000);

return (
<>
<NotesControl notesState={[notes, setNotes]} />
<TasksControl tasksState={[tasks, setTasks]} />

<NoteControl notes={notes} />
<TaskControl tasks={tasks} />
</>
);
}
68 changes: 68 additions & 0 deletions components/JottingsControls/jottingsControl.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
.ownNoteList {
grid-column: 1 / 3;
}

.ownTaskList {
grid-column: 3 / 7;
}

.ownNoteList, .ownTaskList {
grid-row: 2 / 6;
margin: 1em;
}

.fullJotting {
display: grid;
grid-row: 2 / 5;
grid-column-end: -1;
justify-self: flex-start;
overflow: auto;
grid-template-columns: subgrid;
}

.taskControl {
grid-column-start: 1;
}

.noteControl {
grid-column-start: 2;
}

.jottingContent {
grid-row: 1 / 2;
grid-column: 1 / -3;
}

.noteControl, .taskControl {
position: relative;
}

@media only screen and (max-width: 830px) {
.noteControl, .fullJotting, .jottingContent {
grid-column: 1 / -1;
justify-self: stretch;
}

.taskControl {
grid-row: 7 / -1;
}

.ownNoteList {
grid-column: 1 / 3;
}

.ownTaskList {
grid-column: 3 / 5;
}
}

@media only screen and (max-width: 600px) {
.ownNoteList, .ownTaskList {
grid-column: 1 / -1;
}

.ownTaskList {
grid-row: 6;
}
}

54 changes: 54 additions & 0 deletions components/JottingsControls/noteControl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Note from "../Jotting/note";
import ShareMenu from "../ShareMenu/shareMenu";
import { useRef, useState, useEffect } from "react";
import jottingsControl from "./jottingsControl.module.css";
import { useRouter } from "next/router";
import { useOutsideAlerter, useEscapeAlerter } from "../../libs/view";
import UrlService from "../../libs/UrlService";

export default function NoteControl({ notes }) {
const router = useRouter();
const [showShareMenu, setShowShareMenu] = useState(false);

const ref = useRef(null);

// Escape the jot popup when Escape is pressed or the user clicks outside this component
useOutsideAlerter(ref, router);
useEscapeAlerter(router);

const urlService = new UrlService(router);
const showNote =
notes &&
((router.query.type &&
router.query.type == "note" &&
router.query.id) ||
urlService.queryHasJottingInfo("note"));

useEffect(() => {
urlService.setQueryToJottingInfo("note");
}, [notes]);

if (showNote && router.query.type == "note") {
return (
<article ref={showShareMenu ? ref : null} className={`${jottingsControl.fullJotting} ${jottingsControl.noteControl}`}>
<div
ref={showShareMenu ? null : ref}
className={jottingsControl.jottingContent}
>
<Note
note={router.query}
showShareMenuState={[showShareMenu, setShowShareMenu]}
/>
</div>

{showShareMenu && router.query.type == "note" ? (
<ShareMenu jotType="note" setShowShareMenu={setShowShareMenu} />
) : (
""
)}
</article>
);
}

return null;
}
25 changes: 25 additions & 0 deletions components/JottingsControls/notesControl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import ProgressSpinner from "../ProgressSpinner/progressSpinner";
import CreateNoteButton from "../CreateJottingButton/createNoteButton";
import NoteList from "../JottingList/noteList";
import jottingsControl from "./jottingsControl.module.css";

/**
* Control for Notes heading,
* list for view user notes, and
* button to create note
* @param { { notesState: [notes, setNotes] } } props
* @param props.notesState The array returned from useState for the notes state
* @param props.notesState[0] The value of notes
* @param props.notesState[1] The Dispatch to set a new value to the notes state
*/
export default function NotesControl({notesState}) {
const [notes, setNotes] = notesState;

return (
<article className={jottingsControl.ownNoteList}>
<h1>Notes</h1>
{notes ? <NoteList notes={notes} /> : <ProgressSpinner />}
<CreateNoteButton jots={notes} />
</article>
);
}
50 changes: 50 additions & 0 deletions components/JottingsControls/taskControl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useRouter } from "next/router";
import { useRef, useState } from "react";
import UrlService from "../../libs/UrlService";
import { useEscapeAlerter, useOutsideAlerter } from "../../libs/view";
import Task from "../Jotting/task";
import ShareMenu from "../ShareMenu/shareMenu";
import jottingsControl from "./jottingsControl.module.css";

export default function TaskControl({ tasks }) {
const router = useRouter();
const urlService = new UrlService(router);
const ref = useRef(null);
const [showShareMenu, setShowShareMenu] = useState(false);

useOutsideAlerter(ref, router);

// Escape the jot popup when Escape is pressed
useEscapeAlerter(router);

urlService.setQueryToJottingInfo("task");

return tasks &&
((router.query.type &&
router.query.type == "task" &&
router.query.id) ||
urlService.queryHasJottingInfo("task")) ? (
<article
ref={ref}
className={`${jottingsControl.fullJotting} ${jottingsControl.taskControl}`}
>
<div
ref={showShareMenu ? null : ref}
className={jottingsControl.jottingContent}
>
<Task
showShareMenuState={[showShareMenu, setShowShareMenu]}
{...tasks.find((item) => item.id == router.query.id)}
/>
</div>

{showShareMenu && router.query.type == "task" ? (
<ShareMenu jotType="task" setShowShareMenu={setShowShareMenu} />
) : (
""
)}
</article>
) : (
""
);
}
Loading

1 comment on commit 97bb388

@vercel
Copy link

@vercel vercel bot commented on 97bb388 May 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.