272 lines
11 KiB
TypeScript
272 lines
11 KiB
TypeScript
"use client"
|
|
|
|
import { ChevronLeft, ChevronUp, ChevronDown, RefreshCw } from "lucide-react"
|
|
import { Button } from "@/components/ui/button"
|
|
import { cn } from "@/lib/utils"
|
|
import { MailFolderStackIndicator } from "@/components/gmail/mail-folder-stack-indicator"
|
|
import { mailNavVisitKey } from "@/lib/mail-folder-display"
|
|
import { MAIL_LIST_ROW_DIVIDER_CLASS } from "@/lib/mail-chrome-classes"
|
|
import {
|
|
PULL_HOLD_HEIGHT,
|
|
REFRESH_SPIN_CLASS,
|
|
} from "@/components/gmail/email-list/email-list-helpers"
|
|
import { EmailListRow } from "@/components/gmail/email-list/email-list-row"
|
|
import {
|
|
EmailListEmpty,
|
|
EmailListScheduledBanner,
|
|
} from "@/components/gmail/email-list/email-list-empty"
|
|
import { EmailListEmailViewPane } from "@/components/gmail/email-list/email-list-email-view-pane"
|
|
import type { EmailListData } from "@/components/gmail/email-list/hooks/use-email-list-data"
|
|
import type { EmailListLabels } from "@/components/gmail/email-list/hooks/use-email-list-labels"
|
|
import type { EmailListSelection } from "@/components/gmail/email-list/hooks/use-email-list-selection"
|
|
import type { EmailListReading } from "@/components/gmail/email-list/hooks/use-email-list-reading"
|
|
|
|
const MAIN_SCROLL_CLASS =
|
|
"min-h-0 flex-1 overflow-y-auto overflow-x-hidden border-0 bg-mail-surface shadow-none outline-none sm:rounded-b-2xl " +
|
|
"[scrollbar-color:#9aa0a6_#ffffff] [scrollbar-width:auto] " +
|
|
"[&::-webkit-scrollbar]:w-2.5 [&::-webkit-scrollbar]:border-0 [&::-webkit-scrollbar]:bg-white " +
|
|
"[&::-webkit-scrollbar-track]:border-0 [&::-webkit-scrollbar-track]:bg-white [&::-webkit-scrollbar-track]:shadow-none " +
|
|
"[&::-webkit-scrollbar-thumb]:rounded-none [&::-webkit-scrollbar-thumb]:border-0 [&::-webkit-scrollbar-thumb]:shadow-none " +
|
|
"[&::-webkit-scrollbar-thumb]:bg-[#9aa0a6] hover:[&::-webkit-scrollbar-thumb]:bg-[#5f6368] " +
|
|
"[&::-webkit-scrollbar-corner]:border-0 [&::-webkit-scrollbar-corner]:bg-white"
|
|
|
|
type EmailListBodyProps = {
|
|
data: EmailListData
|
|
labels: EmailListLabels
|
|
selection: EmailListSelection
|
|
reading: EmailListReading
|
|
onSelectFolder?: (folder: string) => void
|
|
}
|
|
|
|
export function EmailListBody({
|
|
data,
|
|
labels,
|
|
selection,
|
|
reading,
|
|
onSelectFolder,
|
|
}: EmailListBodyProps) {
|
|
const {
|
|
splitView,
|
|
isViewMode,
|
|
isSearchMode,
|
|
selectedFolder,
|
|
listToolbarMode,
|
|
isRefreshing,
|
|
listViewportRef,
|
|
pullContentRef,
|
|
pullIconRef,
|
|
displayListEmails,
|
|
listEmails,
|
|
inboxCategoryTabLabel,
|
|
sidebarNav,
|
|
searchParams,
|
|
} = data
|
|
|
|
const {
|
|
openEmail,
|
|
openMailIndex,
|
|
goBack,
|
|
goToPrev,
|
|
goToNext,
|
|
handleBreadcrumbNavigate,
|
|
handleCategoryInboxTabClick,
|
|
} = reading
|
|
|
|
const rowPropsBase = {
|
|
allEmails: data.allEmails,
|
|
emailById: data.emailById,
|
|
listMailIndex: data.listMailIndex,
|
|
listRowExtras: data.listRowExtras,
|
|
starredEmails: data.starredEmails,
|
|
importantEmails: data.importantEmails,
|
|
readOverrides: data.readOverrides,
|
|
conversationMode: data.conversationMode,
|
|
savedThreadReplyDrafts: data.savedThreadReplyDrafts,
|
|
selectedEmails: selection.selectedEmails,
|
|
selectedFolder: data.selectedFolder,
|
|
splitView: data.splitView,
|
|
openMailId: data.openMailId,
|
|
isXs: data.isXs,
|
|
isMd: data.isMd,
|
|
density: data.density,
|
|
mobileSelectionMode: selection.mobileSelectionMode,
|
|
touchListSwipeEnabled: selection.touchListSwipeEnabled,
|
|
openSwipeRowId: selection.openSwipeRowId,
|
|
setOpenSwipeRowId: selection.setOpenSwipeRowId,
|
|
listRowLabelBgByTextLower: data.listRowLabelBgByTextLower,
|
|
sidebarNav: data.sidebarNav,
|
|
rescheduleTarget: data.rescheduleTarget,
|
|
setRescheduleTarget: data.setRescheduleTarget,
|
|
rescheduleDismissTimeoutsRef: data.rescheduleDismissTimeoutsRef,
|
|
scheduleReschedulePopoverDismiss: data.scheduleReschedulePopoverDismiss,
|
|
rowContextMenuOpenedAtRef: selection.rowContextMenuOpenedAtRef,
|
|
contextMenuTargetIdsRef: selection.contextMenuTargetIdsRef,
|
|
lastSelectionAnchorIdRef: selection.lastSelectionAnchorIdRef,
|
|
setSelectedEmails: selection.setSelectedEmails,
|
|
setLabelPickerQuery: data.setLabelPickerQuery,
|
|
labelPickerQuery: data.labelPickerQuery,
|
|
catalogLabels: labels.catalogLabels,
|
|
resolveLabelVisual: labels.resolveLabelVisual,
|
|
getCatalogLabelPresence: labels.getCatalogLabelPresence,
|
|
toggleLabelOnEmails: labels.toggleLabelOnEmails,
|
|
addLabelToEmails: labels.addLabelToEmails,
|
|
moveTargets: data.moveTargets,
|
|
moveEmailsToTarget: labels.moveEmailsToTarget,
|
|
cmScheduledRescheduleValue: data.cmScheduledRescheduleValue,
|
|
setCmScheduledRescheduleValue: data.setCmScheduledRescheduleValue,
|
|
mailActions: data.mailActions,
|
|
setReadOverrides: data.setReadOverrides,
|
|
onSelectFolder,
|
|
toggleSelect: selection.toggleSelect,
|
|
handleRowCheckboxClickCapture: selection.handleRowCheckboxClickCapture,
|
|
handleRowActivate: reading.handleRowActivate,
|
|
startRowDrag: selection.startRowDrag,
|
|
archiveListRow: reading.archiveListRow,
|
|
deleteListRow: reading.deleteListRow,
|
|
toggleStar: selection.toggleStar,
|
|
toggleImportant: selection.toggleImportant,
|
|
openSwipeRowLabelSheet: selection.openSwipeRowLabelSheet,
|
|
handleNavigateToLabel: reading.handleNavigateToLabel,
|
|
handleCategoryInboxTabClick,
|
|
closeViewIfShowingEmail: reading.closeViewIfShowingEmail,
|
|
restoreSnoozedRowToMailbox: reading.restoreSnoozedRowToMailbox,
|
|
handleEditScheduledMail: data.handleEditScheduledMail,
|
|
requestArchiveScheduled: data.requestArchiveScheduled,
|
|
requestDeleteScheduled: data.requestDeleteScheduled,
|
|
requestToggleReadScheduled: data.requestToggleReadScheduled,
|
|
requestSnoozeScheduled: data.requestSnoozeScheduled,
|
|
requestRescheduleScheduled: data.requestRescheduleScheduled,
|
|
requestSendScheduledNow: data.requestSendScheduledNow,
|
|
requestSnoozeMailboxEmail: data.requestSnoozeMailboxEmail,
|
|
}
|
|
|
|
return (
|
|
<div className={cn("relative flex min-h-0 flex-1 flex-col")}>
|
|
<div
|
|
ref={listViewportRef}
|
|
className={cn(
|
|
"max-sm:pb-16",
|
|
!splitView && isViewMode && openEmail
|
|
? "relative flex min-h-0 flex-1 flex-col overflow-hidden"
|
|
: MAIN_SCROLL_CLASS,
|
|
"relative min-h-0 flex-1 overscroll-y-none"
|
|
)}
|
|
>
|
|
{listToolbarMode && (
|
|
<div
|
|
className="pointer-events-none absolute inset-x-0 top-0 z-10 flex items-center justify-center pt-2 sm:hidden"
|
|
style={{ height: PULL_HOLD_HEIGHT }}
|
|
aria-hidden
|
|
>
|
|
<RefreshCw
|
|
ref={pullIconRef}
|
|
className={cn(
|
|
"h-5 w-5 text-[#5f6368]",
|
|
isRefreshing && REFRESH_SPIN_CLASS
|
|
)}
|
|
style={{ opacity: 0 }}
|
|
/>
|
|
</div>
|
|
)}
|
|
<div
|
|
ref={pullContentRef}
|
|
className={cn(
|
|
!splitView && isViewMode && openEmail && "relative flex min-h-0 flex-1 flex-col ",
|
|
listToolbarMode && "max-sm:[transform:translateZ(0)]"
|
|
)}
|
|
>
|
|
{!splitView && isViewMode && openEmail ? (
|
|
<>
|
|
<div className="pointer-events-none absolute inset-x-0 top-0 z-30 flex items-center justify-between gap-2 px-3 py-2 sm:hidden">
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
className="pointer-events-auto size-9 shrink-0 rounded-full border border-gray-200 bg-white/80 text-[#444746] shadow-md backdrop-blur hover:bg-white"
|
|
aria-label="Retour à la boîte de réception"
|
|
onClick={goBack}
|
|
>
|
|
<ChevronLeft className="size-5" strokeWidth={1.5} />
|
|
</Button>
|
|
<div className="pointer-events-auto flex shrink-0 overflow-hidden rounded-full border border-gray-200 bg-white/80 shadow-md backdrop-blur">
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
className="size-9 rounded-none text-[#444746] hover:bg-[#f1f3f4] disabled:opacity-40"
|
|
disabled={openMailIndex <= 0}
|
|
onClick={goToPrev}
|
|
aria-label="Message plus récent"
|
|
>
|
|
<ChevronUp className="size-5" strokeWidth={1.5} />
|
|
</Button>
|
|
<span className="w-px shrink-0 self-stretch bg-border" aria-hidden />
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
className="size-9 rounded-none text-[#444746] hover:bg-[#f1f3f4] disabled:opacity-40"
|
|
disabled={openMailIndex >= displayListEmails.length - 1}
|
|
onClick={goToNext}
|
|
aria-label="Message plus ancien"
|
|
>
|
|
<ChevronDown className="size-5" strokeWidth={1.5} />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<EmailListEmailViewPane
|
|
data={data}
|
|
reading={reading}
|
|
selection={selection}
|
|
/>
|
|
</>
|
|
) : (
|
|
<>
|
|
{selectedFolder === "scheduled" && <EmailListScheduledBanner />}
|
|
{displayListEmails.length === 0 ? (
|
|
selectedFolder === "scheduled" ? (
|
|
<EmailListEmpty variant="scheduled" />
|
|
) : isSearchMode && searchParams ? (
|
|
<EmailListEmpty variant="search" searchParams={searchParams} />
|
|
) : (
|
|
<EmailListEmpty
|
|
variant="folder"
|
|
selectedFolder={selectedFolder}
|
|
inboxCategoryTabLabel={inboxCategoryTabLabel}
|
|
folderIdToLabel={sidebarNav.folderIdToLabel}
|
|
/>
|
|
)
|
|
) : (
|
|
<div
|
|
className={cn(
|
|
MAIL_LIST_ROW_DIVIDER_CLASS,
|
|
listToolbarMode && "sm:pb-14"
|
|
)}
|
|
>
|
|
{listEmails.map((email) => (
|
|
<EmailListRow key={email.id} email={email} {...rowPropsBase} />
|
|
))}
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
{listToolbarMode ? (
|
|
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-20 hidden sm:flex sm:justify-start">
|
|
<MailFolderStackIndicator
|
|
currentKey={mailNavVisitKey(selectedFolder, data.inboxTab)}
|
|
folderTree={sidebarNav.folderTree}
|
|
folderIdToLabel={sidebarNav.folderIdToLabel}
|
|
labelRows={sidebarNav.labelRows}
|
|
onNavigate={handleBreadcrumbNavigate}
|
|
className="pointer-events-auto"
|
|
/>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export type { EmailListBodyProps }
|