improve chat scrolling #82

pull/251/merge
Jon Eugster 2 years ago
parent 7dc0a507ed
commit f158250341

@ -241,7 +241,6 @@ export function Hints({ hints, conclusion, counter=undefined } : {
export function ChatPanel ({visible = true}) { export function ChatPanel ({visible = true}) {
let { t } = useTranslation() let { t } = useTranslation()
const chatRef = useRef<HTMLDivElement>(null)
const { mobile } = useContext(PreferencesContext) const { mobile } = useContext(PreferencesContext)
const { gameId, worldId, levelId } = useContext(GameIdContext) const { gameId, worldId, levelId } = useContext(GameIdContext)
@ -255,7 +254,7 @@ export function ChatPanel ({visible = true}) {
const [introText, setIntroText] = useState<Array<GameHintWithStep>>([]) const [introText, setIntroText] = useState<Array<GameHintWithStep>>([])
const [chatMessages, setChatMessages] = useState<Array<GameHintWithStep>>([]) const [chatMessages, setChatMessages] = useState<Array<GameHintWithStep>>([])
const { deletedChat, showHelp, selectedStep } = useContext(ChatContext) const { chatRef, deletedChat, showHelp, selectedStep } = useContext(ChatContext)
const { proof } = useContext(ProofContext) const { proof } = useContext(ProofContext)
const readIntro = useSelector(selectReadIntro(gameId, worldId)) const readIntro = useSelector(selectReadIntro(gameId, worldId))
@ -316,26 +315,32 @@ export function ChatPanel ({visible = true}) {
setChatMessages(messages) setChatMessages(messages)
}, [gameInfo, levelInfo, gameId, worldId, levelId, proof]) }, [gameInfo, levelInfo, gameId, worldId, levelId, proof])
// Scroll to the top when loading a new page // Scroll the chat
useEffect(() => { useEffect(() => {
console.debug(`scroll chat: top`) if (levelId > 0) {
chatRef.current!.scrollTo(0,0)
}, [completed, gameId, worldId, levelId])
// Scroll to first message of the last step. if (proof) {
// If the proof is currently completed, scroll to the conclusion if (proof?.completed) {
useEffect(() => { // proof currently completed: scroll down
if (levelId > 0 && proof) { console.debug('scroll chat: down to conclusion')
if (proof?.completed) { chatRef.current!.lastElementChild?.scrollIntoView({block: "center"})
console.debug('scroll chat: down to conclusion') } else {
chatRef.current!.lastElementChild?.scrollIntoView({block: "center"}) // proof currently not completed: first message of last step
let lastStep = proof?.steps.length - (lastStepHasErrors(proof) ? 2 : 1)
console.debug(`scroll chat: first message of step ${lastStep}`)
chatRef.current?.getElementsByClassName(`step-${lastStep}`)[0]?.scrollIntoView({block: "center"})
}
} else { } else {
let lastStep = proof?.steps.length - (lastStepHasErrors(proof) ? 2 : 1) // no proof available: scroll to top.
console.debug(`scroll chat: first message of step ${lastStep}`) console.debug(`scroll chat: top`)
chatRef.current?.getElementsByClassName(`step-${lastStep}`)[0]?.scrollIntoView({block: "center"}) chatRef.current!.scrollTo(0,0)
} }
} else {
// introduction: scroll to last message
console.debug('scroll chat: down')
chatRef.current!.lastElementChild?.scrollIntoView({block: "center"})
} }
}, [chatMessages, gameId, worldId, levelId]) }, [counter, introText, chatMessages, gameId, worldId, levelId])
// Scroll down when new hidden hints are triggered // Scroll down when new hidden hints are triggered
useEffect(() => { useEffect(() => {
@ -343,6 +348,7 @@ export function ChatPanel ({visible = true}) {
let lastStep = proof?.steps.length - (lastStepHasErrors(proof) ? 2 : 1) let lastStep = proof?.steps.length - (lastStepHasErrors(proof) ? 2 : 1)
if (showHelp.has(lastStep)) { if (showHelp.has(lastStep)) {
console.debug('scroll chat: down to hidden hints') console.debug('scroll chat: down to hidden hints')
// TODO: last element of hidden hints?
chatRef.current!.lastElementChild?.scrollIntoView({block: "center"}) chatRef.current!.lastElementChild?.scrollIntoView({block: "center"})
} }
} }

@ -54,6 +54,7 @@ function Game() {
// const [pageNumber, setPageNumber] = React.useState(readIntro ? 1 : 0) // const [pageNumber, setPageNumber] = React.useState(readIntro ? 1 : 0)
const chatRef = useRef<HTMLDivElement>(null)
// When deleting the proof, we want to keep to old messages around until // When deleting the proof, we want to keep to old messages around until
// a new proof has been entered. e.g. to consult messages coming from dead ends // a new proof has been entered. e.g. to consult messages coming from dead ends
const [deletedChat, setDeletedChat] = useState<Array<GameHint>>([]) const [deletedChat, setDeletedChat] = useState<Array<GameHint>>([])
@ -93,7 +94,7 @@ function Game() {
setShowHelp(new Set()) setShowHelp(new Set())
}, [gameId, worldId, levelId]) }, [gameId, worldId, levelId])
return <ChatContext.Provider value={{selectedStep, setSelectedStep, deletedChat, setDeletedChat, showHelp, setShowHelp}}> return <ChatContext.Provider value={{selectedStep, setSelectedStep, deletedChat, setDeletedChat, showHelp, setShowHelp, chatRef}}>
<ProofContext.Provider value={{proof, setProof, interimDiags, setInterimDiags, crashed: isCrashed, setCrashed: setIsCrashed}}> <ProofContext.Provider value={{proof, setProof, interimDiags, setInterimDiags, crashed: isCrashed, setCrashed: setIsCrashed}}>
{ mobile ? { mobile ?
<div className="app-content mobile"> <div className="app-content mobile">

@ -95,19 +95,21 @@ export const PreferencesContext = React.createContext<IPreferencesContext>({
export const ChatContext = React.createContext<{ export const ChatContext = React.createContext<{
selectedStep : number, selectedStep : number
setSelectedStep: React.Dispatch<React.SetStateAction<number>> setSelectedStep: React.Dispatch<React.SetStateAction<number>>
deletedChat : GameHint[], deletedChat : GameHint[]
setDeletedChat: React.Dispatch<React.SetStateAction<Array<GameHint>>> setDeletedChat: React.Dispatch<React.SetStateAction<Array<GameHint>>>
showHelp : Set<number>, showHelp : Set<number>
setShowHelp: React.Dispatch<React.SetStateAction<Set<number>>> setShowHelp: React.Dispatch<React.SetStateAction<Set<number>>>
chatRef: React.MutableRefObject<HTMLDivElement>
}>({ }>({
selectedStep : undefined, selectedStep : undefined,
setSelectedStep: () => {}, setSelectedStep: () => {},
deletedChat: undefined, deletedChat: undefined,
setDeletedChat: () => {}, setDeletedChat: () => {},
showHelp: undefined, showHelp: undefined,
setShowHelp: () => {} setShowHelp: () => {},
chatRef: null
}) })
export const PageContext = React.createContext<{ export const PageContext = React.createContext<{

Loading…
Cancel
Save