forked from Mirrors/gomuks
web/timeline: auto-load history until screen is full
This commit is contained in:
parent
05f64edeaf
commit
83a4df9375
4 changed files with 27 additions and 7 deletions
|
@ -274,9 +274,8 @@ export default class Client {
|
||||||
const room = this.store.rooms.get(roomID)
|
const room = this.store.rooms.get(roomID)
|
||||||
if (!room) {
|
if (!room) {
|
||||||
throw new Error("Room not found")
|
throw new Error("Room not found")
|
||||||
}
|
} else if (room.paginating) {
|
||||||
if (room.paginating) {
|
throw new Error("Already paginating")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
room.paginating = true
|
room.paginating = true
|
||||||
try {
|
try {
|
||||||
|
@ -288,6 +287,7 @@ export default class Client {
|
||||||
if (room.timeline[0]?.timeline_rowid !== oldestRowID) {
|
if (room.timeline[0]?.timeline_rowid !== oldestRowID) {
|
||||||
throw new Error("Timeline changed while loading history")
|
throw new Error("Timeline changed while loading history")
|
||||||
}
|
}
|
||||||
|
room.hasMoreHistory = resp.has_more
|
||||||
room.applyPagination(resp.events)
|
room.applyPagination(resp.events)
|
||||||
} finally {
|
} finally {
|
||||||
room.paginating = false
|
room.paginating = false
|
||||||
|
|
|
@ -112,6 +112,7 @@ export class RoomStateStore {
|
||||||
paginating = false
|
paginating = false
|
||||||
paginationRequestedForRow = -1
|
paginationRequestedForRow = -1
|
||||||
readUpToRow = -1
|
readUpToRow = -1
|
||||||
|
hasMoreHistory = true
|
||||||
|
|
||||||
constructor(meta: DBRoom, private parent: StateStore) {
|
constructor(meta: DBRoom, private parent: StateStore) {
|
||||||
this.roomID = meta.room_id
|
this.roomID = meta.room_id
|
||||||
|
@ -467,6 +468,7 @@ export class RoomStateStore {
|
||||||
this.#membersCache = null
|
this.#membersCache = null
|
||||||
this.#autocompleteMembersCache = null
|
this.#autocompleteMembersCache = null
|
||||||
this.paginationRequestedForRow = -1
|
this.paginationRequestedForRow = -1
|
||||||
|
this.hasMoreHistory = true
|
||||||
this.timeline = []
|
this.timeline = []
|
||||||
this.notifyTimelineSubscribers()
|
this.notifyTimelineSubscribers()
|
||||||
const eventsToKeepList = this.eventsByRowID.values()
|
const eventsToKeepList = this.eventsByRowID.values()
|
||||||
|
|
|
@ -5,4 +5,15 @@ div.timeline-view {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
|
> div.timeline-beginning {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
display: flex;
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
gap: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import { use, useCallback, useEffect, useLayoutEffect, useRef } from "react"
|
import { use, useCallback, useEffect, useLayoutEffect, useRef, useState } from "react"
|
||||||
|
import { ScaleLoader } from "react-spinners"
|
||||||
import { useRoomTimeline } from "@/api/statestore"
|
import { useRoomTimeline } from "@/api/statestore"
|
||||||
import { MemDBEvent } from "@/api/types"
|
import { MemDBEvent } from "@/api/types"
|
||||||
import useFocus from "@/util/focus.ts"
|
import useFocus from "@/util/focus.ts"
|
||||||
|
@ -27,14 +28,18 @@ const TimelineView = () => {
|
||||||
const room = roomCtx.store
|
const room = roomCtx.store
|
||||||
const timeline = useRoomTimeline(room)
|
const timeline = useRoomTimeline(room)
|
||||||
const client = use(ClientContext)!
|
const client = use(ClientContext)!
|
||||||
|
const [isLoadingHistory, setLoadingHistory] = useState(false)
|
||||||
const loadHistory = useCallback(() => {
|
const loadHistory = useCallback(() => {
|
||||||
|
setLoadingHistory(true)
|
||||||
client.loadMoreHistory(room.roomID)
|
client.loadMoreHistory(room.roomID)
|
||||||
.catch(err => console.error("Failed to load history", err))
|
.catch(err => console.error("Failed to load history", err))
|
||||||
|
.finally(() => setLoadingHistory(false))
|
||||||
}, [client, room])
|
}, [client, room])
|
||||||
const bottomRef = roomCtx.timelineBottomRef
|
const bottomRef = roomCtx.timelineBottomRef
|
||||||
const topRef = useRef<HTMLDivElement>(null)
|
const topRef = useRef<HTMLDivElement>(null)
|
||||||
const timelineViewRef = useRef<HTMLDivElement>(null)
|
const timelineViewRef = useRef<HTMLDivElement>(null)
|
||||||
const prevOldestTimelineRow = useRef(0)
|
const prevOldestTimelineRow = useRef(0)
|
||||||
|
const oldestTimelineRow = timeline[0]?.timeline_rowid
|
||||||
const oldScrollHeight = useRef(0)
|
const oldScrollHeight = useRef(0)
|
||||||
const focused = useFocus()
|
const focused = useFocus()
|
||||||
|
|
||||||
|
@ -93,7 +98,7 @@ const TimelineView = () => {
|
||||||
}, [focused, client, roomCtx, room, timeline])
|
}, [focused, client, roomCtx, room, timeline])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const topElem = topRef.current
|
const topElem = topRef.current
|
||||||
if (!topElem) {
|
if (!topElem || !room.hasMoreHistory) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const observer = new IntersectionObserver(entries => {
|
const observer = new IntersectionObserver(entries => {
|
||||||
|
@ -108,12 +113,14 @@ const TimelineView = () => {
|
||||||
})
|
})
|
||||||
observer.observe(topElem)
|
observer.observe(topElem)
|
||||||
return () => observer.unobserve(topElem)
|
return () => observer.unobserve(topElem)
|
||||||
}, [room, loadHistory])
|
}, [room, room.hasMoreHistory, loadHistory, oldestTimelineRow])
|
||||||
|
|
||||||
let prevEvt: MemDBEvent | null = null
|
let prevEvt: MemDBEvent | null = null
|
||||||
return <div className="timeline-view" onScroll={handleScroll} ref={timelineViewRef}>
|
return <div className="timeline-view" onScroll={handleScroll} ref={timelineViewRef}>
|
||||||
<div className="timeline-beginning">
|
<div className="timeline-beginning">
|
||||||
<button onClick={loadHistory}>Load history</button>
|
{room.hasMoreHistory ? <button onClick={loadHistory} disabled={isLoadingHistory}>
|
||||||
|
{isLoadingHistory ? <><ScaleLoader /> Loading history...</> : "Load more history"}
|
||||||
|
</button> : "No more history available in this room"}
|
||||||
</div>
|
</div>
|
||||||
<div className="timeline-list">
|
<div className="timeline-list">
|
||||||
<div className="timeline-top-ref" ref={topRef}/>
|
<div className="timeline-top-ref" ref={topRef}/>
|
||||||
|
|
Loading…
Add table
Reference in a new issue