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 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
+ ![image](https://user-images.githubusercontent.com/18554747/143771661-00965277-5c45-454d-b4f0-57083ec3065d.png)
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
- eb8a7fca38fed2c16e027f2563e93e826d24e30f
1
+ 004638c4fa18cf4d37e45f3a8704bffe7cdaa49e
@@ -4,4 +4,4 @@ export const KcwikiQuestData = {
4
4
  'zh-CN': zh_CN,
5
5
  }
6
6
 
7
- export const version = 'eb8a7fca38fed2c16e027f2563e93e826d24e30f'
7
+ export const version = '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 大発動艇 ×3 新型兵装资材 ×2",
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.0
1
+ 5.3.3.1
@@ -12,4 +12,4 @@ export const QuestData = {
12
12
  'ko-KR': ko_KR,
13
13
  }
14
14
 
15
- export const version = '5.3.3.0'
15
+ export const version = '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), (전투상보/12.7cm 연장포 C형 개2 or 61cm 4연장(산소)어뢰 후기형)"
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)1, (2식 폭뢰×3 or 야간작전 항공요원×2 or 2식 대정)"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poi-plugin-quest-info-2",
3
- "version": "0.5.1",
3
+ "version": "0.5.5",
4
4
  "private": false,
5
5
  "description": "show quest info",
6
6
  "homepage": "https://github.com/lawvs/poi-plugin-quest-2/",
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 { KcanotifyQuestExt } from './questHelper'
16
- import { IN_POI, usePluginTranslation } from './poi'
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: KcanotifyQuestExt) => {
161
- const text = `${quest.code} ${quest.name} ${quest.desc} ${
162
- quest.memo ?? ''
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: KcanotifyQuestExt) => {
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.0"`)
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
- "zh-CN",
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.Locked]: function Locked() {
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.Default]: () => null,
88
- [QUEST_STATUS.InProgress]: function InProgress() {
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.Completed]: function Completed() {
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.AlreadyCompleted]: function AlreadyCompleted() {
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.Default,
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.Default,
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 { KcanotifyQuestExt, QUEST_STATUS } from '../questHelper'
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
- export const QuestList: React.FC<{ quests: KcanotifyQuestExt[] }> = ({
27
- quests,
28
- }) => {
29
- const { largeCard } = useLargeCard()
30
- const listRef = useRef<List>(null)
31
-
32
- useEffect(() => {
33
- const changedIdx = quests.findIndex((i) => i.gameId === largeCard)
34
- cache.clearAll()
35
- listRef.current?.recomputeRowHeights(changedIdx)
36
- }, [quests, largeCard])
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
- const onResize = useCallback(() => {
39
- cache.clearAll()
40
- listRef.current?.recomputeRowHeights()
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={active ? QUEST_STATUS.InProgress : QUEST_STATUS.Default}
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, importFromPoi } from './poi'
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
- const language = (globalThis as any).i18next.language
61
- const initState = {
62
- [HACK_KEY]: true,
63
- quests: getQuestState(language),
64
- questStatus: {},
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
- const reducer = (
68
- state = initState,
69
- action: { type: string; [x: string]: any }
70
- ) => {
71
- switch (action.type) {
72
- case '@@Config':
73
- // change language
74
- if (action.path === 'poi.misc.language') {
75
- const newLanguage = action.value
76
- return {
77
- ...state,
78
- quests: getQuestState(newLanguage),
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.warn('Hack quest plugin reducer error', e)
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.warn('Clear hack quest plugin reducer error', e)
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
+ }
@@ -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
+ }
@@ -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
+ }
@@ -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 }>
@@ -1,6 +1,7 @@
1
1
  import { QuestData } from '../build/kcanotifyGamedata'
2
+ import { GameQuest, QUEST_API_STATE } from './poi/types'
2
3
 
3
- type KcanotifyQuest = {
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 KcanotifyQuestExt = KcanotifyQuest & {
13
+ export type UnionQuest = {
13
14
  gameId: string
14
- active?: boolean
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: KcanotifyQuestExt) => quest.active
30
- export const isDailyQuest = (quest: KcanotifyQuestExt) =>
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: KcanotifyQuestExt) =>
36
+ export const isWeeklyQuest = (quest: UnionQuest) =>
33
37
  weeklyQuest.includes(quest.gameId)
34
- export const isMonthlyQuest = (quest: KcanotifyQuestExt) =>
38
+ export const isMonthlyQuest = (quest: UnionQuest) =>
35
39
  monthlyQuest.includes(quest.gameId)
36
- export const isQuarterlyQuest = (quest: KcanotifyQuestExt) =>
40
+ export const isQuarterlyQuest = (quest: UnionQuest) =>
37
41
  quarterlyQuest.includes(quest.gameId)
38
- export const isYearlyQuest = (quest: KcanotifyQuestExt) =>
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 }: KcanotifyQuestExt) =>
161
+ export const isCompositionQuest = ({ code }: DocQuest) =>
158
162
  guessQuestCategory(code).type === QUEST_CATEGORY.Composition
159
- export const isSortieQuest = ({ code }: KcanotifyQuestExt) =>
163
+ export const isSortieQuest = ({ code }: DocQuest) =>
160
164
  guessQuestCategory(code).type === QUEST_CATEGORY.Sortie
161
- export const isExerciseQuest = ({ code }: KcanotifyQuestExt) =>
165
+ export const isExerciseQuest = ({ code }: DocQuest) =>
162
166
  guessQuestCategory(code).type === QUEST_CATEGORY.Exercise
163
- export const isExpeditionQuest = ({ code }: KcanotifyQuestExt) =>
167
+ export const isExpeditionQuest = ({ code }: DocQuest) =>
164
168
  guessQuestCategory(code).type === QUEST_CATEGORY.Expedition
165
- export const isSupplyOrDockingQuest = ({ code }: KcanotifyQuestExt) =>
169
+ export const isSupplyOrDockingQuest = ({ code }: DocQuest) =>
166
170
  guessQuestCategory(code).type === QUEST_CATEGORY.SupplyOrDocking
167
- export const isArsenalQuest = ({ code }: KcanotifyQuestExt) =>
171
+ export const isArsenalQuest = ({ code }: DocQuest) =>
168
172
  guessQuestCategory(code).type === QUEST_CATEGORY.Arsenal
169
- export const isModernizationQuest = ({ code }: KcanotifyQuestExt) =>
173
+ export const isModernizationQuest = ({ code }: DocQuest) =>
170
174
  guessQuestCategory(code).type === QUEST_CATEGORY.Modernization
171
- export const isUnknownCategoryQuest = ({ code }: KcanotifyQuestExt) =>
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
- Locked,
177
- Default,
178
- InProgress,
179
- Completed,
180
- AlreadyCompleted,
180
+ LOCKED,
181
+ DEFAULT,
182
+ IN_PROGRESS,
183
+ COMPLETED,
184
+ ALREADY_COMPLETED,
181
185
  }
package/src/reducer.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { GameQuest, PoiAction } from './poi'
1
+ import type { GameQuest, PoiAction } from './poi/types'
2
2
 
3
3
  const initState = {
4
4
  questList: null as null | GameQuest[],
@@ -1,16 +1,9 @@
1
- import { useCallback, useEffect, useState } from 'react'
2
- import { checkIsKcwikiSupportedLanguages } from '.'
1
+ import { useCallback } from 'react'
3
2
  import { QuestData } from '../../build/kcanotifyGamedata'
4
- import {
5
- activeQuestsSelector,
6
- GameQuest,
7
- observePluginStore,
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 useGameQuest = () => {
61
- const [quests, setQuests] = useState<GameQuest[] | null>(null)
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
- const active = gameId in activeQuest
86
- if (gameId in questMap) {
50
+ if (gameId in docQuestMap) {
87
51
  return {
88
52
  gameId,
89
- active,
90
- ...questMap[gameId as unknown as keyof typeof questMap],
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
- active,
99
- code: `${getCategory(quest.api_category).wikiSymbol}?`,
100
- name: quest.api_title,
101
- desc: quest.api_detail,
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(questMap).map(([gameId, val]) => ({
72
+ return Object.entries(docQuestMap).map(([gameId, val]) => ({
107
73
  gameId,
108
- active: gameId in activeQuest,
109
- ...val,
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
- }