From bc65dff61c7213f93ed161cb52adb03345447612 Mon Sep 17 00:00:00 2001 From: mrkvon Date: Fri, 13 Dec 2019 21:06:01 +0100 Subject: [PATCH] Migrate tribe's JoinButton to React (#1123) as seen on /tribes page --- modules/tribes/client/api/tribes.api.js | 11 ++ .../client/components/JoinButton.component.js | 120 ++++++++++++++++++ .../client/components/LeaveTribeModal.js | 35 +++++ .../tribes-list.client.controller.js | 12 +- .../client/views/tribes-list.client.view.html | 10 +- 5 files changed, 183 insertions(+), 5 deletions(-) create mode 100644 modules/tribes/client/api/tribes.api.js create mode 100644 modules/tribes/client/components/JoinButton.component.js create mode 100644 modules/tribes/client/components/LeaveTribeModal.js diff --git a/modules/tribes/client/api/tribes.api.js b/modules/tribes/client/api/tribes.api.js new file mode 100644 index 0000000000..9040de3c0b --- /dev/null +++ b/modules/tribes/client/api/tribes.api.js @@ -0,0 +1,11 @@ +import axios from 'axios'; + +export async function join(tribeId) { + const { data } = await axios.post(`/api/users/memberships/${tribeId}`); + return data; +} + +export async function leave(tribeId) { + const { data } = await axios.delete(`/api/users/memberships/${tribeId}`); + return data; +} diff --git a/modules/tribes/client/components/JoinButton.component.js b/modules/tribes/client/components/JoinButton.component.js new file mode 100644 index 0000000000..26dc6bcd2e --- /dev/null +++ b/modules/tribes/client/components/JoinButton.component.js @@ -0,0 +1,120 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { useTranslation } from 'react-i18next'; +import LeaveTribeModal from './LeaveTribeModal'; +import { OverlayTrigger, Tooltip } from 'react-bootstrap'; + +import * as api from '../api/tribes.api'; + +function JoinButtonPresentational({ isMember, isLoading, joinLabel='Join', joinedLabel='Joined', tribe, isLoggedIn, onToggle }) { + const { t } = useTranslation('tribes'); + + const ariaLabel = (isMember) ? t('Leave Tribe') : t(`${joinLabel} ({{label}})`, { label: tribe.label }); + const buttonLabel = (isMember) ? t(joinedLabel) : t(joinLabel); + + // a button to be shown when user is signed out + if (!isLoggedIn) { + return + {buttonLabel} + ; + } + + // a button for joining and leaving a tribe + const leaveTooltip = {t('Leave Tribe')}; + const btn = ; + + if (isMember) { + return {btn}; + } else { + return btn; + } +} + +JoinButtonPresentational.propTypes = { + isMember: PropTypes.bool.isRequired, + isLoading: PropTypes.bool, + isLoggedIn: PropTypes.bool.isRequired, + joinLabel: PropTypes.string, + joinedLabel: PropTypes.string, + tribe: PropTypes.object.isRequired, + onToggle: PropTypes.func +}; + +// @TODO this can (and should) be replaced by other container, when we finish the migration; when we start using redux etc. +export default function JoinButton({ tribe, user, onUpdated, ...rest }) { + // isLeaving controls whether the modal for leaving a tribe is shown + const [isLeaving, setIsLeaving] = useState(false); + + const [isUpdating, setIsUpdating] = useState(false); + + const isMemberInitial = user && user.memberIds && user.memberIds.indexOf(tribe._id) > -1; + const [isMember, setIsMember] = useState(isMemberInitial); + + /** + * Handle joining or leaving of a tribe + * - Join: join tribe immediately (api call) + * - Leave: show confirmation modal + */ + async function handleToggleMembership() { + if (isUpdating) { + return; + } + + if (isMember) { + setIsLeaving(true); + } else { + // updating starts + setIsUpdating(true); + + // join + const data = await api.join(tribe._id); + // update the membership locally + setIsMember(true); + + // updating finished + setIsUpdating(false); + // tell the ancestor components that the membership was updated + onUpdated(data); + } + } + + /** + * Leave a tribe (api call) + */ + async function handleLeave() { + setIsUpdating(true); + const data = await api.leave(tribe._id); + setIsUpdating(false); + setIsLeaving(false); + setIsMember(false); + // tell the ancestor components that the membership was updated + onUpdated(data); + } + + function handleCancelLeave() { + setIsLeaving(false); + } + + return <> + + + ; +} + +JoinButton.propTypes = { + tribe: PropTypes.object.isRequired, + user: PropTypes.object, + onUpdated: PropTypes.func.isRequired +}; diff --git a/modules/tribes/client/components/LeaveTribeModal.js b/modules/tribes/client/components/LeaveTribeModal.js new file mode 100644 index 0000000000..3f446b0f78 --- /dev/null +++ b/modules/tribes/client/components/LeaveTribeModal.js @@ -0,0 +1,35 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { useTranslation } from 'react-i18next'; +import { Modal } from 'react-bootstrap'; + +export default function LeaveTribeModal({ tribe, show, onConfirm, onCancel }) { + + const { t } = useTranslation('tribes'); + + return ( + +
+ + {t('Leave this Tribe?')} + + + + {t('Do you want to leave "{{label}}"?', { label: tribe.label })} + + + + + + +
+
+ ); +} + +LeaveTribeModal.propTypes = { + show: PropTypes.bool.isRequired, + onConfirm: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, + tribe: PropTypes.object.isRequired +}; diff --git a/modules/tribes/client/controllers/tribes-list.client.controller.js b/modules/tribes/client/controllers/tribes-list.client.controller.js index 84ba34292e..8ab82ae146 100644 --- a/modules/tribes/client/controllers/tribes-list.client.controller.js +++ b/modules/tribes/client/controllers/tribes-list.client.controller.js @@ -4,7 +4,7 @@ .controller('TribesListController', TribesListController); /* @ngInject */ - function TribesListController(tribes, $state, Authentication, TribeService, $scope) { + function TribesListController(tribes, $state, Authentication, TribeService, $rootScope, $scope) { // ViewModel const vm = this; @@ -13,6 +13,16 @@ vm.tribes = tribes; vm.user = Authentication.user; vm.openTribe = openTribe; + vm.broadcastChange = function (data) { + if (data.tribe) { + $rootScope.$broadcast('tribeUpdated', data.tribe); + } + + if (data.user) { + Authentication.user = data.user; + $rootScope.$broadcast('userUpdated'); + } + }; /** * Open tribe diff --git a/modules/tribes/client/views/tribes-list.client.view.html b/modules/tribes/client/views/tribes-list.client.view.html index 6c9cfc7934..6c262cd7de 100644 --- a/modules/tribes/client/views/tribes-list.client.view.html +++ b/modules/tribes/client/views/tribes-list.client.view.html @@ -38,11 +38,13 @@

- - + user="app.user" + icon="true" + onUpdated="tribesList.broadcastChange" + >