mirror of
https://github.com/social-tw/social-tw-website.git
synced 2026-01-09 15:38:09 -05:00
Merge branch 'main' into fix/remove-reputation-check-when-fetching-report-details
This commit is contained in:
@@ -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%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ export enum QueryKeys {
|
||||
ReportsWaitingForTransaction = 'ReportsWaitingForTransaction',
|
||||
Notifications = 'notifications',
|
||||
SingleReport = 'singleReport',
|
||||
Adjudicator = 'adjudicator',
|
||||
}
|
||||
|
||||
export enum MutationKeys {
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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 />,
|
||||
|
||||
Reference in New Issue
Block a user