Forked from
TO / cf-online-ui
44 commits behind the upstream repository.
Home.jsx 12.61 KiB
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;