poi-plugin-quest-info-2 0.5.1 → 0.5.5
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.
- package/README.md +17 -2
- package/build/kcQuestsData/DATA_VERSION +1 -1
- package/build/kcQuestsData/index.ts +1 -1
- package/build/kcQuestsData/quests-scn.json +9 -9
- package/build/kcanotifyGamedata/DATA_VERSION +1 -1
- package/build/kcanotifyGamedata/index.ts +1 -1
- package/build/kcanotifyGamedata/quests-ko.json +2 -2
- package/package.json +1 -1
- package/src/App.tsx +1 -1
- package/src/Settings.tsx +1 -1
- package/src/Toolbar.tsx +8 -7
- package/src/__tests__/kcanotifyData.spec.ts +1 -1
- package/src/__tests__/kcwikiData.spec.ts +11 -5
- package/src/components/QuestCard.tsx +8 -8
- package/src/components/QuestList.tsx +54 -20
- package/src/patch.ts +29 -25
- package/src/poi/env.ts +16 -0
- package/src/poi/hooks.ts +56 -0
- package/src/poi/store.ts +80 -0
- package/src/poi/types.ts +117 -0
- package/src/questHelper.ts +26 -22
- package/src/reducer.ts +1 -1
- package/src/store/quest.ts +21 -54
- package/src/tags.ts +15 -9
- package/src/poi.ts +0 -211
package/README.md
CHANGED
|
@@ -5,14 +5,29 @@
|
|
|
5
5
|
|
|
6
6
|
A [poi](https://github.com/poooi/poi) plugin that helps you view quest info. Data maintained by [kcanotify-gamedata](https://github.com/antest1/kcanotify-gamedata) & [kc3-translations](https://github.com/KC3Kai/kc3-translations) & [kcQuests](https://github.com/kcwikizh/kcQuests).
|
|
7
7
|
|
|
8
|
+

|
|
9
|
+
|
|
8
10
|
## Installation
|
|
9
11
|
|
|
10
12
|
Paste `poi-plugin-quest-info-2` in the plugins tab and click the install button.
|
|
11
13
|
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- Translated quest info.(English/Simplified Chinese/Traditional Chinese/Korean)
|
|
17
|
+
- Task panel translation.
|
|
18
|
+
- Quest search and filter.
|
|
19
|
+
- Sync with game quest data.
|
|
20
|
+
- Auto switch to quest tab when enter quest views.
|
|
21
|
+
|
|
12
22
|
## Thanks
|
|
13
23
|
|
|
14
24
|
- [poi](https://github.com/poooi/poi)
|
|
15
|
-
- [kcanotify-gamedata](https://github.com/antest1/kcanotify-gamedata)
|
|
16
|
-
- [poi-plugin-tabex](https://github.com/momocow/poi-plugin-tabex)
|
|
17
25
|
- [plugin-quest](https://github.com/poooi/plugin-quest)
|
|
26
|
+
- [kcanotify-gamedata](https://github.com/antest1/kcanotify-gamedata)
|
|
18
27
|
- [kcQuests](https://github.com/kcwikizh/kcQuests)
|
|
28
|
+
- [舰娘百科](https://zh.kcwiki.cn/wiki/%E8%88%B0%E5%A8%98%E7%99%BE%E7%A7%91)
|
|
29
|
+
- [poi-plugin-tabex](https://github.com/momocow/poi-plugin-tabex)
|
|
30
|
+
|
|
31
|
+
## License
|
|
32
|
+
|
|
33
|
+
MIT
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
004638c4fa18cf4d37e45f3a8704bffe7cdaa49e
|
|
@@ -803,12 +803,6 @@
|
|
|
803
803
|
"memo": "使用霰/霞/阳炎/不知火出击,击破BOSS,B胜及以上可以携带其他舰娘 奖励:开发资材 *2 家具箱(大) *1",
|
|
804
804
|
"name": "「第十八驱逐队」出击!"
|
|
805
805
|
},
|
|
806
|
-
"240": {
|
|
807
|
-
"code": "B22",
|
|
808
|
-
"desc": "派出包含「第三十驱逐队 (第一次)」的舰队出击,与 3-2 | 基斯岛近海 (3-2) 的敌人交战!",
|
|
809
|
-
"memo": "使用睦月/如月/弥生/望月出击,击破BOSS,B胜及以上可以携带其他舰娘C败北可,D败北不可 奖励:间宫 *1",
|
|
810
|
-
"name": "「第三十驱逐队(第一次)」出击!"
|
|
811
|
-
},
|
|
812
806
|
"241": {
|
|
813
807
|
"code": "Bw7",
|
|
814
808
|
"desc": "向北方海域深处出击,捕捉敌人北方舰队的主力,消灭他们!",
|
|
@@ -827,6 +821,12 @@
|
|
|
827
821
|
"memo": "击破BOSS战(S胜)两次 奖励:开发资材 *2 改修资材 *2",
|
|
828
822
|
"name": "掌握南方海域珊瑚诸岛的制空权!"
|
|
829
823
|
},
|
|
824
|
+
"244": {
|
|
825
|
+
"code": "B22",
|
|
826
|
+
"desc": "派出包含「第三十驱逐队 (第一次)」的舰队出击,与 3-2 | 基斯岛近海 (3-2) 的敌人交战!",
|
|
827
|
+
"memo": "使用睦月/如月/弥生/望月出击,击破BOSS,B胜及以上可以携带其他舰娘C败北可,D败北不可 奖励:间宫 *1",
|
|
828
|
+
"name": "「第三十驱逐队(第一次)」出击!"
|
|
829
|
+
},
|
|
830
830
|
"245": {
|
|
831
831
|
"code": "WB01",
|
|
832
832
|
"desc": "在第一舰队旗舰配备高练度 (Lv.90~99) 的舰娘出击,在 2-3 | 东部奥廖尔海 (2-3) 的拂晓刻下胜利!",
|
|
@@ -3235,14 +3235,14 @@
|
|
|
3235
3235
|
},
|
|
3236
3236
|
"957": {
|
|
3237
3237
|
"code": "B172 ",
|
|
3238
|
-
"desc": "以山风改二 /
|
|
3238
|
+
"desc": "以山风改二 / 山风改二丁作为旗舰,编成含旗舰在内共三艘或以上的驱逐舰或海防舰的舰队,出击南西诸岛冲,制油所地带沿岸,南西诸岛防卫线以及镇守府近海!把侵入近海的敌人全都消灭吧!",
|
|
3239
3239
|
"memo": "编成山风改二(丁)为旗舰,3艘及以上DD/DE的舰队1-2、1-3、1-4、1-5各一次S胜 奖励:三式水中探信仪改",
|
|
3240
3240
|
"name": "【山风改二】,拔锚!"
|
|
3241
3241
|
},
|
|
3242
3242
|
"958": {
|
|
3243
3243
|
"code": "B173 ",
|
|
3244
3244
|
"desc": "【山风改二 / 山风改二丁】【江风改二】【海风改二】任选两艘编成舰队,出击南西诸岛巴士海峡,塔威塔威泊地海域深处,南方海域前面,中部北海域孔雀岛海域,击灭敌方战力吧!",
|
|
3245
|
-
"memo": "包含山风改二(丁)/江风改二/海风改二任意两艘的舰队2-2、7-2P2、5-1、6-4各一次S胜 奖励:以下奖励三选一:改修资材 ×5
|
|
3245
|
+
"memo": "包含山风改二(丁)/江风改二/海风改二任意两艘的舰队2-2、7-2P2、5-1、6-4各一次S胜 奖励:以下奖励三选一:改修资材 ×5大発動艇 ×3新型兵装资材 ×2",
|
|
3246
3246
|
"name": "改白露型驱逐舰,【山风改二】,奋战!"
|
|
3247
3247
|
},
|
|
3248
3248
|
"1101": {
|
|
@@ -3284,7 +3284,7 @@
|
|
|
3284
3284
|
"1107": {
|
|
3285
3285
|
"code": "F102 ",
|
|
3286
3286
|
"desc": "把钢材出口,再买入铝材,以此增加陆航的兵力吧!废弃【舰战】【舰攻】类装备各两个,再准备钢材 24000 以及 10 个开发资材!※完成任务时准备的材料会消失。",
|
|
3287
|
-
"memo": "废弃舰战×2、舰攻×2完成任务时消耗钢材×24000、开发资材×10 奖励:以下奖励三选一:九六式陸攻 ×2一式陸攻 新型航空兵装资材",
|
|
3287
|
+
"memo": "年常任务(9月)废弃舰战×2、舰攻×2完成任务时消耗钢材×24000、开发资材×10 奖励:以下奖励三选一:九六式陸攻 ×2一式陸攻 新型航空兵装资材",
|
|
3288
3288
|
"name": "【钢材出口】增加陆基航空队的兵力吧!"
|
|
3289
3289
|
},
|
|
3290
3290
|
"1108": {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
5.3.3.
|
|
1
|
+
5.3.3.1
|
|
@@ -3165,7 +3165,7 @@
|
|
|
3165
3165
|
"code": "B173",
|
|
3166
3166
|
"name": "개 시라츠유급 구축함 「야마카제 개2」, 분전한다!",
|
|
3167
3167
|
"desc": "「야마카제 개2/정」 「카와카제 개2」 「우미카제 개2」 중에서 2척 이상을 포함한 함대로, 남서제도 해역 바시 해협, 타위타위 정박지 앞바다 심부, 남방해역 전면, 중부 북해역 피콕 섬 앞바다에 출격, 적 전력을 격멸하라!",
|
|
3168
|
-
"memo": "※야마카제 개2/정, 카와카제 개2, 우미카제 개2 중 총 2척 이상을 포함한 함대로 2-2, 5-1, 6-4, 7-2-2 각각 S승리 1회 / 보상 : 연료 880, 탄약 880, 보크사이트 880, (개수자재×5 or 대발동정×3 or 신형병장자재×2), (
|
|
3168
|
+
"memo": "※야마카제 개2/정, 카와카제 개2, 우미카제 개2 중 총 2척 이상을 포함한 함대로 2-2, 5-1, 6-4, 7-2-2 각각 S승리 1회 / 보상 : 연료 880, 탄약 880, 보크사이트 880, (개수자재×5 or 대발동정×3 or 신형병장자재×2), (전투상보 or 12.7cm 연장포 C형 개2 or 61cm 4연장(산소)어뢰 후기형)"
|
|
3169
3169
|
},
|
|
3170
3170
|
"959": {
|
|
3171
3171
|
"code": "B174",
|
|
@@ -3177,7 +3177,7 @@
|
|
|
3177
3177
|
"code": "B175",
|
|
3178
3178
|
"name": "속 : 「진수부 꽁치 축제」 발동 준비!",
|
|
3179
3179
|
"desc": "경순(연습순양함 포함) · 잠수모함 · 특무함을 기함으로한 함대로, 북방해역 모레이 해, 키스 섬 앞바다, 알폰시노 방면, 북방해역 전역, 북방 AL 해역에 전개! 북방해역에서의 꽁치잡이에 대비하라!",
|
|
3180
|
-
"memo": "※경순양함/연습순양함, 잠수모함, 특무함 기함인 함대로 3-1, 3-2, 3-3, 3-4, 3-5 각각 S승리 1회 보상 : 연료 500, 탄약 500, 강재 500, (숙련견시원×2 or 특주가구장인 or 개수자재×4)
|
|
3180
|
+
"memo": "※경순양함/연습순양함, 잠수모함, 특무함 기함인 함대로 3-1, 3-2, 3-3, 3-4, 3-5 각각 S승리 1회 보상 : 연료 500, 탄약 500, 강재 500, (숙련견시원×2 or 특주가구장인 or 개수자재×4), (2식 폭뢰×3 or 야간작전 항공요원×2 or 2식 대정)"
|
|
3181
3181
|
},
|
|
3182
3182
|
"961": {
|
|
3183
3183
|
"code": "B176",
|
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import { Text } from '@blueprintjs/core'
|
|
|
4
4
|
|
|
5
5
|
import { Toolbar, useFilterQuest } from './Toolbar'
|
|
6
6
|
import { StoreProvider } from './store'
|
|
7
|
-
import { usePluginTranslation } from './poi'
|
|
7
|
+
import { usePluginTranslation } from './poi/hooks'
|
|
8
8
|
import { QuestList } from './components/QuestList'
|
|
9
9
|
|
|
10
10
|
const Container = styled.div`
|
package/src/Settings.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import { IconNames } from '@blueprintjs/icons'
|
|
|
4
4
|
import styled from 'styled-components'
|
|
5
5
|
import { version as PACKAGE_VERSION, homepage } from '../package.json'
|
|
6
6
|
import { version as DATA_VERSION } from '../build/kcanotifyGamedata'
|
|
7
|
-
import { usePluginTranslation } from './poi'
|
|
7
|
+
import { usePluginTranslation } from './poi/hooks'
|
|
8
8
|
import {
|
|
9
9
|
useRemoveStorage,
|
|
10
10
|
StoreProvider,
|
package/src/Toolbar.tsx
CHANGED
|
@@ -12,8 +12,9 @@ import {
|
|
|
12
12
|
CATEGORY_TAGS,
|
|
13
13
|
ALL_TAGS,
|
|
14
14
|
} from './tags'
|
|
15
|
-
import type {
|
|
16
|
-
import {
|
|
15
|
+
import type { UnionQuest } from './questHelper'
|
|
16
|
+
import { usePluginTranslation } from './poi/hooks'
|
|
17
|
+
import { IN_POI } from './poi/env'
|
|
17
18
|
|
|
18
19
|
const ToolbarWrapper = styled.div`
|
|
19
20
|
display: flex;
|
|
@@ -157,10 +158,10 @@ const useToolbarFilter = () => {
|
|
|
157
158
|
.map((i) => i.toUpperCase())
|
|
158
159
|
|
|
159
160
|
const stringFilter = useCallback(
|
|
160
|
-
(quest:
|
|
161
|
-
const text = `${quest.code} ${quest.name} ${
|
|
162
|
-
quest.
|
|
163
|
-
}`
|
|
161
|
+
(quest: UnionQuest) => {
|
|
162
|
+
const text = `${quest.docQuest.code} ${quest.docQuest.name} ${
|
|
163
|
+
quest.docQuest.desc
|
|
164
|
+
} ${quest.docQuest.memo ?? ''}`
|
|
164
165
|
if (!searchKeywords) {
|
|
165
166
|
return true
|
|
166
167
|
}
|
|
@@ -172,7 +173,7 @@ const useToolbarFilter = () => {
|
|
|
172
173
|
)
|
|
173
174
|
|
|
174
175
|
const toolbarFilter = useCallback(
|
|
175
|
-
(quest:
|
|
176
|
+
(quest: UnionQuest) => {
|
|
176
177
|
return [...tagsFilter, stringFilter].every((filter) => filter(quest))
|
|
177
178
|
},
|
|
178
179
|
[stringFilter, tagsFilter]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { version, QuestData } from '../../build/kcanotifyGamedata'
|
|
2
2
|
|
|
3
3
|
test('should Kcanotify Game data version correct', () => {
|
|
4
|
-
expect(version).toMatchInlineSnapshot(`"5.3.3.
|
|
4
|
+
expect(version).toMatchInlineSnapshot(`"5.3.3.1"`)
|
|
5
5
|
})
|
|
6
6
|
|
|
7
7
|
test('should Kcanotify Game data keys correct', () => {
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { KcwikiQuestData } from '../../build/kcQuestsData'
|
|
1
|
+
import { version, KcwikiQuestData } from '../../build/kcQuestsData'
|
|
2
|
+
|
|
3
|
+
test('should KcwikiQuestData Game data version correct', () => {
|
|
4
|
+
expect(version).toMatchInlineSnapshot(
|
|
5
|
+
`"004638c4fa18cf4d37e45f3a8704bffe7cdaa49e"`
|
|
6
|
+
)
|
|
7
|
+
})
|
|
2
8
|
|
|
3
9
|
test('should KcwikiQuestData Game data keys correct', () => {
|
|
4
10
|
expect(Object.keys(KcwikiQuestData)).toMatchInlineSnapshot(`
|
|
5
|
-
Array [
|
|
6
|
-
|
|
7
|
-
]
|
|
8
|
-
`)
|
|
11
|
+
Array [
|
|
12
|
+
"zh-CN",
|
|
13
|
+
]
|
|
14
|
+
`)
|
|
9
15
|
})
|
|
@@ -2,7 +2,7 @@ import { Card, Elevation, H5, Text, Tooltip, Icon } from '@blueprintjs/core'
|
|
|
2
2
|
import { IconNames } from '@blueprintjs/icons'
|
|
3
3
|
import React, { useCallback } from 'react'
|
|
4
4
|
import styled from 'styled-components'
|
|
5
|
-
import { usePluginTranslation } from '../poi'
|
|
5
|
+
import { usePluginTranslation } from '../poi/hooks'
|
|
6
6
|
import {
|
|
7
7
|
guessQuestCategory,
|
|
8
8
|
QUEST_CATEGORY,
|
|
@@ -75,7 +75,7 @@ const questIconMap = {
|
|
|
75
75
|
} as const
|
|
76
76
|
|
|
77
77
|
const questStatusMap: Record<QUEST_STATUS, React.FC> = {
|
|
78
|
-
[QUEST_STATUS.
|
|
78
|
+
[QUEST_STATUS.LOCKED]: function Locked() {
|
|
79
79
|
const { t } = usePluginTranslation()
|
|
80
80
|
return (
|
|
81
81
|
<Tooltip content={t('Locked')}>
|
|
@@ -84,8 +84,8 @@ const questStatusMap: Record<QUEST_STATUS, React.FC> = {
|
|
|
84
84
|
)
|
|
85
85
|
},
|
|
86
86
|
// Display nothing
|
|
87
|
-
[QUEST_STATUS.
|
|
88
|
-
[QUEST_STATUS.
|
|
87
|
+
[QUEST_STATUS.DEFAULT]: () => null,
|
|
88
|
+
[QUEST_STATUS.IN_PROGRESS]: function InProgress() {
|
|
89
89
|
const { t } = usePluginTranslation()
|
|
90
90
|
return (
|
|
91
91
|
<Tooltip content={t('In Progress')}>
|
|
@@ -93,7 +93,7 @@ const questStatusMap: Record<QUEST_STATUS, React.FC> = {
|
|
|
93
93
|
</Tooltip>
|
|
94
94
|
)
|
|
95
95
|
},
|
|
96
|
-
[QUEST_STATUS.
|
|
96
|
+
[QUEST_STATUS.COMPLETED]: function Completed() {
|
|
97
97
|
const { t } = usePluginTranslation()
|
|
98
98
|
return (
|
|
99
99
|
<Tooltip content={t('Completed')}>
|
|
@@ -101,7 +101,7 @@ const questStatusMap: Record<QUEST_STATUS, React.FC> = {
|
|
|
101
101
|
</Tooltip>
|
|
102
102
|
)
|
|
103
103
|
},
|
|
104
|
-
[QUEST_STATUS.
|
|
104
|
+
[QUEST_STATUS.ALREADY_COMPLETED]: function AlreadyCompleted() {
|
|
105
105
|
const { t } = usePluginTranslation()
|
|
106
106
|
return (
|
|
107
107
|
<Tooltip content={t('Already Completed')}>
|
|
@@ -126,7 +126,7 @@ export const LargeQuestCard: React.FC<QuestCardProps> = ({
|
|
|
126
126
|
name,
|
|
127
127
|
desc,
|
|
128
128
|
tips,
|
|
129
|
-
status = QUEST_STATUS.
|
|
129
|
+
status = QUEST_STATUS.DEFAULT,
|
|
130
130
|
onClick,
|
|
131
131
|
style,
|
|
132
132
|
}) => {
|
|
@@ -159,7 +159,7 @@ export const MinimalQuestCard: React.FC<QuestCardProps> = ({
|
|
|
159
159
|
name,
|
|
160
160
|
desc,
|
|
161
161
|
tips,
|
|
162
|
-
status = QUEST_STATUS.
|
|
162
|
+
status = QUEST_STATUS.DEFAULT,
|
|
163
163
|
onClick,
|
|
164
164
|
style,
|
|
165
165
|
}) => {
|
|
@@ -7,10 +7,14 @@ import {
|
|
|
7
7
|
List,
|
|
8
8
|
ListRowRenderer,
|
|
9
9
|
} from 'react-virtualized'
|
|
10
|
+
import type { ListRowProps } from 'react-virtualized'
|
|
10
11
|
import styled from 'styled-components'
|
|
11
|
-
import {
|
|
12
|
+
import { QUEST_STATUS } from '../questHelper'
|
|
13
|
+
import type { UnionQuest } from '../questHelper'
|
|
12
14
|
import { useLargeCard } from '../store'
|
|
13
15
|
import { QuestCard } from './QuestCard'
|
|
16
|
+
import { useIsQuestPluginTab } from '../poi/hooks'
|
|
17
|
+
import { QUEST_API_STATE } from '../poi/types'
|
|
14
18
|
|
|
15
19
|
const QuestListWrapper = styled.div`
|
|
16
20
|
flex: 1;
|
|
@@ -23,26 +27,29 @@ const cache = new CellMeasurerCache({
|
|
|
23
27
|
fixedWidth: true,
|
|
24
28
|
})
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
const questStateToQuestStatus = (
|
|
31
|
+
state: QUEST_API_STATE | undefined
|
|
32
|
+
): QUEST_STATUS => {
|
|
33
|
+
switch (state) {
|
|
34
|
+
case QUEST_API_STATE.DEFAULT:
|
|
35
|
+
return QUEST_STATUS.DEFAULT
|
|
36
|
+
case QUEST_API_STATE.COMPLETED:
|
|
37
|
+
return QUEST_STATUS.COMPLETED
|
|
38
|
+
case QUEST_API_STATE.IN_PROGRESS:
|
|
39
|
+
return QUEST_STATUS.IN_PROGRESS
|
|
40
|
+
default:
|
|
41
|
+
return QUEST_STATUS.DEFAULT
|
|
42
|
+
}
|
|
43
|
+
}
|
|
37
44
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
const useQuestsRowRenderer = (quests: UnionQuest[]) => {
|
|
46
|
+
const rowRenderer = useCallback(
|
|
47
|
+
({ key, index, style, parent }: ListRowProps) => {
|
|
48
|
+
const quest = quests[index]
|
|
49
|
+
const { gameId } = quest
|
|
50
|
+
const { code, name, desc, memo } = quest.docQuest
|
|
51
|
+
const questStatus = questStateToQuestStatus(quest.gameQuest?.api_state)
|
|
42
52
|
|
|
43
|
-
const rowRenderer: ListRowRenderer = useCallback(
|
|
44
|
-
({ key, index, style, parent }) => {
|
|
45
|
-
const { gameId, code, name, desc, memo, active } = quests[index]
|
|
46
53
|
return (
|
|
47
54
|
<CellMeasurer
|
|
48
55
|
cache={cache}
|
|
@@ -59,7 +66,7 @@ export const QuestList: React.FC<{ quests: KcanotifyQuestExt[] }> = ({
|
|
|
59
66
|
name={name}
|
|
60
67
|
desc={desc}
|
|
61
68
|
tips={memo}
|
|
62
|
-
status={
|
|
69
|
+
status={questStatus}
|
|
63
70
|
></QuestCard>
|
|
64
71
|
</div>
|
|
65
72
|
</CellMeasurer>
|
|
@@ -67,6 +74,33 @@ export const QuestList: React.FC<{ quests: KcanotifyQuestExt[] }> = ({
|
|
|
67
74
|
},
|
|
68
75
|
[quests]
|
|
69
76
|
)
|
|
77
|
+
return rowRenderer
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const QuestList: React.FC<{ quests: UnionQuest[] }> = ({ quests }) => {
|
|
81
|
+
const { largeCard } = useLargeCard()
|
|
82
|
+
const activeTab = useIsQuestPluginTab()
|
|
83
|
+
const listRef = useRef<List>(null)
|
|
84
|
+
const rowRenderer: ListRowRenderer = useQuestsRowRenderer(quests)
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
const largeCardIdx = quests.findIndex((i) => i.gameId === largeCard)
|
|
88
|
+
cache.clearAll()
|
|
89
|
+
listRef.current?.recomputeRowHeights(largeCardIdx)
|
|
90
|
+
}, [quests, largeCard])
|
|
91
|
+
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (activeTab) {
|
|
94
|
+
cache.clearAll()
|
|
95
|
+
listRef.current?.recomputeRowHeights()
|
|
96
|
+
}
|
|
97
|
+
}, [activeTab])
|
|
98
|
+
|
|
99
|
+
const onResize = useCallback(() => {
|
|
100
|
+
cache.clearAll()
|
|
101
|
+
listRef.current?.recomputeRowHeights()
|
|
102
|
+
}, [])
|
|
103
|
+
|
|
70
104
|
if (!quests.length) {
|
|
71
105
|
// Prevent Uncaught Error: Requested index 0 is outside of range 0..0
|
|
72
106
|
// See https://github.com/bvaughn/react-virtualized/issues/1016
|
package/src/patch.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { name as PACKAGE_NAME } from '../package.json'
|
|
2
|
-
import { getPoiStore
|
|
2
|
+
import { getPoiStore } from './poi/store'
|
|
3
|
+
import { importFromPoi } from './poi/env'
|
|
3
4
|
import { QuestData } from '../build/kcanotifyGamedata'
|
|
4
5
|
import { KcwikiQuestData } from '../build/kcQuestsData'
|
|
5
6
|
import {
|
|
@@ -7,6 +8,8 @@ import {
|
|
|
7
8
|
getStorage,
|
|
8
9
|
isSupportedLanguages,
|
|
9
10
|
} from './store'
|
|
11
|
+
import type { i18n } from 'i18next'
|
|
12
|
+
|
|
10
13
|
const LEGACY_QUEST_PLUGIN_ID = 'poi-plugin-quest-info'
|
|
11
14
|
const HACK_KEY = `__patched-from-${PACKAGE_NAME}`
|
|
12
15
|
|
|
@@ -57,36 +60,37 @@ export const patchLegacyQuestPluginReducer = async () => {
|
|
|
57
60
|
return
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
try {
|
|
64
|
+
const i18next: i18n = await importFromPoi('views/env-parts/i18next')
|
|
65
|
+
const language = i18next.language
|
|
66
|
+
const initState = {
|
|
67
|
+
[HACK_KEY]: true,
|
|
68
|
+
quests: getQuestState(language),
|
|
69
|
+
questStatus: {},
|
|
70
|
+
}
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
const reducer = (
|
|
73
|
+
state = initState,
|
|
74
|
+
action: { type: string; [x: string]: any }
|
|
75
|
+
) => {
|
|
76
|
+
switch (action.type) {
|
|
77
|
+
case '@@Config':
|
|
78
|
+
// change language
|
|
79
|
+
if (action.path === 'poi.misc.language') {
|
|
80
|
+
const newLanguage = action.value
|
|
81
|
+
return {
|
|
82
|
+
...state,
|
|
83
|
+
quests: getQuestState(newLanguage),
|
|
84
|
+
}
|
|
79
85
|
}
|
|
80
|
-
|
|
86
|
+
}
|
|
87
|
+
return state
|
|
81
88
|
}
|
|
82
|
-
return state
|
|
83
|
-
}
|
|
84
89
|
|
|
85
|
-
try {
|
|
86
90
|
const { extendReducer } = await importFromPoi('views/create-store')
|
|
87
91
|
extendReducer(LEGACY_QUEST_PLUGIN_ID, reducer)
|
|
88
92
|
} catch (e) {
|
|
89
|
-
console.
|
|
93
|
+
console.error('Hack quest plugin reducer error', e)
|
|
90
94
|
}
|
|
91
95
|
}
|
|
92
96
|
|
|
@@ -105,6 +109,6 @@ export const clearPatchLegacyQuestPluginReducer = async () => {
|
|
|
105
109
|
const clearReducer = undefined
|
|
106
110
|
extendReducer('poi-plugin-quest-info', clearReducer)
|
|
107
111
|
} catch (e) {
|
|
108
|
-
console.
|
|
112
|
+
console.error('Clear hack quest plugin reducer error', e)
|
|
109
113
|
}
|
|
110
114
|
}
|
package/src/poi/env.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const IN_POI = 'POI_VERSION' in globalThis
|
|
2
|
+
/**
|
|
3
|
+
* Prevent webpack early error
|
|
4
|
+
* Module not found: Error: Can't resolve 'views/create-store'
|
|
5
|
+
* TODO fix webpack warn
|
|
6
|
+
* Critical dependency: the request of a dependency is an expression
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const importFromPoi = <T = any>(path: string): Promise<T> => {
|
|
10
|
+
if (!IN_POI) {
|
|
11
|
+
return new Promise(() => {
|
|
12
|
+
// Never resolve
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
return import(path)
|
|
16
|
+
}
|
package/src/poi/hooks.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
import { useTranslation } from 'react-i18next'
|
|
3
|
+
import { observePluginStore, observePoiStore } from './store'
|
|
4
|
+
import { name as PACKAGE_NAME } from '../../package.json'
|
|
5
|
+
import type { GameQuest, PoiQuestState, PoiState } from './types'
|
|
6
|
+
|
|
7
|
+
export const useActiveQuest = () => {
|
|
8
|
+
const [activeQuests, setActiveQuests] = useState<PoiQuestState>({})
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const listener = (activeQuests: PoiQuestState) =>
|
|
12
|
+
setActiveQuests(activeQuests)
|
|
13
|
+
|
|
14
|
+
return observePoiStore(listener, activeQuestsSelector)
|
|
15
|
+
}, [])
|
|
16
|
+
|
|
17
|
+
return activeQuests
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const activeQuestsSelector = (state: PoiState): PoiQuestState =>
|
|
21
|
+
state?.info?.quests?.activeQuests ?? {}
|
|
22
|
+
|
|
23
|
+
export const usePluginTranslation = () => {
|
|
24
|
+
return useTranslation(PACKAGE_NAME)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const useGameQuest = () => {
|
|
28
|
+
const [quests, setQuests] = useState<GameQuest[]>([])
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const listener = (quests: GameQuest[] | null) => setQuests(quests ?? [])
|
|
31
|
+
// See reducer.ts
|
|
32
|
+
return observePluginStore(listener, (i) => i?._?.questList)
|
|
33
|
+
}, [])
|
|
34
|
+
return quests
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const UNKNOWN_TAB = 'unknown'
|
|
38
|
+
const useActiveTab = () => {
|
|
39
|
+
const [activeMainTab, setActiveMainTab] = useState<string>(UNKNOWN_TAB)
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const listener = (activeMainTab: string) => setActiveMainTab(activeMainTab)
|
|
43
|
+
// poooi/poi/views/redux/ui.es
|
|
44
|
+
return observePoiStore(
|
|
45
|
+
listener,
|
|
46
|
+
(state) => state?.ui?.activeMainTab ?? UNKNOWN_TAB
|
|
47
|
+
)
|
|
48
|
+
}, [])
|
|
49
|
+
|
|
50
|
+
return activeMainTab
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const useIsQuestPluginTab = () => {
|
|
54
|
+
const activeMainTab = useActiveTab()
|
|
55
|
+
return activeMainTab === PACKAGE_NAME
|
|
56
|
+
}
|
package/src/poi/store.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { name as PACKAGE_NAME } from '../../package.json'
|
|
2
|
+
import type { PluginState } from '../reducer'
|
|
3
|
+
import { IN_POI, importFromPoi } from './env'
|
|
4
|
+
import type { PoiState, Store } from './types'
|
|
5
|
+
|
|
6
|
+
const noop = () => {}
|
|
7
|
+
const id = <T>(x: T) => x
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* See https://redux.js.org/api/store#subscribelistener
|
|
11
|
+
*/
|
|
12
|
+
const observeStore = <State, SelectedState = State>(
|
|
13
|
+
store: Store<State>,
|
|
14
|
+
onChange: (state: SelectedState) => void,
|
|
15
|
+
selector: (s: State) => SelectedState = id as any
|
|
16
|
+
) => {
|
|
17
|
+
let currentState: SelectedState
|
|
18
|
+
|
|
19
|
+
const handleChange = () => {
|
|
20
|
+
const nextState = selector(store.getState())
|
|
21
|
+
if (nextState !== currentState) {
|
|
22
|
+
currentState = nextState
|
|
23
|
+
onChange(currentState)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const unsubscribe = store.subscribe(handleChange)
|
|
28
|
+
handleChange()
|
|
29
|
+
return unsubscribe
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const observePoiStore = <SelectedState = PoiState>(
|
|
33
|
+
onChange: (state: SelectedState) => void,
|
|
34
|
+
selector: (state: PoiState) => SelectedState = id as any
|
|
35
|
+
) => {
|
|
36
|
+
let valid = true
|
|
37
|
+
let unsubscribe = noop
|
|
38
|
+
getPoiStore().then((store) => {
|
|
39
|
+
if (!valid) {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
unsubscribe = observeStore(store, onChange, selector)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
return () => {
|
|
46
|
+
valid = false
|
|
47
|
+
unsubscribe()
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const observePluginStore = <SelectedState = PluginState>(
|
|
52
|
+
onChange: (state: SelectedState) => void,
|
|
53
|
+
selector: (state: PluginState) => SelectedState = id as any
|
|
54
|
+
) => observePoiStore(onChange, (s) => selector(s?.ext[PACKAGE_NAME]))
|
|
55
|
+
|
|
56
|
+
const fallbackStore: Store<PoiState> = {
|
|
57
|
+
getState: noop as () => PoiState,
|
|
58
|
+
subscribe: () => (() => {}) as () => () => void,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let globalStore: Store<PoiState> | null = null
|
|
62
|
+
/**
|
|
63
|
+
* Get poi global Store if in poi env
|
|
64
|
+
*/
|
|
65
|
+
export const getPoiStore: () => Promise<Store<PoiState>> = async () => {
|
|
66
|
+
if (globalStore !== null) {
|
|
67
|
+
return globalStore
|
|
68
|
+
}
|
|
69
|
+
if (IN_POI) {
|
|
70
|
+
try {
|
|
71
|
+
const { store } = await importFromPoi('views/create-store')
|
|
72
|
+
globalStore = store
|
|
73
|
+
return store
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.warn('Load global store error', error)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
globalStore = fallbackStore
|
|
79
|
+
return fallbackStore
|
|
80
|
+
}
|
package/src/poi/types.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { PluginState } from '../reducer'
|
|
2
|
+
|
|
3
|
+
export enum QUEST_API_STATE {
|
|
4
|
+
DEFAULT = 1,
|
|
5
|
+
IN_PROGRESS = 2,
|
|
6
|
+
COMPLETED = 3,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// See https://github.com/poooi/poi/blob/master/views/redux/info/quests.es
|
|
10
|
+
export type GameQuest = {
|
|
11
|
+
api_state: QUEST_API_STATE
|
|
12
|
+
api_no: number
|
|
13
|
+
api_title: string
|
|
14
|
+
api_detail: string
|
|
15
|
+
/**
|
|
16
|
+
* 任务类别
|
|
17
|
+
*
|
|
18
|
+
* 1. Composition
|
|
19
|
+
* 1. Sortie
|
|
20
|
+
* 1. Exercise
|
|
21
|
+
* 1. Expedition
|
|
22
|
+
* 1. Supply/Docking
|
|
23
|
+
* 1. Arsenal
|
|
24
|
+
* 1. Modernization
|
|
25
|
+
*
|
|
26
|
+
* @see https://github.com/poooi/plugin-quest/blob/master/index.es#L49-L57
|
|
27
|
+
*/
|
|
28
|
+
api_category: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
|
|
29
|
+
/**
|
|
30
|
+
* 任务类型
|
|
31
|
+
*
|
|
32
|
+
* 1. One-time
|
|
33
|
+
* 1. Daily
|
|
34
|
+
* 1. Weekly
|
|
35
|
+
* 1. -3rd/-7th/-0th
|
|
36
|
+
* 1. -2nd/-8th
|
|
37
|
+
* 1. Monthly
|
|
38
|
+
* 1. Quarterly
|
|
39
|
+
*
|
|
40
|
+
* @see https://github.com/poooi/plugin-quest/blob/master/index.es#L69-L77
|
|
41
|
+
*/
|
|
42
|
+
api_type: 1 | 2 | 3 | 4 | 5 | 6 | 7
|
|
43
|
+
// Rewards 油弹钢铝
|
|
44
|
+
api_get_material: [number, number, number, number]
|
|
45
|
+
api_invalid_flag: 0
|
|
46
|
+
api_label_type: 1
|
|
47
|
+
// 0: Empty: [0.0, 0.5)
|
|
48
|
+
// 1: 50%: [0.5, 0.8)
|
|
49
|
+
// 2: 80%: [0.8, 1.0)
|
|
50
|
+
api_progress_flag: 0 | 1 | 2
|
|
51
|
+
api_select_rewards?: [
|
|
52
|
+
{
|
|
53
|
+
api_count: number
|
|
54
|
+
api_kind: number
|
|
55
|
+
api_mst_id: number
|
|
56
|
+
api_no: number
|
|
57
|
+
}[]
|
|
58
|
+
]
|
|
59
|
+
api_voice_id: 0
|
|
60
|
+
api_bonus_flag: 1
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type QuestListAction = {
|
|
64
|
+
type: '@@Response/kcsapi/api_get_member/questlist'
|
|
65
|
+
path: '/kcsapi/api_get_member/questlist'
|
|
66
|
+
postBody: {
|
|
67
|
+
api_verno: '1'
|
|
68
|
+
api_tab_id:
|
|
69
|
+
| '0' // All
|
|
70
|
+
| '9' // In progress
|
|
71
|
+
| '1' // Daily
|
|
72
|
+
| '2' // Weekly
|
|
73
|
+
| '3' // Monthly
|
|
74
|
+
| '4' // Once
|
|
75
|
+
| '5' // Others
|
|
76
|
+
}
|
|
77
|
+
body: {
|
|
78
|
+
api_completed_kind: number
|
|
79
|
+
// api_list.length
|
|
80
|
+
api_count: number
|
|
81
|
+
// In progress count
|
|
82
|
+
api_exec_count: number
|
|
83
|
+
api_exec_type: number
|
|
84
|
+
api_list: GameQuest[]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
type OtherAction = {
|
|
89
|
+
type: 'otherString' // TODO fix me
|
|
90
|
+
path?: string
|
|
91
|
+
postBody?: unknown
|
|
92
|
+
body?: unknown
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type PoiAction = QuestListAction | OtherAction
|
|
96
|
+
|
|
97
|
+
export type PoiState = {
|
|
98
|
+
ui: {
|
|
99
|
+
activeMainTab: string
|
|
100
|
+
activeFleetId?: number
|
|
101
|
+
activePluginName?: string
|
|
102
|
+
}
|
|
103
|
+
ext: {
|
|
104
|
+
// TODO fix use constant PACKAGE_NAME
|
|
105
|
+
[packageName: string]: PluginState
|
|
106
|
+
}
|
|
107
|
+
plugins: { id: string; enabled: boolean; [x: string]: unknown }[]
|
|
108
|
+
[x: string]: any
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type Store<S> = {
|
|
112
|
+
getState: () => S
|
|
113
|
+
subscribe: (listener: () => void) => () => void
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// state.info.quests.activeQuests
|
|
117
|
+
export type PoiQuestState = Record<number, { time: number; detail: GameQuest }>
|
package/src/questHelper.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { QuestData } from '../build/kcanotifyGamedata'
|
|
2
|
+
import { GameQuest, QUEST_API_STATE } from './poi/types'
|
|
2
3
|
|
|
3
|
-
type
|
|
4
|
+
type DocQuest = {
|
|
4
5
|
code: string
|
|
5
6
|
name: string
|
|
6
7
|
desc: string
|
|
@@ -9,9 +10,10 @@ type KcanotifyQuest = {
|
|
|
9
10
|
tracking?: number[][]
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export type
|
|
13
|
+
export type UnionQuest = {
|
|
13
14
|
gameId: string
|
|
14
|
-
|
|
15
|
+
gameQuest?: GameQuest
|
|
16
|
+
docQuest: DocQuest
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
const questStartsFilter = (str: string) =>
|
|
@@ -26,16 +28,18 @@ export const quarterlyQuest = questStartsFilter('(季任)')
|
|
|
26
28
|
// (年任) (年任 / x 月)
|
|
27
29
|
export const yearlyQuest = questStartsFilter('(年任')
|
|
28
30
|
|
|
29
|
-
export const isInProgressQuest = (quest:
|
|
30
|
-
|
|
31
|
+
export const isInProgressQuest = (quest: UnionQuest) =>
|
|
32
|
+
quest.gameQuest?.api_state === QUEST_API_STATE.IN_PROGRESS ||
|
|
33
|
+
quest.gameQuest?.api_state === QUEST_API_STATE.COMPLETED
|
|
34
|
+
export const isDailyQuest = (quest: UnionQuest) =>
|
|
31
35
|
dailyQuest.includes(quest.gameId)
|
|
32
|
-
export const isWeeklyQuest = (quest:
|
|
36
|
+
export const isWeeklyQuest = (quest: UnionQuest) =>
|
|
33
37
|
weeklyQuest.includes(quest.gameId)
|
|
34
|
-
export const isMonthlyQuest = (quest:
|
|
38
|
+
export const isMonthlyQuest = (quest: UnionQuest) =>
|
|
35
39
|
monthlyQuest.includes(quest.gameId)
|
|
36
|
-
export const isQuarterlyQuest = (quest:
|
|
40
|
+
export const isQuarterlyQuest = (quest: UnionQuest) =>
|
|
37
41
|
quarterlyQuest.includes(quest.gameId)
|
|
38
|
-
export const isYearlyQuest = (quest:
|
|
42
|
+
export const isYearlyQuest = (quest: UnionQuest) =>
|
|
39
43
|
yearlyQuest.includes(quest.gameId)
|
|
40
44
|
|
|
41
45
|
export enum QUEST_CATEGORY {
|
|
@@ -154,28 +158,28 @@ export const guessQuestCategory = (
|
|
|
154
158
|
}
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
export const isCompositionQuest = ({ code }:
|
|
161
|
+
export const isCompositionQuest = ({ code }: DocQuest) =>
|
|
158
162
|
guessQuestCategory(code).type === QUEST_CATEGORY.Composition
|
|
159
|
-
export const isSortieQuest = ({ code }:
|
|
163
|
+
export const isSortieQuest = ({ code }: DocQuest) =>
|
|
160
164
|
guessQuestCategory(code).type === QUEST_CATEGORY.Sortie
|
|
161
|
-
export const isExerciseQuest = ({ code }:
|
|
165
|
+
export const isExerciseQuest = ({ code }: DocQuest) =>
|
|
162
166
|
guessQuestCategory(code).type === QUEST_CATEGORY.Exercise
|
|
163
|
-
export const isExpeditionQuest = ({ code }:
|
|
167
|
+
export const isExpeditionQuest = ({ code }: DocQuest) =>
|
|
164
168
|
guessQuestCategory(code).type === QUEST_CATEGORY.Expedition
|
|
165
|
-
export const isSupplyOrDockingQuest = ({ code }:
|
|
169
|
+
export const isSupplyOrDockingQuest = ({ code }: DocQuest) =>
|
|
166
170
|
guessQuestCategory(code).type === QUEST_CATEGORY.SupplyOrDocking
|
|
167
|
-
export const isArsenalQuest = ({ code }:
|
|
171
|
+
export const isArsenalQuest = ({ code }: DocQuest) =>
|
|
168
172
|
guessQuestCategory(code).type === QUEST_CATEGORY.Arsenal
|
|
169
|
-
export const isModernizationQuest = ({ code }:
|
|
173
|
+
export const isModernizationQuest = ({ code }: DocQuest) =>
|
|
170
174
|
guessQuestCategory(code).type === QUEST_CATEGORY.Modernization
|
|
171
|
-
export const isUnknownCategoryQuest = ({ code }:
|
|
175
|
+
export const isUnknownCategoryQuest = ({ code }: DocQuest) =>
|
|
172
176
|
// Starts with unknown character
|
|
173
177
|
/^[^ABCDEFG]/.test(code)
|
|
174
178
|
|
|
175
179
|
export enum QUEST_STATUS {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
LOCKED,
|
|
181
|
+
DEFAULT,
|
|
182
|
+
IN_PROGRESS,
|
|
183
|
+
COMPLETED,
|
|
184
|
+
ALREADY_COMPLETED,
|
|
181
185
|
}
|
package/src/reducer.ts
CHANGED
package/src/store/quest.ts
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
import { useCallback
|
|
2
|
-
import { checkIsKcwikiSupportedLanguages } from '.'
|
|
1
|
+
import { useCallback } from 'react'
|
|
3
2
|
import { QuestData } from '../../build/kcanotifyGamedata'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
observePoiStore,
|
|
9
|
-
PoiQuestState,
|
|
10
|
-
usePluginTranslation,
|
|
11
|
-
} from '../poi'
|
|
12
|
-
import { getCategory, KcanotifyQuestExt } from '../questHelper'
|
|
13
|
-
import { useKcwikiData } from './kcwiki'
|
|
3
|
+
import { useGameQuest, usePluginTranslation } from '../poi/hooks'
|
|
4
|
+
import { getCategory } from '../questHelper'
|
|
5
|
+
import type { UnionQuest } from '../questHelper'
|
|
6
|
+
import { useKcwikiData, checkIsKcwikiSupportedLanguages } from './kcwiki'
|
|
14
7
|
import { useStore } from './store'
|
|
15
8
|
|
|
16
9
|
const DEFAULT_LANG = 'ja-JP'
|
|
@@ -35,19 +28,6 @@ export const useLanguage = () => {
|
|
|
35
28
|
return lang
|
|
36
29
|
}
|
|
37
30
|
|
|
38
|
-
const useActiveQuest = () => {
|
|
39
|
-
const [activeQuests, setActiveQuests] = useState<PoiQuestState>({})
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
const listener = (activeQuests: PoiQuestState) =>
|
|
43
|
-
setActiveQuests(activeQuests)
|
|
44
|
-
|
|
45
|
-
return observePoiStore(listener, activeQuestsSelector)
|
|
46
|
-
}, [])
|
|
47
|
-
|
|
48
|
-
return activeQuests
|
|
49
|
-
}
|
|
50
|
-
|
|
51
31
|
const useQuestMap = () => {
|
|
52
32
|
const lang = useLanguage()
|
|
53
33
|
const kcwikiData = useKcwikiData(lang)
|
|
@@ -57,37 +37,21 @@ const useQuestMap = () => {
|
|
|
57
37
|
return QuestData[lang]
|
|
58
38
|
}
|
|
59
39
|
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
const listener = (quests: GameQuest[] | null) => setQuests(quests)
|
|
64
|
-
// See reducer.ts
|
|
65
|
-
return observePluginStore(listener, (i) => i?._?.questList)
|
|
66
|
-
}, [])
|
|
67
|
-
return quests
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export const useQuest = (): KcanotifyQuestExt[] => {
|
|
71
|
-
const activeQuest = useActiveQuest()
|
|
72
|
-
const questMap = useQuestMap()
|
|
40
|
+
export const useQuest = (): UnionQuest[] => {
|
|
41
|
+
const docQuestMap = useQuestMap()
|
|
73
42
|
const gameQuest = useGameQuest()
|
|
74
43
|
const {
|
|
75
44
|
store: { syncWithGame },
|
|
76
45
|
} = useStore()
|
|
77
46
|
|
|
78
|
-
if (syncWithGame) {
|
|
79
|
-
if (gameQuest == null) {
|
|
80
|
-
// TODO tip use to sync quest data
|
|
81
|
-
return []
|
|
82
|
-
}
|
|
47
|
+
if (syncWithGame && gameQuest.length) {
|
|
83
48
|
return gameQuest.map((quest) => {
|
|
84
49
|
const gameId = String(quest.api_no)
|
|
85
|
-
|
|
86
|
-
if (gameId in questMap) {
|
|
50
|
+
if (gameId in docQuestMap) {
|
|
87
51
|
return {
|
|
88
52
|
gameId,
|
|
89
|
-
|
|
90
|
-
|
|
53
|
+
gameQuest: quest,
|
|
54
|
+
docQuest: docQuestMap[gameId as keyof typeof docQuestMap],
|
|
91
55
|
}
|
|
92
56
|
}
|
|
93
57
|
|
|
@@ -95,18 +59,21 @@ export const useQuest = (): KcanotifyQuestExt[] => {
|
|
|
95
59
|
// May be a new quest
|
|
96
60
|
return {
|
|
97
61
|
gameId,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
62
|
+
gameQuest: quest,
|
|
63
|
+
docQuest: {
|
|
64
|
+
code: `${getCategory(quest.api_category).wikiSymbol}?`,
|
|
65
|
+
name: quest.api_title,
|
|
66
|
+
desc: quest.api_detail,
|
|
67
|
+
},
|
|
102
68
|
}
|
|
103
69
|
})
|
|
104
70
|
} else {
|
|
105
71
|
// Return all recorded quests
|
|
106
|
-
return Object.entries(
|
|
72
|
+
return Object.entries(docQuestMap).map(([gameId, val]) => ({
|
|
107
73
|
gameId,
|
|
108
|
-
|
|
109
|
-
|
|
74
|
+
// Maybe empty
|
|
75
|
+
gameQuest: gameQuest.find((quest) => quest.api_no === Number(gameId))!,
|
|
76
|
+
docQuest: val,
|
|
110
77
|
}))
|
|
111
78
|
}
|
|
112
79
|
}
|
package/src/tags.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IN_POI } from './poi'
|
|
1
|
+
import { IN_POI } from './poi/env'
|
|
2
2
|
import {
|
|
3
3
|
isDailyQuest,
|
|
4
4
|
isMonthlyQuest,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
isUnknownCategoryQuest,
|
|
16
16
|
isInProgressQuest,
|
|
17
17
|
} from './questHelper'
|
|
18
|
+
import type { UnionQuest } from './questHelper'
|
|
18
19
|
|
|
19
20
|
const yes = () => true as const
|
|
20
21
|
|
|
@@ -25,16 +26,21 @@ export const ALL_CATEGORY_TAG = {
|
|
|
25
26
|
|
|
26
27
|
export const ALL_TYPE_TAG = ALL_CATEGORY_TAG
|
|
27
28
|
|
|
29
|
+
const withDocQuest =
|
|
30
|
+
(filterFn: (q: UnionQuest['docQuest']) => boolean) =>
|
|
31
|
+
(unionQuest: UnionQuest) =>
|
|
32
|
+
filterFn(unionQuest.docQuest)
|
|
33
|
+
|
|
28
34
|
export const CATEGORY_TAGS = [
|
|
29
35
|
ALL_CATEGORY_TAG,
|
|
30
|
-
{ name: 'Composition', filter: isCompositionQuest },
|
|
31
|
-
{ name: 'Sortie', filter: isSortieQuest },
|
|
32
|
-
{ name: 'Exercise', filter: isExerciseQuest },
|
|
33
|
-
{ name: 'Expedition', filter: isExpeditionQuest },
|
|
34
|
-
{ name: 'Supply / Docking', filter: isSupplyOrDockingQuest },
|
|
35
|
-
{ name: 'Arsenal', filter: isArsenalQuest },
|
|
36
|
-
{ name: 'Modernization', filter: isModernizationQuest },
|
|
37
|
-
{ name: 'Others', filter: isUnknownCategoryQuest },
|
|
36
|
+
{ name: 'Composition', filter: withDocQuest(isCompositionQuest) },
|
|
37
|
+
{ name: 'Sortie', filter: withDocQuest(isSortieQuest) },
|
|
38
|
+
{ name: 'Exercise', filter: withDocQuest(isExerciseQuest) },
|
|
39
|
+
{ name: 'Expedition', filter: withDocQuest(isExpeditionQuest) },
|
|
40
|
+
{ name: 'Supply / Docking', filter: withDocQuest(isSupplyOrDockingQuest) },
|
|
41
|
+
{ name: 'Arsenal', filter: withDocQuest(isArsenalQuest) },
|
|
42
|
+
{ name: 'Modernization', filter: withDocQuest(isModernizationQuest) },
|
|
43
|
+
{ name: 'Others', filter: withDocQuest(isUnknownCategoryQuest) },
|
|
38
44
|
] as const
|
|
39
45
|
|
|
40
46
|
export const TYPE_TAGS = [
|
package/src/poi.ts
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import { useTranslation } from 'react-i18next'
|
|
2
|
-
import { name as PACKAGE_NAME } from '../package.json'
|
|
3
|
-
import type { PluginState } from './reducer'
|
|
4
|
-
|
|
5
|
-
// See https://github.com/poooi/poi/blob/master/views/redux/info/quests.es
|
|
6
|
-
export type GameQuest = {
|
|
7
|
-
// 1 Default
|
|
8
|
-
// 2 In progress
|
|
9
|
-
// 3 Completed
|
|
10
|
-
api_state: 1 | 2 | 3
|
|
11
|
-
api_no: number
|
|
12
|
-
api_title: string
|
|
13
|
-
api_detail: string
|
|
14
|
-
/**
|
|
15
|
-
* 任务类别
|
|
16
|
-
*
|
|
17
|
-
* 1. Composition
|
|
18
|
-
* 1. Sortie
|
|
19
|
-
* 1. Exercise
|
|
20
|
-
* 1. Expedition
|
|
21
|
-
* 1. Supply/Docking
|
|
22
|
-
* 1. Arsenal
|
|
23
|
-
* 1. Modernization
|
|
24
|
-
*
|
|
25
|
-
* @see https://github.com/poooi/plugin-quest/blob/master/index.es#L49-L57
|
|
26
|
-
*/
|
|
27
|
-
api_category: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
|
|
28
|
-
/**
|
|
29
|
-
* 任务类型
|
|
30
|
-
*
|
|
31
|
-
* 1. One-time
|
|
32
|
-
* 1. Daily
|
|
33
|
-
* 1. Weekly
|
|
34
|
-
* 1. -3rd/-7th/-0th
|
|
35
|
-
* 1. -2nd/-8th
|
|
36
|
-
* 1. Monthly
|
|
37
|
-
* 1. Quarterly
|
|
38
|
-
*
|
|
39
|
-
* @see https://github.com/poooi/plugin-quest/blob/master/index.es#L69-L77
|
|
40
|
-
*/
|
|
41
|
-
api_type: 1 | 2 | 3 | 4 | 5 | 6 | 7
|
|
42
|
-
// Rewards 油弹钢铝
|
|
43
|
-
api_get_material: [number, number, number, number]
|
|
44
|
-
api_invalid_flag: 0
|
|
45
|
-
api_label_type: 1
|
|
46
|
-
// 0: Empty: [0.0, 0.5)
|
|
47
|
-
// 1: 50%: [0.5, 0.8)
|
|
48
|
-
// 2: 80%: [0.8, 1.0)
|
|
49
|
-
api_progress_flag: 0 | 1 | 2
|
|
50
|
-
api_select_rewards?: [
|
|
51
|
-
{
|
|
52
|
-
api_count: number
|
|
53
|
-
api_kind: number
|
|
54
|
-
api_mst_id: number
|
|
55
|
-
api_no: number
|
|
56
|
-
}[]
|
|
57
|
-
]
|
|
58
|
-
api_voice_id: 0
|
|
59
|
-
api_bonus_flag: 1
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
type QuestListAction = {
|
|
63
|
-
type: '@@Response/kcsapi/api_get_member/questlist'
|
|
64
|
-
path: '/kcsapi/api_get_member/questlist'
|
|
65
|
-
postBody: {
|
|
66
|
-
api_verno: '1'
|
|
67
|
-
api_tab_id:
|
|
68
|
-
| '0' // All
|
|
69
|
-
| '9' // In progress
|
|
70
|
-
| '1' // Daily
|
|
71
|
-
| '2' // Weekly
|
|
72
|
-
| '3' // Monthly
|
|
73
|
-
| '4' // Once
|
|
74
|
-
| '5' // Others
|
|
75
|
-
}
|
|
76
|
-
body: {
|
|
77
|
-
api_completed_kind: number
|
|
78
|
-
// api_list.length
|
|
79
|
-
api_count: number
|
|
80
|
-
// In progress count
|
|
81
|
-
api_exec_count: number
|
|
82
|
-
api_exec_type: number
|
|
83
|
-
api_list: GameQuest[]
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
type OtherAction = {
|
|
88
|
-
type: 'otherString' // TODO fix me
|
|
89
|
-
path?: string
|
|
90
|
-
postBody?: unknown
|
|
91
|
-
body?: unknown
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export type PoiAction = QuestListAction | OtherAction
|
|
95
|
-
|
|
96
|
-
export type PoiState = {
|
|
97
|
-
ext: {
|
|
98
|
-
// TODO fix use constant PACKAGE_NAME
|
|
99
|
-
[packageName: string]: PluginState
|
|
100
|
-
}
|
|
101
|
-
plugins: { id: string; enabled: boolean; [x: string]: unknown }[]
|
|
102
|
-
[x: string]: any
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
type Store<S> = {
|
|
106
|
-
getState: () => S
|
|
107
|
-
subscribe: (listener: () => void) => () => void
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// state.info.quests.activeQuests
|
|
111
|
-
export type PoiQuestState = Record<number, { time: number; detail: GameQuest }>
|
|
112
|
-
|
|
113
|
-
export const IN_POI = 'POI_VERSION' in globalThis
|
|
114
|
-
|
|
115
|
-
const noop = () => {}
|
|
116
|
-
const id = <T>(x: T) => x
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Prevent webpack early error
|
|
120
|
-
* Module not found: Error: Can't resolve 'views/create-store'
|
|
121
|
-
* TODO fix webpack warn
|
|
122
|
-
* Critical dependency: the request of a dependency is an expression
|
|
123
|
-
*/
|
|
124
|
-
export const importFromPoi = <T = any>(path: string): Promise<T> => {
|
|
125
|
-
if (!IN_POI) {
|
|
126
|
-
return new Promise(() => {
|
|
127
|
-
// Never resolve
|
|
128
|
-
})
|
|
129
|
-
}
|
|
130
|
-
return import(path)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* See https://redux.js.org/api/store#subscribelistener
|
|
135
|
-
*/
|
|
136
|
-
const observeStore = <State, SelectedState = State>(
|
|
137
|
-
store: Store<State>,
|
|
138
|
-
onChange: (state: SelectedState) => void,
|
|
139
|
-
selector: (s: State) => SelectedState = id as any
|
|
140
|
-
) => {
|
|
141
|
-
let currentState: SelectedState
|
|
142
|
-
|
|
143
|
-
const handleChange = () => {
|
|
144
|
-
const nextState = selector(store.getState())
|
|
145
|
-
if (nextState !== currentState) {
|
|
146
|
-
currentState = nextState
|
|
147
|
-
onChange(currentState)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const unsubscribe = store.subscribe(handleChange)
|
|
152
|
-
handleChange()
|
|
153
|
-
return unsubscribe
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export const observePoiStore = <SelectedState = PoiState>(
|
|
157
|
-
onChange: (state: SelectedState) => void,
|
|
158
|
-
selector: (state: PoiState) => SelectedState = id as any
|
|
159
|
-
) => {
|
|
160
|
-
let valid = true
|
|
161
|
-
let unsubscribe = noop
|
|
162
|
-
getPoiStore().then((store) => {
|
|
163
|
-
if (!valid) {
|
|
164
|
-
return
|
|
165
|
-
}
|
|
166
|
-
unsubscribe = observeStore(store, onChange, selector)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
return () => {
|
|
170
|
-
valid = false
|
|
171
|
-
unsubscribe()
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export const observePluginStore = <SelectedState = PluginState>(
|
|
176
|
-
onChange: (state: SelectedState) => void,
|
|
177
|
-
selector: (state: PluginState) => SelectedState = id as any
|
|
178
|
-
) => observePoiStore(onChange, (s) => selector(s?.ext[PACKAGE_NAME]))
|
|
179
|
-
|
|
180
|
-
const fallbackStore: Store<PoiState> = {
|
|
181
|
-
getState: noop as () => PoiState,
|
|
182
|
-
subscribe: () => (() => {}) as () => () => void,
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
let globalStore: Store<PoiState> | null = null
|
|
186
|
-
/**
|
|
187
|
-
* Get poi global Store if in poi env
|
|
188
|
-
*/
|
|
189
|
-
export const getPoiStore: () => Promise<Store<PoiState>> = async () => {
|
|
190
|
-
if (globalStore !== null) {
|
|
191
|
-
return globalStore
|
|
192
|
-
}
|
|
193
|
-
if (IN_POI) {
|
|
194
|
-
try {
|
|
195
|
-
const { store } = await importFromPoi('views/create-store')
|
|
196
|
-
globalStore = store
|
|
197
|
-
return store
|
|
198
|
-
} catch (error) {
|
|
199
|
-
console.warn('Load global store error', error)
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
globalStore = fallbackStore
|
|
203
|
-
return fallbackStore
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export const activeQuestsSelector = (state: PoiState): PoiQuestState =>
|
|
207
|
-
state?.info?.quests?.activeQuests ?? {}
|
|
208
|
-
|
|
209
|
-
export const usePluginTranslation = () => {
|
|
210
|
-
return useTranslation(PACKAGE_NAME)
|
|
211
|
-
}
|