git ssb

1+

Daan Patchwork / patchwork



Tree: d1627bb325d9d1acbb1ba98a75ceb37a6b537c85

Files: d1627bb325d9d1acbb1ba98a75ceb37a6b537c85 / lib / scroller.js

3108 bytesRaw
1const pull = require('pull-stream')
2const Pause = require('pull-pause')
3const { h, computed, Value } = require('mutant')
4
5module.exports = Scroller
6
7function Scroller (scroller, content, render, opts) {
8 if (typeof opts === 'function') {
9 opts = { onDone: opts }
10 } else if (!opts) {
11 opts = {}
12 }
13 const toRenderCount = Value(0)
14 const toAppendCount = Value(0)
15 const waitingForItems = Value(false)
16 const pauser = Pause(function () {})
17
18 const endMarker = h('div.endMarker')
19 let intersecting = false
20 content.appendChild(endMarker)
21
22 const endlessObserver = new window.IntersectionObserver((entries) => {
23 if (entries[0].isIntersecting) {
24 // end marker is inside margin, request more items
25 intersecting = true
26 if (running) {
27 waitingForItems.set(true)
28 pauser.resume()
29 }
30 } else {
31 // end marker is no longer inside margin, stop appending items
32 intersecting = false
33 waitingForItems.set(false)
34 }
35 }, {
36 root: scroller,
37 // preload at least 4 screens ahead
38 rootMargin: '0px 0px 400% 0px'
39 })
40
41 const visibleObserver = new window.IntersectionObserver((entries) => {
42 entries.forEach(entry => {
43 if (entry.isIntersecting) {
44 visibleObserver.unobserve(entry.target)
45 process.nextTick(() => opts.onItemVisible && opts.onItemVisible(entry.target))
46 }
47 })
48 }, {
49 root: scroller
50 })
51
52 endlessObserver.observe(endMarker)
53
54 const queueLength = computed([toRenderCount, toAppendCount], (a, b) => a + b)
55
56 const appendQueue = []
57 let flushing = false
58 let running = true
59
60 function queueAppend (element) {
61 // only call appendNow a maximum of once per frame
62 appendQueue.push(element)
63 toAppendCount.set(appendQueue.length)
64
65 if (!flushing) {
66 flushing = true
67 window.requestAnimationFrame(appendNow)
68 }
69 }
70
71 function appendNow () {
72 flushing = false
73 while (appendQueue.length) {
74 const element = appendQueue.shift()
75 content.insertBefore(element, endMarker)
76
77 // notify when this element comes into view (for mark as read)
78 visibleObserver.observe(element)
79 }
80
81 toAppendCount.set(appendQueue.length)
82
83 // oops, looks like we need more items!
84 if (queueLength() < 5 && intersecting) {
85 pauser.resume()
86 }
87
88 if (running === false && !queueLength()) {
89 // we're done already!
90 waitingForItems.set(false)
91 }
92 }
93
94 const stream = pull(
95 pauser,
96 pull.drain(function (msg) {
97 toRenderCount.set(toRenderCount() + 1)
98
99 process.nextTick(() => {
100 // render post when idle
101 const element = render(msg)
102 queueAppend(element)
103 toRenderCount.set(toRenderCount() - 1)
104 })
105
106 if (queueLength() > 5 || !intersecting) {
107 // stop feeding the queue if greater than 5
108 pauser.pause()
109 }
110 }, function (err) {
111 running = false
112 if (!queueLength()) waitingForItems.set(false)
113 opts.onDone ? opts.onDone(err) : console.error(err)
114 })
115 )
116
117 stream.waiting = waitingForItems
118 stream.queue = queueLength
119
120 return stream
121}
122

Built with git-ssb-web