diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 245fc51..66edadc 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,10 +1,9 @@ --- name: Bug report about: Create a report to help us improve -title: '' -labels: 'bug' -assignees: '' - +title: "" +labels: "bug" +assignees: "" --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,8 +24,9 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop:** - - OS: [e.g. Windows 10] - - Browser: [e.g. Chrome, Safari] + +- OS: [e.g. Windows 10] +- Browser: [e.g. Chrome, Safari] **Additional context** -Any other context about the problem. \ No newline at end of file +Any other context about the problem. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 36014cd..9b90523 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,10 +1,9 @@ --- name: Feature request about: Suggest an idea for this project -title: '' -labels: 'enhancement' -assignees: '' - +title: "" +labels: "enhancement" +assignees: "" --- **Is your feature request related to a problem? Please describe.** diff --git a/CHANGELOG.md b/CHANGELOG.md index a2a3b82..9ca7ba3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1560,4 +1560,4 @@ All notable changes to this project will be documented in this file. - \ No newline at end of file + diff --git a/README.md b/README.md index 5834420..c3a9d2d 100644 --- a/README.md +++ b/README.md @@ -120,4 +120,4 @@ npm run unitTests:watch ```sh cd src npm run docs -``` \ No newline at end of file +``` diff --git a/docs/privacy/README.md b/docs/privacy/README.md index 4df9ec0..cdf8079 100644 --- a/docs/privacy/README.md +++ b/docs/privacy/README.md @@ -69,4 +69,4 @@ Limited Use requirements. If you have any questions or concerns about this Privacy Policy or our data handling practices, please contact us at [max@patii.uk](mailto:max@patii.uk) or -open a [GitHub issue](https://github.com/maxpatiiuk/calendar-plus/issues/new) \ No newline at end of file +open a [GitHub issue](https://github.com/maxpatiiuk/calendar-plus/issues/new) diff --git a/docs/spreadsheet-to-changelog.js b/docs/spreadsheet-to-changelog.js index b22c319..c74e5db 100644 --- a/docs/spreadsheet-to-changelog.js +++ b/docs/spreadsheet-to-changelog.js @@ -24,9 +24,9 @@ const group = (entries) => .reduce( (grouped, [key, value]) => grouped.set(key, [...(grouped.get(key) ?? []), value]), - new Map() + new Map(), ) - .entries() + .entries(), ); const assignees = new Set(parsed.map((data) => data["Assignee"])); @@ -64,7 +64,7 @@ const githubLink = (type) => (value) => .map((value) => value.slice(value.lastIndexOf("/") + 1)) .map( (number) => - `#${number}` + `#${number}`, ) .join(" and "); @@ -104,7 +104,7 @@ function formatItem(item) { .filter(Boolean) .join("
\n\t\t\t\t")}\n\t\t\t` : value ?? "" - }` + }`, ) .join("\n")}\n\t\t`; } diff --git a/index.html b/index.html index 7b2a728..bb23c80 100644 --- a/index.html +++ b/index.html @@ -1,25 +1,56 @@ - + - - - - Calendar Plus - - - - - - - - - -
- - - \ No newline at end of file + + + + Calendar Plus + + + + + + + + + +
+ + + diff --git a/manifest.json b/manifest.json index 5fc327b..ae8542f 100644 --- a/manifest.json +++ b/manifest.json @@ -16,33 +16,26 @@ }, "web_accessible_resources": [ { - "resources": ["/src/public/images/btn_google_signin_light_normal_web@2x.png"], + "resources": [ + "/src/public/images/btn_google_signin_light_normal_web@2x.png" + ], "matches": ["https://calendar.google.com/*"] } ], "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuldA6s8qQKAdu33VanYX4yhBYnTbMwARskX7rcwvuIKgRPbY41X1mcIDUHKV7pC5sWQ2QuL0EN5U6M57dJhe+veTDDktpWNXeGJtwvPM6HFbwICgD93TkzwBl4Fs+67eFlsmpoHIdWeryyEfIUAgUwsb8DxvQ8OYhVKet1xkqJRRo112iLZ1bn/ZI8P39jT4cc9pMIXSvEKmtzyqNb9l/5WS8F6reqsRGIwZqmr/9fb7CEhyKPzEtXokcfaTygMwF6rJ6530cLUegofpJed4wZfFCCRtok63aqCwQgfypOKWlXCrExTdD6QKP4941dYQMuCg9+wp/GslGwTUd+odGQIDAQAB", "current_locale": "en", - "permissions": [ - "identity", - "storage" - ], + "permissions": ["identity", "storage"], "oauth2": { "client_id": "626996674772-iegg1iono3q4g3u8e3ajcp37fbuauuh1.apps.googleusercontent.com", - "scopes":[ - "https://www.googleapis.com/auth/calendar.readonly" - ] + "scopes": ["https://www.googleapis.com/auth/calendar.readonly"] }, "background": { - "service_worker":"./src/dist/background.bundle.js" + "service_worker": "./src/dist/background.bundle.js" }, "content_scripts": [ { - "matches": [ - "https://calendar.google.com/calendar/u/0/r/*" - ], - "js": [ - "./src/dist/main.bundle.js" - ] + "matches": ["https://calendar.google.com/calendar/u/0/r/*"], + "js": ["./src/dist/main.bundle.js"] } ], "cross_origin_embedder_policy": { @@ -51,4 +44,4 @@ "cross_origin_opener_policy": { "value": "same-origin" } -} \ No newline at end of file +} diff --git a/src/.eslintrc.js b/src/.eslintrc.js index 5d6c107..8bbde21 100644 --- a/src/.eslintrc.js +++ b/src/.eslintrc.js @@ -14,9 +14,7 @@ module.exports = { browser: true, node: true, }, - extends: [ - '@maxxxxxdlp/eslint-config-react', - ], + extends: ['@maxxxxxdlp/eslint-config-react'], rules: { '@typescript-eslint/no-empty-interface': OFF, }, diff --git a/src/.prettierignore b/src/.prettierignore new file mode 100644 index 0000000..96c0ecc --- /dev/null +++ b/src/.prettierignore @@ -0,0 +1 @@ +docs/ \ No newline at end of file diff --git a/src/README.md b/src/README.md index 57172df..513a301 100644 --- a/src/README.md +++ b/src/README.md @@ -1,6 +1,7 @@ # Getting Started with Create React App -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). +This project was bootstrapped with +[Create React App](https://github.com/facebook/create-react-app). ## Available Scripts @@ -17,54 +18,74 @@ You may also see any lint errors in the console. ### `npm test` Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. +See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) +for more information. ### `npm run build` Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. +It correctly bundles React in production mode and optimizes the build for the best +performance. The build is minified and the filenames include the hashes.\ Your app is ready to be deployed! -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. +See the section about +[deployment](https://facebook.github.io/create-react-app/docs/deployment) for +more information. ### `npm run eject` **Note: this is a one-way operation. Once you `eject`, you can't go back!** -If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. +If you aren't satisfied with the build tool and configuration choices, you can +`eject` at any time. This command will remove the single build dependency from +your project. -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. +Instead, it will copy all the configuration files and the transitive +dependencies (webpack, Babel, ESLint, etc) right into your project so you have +full control over them. All of the commands except `eject` will still work, but +they will point to the copied scripts so you can tweak them. At this point +you're on your own. -You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. +You don't have to ever use `eject`. The curated feature set is suitable for +small and middle deployments, and you shouldn't feel obligated to use this +feature. However we understand that this tool wouldn't be useful if you couldn't +customize it when you are ready for it. ## Learn More -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). +You can learn more in the +[Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). To learn React, check out the [React documentation](https://reactjs.org/). ### Code Splitting -This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) +This section has moved here: +[https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) ### Analyzing the Bundle Size -This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) +This section has moved here: +[https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) ### Making a Progressive Web App -This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) +This section has moved here: +[https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) ### Advanced Configuration -This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) +This section has moved here: +[https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) ### Deployment -This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) +This section has moved here: +[https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) ### `npm run build` fails to minify -This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) +This section has moved here: +[https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) diff --git a/src/babel.config.js b/src/babel.config.js index c2734f3..65a5f45 100644 --- a/src/babel.config.js +++ b/src/babel.config.js @@ -4,20 +4,17 @@ * Webpack uses Babel too, but that config is provided inside of webpack.config.js */ -"use strict"; +'use strict'; module.exports = { env: { test: { presets: [ - [ - '@babel/preset-env', - {targets: {node: 'current'}}, - ], + ['@babel/preset-env', { targets: { node: 'current' } }], ['@babel/preset-react'], ['@babel/preset-typescript'], ], - "plugins": ["@babel/plugin-transform-modules-commonjs"], - } - } -} + plugins: ['@babel/plugin-transform-modules-commonjs'], + }, + }, +}; diff --git a/src/package.json b/src/package.json index b0c6592..ac6c5be 100644 --- a/src/package.json +++ b/src/package.json @@ -101,4 +101,4 @@ "test": "tests" }, "prettier": "@maxxxxxdlp/prettier-config" -} \ No newline at end of file +} diff --git a/src/src/README.md b/src/src/README.md index acea26c..07cfec4 100644 --- a/src/src/README.md +++ b/src/src/README.md @@ -10,8 +10,8 @@ is similar to how most modern React projects are structured. ### Utils -There is an `utils` folder that contains all the utility functions -that don't belong to any single component, but are used by many of them. +There is an `utils` folder that contains all the utility functions that don't +belong to any single component, but are used by many of them. The files in the `utils` folder must not be React components or React hooks (for the sake of consistency). React hooks that are used by several components should diff --git a/src/src/components/Atoms/Internationalization.tsx b/src/src/components/Atoms/Internationalization.tsx index 988251a..ca4d2b3 100644 --- a/src/src/components/Atoms/Internationalization.tsx +++ b/src/src/components/Atoms/Internationalization.tsx @@ -17,7 +17,7 @@ declare namespace Intl { options?: { readonly type?: 'conjunction' | 'disjunction'; readonly style?: 'long' | 'narrow' | 'short'; - } + }, ); public format(values: RA): string; @@ -34,7 +34,7 @@ declare namespace Intl { | 'language' | 'region' | 'script'; - } + }, ); public of(code: string): string; @@ -52,12 +52,12 @@ declare namespace Intl { options?: { readonly numeric: 'always' | 'auto'; readonly style: 'long' | 'narrow' | 'short'; - } + }, ); public format( count: number, - type: 'day' | 'hour' | 'minute' | 'month' | 'second' | 'week' | 'year' + type: 'day' | 'hour' | 'minute' | 'month' | 'second' | 'week' | 'year', ): string; } @@ -70,7 +70,7 @@ declare namespace Intl { readonly month?: 'long' | 'short'; readonly weekday?: 'long' | 'short'; readonly day?: 'numeric'; - } + }, ); public format(value: Readonly): string; @@ -83,7 +83,7 @@ declare namespace Intl { readonly sensitivity?: 'accent' | 'base' | 'case' | 'variant'; readonly caseFirst?: 'lower' | 'upper' | false; readonly ignorePunctuation?: boolean; - } + }, ); public compare(left: string, right: string): -1 | 0 | 1; @@ -93,7 +93,7 @@ declare namespace Intl { function getMonthNames(monthFormat: 'long' | 'short'): RA { const months = new Intl.DateTimeFormat(LANGUAGE, { month: monthFormat }); return Array.from({ length: 12 }, (_, month) => - months.format(new Date(0, month, 2, 0, 0, 0)) + months.format(new Date(0, month, 2, 0, 0, 0)), ); } @@ -192,7 +192,7 @@ export const compareStrings = new Intl.Collator( sensitivity: 'base', caseFirst: 'upper', ignorePunctuation: true, - } + }, ).compare; /** diff --git a/src/src/components/Atoms/index.tsx b/src/src/components/Atoms/index.tsx index 89f12a6..d6fb454 100644 --- a/src/src/components/Atoms/index.tsx +++ b/src/src/components/Atoms/index.tsx @@ -33,7 +33,7 @@ const button = (name: string, classList: string) => onClick: handleClick, disabled: disabled || handleClick === undefined, ...props, - }) + }), ); export const className = { @@ -51,11 +51,11 @@ export const Button = { White: button('Button.White', className.buttonWhite), Blue: button( 'Button.Blue', - `border-blue-600 bg-blue-600 hover:bg-blue-700 active:bg-blue-500 text-white` + `border-blue-600 bg-blue-600 hover:bg-blue-700 active:bg-blue-500 text-white`, ), Red: button( 'Button.Blue', - `border-red-600 bg-red-600 hover:bg-red-700 active:bg-red-500 text-white` + `border-red-600 bg-red-600 hover:bg-red-700 active:bg-red-500 text-white`, ), Icon: wrap< 'button', @@ -76,12 +76,12 @@ export const Link = { White: wrap( 'Link.White', 'a', - `${className.googleButton} ${className.buttonWhite}` + `${className.googleButton} ${className.buttonWhite}`, ), Default: wrap( 'Link.Default', 'a', - '!text-blue-500 hover:underline hover:!text-blue-600' + '!text-blue-500 hover:underline hover:!text-blue-600', ), }; @@ -113,7 +113,7 @@ export const Input = { value={hasValue ? value : ''} onChange={(event): void => { const rawHumber = Number.parseInt( - (event.target as HTMLInputElement).value + (event.target as HTMLInputElement).value, ); const number = Number.isNaN(rawHumber) ? undefined : rawHumber; if (number === undefined) setHasValue(false); @@ -150,7 +150,7 @@ export const Input = { props.onChange?.(event); }, readOnly: isReadOnly, - }) + }), ), Text: wrap< 'input', @@ -173,7 +173,7 @@ export const Input = { props.onChange?.(event); }, readOnly: isReadOnly, - }) + }), ), Generic: wrap< 'input', @@ -194,7 +194,7 @@ export const Input = { props.onChange?.(event); }, readOnly: isReadOnly, - }) + }), ), }; @@ -220,11 +220,11 @@ export const Select = wrap< handleValueChange?.(value); props.onChange?.(event); }, - }) + }), ); export const Widget = wrap( 'Widget', 'section', - 'flex flex-col gap-2 rounded bg-white' -); \ No newline at end of file + 'flex flex-col gap-2 rounded bg-white', +); diff --git a/src/src/components/Atoms/wrapper.tsx b/src/src/components/Atoms/wrapper.tsx index bc06699..7ec8706 100644 --- a/src/src/components/Atoms/wrapper.tsx +++ b/src/src/components/Atoms/wrapper.tsx @@ -42,7 +42,7 @@ export function wrap< * For example, can make some optional props be required, forbid passing * children, or mutate extra props using mergeProps callback */ - EXTRA_PROPS extends IR = RR + EXTRA_PROPS extends IR = RR, >( // Would be shown in React DevTools name: string, @@ -50,10 +50,10 @@ export function wrap< className: string, initialProps?: | TagProps - | ((props: Readonly & TagProps) => TagProps) + | ((props: Readonly & TagProps) => TagProps), ) { const wrapped = ( - props: Readonly & TagProps + props: Readonly & TagProps, ): JSX.Element => { // Merge classNames const fullClassName = diff --git a/src/src/components/Background/index.ts b/src/src/components/Background/index.ts index 9389b51..d382462 100644 --- a/src/src/components/Background/index.ts +++ b/src/src/components/Background/index.ts @@ -27,7 +27,7 @@ chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => { request.type in requestHandlers ) { requestHandlers[request.type as 'ReloadExtension']( - request.request as undefined + request.request as undefined, ) .then(sendResponse) .catch(console.error); @@ -41,7 +41,7 @@ chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => { */ const requestHandlers: { readonly [TYPE in Requests['type']]: ( - request: Extract>['request'] + request: Extract>['request'], ) => Promise>['response']>; } = { Authenticate: async ({ interactive }) => @@ -49,8 +49,8 @@ const requestHandlers: { chrome.identity.getAuthToken({ interactive }, (token) => typeof token === 'string' ? resolve({ token }) - : reject(chrome.runtime.lastError) - ) + : reject(chrome.runtime.lastError), + ), ), ReloadExtension: async () => new Promise((resolve) => { @@ -58,4 +58,4 @@ const requestHandlers: { chrome.runtime.reload(); resolve(undefined); }), -}; \ No newline at end of file +}; diff --git a/src/src/components/Background/messages.ts b/src/src/components/Background/messages.ts index 0269360..6daa78e 100644 --- a/src/src/components/Background/messages.ts +++ b/src/src/components/Background/messages.ts @@ -32,7 +32,7 @@ export type Requests = AuthenticateRequest | ReloadExtension; */ export const sendRequest = async ( type: TYPE, - request: Extract>['request'] + request: Extract>['request'], ): Promise>['response']> => // This function has a lot of overloads, which confuses TypeScript chrome.runtime.sendMessage({ @@ -48,7 +48,7 @@ type BackgroundEvents = TabUpdate; */ export function emitEvent( tabId: number, - response: BackgroundEvents + response: BackgroundEvents, ): Promise { if (chrome.tabs === undefined) throw new Error('This must only be called from the background script'); @@ -61,7 +61,7 @@ export function emitEvent( */ export function listenEvent( type: TYPE, - callback: (payload: BackgroundEvents & State) => void + callback: (payload: BackgroundEvents & State) => void, ): () => void { const handleUpdate = (event: BackgroundEvents & State) => typeof event === 'object' && event !== null && event.type === type @@ -70,4 +70,4 @@ export function listenEvent( chrome.runtime.onMessage.addListener(handleUpdate); return (): void => chrome.runtime.onMessage.removeListener(handleUpdate); -} \ No newline at end of file +} diff --git a/src/src/components/Charts/DoughnutChart.tsx b/src/src/components/Charts/DoughnutChart.tsx index 66835f3..7cb0f95 100644 --- a/src/src/components/Charts/DoughnutChart.tsx +++ b/src/src/components/Charts/DoughnutChart.tsx @@ -48,7 +48,7 @@ export function DoughnutChart({ }, (_, index) => { writable(chart.data.datasets[0].data)[index] = outerData[index]; - } + }, ); Array.from( { @@ -56,7 +56,7 @@ export function DoughnutChart({ }, (_, index) => { writable(chart.data.datasets[1].data)[index] = innerData[index]; - } + }, ); chart.update(); }, [innerData, outerData, chart, handleLoaded]); @@ -133,7 +133,9 @@ export function DoughnutChart({ }, }, }} - ref={(chart:Chart<'doughnut', RA, string> | undefined | null) => setChart(chart ?? undefined)} + ref={( + chart: Chart<'doughnut', RA, string> | undefined | null, + ) => setChart(chart ?? undefined)} /> ) : ( commonText('loading') @@ -150,7 +152,7 @@ type Layer = { function useDataSets( durations: EventsStore | undefined, - calendars: React.ContextType + calendars: React.ContextType, ): readonly [Layer, Layer] { return React.useMemo(() => { const { data, ...rest } = getCategoryData(durations, calendars); @@ -162,8 +164,8 @@ function useDataSets( label || (data.length === 1 ? calendars?.[index].summary ?? index.toString() - : commonText('other')) - ) + : commonText('other')), + ), ), ...rest, }; @@ -182,7 +184,7 @@ function useDataSets( function getCategoryData( durations: EventsStore | undefined, - calendars: React.ContextType + calendars: React.ContextType, ): { readonly data: RA>; readonly backgroundColor: RA; @@ -198,10 +200,10 @@ function getCategoryData( label, minutes: Object.values(durations).reduce( (total, durations) => total + durations.total, - 0 + 0, ), }; - }) + }), ) ?? []; return { data: categoryData, @@ -211,13 +213,13 @@ function getCategoryData( const getCalendarData = ( durations: EventsStore | undefined, - calendars: React.ContextType + calendars: React.ContextType, ) => durations === undefined ? [] : calendars?.map(({ id }) => Object.values(durations[id]?.[summedDurations] ?? {}).reduce( (total, durations) => total + durations.total, - 0 - ) + 0, + ), ) ?? []; diff --git a/src/src/components/Charts/StackedChart.tsx b/src/src/components/Charts/StackedChart.tsx index b9f33d2..f0c6938 100644 --- a/src/src/components/Charts/StackedChart.tsx +++ b/src/src/components/Charts/StackedChart.tsx @@ -36,17 +36,17 @@ export function StackedChart({ const calendars = React.useContext(CalendarsContext); const labels = React.useMemo( () => getLabels(durations, view), - [durations, view] + [durations, view], ); const dataSets = React.useMemo( () => getDataSets(durations, calendars, view), - [durations, calendars, view] + [durations, calendars, view], ); const [loaded, handleLoaded] = useBooleanState(); const transitionDuration = useTransitionDuration(); React.useEffect( () => (dataSets.length > 0 ? handleLoaded() : undefined), - [dataSets, handleLoaded] + [dataSets, handleLoaded], ); const getJsonExport = () => @@ -64,7 +64,7 @@ export function StackedChart({ [commonText('calendar')]: calendar, [commonText('date')]: date, [commonText('duration')]: duration, - })) + })), ); return ( @@ -123,13 +123,13 @@ const getLabels = (durations: EventsStore | undefined, view: SupportedView) => view === 'year' ? (months as WritableArray) : Object.keys( - Object.values(durations ?? {})[0]?.[summedDurations] ?? [] + Object.values(durations ?? {})[0]?.[summedDurations] ?? [], ).map((duration) => formatDateLabel(new Date(duration), view)); const getDataSets = ( durations: EventsStore | undefined, calendars: React.ContextType, - view: SupportedView + view: SupportedView, ): WritableArray<{ readonly id: string; readonly label: string; @@ -147,12 +147,12 @@ const getDataSets = ( ? group( Object.entries(durations[id]?.[summedDurations] ?? {}).map( ([date, duration]) => - [new Date(date).getMonth(), duration.total] as const - ) + [new Date(date).getMonth(), duration.total] as const, + ), ).map(([_key, values]) => - values.reduce((total, value) => total + value, 0) + values.reduce((total, value) => total + value, 0), ) : Object.values(durations[id]?.[summedDurations] ?? {}).map( - ({ total }) => total + ({ total }) => total, ), })); diff --git a/src/src/components/Charts/TimeChart.tsx b/src/src/components/Charts/TimeChart.tsx index 1ca9028..d72d46d 100644 --- a/src/src/components/Charts/TimeChart.tsx +++ b/src/src/components/Charts/TimeChart.tsx @@ -32,7 +32,7 @@ export function TimeChart({ durations === undefined || mode === undefined ? undefined : getTimes(durations, mode), - [durations, mode] + [durations, mode], ); const doJsonExport = () => @@ -45,8 +45,8 @@ export function TimeChart({ ([virtualCalendar, durations]) => [ virtualCalendar, getRowData(durations, mode ?? 'total'), - ] - ) + ], + ), ), })); @@ -66,7 +66,10 @@ export function TimeChart({ [commonText('virtualCalendar')]: virtualCalendar, [commonText('totalHours')]: total, ...Object.fromEntries( - hourly.map((duration, index) => [formatHour(index, 'long'), duration]) + hourly.map((duration, index) => [ + formatHour(index, 'long'), + duration, + ]), ), })); } @@ -132,7 +135,7 @@ export function TimeChart({ key={calendar.id} times={times[calendar.id]} /> - ) : undefined + ) : undefined, )} @@ -156,13 +159,13 @@ const getTimes = (durations: EventsStore, mode: TimeChartMode): IR => Object.entries(durations).map(([id, durations]) => [ id, getRowData(durations[summedDurations], mode), - ]) + ]), ); const getRowData = (data: IR, mode: TimeChartMode): DayHours => summedTimes( Object.values(data), - mode === 'total' ? toTotalHours : toAverageMinutes + mode === 'total' ? toTotalHours : toAverageMinutes, ); const toTotalHours = (durations: RA): number => @@ -175,11 +178,11 @@ const toTwoDecimal = (number: number): number => Math.round(number * 100) / 100; const summedTimes = ( durations: RA, - aggregate: (numbers: RA) => number + aggregate: (numbers: RA) => number, ): DayHours => ({ total: toTwoDecimal(aggregate(durations.map(({ total }) => total))), hourly: Array.from({ length: DAY / HOUR }, (_, index) => - toTwoDecimal(aggregate(durations.map(({ hourly }) => hourly[index]))) + toTwoDecimal(aggregate(durations.map(({ hourly }) => hourly[index]))), ), }); @@ -288,10 +291,10 @@ function TotalsRow({ times }: { readonly times: IR }): JSX.Element { Array.from({ length: DAY / HOUR }, (_, index) => calendars.reduce( (total, { id }) => total + (times[id]?.hourly[index] ?? 0), - 0 - ) + 0, + ), ), - [times, calendars] + [times, calendars], ); return ( @@ -306,7 +309,7 @@ function TotalsRow({ times }: { readonly times: IR }): JSX.Element { ))} {number( - totals.map((total) => total).reduce((sum, total) => sum + total, 0) + totals.map((total) => total).reduce((sum, total) => sum + total, 0), )} diff --git a/src/src/components/Contexts/AuthContext.tsx b/src/src/components/Contexts/AuthContext.tsx index 0f38c89..24f15d3 100644 --- a/src/src/components/Contexts/AuthContext.tsx +++ b/src/src/components/Contexts/AuthContext.tsx @@ -37,7 +37,7 @@ export function AuthenticationProvider({ } else console.warn('Authentication canceled'); }) .catch(console.error), - [] + [], ); React.useEffect(() => void handleAuthenticate(false), [handleAuthenticate]); @@ -46,7 +46,7 @@ export function AuthenticationProvider({ token, handleAuthenticate, }), - [token, handleAuthenticate] + [token, handleAuthenticate], ); return {children}; } diff --git a/src/src/components/Contexts/CalendarsContext.tsx b/src/src/components/Contexts/CalendarsContext.tsx index 19f0bc3..7057932 100644 --- a/src/src/components/Contexts/CalendarsContext.tsx +++ b/src/src/components/Contexts/CalendarsContext.tsx @@ -56,8 +56,8 @@ export function CalendarsSpy({ minAccessRole: 'reader', fields: 'items(id,summary,primary,backgroundColor)', prettyPrint: false.toString(), - } - ) + }, + ), ).catch((error) => { console.error(error); return undefined; @@ -71,13 +71,13 @@ export function CalendarsSpy({ })); const [secondary, primary] = split( calendars, - ({ primary }) => primary === true + ({ primary }) => primary === true, ).map((calendars) => - Array.from(calendars).sort(sortFunction(({ summary }) => summary)) + Array.from(calendars).sort(sortFunction(({ summary }) => summary)), ); return [...primary, ...secondary]; }, [isAuthenticated]), - false + false, ); /* @@ -93,7 +93,7 @@ export function CalendarsSpy({ const filteredCalendars = React.useMemo( () => calendars?.filter(({ id }) => visibleCalendars?.includes(id)), - [calendars, visibleCalendars] + [calendars, visibleCalendars], ); return ( , - [visibleCalendars, setVisibleCalendars]: GetSet | undefined> + [visibleCalendars, setVisibleCalendars]: GetSet | undefined>, ): void { const [sideBar, setSideBar] = React.useState(undefined); @@ -152,11 +152,11 @@ function useVisibilityChangeSpy( const parseCheckbox = React.useCallback( ( - checkbox: HTMLInputElement + checkbox: HTMLInputElement, ): readonly [id: string, checked: boolean] | undefined => { if (calendars === undefined) { console.log( - 'Calendar Plus: Unable to identify visible calendars before user signs in' + 'Calendar Plus: Unable to identify visible calendars before user signs in', ); return undefined; } @@ -174,7 +174,7 @@ function useVisibilityChangeSpy( } return [calendar.id, checkbox.checked]; }, - [calendars] + [calendars], ); /* @@ -188,8 +188,8 @@ function useVisibilityChangeSpy( filterArray( Array.from( sideBar.querySelectorAll('input[type="checkbox"]'), - parseCheckbox - ) + parseCheckbox, + ), ) .filter(([_calendarId, checked]) => checked) .map(([calendarId]) => calendarId); @@ -230,10 +230,10 @@ function useVisibilityChangeSpy( setVisibleCalendars( checked ? visibleCalendarsRef.current?.filter((id) => id !== calendarId) - : [...(visibleCalendarsRef.current ?? []), calendarId] + : [...(visibleCalendarsRef.current ?? []), calendarId], ); }), - [sideBar, setVisibleCalendars] + [sideBar, setVisibleCalendars], ); } @@ -243,7 +243,7 @@ function useVisibilityChangeSpy( export async function awaitElement( callback: () => T | undefined, pollInterval = 50, - limit = 100 + limit = 100, ): Promise { const result = callback(); if (result !== undefined) return result; @@ -251,7 +251,7 @@ export async function awaitElement( return new Promise((resolve) => setTimeout( () => resolve(awaitElement(callback, pollInterval, limit - 1)), - pollInterval - ) + pollInterval, + ), ); } diff --git a/src/src/components/Contexts/Contexts.tsx b/src/src/components/Contexts/Contexts.tsx index c007ffe..f5fb84f 100644 --- a/src/src/components/Contexts/Contexts.tsx +++ b/src/src/components/Contexts/Contexts.tsx @@ -46,7 +46,7 @@ export function Contexts({ }) .catch(crash); }, - [handleLoading, handleLoaded] + [handleLoading, handleLoaded], ); return ( diff --git a/src/src/components/Contexts/CurrentViewContext.tsx b/src/src/components/Contexts/CurrentViewContext.tsx index d0dc734..e4fa5d1 100644 --- a/src/src/components/Contexts/CurrentViewContext.tsx +++ b/src/src/components/Contexts/CurrentViewContext.tsx @@ -11,7 +11,7 @@ import { SettingsContext } from './SettingsContext'; * Keep track of what week/month/year is currently displayed */ export const CurrentViewContext = React.createContext( - undefined + undefined, ); CurrentViewContext.displayName = 'LoadingContext'; @@ -36,7 +36,7 @@ const viewMapper = { custom_weeks: 'customweek', } as const; -export type SupportedView = typeof supportedViews[number]; +export type SupportedView = (typeof supportedViews)[number]; export type CurrentView = { readonly view: SupportedView; @@ -51,7 +51,7 @@ export function TrackCurrentView({ readonly children: React.ReactNode; }): JSX.Element { const [currentView, setCurrentView] = React.useState( - undefined + undefined, ); useCurrentTracker(setCurrentView); @@ -69,7 +69,7 @@ export const defaultCustomViewSize = 4; * Listen for changes to current URL */ function useCurrentTracker( - setCurrentView: (newCurrentView: CurrentView | undefined) => void + setCurrentView: (newCurrentView: CurrentView | undefined) => void, ) { const [customViewSize = defaultCustomViewSize, setCustomViewSize] = useStorage('customViewSize'); @@ -83,12 +83,12 @@ function useCurrentTracker( const parsed = parsePath( window.location.pathname, customViewSize, - weekStart + weekStart, ); setCurrentView(parsed); if (parsed?.view === 'customday' || parsed?.view === 'customweek') detectCustomViewSize([customViewSize, setCustomViewSize]).catch( - console.error + console.error, ); } @@ -111,7 +111,7 @@ const commonPrefix = `/calendar/u/0/r/`; function parsePath( path: string, customViewSize: number, - weekStart: number + weekStart: number, ): CurrentView | undefined { if (path === commonPrefix || path === commonPrefix.slice(0, -1)) { const viewMode = document.querySelector('header div[data-active-view]'); @@ -140,7 +140,7 @@ function parsePath( Number.parseInt(year), // Month is 0-based Number.parseInt(month) - 1, - Number.parseInt(day) + Number.parseInt(day), ); return { view: viewName, @@ -153,19 +153,19 @@ function resolveBoundaries( viewName: SupportedView, selectedDay: Date, customViewSize: number, - weekStart: number + weekStart: number, ): { readonly firstDay: Date; readonly lastDay: Date } { if (viewName === 'day') return { firstDay: new Date( selectedDay.getFullYear(), selectedDay.getMonth(), - selectedDay.getDate() + selectedDay.getDate(), ), lastDay: new Date( selectedDay.getFullYear(), selectedDay.getMonth(), - selectedDay.getDate() + 1 + selectedDay.getDate() + 1, ), }; else if (viewName === 'week') { @@ -176,12 +176,12 @@ function resolveBoundaries( firstDay: new Date( selectedDay.getFullYear(), selectedDay.getMonth(), - dayOffset + dayOffset, ), lastDay: new Date( selectedDay.getFullYear(), selectedDay.getMonth(), - dayOffset + 7 + dayOffset + 7, ), }; } else if (viewName === 'month') @@ -190,7 +190,7 @@ function resolveBoundaries( lastDay: new Date( selectedDay.getFullYear(), selectedDay.getMonth() + 1, - 0 + 0, ), }; else if (viewName === 'customday') { @@ -199,7 +199,7 @@ function resolveBoundaries( lastDay: new Date( selectedDay.getFullYear(), selectedDay.getMonth(), - selectedDay.getDate() + customViewSize + selectedDay.getDate() + customViewSize, ), }; } else if (viewName === 'customweek') { @@ -213,7 +213,7 @@ function resolveBoundaries( lastDay: new Date( firstDay.getFullYear(), firstDay.getMonth(), - firstDay.getDate() + customViewSize + firstDay.getDate() + customViewSize, ), }; } else if (viewName === 'year') @@ -238,7 +238,7 @@ async function detectCustomViewSize([ if (dateKeys === undefined) return; // Find out how many days are displayed const duration = new Set( - Array.from(dateKeys, (dateKey) => dateKey.getAttribute('data-datekey')) + Array.from(dateKeys, (dateKey) => dateKey.getAttribute('data-datekey')), ).size; if (duration !== customViewSize) setCustomViewSize(duration); } diff --git a/src/src/components/Contexts/KeyboardContext.tsx b/src/src/components/Contexts/KeyboardContext.tsx index 67a187c..c7fac35 100644 --- a/src/src/components/Contexts/KeyboardContext.tsx +++ b/src/src/components/Contexts/KeyboardContext.tsx @@ -43,11 +43,11 @@ export function KeyboardListener({ listenersRef.current = [...listenersRef.current, entry]; return () => { listenersRef.current = listenersRef.current.filter( - (listener) => listener !== entry + (listener) => listener !== entry, ); }; }, - [] + [], ); const pressedKeys = React.useRef([]); @@ -60,11 +60,11 @@ export function KeyboardListener({ event.key.length === 1 ? event.key.toUpperCase() : event.key; if (modifierKeyNames.has(event.key)) return; pressedKeys.current = Array.from( - new Set([...pressedKeys.current, key]) + new Set([...pressedKeys.current, key]), ).sort(sortFunction((key) => key)); checkListeners(resolveModifiers(event)); }), - [] + [], ); React.useEffect( @@ -78,7 +78,7 @@ export function KeyboardListener({ .sort(sortFunction((key) => key)); checkListeners(resolveModifiers(event)); }), - [] + [], ); const checkListeners = React.useCallback( @@ -103,11 +103,11 @@ export function KeyboardListener({ shortcut.modifiers.filter((modifier) => modifier !== 'shift') .length === 0 && isInputting() ); - }) + }), ) .forEach((listener) => listener.callback()); }, - [] + [], ); return ( diff --git a/src/src/components/Contexts/README.md b/src/src/components/Contexts/README.md index 51cfa21..10fe52c 100644 --- a/src/src/components/Contexts/README.md +++ b/src/src/components/Contexts/README.md @@ -1,3 +1,3 @@ # Contexts -React Contexts are here \ No newline at end of file +React Contexts are here diff --git a/src/src/components/Contexts/SettingsContext.tsx b/src/src/components/Contexts/SettingsContext.tsx index 959ec51..40965e6 100644 --- a/src/src/components/Contexts/SettingsContext.tsx +++ b/src/src/components/Contexts/SettingsContext.tsx @@ -30,7 +30,7 @@ export function SettingsProvider({ .then(({ value }) => typeof value === 'string' ? setWeekStart(Number.parseInt(value)) - : undefined + : undefined, ) .catch(console.error); }, [token, setWeekStart]); @@ -39,7 +39,7 @@ export function SettingsProvider({ () => ({ weekStart, }), - [weekStart] + [weekStart], ); return ( diff --git a/src/src/components/Core/App.tsx b/src/src/components/Core/App.tsx index 9fb7f7c..01baa76 100644 --- a/src/src/components/Core/App.tsx +++ b/src/src/components/Core/App.tsx @@ -42,19 +42,19 @@ export function App(): JSX.Element | null { openOverlayShortcut, handleOpen, handleClose, - ] + ], ); const currentView = React.useContext(CurrentViewContext); const durations = useEvents( // Don't fetch until the overlay is opened isOpen ? currentView?.firstDay : undefined, - currentView?.lastDay + currentView?.lastDay, ); const [debugOverlay] = useAsyncState( React.useCallback(() => debugOverlayPromise, []), - false + false, ); const calendars = React.useContext(CalendarsContext); diff --git a/src/src/components/Core/FirstAuthScreen.tsx b/src/src/components/Core/FirstAuthScreen.tsx index e6886b7..9d45189 100644 --- a/src/src/components/Core/FirstAuthScreen.tsx +++ b/src/src/components/Core/FirstAuthScreen.tsx @@ -76,5 +76,5 @@ export function FirstAuthScreen({ } const imageUrl = chrome.runtime.getURL( - '/src/public/images/btn_google_signin_light_normal_web@2x.png' + '/src/public/images/btn_google_signin_light_normal_web@2x.png', ); diff --git a/src/src/components/Core/__tests__/App.test.tsx b/src/src/components/Core/__tests__/App.test.tsx index 1ec9782..a96e294 100644 --- a/src/src/components/Core/__tests__/App.test.tsx +++ b/src/src/components/Core/__tests__/App.test.tsx @@ -17,7 +17,7 @@ test('does not render until current date is extracted', () => - + , ); expect(container.textContent).toEqual(''); })); @@ -38,7 +38,7 @@ test.skip('renders a button after current date is extracted', () => - + , ); const linkElement = getByRole('button', { name: commonText('calendarPlus'), diff --git a/src/src/components/Core/index.tsx b/src/src/components/Core/index.tsx index 9a05065..f969d24 100644 --- a/src/src/components/Core/index.tsx +++ b/src/src/components/Core/index.tsx @@ -16,15 +16,15 @@ import { Contexts } from '../Contexts/Contexts'; * is localizable (has different value depending on user's language). */ const icon = Array.from(document.querySelectorAll('header i')).find( - (icon) => icon?.textContent === 'search' + (icon) => icon?.textContent === 'search', ); // Find an element that contains more that one icon const findContainer = (element: Element | undefined): Element | undefined => element === undefined ? undefined : element.querySelectorAll('i').length > 1 - ? element - : findContainer(element.parentElement ?? undefined); + ? element + : findContainer(element.parentElement ?? undefined); const container = findContainer(icon); if (typeof container === 'object') { const reactContainer = document.createElement('div'); @@ -36,6 +36,6 @@ if (typeof container === 'object') { - + , ); } else console.error('Failed to attach Calendar Plus plugin'); diff --git a/src/src/components/Dashboard/index.tsx b/src/src/components/Dashboard/index.tsx index 00196e8..471b7ab 100644 --- a/src/src/components/Dashboard/index.tsx +++ b/src/src/components/Dashboard/index.tsx @@ -94,7 +94,7 @@ export function Dashboard({ setLayout( newWidget === undefined ? removeItem(layout, index) - : replaceItem(layout, index, newWidget) + : replaceItem(layout, index, newWidget), ) } /> diff --git a/src/src/components/Dashboard/useBreakpoint.tsx b/src/src/components/Dashboard/useBreakpoint.tsx index 45c2beb..73d3b57 100644 --- a/src/src/components/Dashboard/useBreakpoint.tsx +++ b/src/src/components/Dashboard/useBreakpoint.tsx @@ -10,12 +10,12 @@ export function useBreakpoint(): BreakPoint { throw new Error('Cannot invoke usePortalWidth outside a portal'); const [breakpoint, setBreakpoint] = React.useState(() => - resolveBreakpoint(container.clientWidth) + resolveBreakpoint(container.clientWidth), ); React.useEffect(() => { const resizeObserver = new ResizeObserver(() => - setBreakpoint(resolveBreakpoint(container.clientWidth)) + setBreakpoint(resolveBreakpoint(container.clientWidth)), ); resizeObserver.observe(container); diff --git a/src/src/components/Errors/ErrorBoundary.tsx b/src/src/components/Errors/ErrorBoundary.tsx index 3171a91..d35d84f 100644 --- a/src/src/components/Errors/ErrorBoundary.tsx +++ b/src/src/components/Errors/ErrorBoundary.tsx @@ -6,38 +6,42 @@ */ import React from 'react'; -import type {State} from 'typesafe-reducer'; +import type { State } from 'typesafe-reducer'; type ErrorBoundaryState = - | State<'Error', - { - readonly hasError: true; - readonly error: Error; - readonly errorInfo: { readonly componentStack: string }; - }> + | State< + 'Error', + { + readonly hasError: true; + readonly error: Error; + readonly errorInfo: { readonly componentStack: string }; + } + > | State<'Main'> | State<'Silenced'>; -export class ErrorBoundary extends React.Component<{ - readonly children: React.ReactNode; - /* - * Can wrap a component in an with silentErrors - * to silence all errors from it (on error, the component is quietly - * deRendered), if in production - * Useful for ensuring non-critical and experimental components don't - * crash the whole application - */ - readonly silentErrors?: boolean; - readonly dismissable?: boolean; -}, - ErrorBoundaryState> { +export class ErrorBoundary extends React.Component< + { + readonly children: React.ReactNode; + /* + * Can wrap a component in an with silentErrors + * to silence all errors from it (on error, the component is quietly + * deRendered), if in production + * Useful for ensuring non-critical and experimental components don't + * crash the whole application + */ + readonly silentErrors?: boolean; + readonly dismissable?: boolean; + }, + ErrorBoundaryState +> { public readonly state: ErrorBoundaryState = { type: 'Main', }; public componentDidCatch( error: Error, - errorInfo: { readonly componentStack: string } + errorInfo: { readonly componentStack: string }, ): void { console.error(error.toString()); this.setState({ @@ -58,12 +62,11 @@ export class ErrorBoundary extends React.Component<{ else return this.state.type === 'Error' ? (
-          Unexpected error has occurred
+ Unexpected error has occurred +
{this.state.error?.toString()} -
-
-            {this.state.errorInfo.componentStack}
-          
+
+
{this.state.errorInfo.componentStack}
) : ( this.props.children ?? null diff --git a/src/src/components/EventsStore/index.ts b/src/src/components/EventsStore/index.ts index 662f84f..7f221c2 100644 --- a/src/src/components/EventsStore/index.ts +++ b/src/src/components/EventsStore/index.ts @@ -54,7 +54,7 @@ type CalendarEvent = Pick< */ export function useEvents( startDate: Date | undefined, - endDate: Date | undefined + endDate: Date | undefined, ): EventsStore | undefined { const eventsStore = React.useRef({}); // Clear temporary cache when overlay is closed @@ -87,7 +87,7 @@ export function useEvents( eventsStore, id, startDate, - daysBetween + daysBetween, ); if (bounds === undefined) return; const [timeMin, timeMax] = bounds; @@ -97,7 +97,7 @@ export function useEvents( const guessCalendar = (input: string): string | undefined => virtualCalendars.find( ({ calendarId, rule, value }) => - calendarId === id && ruleMatchers[rule](input, value) + calendarId === id && ruleMatchers[rule](input, value), )?.virtualCalendar; const durations = group( @@ -116,7 +116,7 @@ export function useEvents( const [startDate, endDate] = dates; const data = calculateEventDuration(startDate, endDate); return [guessCalendar(summary) ?? '', data] as const; - }) + }), ); /* @@ -142,10 +142,10 @@ export function useEvents( calendarDurations[date].total += duration; calendarDurations[date].hourly[hour] = duration; }); - }) + }), ); }); - }) + }), ); return extractData(eventsStore.current, calendars, startDate, endDate); }, [ @@ -156,7 +156,7 @@ export function useEvents( ignoreAllDayEvents, virtualCalendars, ]), - false + false, ); return durations; } @@ -174,12 +174,12 @@ async function fetchEvents( id: string, timeMin: Date, timeMax: Date, - pageToken?: string + pageToken?: string, ): Promise | undefined> { const response = await ajax( formatUrl( `https://www.googleapis.com/calendar/v3/calendars/${encodeURIComponent( - id + id, )}/events`, { maxAttendees: (1).toString(), @@ -191,8 +191,8 @@ async function fetchEvents( 'nextPageToken,items(summary,start(dateTime,date),end(dateTime,date))', singleEvents: true.toString(), ...(typeof pageToken === 'string' ? { pageToken } : {}), - } - ) + }, + ), ).catch((error) => { console.error(error); return undefined; @@ -220,7 +220,7 @@ function calculateBounds( eventsStore: React.MutableRefObject, id: string, startDate: Date, - daysBetween: RA + daysBetween: RA, ): readonly [timeMin: Date, timeMax: Date] | undefined { const durations = eventsStore.current[id]?.['']; const firstDayToFetch = @@ -232,7 +232,7 @@ function calculateBounds( ? daysBetween.length : findLastIndex( daysBetween, - (date) => typeof durations[date] !== 'object' + (date) => typeof durations[date] !== 'object', ) + 1; if (firstDayToFetch === -1) return undefined; const timeMin = new Date(startDate); @@ -257,7 +257,7 @@ export const getDatesBetween = (startDate: Date, endDate: Date): RA => const date = new Date(startDate); date.setDate(startDate.getDate() + index); return dateToString(date); - } + }, ); /** @@ -282,31 +282,31 @@ function resolveEventDates( timeMin: Date, timeMax: Date, start: CalendarEvent['start'], - end: CalendarEvent['end'] + end: CalendarEvent['end'], ): readonly [start: Date, end: Date] | undefined { // Date is defined instead of DateTime for multi-day events const unboundedStartDate = typeof start.dateTime === 'string' ? new Date(start.dateTime) : typeof start.date === 'string' - ? dateToDateTime(start.date) - : undefined; + ? dateToDateTime(start.date) + : undefined; const unboundedEndDate = typeof end.dateTime === 'string' ? new Date(end.dateTime) : typeof end.date === 'string' - ? dateToDateTime(end.date) - : undefined; + ? dateToDateTime(end.date) + : undefined; if (unboundedStartDate === undefined || unboundedEndDate === undefined) return undefined; // Multi-date events might begin/end outside of current preview range const startDate = new Date( - Math.max(unboundedStartDate.getTime(), timeMin.getTime()) + Math.max(unboundedStartDate.getTime(), timeMin.getTime()), ); const endDate = new Date( - Math.min(unboundedEndDate.getTime(), timeMax.getTime()) + Math.min(unboundedEndDate.getTime(), timeMax.getTime()), ); return [startDate, endDate]; } @@ -329,13 +329,13 @@ function extractData( eventsStore: RawEventsStore, calendars: Exclude, undefined>, startDate: Date, - endDate: Date + endDate: Date, ): EventsStore { const daysBetween = getDatesBetween(startDate, endDate); return Object.fromEntries( calendars.map(({ id }) => { const totals: R = Object.fromEntries( - daysBetween.map((date) => [date, blankHours()]) + daysBetween.map((date) => [date, blankHours()]), ); // "eventsStore" won't have an entry for current calendar if fetching failed const entries = Object.entries(eventsStore?.[id] ?? {}) @@ -352,7 +352,7 @@ function extractData( }); categoryTotal += total.total; return [date, total]; - }) + }), ), categoryTotal, ] as const; @@ -360,11 +360,11 @@ function extractData( .sort( sortFunction( ([_label, _durations, categoryTotal]) => categoryTotal, - true - ) + true, + ), ); return [id, Object.fromEntries([...entries, [summedDurations, totals]])]; - }) + }), ); } @@ -374,7 +374,7 @@ function extractData( */ function calculateEventDuration( startDate: Date, - endDate: Date + endDate: Date, ): IR> { const results: R> = {}; const startDateString = dateToString(startDate); diff --git a/src/src/components/Goals/GoalsEditor.tsx b/src/src/components/Goals/GoalsEditor.tsx index f32af01..c5e96fb 100644 --- a/src/src/components/Goals/GoalsEditor.tsx +++ b/src/src/components/Goals/GoalsEditor.tsx @@ -43,7 +43,7 @@ export function GoalsEditor({ replaceItem(goals, index, { ...goal, calendarId, - }) + }), ) } /> @@ -56,7 +56,7 @@ export function GoalsEditor({ replaceItem(goals, index, { ...goal, virtualCalendar, - }) + }), ) } /> @@ -67,12 +67,12 @@ export function GoalsEditor({ replaceItem(goals, index, { ...goal, duration, - }) + }), ) } /> - ) : undefined + ) : undefined, )}
computeGoal(currentDurations, virtualCalendar), - [currentDurations, virtualCalendar] + [currentDurations, virtualCalendar], ); return typeof calendar === 'object' ? ( - Object.values(durations) + Object.values(durations), ) : Object.values(currentDurations?.[virtualCalendar] ?? {}); return durations.reduce((total, value) => total + value.total, 0); diff --git a/src/src/components/Molecules/CalendarList.tsx b/src/src/components/Molecules/CalendarList.tsx index a63abc5..7935d41 100644 --- a/src/src/components/Molecules/CalendarList.tsx +++ b/src/src/components/Molecules/CalendarList.tsx @@ -56,12 +56,12 @@ export function VirtualCalendarsList({ .unique( virtualCalendars .filter( - (virtualCalendar) => virtualCalendar.calendarId === calendarId + (virtualCalendar) => virtualCalendar.calendarId === calendarId, ) - .map(({ virtualCalendar }) => virtualCalendar) + .map(({ virtualCalendar }) => virtualCalendar), ) .filter((category) => category !== undefined), - [virtualCalendars, calendarId] + [virtualCalendars, calendarId], ); return (