diff --git a/src/DevPanel.tsx b/src/DevPanel.tsx index eeab7b5..c81f7f0 100644 --- a/src/DevPanel.tsx +++ b/src/DevPanel.tsx @@ -76,7 +76,7 @@ function DevPanel({ children }: DevPanelProps) { let data = cloneDeep(mockData); if (!data) { const res = await fetch( - 'https://unpkg.com/ffxiv-overlay-api@4.4.0/test/fake_cn.json' + 'https://cdnjs.cloudflare.com/ajax/libs/ffxiv-overlay-api/4.4.0/fake_cn.json' ); const json = await res.json(); data = cloneDeep(json); diff --git a/src/assets/icons/i-lock-closed.svg b/src/assets/icons/i-lock-closed.svg new file mode 100644 index 0000000..38b3ea4 --- /dev/null +++ b/src/assets/icons/i-lock-closed.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/i-lock-open.svg b/src/assets/icons/i-lock-open.svg new file mode 100644 index 0000000..e38d66c --- /dev/null +++ b/src/assets/icons/i-lock-open.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index 65e1369..6b17187 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -8,6 +8,8 @@ export { ReactComponent as IChevronUp } from './i-chevron-up.svg'; export { ReactComponent as IClose } from './i-close.svg'; export { ReactComponent as ICreate } from './i-create.svg'; export { ReactComponent as IDownload } from './i-download.svg'; +export { ReactComponent as ILockClosed } from './i-lock-closed.svg'; +export { ReactComponent as ILockOpen } from './i-lock-open.svg'; export { ReactComponent as IRefresh } from './i-refresh.svg'; export { ReactComponent as IRemove } from './i-remove.svg'; export { ReactComponent as ISettings } from './i-settings.svg'; diff --git a/src/store/index.ts b/src/store/index.ts index 97196ac..56077cb 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -11,6 +11,7 @@ export const store = configureStore({ }, middleware: (getDefaultMiddleware) => getDefaultMiddleware() + .prepend(api.middleware) .prepend(settings.middleware) .prepend(theme.middleware), }); diff --git a/src/store/slices/api.ts b/src/store/slices/api.ts index c1f1643..b37afb3 100644 --- a/src/store/slices/api.ts +++ b/src/store/slices/api.ts @@ -1,5 +1,11 @@ -import { createSlice, PayloadAction as PA } from '@reduxjs/toolkit'; +import { + createListenerMiddleware, + createSlice, + PayloadAction as PA, +} from '@reduxjs/toolkit'; import { ExtendData } from 'ffxiv-overlay-api'; +import { RootState } from '..'; +import { toggleShowCombatants } from './settings'; interface HistoryData extends ExtendData { time: number; @@ -75,6 +81,21 @@ export const { updateCombat, showHistory, pushHistory } = apiSlice.actions; /** @redux effects */ +export const listener = createListenerMiddleware(); + +// add a new history means a new battle, +// so we need to show the temporarily hided combatants +listener.startListening({ + actionCreator: pushHistory, + effect: (_, api) => { + const state = api.getState() as RootState; + if (!state.settings.combatantsLocked) { + api.dispatch(toggleShowCombatants(true)); + } + }, +}); + export default { reducer: apiSlice.reducer, + middleware: listener.middleware, }; diff --git a/src/store/slices/settings.ts b/src/store/slices/settings.ts index fd5628c..039c413 100644 --- a/src/store/slices/settings.ts +++ b/src/store/slices/settings.ts @@ -3,6 +3,7 @@ import { createSlice, PayloadAction as PA, } from '@reduxjs/toolkit'; +import { RootState } from '..'; import lang from '../../lang'; import { CUSTOM_CSS_DOM_ID } from '../../utils/constants'; import { @@ -69,6 +70,7 @@ export interface Settings { interface SettingsState extends Settings { showCombatants: boolean; + combatantsLocked: boolean; showSettings: boolean; blurName: boolean; } @@ -101,6 +103,7 @@ export const defaultSettings: Settings = { }; let initialState: SettingsState = { showCombatants: true, + combatantsLocked: false, showSettings: false, blurName: false, ...cloneDeep(defaultSettings), @@ -173,6 +176,13 @@ export const settingsSlice = createSlice({ state.showCombatants = !state.showCombatants; } }, + toggleCombatantsLocked(state, { payload }: PA) { + if (payload !== undefined) { + state.combatantsLocked = payload; + } else { + state.combatantsLocked = !state.combatantsLocked; + } + }, toggleSettings(state) { state.showSettings = !state.showSettings; }, @@ -263,6 +273,7 @@ export const settingsSlice = createSlice({ export const { toggleShowCombatants, + toggleCombatantsLocked, toggleSettings, toggleBlurName, updateSort, @@ -290,6 +301,21 @@ export const { export const listener = createListenerMiddleware(); +// when click to show combatants, +// unlock them if locked +listener.startListening({ + actionCreator: toggleShowCombatants, + effect: ({ payload }, api) => { + const state = api.getState() as RootState; + if ( + payload === true || + (payload === undefined && state.settings.showCombatants === false) + ) { + api.dispatch(toggleCombatantsLocked(false)); + } + }, +}); + // apply dom when settings changed listener.startListening({ actionCreator: updateLang, diff --git a/src/utils/overlay.ts b/src/utils/overlay.ts index ceea36c..496bf87 100644 --- a/src/utils/overlay.ts +++ b/src/utils/overlay.ts @@ -17,6 +17,7 @@ function tryPushHistory(newData: ExtendData) { lastData.encounter.durationSeconds !== 0 && lastData.encounter.dps !== 0 ) { + // this will also trigger a toggleCombatant(true) if not locked store.dispatch(pushHistory(lastData)); } } diff --git a/src/views/Encounter.tsx b/src/views/Encounter.tsx index 49a0b3f..9295302 100644 --- a/src/views/Encounter.tsx +++ b/src/views/Encounter.tsx @@ -6,10 +6,16 @@ import { IChevronUpCircle, IChevronDownCircle, ISettings, + ILockClosed, + ILockOpen, } from '../assets/icons'; import { useAppDispatch, useAppSelector } from '../hooks'; import { fmtDuration, fmtNumber, fmtZoneName } from '../utils/formatters'; -import { toggleSettings, toggleShowCombatants } from '../store/slices/settings'; +import { + toggleCombatantsLocked, + toggleSettings, + toggleShowCombatants, +} from '../store/slices/settings'; function Encounter() { const dispatch = useAppDispatch(); @@ -18,6 +24,9 @@ function Encounter() { const showCombatants = useAppSelector( (state) => state.settings.showCombatants ); + const combatantsLocked = useAppSelector( + (state) => state.settings.combatantsLocked + ); const shortNumber = useAppSelector((state) => state.settings.shortNumber); const bigNumberMode = useAppSelector((state) => state.settings.bigNumberMode); @@ -35,6 +44,9 @@ function Encounter() { const handleToggleShowCombatants = useCallback(() => { dispatch(toggleShowCombatants()); }, [dispatch]); + const handleToggleLockCombatants = useCallback(() => { + dispatch(toggleCombatantsLocked()); + }, [dispatch]); const handleToggleSettings = useCallback(() => { dispatch(toggleSettings()); }, [dispatch]); @@ -97,6 +109,11 @@ function Encounter() {
+ {!showCombatants && ( +
+ {combatantsLocked ? : } +
+ )}
{showCombatants ? : }