Getting started on Tech Tracker. Added TV Context
This commit is contained in:
@ -110,7 +110,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
|
||||
|
||||
const {
|
||||
data: { subscription },
|
||||
} = supabase.auth.onAuthStateChange(async (event, session) => {
|
||||
} = supabase.auth.onAuthStateChange(async (event, _session) => {
|
||||
console.log('Auth state change:', event); // Debug log
|
||||
|
||||
if (event === 'SIGNED_IN') {
|
79
src/components/context/TVMode.tsx
Normal file
79
src/components/context/TVMode.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
'use client';
|
||||
import Image from 'next/image';
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useAuth } from '@/components/context';
|
||||
import { Button, type buttonVariants } from '@/components/ui';
|
||||
import { type ComponentProps } from 'react';
|
||||
import { type VariantProps } from 'class-variance-authority';
|
||||
|
||||
type TVModeContextProps = {
|
||||
tvMode: boolean;
|
||||
toggleTVMode: () => void;
|
||||
};
|
||||
|
||||
type TVToggleProps = {
|
||||
className?: ComponentProps<'button'>['className'];
|
||||
buttonSize?: VariantProps<typeof buttonVariants>['size'];
|
||||
buttonVariant?: VariantProps<typeof buttonVariants>['variant'];
|
||||
imageWidth?: number;
|
||||
imageHeight?: number;
|
||||
};
|
||||
|
||||
const TVModeContext = createContext<TVModeContextProps | undefined>(undefined);
|
||||
|
||||
export const TVModeProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [tvMode, setTVMode] = useState(false);
|
||||
const toggleTVMode = () => {
|
||||
setTVMode((prev) => !prev);
|
||||
};
|
||||
return (
|
||||
<TVModeContext.Provider value={{ tvMode, toggleTVMode }}>
|
||||
{children}
|
||||
</TVModeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useTVMode = () => {
|
||||
const context = useContext(TVModeContext);
|
||||
if (!context) {
|
||||
throw new Error('useTVMode must be used within a TVModeProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export const TVToggle = ({
|
||||
className = 'my-auto cursor-pointer',
|
||||
buttonSize = 'default',
|
||||
buttonVariant = 'link',
|
||||
imageWidth = 25,
|
||||
imageHeight = 25,
|
||||
}: TVToggleProps) => {
|
||||
const { tvMode, toggleTVMode } = useTVMode();
|
||||
const { isAuthenticated } = useAuth();
|
||||
if (!isAuthenticated) return <div />;
|
||||
return (
|
||||
<Button
|
||||
onClick={toggleTVMode}
|
||||
className={className}
|
||||
size={buttonSize}
|
||||
variant={buttonVariant}
|
||||
>
|
||||
{tvMode ? (
|
||||
<Image
|
||||
src='/icons/tv/exit.svg'
|
||||
alt='Exit TV Mode'
|
||||
width={imageWidth}
|
||||
height={imageHeight}
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src='/icons/tv/enter.svg'
|
||||
alt='Exit TV Mode'
|
||||
width={imageWidth}
|
||||
height={imageHeight}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
};
|
@ -20,10 +20,9 @@ export const ThemeProvider = ({
|
||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
||||
};
|
||||
|
||||
export interface ThemeToggleProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
type ThemeToggleProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
size?: number;
|
||||
}
|
||||
};
|
||||
|
||||
export const ThemeToggle = ({ size = 1, ...props }: ThemeToggleProps) => {
|
||||
const { setTheme, resolvedTheme } = useTheme();
|
3
src/components/context/index.tsx
Normal file
3
src/components/context/index.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export { AuthProvider, useAuth } from './Auth';
|
||||
export { ThemeProvider, ThemeToggle } from './Theme';
|
||||
export { TVModeProvider, useTVMode, TVToggle } from './TVMode';
|
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
import { signInWithApple } from '@/lib/actions';
|
||||
import { StatusMessage, SubmitButton } from '@/components/default';
|
||||
import { useAuth } from '@/components/context/auth';
|
||||
import { useAuth } from '@/components/context';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
@ -37,7 +37,7 @@ export const SignInWithApple = ({
|
||||
// Redirect to Apple OAuth page
|
||||
window.location.href = result.data;
|
||||
} else {
|
||||
setStatusMessage(`Error: ${result.error}`);
|
||||
setStatusMessage(`Error signing in with Apple!`);
|
||||
}
|
||||
} catch (error) {
|
||||
setStatusMessage(
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
import { signInWithMicrosoft } from '@/lib/actions';
|
||||
import { StatusMessage, SubmitButton } from '@/components/default';
|
||||
import { useAuth } from '@/components/context/auth';
|
||||
import { useAuth } from '@/components/context';
|
||||
import { useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
import { type buttonVariants } from '@/components/ui';
|
||||
@ -35,7 +35,7 @@ export const SignInWithMicrosoft = ({
|
||||
// Redirect to Microsoft OAuth page
|
||||
window.location.href = result.data;
|
||||
} else {
|
||||
setStatusMessage(`Error: ${result.error}`);
|
||||
setStatusMessage(`Error: Could not sign in with Microsoft!`);
|
||||
}
|
||||
} catch (error) {
|
||||
setStatusMessage(
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui';
|
||||
import { useAuth } from '@/components/context/auth';
|
||||
import { useAuth } from '@/components/context';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { signOut } from '@/lib/actions';
|
||||
import { User } from 'lucide-react';
|
||||
|
@ -3,7 +3,7 @@
|
||||
import Link from 'next/link';
|
||||
import { Button } from '@/components/ui';
|
||||
import NavigationAuth from './auth';
|
||||
import { ThemeToggle } from '@/components/context/theme';
|
||||
import { ThemeToggle, TVToggle } from '@/components/context';
|
||||
import Image from 'next/image';
|
||||
|
||||
const Navigation = () => {
|
||||
@ -30,6 +30,7 @@ const Navigation = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<TVToggle />
|
||||
<ThemeToggle />
|
||||
<NavigationAuth />
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useFileUpload } from '@/lib/hooks/useFileUpload';
|
||||
import { useAuth } from '@/components/context/auth';
|
||||
import { useAuth } from '@/components/context';
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
Input,
|
||||
} from '@/components/ui';
|
||||
import { useEffect } from 'react';
|
||||
import { useAuth } from '@/components/context/auth';
|
||||
import { useAuth } from '@/components/context';
|
||||
import { SubmitButton } from '@/components/default';
|
||||
|
||||
const formSchema = z.object({
|
||||
|
@ -122,18 +122,15 @@ export const ResetPasswordForm = ({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{statusMessage && (
|
||||
<div
|
||||
className={`text-sm text-center ${
|
||||
statusMessage.includes('Error') ||
|
||||
statusMessage.includes('failed')
|
||||
? 'text-destructive'
|
||||
: 'text-green-600'
|
||||
}`}
|
||||
>
|
||||
{statusMessage}
|
||||
</div>
|
||||
)}
|
||||
{statusMessage &&
|
||||
(statusMessage.includes('Error') ||
|
||||
statusMessage.includes('error') ||
|
||||
statusMessage.includes('failed') ||
|
||||
statusMessage.includes('invalid') ? (
|
||||
<StatusMessage message={{ error: statusMessage }} />
|
||||
) : (
|
||||
<StatusMessage message={{ message: statusMessage }} />
|
||||
))}
|
||||
<div className='flex justify-center'>
|
||||
<SubmitButton
|
||||
disabled={isLoading}
|
||||
|
@ -3,7 +3,7 @@
|
||||
import { CardHeader } from '@/components/ui';
|
||||
import { SubmitButton } from '@/components/default';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useAuth } from '@/components/context/auth';
|
||||
import { useAuth } from '@/components/context';
|
||||
import { signOut } from '@/lib/actions';
|
||||
|
||||
export const SignOut = () => {
|
||||
|
Reference in New Issue
Block a user