Skip to content

Commit

Permalink
Events - Fixing events performance (#3274)
Browse files Browse the repository at this point in the history
* Events - Removing the search Param

* Removing reducer

* removing redundant code

* removing unused reducer import

* Adding placeholder image for the events

* Adding lazy loading for the images

* adding minimum height for the timing

* Adding query param feature

* Adding breadcrumb

* fixing mobile view

* removing query param

* Adding bluredBase 64 image

* removing unused func

* Adding the blurred image for the events

* fixing the image import path

* Adding search param for client rendering

* Adding some spaces for mobile view on relative timings

* removing unused placeholder image

* Updating the comment
  • Loading branch information
amankumarrr authored Nov 1, 2024
1 parent 4917ee7 commit 7cf6c7d
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 92 deletions.
25 changes: 16 additions & 9 deletions app/events/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,34 @@ import { Blocks } from "@/components/blocks-renderer";
import { componentRenderer } from "@/components/blocks/mdxComponentRenderer";
import { EventsFilter } from "@/components/filter/events";
import { Container } from "@/components/util/container";
import { Section } from "@/components/util/section";
import { removeExtension } from "@/services/client/utils.service";
import { HydrationBoundary } from "@tanstack/react-query";
import { Breadcrumbs } from "app/components/breadcrumb";
import { QueryProvider } from "app/providers/query-provider";
import { useSearchParams } from "next/navigation";
import { TinaMarkdown } from "tinacms/dist/rich-text";

export default function EventsIndexPage({ props, tinaProps }) {
const { filterCategories } = props;
const { data } = tinaProps;

const searchParams = useSearchParams();

const defaultToPastTab = searchParams.get("past") === "1";

return (
<QueryProvider>
<HydrationBoundary state={props.dehydratedState}>
<Container size="small">
<Section className="mx-auto min-h-24 w-full max-w-9xl px-8 py-5 md:min-h-16">
<Breadcrumbs
path={removeExtension(props.variables.relativePath)}
suffix={data.global.breadcrumbSuffix}
title={data.eventsIndex.seo?.title}
seoSchema={data.eventsIndex.seo}
/>
</Section>
<Container size="small" className="!pb-8 !pt-0">
<div className="md:flex md:flex-row">
<h1 className="md:mr-12 md:shrink-0 md:basis-64">SSW Events</h1>
<div className="mt-5 min-w-0 max-w-full shrink grow overflow-auto whitespace-normal break-all pb-1 pt-15 md:mr-12 md:shrink-0 md:basis-64">
<h1 className="pt-0 md:mr-12 md:shrink-0 md:basis-64">
SSW Events
</h1>
<div className="mt-5 min-w-0 max-w-full shrink grow overflow-auto whitespace-normal break-all pb-1 pt-5 md:mr-12 md:shrink-0 md:basis-64">
<TinaMarkdown
content={data.eventsIndex.preface}
components={componentRenderer}
Expand All @@ -33,7 +41,6 @@ export default function EventsIndexPage({ props, tinaProps }) {
<EventsFilter
filterCategories={filterCategories}
sidebarBody={data.eventsIndex.sidebarBody}
defaultToPastTab={defaultToPastTab}
/>
</Container>
<Blocks
Expand Down
8 changes: 5 additions & 3 deletions app/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Container } from "@/components/util/container";

export default function Loading() {
const Loading = () => (
<Container className="flex justify-center" width="large" size="custom">
<h1>Loading...</h1>
</Container>;
}
</Container>
);

export default Loading;
4 changes: 4 additions & 0 deletions components/blocks/customImage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import classNames from "classnames";
import Image from "next/image";
import type { Template } from "tinacms";
import { BluredBase64Image } from "../../helpers/images";
import { CustomLink } from "../customLink";
import { customClasses } from "../util/constants";

Expand Down Expand Up @@ -36,6 +37,9 @@ export const CustomImage = ({ data }: { data: CustomImageProps }) => {
alt={data.altText}
height={data.height || 400}
width={data.width || 400}
loading="lazy"
placeholder="blur"
blurDataURL={BluredBase64Image}
className={classNames(
"inline-block",
(customClasses[data.customClass] || data.customClass) ?? ""
Expand Down
2 changes: 1 addition & 1 deletion components/events/eventsRelativeBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const EventsRelativeBox = ({
dateFontSize,
}: EventsRelativeBoxProps) => {
return (
<time className="my-1.5 flex items-center">
<time className="my-3 flex min-h-6 w-full items-center md:my-1.5">
{relativeDate && (
<span
className={classNames(
Expand Down
90 changes: 42 additions & 48 deletions components/filter/events.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
"use client";
import { Tab, Transition } from "@headlessui/react";
import Image from "next/image";
import React, {
Fragment,
useEffect,
useMemo,
useReducer,
useState,
} from "react";
import React, { Fragment, useEffect, useMemo, useState } from "react";
import { FaSpinner } from "react-icons/fa";
import type { Event, WithContext } from "schema-dts";
import { TinaMarkdown, TinaMarkdownContent } from "tinacms/dist/rich-text";
import { BluredBase64Image } from "../../helpers/images";
import { useEvents } from "../../hooks/useEvents";
import {
useFetchFutureEvents,
Expand All @@ -31,7 +26,6 @@ const EVENTS_JSON_LD_LIMIT = 5;

interface EventsFilterProps {
sidebarBody: TinaMarkdownContent;
defaultToPastTab?: boolean;
filterCategories: EventFilterAllCategories;
}

Expand Down Expand Up @@ -59,12 +53,12 @@ export type EventTrimmed = {
export const EventsFilter = ({
filterCategories,
sidebarBody,
defaultToPastTab,
}: EventsFilterProps) => {
const [pastSelected, setPastSelected] = useState<boolean>(defaultToPastTab);
const [pastSelected, setPastSelected] = useState<boolean>(false);
const { past, upcoming } = filterCategories;
const { filters: futureFilters } = useEvents(upcoming);
const { filters: pastFilters } = useEvents(past);

const pastSelectedFilters = useMemo<SelectedFilters>(() => {
const filters = getFilterState(pastFilters);
return filters;
Expand All @@ -91,6 +85,16 @@ export const EventsFilter = ({
isLoadingPastPages,
} = useFetchPastEvents(pastSelectedFilters);

useEffect(() => {
// Using Next.js's useSearchParams function leads to complete client-side rendering, which impacts SEO and page load performance, therefore using javascript's function
const params = new URLSearchParams(window.location.search);
const queryTab = params.get("past");

if (queryTab === "1") {
setPastSelected(true);
}
}, []);

return (
<FilterBlock
sidebarChildren={
Expand All @@ -102,7 +106,7 @@ export const EventsFilter = ({
>
<Tab.Group
onChange={(index) => setPastSelected(index === 1)}
defaultIndex={defaultToPastTab ? 1 : 0}
selectedIndex={pastSelected ? 1 : 0}
>
<Tab.List className="mb-8 flex flex-row">
<EventTab>Upcoming Events</EventTab>
Expand Down Expand Up @@ -157,23 +161,6 @@ interface EventsListProps {
isLoading?: boolean;
}

const eventsReducer = (state, action) => {
if (!state.visible && arraysEqual(state.firstEvents, action.payload)) {
return state;
}
if (state.visible && arraysEqual(state.secondEvents, action.payload)) {
return state;
}
switch (action.type) {
case "SET_EVENTS":
return state.visible
? { ...state, firstEvents: action.payload, visible: !state.visible }
: { ...state, secondEvents: action.payload, visible: !state.visible };
default:
return state;
}
};

// TODO: Compare arrays by reference instead of value https://github.com/SSWConsulting/SSW.Website/issues/3066
const arraysEqual = (arr1: EventTrimmed[], arr2: EventTrimmed[]): boolean => {
if (arr1.length !== arr2.length) return false;
Expand All @@ -182,18 +169,22 @@ const arraysEqual = (arr1: EventTrimmed[], arr2: EventTrimmed[]): boolean => {
);
};

const initialState = {
firstEvents: [],
secondEvents: [],
isFetching: false,
visible: true,
};

const EventsList = ({ events, isUpcoming, isLoading }: EventsListProps) => {
const [state, dispatch] = useReducer(eventsReducer, initialState);
const [firstEvents, setFirstEvents] = useState(events);
const [secondEvents, setSecondEvents] = useState(events);
const [visible, setVisible] = useState(true);

// Update events and toggle visibility if `events` changes
useEffect(() => {
dispatch({ type: "SET_EVENTS", payload: events });
}, [events]);
if (!arraysEqual(visible ? firstEvents : secondEvents, events)) {
if (visible) {
setFirstEvents(events);
} else {
setSecondEvents(events);
}
setVisible(!visible); // Toggle visibility
}
}, [events, visible, firstEvents, secondEvents]);

return (
<div>
Expand All @@ -202,15 +193,15 @@ const EventsList = ({ events, isUpcoming, isLoading }: EventsListProps) => {
) : (
<>
<LoadedEvents
visible={!state.visible}
events={state.firstEvents}
visible={!visible}
events={firstEvents}
isUpcoming={isUpcoming}
></LoadedEvents>
/>
<LoadedEvents
visible={state.visible}
events={state.secondEvents}
visible={visible}
events={secondEvents}
isUpcoming={isUpcoming}
></LoadedEvents>
/>
</>
)}
</div>
Expand Down Expand Up @@ -295,7 +286,7 @@ const Event = ({ visible, event, jsonLd }: EventProps) => {
*/

const [thumbnail, setFallbackImage] = useState("");
const [thumbnail, setFallbackImage] = useState(event.thumbnail);
useEffect(() => {
setFallbackImage(event.thumbnail);
}, [event.thumbnail]);
Expand Down Expand Up @@ -331,14 +322,17 @@ const Event = ({ visible, event, jsonLd }: EventProps) => {
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
<div className="mb-8 flex max-md:flex-col md:flex-row">
<div className="mr-3 shrink-0">
<div className="mb-8 block md:flex md:flex-row">
<div className="float-left mb-3 mr-3 shrink-0 pr-2 md:float-none md:pr-0">
<Image
className="rounded-md max-md:pb-3"
className={"rounded-md"}
height={100}
width={100}
placeholder="blur"
alt={`${event.thumbnailDescription || event.title} logo`}
src={thumbnail}
loading="lazy"
blurDataURL={BluredBase64Image}
onError={handleImageError}
/>
</div>
Expand Down
3 changes: 1 addition & 2 deletions content/events/index/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ seo:
description: >-
Learn from software industry professionals about the latest tech and best
practices for MAUI, .Net, GitHub, DevOps.
preface: ''
showBreadcrumb: true
sidebarBody: >
### Video On Demand
Expand Down Expand Up @@ -45,4 +45,3 @@ afterEvents:
_template: BuiltOnAzure
---


20 changes: 20 additions & 0 deletions helpers/getTrimmedEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const getTrimmedEvent = (events) =>
events?.pages.flat().flatMap((item) =>
item.eventsCalendarConnection.edges.map((edge) => ({
...edge.node,
startDateTime: new Date(edge.node.startDateTime),
endDateTime: new Date(edge.node.endDateTime),
category: formatCategory(edge.node.category),
}))
) || [];

export const formatCategory = (category: string): string => {
{
const categoryReplacements = {
"Non-English Courses": "Other",
};
const lookup = categoryReplacements[category];

return lookup ? lookup : category;
}
};
3 changes: 3 additions & 0 deletions helpers/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ export function getBackgroundImage(srcSet = "") {
.join(", ");
return `image-set(${imageSet})`;
}

export const BluredBase64Image =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAW1JREFUeF7t1bEJADAMxMB4/33dJpApVJwnEBKPZ3fvcRkDI0imxQcRpNVDkFgPQQSpGYjx+CGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4ViIIDEDMRwLESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4ViIIDEDMRwLESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzGcBz76hFhWkH1sAAAAAElFTkSuQmCC";
32 changes: 3 additions & 29 deletions hooks/useFetchEvents.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EventFilterAllCategories } from "@/components/filter/FilterBlock";
import { formatCategory, getTrimmedEvent } from "@/helpers/getTrimmedEvents";
import { EVENTS_MAX_SIZE_OVERRIDE } from "@/services/server/getEvents";
import { GetPastEventsQueryQuery } from "@/tina/types";
import { useInfiniteQuery } from "@tanstack/react-query";
Expand All @@ -20,17 +21,6 @@ const getCategoriesForFilter = (category: string) => {
return lookup ? lookup : [category];
};

const formatCategory = (category: string): string => {
{
const categoryReplacements = {
"Non-English Courses": "Other",
};
const lookup = categoryReplacements[category];

return lookup ? lookup : category;
}
};

export const TODAY = new Date();
TODAY.setHours(0, 0, 0, 0);

Expand Down Expand Up @@ -69,15 +59,7 @@ export const useFetchFutureEvents = (filters: SelectedCategories) => {
},
});
return {
futureEvents:
data?.pages.flat().flatMap((item) =>
item.eventsCalendarConnection.edges.map((edge) => ({
...edge.node,
startDateTime: new Date(edge.node.startDateTime),
endDateTime: new Date(edge.node.endDateTime),
category: formatCategory(edge.node.category),
}))
) || [],
futureEvents: getTrimmedEvent(data),
error,
isLoadingFuturePages: isLoading,
fetchFutureNextPage: fetchNextPage,
Expand Down Expand Up @@ -117,15 +99,7 @@ export const useFetchPastEvents = (filters: SelectedCategories) => {
});

return {
pastEvents:
data?.pages.flat().flatMap((item) =>
item.eventsCalendarConnection.edges.map((edge) => ({
...edge.node,
startDateTime: new Date(edge.node.startDateTime),
endDateTime: new Date(edge.node.endDateTime),
category: formatCategory(edge.node.category),
}))
) || [],
pastEvents: getTrimmedEvent(data),
error,
isLoadingPastPages: isLoading,
fetchNextPastPage: fetchNextPage,
Expand Down

0 comments on commit 7cf6c7d

Please sign in to comment.