diff --git a/src/components/Inputs/OrderableList.tsx b/src/components/Inputs/OrderableList.tsx index 51164d4..614db91 100644 --- a/src/components/Inputs/OrderableList.tsx +++ b/src/components/Inputs/OrderableList.tsx @@ -28,7 +28,6 @@ const InsertedLabel = ({ label }: InsertedLabelProps) => ( export const OrderableList = ({ onChange, items, insertLabels }: Props): JSX.Element => { const updateOrder = useCallback( (sourceIndex: number, targetIndex: number) => { - console.log("updateOrder"); onChange?.(arrayMove(items, sourceIndex, targetIndex)); }, [items, onChange] diff --git a/src/contexts/localData.ts b/src/contexts/localData.ts index e080a43..cd9f5d2 100644 --- a/src/contexts/localData.ts +++ b/src/contexts/localData.ts @@ -11,8 +11,10 @@ import { import { LocalDataFile } from "graphql/fetchLocalData"; import { internalAtoms } from "contexts/atoms"; import { processLanguages, processCurrencies, processLangui } from "helpers/localData"; +import { getLogger } from "helpers/logger"; const getFileName = (name: LocalDataFile): string => `/local-data/${name}.json`; +const logger = getLogger("πŸ’½ [Local Data]"); export const useLocalData = (): void => { const setLanguages = useAtomSetter(internalAtoms.localData.languages); @@ -28,22 +30,22 @@ export const useLocalData = (): void => { ); useEffect(() => { - console.log("[useLocalData] Refresh languages"); + logger.log("Refresh languages"); setLanguages(processLanguages(rawLanguages)); }, [rawLanguages, setLanguages]); useEffect(() => { - console.log("[useLocalData] Refresh currencies"); + logger.log("Refresh currencies"); setCurrencies(processCurrencies(rawCurrencies)); }, [rawCurrencies, setCurrencies]); useEffect(() => { - console.log("[useLocalData] Refresh langui"); + logger.log("Refresh langui"); setLangui(processLangui(rawLangui, locale)); }, [locale, rawLangui, setLangui]); useEffect(() => { - console.log("[useLocalData] Refresh fallback langui"); + logger.log("Refresh fallback langui"); setFallbackLangui(processLangui(rawLangui, "en")); }, [rawLangui, setFallbackLangui]); }; diff --git a/src/contexts/settings.ts b/src/contexts/settings.ts index 9cb836c..ac8f4cd 100644 --- a/src/contexts/settings.ts +++ b/src/contexts/settings.ts @@ -84,7 +84,6 @@ export const useSettings = (): void => { useEffect(() => { if (preferredLanguages.length === 0) { if (isDefinedAndNotEmpty(router.locale) && router.locales) { - console.log(router.locale, getDefaultPreferredLanguages(router.locale, router.locales)); setPreferredLanguages(getDefaultPreferredLanguages(router.locale, router.locales)); } } else if (router.locale !== preferredLanguages[0]) { diff --git a/src/graphql/fetchLocalData.ts b/src/graphql/fetchLocalData.ts index 8eb1b92..523b2c3 100644 --- a/src/graphql/fetchLocalData.ts +++ b/src/graphql/fetchLocalData.ts @@ -5,15 +5,17 @@ import { config } from "dotenv"; import { getReadySdk } from "./sdk"; import { LocalDataGetWebsiteInterfacesQuery } from "./generated"; import { processLangui, Langui } from "helpers/localData"; +import { getLogger } from "helpers/logger"; config({ path: resolve(process.cwd(), ".env.local") }); const LOCAL_DATA_FOLDER = `${process.cwd()}/public/local-data`; +const logger = getLogger("πŸ’½ [Local Data]"); const writeLocalData = (name: LocalDataFile, localData: unknown) => { const path = `${LOCAL_DATA_FOLDER}/${name}.json`; writeFileSync(path, JSON.stringify(localData), { encoding: "utf-8" }); - console.log(`${path} has been written!`); + logger.log(`${name}.json has been written`); }; const readLocalData = (name: LocalDataFile): T => { diff --git a/src/graphql/icuToTypescript.ts b/src/graphql/icuToTypescript.ts index 6ed8144..d6c47e8 100644 --- a/src/graphql/icuToTypescript.ts +++ b/src/graphql/icuToTypescript.ts @@ -3,8 +3,10 @@ import { createWriteStream } from "fs"; import { parse, TYPE } from "@formatjs/icu-messageformat-parser"; import { getLangui } from "./fetchLocalData"; import { filterDefined } from "helpers/asserts"; +import { getLogger } from "helpers/logger"; const OUTPUT_FOLDER = `${process.cwd()}/src/graphql`; +const logger = getLogger("πŸ’½ [ICU to TS]"); const icuToTypescript = () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -45,7 +47,7 @@ const icuToTypescript = () => { output.write("}\n"); - console.log(`${OUTPUT_FOLDER}/icu-params.ts has been written!`); + logger.log(`icu-params.ts has been written!`); }; if (process.argv[2] === "--icu") { diff --git a/src/helpers/analytics.ts b/src/helpers/analytics.ts index 59c7e9a..baa0126 100644 --- a/src/helpers/analytics.ts +++ b/src/helpers/analytics.ts @@ -1,10 +1,14 @@ +import { getLogger } from "helpers/logger"; + +const logger = getLogger("πŸ“Š [Analytics]"); + export const sendAnalytics = (category: string, event: string): void => { const eventName = `[${category}] ${event}`; - console.log(`Event: ${eventName}`); + logger.log(eventName); try { umami(eventName); } catch (error) { if (error instanceof ReferenceError) return; - console.log(error); + logger.error(error); } }; diff --git a/src/helpers/i18n.ts b/src/helpers/i18n.ts index 8310dbc..f6d571b 100644 --- a/src/helpers/i18n.ts +++ b/src/helpers/i18n.ts @@ -5,9 +5,7 @@ import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts"; import { getLangui } from "graphql/fetchLocalData"; type WordingKey = keyof ICUParams; - type LibraryItemType = Exclude; - type ContentStatus = "Done" | "Draft" | "Incomplete" | "Review"; const componentMetadataToWording: Record = { @@ -58,7 +56,13 @@ export const getFormat = ( if (isDefinedAndNotEmpty(result)) { return result; } - return new IntlMessageFormat(fallbackLangui[key] ?? "").format(processedValues).toString(); + const fallback = new IntlMessageFormat(fallbackLangui[key] ?? "") + .format(processedValues) + .toString(); + if (isDefinedAndNotEmpty(fallback)) { + return fallback; + } + return key; }; const formatLibraryItemType = (metadata: { __typename: LibraryItemType }): string => diff --git a/src/helpers/logger.ts b/src/helpers/logger.ts new file mode 100644 index 0000000..4ec5289 --- /dev/null +++ b/src/helpers/logger.ts @@ -0,0 +1,13 @@ +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const getLogger = (prefix: string) => ({ + error: (message?: unknown, ...optionalParams: unknown[]) => + console.error(prefix, message, ...optionalParams), + warn: (message?: unknown, ...optionalParams: unknown[]) => + console.warn(prefix, message, ...optionalParams), + log: (message?: unknown, ...optionalParams: unknown[]) => + console.log(prefix, message, ...optionalParams), + info: (message?: unknown, ...optionalParams: unknown[]) => + console.info(prefix, message, ...optionalParams), + debug: (message?: unknown, ...optionalParams: unknown[]) => + console.debug(prefix, message, ...optionalParams), +}); diff --git a/src/hooks/useFormat.ts b/src/hooks/useFormat.ts index ae72fe5..f698241 100644 --- a/src/hooks/useFormat.ts +++ b/src/hooks/useFormat.ts @@ -5,11 +5,12 @@ import { useAtomGetter } from "helpers/atoms"; import { LibraryItemMetadataDynamicZone } from "graphql/generated"; import { ICUParams } from "graphql/icuParams"; import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts"; +import { getLogger } from "helpers/logger"; + +const logger = getLogger("πŸ—ΊοΈ [I18n]"); type WordingKey = keyof ICUParams; - type LibraryItemType = Exclude; - export type ContentStatus = "Done" | "Draft" | "Incomplete" | "Review"; const componentMetadataToWording: Record = { @@ -59,12 +60,17 @@ export const useFormat = (): { if (isDefinedAndNotEmpty(result)) { return result; } + logger.warn( + `Missing ${langui.ui_language?.data?.attributes?.code} translation for ${key}. \ +Falling back to en translation.` + ); const fallback = new IntlMessageFormat(fallbackLangui[key] ?? "") .format(processedValues) .toString(); if (isDefinedAndNotEmpty(fallback)) { return fallback; } + logger.warn(`Missing fallback translation for ${key}. The key seems unvalid`); return key; }, [langui, fallbackLangui] diff --git a/src/hooks/useIntersectionList.ts b/src/hooks/useIntersectionList.ts index 4a02425..06889ae 100644 --- a/src/hooks/useIntersectionList.ts +++ b/src/hooks/useIntersectionList.ts @@ -4,6 +4,9 @@ import { useIsClient } from "usehooks-ts"; import { useOnScroll } from "./useOnScroll"; import { isDefined, isUndefined } from "helpers/asserts"; import { Ids } from "types/ids"; +import { getLogger } from "helpers/logger"; + +const logger = getLogger("β›’ [Intersection List]"); export const useIntersectionList = (ids: string[]): number => { const [currentIntersection, setCurrentIntersection] = useState(-1); @@ -14,7 +17,7 @@ export const useIntersectionList = (ids: string[]): number => { const refreshCurrentIntersection = useCallback( (scroll: number) => { - console.log("useIntersectionList"); + logger.debug("Recalculating intersection"); if (isUndefined(contentPanel)) { setCurrentIntersection(-1); diff --git a/src/hooks/useOnResize.ts b/src/hooks/useOnResize.ts index 678737d..e295890 100644 --- a/src/hooks/useOnResize.ts +++ b/src/hooks/useOnResize.ts @@ -1,6 +1,9 @@ import { useEffect } from "react"; import { useIsClient } from "usehooks-ts"; import { isDefined } from "helpers/asserts"; +import { getLogger } from "helpers/logger"; + +const logger = getLogger("πŸ“ [Resize Observer]"); export const useOnResize = ( id: string, @@ -9,7 +12,7 @@ export const useOnResize = ( const isClient = useIsClient(); useEffect(() => { - console.log("[useOnResize]", id); + logger.log(`Creating observer for ${id}`); const elem = isClient ? document.querySelector(`#${id}`) : null; const ro = new ResizeObserver((resizeObserverEntry) => { const entry = resizeObserverEntry[0]; diff --git a/src/hooks/useScrollIntoView.ts b/src/hooks/useScrollIntoView.ts index cc7631f..c9893c7 100644 --- a/src/hooks/useScrollIntoView.ts +++ b/src/hooks/useScrollIntoView.ts @@ -2,8 +2,11 @@ import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { useCounter } from "usehooks-ts"; import { isDefined } from "helpers/asserts"; +import { getLogger } from "helpers/logger"; const NUM_RETRIES = 10; +const DELAY_BETWEEN_RETRY = 200; +const logger = getLogger("↕️ [Scroll Into View]"); export const useScrollIntoView = (): void => { const router = useRouter(); @@ -18,14 +21,14 @@ export const useScrollIntoView = (): void => { if (hash !== "") { const element = document.getElementById(hash); if (isDefined(element)) { - console.log(`[useScrollIntoView] ${hash} found`); + logger.log(`#${hash} found`); element.scrollIntoView(); setHasReachedElem(true); } else { - console.log(`[useScrollIntoView] ${hash} not found`); + logger.warn(`#${hash} not found, retrying in ${DELAY_BETWEEN_RETRY} ms`); setTimeout(() => { increment(); - }, 200); + }, DELAY_BETWEEN_RETRY); } } } diff --git a/src/hooks/useScrollTopOnChange.ts b/src/hooks/useScrollTopOnChange.ts index bc4ae13..f77610c 100644 --- a/src/hooks/useScrollTopOnChange.ts +++ b/src/hooks/useScrollTopOnChange.ts @@ -1,10 +1,16 @@ import { DependencyList, useEffect } from "react"; +import { getLogger } from "helpers/logger"; import { Ids } from "types/ids"; +const logger = getLogger("⬆️ [Scroll Top On Change]"); + // Scroll to top of element "id" when "deps" update. export const useScrollTopOnChange = (id: Ids, deps: DependencyList, enabled = true): void => { useEffect(() => { - if (enabled) document.querySelector(`#${id}`)?.scrollTo({ top: 0, behavior: "smooth" }); + if (enabled) { + logger.log("Change detected. Scrolling to top"); + document.querySelector(`#${id}`)?.scrollTo({ top: 0, behavior: "smooth" }); + } // eslint-disable-next-line react-hooks/exhaustive-deps }, [id, ...deps, enabled]); };