Skip to content

Commit

Permalink
Merge pull request #517 from social-tw/fix/hide-notification-after-ch…
Browse files Browse the repository at this point in the history
…ecking-in-successfully

[Fix] [Frontend] [Relay] Improve check in flow
  • Loading branch information
Xiawpohr authored Sep 25, 2024
2 parents 77a8dfa + 713000b commit a5a2e13
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 38 deletions.
2 changes: 2 additions & 0 deletions packages/frontend/src/constants/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const SERVER = process.env.REACT_APP_SERVER ?? 'http://localhost:8000'
export const KEY_SERVER = `${SERVER}/build/`
export const NUM_EPOCH_KEY_NONCE_PER_EPOCH = 3
export const CHECKED_IN_AT = 'checked-in-at'
export const DISCARDED_CHECK_IN_AT = 'discarded-check-in-at'
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ function ActionLink({
if (
action.type === ActionType.ReportComment ||
action.type === ActionType.ReportPost ||
action.type === ActionType.Adjudicate
action.type === ActionType.Adjudicate ||
action.type === ActionType.CheckIn
) {
return <span className="text-white">-</span>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function CheckInNotification() {
const [isOpenCheckIn, toggleCheckIn] = useToggle(false)
const [isOpenDiscardCheckIn, toggleDiscardCheckIn] = useToggle(false)

const { isOpen } = useNotifyCheckIn()
const { isOpen, discardCheckIn } = useNotifyCheckIn()

if (!isOpen) {
return null
Expand All @@ -28,6 +28,9 @@ export default function CheckInNotification() {
<DiscardCheckIn
open={isOpenDiscardCheckIn}
onClose={() => toggleDiscardCheckIn(false)}
onConfirm={() => {
discardCheckIn()
}}
onCheckIn={() => {
toggleDiscardCheckIn(false)
toggleCheckIn(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ import {
DialogBackdrop,
DialogPanel,
} from '@headlessui/react'
import { useNotifyCheckIn } from '../../hooks/useNotifyCheckIn/useNotifyCheckIn'

export default function DiscardCheckIn({
open = false,
onClose = () => {},
onCheckIn = () => {},
}: {
interface DiscardCheckInProps {
open?: boolean
onClose?: () => void
onConfirm?: () => void
onCheckIn?: () => void
}) {
const { discard } = useNotifyCheckIn()
}

export default function DiscardCheckIn({
open = false,
onClose = () => {},
onConfirm = () => {},
onCheckIn = () => {},
}: DiscardCheckInProps) {
return (
<Dialog className="relative z-50" open={open} onClose={onClose}>
<DialogBackdrop className="fixed inset-0 bg-black/70" />
Expand All @@ -37,7 +38,7 @@ export default function DiscardCheckIn({
<div className="flex gap-4">
<button
className="flex-1 text-lg font-bold text-white btn btn-primary"
onClick={discard}
onClick={onConfirm}
>
確認放棄
</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MutationKeys } from '@/constants/queryKeys'
import { MutationKeys, QueryKeys } from '@/constants/queryKeys'
import {
ActionType,
addAction,
Expand All @@ -8,10 +8,12 @@ import {
useUserState,
useUserStateTransition,
} from '@/features/core'
import { useMutation } from '@tanstack/react-query'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { AdjudicateFormValues } from '../../components/Adjudicate/AdjudicateForm'

export function useAdjudicate() {
const queryClient = useQueryClient()

const { userState } = useUserState()

const { stateTransition } = useUserStateTransition()
Expand Down Expand Up @@ -41,6 +43,10 @@ export function useAdjudicate() {
if (context?.actionId) {
succeedActionById(context.actionId)
}

await queryClient.invalidateQueries({
queryKey: [QueryKeys.PendingReports],
})
},
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '@/features/core'
import { getEpochKeyNonce } from '@/utils/helpers/getEpochKeyNonce'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useNotifyCheckIn } from '../useNotifyCheckIn/useNotifyCheckIn'

export function useCheckIn() {
const queryClient = useQueryClient()
Expand All @@ -24,6 +25,8 @@ export function useCheckIn() {

const actionCount = useActionCount()

const { startCheckIn, failCheckIn } = useNotifyCheckIn()

return useMutation({
mutationKey: [MutationKeys.CheckIn],
mutationFn: async () => {
Expand Down Expand Up @@ -52,10 +55,12 @@ export function useCheckIn() {
}
},
onMutate: (_variables) => {
startCheckIn()
const actionId = addAction(ActionType.CheckIn, undefined)
return { actionId }
},
onError: (_error, _variables, context) => {
failCheckIn()
if (context?.actionId) {
failActionById(context.actionId)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,57 @@
import { CHECKED_IN_AT, DISCARDED_CHECK_IN_AT } from '@/constants/config'
import { useReputationScore } from '@/features/reporting'
import { useLocalStorage } from '@uidotdev/usehooks'
import dayjs from 'dayjs'
import { useEffect, useMemo } from 'react'

const DISCARDED_AT = 'discarded-checkin-at'
import { useEffect } from 'react'

export function useNotifyCheckIn() {
const { reputationScore } = useReputationScore()

const [discardedAt, saveDiscardedAt] = useLocalStorage<string | null>(
DISCARDED_AT,
const [checkedInAt, setCheckedInAt] = useLocalStorage<string | null>(
CHECKED_IN_AT,
null,
)

const isOpen = useMemo(
() => !!reputationScore && reputationScore < 0 && !discardedAt,
[reputationScore, discardedAt],
const [discardedAt, setDiscardedAt] = useLocalStorage<string | null>(
DISCARDED_CHECK_IN_AT,
null,
)

const discard = () => {
saveDiscardedAt(new Date().toISOString())
const isOpen =
!!reputationScore && reputationScore < 0 && !checkedInAt && !discardedAt

const startCheckIn = () => {
setCheckedInAt(new Date().toISOString())
}

const failCheckIn = () => {
setCheckedInAt(null)
}

const discardCheckIn = () => {
setDiscardedAt(new Date().toISOString())
}

useEffect(() => {
if (!checkedInAt || dayjs().isSame(dayjs(checkedInAt), 'day')) {
return
}

setCheckedInAt(null)
}, [checkedInAt, setCheckedInAt])

useEffect(() => {
if (!discardedAt || dayjs().isSame(dayjs(discardedAt), 'day')) {
return
}

saveDiscardedAt(null)
}, [discardedAt, saveDiscardedAt])
setDiscardedAt(null)
}, [discardedAt, setDiscardedAt])

return {
isOpen,
discard,
startCheckIn,
failCheckIn,
discardCheckIn,
}
}
4 changes: 2 additions & 2 deletions packages/frontend/src/routes/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
AdjudicationNotification,
CheckInNotification,
} from '@/features/reporting'
import { useBackgroundReputationClaim } from '@/features/reporting/hooks/useBackgroundReputationClaim/useBackgroundReputationClaim'
import { MobileBottomNav } from '@/features/shared'
import { ForbidActionDialog } from '@/features/shared/components/Dialog/ForbidActionDialog'
import {
Expand All @@ -27,13 +28,12 @@ import {
useMatch,
useNavigate,
} from 'react-router-dom'
import { useBackgroundReputationClaim } from '@/features/reporting/hooks/useBackgroundReputationClaim/useBackgroundReputationClaim'

function NotificationContainer({ children }: { children: React.ReactNode }) {
return (
<div
id="notifications"
className="fixed z-20 right-4 bottom-28 lg:right-10 lg:bottom-20"
className="fixed z-20 right-4 bottom-28 lg:left-10 lg:right-auto lg:bottom-20"
>
{children}
</div>
Expand Down
6 changes: 5 additions & 1 deletion packages/relay/src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,11 @@ const _schema = [
['epochKey', 'String'],
['score', 'Int'],
['type', 'Int'],
['reportId', 'String'],
{
name: 'reportId',
type: 'String',
optional: true,
},
{
name: 'report',
type: 'Object',
Expand Down
11 changes: 3 additions & 8 deletions packages/relay/src/routes/checkinRoute.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { DB } from 'anondb/node'
import { Express } from 'express'
import { createCheckReputationMiddleware } from '../middlewares/CheckReputationMiddleware'
import { reputationService } from '../services/ReputationService'
import { UnirepSocialSynchronizer } from '../services/singletons/UnirepSocialSynchronizer'
import { errorHandler } from '../services/utils/ErrorHandler'
import ProofHelper from '../services/utils/ProofHelper'
import TransactionManager from '../services/utils/TransactionManager'
import { Errors } from '../types'

export default (
Expand All @@ -21,17 +20,13 @@ export default (

const { publicSignals, proof } = req.body

const epochKeyProof = await ProofHelper.getAndVerifyEpochKeyProof(
const txHash = await reputationService.claimCheckInReputation(
publicSignals,
proof,
db,
synchronizer
)

const txHash = await TransactionManager.callContract(
'claimDailyLoginRep',
[epochKeyProof.publicSignals, epochKeyProof.proof]
)

res.json({ txHash })
})
)
Expand Down
39 changes: 38 additions & 1 deletion packages/relay/src/services/ReputationService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { DB } from 'anondb/node'
import { ReputationHistory } from '../types/Reputation'
import { Groth16Proof, PublicSignals } from 'snarkjs'
import {
RepChangeType,
ReputationHistory,
ReputationType,
} from '../types/Reputation'
import { UnirepSocialSynchronizer } from './singletons/UnirepSocialSynchronizer'
import ProofHelper from './utils/ProofHelper'
import TransactionManager from './utils/TransactionManager'

export class ReputationService {
async findManyReputationHistory(
Expand All @@ -24,6 +32,35 @@ export class ReputationService {

return reputations
}

async claimCheckInReputation(
publicSignals: PublicSignals,
proof: Groth16Proof,
db: DB,
synchronizer: UnirepSocialSynchronizer
) {
const epochKeyProof = await ProofHelper.getAndVerifyEpochKeyProof(
publicSignals,
proof,
synchronizer
)

const txHash = await TransactionManager.callContract(
'claimDailyLoginRep',
[epochKeyProof.publicSignals, epochKeyProof.proof]
)

db.create('ReputationHistory', {
transactionHash: txHash,
epoch: Number(epochKeyProof.epoch),
epochKey: String(epochKeyProof.epochKey),
score: RepChangeType.CHECK_IN_REP,
type: ReputationType.CHECK_IN,
reportId: null,
})

return txHash
}
}

export const reputationService = new ReputationService()
1 change: 1 addition & 0 deletions packages/relay/src/types/Reputation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export enum RepChangeType {
RESPONDENT_REP = 5,
FAILED_REPORTER_REP = 1,
ADJUDICATOR_REP = 1,
CHECK_IN_REP = 1,
}

// Reputation user type
Expand Down

0 comments on commit a5a2e13

Please sign in to comment.