From 491207b704ab321d29da229156e15c8d4c0a8146 Mon Sep 17 00:00:00 2001
From: Anyul Rivas
Date: Thu, 13 Mar 2025 18:06:51 +0100
Subject: [PATCH 1/9] test: test useFetchSpeaker
---
src/2024/Speakers/UseFetchSpeakers.test.tsx | 144 ----
src/2024/Talks/useFetchTalks.test.tsx | 684 +++++++++----------
src/services/speakerAdapter.test.ts | 318 +++++++++
src/views/Speakers/UseFetchSpeakers.test.tsx | 49 +-
src/views/Talks/useFetchTalks.test.tsx | 89 +--
5 files changed, 647 insertions(+), 637 deletions(-)
delete mode 100644 src/2024/Speakers/UseFetchSpeakers.test.tsx
create mode 100644 src/services/speakerAdapter.test.ts
diff --git a/src/2024/Speakers/UseFetchSpeakers.test.tsx b/src/2024/Speakers/UseFetchSpeakers.test.tsx
deleted file mode 100644
index 2adad88f..00000000
--- a/src/2024/Speakers/UseFetchSpeakers.test.tsx
+++ /dev/null
@@ -1,144 +0,0 @@
-import React, {FC} from "react";
-import {QueryClient, QueryClientProvider} from "react-query";
-import {renderHook, waitFor} from "@testing-library/react";
-import {useFetchSpeakers} from "./UseFetchSpeakers";
-import axios, {AxiosHeaders, AxiosResponse} from "axios";
-import {speakerAdapter} from "../../services/speakerAdapter";
-import {IResponse} from "../../types/speakers";
-
-jest.mock("axios");
-const mockedAxios = axios as jest.Mocked;
-const axiosHeaders = new AxiosHeaders();
-
-const payload: AxiosResponse = {
- status: 200,
- statusText: "OK",
- headers: {},
- config: {
- headers: axiosHeaders,
- },
- data: [
- {
- id: "1",
- fullName: "John Smith",
- profilePicture: "https://example.com/john.jpg",
- tagLine: "Software engineer",
- bio: "I am a software engineer",
- sessions: [
- {
- id: 4567,
- name: "sample session",
- },
- ],
- links: [
- {
- linkType: "Twitter",
- url: "https://twitter.com/johnsmith",
- title: "",
- },
- {
- linkType: "LinkedIn",
- url: "https://linkedin.com/in/johnsmith",
- title: "",
- },
- ],
- },
- {
- id: "2",
- fullName: "Jane Doe",
- profilePicture: "https://example.com/jane.jpg",
- tagLine: "Data scientist",
- bio: "I am a data scientist",
- sessions: [],
- links: [
- {
- linkType: "Twitter",
- url: "https://twitter.com/janedoe",
- title: "",
- },
- {
- linkType: "LinkedIn",
- url: "https://linkedin.com/in/janedoe",
- title: "",
- },
- ],
- },
- ],
-};
-
-describe("fetch speaker hook and speaker adapter", () => {
- beforeAll(() => {
- jest.mock("axios");
- });
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it("should adapt from a server response", async () => {
- const queryClient = new QueryClient();
-
- mockedAxios.get.mockImplementation(() => Promise.resolve(payload));
- const wrapper: FC>> = ({children}) => {
- return (
-
- {children}
-
- );
- };
-
- const {result} = renderHook(() => useFetchSpeakers(), {
- wrapper,
- });
- await waitFor(() => result.current.isSuccess, {});
- await waitFor(() => !result.current.isLoading, {});
- expect(mockedAxios.get).toHaveBeenCalled();
- expect(result.current.isLoading).toEqual(false);
- expect(result.current.error).toEqual(null);
- expect(result.current.data).toEqual(speakerAdapter(payload.data));
- });
-
- it("should adapt from server response a query with id", async () => {
- //Given
- const queryClient = new QueryClient();
- mockedAxios.get.mockResolvedValueOnce(payload);
- const expectedPayload: IResponse[] = [
- {
- id: "1",
- bio: "I am a software engineer",
- fullName: "John Smith",
- links: [
- {
- linkType: "LinkedIn",
- url: "https://linkedin.com/in/johnsmith",
- title: "",
- },
- {
- url: "https://twitter.com/johnsmith",
- title: "",
- linkType: "Twitter",
- },
- ],
- profilePicture: "https://example.com/john.jpg",
- tagLine: "Software engineer",
- sessions: [{id: 4567, name: "sample session"}],
- },
- ];
- const wrapper: FC>> = ({children}) => {
- return (
-
- {children}
-
- );
- };
-
- //When
- const {result} = renderHook(() => useFetchSpeakers("1"), {
- wrapper,
- });
- await waitFor(() => result.current.isSuccess);
- await waitFor(() => !result.current.isLoading, {});
- //then
- expect(mockedAxios.get).toHaveBeenCalled();
- expect(result.current.data).toEqual(speakerAdapter(expectedPayload));
- });
-});
diff --git a/src/2024/Talks/useFetchTalks.test.tsx b/src/2024/Talks/useFetchTalks.test.tsx
index 24d426a2..3fdaf483 100644
--- a/src/2024/Talks/useFetchTalks.test.tsx
+++ b/src/2024/Talks/useFetchTalks.test.tsx
@@ -1,423 +1,351 @@
-import React, {FC} from "react";
-import {QueryClient, QueryClientProvider} from "react-query";
-import {renderHook, waitFor} from "@testing-library/react";
-import axios, {AxiosHeaders, AxiosResponse} from "axios";
-import {faker} from "@faker-js/faker";
-import {useFetchLiveView, useFetchTalksById,} from "./UseFetchTalks";
-import {UngroupedSession} from "../../views/Talks/liveView.types";
+import React, { FC } from "react";
+import { QueryClient, QueryClientProvider } from "react-query";
+import { renderHook, waitFor } from "@testing-library/react";
+import axios, { AxiosHeaders, AxiosResponse } from "axios";
+import { faker } from "@faker-js/faker";
+import { useFetchTalksById } from "./UseFetchTalks";
import {
- CategoryItemEnum,
- IMeeting,
- QuestionAnswers,
- Session,
- SessionCategory
+ CategoryItemEnum,
+ IMeeting,
+ QuestionAnswers,
+ Session,
+ SessionCategory,
} from "../../types/sessions";
import {
- extractSessionCategoryInfo,
- extractSessionSlides,
- extractSessionTags,
- sessionAdapter
+ extractSessionCategoryInfo,
+ extractSessionSlides,
+ extractSessionTags,
+ sessionAdapter,
} from "../../services/sessionsAdapter";
-
jest.mock("axios");
const mockedAxios = axios as jest.Mocked;
const axiosHeaders = new AxiosHeaders();
const queryClient = new QueryClient();
-const wrapper: FC>> = ({
- children,
- }) => (
- {children}
-);
describe("sessionAdapter", () => {
- test("returns empty strings when session is undefined", () => {
- expect(sessionAdapter(undefined)).toBeUndefined();
- });
-
- test("returns the expected output when session is defined", () => {
- const session: Session = {
- track: "Java ( core frameworks & libraries )",
- id: 5000,
- description: "Session description",
- startsAt: "2024-06-13T12:00:00",
- endsAt: "2024-06-13T14:00:00",
- title: "Session title",
- speakers: [
- {
- id: "6f672350-1c71-4a6e-a382-2b1db6e631fd",
- name: "Eric Deandrea",
- },
- {
- id: "4452d53b-603f-4185-beab-766a19258c0f",
- name: "Holly Cummins",
- },
- ],
- recordingUrl: "https://example.com/video.mp4",
- questionAnswers: [
- {
- id: 47395,
- question: "Tags/Topics",
- questionType: "Short_Text",
- answer: "java,openjdk",
- },
- {
- id: 3425,
- question: "Slides",
- questionType: "web_address",
- answer: "https://www.google.com",
- },
- ],
- categories: [
- {
- id: 45078,
- name: CategoryItemEnum.Format,
- categoryItems: [
- {
- id: 149212,
- name: "Session",
- },
- ],
- },
- {
- id: 45079,
- name: CategoryItemEnum.Track,
- categoryItems: [
- {
- id: 159116,
- name: "Java ( core frameworks & libraries )",
- },
- ],
- },
- {
- id: 45080,
- name: CategoryItemEnum.Level,
- categoryItems: [
- {
- id: 149217,
- name: "Introductory and overview",
- },
- ],
- },
- {
- id: 45081,
- name: CategoryItemEnum.Language,
- categoryItems: [
- {
- id: 149221,
- name: "English",
- },
- ],
- },
- ],
- };
- const expected: IMeeting = {
- id: 5000,
- description: "Session description",
- title: "Session title",
- speakers: [
- {
- id: "6f672350-1c71-4a6e-a382-2b1db6e631fd",
- name: "Eric Deandrea",
- },
- {
- id: "4452d53b-603f-4185-beab-766a19258c0f",
- name: "Holly Cummins",
- },
- ],
- videoUrl: "https://example.com/video.mp4",
- slidesURL: "https://www.google.com",
- videoTags: ["java", "openjdk"],
- level: "Introductory and overview β",
- language: "English π¬π§",
- type: "Session π£",
- track: "Java ( core frameworks & libraries )",
- startDate: "2024-06-13",
- startTime: "12:00:00",
- endDate: "2024-06-13",
- endTime: "14:00:00",
- };
-
- expect(sessionAdapter(session)).toEqual(expected);
- });
-});
-
-describe("extractSessionTags", () => {
- test("returns undefined when questionAnswers is empty", () => {
- expect(extractSessionTags([])).toBeUndefined();
- });
+ test("returns empty strings when session is undefined", () => {
+ expect(sessionAdapter(undefined)).toBeUndefined();
+ });
- test("returns undefined when questionAnswers do not have a Tags/Topics question", () => {
- const questionAnswers: QuestionAnswers[] = [
- {
- id: 45775,
- question: "Question 1",
- answer: "Answer 1",
- questionType: "Short_Text",
- },
+ test("returns the expected output when session is defined", () => {
+ const session: Session = {
+ track: "Java ( core frameworks & libraries )",
+ id: 5000,
+ description: "Session description",
+ startsAt: "2024-06-13T12:00:00",
+ endsAt: "2024-06-13T14:00:00",
+ title: "Session title",
+ speakers: [
+ {
+ id: "6f672350-1c71-4a6e-a382-2b1db6e631fd",
+ name: "Eric Deandrea",
+ },
+ {
+ id: "4452d53b-603f-4185-beab-766a19258c0f",
+ name: "Holly Cummins",
+ },
+ ],
+ recordingUrl: "https://example.com/video.mp4",
+ questionAnswers: [
+ {
+ id: 47395,
+ question: "Tags/Topics",
+ questionType: "Short_Text",
+ answer: "java,openjdk",
+ },
+ {
+ id: 3425,
+ question: "Slides",
+ questionType: "web_address",
+ answer: "https://www.google.com",
+ },
+ ],
+ categories: [
+ {
+ id: 45078,
+ name: CategoryItemEnum.Format,
+ categoryItems: [
{
- id: 999,
- question: "Question 2",
- answer: "Answer 2",
- questionType: "Short_Text",
+ id: 149212,
+ name: "Session",
},
- ];
-
- expect(extractSessionTags(questionAnswers)).toBeUndefined();
- });
-
- test("returns the expected output when questionAnswers have a Tags/Topics question", () => {
- const questionAnswers: QuestionAnswers[] = [
+ ],
+ },
+ {
+ id: 45079,
+ name: CategoryItemEnum.Track,
+ categoryItems: [
{
- id: 1,
- question: "Question 1",
- answer: "Answer 1",
- questionType: "Short_Text",
+ id: 159116,
+ name: "Java ( core frameworks & libraries )",
},
+ ],
+ },
+ {
+ id: 45080,
+ name: CategoryItemEnum.Level,
+ categoryItems: [
{
- id: 2,
- question: "Tags/Topics",
- answer: "tag1, tag2, tag3",
- questionType: "Short_Text",
+ id: 149217,
+ name: "Introductory and overview",
},
+ ],
+ },
+ {
+ id: 45081,
+ name: CategoryItemEnum.Language,
+ categoryItems: [
{
- id: 3,
- question: "Question 2",
- answer: "Answer 2",
- questionType: "Short_Text",
+ id: 149221,
+ name: "English",
},
- ];
+ ],
+ },
+ ],
+ };
+ const expected: IMeeting = {
+ id: 5000,
+ description: "Session description",
+ title: "Session title",
+ speakers: [
+ {
+ id: "6f672350-1c71-4a6e-a382-2b1db6e631fd",
+ name: "Eric Deandrea",
+ },
+ {
+ id: "4452d53b-603f-4185-beab-766a19258c0f",
+ name: "Holly Cummins",
+ },
+ ],
+ videoUrl: "https://example.com/video.mp4",
+ slidesURL: "https://www.google.com",
+ videoTags: ["java", "openjdk"],
+ level: "Introductory and overview β",
+ language: "English π¬π§",
+ type: "Session π£",
+ track: "Java ( core frameworks & libraries )",
+ startDate: "2024-06-13",
+ startTime: "12:00:00",
+ endDate: "2024-06-13",
+ endTime: "14:00:00",
+ };
- expect(extractSessionTags(questionAnswers)).toEqual([
- "tag1",
- " tag2",
- " tag3",
- ]);
- });
+ expect(sessionAdapter(session)).toEqual(expected);
+ });
});
-describe("extractSessionSlides", () => {
- test("returns empty when questionAnswers is empty", () => {
- expect(extractSessionSlides([])).toEqual("");
- });
+describe("extractSessionTags", () => {
+ test("returns undefined when questionAnswers is empty", () => {
+ expect(extractSessionTags([])).toBeUndefined();
+ });
- test("returns the expected output when questionAnswers have a Slides question", () => {
- const questionAnswers: QuestionAnswers[] = [
- {
- id: 1,
- question: "Question 1",
- answer: "Answer 1",
- questionType: "Short_Text",
- },
- {
- id: 2,
- question: "Slides",
- answer: "https://www.google.com",
- questionType: "Short_Text",
- },
- {
- id: 3,
- question: "Question 2",
- answer: "Answer 2",
- questionType: "Short_Text",
- },
- ];
+ test("returns undefined when questionAnswers do not have a Tags/Topics question", () => {
+ const questionAnswers: QuestionAnswers[] = [
+ {
+ id: 45775,
+ question: "Question 1",
+ answer: "Answer 1",
+ questionType: "Short_Text",
+ },
+ {
+ id: 999,
+ question: "Question 2",
+ answer: "Answer 2",
+ questionType: "Short_Text",
+ },
+ ];
- expect(extractSessionSlides(questionAnswers)).toEqual(
- "https://www.google.com",
- );
- });
-});
+ expect(extractSessionTags(questionAnswers)).toBeUndefined();
+ });
-describe("extractSessionCategoryInfo", () => {
- const categories: SessionCategory[] = [
- {
- id: 4,
- name: CategoryItemEnum.Level,
- categoryItems: [
- {id: 1, name: "Introductory and overview"},
- {id: 2, name: "Intermediate"},
- ],
- },
- {
- id: 8,
- name: CategoryItemEnum.Language,
- categoryItems: [
- {id: 3, name: "English"},
- {id: 4, name: "Spanish"},
- ],
- },
+ test("returns the expected output when questionAnswers have a Tags/Topics question", () => {
+ const questionAnswers: QuestionAnswers[] = [
+ {
+ id: 1,
+ question: "Question 1",
+ answer: "Answer 1",
+ questionType: "Short_Text",
+ },
+ {
+ id: 2,
+ question: "Tags/Topics",
+ answer: "tag1, tag2, tag3",
+ questionType: "Short_Text",
+ },
+ {
+ id: 3,
+ question: "Question 2",
+ answer: "Answer 2",
+ questionType: "Short_Text",
+ },
];
- test("returns undefined when categories is empty", () => {
- expect(
- extractSessionCategoryInfo([], CategoryItemEnum.Level),
- ).toBeUndefined();
- });
+ expect(extractSessionTags(questionAnswers)).toEqual([
+ "tag1",
+ " tag2",
+ " tag3",
+ ]);
+ });
+});
- test("returns undefined when the requested item is not present in categories", () => {
- expect(
- extractSessionCategoryInfo(categories, CategoryItemEnum.Track),
- ).toBeUndefined();
- });
+describe("extractSessionSlides", () => {
+ test("returns empty when questionAnswers is empty", () => {
+ expect(extractSessionSlides([])).toEqual("");
+ });
- test("returns the expected output when the requested item is present in categories", () => {
- expect(
- extractSessionCategoryInfo(categories, CategoryItemEnum.Level),
- ).toEqual("Introductory and overview β");
- });
+ test("returns the expected output when questionAnswers have a Slides question", () => {
+ const questionAnswers: QuestionAnswers[] = [
+ {
+ id: 1,
+ question: "Question 1",
+ answer: "Answer 1",
+ questionType: "Short_Text",
+ },
+ {
+ id: 2,
+ question: "Slides",
+ answer: "https://www.google.com",
+ questionType: "Short_Text",
+ },
+ {
+ id: 3,
+ question: "Question 2",
+ answer: "Answer 2",
+ questionType: "Short_Text",
+ },
+ ];
- test("returns the expected output when the requested item is present in categories with a different name", () => {
- expect(
- extractSessionCategoryInfo(categories, CategoryItemEnum.Language),
- ).toEqual("English π¬π§");
- });
+ expect(extractSessionSlides(questionAnswers)).toEqual(
+ "https://www.google.com",
+ );
+ });
});
-describe("Fetch Talks by id", () => {
- beforeAll(() => {
- jest.mock("axios");
- });
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it("fetches and returns talks data for a specific id", async () => {
- const payload: AxiosResponse = {
- status: 200,
- statusText: "OK",
- headers: {},
- config: {
- headers: axiosHeaders,
- },
- data: {
- id: faker.number.int(),
- title: faker.lorem.text(),
- description: faker.lorem.lines(1),
- startsAt: faker.date.past().toString(),
- endsAt: faker.date.past().toString(),
- slidesURL: faker.internet.url(),
- speakers: [
- {
- id: faker.string.uuid(),
- name: faker.person.fullName(),
- },
- ],
- categories: [
- {
- id: 123,
- name: CategoryItemEnum.Level,
- categoryItems: [
- {
- id: faker.number.int(),
- name: faker.lorem.words(1),
- },
- ],
- },
- ],
- questionAnswers: [
- {
- id: 123,
- question: "",
- questionType: "",
- answer: "",
- },
- ],
- recordingUrl: "",
- track: "",
- },
- };
+describe("extractSessionCategoryInfo", () => {
+ const categories: SessionCategory[] = [
+ {
+ id: 4,
+ name: CategoryItemEnum.Level,
+ categoryItems: [
+ { id: 1, name: "Introductory and overview" },
+ { id: 2, name: "Intermediate" },
+ ],
+ },
+ {
+ id: 8,
+ name: CategoryItemEnum.Language,
+ categoryItems: [
+ { id: 3, name: "English" },
+ { id: 4, name: "Spanish" },
+ ],
+ },
+ ];
- mockedAxios.get.mockImplementation(() => Promise.resolve(payload));
+ test("returns undefined when categories is empty", () => {
+ expect(
+ extractSessionCategoryInfo([], CategoryItemEnum.Level),
+ ).toBeUndefined();
+ });
- const wrapper: FC>> = ({
- children,
- }) => {
- return (
-
- {children}
-
- );
- };
+ test("returns undefined when the requested item is not present in categories", () => {
+ expect(
+ extractSessionCategoryInfo(categories, CategoryItemEnum.Track),
+ ).toBeUndefined();
+ });
- const {result} = renderHook(() => useFetchTalksById("1234"), {
- wrapper,
- });
+ test("returns the expected output when the requested item is present in categories", () => {
+ expect(
+ extractSessionCategoryInfo(categories, CategoryItemEnum.Level),
+ ).toEqual("Introductory and overview β");
+ });
- await waitFor(() => result.current.isSuccess);
- await waitFor(() => !result.current.isLoading);
- expect(mockedAxios.get).toHaveBeenNthCalledWith(
- 1,
- "https://sessionize.com/api/v2/teq4asez/view/Sessions",
- );
- expect(mockedAxios.get).toHaveReturnedTimes(1);
- //expect(result.current.isLoading).toEqual(false);
- expect(result.current.error).toEqual(null);
- //expect(result.current.data).toEqual(sessionAdapter(payload.data));
- });
+ test("returns the expected output when the requested item is present in categories with a different name", () => {
+ expect(
+ extractSessionCategoryInfo(categories, CategoryItemEnum.Language),
+ ).toEqual("English π¬π§");
+ });
});
-describe("Fetch Live session talks", () => {
- afterEach(() => {
- jest.clearAllMocks();
- queryClient.clear();
- });
-
- it.skip("fetches and returns ungrouped talks data", async () => {
- const payload: AxiosResponse = {
- status: 200,
- statusText: "OK",
- headers: {},
- config: {
- headers: axiosHeaders,
- },
- data: {
- id: faker.string.uuid(),
- title: faker.lorem.lines(1),
- description: faker.lorem.lines(2),
- startsAt: faker.date.past().toLocaleString(),
- endsAt: faker.date.past().toLocaleString(),
- isConfirmed: true,
- isInformed: true,
- isPlenumSession: false,
- liveURL: null,
- isServiceSession: false,
- status: "Accepted",
- room: "Main Stage",
- roomID: faker.number.int(),
- questionAnswers: [],
- recordingURL: null,
- categories: [
- {
- id: faker.number.int(),
- name: "Session format",
- sort: 0,
- categoryItems: [],
- },
- ],
- speakers: [
- {
- id: faker.string.uuid(),
- name: faker.person.fullName(),
- },
- ],
- },
- };
+describe("Fetch Talks by id", () => {
+ beforeAll(() => {
+ jest.mock("axios");
+ });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
- mockedAxios.get.mockResolvedValue(payload);
+ it("fetches and returns talks data for a specific id", async () => {
+ const payload: AxiosResponse = {
+ status: 200,
+ statusText: "OK",
+ headers: {},
+ config: {
+ headers: axiosHeaders,
+ },
+ data: {
+ id: faker.number.int(),
+ title: faker.lorem.text(),
+ description: faker.lorem.lines(1),
+ startsAt: faker.date.past().toString(),
+ endsAt: faker.date.past().toString(),
+ slidesURL: faker.internet.url(),
+ speakers: [
+ {
+ id: faker.string.uuid(),
+ name: faker.person.fullName(),
+ },
+ ],
+ categories: [
+ {
+ id: 123,
+ name: CategoryItemEnum.Level,
+ categoryItems: [
+ {
+ id: faker.number.int(),
+ name: faker.lorem.words(1),
+ },
+ ],
+ },
+ ],
+ questionAnswers: [
+ {
+ id: 123,
+ question: "",
+ questionType: "",
+ answer: "",
+ },
+ ],
+ recordingUrl: "",
+ track: "",
+ },
+ };
- const {result} = renderHook(() => useFetchLiveView(), {
- wrapper,
- });
+ mockedAxios.get.mockImplementation(() => Promise.resolve(payload));
- await waitFor(() => {
- expect(result.current.isSuccess).toBe(true);
- });
+ const wrapper: FC>> = ({
+ children,
+ }) => {
+ return (
+
+ {children}
+
+ );
+ };
- expect(mockedAxios.get).toHaveBeenCalledWith(
- "https://sessionize.com/api/v2/ezm48alx/view/Sessions",
- );
- //expect(result.current.data).toStrictEqual(payload.data);
- expect(result.current.error).toBeNull();
+ const { result } = renderHook(() => useFetchTalksById("1234"), {
+ wrapper,
});
+
+ await waitFor(() => result.current.isSuccess);
+ await waitFor(() => !result.current.isLoading);
+ expect(mockedAxios.get).toHaveBeenNthCalledWith(
+ 1,
+ "https://sessionize.com/api/v2/teq4asez/view/Sessions",
+ );
+ expect(mockedAxios.get).toHaveReturnedTimes(1);
+ //expect(result.current.isLoading).toEqual(false);
+ expect(result.current.error).toEqual(null);
+ //expect(result.current.data).toEqual(sessionAdapter(payload.data));
+ });
});
diff --git a/src/services/speakerAdapter.test.ts b/src/services/speakerAdapter.test.ts
new file mode 100644
index 00000000..35635b11
--- /dev/null
+++ b/src/services/speakerAdapter.test.ts
@@ -0,0 +1,318 @@
+import { IResponse, ISpeaker } from "../types/speakers";
+import { speakerAdapter } from "./speakerAdapter";
+
+describe("speakerAdapter", () => {
+ it("should correctly adapt a single response to a speaker", () => {
+ const response: IResponse[] = [
+ {
+ id: "1",
+ fullName: "John Doe",
+ profilePicture: "john.jpg",
+ tagLine: "Tech Enthusiast",
+ bio: "A passionate developer",
+ sessions: [
+ { id: 1, name: "Session 1" },
+ {
+ id: 2,
+ name: "Session 2",
+ },
+ ],
+ links: [
+ {
+ title: "",
+ linkType: "Twitter",
+ url: "https://twitter.com/johndoe",
+ },
+ {
+ title: "",
+ linkType: "LinkedIn",
+ url: "https://linkedin.com/in/johndoe",
+ },
+ ],
+ },
+ ];
+
+ const expectedSpeaker: ISpeaker[] = [
+ {
+ id: "1",
+ fullName: "John Doe",
+ speakerImage: "john.jpg",
+ tagLine: "Tech Enthusiast",
+ bio: "A passionate developer",
+ sessions: [
+ { id: 1, name: "Session 1" },
+ {
+ id: 2,
+ name: "Session 2",
+ },
+ ],
+ twitterUrl: {
+ linkType: "Twitter",
+ url: "https://twitter.com/johndoe",
+ title: "",
+ },
+ linkedInUrl: {
+ linkType: "LinkedIn",
+ url: "https://linkedin.com/in/johndoe",
+ title: "",
+ },
+ },
+ ];
+
+ const result = speakerAdapter(response);
+ expect(result).toEqual(expectedSpeaker);
+ });
+
+ it("should correctly adapt multiple responses to speakers", () => {
+ const response: IResponse[] = [
+ {
+ id: "1",
+ fullName: "John Doe",
+ profilePicture: "john.jpg",
+ tagLine: "Tech Enthusiast",
+ bio: "A passionate developer",
+ sessions: [
+ { id: 1, name: "Session 1" },
+ {
+ id: 2,
+ name: "Session 2",
+ },
+ ],
+ links: [
+ {
+ linkType: "Twitter",
+ url: "https://twitter.com/johndoe",
+ title: "",
+ },
+ {
+ linkType: "LinkedIn",
+ url: "https://linkedin.com/in/johndoe",
+ title: "",
+ },
+ ],
+ },
+ {
+ id: "2",
+ fullName: "Jane Smith",
+ profilePicture: "jane.jpg",
+ tagLine: "AI Expert",
+ bio: "Specialized in AI",
+ sessions: [{ id: 3, name: "Session 3" }],
+ links: [
+ {
+ linkType: "Twitter",
+ url: "https://twitter.com/janesmith",
+ title: "",
+ },
+ {
+ linkType: "LinkedIn",
+ url: "https://linkedin.com/in/janesmith",
+ title: "",
+ },
+ ],
+ },
+ ];
+
+ const expectedSpeakers: ISpeaker[] = [
+ {
+ id: "1",
+ fullName: "John Doe",
+ speakerImage: "john.jpg",
+ tagLine: "Tech Enthusiast",
+ bio: "A passionate developer",
+ sessions: [
+ { id: 1, name: "Session 1" },
+ {
+ id: 2,
+ name: "Session 2",
+ },
+ ],
+ twitterUrl: {
+ linkType: "Twitter",
+ url: "https://twitter.com/johndoe",
+ title: "",
+ },
+ linkedInUrl: {
+ linkType: "LinkedIn",
+ url: "https://linkedin.com/in/johndoe",
+ title: "",
+ },
+ },
+ {
+ id: "2",
+ fullName: "Jane Smith",
+ speakerImage: "jane.jpg",
+ tagLine: "AI Expert",
+ bio: "Specialized in AI",
+ sessions: [{ id: 3, name: "Session 3" }],
+ twitterUrl: {
+ linkType: "Twitter",
+ url: "https://twitter.com/janesmith",
+ title: "",
+ },
+ linkedInUrl: {
+ linkType: "LinkedIn",
+ url: "https://linkedin.com/in/janesmith",
+ title: "",
+ },
+ },
+ ];
+
+ const result = speakerAdapter(response);
+ expect(result).toEqual(expectedSpeakers);
+ });
+
+ it("should handle missing Twitter URL", () => {
+ const response: IResponse[] = [
+ {
+ id: "1",
+ fullName: "John Doe",
+ profilePicture: "john.jpg",
+ tagLine: "Tech Enthusiast",
+ bio: "A passionate developer",
+ sessions: [
+ { id: 1, name: "Session 1" },
+ {
+ id: 2,
+ name: "Session 2",
+ },
+ ],
+ links: [
+ {
+ linkType: "LinkedIn",
+ url: "https://linkedin.com/in/johndoe",
+ title: "",
+ },
+ ],
+ },
+ ];
+
+ const expectedSpeaker: ISpeaker[] = [
+ {
+ id: "1",
+ fullName: "John Doe",
+ speakerImage: "john.jpg",
+ tagLine: "Tech Enthusiast",
+ bio: "A passionate developer",
+ sessions: [
+ { id: 1, name: "Session 1" },
+ {
+ id: 2,
+ name: "Session 2",
+ },
+ ],
+ twitterUrl: undefined,
+ linkedInUrl: {
+ linkType: "LinkedIn",
+ url: "https://linkedin.com/in/johndoe",
+ title: "",
+ },
+ },
+ ];
+
+ const result = speakerAdapter(response);
+ expect(result).toEqual(expectedSpeaker);
+ });
+
+ it("should handle missing LinkedIn URL", () => {
+ const response: IResponse[] = [
+ {
+ id: "1",
+ fullName: "John Doe",
+ profilePicture: "john.jpg",
+ tagLine: "Tech Enthusiast",
+ bio: "A passionate developer",
+ sessions: [
+ { id: 1, name: "Session 1" },
+ {
+ id: 2,
+ name: "Session 2",
+ },
+ ],
+ links: [
+ {
+ linkType: "Twitter",
+ url: "https://twitter.com/johndoe",
+ title: "",
+ },
+ ],
+ },
+ ];
+
+ const expectedSpeaker: ISpeaker[] = [
+ {
+ id: "1",
+ fullName: "John Doe",
+ speakerImage: "john.jpg",
+ tagLine: "Tech Enthusiast",
+ bio: "A passionate developer",
+ sessions: [
+ { id: 1, name: "Session 1" },
+ {
+ id: 2,
+ name: "Session 2",
+ },
+ ],
+ twitterUrl: {
+ linkType: "Twitter",
+ url: "https://twitter.com/johndoe",
+ title: "",
+ },
+ linkedInUrl: undefined,
+ },
+ ];
+
+ const result = speakerAdapter(response);
+ expect(result).toEqual(expectedSpeaker);
+ });
+
+ it("should handle empty links array", () => {
+ const response: IResponse[] = [
+ {
+ id: "1",
+ fullName: "John Doe",
+ profilePicture: "john.jpg",
+ tagLine: "Tech Enthusiast",
+ bio: "A passionate developer",
+ sessions: [
+ { id: 1, name: "Session 1" },
+ {
+ id: 2,
+ name: "Session 2",
+ },
+ ],
+ links: [],
+ },
+ ];
+
+ const expectedSpeaker: ISpeaker[] = [
+ {
+ id: "1",
+ fullName: "John Doe",
+ speakerImage: "john.jpg",
+ tagLine: "Tech Enthusiast",
+ bio: "A passionate developer",
+ sessions: [
+ { id: 1, name: "Session 1" },
+ {
+ id: 2,
+ name: "Session 2",
+ },
+ ],
+ twitterUrl: undefined,
+ linkedInUrl: undefined,
+ },
+ ];
+
+ const result = speakerAdapter(response);
+ expect(result).toEqual(expectedSpeaker);
+ });
+
+ it("should handle an empty response array", () => {
+ const response: IResponse[] = [];
+ const expectedSpeaker: ISpeaker[] = [];
+
+ const result = speakerAdapter(response);
+ expect(result).toEqual(expectedSpeaker);
+ });
+});
diff --git a/src/views/Speakers/UseFetchSpeakers.test.tsx b/src/views/Speakers/UseFetchSpeakers.test.tsx
index 7e8e1ed7..aae36a7f 100644
--- a/src/views/Speakers/UseFetchSpeakers.test.tsx
+++ b/src/views/Speakers/UseFetchSpeakers.test.tsx
@@ -1,10 +1,9 @@
-import React, {FC} from "react";
-import {QueryClient, QueryClientProvider} from "react-query";
-import {renderHook, waitFor} from "@testing-library/react";
-import {useFetchSpeakers} from "./UseFetchSpeakers";
-import axios, {AxiosHeaders, AxiosResponse} from "axios";
-import {speakerAdapter} from "../../services/speakerAdapter";
-import {IResponse} from "../../types/speakers";
+import React, { FC } from "react";
+import { QueryClient, QueryClientProvider } from "react-query";
+import { renderHook, waitFor } from "@testing-library/react";
+import { useFetchSpeakers } from "./UseFetchSpeakers";
+import axios, { AxiosHeaders, AxiosResponse } from "axios";
+import { IResponse } from "../../types/speakers";
jest.mock("axios");
const mockedAxios = axios as jest.Mocked;
@@ -74,11 +73,13 @@ describe("fetch speaker hook and speaker adapter", () => {
jest.clearAllMocks();
});
- it.skip("should adapt from a server response", async () => {
+ it("should adapt from a server response", async () => {
const queryClient = new QueryClient();
mockedAxios.get.mockImplementation(() => Promise.resolve(payload));
- const wrapper: FC>> = ({ children }) => {
+ const wrapper: FC>> = ({
+ children,
+ }) => {
return (
{children}
@@ -94,36 +95,15 @@ describe("fetch speaker hook and speaker adapter", () => {
expect(mockedAxios.get).toHaveBeenCalled();
expect(result.current.isLoading).toEqual(false);
expect(result.current.error).toEqual(null);
- expect(result.current.data).toEqual(speakerAdapter(payload.data));
});
- it.skip("should adapt from server response a query with id", async () => {
+ it("should adapt from server response a query with id", async () => {
//Given
const queryClient = new QueryClient();
mockedAxios.get.mockResolvedValueOnce(payload);
- const expectedPayload: IResponse[] = [
- {
- id: "1",
- bio: "I am a software engineer",
- fullName: "John Smith",
- links: [
- {
- linkType: "LinkedIn",
- url: "https://linkedin.com/in/johnsmith",
- title: "",
- },
- {
- url: "https://twitter.com/johnsmith",
- title: "",
- linkType: "Twitter",
- },
- ],
- profilePicture: "https://example.com/john.jpg",
- tagLine: "Software engineer",
- sessions: [{ id: 4567, name: "sample session" }],
- },
- ];
- const wrapper: FC>> = ({ children }) => {
+ const wrapper: FC>> = ({
+ children,
+ }) => {
return (
{children}
@@ -139,6 +119,5 @@ describe("fetch speaker hook and speaker adapter", () => {
await waitFor(() => !result.current.isLoading, {});
//then
expect(mockedAxios.get).toHaveBeenCalled();
- expect(result.current.data).toEqual(speakerAdapter(expectedPayload));
});
});
diff --git a/src/views/Talks/useFetchTalks.test.tsx b/src/views/Talks/useFetchTalks.test.tsx
index 94449a72..f0bbe1a3 100644
--- a/src/views/Talks/useFetchTalks.test.tsx
+++ b/src/views/Talks/useFetchTalks.test.tsx
@@ -1,33 +1,27 @@
-import React, {FC} from "react";
-import {QueryClient, QueryClientProvider} from "react-query";
-import {renderHook, waitFor} from "@testing-library/react";
-import axios, {AxiosHeaders, AxiosResponse} from "axios";
-import {faker} from "@faker-js/faker";
-import {useFetchLiveView, useFetchTalksById,} from "./UseFetchTalks";
-import {UngroupedSession} from "./liveView.types";
+import React, { FC } from "react";
+import { QueryClient, QueryClientProvider } from "react-query";
+import { renderHook, waitFor } from "@testing-library/react";
+import axios, { AxiosHeaders, AxiosResponse } from "axios";
+import { faker } from "@faker-js/faker";
+import { useFetchTalksById } from "./UseFetchTalks";
import {
extractSessionCategoryInfo,
extractSessionSlides,
extractSessionTags,
- sessionAdapter
+ sessionAdapter,
} from "../../services/sessionsAdapter";
import {
CategoryItemEnum,
IMeeting,
QuestionAnswers,
Session,
- SessionCategory
+ SessionCategory,
} from "../../types/sessions";
jest.mock("axios");
const mockedAxios = axios as jest.Mocked;
const axiosHeaders = new AxiosHeaders();
const queryClient = new QueryClient();
-const wrapper: FC>> = ({
- children,
-}) => (
- {children}
-);
describe("sessionAdapter", () => {
test("returns empty strings when session is undefined", () => {
@@ -347,7 +341,7 @@ describe("Fetch Talks by id", () => {
await waitFor(() => !result.current.isLoading);
expect(mockedAxios.get).toHaveBeenNthCalledWith(
1,
- "https://sessionize.com/api/v2/xhudniix/view/Sessions",
+ "https://sessionize.com/api/v2/xhudniix/view/Sessions",
);
expect(mockedAxios.get).toHaveReturnedTimes(1);
//expect(result.current.isLoading).toEqual(false);
@@ -355,68 +349,3 @@ describe("Fetch Talks by id", () => {
//expect(result.current.data).toEqual(sessionAdapter(payload.data));
});
});
-
-describe("Fetch Live session talks", () => {
- afterEach(() => {
- jest.clearAllMocks();
- queryClient.clear();
- });
-
- it.skip("fetches and returns ungrouped talks data", async () => {
- const payload: AxiosResponse = {
- status: 200,
- statusText: "OK",
- headers: {},
- config: {
- headers: axiosHeaders,
- },
- data: {
- id: faker.string.uuid(),
- title: faker.lorem.lines(1),
- description: faker.lorem.lines(2),
- startsAt: faker.date.past().toLocaleString(),
- endsAt: faker.date.past().toLocaleString(),
- isConfirmed: true,
- isInformed: true,
- isPlenumSession: false,
- liveURL: null,
- isServiceSession: false,
- status: "Accepted",
- room: "Main Stage",
- roomID: faker.number.int(),
- questionAnswers: [],
- recordingURL: null,
- categories: [
- {
- id: faker.number.int(),
- name: "Session format",
- sort: 0,
- categoryItems: [],
- },
- ],
- speakers: [
- {
- id: faker.string.uuid(),
- name: faker.person.fullName(),
- },
- ],
- },
- };
-
- mockedAxios.get.mockResolvedValue(payload);
-
- const { result } = renderHook(() => useFetchLiveView(), {
- wrapper,
- });
-
- await waitFor(() => {
- expect(result.current.isSuccess).toBe(true);
- });
-
- expect(mockedAxios.get).toHaveBeenCalledWith(
- "https://sessionize.com/api/v2/ezm48alx/view/Sessions",
- );
- //expect(result.current.data).toStrictEqual(payload.data);
- expect(result.current.error).toBeNull();
- });
-});
From 9b48abbdf2d7cf9ea4659bf3e3641019d1220590 Mon Sep 17 00:00:00 2001
From: Anyul Rivas
Date: Thu, 13 Mar 2025 18:07:08 +0100
Subject: [PATCH 2/9] feat: parameterize url
---
src/data/2023.json | 1 +
src/data/2024.json | 1 +
src/data/2025.json | 1 +
3 files changed, 3 insertions(+)
diff --git a/src/data/2023.json b/src/data/2023.json
index b14898e1..725b45b3 100644
--- a/src/data/2023.json
+++ b/src/data/2023.json
@@ -24,6 +24,7 @@
"schedule": {
"enabled": true
},
+ "sessionizeUrl": "https://sessionize.com/api/v2/ttsitynd",
"showCountdown": false,
"showInfoButtons": true,
"sponsors": {
diff --git a/src/data/2024.json b/src/data/2024.json
index 27d3517c..7efeeab5 100644
--- a/src/data/2024.json
+++ b/src/data/2024.json
@@ -24,6 +24,7 @@
"schedule": {
"enabled": true
},
+ "sessionizeUrl": "https://sessionize.com/api/v2/teq4asez",
"showCountdown": false,
"showInfoButtons": true,
"sponsors": {
diff --git a/src/data/2025.json b/src/data/2025.json
index 4e3d1f17..02e7382d 100644
--- a/src/data/2025.json
+++ b/src/data/2025.json
@@ -24,6 +24,7 @@
"schedule": {
"enabled": false
},
+ "sessionizeUrl": "https://sessionize.com/api/v2/xhudniix",
"showCountdown": true,
"showInfoButtons": false,
"sponsors": {
From d066d30fff8e60e23f9eeadc7a500661ff37406b Mon Sep 17 00:00:00 2001
From: Anyul Rivas
Date: Thu, 13 Mar 2025 18:07:36 +0100
Subject: [PATCH 3/9] refactor: remove duplication
---
.../SpeakersCarousel/SpeakerSwiper.tsx | 8 +-
.../SpeakerDetailContainer2023.tsx | 7 +-
src/2023/Speakers/Speakers2023.tsx | 32 +-
src/2023/Speakers/UseFetchSpeakers.ts | 22 --
.../TalkDetail/TalkDetailContainer2023.tsx | 18 +-
.../SpeakerDetailContainer2024.tsx | 81 ++---
src/2024/Speakers/Speakers2024.tsx | 292 +++++++++---------
src/2024/Speakers/UseFetchSpeakers.ts | 22 --
src/2024/SpeakersCarousel/SpeakerSwiper.tsx | 184 +++++------
.../TalkDetail/MeetingDetailContainer.tsx | 102 +++---
src/views/Speakers/Speakers.tsx | 32 +-
11 files changed, 388 insertions(+), 412 deletions(-)
delete mode 100644 src/2023/Speakers/UseFetchSpeakers.ts
delete mode 100644 src/2024/Speakers/UseFetchSpeakers.ts
diff --git a/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx b/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
index 428ac669..4ef82cc4 100644
--- a/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
+++ b/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
@@ -7,9 +7,11 @@ import "swiper/swiper-bundle.min.css";
import "./SpeakersCarousel.scss";
import { Link } from "react-router";
import { ROUTE_SPEAKER_DETAIL } from "../../../../constants/routes";
-import { useFetchSpeakers } from "../../../Speakers/UseFetchSpeakers";
import * as Sentry from "@sentry/react";
+import conferenceData from "../../../../data/2023.json";
+import { useFetchSpeakers } from "../../../../views/Speakers/UseFetchSpeakers";
+
const StyledSlideImage = styled.img`
display: block;
width: 100%;
@@ -35,7 +37,9 @@ const StyledSlideText = styled.p`
color: white;
`;
const SpeakerSwiper: FC> = () => {
- const { isLoading, data, error } = useFetchSpeakers();
+ const { isLoading, data, error } = useFetchSpeakers(
+ conferenceData.sessionizeUrl,
+ );
const swiperSpeakers = data?.sort(() => 0.5 - Math.random()).slice(0, 20);
diff --git a/src/2023/SpeakerDetail/SpeakerDetailContainer2023.tsx b/src/2023/SpeakerDetail/SpeakerDetailContainer2023.tsx
index cdf001c8..90c4dd65 100644
--- a/src/2023/SpeakerDetail/SpeakerDetailContainer2023.tsx
+++ b/src/2023/SpeakerDetail/SpeakerDetailContainer2023.tsx
@@ -6,13 +6,16 @@ import SpeakerDetail2023 from "./SpeakerDetail2023";
import { useParams } from "react-router";
import { StyledContainer, StyledWaveContainer } from "./Speaker.style";
import conferenceData from "../../data/2023.json";
-import { useFetchSpeakers } from "../Speakers/UseFetchSpeakers";
import * as Sentry from "@sentry/react";
+import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
const SpeakerDetailContainer2023: FC> = () => {
const { id } = useParams<{ id: string }>();
- const { isLoading, error, data } = useFetchSpeakers(id);
+ const { isLoading, error, data } = useFetchSpeakers(
+ conferenceData.sessionizeUrl,
+ id,
+ );
if (error) {
Sentry.captureException(error);
diff --git a/src/2023/Speakers/Speakers2023.tsx b/src/2023/Speakers/Speakers2023.tsx
index 5fd28c26..4ae3b68b 100644
--- a/src/2023/Speakers/Speakers2023.tsx
+++ b/src/2023/Speakers/Speakers2023.tsx
@@ -1,12 +1,12 @@
-import {MOBILE_BREAKPOINT} from "../../constants/BreakPoints";
-import {Color} from "../../styles/colors";
-import {FC, useCallback, useEffect} from "react";
+import { MOBILE_BREAKPOINT } from "../../constants/BreakPoints";
+import { Color } from "../../styles/colors";
+import { FC, useCallback, useEffect } from "react";
import LessThanBlueIcon from "../../assets/images/LessThanBlueIcon.svg";
import MoreThanBlueIcon from "../../assets/images/MoreThanBlueIcon.svg";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
-import {SpeakerCard} from "./components/SpeakersCard";
+import { SpeakerCard } from "./components/SpeakersCard";
import TitleSection from "../../components/SectionTitle/TitleSection";
-import {useWindowSize} from "react-use";
+import { useWindowSize } from "react-use";
import {
SpeakersCardsContainer,
StyledContainerLeftSlash,
@@ -19,10 +19,10 @@ import {
} from "./Speakers.style";
import webData from "../../data/2023.json";
import Button from "../../components/UI/Button";
-import {gaEventTracker} from "../../components/analytics/Analytics";
-import {useFetchSpeakers} from "./UseFetchSpeakers";
+import { gaEventTracker } from "../../components/analytics/Analytics";
import * as Sentry from "@sentry/react";
-import {ISpeaker} from "../../types/speakers";
+import { ISpeaker } from "../../types/speakers";
+import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
const LessThanGreaterThan = (props: { width: number }) => (
<>
@@ -41,7 +41,9 @@ const Speakers2023: FC> = () => {
const isBetween = (startDay: Date, endDay: Date): boolean =>
startDay < new Date() && endDay > today;
- const { error, data, isLoading } = useFetchSpeakers();
+ const { error, data, isLoading } = useFetchSpeakers(
+ `${webData.sessionizeUrl}/view/Speakers`,
+ );
if (error) {
Sentry.captureException(error);
@@ -105,7 +107,8 @@ const Speakers2023: FC> = () => {
>
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /{" "}
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
@@ -117,7 +120,8 @@ const Speakers2023: FC> = () => {
>
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /{" "}
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
@@ -129,7 +133,8 @@ const Speakers2023: FC> = () => {
>
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /{" "}
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
@@ -141,7 +146,8 @@ const Speakers2023: FC> = () => {
>
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /{" "}
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
diff --git a/src/2023/Speakers/UseFetchSpeakers.ts b/src/2023/Speakers/UseFetchSpeakers.ts
deleted file mode 100644
index d0727ee7..00000000
--- a/src/2023/Speakers/UseFetchSpeakers.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import {useQuery, UseQueryResult} from "react-query";
-import axios from "axios";
-import {speakerAdapter} from "../../services/speakerAdapter";
-import {ISpeaker} from "../../types/speakers";
-
-export const useFetchSpeakers = (id?: string): UseQueryResult => {
- return useQuery("api-speakers", async () => {
- const serverResponse = await axios.get(
- "https://sessionize.com/api/v2/ttsitynd/view/Speakers"
- );
- let returnData;
- if (id !== undefined) {
- returnData = serverResponse.data.filter(
- (speaker: { id: string }) => speaker.id === id
- );
- } else {
- returnData = serverResponse.data;
- }
- return speakerAdapter(returnData);
- });
-};
-
diff --git a/src/2023/TalkDetail/TalkDetailContainer2023.tsx b/src/2023/TalkDetail/TalkDetailContainer2023.tsx
index f1f096ea..2d29cc21 100644
--- a/src/2023/TalkDetail/TalkDetailContainer2023.tsx
+++ b/src/2023/TalkDetail/TalkDetailContainer2023.tsx
@@ -1,17 +1,17 @@
-import {Color} from "../../styles/colors";
-import React, {FC, useEffect} from "react";
+import { Color } from "../../styles/colors";
+import React, { FC, useEffect } from "react";
import NotFoundError from "../../components/NotFoundError/NotFoundError";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import styled from "styled-components";
-import {useParams} from "react-router";
+import { useParams } from "react-router";
import conferenceData from "../../data/2023.json";
-import {useFetchTalksById} from "../Talks/UseFetchTalks";
+import { useFetchTalksById } from "../Talks/UseFetchTalks";
import * as Sentry from "@sentry/react";
-import {useFetchSpeakers} from "../Speakers/UseFetchSpeakers";
-import {Session} from "../../types/sessions";
+import { Session } from "../../types/sessions";
import TalkDetail from "./TalkDetail";
-import {ISpeaker} from "../../types/speakers";
-import {sessionAdapter} from "../../services/sessionsAdapter";
+import { ISpeaker } from "../../types/speakers";
+import { sessionAdapter } from "../../services/sessionsAdapter";
+import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
const StyledContainer = styled.div`
background-color: ${Color.WHITE};
@@ -19,7 +19,7 @@ const StyledContainer = styled.div`
const TalkDetailContainer2023: FC> = () => {
const { id } = useParams<{ id: string }>();
const { isLoading, error, data } = useFetchTalksById(id!);
- const { data: speakerData } = useFetchSpeakers();
+ const { data: speakerData } = useFetchSpeakers(conferenceData.sessionizeUrl);
const getTalkSpeakers = (
data: Session[] | undefined,
diff --git a/src/2024/SpeakerDetail/SpeakerDetailContainer2024.tsx b/src/2024/SpeakerDetail/SpeakerDetailContainer2024.tsx
index 8d7be2b8..7c6177ef 100644
--- a/src/2024/SpeakerDetail/SpeakerDetailContainer2024.tsx
+++ b/src/2024/SpeakerDetail/SpeakerDetailContainer2024.tsx
@@ -1,52 +1,55 @@
-import {Color} from "../../styles/colors";
+import { Color } from "../../styles/colors";
-import React, {FC} from "react";
+import React, { FC } from "react";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import SpeakerDetail from "./SpeakerDetail";
-import {useParams} from "react-router";
+import { useParams } from "react-router";
import conferenceData from "../../data/2024.json";
-import {useFetchSpeakers} from "../Speakers/UseFetchSpeakers";
import * as Sentry from "@sentry/react";
-import {StyledContainer} from "../../views/SpeakerDetail/Speaker.style";
-import {StyledWaveContainer} from "../../views/Talks/Talks.style";
+import { StyledContainer } from "../../views/SpeakerDetail/Speaker.style";
+import { StyledWaveContainer } from "../../views/Talks/Talks.style";
+import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
const SpeakerDetailContainer2024: FC> = () => {
- const {id} = useParams<{ id: string }>();
+ const { id } = useParams<{ id: string }>();
- const {isLoading, error, data} = useFetchSpeakers(id);
+ const { isLoading, error, data } = useFetchSpeakers(
+ conferenceData.sessionizeUrl,
+ id,
+ );
- if (error) {
- Sentry.captureException(error);
+ if (error) {
+ Sentry.captureException(error);
+ }
+ React.useEffect(() => {
+ if (data) {
+ document.title = `${data[0]?.fullName} - DevBcn - ${conferenceData.edition}`;
}
- React.useEffect(() => {
- if (data) {
- document.title = `${data[0]?.fullName} - DevBcn - ${conferenceData.edition}`;
- }
- }, [id, data]);
- return (
-
-
- {isLoading && Loading }
- {!isLoading && data && data.length > 0 ? (
-
- ) : (
- "not found"
- )}
-
-
-
-
-
-
-
- );
+ }, [id, data]);
+ return (
+
+
+ {isLoading && Loading }
+ {!isLoading && data && data.length > 0 ? (
+
+ ) : (
+ "not found"
+ )}
+
+
+
+
+
+
+
+ );
};
export default SpeakerDetailContainer2024;
diff --git a/src/2024/Speakers/Speakers2024.tsx b/src/2024/Speakers/Speakers2024.tsx
index c5f4e973..751fb53e 100644
--- a/src/2024/Speakers/Speakers2024.tsx
+++ b/src/2024/Speakers/Speakers2024.tsx
@@ -1,176 +1,172 @@
-import {MOBILE_BREAKPOINT} from "../../constants/BreakPoints";
-import {Color} from "../../styles/colors";
-import React, {FC, useCallback, useEffect} from "react";
+import { MOBILE_BREAKPOINT } from "../../constants/BreakPoints";
+import { Color } from "../../styles/colors";
+import React, { FC, useCallback, useEffect } from "react";
import LessThanBlueIcon from "../../assets/images/LessThanBlueIcon.svg";
import MoreThanBlueIcon from "../../assets/images/MoreThanBlueIcon.svg";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import TitleSection from "../../components/SectionTitle/TitleSection";
-import {useWindowSize} from "react-use";
+import { useWindowSize } from "react-use";
import {
- SpeakersCardsContainer,
- StyledContainerLeftSlash,
- StyledContainerRightSlash,
- StyledLessIcon,
- StyledMoreIcon,
- StyledSlash,
- StyledSpeakersSection,
- StyledWaveContainer,
+ SpeakersCardsContainer,
+ StyledContainerLeftSlash,
+ StyledContainerRightSlash,
+ StyledLessIcon,
+ StyledMoreIcon,
+ StyledSlash,
+ StyledSpeakersSection,
+ StyledWaveContainer,
} from "./Speakers.style";
import webData from "../../data/2024.json";
import Button from "../../components/UI/Button";
-import {gaEventTracker} from "../../components/analytics/Analytics";
-import {useFetchSpeakers} from "./UseFetchSpeakers";
+import { gaEventTracker } from "../../components/analytics/Analytics";
import * as Sentry from "@sentry/react";
-import {SpeakerCard} from "../../views/Speakers/components/SpeakersCard";
-import {ISpeaker} from "../../types/speakers";
+import { SpeakerCard } from "../../views/Speakers/components/SpeakersCard";
+import { ISpeaker } from "../../types/speakers";
+import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
const LessThanGreaterThan = (props: { width: number }) => (
- <>
- {props.width > MOBILE_BREAKPOINT && (
- <>
-
-
- >
- )}
- >
+ <>
+ {props.width > MOBILE_BREAKPOINT && (
+ <>
+
+
+ >
+ )}
+ >
);
const Speakers2024: FC> = () => {
- const {width} = useWindowSize();
- const today = new Date();
- const isBetween = (startDay: Date, endDay: Date): boolean =>
- startDay < new Date() && endDay > today;
+ const { width } = useWindowSize();
+ const today = new Date();
+ const isBetween = (startDay: Date, endDay: Date): boolean =>
+ startDay < new Date() && endDay > today;
- const {error, data, isLoading} = useFetchSpeakers();
+ const { error, data, isLoading } = useFetchSpeakers(
+ `${webData.sessionizeUrl}/view/Speakers`,
+ );
- if (error) {
- Sentry.captureException(error);
- }
+ if (error) {
+ Sentry.captureException(error);
+ }
- const trackCFP = useCallback(() => {
- gaEventTracker("CFP", "CFP");
- }, []);
+ const trackCFP = useCallback(() => {
+ gaEventTracker("CFP", "CFP");
+ }, []);
- useEffect(() => {
- document.title = `Speakers β ${webData.title} β ${webData.edition}`;
- });
+ useEffect(() => {
+ document.title = `Speakers β ${webData.title} β ${webData.edition}`;
+ });
- const CFPStartDay = new Date(webData.cfp.startDay);
- const CFPEndDay = new Date(webData.cfp.endDay);
- return (
- <>
-
-
-
-
-
- {isLoading && Loading...
}
- {isBetween(CFPStartDay, CFPEndDay) && (
-
-
-
- )}
- {webData.hideSpeakers ? (
-
- No selected speakers yet. Keep in touch in our
- social media for
- upcoming announcements
-
- ) : (
- data?.map((speaker: ISpeaker) => (
-
- ))
- )}
-
-
-
- / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / /{" "}
-
-
+ color={Color.WHITE}
+ />
+
+
+ {isLoading && Loading...
}
+ {isBetween(CFPStartDay, CFPEndDay) && (
+
+
+
+ )}
+ {webData.hideSpeakers ? (
+
+ No selected speakers yet. Keep in touch in our social media for
+ upcoming announcements
+
+ ) : (
+ data?.map((speaker: ISpeaker) => (
+
+ ))
+ )}
+
+
+
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
+
+
-
-
- / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / /{" "}
-
-
+
+
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
+
+
-
-
- / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / /{" "}
-
-
+
+
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
+
+
-
-
- / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / /{" "}
-
-
-
-
-
-
-
-
-
- >
- );
+
+
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
+
+
+
+
+
+
+
+
+
+ >
+ );
};
export default Speakers2024;
diff --git a/src/2024/Speakers/UseFetchSpeakers.ts b/src/2024/Speakers/UseFetchSpeakers.ts
deleted file mode 100644
index 40c738be..00000000
--- a/src/2024/Speakers/UseFetchSpeakers.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import {useQuery, UseQueryResult} from "react-query";
-import axios from "axios";
-import {speakerAdapter} from "../../services/speakerAdapter";
-import {ISpeaker} from "../../types/speakers";
-
-export const useFetchSpeakers = (id?: string): UseQueryResult => {
- return useQuery("api-speakers", async () => {
- const serverResponse = await axios.get(
- "https://sessionize.com/api/v2/teq4asez/view/Speakers",
- );
- let returnData;
- if (id !== undefined) {
- returnData = serverResponse.data.filter(
- (speaker: { id: string }) => speaker.id === id,
- );
- } else {
- returnData = serverResponse.data;
- }
- return speakerAdapter(returnData);
- });
-};
-
diff --git a/src/2024/SpeakersCarousel/SpeakerSwiper.tsx b/src/2024/SpeakersCarousel/SpeakerSwiper.tsx
index 7231db56..e0ad9550 100644
--- a/src/2024/SpeakersCarousel/SpeakerSwiper.tsx
+++ b/src/2024/SpeakersCarousel/SpeakerSwiper.tsx
@@ -1,129 +1,131 @@
-import React, {FC} from "react";
-import {Autoplay, Parallax} from "swiper";
-import {Swiper, SwiperSlide} from "swiper/react";
+import React, { FC } from "react";
+import { Autoplay, Parallax } from "swiper";
+import { Swiper, SwiperSlide } from "swiper/react";
import styled from "styled-components";
import "swiper/swiper-bundle.min.css";
import "./SpeakersCarousel.scss";
-import {Link} from "react-router";
+import { Link } from "react-router";
import conferenceData from "../../data/2024.json";
-import {useFetchSpeakers} from "../Speakers/UseFetchSpeakers";
import * as Sentry from "@sentry/react";
-import {Color} from "../../styles/colors";
-import {ROUTE_SPEAKER_DETAIL} from "../../constants/routes";
+import { Color } from "../../styles/colors";
+import { ROUTE_SPEAKER_DETAIL } from "../../constants/routes";
+import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
const StyledSlideImage = styled.img`
- display: block;
- width: 100%;
- aspect-ratio: 1/1;
- border-radius: 10px;
+ display: block;
+ width: 100%;
+ aspect-ratio: 1/1;
+ border-radius: 10px;
`;
const StyledSlideContain = styled.div`
- position: absolute;
- bottom: 0;
- background: ${Color.MAGENTA};
- background: linear-gradient(
- to bottom,
- rgba(255, 0, 0, 0),
- ${Color.DARK_BLUE}
- );
- padding: 0.5rem 0.25rem;
- min-width: 100%;
+ position: absolute;
+ bottom: 0;
+ background: ${Color.MAGENTA};
+ background: linear-gradient(
+ to bottom,
+ rgba(255, 0, 0, 0),
+ ${Color.DARK_BLUE}
+ );
+ padding: 0.5rem 0.25rem;
+ min-width: 100%;
`;
const StyledSlideText = styled.p`
- font-size: 0.875rem;
- color: white;
+ font-size: 0.875rem;
+ color: white;
`;
const SpeakerSwiper: FC> = () => {
- const {isLoading, data, error} = useFetchSpeakers();
+ const { isLoading, data, error } = useFetchSpeakers(
+ conferenceData.sessionizeUrl,
+ );
- // Securely shuffle the speakers using Fisher-Yates algorithm with crypto API
+ // Securely shuffle the speakers using Fisher-Yates algorithm with crypto API
const swiperSpeakers = React.useMemo(() => {
if (!data) return null;
-
+
// Create a copy of the data to avoid mutating the original
const speakersCopy = [...data];
-
+
// Fisher-Yates shuffle with crypto.getRandomValues for secure randomization
for (let i = speakersCopy.length - 1; i > 0; i--) {
// Generate a secure random value using crypto API
const randomBuffer = new Uint32Array(1);
window.crypto.getRandomValues(randomBuffer);
-
+
// Use the random value to get an index between 0 and i (inclusive)
const j = randomBuffer[0] % (i + 1);
-
+
// Swap elements at i and j
[speakersCopy[i], speakersCopy[j]] = [speakersCopy[j], speakersCopy[i]];
}
-
+
// Return the first 20 speakers from the shuffled array
return speakersCopy.slice(0, 20);
}, [data]);
- if (error) {
- Sentry.captureException(error);
- }
+ if (error) {
+ Sentry.captureException(error);
+ }
- return (
- <>
- {isLoading && Loading
}
- {conferenceData.carrousel.enabled && swiperSpeakers && (
-
- {swiperSpeakers.map((speaker) => (
-
-
-
-
- {speaker.fullName}
-
-
-
- ))}
-
- )}
- >
- );
+ return (
+ <>
+ {isLoading && Loading
}
+ {conferenceData.carrousel.enabled && swiperSpeakers && (
+
+ {swiperSpeakers.map((speaker) => (
+
+
+
+
+ {speaker.fullName}
+
+
+
+ ))}
+
+ )}
+ >
+ );
};
export default SpeakerSwiper;
diff --git a/src/2024/TalkDetail/MeetingDetailContainer.tsx b/src/2024/TalkDetail/MeetingDetailContainer.tsx
index f48834d9..0e049ca2 100644
--- a/src/2024/TalkDetail/MeetingDetailContainer.tsx
+++ b/src/2024/TalkDetail/MeetingDetailContainer.tsx
@@ -1,71 +1,71 @@
-import {Color} from "../../styles/colors";
-import React, {FC, useEffect} from "react";
+import { Color } from "../../styles/colors";
+import React, { FC, useEffect } from "react";
import NotFoundError from "../../components/NotFoundError/NotFoundError";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import styled from "styled-components";
-import {useParams} from "react-router";
+import { useParams } from "react-router";
import conferenceData from "../../data/2024.json";
-import {useFetchTalksById} from "../Talks/UseFetchTalks";
+import { useFetchTalksById } from "../Talks/UseFetchTalks";
import * as Sentry from "@sentry/react";
-import {useFetchSpeakers} from "../Speakers/UseFetchSpeakers";
import MeetingDetail from "./MeetingDetail";
-import {ISpeaker} from "../../types/speakers";
-import {Session} from "../../types/sessions";
-import {sessionAdapter} from "../../services/sessionsAdapter";
+import { ISpeaker } from "../../types/speakers";
+import { Session } from "../../types/sessions";
+import { sessionAdapter } from "../../services/sessionsAdapter";
+import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
const StyledContainer = styled.div`
- background-color: ${Color.WHITE};
+ background-color: ${Color.WHITE};
`;
const MeetingDetailContainer: FC> = () => {
- const {id} = useParams<{ id: string }>();
- const {isLoading, error, data} = useFetchTalksById(id!);
- const {data: speakerData} = useFetchSpeakers();
+ const { id } = useParams<{ id: string }>();
+ const { isLoading, error, data } = useFetchTalksById(id!);
+ const { data: speakerData } = useFetchSpeakers(conferenceData.sessionizeUrl);
- const getTalkSpeakers = (
- data: Session[] | undefined,
- ): string[] | undefined => {
- const speakers = data?.[0]?.speakers;
- return speakers?.map((speaker) => speaker.id);
- };
+ const getTalkSpeakers = (
+ data: Session[] | undefined,
+ ): string[] | undefined => {
+ const speakers = data?.[0]?.speakers;
+ return speakers?.map((speaker) => speaker.id);
+ };
- const talkSpeakers: string[] | undefined = getTalkSpeakers(data);
- const sessionSpeakers: ISpeaker[] | undefined = speakerData?.filter(
- (speaker) => talkSpeakers?.includes(speaker.id),
- );
+ const talkSpeakers: string[] | undefined = getTalkSpeakers(data);
+ const sessionSpeakers: ISpeaker[] | undefined = speakerData?.filter(
+ (speaker) => talkSpeakers?.includes(speaker.id),
+ );
- const adaptedMeeting = sessionAdapter(data?.at(0));
+ const adaptedMeeting = sessionAdapter(data?.at(0));
- useEffect(() => {
- document.title = `${data?.at(0)?.title} - DevBcn - ${
- conferenceData.edition
- }`;
- }, [data]);
+ useEffect(() => {
+ document.title = `${data?.at(0)?.title} - DevBcn - ${
+ conferenceData.edition
+ }`;
+ }, [data]);
- if (error) {
- Sentry.captureException(error);
- }
+ if (error) {
+ Sentry.captureException(error);
+ }
- return (
-
-
- {isLoading && Loading }
- {!isLoading &&
- sessionSpeakers !== undefined &&
- sessionSpeakers.length > 0 &&
- adaptedMeeting !== undefined && (
-
- )}
- {!isLoading &&
- (!sessionSpeakers ||
- sessionSpeakers.length === 0 ||
- !adaptedMeeting) && }
-
-
- );
+ return (
+
+
+ {isLoading && Loading }
+ {!isLoading &&
+ sessionSpeakers !== undefined &&
+ sessionSpeakers.length > 0 &&
+ adaptedMeeting !== undefined && (
+
+ )}
+ {!isLoading &&
+ (!sessionSpeakers ||
+ sessionSpeakers.length === 0 ||
+ !adaptedMeeting) && }
+
+
+ );
};
export default MeetingDetailContainer;
diff --git a/src/views/Speakers/Speakers.tsx b/src/views/Speakers/Speakers.tsx
index f2525fff..59fafd9c 100644
--- a/src/views/Speakers/Speakers.tsx
+++ b/src/views/Speakers/Speakers.tsx
@@ -1,12 +1,12 @@
-import {MOBILE_BREAKPOINT} from "../../constants/BreakPoints";
-import {Color} from "../../styles/colors";
-import {FC, useCallback, useEffect} from "react";
+import { MOBILE_BREAKPOINT } from "../../constants/BreakPoints";
+import { Color } from "../../styles/colors";
+import { FC, useCallback, useEffect } from "react";
import LessThanBlueIcon from "../../assets/images/LessThanBlueIcon.svg";
import MoreThanBlueIcon from "../../assets/images/MoreThanBlueIcon.svg";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
-import {SpeakerCard} from "./components/SpeakersCard";
+import { SpeakerCard } from "./components/SpeakersCard";
import TitleSection from "../../components/SectionTitle/TitleSection";
-import {useWindowSize} from "react-use";
+import { useWindowSize } from "react-use";
import {
SpeakersCardsContainer,
StyledContainerLeftSlash,
@@ -19,10 +19,10 @@ import {
} from "./Speakers.style";
import webData from "../../data/2024.json";
import Button from "../../components/UI/Button";
-import {gaEventTracker} from "../../components/analytics/Analytics";
-import {useFetchSpeakers} from "./UseFetchSpeakers";
+import { gaEventTracker } from "../../components/analytics/Analytics";
+import { useFetchSpeakers } from "./UseFetchSpeakers";
import * as Sentry from "@sentry/react";
-import {ISpeaker} from "../../types/speakers";
+import { ISpeaker } from "../../types/speakers";
const LessThanGreaterThan = (props: { width: number }) => (
<>
@@ -41,7 +41,9 @@ const Speakers: FC> = () => {
const isBetween = (startDay: Date, endDay: Date): boolean =>
startDay < new Date() && endDay > today;
- const { error, data, isLoading } = useFetchSpeakers();
+ const { error, data, isLoading } = useFetchSpeakers(
+ `${webData.sessionizeUrl}/view/Speakers`,
+ );
if (error) {
Sentry.captureException(error);
@@ -106,7 +108,8 @@ const Speakers: FC> = () => {
>
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /{" "}
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
@@ -118,7 +121,8 @@ const Speakers: FC> = () => {
>
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /{" "}
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
@@ -130,7 +134,8 @@ const Speakers: FC> = () => {
>
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /{" "}
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
@@ -142,7 +147,8 @@ const Speakers: FC> = () => {
>
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
- / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /{" "}
+ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
+ /{" "}
From d20db04f63e41ca0bd8facff7bc5de21b837d83a Mon Sep 17 00:00:00 2001
From: Anyul Rivas
Date: Thu, 13 Mar 2025 18:08:00 +0100
Subject: [PATCH 4/9] feat: parameterize url
---
src/views/Speakers/UseFetchSpeakers.ts | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/views/Speakers/UseFetchSpeakers.ts b/src/views/Speakers/UseFetchSpeakers.ts
index 8ff0fecb..82d0a0bf 100644
--- a/src/views/Speakers/UseFetchSpeakers.ts
+++ b/src/views/Speakers/UseFetchSpeakers.ts
@@ -1,13 +1,14 @@
import { useQuery, UseQueryResult } from "react-query";
import axios from "axios";
import { speakerAdapter } from "../../services/speakerAdapter";
-import { ISpeaker } from "../../types/speakers";
+import { ISpeaker } from "../../types/speakers";
-export const useFetchSpeakers = (id?: string): UseQueryResult => {
- return useQuery("api-speakers", async () => {
- const serverResponse = await axios.get(
- "https://sessionize.com/api/v2/xhudniix/view/Speakers",
- );
+export const useFetchSpeakers = (
+ url: string = "https://sessionize.com/api/v2/xhudniix/view/Speakers",
+ id?: string,
+): UseQueryResult => {
+ return useQuery(["api-speakers", url], async () => {
+ const serverResponse = await axios.get(url);
let returnData;
if (id !== undefined) {
returnData = serverResponse.data.filter(
From 0e07f73ce8f23bd58cd1286fbc4fe4ab87333ff6 Mon Sep 17 00:00:00 2001
From: Anyul Rivas
Date: Sat, 15 Mar 2025 05:48:14 +0100
Subject: [PATCH 5/9] test: mock API calls
---
src/App.test.tsx | 69 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/src/App.test.tsx b/src/App.test.tsx
index 9602e8d6..26a257b6 100644
--- a/src/App.test.tsx
+++ b/src/App.test.tsx
@@ -3,8 +3,77 @@ import { BrowserRouter, Route, Routes } from "react-router";
import App from "./App";
import React from "react";
import userEvent from "@testing-library/user-event";
+import { IResponse } from "./types/speakers";
+import axios, { AxiosHeaders, AxiosResponse } from "axios";
+
+jest.mock("axios");
+const mockedAxios = axios as jest.Mocked;
+const axiosHeaders = new AxiosHeaders();
+const payload: AxiosResponse = {
+ status: 200,
+ statusText: "OK",
+ headers: {},
+ config: {
+ headers: axiosHeaders,
+ },
+ data: [
+ {
+ id: "1",
+ fullName: "John Smith",
+ profilePicture: "https://example.com/john.jpg",
+ tagLine: "Software engineer",
+ bio: "I am a software engineer",
+ sessions: [
+ {
+ id: 4567,
+ name: "sample session",
+ },
+ ],
+ links: [
+ {
+ linkType: "Twitter",
+ url: "https://twitter.com/johnsmith",
+ title: "",
+ },
+ {
+ linkType: "LinkedIn",
+ url: "https://linkedin.com/in/johnsmith",
+ title: "",
+ },
+ ],
+ },
+ {
+ id: "2",
+ fullName: "Jane Doe",
+ profilePicture: "https://example.com/jane.jpg",
+ tagLine: "Data scientist",
+ bio: "I am a data scientist",
+ sessions: [],
+ links: [
+ {
+ linkType: "Twitter",
+ url: "https://twitter.com/janedoe",
+ title: "",
+ },
+ {
+ linkType: "LinkedIn",
+ url: "https://linkedin.com/in/janedoe",
+ title: "",
+ },
+ ],
+ },
+ ],
+};
describe("navigation pages", () => {
+ beforeAll(() => {
+ jest.mock("axios");
+ mockedAxios.get.mockImplementation(() => Promise.resolve(payload));
+ });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
test("it render the HOME page", async () => {
render(
Loading...}>
From 96899542dc6566eea75811f1287dae154f24c55a Mon Sep 17 00:00:00 2001
From: Anyul Rivas
Date: Tue, 18 Mar 2025 15:18:19 +0100
Subject: [PATCH 6/9] fix: fix 2024 diversity route
---
src/2023/Cfp/CfpSection2023.test.tsx | 2 +-
src/2023/Cfp/CfpSection2023.tsx | 6 +-
src/2023/Communities/Communities2023.tsx | 5 +-
src/2023/Diversity/Diversity2023.tsx | 59 +-
src/2023/Home/Home2023Wrapper.tsx | 4 +-
.../SpeakersCarousel/SpeakerSwiper.tsx | 29 +-
src/2023/JobOffers/JobOffers2023.tsx | 5 +-
src/2023/Schedule/Schedule2023.tsx | 5 +-
.../SessionFeedback/SessionFeedback2023.tsx | 5 +-
.../SpeakerDetailContainer2023.tsx | 15 +-
src/2023/Speakers/Speakers2023.tsx | 13 +-
src/2023/TalkDetail/TalkDetail.tsx | 11 +-
.../TalkDetail/TalkDetailContainer2023.tsx | 16 +-
src/2023/Talks/Talks2023.tsx | 13 +-
src/2023/Workshops/Workshops2023.tsx | 23 +-
src/2024/Cfp/CfpSection.test.tsx | 2 +-
src/2024/Cfp/CfpSection2024.tsx | 55 +-
src/2024/HomeWrapper2024.tsx | 60 +-
src/2024/SpeakerDetail/SpeakerDetail.tsx | 292 +++++----
.../SpeakerDetailContainer2024.tsx | 14 +-
src/2024/Speakers/Speakers2024.tsx | 13 +-
src/2024/SpeakersCarousel/SpeakerSwiper.tsx | 10 +-
src/2024/TalkDetail/MeetingDetail.tsx | 498 +++++++--------
.../TalkDetail/MeetingDetailContainer.tsx | 16 +-
src/2024/Talks/LiveView.tsx | 111 ++--
src/2024/Talks/Talks2024.tsx | 181 +++---
.../NotFoundError/NotFoundError.tsx | 11 +-
src/components/Swiper/SpeakerSwiper.tsx | 182 +++---
src/services/useDocumentTitleUpdate.ts | 7 +
src/services/useSentryErrorReport.ts | 7 +
src/views/About/About.tsx | 5 +-
src/views/Attendee/AttendeeInformation.tsx | 7 +-
src/views/Cfp/CfpSection.test.tsx | 2 +-
src/views/Cfp/CfpSection.tsx | 64 +-
src/views/CodeOfConduct/CodeOfConduct.tsx | 7 +-
src/views/Communities/Communities.tsx | 6 +-
src/views/Conditions/Conditions.tsx | 50 +-
src/views/Diversity/Diversity.tsx | 7 +-
src/views/Home/HomeWrapper.tsx | 4 +-
src/views/JobOffers/JobOffers.tsx | 5 +-
src/views/MeetingDetail/MeetingDetail.tsx | 11 +-
.../MeetingDetail/TalkDetailContainer2024.tsx | 30 +-
src/views/Schedule/Schedule.tsx | 5 +-
src/views/SessionFeedback/SessionFeedback.tsx | 5 +-
src/views/SpeakerDetail/SpeakerDetail.tsx | 9 +-
.../SpeakerDetail/SpeakerDetailContainer.tsx | 66 +-
src/views/Speakers/Speakers.tsx | 17 +-
src/views/Speakers/UseFetchSpeakers.ts | 4 +-
src/views/Talks/LiveView.tsx | 15 +-
src/views/Talks/Talks.tsx | 10 +-
src/views/Travel/Travel.tsx | 54 +-
src/views/Workshops/Workshops.tsx | 34 +-
src/views/kcd/Kcd.tsx | 13 +-
src/views/sponsorship/Sponsorship.tsx | 600 +++++++++---------
54 files changed, 1327 insertions(+), 1373 deletions(-)
create mode 100644 src/services/useDocumentTitleUpdate.ts
create mode 100644 src/services/useSentryErrorReport.ts
diff --git a/src/2023/Cfp/CfpSection2023.test.tsx b/src/2023/Cfp/CfpSection2023.test.tsx
index e8bacfd8..67e989be 100644
--- a/src/2023/Cfp/CfpSection2023.test.tsx
+++ b/src/2023/Cfp/CfpSection2023.test.tsx
@@ -98,7 +98,7 @@ describe("CfpSection2023", () => {
render( );
await waitFor(() => {
expect(document.title).toBe(
- `CFP Committee - DevBcn - ${conferenceData.edition}`,
+ `CFP Committee β DevBcn - Barcelona Developers Conference β ${conferenceData.edition}`,
);
});
});
diff --git a/src/2023/Cfp/CfpSection2023.tsx b/src/2023/Cfp/CfpSection2023.tsx
index c03e02a4..94558ccb 100644
--- a/src/2023/Cfp/CfpSection2023.tsx
+++ b/src/2023/Cfp/CfpSection2023.tsx
@@ -21,6 +21,7 @@ import {
StyledAboutImage,
StyledSocialIconsWrapper,
} from "../../views/About/components/Style.AboutCard";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const TrackName = styled.h2`
padding-top: 1.2rem;
@@ -75,9 +76,8 @@ const CfpTrackComponent: FC> = ({
const CfpSection2023: FC> = () => {
const { width } = useWindowSize();
- React.useEffect(() => {
- document.title = `CFP Committee - DevBcn - ${conferenceData.edition}`;
- }, []);
+
+ useDocumentTitleUpdater("CFP Committee", conferenceData.edition);
return (
<>
diff --git a/src/2023/Communities/Communities2023.tsx b/src/2023/Communities/Communities2023.tsx
index 301429a7..53a34919 100644
--- a/src/2023/Communities/Communities2023.tsx
+++ b/src/2023/Communities/Communities2023.tsx
@@ -3,6 +3,7 @@ import styled from "styled-components";
import TwitterIcon from "../../components/Icons/Twitter";
import { Color } from "../../styles/colors";
import WebsiteIcon from "../../components/Icons/website";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const Heading = styled.h1`
padding-top: 10rem;
@@ -29,9 +30,7 @@ const FoSS = styled.div`
`;
const Communities2023: FC> = () => {
- React.useEffect(() => {
- document.title = "Communities";
- });
+ useDocumentTitleUpdater("Communities", "2023");
return (
<>
FOSS & Diversity Communities
diff --git a/src/2023/Diversity/Diversity2023.tsx b/src/2023/Diversity/Diversity2023.tsx
index 12e72bb1..06f8a9d3 100644
--- a/src/2023/Diversity/Diversity2023.tsx
+++ b/src/2023/Diversity/Diversity2023.tsx
@@ -1,4 +1,4 @@
-import { FC, useEffect } from "react";
+import { FC } from "react";
import { Color } from "../../styles/colors";
import data from "../../data/2023.json";
import styled from "styled-components";
@@ -8,25 +8,26 @@ import {
ROUTE_CODE_OF_CONDUCT,
ROUTE_CONDITIONS,
} from "../../constants/routes";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledSection = styled.section`
- {
+{
padding-top: 48px;
- }
+}
- .top {
- clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 50px));
- height: 51px;
- background-color: ${Color.DARK_BLUE};
- border-top: 1px solid ${Color.DARK_BLUE};
- }
+ .top {
+ clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 50px));
+ height: 51px;
+ background-color: ${Color.DARK_BLUE};
+ border-top: 1px solid ${Color.DARK_BLUE};
+ }
- .bottom {
- clip-path: polygon(0 0, 100% 50px, 100% 100%, 0 100%);
- margin-top: -50px;
- height: 50px;
- background-color: ${Color.WHITE};
- }
+ .bottom {
+ clip-path: polygon(0 0, 100% 50px, 100% 100%, 0 100%);
+ margin-top: -50px;
+ height: 50px;
+ background-color: ${Color.WHITE};
+ }
`;
const StyledWave = styled.section`
@@ -36,15 +37,15 @@ const StyledWave = styled.section`
`;
const StyledLogo = styled.img`
- {
+{
max-width: 30vw;
flex: 2 1 auto;
padding-bottom: 50px;
- }
- @media only screen and (max-width: ${BIG_BREAKPOINT}px) {
- padding-bottom: 20px;
- max-width: 65vw;
- }
+}
+ @media only screen and (max-width: ${BIG_BREAKPOINT}px) {
+ padding-bottom: 20px;
+ max-width: 65vw;
+ }
`;
const Heading = styled.h1`
@@ -68,17 +69,17 @@ const StyledP = styled.p`
`;
const FlexDiv = styled.div`
- {
+{
display: flex;
width: 20%;
margin: 0 auto;
flex-direction: column;
padding-bottom: 20px;
- }
- @media only screen and (max-width: ${BIG_BREAKPOINT}px) {
- width: 60%;
- padding-bottom: 0.5rem;
- }
+}
+ @media only screen and (max-width: ${BIG_BREAKPOINT}px) {
+ width: 60%;
+ padding-bottom: 0.5rem;
+ }
`;
const StyledParagraph = styled.section`
@@ -96,9 +97,7 @@ const StyledParagraph = styled.section`
}
`;
const Diversity2023: FC> = () => {
- useEffect(() => {
- document.title = `Diversity - DevBcn ${data.edition}`;
- });
+ useDocumentTitleUpdater("Diversity", data.edition);
return (
diff --git a/src/2023/Home/Home2023Wrapper.tsx b/src/2023/Home/Home2023Wrapper.tsx
index a7a77d7c..cc45e91f 100644
--- a/src/2023/Home/Home2023Wrapper.tsx
+++ b/src/2023/Home/Home2023Wrapper.tsx
@@ -7,6 +7,7 @@ import Sponsors from "./components/Sponsors/Sponsors";
import styled from "styled-components";
import data from "../../data/2023.json";
import { useLocation } from "react-router";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledContainer = styled.div`
padding-bottom: 10rem;
@@ -20,12 +21,13 @@ export const Home2023Wrapper: FC> = () => {
const { hash } = useLocation();
React.useEffect(() => {
- document.title = `Home - DevBcn - ${data.edition}`;
if (hash != null && hash !== "") {
const scroll = document.getElementById(hash.substring(1));
scroll?.scrollIntoView();
}
}, [hash]);
+
+ useDocumentTitleUpdater("Home", data.edition);
return (
diff --git a/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx b/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
index 4ef82cc4..1fe56d78 100644
--- a/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
+++ b/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
@@ -7,10 +7,11 @@ import "swiper/swiper-bundle.min.css";
import "./SpeakersCarousel.scss";
import { Link } from "react-router";
import { ROUTE_SPEAKER_DETAIL } from "../../../../constants/routes";
-import * as Sentry from "@sentry/react";
import conferenceData from "../../../../data/2023.json";
import { useFetchSpeakers } from "../../../../views/Speakers/UseFetchSpeakers";
+import { useSentryErrorReport } from "../../../../services/useSentryErrorReport";
+import { ISpeaker } from "../../../../types/speakers";
const StyledSlideImage = styled.img`
display: block;
@@ -36,16 +37,34 @@ const StyledSlideText = styled.p`
font-size: 0.875rem;
color: white;
`;
+
+/** Fisher-Yates shuffle algorithm using window.crypto.getRandomValues() */
+export const shuffleArray = (array: T[]): T[] => {
+ if (!array) {
+ return [];
+ }
+ const shuffledArray = [...array]; // Create a copy to avoid modifying the original array
+ for (let i = shuffledArray.length - 1; i > 0; i--) {
+ const j = Math.floor(
+ (window.crypto.getRandomValues(new Uint32Array(1))[0] /
+ (0xffffffff + 1)) *
+ (i + 1),
+ );
+ [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
+ }
+ return shuffledArray;
+};
+
const SpeakerSwiper: FC> = () => {
const { isLoading, data, error } = useFetchSpeakers(
conferenceData.sessionizeUrl,
);
- const swiperSpeakers = data?.sort(() => 0.5 - Math.random()).slice(0, 20);
+ const swiperSpeakers: ISpeaker[] = data
+ ? shuffleArray(data).slice(0, 20)
+ : [];
- if (error) {
- Sentry.captureException(error);
- }
+ useSentryErrorReport(error);
return (
<>
diff --git a/src/2023/JobOffers/JobOffers2023.tsx b/src/2023/JobOffers/JobOffers2023.tsx
index cb169644..b166d8dd 100644
--- a/src/2023/JobOffers/JobOffers2023.tsx
+++ b/src/2023/JobOffers/JobOffers2023.tsx
@@ -14,6 +14,7 @@ import {
StyledTitleContainer
} from "../../styles/JobOffers/JobOffers.Style";
import CompanyOffers from "../../components/JobOffers/CompanyOffers";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const NoOffersAvailable = () => (
No job offers available yet
@@ -29,9 +30,7 @@ const MoreThanLessThan = (props: { width: number }) => (
const JobOffers2023: FC> = () => {
const { width } = useWindowSize();
- React.useEffect(() => {
- document.title = `Job Offers - DevBcn - ${data.edition}`;
- }, []);
+ useDocumentTitleUpdater("Job Offers", data.edition);
return (
diff --git a/src/2023/Schedule/Schedule2023.tsx b/src/2023/Schedule/Schedule2023.tsx
index 81f437d8..792d0833 100644
--- a/src/2023/Schedule/Schedule2023.tsx
+++ b/src/2023/Schedule/Schedule2023.tsx
@@ -13,13 +13,14 @@ import {
StyledScheduleSection,
} from "../../styles/Schedule/Schedule.style";
import * as Sentry from "@sentry/react";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const Schedule2023: FC> = () => {
const { width } = useWindowSize();
- React.useEffect(() => {
- document.title = `Schedule - DevBcn - ${data.edition}`;
+ useDocumentTitleUpdater("Schedule", data.edition);
+ React.useEffect(() => {
fetch("https://sessionize.com/api/v2/a2sw0wks/view/GridSmart")
.then((value) => value.text())
.then((value) => {
diff --git a/src/2023/SessionFeedback/SessionFeedback2023.tsx b/src/2023/SessionFeedback/SessionFeedback2023.tsx
index 0f53ef14..9bda1b0f 100644
--- a/src/2023/SessionFeedback/SessionFeedback2023.tsx
+++ b/src/2023/SessionFeedback/SessionFeedback2023.tsx
@@ -15,6 +15,7 @@ import { FilterMatchMode } from "primereact/api";
import { Color } from "../../styles/colors";
import { Link } from "react-router";
import { ROUTE_TALK_DETAIL } from "../../constants/routes";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const SessionFeedback2023: FC> = () => {
const bodyTemplate = React.useCallback(
@@ -64,9 +65,7 @@ const SessionFeedback2023: FC> = () => {
);
- React.useEffect(() => {
- document.title = "DevBcn 2023 - Session Feedback";
- });
+ useDocumentTitleUpdater("Session Feedback", "2023");
const header = renderHeader();
diff --git a/src/2023/SpeakerDetail/SpeakerDetailContainer2023.tsx b/src/2023/SpeakerDetail/SpeakerDetailContainer2023.tsx
index 90c4dd65..af250753 100644
--- a/src/2023/SpeakerDetail/SpeakerDetailContainer2023.tsx
+++ b/src/2023/SpeakerDetail/SpeakerDetailContainer2023.tsx
@@ -6,8 +6,9 @@ import SpeakerDetail2023 from "./SpeakerDetail2023";
import { useParams } from "react-router";
import { StyledContainer, StyledWaveContainer } from "./Speaker.style";
import conferenceData from "../../data/2023.json";
-import * as Sentry from "@sentry/react";
import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const SpeakerDetailContainer2023: FC> = () => {
const { id } = useParams<{ id: string }>();
@@ -17,14 +18,10 @@ const SpeakerDetailContainer2023: FC> = () => {
id,
);
- if (error) {
- Sentry.captureException(error);
- }
- React.useEffect(() => {
- if (data) {
- document.title = `${data[0]?.fullName} - DevBcn - ${conferenceData.edition}`;
- }
- }, [id, data]);
+ useSentryErrorReport(error);
+
+ useDocumentTitleUpdater(data?.[0]?.fullName ?? "", conferenceData.edition);
+
return (
diff --git a/src/2023/Speakers/Speakers2023.tsx b/src/2023/Speakers/Speakers2023.tsx
index 4ae3b68b..5b71be6c 100644
--- a/src/2023/Speakers/Speakers2023.tsx
+++ b/src/2023/Speakers/Speakers2023.tsx
@@ -1,6 +1,6 @@
import { MOBILE_BREAKPOINT } from "../../constants/BreakPoints";
import { Color } from "../../styles/colors";
-import { FC, useCallback, useEffect } from "react";
+import { FC, useCallback } from "react";
import LessThanBlueIcon from "../../assets/images/LessThanBlueIcon.svg";
import MoreThanBlueIcon from "../../assets/images/MoreThanBlueIcon.svg";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
@@ -20,9 +20,10 @@ import {
import webData from "../../data/2023.json";
import Button from "../../components/UI/Button";
import { gaEventTracker } from "../../components/analytics/Analytics";
-import * as Sentry from "@sentry/react";
import { ISpeaker } from "../../types/speakers";
import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const LessThanGreaterThan = (props: { width: number }) => (
<>
@@ -45,17 +46,13 @@ const Speakers2023: FC> = () => {
`${webData.sessionizeUrl}/view/Speakers`,
);
- if (error) {
- Sentry.captureException(error);
- }
+ useSentryErrorReport(error);
const trackCFP = useCallback(() => {
gaEventTracker("CFP", "CFP");
}, []);
- useEffect(() => {
- document.title = `Speakers2023 - DevBcn ${webData.edition}`;
- });
+ useDocumentTitleUpdater("Speakers 2023", webData.edition);
const CFPStartDay = new Date(webData.cfp.startDay);
const CFPEndDay = new Date(webData.cfp.endDay);
diff --git a/src/2023/TalkDetail/TalkDetail.tsx b/src/2023/TalkDetail/TalkDetail.tsx
index e8ec2f5d..a52e14a7 100644
--- a/src/2023/TalkDetail/TalkDetail.tsx
+++ b/src/2023/TalkDetail/TalkDetail.tsx
@@ -4,7 +4,7 @@ import {
MOBILE_BREAKPOINT,
} from "../../constants/BreakPoints";
import {Color} from "../../styles/colors";
-import React, {FC, Suspense, useEffect} from "react";
+import React, { FC, Suspense } from "react";
import LessThanIconWhite from "../../assets/images/LessThanIconWhite.svg";
import LessThanIcon from "../../assets/images/LessThanBlueIcon.svg";
import MoreThanIcon from "../../assets/images/MoreThanBlueIcon.svg";
@@ -33,8 +33,9 @@ import {
ROUTE_2023_TALKS,
} from "../../constants/routes";
import conferenceData from "../../data/2023.json";
-import {Tag} from "../../components/Tag/Tag";
-import {IMeetingDetailProps, MyType} from "../../types/sessions";
+import { Tag } from "../../components/Tag/Tag";
+import { IMeetingDetailProps, MyType } from "../../types/sessions";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const getVideoHeight = (windowWidth: number) => {
let videoHeight;
@@ -102,9 +103,7 @@ const TalkDetail: FC> = ({
}) => {
const { width } = useWindowSize();
- useEffect(() => {
- document.title = `${meeting.title} - DevBcn ${conferenceData.edition}`;
- }, [meeting.title]);
+ useDocumentTitleUpdater(meeting.title, conferenceData.edition);
const finalMeetingInfo: MyType = {
...meeting,
diff --git a/src/2023/TalkDetail/TalkDetailContainer2023.tsx b/src/2023/TalkDetail/TalkDetailContainer2023.tsx
index 2d29cc21..f36bbb30 100644
--- a/src/2023/TalkDetail/TalkDetailContainer2023.tsx
+++ b/src/2023/TalkDetail/TalkDetailContainer2023.tsx
@@ -1,17 +1,18 @@
import { Color } from "../../styles/colors";
-import React, { FC, useEffect } from "react";
+import React, { FC } from "react";
import NotFoundError from "../../components/NotFoundError/NotFoundError";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import styled from "styled-components";
import { useParams } from "react-router";
import conferenceData from "../../data/2023.json";
import { useFetchTalksById } from "../Talks/UseFetchTalks";
-import * as Sentry from "@sentry/react";
import { Session } from "../../types/sessions";
import TalkDetail from "./TalkDetail";
import { ISpeaker } from "../../types/speakers";
import { sessionAdapter } from "../../services/sessionsAdapter";
import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledContainer = styled.div`
background-color: ${Color.WHITE};
@@ -35,15 +36,8 @@ const TalkDetailContainer2023: FC> = () => {
const adaptedMeeting = sessionAdapter(data?.at(0));
- useEffect(() => {
- document.title = `${data?.at(0)?.title} - DevBcn - ${
- conferenceData.edition
- }`;
- }, [data]);
-
- if (error) {
- Sentry.captureException(error);
- }
+ useDocumentTitleUpdater(data?.at(0)?.title ?? "", conferenceData.edition);
+ useSentryErrorReport(error);
return (
diff --git a/src/2023/Talks/Talks2023.tsx b/src/2023/Talks/Talks2023.tsx
index b7759c44..f0a6554b 100644
--- a/src/2023/Talks/Talks2023.tsx
+++ b/src/2023/Talks/Talks2023.tsx
@@ -13,11 +13,12 @@ import {
StyledWaveContainer,
} from "./Talks.style";
import { useFetchTalks } from "./UseFetchTalks";
-import * as Sentry from "@sentry/react";
import { Dropdown, DropdownChangeEvent } from "primereact/dropdown";
import "primereact/resources/primereact.min.css";
import "primereact/resources/themes/lara-light-indigo/theme.css";
import "../../styles/theme.css";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
import TrackInformation from "../../components/Talk/TrackInformation";
interface TrackInfo {
@@ -27,7 +28,7 @@ interface TrackInfo {
const Talks2023: FC> = () => {
const [selectedGroupId, setSelectedGroupId] = useState(
- null
+ null,
);
const { isLoading, error, data } = useFetchTalks();
@@ -36,9 +37,6 @@ const Talks2023: FC> = () => {
sessionStorage.getItem("selectedGroupCode");
const sessionSelectedGroupName =
sessionStorage.getItem("selectedGroupName");
-
- document.title = `Talks - DevBcn - ${conferenceData.edition}`;
-
if (sessionSelectedGroupCode && sessionSelectedGroupName) {
setSelectedGroupId({
name: sessionSelectedGroupName,
@@ -47,9 +45,8 @@ const Talks2023: FC> = () => {
}
}, []);
- if (error) {
- Sentry.captureException(error);
- }
+ useDocumentTitleUpdater("Talks ", conferenceData.edition);
+ useSentryErrorReport(error);
const dropDownOptions = [
{ name: "All Tracks", code: undefined },
diff --git a/src/2023/Workshops/Workshops2023.tsx b/src/2023/Workshops/Workshops2023.tsx
index f80ac7d3..14d7cf0f 100644
--- a/src/2023/Workshops/Workshops2023.tsx
+++ b/src/2023/Workshops/Workshops2023.tsx
@@ -1,4 +1,4 @@
-import React, { FC, useEffect } from "react";
+import React, { FC } from "react";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import { Color } from "../../styles/colors";
import {
@@ -12,18 +12,19 @@ import LessThanDarkBlueIcon from "../../assets/images/LessThanDarkBlueIcon.svg";
import TitleSection from "../../components/SectionTitle/TitleSection";
import MoreThanBlueIcon from "../../assets/images/MoreThanBlueIcon.svg";
import { useFetchTalks } from "../Talks/UseFetchTalks";
-import * as Sentry from "@sentry/react";
+import { TalkCard } from "../Talks/components/TalkCard";
import conferenceData from "../../data/2023.json";
import styled from "styled-components";
import { BIG_BREAKPOINT } from "../../constants/BreakPoints";
-import {TalkCard} from "../../components/Talk/TalkCard";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledSection = styled.section`
- {
+{
display: flex;
padding: 0 10rem;
flex-wrap: wrap;
- }
+}
@media (max-width: ${BIG_BREAKPOINT}px) {
padding: 1rem;
@@ -37,9 +38,7 @@ const StyledSection = styled.section`
`;
const Workshops2023: FC> = () => {
const { isLoading, data, error } = useFetchTalks();
- useEffect(() => {
- document.title = `Workshops - DevBcn - ${conferenceData.edition}`;
- }, []);
+ useDocumentTitleUpdater("Workshops", conferenceData.edition);
//region workshops
const categoryId = 149213;
@@ -49,14 +48,12 @@ const Workshops2023: FC> = () => {
?.flatMap((group) => group.sessions)
.filter((session) =>
session.categories.some((category) =>
- category.categoryItems.some((item) => categoryItemIds.has(item.id))
- )
+ category.categoryItems.some((item) => categoryItemIds.has(item.id)),
+ ),
);
//endregion
- if (error) {
- Sentry.captureException(error);
- }
+ useSentryErrorReport(error);
return (
<>
diff --git a/src/2024/Cfp/CfpSection.test.tsx b/src/2024/Cfp/CfpSection.test.tsx
index 43f2ea26..a97bcce1 100644
--- a/src/2024/Cfp/CfpSection.test.tsx
+++ b/src/2024/Cfp/CfpSection.test.tsx
@@ -9,7 +9,7 @@ describe("CfpSection", () => {
it("sets document title on mount", () => {
render( );
expect(document.title).toBe(
- `CFP Committee β ${conferenceData.title}β ${conferenceData.edition}`,
+ `CFP Committee β DevBcn - Barcelona Developers Conference β ${conferenceData.edition}`,
);
});
diff --git a/src/2024/Cfp/CfpSection2024.tsx b/src/2024/Cfp/CfpSection2024.tsx
index 2740852e..e20dba77 100644
--- a/src/2024/Cfp/CfpSection2024.tsx
+++ b/src/2024/Cfp/CfpSection2024.tsx
@@ -18,9 +18,10 @@ import conferenceData from "../../data/2024.json";
import { CfpTrackProps, data } from "./CfpData";
import { MemberName, TrackName } from "./Cfp.style";
import {
- StyledAboutImage,
- StyledSocialIconsWrapper
+ StyledAboutImage,
+ StyledSocialIconsWrapper,
} from "../../views/About/components/Style.AboutCard";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
export const CfpTrackComponent: FC> = ({
track,
@@ -62,35 +63,33 @@ export const CfpTrackComponent: FC> = ({
const CfpSection2024: FC> = () => {
const { width } = useWindowSize();
- React.useEffect(() => {
- document.title = `CFP Committee β ${conferenceData.title} β ${conferenceData.edition}`;
- }, []);
- return (
- <>
-
-
-
+
+
+
- {width > MOBILE_BREAKPOINT && (
- <>
-
-
- >
- )}
-
- {data.map((track) => (
-
- ))}
-
-
- >
- );
+ color={Color.BLUE}
+ />
+ {width > MOBILE_BREAKPOINT && (
+ <>
+
+
+ >
+ )}
+
+ {data.map((track) => (
+
+ ))}
+
+
+ >
+ );
};
export default CfpSection2024;
diff --git a/src/2024/HomeWrapper2024.tsx b/src/2024/HomeWrapper2024.tsx
index 5668c53f..79ef0889 100644
--- a/src/2024/HomeWrapper2024.tsx
+++ b/src/2024/HomeWrapper2024.tsx
@@ -1,45 +1,47 @@
-import React, {FC, useState} from "react";
+import React, { FC, useState } from "react";
import styled from "styled-components";
-import {useLocation} from "react-router";
-import {BIG_BREAKPOINT} from "../constants/BreakPoints";
+import { useLocation } from "react-router";
+import { BIG_BREAKPOINT } from "../constants/BreakPoints";
-import {useEventEdition} from "../views/Home/UseEventEdition";
+import { useEventEdition } from "../views/Home/UseEventEdition";
import Faqs from "../views/Home/components/Faqs/Faqs";
import Home from "./Home/Home";
import SpeakersCarousel from "./SpeakersCarousel/SpeakersCarousel";
import Sponsors from "./Sponsors/Sponsors";
-import {Edition} from "../types/types";
+import { Edition } from "../types/types";
+import { useDocumentTitleUpdater } from "../services/useDocumentTitleUpdate";
const StyledContainer = styled.div`
- padding-bottom: 10rem;
+ padding-bottom: 10rem;
- @media only screen and (max-width: ${BIG_BREAKPOINT}px) {
- padding-bottom: 20rem;
- }
+ @media only screen and (max-width: ${BIG_BREAKPOINT}px) {
+ padding-bottom: 20rem;
+ }
`;
export const HomeWrapper2024: FC> = () => {
- const {hash} = useLocation();
- const [edition, setEdition] = useState();
-
- useEventEdition(setEdition);
- React.useEffect(() => {
- document.title = `Home - ${edition?.title} - ${edition?.edition}`;
- if (hash != null && hash !== "") {
- const scroll = document.getElementById(hash.substring(1));
- scroll?.scrollIntoView();
- }
- }, [hash, edition]);
-
- return (
-
-
-
-
-
-
- );
+ const { hash } = useLocation();
+ const [edition, setEdition] = useState();
+
+ useEventEdition(setEdition);
+ React.useEffect(() => {
+ if (hash != null && hash !== "") {
+ const scroll = document.getElementById(hash.substring(1));
+ scroll?.scrollIntoView();
+ }
+ }, [hash, edition]);
+
+ useDocumentTitleUpdater("Home", edition?.edition ?? "2024");
+
+ return (
+
+
+
+
+
+
+ );
};
diff --git a/src/2024/SpeakerDetail/SpeakerDetail.tsx b/src/2024/SpeakerDetail/SpeakerDetail.tsx
index eabc6c61..1b655449 100644
--- a/src/2024/SpeakerDetail/SpeakerDetail.tsx
+++ b/src/2024/SpeakerDetail/SpeakerDetail.tsx
@@ -1,172 +1,164 @@
-import {BIG_BREAKPOINT} from "../../constants/BreakPoints";
+import { BIG_BREAKPOINT } from "../../constants/BreakPoints";
-import {FC, Suspense, useEffect} from "react";
+import { FC, Suspense } from "react";
import MoreThanIcon from "../../assets/images/MoreThanBlueIcon.svg";
import LessThan from "../../assets/images/MoreThanIcon.svg";
import SlashesWhite from "../../assets/images/SlashesWhite.svg";
import linkedinIcon from "../../assets/images/linkedinIcon.svg";
import twitterIcon from "../../assets/images/twitterIcon.svg";
-import {useWindowSize} from "react-use";
+import { useWindowSize } from "react-use";
-import {ROUTE_SPEAKERS, ROUTE_TALK_DETAIL} from "../../constants/routes";
-import {Link} from "react-router";
-import {Color} from "../../styles/colors";
+import { ROUTE_SPEAKERS, ROUTE_TALK_DETAIL } from "../../constants/routes";
+import { Link } from "react-router";
+import { Color } from "../../styles/colors";
import conferenceData from "../../data/2024.json";
import {
- StyledDetailsContainer,
- StyledFlexCol,
- StyledImageContainer,
- StyledInfoContainer,
- StyledLink,
- StyledMoreThanIcon,
- StyledMoreThanIconContainer,
- StyledName,
- StyledNameContainer,
- StyledRightContainer,
- StyledSlashes,
- StyledSocialMediaContainer,
- StyledSocialMediaIcon,
- StyledSpeakerDescription,
- StyledSpeakerDetailContainer,
- StyledSpeakerImg,
- StyledSpeakerTitle
+ StyledDetailsContainer,
+ StyledFlexCol,
+ StyledImageContainer,
+ StyledInfoContainer,
+ StyledLink,
+ StyledMoreThanIcon,
+ StyledMoreThanIconContainer,
+ StyledName,
+ StyledNameContainer,
+ StyledRightContainer,
+ StyledSlashes,
+ StyledSocialMediaContainer,
+ StyledSocialMediaIcon,
+ StyledSpeakerDescription,
+ StyledSpeakerDetailContainer,
+ StyledSpeakerImg,
+ StyledSpeakerTitle,
} from "../../views/SpeakerDetail/Speaker.style";
-import {
- StyledTalkDescription
-} from "../../views/SpeakerDetail/SpeakerDetail.style";
-import {ISpeaker} from "../../types/speakers";
+import { StyledTalkDescription } from "../../views/SpeakerDetail/SpeakerDetail.style";
+import { ISpeaker } from "../../types/speakers";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
interface ISpeakerDetailProps {
- speaker: ISpeaker;
+ speaker: ISpeaker;
}
-const SpeakerDetail: FC> = ({speaker}) => {
- const {width} = useWindowSize();
+const SpeakerDetail: FC> = ({
+ speaker,
+}) => {
+ const { width } = useWindowSize();
- useEffect(() => {
- document.title = `${speaker.fullName} β ${conferenceData.title} β ${conferenceData.edition}`;
- }, [speaker.fullName]);
+ useDocumentTitleUpdater(speaker.fullName, conferenceData.edition);
- const hasSessions = (): boolean =>
- (speaker.sessions && speaker.sessions.length > 0) || false;
+ const hasSessions = (): boolean =>
+ (speaker.sessions && speaker.sessions.length > 0) || false;
- return (
-
-
- {width > BIG_BREAKPOINT && (
-
- loading
}>
-
-
-
- {speaker.twitterUrl && (
-
-
-
- )}
- {speaker.linkedInUrl && (
-
-
-
- )}
-
-
- )}
-
-
- {speaker.fullName}
- {width < BIG_BREAKPOINT && (
- <>
- loading}>
-
-
-
- {speaker.twitterUrl && (
-
-
-
- )}
- {speaker.linkedInUrl && (
-
-
-
- )}
-
- >
- )}
-
-
-
-
- {speaker.tagLine}
- {speaker.bio}
+ return (
+
+
+ {width > BIG_BREAKPOINT && (
+
+ loading}>
+
+
+
+ {speaker.twitterUrl && (
+
+
+
+ )}
+ {speaker.linkedInUrl && (
+
+
+
+ )}
+
+
+ )}
+
+
+ {speaker.fullName}
+ {width < BIG_BREAKPOINT && (
+ <>
+ loading}>
+
+
+
+ {speaker.twitterUrl && (
+
+
+
+ )}
+ {speaker.linkedInUrl && (
+
+
+
+ )}
+
+ >
+ )}
+
+
+
+
+ {speaker.tagLine}
+ {speaker.bio}
- {hasSessions() && (
- <>
- Sessions
-
- {speaker?.sessions?.map((session) => (
-
-
-
-
- {session.name}
-
-
-
- ))}
-
- >
- )}
+ {hasSessions() && (
+ <>
+ Sessions
+
+ {speaker?.sessions?.map((session) => (
+
+
+
+
+ {session.name}
+
+
+
+ ))}
+
+ >
+ )}
-
- Go back
-
-
-
-
-
-
-
-
-
- );
+
+ Go back
+
+
+
+
+
+
+
+
+
+ );
};
export default SpeakerDetail;
diff --git a/src/2024/SpeakerDetail/SpeakerDetailContainer2024.tsx b/src/2024/SpeakerDetail/SpeakerDetailContainer2024.tsx
index 7c6177ef..70c6e072 100644
--- a/src/2024/SpeakerDetail/SpeakerDetailContainer2024.tsx
+++ b/src/2024/SpeakerDetail/SpeakerDetailContainer2024.tsx
@@ -5,10 +5,11 @@ import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import SpeakerDetail from "./SpeakerDetail";
import { useParams } from "react-router";
import conferenceData from "../../data/2024.json";
-import * as Sentry from "@sentry/react";
import { StyledContainer } from "../../views/SpeakerDetail/Speaker.style";
import { StyledWaveContainer } from "../../views/Talks/Talks.style";
import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const SpeakerDetailContainer2024: FC> = () => {
const { id } = useParams<{ id: string }>();
@@ -18,14 +19,9 @@ const SpeakerDetailContainer2024: FC> = () => {
id,
);
- if (error) {
- Sentry.captureException(error);
- }
- React.useEffect(() => {
- if (data) {
- document.title = `${data[0]?.fullName} - DevBcn - ${conferenceData.edition}`;
- }
- }, [id, data]);
+ useSentryErrorReport(error);
+
+ useDocumentTitleUpdater(data?.[0]?.fullName ?? "", conferenceData.edition);
return (
diff --git a/src/2024/Speakers/Speakers2024.tsx b/src/2024/Speakers/Speakers2024.tsx
index 751fb53e..830bbb11 100644
--- a/src/2024/Speakers/Speakers2024.tsx
+++ b/src/2024/Speakers/Speakers2024.tsx
@@ -1,6 +1,6 @@
import { MOBILE_BREAKPOINT } from "../../constants/BreakPoints";
import { Color } from "../../styles/colors";
-import React, { FC, useCallback, useEffect } from "react";
+import React, { FC, useCallback } from "react";
import LessThanBlueIcon from "../../assets/images/LessThanBlueIcon.svg";
import MoreThanBlueIcon from "../../assets/images/MoreThanBlueIcon.svg";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
@@ -19,10 +19,11 @@ import {
import webData from "../../data/2024.json";
import Button from "../../components/UI/Button";
import { gaEventTracker } from "../../components/analytics/Analytics";
-import * as Sentry from "@sentry/react";
import { SpeakerCard } from "../../views/Speakers/components/SpeakersCard";
import { ISpeaker } from "../../types/speakers";
import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const LessThanGreaterThan = (props: { width: number }) => (
<>
@@ -45,17 +46,13 @@ const Speakers2024: FC> = () => {
`${webData.sessionizeUrl}/view/Speakers`,
);
- if (error) {
- Sentry.captureException(error);
- }
+ useSentryErrorReport(error);
const trackCFP = useCallback(() => {
gaEventTracker("CFP", "CFP");
}, []);
- useEffect(() => {
- document.title = `Speakers β ${webData.title} β ${webData.edition}`;
- });
+ useDocumentTitleUpdater("Speakers", webData.edition);
const CFPStartDay = new Date(webData.cfp.startDay);
const CFPEndDay = new Date(webData.cfp.endDay);
diff --git a/src/2024/SpeakersCarousel/SpeakerSwiper.tsx b/src/2024/SpeakersCarousel/SpeakerSwiper.tsx
index e0ad9550..661d401e 100644
--- a/src/2024/SpeakersCarousel/SpeakerSwiper.tsx
+++ b/src/2024/SpeakersCarousel/SpeakerSwiper.tsx
@@ -6,10 +6,14 @@ import "swiper/swiper-bundle.min.css";
import "./SpeakersCarousel.scss";
import { Link } from "react-router";
import conferenceData from "../../data/2024.json";
-import * as Sentry from "@sentry/react";
import { Color } from "../../styles/colors";
import { ROUTE_SPEAKER_DETAIL } from "../../constants/routes";
import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import {ISpeaker} from "../../types/speakers";
+import {
+ shuffleArray
+} from "../../2023/Home/components/SpeakersCarousel/SpeakerSwiper";
const StyledSlideImage = styled.img`
display: block;
@@ -64,9 +68,7 @@ const swiperSpeakers = React.useMemo(() => {
return speakersCopy.slice(0, 20);
}, [data]);
- if (error) {
- Sentry.captureException(error);
- }
+ useSentryErrorReport(error);
return (
<>
diff --git a/src/2024/TalkDetail/MeetingDetail.tsx b/src/2024/TalkDetail/MeetingDetail.tsx
index 163e0ab5..4be33684 100644
--- a/src/2024/TalkDetail/MeetingDetail.tsx
+++ b/src/2024/TalkDetail/MeetingDetail.tsx
@@ -1,301 +1,301 @@
import {
- BIG_BREAKPOINT,
- LARGE_BREAKPOINT,
- MOBILE_BREAKPOINT,
+ BIG_BREAKPOINT,
+ LARGE_BREAKPOINT,
+ MOBILE_BREAKPOINT,
} from "../../constants/BreakPoints";
-import {Color} from "../../styles/colors";
-import React, {FC, Suspense, useEffect} from "react";
+import { Color } from "../../styles/colors";
+import React, { FC, Suspense } from "react";
import LessThanIconWhite from "../../assets/images/LessThanIconWhite.svg";
import LessThanIcon from "../../assets/images/LessThanBlueIcon.svg";
import MoreThanIcon from "../../assets/images/MoreThanBlueIcon.svg";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
-import {useWindowSize} from "react-use";
+import { useWindowSize } from "react-use";
-import {Link} from "react-router";
+import { Link } from "react-router";
import {
- ROUTE_2024_SPEAKER_DETAIL,
- ROUTE_2024_TALKS,
+ ROUTE_2024_SPEAKER_DETAIL,
+ ROUTE_2024_TALKS,
} from "../../constants/routes";
import conferenceData from "../../data/2024.json";
-import {Tag} from "../../components/Tag/Tag";
+import { Tag } from "../../components/Tag/Tag";
import styled from "styled-components";
-import {AddToCalendarButton} from "add-to-calendar-button-react";
+import { AddToCalendarButton } from "add-to-calendar-button-react";
import {
- StyledContainer,
- StyledDetailsContainer,
- StyledFlexCol,
- StyledName,
- StyledNameContainer,
- StyledRightContainer,
- StyledSpeakerDetailContainer
+ StyledContainer,
+ StyledDetailsContainer,
+ StyledFlexCol,
+ StyledName,
+ StyledNameContainer,
+ StyledRightContainer,
+ StyledSpeakerDetailContainer,
} from "../../views/SpeakerDetail/Speaker.style";
import {
- StyledDescription,
- StyledExtraInfo,
- StyledLessThan,
- StyledMeetingTitleContainer,
- StyledTitleImg,
- StyledVideoContainer,
- StyledVideoTagsContainer
+ StyledDescription,
+ StyledExtraInfo,
+ StyledLessThan,
+ StyledMeetingTitleContainer,
+ StyledTitleImg,
+ StyledVideoContainer,
+ StyledVideoTagsContainer,
} from "../../views/MeetingDetail/Style.MeetingDetail";
-import {StyledTitle} from "../Home/Style.Home";
-import {ISpeaker} from "../../types/speakers";
-import {IMeeting} from "../../types/sessions";
+import { StyledTitle } from "../Home/Style.Home";
+import { ISpeaker } from "../../types/speakers";
+import { IMeeting } from "../../types/sessions";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const getVideoHeight = (windowWidth: number) => {
- let videoHeight;
- if (windowWidth < MOBILE_BREAKPOINT) {
- videoHeight = 250;
- } else if (windowWidth >= MOBILE_BREAKPOINT && windowWidth < BIG_BREAKPOINT) {
- videoHeight = 300;
- } else if (windowWidth >= BIG_BREAKPOINT && windowWidth < LARGE_BREAKPOINT) {
- videoHeight = 450;
- } else {
- videoHeight = 600;
- }
+ let videoHeight;
+ if (windowWidth < MOBILE_BREAKPOINT) {
+ videoHeight = 250;
+ } else if (windowWidth >= MOBILE_BREAKPOINT && windowWidth < BIG_BREAKPOINT) {
+ videoHeight = 300;
+ } else if (windowWidth >= BIG_BREAKPOINT && windowWidth < LARGE_BREAKPOINT) {
+ videoHeight = 450;
+ } else {
+ videoHeight = 600;
+ }
- return videoHeight.toString();
+ return videoHeight.toString();
};
const leftVariants = {
- initial: {
- x: -100,
- opacity: 0,
- },
- animate: {
- x: 0,
- opacity: 1,
- },
+ initial: {
+ x: -100,
+ opacity: 0,
+ },
+ animate: {
+ x: 0,
+ opacity: 1,
+ },
};
const rightVariants = {
- initial: {
- x: 100,
- opacity: 0,
- },
- animate: {
- x: 0,
- opacity: 1,
- },
+ initial: {
+ x: 100,
+ opacity: 0,
+ },
+ animate: {
+ x: 0,
+ opacity: 1,
+ },
};
const downVariants = {
- initial: {
- y: 100,
- opacity: 0,
- },
- animate: {
- y: 0,
- opacity: 1,
- },
+ initial: {
+ y: 100,
+ opacity: 0,
+ },
+ animate: {
+ y: 0,
+ opacity: 1,
+ },
};
const opacityVariants = {
- initial: {
- opacity: 0,
- },
- animate: {
- opacity: 1,
- transition: {
- duration: 1,
- },
+ initial: {
+ opacity: 0,
+ },
+ animate: {
+ opacity: 1,
+ transition: {
+ duration: 1,
},
+ },
};
export const StyledVoteTalkLink = styled.a`
- text-decoration: none;
- color: ${Color.BLACK_BLUE};
- font-size: 0.8rem;
+ text-decoration: none;
+ color: ${Color.BLACK_BLUE};
+ font-size: 0.8rem;
`;
interface IMeetingDetailProps {
- meeting: IMeeting;
- speakers?: ISpeaker[];
+ meeting: IMeeting;
+ speakers?: ISpeaker[];
}
type MyType = {
- urlName?: string;
- videoUrl?: string;
- level?: string;
- videoTags?: string[];
- speakers?: ISpeaker[];
- description: string;
- language?: string;
- title: string;
- type?: string;
- track?: string;
+ urlName?: string;
+ videoUrl?: string;
+ level?: string;
+ videoTags?: string[];
+ speakers?: ISpeaker[];
+ description: string;
+ language?: string;
+ title: string;
+ type?: string;
+ track?: string;
};
const MeetingDetail: FC> = ({
- meeting,
- speakers: mySpeakers,
- }) => {
- const {width} = useWindowSize();
+ meeting,
+ speakers: mySpeakers,
+}) => {
+ const { width } = useWindowSize();
- useEffect(() => {
- document.title = `${meeting.title} β ${conferenceData.title} β ${conferenceData.edition}`;
- }, [meeting.title]);
+ useDocumentTitleUpdater(meeting.title, conferenceData.edition);
- const finalMeetingInfo: MyType = {
- ...meeting,
- speakers: mySpeakers,
- };
+ const finalMeetingInfo: MyType = {
+ ...meeting,
+ speakers: mySpeakers,
+ };
- return (
-
-
-
-
-
- / {meeting.title}
- Description
- {meeting.description}
-
- {`${meeting.type} ${meeting.level}`}
- Track:
- {meeting.track}
+ return (
+
+
+
+
+
+ / {meeting.title}
+ Description
+ {meeting.description}
+
+ {`${meeting.type} ${meeting.level}`}
+ Track:
+ {meeting.track}
- {meeting.slidesURL !== "" && (
-
-
-
-
-
- {" "}
- Slides
-
-
- )}
-
-
-
-
-
- {meeting.videoUrl && (
-
- )}
-
- {meeting.videoTags?.map((tag) => )}
-
-
-
- π³οΈ Vote this talk
-
-
-
-
-
-
-
-
- {finalMeetingInfo.speakers?.map((speaker) => (
-
- loading}>
-
-
-
-
- {speaker.fullName}
-
-
-
- ))}
-
-
-
-
-
-
+
- Go back
- {" "}
-
-
-
- );
+
+ {" "}
+ Slides
+
+
+ )}
+
+
+
+
+
+ {meeting.videoUrl && (
+
+ )}
+
+ {meeting.videoTags?.map((tag) => )}
+
+
+
+ π³οΈ Vote this talk
+
+
+
+
+
+
+
+
+ {finalMeetingInfo.speakers?.map((speaker) => (
+
+ loading}>
+
+
+
+
+ {speaker.fullName}
+
+
+
+ ))}
+
+
+
+
+
+
+ Go back
+ {" "}
+
+
+
+ );
};
export default MeetingDetail;
diff --git a/src/2024/TalkDetail/MeetingDetailContainer.tsx b/src/2024/TalkDetail/MeetingDetailContainer.tsx
index 0e049ca2..da60e456 100644
--- a/src/2024/TalkDetail/MeetingDetailContainer.tsx
+++ b/src/2024/TalkDetail/MeetingDetailContainer.tsx
@@ -1,18 +1,19 @@
import { Color } from "../../styles/colors";
-import React, { FC, useEffect } from "react";
+import React, { FC } from "react";
import NotFoundError from "../../components/NotFoundError/NotFoundError";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import styled from "styled-components";
import { useParams } from "react-router";
import conferenceData from "../../data/2024.json";
import { useFetchTalksById } from "../Talks/UseFetchTalks";
-import * as Sentry from "@sentry/react";
import MeetingDetail from "./MeetingDetail";
import { ISpeaker } from "../../types/speakers";
import { Session } from "../../types/sessions";
import { sessionAdapter } from "../../services/sessionsAdapter";
import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledContainer = styled.div`
background-color: ${Color.WHITE};
@@ -33,18 +34,11 @@ const MeetingDetailContainer: FC> = () => {
const sessionSpeakers: ISpeaker[] | undefined = speakerData?.filter(
(speaker) => talkSpeakers?.includes(speaker.id),
);
+ useDocumentTitleUpdater(data?.at(0)?.title ?? "", conferenceData.edition);
const adaptedMeeting = sessionAdapter(data?.at(0));
- useEffect(() => {
- document.title = `${data?.at(0)?.title} - DevBcn - ${
- conferenceData.edition
- }`;
- }, [data]);
-
- if (error) {
- Sentry.captureException(error);
- }
+ useSentryErrorReport(error);
return (
diff --git a/src/2024/Talks/LiveView.tsx b/src/2024/Talks/LiveView.tsx
index e0240b0e..9028a490 100644
--- a/src/2024/Talks/LiveView.tsx
+++ b/src/2024/Talks/LiveView.tsx
@@ -1,65 +1,60 @@
-import React, {FC, useCallback, useEffect, useMemo} from "react";
-import {useFetchLiveView} from "./UseFetchTalks";
+import React, { FC, useCallback, useMemo } from "react";
+import { useFetchLiveView } from "./UseFetchTalks";
import Loading from "../../components/Loading/Loading";
import conference from "../../data/2024.json";
-import * as Sentry from "@sentry/react";
-import {UngroupedSession} from "../../views/Talks/liveView.types";
-import {TalkCard} from "../../views/Talks/components/TalkCard";
-import {talkCardAdapter} from "../../views/Talks/TalkCardAdapter";
-import {StyledMain} from "../../views/Talks/Talks.style";
+import { UngroupedSession } from "../../views/Talks/liveView.types";
+import { TalkCard } from "../../views/Talks/components/TalkCard";
+import { talkCardAdapter } from "../../views/Talks/TalkCardAdapter";
+import { StyledMain } from "../../views/Talks/Talks.style";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const LiveView: FC> = () => {
- const {isLoading, error, data} = useFetchLiveView();
- const today = useMemo(() => new Date(), []);
-
- const isBetween = useCallback(
- (today: Date, startDate: string, endDate: string): boolean => {
- return today >= new Date(startDate) && today <= new Date(endDate);
- },
- [],
- );
-
- const getPredicate = useCallback(
- () => (session: UngroupedSession) =>
- isBetween(today, session.startsAt, session.endsAt),
- [today, isBetween],
- );
-
- const filteredTalks = useMemo(() => {
- return data?.sessions?.filter(getPredicate());
- }, [data, getPredicate]);
-
- useEffect(() => {
- document.title = `Live view - ${conference.title} - ${conference.edition} Edition`;
- }, []);
-
- useEffect(() => {
- if (error) {
- Sentry.captureException(error);
- }
- }, [error]);
-
- return (
-
-
-
- {conference.title} - {conference.edition} Edition
-
-
- {isLoading && }
- Live Schedule
- {!isBetween(today, conference.startDay, conference.endDay) && (
- The live schedule is not ready yet
- )}
- {filteredTalks?.map((session) => (
-
- ))}
-
- );
+ const { isLoading, error, data } = useFetchLiveView();
+ const today = useMemo(() => new Date(), []);
+
+ const isBetween = useCallback(
+ (today: Date, startDate: string, endDate: string): boolean => {
+ return today >= new Date(startDate) && today <= new Date(endDate);
+ },
+ [],
+ );
+
+ const getPredicate = useCallback(
+ () => (session: UngroupedSession) =>
+ isBetween(today, session.startsAt, session.endsAt),
+ [today, isBetween],
+ );
+
+ const filteredTalks = useMemo(() => {
+ return data?.sessions?.filter(getPredicate());
+ }, [data, getPredicate]);
+
+ useDocumentTitleUpdater("Live view", conference.edition);
+
+ useSentryErrorReport(error);
+
+ return (
+
+
+
+ {conference.title} - {conference.edition} Edition
+
+
+ {isLoading && }
+ Live Schedule
+ {!isBetween(today, conference.startDay, conference.endDay) && (
+ The live schedule is not ready yet
+ )}
+ {filteredTalks?.map((session) => (
+
+ ))}
+
+ );
};
export default LiveView;
diff --git a/src/2024/Talks/Talks2024.tsx b/src/2024/Talks/Talks2024.tsx
index c860a590..566572ee 100644
--- a/src/2024/Talks/Talks2024.tsx
+++ b/src/2024/Talks/Talks2024.tsx
@@ -7,7 +7,6 @@ import {Color} from "../../styles/colors";
import conferenceData from "../../data/2024.json";
import {useFetchTalks} from "./UseFetchTalks";
-import * as Sentry from "@sentry/react";
import {Dropdown, DropdownChangeEvent} from "primereact/dropdown";
import "primereact/resources/primereact.min.css";
import "primereact/resources/themes/lara-light-indigo/theme.css";
@@ -20,6 +19,8 @@ import {
StyledWaveContainer
} from "../../views/Talks/Talks.style";
import TrackInformation from "../../components/Talk/TrackInformation";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
interface TrackInfo {
name: string;
@@ -33,112 +34,108 @@ const Talks2024: FC> = () => {
const {isLoading, error, data} = useFetchTalks();
useEffect(() => {
- const sessionSelectedGroupCode =
- sessionStorage.getItem("selectedGroupCode");
- const sessionSelectedGroupName =
- sessionStorage.getItem("selectedGroupName");
+ const sessionSelectedGroupCode =
+ sessionStorage.getItem("selectedGroupCode");
+ const sessionSelectedGroupName =
+ sessionStorage.getItem("selectedGroupName");
- document.title = `Talks - ${conferenceData.title} - ${conferenceData.edition}`;
-
- if (sessionSelectedGroupCode && sessionSelectedGroupName) {
- setSelectedGroupId({
- name: sessionSelectedGroupName,
- code: sessionSelectedGroupCode,
- });
- }
+ if (sessionSelectedGroupCode && sessionSelectedGroupName) {
+ setSelectedGroupId({
+ name: sessionSelectedGroupName,
+ code: sessionSelectedGroupCode,
+ });
+ }
}, []);
- if (error) {
- Sentry.captureException(error);
- }
+ useDocumentTitleUpdater("Talks", conferenceData.edition);
+
+ useSentryErrorReport(error);
const dropDownOptions = [
- {name: "All Tracks", code: undefined},
- ...(data !== undefined
- ? data.flatMap((group) => ({
- code: group.groupId.toString(),
- name: group.groupName,
- }))
- : []),
+ { name: "All Tracks", code: undefined },
+ ...(data !== undefined
+ ? data.flatMap((group) => ({
+ code: group.groupId.toString(),
+ name: group.groupName,
+ }))
+ : []),
];
const filteredTalks = selectedGroupId?.code
- ? data?.filter((talk) => talk.groupId.toString() === selectedGroupId.code)
- : data;
+ ? data?.filter((talk) => talk.groupId.toString() === selectedGroupId.code)
+ : data;
const onChangeSelectedTrack = (e: DropdownChangeEvent) => {
- const value = e.value;
- setSelectedGroupId(value || null);
- sessionStorage.setItem("selectedGroupCode", value?.code || "");
- sessionStorage.setItem("selectedGroupName", value?.name || "");
+ const value = e.value;
+ setSelectedGroupId(value || null);
+ sessionStorage.setItem("selectedGroupCode", value?.code || "");
+ sessionStorage.setItem("selectedGroupName", value?.name || "");
};
return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {isLoading && Loading }
- {conferenceData.hideTalks ? (
-
- No talks selected yet. Keep in touch in our social
- media for
- upcoming announcements
-
- ) : (
- filteredTalks &&
- Array.isArray(filteredTalks) && (
- <>
-
-
- Filter by Track:
-
-
-
- {filteredTalks.map((track) => (
-
- ))}
- >
- )
- )}
-
-
-
- >
+ color={Color.WHITE}
+ />
+
+
+
+
+
+
+
+
+
+
+
+ {isLoading && Loading }
+ {conferenceData.hideTalks ? (
+
+ No talks selected yet. Keep in touch in our social media for
+ upcoming announcements
+
+ ) : (
+ filteredTalks &&
+ Array.isArray(filteredTalks) && (
+ <>
+
+
+ Filter by Track:
+
+
+
+ {filteredTalks.map((track) => (
+
+ ))}
+ >
+ )
+ )}
+
+
+
+ >
);
};
diff --git a/src/components/NotFoundError/NotFoundError.tsx b/src/components/NotFoundError/NotFoundError.tsx
index 72b76e2f..32cf74be 100644
--- a/src/components/NotFoundError/NotFoundError.tsx
+++ b/src/components/NotFoundError/NotFoundError.tsx
@@ -1,10 +1,11 @@
import { Color } from "../../styles/colors";
-import { FC, useEffect } from "react";
+import { FC } from "react";
import { Link } from "react-router";
import SectionWrapper from "../SectionWrapper/SectionWrapper";
import styled from "styled-components";
import ActionButtons from "../../views/Home/components/ActionButtons/ActionButtons";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledContainer = styled.div`
display: flex;
@@ -34,10 +35,10 @@ interface INotFoundErrorProps {
message?: string;
}
-const NotFoundError: FC> = ({ message = "Page" }) => {
- useEffect(() => {
- document.title = "DevBcn - page not Found";
- });
+const NotFoundError: FC> = ({
+ message = "Page",
+}) => {
+ useDocumentTitleUpdater("Page not Found", "");
return (
diff --git a/src/components/Swiper/SpeakerSwiper.tsx b/src/components/Swiper/SpeakerSwiper.tsx
index f22ff077..475bcb2e 100644
--- a/src/components/Swiper/SpeakerSwiper.tsx
+++ b/src/components/Swiper/SpeakerSwiper.tsx
@@ -1,111 +1,111 @@
-import React, {FC} from "react";
-import {Autoplay, Parallax} from "swiper";
-import {Swiper, SwiperSlide} from "swiper/react";
+import React, { FC } from "react";
+import { Autoplay,Parallax } from "swiper";
+import { Swiper, SwiperSlide } from "swiper/react";
import styled from "styled-components";
-import {Color} from "../../styles/colors";
+import { Color } from "../../styles/colors";
import "swiper/swiper-bundle.min.css";
import "../../views/Home/components/SpeakersCarousel/SpeakersCarousel.scss";
import conferenceData from "../../data/2025.json";
-import {useFetchSpeakers} from "../../views/Speakers/UseFetchSpeakers";
-import * as Sentry from "@sentry/react";
-import {ISpeaker} from "../../types/speakers";
-import {ROUTE_SPEAKER_DETAIL} from "../../constants/routes";
-import {Link} from "react-router";
+import { useFetchSpeakers } from "../../views/Speakers/UseFetchSpeakers";
+import { ISpeaker } from "../../types/speakers";
+import { ROUTE_SPEAKER_DETAIL } from "../../constants/routes";
+import { Link } from "react-router";
+import { useSentryErrorReport } from "../../../../services/useSentryErrorReport";
+import { shuffleArray } from "../../../../2023/Home/components/SpeakersCarousel/SpeakerSwiper";
const StyledSlideImage = styled.img`
- display: block;
- width: 100%;
- aspect-ratio: 1/1;
- border-radius: 10px;
+ display: block;
+ width: 100%;
+ aspect-ratio: 1/1;
+ border-radius: 10px;
`;
const StyledSlideContain = styled.div`
- position: absolute;
- bottom: 0;
- background: ${Color.MAGENTA};
- background: linear-gradient(
- to bottom,
- rgba(255, 0, 0, 0),
- ${Color.DARK_BLUE}
- );
- padding: 0.5rem 0.25rem;
- min-width: 100%;
+ position: absolute;
+ bottom: 0;
+ background: ${Color.MAGENTA};
+ background: linear-gradient(
+ to bottom,
+ rgba(255, 0, 0, 0),
+ ${Color.DARK_BLUE}
+ );
+ padding: 0.5rem 0.25rem;
+ min-width: 100%;
`;
const StyledSlideText = styled.p`
- font-size: 0.875rem;
- color: white;
+ font-size: 0.875rem;
+ color: white;
`;
const SpeakerSwiper: FC> = () => {
- const {isLoading, data, error} = useFetchSpeakers();
+ const { isLoading, data, error } = useFetchSpeakers(
+ conferenceData.sessionizeUrl,
+ );
+ const cachedSpeakers: ISpeaker[] = data
+ ? shuffleArray(data).slice(0, 20)
+ : [];
- const cachedSpeakers = React.useMemo(() => {
- return data?.toSorted(() => 0.5 - Math.random()).slice(0, 20);
- }, [data]);
+ useSentryErrorReport(error);
- if (error) {
- Sentry.captureException(error);
- }
-
- return (
- <>
- {isLoading && Loading
}
- {conferenceData.carrousel.enabled && cachedSpeakers && (
-
- {cachedSpeakers.map((speaker:ISpeaker) => (
-
-
-
-
- {speaker.fullName}
-
-
-
- ))}
-
- )}
- >
- );
+ return (
+ <>
+ {isLoading && Loading
}
+ {conferenceData.carrousel.enabled && cachedSpeakers && (
+
+ {cachedSpeakers.map((speaker: ISpeaker) => (
+
+
+
+
+ {speaker.fullName}
+
+
+
+ ))}
+
+ )}
+ >
+ );
};
export default SpeakerSwiper;
diff --git a/src/services/useDocumentTitleUpdate.ts b/src/services/useDocumentTitleUpdate.ts
new file mode 100644
index 00000000..74f63ae7
--- /dev/null
+++ b/src/services/useDocumentTitleUpdate.ts
@@ -0,0 +1,7 @@
+import React from "react";
+
+export const useDocumentTitleUpdater = (title: string, year: string) => {
+ React.useEffect(() => {
+ document.title = `${title} β DevBcn - Barcelona Developers Conference β ${year}`;
+ }, [title, year]);
+};
diff --git a/src/services/useSentryErrorReport.ts b/src/services/useSentryErrorReport.ts
new file mode 100644
index 00000000..e44b641f
--- /dev/null
+++ b/src/services/useSentryErrorReport.ts
@@ -0,0 +1,7 @@
+import * as Sentry from "@sentry/react";
+
+export const useSentryErrorReport = (error: unknown) => {
+ if (error instanceof Error) {
+ Sentry.captureException(error);
+ }
+};
diff --git a/src/views/About/About.tsx b/src/views/About/About.tsx
index 1196ae85..dad94c65 100644
--- a/src/views/About/About.tsx
+++ b/src/views/About/About.tsx
@@ -16,6 +16,7 @@ import {
} from "../Speakers/Speakers.style";
import { StyledMarginBottom } from "../Talks/Talks.style";
import data from "../../data/2024.json";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledUsersWrapper = styled.div`
padding-top: 5rem;
@@ -42,9 +43,7 @@ const StyledLink = styled.a`
const About: FC> = () => {
const { width } = useWindowSize();
- React.useEffect(() => {
- document.title = `About us β ${data.title} β ${data.edition}`;
- }, []);
+ useDocumentTitleUpdater("About us", data.edition);
return (
diff --git a/src/views/Attendee/AttendeeInformation.tsx b/src/views/Attendee/AttendeeInformation.tsx
index 0bfac404..8f3fd65c 100644
--- a/src/views/Attendee/AttendeeInformation.tsx
+++ b/src/views/Attendee/AttendeeInformation.tsx
@@ -1,9 +1,10 @@
-import { FC, useEffect } from "react";
+import { FC } from "react";
import { Color } from "../../styles/colors";
import styled from "styled-components";
import { BIG_BREAKPOINT } from "../../constants/BreakPoints";
import data from "../../data/2024.json";
import { format } from "date-fns";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const PrePartyImg = styled.img`
{
@@ -64,9 +65,7 @@ const SectionWrapper = styled.div`
const AttendeeInformation: FC> = () => {
const formattedDate = format(new Date(data.startDay), "EEEE, MMMM do");
- useEffect(() => {
- document.title = `Attendee information β ${data.title} β ${data.edition}`;
- }, []);
+ useDocumentTitleUpdater("Attendee information", data.edition);
return (
diff --git a/src/views/Cfp/CfpSection.test.tsx b/src/views/Cfp/CfpSection.test.tsx
index 509eab3e..4cee0f4b 100644
--- a/src/views/Cfp/CfpSection.test.tsx
+++ b/src/views/Cfp/CfpSection.test.tsx
@@ -9,7 +9,7 @@ describe("CfpSection", () => {
it("sets document title on mount", () => {
render( );
expect(document.title).toBe(
- `CFP Committee β ${conferenceData.title}β ${conferenceData.edition}`,
+ `CFP Committee β DevBcn - Barcelona Developers Conference β ${conferenceData.edition}`,
);
});
diff --git a/src/views/Cfp/CfpSection.tsx b/src/views/Cfp/CfpSection.tsx
index 7e64f274..70854254 100644
--- a/src/views/Cfp/CfpSection.tsx
+++ b/src/views/Cfp/CfpSection.tsx
@@ -22,6 +22,7 @@ import {
import conferenceData from "../../data/2025.json";
import {CfpTrackProps, data} from "./CfpData";
import {MemberName, TrackName} from "./Cfp.style";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
export const CfpTrackComponent: FC> = ({
track,
@@ -63,42 +64,43 @@ export const CfpTrackComponent: FC> = ({
);
const CfpSection: FC> = () => {
- const {width} = useWindowSize();
- React.useEffect(() => {
- document.title = `CFP Committee β ${conferenceData.title} β ${conferenceData.edition}`;
- }, []);
+ const { width } = useWindowSize();
- const isCFPCommitteeReady = (): boolean => data.every((track) => track.members.length > 0)
+ useDocumentTitleUpdater("CFP Committee", conferenceData.edition);
- return (
- <>
-
-
-
- {width > MOBILE_BREAKPOINT && (
- <>
-
-
- >
- )}
-
- {!isCFPCommitteeReady() &&
- CFP Committee in
- progress }
- {isCFPCommitteeReady() && data.map((track) => (
-
- ))}
-
-
- >
- );
+ color={Color.BLUE}
+ />
+ {width > MOBILE_BREAKPOINT && (
+ <>
+
+
+ >
+ )}
+
+ {!isCFPCommitteeReady() && (
+ CFP Committee in progress
+ )}
+ {isCFPCommitteeReady() &&
+ data.map((track) => (
+
+ ))}
+
+
+ >
+ );
};
export default CfpSection;
diff --git a/src/views/CodeOfConduct/CodeOfConduct.tsx b/src/views/CodeOfConduct/CodeOfConduct.tsx
index fe46fdf3..9bb1e8f8 100644
--- a/src/views/CodeOfConduct/CodeOfConduct.tsx
+++ b/src/views/CodeOfConduct/CodeOfConduct.tsx
@@ -1,4 +1,4 @@
-import React, { FC, useEffect } from "react";
+import React, { FC } from "react";
import TitleSection from "../../components/SectionTitle/TitleSection";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import { BIG_BREAKPOINT, MOBILE_BREAKPOINT } from "../../constants/BreakPoints";
@@ -21,6 +21,7 @@ import {
import { StyledMarginBottom, StyledTagsWrapper } from "../Talks/Talks.style";
import data from "../../data/2024.json";
import { format } from "date-fns";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledWaveContainer = styled.div`
background: ${Color.DARK_BLUE};
@@ -82,9 +83,7 @@ const StyleMoreIcon = styled.img`
export const CodeOfConduct: FC = () => {
const { width } = useWindowSize();
- useEffect(() => {
- document.title = `Code of Conduct β ${data.title} β ${data.edition}`;
- });
+ useDocumentTitleUpdater("Code of Conduct", data.edition);
return (
<>
diff --git a/src/views/Communities/Communities.tsx b/src/views/Communities/Communities.tsx
index 1aa891ed..22896ef8 100644
--- a/src/views/Communities/Communities.tsx
+++ b/src/views/Communities/Communities.tsx
@@ -4,6 +4,7 @@ import TwitterIcon from "../../components/Icons/Twitter";
import { Color } from "../../styles/colors";
import WebsiteIcon from "../../components/Icons/website";
import data from "../../data/2024.json";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const Heading = styled.h1`
{
@@ -93,9 +94,8 @@ const FoSS = styled.div`
`;
const Communities: FC> = () => {
- React.useEffect(() => {
- document.title = `Communities β ${data.title} β ${data.edition}`;
- });
+ useDocumentTitleUpdater("Communities", data.edition);
+
return (
<>
FOSS & Diversity Communities
diff --git a/src/views/Conditions/Conditions.tsx b/src/views/Conditions/Conditions.tsx
index 887cc439..4b846400 100644
--- a/src/views/Conditions/Conditions.tsx
+++ b/src/views/Conditions/Conditions.tsx
@@ -2,42 +2,42 @@ import React, { FC } from "react";
import styled from "styled-components";
import { Color } from "../../styles/colors";
import data from "../../data/2024.json";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledDiv = styled.div`
- {
+{
width: 70%;
margin: 90px auto;
- }
+}
- p {
- text-align: justify;
- margin: 5px;
- }
+ p {
+ text-align: justify;
+ margin: 5px;
+ }
- h1,
- h2,
- h3,
- h4 {
- text-align: left;
- margin-top: 10px;
- margin-bottom: 20px;
- }
+ h1,
+ h2,
+ h3,
+ h4 {
+ text-align: left;
+ margin-top: 10px;
+ margin-bottom: 20px;
+ }
- li {
- margin-left: 30px;
- text-align: left;
- }
+ li {
+ margin-left: 30px;
+ text-align: left;
+ }
- hr {
- color: ${Color.DARK_BLUE};
- margin: 20px;
- }
+ hr {
+ color: ${Color.DARK_BLUE};
+ margin: 20px;
+ }
`;
const Conditions: FC> = () => {
- React.useEffect(() => {
- document.title = `Communities β ${data.title} β ${data.edition}`;
- });
+ useDocumentTitleUpdater("Communities", data.edition);
+
return (
TERMS AND CONDITIONS
diff --git a/src/views/Diversity/Diversity.tsx b/src/views/Diversity/Diversity.tsx
index 2636d196..deb931fb 100644
--- a/src/views/Diversity/Diversity.tsx
+++ b/src/views/Diversity/Diversity.tsx
@@ -1,4 +1,4 @@
-import React, { FC, useEffect } from "react";
+import React, { FC } from "react";
import { Color } from "../../styles/colors";
import data from "../../data/2024.json";
import styled from "styled-components";
@@ -8,6 +8,7 @@ import {
ROUTE_CODE_OF_CONDUCT,
ROUTE_CONDITIONS,
} from "../../constants/routes";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledSection = styled.section`
{
@@ -93,9 +94,7 @@ const StyledParagraph = styled.section`
`;
const Diversity: FC> = () => {
- useEffect(() => {
- document.title = `Diversity β ${data.title} β ${data.edition}`;
- });
+ useDocumentTitleUpdater("Diversity", data.edition);
return (
diff --git a/src/views/Home/HomeWrapper.tsx b/src/views/Home/HomeWrapper.tsx
index 5e87ec7c..646fcfa3 100644
--- a/src/views/Home/HomeWrapper.tsx
+++ b/src/views/Home/HomeWrapper.tsx
@@ -9,6 +9,7 @@ import styled from "styled-components";
import {useLocation} from "react-router";
import {useEventEdition} from "./UseEventEdition";
import {Edition} from "../../types/types";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledContainer = styled.div`
padding-bottom: 10rem;
@@ -24,13 +25,14 @@ const HomeWrapper: FC> = () => {
useEventEdition(setEdition);
React.useEffect(() => {
- document.title = `Home - ${edition?.title} - ${edition?.edition}`;
if (hash != null && hash !== "") {
const scroll = document.getElementById(hash.substring(1));
scroll?.scrollIntoView();
}
}, [hash, edition]);
+ useDocumentTitleUpdater("Home", edition?.edition ?? "2025");
+
return (
diff --git a/src/views/JobOffers/JobOffers.tsx b/src/views/JobOffers/JobOffers.tsx
index 64f308cc..335de69e 100644
--- a/src/views/JobOffers/JobOffers.tsx
+++ b/src/views/JobOffers/JobOffers.tsx
@@ -17,6 +17,7 @@ import {
StyledTitleContainer,
} from "../../styles/JobOffers/JobOffers.Style";
import CompanyOffers from "../../components/JobOffers/CompanyOffers";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const NoOffersAvailable = () => (
<>
@@ -40,9 +41,7 @@ const MoreThanLessThan = (props: { width: number }) => (
const JobOffers: FC> = () => {
const { width } = useWindowSize();
- React.useEffect(() => {
- document.title = `Job Offers - ${data.title} - ${data.edition}`;
- }, []);
+ useDocumentTitleUpdater("Job Offers", data.edition);
return (
diff --git a/src/views/MeetingDetail/MeetingDetail.tsx b/src/views/MeetingDetail/MeetingDetail.tsx
index f56662af..5f38927d 100644
--- a/src/views/MeetingDetail/MeetingDetail.tsx
+++ b/src/views/MeetingDetail/MeetingDetail.tsx
@@ -4,7 +4,7 @@ import {
MOBILE_BREAKPOINT,
} from "../../constants/BreakPoints";
import {Color} from "../../styles/colors";
-import React, {FC, Suspense, useEffect} from "react";
+import React, { FC, Suspense } from "react";
import LessThanIconWhite from "../../assets/images/LessThanIconWhite.svg";
import LessThanIcon from "../../assets/images/LessThanBlueIcon.svg";
import MoreThanIcon from "../../assets/images/MoreThanBlueIcon.svg";
@@ -32,8 +32,9 @@ import {ROUTE_SPEAKER_DETAIL, ROUTE_TALKS} from "../../constants/routes";
import conferenceData from "../../data/2024.json";
import {Tag} from "../../components/Tag/Tag";
import styled from "styled-components";
-import {AddToCalendarButton} from "add-to-calendar-button-react";
-import {IMeetingDetailProps, MyType} from "../../types/sessions";
+import { AddToCalendarButton } from "add-to-calendar-button-react";
+import { IMeetingDetailProps, MyType } from "../../types/sessions";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const getVideoHeight = (windowWidth: number) => {
let videoHeight;
@@ -107,9 +108,7 @@ const MeetingDetail: FC> = ({
}) => {
const { width } = useWindowSize();
- useEffect(() => {
- document.title = `${meeting.title} β ${conferenceData.title} β ${conferenceData.edition}`;
- }, [meeting.title]);
+ useDocumentTitleUpdater(meeting.title, conferenceData.edition);
const finalMeetingInfo: MyType = {
...meeting,
diff --git a/src/views/MeetingDetail/TalkDetailContainer2024.tsx b/src/views/MeetingDetail/TalkDetailContainer2024.tsx
index 5c0a1b60..e0950d14 100644
--- a/src/views/MeetingDetail/TalkDetailContainer2024.tsx
+++ b/src/views/MeetingDetail/TalkDetailContainer2024.tsx
@@ -1,17 +1,18 @@
-import {Color} from "../../styles/colors";
-import React, {FC, useEffect} from "react";
+import { Color } from "../../styles/colors";
+import React, { FC } from "react";
import NotFoundError from "../../components/NotFoundError/NotFoundError";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import styled from "styled-components";
-import {useParams} from "react-router";
+import { useParams } from "react-router";
import conferenceData from "../../data/2024.json";
-import {useFetchTalksById} from "../Talks/UseFetchTalks";
-import * as Sentry from "@sentry/react";
-import {useFetchSpeakers} from "../Speakers/UseFetchSpeakers";
+import { useFetchTalksById } from "../Talks/UseFetchTalks";
+import { useFetchSpeakers } from "../Speakers/UseFetchSpeakers";
import MeetingDetail from "./MeetingDetail";
-import {ISpeaker} from "../../types/speakers";
-import {sessionAdapter} from "../../services/sessionsAdapter";
-import {Session} from "../../types/sessions";
+import { ISpeaker } from "../../types/speakers";
+import { sessionAdapter } from "../../services/sessionsAdapter";
+import { Session } from "../../types/sessions";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledContainer = styled.div`
background-color: ${Color.WHITE};
@@ -35,15 +36,8 @@ const TalkDetailContainer2024: FC> = () => {
const adaptedMeeting = sessionAdapter(data?.at(0));
- useEffect(() => {
- document.title = `${data?.at(0)?.title} - DevBcn - ${
- conferenceData.edition
- }`;
- }, [data]);
-
- if (error) {
- Sentry.captureException(error);
- }
+ useDocumentTitleUpdater(data?.at(0)?.title ?? "", conferenceData.edition);
+ useSentryErrorReport(error);
return (
diff --git a/src/views/Schedule/Schedule.tsx b/src/views/Schedule/Schedule.tsx
index 984b44c7..05a2f566 100644
--- a/src/views/Schedule/Schedule.tsx
+++ b/src/views/Schedule/Schedule.tsx
@@ -10,6 +10,7 @@ import data from "../../data/2024.json";
import * as Sentry from "@sentry/react";
import { Link } from "react-router";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
import {
StyledLessIcon, StyledMoreIcon,
StyledScheduleSection
@@ -19,8 +20,6 @@ const Schedule: FC> = () => {
const { width } = useWindowSize();
React.useEffect(() => {
- document.title = `Schedule β ${data.title} β ${data.edition}`;
-
fetch("https://sessionize.com/api/v2/w8mdb9k5/view/GridSmart")
.then((value) => value.text())
.then((value) => {
@@ -32,6 +31,8 @@ const Schedule: FC> = () => {
.catch((err) => Sentry.captureException(err));
}, []);
+ useDocumentTitleUpdater("Schedule", data.edition);
+
return (
diff --git a/src/views/SessionFeedback/SessionFeedback.tsx b/src/views/SessionFeedback/SessionFeedback.tsx
index 9ef9ee14..73855ec2 100644
--- a/src/views/SessionFeedback/SessionFeedback.tsx
+++ b/src/views/SessionFeedback/SessionFeedback.tsx
@@ -16,6 +16,7 @@ import { Color } from "../../styles/colors";
import { Link } from "react-router";
import { ROUTE_TALK_DETAIL } from "../../constants/routes";
import data from "../../data/2024.json";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const SessionFeedback: FC> = () => {
const bodyTemplate = React.useCallback(
@@ -65,9 +66,7 @@ const SessionFeedback: FC> = () => {
);
- React.useEffect(() => {
- document.title = `Session Feedback β ${data.title} - ${data.edition}`;
- });
+ useDocumentTitleUpdater("Session Feedback", data.edition);
const header = renderHeader();
diff --git a/src/views/SpeakerDetail/SpeakerDetail.tsx b/src/views/SpeakerDetail/SpeakerDetail.tsx
index b90d84f3..26e84432 100644
--- a/src/views/SpeakerDetail/SpeakerDetail.tsx
+++ b/src/views/SpeakerDetail/SpeakerDetail.tsx
@@ -1,6 +1,6 @@
import {BIG_BREAKPOINT} from "../../constants/BreakPoints";
-import {FC, Suspense, useEffect} from "react";
+import { FC, Suspense } from "react";
import MoreThanIcon from "../../assets/images/MoreThanBlueIcon.svg";
import LessThan from "../../assets/images/MoreThanIcon.svg";
import SlashesWhite from "../../assets/images/SlashesWhite.svg";
@@ -31,7 +31,8 @@ import {StyledTalkDescription} from "./SpeakerDetail.style";
import {Link} from "react-router";
import {Color} from "../../styles/colors";
import conferenceData from "../../data/2024.json";
-import {ISpeaker} from "../../types/speakers";
+import { ISpeaker } from "../../types/speakers";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
interface ISpeakerDetailProps {
speaker: ISpeaker;
@@ -40,9 +41,7 @@ interface ISpeakerDetailProps {
const SpeakerDetail: FC> = ({ speaker }) => {
const { width } = useWindowSize();
- useEffect(() => {
- document.title = `${speaker.fullName} β ${conferenceData.title} β ${conferenceData.edition}`;
- }, [speaker.fullName]);
+ useDocumentTitleUpdater(speaker.fullName, conferenceData.edition);
const hasSessions = (): boolean =>
(speaker.sessions && speaker.sessions.length > 0) || false;
diff --git a/src/views/SpeakerDetail/SpeakerDetailContainer.tsx b/src/views/SpeakerDetail/SpeakerDetailContainer.tsx
index f9c21258..11c74623 100644
--- a/src/views/SpeakerDetail/SpeakerDetailContainer.tsx
+++ b/src/views/SpeakerDetail/SpeakerDetailContainer.tsx
@@ -7,45 +7,43 @@ import { useParams } from "react-router";
import { StyledContainer, StyledWaveContainer } from "./Speaker.style";
import conferenceData from "../../data/2024.json";
import { useFetchSpeakers } from "../Speakers/UseFetchSpeakers";
-import * as Sentry from "@sentry/react";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const SpeakerDetailContainer: FC> = () => {
const { id } = useParams<{ id: string }>();
- const { isLoading, error, data } = useFetchSpeakers(id);
-
- if (error) {
- Sentry.captureException(error);
- }
- React.useEffect(() => {
- if (data) {
- document.title = `${data[0]?.fullName} - DevBcn - ${conferenceData.edition}`;
- }
- }, [id, data]);
- return (
-
-
- {isLoading && Loading }
- {!isLoading && data && data.length > 0 ? (
-
- ) : (
- "not found"
- )}
-
-
-
-
-
-
-
+ const { isLoading, error, data } = useFetchSpeakers(
+ conferenceData.sessionizeUrl,
+ id,
);
+
+ useSentryErrorReport(error);
+ useDocumentTitleUpdater(data?.[0]?.fullName ?? "", conferenceData.edition);
+ return (
+
+
+ {isLoading && Loading }
+ {!isLoading && data && data.length > 0 ? (
+
+ ) : (
+ "not found"
+ )}
+
+
+
+
+
+
+
+ );
};
export default SpeakerDetailContainer;
diff --git a/src/views/Speakers/Speakers.tsx b/src/views/Speakers/Speakers.tsx
index 59fafd9c..c141efa1 100644
--- a/src/views/Speakers/Speakers.tsx
+++ b/src/views/Speakers/Speakers.tsx
@@ -1,6 +1,6 @@
import { MOBILE_BREAKPOINT } from "../../constants/BreakPoints";
import { Color } from "../../styles/colors";
-import { FC, useCallback, useEffect } from "react";
+import { FC, useCallback } from "react";
import LessThanBlueIcon from "../../assets/images/LessThanBlueIcon.svg";
import MoreThanBlueIcon from "../../assets/images/MoreThanBlueIcon.svg";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
@@ -21,8 +21,9 @@ import webData from "../../data/2024.json";
import Button from "../../components/UI/Button";
import { gaEventTracker } from "../../components/analytics/Analytics";
import { useFetchSpeakers } from "./UseFetchSpeakers";
-import * as Sentry from "@sentry/react";
import { ISpeaker } from "../../types/speakers";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const LessThanGreaterThan = (props: { width: number }) => (
<>
@@ -41,21 +42,15 @@ const Speakers: FC> = () => {
const isBetween = (startDay: Date, endDay: Date): boolean =>
startDay < new Date() && endDay > today;
- const { error, data, isLoading } = useFetchSpeakers(
- `${webData.sessionizeUrl}/view/Speakers`,
- );
+ const { error, data, isLoading } = useFetchSpeakers(webData.sessionizeUrl);
- if (error) {
- Sentry.captureException(error);
- }
+ useSentryErrorReport(error);
const trackCFP = useCallback(() => {
gaEventTracker("CFP", "CFP");
}, []);
- useEffect(() => {
- document.title = `Speakers β ${webData.title} β ${webData.edition}`;
- });
+ useDocumentTitleUpdater("Speakers", webData.edition);
const CFPStartDay = new Date(webData.cfp.startDay);
const CFPEndDay = new Date(webData.cfp.endDay);
diff --git a/src/views/Speakers/UseFetchSpeakers.ts b/src/views/Speakers/UseFetchSpeakers.ts
index 82d0a0bf..328b0209 100644
--- a/src/views/Speakers/UseFetchSpeakers.ts
+++ b/src/views/Speakers/UseFetchSpeakers.ts
@@ -4,11 +4,11 @@ import { speakerAdapter } from "../../services/speakerAdapter";
import { ISpeaker } from "../../types/speakers";
export const useFetchSpeakers = (
- url: string = "https://sessionize.com/api/v2/xhudniix/view/Speakers",
+ url: string = "https://sessionize.com/api/v2/xhudniix",
id?: string,
): UseQueryResult => {
return useQuery(["api-speakers", url], async () => {
- const serverResponse = await axios.get(url);
+ const serverResponse = await axios.get(`${url}/view/Speakers`);
let returnData;
if (id !== undefined) {
returnData = serverResponse.data.filter(
diff --git a/src/views/Talks/LiveView.tsx b/src/views/Talks/LiveView.tsx
index d54078ab..cb7df4a4 100644
--- a/src/views/Talks/LiveView.tsx
+++ b/src/views/Talks/LiveView.tsx
@@ -1,12 +1,13 @@
-import React, { FC, useCallback, useEffect, useMemo } from "react";
+import React, { FC, useCallback, useMemo } from "react";
import { useFetchLiveView } from "./UseFetchTalks";
import Loading from "../../components/Loading/Loading";
import { UngroupedSession } from "./liveView.types";
import conference from "../../data/2024.json";
import { TalkCard } from "./components/TalkCard";
-import * as Sentry from "@sentry/react";
import { StyledMain } from "./Talks.style";
import { talkCardAdapter } from "./TalkCardAdapter";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const LiveView: FC> = () => {
const { isLoading, error, data } = useFetchLiveView();
@@ -29,15 +30,9 @@ const LiveView: FC> = () => {
return data?.sessions?.filter(getPredicate());
}, [data, getPredicate]);
- useEffect(() => {
- document.title = `Live view - ${conference.title} - ${conference.edition} Edition`;
- }, []);
+ useDocumentTitleUpdater("Live view - ", conference.edition);
- useEffect(() => {
- if (error) {
- Sentry.captureException(error);
- }
- }, [error]);
+ useSentryErrorReport(error);
return (
diff --git a/src/views/Talks/Talks.tsx b/src/views/Talks/Talks.tsx
index 8682517c..23657bc6 100644
--- a/src/views/Talks/Talks.tsx
+++ b/src/views/Talks/Talks.tsx
@@ -14,11 +14,12 @@ import {
} from "./Talks.style";
import TrackInformation from "./components/TrackInformation";
import { useFetchTalks } from "./UseFetchTalks";
-import * as Sentry from "@sentry/react";
import { Dropdown, DropdownChangeEvent } from "primereact/dropdown";
import "primereact/resources/primereact.min.css";
import "primereact/resources/themes/lara-light-indigo/theme.css";
import "../../styles/theme.css";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
interface TrackInfo {
name: string;
@@ -37,8 +38,6 @@ const Talks: FC> = () => {
const sessionSelectedGroupName =
sessionStorage.getItem("selectedGroupName");
- document.title = `Talks - ${conferenceData.title} - ${conferenceData.edition}`;
-
if (sessionSelectedGroupCode && sessionSelectedGroupName) {
setSelectedGroupId({
name: sessionSelectedGroupName,
@@ -47,9 +46,8 @@ const Talks: FC> = () => {
}
}, []);
- if (error) {
- Sentry.captureException(error);
- }
+ useDocumentTitleUpdater("Talks", conferenceData.edition);
+ useSentryErrorReport(error);
const dropDownOptions = [
{ name: "All Tracks", code: undefined },
diff --git a/src/views/Travel/Travel.tsx b/src/views/Travel/Travel.tsx
index 591c5dc8..ea79c413 100644
--- a/src/views/Travel/Travel.tsx
+++ b/src/views/Travel/Travel.tsx
@@ -1,41 +1,39 @@
-import React, {FC, useEffect} from "react";
-import {Venue} from "./Venue";
-import {ToBarcelona} from "./ToBarcelona";
+import React, { FC } from "react";
+import { Venue } from "./Venue";
+import { ToBarcelona } from "./ToBarcelona";
import data from "../../data/2024.json";
-import {StyledWaveContainer} from "../Speakers/Speakers.style";
+import { StyledWaveContainer } from "../Speakers/Speakers.style";
import styled from "styled-components";
-import {Color} from "../../styles/colors";
-import {Accommodation} from "./Accommodation";
+import { Color } from "../../styles/colors";
+import { Accommodation } from "./Accommodation";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledTravel = styled.div`
+ max-width: 85rem;
+ margin-left: auto;
+ margin-right: auto;
- max-width: 85rem;
- margin-left: auto;
- margin-right: auto;
-
- .top {
- clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 50px));
- height: 51px;
- background-color: ${Color.LIGHT_BLUE};
- border-top: 1px solid ${Color.LIGHT_BLUE};
- }
+ .top {
+ clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 50px));
+ height: 51px;
+ background-color: ${Color.LIGHT_BLUE};
+ border-top: 1px solid ${Color.LIGHT_BLUE};
+ }
- .bottom {
- clip-path: polygon(0 0, 100% 50px, 100% 100%, 0 100%);
- margin-top: -50px;
- height: 50px;
- background-color: ${Color.DARK_BLUE};
- }
+ .bottom {
+ clip-path: polygon(0 0, 100% 50px, 100% 100%, 0 100%);
+ margin-top: -50px;
+ height: 50px;
+ background-color: ${Color.DARK_BLUE};
+ }
- .to-barcelona {
- background-color: ${Color.DARK_BLUE};
- }
+ .to-barcelona {
+ background-color: ${Color.DARK_BLUE};
+ }
`;
const Travel: FC> = () => {
- useEffect(() => {
- document.title = `Travel β ${data.title} β ${data.edition}`;
- }, []);
+ useDocumentTitleUpdater("Travel", data.edition);
return (
diff --git a/src/views/Workshops/Workshops.tsx b/src/views/Workshops/Workshops.tsx
index d9b911b3..d7677f04 100644
--- a/src/views/Workshops/Workshops.tsx
+++ b/src/views/Workshops/Workshops.tsx
@@ -1,4 +1,4 @@
-import React, { FC, useEffect } from "react";
+import React, { FC } from "react";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import { Color } from "../../styles/colors";
import {
@@ -12,34 +12,34 @@ import LessThanDarkBlueIcon from "../../assets/images/LessThanDarkBlueIcon.svg";
import TitleSection from "../../components/SectionTitle/TitleSection";
import MoreThanBlueIcon from "../../assets/images/MoreThanBlueIcon.svg";
import { useFetchTalks } from "../Talks/UseFetchTalks";
-import * as Sentry from "@sentry/react";
import { TalkCard } from "../Talks/components/TalkCard";
import conferenceData from "../../data/2025.json";
import styled from "styled-components";
import { BIG_BREAKPOINT } from "../../constants/BreakPoints";
+import { useSentryErrorReport } from "../../services/useSentryErrorReport";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledSection = styled.section`
- {
+{
display: flex;
padding: 0 10rem;
flex-wrap: wrap;
- }
+}
- @media (max-width: ${BIG_BREAKPOINT}px) {
- padding: 1rem;
- flex-direction: column;
- }
+ @media (max-width: ${BIG_BREAKPOINT}px) {
+ padding: 1rem;
+ flex-direction: column;
+ }
- & > div {
- margin: 1rem;
- min-width: 14%;
- }
+ & > div {
+ margin: 1rem;
+ min-width: 14%;
+ }
`;
const Workshops: FC> = () => {
const { isLoading, data, error } = useFetchTalks();
- useEffect(() => {
- document.title = `Workshops - DevBcn - ${conferenceData.edition}`;
- }, []);
+
+ useDocumentTitleUpdater("Workshops ", conferenceData.edition);
//region workshops
const workshopCategoryList = new Set([149213]);
@@ -55,9 +55,7 @@ const Workshops: FC> = () => {
);
//endregion
- if (error) {
- Sentry.captureException(error);
- }
+ useSentryErrorReport(error);
return (
<>
diff --git a/src/views/kcd/Kcd.tsx b/src/views/kcd/Kcd.tsx
index 36cf598e..1e038036 100644
--- a/src/views/kcd/Kcd.tsx
+++ b/src/views/kcd/Kcd.tsx
@@ -1,4 +1,3 @@
-import { useEffect } from "react";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
import { Color } from "../../styles/colors";
import {
@@ -17,6 +16,7 @@ import { useWindowSize } from "react-use";
import youtube from "../../assets/images/youtube.svg";
import linkedinIcon from "../../assets/images/linkedinIcon.svg";
import twitterIcon from "../../assets/images/twitterIcon.svg";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledParagraph = styled.p`
color: white;
@@ -60,9 +60,8 @@ const StyledImage = styled.img`
export default function Kcd() {
const { width } = useWindowSize();
- useEffect(() => {
- document.title = `KCD Barcelona β ${data.title} β ${data.edition}`;
- });
+
+ useDocumentTitleUpdater("KCD Barcelona", data.edition);
return (
<>
@@ -84,7 +83,11 @@ export default function Kcd() {
-
+
KCD Barcelona
{" "}
is an event within the CNCF framework called Kubernetes Community Days
diff --git a/src/views/sponsorship/Sponsorship.tsx b/src/views/sponsorship/Sponsorship.tsx
index 8b6b65b7..f144f3a3 100644
--- a/src/views/sponsorship/Sponsorship.tsx
+++ b/src/views/sponsorship/Sponsorship.tsx
@@ -1,347 +1,337 @@
-import {FC, useEffect} from "react";
+import { FC } from "react";
import TitleSection from "../../components/SectionTitle/TitleSection";
import SectionWrapper from "../../components/SectionWrapper/SectionWrapper";
-import {BIG_BREAKPOINT, MOBILE_BREAKPOINT} from "../../constants/BreakPoints";
-import {Color} from "../../styles/colors";
+import { BIG_BREAKPOINT, MOBILE_BREAKPOINT } from "../../constants/BreakPoints";
+import { Color } from "../../styles/colors";
import LessThanBlue from "../../assets/images/MoreThanBlueWhiteIcon.svg";
-import LessThanTransparentIcon
- from "../../assets/images/LessThanTransparentIcon.svg";
+import LessThanTransparentIcon from "../../assets/images/LessThanTransparentIcon.svg";
import MoreThanBlue from "../../assets/images/LessThanBlueWhiteIcon.svg";
-import MoreThanTransparentIcon
- from "../../assets/images/MoreThanTransparentIcon.svg";
+import MoreThanTransparentIcon from "../../assets/images/MoreThanTransparentIcon.svg";
import styled from "styled-components";
-import {useWindowSize} from "react-use";
+import { useWindowSize } from "react-use";
import {
- StyledLessIcon,
- StyledMoreIcon,
- StyledSpeakersSection,
+ StyledLessIcon,
+ StyledMoreIcon,
+ StyledSpeakersSection,
} from "../Speakers/Speakers.style";
-import {StyledMarginBottom} from "../Talks/Talks.style";
+import { StyledMarginBottom } from "../Talks/Talks.style";
import data from "../../data/2025.json";
-import {format} from "date-fns";
+import { format } from "date-fns";
import Flicking from "@egjs/react-flicking";
-import {AutoPlay} from "@egjs/flicking-plugins";
+import { AutoPlay } from "@egjs/flicking-plugins";
import "@egjs/react-flicking/dist/flicking.css";
import Button from "../../components/UI/Button";
-import {gaEventTracker} from "../../components/analytics/Analytics";
-
+import { gaEventTracker } from "../../components/analytics/Analytics";
+import { useDocumentTitleUpdater } from "../../services/useDocumentTitleUpdate";
const StyledWaveContainer = styled.div`
- background: ${Color.DARK_BLUE};
- overflow-y: hidden;
- height: 3rem;
- width: 100%;
+ background: ${Color.DARK_BLUE};
+ overflow-y: hidden;
+ height: 3rem;
+ width: 100%;
`;
export const StyledSectionsSeparator = styled.div`
- background: ${Color.WHITE};
- height: 3rem;
- @media (min-width: ${BIG_BREAKPOINT}px) {
- height: 5rem;
- }
+ background: ${Color.WHITE};
+ height: 3rem;
+ @media (min-width: ${BIG_BREAKPOINT}px) {
+ height: 5rem;
+ }
`;
const StyledSponsorshipText = styled.div`
- text-align: start;
- color: ${Color.BLACK_BLUE};
- max-width: 95vw;
+ text-align: start;
+ color: ${Color.BLACK_BLUE};
+ max-width: 95vw;
- p {
- margin: 5px 20px;
- text-align: justify;
- }
+ p {
+ margin: 5px 20px;
+ text-align: justify;
+ }
- ul {
- margin: 5px 20px;
+ ul {
+ margin: 5px 20px;
- li {
- margin: 5px 0;
- }
+ li {
+ margin: 5px 0;
}
+ }
- h4 {
- margin: 20px 0;
- }
+ h4 {
+ margin: 20px 0;
+ }
- a:visited {
- color: ${Color.DARK_BLUE};
- font-weight: normal;
- }
+ a:visited {
+ color: ${Color.DARK_BLUE};
+ font-weight: normal;
+ }
- @media only screen and (max-width: ${BIG_BREAKPOINT}px) {
- iframe {
- width: 90vw;
- }
+ @media only screen and (max-width: ${BIG_BREAKPOINT}px) {
+ iframe {
+ width: 90vw;
}
+ }
`;
const StyleLessIcon = styled.img`
- position: absolute;
- left: -1rem;
- top: 12rem;
- height: 5rem;
- @media (min-width: ${BIG_BREAKPOINT}px) {
- height: 10rem;
- }
+ position: absolute;
+ left: -1rem;
+ top: 12rem;
+ height: 5rem;
+ @media (min-width: ${BIG_BREAKPOINT}px) {
+ height: 10rem;
+ }
`;
const StyleMoreIcon = styled.img`
- position: absolute;
- right: -1rem;
- top: 2rem;
- height: 5rem;
- @media (min-width: 800px) {
- height: 10rem;
- }
+ position: absolute;
+ right: -1rem;
+ top: 2rem;
+ height: 5rem;
+ @media (min-width: 800px) {
+ height: 10rem;
+ }
`;
const Sponsorship: FC> = () => {
- const {width} = useWindowSize();
- const plugins = [
- new AutoPlay({duration: 2000, direction: "NEXT", stopOnHover: false})
- ];
+ const { width } = useWindowSize();
+ const plugins = [
+ new AutoPlay({ duration: 2000, direction: "NEXT", stopOnHover: false }),
+ ];
- const handleCLick = () => {
- gaEventTracker("download brochure", "download brochure");
- };
+ const handleCLick = () => {
+ gaEventTracker("download brochure", "download brochure");
+ };
- useEffect(() => {
- document.title = `Sponsorship β ${data.title} β ${data.edition}`;
- });
+ useDocumentTitleUpdater("Sponsorship", data.edition);
- return (
-
-
-
-
- {width > MOBILE_BREAKPOINT && (
- <>
-
-
- >
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ {width > MOBILE_BREAKPOINT && (
+ <>
+
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {width > MOBILE_BREAKPOINT && (
+ <>
+
+
+ >
+ )}
+
+
+ Mark Your Calendars!
+
+ DevBcn {data?.edition} is set for{" "}
+
+ {format(new Date(data.startDay), "MMMM do")} β
+ {" ".concat(format(data.endDay, "do"))}
+ {" "}
+ at the iconic La Farga, Hospitalet de Llobregat. This year, we're
+ diving deep into the realms of Java, JVM, Cloud, DevOps, Frontend
+ technologies, Leadership strategies, and groundbreaking
+ advancements in Big Data and AI.
+
+ A New Era of Tech Innovation
+
+ Dive into tracks covering Java, JVM, Cloud, DevOps, Frontend
+ technologies, Leadership, Big Data, AI, and more. DevBcn{" "}
+ {data?.edition} is the perfect stage to connect with tech
+ professionals, thought leaders, and innovators.
+
+ Tailored Sponsorship Opportunities
+
+ While we're keeping the details of our sponsorship packages
+ exclusive, we promise they're more engaging and impactful than
+ ever. Curious? Access our{" "}
+
+
-
-
-
-
- {width > MOBILE_BREAKPOINT && (
- <>
-
-
- >
- )}
-
-
- Mark Your Calendars!
-
- DevBcn {data?.edition} is set for {format(new Date(data.startDay),"MMMM do")} β
- {" ".concat(format(data.endDay,"do"))} at the
- iconic La Farga, Hospitalet de Llobregat. This year,
- we're diving
- deep into the realms of Java, JVM, Cloud, DevOps,
- Frontend
- technologies, Leadership strategies, and
- groundbreaking
- advancements in Big Data and AI.
-
- A New Era of Tech Innovation
-
- Dive into tracks covering Java, JVM, Cloud, DevOps,
- Frontend
- technologies, Leadership, Big Data, AI, and more.
- DevBcn {data?.edition} is
- the perfect stage to connect with tech
- professionals, thought
- leaders, and innovators.
-
- Tailored Sponsorship Opportunities
-
- While we're keeping the details of our sponsorship
- packages
- exclusive, we promise they're more engaging and
- impactful than
- ever. Curious? Access our{" "}
-
-
- detailed brochure
- {" "}
- {" "}
- at and discover the myriad of ways you can shine at
- DevBcn {data?.edition}.
-
-
- Why Partner with DevBcn?
-
-
-
- Expand Your Reach: Engage
- with a diverse,
- tech-savvy audience. Our latest edition held
- more than 800
- attendees.
-
-
- Elevate Your
- Brand: Showcase your products
- and innovations in a dynamic environment.
-
-
- Network with the
- Best: Connect with industry
- leaders and potential collaborators. Nearly
- 30 companies have
- pledged their trust in DevBcn.
-
-
- Showcase Thought
- Leadership: Share your
- expertise and insights with a global
- audience.
-
-
-
- Join us on this exciting journey
-
- To discuss how we can align our sponsorship
- opportunities with
- your brand's vision, contact us at{" "}
- sponsors@devbcn.com
-
-
- Letβs make DevBcn {data?.edition} an unforgettable
- experience together! Stay
- updated and spread the excitement using{" "}
-
- #devbcn25.
-
-
-
- We eagerly await the opportunity to collaborate with
- you once more
- for an extraordinary event!
-
- Take a look at our latest edition summary
- VIDEO
- Explore DevBcn Talks Online!
-
-
-
-
-
-
- );
+ detailed brochure
+ {" "}
+ {" "}
+ at and discover the myriad of ways you can shine at DevBcn{" "}
+ {data?.edition}.
+
+
+ Why Partner with DevBcn?
+
+
+
+ Expand Your Reach: Engage with a diverse,
+ tech-savvy audience. Our latest edition held more than 800
+ attendees.
+
+
+ Elevate Your Brand: Showcase your products
+ and innovations in a dynamic environment.
+
+
+ Network with the Best: Connect with industry
+ leaders and potential collaborators. Nearly 30 companies have
+ pledged their trust in DevBcn.
+
+
+ Showcase Thought Leadership: Share your
+ expertise and insights with a global audience.
+
+
+
+ Join us on this exciting journey
+
+ To discuss how we can align our sponsorship opportunities with
+ your brand's vision, contact us at{" "}
+ sponsors@devbcn.com
+
+
+ Letβs make DevBcn {data?.edition} an unforgettable experience
+ together! Stay updated and spread the excitement using{" "}
+
+ #devbcn25.
+
+
+
+ We eagerly await the opportunity to collaborate with you once more
+ for an extraordinary event!
+
+ Take a look at our latest edition summary
+ VIDEO
+ Explore DevBcn Talks Online!
+
+
+
+
+
+
+ );
};
-export default Sponsorship;
\ No newline at end of file
+export default Sponsorship;
From ecb9c4101b0cdee56d9c1c272062e73c0c50b67a Mon Sep 17 00:00:00 2001
From: Anyul Rivas
Date: Tue, 18 Mar 2025 15:27:37 +0100
Subject: [PATCH 7/9] Potential fix for code scanning alert no. 5: Creating
biased random numbers from a cryptographically secure source
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
---
.../Home/components/SpeakersCarousel/SpeakerSwiper.tsx | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx b/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
index 1fe56d78..fe7ec5b8 100644
--- a/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
+++ b/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
@@ -45,11 +45,10 @@ export const shuffleArray = (array: T[]): T[] => {
}
const shuffledArray = [...array]; // Create a copy to avoid modifying the original array
for (let i = shuffledArray.length - 1; i > 0; i--) {
- const j = Math.floor(
- (window.crypto.getRandomValues(new Uint32Array(1))[0] /
- (0xffffffff + 1)) *
- (i + 1),
- );
+ let j;
+ do {
+ j = window.crypto.getRandomValues(new Uint32Array(1))[0] % (i + 1);
+ } while (j >= (i + 1));
[shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
}
return shuffledArray;
From 037baffee4f9d262ed825617f0adc73aeb0f3cb2 Mon Sep 17 00:00:00 2001
From: Anyul Rivas
Date: Tue, 18 Mar 2025 15:31:00 +0100
Subject: [PATCH 8/9] Potential fix for code scanning alert no. 6: Creating
biased random numbers from a cryptographically secure source
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
---
src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx b/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
index fe7ec5b8..8ced28f8 100644
--- a/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
+++ b/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
@@ -46,9 +46,11 @@ export const shuffleArray = (array: T[]): T[] => {
const shuffledArray = [...array]; // Create a copy to avoid modifying the original array
for (let i = shuffledArray.length - 1; i > 0; i--) {
let j;
+ const max = (i + 1) * (2 ** 32 / (i + 1));
do {
- j = window.crypto.getRandomValues(new Uint32Array(1))[0] % (i + 1);
- } while (j >= (i + 1));
+ j = window.crypto.getRandomValues(new Uint32Array(1))[0];
+ } while (j >= max);
+ j = j % (i + 1);
[shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
}
return shuffledArray;
From 39d3559be90d30688f7040892ce4b3d2ca09f9a9 Mon Sep 17 00:00:00 2001
From: Anyul Rivas
Date: Tue, 18 Mar 2025 15:31:44 +0100
Subject: [PATCH 9/9] chore: PR review
---
.../Home/components/SpeakersCarousel/SpeakerSwiper.tsx | 8 ++++----
src/components/Swiper/SpeakerSwiper.tsx | 6 +++---
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx b/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
index 8ced28f8..7ed1b54d 100644
--- a/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
+++ b/src/2023/Home/components/SpeakersCarousel/SpeakerSwiper.tsx
@@ -1,4 +1,4 @@
-import { FC } from "react";
+import React, { FC } from "react";
import { Autoplay, Parallax } from "swiper";
import { Swiper, SwiperSlide } from "swiper/react";
import styled from "styled-components";
@@ -61,9 +61,9 @@ const SpeakerSwiper: FC> = () => {
conferenceData.sessionizeUrl,
);
- const swiperSpeakers: ISpeaker[] = data
- ? shuffleArray(data).slice(0, 20)
- : [];
+ const swiperSpeakers: ISpeaker[] = React.useMemo(() => data
+ ? shuffleArray(data).slice(0, 20)
+ : [], [data]);
useSentryErrorReport(error);
diff --git a/src/components/Swiper/SpeakerSwiper.tsx b/src/components/Swiper/SpeakerSwiper.tsx
index 475bcb2e..e303ae94 100644
--- a/src/components/Swiper/SpeakerSwiper.tsx
+++ b/src/components/Swiper/SpeakerSwiper.tsx
@@ -42,9 +42,9 @@ const SpeakerSwiper: FC> = () => {
conferenceData.sessionizeUrl,
);
- const cachedSpeakers: ISpeaker[] = data
- ? shuffleArray(data).slice(0, 20)
- : [];
+ const cachedSpeakers: ISpeaker[] = React.useMemo(() => data
+ ? shuffleArray(data).slice(0, 20)
+ : [], [data]);
useSentryErrorReport(error);