I18N Caching (#6823)
* Caching Logic Changes Enabled this function to change its cache value based on incoming paths value * Invalidate I18N Cache Invalidating the I18N cache when a user saves changes to their General settings * Removes deprecated useLocale location * Overriding the default getSchedule cache to have a revalidation time of 1 second * Update apps/web/pages/api/trpc/[trpc].ts * Updated cache values to match the comment --------- Co-authored-by: zomars <zomars@me.com>
This commit is contained in:
parent
4bf89d68fa
commit
be77464f44
|
@ -1,11 +1,11 @@
|
||||||
import { FormEvent, useCallback, useEffect, useState } from "react";
|
import { FormEvent, useCallback, useEffect, useState } from "react";
|
||||||
import Cropper from "react-easy-crop";
|
import Cropper from "react-easy-crop";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Button, Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui";
|
import { Button, Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui";
|
||||||
|
|
||||||
import { Area, getCroppedImg } from "@lib/cropImage";
|
import { Area, getCroppedImg } from "@lib/cropImage";
|
||||||
import { useFileReader } from "@lib/hooks/useFileReader";
|
import { useFileReader } from "@lib/hooks/useFileReader";
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
|
|
||||||
import Slider from "@components/Slider";
|
import Slider from "@components/Slider";
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Badge, Button } from "@calcom/ui";
|
import { Badge, Button } from "@calcom/ui";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
|
|
||||||
import DisableTwoFactorModal from "./DisableTwoFactorModal";
|
import DisableTwoFactorModal from "./DisableTwoFactorModal";
|
||||||
import EnableTwoFactorModal from "./EnableTwoFactorModal";
|
import EnableTwoFactorModal from "./EnableTwoFactorModal";
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,9 @@ import Link from "next/link";
|
||||||
import { TeamPageProps } from "pages/team/[slug]";
|
import { TeamPageProps } from "pages/team/[slug]";
|
||||||
|
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Avatar } from "@calcom/ui";
|
import { Avatar } from "@calcom/ui";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
|
|
||||||
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
|
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
|
||||||
|
|
||||||
type TeamType = TeamPageProps["team"];
|
type TeamType = TeamPageProps["team"];
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { useTranslation } from "next-i18next";
|
|
||||||
|
|
||||||
/** @deprecated use the one from `@calcom/lib/hooks/useLocale` */
|
|
||||||
export const useLocale = () => {
|
|
||||||
const { i18n, t } = useTranslation("common");
|
|
||||||
|
|
||||||
return {
|
|
||||||
i18n,
|
|
||||||
t,
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -4,11 +4,10 @@ import { useRouter } from "next/router";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { COMPANY_NAME, DEVELOPER_DOCS, DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants";
|
import { COMPANY_NAME, DEVELOPER_DOCS, DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants";
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { HeadSeo } from "@calcom/ui";
|
import { HeadSeo } from "@calcom/ui";
|
||||||
import { FiFileText, FiCheck, FiBookOpen, FiChevronRight } from "@calcom/ui/components/icon";
|
import { FiFileText, FiCheck, FiBookOpen, FiChevronRight } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
|
|
||||||
import { ssgInit } from "@server/lib/ssg";
|
import { ssgInit } from "@server/lib/ssg";
|
||||||
|
|
||||||
export default function Custom404() {
|
export default function Custom404() {
|
||||||
|
|
|
@ -30,34 +30,58 @@ export default trpcNext.createNextApiHandler({
|
||||||
* @link https://trpc.io/docs/caching#api-response-caching
|
* @link https://trpc.io/docs/caching#api-response-caching
|
||||||
*/
|
*/
|
||||||
responseMeta({ ctx, paths, type, errors }) {
|
responseMeta({ ctx, paths, type, errors }) {
|
||||||
// assuming we have all our public routes in `viewer.public`
|
// Some helpers relevant to this function only
|
||||||
const allPublic = paths && paths.every((path) => path.startsWith("viewer.public."));
|
const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
|
||||||
// checking that no procedures errored
|
const TWO_HOURS_IN_SECONDS = 60 * 60 * 2;
|
||||||
const allOk = errors.length === 0;
|
|
||||||
// checking we're doing a query request
|
|
||||||
const isQuery = type === "query";
|
|
||||||
|
|
||||||
// i18n response depends on request header
|
// Our response to indicate no caching
|
||||||
const nonCacheableQueries = ["viewer.public.i18n"];
|
const noCacheResponse = {};
|
||||||
const isThereANonCacheableQuery = paths?.some((path) => nonCacheableQueries.includes(path));
|
|
||||||
const isThereACacheableQuery = paths?.some((path) => !nonCacheableQueries.includes(path));
|
|
||||||
if (isThereANonCacheableQuery && isThereACacheableQuery) {
|
|
||||||
console.warn(
|
|
||||||
"Cacheable and Non-cacheable queries are mixed in the same request. Not going to cache the request"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore ctx.req is available for SSR but not SSG
|
// @ts-ignore ctx.req is available for SSR but not SSG
|
||||||
if (!!ctx?.req && allPublic && allOk && isQuery && !isThereANonCacheableQuery) {
|
const isSSR = !!ctx?.req;
|
||||||
// cache request for 1 day + revalidate once every 5 seconds
|
if (isSSR) {
|
||||||
const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
|
return noCacheResponse;
|
||||||
return {
|
|
||||||
headers: {
|
|
||||||
"cache-control": `s-maxage=5, stale-while-revalidate=${ONE_DAY_IN_SECONDS}`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
|
// No caching if we have a non-public path
|
||||||
|
// Assuming we have all our public routes in `viewer.public`
|
||||||
|
if (!paths || !paths.every((path) => path.startsWith("viewer.public."))) {
|
||||||
|
return noCacheResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No caching if we have any procedures errored
|
||||||
|
if (errors.length !== 0) {
|
||||||
|
return noCacheResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Never cache non-queries (aka mutations)
|
||||||
|
if (type !== "query") {
|
||||||
|
return noCacheResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache request for 1 day + revalidate once every 5 seconds
|
||||||
|
let cacheValue = `s-maxage=${ONE_DAY_IN_SECONDS}, stale-while-revalidate=5`;
|
||||||
|
|
||||||
|
// Our cache can change depending on our current paths value. Since paths is an array,
|
||||||
|
// we want to create a map that can match potential paths with their desired cache value
|
||||||
|
const cacheRules = {
|
||||||
|
"viewer.public.i18n": `maxage=${TWO_HOURS_IN_SECONDS}, stale-while-revalidate=30`,
|
||||||
|
// Revalidation time here should be 1 second, per https://github.com/calcom/cal.com/pull/6823#issuecomment-1423215321
|
||||||
|
"viewer.public.slots.getSchedule": `s-maxage=5, stale-while-revalidate=1`,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// Find which element above is an exact match for this group of paths
|
||||||
|
const matchedPath = paths.find((v) => v in cacheRules) as keyof typeof cacheRules;
|
||||||
|
|
||||||
|
// Get the cache value of the matching element, if any
|
||||||
|
if (matchedPath) cacheValue = cacheRules[matchedPath];
|
||||||
|
|
||||||
|
// Finally we respond with our resolved cache value
|
||||||
|
return {
|
||||||
|
headers: {
|
||||||
|
"Cache-Control": cacheValue,
|
||||||
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,11 +6,10 @@ import Link from "next/link";
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { Button, TextField } from "@calcom/ui";
|
import { Button, TextField } from "@calcom/ui";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
|
|
||||||
import AuthContainer from "@components/ui/AuthContainer";
|
import AuthContainer from "@components/ui/AuthContainer";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
|
@ -5,10 +5,10 @@ import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import React, { SyntheticEvent } from "react";
|
import React, { SyntheticEvent } from "react";
|
||||||
|
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Button, EmailField } from "@calcom/ui";
|
import { Button, EmailField } from "@calcom/ui";
|
||||||
|
|
||||||
import { getSession } from "@lib/auth";
|
import { getSession } from "@lib/auth";
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
|
||||||
|
|
||||||
import AuthContainer from "@components/ui/AuthContainer";
|
import AuthContainer from "@components/ui/AuthContainer";
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,9 @@ const GeneralView = ({ localeProp, user }: GeneralViewProps) => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
const mutation = trpc.viewer.updateProfile.useMutation({
|
const mutation = trpc.viewer.updateProfile.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: async () => {
|
||||||
|
// Invalidate our previous i18n cache
|
||||||
|
await utils.viewer.public.i18n.invalidate();
|
||||||
reset(getValues());
|
reset(getValues());
|
||||||
showToast(t("settings_updated_successfully"), "success");
|
showToast(t("settings_updated_successfully"), "success");
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user