diff --git a/app/routes/_app+/recipients+/$recipientId.index.tsx b/app/routes/_app+/recipients+/$recipientId.index.tsx
index b73efcea..b4871a44 100644
--- a/app/routes/_app+/recipients+/$recipientId.index.tsx
+++ b/app/routes/_app+/recipients+/$recipientId.index.tsx
@@ -48,7 +48,7 @@ type FutureMessage = LoaderData['futureMessages'][number]
const PAST_MESSAGES_PER_PAGE = 30
-function getDateRange(value: string, timeZone: string) {
+function parseDateValue(value: string) {
if (!value) return null
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value)
if (!match) return null
@@ -56,18 +56,39 @@ function getDateRange(value: string, timeZone: string) {
const month = Number(match[2])
const day = Number(match[3])
if (!year || !month || !day) return null
+ return { year, month, day }
+}
+
+function getStartDate(value: string, timeZone: string) {
+ const parts = parseDateValue(value)
+ if (!parts) return null
+ try {
+ const start = getDateInTimeZone(
+ parts.year,
+ parts.month,
+ parts.day,
+ timeZone,
+ )
+ return Number.isNaN(start.getTime()) ? null : start
+ } catch {
+ return null
+ }
+}
+
+function getEndDate(value: string, timeZone: string) {
+ const parts = parseDateValue(value)
+ if (!parts) return null
try {
- const start = getDateInTimeZone(year, month, day, timeZone)
- if (Number.isNaN(start.getTime())) return null
- const nextDay = new Date(Date.UTC(year, month - 1, day + 1))
+ const nextDay = new Date(
+ Date.UTC(parts.year, parts.month - 1, parts.day + 1),
+ )
const end = getDateInTimeZone(
nextDay.getUTCFullYear(),
nextDay.getUTCMonth() + 1,
nextDay.getUTCDate(),
timeZone,
)
- if (Number.isNaN(end.getTime())) return null
- return { start, end }
+ return Number.isNaN(end.getTime()) ? null : end
} catch {
return null
}
@@ -115,7 +136,8 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
const hints = getHints(request)
const url = new URL(request.url)
const searchQuery = url.searchParams.get('search') ?? ''
- const dateFilter = url.searchParams.get('date') ?? ''
+ const startDateFilter = url.searchParams.get('startDate') ?? ''
+ const endDateFilter = url.searchParams.get('endDate') ?? ''
const cursor = url.searchParams.get('cursor')
const recipient = await prisma.recipient.findUnique({
where: { id: params.recipientId },
@@ -140,13 +162,21 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
where: { phoneNumber: recipient.phoneNumber },
})
- const dateRange = getDateRange(
- dateFilter,
+ const startDate = getStartDate(
+ startDateFilter,
hints.timeZone ?? recipient.timeZone,
)
- const sentAtFilter = dateRange
- ? { gte: dateRange.start, lt: dateRange.end }
- : { not: null }
+ const endDate = getEndDate(
+ endDateFilter,
+ hints.timeZone ?? recipient.timeZone,
+ )
+ const sentAtFilter =
+ startDate || endDate
+ ? {
+ ...(startDate ? { gte: startDate } : {}),
+ ...(endDate ? { lt: endDate } : {}),
+ }
+ : { not: null }
const pastMessageWhere = {
recipientId: params.recipientId,
sentAt: sentAtFilter,
@@ -173,7 +203,8 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
optedOut: Boolean(optOut),
recipient: recipientProps,
searchQuery,
- dateFilter,
+ startDateFilter,
+ endDateFilter,
nextCursor,
cronError: (() => {
try {
@@ -461,7 +492,8 @@ export default function RecipientRoute() {
data.pastMessages,
data.nextCursor,
data.searchQuery,
- data.dateFilter,
+ data.startDateFilter,
+ data.endDateFilter,
data.recipient.phoneNumber,
])
@@ -470,7 +502,8 @@ export default function RecipientRoute() {
if (
loadMoreData.recipient.phoneNumber !== data.recipient.phoneNumber ||
loadMoreData.searchQuery !== data.searchQuery ||
- loadMoreData.dateFilter !== data.dateFilter
+ loadMoreData.startDateFilter !== data.startDateFilter ||
+ loadMoreData.endDateFilter !== data.endDateFilter
) {
return
}
@@ -488,7 +521,8 @@ export default function RecipientRoute() {
loadMoreData,
data.recipient.phoneNumber,
data.searchQuery,
- data.dateFilter,
+ data.startDateFilter,
+ data.endDateFilter,
])
useLayoutEffect(() => {
@@ -535,10 +569,15 @@ export default function RecipientRoute() {
}
}, [handleScroll, scrollContainer])
- const isPastFiltered = Boolean(data.searchQuery || data.dateFilter)
- const emptyPastMessage = isPastFiltered
+ const isPastFiltered = Boolean(
+ data.searchQuery || data.startDateFilter || data.endDateFilter,
+ )
+ const hasPastMessages = pastMessagesForDisplay.length > 0
+ const hasFutureMessages = data.futureMessages.length > 0
+ const hasAnyMessages = hasPastMessages || hasFutureMessages
+ const emptyThreadMessage = isPastFiltered
? 'No messages match your search.'
- : 'No past messages yet.'
+ : 'No messages yet.'
const loadMoreLabel = pastNextCursor
? isLoadingMore
? 'Loading earlier messages...'
@@ -559,70 +598,56 @@ export default function RecipientRoute() {
- Past messages
+ Messages
-
- {pastMessagesForDisplay.length === 0 ? (
-
- {emptyPastMessage}
-
- ) : (
-
-
-
- {loadMoreLabel}
-
-
- {pastMessagesForDisplay.map((m) => (
- -
-
-
-
- ))}
-
+ {hasAnyMessages ? (
+
+ {hasPastMessages || pastNextCursor ? (
+
+ {loadMoreLabel}
-
- )}
-
-
-
-
- Upcoming messages
-
-
- {data.futureMessages.length ? (
- data.futureMessages.map((m, index) => (
- -
-
-
- ))
- ) : (
+ ) : null}
+
+ {pastMessagesForDisplay.map((m) => (
+ -
+
+
+
+ ))}
+ {data.futureMessages.map((m) => (
+
+ ))}
+
+
+ ) : (
+
+
{emptyThreadMessage}
Create a new message
- )}
-
+
+ )}