kopia lustrzana https://github.com/bugout-dev/moonstream
Got existing frontend working with updated API
Currently only renders `"ethereum_blockchain"` events. Moved all stream state into the `useStream` hook. This is how we started and really this is what makes sense rather than putting some pretty heavy logic into the component/view.pull/105/head
rodzic
6959b78daf
commit
686bfef49b
|
@ -44,7 +44,7 @@ const FILTER_TYPES = {
|
|||
ADDRESS: 0,
|
||||
GAS: 1,
|
||||
GAS_PRICE: 2,
|
||||
AMMOUNT: 3,
|
||||
AMOUNT: 3,
|
||||
HASH: 4,
|
||||
DISABLED: 99,
|
||||
};
|
||||
|
@ -63,6 +63,7 @@ const EntriesNavigation = () => {
|
|||
const ui = useContext(UIContext);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { subscriptionsCache } = useSubscriptions();
|
||||
const [entries, setEntries] = useState([]);
|
||||
const [newFilterState, setNewFilterState] = useState([
|
||||
{
|
||||
type: FILTER_TYPES.ADDRESS,
|
||||
|
@ -75,93 +76,44 @@ const EntriesNavigation = () => {
|
|||
|
||||
const loadMoreButtonRef = useRef(null);
|
||||
|
||||
const [streamBoundary, setStreamBoundary] = useState({
|
||||
start_time: null,
|
||||
end_time: null,
|
||||
include_start: false,
|
||||
include_end: true,
|
||||
next_event_time: null,
|
||||
previous_event_time: null,
|
||||
});
|
||||
|
||||
const updateStreamBoundaryWith = (pageBoundary) => {
|
||||
if (!pageBoundary) {
|
||||
return streamBoundary;
|
||||
}
|
||||
|
||||
let newBoundary = { ...streamBoundary };
|
||||
// We do not check if there is no overlap between the streamBoundary and the pageBoundary - we assume
|
||||
// that there *is* an overlap and even if there isn't the stream should gracefully respect the
|
||||
// pageBoundary because that was the most recent request the user made.
|
||||
// TODO(zomglings): If there is no overlap in boundaries, replace streamBoundary with pageBoundary.
|
||||
// No overlap logic:
|
||||
// if (<no overlap>) {
|
||||
// setStreamBoundary(pageBoundary)
|
||||
// return pageBoundary
|
||||
// }
|
||||
|
||||
if (
|
||||
!newBoundary.start_time ||
|
||||
(pageBoundary.start_time &&
|
||||
pageBoundary.start_time <= newBoundary.start_time)
|
||||
) {
|
||||
newBoundary.start_time = pageBoundary.start_time;
|
||||
newBoundary.include_start =
|
||||
newBoundary.include_start || pageBoundary.include_start;
|
||||
}
|
||||
newBoundary.include_start =
|
||||
newBoundary.include_start || pageBoundary.include_start;
|
||||
|
||||
if (
|
||||
!newBoundary.end_time ||
|
||||
(pageBoundary.end_time && pageBoundary.end_time >= newBoundary.end_time)
|
||||
) {
|
||||
newBoundary.end_time = pageBoundary.end_time;
|
||||
newBoundary.include_end =
|
||||
newBoundary.include_end || pageBoundary.include_end;
|
||||
}
|
||||
|
||||
newBoundary.include_end =
|
||||
newBoundary.include_end || pageBoundary.include_end;
|
||||
|
||||
if (
|
||||
!newBoundary.next_event_time ||
|
||||
!pageBoundary.next_event_time ||
|
||||
(pageBoundary.next_event_time &&
|
||||
pageBoundary.next_event_time > newBoundary.next_event_time)
|
||||
) {
|
||||
newBoundary.next_event_time = pageBoundary.next_event_time;
|
||||
}
|
||||
|
||||
if (
|
||||
!newBoundary.previous_event_time ||
|
||||
!pageBoundary.previous_event_time ||
|
||||
(pageBoundary.previous_event_time &&
|
||||
pageBoundary.previous_event_time < newBoundary.previous_event_time)
|
||||
) {
|
||||
newBoundary.previous_event_time = pageBoundary.previous_event_time;
|
||||
}
|
||||
setStreamBoundary(newBoundary);
|
||||
return newBoundary;
|
||||
};
|
||||
|
||||
const { EntriesPages, isLoading, refetch, isFetching, remove } = useStream({
|
||||
searchQuery: ui.searchTerm,
|
||||
start_time: streamBoundary.start_time,
|
||||
end_time: streamBoundary.end_time,
|
||||
include_start: streamBoundary.include_start,
|
||||
include_end: streamBoundary.include_end,
|
||||
updateStreamBoundaryWith: updateStreamBoundaryWith,
|
||||
streamBoundary: streamBoundary,
|
||||
setStreamBoundary: setStreamBoundary,
|
||||
isContent: false,
|
||||
});
|
||||
const {
|
||||
events,
|
||||
eventsIsLoading,
|
||||
eventsRefetch,
|
||||
eventsIsFetching,
|
||||
eventsRemove,
|
||||
latestEvents,
|
||||
latestEventsIsLoading,
|
||||
latestEventsRefetch,
|
||||
latestEventsIsFetching,
|
||||
nextEvent,
|
||||
nextEventRefetch,
|
||||
previousEvent,
|
||||
previousEventRefetch,
|
||||
streamBoundary,
|
||||
setDefaultBoundary,
|
||||
} = useStream(ui.searchTerm.q);
|
||||
|
||||
useEffect(() => {
|
||||
if (!streamBoundary.start_time && !streamBoundary.end_time) {
|
||||
refetch();
|
||||
setDefaultBoundary();
|
||||
} else {
|
||||
eventsRefetch();
|
||||
latestEventsRefetch();
|
||||
nextEventRefetch();
|
||||
previousEventRefetch();
|
||||
}
|
||||
}, [streamBoundary, refetch]);
|
||||
}, [streamBoundary]);
|
||||
|
||||
useEffect(() => {
|
||||
if (events) {
|
||||
events.then((data) => {
|
||||
if (data && data.events) {
|
||||
setEntries(data.events);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [events]);
|
||||
|
||||
const setFilterProps = useCallback(
|
||||
(filterIdx, props) => {
|
||||
|
@ -183,13 +135,6 @@ const EntriesNavigation = () => {
|
|||
}
|
||||
}, [subscriptionsCache, newFilterState, setFilterProps]);
|
||||
|
||||
const entriesPagesData = EntriesPages
|
||||
? EntriesPages.data.map((page) => {
|
||||
return page;
|
||||
})
|
||||
: [""];
|
||||
|
||||
const entries = entriesPagesData.flat();
|
||||
const canCreate = false;
|
||||
|
||||
const canDelete = false;
|
||||
|
@ -265,7 +210,7 @@ const EntriesNavigation = () => {
|
|||
direction="column"
|
||||
flexGrow={1}
|
||||
>
|
||||
{entries && !isLoading ? (
|
||||
{entries && !eventsIsLoading ? (
|
||||
<>
|
||||
<Drawer onClose={onClose} isOpen={isOpen} size="lg">
|
||||
<DrawerOverlay />
|
||||
|
@ -476,10 +421,10 @@ const EntriesNavigation = () => {
|
|||
//onScroll={(e) => handleScroll(e)}
|
||||
>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
{!isFetching ? (
|
||||
{!eventsIsFetching ? (
|
||||
<Button
|
||||
onClick={() => {
|
||||
remove();
|
||||
eventsRemove();
|
||||
setStreamBoundary({
|
||||
start_time: null,
|
||||
end_time: null,
|
||||
|
@ -505,7 +450,7 @@ const EntriesNavigation = () => {
|
|||
|
||||
{streamBoundary.next_event_time &&
|
||||
streamBoundary.end_time != 0 &&
|
||||
!isFetching ? (
|
||||
!eventsIsFetching ? (
|
||||
<Button
|
||||
onClick={() => {
|
||||
updateStreamBoundaryWith({
|
||||
|
@ -523,24 +468,22 @@ const EntriesNavigation = () => {
|
|||
"" // some strange behaivior without else condition return 0 wich can see on frontend page
|
||||
)}
|
||||
</Stack>
|
||||
{entries
|
||||
?.sort((a, b) => b.timestamp - a.timestamp) // TODO(Andrey) improve that for bi chunks of data sorting can take time
|
||||
.map((entry, idx) => (
|
||||
<StreamEntry
|
||||
showOnboardingTooltips={false}
|
||||
key={`entry-list-${idx}`}
|
||||
entry={entry}
|
||||
disableDelete={!canDelete}
|
||||
disableCopy={!canCreate}
|
||||
filterCallback={handleFilterStateCallback}
|
||||
filterConstants={{ DIRECTIONS, CONDITION, FILTER_TYPES }}
|
||||
/>
|
||||
))}
|
||||
{streamBoundary.previous_event_time && !isFetching ? (
|
||||
{entries.map((entry, idx) => (
|
||||
<StreamEntry
|
||||
showOnboardingTooltips={false}
|
||||
key={`entry-list-${idx}`}
|
||||
entry={entry}
|
||||
disableDelete={!canDelete}
|
||||
disableCopy={!canCreate}
|
||||
filterCallback={handleFilterStateCallback}
|
||||
filterConstants={{ DIRECTIONS, CONDITION, FILTER_TYPES }}
|
||||
/>
|
||||
))}
|
||||
{streamBoundary.previous_event_time && !eventsIsFetching ? (
|
||||
<Center>
|
||||
<Button
|
||||
onClick={() => {
|
||||
remove();
|
||||
eventsRemove();
|
||||
updateStreamBoundaryWith({
|
||||
start_time: streamBoundary.previous_event_time - 5 * 60,
|
||||
include_start: false,
|
||||
|
@ -555,7 +498,7 @@ const EntriesNavigation = () => {
|
|||
</Center>
|
||||
) : (
|
||||
<Center>
|
||||
{!isFetching ? (
|
||||
{!eventsIsFetching ? (
|
||||
"Тransactions not found. You can subscribe to more addresses in Subscriptions menu."
|
||||
) : (
|
||||
<Button
|
||||
|
@ -567,7 +510,7 @@ const EntriesNavigation = () => {
|
|||
)}
|
||||
</Center>
|
||||
)}
|
||||
{streamBoundary.previous_event_time && isLoading ? (
|
||||
{streamBoundary.previous_event_time && eventsIsLoading ? (
|
||||
<Center>
|
||||
<Spinner
|
||||
//hidden={!isFetchingMore}
|
||||
|
|
|
@ -34,7 +34,7 @@ const StreamEntry_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
h="100%"
|
||||
spacing={0}
|
||||
>
|
||||
{entry.subscription_type_id === "0" && (
|
||||
{entry.event_type === "ethereum_blockchain" && (
|
||||
<EthereumMempoolCard entry={entry} />
|
||||
)}
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ const EthereumMempoolCard_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
const { onCopy, hasCopied } = useClipboard(copyString, 1);
|
||||
const toast = useToast();
|
||||
|
||||
console.log("GREEN RAIN:", subscriptionsCache.data);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasCopied && copyString) {
|
||||
toast("Copied to clipboard", "success");
|
||||
|
@ -38,14 +40,24 @@ const EthereumMempoolCard_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
|
||||
const from_color =
|
||||
subscriptionsCache.data.subscriptions.find((obj) => {
|
||||
return obj.address === entry.from_address;
|
||||
return obj.address === entry.event_data.from;
|
||||
})?.color ?? "gray.500";
|
||||
|
||||
const from_label =
|
||||
subscriptionsCache.data.subscriptions.find((obj) => {
|
||||
return obj.address === entry.event_data.from;
|
||||
})?.label ?? entry.event_data.from;
|
||||
|
||||
const to_color =
|
||||
subscriptionsCache.data.subscriptions.find((obj) => {
|
||||
return obj.address === entry.to_address;
|
||||
return obj.address === entry.event_data.to;
|
||||
})?.color ?? "gray.500";
|
||||
|
||||
const to_label =
|
||||
subscriptionsCache.data.subscriptions.find((obj) => {
|
||||
return obj.address === entry.event_data.to;
|
||||
})?.label ?? entry.event_data.to;
|
||||
|
||||
return (
|
||||
<Stack className={className}>
|
||||
<Tooltip
|
||||
|
@ -76,8 +88,12 @@ const EthereumMempoolCard_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
Hash
|
||||
</Heading>
|
||||
<Spacer />
|
||||
<Text isTruncated onClick={() => setCopyString(entry.hash)} pr={12}>
|
||||
{entry.hash}
|
||||
<Text
|
||||
isTruncated
|
||||
onClick={() => setCopyString(entry.event_data.hash)}
|
||||
pr={12}
|
||||
>
|
||||
{entry.event_data.hash}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Tooltip>
|
||||
|
@ -125,7 +141,7 @@ const EthereumMempoolCard_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
From:
|
||||
</Text>
|
||||
</Tooltip>
|
||||
<Tooltip label={entry.from_address} aria-label="From:">
|
||||
<Tooltip label={entry.event_data.from} aria-label="From:">
|
||||
<Text
|
||||
mx={0}
|
||||
py="2px"
|
||||
|
@ -134,9 +150,9 @@ const EthereumMempoolCard_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
isTruncated
|
||||
w="calc(100%)"
|
||||
h="100%"
|
||||
onClick={() => setCopyString(entry.from_address)}
|
||||
onClick={() => setCopyString(entry.event_data.from)}
|
||||
>
|
||||
{entry.from_label ?? entry.from_address}
|
||||
{from_label}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
|
@ -162,15 +178,15 @@ const EthereumMempoolCard_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
>
|
||||
To:
|
||||
</Text>
|
||||
<Tooltip label={entry.to_address} aria-label="From:">
|
||||
<Tooltip label={entry.event_data.to} aria-label="From:">
|
||||
<Text
|
||||
bgColor={to_color}
|
||||
isTruncated
|
||||
w="calc(100%)"
|
||||
h="100%"
|
||||
onClick={() => setCopyString(entry.to_address)}
|
||||
onClick={() => setCopyString(entry.event_data.to)}
|
||||
>
|
||||
{entry.to_label ?? entry.to_address}
|
||||
{to_label}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
|
@ -188,16 +204,16 @@ const EthereumMempoolCard_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
>
|
||||
Gas Price:
|
||||
</Text>
|
||||
<Tooltip label={entry.gasPrice} aria-label="Gas Price:">
|
||||
<Tooltip label={entry.event_data.gasPrice} aria-label="Gas Price:">
|
||||
<Text
|
||||
mx={0}
|
||||
py="2px"
|
||||
fontSize="sm"
|
||||
w="calc(100%)"
|
||||
h="100%"
|
||||
onClick={() => setCopyString(entry.gasPrice)}
|
||||
onClick={() => setCopyString(entry.event_data.gasPrice)}
|
||||
>
|
||||
{entry.gasPrice}
|
||||
{entry.event_data.gasPrice}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
|
@ -212,16 +228,16 @@ const EthereumMempoolCard_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
>
|
||||
Gas:
|
||||
</Text>
|
||||
<Tooltip label={entry.gas} aria-label="Gas:">
|
||||
<Tooltip label={entry.event_data.gas} aria-label="Gas:">
|
||||
<Text
|
||||
mx={0}
|
||||
py="2px"
|
||||
fontSize="sm"
|
||||
w="calc(100%)"
|
||||
h="100%"
|
||||
onClick={() => setCopyString(entry.gas)}
|
||||
onClick={() => setCopyString(entry.event_data.gas)}
|
||||
>
|
||||
{entry.gas}
|
||||
{entry.event_data.gas}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
|
@ -236,16 +252,16 @@ const EthereumMempoolCard_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
>
|
||||
Value:
|
||||
</Text>
|
||||
<Tooltip label={entry.value} aria-label="Value:">
|
||||
<Tooltip label={entry.event_data.value} aria-label="Value:">
|
||||
<Text
|
||||
mx={0}
|
||||
py="2px"
|
||||
fontSize="sm"
|
||||
w="calc(100%)"
|
||||
h="100%"
|
||||
onClick={() => setCopyString(entry.value)}
|
||||
onClick={() => setCopyString(entry.event_data.value)}
|
||||
>
|
||||
{entry.value}
|
||||
{entry.event_data.value}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
|
@ -261,20 +277,20 @@ const EthereumMempoolCard_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
>
|
||||
Nonce:
|
||||
</Text>
|
||||
<Tooltip label={entry.value} aria-label="Value:">
|
||||
<Tooltip label={entry.event_data.value} aria-label="Value:">
|
||||
<Text
|
||||
mx={0}
|
||||
py="2px"
|
||||
fontSize="sm"
|
||||
w="calc(100%)"
|
||||
h="100%"
|
||||
onClick={() => setCopyString(entry.value)}
|
||||
onClick={() => setCopyString(entry.event_data.value)}
|
||||
>
|
||||
{entry.nonce}
|
||||
{entry.event_data.nonce}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
{entry.timestamp && (
|
||||
{entry.event_timestamp && (
|
||||
<Flex h="auto" minW="fit-content">
|
||||
<Text
|
||||
px={1}
|
||||
|
@ -285,7 +301,9 @@ const EthereumMempoolCard_ = ({ entry, showOnboardingTooltips, className }) => {
|
|||
h="100%"
|
||||
borderColor="gray.700"
|
||||
>
|
||||
{moment(entry.timestamp * 1000).format("DD MMM, YYYY, HH:mm:ss")}{" "}
|
||||
{moment(entry.event_timestamp * 1000).format(
|
||||
"DD MMM, YYYY, HH:mm:ss"
|
||||
)}{" "}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export const BUGOUT_API_URL = process.env.NEXT_PUBLIC_SIMIOTICS_SEARCH_URL;
|
||||
export const MOONSTREAM_API_URL = process.env.NEXT_PUBLIC_MOONSTREAM_API_URL;
|
||||
|
||||
export const BUGOUT_ENDPOINTS = {
|
||||
Usage: "usage",
|
||||
|
|
|
@ -1,61 +1,211 @@
|
|||
import { useState } from "react";
|
||||
|
||||
import { StreamService } from "../services";
|
||||
import { useQuery } from "react-query";
|
||||
import { queryCacheProps } from "./hookCommon";
|
||||
import { defaultStreamBoundary } from "../services/servertime.service.js";
|
||||
|
||||
const useJournalEntries = ({
|
||||
searchQuery,
|
||||
start_time,
|
||||
end_time,
|
||||
include_start,
|
||||
include_end,
|
||||
updateStreamBoundaryWith,
|
||||
}) => {
|
||||
// set our get method
|
||||
const getStream =
|
||||
(searchTerm, start_time, end_time, include_start, include_end) =>
|
||||
async () => {
|
||||
// Request with params to streams
|
||||
const response = await StreamService.getStream({
|
||||
searchTerm: searchTerm,
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
include_start: include_start,
|
||||
include_end: include_end,
|
||||
});
|
||||
const useStream = (q) => {
|
||||
const [streamQuery, setStreamQuery] = useState(q || "");
|
||||
const [streamBoundary, setStreamBoundary] = useState({});
|
||||
|
||||
// new events from stream
|
||||
const newEventsList = response.data.stream.map((event) => ({
|
||||
...event,
|
||||
}));
|
||||
const isStreamBoundaryEmpty = () => {
|
||||
return !streamBoundary.start_time && !streamBoundary.end_time;
|
||||
};
|
||||
|
||||
return {
|
||||
data: [...newEventsList],
|
||||
boundaries: { ...response.data.boundaries, update: false },
|
||||
};
|
||||
};
|
||||
const setDefaultBoundary = async () => {
|
||||
const defaultBoundary = await defaultStreamBoundary();
|
||||
setStreamBoundary(defaultBoundary);
|
||||
};
|
||||
|
||||
const { data, isLoading, refetch, isFetching, remove } = useQuery(
|
||||
["stream", searchQuery, start_time, end_time],
|
||||
getStream(searchQuery, start_time, end_time, include_start, include_end),
|
||||
const updateStreamBoundaryWith = (extensionBoundary) => {
|
||||
if (!extensionBoundary) {
|
||||
return streamBoundary;
|
||||
}
|
||||
|
||||
let newBoundary = { ...streamBoundary };
|
||||
// We do not check if there is no overlap between the streamBoundary and the pageBoundary - we assume
|
||||
// that there *is* an overlap and even if there isn't the stream should gracefully respect the
|
||||
// pageBoundary because that was the most recent request the user made.
|
||||
// TODO(zomglings): If there is no overlap in boundaries, replace streamBoundary with pageBoundary.
|
||||
// No overlap logic:
|
||||
// if (<no overlap>) {
|
||||
// setStreamBoundary(pageBoundary)
|
||||
// return pageBoundary
|
||||
// }
|
||||
|
||||
if (
|
||||
!newBoundary.start_time ||
|
||||
(extensionBoundary.start_time &&
|
||||
extensionBoundary.start_time <= newBoundary.start_time)
|
||||
) {
|
||||
newBoundary.start_time = extensionBoundary.start_time;
|
||||
newBoundary.include_start =
|
||||
newBoundary.include_start || extensionBoundary.include_start;
|
||||
}
|
||||
newBoundary.include_start =
|
||||
newBoundary.include_start || extensionBoundary.include_start;
|
||||
|
||||
if (
|
||||
!newBoundary.end_time ||
|
||||
(extensionBoundary.end_time &&
|
||||
extensionBoundary.end_time >= newBoundary.end_time)
|
||||
) {
|
||||
newBoundary.end_time = extensionBoundary.end_time;
|
||||
newBoundary.include_end =
|
||||
newBoundary.include_end || extensionBoundary.include_end;
|
||||
}
|
||||
|
||||
newBoundary.include_end =
|
||||
newBoundary.include_end || extensionBoundary.include_end;
|
||||
|
||||
setStreamBoundary(newBoundary);
|
||||
return newBoundary;
|
||||
};
|
||||
|
||||
const getEvents = () => async () => {
|
||||
const response = await StreamService.getEvents({
|
||||
streamQuery,
|
||||
...streamBoundary,
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const {
|
||||
data: events,
|
||||
isLoading: eventsIsLoading,
|
||||
refetch: eventsRefetch,
|
||||
isFetching: eventsIsFetching,
|
||||
remove: eventsRemove,
|
||||
} = useQuery(
|
||||
["stream-events", streamQuery],
|
||||
() => {
|
||||
if (isStreamBoundaryEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return getEvents();
|
||||
},
|
||||
{
|
||||
//refetchInterval: refreshRate,
|
||||
...queryCacheProps,
|
||||
keepPreviousData: true,
|
||||
retry: 3,
|
||||
onSuccess: (response) => {
|
||||
// response is object which return condition in getStream
|
||||
// TODO(andrey): Response should send page parameters inside "boundary" object (can be null).
|
||||
updateStreamBoundaryWith(response.boundaries);
|
||||
retry: 2,
|
||||
onSuccess: (newEvents) => {
|
||||
if (newEvents) {
|
||||
updateStreamBoundaryWith(newEvents.stream_boundary);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const getLatestEvents = async () => {
|
||||
const response = await StreamService.latestEvents({ q: streamQuery });
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const {
|
||||
data: latestEvents,
|
||||
isLoading: latestEventsIsLoading,
|
||||
refetch: latestEventsRefetch,
|
||||
isFetching: latestEventsIsFetching,
|
||||
remove: latestEventsRemove,
|
||||
} = useQuery(
|
||||
["stream-latest", streamQuery],
|
||||
() => {
|
||||
if (isStreamBoundaryEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return getLatestEvents();
|
||||
},
|
||||
{
|
||||
...queryCacheProps,
|
||||
keepPreviousData: false,
|
||||
retry: 2,
|
||||
}
|
||||
);
|
||||
|
||||
const getNextEvent = async () => {
|
||||
const response = await StreamService.nextEvent({
|
||||
q: streamQuery,
|
||||
...streamBoundary,
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const {
|
||||
data: nextEvent,
|
||||
isLoading: nextEventIsLoading,
|
||||
refetch: nextEventRefetch,
|
||||
isFetching: nextEventIsFetching,
|
||||
remove: nextEventRemove,
|
||||
} = useQuery(
|
||||
["stream-next", streamQuery],
|
||||
() => {
|
||||
if (isStreamBoundaryEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return getNextEvent();
|
||||
},
|
||||
{
|
||||
...queryCacheProps,
|
||||
keepPreviousData: false,
|
||||
retry: 2,
|
||||
}
|
||||
);
|
||||
|
||||
const getPreviousEvent = async () => {
|
||||
const response = await StreamService.previousEvent({
|
||||
q: streamQuery,
|
||||
...streamBoundary,
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const {
|
||||
data: previousEvent,
|
||||
isLoading: previousEventIsLoading,
|
||||
refetch: previousEventRefetch,
|
||||
isFetching: previousEventIsFetching,
|
||||
remove: previousEventRemove,
|
||||
} = useQuery(
|
||||
["stream-previous", streamQuery],
|
||||
() => {
|
||||
if (isStreamBoundaryEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return getPreviousEvent();
|
||||
},
|
||||
{
|
||||
...queryCacheProps,
|
||||
keepPreviousData: false,
|
||||
retry: 2,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
EntriesPages: data,
|
||||
isLoading,
|
||||
refetch,
|
||||
isFetching,
|
||||
remove,
|
||||
streamBoundary,
|
||||
setDefaultBoundary,
|
||||
updateStreamBoundaryWith,
|
||||
events,
|
||||
eventsIsLoading,
|
||||
eventsRefetch,
|
||||
eventsIsFetching,
|
||||
eventsRemove,
|
||||
setStreamQuery,
|
||||
latestEvents,
|
||||
latestEventsIsLoading,
|
||||
latestEventsRefetch,
|
||||
latestEventsIsFetching,
|
||||
latestEventsRemove,
|
||||
nextEvent,
|
||||
nextEventIsLoading,
|
||||
nextEventRefetch,
|
||||
nextEventIsFetching,
|
||||
nextEventRemove,
|
||||
previousEvent,
|
||||
previousEventIsLoading,
|
||||
previousEventRefetch,
|
||||
previousEventIsFetching,
|
||||
previousEventRemove,
|
||||
};
|
||||
};
|
||||
export default useJournalEntries;
|
||||
export default useStream;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import * as SearchService from "./search.service";
|
||||
import * as AuthService from "./auth.service";
|
||||
import * as JournalService from "./journal.service";
|
||||
import * as EntryService from "./entry.service";
|
||||
|
@ -11,7 +10,6 @@ import * as SubscriptionsService from "./subscriptions.service";
|
|||
import * as StreamService from "./stream.service";
|
||||
import * as TxInfoService from "./txinfo.service";
|
||||
export {
|
||||
SearchService,
|
||||
AuthService,
|
||||
JournalService,
|
||||
EntryService,
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import { http } from "../utils";
|
||||
|
||||
import { MOONSTREAM_API_URL } from "../constants";
|
||||
|
||||
export const serverTimeNow = async () => {
|
||||
const response = await http({
|
||||
method: "GET",
|
||||
url: `${MOONSTREAM_API_URL}/now`,
|
||||
});
|
||||
|
||||
const timestamp = response.data.epoch_time;
|
||||
// Javascript Date objects are loaded from Unix timestamps at the level of precision of milliseconds
|
||||
// since epoch start. The server returns microseconds since epoch, but the integer part of the response
|
||||
// time is at second-level precision.
|
||||
const jsTimestamp = Math.floor(timestamp * 1000);
|
||||
const jsDate = new Date(jsTimestamp);
|
||||
return jsDate;
|
||||
};
|
||||
|
||||
// Returns the milliseconds of difference between the clocks of the client and the Moonstream API
|
||||
// server. Since this involves a request to the server, it also includes the latency of making an HTTP
|
||||
// request to the server's /now endpoint and getting a response.
|
||||
export const clientServerOffsetMillis = async () => {
|
||||
// TODO(zomglings): Time the request and use a simple estimate for the latency based on:
|
||||
// 1. Size of request
|
||||
// 2. Size of resposne
|
||||
// 3. Profiling the body of the `/now` handler on the server.
|
||||
// At least a naive estimate would be something like:
|
||||
// const currentServerTime = serverTime + (responseAt - requestAt)*3/4
|
||||
//
|
||||
// This assumes that 3/4 of the latency was involved in sending the response back to the client.
|
||||
//
|
||||
// Unfortunately, it also assumes that the client clock and server clock are moving at the same
|
||||
// speed. Of course, we could check the speeds against each other by repeated calls to this method,
|
||||
// but I think we don't need that level of synchronizating yet.
|
||||
const serverTime = await serverTimeNow();
|
||||
const clientTime = new Date();
|
||||
return clientTime - serverTime;
|
||||
};
|
||||
|
||||
// Returns a stream boundary representing the past 10 minutes.
|
||||
export const defaultStreamBoundary = async () => {
|
||||
const endTime = await serverTimeNow();
|
||||
// 1 hour ago (in milliseconds)
|
||||
const startTimeMillis = endTime - 60 * 60 * 1000;
|
||||
const streamBoundary = {
|
||||
start_time: Math.floor(startTimeMillis / 1000),
|
||||
end_time: Math.floor(endTime / 1000),
|
||||
include_start: true,
|
||||
include_end: true,
|
||||
};
|
||||
return streamBoundary;
|
||||
};
|
|
@ -3,16 +3,17 @@ import { http } from "../utils";
|
|||
|
||||
const API = process.env.NEXT_PUBLIC_MOONSTREAM_API_URL;
|
||||
|
||||
export const getStream = ({
|
||||
searchTerm,
|
||||
export const getEvents = ({
|
||||
q,
|
||||
start_time,
|
||||
end_time,
|
||||
include_start,
|
||||
include_end,
|
||||
}) => {
|
||||
let params = {
|
||||
q: searchTerm,
|
||||
};
|
||||
let params = {};
|
||||
if (q) {
|
||||
params.q = q;
|
||||
}
|
||||
if (start_time || start_time === 0) {
|
||||
params.start_time = start_time;
|
||||
}
|
||||
|
@ -32,3 +33,75 @@ export const getStream = ({
|
|||
params,
|
||||
});
|
||||
};
|
||||
|
||||
// params is expected to be an object defining the query parameters to the /streams/latest endpoint
|
||||
// The /streams/latest endpoint accepts the following query parameters:
|
||||
// - q: Query filters of the form type:<subscription type> or subscription:from:<address> or subscription:to:<address>
|
||||
// This will change over time and the API documentation should be the source of truth for these parameters.
|
||||
// TODO(zomglings): Link here once API docs are set up.
|
||||
export const latestEvents = (params) => {
|
||||
return http({ method: "GET", url: `${API}/streams/latest`, params });
|
||||
};
|
||||
|
||||
export const nextEvent = ({
|
||||
q,
|
||||
start_time,
|
||||
end_time,
|
||||
include_start,
|
||||
include_end,
|
||||
}) => {
|
||||
let params = {};
|
||||
if (q) {
|
||||
params.q = q;
|
||||
}
|
||||
if (start_time || start_time === 0) {
|
||||
params.start_time = start_time;
|
||||
}
|
||||
if (end_time || end_time === 0) {
|
||||
params.end_time = end_time;
|
||||
}
|
||||
if (include_start) {
|
||||
params.include_start = include_start;
|
||||
}
|
||||
if (include_end) {
|
||||
params.include_end = include_end;
|
||||
}
|
||||
|
||||
return http({
|
||||
method: "GET",
|
||||
url: `${API}/streams/next`,
|
||||
params,
|
||||
});
|
||||
};
|
||||
|
||||
export const previousEvent = ({
|
||||
q,
|
||||
start_time,
|
||||
end_time,
|
||||
include_start,
|
||||
include_end,
|
||||
}) => {
|
||||
// TODO(zomglings): Factor this query parameter building code out into a separate function.
|
||||
let params = {};
|
||||
if (q) {
|
||||
params.q = q;
|
||||
}
|
||||
if (start_time || start_time === 0) {
|
||||
params.start_time = start_time;
|
||||
}
|
||||
if (end_time || end_time === 0) {
|
||||
params.end_time = end_time;
|
||||
}
|
||||
if (include_start) {
|
||||
params.include_start = include_start;
|
||||
}
|
||||
if (include_end) {
|
||||
params.include_end = include_end;
|
||||
}
|
||||
|
||||
return http({
|
||||
method: "GET",
|
||||
url: `${API}/streams/previous`,
|
||||
params,
|
||||
});
|
||||
};
|
||||
|
|
Ładowanie…
Reference in New Issue