poi-plugin-quest-info-2 0.4.2 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/build.yml +1 -1
- package/.github/workflows/publish.yml +1 -1
- package/.vscode/extensions.json +6 -0
- package/.vscode/settings.json +5 -1
- package/README.md +2 -1
- package/build/kcQuestsData/DATA_VERSION +1 -0
- package/build/kcQuestsData/index.ts +7 -0
- package/build/kcQuestsData/quests-scn.json +3296 -0
- package/build/kcanotifyGamedata/DATA_VERSION +1 -1
- package/build/kcanotifyGamedata/index.ts +1 -1
- package/build/kcanotifyGamedata/quests-en.json +84 -36
- package/build/kcanotifyGamedata/quests-jp.json +84 -36
- package/build/kcanotifyGamedata/quests-ko.json +84 -36
- package/build/kcanotifyGamedata/quests-scn.json +82 -36
- package/build/kcanotifyGamedata/quests-tcn.json +82 -36
- package/i18n/en-US.json +1 -0
- package/i18n/ja-JP.json +1 -0
- package/i18n/ko-KR.json +1 -0
- package/i18n/zh-CN.json +2 -1
- package/i18n/zh-TW.json +1 -0
- package/package.json +6 -14
- package/scripts/downloadKcQuestsData.ts +107 -0
- package/scripts/downloadKcanotifyGamedata.ts +2 -0
- package/src/Settings.tsx +52 -22
- package/src/Toolbar.tsx +4 -2
- package/src/__tests__/kcanotifyData.spec.ts +14 -2
- package/src/__tests__/kcwikiData.spec.ts +9 -0
- package/src/components/QuestCard.tsx +4 -0
- package/src/patch.ts +28 -18
- package/src/questHelper.ts +1 -1
- package/src/store/index.ts +1 -0
- package/src/store/kcwiki.ts +29 -0
- package/src/store/quest.ts +28 -13
- package/src/store/store.tsx +24 -24
- package/src/utils.ts +0 -77
package/src/Settings.tsx
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import React, { StrictMode, useCallback } from 'react'
|
|
2
|
-
import { Button, AnchorButton, Text } from '@blueprintjs/core'
|
|
2
|
+
import { Button, AnchorButton, Text, Checkbox } from '@blueprintjs/core'
|
|
3
3
|
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
7
|
import { usePluginTranslation } from './poi'
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
useRemoveStorage,
|
|
10
|
+
StoreProvider,
|
|
11
|
+
useLanguage,
|
|
12
|
+
usePreferKcwiki,
|
|
13
|
+
} from './store'
|
|
9
14
|
|
|
10
15
|
const Container = styled.div`
|
|
11
16
|
display: flex;
|
|
@@ -18,27 +23,52 @@ const Container = styled.div`
|
|
|
18
23
|
}
|
|
19
24
|
`
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
const useIsSimplifiedChinese = () => useLanguage() === 'zh-CN'
|
|
27
|
+
|
|
28
|
+
const SettingsMain = () => {
|
|
22
29
|
const { t } = usePluginTranslation()
|
|
23
|
-
const
|
|
30
|
+
const isSimplifiedChinese = useIsSimplifiedChinese()
|
|
31
|
+
const removeStorage = useRemoveStorage()
|
|
32
|
+
const [preferKcwiki, setPreferKcwiki] = usePreferKcwiki()
|
|
33
|
+
const handleEnabledChange: React.FormEventHandler<HTMLInputElement> =
|
|
34
|
+
useCallback(() => {
|
|
35
|
+
setPreferKcwiki(!preferKcwiki)
|
|
36
|
+
}, [preferKcwiki, setPreferKcwiki])
|
|
37
|
+
|
|
24
38
|
return (
|
|
25
|
-
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
<>
|
|
40
|
+
<Checkbox
|
|
41
|
+
checked={preferKcwiki}
|
|
42
|
+
disabled={!isSimplifiedChinese}
|
|
43
|
+
label={t('Use Kcwiki data')}
|
|
44
|
+
onChange={handleEnabledChange}
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
<Text>{t('Version', { version: PACKAGE_VERSION })}</Text>
|
|
48
|
+
<Text>{t('Data Version', { version: DATA_VERSION })}</Text>
|
|
49
|
+
<AnchorButton
|
|
50
|
+
icon={IconNames.CODE}
|
|
51
|
+
rightIcon={IconNames.SHARE}
|
|
52
|
+
text={t('View source code on GitHub')}
|
|
53
|
+
href={homepage}
|
|
54
|
+
target="_blank"
|
|
55
|
+
></AnchorButton>
|
|
56
|
+
|
|
57
|
+
<Button
|
|
58
|
+
icon={IconNames.TRASH}
|
|
59
|
+
text={t('Restore defaults')}
|
|
60
|
+
onClick={removeStorage}
|
|
61
|
+
></Button>
|
|
62
|
+
</>
|
|
43
63
|
)
|
|
44
64
|
}
|
|
65
|
+
|
|
66
|
+
export const Settings = () => (
|
|
67
|
+
<StrictMode>
|
|
68
|
+
<StoreProvider>
|
|
69
|
+
<Container>
|
|
70
|
+
<SettingsMain />
|
|
71
|
+
</Container>
|
|
72
|
+
</StoreProvider>
|
|
73
|
+
</StrictMode>
|
|
74
|
+
)
|
package/src/Toolbar.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { IconNames } from '@blueprintjs/icons'
|
|
|
3
3
|
import styled from 'styled-components'
|
|
4
4
|
import React, { useCallback } from 'react'
|
|
5
5
|
import type { ChangeEvent } from 'react'
|
|
6
|
-
import { useThrottle } from '
|
|
6
|
+
import { useThrottle } from 'react-use'
|
|
7
7
|
import { useQuest, useStore } from './store'
|
|
8
8
|
import {
|
|
9
9
|
ALL_TYPE_TAG,
|
|
@@ -158,7 +158,9 @@ const useToolbarFilter = () => {
|
|
|
158
158
|
|
|
159
159
|
const stringFilter = useCallback(
|
|
160
160
|
(quest: KcanotifyQuestExt) => {
|
|
161
|
-
const text = `${quest.code} ${quest.name} ${quest.desc}
|
|
161
|
+
const text = `${quest.code} ${quest.name} ${quest.desc} ${
|
|
162
|
+
quest.memo ?? ''
|
|
163
|
+
}`
|
|
162
164
|
if (!searchKeywords) {
|
|
163
165
|
return true
|
|
164
166
|
}
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import { version } from '../../build/kcanotifyGamedata'
|
|
1
|
+
import { version, QuestData } from '../../build/kcanotifyGamedata'
|
|
2
2
|
|
|
3
3
|
test('should Kcanotify Game data version correct', () => {
|
|
4
|
-
expect(version).toMatchInlineSnapshot(`"5.3.0
|
|
4
|
+
expect(version).toMatchInlineSnapshot(`"5.3.3.0"`)
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
test('should Kcanotify Game data keys correct', () => {
|
|
8
|
+
expect(Object.keys(QuestData)).toMatchInlineSnapshot(`
|
|
9
|
+
Array [
|
|
10
|
+
"zh-CN",
|
|
11
|
+
"zh-TW",
|
|
12
|
+
"ja-JP",
|
|
13
|
+
"en-US",
|
|
14
|
+
"ko-KR",
|
|
15
|
+
]
|
|
16
|
+
`)
|
|
5
17
|
})
|
package/src/patch.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { name as PACKAGE_NAME } from '../package.json'
|
|
2
2
|
import { getPoiStore, importFromPoi } from './poi'
|
|
3
3
|
import { QuestData } from '../build/kcanotifyGamedata'
|
|
4
|
-
|
|
4
|
+
import { KcwikiQuestData } from '../build/kcQuestsData'
|
|
5
|
+
import {
|
|
6
|
+
checkIsKcwikiSupportedLanguages,
|
|
7
|
+
getStorage,
|
|
8
|
+
isSupportedLanguages,
|
|
9
|
+
} from './store'
|
|
5
10
|
const LEGACY_QUEST_PLUGIN_ID = 'poi-plugin-quest-info'
|
|
6
11
|
const HACK_KEY = `__patched-from-${PACKAGE_NAME}`
|
|
7
12
|
|
|
@@ -19,6 +24,28 @@ const isLegacyQuestPluginEnabled = async () => {
|
|
|
19
24
|
return false
|
|
20
25
|
}
|
|
21
26
|
|
|
27
|
+
const getQuestState = (maybeLanguage: string) => {
|
|
28
|
+
const supported = isSupportedLanguages(maybeLanguage)
|
|
29
|
+
if (!supported) {
|
|
30
|
+
return {}
|
|
31
|
+
}
|
|
32
|
+
const preferKcwikiData = getStorage()?.preferKcwikiData ?? true
|
|
33
|
+
const kcwikiSupported = checkIsKcwikiSupportedLanguages(maybeLanguage)
|
|
34
|
+
|
|
35
|
+
const data =
|
|
36
|
+
preferKcwikiData && kcwikiSupported
|
|
37
|
+
? KcwikiQuestData[maybeLanguage]
|
|
38
|
+
: QuestData[maybeLanguage]
|
|
39
|
+
|
|
40
|
+
return Object.entries(data).reduce((acc, [apiNo, { code, desc }]) => {
|
|
41
|
+
acc[apiNo] = {
|
|
42
|
+
wiki_id: code,
|
|
43
|
+
condition: desc,
|
|
44
|
+
}
|
|
45
|
+
return acc
|
|
46
|
+
}, {} as Record<string, { wiki_id: string; condition: string }>)
|
|
47
|
+
}
|
|
48
|
+
|
|
22
49
|
/**
|
|
23
50
|
* Patch the reducer of `poi-plugin-quest-info` for poi's task panel tips
|
|
24
51
|
* See https://github.com/poooi/poi/blob/da75b507e8f67615a39dc4fdb466e34ff5b5bdcf/views/components/main/parts/task-panel.es#L243
|
|
@@ -30,23 +57,6 @@ export const patchLegacyQuestPluginReducer = async () => {
|
|
|
30
57
|
return
|
|
31
58
|
}
|
|
32
59
|
|
|
33
|
-
const getQuestState = (maybeLanguage: string) => {
|
|
34
|
-
if (!(maybeLanguage in QuestData)) {
|
|
35
|
-
return {}
|
|
36
|
-
}
|
|
37
|
-
const lang = maybeLanguage as keyof typeof QuestData
|
|
38
|
-
return Object.entries(QuestData[lang]).reduce(
|
|
39
|
-
(acc, [apiNo, { code, desc }]) => {
|
|
40
|
-
acc[apiNo] = {
|
|
41
|
-
wiki_id: code,
|
|
42
|
-
condition: desc,
|
|
43
|
-
}
|
|
44
|
-
return acc
|
|
45
|
-
},
|
|
46
|
-
{} as Record<string, { wiki_id: string; condition: string }>
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
60
|
const language = (globalThis as any).i18next.language
|
|
51
61
|
const initState = {
|
|
52
62
|
[HACK_KEY]: true,
|
package/src/questHelper.ts
CHANGED
package/src/store/index.ts
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { KcwikiQuestData } from '../../build/kcQuestsData'
|
|
2
|
+
import { useStore } from '.'
|
|
3
|
+
|
|
4
|
+
export const usePreferKcwiki = () => {
|
|
5
|
+
const {
|
|
6
|
+
store: { preferKcwikiData },
|
|
7
|
+
updateStore,
|
|
8
|
+
} = useStore()
|
|
9
|
+
const setPreferKcwikiData = (val: boolean) =>
|
|
10
|
+
updateStore({ preferKcwikiData: val })
|
|
11
|
+
return [preferKcwikiData, setPreferKcwikiData] as const
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const checkIsKcwikiSupportedLanguages = (
|
|
15
|
+
lang: string
|
|
16
|
+
): lang is keyof typeof KcwikiQuestData => lang in KcwikiQuestData
|
|
17
|
+
|
|
18
|
+
export const useKcwikiData = (lang: string) => {
|
|
19
|
+
const [preferKcwiki] = usePreferKcwiki()
|
|
20
|
+
const supported = checkIsKcwikiSupportedLanguages(lang)
|
|
21
|
+
|
|
22
|
+
if (!preferKcwiki) {
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
if (!supported) {
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
return KcwikiQuestData[lang]
|
|
29
|
+
}
|
package/src/store/quest.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from 'react'
|
|
2
|
+
import { checkIsKcwikiSupportedLanguages } from '.'
|
|
2
3
|
import { QuestData } from '../../build/kcanotifyGamedata'
|
|
3
4
|
import {
|
|
4
5
|
activeQuestsSelector,
|
|
@@ -9,17 +10,29 @@ import {
|
|
|
9
10
|
usePluginTranslation,
|
|
10
11
|
} from '../poi'
|
|
11
12
|
import { getCategory, KcanotifyQuestExt } from '../questHelper'
|
|
13
|
+
import { useKcwikiData } from './kcwiki'
|
|
12
14
|
import { useStore } from './store'
|
|
13
15
|
|
|
14
16
|
const DEFAULT_LANG = 'ja-JP'
|
|
15
17
|
|
|
18
|
+
export const checkIsKcanotifySupportedLanguages = (
|
|
19
|
+
lang: string
|
|
20
|
+
): lang is keyof typeof QuestData => lang in QuestData
|
|
21
|
+
|
|
22
|
+
export const isSupportedLanguages = (
|
|
23
|
+
lang: string
|
|
24
|
+
): lang is keyof typeof QuestData =>
|
|
25
|
+
checkIsKcanotifySupportedLanguages(lang) ||
|
|
26
|
+
checkIsKcwikiSupportedLanguages(lang)
|
|
27
|
+
|
|
16
28
|
export const useLanguage = () => {
|
|
17
|
-
const {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
const {
|
|
30
|
+
i18n: { language },
|
|
31
|
+
} = usePluginTranslation()
|
|
32
|
+
const lang = checkIsKcanotifySupportedLanguages(language)
|
|
33
|
+
? language
|
|
34
|
+
: DEFAULT_LANG
|
|
35
|
+
return lang
|
|
23
36
|
}
|
|
24
37
|
|
|
25
38
|
const useActiveQuest = () => {
|
|
@@ -35,8 +48,12 @@ const useActiveQuest = () => {
|
|
|
35
48
|
return activeQuests
|
|
36
49
|
}
|
|
37
50
|
|
|
38
|
-
const
|
|
51
|
+
const useQuestMap = () => {
|
|
39
52
|
const lang = useLanguage()
|
|
53
|
+
const kcwikiData = useKcwikiData(lang)
|
|
54
|
+
if (kcwikiData) {
|
|
55
|
+
return kcwikiData
|
|
56
|
+
}
|
|
40
57
|
return QuestData[lang]
|
|
41
58
|
}
|
|
42
59
|
|
|
@@ -52,7 +69,7 @@ const useGameQuest = () => {
|
|
|
52
69
|
|
|
53
70
|
export const useQuest = (): KcanotifyQuestExt[] => {
|
|
54
71
|
const activeQuest = useActiveQuest()
|
|
55
|
-
const
|
|
72
|
+
const questMap = useQuestMap()
|
|
56
73
|
const gameQuest = useGameQuest()
|
|
57
74
|
const {
|
|
58
75
|
store: { syncWithGame },
|
|
@@ -66,13 +83,11 @@ export const useQuest = (): KcanotifyQuestExt[] => {
|
|
|
66
83
|
return gameQuest.map((quest) => {
|
|
67
84
|
const gameId = String(quest.api_no)
|
|
68
85
|
const active = gameId in activeQuest
|
|
69
|
-
if (gameId in
|
|
86
|
+
if (gameId in questMap) {
|
|
70
87
|
return {
|
|
71
88
|
gameId,
|
|
72
89
|
active,
|
|
73
|
-
...
|
|
74
|
-
gameId as unknown as keyof typeof kcanotifyQuestMap
|
|
75
|
-
],
|
|
90
|
+
...questMap[gameId as unknown as keyof typeof questMap],
|
|
76
91
|
}
|
|
77
92
|
}
|
|
78
93
|
|
|
@@ -88,7 +103,7 @@ export const useQuest = (): KcanotifyQuestExt[] => {
|
|
|
88
103
|
})
|
|
89
104
|
} else {
|
|
90
105
|
// Return all recorded quests
|
|
91
|
-
return Object.entries(
|
|
106
|
+
return Object.entries(questMap).map(([gameId, val]) => ({
|
|
92
107
|
gameId,
|
|
93
108
|
active: gameId in activeQuest,
|
|
94
109
|
...val,
|
package/src/store/store.tsx
CHANGED
|
@@ -4,13 +4,10 @@ import React, {
|
|
|
4
4
|
SetStateAction,
|
|
5
5
|
useCallback,
|
|
6
6
|
useContext,
|
|
7
|
-
useEffect,
|
|
8
|
-
useState,
|
|
9
7
|
} from 'react'
|
|
10
|
-
import { IN_POI } from '../poi'
|
|
11
8
|
import { ALL_TYPE_TAG, ALL_CATEGORY_TAG } from '../tags'
|
|
12
9
|
import { name as PACKAGE_NAME } from '../../package.json'
|
|
13
|
-
import { useMount, useUpdateEffect } from '
|
|
10
|
+
import { createGlobalState, useMount, useUpdateEffect } from 'react-use'
|
|
14
11
|
|
|
15
12
|
export const initialState = {
|
|
16
13
|
searchInput: '',
|
|
@@ -21,23 +18,18 @@ export const initialState = {
|
|
|
21
18
|
[ALL_CATEGORY_TAG.name]: true,
|
|
22
19
|
} as Record<string, boolean>,
|
|
23
20
|
largeCard: null as null | string,
|
|
24
|
-
syncWithGame:
|
|
21
|
+
syncWithGame: false,
|
|
22
|
+
preferKcwikiData: true,
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
export type State = typeof initialState
|
|
28
26
|
|
|
29
27
|
// Persist state
|
|
30
28
|
const STORAGE_KEY = PACKAGE_NAME
|
|
31
|
-
let onRemoveStorage: (() => void)[] = []
|
|
32
|
-
export const removeStorage = () => {
|
|
33
|
-
localStorage.removeItem(STORAGE_KEY)
|
|
34
|
-
onRemoveStorage.forEach((i) => i())
|
|
35
|
-
}
|
|
36
29
|
|
|
37
30
|
const useStorage = (
|
|
38
31
|
store: State,
|
|
39
32
|
setState: (state: State) => void,
|
|
40
|
-
onRemove = () => {},
|
|
41
33
|
merge = true
|
|
42
34
|
) => {
|
|
43
35
|
// Load storage at mount
|
|
@@ -46,7 +38,7 @@ const useStorage = (
|
|
|
46
38
|
if (stringStore == null) {
|
|
47
39
|
return
|
|
48
40
|
}
|
|
49
|
-
const parsedStorage = JSON.parse(stringStore)
|
|
41
|
+
const parsedStorage: State = JSON.parse(stringStore)
|
|
50
42
|
// TODO use deep merge
|
|
51
43
|
const storageStore = merge ? { ...store, ...parsedStorage } : parsedStorage
|
|
52
44
|
setState(storageStore)
|
|
@@ -57,24 +49,24 @@ const useStorage = (
|
|
|
57
49
|
const serializedStore = JSON.stringify(store)
|
|
58
50
|
localStorage.setItem(STORAGE_KEY, serializedStore)
|
|
59
51
|
}, [store])
|
|
52
|
+
}
|
|
60
53
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
export const getStorage = () => {
|
|
55
|
+
const stringStore = localStorage.getItem(STORAGE_KEY)
|
|
56
|
+
if (stringStore == null) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
return JSON.parse(stringStore) as State
|
|
67
60
|
}
|
|
68
61
|
|
|
69
62
|
const StateContext = createContext<State>(initialState)
|
|
70
63
|
const SetStateContext = createContext<Dispatch<SetStateAction<State>>>(() => {})
|
|
71
64
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
useStorage(state, setState, onStorageRemove)
|
|
65
|
+
const useGlobalState = createGlobalState<State>(initialState)
|
|
66
|
+
|
|
67
|
+
export const StoreProvider = ({ children }: { children?: React.ReactNode }) => {
|
|
68
|
+
const [state, setState] = useGlobalState()
|
|
69
|
+
useStorage(state, setState)
|
|
78
70
|
return (
|
|
79
71
|
<SetStateContext.Provider value={setState}>
|
|
80
72
|
<StateContext.Provider value={state}>{children}</StateContext.Provider>
|
|
@@ -91,3 +83,11 @@ export const useStore = () => {
|
|
|
91
83
|
)
|
|
92
84
|
return { store, setStore, updateStore }
|
|
93
85
|
}
|
|
86
|
+
|
|
87
|
+
export const useRemoveStorage = () => {
|
|
88
|
+
const { updateStore } = useStore()
|
|
89
|
+
return () => {
|
|
90
|
+
localStorage.removeItem(STORAGE_KEY)
|
|
91
|
+
updateStore(initialState)
|
|
92
|
+
}
|
|
93
|
+
}
|
package/src/utils.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState, EffectCallback } from 'react'
|
|
2
|
-
|
|
3
|
-
// See react-use
|
|
4
|
-
// licensed under the The Unlicense
|
|
5
|
-
// https://github.com/streamich/react-use
|
|
6
|
-
|
|
7
|
-
const useEffectOnce = (effect: EffectCallback) => {
|
|
8
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
9
|
-
useEffect(effect, [])
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const useMount = (fn: () => void) => {
|
|
13
|
-
useEffectOnce(() => {
|
|
14
|
-
fn()
|
|
15
|
-
})
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const useUnmount = (fn: () => any): void => {
|
|
19
|
-
const fnRef = useRef(fn)
|
|
20
|
-
|
|
21
|
-
// update the ref each render so if it change the newest callback will be invoked
|
|
22
|
-
fnRef.current = fn
|
|
23
|
-
|
|
24
|
-
useEffectOnce(() => () => fnRef.current())
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const useThrottle = <T>(value: T, ms = 200) => {
|
|
28
|
-
const [state, setState] = useState<T>(value)
|
|
29
|
-
const timeout = useRef<ReturnType<typeof setTimeout>>()
|
|
30
|
-
const nextValue = useRef(null) as any
|
|
31
|
-
const hasNextValue = useRef(0) as any
|
|
32
|
-
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
if (!timeout.current) {
|
|
35
|
-
setState(value)
|
|
36
|
-
const timeoutCallback = () => {
|
|
37
|
-
if (hasNextValue.current) {
|
|
38
|
-
hasNextValue.current = false
|
|
39
|
-
setState(nextValue.current)
|
|
40
|
-
timeout.current = setTimeout(timeoutCallback, ms)
|
|
41
|
-
} else {
|
|
42
|
-
timeout.current = undefined
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
timeout.current = setTimeout(timeoutCallback, ms)
|
|
46
|
-
} else {
|
|
47
|
-
nextValue.current = value
|
|
48
|
-
hasNextValue.current = true
|
|
49
|
-
}
|
|
50
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
51
|
-
}, [value])
|
|
52
|
-
|
|
53
|
-
useUnmount(() => {
|
|
54
|
-
timeout.current && clearTimeout(timeout.current)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
return state
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function useFirstMountState(): boolean {
|
|
61
|
-
const isFirst = useRef(true)
|
|
62
|
-
if (isFirst.current) {
|
|
63
|
-
isFirst.current = false
|
|
64
|
-
return true
|
|
65
|
-
}
|
|
66
|
-
return isFirst.current
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export const useUpdateEffect: typeof useEffect = (effect, deps) => {
|
|
70
|
-
const isFirstMount = useFirstMountState()
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
if (!isFirstMount) {
|
|
73
|
-
return effect()
|
|
74
|
-
}
|
|
75
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
76
|
-
}, deps)
|
|
77
|
-
}
|