New experiment: auto refresh
This commit is contained in:
parent
51c03fb5be
commit
711842916d
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
@ -35,6 +35,7 @@ function Timeline({
|
|||
allowFilters,
|
||||
refresh,
|
||||
}) {
|
||||
const snapStates = useSnapshot(states);
|
||||
const [items, setItems] = useState([]);
|
||||
const [uiState, setUIState] = useState('default');
|
||||
const [showMore, setShowMore] = useState(false);
|
||||
|
@ -203,41 +204,51 @@ function Timeline({
|
|||
}
|
||||
}, [nearReachEnd, showMore]);
|
||||
|
||||
const isHovering = useRef(false);
|
||||
const loadOrCheckUpdates = useCallback(
|
||||
async ({ disableHoverCheck = false } = {}) => {
|
||||
console.log('✨ Load or check updates', snapStates.settings.autoRefresh);
|
||||
if (
|
||||
snapStates.settings.autoRefresh &&
|
||||
scrollableRef.current.scrollTop === 0 &&
|
||||
(disableHoverCheck || !isHovering.current) &&
|
||||
!inBackground()
|
||||
) {
|
||||
console.log('✨ Load updates', snapStates.settings.autoRefresh);
|
||||
loadItems(true);
|
||||
} else {
|
||||
console.log('✨ Check updates', snapStates.settings.autoRefresh);
|
||||
const hasUpdate = await checkForUpdates();
|
||||
if (hasUpdate) {
|
||||
console.log('✨ Has new updates', id);
|
||||
setShowNew(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
[id, loadItems, checkForUpdates, snapStates.settings.autoRefresh],
|
||||
);
|
||||
|
||||
const lastHiddenTime = useRef();
|
||||
usePageVisibility(
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
const timeDiff = Date.now() - lastHiddenTime.current;
|
||||
if (!lastHiddenTime.current || timeDiff > 1000 * 60) {
|
||||
(async () => {
|
||||
console.log('✨ Check updates');
|
||||
const hasUpdate = await checkForUpdates();
|
||||
if (hasUpdate) {
|
||||
console.log('✨ Has new updates', id);
|
||||
setShowNew(true);
|
||||
}
|
||||
})();
|
||||
loadOrCheckUpdates({
|
||||
disableHoverCheck: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
lastHiddenTime.current = Date.now();
|
||||
}
|
||||
setVisible(visible);
|
||||
},
|
||||
[checkForUpdates],
|
||||
[checkForUpdates, loadOrCheckUpdates, snapStates.settings.autoRefresh],
|
||||
);
|
||||
|
||||
// checkForUpdates interval
|
||||
useInterval(
|
||||
() => {
|
||||
(async () => {
|
||||
console.log('✨ Check updates');
|
||||
const hasUpdate = await checkForUpdates();
|
||||
if (hasUpdate) {
|
||||
console.log('✨ Has new updates', id);
|
||||
setShowNew(true);
|
||||
}
|
||||
})();
|
||||
},
|
||||
loadOrCheckUpdates,
|
||||
visible && !showNew ? checkForUpdatesInterval : null,
|
||||
);
|
||||
|
||||
|
@ -254,6 +265,12 @@ function Timeline({
|
|||
oRef.current = node;
|
||||
}}
|
||||
tabIndex="-1"
|
||||
onPointerEnter={(e) => {
|
||||
isHovering.current = true;
|
||||
}}
|
||||
onPointerLeave={() => {
|
||||
isHovering.current = false;
|
||||
}}
|
||||
>
|
||||
<div class="timeline-deck deck">
|
||||
<header
|
||||
|
@ -597,4 +614,8 @@ function TimelineStatusCompact({ status, instance }) {
|
|||
);
|
||||
}
|
||||
|
||||
function inBackground() {
|
||||
return !!document.querySelector('.deck-backdrop, #modal-container > *');
|
||||
}
|
||||
|
||||
export default Timeline;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import './notifications.css';
|
||||
|
||||
import { memo } from 'preact/compat';
|
||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
||||
import { useSnapshot } from 'valtio';
|
||||
|
||||
import Icon from '../components/icon';
|
||||
|
@ -95,6 +95,33 @@ function Notifications() {
|
|||
}
|
||||
}, [nearReachEnd, showMore]);
|
||||
|
||||
const isHovering = useRef(false);
|
||||
const loadUpdates = useCallback(() => {
|
||||
console.log('✨ Load updates', {
|
||||
autoRefresh: snapStates.settings.autoRefresh,
|
||||
scrollTop: scrollableRef.current?.scrollTop === 0,
|
||||
isHovering: isHovering.current,
|
||||
inBackground: inBackground(),
|
||||
notificationsShowNew: snapStates.notificationsShowNew,
|
||||
uiState,
|
||||
});
|
||||
if (
|
||||
snapStates.settings.autoRefresh &&
|
||||
scrollableRef.current?.scrollTop === 0 &&
|
||||
!isHovering.current &&
|
||||
!inBackground() &&
|
||||
snapStates.notificationsShowNew &&
|
||||
uiState !== 'loading'
|
||||
) {
|
||||
loadNotifications(true);
|
||||
}
|
||||
}, [
|
||||
snapStates.notificationsShowNew,
|
||||
snapStates.settings.autoRefresh,
|
||||
uiState,
|
||||
]);
|
||||
useEffect(loadUpdates, [snapStates.notificationsShowNew]);
|
||||
|
||||
const todayDate = new Date();
|
||||
const yesterdayDate = new Date(todayDate - 24 * 60 * 60 * 1000);
|
||||
let currentDay = new Date();
|
||||
|
@ -110,6 +137,14 @@ function Notifications() {
|
|||
class="deck-container"
|
||||
ref={scrollableRef}
|
||||
tabIndex="-1"
|
||||
onPointerEnter={() => {
|
||||
console.log('👆 Pointer enter');
|
||||
isHovering.current = true;
|
||||
}}
|
||||
onPointerLeave={() => {
|
||||
console.log('👇 Pointer leave');
|
||||
isHovering.current = false;
|
||||
}}
|
||||
>
|
||||
<div class={`timeline-deck deck ${onlyMentions ? 'only-mentions' : ''}`}>
|
||||
<header
|
||||
|
@ -245,4 +280,8 @@ function Notifications() {
|
|||
);
|
||||
}
|
||||
|
||||
function inBackground() {
|
||||
return !!document.querySelector('.deck-backdrop, #modal-container > *');
|
||||
}
|
||||
|
||||
export default memo(Notifications);
|
||||
|
|
|
@ -147,6 +147,18 @@ function Settings({ onClose }) {
|
|||
<h3>Experiments</h3>
|
||||
<section>
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={snapStates.settings.autoRefresh}
|
||||
onChange={(e) => {
|
||||
states.settings.autoRefresh = e.target.checked;
|
||||
}}
|
||||
/>{' '}
|
||||
Auto refresh timeline posts
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<input
|
||||
|
|
|
@ -41,6 +41,7 @@ const states = proxy({
|
|||
shortcuts: store.account.get('shortcuts') ?? [],
|
||||
// Settings
|
||||
settings: {
|
||||
autoRefresh: store.account.get('settings-autoRefresh') ?? false,
|
||||
shortcutsViewMode: store.account.get('settings-shortcutsViewMode') ?? null,
|
||||
shortcutsColumnsMode:
|
||||
store.account.get('settings-shortcutsColumnsMode') ?? false,
|
||||
|
@ -64,6 +65,9 @@ subscribeKey(states, 'notificationsLast', (v) => {
|
|||
subscribe(states, (changes) => {
|
||||
console.debug('STATES change', changes);
|
||||
for (const [action, path, value, prevValue] of changes) {
|
||||
if (path.join('.') === 'settings.autoRefresh') {
|
||||
store.account.set('settings-autoRefresh', !!value);
|
||||
}
|
||||
if (path.join('.') === 'settings.boostsCarousel') {
|
||||
store.account.set('settings-boostsCarousel', !!value);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import { useEffect, useRef } from 'preact/hooks';
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
function useInterval(callback, delay, immediate) {
|
||||
const savedCallback = useRef(noop);
|
||||
|
||||
function useInterval(fn, delay, deps, immediate) {
|
||||
const savedCallback = useRef(fn);
|
||||
useEffect(() => {
|
||||
savedCallback.current = callback;
|
||||
}, []);
|
||||
savedCallback.current = fn;
|
||||
}, [deps]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!immediate || delay === null || delay === false) return;
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { useEffect, useRef } from 'preact/hooks';
|
||||
|
||||
export default function usePageVisibility(fn = () => {}, deps = []) {
|
||||
const savedCallback = useRef(fn, deps);
|
||||
const savedCallback = useRef(fn);
|
||||
useEffect(() => {
|
||||
savedCallback.current = fn;
|
||||
}, [deps]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleVisibilityChange = () => {
|
||||
|
|
Loading…
Reference in a new issue