Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { ColorModeProvider } from '@/components/ui/color-mode'
import { ChakraProvider } from '@chakra-ui/react'
import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { PostHogProvider } from 'posthog-js/react'
import { useState } from 'react'
import { OpenAPI } from './client'
import { ApiError } from './client'
import { AuthProvider } from './hooks/useAuthContext'
import { routeTree } from './routeTree.gen'
import { system } from './theme'
import CookieConsent from './components/commonUI/CookieConsentBanner'

OpenAPI.BASE = import.meta.env.VITE_API_URL
OpenAPI.TOKEN = async () => {
return localStorage.getItem('access_token') || ''
}


const posthogApiKey = import.meta.env.VITE_POSTHOG_API_KEY
const posthogConfig = {
enabled: import.meta.env.PROD && !!posthogApiKey,
options: import.meta.env.VITE_POSTHOG_HOST ? { api_host: import.meta.env.VITE_POSTHOG_HOST } : {},
}

const handleApiError = (error: Error) => {
if (error instanceof ApiError && [401, 403].includes(error.status)) {
localStorage.removeItem('access_token')
window.location.href = '/login'
}
}


const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: handleApiError,
}),
mutationCache: new MutationCache({
onError: handleApiError,
}),
})

const router = createRouter({ routeTree })

declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}

const App = () => {
const [isPosthogEnabled, setIsPosthogEnabled] = useState(false);
const onConsent=(status:boolean)=>{
setIsPosthogEnabled(status)
}
return (
<AuthProvider>
<ChakraProvider value={system}>
<ColorModeProvider>
<QueryClientProvider client={queryClient}>
<CookieConsent consented={isPosthogEnabled} onConsent={onConsent} />
{isPosthogEnabled && posthogConfig.enabled ? (
<PostHogProvider apiKey={posthogApiKey} options={posthogConfig.options}>
<RouterProvider router={router} />
</PostHogProvider>
) : (
<RouterProvider router={router} />
)}
</QueryClientProvider>
</ColorModeProvider>
</ChakraProvider>
</AuthProvider>
);
};

export default App;
129 changes: 129 additions & 0 deletions frontend/src/components/commonUI/CookieConsentBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React, { useState, useEffect } from "react";
import {
Box,
Button,
Flex,
Text,
Link,
VStack,
IconButton,
} from "@chakra-ui/react";

import { FaTimes as CloseIcon } from "react-icons/fa";

interface CookieConsentProps{
consented:boolean
onConsent:(status:boolean)=>void
}

const CookieConsent:React.FC<CookieConsentProps> = ({consented,onConsent}) => {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const consent = localStorage.getItem("cookie-consent");
if (consent==="all") {
onConsent(true);
} else {
const timer = setTimeout(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get out this logic to a new function. By this way, it wil be more descriptive with his title.

onConsent(false)
setIsVisible(true);
}, 300);
return () => clearTimeout(timer);
}
}, []);

const handleAcceptAll = () => {
localStorage.setItem("cookie-consent", "all");
setIsVisible(false);
setTimeout(() => onConsent(true), 300);
};

const handleAcceptNecessary = () => {
localStorage.setItem("cookie-consent", "deny");
setIsVisible(false);
setTimeout(() => onConsent(false), 300);
};

const handleClose = () => {
setIsVisible(false);
};

if (consented) return null;
return (
<Box
position="fixed"
bottom="0"
right="0"
bg="#2A2438"
color="white"
px={6}
py={4}
zIndex="toast"
boxShadow="0 -2px 10px rgba(0, 0, 0, 0.2)"
transform={isVisible ? "translateY(0)" : "translateY(100%)"}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this logic out of the return size to avoid mix logic with cosmetic part

transition="transform 0.5s ease-in-out"
w={350}
m={4}
>
<Flex
direction={["column", "row"]}
align="center"
justify="space-between"
maxW="6xl"
mx="auto"
gap={4}
flexWrap="wrap"
>
<VStack align="start" flex="1">
<Text fontWeight="bold" fontSize="md">We use cookies 🍪</Text>
<Text fontSize="sm">
To enhance your experience on FlashNotes. By continuing, you agree to our use of cookies as outlined in our
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this text to locales folder and take it with useTranslation react hook.

<Link color="#A799B7" ml={1} href="/privacy-policy">
Privacy Policy
</Link>.
</Text>
</VStack>

<Flex gap={2} wrap="wrap">
<Button
variant="outline"
size="sm"
onClick={handleAcceptNecessary}
borderColor="#A799B7"
color="#A799B7"
minW={30}
_hover={{ bg: "#3B3049" }}

>
Deny All
</Button>
<Button
size="sm"
onClick={handleAcceptAll}
bg="#A799B7"
color="#2A2438"
minW={40}
_hover={{ bg: "#8A7B99" }}
>
Accept All
</Button>
</Flex>
<IconButton
position="absolute"
top={0}
right={0}
aria-label="Close cookie consent banner"
size="sm"
variant="ghost"
color="#A799B7"
_hover={{ bg: "#3B3049" }}
onClick={handleClose}
alignSelf="flex-start"
>
<CloseIcon/>
</IconButton>
</Flex>
</Box>
);
};

export default CookieConsent;
59 changes: 2 additions & 57 deletions frontend/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,14 @@
import './i18n'
import { ColorModeProvider } from '@/components/ui/color-mode'
import { ChakraProvider } from '@chakra-ui/react'
import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { PostHogProvider } from 'posthog-js/react'
import { StrictMode } from 'react'
import ReactDOM from 'react-dom/client'
import { ApiError, OpenAPI } from './client'
import { AuthProvider } from './hooks/useAuthContext'
import { routeTree } from './routeTree.gen'
import { system } from './theme'

OpenAPI.BASE = import.meta.env.VITE_API_URL
OpenAPI.TOKEN = async () => {
return localStorage.getItem('access_token') || ''
}

const handleApiError = (error: Error) => {
if (error instanceof ApiError && [401, 403].includes(error.status)) {
localStorage.removeItem('access_token')
window.location.href = '/login'
}
}
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: handleApiError,
}),
mutationCache: new MutationCache({
onError: handleApiError,
}),
})

const router = createRouter({ routeTree })
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}

const posthogApiKey = import.meta.env.VITE_POSTHOG_API_KEY
const posthogConfig = {
enabled: import.meta.env.PROD && !!posthogApiKey,
options: import.meta.env.VITE_POSTHOG_HOST ? { api_host: import.meta.env.VITE_POSTHOG_HOST } : {},
}
import App from './App'

const rootElement = document.getElementById('root')
if (rootElement && !rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement)
root.render(
<StrictMode>
<AuthProvider>
<ChakraProvider value={system}>
<ColorModeProvider>
<QueryClientProvider client={queryClient}>
{posthogConfig.enabled ? (
<PostHogProvider apiKey={posthogApiKey} options={posthogConfig.options}>
<RouterProvider router={router} />
</PostHogProvider>
) : (
<RouterProvider router={router} />
)}
</QueryClientProvider>
</ColorModeProvider>
</ChakraProvider>
</AuthProvider>
<App/>
</StrictMode>,
)
}