Merge branch 'main' into fix/remove-reputation-check-when-fetching-report-details

This commit is contained in:
HSIAO PO WEN
2025-01-23 14:10:49 +08:00
committed by GitHub
4 changed files with 226 additions and 122 deletions

View File

@@ -50,4 +50,14 @@
.progress-gradient::-moz-progress-bar {
@apply bg-gradient-to-r from-[#52acbc] to-[#ff892a];
}
.progress-gradient:indeterminate {
background-image: linear-gradient(
90deg,
#52acbc -1%,
#ff892a 10%,
transparent 10%,
transparent 90%
);
}
}

View File

@@ -20,6 +20,7 @@ export enum QueryKeys {
ReportsWaitingForTransaction = 'ReportsWaitingForTransaction',
Notifications = 'notifications',
SingleReport = 'singleReport',
Adjudicator = 'adjudicator',
}
export enum MutationKeys {

View File

@@ -1,84 +1,118 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useParams } from 'react-router-dom'
import { useQuery } from '@tanstack/react-query'
import { PATHS } from '@/constants/paths'
import { QueryKeys } from '@/constants/queryKeys'
import { ReportService } from '@/features/core/services/ReportService/ReportService'
import dayjs from 'dayjs'
import { ReportHistory, ReportStatus } from '@/types/Report'
import { useFetchReportCategories } from '@/features/reporting'
import { useAuthStatus } from '@/features/auth'
import { genReportIdentityProof, useUserState } from '@/features/core'
import { ReportService } from '@/features/core/services/ReportService/ReportService'
import { useFetchReportCategories } from '@/features/reporting'
import { ReportStatus } from '@/types/Report'
import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react'
import { useQuery } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { useMemo } from 'react'
import { Link, useParams } from 'react-router-dom'
const ReportDetailsPage: React.FC = () => {
const { userState } = useUserState()
const { id } = useParams()
const { reportCategories } = useFetchReportCategories()
const [adjudicationResult, setAdjudicationResult] =
useState<string>('尚未評判')
const { data: report } = useQuery({
queryKey: [QueryKeys.SingleReport, id],
queryFn: async () => {
const reportId = Number(id)
if (isNaN(reportId) || reportId < 0) return undefined
const reportService = new ReportService(userState)
return reportService.fetchReportById(reportId.toString())
},
})
const getAdjudicationResult = useCallback(
async (report: ReportHistory) => {
if (!userState || !report) return '尚未評判'
try {
const { publicSignals } = await genReportIdentityProof(
userState,
{
reportId: report.reportId,
},
)
const nullifierStr = publicSignals[0].toString()
const userAdjudication = report.adjudicatorsNullifier?.find(
(adj) => adj.nullifier === nullifierStr,
)
if (!userAdjudication) {
return '尚未評判'
}
return userAdjudication.adjudicateValue === 1
? '同意'
: '不同意'
} catch (error) {
console.error('Error getting adjudication result:', error)
return '尚未評判'
}
},
[userState],
export default function ReportDetailsPage() {
return (
<>
<div className="p-4 space-y-6">
<ReportResult />
<ReportObjectContent />
<ReportReason />
</div>
<FetchReportPending />
<FetchReportFailure />
</>
)
}
useEffect(() => {
if (!report || !userState) return
function FetchReportPending() {
const { id } = useParams()
const { isPending } = useReportItem(id)
const { isLoggingIn } = useAuthStatus()
const fetchAdjudicationResult = async () => {
const result = await getAdjudicationResult(report)
setAdjudicationResult(result)
}
return (
<Dialog
className="relative z-50"
open={isLoggingIn || isPending}
onClose={() => {}}
>
<DialogBackdrop className="fixed inset-0 bg-black/70" />
<div className="fixed inset-0 flex items-center justify-center w-screen p-4">
<DialogPanel className="relative p-0 w-85 shadow-base">
<div className="px-6 py-10 md:px-10 md:py-14 rounded-xl bg-white/90">
<progress className="h-3 mb-2 bg-white shadow-inner shadow-black/25 progress progress-gradient" />
<p className="text-sm text-[#080717] font-normal text-center">
<br />
</p>
</div>
</DialogPanel>
</div>
</Dialog>
)
}
fetchAdjudicationResult()
}, [report, userState, getAdjudicationResult])
function FetchReportFailure() {
const { id } = useParams()
const { isFetched } = useReportItem(id)
const { isLoggedIn } = useAuthStatus()
const reportCategoryLabel = useMemo(() => {
if (!report) return ''
const reportCategory = reportCategories.find(
(c) => c.number === report?.category,
)
return reportCategory?.description ?? ''
}, [report, reportCategories])
return (
<Dialog
className="relative z-50"
open={!isLoggedIn && isFetched}
onClose={() => {}}
>
<DialogBackdrop className="fixed inset-0 bg-black/70" />
<div className="fixed inset-0 flex items-center justify-center w-screen p-4">
<DialogPanel className="relative p-0 w-85 shadow-base">
<article className="px-6 py-10 md:px-10 md:py-14 rounded-xl bg-white/90">
<section>
<p className="text-base text-[#080717] font-medium leading-7">
/ Unirep Social Taiwan
</p>
</section>
<footer className="flex flex-col gap-4 mt-10">
<Link
className="py-3 text-base font-bold text-center text-white rounded-md bg-primary"
to={PATHS.LAUNCH}
>
Unirep Social Taiwan
</Link>
<Link
className="py-3 text-base font-bold text-center text-white rounded-md bg-primary"
to={PATHS.WELCOME}
>
/
</Link>
</footer>
</article>
</DialogPanel>
</div>
</Dialog>
)
}
if (!report) return null
function ReportResult() {
const { id } = useParams()
const { data: report } = useReportItem(id)
const { data: adjudicator, isPending } = useReportAdjudicator(id)
const getJudgementResult = () => {
const dateLabel = formatDate(Number(report?.reportAt))
const adjudicationLabel = useMemo(() => {
return isPending
? ''
: adjudicator
? adjudicator.adjudicateValue === 1
? '同意'
: '不同意'
: '尚未評判'
}, [adjudicator, isPending])
const resultLabel = useMemo(() => {
if (!report) return ''
switch (report.status) {
@@ -95,55 +129,114 @@ const ReportDetailsPage: React.FC = () => {
default:
return '評判中'
}
}
const formatDate = (date: number) => {
if (!date) return '尚未評判'
return dayjs(date).format('YYYY/MM/DD')
}
}, [report])
return (
<div className="text-white">
<div className="p-4 space-y-6">
<section>
<h2 className="text-xl font-bold mb-4"></h2>
<div className="bg-white rounded-xl p-4 space-y-2 text-content">
<p>
{formatDate(Number(report.reportAt))}
</p>
<p>{adjudicationResult}</p>
<p>{getJudgementResult()}</p>
</div>
</section>
<section>
<h2 className="text-xl font-bold mb-4"></h2>
<div className="bg-white rounded-xl p-4 text-content">
<p className="whitespace-pre-wrap">
{report.object.content}
</p>
</div>
</section>
<section>
<h2 className="text-xl font-bold mb-4">
&
</h2>
<div className="bg-white rounded-xl p-4 space-y-4 text-content">
<div>
<p className="font-bold"></p>
<p>{reportCategoryLabel}</p>
</div>
<div>
<p className="font-bold"></p>
<p>{report.reason}</p>
</div>
</div>
</section>
<section>
<h2 className="mb-4 text-xl font-bold text-white"></h2>
<div className="p-4 space-y-2 bg-white rounded-xl text-content">
<p>{dateLabel}</p>
<p>{adjudicationLabel}</p>
<p>{resultLabel}</p>
</div>
</div>
</section>
)
}
export default ReportDetailsPage
function ReportObjectContent() {
const { id } = useParams()
const { data: report } = useReportItem(id)
return (
<section>
<h2 className="mb-4 text-xl font-bold text-white"></h2>
<div className="p-4 bg-white rounded-xl text-content">
<p className="whitespace-pre-wrap">{report?.object?.content}</p>
</div>
</section>
)
}
function ReportReason() {
const { id } = useParams()
const { data: report } = useReportItem(id)
const { reportCategories } = useFetchReportCategories()
const reportCategoryLabel = useMemo(() => {
if (!report) return ''
const reportCategory = reportCategories.find(
(c) => c.number === report?.category,
)
return reportCategory?.description ?? ''
}, [report, reportCategories])
return (
<section>
<h2 className="mb-4 text-xl font-bold text-white">
&
</h2>
<div className="p-4 space-y-4 bg-white rounded-xl text-content">
<p>
<span className="font-bold"></span>
<span>{reportCategoryLabel}</span>
</p>
<p>
<span className="font-bold"></span>
<span>{report?.reason}</span>
</p>
</div>
</section>
)
}
function useReportItem(reportId?: string) {
const { userState } = useUserState()
return useQuery({
queryKey: [QueryKeys.SingleReport, reportId],
queryFn: async () => {
const _reportId = Number(reportId)
if (isNaN(_reportId) || _reportId < 0) return undefined
const reportService = new ReportService(userState)
return reportService.fetchReportById(_reportId.toString())
},
enabled: !!reportId,
})
}
function useReportAdjudicator(reportId?: string) {
const { userState } = useUserState()
const { data: report } = useReportItem(reportId)
return useQuery({
queryKey: [
QueryKeys.Adjudicator,
report?.reportId,
userState?.id.toString(),
],
queryFn: async () => {
if (!userState || !report) {
return undefined
}
const { publicSignals } = await genReportIdentityProof(userState, {
reportId: report.reportId,
})
const nullifierStr = publicSignals[0].toString()
const adjudicator = report.adjudicatorsNullifier?.find(
(adj) => adj.nullifier === nullifierStr,
)
return adjudicator
},
enabled: !!report && !!userState,
})
}
function formatDate(date: number) {
if (!date) return ''
return dayjs(date).format('YYYY/MM/DD')
}

View File

@@ -10,6 +10,7 @@ import PostListPage from './app/posts/page'
import HistoryPage from './app/profile/history/page'
import ProfilePage from './app/profile/page'
import ReputationPage from './app/profile/reputation/page'
import ReportDetailsPage from './app/reports/[id]/page'
import FullScreenLayout from './full-screen/layout'
import WritePostPage from './full-screen/write-post/page'
import OnboardingLayout from './onboarding/layout'
@@ -22,7 +23,6 @@ import LaunchPage from './start/launch/page'
import StartLayout from './start/layout'
import WelcomePage from './start/welcome/page'
import TwitterCallbackPage from './twitter/callback/page'
import ReportDetailsPage from './app/reports/[id]/page'
const router = createBrowserRouter([
{
@@ -101,12 +101,12 @@ const router = createBrowserRouter([
path: PATHS.NOTIFICATION,
element: <NotificationPage />,
},
{
path: PATHS.REPORT_DETAIL,
element: <ReportDetailsPage />,
},
],
},
{
path: PATHS.REPORT_DETAIL,
element: <ReportDetailsPage />,
},
{
path: PATHS.ABOUT_US,
element: <AboutPage />,