Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion packages/mask/src/components/shared/ApplicationBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ export function ApplicationBoard({ secondEntries, secondEntryChainTabs }: MaskAp
new URL('./assets/findtruman.png', import.meta.url).toString(),
onFindTrumanDialogOpen,
[ChainId.Mainnet],
false,
isNotEvm,
true,
),
]
Expand Down
205 changes: 205 additions & 0 deletions packages/mask/src/plugins/FindTruman/SNSAdaptor/CompletionCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import type { CompletionQuestionItem, UserCompletionStatus, CompletionQuestionAnswer } from '../types'
import { Alert, Box, Button, CardContent, Snackbar, TextField, Typography } from '@mui/material'
import { useCallback, useContext, useEffect, useMemo, useState } from 'react'
import { FindTrumanContext } from '../context'
import { makeStyles } from '@masknet/theme'
import { ActionButtonPromise } from '../../../extension/options-page/DashboardComponents/ActionButton'
import { DoneOutlined, RefreshOutlined, Send, CheckRounded, ClearRounded, KeyRounded } from '@mui/icons-material'
import NoNftCard from './NoNftCard'

const useStyles = makeStyles()((theme, props) => ({
successButton: {
backgroundColor: theme.palette.mode === 'light' ? '#2e7d32' : '#66bb6a',
color: theme.palette.mode === 'light' ? '#fff' : 'rgba(0, 0, 0, .87)',
'&:hover': {
backgroundColor: theme.palette.mode === 'light' ? '#1B5E20' : '#388E3C',
},
},
errorButton: {},
helperText: {
display: 'flex',
alignItems: 'center',
marginBottom: theme.spacing(1),
},
helperTextIcon: {
marginRight: '8px',
fontSize: '20px',
},
helperTextContent: {
fontSize: '14px',
},
}))

interface CompletionCardProps {
completionStatus?: UserCompletionStatus
onSubmit(quesId: string, answers: CompletionQuestionAnswer[]): Promise<void>
}
export default function CompletionCard(props: CompletionCardProps) {
const { completionStatus, onSubmit } = props
const [submitted, setSubmitted] = useState(completionStatus?.answered)
const [questions, setQuestions] = useState<CompletionQuestionItem[]>(completionStatus?.questions ?? [])
const [submitting, setSubmitting] = useState(false)
const [error, setError] = useState<'' | 'insufficient-nft'>('')
const [snackVisible, setSnackVisible] = useState(false)

const { t } = useContext(FindTrumanContext)
const { classes } = useStyles()

useEffect(() => {
setError('')
if (!completionStatus) return
setQuestions(completionStatus.questions)
setSubmitted(completionStatus.answered)
!!completionStatus.notMeetConditions.length && setError('insufficient-nft')
}, [completionStatus])

const handleSubmit = useCallback(async () => {
setSubmitting(true)
try {
await onSubmit(
completionStatus?.id ?? '',
questions.map((e) => ({
id: e.id,
answer: e.answer || '',
})),
)
} catch (error) {
throw error
} finally {
setSubmitting(false)
}
}, [questions.map((e) => e.answer).join(',')])

const enabled = useMemo(() => {
return (
completionStatus &&
!completionStatus.answersPublished &&
!submitting &&
completionStatus.notMeetConditions.length === 0
)
}, [completionStatus, submitting])

const isClosed = useMemo(() => !!completionStatus?.answersPublished, [completionStatus])

return (
<CardContent>
<Snackbar
autoHideDuration={2000}
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
open={snackVisible}
onClose={() => setSnackVisible(false)}>
<Alert onClose={() => setSnackVisible(false)} variant="filled" severity="error" sx={{ width: '100%' }}>
{t('plugin_find_truman_submit_failed')}
</Alert>
</Snackbar>
{completionStatus && (
<>
<Typography variant="h6" color="textPrimary" paddingLeft={1} paddingRight={1} marginBottom={2}>
{completionStatus.title}
</Typography>
<Box marginBottom={2}>
{questions.map((question, index) => (
<Box key={question.id} marginBottom={2}>
{completionStatus.answersPublished ? (
<TextField
fullWidth
InputProps={{
readOnly: true,
}}
size="small"
focused
variant="outlined"
color={question.correct ? 'success' : 'error'}
error={!question.correct}
value={question.answer ?? ''}
label={question.title}
helperText={
<span className={classes.helperText}>
<KeyRounded className={classes.helperTextIcon} />
<span className={classes.helperTextContent}>
{question.correctAnswer ?? ''}
</span>
</span>
}
/>
) : completionStatus.answered ? (
<TextField
fullWidth
InputProps={{
readOnly: true,
}}
size="small"
variant="outlined"
label={question.title}
value={question.answer ?? ''}
/>
) : (
<TextField
fullWidth
required
disabled={!enabled && !submitted}
InputProps={{
readOnly: submitted,
}}
size="small"
variant="outlined"
placeholder={t('plugin_find_truman_completion_placeholder')}
label={question.title}
value={questions[index].answer ?? ''}
onChange={({ currentTarget }) => {
const ques = [...questions]
ques[index].answer = currentTarget.value
setQuestions(ques)
}}
/>
)}
</Box>
))}
</Box>
{!error && (
<Box textAlign="right" marginTop={1} paddingBottom={1}>
{submitted ? (
<Button
className={completionStatus.correct ? classes.successButton : classes.errorButton}
variant="contained"
startIcon={completionStatus.correct ? <CheckRounded /> : <ClearRounded />}
color={completionStatus.correct ? 'success' : 'error'}>
{completionStatus.correct
? t('plugin_find_truman_completion_correct')
: t('plugin_find_truman_completion_wrong')}
</Button>
) : (
<ActionButtonPromise
color="primary"
variant="contained"
disabled={!enabled}
init={t(
isClosed
? 'plugin_find_truman_completion_closed'
: 'plugin_find_truman_completion_submit',
)}
waiting={t('plugin_find_truman_completion_submitting')}
failed={t('plugin_find_truman_submit_failed')}
complete={t('plugin_find_truman_completion_submitted')}
executor={handleSubmit}
failedOnClick="use executor"
startIcon={isClosed ? undefined : <Send />}
completeIcon={<DoneOutlined />}
failIcon={<RefreshOutlined />}
/>
)}
</Box>
)}
{completionStatus.notMeetConditions.length > 0 && (
<Box>
<Alert severity="info" sx={{ mb: 1 }}>
{t('plugin_find_truman_insufficient_nft')}
</Alert>
<NoNftCard conditions={completionStatus.notMeetConditions} />
</Box>
)}
</>
)}
</CardContent>
)
}
38 changes: 31 additions & 7 deletions packages/mask/src/plugins/FindTruman/SNSAdaptor/FindTruman.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@ import { useContext, useState } from 'react'
import { makeStyles } from '@masknet/theme'
import { FindTrumanContext } from '../context'
import { Alert, Avatar, Box, Card, CardHeader, CardMedia, Chip, Skeleton, Tooltip, Typography } from '@mui/material'
import type { PollResult, PuzzleResult, StoryInfo, UserPollStatus, UserStoryStatus } from '../types'
import type {
CompletionQuestionAnswer,
PollResult,
PuzzleResult,
StoryInfo,
UserCompletionStatus,
UserPollStatus,
UserStoryStatus,
} from '../types'
import { FindTrumanI18nFunction, PostType } from '../types'
import ResultCard from './ResultCard'
import OptionsCard from './OptionsCard'
import Footer from './Footer'
import StageCard from './StageCard'
import EncryptionCard from './EncryptionCard'
import CompletionCard from './CompletionCard'

const useStyles = makeStyles()((theme) => {
return {
Expand Down Expand Up @@ -99,9 +108,11 @@ interface FindTrumanProps {
userStoryStatus?: UserStoryStatus
userPuzzleStatus?: UserPollStatus
userPollStatus?: UserPollStatus
userCompletionStatus?: UserCompletionStatus
puzzleResult?: PuzzleResult
pollResult?: PollResult
onSubmit(choice: number): Promise<void>
onSubmitPoll(choice: number): Promise<void>
onSubmitCompletion(answers: CompletionQuestionAnswer[]): Promise<void>
}

export function getPostTypeTitle(t: FindTrumanI18nFunction, postType: PostType) {
Expand All @@ -110,6 +121,8 @@ export function getPostTypeTitle(t: FindTrumanI18nFunction, postType: PostType)
return t('plugin_find_truman_status_poll')
case PostType.Puzzle:
return t('plugin_find_truman_status_puzzle')
case PostType.Completion:
return t('plugin_find_truman_status_completion')
case PostType.PuzzleResult:
return t('plugin_find_truman_status_puzzle_result')
case PostType.PollResult:
Expand All @@ -131,24 +144,35 @@ export function FindTruman(props: FindTrumanProps) {
userStoryStatus,
userPuzzleStatus,
userPollStatus,
userCompletionStatus,
puzzleResult,
pollResult,
onSubmit,
onSubmitPoll,
onSubmitCompletion,
} = props

const [loadImg, setLoadImg] = useState(true)

const isCritical = userPuzzleStatus?.critical || userPollStatus?.critical
const isCritical = userPuzzleStatus?.critical || userPollStatus?.critical || userCompletionStatus?.critical
const isNoncritical =
(userPuzzleStatus && !userPuzzleStatus.critical) || (userPollStatus && !userPollStatus.critical)
(userPuzzleStatus && !userPuzzleStatus.critical) ||
(userPollStatus && !userPollStatus.critical) ||
(userCompletionStatus && !userCompletionStatus.critical)

const renderCard = () => {
if (postType === PostType.Status) {
return <StageCard userStoryStatus={userStoryStatus} />
} else if (postType === PostType.Puzzle && userPuzzleStatus) {
return <OptionsCard onSubmit={onSubmit} userStatus={userPuzzleStatus} />
return <OptionsCard onSubmit={onSubmitPoll} userStatus={userPuzzleStatus} />
} else if (postType === PostType.Poll && userPollStatus) {
return <OptionsCard onSubmit={onSubmit} userStatus={userPollStatus} />
return <OptionsCard onSubmit={onSubmitPoll} userStatus={userPollStatus} />
} else if (postType === PostType.Completion) {
return (
<CompletionCard
onSubmit={(_, answers) => onSubmitCompletion(answers)}
completionStatus={userCompletionStatus}
/>
)
} else if (postType === PostType.PuzzleResult && puzzleResult) {
return <ResultCard type={PostType.PuzzleResult} userStatus={userPuzzleStatus} result={puzzleResult} />
} else if (postType === PostType.PollResult && pollResult) {
Expand Down
Loading