mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-23 09:37:54 +01:00
Automatically update to newest data when receiving cached data
This commit is contained in:
parent
0bd0bbef24
commit
ef0ba83412
@ -24,6 +24,7 @@ import com.djrapitops.plan.identification.ServerInfo;
|
||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.settings.config.paths.DisplaySettings;
|
||||
import com.djrapitops.plan.settings.config.paths.ProxySettings;
|
||||
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
|
||||
import com.djrapitops.plan.settings.theme.Theme;
|
||||
import com.djrapitops.plan.settings.theme.ThemeVal;
|
||||
import com.djrapitops.plan.utilities.java.Maps;
|
||||
@ -85,6 +86,7 @@ public class MetadataJSONResolver implements NoAuthResolver {
|
||||
.put("serverUUID", serverInfo.getServer().getUuid().toString())
|
||||
.put("networkName", serverInfo.getServer().isProxy() ? config.get(ProxySettings.NETWORK_NAME) : null)
|
||||
.put("mainCommand", mainCommand)
|
||||
.put("refreshBarrierMs", config.get(WebserverSettings.REDUCED_REFRESH_BARRIER))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ const Header = ({page, tab}) => {
|
||||
const {toggleColorChooser} = useTheme();
|
||||
const {t} = useTranslation();
|
||||
|
||||
const {requestUpdate, updating, lastUpdate, toggleSidebar} = useNavigation();
|
||||
const {requestUpdate, lastUpdate, updating, toggleSidebar} = useNavigation();
|
||||
|
||||
const {getPlayerHeadImageUrl} = useMetadata();
|
||||
const headImageUrl = user ? getPlayerHeadImageUrl(user.playerName, user.linkedToUuid) : undefined
|
||||
|
@ -1,23 +1,64 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {useNavigation} from "./navigationHook";
|
||||
import {useDataStore} from "./datastoreHook";
|
||||
import {useMetadata} from "./metadataHook";
|
||||
|
||||
export const useDataRequest = (fetchMethod, parameters) => {
|
||||
const [data, setData] = useState(undefined);
|
||||
const [loadingError, setLoadingError] = useState(undefined);
|
||||
const {updateRequested, finishUpdate} = useNavigation();
|
||||
const {refreshBarrierMs} = useMetadata();
|
||||
const datastore = useDataStore();
|
||||
|
||||
/*eslint-disable react-hooks/exhaustive-deps */
|
||||
useEffect(() => {
|
||||
fetchMethod(updateRequested, ...parameters).then(({data: json, error}) => {
|
||||
datastore.setAsUpdating(fetchMethod);
|
||||
const handleResponse = (json, error, skipOldData, timeout) => {
|
||||
if (json) {
|
||||
setData(json);
|
||||
finishUpdate(json.timestamp, json.timestamp_f);
|
||||
const timestamp = json.timestamp;
|
||||
if (timestamp) {
|
||||
// Data has timestamp, the data may come from cache
|
||||
const acceptedTimestamp = timestamp + (refreshBarrierMs ? refreshBarrierMs : 15000);
|
||||
if (acceptedTimestamp < updateRequested) {
|
||||
// Request again, received data was too old
|
||||
setTimeout(() => {
|
||||
fetchMethod(new Date().getTime(), ...parameters)
|
||||
.then(({data: json, error}) => {
|
||||
handleResponse(json, error, true, timeout >= 12000 ? timeout : timeout * 2);
|
||||
});
|
||||
}, timeout);
|
||||
} else {
|
||||
// Received data was new enough to be shown
|
||||
setData(json);
|
||||
datastore.storeData(fetchMethod, json);
|
||||
datastore.finishUpdate(fetchMethod)
|
||||
finishUpdate(json.timestamp, json.timestamp_f, datastore.isSomethingUpdating());
|
||||
}
|
||||
|
||||
if (!skipOldData) {
|
||||
// Old data is shown on first pass, further passes skip old data.
|
||||
setData(json);
|
||||
datastore.storeData(fetchMethod, json);
|
||||
finishUpdate(json.timestamp, json.timestamp_f, datastore.isSomethingUpdating());
|
||||
}
|
||||
} else {
|
||||
// Response data is not cached, no timestamp, show it immediately
|
||||
setData(json);
|
||||
datastore.finishUpdate(fetchMethod);
|
||||
finishUpdate(json.timestamp, json.timestamp_f, datastore.isSomethingUpdating());
|
||||
}
|
||||
} else if (error) {
|
||||
console.warn(error);
|
||||
datastore.finishUpdate(fetchMethod)
|
||||
setLoadingError(error);
|
||||
finishUpdate(new Date().getTime(), "Error", datastore.isSomethingUpdating());
|
||||
}
|
||||
};
|
||||
|
||||
fetchMethod(updateRequested, ...parameters).then(({data: json, error}) => {
|
||||
handleResponse(json, error, false, 1000);
|
||||
});
|
||||
}, [fetchMethod, ...parameters, updateRequested])
|
||||
}, [fetchMethod, ...parameters, updateRequested, refreshBarrierMs])
|
||||
/* eslint-enable react-hooks/exhaustive-deps */
|
||||
|
||||
return {data, loadingError};
|
||||
|
45
Plan/react/dashboard/src/hooks/datastoreHook.js
Normal file
45
Plan/react/dashboard/src/hooks/datastoreHook.js
Normal file
@ -0,0 +1,45 @@
|
||||
import {useCallback} from "react";
|
||||
import {useMetadata} from "./metadataHook";
|
||||
|
||||
export const useDataStore = () => {
|
||||
const {datastore} = useMetadata();
|
||||
|
||||
if (datastore && !datastore.dataByMethod) datastore.dataByMethod = {};
|
||||
if (datastore && !datastore.lastUpdateByMethod) datastore.lastUpdateByMethod = {};
|
||||
if (datastore && !datastore.currentlyUpdatingMethods) datastore.currentlyUpdatingMethods = {};
|
||||
|
||||
const storeData = useCallback((method, data) => {
|
||||
const hadPrevious = Boolean(datastore.dataByMethod[method]);
|
||||
if (data) {
|
||||
datastore.lastUpdateByMethod[method] = data.timestamp;
|
||||
datastore.dataByMethod[method] = data;
|
||||
}
|
||||
return hadPrevious;
|
||||
}, [datastore]);
|
||||
|
||||
const getLastUpdate = useCallback((method) => {
|
||||
return datastore?.lastUpdateByMethod[method];
|
||||
}, [datastore]);
|
||||
|
||||
const getData = useCallback((method) => {
|
||||
return datastore?.dataByMethod[method];
|
||||
}, [datastore]);
|
||||
|
||||
const isCurrentlyUpdating = useCallback((method) => {
|
||||
return datastore && Boolean(datastore.currentlyUpdatingMethods[method]);
|
||||
}, [datastore]);
|
||||
|
||||
const setAsUpdating = useCallback((method) => {
|
||||
datastore.currentlyUpdatingMethods[method] = true;
|
||||
}, [datastore]);
|
||||
|
||||
const finishUpdate = useCallback((method) => {
|
||||
delete datastore.currentlyUpdatingMethods[method];
|
||||
}, [datastore])
|
||||
|
||||
const isSomethingUpdating = useCallback(() => {
|
||||
return datastore && Boolean(Object.values(datastore.currentlyUpdatingMethods).filter(value => Boolean(value)).length);
|
||||
}, [datastore]);
|
||||
|
||||
return {storeData, getLastUpdate, getData, isCurrentlyUpdating, isSomethingUpdating, setAsUpdating, finishUpdate};
|
||||
}
|
@ -6,6 +6,7 @@ import terminal from '../Terminal-icon.png'
|
||||
const MetadataContext = createContext({});
|
||||
|
||||
export const MetadataContextProvider = ({children}) => {
|
||||
const [datastore] = useState({});
|
||||
const [metadata, setMetadata] = useState({});
|
||||
|
||||
const updateMetadata = useCallback(async () => {
|
||||
@ -34,7 +35,7 @@ export const MetadataContextProvider = ({children}) => {
|
||||
updateMetadata();
|
||||
}, [updateMetadata]);
|
||||
|
||||
const sharedState = {...metadata, getPlayerHeadImageUrl}
|
||||
const sharedState = {...metadata, getPlayerHeadImageUrl, datastore}
|
||||
return (<MetadataContext.Provider value={sharedState}>
|
||||
{children}
|
||||
</MetadataContext.Provider>
|
||||
|
@ -5,8 +5,8 @@ const NavigationContext = createContext({});
|
||||
export const NavigationContextProvider = ({children}) => {
|
||||
const [currentTab, setCurrentTab] = useState(undefined);
|
||||
const [updateRequested, setUpdateRequested] = useState(Date.now());
|
||||
const [updating, setUpdating] = useState(false);
|
||||
const [lastUpdate, setLastUpdate] = useState({date: 0, formatted: ""});
|
||||
const [updating, setUpdating] = useState({});
|
||||
const [lastUpdate, setLastUpdate] = useState({});
|
||||
|
||||
const [items, setItems] = useState([]);
|
||||
const [sidebarExpanded, setSidebarExpanded] = useState(window.innerWidth > 1350);
|
||||
@ -31,13 +31,17 @@ export const NavigationContextProvider = ({children}) => {
|
||||
}
|
||||
}, [updating, setUpdateRequested, setUpdating]);
|
||||
|
||||
const finishUpdate = useCallback((date, formatted) => {
|
||||
// TODO Logic to retry if received data is too old
|
||||
// TODO currently not possible due to extensionData getting updated off-tab
|
||||
// useEffect(requestUpdate, [currentTab]); // Force data to update when changing tab
|
||||
|
||||
const finishUpdate = useCallback((date, formatted, isStillUpdating) => {
|
||||
if (date) {
|
||||
setLastUpdate({date, formatted});
|
||||
setUpdating(false);
|
||||
if (!lastUpdate.date || date > lastUpdate.date) {
|
||||
setLastUpdate({date, formatted});
|
||||
}
|
||||
setUpdating(isStillUpdating);
|
||||
}
|
||||
}, [setLastUpdate, setUpdating]);
|
||||
}, [setLastUpdate, setUpdating, lastUpdate]);
|
||||
|
||||
const toggleSidebar = useCallback(() => {
|
||||
setSidebarExpanded(!sidebarExpanded);
|
||||
|
Loading…
Reference in New Issue
Block a user