diff --git a/invenio_communities/assets/semantic-ui/js/invenio_communities/api/CommunityLinksExtractor.js b/invenio_communities/assets/semantic-ui/js/invenio_communities/api/CommunityLinksExtractor.js index c831f9f77..67320a86f 100644 --- a/invenio_communities/assets/semantic-ui/js/invenio_communities/api/CommunityLinksExtractor.js +++ b/invenio_communities/assets/semantic-ui/js/invenio_communities/api/CommunityLinksExtractor.js @@ -35,4 +35,12 @@ export class CommunityLinksExtractor { } return this.#urls.invitations; } + + url(key) { + const urlOfKey = this.#urls[key]; + if (!urlOfKey) { + throw TypeError(`"${key}" link missing from resource.`); + } + return urlOfKey; + } } diff --git a/invenio_communities/assets/semantic-ui/js/invenio_communities/api/RequestLinksExtractor.js b/invenio_communities/assets/semantic-ui/js/invenio_communities/api/RequestLinksExtractor.js new file mode 100644 index 000000000..21267ad39 --- /dev/null +++ b/invenio_communities/assets/semantic-ui/js/invenio_communities/api/RequestLinksExtractor.js @@ -0,0 +1,29 @@ +// This file is part of Invenio-communities +// Copyright (C) 2024 Northwestern University. +// +// Invenio-communities is free software; you can redistribute it and/or modify it +// under the terms of the MIT License; see LICENSE file for more details. + +export class RequestLinksExtractor { + #urls; + + constructor(request) { + if (!request?.links) { + throw TypeError("Request resource links are undefined"); + } + this.#urls = request.links; + } + + url(key) { + const urlOfKey = this.#urls[key]; + if (!urlOfKey) { + throw TypeError(`"${key}" link missing from resource.`); + } + return urlOfKey; + } + + get userDiscussionUrl() { + const result = this.url("self_html"); + return result.replace("/requests/", "/me/requests/"); + } +} diff --git a/invenio_communities/assets/semantic-ui/js/invenio_communities/api/membershipRequests/api.js b/invenio_communities/assets/semantic-ui/js/invenio_communities/api/membershipRequests/api.js new file mode 100644 index 000000000..c9954d29f --- /dev/null +++ b/invenio_communities/assets/semantic-ui/js/invenio_communities/api/membershipRequests/api.js @@ -0,0 +1,26 @@ +// This file is part of Invenio-communities +// Copyright (C) 2022 CERN. +// Copyright (C) 2024 Northwestern University. +// +// Invenio-communities is free software; you can redistribute it and/or modify it +// under the terms of the MIT License; see LICENSE file for more details. + +import { CommunityLinksExtractor } from "../CommunityLinksExtractor"; +import { http } from "react-invenio-forms"; + +/** + * API Client for community membership requests. + * + * It mostly uses the API links passed to it from initial community. + * + */ +export class CommunityMembershipRequestsApi { + constructor(community) { + this.community = community; + this.linksExtractor = new CommunityLinksExtractor(community); + } + + requestMembership = async (payload) => { + return await http.post(this.linksExtractor.url("membership_requests"), payload); + }; +} diff --git a/invenio_communities/assets/semantic-ui/js/invenio_communities/community/header/RequestMembershipButton.js b/invenio_communities/assets/semantic-ui/js/invenio_communities/community/header/RequestMembershipButton.js new file mode 100644 index 000000000..d09c5c8f2 --- /dev/null +++ b/invenio_communities/assets/semantic-ui/js/invenio_communities/community/header/RequestMembershipButton.js @@ -0,0 +1,148 @@ +/* + * This file is part of Invenio. + * Copyright (C) 2024 CERN. + * Copyright (C) 2024 Northwestern University. + * + * Invenio is free software; you can redistribute it and/or modify it + * under the terms of the MIT License; see LICENSE file for more details. + */ + +import { i18next } from "@translations/invenio_communities/i18next"; +import { Formik } from "formik"; +import PropTypes from "prop-types"; +import React, { useState } from "react"; +import { TextAreaField } from "react-invenio-forms"; +import { Button, Form, Grid, Message, Modal } from "semantic-ui-react"; + +import { CommunityMembershipRequestsApi } from "../../api/membershipRequests/api"; +import { communityErrorSerializer } from "../../api/serializers"; +import { RequestLinksExtractor } from "../../api/RequestLinksExtractor"; + +export function RequestMembershipModal(props) { + const [errorMsg, setErrorMsg] = useState(""); + + const { community, isOpen, onClose } = props; + + const onSubmit = async (values, { setSubmitting, setFieldError }) => { + /**Submit callback called from Formik. */ + setSubmitting(true); + + const client = new CommunityMembershipRequestsApi(community); + + try { + const response = await client.requestMembership(values); + const linksExtractor = new RequestLinksExtractor(response.data); + window.location.href = linksExtractor.userDiscussionUrl; + } catch (error) { + setSubmitting(false); + + console.log("Error"); + console.dir(error); + + const { errors, message } = communityErrorSerializer(error); + + if (message) { + setErrorMsg(message); + } + + if (errors) { + errors.forEach(({ field, messages }) => setFieldError(field, messages[0])); + } + } + }; + + return ( + + {({ values, isSubmitting, handleSubmit }) => ( + + {i18next.t("Request Membership")} + + + +
+ + +
+ + + + +
+ )} +
+ ); +} + +RequestMembershipModal.propTypes = { + isOpen: PropTypes.bool.isRequired, + onClose: PropTypes.func.isRequired, + community: PropTypes.object.isRequired, +}; + +export function RequestMembershipButton(props) { + const [isModalOpen, setModalOpen] = useState(false); + const { community } = props; + + const handleClick = () => { + setModalOpen(true); + }; + + const handleClose = () => { + setModalOpen(false); + }; + + return ( + <> +