159 lines
4.5 KiB
TypeScript
159 lines
4.5 KiB
TypeScript
//@ts-check
|
|
|
|
import { proxy, ref, subscribe } from 'valtio'
|
|
import type { WorldWarp } from 'flying-squid/dist/lib/modules/warps'
|
|
import type { OptionsGroupType } from './optionsGuiScheme'
|
|
import { options, disabledSettings } from './optionsStorage'
|
|
import { AppConfig } from './appConfig'
|
|
|
|
// todo: refactor structure with support of hideNext=false
|
|
|
|
export const notHideableModalsWithoutForce = new Set([
|
|
'app-status',
|
|
'divkit:nonclosable',
|
|
'only-connect-server',
|
|
])
|
|
|
|
type Modal = ({ elem?: HTMLElement & Record<string, any> } & { reactType: string })
|
|
|
|
type ContextMenuItem = { callback; label }
|
|
|
|
export const activeModalStack: Modal[] = proxy([])
|
|
|
|
export const insertActiveModalStack = (name: string, newModalStack = activeModalStacks[name]) => {
|
|
hideModal(undefined, undefined, { restorePrevious: false, force: true })
|
|
activeModalStack.splice(0, activeModalStack.length, ...newModalStack)
|
|
const last = activeModalStack.at(-1)
|
|
if (last) showModalInner(last)
|
|
}
|
|
|
|
export const activeModalStacks: Record<string, Modal[]> = {}
|
|
|
|
window.activeModalStack = activeModalStack
|
|
|
|
/**
|
|
* @returns true if operation was successful
|
|
*/
|
|
const showModalInner = (modal: Modal) => {
|
|
const cancel = modal.elem?.show?.()
|
|
return true
|
|
}
|
|
|
|
export const showModal = (elem: /* (HTMLElement & Record<string, any>) | */{ reactType: string } | string) => {
|
|
const resolved = typeof elem === 'string' ? { reactType: elem } : elem
|
|
const curModal = activeModalStack.at(-1)
|
|
if ((resolved.reactType && resolved.reactType === curModal?.reactType) || !showModalInner(resolved)) return
|
|
activeModalStack.push(resolved)
|
|
}
|
|
|
|
window.showModal = showModal
|
|
|
|
/**
|
|
*
|
|
* @returns true if previous modal was restored
|
|
*/
|
|
export const hideModal = (modal = activeModalStack.at(-1), data: any = undefined, options: { force?: boolean; restorePrevious?: boolean } = {}) => {
|
|
const { force = false, restorePrevious = true } = options
|
|
if (!modal) return
|
|
let cancel = [...notHideableModalsWithoutForce].some(m => modal.reactType.startsWith(m)) ? !force : undefined
|
|
if (force) {
|
|
cancel = undefined
|
|
}
|
|
|
|
if (!cancel) {
|
|
const lastModal = activeModalStack.at(-1)
|
|
for (let i = activeModalStack.length - 1; i >= 0; i--) {
|
|
if (activeModalStack[i].reactType === modal.reactType) {
|
|
activeModalStack.splice(i, 1)
|
|
break
|
|
}
|
|
}
|
|
const newModal = activeModalStack.at(-1)
|
|
if (newModal && lastModal !== newModal && restorePrevious) {
|
|
// would be great to ignore cancel I guess?
|
|
showModalInner(newModal)
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
export const hideCurrentModal = (_data?, onHide?: () => void) => {
|
|
if (hideModal(undefined, undefined)) {
|
|
onHide?.()
|
|
}
|
|
}
|
|
|
|
export const hideAllModals = () => {
|
|
while (activeModalStack.length > 0) {
|
|
if (!hideModal()) break
|
|
}
|
|
return activeModalStack.length === 0
|
|
}
|
|
|
|
export const openOptionsMenu = (group: OptionsGroupType) => {
|
|
showModal({ reactType: `options-${group}` })
|
|
}
|
|
|
|
subscribe(activeModalStack, () => {
|
|
document.body.style.setProperty('--has-modals-z', activeModalStack.length ? '-1' : null)
|
|
})
|
|
|
|
// ---
|
|
|
|
export const currentContextMenu = proxy({ items: [] as ContextMenuItem[] | null, x: 0, y: 0 })
|
|
|
|
export const showContextmenu = (items: ContextMenuItem[], { clientX, clientY }) => {
|
|
Object.assign(currentContextMenu, {
|
|
items,
|
|
x: clientX,
|
|
y: clientY,
|
|
})
|
|
}
|
|
|
|
// ---
|
|
|
|
export const miscUiState = proxy({
|
|
currentDisplayQr: null as string | null,
|
|
currentTouch: null as boolean | null,
|
|
hasErrors: false,
|
|
singleplayer: false,
|
|
flyingSquid: false,
|
|
wanOpened: false,
|
|
wanOpening: false,
|
|
/** wether game hud is shown (in playing state) */
|
|
gameLoaded: false,
|
|
showUI: true,
|
|
showDebugHud: false,
|
|
loadedServerIndex: '',
|
|
/** currently trying to load or loaded mc version, after all data is loaded */
|
|
loadedDataVersion: null as string | null,
|
|
fsReady: false,
|
|
singleplayerAvailable: false,
|
|
usingGamepadInput: false,
|
|
appConfig: null as AppConfig | null,
|
|
displaySearchInput: false,
|
|
displayFullmap: false
|
|
})
|
|
|
|
export const isGameActive = (foregroundCheck: boolean) => {
|
|
if (foregroundCheck && activeModalStack.length) return false
|
|
return miscUiState.gameLoaded
|
|
}
|
|
|
|
window.miscUiState = miscUiState
|
|
|
|
// state that is not possible to get via bot and in-game specific
|
|
export const gameAdditionalState = proxy({
|
|
isFlying: false,
|
|
isSprinting: false,
|
|
isSneaking: false,
|
|
isZooming: false,
|
|
warps: [] as WorldWarp[],
|
|
noConnection: false,
|
|
poorConnection: false,
|
|
viewerConnection: false,
|
|
|
|
usingServerResourcePack: false,
|
|
})
|
|
|
|
window.gameAdditionalState = gameAdditionalState
|