fix: make chat arrows work on ios

fix: disable annoying in many cases auto correct on ios (more annoying than useful especially in commands)
fix: make stats dont overlap with chat
fix: fix edgecases when focusing on chat was not possible on mobile
This commit is contained in:
Vitaly Turovsky 2025-03-14 01:36:14 +03:00
commit a8564232f7
5 changed files with 57 additions and 25 deletions

View file

@ -3,7 +3,7 @@ const rightOffset = 0
const stats = {}
let lastY = 20
let lastY = 40
export const addNewStat = (id: string, width = 80, x = rightOffset, y = lastY) => {
const pane = document.createElement('div')
pane.style.position = 'fixed'

View file

@ -829,6 +829,7 @@ export async function connect (connectOptions: ConnectOptions) {
if (appStatusState.isError) return
const waitForChunks = async () => {
if (appQueryParams.sp === '1') return //todo
const waitForChunks = options.waitForChunksRender === 'sp-only' ? !!singleplayer : options.waitForChunksRender
if (viewer.world.allChunksFinished || !waitForChunks) {
return

View file

@ -165,7 +165,7 @@ const migrateOptions = (options: Partial<AppOptions & Record<string, any>>) => {
export type AppOptions = typeof defaultOptions
// when opening html file locally in browser, localStorage is shared between all ever opened html files, so we try to avoid conflicts
const localStorageKey = process.env.SINGLE_FILE_BUILD ? 'minecraftWebClientOptions' : 'options'
const localStorageKey = process.env?.SINGLE_FILE_BUILD ? 'minecraftWebClientOptions' : 'options'
export const options: AppOptions = proxy({
...defaultOptions,
...initialAppConfig.defaultSettings,

View file

@ -29,6 +29,12 @@ div.chat-wrapper {
gap: 1px;
}
.chat-submit-button {
visibility: hidden;
position: absolute;
pointer-events: none !important;
}
.chat-input-wrapper form {
display: flex;
}
@ -157,17 +163,22 @@ input[type=text],
height: 15px;
}
.chat-mobile-hidden {
width: 8px;
height: 0;
.chat-mobile-input-hidden {
position: absolute;
width: 8px;
height: 1px !important;
display: block !important;
opacity: 0;
pointer-events: none;
height: 1px !important;
/* ios: using z-index, pointer-events: none or top below -10px breaks arrows */
}
.chat-mobile-hidden:nth-last-child(1) {
height: 8px;
.chat-mobile-input-hidden-up {
top: -10px;
}
.chat-mobile-input-hidden-down {
top: -5px;
}
#chatinput:focus {

View file

@ -71,6 +71,8 @@ export default ({
}: Props) => {
const sendHistoryRef = useRef(JSON.parse(window.sessionStorage.chatHistory || '[]'))
const [isInputFocused, setIsInputFocused] = useState(false)
// const [spellCheckEnabled, setSpellCheckEnabled] = useState(false)
const spellCheckEnabled = false
const [completePadText, setCompletePadText] = useState('')
const completeRequestValue = useRef('')
@ -107,9 +109,28 @@ export default ({
}, 0)
}
const auxInputFocus = (fireKey: string) => {
const handleArrowUp = () => {
if (chatHistoryPos.current === 0) return
if (chatHistoryPos.current === sendHistoryRef.current.length) { // started navigating history
inputCurrentlyEnteredValue.current = chatInput.current.value
}
chatHistoryPos.current--
updateInputValue(sendHistoryRef.current[chatHistoryPos.current] || '')
}
const handleArrowDown = () => {
if (chatHistoryPos.current === sendHistoryRef.current.length) return
chatHistoryPos.current++
updateInputValue(sendHistoryRef.current[chatHistoryPos.current] || inputCurrentlyEnteredValue.current || '')
}
const auxInputFocus = (direction: 'up' | 'down') => {
chatInput.current.focus()
chatInput.current.dispatchEvent(new KeyboardEvent('keydown', { code: fireKey, bubbles: true }))
if (direction === 'up') {
handleArrowUp()
} else {
handleArrowDown()
}
}
useEffect(() => {
@ -125,6 +146,7 @@ export default ({
if (opened) {
updateInputValue(chatInputValueGlobal.value)
chatInputValueGlobal.value = ''
chatHistoryPos.current = sendHistoryRef.current.length
if (!usingTouch) {
chatInput.current.focus()
}
@ -167,6 +189,8 @@ export default ({
const onMainInputChange = () => {
const completeValue = getCompleteValue()
setCompletePadText(completeValue === '/' ? '' : completeValue)
// not sure if enabling would be useful at all (maybe make as a setting in the future?)
// setSpellCheckEnabled(!chatInput.current.value.startsWith('/'))
if (completeRequestValue.current === completeValue) {
updateFilteredCompleteItems(completionItemsSource)
return
@ -271,20 +295,23 @@ export default ({
{isIos && <input
value=''
type="text"
className="chat-mobile-hidden"
className="chat-mobile-input-hidden chat-mobile-input-hidden-up"
id="chatinput-next-command"
spellCheck={false}
autoComplete="off"
onFocus={() => auxInputFocus('ArrowUp')}
onFocus={() => auxInputFocus('up')}
onChange={() => { }}
/>}
<input
defaultValue=''
// ios doesn't support toggling autoCorrect on the fly so we need to re-create the input
key={spellCheckEnabled ? 'true' : 'false'}
ref={chatInput}
type="text"
className="chat-input"
id="chatinput"
spellCheck={false}
spellCheck={spellCheckEnabled}
autoCorrect={spellCheckEnabled ? 'on' : 'off'}
autoComplete="off"
aria-autocomplete="both"
onChange={onMainInputChange}
@ -294,16 +321,9 @@ export default ({
onBlur={() => setIsInputFocused(false)}
onKeyDown={(e) => {
if (e.code === 'ArrowUp') {
if (chatHistoryPos.current === 0) return
if (chatHistoryPos.current === sendHistoryRef.current.length) { // started navigating history
inputCurrentlyEnteredValue.current = e.currentTarget.value
}
chatHistoryPos.current--
updateInputValue(sendHistoryRef.current[chatHistoryPos.current] || '')
handleArrowUp()
} else if (e.code === 'ArrowDown') {
if (chatHistoryPos.current === sendHistoryRef.current.length) return
chatHistoryPos.current++
updateInputValue(sendHistoryRef.current[chatHistoryPos.current] || inputCurrentlyEnteredValue.current || '')
handleArrowDown()
}
if (e.code === 'Tab') {
if (completionItemsSource.length) {
@ -327,15 +347,15 @@ export default ({
{isIos && <input
value=''
type="text"
className="chat-mobile-hidden"
className="chat-mobile-input-hidden chat-mobile-input-hidden-down"
id="chatinput-prev-command"
spellCheck={false}
autoComplete="off"
onFocus={() => auxInputFocus('ArrowDown')}
onFocus={() => auxInputFocus('down')}
onChange={() => { }}
/>}
{/* for some reason this is needed to make Enter work on android chrome */}
<button type='submit' style={{ visibility: 'hidden' }} />
<button type='submit' className="chat-submit-button" />
</form>
</div>
</div>