Skip to content

Commit

Permalink
Merge pull request #1376 from SocialGouv/feat/2650-surbrillance-reche…
Browse files Browse the repository at this point in the history
…rche-message

feat(us-2650): surbrillance recherche message
  • Loading branch information
octo-theg committed Jun 17, 2024
2 parents 9654331 + b5fd435 commit 4cf8e97
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 78 deletions.
34 changes: 22 additions & 12 deletions clients/firebase.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
InfoOffre,
Message,
MessageListeDiffusion,
MessageRechercheMatch,
TypeMessage,
} from 'interfaces/message'
import { BaseOffre, TypeOffre } from 'interfaces/offre'
Expand Down Expand Up @@ -470,27 +471,36 @@ export async function rechercherMessages(
accessToken: string,
idBeneficiaire: string,
recherche: string
): Promise<Message[]> {
): Promise<
Array<{
message: Message
matches: MessageRechercheMatch[]
}>
> {
const {
content: { resultats },
} = await apiGet<{
resultats: Array<{
id: string
message: FirebaseMessage & { creationDate: { _seconds: number } }
matches: MessageRechercheMatch[]
}>
}>(`/jeunes/${idBeneficiaire}/messages?recherche=${recherche}`, accessToken)

return resultats.map(({ message, id }) =>
firebaseMessageToMessage(
{
...message,
creationDate: Timestamp.fromMillis(
message.creationDate._seconds * 1000
),
},
id
)
)
return resultats.map(({ message, id, matches }) => {
return {
matches,
message: firebaseMessageToMessage(
{
...message,
creationDate: Timestamp.fromMillis(
message.creationDate._seconds * 1000
),
},
id
),
}
})
}

export async function getMessagesPeriode(
Expand Down
35 changes: 31 additions & 4 deletions components/chat/DisplayMessageBeneficiaire.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ import { LienPieceJointe } from 'components/chat/LienPieceJointe'
import LienSessionMilo from 'components/chat/LienSessionMilo'
import TexteAvecLien from 'components/chat/TexteAvecLien'
import { SpinningLoader } from 'components/ui/SpinningLoader'
import { isDeleted, isEdited, Message, TypeMessage } from 'interfaces/message'
import {
isDeleted,
isEdited,
Message,
MessageRechercheMatch,
TypeMessage,
} from 'interfaces/message'
import { toFrenchTime, toLongMonthDate, toShortDate } from 'utils/date'

type MessageBeneficiaireProps = {
message: Message
beneficiaireNomComplet: string
highlight?: MessageRechercheMatch
}

type ResultatRechercheProps = MessageBeneficiaireProps & {
Expand Down Expand Up @@ -45,11 +52,21 @@ export default function DisplayMessageBeneficiaire(
{message.type === TypeMessage.MESSAGE_PJ &&
message.infoPiecesJointes &&
message.infoPiecesJointes.map((pj, key) => {
return <MessagePJ key={key} {...pj} />
return (
<MessagePJ key={key} {...pj} highlight={props.highlight} />
)
})}

{message.type !== TypeMessage.MESSAGE_PJ && (
<TexteAvecLien texte={message.content} lighten={true} />
<TexteAvecLien
texte={message.content}
lighten={true}
highlight={
props.highlight?.key === 'content'
? props.highlight
: undefined
}
/>
)}

{message.type === TypeMessage.MESSAGE_OFFRE &&
Expand Down Expand Up @@ -120,10 +137,12 @@ function MessagePJ({
id,
nom,
statut,
highlight,
}: {
id: string
nom: string
statut?: string
highlight?: MessageRechercheMatch
}) {
switch (statut) {
case 'valide':
Expand All @@ -134,7 +153,15 @@ function MessagePJ({
Celle-ci sera conservée 4 mois. Enregistrez la dans i-milo pour la
conserver de manière sécurisée.
</p>
<LienPieceJointe key={id} id={id} nom={nom} className='fill-blanc' />
<LienPieceJointe
key={id}
id={id}
nom={nom}
className='fill-blanc'
highlight={
highlight?.key === 'piecesJointes.nom' ? highlight : undefined
}
/>
</>
)
case 'non_valide':
Expand Down
21 changes: 19 additions & 2 deletions components/chat/DisplayMessageConseiller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import LienSessionMilo from 'components/chat/LienSessionMilo'
import TexteAvecLien from 'components/chat/TexteAvecLien'
import IconComponent, { IconName } from 'components/ui/IconComponent'
import { SpinningLoader } from 'components/ui/SpinningLoader'
import { isDeleted, isEdited, Message, TypeMessage } from 'interfaces/message'
import {
isDeleted,
isEdited,
Message,
MessageRechercheMatch,
TypeMessage,
} from 'interfaces/message'
import {
toFrenchDateTime,
toFrenchTime,
Expand All @@ -21,6 +27,7 @@ type Base = {
message: Message
conseillerNomComplet: string | undefined
isConseillerCourant: boolean
highlight?: MessageRechercheMatch
}

type ResultatRechercheProps = Base & {
Expand Down Expand Up @@ -124,7 +131,12 @@ function MessageConseiller(props: DisplayMessageConseillerProps) {
{isConseillerCourant ? 'Vous' : conseillerNomComplet}
</p>

<TexteAvecLien texte={message.content} />
<TexteAvecLien
texte={message.content}
highlight={
props.highlight?.key === 'content' ? props.highlight : undefined
}
/>

{message.type === TypeMessage.MESSAGE_OFFRE && message.infoOffre && (
<LienOffre infoOffre={message.infoOffre} isSentByConseiller={true} />
Expand Down Expand Up @@ -155,6 +167,11 @@ function MessageConseiller(props: DisplayMessageConseillerProps) {
id={id}
nom={nom}
className='fill-primary'
highlight={
props.highlight?.key === 'piecesJointes.nom'
? props.highlight
: undefined
}
/>
))}

Expand Down
17 changes: 16 additions & 1 deletion components/chat/LienPieceJointe.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
import parse from 'html-react-parser'
import React from 'react'

import IconComponent, { IconName } from 'components/ui/IconComponent'
import { MessageRechercheMatch } from 'interfaces/message'

export function LienPieceJointe({
id,
nom,
className,
highlight,
}: {
id: string
nom: string
className?: string
highlight?: MessageRechercheMatch
}) {
function surlignerTexte(texte: string) {
const indexDebut = highlight!.match[0]
const indexFin = highlight!.match[1] + 1

const debut = texte.slice(0, indexDebut)
const highlightedText = texte.slice(indexDebut, indexFin)
const fin = texte.slice(indexFin)

return parse(`${debut}<mark>${highlightedText}</mark>${fin}`)
}

return (
<div className='flex flex-row justify-end underline'>
<IconComponent
Expand All @@ -27,7 +42,7 @@ export function LienPieceJointe({
className='font-bold break-all'
>
<span className='sr-only'>Télécharger la pièce jointe </span>
{nom}
{highlight ? surlignerTexte(nom) : nom}
</a>
</div>
)
Expand Down
21 changes: 16 additions & 5 deletions components/chat/RechercheMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import IllustrationComponent, {
} from 'components/ui/IllustrationComponent'
import { ValueWithError } from 'components/ValueWithError'
import { JeuneChat } from 'interfaces/jeune'
import { fromConseiller, Message } from 'interfaces/message'
import {
fromConseiller,
Message,
MessageRechercheMatch,
} from 'interfaces/message'
import { useChatCredentials } from 'utils/chat/chatCredentialsContext'
import { useConseiller } from 'utils/conseiller/conseillerContext'

Expand All @@ -32,7 +36,7 @@ export function RechercheMessage({
const [conseiller] = useConseiller()

const [resultatsRecherche, setResultatsRecherche] = useState<
Message[] | undefined
Array<{ message: Message; matches: MessageRechercheMatch[] }> | undefined
>()
const [messageSelectionne, setMessageSelectionne] = useState<
Message | undefined
Expand Down Expand Up @@ -121,7 +125,9 @@ function RechercheMessageForm({
onResultat,
}: {
idJeuneChat: string
onResultat: (message: Message[]) => void
onResultat: (
messages: Array<{ message: Message; matches: MessageRechercheMatch[] }>
) => void
}) {
const chatCredentials = useChatCredentials()

Expand Down Expand Up @@ -194,7 +200,10 @@ function ResultatsRecherche({
getConseillerNomComplet,
onSelectionnerMessage,
}: {
resultatsRecherche: Message[]
resultatsRecherche: Array<{
message: Message
matches: MessageRechercheMatch[]
}>
beneficiaireNomComplet: string
idConseiller: string
getConseillerNomComplet: (message: Message) => string | undefined
Expand All @@ -210,14 +219,15 @@ function ResultatsRecherche({
</p>
{resultatsRecherche.length >= 1 && (
<ul className='p-4 overflow-y-auto'>
{resultatsRecherche.map((message, key) => (
{resultatsRecherche.map(({ message, matches }, key) => (
<Fragment key={key}>
{!fromConseiller(message) && (
<DisplayMessageBeneficiaire
message={message}
beneficiaireNomComplet={beneficiaireNomComplet}
estResultatDeRecherche={true}
onAllerAuMessage={() => onSelectionnerMessage(message)}
highlight={matches[0]}
/>
)}

Expand All @@ -229,6 +239,7 @@ function ResultatsRecherche({
isEnCoursDeModification={false}
estResultatDeRecherche={true}
onAllerAuMessage={() => onSelectionnerMessage(message)}
highlight={matches[0]}
/>
)}
</Fragment>
Expand Down
35 changes: 30 additions & 5 deletions components/chat/TexteAvecLien.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import parse, { domToReact } from 'html-react-parser'
import React from 'react'

import { MessageRechercheMatch } from 'interfaces/message'

type TexteAvecLienProps = {
texte: string
lighten?: boolean
highlight?: MessageRechercheMatch
}
export default function TexteAvecLien({ texte, lighten }: TexteAvecLienProps) {
export default function TexteAvecLien({
texte,
lighten,
highlight,
}: TexteAvecLienProps) {
function confirmationRedirectionLienExterne(
e: React.MouseEvent<HTMLAnchorElement>,
lien: string
Expand All @@ -16,8 +23,8 @@ export default function TexteAvecLien({ texte, lighten }: TexteAvecLienProps) {
}
}

function formateTexteAvecLien() {
const messageFormate = texte
function formateTexteAvecLien(texteAFormater: string) {
const messageFormate = texteAFormater
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.split(/\r?\n|\s+/)
Expand Down Expand Up @@ -70,6 +77,24 @@ export default function TexteAvecLien({ texte, lighten }: TexteAvecLienProps) {
return str.includes('http') || str.includes('https')
}

if (!detecteLien(texte)) return <p className='whitespace-pre-wrap'>{texte}</p>
return <>{formateTexteAvecLien()}</>
function surlignerTexte(texteASurligner: string) {
const coordDebut = highlight!.match[0]
const coordFin = highlight!.match[1] + 1

const debut = texteASurligner.slice(0, coordDebut)
const highlightedText = texteASurligner.slice(coordDebut, coordFin)
const fin = texteASurligner.slice(coordFin)

return `${debut}<mark>${highlightedText}</mark>${fin}`
}

let texteAAfficher = texte

if (highlight) {
texteAAfficher = surlignerTexte(texteAAfficher)
}

if (!detecteLien(texteAAfficher))
return parse(`<p className='whitespace-pre-wrap'>${texteAAfficher}</p>`)
return <>{formateTexteAvecLien(texteAAfficher)}</>
}
5 changes: 5 additions & 0 deletions interfaces/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export interface ByDay<T> {
messages: T[]
}

export type MessageRechercheMatch = {
match: [number, number]
key?: 'content' | 'piecesJointes.nom'
}

export interface ChatCredentials {
token: string
cleChiffrement: string
Expand Down
Loading

0 comments on commit 4cf8e97

Please sign in to comment.