From 8d8f3ebac45db17795e2acc1d01c6eb52044edc3 Mon Sep 17 00:00:00 2001 From: mrkvon Date: Fri, 13 Dec 2019 16:34:52 +0100 Subject: [PATCH] Migrate a header for Tribes List to React (#1137) Also partially migrate the tr-boards directive to React --- modules/core/client/components/Board.js | 62 +++++++++++++++++++ .../controllers/app.client.controller.js | 7 +++ .../components/TribesHeader.component.js | 43 +++++++++++++ .../tribes-list.client.controller.js | 13 +++- .../client/views/tribes-list.client.view.html | 25 ++------ 5 files changed, 129 insertions(+), 21 deletions(-) create mode 100644 modules/core/client/components/Board.js create mode 100644 modules/tribes/client/components/TribesHeader.component.js diff --git a/modules/core/client/components/Board.js b/modules/core/client/components/Board.js new file mode 100644 index 0000000000..32d061c913 --- /dev/null +++ b/modules/core/client/components/Board.js @@ -0,0 +1,62 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { selectPhoto } from '../services/photos.service'; + +/** + * @param {string[]|string} names - array of names or a single name + * + * @returns {string} the provided name or randomly picked name from array + */ +function selectName(names) { + return Array.isArray(names) ? names[Math.floor(Math.random() * names.length)] : names; +} + +/** + * Partially migrated tr-boards directive + * modules/core/client/directives/tr-boards.client.directive.js + * + * @TODO implement tr-boards-ignore-small directive + * @TODO implement primary, inset, error and maybe other attributes, which are currently board classes + * and which could become attributes + */ +export default function Board({ names='bokeh', children, onDisplayPhoto=() => {}, onHidePhoto=() => {}, className, ...rest }) { + + const [photo, setPhoto] = useState(null); + + useEffect(() => { + // pick a name from provided names + const selectedName = selectName(names); + // select an appropriate photo by name + const photo = selectPhoto(selectedName); + const photoObject = { [selectedName]: photo }; + + // update the state with the selected photo + setPhoto(photo); + + // inform the parent that the photo is displayed + // ...useful e.g. for displaying photo credits elsewere + onDisplayPhoto(photoObject); + + // inform the parent that the photo is not displayed anymore + return () => onHidePhoto(photoObject); + }, []); + + const style = photo ? { backgroundImage: `url("${photo.imageUrl}")` } : null; + return ( +
+ {children} +
+ ); +} + +Board.propTypes = { + names: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.arrayOf(PropTypes.string), + ]).isRequired, + className: PropTypes.string, + children: PropTypes.node, + onDisplayPhoto: PropTypes.func, + onHidePhoto: PropTypes.func, +}; diff --git a/modules/core/client/controllers/app.client.controller.js b/modules/core/client/controllers/app.client.controller.js index 9c049a7c1a..3e36076983 100644 --- a/modules/core/client/controllers/app.client.controller.js +++ b/modules/core/client/controllers/app.client.controller.js @@ -231,6 +231,13 @@ function AppController( vm.photoCreditsCount++; }); + $scope.$on('photoCreditsRemoved', function (scope, photo) { + const photoName = Object.keys(photo)[0]; + // @TODO inconsistent results when there is the same photo displayed multiple times + delete vm.photoCredits[photoName]; + vm.photoCreditsCount--; + }); + } /** diff --git a/modules/tribes/client/components/TribesHeader.component.js b/modules/tribes/client/components/TribesHeader.component.js new file mode 100644 index 0000000000..6775b271f7 --- /dev/null +++ b/modules/tribes/client/components/TribesHeader.component.js @@ -0,0 +1,43 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { useTranslation } from 'react-i18next'; +import Board from '@/modules/core/client/components/Board'; + +export default function TribesHeader({ isLoggedIn, onDisplayPhoto, onHidePhoto }) { + + const { t } = useTranslation('tribes'); + + return ( + +
+
+
+

+

{t('Discover Tribes')}

+
+

+ {t('Joining Tribes helps you find likeminded Trustroots members.')} +

+ {!isLoggedIn && } +
+
+
+
+ ); +} + +TribesHeader.propTypes = { + isLoggedIn: PropTypes.bool.isRequired, + onDisplayPhoto: PropTypes.func.isRequired, + onHidePhoto: PropTypes.func.isRequired, +}; diff --git a/modules/tribes/client/controllers/tribes-list.client.controller.js b/modules/tribes/client/controllers/tribes-list.client.controller.js index cca04ccc6d..e15e6c01e2 100644 --- a/modules/tribes/client/controllers/tribes-list.client.controller.js +++ b/modules/tribes/client/controllers/tribes-list.client.controller.js @@ -3,7 +3,7 @@ angular .controller('TribesListController', TribesListController); /* @ngInject */ -function TribesListController(tribes, $state, Authentication, TribeService) { +function TribesListController(tribes, $state, Authentication, TribeService, $scope) { // ViewModel const vm = this; @@ -22,4 +22,15 @@ function TribesListController(tribes, $state, Authentication, TribeService) { TribeService.fillCache(angular.copy(tribe)); $state.go('tribes.tribe', { 'tribe': tribe.slug }); } + + /** + * Emit photo credits info + * @TODO remove this + */ + vm.addPhotoCredits = function addPhotoCredits(photo) { + $scope.$emit('photoCreditsUpdated', photo); + }; + vm.removePhotoCredits = function removePhotoCredits(photo) { + $scope.$emit('photoCreditsRemoved', photo); + }; } diff --git a/modules/tribes/client/views/tribes-list.client.view.html b/modules/tribes/client/views/tribes-list.client.view.html index f7ca11468e..6c9cfc7934 100644 --- a/modules/tribes/client/views/tribes-list.client.view.html +++ b/modules/tribes/client/views/tribes-list.client.view.html @@ -1,23 +1,8 @@ -
-
-
-
-

-

Discover Tribes

-
-

- Joining Tribes helps you find likeminded Trustroots members. -

- -
-
-
-
+