Commit 0a3e12c5 authored by Marek Veselý's avatar Marek Veselý
Browse files

Merge branch 'main' into '293-change-highlighting-of-unread-emails'

# Conflicts:
#   CHANGELOG.md
parents 2e82afbd 66a23941
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
2024-05-07 - improve new email highlighting
2024-05-14 - improve new email highlighting
2024-05-07 - add the option to add not team-visible addresses to the recipients list
2024-05-07 - add exercise name, rework exercise panel add dialogs
2024-05-07 - add learning objectives page to the instructor view
2024-05-07 - change the tab icon to the INJECT logo
Compare caf6b1c1 to b0d99e75
Original line number Diff line number Diff line
Subproject commit caf6b1c1038b43f45124e4a9220e63d822a8e900
Subproject commit b0d99e75be6486619ae5abddcfc347c79c72f54a
+3 −0
Original line number Diff line number Diff line
query ValidateEmailAddress($exerciseId: ID!, $address: String!) {
  validateEmailAddress(exerciseId: $exerciseId, address: $address)
}
+49 −4
Original line number Diff line number Diff line
@@ -2,7 +2,9 @@ import { MenuItem, Tooltip } from '@blueprintjs/core'
import type { ItemRenderer } from '@blueprintjs/select'
import { MultiSelect } from '@blueprintjs/select'
import type { EmailParticipant } from '@inject/graphql/fragments/EmailParticipant.generated'
import type { Dispatch, FC, SetStateAction } from 'react'
import { useValidateEmailAddressLazyQuery } from '@inject/graphql/queries/ValidateEmailAddress.generated'
import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
import type { Dispatch, FC, MouseEventHandler, SetStateAction } from 'react'
import { memo, useCallback, useMemo } from 'react'
import type { ExtendedItemRenderer } from '../typing'

@@ -12,6 +14,7 @@ interface EmailContactSelectorProps {
  selectedContacts: string[]
  setSelectedContacts: Dispatch<SetStateAction<string[]>>
  inInstructor: boolean
  exerciseId: string
}

const EmailContactSelector: FC<EmailContactSelectorProps> = ({
@@ -20,12 +23,17 @@ const EmailContactSelector: FC<EmailContactSelectorProps> = ({
  senderAddress,
  setSelectedContacts,
  inInstructor,
  exerciseId,
}) => {
  const suggestedItems = useMemo(
    () => emailContacts.map(x => x?.address || '') || [],
    [emailContacts]
  )

  const [validateEmailAddress] = useValidateEmailAddressLazyQuery()

  const { notify } = useNotifyContext()

  const itemRenderer: ExtendedItemRenderer<string> = useCallback(
    (i, { handleClick, handleFocus, modifiers, hasTooltip }) => (
      <MenuItem
@@ -66,20 +74,55 @@ const EmailContactSelector: FC<EmailContactSelectorProps> = ({
    [selectedContacts, senderAddress]
  )
  const onItemSelect = useCallback(
    (i: string) =>
    async (i: string) => {
      const { data: validateData } = await validateEmailAddress({
        variables: {
          exerciseId,
          address: i,
        },
      })
      if (!validateData?.validateEmailAddress) {
        notify('Invalid email address', { intent: 'danger' })
        return
      }

      setSelectedContacts(prev => {
        if (prev.includes(i)) {
          return prev.filter(x => x !== i)
        }
        return [...prev, i]
      }),
    [setSelectedContacts]
      })
    },
    [exerciseId, notify, setSelectedContacts, validateEmailAddress]
  )
  const onRemove = useCallback(
    (i: string) => setSelectedContacts(prev => prev.filter(x => x !== i)),
    [setSelectedContacts]
  )

  const createNewItemFromQuery: (query: string) => string | string[] =
    useCallback((query: string) => query, [])
  const createNewItemRenderer: (
    query: string,
    active: boolean,
    handleClick: MouseEventHandler<HTMLElement>
  ) => undefined | JSX.Element = useCallback(
    (
      query: string,
      active: boolean,
      handleClick: MouseEventHandler<HTMLElement>
    ) => (
      <MenuItem
        active={active}
        onClick={handleClick}
        roleStructure='listoption'
        shouldDismissPopover={false}
        text={query}
      />
    ),
    []
  )

  return (
    <MultiSelect<string>
      placeholder='Recipients'
@@ -98,6 +141,8 @@ const EmailContactSelector: FC<EmailContactSelectorProps> = ({
      popoverContentProps={{
        style: { maxHeight: '50vh', overflowY: 'auto', overflowX: 'hidden' },
      }}
      createNewItemFromQuery={createNewItemFromQuery}
      createNewItemRenderer={createNewItemRenderer}
    />
  )
}
+1 −0
Original line number Diff line number Diff line
@@ -110,6 +110,7 @@ const TraineeEmailForm: FC<TraineeEmailFormProps> = ({
            })}
        contacts={traineeList}
        senderAddress={teamAddress}
        exerciseId={exerciseId}
      />
      <Divider style={{ margin: '0.5rem 0' }} />

Loading