phanpy/src/pages/following.jsx

143 lines
3.6 KiB
React
Raw Normal View History

2023-02-07 16:31:46 +00:00
import { useEffect, useRef } from 'preact/hooks';
2023-02-03 13:08:08 +00:00
import { useSnapshot } from 'valtio';
2023-02-09 14:27:49 +00:00
import Icon from '../components/icon';
import Link from '../components/link';
2023-02-03 13:08:08 +00:00
import Timeline from '../components/timeline';
import { api } from '../utils/api';
2023-02-05 17:10:49 +00:00
import states from '../utils/states';
2023-02-07 16:31:46 +00:00
import { getStatus, saveStatus } from '../utils/states';
2023-02-03 13:08:08 +00:00
import useTitle from '../utils/useTitle';
const LIMIT = 20;
function Following({ title, path, id, headerStart }) {
useTitle(title || 'Following', path, '/l/f');
const { masto, instance } = api();
2023-02-03 13:08:08 +00:00
const snapStates = useSnapshot(states);
const homeIterator = useRef();
2023-02-07 16:31:46 +00:00
const latestItem = useRef();
2023-02-03 13:08:08 +00:00
async function fetchHome(firstLoad) {
if (firstLoad || !homeIterator.current) {
homeIterator.current = masto.v1.timelines.listHome({ limit: LIMIT });
}
const results = await homeIterator.current.next();
const { value } = results;
if (value?.length) {
2023-02-07 16:31:46 +00:00
if (firstLoad) {
latestItem.current = value[0].id;
}
value.forEach((item) => {
saveStatus(item, instance);
});
// ENFORCE sort by datetime (Latest first)
value.sort((a, b) => {
const aDate = new Date(a.createdAt);
const bDate = new Date(b.createdAt);
return bDate - aDate;
});
}
return results;
2023-02-03 13:08:08 +00:00
}
2023-02-07 16:31:46 +00:00
async function checkForUpdates() {
try {
const results = await masto.v1.timelines
.listHome({
limit: 5,
since_id: latestItem.current,
})
.next();
const { value } = results;
2023-02-09 14:27:49 +00:00
console.log('checkForUpdates', latestItem.current, value);
if (value?.length && value.some((item) => !item.reblog)) {
2023-02-07 16:31:46 +00:00
return true;
}
return false;
} catch (e) {
return false;
}
}
const ws = useRef();
2023-02-08 11:11:33 +00:00
const streamUser = async () => {
console.log('🎏 Start streaming user', ws.current);
2023-02-07 16:31:46 +00:00
if (
ws.current &&
(ws.current.readyState === WebSocket.CONNECTING ||
ws.current.readyState === WebSocket.OPEN)
) {
console.log('🎏 Streaming user already open');
return;
}
const stream = await masto.v1.stream.streamUser();
ws.current = stream.ws;
2023-02-08 11:11:33 +00:00
ws.current.__id = Math.random();
console.log('🎏 Streaming user', ws.current);
2023-02-07 16:31:46 +00:00
stream.on('status.update', (status) => {
console.log(`🔄 Status ${status.id} updated`);
saveStatus(status, instance);
});
stream.on('delete', (statusID) => {
console.log(`❌ Status ${statusID} deleted`);
// delete states.statuses[statusID];
const s = getStatus(statusID, instance);
if (s) s._deleted = true;
});
2023-02-08 11:11:33 +00:00
stream.ws.onclose = () => {
console.log('🎏 Streaming user closed');
};
2023-02-07 16:31:46 +00:00
return stream;
2023-02-08 11:11:33 +00:00
};
2023-02-07 16:31:46 +00:00
useEffect(() => {
2023-02-08 11:11:33 +00:00
let stream;
(async () => {
stream = await streamUser();
})();
2023-02-07 16:31:46 +00:00
return () => {
2023-02-08 11:11:33 +00:00
if (stream) {
stream.ws.close();
2023-02-07 16:31:46 +00:00
ws.current = null;
}
};
}, []);
2023-02-09 14:27:49 +00:00
const headerEnd = (
<Link
to="/notifications"
class={`button plain ${
2023-02-12 09:38:50 +00:00
snapStates.notificationsShowNew ? 'has-badge' : ''
2023-02-09 14:27:49 +00:00
}`}
onClick={(e) => {
e.stopPropagation();
}}
>
<Icon icon="notification" size="l" alt="Notifications" />
</Link>
);
2023-02-03 13:08:08 +00:00
return (
<Timeline
2023-02-09 14:27:49 +00:00
title={title || 'Following'}
id={id || 'following'}
2023-02-03 13:08:08 +00:00
emptyText="Nothing to see here."
errorText="Unable to load posts."
fetchItems={fetchHome}
2023-02-07 16:31:46 +00:00
checkForUpdates={checkForUpdates}
useItemID
2023-02-09 14:27:49 +00:00
headerStart={headerStart}
headerEnd={headerEnd}
2023-02-03 13:08:08 +00:00
boostsCarousel={snapStates.settings.boostsCarousel}
/>
);
}
export default Following;