mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-09 17:14:21 +02:00
Refactor Notifications into Asks, replace all <Link> by <a>
This commit is contained in:
parent
70b53c5af8
commit
4273ea1603
@ -1,4 +1,3 @@
|
|||||||
import { Link } from "react-router-dom"
|
|
||||||
import { RouteConfig, renderRoutes } from "react-router-config"
|
import { RouteConfig, renderRoutes } from "react-router-config"
|
||||||
import { Helmet } from "react-helmet"
|
import { Helmet } from "react-helmet"
|
||||||
import { ToastContainer } from "react-toastify"
|
import { ToastContainer } from "react-toastify"
|
||||||
@ -29,7 +28,7 @@ const App = ({ route }: Route): JSX.Element => (
|
|||||||
<div className={styles.logo} />
|
<div className={styles.logo} />
|
||||||
<div>
|
<div>
|
||||||
<h1 className={styles.siteName}>
|
<h1 className={styles.siteName}>
|
||||||
<Link to="/">{config.APP.title}</Link>
|
<a href="/">{config.APP.title}</a>
|
||||||
</h1>
|
</h1>
|
||||||
<div className={styles.siteDescription}>{config.APP.description}</div>
|
<div className={styles.siteDescription}>{config.APP.description}</div>
|
||||||
</div>
|
</div>
|
||||||
|
38
src/components/Asks/AskDayWishes.tsx
Normal file
38
src/components/Asks/AskDayWishes.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { get } from "lodash"
|
||||||
|
import { useCallback } from "react"
|
||||||
|
import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
|
||||||
|
import { useAskTools, addAsk, answerLaterOnProfile } from "./utils"
|
||||||
|
import { useUserDayWishes } from "../VolunteerBoard/daysWishes.utils"
|
||||||
|
import DayWishesForm, {
|
||||||
|
fetchFor as fetchForDayWishesForm,
|
||||||
|
} from "../VolunteerBoard/DayWishesForm/DayWishesForm"
|
||||||
|
|
||||||
|
export function AskDayWishes(asks: JSX.Element[], id: number): void {
|
||||||
|
const { dispatch, jwtToken, volunteerAsks } = useAskTools()
|
||||||
|
|
||||||
|
const onSubmit = useCallback((): void => {
|
||||||
|
dispatch(
|
||||||
|
fetchVolunteerAsksSet(jwtToken, 0, {
|
||||||
|
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}, [dispatch, id, jwtToken, volunteerAsks?.hiddenAsks])
|
||||||
|
|
||||||
|
const [userWishes] = useUserDayWishes()
|
||||||
|
const participation = get(userWishes, "active", "inconnu")
|
||||||
|
const newSelection = get(userWishes, "dayWishes", [])
|
||||||
|
const comment = get(userWishes, "dayWishesComment", "")
|
||||||
|
const needToShow = participation === "inconnu" || (newSelection.length === 0 && !comment)
|
||||||
|
|
||||||
|
addAsk(
|
||||||
|
asks,
|
||||||
|
id,
|
||||||
|
volunteerAsks,
|
||||||
|
false,
|
||||||
|
needToShow,
|
||||||
|
<DayWishesForm afterSubmit={onSubmit}>{answerLaterOnProfile}</DayWishesForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch server-side data here
|
||||||
|
export const fetchFor = [...fetchForDayWishesForm]
|
41
src/components/Asks/AskGazette.tsx
Normal file
41
src/components/Asks/AskGazette.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { useCallback } from "react"
|
||||||
|
import classnames from "classnames"
|
||||||
|
import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
import { useAskTools, addAsk } from "./utils"
|
||||||
|
import FormButton from "../Form/FormButton/FormButton"
|
||||||
|
|
||||||
|
export function AskWelcome(asks: JSX.Element[], id: number): void {
|
||||||
|
const { dispatch, jwtToken, volunteerAsks } = useAskTools()
|
||||||
|
|
||||||
|
const onSubmit = useCallback((): void => {
|
||||||
|
dispatch(
|
||||||
|
fetchVolunteerAsksSet(jwtToken, 0, {
|
||||||
|
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}, [dispatch, id, jwtToken, volunteerAsks?.hiddenAsks])
|
||||||
|
|
||||||
|
addAsk(
|
||||||
|
asks,
|
||||||
|
id,
|
||||||
|
volunteerAsks,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
<form>
|
||||||
|
<div className={classnames(styles.notifIntro, styles.notifCentered)} key="login-intro">
|
||||||
|
La{" "}
|
||||||
|
<a
|
||||||
|
href="https://mailchi.mp/3c75c3b3a20f/gazette_2020_02-8978118"
|
||||||
|
onClick={onSubmit}
|
||||||
|
>
|
||||||
|
gazette de février
|
||||||
|
</a>{" "}
|
||||||
|
est disponible !<br />
|
||||||
|
<div className={styles.formButtons}>
|
||||||
|
<FormButton onClick={onSubmit}>Ok, masquer</FormButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
39
src/components/Asks/AskParticipationDetails.tsx
Normal file
39
src/components/Asks/AskParticipationDetails.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { get } from "lodash"
|
||||||
|
import { useCallback } from "react"
|
||||||
|
import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
|
||||||
|
import { useAskTools, addAsk, answerLaterOnProfile } from "./utils"
|
||||||
|
import ParticipationDetailsForm, {
|
||||||
|
fetchFor as fetchForParticipationDetailsForm,
|
||||||
|
} from "../VolunteerBoard/ParticipationDetailsForm/ParticipationDetailsForm"
|
||||||
|
import { useUserParticipationDetails } from "../VolunteerBoard/participationDetails.utils"
|
||||||
|
|
||||||
|
export function AskParticipationDetails(asks: JSX.Element[], id: number): void {
|
||||||
|
const { dispatch, jwtToken, volunteerAsks } = useAskTools()
|
||||||
|
|
||||||
|
const onSubmit = useCallback((): void => {
|
||||||
|
dispatch(
|
||||||
|
fetchVolunteerAsksSet(jwtToken, 0, {
|
||||||
|
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}, [dispatch, id, jwtToken, volunteerAsks?.hiddenAsks])
|
||||||
|
|
||||||
|
const [participationDetails] = useUserParticipationDetails()
|
||||||
|
const tshirtSize = get(participationDetails, "tshirtSize", "")
|
||||||
|
const food = get(participationDetails, "food", "")
|
||||||
|
const needToShow = !tshirtSize && !food
|
||||||
|
|
||||||
|
addAsk(
|
||||||
|
asks,
|
||||||
|
id,
|
||||||
|
volunteerAsks,
|
||||||
|
false,
|
||||||
|
needToShow,
|
||||||
|
<ParticipationDetailsForm afterSubmit={onSubmit}>
|
||||||
|
{answerLaterOnProfile}
|
||||||
|
</ParticipationDetailsForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch server-side data here
|
||||||
|
export const fetchFor = [...fetchForParticipationDetailsForm]
|
229
src/components/Asks/AskPushNotif.tsx
Normal file
229
src/components/Asks/AskPushNotif.tsx
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import React, { useCallback, useEffect, useRef, useState } from "react"
|
||||||
|
import isNode from "detect-node"
|
||||||
|
import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
import { useAskTools, addAsk } from "./utils"
|
||||||
|
import FormButton from "../Form/FormButton/FormButton"
|
||||||
|
import { toastError, toastSuccess } from "../../store/utils"
|
||||||
|
|
||||||
|
export function AskPushNotif(asks: JSX.Element[], id: number): void {
|
||||||
|
const { dispatch, jwtToken, volunteerAsks } = useAskTools()
|
||||||
|
|
||||||
|
const [acceptsNotifs, setAcceptsNotifs] = useState("")
|
||||||
|
|
||||||
|
const mounted = useRef(false)
|
||||||
|
useEffect(() => {
|
||||||
|
if (mounted.current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mounted.current = true
|
||||||
|
if (!isNode) {
|
||||||
|
if (volunteerAsks?.acceptsNotifs === "oui") {
|
||||||
|
navigator.serviceWorker.ready.then((registration) =>
|
||||||
|
registration.pushManager.getSubscription().then((existedSubscription) => {
|
||||||
|
const doesAcceptNotifs =
|
||||||
|
_.isEqual(
|
||||||
|
JSON.parse(JSON.stringify(existedSubscription)),
|
||||||
|
JSON.parse(volunteerAsks?.pushNotifSubscription)
|
||||||
|
) && volunteerAsks?.acceptsNotifs === "oui"
|
||||||
|
setAcceptsNotifs(doesAcceptNotifs ? "oui" : "non")
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
setAcceptsNotifs("non")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onChangeValue = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
setAcceptsNotifs(e.target.value)
|
||||||
|
|
||||||
|
const onSubmit = useCallback(async (): Promise<void> => {
|
||||||
|
if (isNode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("serviceWorker" in navigator)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acceptsNotifs === "non") {
|
||||||
|
dispatch(
|
||||||
|
fetchVolunteerAsksSet(jwtToken, 0, {
|
||||||
|
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
||||||
|
acceptsNotifs: "non",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const registration = await navigator.serviceWorker.ready
|
||||||
|
|
||||||
|
if (!registration.pushManager) {
|
||||||
|
toastError(
|
||||||
|
"Il y a un problème avec le push manager. Il faudrait utiliser un navigateur plus récent !"
|
||||||
|
)
|
||||||
|
dispatch(
|
||||||
|
fetchVolunteerAsksSet(jwtToken, 0, {
|
||||||
|
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const convertedVapidKey = urlBase64ToUint8Array(process.env.FORCE_ORANGE_PUBLIC_VAPID_KEY)
|
||||||
|
|
||||||
|
function urlBase64ToUint8Array(base64String?: string) {
|
||||||
|
if (!base64String) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
const padding = "=".repeat((4 - (base64String.length % 4)) % 4)
|
||||||
|
// eslint-disable-next-line
|
||||||
|
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/")
|
||||||
|
|
||||||
|
const rawData = atob(base64)
|
||||||
|
const outputArray = new Uint8Array(rawData.length)
|
||||||
|
|
||||||
|
for (let i = 0; i < rawData.length; i += 1) {
|
||||||
|
outputArray[i] = rawData.charCodeAt(i)
|
||||||
|
}
|
||||||
|
return outputArray
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!convertedVapidKey) {
|
||||||
|
toastError("No convertedVapidKey available")
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const existedSubscription = await registration.pushManager.getSubscription()
|
||||||
|
|
||||||
|
if (existedSubscription === null) {
|
||||||
|
// No subscription detected, make a request
|
||||||
|
try {
|
||||||
|
const newSubscription = await registration.pushManager.subscribe({
|
||||||
|
applicationServerKey: convertedVapidKey,
|
||||||
|
userVisibleOnly: true,
|
||||||
|
})
|
||||||
|
// New subscription added
|
||||||
|
if (
|
||||||
|
volunteerAsks?.acceptsNotifs === "oui" &&
|
||||||
|
!subscriptionEqualsSave(
|
||||||
|
newSubscription,
|
||||||
|
volunteerAsks?.pushNotifSubscription
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
toastSuccess(
|
||||||
|
"Un autre navigateur était notifié, mais c'est maintenant celui-ci qui le sera."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
fetchVolunteerAsksSet(jwtToken, 0, {
|
||||||
|
pushNotifSubscription: JSON.stringify(newSubscription),
|
||||||
|
acceptsNotifs: "oui",
|
||||||
|
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} catch (_e) {
|
||||||
|
if (Notification.permission !== "granted") {
|
||||||
|
toastError(
|
||||||
|
"Mince tu as bloqué les notifications pour le site des bénévoles ! En haut juste à gauche de la barre d'adresse, il y a une icone de cadenas ou de message barré sur lequel cliquer pour annuler ce blocage.",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
toastError(
|
||||||
|
"Il y a eu une erreur avec l'enregistrement avec le Service Worker. Il faudrait utiliser un navigateur plus récent !"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Existed subscription detected
|
||||||
|
if (
|
||||||
|
volunteerAsks?.acceptsNotifs === "oui" &&
|
||||||
|
!subscriptionEqualsSave(
|
||||||
|
existedSubscription,
|
||||||
|
volunteerAsks?.pushNotifSubscription
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
toastSuccess(
|
||||||
|
"Un autre navigateur était notifié, mais c'est maintenant celui-ci qui le sera."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
fetchVolunteerAsksSet(jwtToken, 0, {
|
||||||
|
pushNotifSubscription: JSON.stringify(existedSubscription),
|
||||||
|
acceptsNotifs: "oui",
|
||||||
|
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (_e) {
|
||||||
|
toastError(
|
||||||
|
"Il y a eu une erreur avec l'enregistrement avec le Service Worker. Il faudrait utiliser un navigateur plus récent !"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
acceptsNotifs,
|
||||||
|
dispatch,
|
||||||
|
id,
|
||||||
|
jwtToken,
|
||||||
|
volunteerAsks?.acceptsNotifs,
|
||||||
|
volunteerAsks?.hiddenAsks,
|
||||||
|
volunteerAsks?.pushNotifSubscription,
|
||||||
|
])
|
||||||
|
|
||||||
|
function subscriptionEqualsSave(toCheck: PushSubscription, save: string | undefined): boolean {
|
||||||
|
if (!save) {
|
||||||
|
return !toCheck
|
||||||
|
}
|
||||||
|
return _.isEqual(JSON.parse(JSON.stringify(toCheck)), JSON.parse(save))
|
||||||
|
}
|
||||||
|
|
||||||
|
const needToShow =
|
||||||
|
volunteerAsks?.acceptsNotifs !== "oui" && volunteerAsks?.acceptsNotifs !== "non"
|
||||||
|
|
||||||
|
addAsk(
|
||||||
|
asks,
|
||||||
|
id,
|
||||||
|
volunteerAsks,
|
||||||
|
true,
|
||||||
|
needToShow,
|
||||||
|
<div className={styles.formLine} key="line-participation">
|
||||||
|
<label>
|
||||||
|
Acceptes-tu de recevoir une alerte dans ton navigateur quand on en aura
|
||||||
|
d'autres à t'afficher ici ?<br />
|
||||||
|
<span className={styles.sousMessage}>
|
||||||
|
(Ça nous simplifierait la vie, on a des soucis à contacter les bénévoles par
|
||||||
|
email.)
|
||||||
|
</span>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value="oui"
|
||||||
|
name="notifs"
|
||||||
|
checked={acceptsNotifs === "oui"}
|
||||||
|
onChange={onChangeValue}
|
||||||
|
/>{" "}
|
||||||
|
Oui
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value="non"
|
||||||
|
name="notifs"
|
||||||
|
checked={acceptsNotifs === "non"}
|
||||||
|
onChange={onChangeValue}
|
||||||
|
/>{" "}
|
||||||
|
Non
|
||||||
|
</label>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className={styles.formButtons}>
|
||||||
|
<FormButton onClick={onSubmit}>Enregistrer</FormButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
37
src/components/Asks/AskTeamWishes.tsx
Normal file
37
src/components/Asks/AskTeamWishes.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { get } from "lodash"
|
||||||
|
import { useCallback } from "react"
|
||||||
|
import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
|
||||||
|
import { useAskTools, addAsk, answerLaterOnProfile } from "./utils"
|
||||||
|
import { useUserTeamWishes } from "../VolunteerBoard/teamWishes.utils"
|
||||||
|
import TeamWishesForm, {
|
||||||
|
fetchFor as fetchForTeamWishesForm,
|
||||||
|
} from "../VolunteerBoard/TeamWishesForm/TeamWishesForm"
|
||||||
|
|
||||||
|
export function AskTeamWishes(asks: JSX.Element[], id: number): void {
|
||||||
|
const { dispatch, jwtToken, volunteerAsks } = useAskTools()
|
||||||
|
|
||||||
|
const onSubmit = useCallback((): void => {
|
||||||
|
dispatch(
|
||||||
|
fetchVolunteerAsksSet(jwtToken, 0, {
|
||||||
|
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}, [dispatch, id, jwtToken, volunteerAsks?.hiddenAsks])
|
||||||
|
|
||||||
|
const [teamWishesData] = useUserTeamWishes()
|
||||||
|
const teamWishesString = get(teamWishesData, "teamWishes", [])
|
||||||
|
const comment = get(teamWishesData, "teamWishesComment", "")
|
||||||
|
const needToShow = teamWishesString.length === 0 && !comment
|
||||||
|
|
||||||
|
addAsk(
|
||||||
|
asks,
|
||||||
|
id,
|
||||||
|
volunteerAsks,
|
||||||
|
false,
|
||||||
|
needToShow,
|
||||||
|
<TeamWishesForm afterSubmit={onSubmit}>{answerLaterOnProfile}</TeamWishesForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch server-side data here
|
||||||
|
export const fetchFor = [...fetchForTeamWishesForm]
|
35
src/components/Asks/AskWelcome.tsx
Normal file
35
src/components/Asks/AskWelcome.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { useCallback } from "react"
|
||||||
|
import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
import { useAskTools, addAsk } from "./utils"
|
||||||
|
import FormButton from "../Form/FormButton/FormButton"
|
||||||
|
|
||||||
|
export function AskWelcome(asks: JSX.Element[], id: number): void {
|
||||||
|
const { dispatch, jwtToken, volunteerAsks } = useAskTools()
|
||||||
|
|
||||||
|
const onSubmit = useCallback((): void => {
|
||||||
|
dispatch(
|
||||||
|
fetchVolunteerAsksSet(jwtToken, 0, {
|
||||||
|
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}, [dispatch, id, jwtToken, volunteerAsks?.hiddenAsks])
|
||||||
|
|
||||||
|
addAsk(
|
||||||
|
asks,
|
||||||
|
id,
|
||||||
|
volunteerAsks,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
<form>
|
||||||
|
Salut {volunteerAsks?.firstname} !
|
||||||
|
<div className={styles.notifIntro} key="login-intro">
|
||||||
|
Ici tu seras notifié(e) des nouvelles importantes et des questions pour lesquelles
|
||||||
|
il nous faudrait absolument ta réponse.
|
||||||
|
<div className={styles.formButtons}>
|
||||||
|
<FormButton onClick={onSubmit}>Ok, continuer</FormButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
60
src/components/Asks/index.tsx
Normal file
60
src/components/Asks/index.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import React, { memo } from "react"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
import { useAskTools } from "./utils"
|
||||||
|
import { AskWelcome } from "./AskWelcome"
|
||||||
|
import { AskPushNotif } from "./AskPushNotif"
|
||||||
|
import { AskDayWishes, fetchFor as fetchForDayWishes } from "./AskDayWishes"
|
||||||
|
import { AskTeamWishes, fetchFor as fetchForTeamWishes } from "./AskTeamWishes"
|
||||||
|
import {
|
||||||
|
AskParticipationDetails,
|
||||||
|
fetchFor as fetchForParticipationDetails,
|
||||||
|
} from "./AskParticipationDetails"
|
||||||
|
|
||||||
|
const Asks = (): JSX.Element | null => {
|
||||||
|
const { volunteerAsks } = useAskTools()
|
||||||
|
const asks: JSX.Element[] = []
|
||||||
|
|
||||||
|
AskWelcome(asks, 1)
|
||||||
|
|
||||||
|
AskDayWishes(asks, 10)
|
||||||
|
AskTeamWishes(asks, 11)
|
||||||
|
AskParticipationDetails(asks, 12)
|
||||||
|
|
||||||
|
AskPushNotif(asks, 99)
|
||||||
|
|
||||||
|
if (_.isEmpty(asks)) {
|
||||||
|
asks.push(
|
||||||
|
<div key="pushNotifs">
|
||||||
|
<div className={styles.notificationsPage}>
|
||||||
|
<div className={styles.notificationsContent}>
|
||||||
|
<div className={styles.formLine} key="line-participation">
|
||||||
|
<label>
|
||||||
|
Tu as fait le tour des dernières infos ou questions importantes,
|
||||||
|
merci ! :)
|
||||||
|
<br />
|
||||||
|
Nous te préviendrons quand il y en aura de nouvelles.
|
||||||
|
<br />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (volunteerAsks === undefined) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div>{asks.map<React.ReactNode>((t) => t).reduce((prev, curr) => [prev, curr])}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Asks)
|
||||||
|
|
||||||
|
// Fetch server-side data here
|
||||||
|
export const fetchFor = [
|
||||||
|
...fetchForDayWishes,
|
||||||
|
...fetchForTeamWishes,
|
||||||
|
...fetchForParticipationDetails,
|
||||||
|
]
|
63
src/components/Asks/utils.tsx
Normal file
63
src/components/Asks/utils.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import { Dispatch } from "react"
|
||||||
|
import { shallowEqual, useDispatch, useSelector } from "react-redux"
|
||||||
|
import { VolunteerAsks } from "../../services/volunteers"
|
||||||
|
import { AppState } from "../../store"
|
||||||
|
import { selectUserJwtToken } from "../../store/auth"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
|
||||||
|
let prevVolunteerAsks: VolunteerAsks | undefined
|
||||||
|
|
||||||
|
type AskTools = {
|
||||||
|
dispatch: Dispatch<any>
|
||||||
|
jwtToken: string
|
||||||
|
volunteerAsks: VolunteerAsks | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAskTools(): AskTools {
|
||||||
|
const dispatch = useDispatch()
|
||||||
|
const jwtToken = useSelector(selectUserJwtToken)
|
||||||
|
const volunteerAsks = useSelector((state: AppState) => {
|
||||||
|
const vAsks = state.volunteerAsksSet?.entity
|
||||||
|
if (vAsks) {
|
||||||
|
prevVolunteerAsks = vAsks
|
||||||
|
return vAsks
|
||||||
|
}
|
||||||
|
return prevVolunteerAsks
|
||||||
|
}, shallowEqual)
|
||||||
|
return { dispatch, jwtToken, volunteerAsks }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addAsk(
|
||||||
|
asks: JSX.Element[],
|
||||||
|
id: number,
|
||||||
|
volunteerAsks: VolunteerAsks | undefined,
|
||||||
|
isNarrow: boolean,
|
||||||
|
needToShow: boolean,
|
||||||
|
children: JSX.Element
|
||||||
|
): void {
|
||||||
|
const hidden = volunteerAsks?.hiddenAsks || []
|
||||||
|
if (_.includes(hidden, id) || !_.isEmpty(asks) || !needToShow) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
asks.push(
|
||||||
|
<div key={id}>
|
||||||
|
<div className={styles.notificationsPage}>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
isNarrow ? styles.notificationsContentNarrow : styles.notificationsContent
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const answerLaterOnProfile = (
|
||||||
|
<>
|
||||||
|
Tu pourras y répondre plus tard sur la page <a href="/profil">Mon profil</a>.
|
||||||
|
</>
|
||||||
|
)
|
@ -1,5 +1,4 @@
|
|||||||
import React, { memo, useCallback } from "react"
|
import React, { memo, useCallback } from "react"
|
||||||
import { Link } from "react-router-dom"
|
|
||||||
import { AppDispatch } from "../../store"
|
import { AppDispatch } from "../../store"
|
||||||
import { fetchVolunteerForgot } from "../../store/volunteerForgot"
|
import { fetchVolunteerForgot } from "../../store/volunteerForgot"
|
||||||
import styles from "./styles.module.scss"
|
import styles from "./styles.module.scss"
|
||||||
@ -40,7 +39,7 @@ const ForgotForm = ({ dispatch, error, message }: Props): JSX.Element => {
|
|||||||
<div className={styles.error}>{error}</div>
|
<div className={styles.error}>{error}</div>
|
||||||
<div className={styles.message}>{message}</div>
|
<div className={styles.message}>{message}</div>
|
||||||
<div className={styles.link}>
|
<div className={styles.link}>
|
||||||
<Link to="/"> S'identifier </Link>
|
<a href="/"> S'identifier </a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { memo, useCallback } from "react"
|
import React, { memo, useCallback } from "react"
|
||||||
import { Link } from "react-router-dom"
|
|
||||||
import { shallowEqual, useDispatch, useSelector } from "react-redux"
|
import { shallowEqual, useDispatch, useSelector } from "react-redux"
|
||||||
import { AppState } from "../../store"
|
import { AppState } from "../../store"
|
||||||
import { fetchVolunteerLogin } from "../../store/volunteerLogin"
|
import { fetchVolunteerLogin } from "../../store/volunteerLogin"
|
||||||
@ -43,7 +42,7 @@ const LoginForm = (): JSX.Element => {
|
|||||||
</div>
|
</div>
|
||||||
{loginError && <div className={styles.error}>{loginError}</div>}
|
{loginError && <div className={styles.error}>{loginError}</div>}
|
||||||
<div className={styles.link}>
|
<div className={styles.link}>
|
||||||
<Link to="/oubli"> Demander un nouveau mot de passe </Link>
|
<a href="/oubli"> Demander un nouveau mot de passe </a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
|
@ -37,7 +37,7 @@ const MainMenu: FC = (): JSX.Element | null => {
|
|||||||
<ul className={classnames(styles.mainMenu, opened && styles.opened)}>
|
<ul className={classnames(styles.mainMenu, opened && styles.opened)}>
|
||||||
{createMenuItem("Questions", "/")}
|
{createMenuItem("Questions", "/")}
|
||||||
{createMenuItem("Annonces", "/annonces")}
|
{createMenuItem("Annonces", "/annonces")}
|
||||||
createMenuItem("Mon profil", "/profil")
|
{createMenuItem("Mon profil", "/profil")}
|
||||||
<button type="button" className={styles.close} onClick={onClose}>
|
<button type="button" className={styles.close} onClick={onClose}>
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,546 +0,0 @@
|
|||||||
import _, { get } from "lodash"
|
|
||||||
import React, { memo, useCallback, useEffect, useRef, useState } from "react"
|
|
||||||
import isNode from "detect-node"
|
|
||||||
import { shallowEqual, useDispatch, useSelector } from "react-redux"
|
|
||||||
import { fetchVolunteerNotifsSet } from "../../store/volunteerNotifsSet"
|
|
||||||
import styles from "./styles.module.scss"
|
|
||||||
import { selectUserJwtToken } from "../../store/auth"
|
|
||||||
import { VolunteerNotifs } from "../../services/volunteers"
|
|
||||||
import TeamWishesForm, {
|
|
||||||
fetchFor as fetchForTeamWishesForm,
|
|
||||||
} from "../VolunteerBoard/TeamWishesForm/TeamWishesForm"
|
|
||||||
import { AppState } from "../../store"
|
|
||||||
import { useUserTeamWishes } from "../VolunteerBoard/teamWishes.utils"
|
|
||||||
import { useUserDayWishes } from "../VolunteerBoard/daysWishes.utils"
|
|
||||||
import ParticipationDetailsForm, {
|
|
||||||
fetchFor as fetchForarticipationDetailsForm,
|
|
||||||
} from "../VolunteerBoard/ParticipationDetailsForm/ParticipationDetailsForm"
|
|
||||||
import DayWishesForm, {
|
|
||||||
fetchFor as fetchForDayWishesForm,
|
|
||||||
} from "../VolunteerBoard/DayWishesForm/DayWishesForm"
|
|
||||||
import { useUserParticipationDetails } from "../VolunteerBoard/participationDetails.utils"
|
|
||||||
import FormButton from "../Form/FormButton/FormButton"
|
|
||||||
import { toastError, toastSuccess } from "../../store/utils"
|
|
||||||
|
|
||||||
let prevNotifs: VolunteerNotifs | undefined
|
|
||||||
|
|
||||||
const Notifications = (): JSX.Element | null => {
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
const jwtToken = useSelector(selectUserJwtToken)
|
|
||||||
const volunteerNotifs = useSelector((state: AppState) => {
|
|
||||||
const notifs = state.volunteerNotifsSet?.entity
|
|
||||||
if (notifs) {
|
|
||||||
prevNotifs = notifs
|
|
||||||
return notifs
|
|
||||||
}
|
|
||||||
return prevNotifs
|
|
||||||
}, shallowEqual)
|
|
||||||
const hidden = volunteerNotifs?.hiddenNotifs || []
|
|
||||||
const notifs: JSX.Element[] = []
|
|
||||||
|
|
||||||
const onSubmit1 = useCallback((): void => {
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerNotifsSet(jwtToken, 0, {
|
|
||||||
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 1],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}, [dispatch, jwtToken, volunteerNotifs])
|
|
||||||
|
|
||||||
if (!_.includes(hidden, 1)) {
|
|
||||||
notifs.push(
|
|
||||||
<div key="1">
|
|
||||||
<div className={styles.notificationsPage}>
|
|
||||||
<div className={styles.notificationsContentNarrow}>
|
|
||||||
<form>
|
|
||||||
Salut {volunteerNotifs?.firstname} !
|
|
||||||
<div className={styles.notifIntro} key="login-intro">
|
|
||||||
Ici tu seras notifié(e) des nouvelles importantes et des questions
|
|
||||||
pour lesquelles il nous faudrait absolument ta réponse.
|
|
||||||
<div className={styles.formButtons}>
|
|
||||||
<FormButton onClick={onSubmit1}>Ok, continuer</FormButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// const [participation, setParticipation] = useState(volunteerNotifs?.active || "inconnu")
|
|
||||||
// const [participationMessage, setParticipationMessage] = useState("")
|
|
||||||
// const onChangeValue2 = (e: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
// setParticipation(e.target.value)
|
|
||||||
|
|
||||||
// const onSubmit2 = useCallback(
|
|
||||||
// (): void => {
|
|
||||||
// if (participation === "inconnu") {
|
|
||||||
// setParticipationMessage("Il nous faudrait une réponse ^^")
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// dispatch(
|
|
||||||
// fetchVolunteerNotifsSet(jwtToken, 0, {
|
|
||||||
// hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 2],
|
|
||||||
// active: participation,
|
|
||||||
// })
|
|
||||||
// )
|
|
||||||
// },
|
|
||||||
// [dispatch, jwtToken, volunteerNotifs, participation]
|
|
||||||
// )
|
|
||||||
|
|
||||||
// if (!_.includes(hidden, 2)) {
|
|
||||||
// notifs.push(
|
|
||||||
// <div key="2">
|
|
||||||
// <div className={styles.notificationsPage}>
|
|
||||||
// <div className={styles.notificationsContentNarrow}>
|
|
||||||
// <div className={styles.formLine} key="line-participation">
|
|
||||||
// <form>
|
|
||||||
// Si les conditions sanitaires te le permettent, souhaites-tu être
|
|
||||||
// bénévole à PeL 2022 ?<br />
|
|
||||||
// <label>
|
|
||||||
// <input
|
|
||||||
// type="radio"
|
|
||||||
// value="oui"
|
|
||||||
// name="participation"
|
|
||||||
// checked={participation === "oui"}
|
|
||||||
// onChange={onChangeValue2}
|
|
||||||
// />{" "}
|
|
||||||
// Oui
|
|
||||||
// </label>
|
|
||||||
// <label>
|
|
||||||
// <input
|
|
||||||
// type="radio"
|
|
||||||
// value="non"
|
|
||||||
// name="participation"
|
|
||||||
// checked={participation === "non"}
|
|
||||||
// onChange={onChangeValue2}
|
|
||||||
// />{" "}
|
|
||||||
// Non
|
|
||||||
// </label>
|
|
||||||
// <label>
|
|
||||||
// <input
|
|
||||||
// type="radio"
|
|
||||||
// value="peut-etre"
|
|
||||||
// name="participation"
|
|
||||||
// checked={participation === "peut-etre"}
|
|
||||||
// onChange={onChangeValue2}
|
|
||||||
// />{" "}
|
|
||||||
// Je ne sais pas encore
|
|
||||||
// </label>
|
|
||||||
// {participation === "peut-etre" ? (
|
|
||||||
// <div>
|
|
||||||
// On te le reproposera dans quelques temps.
|
|
||||||
// <br />
|
|
||||||
// Si tu as besoin d'infos, viens nous en parler sur le
|
|
||||||
// serveur Discord ! Pour le rejoindre,{" "}
|
|
||||||
// <a
|
|
||||||
// href="https://discord.com/invite/eXhjKxSBB4"
|
|
||||||
// target="_blank"
|
|
||||||
// rel="noreferrer"
|
|
||||||
// >
|
|
||||||
// clique ici{" "}
|
|
||||||
// </a>
|
|
||||||
// .
|
|
||||||
// </div>
|
|
||||||
// ) : null}
|
|
||||||
// <div className={styles.formButtons}>
|
|
||||||
// <FormButton onClick={onSubmit2}>Confirmer</FormButton>
|
|
||||||
// </div>
|
|
||||||
// <div className={styles.message}>{participationMessage}</div>
|
|
||||||
// </form>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const onSubmit3 = useCallback((): void => {
|
|
||||||
// dispatch(
|
|
||||||
// fetchVolunteerNotifsSet(jwtToken, 0, {
|
|
||||||
// hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 3],
|
|
||||||
// })
|
|
||||||
// )
|
|
||||||
// }, [dispatch, jwtToken, volunteerNotifs])
|
|
||||||
|
|
||||||
// if (!_.includes(hidden, 3)) {
|
|
||||||
// notifs.push(
|
|
||||||
// <div key="3">
|
|
||||||
// <div className={styles.notificationsPage}>
|
|
||||||
// <div className={styles.notificationsContentNarrow}>
|
|
||||||
// <form>
|
|
||||||
// <div
|
|
||||||
// className={classnames(styles.notifIntro, styles.notifCentered)}
|
|
||||||
// key="login-intro"
|
|
||||||
// >
|
|
||||||
// La{" "}
|
|
||||||
// <a
|
|
||||||
// href="https://mailchi.mp/3c75c3b3a20f/gazette_2020_02-8978118"
|
|
||||||
// onClick={onSubmit3}
|
|
||||||
// >
|
|
||||||
// gazette de février
|
|
||||||
// </a>{" "}
|
|
||||||
// est disponible !<br />
|
|
||||||
// <div className={styles.formButtons}>
|
|
||||||
// <FormButton onClick={onSubmit3}>Ok, masquer</FormButton>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// </form>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
const answerLaterOnProfile = (
|
|
||||||
<>
|
|
||||||
Tu pourras y répondre plus tard sur la page <a href="/profil">Mon profil</a>.
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
|
|
||||||
const onSubmit10 = useCallback((): void => {
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerNotifsSet(jwtToken, 0, {
|
|
||||||
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 10],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}, [dispatch, jwtToken, volunteerNotifs])
|
|
||||||
|
|
||||||
const [userWishes] = useUserDayWishes()
|
|
||||||
const participation10 = get(userWishes, "active", "inconnu")
|
|
||||||
const newSelection = get(userWishes, "dayWishes", [])
|
|
||||||
const comment10 = get(userWishes, "dayWishesComment", "")
|
|
||||||
const needToShow10 = participation10 === "inconnu" || (newSelection.length === 0 && !comment10)
|
|
||||||
|
|
||||||
if (!_.includes(hidden, 10) && _.isEmpty(notifs) && needToShow10) {
|
|
||||||
notifs.push(
|
|
||||||
<div key="10">
|
|
||||||
<div className={styles.notificationsPage}>
|
|
||||||
<div className={styles.notificationsContent}>
|
|
||||||
<DayWishesForm afterSubmit={onSubmit10}>
|
|
||||||
{answerLaterOnProfile}
|
|
||||||
</DayWishesForm>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSubmit11 = useCallback((): void => {
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerNotifsSet(jwtToken, 0, {
|
|
||||||
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 11],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}, [dispatch, jwtToken, volunteerNotifs])
|
|
||||||
|
|
||||||
const [teamWishesData] = useUserTeamWishes()
|
|
||||||
const teamWishesString = get(teamWishesData, "teamWishes", [])
|
|
||||||
const comment = get(teamWishesData, "teamWishesComment", "")
|
|
||||||
const needToShow11 = teamWishesString.length === 0 && !comment
|
|
||||||
|
|
||||||
if (!_.includes(hidden, 11) && _.isEmpty(notifs) && needToShow11) {
|
|
||||||
notifs.push(
|
|
||||||
<div key="11">
|
|
||||||
<div className={styles.notificationsPage}>
|
|
||||||
<div className={styles.notificationsContent}>
|
|
||||||
<TeamWishesForm afterSubmit={onSubmit11}>
|
|
||||||
{answerLaterOnProfile}
|
|
||||||
</TeamWishesForm>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSubmit12 = useCallback((): void => {
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerNotifsSet(jwtToken, 0, {
|
|
||||||
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 12],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}, [dispatch, jwtToken, volunteerNotifs])
|
|
||||||
|
|
||||||
const [participationDetails] = useUserParticipationDetails()
|
|
||||||
const tshirtSize = get(participationDetails, "tshirtSize", "")
|
|
||||||
const food = get(participationDetails, "food", "")
|
|
||||||
const needToShow12 = !tshirtSize && !food
|
|
||||||
|
|
||||||
if (!_.includes(hidden, 12) && _.isEmpty(notifs) && needToShow12) {
|
|
||||||
notifs.push(
|
|
||||||
<div key="12">
|
|
||||||
<div className={styles.notificationsPage}>
|
|
||||||
<div className={styles.notificationsContent}>
|
|
||||||
<ParticipationDetailsForm afterSubmit={onSubmit12}>
|
|
||||||
{answerLaterOnProfile}
|
|
||||||
</ParticipationDetailsForm>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* DISCORD
|
|
||||||
Discord nous donne à tous la parole via nos téléphone ou navigateurs, pour organiser le meilleur des festivals !
|
|
||||||
Il permet de discuter sujet par sujet entre tous les bénévoles, entre les membres d'une même équipe, ou avec ton référent.
|
|
||||||
Il permet de choisir les sujets spécifiques sur lesquels être notifié de nouveaux messages.
|
|
||||||
|
|
||||||
Rejoindre les 86 bénévoles déjà présents sur le serveur se fait en cliquant ici.
|
|
||||||
Tu n'y es absolument pas obligé(e) ! C'est juste plus pratique.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const [acceptsNotifs, setAcceptsNotifs] = useState("")
|
|
||||||
|
|
||||||
const mounted = useRef(false)
|
|
||||||
useEffect(() => {
|
|
||||||
if (mounted.current) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mounted.current = true
|
|
||||||
if (!isNode) {
|
|
||||||
if (volunteerNotifs?.acceptsNotifs === "oui") {
|
|
||||||
navigator.serviceWorker.ready.then((registration) =>
|
|
||||||
registration.pushManager.getSubscription().then((existedSubscription) => {
|
|
||||||
const doesAcceptNotifs =
|
|
||||||
_.isEqual(
|
|
||||||
JSON.parse(JSON.stringify(existedSubscription)),
|
|
||||||
JSON.parse(volunteerNotifs?.pushNotifSubscription)
|
|
||||||
) && volunteerNotifs?.acceptsNotifs === "oui"
|
|
||||||
setAcceptsNotifs(doesAcceptNotifs ? "oui" : "non")
|
|
||||||
})
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
setAcceptsNotifs("non")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const onChangeValue99 = (e: React.ChangeEvent<HTMLInputElement>) =>
|
|
||||||
setAcceptsNotifs(e.target.value)
|
|
||||||
|
|
||||||
const onSubmit99 = useCallback(async (): Promise<void> => {
|
|
||||||
if (isNode) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!("serviceWorker" in navigator)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (acceptsNotifs === "non") {
|
|
||||||
setAcceptsNotifs("non")
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerNotifsSet(jwtToken, 0, {
|
|
||||||
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 99],
|
|
||||||
acceptsNotifs: "non",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const registration = await navigator.serviceWorker.ready
|
|
||||||
|
|
||||||
if (!registration.pushManager) {
|
|
||||||
toastError(
|
|
||||||
"Il y a un problème avec le push manager. Il faudrait utiliser un navigateur plus récent !"
|
|
||||||
)
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerNotifsSet(jwtToken, 0, {
|
|
||||||
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 99],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const convertedVapidKey = urlBase64ToUint8Array(process.env.FORCE_ORANGE_PUBLIC_VAPID_KEY)
|
|
||||||
|
|
||||||
function urlBase64ToUint8Array(base64String?: string) {
|
|
||||||
if (!base64String) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
const padding = "=".repeat((4 - (base64String.length % 4)) % 4)
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/")
|
|
||||||
|
|
||||||
const rawData = atob(base64)
|
|
||||||
const outputArray = new Uint8Array(rawData.length)
|
|
||||||
|
|
||||||
for (let i = 0; i < rawData.length; i += 1) {
|
|
||||||
outputArray[i] = rawData.charCodeAt(i)
|
|
||||||
}
|
|
||||||
return outputArray
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!convertedVapidKey) {
|
|
||||||
toastError("No convertedVapidKey available")
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const existedSubscription = await registration.pushManager.getSubscription()
|
|
||||||
|
|
||||||
if (existedSubscription === null) {
|
|
||||||
// No subscription detected, make a request
|
|
||||||
try {
|
|
||||||
const newSubscription = await registration.pushManager.subscribe({
|
|
||||||
applicationServerKey: convertedVapidKey,
|
|
||||||
userVisibleOnly: true,
|
|
||||||
})
|
|
||||||
// New subscription added
|
|
||||||
if (
|
|
||||||
volunteerNotifs?.acceptsNotifs === "oui" &&
|
|
||||||
!subscriptionEqualsSave(
|
|
||||||
newSubscription,
|
|
||||||
volunteerNotifs?.pushNotifSubscription
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
toastSuccess(
|
|
||||||
"Un autre navigateur était notifié, mais c'est maintenant celui-ci qui le sera."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerNotifsSet(jwtToken, 0, {
|
|
||||||
pushNotifSubscription: JSON.stringify(newSubscription),
|
|
||||||
acceptsNotifs: "oui",
|
|
||||||
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 99],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
} catch (_e) {
|
|
||||||
if (Notification.permission !== "granted") {
|
|
||||||
toastError(
|
|
||||||
"Mince tu as bloqué les notifications pour le site des bénévoles ! En haut juste à gauche de la barre d'adresse, il y a une icone de cadenas ou de message barré sur lequel cliquer pour annuler ce blocage.",
|
|
||||||
false
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
toastError(
|
|
||||||
"Il y a eu une erreur avec l'enregistrement avec le Service Worker. Il faudrait utiliser un navigateur plus récent !"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Existed subscription detected
|
|
||||||
if (
|
|
||||||
volunteerNotifs?.acceptsNotifs === "oui" &&
|
|
||||||
!subscriptionEqualsSave(
|
|
||||||
existedSubscription,
|
|
||||||
volunteerNotifs?.pushNotifSubscription
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
toastSuccess(
|
|
||||||
"Un autre navigateur était notifié, mais c'est maintenant celui-ci qui le sera."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(
|
|
||||||
fetchVolunteerNotifsSet(jwtToken, 0, {
|
|
||||||
pushNotifSubscription: JSON.stringify(existedSubscription),
|
|
||||||
acceptsNotifs: "oui",
|
|
||||||
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 99],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (_e) {
|
|
||||||
toastError(
|
|
||||||
"Il y a eu une erreur avec l'enregistrement avec le Service Worker. Il faudrait utiliser un navigateur plus récent !"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
acceptsNotifs,
|
|
||||||
dispatch,
|
|
||||||
jwtToken,
|
|
||||||
volunteerNotifs?.acceptsNotifs,
|
|
||||||
volunteerNotifs?.hiddenNotifs,
|
|
||||||
volunteerNotifs?.pushNotifSubscription,
|
|
||||||
])
|
|
||||||
|
|
||||||
function subscriptionEqualsSave(toCheck: PushSubscription, save: string | undefined): boolean {
|
|
||||||
if (!save) {
|
|
||||||
return !toCheck
|
|
||||||
}
|
|
||||||
return _.isEqual(JSON.parse(JSON.stringify(toCheck)), JSON.parse(save))
|
|
||||||
}
|
|
||||||
|
|
||||||
const needToShow99 = volunteerNotifs?.acceptsNotifs !== "oui"
|
|
||||||
|
|
||||||
if (!_.includes(hidden, 99) && _.isEmpty(notifs) && needToShow99) {
|
|
||||||
notifs.push(
|
|
||||||
<div key="pushNotifs">
|
|
||||||
<div className={styles.notificationsPage}>
|
|
||||||
<div className={styles.notificationsContent}>
|
|
||||||
<div className={styles.formLine} key="line-participation">
|
|
||||||
<label>
|
|
||||||
Acceptes-tu de recevoir une alerte dans ton navigateur quand on en
|
|
||||||
aura d'autres à t'afficher ici ?<br />
|
|
||||||
<span className={styles.sousMessage}>
|
|
||||||
(Ça nous simplifierait la vie, on a des soucis à contacter les
|
|
||||||
bénévoles par email.)
|
|
||||||
</span>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value="oui"
|
|
||||||
name="gender"
|
|
||||||
checked={acceptsNotifs === "oui"}
|
|
||||||
onChange={onChangeValue99}
|
|
||||||
/>{" "}
|
|
||||||
Oui
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
value="non"
|
|
||||||
name="gender"
|
|
||||||
checked={acceptsNotifs === "non"}
|
|
||||||
onChange={onChangeValue99}
|
|
||||||
/>{" "}
|
|
||||||
Non
|
|
||||||
</label>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div className={styles.formButtons}>
|
|
||||||
<FormButton onClick={onSubmit99}>Enregistrer</FormButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_.isEmpty(notifs)) {
|
|
||||||
notifs.push(
|
|
||||||
<div key="pushNotifs">
|
|
||||||
<div className={styles.notificationsPage}>
|
|
||||||
<div className={styles.notificationsContent}>
|
|
||||||
<div className={styles.formLine} key="line-participation">
|
|
||||||
<label>
|
|
||||||
Tu as fait le tour des dernières infos ou questions importantes,
|
|
||||||
merci ! :)
|
|
||||||
<br />
|
|
||||||
Nous te préviendrons quand il y en aura de nouvelles.
|
|
||||||
<br />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (volunteerNotifs === undefined) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div>{notifs.map<React.ReactNode>((t) => t).reduce((prev, curr) => [prev, curr])}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(Notifications)
|
|
||||||
|
|
||||||
// Fetch server-side data here
|
|
||||||
export const fetchFor = [
|
|
||||||
...fetchForTeamWishesForm,
|
|
||||||
...fetchForarticipationDetailsForm,
|
|
||||||
...fetchForDayWishesForm,
|
|
||||||
]
|
|
@ -1,5 +1,4 @@
|
|||||||
import { memo } from "react"
|
import { memo } from "react"
|
||||||
import { Link } from "react-router-dom"
|
|
||||||
|
|
||||||
import { Volunteer } from "../../services/volunteers"
|
import { Volunteer } from "../../services/volunteers"
|
||||||
import styles from "./styles.module.scss"
|
import styles from "./styles.module.scss"
|
||||||
@ -14,9 +13,9 @@ const VolunteerList = ({ items }: Props) => (
|
|||||||
<ul>
|
<ul>
|
||||||
{items.map(({ id, lastname, firstname }) => (
|
{items.map(({ id, lastname, firstname }) => (
|
||||||
<li key={id}>
|
<li key={id}>
|
||||||
<Link to={`/Volunteer/${id}`}>
|
<a href={`/Volunteer/${id}`}>
|
||||||
<b>{firstname}</b> {lastname}
|
<b>{firstname}</b> {lastname}
|
||||||
</Link>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -7,7 +7,7 @@ import ErrorBoundary from "./ErrorBoundary"
|
|||||||
import GameList from "./GameList"
|
import GameList from "./GameList"
|
||||||
import Loading from "./Loading"
|
import Loading from "./Loading"
|
||||||
import LoginForm from "./LoginForm"
|
import LoginForm from "./LoginForm"
|
||||||
import Notifications, { fetchFor as fetchForNotifications } from "./Notifications"
|
import Asks, { fetchFor as fetchForAsks } from "./Asks"
|
||||||
import ParticipationDetailsForm, {
|
import ParticipationDetailsForm, {
|
||||||
fetchFor as fetchForParticipationDetailsForm,
|
fetchFor as fetchForParticipationDetailsForm,
|
||||||
} from "./VolunteerBoard/ParticipationDetailsForm/ParticipationDetailsForm"
|
} from "./VolunteerBoard/ParticipationDetailsForm/ParticipationDetailsForm"
|
||||||
@ -30,8 +30,8 @@ export {
|
|||||||
GameList,
|
GameList,
|
||||||
Loading,
|
Loading,
|
||||||
LoginForm,
|
LoginForm,
|
||||||
Notifications,
|
Asks,
|
||||||
fetchForNotifications,
|
fetchForAsks,
|
||||||
ParticipationDetailsForm,
|
ParticipationDetailsForm,
|
||||||
fetchForParticipationDetailsForm,
|
fetchForParticipationDetailsForm,
|
||||||
PreRegisterForm,
|
PreRegisterForm,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FC, memo } from "react"
|
import { FC, memo } from "react"
|
||||||
import * as _ from "lodash"
|
import * as _ from "lodash"
|
||||||
import { RouteComponentProps, Link } from "react-router-dom"
|
import { RouteComponentProps } from "react-router-dom"
|
||||||
import { useSelector, shallowEqual } from "react-redux"
|
import { useSelector, shallowEqual } from "react-redux"
|
||||||
import { Helmet } from "react-helmet"
|
import { Helmet } from "react-helmet"
|
||||||
import { EntityState } from "@reduxjs/toolkit"
|
import { EntityState } from "@reduxjs/toolkit"
|
||||||
@ -60,7 +60,7 @@ const AnnouncementsPage: FC<Props> = (): JSX.Element => {
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.announcements}>
|
<div className={styles.announcements}>
|
||||||
<div className={styles.navigationLink}>
|
<div className={styles.navigationLink}>
|
||||||
<Link to="/sinscrire"> S'informer sur le bénévolat </Link>
|
<a href="/sinscrire"> S'informer sur le bénévolat </a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { FC, memo } from "react"
|
import { FC, memo } from "react"
|
||||||
import { RouteComponentProps, Link } from "react-router-dom"
|
import { RouteComponentProps } from "react-router-dom"
|
||||||
import { useSelector } from "react-redux"
|
import { useSelector } from "react-redux"
|
||||||
import { Helmet } from "react-helmet"
|
import { Helmet } from "react-helmet"
|
||||||
|
|
||||||
import { AppThunk } from "../../store"
|
import { AppThunk } from "../../store"
|
||||||
import { fetchForNotifications, LoginForm, Notifications } from "../../components"
|
import { fetchForAsks, LoginForm, Asks } from "../../components"
|
||||||
import styles from "./styles.module.scss"
|
import styles from "./styles.module.scss"
|
||||||
import { fetchVolunteerNotifsSetIfNeed } from "../../store/volunteerNotifsSet"
|
import { fetchVolunteerAsksSetIfNeed } from "../../store/volunteerAsksSet"
|
||||||
import { selectUserJwtToken } from "../../store/auth"
|
import { selectUserJwtToken } from "../../store/auth"
|
||||||
|
|
||||||
export type Props = RouteComponentProps
|
export type Props = RouteComponentProps
|
||||||
@ -17,7 +17,7 @@ const HomePage: FC<Props> = (): JSX.Element => {
|
|||||||
if (jwtToken === undefined) return <p>Loading...</p>
|
if (jwtToken === undefined) return <p>Loading...</p>
|
||||||
|
|
||||||
if (jwtToken) {
|
if (jwtToken) {
|
||||||
return <Notifications />
|
return <Asks />
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -29,7 +29,7 @@ const HomePage: FC<Props> = (): JSX.Element => {
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.homePage}>
|
<div className={styles.homePage}>
|
||||||
<div className={styles.navigationLink}>
|
<div className={styles.navigationLink}>
|
||||||
<Link to="/sinscrire"> S'informer sur le bénévolat </Link>
|
<a href="/sinscrire"> S'informer sur le bénévolat </a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -38,8 +38,8 @@ const HomePage: FC<Props> = (): JSX.Element => {
|
|||||||
|
|
||||||
// Fetch server-side data here
|
// Fetch server-side data here
|
||||||
export const loadData = (): AppThunk[] => [
|
export const loadData = (): AppThunk[] => [
|
||||||
fetchVolunteerNotifsSetIfNeed(),
|
fetchVolunteerAsksSetIfNeed(),
|
||||||
...fetchForNotifications.map((f) => f()),
|
...fetchForAsks.map((f) => f()),
|
||||||
]
|
]
|
||||||
|
|
||||||
export default memo(HomePage)
|
export default memo(HomePage)
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
Volunteer,
|
Volunteer,
|
||||||
VolunteerWithoutId,
|
VolunteerWithoutId,
|
||||||
VolunteerLogin,
|
VolunteerLogin,
|
||||||
VolunteerNotifs,
|
VolunteerAsks,
|
||||||
VolunteerTeamWishes,
|
VolunteerTeamWishes,
|
||||||
translationVolunteer,
|
translationVolunteer,
|
||||||
VolunteerDayWishes,
|
VolunteerDayWishes,
|
||||||
@ -115,10 +115,10 @@ async function sendForgetEmail(email: string, password: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const volunteerNotifsSet = expressAccessor.set(async (list, body, id) => {
|
export const volunteerAsksSet = expressAccessor.set(async (list, body, id) => {
|
||||||
const requestedId = +body[0] || id
|
const requestedId = +body[0] || id
|
||||||
if (requestedId !== id && requestedId !== 0) {
|
if (requestedId !== id && requestedId !== 0) {
|
||||||
throw Error(`On ne peut acceder qu'à ses propres notifs`)
|
throw Error(`On ne peut acceder qu'à ses propres questions`)
|
||||||
}
|
}
|
||||||
const notifChanges = body[1]
|
const notifChanges = body[1]
|
||||||
const volunteer = list.find((v) => v.id === requestedId)
|
const volunteer = list.find((v) => v.id === requestedId)
|
||||||
@ -136,10 +136,10 @@ export const volunteerNotifsSet = expressAccessor.set(async (list, body, id) =>
|
|||||||
firstname: newVolunteer.firstname,
|
firstname: newVolunteer.firstname,
|
||||||
adult: newVolunteer.adult,
|
adult: newVolunteer.adult,
|
||||||
active: newVolunteer.active,
|
active: newVolunteer.active,
|
||||||
hiddenNotifs: newVolunteer.hiddenNotifs,
|
hiddenAsks: newVolunteer.hiddenAsks,
|
||||||
pushNotifSubscription: newVolunteer.pushNotifSubscription,
|
pushNotifSubscription: newVolunteer.pushNotifSubscription,
|
||||||
acceptsNotifs: newVolunteer.acceptsNotifs,
|
acceptsNotifs: newVolunteer.acceptsNotifs,
|
||||||
} as VolunteerNotifs,
|
} as VolunteerAsks,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ import {
|
|||||||
volunteerSet,
|
volunteerSet,
|
||||||
volunteerLogin,
|
volunteerLogin,
|
||||||
volunteerForgot,
|
volunteerForgot,
|
||||||
volunteerNotifsSet,
|
volunteerAsksSet,
|
||||||
volunteerParticipationDetailsSet,
|
volunteerParticipationDetailsSet,
|
||||||
volunteerTeamWishesSet,
|
volunteerTeamWishesSet,
|
||||||
volunteerDayWishesSet,
|
volunteerDayWishesSet,
|
||||||
@ -92,7 +92,7 @@ app.get("/AnnouncementListGet", secure as RequestHandler, announcementListGet)
|
|||||||
app.post("/VolunteerSet", secure as RequestHandler, volunteerSet)
|
app.post("/VolunteerSet", secure as RequestHandler, volunteerSet)
|
||||||
app.get("/TeamListGet", teamListGet)
|
app.get("/TeamListGet", teamListGet)
|
||||||
// UNSAFE app.post("/VolunteerGet", secure as RequestHandler, volunteerGet)
|
// UNSAFE app.post("/VolunteerGet", secure as RequestHandler, volunteerGet)
|
||||||
app.post("/VolunteerNotifsSet", secure as RequestHandler, volunteerNotifsSet)
|
app.post("/VolunteerAsksSet", secure as RequestHandler, volunteerAsksSet)
|
||||||
app.post(
|
app.post(
|
||||||
"/VolunteerParticipationDetailsSet",
|
"/VolunteerParticipationDetailsSet",
|
||||||
secure as RequestHandler,
|
secure as RequestHandler,
|
||||||
|
@ -33,7 +33,7 @@ export class Volunteer {
|
|||||||
|
|
||||||
teamWishesComment = ""
|
teamWishesComment = ""
|
||||||
|
|
||||||
hiddenNotifs: number[] = []
|
hiddenAsks: number[] = []
|
||||||
|
|
||||||
created = new Date()
|
created = new Date()
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ export const translationVolunteer: { [k in keyof Volunteer]: string } = {
|
|||||||
food: "alimentation",
|
food: "alimentation",
|
||||||
teamWishes: "enviesEquipe",
|
teamWishes: "enviesEquipe",
|
||||||
teamWishesComment: "commentaireEnviesEquipe",
|
teamWishesComment: "commentaireEnviesEquipe",
|
||||||
hiddenNotifs: "notifsCachees",
|
hiddenAsks: "questionsCachees",
|
||||||
created: "creation",
|
created: "creation",
|
||||||
password1: "passe1",
|
password1: "passe1",
|
||||||
password2: "passe2",
|
password2: "passe2",
|
||||||
@ -92,7 +92,7 @@ export const volunteerExample: Volunteer = {
|
|||||||
food: "Végétarien",
|
food: "Végétarien",
|
||||||
teamWishes: [],
|
teamWishes: [],
|
||||||
teamWishesComment: "",
|
teamWishesComment: "",
|
||||||
hiddenNotifs: [],
|
hiddenAsks: [],
|
||||||
created: new Date(0),
|
created: new Date(0),
|
||||||
password1: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
|
password1: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
|
||||||
password2: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
|
password2: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
|
||||||
@ -116,12 +116,12 @@ export interface VolunteerForgot {
|
|||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VolunteerNotifs {
|
export interface VolunteerAsks {
|
||||||
id: Volunteer["id"]
|
id: Volunteer["id"]
|
||||||
firstname: Volunteer["firstname"]
|
firstname: Volunteer["firstname"]
|
||||||
adult: Volunteer["adult"]
|
adult: Volunteer["adult"]
|
||||||
active: Volunteer["active"]
|
active: Volunteer["active"]
|
||||||
hiddenNotifs: Volunteer["hiddenNotifs"]
|
hiddenAsks: Volunteer["hiddenAsks"]
|
||||||
pushNotifSubscription: Volunteer["pushNotifSubscription"]
|
pushNotifSubscription: Volunteer["pushNotifSubscription"]
|
||||||
acceptsNotifs: Volunteer["acceptsNotifs"]
|
acceptsNotifs: Volunteer["acceptsNotifs"]
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import {
|
|||||||
elementName,
|
elementName,
|
||||||
Volunteer,
|
Volunteer,
|
||||||
VolunteerDayWishes,
|
VolunteerDayWishes,
|
||||||
VolunteerNotifs,
|
VolunteerAsks,
|
||||||
VolunteerParticipationDetails,
|
VolunteerParticipationDetails,
|
||||||
VolunteerTeamWishes,
|
VolunteerTeamWishes,
|
||||||
VolunteerWithoutId,
|
VolunteerWithoutId,
|
||||||
@ -21,8 +21,8 @@ export const volunteerLogin =
|
|||||||
|
|
||||||
export const volunteerForgot = serviceAccessors.customPost<[{ email: string }]>("Forgot")
|
export const volunteerForgot = serviceAccessors.customPost<[{ email: string }]>("Forgot")
|
||||||
|
|
||||||
export const volunteerNotifsSet =
|
export const volunteerAsksSet =
|
||||||
serviceAccessors.securedCustomPost<[number, Partial<VolunteerNotifs>]>("NotifsSet")
|
serviceAccessors.securedCustomPost<[number, Partial<VolunteerAsks>]>("AsksSet")
|
||||||
|
|
||||||
export const volunteerTeamWishesSet =
|
export const volunteerTeamWishesSet =
|
||||||
serviceAccessors.securedCustomPost<[number, Partial<VolunteerTeamWishes>]>("TeamWishesSet")
|
serviceAccessors.securedCustomPost<[number, Partial<VolunteerTeamWishes>]>("TeamWishesSet")
|
||||||
|
@ -14,7 +14,7 @@ import volunteerList from "./volunteerList"
|
|||||||
import volunteerSet from "./volunteerSet"
|
import volunteerSet from "./volunteerSet"
|
||||||
import volunteerLogin from "./volunteerLogin"
|
import volunteerLogin from "./volunteerLogin"
|
||||||
import volunteerForgot from "./volunteerForgot"
|
import volunteerForgot from "./volunteerForgot"
|
||||||
import volunteerNotifsSet from "./volunteerNotifsSet"
|
import volunteerAsksSet from "./volunteerAsksSet"
|
||||||
import volunteerParticipationDetailsSet from "./volunteerParticipationDetailsSet"
|
import volunteerParticipationDetailsSet from "./volunteerParticipationDetailsSet"
|
||||||
import volunteerDayWishesSet from "./volunteerDayWishesSet"
|
import volunteerDayWishesSet from "./volunteerDayWishesSet"
|
||||||
import volunteerTeamWishesSet from "./volunteerTeamWishesSet"
|
import volunteerTeamWishesSet from "./volunteerTeamWishesSet"
|
||||||
@ -37,7 +37,7 @@ export default (history: History) => ({
|
|||||||
volunteerSet,
|
volunteerSet,
|
||||||
volunteerLogin,
|
volunteerLogin,
|
||||||
volunteerForgot,
|
volunteerForgot,
|
||||||
volunteerNotifsSet,
|
volunteerAsksSet,
|
||||||
volunteerParticipationDetailsSet,
|
volunteerParticipationDetailsSet,
|
||||||
volunteerDayWishesSet,
|
volunteerDayWishesSet,
|
||||||
volunteerTeamWishesSet,
|
volunteerTeamWishesSet,
|
||||||
|
59
src/store/volunteerAsksSet.ts
Normal file
59
src/store/volunteerAsksSet.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
||||||
|
|
||||||
|
import { StateRequest, toastError, elementFetch } from "./utils"
|
||||||
|
import { VolunteerAsks } from "../services/volunteers"
|
||||||
|
import { AppThunk, AppState } from "."
|
||||||
|
import { volunteerAsksSet } from "../services/volunteersAccessors"
|
||||||
|
|
||||||
|
type StateVolunteerAsksSet = { entity?: VolunteerAsks } & StateRequest
|
||||||
|
|
||||||
|
export const initialState: StateVolunteerAsksSet = {
|
||||||
|
readyStatus: "idle",
|
||||||
|
}
|
||||||
|
|
||||||
|
const volunteerAsksSetSlice = createSlice({
|
||||||
|
name: "volunteerAsksSet",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
getRequesting: (_) => ({
|
||||||
|
readyStatus: "request",
|
||||||
|
}),
|
||||||
|
getSuccess: (_, { payload }: PayloadAction<VolunteerAsks>) => ({
|
||||||
|
readyStatus: "success",
|
||||||
|
entity: payload,
|
||||||
|
}),
|
||||||
|
getFailure: (_, { payload }: PayloadAction<string>) => ({
|
||||||
|
readyStatus: "failure",
|
||||||
|
error: payload,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default volunteerAsksSetSlice.reducer
|
||||||
|
export const { getRequesting, getSuccess, getFailure } = volunteerAsksSetSlice.actions
|
||||||
|
|
||||||
|
export const fetchVolunteerAsksSet = elementFetch(
|
||||||
|
volunteerAsksSet,
|
||||||
|
getRequesting,
|
||||||
|
getSuccess,
|
||||||
|
getFailure,
|
||||||
|
(error: Error) => toastError(`Erreur lors du chargement des notifications: ${error.message}`)
|
||||||
|
)
|
||||||
|
|
||||||
|
const shouldFetchVolunteerAsksSet = (state: AppState, id: number) =>
|
||||||
|
state.volunteerAsksSet?.readyStatus !== "success" ||
|
||||||
|
(state.volunteerAsksSet?.entity && state.volunteerAsksSet?.entity?.id !== id)
|
||||||
|
|
||||||
|
export const fetchVolunteerAsksSetIfNeed =
|
||||||
|
(id = 0, notif: Partial<VolunteerAsks> = {}): AppThunk =>
|
||||||
|
(dispatch, getState) => {
|
||||||
|
let jwt = ""
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
;({ jwt, id } = getState().auth)
|
||||||
|
}
|
||||||
|
if (shouldFetchVolunteerAsksSet(getState(), id))
|
||||||
|
return dispatch(fetchVolunteerAsksSet(jwt, id, notif))
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
@ -1,78 +0,0 @@
|
|||||||
import { PayloadAction, createSlice, createSelector } from "@reduxjs/toolkit"
|
|
||||||
import get from "lodash/get"
|
|
||||||
|
|
||||||
import { StateRequest, toastError, elementFetch } from "./utils"
|
|
||||||
import { VolunteerNotifs } from "../services/volunteers"
|
|
||||||
import { AppThunk, AppState } from "."
|
|
||||||
import { volunteerNotifsSet } from "../services/volunteersAccessors"
|
|
||||||
|
|
||||||
type StateVolunteerNotifsSet = { entity?: VolunteerNotifs } & StateRequest
|
|
||||||
|
|
||||||
export const initialState: StateVolunteerNotifsSet = {
|
|
||||||
readyStatus: "idle",
|
|
||||||
}
|
|
||||||
|
|
||||||
const volunteerNotifsSetSlice = createSlice({
|
|
||||||
name: "volunteerNotifsSet",
|
|
||||||
initialState,
|
|
||||||
reducers: {
|
|
||||||
getRequesting: (_) => ({
|
|
||||||
readyStatus: "request",
|
|
||||||
}),
|
|
||||||
getSuccess: (_, { payload }: PayloadAction<VolunteerNotifs>) => ({
|
|
||||||
readyStatus: "success",
|
|
||||||
entity: payload,
|
|
||||||
}),
|
|
||||||
getFailure: (_, { payload }: PayloadAction<string>) => ({
|
|
||||||
readyStatus: "failure",
|
|
||||||
error: payload,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
export default volunteerNotifsSetSlice.reducer
|
|
||||||
export const { getRequesting, getSuccess, getFailure } = volunteerNotifsSetSlice.actions
|
|
||||||
|
|
||||||
export const fetchVolunteerNotifsSet = elementFetch(
|
|
||||||
volunteerNotifsSet,
|
|
||||||
getRequesting,
|
|
||||||
getSuccess,
|
|
||||||
getFailure,
|
|
||||||
(error: Error) => toastError(`Erreur lors du chargement des notifications: ${error.message}`)
|
|
||||||
)
|
|
||||||
|
|
||||||
const shouldFetchVolunteerNotifsSet = (state: AppState, id: number) =>
|
|
||||||
state.volunteerNotifsSet?.readyStatus !== "success" ||
|
|
||||||
(state.volunteerNotifsSet?.entity && state.volunteerNotifsSet?.entity?.id !== id)
|
|
||||||
|
|
||||||
export const fetchVolunteerNotifsSetIfNeed =
|
|
||||||
(id = 0, notif: Partial<VolunteerNotifs> = {}): AppThunk =>
|
|
||||||
(dispatch, getState) => {
|
|
||||||
let jwt = ""
|
|
||||||
|
|
||||||
if (!id) {
|
|
||||||
;({ jwt, id } = getState().auth)
|
|
||||||
}
|
|
||||||
if (shouldFetchVolunteerNotifsSet(getState(), id))
|
|
||||||
return dispatch(fetchVolunteerNotifsSet(jwt, id, notif))
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const openedNotifsIds = [1, 2, 3]
|
|
||||||
|
|
||||||
export const selectVolunteerNotifsSetState = (state: AppState): StateVolunteerNotifsSet =>
|
|
||||||
state.volunteerNotifsSet
|
|
||||||
|
|
||||||
export const selectHiddenNotifs = createSelector(selectVolunteerNotifsSetState, (notifState) =>
|
|
||||||
get(notifState, "entity.hiddenNotifs", [])
|
|
||||||
)
|
|
||||||
|
|
||||||
export const selectWaitingsNotifs = createSelector(selectHiddenNotifs, (hidden) =>
|
|
||||||
openedNotifsIds.filter((id) => !hidden.find((hiddenId: number) => hiddenId === id))
|
|
||||||
)
|
|
||||||
|
|
||||||
export const hasWaitingNotifs = createSelector(
|
|
||||||
selectWaitingsNotifs,
|
|
||||||
(waiting) => waiting.length > 0
|
|
||||||
)
|
|
Loading…
x
Reference in New Issue
Block a user