Skip to content

Commit

Permalink
Extract the page listening logic
Browse files Browse the repository at this point in the history
It might be used by other parts later
  • Loading branch information
maxpatiiuk committed Feb 19, 2023
1 parent 452b193 commit 43b10d3
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 27 deletions.
5 changes: 3 additions & 2 deletions src/src/components/Contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';

import { sendRequest } from '../Background/messages';

type Authenticated = {
Expand All @@ -19,10 +20,10 @@ export const unsafeGetToken = () => unsafeToken;
export function AuthenticationProvider({
children,
}: {
children: React.ReactNode;
readonly children: React.ReactNode;
}): JSX.Element {
const handleAuthenticate = React.useCallback(
(interactive = true) =>
async (interactive = true) =>
sendRequest('Authenticate', { interactive })
.then(({ token }) => {
if (typeof token === 'string') {
Expand Down
14 changes: 4 additions & 10 deletions src/src/components/EventsStore/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function useEvents(
endDate: Date | undefined
): EventsStore | undefined {
const eventsStore = React.useRef<RawEventsStore>({});
// Clean temporary cache when overlay is closed
// Clear temporary cache when overlay is closed
const clearCache = startDate === undefined || endDate === undefined;
if (clearCache) eventsStore.current = {};
const calendars = React.useContext(CalendarsContext);
Expand Down Expand Up @@ -145,14 +145,7 @@ export function useEvents(
})
);
return extractData(eventsStore.current, calendars, startDate, endDate);
}, [
eventsStore,
calendars,
startDate,
endDate,
ignoreAllDayEvents,
virtualCalendars,
]),
}, [calendars, startDate, endDate, ignoreAllDayEvents, virtualCalendars]),
false
);
return durations;
Expand Down Expand Up @@ -331,7 +324,8 @@ function extractData(
const totals: R<WritableDayHours> = Object.fromEntries(
daysBetween.map((date) => [date, blankHours()])
);
const entries = Object.entries(eventsStore[id])
// "eventsStore" won't have an entry for current calendar if fetching failed
const entries = Object.entries(eventsStore?.[id] ?? {})
.map(([virtualCalendar, dates]) => {
let categoryTotal = 0;
return [
Expand Down
19 changes: 5 additions & 14 deletions src/src/components/PowerTools/GhostEvents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import React from 'react';
import { useSafeStorage } from '../../hooks/useStorage';
import { listen } from '../../utils/events';
import { f } from '../../utils/functools';
import { debounce } from '../../utils/utils';
import { findMainContainer } from '../Molecules/Portal';
import { usePref } from '../Preferences/usePref';
import { usePageListener } from './PageListener';

export function GhostEvents(): null {
const [ghostEvents = [], setGhostEvents] = useSafeStorage(
Expand Down Expand Up @@ -37,7 +37,6 @@ export function GhostEvents(): null {
() => findMainContainer()?.parentElement ?? undefined,
[]
);

const doGhosting = React.useCallback(
(): void =>
mainContainer === undefined
Expand All @@ -51,29 +50,21 @@ export function GhostEvents(): null {
),
[mainContainer]
);
usePageListener(mainContainer, doGhosting);

const ghostEventsRef = React.useRef(new Set());
React.useEffect(() => {
ghostEventsRef.current = new Set(ghostEvents);
doGhosting();
}, [ghostEvents, doGhosting]);

// Listen for DOM changes
React.useEffect(() => {
if (mainContainer === undefined) return undefined;
const config = { childList: true, subtree: true };
const observer = new MutationObserver(debounce(doGhosting, 60));
observer.observe(mainContainer, config);
return (): void => observer.disconnect();
}, [mainContainer, doGhosting]);

// Listen for key press
React.useEffect(
() =>
ghostEventShortcut === 'none'
ghostEventShortcut === 'none' || mainContainer === undefined
? undefined
: listen(
document.body,
mainContainer,
'click',
({ shiftKey, ctrlKey, metaKey, target }) => {
const keys = {
Expand All @@ -94,7 +85,7 @@ export function GhostEvents(): null {
setGhostEvents(f.unique([...ghostEvents, eventName]));
}
),
[ghostEventShortcut, ghostEvents, setGhostEvents]
[mainContainer, ghostEventShortcut, ghostEvents, setGhostEvents]
);

return null;
Expand Down
21 changes: 21 additions & 0 deletions src/src/components/PowerTools/PageListener.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';

import { throttle } from '../../utils/utils';

/**
* Listen for the changes to events displayed on the page and call a callback
* when changes are detected.
*/
export function usePageListener(
mainContainer: HTMLElement | undefined,
callback: () => void
): void {
// Listen for DOM changes
React.useEffect(() => {
if (mainContainer === undefined) return undefined;
const config = { childList: true, subtree: true };
const observer = new MutationObserver(throttle(callback, 60));
observer.observe(mainContainer, config);
return (): void => observer.disconnect();
}, [mainContainer, callback]);
}
21 changes: 20 additions & 1 deletion src/src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
*
* @module
*/
import { IR, RA } from './types';
import { f } from './functools';
import type { IR, RA } from './types';

export const capitalize = <T extends string>(string: T): Capitalize<T> =>
(string.charAt(0).toUpperCase() + string.slice(1)) as Capitalize<T>;
Expand All @@ -26,6 +26,7 @@ export const sortFunction =
else if (typeof leftValue === 'string' && typeof rightValue === 'string')
return leftValue.localeCompare(rightValue) as -1 | 0 | 1;
// Treat null and undefined as the same
// eslint-disable-next-line eqeqeq
else if (leftValue == rightValue) return 0;
return (leftValue ?? '') > (rightValue ?? '') ? 1 : -1;
};
Expand Down Expand Up @@ -186,3 +187,21 @@ export function debounce(callback: () => void, timeout: number): () => void {
timer = setTimeout(callback, timeout);
};
}

/**
* Based on simplified version of Underscore.js's throttle function
*/
export function throttle(callback: () => void, wait: number): () => void {
let timeout: ReturnType<typeof setTimeout>;
let previous = 0;

return (): void => {
const time = Date.now();
const remaining = wait - (time - previous);
if (remaining <= 0 || remaining > wait) {
clearTimeout(timeout);
previous = time;
callback();
}
};
}

0 comments on commit 43b10d3

Please sign in to comment.