Skip to content

Commit 0e4435e

Browse files
committed
feat: add support for /ping command, fix chat fading!
1 parent 3336680 commit 0e4435e

File tree

6 files changed

+55
-46
lines changed

6 files changed

+55
-46
lines changed

src/react/Chat.css

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,23 +189,22 @@ input[type=text],
189189
background-color: rgba(0, 0, 0, 0.5);
190190
list-style: none;
191191
overflow-wrap: break-word;
192-
}
193-
194-
.chat-message-fadeout {
195192
opacity: 1;
196-
transition: all 3s;
197193
}
198194

199-
.chat-message-fade {
195+
.chat-message-fading {
200196
opacity: 0;
197+
transition: opacity 3s ease-in-out;
201198
}
202199

203200
.chat-message-faded {
204-
transition: none !important;
201+
display: none;
205202
}
206203

204+
/* Ensure messages are always visible when chat is open */
207205
.chat.opened .chat-message {
208206
opacity: 1 !important;
207+
display: block !important;
209208
transition: none !important;
210209
}
211210

src/react/Chat.tsx

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,37 @@ import { useScrollBehavior } from './hooks/useScrollBehavior'
1111
export type Message = {
1212
parts: MessageFormatPart[],
1313
id: number
14-
fading?: boolean
15-
faded?: boolean
14+
timestamp?: number
1615
}
1716

18-
const MessageLine = ({ message, currentPlayerName }: { message: Message, currentPlayerName?: string }) => {
17+
const MessageLine = ({ message, currentPlayerName, chatOpened }: { message: Message, currentPlayerName?: string, chatOpened?: boolean }) => {
18+
const [fadeState, setFadeState] = useState<'visible' | 'fading' | 'faded'>('visible')
19+
20+
useEffect(() => {
21+
// Start fading after 5 seconds
22+
const fadeTimeout = setTimeout(() => {
23+
setFadeState('fading')
24+
}, 5000)
25+
26+
// Remove after fade animation (3s) completes
27+
const removeTimeout = setTimeout(() => {
28+
setFadeState('faded')
29+
}, 8000)
30+
31+
// Cleanup timeouts if component unmounts
32+
return () => {
33+
clearTimeout(fadeTimeout)
34+
clearTimeout(removeTimeout)
35+
}
36+
}, []) // Empty deps array since we only want this to run once when message is added
37+
1938
const classes = {
20-
'chat-message-fadeout': message.fading,
21-
'chat-message-fade': message.fading,
22-
'chat-message-faded': message.faded,
23-
'chat-message': true
39+
'chat-message': true,
40+
'chat-message-fading': !chatOpened && fadeState === 'fading',
41+
'chat-message-faded': !chatOpened && fadeState === 'faded'
2442
}
2543

26-
return <li className={Object.entries(classes).filter(([, val]) => val).map(([name]) => name).join(' ')}>
44+
return <li className={Object.entries(classes).filter(([, val]) => val).map(([name]) => name).join(' ')} data-time={message.timestamp ? new Date(message.timestamp).toLocaleString('en-US', { hour12: false }) : undefined}>
2745
{message.parts.map((msg, i) => {
2846
// Check if this is a text part that might contain a mention
2947
if (msg.text && currentPlayerName) {
@@ -70,17 +88,6 @@ export const chatInputValueGlobal = proxy({
7088
value: ''
7189
})
7290

73-
export const fadeMessage = (message: Message, initialTimeout: boolean, requestUpdate: () => void) => {
74-
setTimeout(() => {
75-
message.fading = true
76-
requestUpdate()
77-
setTimeout(() => {
78-
message.faded = true
79-
requestUpdate()
80-
}, 3000)
81-
}, initialTimeout ? 5000 : 0)
82-
}
83-
8491
export default ({
8592
messages,
8693
opacity = 1,
@@ -372,7 +379,7 @@ export default ({
372379
</div>
373380
)}
374381
{messages.map((m) => (
375-
<MessageLine key={reactKeyForMessage(m)} message={m} currentPlayerName={playerNameValidated} />
382+
<MessageLine key={reactKeyForMessage(m)} message={m} currentPlayerName={playerNameValidated} chatOpened={opened} />
376383
))}
377384
</div> || undefined}
378385
</div>

src/react/ChatProvider.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { getBuiltinCommandsList, tryHandleBuiltinCommand } from '../builtinComma
55
import { gameAdditionalState, hideCurrentModal, miscUiState } from '../globalState'
66
import { options } from '../optionsStorage'
77
import { viewerVersionState } from '../viewerConnector'
8-
import Chat, { Message, fadeMessage } from './Chat'
8+
import Chat, { Message } from './Chat'
99
import { useIsModalActive } from './utilsApp'
1010
import { hideNotification, notificationProxy, showNotification } from './NotificationProvider'
1111
import { getServerIndex, updateLoadedServerData } from './serversStorage'
@@ -16,6 +16,7 @@ export default () => {
1616
const [messages, setMessages] = useState([] as Message[])
1717
const isChatActive = useIsModalActive('chat')
1818
const lastMessageId = useRef(0)
19+
const lastPingTime = useRef(0)
1920
const usingTouch = useSnapshot(miscUiState).currentTouch
2021
const { chatSelect, messagesLimit, chatOpacity, chatOpacityOpened, chatVanillaRestrictions, debugChatScroll, chatPingExtension } = useSnapshot(options)
2122
const isUsingMicrosoftAuth = useMemo(() => !!lastConnectOptions.value?.authenticatedAccount, [])
@@ -29,18 +30,23 @@ export default () => {
2930
jsonMsg = jsonMsg['unsigned']
3031
}
3132
const parts = formatMessage(jsonMsg)
33+
const messageText = parts.map(part => part.text).join('')
34+
35+
// Handle ping response
36+
if (messageText === 'Pong!' && lastPingTime.current > 0) {
37+
const latency = Date.now() - lastPingTime.current
38+
parts.push({ text: ` Latency: ${latency}ms`, color: '#00ff00' })
39+
lastPingTime.current = 0
40+
}
3241

3342
setMessages(m => {
3443
lastMessageId.current++
3544
const newMessage: Message = {
3645
parts,
3746
id: lastMessageId.current,
38-
faded: false,
47+
timestamp: Date.now()
3948
}
40-
fadeMessage(newMessage, true, () => {
41-
// eslint-disable-next-line max-nested-callbacks
42-
setMessages(m => [...m])
43-
})
49+
4450
return [...m, newMessage].slice(-messagesLimit)
4551
})
4652
})
@@ -61,6 +67,11 @@ export default () => {
6167
return players.filter(name => (!value || name.toLowerCase().includes(value.toLowerCase())) && name !== bot.username).map(name => `@${name}`)
6268
}}
6369
sendMessage={async (message) => {
70+
// Record ping command time
71+
if (message === '/ping') {
72+
lastPingTime.current = Date.now()
73+
}
74+
6475
const builtinHandled = tryHandleBuiltinCommand(message)
6576
if (getServerIndex() !== undefined && (message.startsWith('/login') || message.startsWith('/register'))) {
6677
showNotification('Click here to save your password in browser for auto-login', undefined, false, undefined, () => {

src/react/StorageConflictModal.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ export default () => {
3434
fontFamily: 'minecraft, monospace',
3535
textAlign: 'center',
3636
zIndex: 1000,
37+
position: 'fixed',
38+
left: 0,
39+
right: 0
3740
}}>
3841
<div style={{
3942
fontSize: '16px',

src/react/appStorageProvider.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ const detectStorageConflicts = (): StorageConflict[] => {
120120
const localParsed = JSON.parse(localStorageValue)
121121
const cookieParsed = JSON.parse(cookieValue)
122122

123-
if (localParsed?.migrated) {
123+
if (localStorage.getItem(`${localStorageKey}:migrated`)) {
124124
continue
125125
}
126126

@@ -309,18 +309,7 @@ const markLocalStorageAsMigrated = (key: keyof StorageData) => {
309309
return
310310
}
311311

312-
const data = localStorage.getItem(localStorageKey)
313-
if (data) {
314-
try {
315-
const parsed = JSON.parse(data)
316-
localStorage.setItem(
317-
localStorageKey, JSON.stringify(typeof parsed === 'object' ? {
318-
...parsed, migrated: Date.now()
319-
} : { data: parsed, migrated: Date.now() })
320-
)
321-
} catch (err) {
322-
}
323-
}
312+
localStorage.setItem(`${localStorageKey}:migrated`, 'true')
324313
}
325314

326315
const saveKey = (key: keyof StorageData) => {

src/reactUi.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ const App = () => {
229229
<div />
230230
</RobustPortal>
231231
<EnterFullscreenButton />
232+
<StorageConflictModal />
232233
<InGameUi />
233234
<RobustPortal to={document.querySelector('#ui-root')}>
234235
<AllWidgets />
@@ -248,7 +249,6 @@ const App = () => {
248249

249250
<SelectOption />
250251
<CreditsAboutModal />
251-
<StorageConflictModal />
252252
<NoModalFoundProvider />
253253
</RobustPortal>
254254
<RobustPortal to={document.body}>

0 commit comments

Comments
 (0)