{ "version": 3, "sources": ["../../src/modern/models/resources.js", "../../src/modern/components/ProjectsList/ProjectsList.js", "../../src/modern/components/ProjectsList/ProjectsListItem.js", "../../src/modern/components/ProjectsList/ProjectsListPlaceholderItem.js", "../../src/modern/components/Project/Modals.js", "../../src/modern/utils/misc.js"], "sourcesContent": ["import { getCurrentLanguage, getDefaultLanguage } from '$features/init/language'\n\nexport const getResourceUrl = (resource, size = 'full') => {\n return resource && resource.versions && resource.versions[size] && resource.versions[size].url\n}\n\nexport const getResourceWithLanguage = (\n resource,\n language = getCurrentLanguage(),\n fallbackLanguage = getDefaultLanguage()\n) => {\n if (!resource) return null\n\n if (resource[language]) {\n return resource[language]\n }\n\n if (resource[fallbackLanguage]) {\n return resource[fallbackLanguage]\n }\n\n return Object.values(resource).find(value => value !== '')\n}\n", "import React from 'react'\nimport PropTypes from 'prop-types'\n\nimport * as cookie from '$utils/cookie'\nimport { dataUTMPageContextShape } from '$utils/prop-types'\n\nimport ProjectsListItem from './ProjectsListItem'\nimport ProjectsListPlaceholderItem from './ProjectsListPlaceholderItem'\nimport { ModalProjectRemindMe, ModalProjectNoAuth } from '../Project/Modals'\n\nconst COOKIE_MODAL_NAMESPACE = 'ul_modal_remind_me'\n\nclass ProjectsList extends React.Component {\n constructor(props) {\n super(props)\n this.state = {\n isOpen: false,\n modalShowedUp: false\n }\n }\n\n handleCloseModal = () => {\n this.setState({\n isOpen: false\n })\n }\n\n handleModal = (project, status) => {\n // we don't want to show modal for unlike event\n if (status && status === 'neutral') return\n\n const { isAuthenticated } = this.props\n const hasCookie = cookie.get(COOKIE_MODAL_NAMESPACE) === 'true'\n if (hasCookie) {\n this.setState({\n modalShowedUp: true\n })\n }\n\n // do not display modal if project ended\n // also shutdown the modal if cookie is here and\n // user is authenticated\n if ((isAuthenticated && hasCookie) || project.finished) {\n this.setState({\n isOpen: false\n })\n return\n }\n\n this.setState(\n {\n isOpen: this.state.isOpen ? false : true\n },\n () => {\n cookie.set(COOKIE_MODAL_NAMESPACE, true, { expires: +new Date(), path: '/', domain: 'ulule.com' })\n }\n )\n }\n\n onError = () => {\n const { isAuthenticated } = this.props\n\n if (!isAuthenticated) {\n this.setState({\n isOpen: true\n })\n }\n\n return\n }\n\n render() {\n const {\n dataUTMPageContext,\n nbPlaceholders,\n noCss,\n currentLanguage,\n currentCurrency,\n rates,\n isAuthenticated,\n urls,\n modalImg,\n projects,\n isLikeCompact\n } = this.props\n\n let list\n if (nbPlaceholders) {\n if (nbPlaceholders < 0) {\n list = projects && new Array(-nbPlaceholders).fill(null).concat(projects)\n } else {\n list = projects && projects.concat(new Array(nbPlaceholders).fill(null))\n }\n } else {\n list = projects\n }\n\n const defaultClasses = 'b-list b-list--inline'\n\n return (\n \n )\n }\n}\n\nProjectsList.defaultProps = {\n nbPlaceholders: 0,\n noCss: false,\n isLikeCompact: true\n}\n\nProjectsList.propTypes = {\n dataUTMPageContext: dataUTMPageContextShape,\n projects: PropTypes.arrayOf(PropTypes.object).isRequired,\n currentLanguage: PropTypes.string.isRequired,\n currentCurrency: PropTypes.string.isRequired,\n isLikeCompact: PropTypes.bool,\n nbPlaceholders: PropTypes.number,\n // NOTE: The noCss prop is here to ensure this component can be used both with CSS and CSS-in-JS\n // environments, until the component is fully migrated to styled-components.\n noCss: PropTypes.bool\n}\n\nexport default ProjectsList\n", "import React from 'react'\nimport PropTypes from 'prop-types'\nimport styled from 'styled-components'\n\nimport { RoundButton } from '@ulule/owl-kit-components'\nimport * as duvet from '@ulule/duvet'\nimport { t, tn } from '@ulule/localize'\nimport { text } from '@owl-nest/utils'\n\nimport { getComingSoonLabel, getProgress, getShortCountdown, getFinishTrad, getStatus } from '$models/projects'\nimport { updateUserRole, saveAction } from '../../features/init/services'\n\nconst USER_ROLE_STATUS = {\n neutral: null,\n listed: 'fan',\n created: 'owner',\n backed: 'supporter',\n}\n\nconst ProjectCardWrapper = styled.div`\n display: flex;\n position: relative;\n flex-direction: column;\n width: 100%;\n min-width: 230px;\n max-width: 310px;\n`\n\nconst Button = styled(RoundButton)`\n position: relative;\n padding: 10px;\n`\n\nconst getRelatedUserRole = (userRole, isLikeCompact) => {\n switch (userRole) {\n case 'fan': {\n const fanLabel = isLikeCompact ? t('Listed') : t('Signed up for the launch')\n return {\n status: 'listed',\n label: fanLabel,\n apiAction: 'unlike',\n roundButton: {\n text: fanLabel,\n icon: 'active-heart',\n isCompact: isLikeCompact,\n },\n }\n }\n\n case 'supporter':\n return {\n status: 'backed',\n label: t('Backed'),\n apiAction: null,\n roundButton: {\n text: t('Backed'),\n icon: 'backed',\n isCompact: isLikeCompact,\n },\n }\n\n case 'owner':\n case 'editor':\n case 'moderator':\n return {\n status: 'created',\n label: t('Created'),\n apiAction: null,\n roundButton: {\n text: t('Created'),\n icon: 'created',\n isCompact: isLikeCompact,\n },\n }\n\n default: {\n const neutralLabel = isLikeCompact ? t('Add to my list') : t('Sign up for the launch')\n return {\n status: 'neutral',\n label: neutralLabel,\n apiAction: 'like',\n roundButton: {\n text: neutralLabel,\n icon: 'inactive-heart',\n isCompact: isLikeCompact,\n },\n }\n }\n }\n}\n\nconst LOCALSTORAGE_NAMESPACE = 'ul_modalShowedUp'\n\nclass ProjectsListItem extends React.Component {\n constructor(props) {\n super(props)\n const { project, isLikeCompact } = this.props\n\n if (project && project.user_role === undefined) return\n\n this.state = {\n ...getRelatedUserRole(project && project.user_role, isLikeCompact),\n }\n }\n\n componentDidUpdate(prevProps, prevState) {\n const status = this.state && this.state.status\n const prevStatus = prevState && prevState.status\n if (status !== prevStatus) {\n if (this.props.project && this.props.project.user_role === undefined) return\n this.setState({\n ...getRelatedUserRole(USER_ROLE_STATUS[this.state.status], this.props.isLikeCompact),\n })\n }\n\n if (window.localStorage.getItem(LOCALSTORAGE_NAMESPACE) === 'true') {\n return {\n modalShowedUp: true,\n }\n }\n }\n\n onClick = (event) => {\n event.preventDefault()\n const { project, action, isAuthenticated } = this.props\n const { status } = this.state\n if (isAuthenticated) {\n if (status && (status === 'created' || status === 'backed')) return\n\n const newStatus = status && status === 'listed' ? 'neutral' : 'listed'\n\n this.setState({\n status: newStatus,\n })\n\n updateUserRole(project && project.id, this.state.apiAction).then(() => {\n if (window.dataLayer) {\n window.dataLayer.push({\n event: 'socialInt',\n socialNetwork: 'Ulule',\n socialAction: newStatus === 'listed' ? 'Like' : 'Unlike',\n socialTarget: project && project.absolute_url,\n ululeProjectId: project && project.id,\n })\n }\n action.onSuccess(project, newStatus)\n }, action.onError)\n return\n }\n saveAction(project.id, this.state.apiAction)\n\n action.onError()\n return\n }\n\n isProjectAlmostEnded = () => {\n const { project } = this.props\n\n const FourtyHeightHours = 48 * 3600\n const projectEndDate = +new Date(project && project.date_end).toISOString()\n const today = +new Date().toISOString()\n\n if (today - projectEndDate < FourtyHeightHours) {\n return true\n }\n\n return false\n }\n\n handleCloseModal = () => {\n this.setState({\n isOpen: false,\n })\n }\n\n render() {\n const { currentLanguage, currentCurrency, rates, project, isLikeCompact } = this.props\n const progress = getProgress(project)\n let countdownText = ''\n if (project && project.date_end) {\n countdownText = getShortCountdown(project)\n }\n const isOnline = project && project.status === 'online'\n const isProjectFinished = project && project.finished\n\n const action =\n project && project.user_role !== undefined\n ? {\n ...this.state.roundButton,\n onClick: (event) => this.onClick(event),\n }\n : undefined\n\n let textFinishProject = t('Finished')\n if (isProjectFinished) {\n textFinishProject = getFinishTrad(project.date_end)\n }\n\n return (\n \n {project && (\n \n \n {!isLikeCompact &&