poi-plugin-quest-info-2 0.7.3 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.eslintrc.js +0 -2
  2. package/build/kcQuestsData/DATA_VERSION +1 -1
  3. package/build/kcQuestsData/index.ts +1 -1
  4. package/build/kcQuestsData/quests-scn-new.json +1 -3
  5. package/build/kcQuestsData/quests-scn.json +72 -72
  6. package/build/kcanotifyGamedata/DATA_VERSION +1 -1
  7. package/build/kcanotifyGamedata/index.ts +1 -1
  8. package/build/kcanotifyGamedata/quests-en.json +12 -0
  9. package/build/kcanotifyGamedata/quests-jp.json +12 -0
  10. package/build/kcanotifyGamedata/quests-ko.json +12 -0
  11. package/build/kcanotifyGamedata/quests-scn.json +12 -0
  12. package/build/kcanotifyGamedata/quests-tcn.json +12 -0
  13. package/build/prePostQuest.json +3 -3
  14. package/build/questCategory.json +2 -0
  15. package/build/{questMap.json → questCodeMap.json} +0 -0
  16. package/i18n/en-US.json +2 -0
  17. package/i18n/ja-JP.json +3 -1
  18. package/i18n/ko-KR.json +3 -1
  19. package/i18n/zh-CN.json +3 -1
  20. package/i18n/zh-TW.json +3 -1
  21. package/package.json +14 -13
  22. package/src/__tests__/__snapshots__/questHelper.spec.ts.snap +399 -0
  23. package/src/__tests__/kcanotifyData.spec.ts +1 -1
  24. package/src/__tests__/kcwikiData.spec.ts +1 -1
  25. package/src/__tests__/questCategory.spec.ts +1 -1
  26. package/src/__tests__/questHelper.spec.ts +36 -0
  27. package/src/components/QuestCard/MinimalQuestCard.tsx +3 -0
  28. package/src/components/QuestCard/index.tsx +37 -45
  29. package/src/components/QuestCard/utils.tsx +8 -0
  30. package/src/components/QuestList.tsx +4 -23
  31. package/src/components/QuestTag.tsx +75 -12
  32. package/src/poi/hooks.ts +6 -6
  33. package/src/questHelper.ts +73 -3
  34. package/src/store/kcwiki.ts +7 -3
  35. package/src/store/quest.ts +55 -9
  36. package/src/tags.tsx +2 -2
  37. package/tsconfig.json +3 -2
@@ -4,7 +4,7 @@ import newQuestData from '../../build/kcQuestsData/quests-scn-new.json'
4
4
  describe('should version correct', () => {
5
5
  test('should KcwikiQuestData Game data version correct', () => {
6
6
  expect(version).toMatchInlineSnapshot(
7
- `"2b2c2222f933ff9863dec8c72fc6037fbfe375ac"`
7
+ `"cf24003d54c1b9cb2f6a76cdd670de717de0886c"`
8
8
  )
9
9
  })
10
10
 
@@ -9,7 +9,7 @@ describe('should questCategory correct', () => {
9
9
  expect(questCategory.monthlyQuest.length).toMatchInlineSnapshot(`11`)
10
10
  expect(questCategory.quarterlyQuest.length).toMatchInlineSnapshot(`25`)
11
11
  expect(questCategory.yearlyQuest.length).toMatchInlineSnapshot(`37`)
12
- expect(questCategory.singleQuest.length).toMatchInlineSnapshot(`475`)
12
+ expect(questCategory.singleQuest.length).toMatchInlineSnapshot(`477`)
13
13
  })
14
14
 
15
15
  test('snapshot', () => {
@@ -0,0 +1,36 @@
1
+ import {
2
+ getCompletedQuest,
3
+ getLockedQuest,
4
+ getPostQuestIds,
5
+ getPreQuestIds,
6
+ } from '../questHelper'
7
+
8
+ describe('questHelper', () => {
9
+ test('should getPreQuestIds correct', () => {
10
+ expect(getPreQuestIds(101)).toEqual([])
11
+ expect(getPreQuestIds(102)).toEqual([101])
12
+ expect(getPreQuestIds(236)).toEqual([235, 273])
13
+ })
14
+
15
+ test('should getPostQuestIds correct', () => {
16
+ expect(getPostQuestIds(101)).toEqual([102])
17
+ expect(getPostQuestIds(105)).toEqual([106, 108, 254, 401, 612, 816])
18
+ expect(getPostQuestIds(140)).toEqual([])
19
+ })
20
+
21
+ test('should 101 no completed quest', () => {
22
+ expect(getCompletedQuest([101])).toEqual({})
23
+ })
24
+
25
+ test('should 236 getCompletedQuest correct', () => {
26
+ expect(getCompletedQuest([236])).toMatchSnapshot()
27
+ })
28
+
29
+ test('should 101 locked quests match snapshot', () => {
30
+ expect(getLockedQuest([101])).toMatchSnapshot()
31
+ })
32
+
33
+ test('should 196 getLockedQuest correct', () => {
34
+ expect(getLockedQuest([196])).toMatchSnapshot()
35
+ })
36
+ })
@@ -6,6 +6,9 @@ import type { QuestCardProps } from './index'
6
6
  import { CardBody, CardTail, CatIndicator, FlexCard } from './styles'
7
7
  import { questStatusMap } from './utils'
8
8
 
9
+ /**
10
+ * @deprecated
11
+ */
9
12
  export const MinimalQuestCard = forwardRef<
10
13
  Card,
11
14
  // eslint-disable-next-line @typescript-eslint/ban-types
@@ -1,8 +1,13 @@
1
- import { Card, Elevation, H5, Text } from '@blueprintjs/core'
1
+ import { Card, Elevation, H5 } from '@blueprintjs/core'
2
2
  import React, { forwardRef } from 'react'
3
3
  import type { StyledComponentProps } from 'styled-components'
4
4
  import { usePluginTranslation } from '../../poi/hooks'
5
- import { getPrePost, guessQuestCategory, QUEST_STATUS } from '../../questHelper'
5
+ import {
6
+ getQuestPrePost,
7
+ guessQuestCategory,
8
+ QUEST_STATUS,
9
+ } from '../../questHelper'
10
+ import { useQuestStatus } from '../../store/quest'
6
11
  import { QuestTag } from '../QuestTag'
7
12
  import {
8
13
  CardActionWrapper,
@@ -14,6 +19,7 @@ import {
14
19
  TagsWrapper,
15
20
  } from './styles'
16
21
  import { questIconMap, questStatusMap } from './utils'
22
+ import { IN_POI } from '../../poi/env'
17
23
 
18
24
  export type QuestCardProps = {
19
25
  gameId: number
@@ -29,15 +35,15 @@ export type QuestCardProps = {
29
35
  const CardAction = ({ gameId }: { gameId: number }) => {
30
36
  const { t } = usePluginTranslation()
31
37
 
32
- const preQuests = getPrePost(gameId)
38
+ const prePostQuests = getQuestPrePost(gameId)
33
39
 
34
40
  return (
35
41
  <CardActionWrapper>
36
42
  <TagsWrapper>
37
- {!!preQuests.pre.length && (
43
+ {!!prePostQuests.pre.length && (
38
44
  <>
39
45
  <SpanText>{t('Requires')}</SpanText>
40
- {preQuests.pre.map((i) => (
46
+ {prePostQuests.pre.map((i) => (
41
47
  <QuestTag key={i} code={i}></QuestTag>
42
48
  ))}
43
49
  </>
@@ -45,10 +51,10 @@ const CardAction = ({ gameId }: { gameId: number }) => {
45
51
  </TagsWrapper>
46
52
 
47
53
  <TagsWrapper>
48
- {!!preQuests.post.length && (
54
+ {!!prePostQuests.post.length && (
49
55
  <>
50
56
  <SpanText>{t('Unlocks')}</SpanText>
51
- {preQuests.post.map((i) => (
57
+ {prePostQuests.post.map((i) => (
52
58
  <QuestTag key={i} code={i}></QuestTag>
53
59
  ))}
54
60
  </>
@@ -62,44 +68,30 @@ export const QuestCard = forwardRef<
62
68
  Card,
63
69
  // eslint-disable-next-line @typescript-eslint/ban-types
64
70
  QuestCardProps & StyledComponentProps<typeof Card, any, {}, never>
65
- >(
66
- (
67
- {
68
- gameId,
69
- code,
70
- name,
71
- desc,
72
- tip,
73
- tip2,
74
- status = QUEST_STATUS.DEFAULT,
75
- ...props
76
- },
77
- ref
78
- ) => {
79
- const headIcon = questIconMap[guessQuestCategory(code).type]
80
- const TailIcon = questStatusMap[status]
71
+ >(({ gameId, code, name, desc, tip, tip2, ...props }, ref) => {
72
+ const status = useQuestStatus(gameId)
73
+ const headIcon = questIconMap[guessQuestCategory(code).type]
74
+ const TailIcon = questStatusMap[status]
75
+ const inPoi = IN_POI
81
76
 
82
- return (
83
- <FlexCard
84
- ref={ref}
85
- elevation={Elevation.ZERO}
86
- interactive={false}
87
- {...props}
88
- >
89
- <CardMedia src={headIcon}></CardMedia>
90
- <CardBody>
91
- <H5>{[code, name].filter((i) => i != undefined).join(' - ')}</H5>
92
- <Text>{desc}</Text>
93
- {tip2 && <b>{tip2}</b>}
94
- {tip && <i>{tip}</i>}
77
+ return (
78
+ <FlexCard
79
+ ref={ref}
80
+ elevation={Elevation.ZERO}
81
+ interactive={false}
82
+ {...props}
83
+ >
84
+ <CardMedia src={headIcon}></CardMedia>
85
+ <CardBody>
86
+ <H5>{[code, name].filter((i) => i != undefined).join(' - ')}</H5>
87
+ <div>{desc}</div>
88
+ {tip2 && <b>{tip2}</b>}
89
+ {tip && <i>{tip}</i>}
95
90
 
96
- <CardAction gameId={gameId}></CardAction>
97
- </CardBody>
91
+ <CardAction gameId={gameId}></CardAction>
92
+ </CardBody>
98
93
 
99
- <CardTail>
100
- <TailIcon />
101
- </CardTail>
102
- </FlexCard>
103
- )
104
- }
105
- )
94
+ <CardTail>{inPoi && <TailIcon />}</CardTail>
95
+ </FlexCard>
96
+ )
97
+ })
@@ -50,6 +50,14 @@ export const questStatusMap: Record<QUEST_STATUS, React.FC> = {
50
50
  </Tooltip>
51
51
  )
52
52
  },
53
+ [QUEST_STATUS.UNKNOWN]: function AlreadyCompleted() {
54
+ const { t } = usePluginTranslation()
55
+ return (
56
+ <Tooltip content={t('Unknown')}>
57
+ <Icon icon={IconNames.HELP} iconSize={Icon.SIZE_LARGE}></Icon>
58
+ </Tooltip>
59
+ )
60
+ },
53
61
  }
54
62
 
55
63
  // transparent GIF pixel
@@ -1,4 +1,5 @@
1
- import React, { useRef, useEffect, useCallback } from 'react'
1
+ import React, { useCallback, useEffect, useRef } from 'react'
2
+ import type { ListRowProps } from 'react-virtualized'
2
3
  // See https://github.com/bvaughn/react-virtualized
3
4
  import {
4
5
  AutoSizer,
@@ -7,13 +8,10 @@ import {
7
8
  List,
8
9
  ListRowRenderer,
9
10
  } from 'react-virtualized'
10
- import type { ListRowProps } from 'react-virtualized'
11
11
  import styled from 'styled-components'
12
- import { QUEST_STATUS } from '../questHelper'
12
+ import { useIsQuestPluginTab } from '../poi/hooks'
13
13
  import type { UnionQuest } from '../questHelper'
14
14
  import { QuestCard } from './QuestCard'
15
- import { useIsQuestPluginTab } from '../poi/hooks'
16
- import { QUEST_API_STATE } from '../poi/types'
17
15
 
18
16
  const QuestListWrapper = styled.div`
19
17
  flex: 1;
@@ -26,28 +24,12 @@ const cache = new CellMeasurerCache({
26
24
  fixedWidth: true,
27
25
  })
28
26
 
29
- const questStateToQuestStatus = (
30
- state: QUEST_API_STATE | undefined
31
- ): QUEST_STATUS => {
32
- switch (state) {
33
- case QUEST_API_STATE.DEFAULT:
34
- return QUEST_STATUS.DEFAULT
35
- case QUEST_API_STATE.COMPLETED:
36
- return QUEST_STATUS.COMPLETED
37
- case QUEST_API_STATE.IN_PROGRESS:
38
- return QUEST_STATUS.IN_PROGRESS
39
- default:
40
- return QUEST_STATUS.DEFAULT
41
- }
42
- }
43
-
44
27
  const useQuestsRowRenderer = (quests: UnionQuest[]) => {
45
28
  const rowRenderer = useCallback(
46
29
  ({ key, index, style, parent }: ListRowProps) => {
47
30
  const quest = quests[index]
48
31
  const { gameId } = quest
49
32
  const { code, name, desc, memo, memo2, pre } = quest.docQuest
50
- const questStatus = questStateToQuestStatus(quest.gameQuest?.api_state)
51
33
 
52
34
  return (
53
35
  <CellMeasurer
@@ -67,7 +49,6 @@ const useQuestsRowRenderer = (quests: UnionQuest[]) => {
67
49
  tip={memo}
68
50
  tip2={memo2}
69
51
  preQuest={pre}
70
- status={questStatus}
71
52
  ></QuestCard>
72
53
  </div>
73
54
  </CellMeasurer>
@@ -78,7 +59,7 @@ const useQuestsRowRenderer = (quests: UnionQuest[]) => {
78
59
  return rowRenderer
79
60
  }
80
61
 
81
- export const QuestList: React.FC<{ quests: UnionQuest[] }> = ({ quests }) => {
62
+ export const QuestList = ({ quests }: { quests: UnionQuest[] }) => {
82
63
  const activeTab = useIsQuestPluginTab()
83
64
  const listRef = useRef<List>(null)
84
65
  const rowRenderer: ListRowRenderer = useQuestsRowRenderer(quests)
@@ -1,23 +1,70 @@
1
- import { Tag } from '@blueprintjs/core'
2
- import React, { useCallback } from 'react'
1
+ import type { TooltipProps } from '@blueprintjs/core'
2
+ import { Tag, Tooltip } from '@blueprintjs/core'
3
+ import { IconNames } from '@blueprintjs/icons'
4
+ import React, { forwardRef, useCallback } from 'react'
3
5
  import styled from 'styled-components'
4
- import { guessQuestCategory } from '../questHelper'
6
+ import { DocQuest, guessQuestCategory, QUEST_STATUS } from '../questHelper'
5
7
  import { useFilterTags } from '../store/filterTags'
8
+ import { useQuestByCode, useQuestStatus } from '../store/quest'
6
9
  import { useSearchInput } from '../store/search'
7
10
 
8
11
  const TagWrapper = styled(Tag)`
9
12
  margin: 2px 4px;
10
- user-select: none;
13
+ user-select: ${({ interactive }) => (interactive ? 'none' : 'auto')};
11
14
  overflow: visible;
12
15
 
13
16
  & > span {
14
- cursor: pointer;
17
+ cursor: ${({ interactive }) => (interactive ? 'pointer' : 'auto')};
15
18
  }
16
19
  `
17
20
 
21
+ const QuestTooltip = forwardRef<
22
+ Tooltip,
23
+ Omit<TooltipProps, 'content'> & {
24
+ quest: DocQuest
25
+ children: React.ReactNode
26
+ }
27
+ >(({ quest, children, ...props }, ref) => {
28
+ if (!quest) {
29
+ return <>{children}</>
30
+ }
31
+ return (
32
+ <Tooltip
33
+ ref={ref}
34
+ content={
35
+ <>
36
+ <div>{`${quest.code} - ${quest.name}`}</div>
37
+ <div>{quest.desc}</div>
38
+ {quest.memo2 && <b>{quest.memo2}</b>}
39
+ {quest.memo && <i>{quest.memo}</i>}
40
+ </>
41
+ }
42
+ placement={'top'}
43
+ {...props}
44
+ >
45
+ {children}
46
+ </Tooltip>
47
+ )
48
+ })
49
+
50
+ const getTagIcon = (questStatus: QUEST_STATUS) => {
51
+ switch (questStatus) {
52
+ case QUEST_STATUS.ALREADY_COMPLETED:
53
+ return IconNames.TICK
54
+ case QUEST_STATUS.LOCKED:
55
+ return IconNames.LOCK
56
+ default:
57
+ return null
58
+ }
59
+ }
60
+
18
61
  export const QuestTag = ({ code }: { code: string }) => {
19
62
  const { setSearchInput } = useSearchInput()
20
63
  const { setCategoryTagsAll, setTypeTagsAll } = useFilterTags()
64
+ const maybeQuest = useQuestByCode(code)
65
+ const maybeGameId = maybeQuest?.gameId ?? null
66
+ const questStatus = useQuestStatus(maybeGameId)
67
+ const tagIcon = getTagIcon(questStatus)
21
68
 
22
69
  const handleClick = useCallback(() => {
23
70
  setSearchInput(code)
@@ -29,13 +76,29 @@ export const QuestTag = ({ code }: { code: string }) => {
29
76
  indicatorColor === '#fff' || indicatorColor === '#87da61'
30
77
  ? 'black'
31
78
  : 'white'
79
+
80
+ if (!maybeQuest) {
81
+ return (
82
+ <TagWrapper
83
+ icon={IconNames.HELP}
84
+ style={{ color: fontColor, background: indicatorColor }}
85
+ >
86
+ {code}
87
+ </TagWrapper>
88
+ )
89
+ }
90
+
91
+ const quest = maybeQuest.docQuest
32
92
  return (
33
- <TagWrapper
34
- onClick={handleClick}
35
- interactive
36
- style={{ color: fontColor, background: indicatorColor }}
37
- >
38
- {code}
39
- </TagWrapper>
93
+ <QuestTooltip quest={quest}>
94
+ <TagWrapper
95
+ interactive
96
+ icon={tagIcon}
97
+ onClick={handleClick}
98
+ style={{ color: fontColor, background: indicatorColor }}
99
+ >
100
+ {code}
101
+ </TagWrapper>
102
+ </QuestTooltip>
40
103
  )
41
104
  }
package/src/poi/hooks.ts CHANGED
@@ -1,9 +1,12 @@
1
- import { useState, useEffect } from 'react'
1
+ import { useEffect, useState } from 'react'
2
2
  import { useTranslation } from 'react-i18next'
3
- import { observePluginStore, observePoiStore } from './store'
4
3
  import { name as PACKAGE_NAME } from '../../package.json'
4
+ import { observePluginStore, observePoiStore } from './store'
5
5
  import { GameQuest, PoiQuestState, PoiState, QuestTab } from './types'
6
6
 
7
+ export const activeQuestsSelector = (state: PoiState): PoiQuestState =>
8
+ state?.info?.quests?.activeQuests ?? {}
9
+
7
10
  export const useActiveQuest = () => {
8
11
  const [activeQuests, setActiveQuests] = useState<PoiQuestState>({})
9
12
 
@@ -17,9 +20,6 @@ export const useActiveQuest = () => {
17
20
  return activeQuests
18
21
  }
19
22
 
20
- export const activeQuestsSelector = (state: PoiState): PoiQuestState =>
21
- state?.info?.quests?.activeQuests ?? {}
22
-
23
23
  export const usePluginTranslation = () => {
24
24
  return useTranslation(PACKAGE_NAME)
25
25
  }
@@ -30,7 +30,7 @@ export const useGameQuest = () => {
30
30
  const listener = (quests: GameQuest[] | null) => setQuests(quests ?? [])
31
31
  // See reducer.ts
32
32
  return observePluginStore(listener, (i) => i?._?.questList)
33
- }, [])
33
+ }, [setQuests])
34
34
  return quests
35
35
  }
36
36
 
@@ -1,9 +1,13 @@
1
- import questCategory from '../build/questCategory.json'
1
+ import moize from 'moize'
2
+ import { QuestData } from '../build/kcanotifyGamedata'
3
+ import { KcwikiQuestData } from '../build/kcQuestsData'
2
4
  import newQuestData from '../build/kcQuestsData/quests-scn-new.json'
3
5
  import prePostQuest from '../build/prePostQuest.json'
6
+ import questCategory from '../build/questCategory.json'
7
+ import questCodeMap from '../build/questCodeMap.json'
4
8
  import { GameQuest, QUEST_API_STATE } from './poi/types'
5
9
 
6
- type DocQuest = {
10
+ export type DocQuest = {
7
11
  code: string
8
12
  name: string
9
13
  desc: string
@@ -35,6 +39,9 @@ export type UnionQuest = {
35
39
  docQuest: DocQuest
36
40
  }
37
41
 
42
+ export const getKcwikiQuestData = () => KcwikiQuestData
43
+ export const getKcanotifyQuestData = () => QuestData
44
+
38
45
  const dailyQuest = new Set(questCategory.dailyQuest)
39
46
  const weeklyQuest = new Set(questCategory.weeklyQuest)
40
47
  const monthlyQuest = new Set(questCategory.monthlyQuest)
@@ -197,17 +204,80 @@ export const isUnknownCategoryQuest = ({ code }: DocQuest) =>
197
204
  // Starts with unknown character
198
205
  /^[^ABCDEFG]/.test(code)
199
206
 
200
- export const getPrePost = (gameId: number) => {
207
+ export const getQuestPrePost = (gameId: number) => {
201
208
  if (!(gameId in prePostQuest)) {
202
209
  return { pre: [], post: [] }
203
210
  }
204
211
  return prePostQuest[String(gameId) as keyof typeof prePostQuest]
205
212
  }
206
213
 
214
+ export const getQuestIdByCode = (code: string) => {
215
+ if (code in questCodeMap) {
216
+ return questCodeMap[code as keyof typeof questCodeMap]
217
+ }
218
+ return null
219
+ }
220
+
221
+ export const getPreQuestIds = (gameId: number): number[] =>
222
+ getQuestPrePost(gameId)
223
+ .pre.map((code) => getQuestIdByCode(code))
224
+ .filter(Boolean) as number[]
225
+
226
+ export const getPostQuestIds = (gameId: number): number[] =>
227
+ getQuestPrePost(gameId)
228
+ .post.map((code) => getQuestIdByCode(code))
229
+ .filter(Boolean) as number[]
230
+
231
+ const calcQuestMap = (
232
+ inProgressQuests: number[],
233
+ next: (gameId: number) => number[]
234
+ ) => {
235
+ const map: Record<number, true> = {}
236
+ const queue: number[] = inProgressQuests.flatMap(next)
237
+ while (queue.length) {
238
+ const gameId = queue.shift()!
239
+ if (gameId in map) {
240
+ continue
241
+ }
242
+ map[gameId] = true
243
+
244
+ next(gameId).forEach((nextGameId) => {
245
+ queue.push(nextGameId)
246
+ })
247
+ }
248
+ return map
249
+ }
250
+
251
+ export const getCompletedQuest = moize((inProgressQuest: number[]) => {
252
+ const completedQuest = calcQuestMap(inProgressQuest, getPreQuestIds)
253
+ return completedQuest
254
+ })
255
+
256
+ export const getLockedQuest = moize((inProgressQuest: number[]) => {
257
+ const lockedQuest = calcQuestMap(inProgressQuest, getPostQuestIds)
258
+ return lockedQuest
259
+ })
260
+
261
+ export const questApiStateToQuestStatus = (
262
+ state: QUEST_API_STATE | undefined
263
+ ): QUEST_STATUS => {
264
+ switch (state) {
265
+ case QUEST_API_STATE.DEFAULT:
266
+ return QUEST_STATUS.DEFAULT
267
+ case QUEST_API_STATE.COMPLETED:
268
+ return QUEST_STATUS.COMPLETED
269
+ case QUEST_API_STATE.IN_PROGRESS:
270
+ return QUEST_STATUS.IN_PROGRESS
271
+ default:
272
+ return QUEST_STATUS.DEFAULT
273
+ }
274
+ }
275
+
207
276
  export enum QUEST_STATUS {
208
277
  LOCKED,
209
278
  DEFAULT,
210
279
  IN_PROGRESS,
211
280
  COMPLETED,
212
281
  ALREADY_COMPLETED,
282
+ UNKNOWN,
213
283
  }
@@ -1,5 +1,5 @@
1
- import { KcwikiQuestData } from '../../build/kcQuestsData'
2
1
  import { useStore } from '.'
2
+ import { getKcwikiQuestData } from '../questHelper'
3
3
 
4
4
  export const usePreferKcwiki = () => {
5
5
  const {
@@ -13,7 +13,10 @@ export const usePreferKcwiki = () => {
13
13
 
14
14
  export const checkIsKcwikiSupportedLanguages = (
15
15
  lang: string
16
- ): lang is keyof typeof KcwikiQuestData => lang in KcwikiQuestData
16
+ ): lang is keyof typeof kcwikiQuestData => {
17
+ const kcwikiQuestData = getKcwikiQuestData()
18
+ return lang in kcwikiQuestData
19
+ }
17
20
 
18
21
  export const useKcwikiData = (lang: string) => {
19
22
  const [preferKcwiki] = usePreferKcwiki()
@@ -25,5 +28,6 @@ export const useKcwikiData = (lang: string) => {
25
28
  if (!supported) {
26
29
  return null
27
30
  }
28
- return KcwikiQuestData[lang]
31
+ const kcwikiQuestData = getKcwikiQuestData()
32
+ return kcwikiQuestData[lang]
29
33
  }
@@ -1,20 +1,31 @@
1
1
  import { useCallback } from 'react'
2
- import { QuestData } from '../../build/kcanotifyGamedata'
3
2
  import { useGameQuest, usePluginTranslation } from '../poi/hooks'
4
- import { getCategory } from '../questHelper'
5
- import type { UnionQuest } from '../questHelper'
6
- import { useKcwikiData, checkIsKcwikiSupportedLanguages } from './kcwiki'
3
+ import {
4
+ DocQuest,
5
+ getCategory,
6
+ getCompletedQuest,
7
+ getKcanotifyQuestData,
8
+ getLockedQuest,
9
+ getQuestIdByCode,
10
+ questApiStateToQuestStatus,
11
+ QUEST_STATUS,
12
+ UnionQuest,
13
+ } from '../questHelper'
14
+ import { checkIsKcwikiSupportedLanguages, useKcwikiData } from './kcwiki'
7
15
  import { useStore, useSyncWithGame } from './store'
8
16
 
9
17
  const DEFAULT_LANG = 'ja-JP'
10
18
 
11
- export const checkIsKcanotifySupportedLanguages = (
19
+ const checkIsKcanotifySupportedLanguages = (
12
20
  lang: string
13
- ): lang is keyof typeof QuestData => lang in QuestData
21
+ ): lang is keyof typeof kcaQuestData => {
22
+ const kcaQuestData = getKcanotifyQuestData()
23
+ return lang in kcaQuestData
24
+ }
14
25
 
15
26
  export const isSupportedLanguages = (
16
27
  lang: string
17
- ): lang is keyof typeof QuestData =>
28
+ ): lang is keyof ReturnType<typeof getKcanotifyQuestData> =>
18
29
  checkIsKcanotifySupportedLanguages(lang) ||
19
30
  checkIsKcwikiSupportedLanguages(lang)
20
31
 
@@ -28,13 +39,14 @@ export const useLanguage = () => {
28
39
  return lang
29
40
  }
30
41
 
31
- const useQuestMap = () => {
42
+ const useQuestMap = (): Record<string, DocQuest> => {
32
43
  const lang = useLanguage()
33
44
  const kcwikiData = useKcwikiData(lang)
34
45
  if (kcwikiData) {
35
46
  return kcwikiData
36
47
  }
37
- return QuestData[lang]
48
+ const kcaQuestData = getKcanotifyQuestData()
49
+ return kcaQuestData[lang]
38
50
  }
39
51
 
40
52
  export const useQuest = (): UnionQuest[] => {
@@ -76,6 +88,40 @@ export const useQuest = (): UnionQuest[] => {
76
88
  }
77
89
  }
78
90
 
91
+ export const useQuestByCode = (code: string): UnionQuest | null => {
92
+ const questMap = useQuestMap()
93
+ const gameId = getQuestIdByCode(code)
94
+ if (gameId && gameId in questMap) {
95
+ return {
96
+ gameId,
97
+ docQuest: questMap[String(gameId) as keyof typeof questMap],
98
+ }
99
+ }
100
+ return null
101
+ }
102
+
103
+ export const useQuestStatus = (gameId: number | null) => {
104
+ const gameQuest = useGameQuest()
105
+ const gameQuestId = gameQuest.map((quest) => quest.api_no)
106
+ const completedQuest = getCompletedQuest(gameQuestId)
107
+ const lockedQuest = getLockedQuest(gameQuestId)
108
+
109
+ if (!gameId) {
110
+ return QUEST_STATUS.UNKNOWN
111
+ }
112
+ const theGameQuest = gameQuest.find((quest) => quest.api_no === gameId)
113
+ if (theGameQuest) {
114
+ return questApiStateToQuestStatus(theGameQuest.api_state)
115
+ }
116
+ if (gameId in lockedQuest) {
117
+ return QUEST_STATUS.LOCKED
118
+ }
119
+ if (gameId in completedQuest) {
120
+ return QUEST_STATUS.ALREADY_COMPLETED
121
+ }
122
+ return QUEST_STATUS.UNKNOWN
123
+ }
124
+
79
125
  /**
80
126
  * @deprecated Not large card now
81
127
  */
package/src/tags.tsx CHANGED
@@ -38,12 +38,12 @@ export const ALL_CATEGORY_TAG = {
38
38
  export const ALL_TYPE_TAG = ALL_CATEGORY_TAG
39
39
 
40
40
  const withDocQuest =
41
- <T extends any>(filterFn: (q: UnionQuest['docQuest']) => T) =>
41
+ <T,>(filterFn: (q: UnionQuest['docQuest']) => T) =>
42
42
  (unionQuest: UnionQuest) =>
43
43
  filterFn(unionQuest.docQuest)
44
44
 
45
45
  const withGameQuestOr =
46
- <T extends any>(filterFn: (q: GameQuest) => T, fallback: T) =>
46
+ <T,>(filterFn: (q: GameQuest) => T, fallback: T) =>
47
47
  ({ gameQuest }: UnionQuest) => {
48
48
  if (!gameQuest) {
49
49
  return fallback
package/tsconfig.json CHANGED
@@ -44,7 +44,7 @@
44
44
  // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
45
45
  // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
46
46
  // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
47
- "importsNotUsedAsValues": "error",
47
+ // "importsNotUsedAsValues": "error",
48
48
 
49
49
  /* Module Resolution Options */
50
50
  // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
@@ -72,5 +72,6 @@
72
72
  /* Advanced Options */
73
73
  "skipLibCheck": true /* Skip type checking of declaration files. */,
74
74
  "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
75
- }
75
+ },
76
+ "exclude": ["./node_modules/**/*"]
76
77
  }