diff --git a/code/frontend/src/App.js b/code/frontend/src/App.js index ae2855a..47f49bd 100644 --- a/code/frontend/src/App.js +++ b/code/frontend/src/App.js @@ -1,3 +1,4 @@ +import React from "react" import Layout from "./components/interactive/Layout" import { BrowserRouter } from "react-router-dom" import GlobalTheme from "./components/providers/GlobalTheme" diff --git a/code/frontend/src/components/interactive/BoxRepositoriesActive.js b/code/frontend/src/components/interactive/BoxRepositoriesActive.js index fa16042..572868c 100644 --- a/code/frontend/src/components/interactive/BoxRepositoriesActive.js +++ b/code/frontend/src/components/interactive/BoxRepositoriesActive.js @@ -3,41 +3,50 @@ import BoxFull from "../base/BoxFull" import SummaryRepository from "./SummaryRepository" import { faFolderOpen } from "@fortawesome/free-solid-svg-icons" import ContextUser from "../../contexts/ContextUser" +import Loading from "../base/Loading" +import BoxFullScrollable from "../base/BoxFullScrollable" /** * A {@link BoxFull} listing all the user's active repositories. * * @param repositories - Array of repositories to display in the box. - * @param refresh - Function that can be called to refresh the repositories list. + * @param archiveRepository - Function to be called when archive is pressed on a repository summary. + * @param destroyRepository - Function to be called when delete is pressed on a repository summary. + * @param running - If an action is currently running. * @param props - Additional props to pass to the box. * @returns {JSX.Element} * @constructor */ -export default function BoxRepositoriesActive({ repositories, refresh, ...props }) { +export default function BoxRepositoriesActive({ repositories, archiveRepository, destroyRepository, running, ...props }) { const { user } = useContext(ContextUser) let contents - if(repositories.length > 0) { + if(repositories === null) { + contents = + } + else if(repositories.length === 0) { + contents = There's nothing here. + } + else { contents = repositories.map(repo => ( archiveRepository(repo["id"])} + deleteSelf={() => destroyRepository(repo["id"])} canArchive={true} canEdit={true} canDelete={repo["owner"]["username"] === user["username"]} + running={running} /> )) } - else { - contents = There's nothing here. - } return ( - + {contents} - + ) } diff --git a/code/frontend/src/components/interactive/BoxRepositoriesArchived.js b/code/frontend/src/components/interactive/BoxRepositoriesArchived.js index 6db7a31..2910c0f 100644 --- a/code/frontend/src/components/interactive/BoxRepositoriesArchived.js +++ b/code/frontend/src/components/interactive/BoxRepositoriesArchived.js @@ -1,43 +1,52 @@ import React, { useContext } from "react" import BoxFull from "../base/BoxFull" -import ContextUser from "../../contexts/ContextUser" import SummaryRepository from "./SummaryRepository" -import { faFolder } from "@fortawesome/free-solid-svg-icons" +import { faFolderOpen } from "@fortawesome/free-solid-svg-icons" +import ContextUser from "../../contexts/ContextUser" +import Loading from "../base/Loading" +import BoxFullScrollable from "../base/BoxFullScrollable" /** * A {@link BoxFull} listing all the user's archived repositories. * * @param repositories - Array of repositories to display in the box. - * @param refresh - Function that can be called to refresh the repositories list. + * @param archiveRepository - Function to be called when archive is pressed on a repository summary. + * @param destroyRepository - Function to be called when delete is pressed on a repository summary. + * @param running - If an action is currently running. * @param props - Additional props to pass to the box. * @returns {JSX.Element} * @constructor */ -export default function BoxRepositoriesArchived({ repositories, refresh, ...props }) { +export default function BoxRepositoriesArchived({ repositories, archiveRepository, destroyRepository, running, ...props }) { const { user } = useContext(ContextUser) let contents - if(repositories.length > 0) { + if(repositories === null) { + contents = + } + else if(repositories.length === 0) { + contents = There's nothing here. + } + else { contents = repositories.map(repo => ( archiveRepository(repo["id"])} + deleteSelf={() => destroyRepository(repo["id"])} canArchive={false} canEdit={false} canDelete={repo["owner"]["username"] === user["username"]} + running={running} /> )) } - else { - contents = There's nothing here. - } return ( - + {contents} - + ) } diff --git a/code/frontend/src/components/interactive/SummaryRepository.js b/code/frontend/src/components/interactive/SummaryRepository.js index 2b3365f..293d1c8 100644 --- a/code/frontend/src/components/interactive/SummaryRepository.js +++ b/code/frontend/src/components/interactive/SummaryRepository.js @@ -13,20 +13,20 @@ import Summary from "../base/Summary" * @param repo - The repository object. * @param refresh - Function that can be called to refresh the repositories list. * @param canDelete - If the Delete button should be displayed or not. + * @param deleteSelf - Function to call when the Delete button is pressed. * @param canEdit - If the Edit button should be displayed or not. * @param canArchive - If the Archive button should be displayed or not. + * @param archiveSelf - Function to call when the Archive button is pressed. + * @param running - If an action is currently running. * @param className - Additional class(es) to be added to the outer box. * @param props - Additional props to pass to the outer box. * @returns {JSX.Element} * @constructor */ export default function SummaryRepository( - { repo, refresh, canDelete, canEdit, canArchive, className, ...props }, + { repo, refresh, canDelete, deleteSelf, canEdit, canArchive, archiveSelf, running, className, ...props }, ) { - const { fetchDataAuth } = useContext(ContextUser) const history = useHistory() - const { fetchNow: archiveThis } = useBackend(fetchDataAuth, "PATCH", `/api/v1/repositories/${repo.id}`, { "close": true }) - const { fetchNow: deletThis } = useBackend(fetchDataAuth, "DELETE", `/api/v1/repositories/${repo.id}`) const onRepoClick = () => { history.push(`/repositories/${repo.id}`) @@ -36,22 +36,13 @@ export default function SummaryRepository( history.push(`/repositories/${repo.id}/edit`) } - const onArchiveClick = async () => { - await archiveThis() - await refresh() - } - - const onDeleteClick = async () => { - await deletThis() - await refresh() - } - const buttons = <> {canDelete ? @@ -61,6 +52,7 @@ export default function SummaryRepository( color={"Yellow"} icon={faPencilAlt} onClick={onEditClick} + disabled={running} > Edit @@ -69,7 +61,8 @@ export default function SummaryRepository( diff --git a/code/frontend/src/hooks/useBackendViewset.js b/code/frontend/src/hooks/useBackendViewset.js index d5c7709..c7843fc 100644 --- a/code/frontend/src/hooks/useBackendViewset.js +++ b/code/frontend/src/hooks/useBackendViewset.js @@ -127,6 +127,25 @@ export default function useBackendViewset(resourcesPath, pkName) { [apiList], ) + const refreshResource = useCallback( + async (pk) => { + try { + const refreshedResource = await apiRetrieve(pk) + setResources(resources => resources.map(resource => { + if(resource[pkName] === pk) { + return refreshedResource + } + return resource + })) + } + catch(e) { + return { error: e } + } + return {} + }, + [apiRetrieve, pkName] + ) + const createResource = useCallback( async (data) => { try { @@ -191,12 +210,14 @@ export default function useBackendViewset(resourcesPath, pkName) { resources, running, loaded, + apiRequest, apiList, apiRetrieve, apiCreate, apiEdit, apiDestroy, refreshResources, + refreshResource, createResource, editResource, destroyResource, diff --git a/code/frontend/src/routes/PageRepositories.js b/code/frontend/src/routes/PageRepositories.js index b706154..995c582 100644 --- a/code/frontend/src/routes/PageRepositories.js +++ b/code/frontend/src/routes/PageRepositories.js @@ -1,32 +1,44 @@ -import React, { useContext } from "react" +import React, { useCallback, useContext } from "react" import Style from "./PageRepositories.module.css" import classNames from "classnames" import BoxRepositoriesActive from "../components/interactive/BoxRepositoriesActive" import BoxRepositoriesArchived from "../components/interactive/BoxRepositoriesArchived" -import useBackendImmediately from "../hooks/useBackendImmediately" -import ContextUser from "../contexts/ContextUser" -import renderContents from "../utils/renderContents" +import useBackendViewset from "../hooks/useBackendViewset" export default function PageRepositories({ children, className, ...props }) { - const { fetchDataAuth } = useContext(ContextUser) - const repositoryRequest = useBackendImmediately(fetchDataAuth, "GET", "/api/v1/repositories/") - const contents = renderContents( - repositoryRequest, - data => { - const repositories = [...data["owner"], ...data["spectator"]] - const active = repositories.filter(r => r.is_active) - const archived = repositories.filter(r => !r.is_active) - return <> - - - + const bv = useBackendViewset("/api/v1/repositories/", "id") + + const archiveRepository = useCallback( + async (pk) => { + try { + await bv.apiRequest("PATCH", `/api/v1/repositories/${pk}`, { + "close": true, + }) + await bv.refreshResource(pk) + } + catch(e) { + return { error: e } + } + return {} }, + [bv.apiRequest, bv.refreshResource] ) return (
- {contents} + r.is_active) : null} + archiveRepository={archiveRepository} + destroyRepository={bv.destroyResource} + running={bv.running} + /> + !r.is_active) : null} + archiveRepository={archiveRepository} + destroyRepository={bv.destroyResource} + running={bv.running} + />
) } diff --git a/code/frontend/src/utils/renderContents.js b/code/frontend/src/utils/renderContents.js index eb3827d..d84965a 100644 --- a/code/frontend/src/utils/renderContents.js +++ b/code/frontend/src/utils/renderContents.js @@ -1,3 +1,4 @@ +import React from "react" import Loading from "../components/base/Loading" import BoxAlert from "../components/base/BoxAlert" import Starting from "../components/base/Starting"