Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
377a5a8
Introduce swr
harryzcy Apr 17, 2026
e72d7fa
Use swr to load emails
harryzcy Apr 17, 2026
34f8cb9
Reorder states
harryzcy Apr 17, 2026
0968ff6
Don't declare hook as async
harryzcy Apr 17, 2026
f83488c
Don't duplicate list emails logic
harryzcy Apr 17, 2026
cd5d027
Fix some lint issues
harryzcy Apr 17, 2026
c0ad6ec
Fix lint
harryzcy Apr 17, 2026
3d94086
Load initial data with useEmails and remove useEffect
harryzcy Apr 17, 2026
b0d4733
Merge branch 'main' into swr
harryzcy Apr 17, 2026
df0ff20
Remove unused import
harryzcy Apr 17, 2026
93a82ed
Use stable key for cache
harryzcy Apr 17, 2026
f10d2c7
Set key beforehand
harryzcy Apr 17, 2026
ac5c0b1
Seperate return type for use emails
harryzcy Apr 17, 2026
ae589b1
Revert "Seperate return type for use emails"
harryzcy Apr 17, 2026
569d075
Fix typing
harryzcy Apr 17, 2026
348bc96
Seperate return type for use emails
harryzcy Apr 17, 2026
d473a7a
Introduce useEmailsEffect
harryzcy Apr 17, 2026
e4c228a
Load data
harryzcy Apr 17, 2026
e037c19
Debug
harryzcy Apr 17, 2026
a3b2406
Set values
harryzcy Apr 17, 2026
7ddf42e
Don't set next cursor
harryzcy Apr 17, 2026
3421ddc
Remove unused count
harryzcy Apr 17, 2026
3345b6f
Merge branch 'main' into swr
harryzcy Apr 18, 2026
1f8b07d
Merge branch 'main' into swr
harryzcy Apr 18, 2026
9bec690
Merge branch 'main' into swr
harryzcy Apr 18, 2026
1035351
Merge branch 'main' into swr
harryzcy Apr 19, 2026
b464344
Merge branch 'main' into swr
harryzcy Apr 19, 2026
0ad5316
Merge branch 'main' into swr
harryzcy Apr 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions web/src/pages/EmailList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@

export default function EmailList() {
const {
count,
setCount,
hasMore,
setHasMore,
nextCursor,

Check failure on line 25 in web/src/pages/EmailList.tsx

View workflow job for this annotation

GitHub Actions / ESLint / NPM Lint

'nextCursor' is assigned a value but never used

Check failure on line 25 in web/src/pages/EmailList.tsx

View workflow job for this annotation

GitHub Actions / ESLint / NPM Lint

'nextCursor' is assigned a value but never used
setNextCursor,
emails,
setEmails,
Expand Down Expand Up @@ -111,22 +110,22 @@
return currentYear > year || (currentYear === year && currentMonth > month)
}

const loadMoreEmails = async () => {

Check failure on line 113 in web/src/pages/EmailList.tsx

View workflow job for this annotation

GitHub Actions / ESLint / NPM Lint

Async arrow function 'loadMoreEmails' has no 'await' expression

Check failure on line 113 in web/src/pages/EmailList.tsx

View workflow job for this annotation

GitHub Actions / ESLint / NPM Lint

Async arrow function 'loadMoreEmails' has no 'await' expression
if (!hasMore) return
try {
const data = await loadEmails({
year,
month,
nextCursor
})
setEmails([...emails, ...data.items])
setCount(data.count + count)
setHasMore(data.hasMore)
setNextCursor(data.nextCursor)
} catch (e) {
console.error('Failed to load emails', e)
toast.error('Failed to load emails')
}
// try {
// const data = await loadEmails({
// year,
// month,
// nextCursor
// })
// setEmails([...emails, ...data.items])
// setCount(data.count + count)
// setHasMore(data.hasMore)
// setNextCursor(data.nextCursor)
// } catch (e) {
// console.error('Failed to load emails', e)
// toast.error('Failed to load emails')
// }
}

const handleDelete = async () => {
Expand Down
37 changes: 33 additions & 4 deletions web/src/pages/EmailRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@

import { DraftEmailsContext } from 'contexts/DraftEmailContext'

import { EmailInfo, ListEmailsResponse, listEmails } from 'services/emails'
import {
EmailInfo,
ListEmailsResponse,
listEmails,
useEmails
} from 'services/emails'

import { getCurrentYearMonth } from 'utils/time'

Expand Down Expand Up @@ -46,6 +51,10 @@
}

export default function EmailRoot(props: EmailRootProps) {
const { year: initialYear, month: initialMonth } = getCurrentYearMonth()
const [year, setYear] = useState(initialYear)
const [month, setMonth] = useState(initialMonth)

const [count, setCount] = useState(0)
const [hasMore, setHasMore] = useState(true)
const [nextCursor, setNextCursor] = useState<string | undefined>(undefined)
Expand All @@ -56,15 +65,35 @@
'idle' | 'loading' | 'loaded' | 'error'
>('idle')

const { year: initialYear, month: initialMonth } = getCurrentYearMonth()
const [year, setYear] = useState(initialYear)
const [month, setMonth] = useState(initialMonth)
const useEmailsEffect = () => {
const data = useEmails({
type: props.type,
year,
month,
order: 'desc',
nextCursor
})
return data
}

const emailsData = useEmailsEffect()

useEffect(() => {

Check failure on line 81 in web/src/pages/EmailRoot.tsx

View workflow job for this annotation

GitHub Actions / ESLint / NPM Lint

Unsafe call of a type that could not be resolved

Check failure on line 81 in web/src/pages/EmailRoot.tsx

View workflow job for this annotation

GitHub Actions / ESLint / NPM Lint

Unsafe call of a type that could not be resolved
console.log('84 emailsData', emailsData)
if (emailsData.loadingState === 'loaded') {
setEmails(emailsData.items)
setCount(emailsData.count)
setHasMore(emailsData.hasMore)
// setNextCursor(emailsData.nextCursor)
}
}, [emailsData.loadingState])

const loadEmails = async (input: {
year?: number
month?: number
nextCursor?: string
}) => {
return []
const { nextCursor } = input

const data = await listEmails({
Expand Down
47 changes: 43 additions & 4 deletions web/src/services/emails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ export interface ListEmailsResponse {
nextCursor?: string
Comment thread
harryzcy marked this conversation as resolved.
}

export async function listEmails(
props: ListEmailsProps
): Promise<ListEmailsResponse> {
export interface UseEmailsResponse extends ListEmailsResponse {
loadingState: 'idle' | 'loading' | 'loaded' | 'error'
}

function generateListEmailsParamString(props: ListEmailsProps): string {
const { type, year, month, order, pageSize, nextCursor } = props
const params = new URLSearchParams({
type
Expand All @@ -53,8 +55,45 @@ export async function listEmails(
if (nextCursor) {
params.append('nextCursor', nextCursor)
}
return params.toString()
}

export function useEmails(props: ListEmailsProps): UseEmailsResponse {
const key = ['emails', generateListEmailsParamString(props)]
const { data, error, isLoading } = useSWR<ListEmailsResponse, Error>(
key,
async () => {
return await listEmails(props)
},
Comment thread
harryzcy marked this conversation as resolved.
{
revalidateOnFocus: false,
revalidateOnReconnect: false
}
)

let loadingState: 'idle' | 'loading' | 'loaded' | 'error' = 'idle'
if (isLoading) {
loadingState = 'loading'
} else if (error) {
loadingState = 'error'
} else if (data) {
loadingState = 'loaded'
}

return {
items: data?.items ?? [],
count: data?.count ?? 0,
hasMore: data?.hasMore ?? false,
nextCursor: data?.nextCursor,
loadingState
}
}

const response = await fetch('/web/emails?' + params.toString(), {
export async function listEmails(
props: ListEmailsProps
): Promise<ListEmailsResponse> {
const params = generateListEmailsParamString(props)
const response = await fetch('/web/emails?' + params, {
method: 'GET'
})
return response.json() as Promise<ListEmailsResponse>
Expand Down
Loading