mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
Prefetch all the things!
This commit is contained in:
@@ -20,8 +20,14 @@
|
||||
<script>
|
||||
// Start critical data fetches before React loads — shaves ~1-2s off startup.
|
||||
// React hooks consume the promises via window.__prefetch.
|
||||
var j = function(r) { return r.json(); };
|
||||
window.__prefetch = {
|
||||
unreads: fetch('/api/read-state/unreads').then(function(r) { return r.json(); }),
|
||||
config: fetch('/api/radio/config').then(j),
|
||||
settings: fetch('/api/settings').then(j),
|
||||
channels: fetch('/api/channels').then(j),
|
||||
contacts: fetch('/api/contacts?limit=1000&offset=0').then(j),
|
||||
unreads: fetch('/api/read-state/unreads').then(j),
|
||||
undecryptedCount: fetch('/api/packets/undecrypted/count').then(j),
|
||||
};
|
||||
</script>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Suspense,
|
||||
} from 'react';
|
||||
import { api } from './api';
|
||||
import { takePrefetch } from './prefetch';
|
||||
import { useWebSocket } from './useWebSocket';
|
||||
import {
|
||||
useRepeaterMode,
|
||||
@@ -308,7 +309,7 @@ export function App() {
|
||||
// Fetch radio config (not sent via WebSocket)
|
||||
const fetchConfig = useCallback(async () => {
|
||||
try {
|
||||
const data = await api.getRadioConfig();
|
||||
const data = await (takePrefetch('config') ?? api.getRadioConfig());
|
||||
setConfig(data);
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch config:', err);
|
||||
@@ -318,7 +319,7 @@ export function App() {
|
||||
// Fetch app settings
|
||||
const fetchAppSettings = useCallback(async () => {
|
||||
try {
|
||||
const data = await api.getSettings();
|
||||
const data = await (takePrefetch('settings') ?? api.getSettings());
|
||||
setAppSettings(data);
|
||||
// Initialize in-memory cache with server data
|
||||
initLastMessageTimes(data.last_message_times ?? {});
|
||||
@@ -330,7 +331,7 @@ export function App() {
|
||||
// Fetch undecrypted packet count
|
||||
const fetchUndecryptedCount = useCallback(async () => {
|
||||
try {
|
||||
const data = await api.getUndecryptedPacketCount();
|
||||
const data = await (takePrefetch('undecryptedCount') ?? api.getUndecryptedPacketCount());
|
||||
setUndecryptedCount(data.count);
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch undecrypted count:', err);
|
||||
@@ -340,7 +341,7 @@ export function App() {
|
||||
// Fetch all contacts, paginating if >1000
|
||||
const fetchAllContacts = useCallback(async (): Promise<Contact[]> => {
|
||||
const pageSize = 1000;
|
||||
const first = await api.getContacts(pageSize, 0);
|
||||
const first = await (takePrefetch('contacts') ?? api.getContacts(pageSize, 0));
|
||||
if (first.length < pageSize) return first;
|
||||
let all = [...first];
|
||||
let offset = pageSize;
|
||||
@@ -360,7 +361,7 @@ export function App() {
|
||||
fetchUndecryptedCount();
|
||||
|
||||
// Fetch contacts and channels via REST (parallel, faster than WS serial push)
|
||||
api.getChannels().then(setChannels).catch(console.error);
|
||||
(takePrefetch('channels') ?? api.getChannels()).then(setChannels).catch(console.error);
|
||||
fetchAllContacts()
|
||||
.then((data) => {
|
||||
setContacts(data);
|
||||
|
||||
@@ -7,12 +7,7 @@ import {
|
||||
type ConversationTimes,
|
||||
} from '../utils/conversationState';
|
||||
import type { Channel, Contact, Conversation, Message, UnreadCounts } from '../types';
|
||||
|
||||
// Consume the prefetched unreads promise started in index.html (if available).
|
||||
// This lets the fetch run while React JS is still downloading/parsing.
|
||||
const prefetchedUnreads: Promise<UnreadCounts> | undefined = (
|
||||
window as unknown as { __prefetch?: { unreads?: Promise<UnreadCounts> } }
|
||||
).__prefetch?.unreads;
|
||||
import { takePrefetch } from '../prefetch';
|
||||
|
||||
export interface UseUnreadCountsResult {
|
||||
unreadCounts: Record<string, number>;
|
||||
@@ -63,8 +58,9 @@ export function useUnreadCounts(
|
||||
const contactsLen = contacts.length;
|
||||
const prevLens = useRef({ channels: 0, contacts: 0 });
|
||||
useEffect(() => {
|
||||
if (prefetchedUnreads) {
|
||||
prefetchedUnreads.then(applyUnreads).catch(() => fetchUnreads());
|
||||
const prefetched = takePrefetch('unreads');
|
||||
if (prefetched) {
|
||||
prefetched.then(applyUnreads).catch(() => fetchUnreads());
|
||||
} else {
|
||||
fetchUnreads();
|
||||
}
|
||||
|
||||
26
frontend/src/prefetch.ts
Normal file
26
frontend/src/prefetch.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Consume prefetched API promises started in index.html before React loaded.
|
||||
*
|
||||
* Each key is consumed at most once — the first caller gets the promise,
|
||||
* subsequent callers get undefined and should fall back to a normal fetch.
|
||||
*/
|
||||
|
||||
import type { AppSettings, Channel, Contact, RadioConfig, UnreadCounts } from './types';
|
||||
|
||||
interface PrefetchMap {
|
||||
config?: Promise<RadioConfig>;
|
||||
settings?: Promise<AppSettings>;
|
||||
channels?: Promise<Channel[]>;
|
||||
contacts?: Promise<Contact[]>;
|
||||
unreads?: Promise<UnreadCounts>;
|
||||
undecryptedCount?: Promise<{ count: number }>;
|
||||
}
|
||||
|
||||
const store: PrefetchMap = (window as unknown as { __prefetch?: PrefetchMap }).__prefetch ?? {};
|
||||
|
||||
/** Take a prefetched promise (consumed once, then gone). */
|
||||
export function takePrefetch<K extends keyof PrefetchMap>(key: K): PrefetchMap[K] {
|
||||
const p = store[key];
|
||||
delete store[key];
|
||||
return p;
|
||||
}
|
||||
Reference in New Issue
Block a user