import React, { useCallback, useEffect, useState } from "react"; import { Helmet } from "react-helmet-async"; import Joyride, { EVENTS } from "react-joyride"; import ReactPlayer from "react-player/lazy"; import { useKeycloak } from "@react-keycloak/web"; import useWindowSize from "@rooks/use-window-size"; import { closeDiscussion, endProgramPoint, openDiscussion, renameProgramPoint, } from "actions/program"; import { DropdownMenu, DropdownMenuItem } from "components/dropdown-menu"; import { AlreadyFinished, BreakInProgress, NotYetStarted, } from "components/home"; import ModalConfirm from "components/modals/ModalConfirm"; import { Beacon, steps } from "components/onboarding"; import ProgramEntryEditModal from "components/program/ProgramEntryEditModal"; import AddAnnouncementForm from "containers/AddAnnouncementForm"; import AddPostForm from "containers/AddPostForm"; import AnnouncementsContainer from "containers/AnnoucementsContainer"; import GlobalStats from "containers/GlobalStats"; import JitsiInviteCard from "containers/JitsiInviteCard"; import PostFilters from "containers/PostFilters"; import PostsContainer from "containers/PostsContainer"; import { useActionConfirm } from "hooks"; import { AuthStore, GlobalInfoStore, ProgramStore } from "stores"; import "./Home.css"; const tourLSKey = "cf2021__tour"; const Home = () => { const { currentId, items: programEntries, scheduleIds, } = ProgramStore.useState(); const { isAuthenticated, user } = AuthStore.useState(); const { streamUrl } = GlobalInfoStore.useState(); const programEntry = currentId ? programEntries[currentId] : null; const [showProgramEditModal, setShowProgramEditModal] = useState(false); const [runJoyRide, setRunJoyride] = useState(false); // The easiest way to restart the joyride tour is by simply re-rendering the component. const [joyrideRenderKey, setJoyrideRenderKey] = useState(0); const { innerWidth } = useWindowSize(); const isLg = innerWidth >= 1024; const [ showCloseDiscussion, setShowCloseDiscussion, onCloseDiscussionConfirm, onCloseDiscussionCancel, ] = useActionConfirm(closeDiscussion, programEntry); const [ showOpenDiscussion, setShowOpenDiscussion, onOpenDiscussionConfirm, onOpenDiscussionCancel, ] = useActionConfirm(openDiscussion, programEntry); const [ showEndProgramPoint, setShowEndProgramPoint, onEndProgramPointConfirm, onEndProgramPointCancel, ] = useActionConfirm(endProgramPoint, programEntry); const { keycloak } = useKeycloak(); const login = useCallback(() => { keycloak.login(); }, [keycloak]); useEffect(() => { if (isLg && !localStorage.getItem(tourLSKey)) { setRunJoyride(true); } }, [isLg, setRunJoyride]); const onEditProgramConfirm = async (newTitle) => { await renameProgramPoint.run({ programEntry, newTitle }); setShowProgramEditModal(false); }; const onEditProgramCancel = () => { setShowProgramEditModal(false); }; const showTutorial = useCallback(() => { setRunJoyride(true); setJoyrideRenderKey(joyrideRenderKey + 1); }, [joyrideRenderKey, setRunJoyride, setJoyrideRenderKey]); const handleJoyrideCallback = ({ action, index, status, type }) => { if (type === EVENTS.TOUR_END) { localStorage.setItem(tourLSKey, "COMPLETED"); } }; const firstProgramEntry = scheduleIds.length ? programEntries[scheduleIds[0]] : null; const lastProgramEntry = scheduleIds.length ? programEntries[scheduleIds[0]] : null; if ( !programEntry && (!firstProgramEntry || new Date() < firstProgramEntry.expectedStartAt) ) { return ( <NotYetStarted startAt={ firstProgramEntry ? firstProgramEntry.expectedStartAt : undefined } /> ); } if ( !programEntry && lastProgramEntry && new Date() > lastProgramEntry.expectedStartAt ) { return <AlreadyFinished />; } if (!programEntry) { return <BreakInProgress />; } const displayActions = isAuthenticated && user.role === "chairman"; return ( <> <Helmet> <title>Přímý přenos | CF 2021 | Pirátská strana</title> <meta name="description" content="Přímý přenos a diskuse z on-line zasedání Celostátního fóra České pirátské strany, 9. 1. 2021." /> <meta property="og:title" content="Přímý přenos | CF 2021 | Pirátská strana" /> <meta property="og:description" content="Přímý přenos a diskuse z on-line zasedání Celostátního fóra České pirátské strany, 9. 1. 2021." /> </Helmet> <Joyride beaconComponent={Beacon} continuous={true} locale={{ back: "Zpět", close: "Zavřít", last: "Poslední", next: "Další", skip: "Přeskočit intro", }} key={joyrideRenderKey} run={runJoyRide} showProgress={true} showSkipButton={true} scrollToFirstStep={true} callback={handleJoyrideCallback} steps={steps} styles={{ options: { arrowColor: "#fff", backgroundColor: "#fff", overlayColor: "rgba(255, 255, 255, 0.75)", primaryColor: "#000", textColor: "#000", textAlign: "left", outline: "none", zIndex: 1000, borderRadius: 0, }, tooltip: { borderRadius: 0, }, tooltipContent: { textAlign: "left", }, buttonClose: { borderRadius: 0, fontSize: "0.875rem", }, buttonNext: { borderRadius: 0, padding: ".75em 2em", fontSize: "0.875rem", }, buttonBack: { color: "#4c4c4c", fontSize: "0.875rem", }, buttonSkip: { color: "#4c4c4c", fontSize: "0.875rem", }, }} /> <article className="container container--wide py-8 lg:py-24 cf2021 bg-white"> <div className="cf2021__title flex justify-between"> <h1 className="head-alt-base lg:head-alt-lg"> {programEntry.number !== "" && `Bod č. ${programEntry.number}: `} {programEntry.title} </h1> <div className="pl-4 pt-1 lg:pt-5"> <div className="space-x-4 inline-flex items-center"> <button className="ico--question text-grey-200 hidden lg:block hover:text-black text-lg" aria-label="Potřebuješ pomoc? Spusť si znovu nápovědu jak tuhle aplikaci používat." data-tip="Potřebuješ pomoc? Spusť si znovu nápovědu jak tuhle aplikaci používat." data-tip-at="top" onClick={showTutorial} /> {displayActions && ( <DropdownMenu right triggerSize="lg"> <DropdownMenuItem onClick={() => setShowProgramEditModal(true)} icon="ico--pencil" title="Přejmenovat bod programu" titleSize="base" iconSize="base" /> {programEntry.discussionOpened && ( <DropdownMenuItem onClick={() => setShowCloseDiscussion(true)} icon="ico--bubbles" title="Ukončit rozpravu" titleSize="base" iconSize="base" /> )} {!programEntry.discussionOpened && ( <DropdownMenuItem onClick={() => setShowOpenDiscussion(true)} icon="ico--bubbles" title="Otevřít rozpravu" titleSize="base" iconSize="base" /> )} <DropdownMenuItem onClick={() => setShowEndProgramPoint(true)} icon="ico--switch" title="Ukončit bod programu" titleSize="base" iconSize="base" /> </DropdownMenu> )} </div> </div> </div> <section className="cf2021__video"> <div className="container-padding--zero md:container-padding--auto"> {streamUrl && ( <div className="iframe-container joyride-player"> <ReactPlayer url={streamUrl} title="Video stream" controls={true} playing={true} muted={true} width="100%" height="" /> </div> )} {!streamUrl && ( <p> Server neposlal informaci o aktuálním streamu. Vyčkejte na aktualizaci. </p> )} <GlobalStats /> </div> </section> <section className="cf2021__notifications space-y-8"> <JitsiInviteCard /> <div className="lg:card lg:elevation-10 joyride-announcements"> <AnnouncementsContainer className="container-padding--zero lg:container-padding--auto" /> {isAuthenticated && user.role === "chairman" && ( <AddAnnouncementForm className="lg:card__body pt-4 lg:py-6" /> )} </div> </section> <section className="cf2021__posts joyride-posts"> <div className="flex flex-col xl:flex-row xl:justify-between xl:items-center mb-4"> <h2 className="head-heavy-xs md:head-heavy-sm whitespace-no-wrap"> <span>Příspěvky v rozpravě</span> </h2> <PostFilters /> </div> {!programEntry.discussionOpened && (!isAuthenticated || (isAuthenticated && !user.isBanned)) && ( <p className="alert alert--light items-center mb-4 elevation-4"> <i className="alert__icon ico--lock text-lg" /> Rozprava je uzavřena - příspěvky teď nelze přidávat. </p> )} {programEntry.discussionOpened && !isAuthenticated && ( <p className="alert alert--light items-center mb-4"> <i className="alert__icon ico--info text-lg" /> <span> Pokud chceš přidat nový příspěvek,{" "} <button onClick={login} className="underline cursor-pointer"> přihlaš se pomocí Pirátské identity </button> . </span> </p> )} {programEntry.discussionOpened && isAuthenticated && user.isBanned && ( <p className="alert alert--error items-center mb-4"> <i className="alert__icon ico--warning text-lg" /> Jejda! Nemůžeš přidávat příspěvky, protože máš ban. Vyčkej než ti ho předsedající odebere. </p> )} {programEntry.discussionOpened && isAuthenticated && !user.isBanned && <AddPostForm className="mb-8" canAddProposal={user.role === "member" || user.role === "chairman"} />} <PostsContainer className="container-padding--zero lg:container-padding--auto" showAddPostCta={programEntry.discussionOpened} /> </section> </article> <ProgramEntryEditModal isOpen={showProgramEditModal} onConfirm={onEditProgramConfirm} onCancel={onEditProgramCancel} programEntry={programEntry} /> <ModalConfirm isOpen={showCloseDiscussion} onConfirm={onCloseDiscussionConfirm} onCancel={onCloseDiscussionCancel} title="Ukončit rozpravu?" yesActionLabel="Ukončit" > Opravdu chcete ukončit rozpravu? </ModalConfirm> <ModalConfirm isOpen={showOpenDiscussion} onConfirm={onOpenDiscussionConfirm} onCancel={onOpenDiscussionCancel} title="Otevřít rozpravu?" yesActionLabel="Otevřít" > Opravdu chcete otevřít rozpravu k tomuto bodu programu? </ModalConfirm> <ModalConfirm isOpen={showEndProgramPoint} onConfirm={onEndProgramPointConfirm} onCancel={onEndProgramPointCancel} title="Ukončit bod programu?" yesActionLabel="Ukončit bod programu" > Bod programu <strong>bude ukončen</strong>. Opravdu to chcete? </ModalConfirm> </> ); }; export default Home;