Skip to content

Commit

Permalink
Merge branch 'main' into fix/show-reputation-history-related-to-user
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiawpohr authored Sep 18, 2024
2 parents 9a9ff00 + b1b0bdc commit 0020cfe
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { renderHook, waitFor } from '@testing-library/react'
import { wrapper } from '@/utils/test-helpers/wrapper'
import { renderHook, waitFor } from '@testing-library/react'
import { useEpoch } from './useEpoch'

// Mock useUserState
jest.mock('@/features/core/hooks/useUserState/useUserState', () => ({
useUserState: () => ({
userState: {
Expand All @@ -13,10 +14,22 @@ jest.mock('@/features/core/hooks/useUserState/useUserState', () => ({
}),
}))

// Mock useRelayConfig
jest.mock('@/features/core/hooks/useRelayConfig/useRelayConfig', () => ({
useRelayConfig: () => ({
data: {
EPOCH_LENGTH: 300, // 300 seconds
},
isPending: false,
isSuccess: true,
}),
}))

describe('useEpoch', () => {
it('should get epoch time information', async () => {
const { result } = renderHook(useEpoch, { wrapper })

await waitFor(() => expect(result.current.isPending).toBe(false))
await waitFor(() => expect(result.current.currentEpoch).toBe(2))
await waitFor(() => expect(result.current.remainingTime).toBe(120000))
await waitFor(() => expect(result.current.epochLength).toBe(300000))
Expand Down
34 changes: 26 additions & 8 deletions packages/frontend/src/features/core/hooks/useEpoch/useEpoch.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { QueryKeys } from '@/constants/queryKeys'
import { useUserState } from '@/features/core'
import { useRelayConfig, useUserState } from '@/features/core'
import { useQuery } from '@tanstack/react-query'
import isNull from 'lodash/isNull'
import isUndefined from 'lodash/isUndefined'
import { useEffect, useMemo } from 'react'

const epochLength = 300000 // 300000 ms

export function useEpoch() {
const { isPending: isUserStatePending, userState } = useUserState()
const { data: config, isPending: isConfigPending } = useRelayConfig()

// Convert EPOCH_LENGTH from seconds to milliseconds
const epochLength = useMemo(() => {
return config?.EPOCH_LENGTH ? config.EPOCH_LENGTH * 1000 : undefined
}, [config])

const {
isPending: isCurrentEpochPending,
Expand All @@ -23,6 +27,7 @@ export function useEpoch() {
return userState.sync.calcCurrentEpoch()
},
staleTime: epochLength,
enabled: !!epochLength,
})

const {
Expand All @@ -39,21 +44,27 @@ export function useEpoch() {
return time * 1000
},
staleTime: epochLength,
enabled: !!epochLength,
})

const isPending =
isUserStatePending || isCurrentEpochPending || isRemainingTimePending
isUserStatePending ||
isCurrentEpochPending ||
isRemainingTimePending ||
isConfigPending ||
!epochLength

const epochStartTime = useMemo(() => {
if (
isUndefined(currentEpoch) ||
isNull(currentEpoch) ||
!remainingTime
!remainingTime ||
!epochLength
) {
return 0
}
return Date.now() - (epochLength - remainingTime)
}, [currentEpoch, remainingTime])
}, [currentEpoch, remainingTime, epochLength])

const epochEndTime = useMemo(() => {
if (
Expand All @@ -70,7 +81,8 @@ export function useEpoch() {
if (
isUndefined(currentEpoch) ||
isNull(currentEpoch) ||
!remainingTime
!remainingTime ||
!epochLength
) {
return
}
Expand All @@ -85,7 +97,13 @@ export function useEpoch() {
return () => {
clearTimeout(timer)
}
}, [currentEpoch, refetchCurrentEpoch, refetchRemainingTime, remainingTime])
}, [
currentEpoch,
refetchCurrentEpoch,
refetchRemainingTime,
remainingTime,
epochLength,
])

return {
isPending,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ import { act, renderHook } from '@testing-library/react'
import nock from 'nock'
import { useCreatePost } from './useCreatePost'

jest.mock('@/features/core/hooks/useRelayConfig/useRelayConfig', () => ({
useRelayConfig: () => ({
data: {
EPOCH_LENGTH: 300,
},
isPending: false,
isSuccess: true,
}),
}))

jest.mock('@/features/core/hooks/useWeb3Provider/useWeb3Provider', () => ({
useWeb3Provider: () => ({
getGuaranteedProvider: () => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ import { act, renderHook } from '@testing-library/react'
import nock from 'nock'
import { useVotes } from './useVotes'

jest.mock('@/features/core/hooks/useRelayConfig/useRelayConfig', () => ({
useRelayConfig: () => ({
data: {
EPOCH_LENGTH: 300,
},
isPending: false,
isSuccess: true,
}),
}))

jest.mock('@/features/core/hooks/useUserState/useUserState', () => ({
useUserState: () => ({
userState: {
Expand Down
15 changes: 11 additions & 4 deletions packages/frontend/src/features/shared/hooks/useDatePicker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useUserState } from '@/features/core'
import { useRelayConfig, useUserState } from '@/features/core'
import dayjs from 'dayjs'
import { useCallback, useState } from 'react'
import { useCallback, useMemo, useState } from 'react'
import {
EpochDateService,
FromToEpoch,
Expand All @@ -9,14 +9,20 @@ import {

export function useDatePicker() {
const { userState } = useUserState()
const { data: config } = useRelayConfig()
const [startDate, setStartDate] = useState<undefined | Date>(undefined)
const [endDate, setEndDate] = useState<undefined | Date>(undefined)
const [fromToEpoch, setFromToEpoch] = useState<FromToEpoch>(
new InvalidFromToEpoch(),
)

// Calculate epochLength in milliseconds
const epochLength = useMemo(() => {
return config?.EPOCH_LENGTH ? config.EPOCH_LENGTH * 1000 : undefined
}, [config])

const updateFromToEpoch = useCallback(async () => {
if (!userState) {
if (!userState || !epochLength) {
setFromToEpoch(new InvalidFromToEpoch())
return
}
Expand All @@ -25,9 +31,10 @@ export function useDatePicker() {
startDate,
endDate,
userState.sync,
epochLength,
),
)
}, [startDate, endDate, userState])
}, [startDate, endDate, userState, epochLength])

const onChange = (dates: [Date | null, Date | null]) => {
const [start, end] = dates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('EpochDateService', () => {
it('should execute properly', () => {
const synchronizer =
new MockSynchronizer() as unknown as Synchronizer
const epochLength = 300000 // 5 minutes in milliseconds

// service start time: 1720656000000 = 1721088000000 - 0 - 300000 * 1440
// service start time: 2024-07-11T00:00:00.000Z
Expand All @@ -23,25 +24,45 @@ describe('EpochDateService', () => {
// at service start & now
const date0 = new Date('2024-07-11T00:00:00Z')
expect(
EpochDateService.calcEpochByDate(mockNow, date0, synchronizer),
EpochDateService.calcEpochByDate(
mockNow,
date0,
synchronizer,
epochLength,
),
).toBe(0)

// between service start & now
const date1 = new Date('2024-07-11T00:15:00Z')
expect(
EpochDateService.calcEpochByDate(mockNow, date1, synchronizer),
EpochDateService.calcEpochByDate(
mockNow,
date1,
synchronizer,
epochLength,
),
).toBe(3) // 724 * 300000 + 1720655850000

// before service start
const date2 = new Date('2024-07-10T23:59:00Z')
expect(
EpochDateService.calcEpochByDate(mockNow, date2, synchronizer),
EpochDateService.calcEpochByDate(
mockNow,
date2,
synchronizer,
epochLength,
),
).toBe(0)

// future
const date3 = new Date('2024-07-17T00:00:00Z')
expect(
EpochDateService.calcEpochByDate(mockNow, date3, synchronizer),
EpochDateService.calcEpochByDate(
mockNow,
date3,
synchronizer,
epochLength,
),
).toBe(1440 + 12 * 24)
})
})
Expand Down
17 changes: 14 additions & 3 deletions packages/frontend/src/features/shared/services/EpochDateService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,41 @@ export class EpochDateService {
start: Date | undefined,
end: Date | undefined,
synchoronizer: Synchronizer,
epochLength: number,
) {
if (!!start && !!end && EpochDateService.isValidDateRange(start, end)) {
const [from, to] = EpochDateService.calcEpochsByDates(
[start, end],
synchoronizer,
epochLength,
)
return new ValidFromToEpoch(from, to)
}
return new InvalidFromToEpoch()
}

static calcEpochsByDates(dates: Date[], synchoronizer: Synchronizer) {
static calcEpochsByDates(
dates: Date[],
synchoronizer: Synchronizer,
epochLength: number,
) {
const now = Date.now()
return dates.map((date) =>
EpochDateService.calcEpochByDate(now, date, synchoronizer),
EpochDateService.calcEpochByDate(
now,
date,
synchoronizer,
epochLength,
),
)
}

static calcEpochByDate(
now: number,
date: Date,
synchoronizer: Synchronizer,
epochLength: number,
) {
const epochLength = 300000
const currentEpoch = synchoronizer.calcCurrentEpoch()
const remainingTime = synchoronizer.calcEpochRemainingTime() * 1000
const currentEpochStartTime = now - (epochLength - remainingTime)
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/src/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface FetchRelayConfigResponse {
UNIREP_ADDRESS: string
APP_ADDRESS: string
ETH_PROVIDER_URL: string
EPOCH_LENGTH: number
}

export type FetchPostsResponse = RelayRawPost[]
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/src/utils/test-helpers/buildMockAPIs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export function buildMockConfigAPI() {
UNIREP_ADDRESS: '0x83cB6AF63eAfEc7998cC601eC3f56d064892b386',
APP_ADDRESS: '0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1',
ETH_PROVIDER_URL: 'http://127.0.0.1:8545',
EPOCH_LENGTH: 300,
}
const expectation = nock(SERVER).get('/api/config').reply(200, response)

Expand Down
20 changes: 16 additions & 4 deletions packages/relay/src/routes/appConfigRoute.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import { DB } from 'anondb/node'
import { Express } from 'express'
import { UNIREP_ADDRESS, APP_ADDRESS, ETH_PROVIDER_URL } from '../config'
import { APP_ADDRESS, ETH_PROVIDER_URL, UNIREP_ADDRESS } from '../config'
import { UnirepSocialSynchronizer } from '../services/singletons/UnirepSocialSynchronizer'

export default (
app: Express,
_: DB,
synchronizer: UnirepSocialSynchronizer
) => {
app.get('/api/config', async (_, res) => {
const epochLength =
await synchronizer.unirepContract.attesterEpochLength(
BigInt(APP_ADDRESS).toString()
)

export default (app: Express) => {
app.get('/api/config', (_, res) =>
res.json({
UNIREP_ADDRESS,
APP_ADDRESS,
ETH_PROVIDER_URL,
EPOCH_LENGTH: epochLength,
})
)
})
}
Loading

0 comments on commit 0020cfe

Please sign in to comment.