this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

New experiment: auto refresh

+105 -29
+41 -20
src/components/timeline.jsx
··· 1 - import { useEffect, useRef, useState } from 'preact/hooks'; 1 + import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; 2 2 import { useHotkeys } from 'react-hotkeys-hook'; 3 3 import { InView } from 'react-intersection-observer'; 4 4 import { useDebouncedCallback } from 'use-debounce'; ··· 35 35 allowFilters, 36 36 refresh, 37 37 }) { 38 + const snapStates = useSnapshot(states); 38 39 const [items, setItems] = useState([]); 39 40 const [uiState, setUIState] = useState('default'); 40 41 const [showMore, setShowMore] = useState(false); ··· 203 204 } 204 205 }, [nearReachEnd, showMore]); 205 206 207 + const isHovering = useRef(false); 208 + const loadOrCheckUpdates = useCallback( 209 + async ({ disableHoverCheck = false } = {}) => { 210 + console.log('✨ Load or check updates', snapStates.settings.autoRefresh); 211 + if ( 212 + snapStates.settings.autoRefresh && 213 + scrollableRef.current.scrollTop === 0 && 214 + (disableHoverCheck || !isHovering.current) && 215 + !inBackground() 216 + ) { 217 + console.log('✨ Load updates', snapStates.settings.autoRefresh); 218 + loadItems(true); 219 + } else { 220 + console.log('✨ Check updates', snapStates.settings.autoRefresh); 221 + const hasUpdate = await checkForUpdates(); 222 + if (hasUpdate) { 223 + console.log('✨ Has new updates', id); 224 + setShowNew(true); 225 + } 226 + } 227 + }, 228 + [id, loadItems, checkForUpdates, snapStates.settings.autoRefresh], 229 + ); 230 + 206 231 const lastHiddenTime = useRef(); 207 232 usePageVisibility( 208 233 (visible) => { 209 234 if (visible) { 210 235 const timeDiff = Date.now() - lastHiddenTime.current; 211 236 if (!lastHiddenTime.current || timeDiff > 1000 * 60) { 212 - (async () => { 213 - console.log('✨ Check updates'); 214 - const hasUpdate = await checkForUpdates(); 215 - if (hasUpdate) { 216 - console.log('✨ Has new updates', id); 217 - setShowNew(true); 218 - } 219 - })(); 237 + loadOrCheckUpdates({ 238 + disableHoverCheck: true, 239 + }); 220 240 } 221 241 } else { 222 242 lastHiddenTime.current = Date.now(); 223 243 } 224 244 setVisible(visible); 225 245 }, 226 - [checkForUpdates], 246 + [checkForUpdates, loadOrCheckUpdates, snapStates.settings.autoRefresh], 227 247 ); 228 248 229 249 // checkForUpdates interval 230 250 useInterval( 231 - () => { 232 - (async () => { 233 - console.log('✨ Check updates'); 234 - const hasUpdate = await checkForUpdates(); 235 - if (hasUpdate) { 236 - console.log('✨ Has new updates', id); 237 - setShowNew(true); 238 - } 239 - })(); 240 - }, 251 + loadOrCheckUpdates, 241 252 visible && !showNew ? checkForUpdatesInterval : null, 242 253 ); 243 254 ··· 254 265 oRef.current = node; 255 266 }} 256 267 tabIndex="-1" 268 + onPointerEnter={(e) => { 269 + isHovering.current = true; 270 + }} 271 + onPointerLeave={() => { 272 + isHovering.current = false; 273 + }} 257 274 > 258 275 <div class="timeline-deck deck"> 259 276 <header ··· 595 612 </div> 596 613 </article> 597 614 ); 615 + } 616 + 617 + function inBackground() { 618 + return !!document.querySelector('.deck-backdrop, #modal-container > *'); 598 619 } 599 620 600 621 export default Timeline;
+40 -1
src/pages/notifications.jsx
··· 1 1 import './notifications.css'; 2 2 3 3 import { memo } from 'preact/compat'; 4 - import { useEffect, useRef, useState } from 'preact/hooks'; 4 + import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; 5 5 import { useSnapshot } from 'valtio'; 6 6 7 7 import Icon from '../components/icon'; ··· 95 95 } 96 96 }, [nearReachEnd, showMore]); 97 97 98 + const isHovering = useRef(false); 99 + const loadUpdates = useCallback(() => { 100 + console.log('✨ Load updates', { 101 + autoRefresh: snapStates.settings.autoRefresh, 102 + scrollTop: scrollableRef.current?.scrollTop === 0, 103 + isHovering: isHovering.current, 104 + inBackground: inBackground(), 105 + notificationsShowNew: snapStates.notificationsShowNew, 106 + uiState, 107 + }); 108 + if ( 109 + snapStates.settings.autoRefresh && 110 + scrollableRef.current?.scrollTop === 0 && 111 + !isHovering.current && 112 + !inBackground() && 113 + snapStates.notificationsShowNew && 114 + uiState !== 'loading' 115 + ) { 116 + loadNotifications(true); 117 + } 118 + }, [ 119 + snapStates.notificationsShowNew, 120 + snapStates.settings.autoRefresh, 121 + uiState, 122 + ]); 123 + useEffect(loadUpdates, [snapStates.notificationsShowNew]); 124 + 98 125 const todayDate = new Date(); 99 126 const yesterdayDate = new Date(todayDate - 24 * 60 * 60 * 1000); 100 127 let currentDay = new Date(); ··· 110 137 class="deck-container" 111 138 ref={scrollableRef} 112 139 tabIndex="-1" 140 + onPointerEnter={() => { 141 + console.log('👆 Pointer enter'); 142 + isHovering.current = true; 143 + }} 144 + onPointerLeave={() => { 145 + console.log('👇 Pointer leave'); 146 + isHovering.current = false; 147 + }} 113 148 > 114 149 <div class={`timeline-deck deck ${onlyMentions ? 'only-mentions' : ''}`}> 115 150 <header ··· 243 278 </div> 244 279 </div> 245 280 ); 281 + } 282 + 283 + function inBackground() { 284 + return !!document.querySelector('.deck-backdrop, #modal-container > *'); 246 285 } 247 286 248 287 export default memo(Notifications);
+12
src/pages/settings.jsx
··· 151 151 <label> 152 152 <input 153 153 type="checkbox" 154 + checked={snapStates.settings.autoRefresh} 155 + onChange={(e) => { 156 + states.settings.autoRefresh = e.target.checked; 157 + }} 158 + />{' '} 159 + Auto refresh timeline posts 160 + </label> 161 + </li> 162 + <li> 163 + <label> 164 + <input 165 + type="checkbox" 154 166 checked={snapStates.settings.boostsCarousel} 155 167 onChange={(e) => { 156 168 states.settings.boostsCarousel = e.target.checked;
+4
src/utils/states.js
··· 41 41 shortcuts: store.account.get('shortcuts') ?? [], 42 42 // Settings 43 43 settings: { 44 + autoRefresh: store.account.get('settings-autoRefresh') ?? false, 44 45 shortcutsViewMode: store.account.get('settings-shortcutsViewMode') ?? null, 45 46 shortcutsColumnsMode: 46 47 store.account.get('settings-shortcutsColumnsMode') ?? false, ··· 64 65 subscribe(states, (changes) => { 65 66 console.debug('STATES change', changes); 66 67 for (const [action, path, value, prevValue] of changes) { 68 + if (path.join('.') === 'settings.autoRefresh') { 69 + store.account.set('settings-autoRefresh', !!value); 70 + } 67 71 if (path.join('.') === 'settings.boostsCarousel') { 68 72 store.account.set('settings-boostsCarousel', !!value); 69 73 }
+4 -7
src/utils/useInterval.js
··· 1 1 import { useEffect, useRef } from 'preact/hooks'; 2 2 3 - const noop = () => {}; 4 - 5 - function useInterval(callback, delay, immediate) { 6 - const savedCallback = useRef(noop); 7 - 3 + function useInterval(fn, delay, deps, immediate) { 4 + const savedCallback = useRef(fn); 8 5 useEffect(() => { 9 - savedCallback.current = callback; 10 - }, []); 6 + savedCallback.current = fn; 7 + }, [deps]); 11 8 12 9 useEffect(() => { 13 10 if (!immediate || delay === null || delay === false) return;
+4 -1
src/utils/usePageVisibility.js
··· 1 1 import { useEffect, useRef } from 'preact/hooks'; 2 2 3 3 export default function usePageVisibility(fn = () => {}, deps = []) { 4 - const savedCallback = useRef(fn, deps); 4 + const savedCallback = useRef(fn); 5 + useEffect(() => { 6 + savedCallback.current = fn; 7 + }, [deps]); 5 8 6 9 useEffect(() => { 7 10 const handleVisibilityChange = () => {