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

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.
@@ -15,3 +15,16 @@ Array [
15
15
  ]
16
16
  `)
17
17
  })
18
+
19
+ describe('should format correct', () => {
20
+ Object.keys(QuestData).forEach((lang) => {
21
+ test(`${lang} key format`, () => {
22
+ Object.keys(QuestData[lang as keyof typeof QuestData]).forEach((key) => {
23
+ // gameId should not extra space
24
+ expect(key.trim()).toEqual(key)
25
+ // gameId should be number
26
+ expect(String(+key)).toEqual(key)
27
+ })
28
+ })
29
+ })
30
+ })
@@ -1,4 +1,5 @@
1
1
  import { version, KcwikiQuestData } from '../../build/kcQuestsData'
2
+ import newQuestData from '../../build/kcQuestsData/quests-scn-new.json'
2
3
 
3
4
  describe('should version correct', () => {
4
5
  test('should KcwikiQuestData Game data version correct', () => {
@@ -25,4 +26,13 @@ describe('should format correct', () => {
25
26
  expect(String(+key)).toEqual(key)
26
27
  })
27
28
  })
29
+
30
+ test('new quest key format', () => {
31
+ Object.keys(newQuestData).forEach((gameId) => {
32
+ // gameId should not extra space
33
+ expect(gameId.trim()).toEqual(gameId)
34
+ // gameId should be number
35
+ expect(String(+gameId)).toEqual(gameId)
36
+ })
37
+ })
28
38
  })
@@ -21,11 +21,11 @@ describe('should questCategory correct', () => {
21
21
  Object.entries(questCategory).map(([key, val]) => [
22
22
  key,
23
23
  val
24
- .sort((a, b) => +a - +b)
24
+ .sort((a, b) => a - b)
25
25
  .map((gameId) => ({
26
26
  gameId,
27
- code: mergeData[gameId as keyof typeof mergeData].code,
28
- name: mergeData[gameId as keyof typeof mergeData].name,
27
+ code: mergeData[String(gameId) as keyof typeof mergeData].code,
28
+ name: mergeData[String(gameId) as keyof typeof mergeData].name,
29
29
  })),
30
30
  ])
31
31
  )
@@ -1,19 +1,16 @@
1
- import { Elevation, Text, Tooltip } from '@blueprintjs/core'
2
- import React from 'react'
1
+ import { Card, Elevation, Text, Tooltip } from '@blueprintjs/core'
2
+ import React, { forwardRef } from 'react'
3
+ import type { StyledComponentProps } from 'styled-components'
3
4
  import { guessQuestCategory, QUEST_STATUS } from '../../questHelper'
4
5
  import type { QuestCardProps } from './index'
5
6
  import { CardBody, CardTail, CatIndicator, FlexCard } from './styles'
6
7
  import { questStatusMap } from './utils'
7
8
 
8
- export const MinimalQuestCard: React.FC<QuestCardProps> = ({
9
- code,
10
- name,
11
- desc,
12
- tip,
13
- status = QUEST_STATUS.DEFAULT,
14
- onClick,
15
- style,
16
- }) => {
9
+ export const MinimalQuestCard = forwardRef<
10
+ Card,
11
+ // eslint-disable-next-line @typescript-eslint/ban-types
12
+ QuestCardProps & StyledComponentProps<typeof Card, any, {}, never>
13
+ >(({ code, name, desc, tip, status = QUEST_STATUS.DEFAULT, ...props }, ref) => {
17
14
  const indicatorColor = guessQuestCategory(code).color
18
15
  const TailIcon = questStatusMap[status]
19
16
 
@@ -29,10 +26,10 @@ export const MinimalQuestCard: React.FC<QuestCardProps> = ({
29
26
  }
30
27
  >
31
28
  <FlexCard
29
+ ref={ref}
32
30
  elevation={Elevation.ZERO}
33
31
  interactive={true}
34
- onClick={onClick}
35
- style={style}
32
+ {...props}
36
33
  >
37
34
  <CatIndicator color={indicatorColor}></CatIndicator>
38
35
  <CardBody>
@@ -45,4 +42,4 @@ export const MinimalQuestCard: React.FC<QuestCardProps> = ({
45
42
  </FlexCard>
46
43
  </Tooltip>
47
44
  )
48
- }
45
+ })
@@ -1,74 +1,105 @@
1
- import { Elevation, H5, Text } from '@blueprintjs/core'
2
- import React from 'react'
3
- import styled from 'styled-components'
1
+ import { Card, Elevation, H5, Text } from '@blueprintjs/core'
2
+ import React, { forwardRef } from 'react'
3
+ import type { StyledComponentProps } from 'styled-components'
4
4
  import { usePluginTranslation } from '../../poi/hooks'
5
- import { guessQuestCategory, QUEST_STATUS } from '../../questHelper'
6
- import { PreTaskTag } from '../PreTaskTag'
7
- import { CardBody, CardMedia, CardTail, FlexCard } from './styles'
5
+ import { getPrePost, guessQuestCategory, QUEST_STATUS } from '../../questHelper'
6
+ import { QuestTag } from '../QuestTag'
7
+ import {
8
+ CardActionWrapper,
9
+ CardBody,
10
+ CardMedia,
11
+ CardTail,
12
+ FlexCard,
13
+ SpanText,
14
+ TagsWrapper,
15
+ } from './styles'
8
16
  import { questIconMap, questStatusMap } from './utils'
9
17
 
10
18
  export type QuestCardProps = {
19
+ gameId: number
11
20
  code: string
12
21
  name: string
13
22
  desc: string | JSX.Element
14
23
  tip?: string
15
24
  tip2?: string
16
25
  status?: QUEST_STATUS
17
- preTask?: string[]
18
- onClick?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
19
- style?: React.CSSProperties
26
+ preQuest?: string[]
20
27
  }
21
28
 
22
- const PreTaskTagWrapper = styled.div`
23
- display: flex;
24
- align-items: baseline;
25
- `
26
-
27
- export const LargeQuestCard = ({
28
- code,
29
- name,
30
- desc,
31
- tip,
32
- tip2,
33
- preTask,
34
- status = QUEST_STATUS.DEFAULT,
35
- onClick,
36
- style,
37
- }: QuestCardProps) => {
38
- const headIcon = questIconMap[guessQuestCategory(code).type]
39
- const TailIcon = questStatusMap[status]
29
+ const CardAction = ({ gameId }: { gameId: number }) => {
40
30
  const { t } = usePluginTranslation()
41
31
 
32
+ const preQuests = getPrePost(gameId)
33
+
42
34
  return (
43
- <FlexCard
44
- elevation={Elevation.ZERO}
45
- interactive={false}
46
- onClick={onClick}
47
- style={style}
48
- >
49
- <CardMedia src={headIcon}></CardMedia>
50
- <CardBody>
51
- <H5>{[code, name].filter((i) => i != undefined).join(' - ')}</H5>
52
- <Text>{desc}</Text>
53
- {tip2 && <b>{tip2}</b>}
54
- {tip && <i>{tip}</i>}
55
- <PreTaskTagWrapper>
56
- {!!preTask?.length && <span>{t('Requires')}</span>}
57
- {preTask?.map((i) => (
58
- <PreTaskTag key={i} code={i}></PreTaskTag>
59
- ))}
60
- </PreTaskTagWrapper>
61
- </CardBody>
35
+ <CardActionWrapper>
36
+ <TagsWrapper>
37
+ {!!preQuests.pre.length && (
38
+ <>
39
+ <SpanText>{t('Requires')}</SpanText>
40
+ {preQuests.pre.map((i) => (
41
+ <QuestTag key={i} code={i}></QuestTag>
42
+ ))}
43
+ </>
44
+ )}
45
+ </TagsWrapper>
62
46
 
63
- <CardTail>
64
- <TailIcon />
65
- </CardTail>
66
- </FlexCard>
47
+ <TagsWrapper>
48
+ {!!preQuests.post.length && (
49
+ <>
50
+ <SpanText>{t('Unlocks')}</SpanText>
51
+ {preQuests.post.map((i) => (
52
+ <QuestTag key={i} code={i}></QuestTag>
53
+ ))}
54
+ </>
55
+ )}
56
+ </TagsWrapper>
57
+ </CardActionWrapper>
67
58
  )
68
59
  }
69
60
 
70
- export const QuestCard: React.FC<QuestCardProps & { gameId: string }> = ({
71
- ...props
72
- }) => {
73
- return <LargeQuestCard {...props}></LargeQuestCard>
74
- }
61
+ export const QuestCard = forwardRef<
62
+ Card,
63
+ // eslint-disable-next-line @typescript-eslint/ban-types
64
+ 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]
81
+
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>}
95
+
96
+ <CardAction gameId={gameId}></CardAction>
97
+ </CardBody>
98
+
99
+ <CardTail>
100
+ <TailIcon />
101
+ </CardTail>
102
+ </FlexCard>
103
+ )
104
+ }
105
+ )
@@ -40,3 +40,20 @@ export const CardTail = styled.div`
40
40
  height: 20px;
41
41
  }
42
42
  `
43
+
44
+ export const CardActionWrapper = styled.div`
45
+ display: flex;
46
+ flex-direction: column;
47
+ align-items: baseline;
48
+ `
49
+
50
+ export const TagsWrapper = styled.div`
51
+ display: flex;
52
+ flex: 1;
53
+ flex-wrap: wrap;
54
+ align-items: center;
55
+ `
56
+
57
+ export const SpanText = styled.span`
58
+ white-space: nowrap;
59
+ `
@@ -11,7 +11,6 @@ import type { ListRowProps } from 'react-virtualized'
11
11
  import styled from 'styled-components'
12
12
  import { QUEST_STATUS } from '../questHelper'
13
13
  import type { UnionQuest } from '../questHelper'
14
- import { useLargeCard } from '../store'
15
14
  import { QuestCard } from './QuestCard'
16
15
  import { useIsQuestPluginTab } from '../poi/hooks'
17
16
  import { QUEST_API_STATE } from '../poi/types'
@@ -67,7 +66,7 @@ const useQuestsRowRenderer = (quests: UnionQuest[]) => {
67
66
  desc={desc}
68
67
  tip={memo}
69
68
  tip2={memo2}
70
- preTask={pre}
69
+ preQuest={pre}
71
70
  status={questStatus}
72
71
  ></QuestCard>
73
72
  </div>
@@ -80,16 +79,14 @@ const useQuestsRowRenderer = (quests: UnionQuest[]) => {
80
79
  }
81
80
 
82
81
  export const QuestList: React.FC<{ quests: UnionQuest[] }> = ({ quests }) => {
83
- const { largeCard } = useLargeCard()
84
82
  const activeTab = useIsQuestPluginTab()
85
83
  const listRef = useRef<List>(null)
86
84
  const rowRenderer: ListRowRenderer = useQuestsRowRenderer(quests)
87
85
 
88
86
  useEffect(() => {
89
- const largeCardIdx = quests.findIndex((i) => i.gameId === largeCard)
90
87
  cache.clearAll()
91
- listRef.current?.recomputeRowHeights(largeCardIdx)
92
- }, [quests, largeCard])
88
+ listRef.current?.recomputeRowHeights()
89
+ }, [quests])
93
90
 
94
91
  useEffect(() => {
95
92
  if (activeTab) {
@@ -6,15 +6,16 @@ import { useFilterTags } from '../store/filterTags'
6
6
  import { useSearchInput } from '../store/search'
7
7
 
8
8
  const TagWrapper = styled(Tag)`
9
- margin: 0 4px;
9
+ margin: 2px 4px;
10
10
  user-select: none;
11
+ overflow: visible;
11
12
 
12
13
  & > span {
13
14
  cursor: pointer;
14
15
  }
15
16
  `
16
17
 
17
- export const PreTaskTag = ({ code }: { code: string }) => {
18
+ export const QuestTag = ({ code }: { code: string }) => {
18
19
  const { setSearchInput } = useSearchInput()
19
20
  const { setCategoryTagsAll, setTypeTagsAll } = useFilterTags()
20
21
 
@@ -1,5 +1,6 @@
1
1
  import questCategory from '../build/questCategory.json'
2
2
  import newQuestData from '../build/kcQuestsData/quests-scn-new.json'
3
+ import prePostQuest from '../build/prePostQuest.json'
3
4
  import { GameQuest, QUEST_API_STATE } from './poi/types'
4
5
 
5
6
  type DocQuest = {
@@ -29,7 +30,7 @@ type DocQuest = {
29
30
  }
30
31
 
31
32
  export type UnionQuest = {
32
- gameId: string
33
+ gameId: number
33
34
  gameQuest?: GameQuest
34
35
  docQuest: DocQuest
35
36
  }
@@ -40,7 +41,7 @@ const monthlyQuest = new Set(questCategory.monthlyQuest)
40
41
  const quarterlyQuest = new Set(questCategory.quarterlyQuest)
41
42
  const yearlyQuest = new Set(questCategory.yearlyQuest)
42
43
  const singleQuest = new Set(questCategory.singleQuest)
43
- const newQuest = new Set(newQuestData)
44
+ const newQuest = new Set(newQuestData.map((gameId) => +gameId))
44
45
 
45
46
  export const isInProgressQuest = (quest: GameQuest) =>
46
47
  quest.api_state === QUEST_API_STATE.IN_PROGRESS ||
@@ -196,6 +197,13 @@ export const isUnknownCategoryQuest = ({ code }: DocQuest) =>
196
197
  // Starts with unknown character
197
198
  /^[^ABCDEFG]/.test(code)
198
199
 
200
+ export const getPrePost = (gameId: number) => {
201
+ if (!(gameId in prePostQuest)) {
202
+ return { pre: [], post: [] }
203
+ }
204
+ return prePostQuest[String(gameId) as keyof typeof prePostQuest]
205
+ }
206
+
199
207
  export enum QUEST_STATUS {
200
208
  LOCKED,
201
209
  DEFAULT,
@@ -44,12 +44,12 @@ export const useQuest = (): UnionQuest[] => {
44
44
 
45
45
  if (syncWithGame && gameQuest.length) {
46
46
  return gameQuest.map((quest) => {
47
- const gameId = String(quest.api_no)
47
+ const gameId = quest.api_no
48
48
  if (gameId in docQuestMap) {
49
49
  return {
50
50
  gameId,
51
51
  gameQuest: quest,
52
- docQuest: docQuestMap[gameId as keyof typeof docQuestMap],
52
+ docQuest: docQuestMap[String(gameId) as keyof typeof docQuestMap],
53
53
  }
54
54
  }
55
55
 
@@ -68,7 +68,7 @@ export const useQuest = (): UnionQuest[] => {
68
68
  } else {
69
69
  // Return all recorded quests
70
70
  return Object.entries(docQuestMap).map(([gameId, val]) => ({
71
- gameId,
71
+ gameId: +gameId,
72
72
  // Maybe empty
73
73
  gameQuest: gameQuest.find((quest) => quest.api_no === Number(gameId))!,
74
74
  docQuest: val,
@@ -1,57 +0,0 @@
1
- import fs from 'fs'
2
- import path from 'path'
3
- import { prepareDir } from './utils'
4
-
5
- const ASSETS_PATH = path.resolve('assets')
6
- const OUTPUT_PATH = path.resolve('build')
7
- const OUTPUT_FILE = path.resolve(OUTPUT_PATH, 'assets.ts')
8
- const CONVERT_EXTS = ['jpg', 'png'] as const
9
-
10
- const HEADER = `/* eslint-disable prettier/prettier */
11
- /**
12
- * This file was automatically generated by \`${path.relative(
13
- // project root
14
- process.cwd(),
15
- __filename
16
- )}\`
17
- * Do not edit this file directly.
18
- */` as const
19
-
20
- function base64Encode(file: string) {
21
- const bitmap = fs.readFileSync(file)
22
- return bitmap.toString('base64')
23
- }
24
-
25
- function main() {
26
- prepareDir(OUTPUT_PATH)
27
- const imageData = fs
28
- .readdirSync(ASSETS_PATH)
29
- // exclusive ignored ext
30
- .filter((f) => CONVERT_EXTS.some((ext) => f.endsWith('.' + ext)))
31
- .map((fileName) => {
32
- const filePath = path.resolve(ASSETS_PATH, fileName)
33
- const parsedFile = path.parse(fileName)
34
- return {
35
- name: parsedFile.name,
36
- ext: parsedFile.ext.slice(1),
37
- base64: base64Encode(filePath),
38
- }
39
- })
40
-
41
- const data = `${HEADER}
42
-
43
- ${imageData
44
- .map(
45
- ({ name, ext, base64 }) =>
46
- `export const ${name} = 'data:image/${ext};base64, ${base64}'`
47
- )
48
- .join('\n')}
49
- `
50
-
51
- fs.writeFileSync(OUTPUT_FILE, data)
52
-
53
- // eslint-disable-next-line no-console
54
- console.log('Converted', imageData.length, 'images.')
55
- }
56
-
57
- main()
@@ -1,136 +0,0 @@
1
- /* eslint-disable no-console */
2
- import { existsSync, readFileSync, writeFileSync } from 'fs'
3
- import pangu from 'pangu'
4
- import path from 'path'
5
- import { fetch } from './proxyFetch'
6
- import { prepareDir } from './utils'
7
-
8
- // See https://github.com/kcwikizh/kcQuests
9
-
10
- const OUTPUT_PATH = path.resolve('build', 'kcQuestsData')
11
- const DATA_FILE_NAME = 'quests-scn.json'
12
- const DATA_URL = `https://kcwikizh.github.io/kcQuests/${DATA_FILE_NAME}`
13
- const NEW_QUEST_FILE_NAME = 'quests-scn-new.json'
14
- const NEW_QUEST_URL = `https://kcwikizh.github.io/kcQuests/${NEW_QUEST_FILE_NAME}`
15
- const VERSION_URL =
16
- 'https://api.github.com/repos/kcwikizh/kcQuests/branches/main'
17
-
18
- // maybe need ignore some expired quest
19
- const IGNORE_DATA = [] as const
20
-
21
- const getRemoteVersion = async () => {
22
- const resp = await fetch(VERSION_URL)
23
- if (!resp.ok) {
24
- throw new Error(`Fetch Error!\nurl: ${resp.url}\nstatus: ${resp.status}`)
25
- }
26
- return (await resp.json()).commit.sha
27
- }
28
-
29
- const getLocalVersion = () => {
30
- const localVersionFile = path.resolve(OUTPUT_PATH, 'DATA_VERSION')
31
- const version = existsSync(localVersionFile)
32
- ? readFileSync(localVersionFile).toString()
33
- : '0'
34
- return version
35
- }
36
-
37
- /**
38
- * @example
39
- * ```ts
40
- * import zh_CN from './quests-scn.json'
41
- * export default {
42
- * 'zh-CN': zh_CN,
43
- * }
44
- * export const version = '5.1.2.1'
45
- * ```
46
- */
47
- const genTS = (version: string) => {
48
- const importCode = `import zh_CN from './quests-scn.json'`
49
-
50
- const exportCode = [
51
- 'export const KcwikiQuestData = {',
52
- ` 'zh-CN': zh_CN,`,
53
- '}',
54
- ].join('\n')
55
-
56
- const versionCode = `export const version = '${version}'`
57
- return `${importCode}\n\n${exportCode}\n\n${versionCode}\n`
58
- }
59
-
60
- const downloadQuestData = async () => {
61
- const resp = await fetch(DATA_URL)
62
- if (!resp.ok) {
63
- console.error(`Fetch Error!\nurl: ${resp.url}\nstatus: ${resp.status}`)
64
- return
65
- }
66
- const text = await resp.text()
67
-
68
- const json = JSON.parse(text) as {
69
- [gameId: string]: {
70
- code: string
71
- name: string
72
- desc: string
73
- memo?: string
74
- memo2?: string
75
- }
76
- }
77
- for (const gameId in json) {
78
- const { code, name, desc, memo, memo2 } = json[gameId]
79
- json[gameId].code = code.trim()
80
- json[gameId].name = pangu.spacing(name)
81
- json[gameId].desc = pangu.spacing(desc)
82
- if (memo) {
83
- json[gameId].memo = pangu.spacing(memo)
84
- }
85
- if (memo2) {
86
- json[gameId].memo2 = pangu.spacing(memo2)
87
- }
88
- }
89
-
90
- IGNORE_DATA.forEach((gameId) => delete json[gameId])
91
- const data = JSON.stringify(json, undefined, 2)
92
- writeFileSync(path.resolve(OUTPUT_PATH, DATA_FILE_NAME), data)
93
- }
94
-
95
- const downloadNewQuest = async () => {
96
- const resp = await fetch(NEW_QUEST_URL)
97
- if (!resp.ok) {
98
- console.error(`Fetch Error!\nurl: ${resp.url}\nstatus: ${resp.status}`)
99
- return
100
- }
101
- const text = await resp.text()
102
- const json = Object.keys(JSON.parse(text))
103
- const data = JSON.stringify(json, undefined, 2)
104
- writeFileSync(path.resolve(OUTPUT_PATH, NEW_QUEST_FILE_NAME), data)
105
- }
106
-
107
- const main = async () => {
108
- const args = process.argv.slice(2)
109
-
110
- prepareDir(OUTPUT_PATH)
111
- const remoteVersion = await getRemoteVersion()
112
- const localVersion = getLocalVersion()
113
- if (remoteVersion === localVersion) {
114
- console.log('The local version is up to date. Version:', localVersion)
115
- if (!args.find((v) => v === '-f' || v === '--force')) {
116
- return
117
- }
118
- } else {
119
- console.log('New Version Detected. Version:', remoteVersion)
120
- }
121
-
122
- console.log(`Download kcQuests data...`)
123
- await Promise.all([downloadQuestData(), downloadNewQuest()])
124
-
125
- const ts = genTS(remoteVersion)
126
- writeFileSync(`${OUTPUT_PATH}/index.ts`, ts)
127
-
128
- // Finally record the version number
129
- writeFileSync(`${OUTPUT_PATH}/DATA_VERSION`, remoteVersion)
130
- }
131
-
132
- main()
133
-
134
- process.on('unhandledRejection', (up) => {
135
- throw up
136
- })