Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added Language dropdown feature #122

Closed
wants to merge 9 commits into from
9 changes: 6 additions & 3 deletions nepalingo-web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React from "react";
import {
BrowserRouter as Router,
Route,
Expand All @@ -7,7 +7,7 @@ import {
} from "react-router-dom";
import User_auth from "./components/userAuth/UserAuth";
import Home from "./pages/Home/Home";
import FlashcardPage from "./pages/FlashcardPage"
import FlashcardPage from "./pages/FlashcardPage";
import { useAuth } from "./components/userAuth/AuthContext";
import ReactGA from "react-ga4";

Expand All @@ -20,7 +20,10 @@ const App: React.FC = () => {
<Router>
<Routes>
<Route path="/login" element={<User_auth />} />
<Route path="/learn" element={<FlashcardPage />} />
<Route
path="/flashcard"
element={user ? <FlashcardPage /> : <Navigate to="/login" />}
/>
{/* Protect the / route, redirect to /login if not authenticated */}
<Route path="/" element={user ? <Home /> : <Navigate to="/login" />} />
{/* Default route redirects to /login */}
Expand Down
33 changes: 33 additions & 0 deletions nepalingo-web/src/components/DailyQuiz.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { useNavigate } from 'react-router-dom'; // Import useNavigate
import Button from './Button';
const DailyQuiz: React.FC = () => {
const backgroundImageUrl = 'https://t3.ftcdn.net/jpg/00/73/08/22/360_F_73082224_ay4Tus31QNHNmGSIty53ZE6mBrBc47cV.jpg'; // Set your image URL
const navigate = useNavigate(); // Get the navigate function

const handleStartQuizClick = () => {
// Redirect to /flashcard when the button is clicked
navigate('/flashcard');
};

return (
<div
className="daily-quiz-card bg-cover text-white p-8 rounded-lg"
style={{ backgroundImage: `url(${backgroundImageUrl})` }}
>
<h1 className="mb-2">Nepalingo</h1>
<p className="text-lg font-bold mb-2">QUIZ YOURSELF</p>
<p className="text-sm opacity-80 mb-2">
Taking Quiz is a better and fun way for learning
</p>
<Button
className="bg-slate-800"
onClick={handleStartQuizClick} // Call the function on button click
>
Start Quiz
</Button>
</div>
);
};

export default DailyQuiz;
112 changes: 112 additions & 0 deletions nepalingo-web/src/components/StreakContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import React, {
createContext,
useContext,
useState,
useEffect,
ReactNode,
} from "react";
import supabase from "./userAuth/supabaseClient";
import { useAuth } from "./userAuth/AuthContext";

interface StreakContextProps {
currentStreak: number;
longestStreak: number;
updateStreak: () => void;
}

export const StreakContext = createContext<StreakContextProps>({
currentStreak: 0,
longestStreak: 0,
updateStreak: () => {},
});

export const StreakProvider = ({ children }: { children: ReactNode }) => {
const { user } = useAuth();
const [currentStreak, setCurrentStreak] = useState(0);
const [longestStreak, setLongestStreak] = useState(0);
const [streakEndDate, setStreakEndDate] = useState<string | null>(null);

const fetchStreakData = async () => {
if (user) {
const { data, error } = await supabase
.from("user_daily_streaks")
.select("*")
.eq("user_id", user.id)
.single();

if (error && error.code !== "PGRST116") {
console.error("Error fetching streak data:", error);
}

if (data) {
setCurrentStreak(data.current_streak);
setLongestStreak(data.longest_streak);
setStreakEndDate(data.streak_end_date);
}
}
};

useEffect(() => {
fetchStreakData();
}, [user]);

const updateStreak = async () => {
const currentDate = new Date().toISOString().split("T")[0]; // Get current date

if (user) {
let newStreak = 1;
let newLongestStreak = 1;

if (streakEndDate) {
const lastDate = new Date(streakEndDate).toISOString().split("T")[0];
if (lastDate === currentDate) {
return; // No update needed if the user has already visited today
} else if (
new Date(currentDate).getTime() - new Date(lastDate).getTime() ===
86400000
) {
newStreak = currentStreak + 1;
newLongestStreak = Math.max(newStreak, longestStreak);
}
}

// Update state
setCurrentStreak(newStreak);
setLongestStreak(newLongestStreak);
setStreakEndDate(currentDate);

// Upsert database
const { error } = await supabase
.from("user_daily_streaks")
.upsert({
user_id: user.id,
streak_start_date: newStreak === 1 ? currentDate : undefined,
streak_end_date: currentDate, // Update streak_end_date with currentDate
current_streak: newStreak,
longest_streak: newLongestStreak,
created_at: new Date().toISOString(),
})
.select();

if (error) {
console.error("Error updating streak data:", error);
}
}
};

return (
<StreakContext.Provider
value={{ currentStreak, longestStreak, updateStreak }}
>
{children}
</StreakContext.Provider>
);
};

export const useStreak = () => {
const context = useContext(StreakContext);
if (context === undefined) {
throw new Error("useStreak must be used within a StreakProvider");
}
return context;
};
43 changes: 43 additions & 0 deletions nepalingo-web/src/components/header/ChangeLanguage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useState } from "react";
import Menu from "./Menu";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChevronDown } from "@fortawesome/free-solid-svg-icons";

const ChangeLanguage: React.FC = () => {
const [isOpen, setIsOpen] = useState(false);
const [selectedLanguage, setSelectedLanguage] = useState("Nepal Bhasa");

const toggleMenu = () => {
setIsOpen(!isOpen);
};

const handleSelect = (option: string) => {
if (option === "newari") {
//will be handled later
}
setSelectedLanguage(option);
setIsOpen(false);
};

const options = [
{ label: "Nepal Bhasa", value: "newari" },
{ label: "Tajpuriya", value: "comingSoon" },
{ label: "Maithili", value: "comingSoon" },
];

return (
<div className="relative inline-block text-left">
<button
type="button"
className="inline-flex justify-between items-center w-48 h-11 px-4 rounded-md border border-gray-300 shadow-sm bg-gray-900 text-sm font-medium text-white hover:bg-gray-800 focus:outline-none"
onClick={toggleMenu}
>
{selectedLanguage}
<FontAwesomeIcon icon={faChevronDown} className="ml-2" />
</button>
<Menu isOpen={isOpen} onSelect={handleSelect} options={options} />
</div>
);
};

export default ChangeLanguage;
19 changes: 14 additions & 5 deletions nepalingo-web/src/components/header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import React from "react";
import logo from "../../assets/NepaLingoLogoWhiteBg.jpg";
import UserProfile from "./UserProfile";
import ChangeLanguage from "./ChangeLanguage";

const Header: React.FC = () => {
return (
<nav className="bg-white border-gray-200 px-4 lg:px-6 py-2.5 dark:bg-gray-800">
<div className="flex justify-between items-center mx-auto max-w-screen-xl">
<a href="https://nepalingo.com" className="flex items-center">
<img src={logo} alt="Nepalingo Logo" className="h-24" />
</a>
<nav
className="bg-white border-gray-200 px-4 lg:px-6 py-1 bg-gradient-to-r from-black via-gray-900 to-black
text-white"
>
<div className="flex justify-between items-center mx-auto max-w-screen-xl h-16">
<div className="absolute left-1/2 transform -translate-x-1/2">
<a href="https://nepalingo.com" className="flex items-center">
<img src={logo} alt="Nepalingo Logo" className="h-12" />
</a>
</div>
<div className="absolute left-1/2 transform -translate-x-1/2 translate-x-40">
<ChangeLanguage />
</div>
<div className="absolute right-4">
<UserProfile />
</div>
Expand Down
16 changes: 13 additions & 3 deletions nepalingo-web/src/components/header/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,20 @@ const Menu: React.FC<MenuProps> = ({ isOpen, onSelect, options }) => {
<button
key={option.value}
type="button"
className="w-full text-left px-4 py-2 text-sm text-white hover:bg-gray-800"
onClick={() => onSelect(option.value)}
className={`w-full text-left px-4 py-2 text-sm text-white ${
option.value === "comingSoon"
? "text-gray-500 cursor-not-allowed"
: "hover:bg-gray-800"
}`}
onClick={() =>
option.value !== "comingSoon" && onSelect(option.value)
}
disabled={option.value === "comingSoon"}
>
{option.label}
{option.label}{" "}
{option.value === "comingSoon" && (
<span className="text-xs text-gray-400">(Coming Soon)</span>
)}
</button>
))}
</div>
Expand Down
25 changes: 22 additions & 3 deletions nepalingo-web/src/components/header/UserProfile.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import React, { useState } from "react";
import React, { useState, useContext } from "react";
import UserAvatar from "../UserAvatar";
import Menu from "./Menu";
import { useAuth } from "../userAuth/AuthContext";
import { StreakContext } from "../StreakContext";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faFire } from "@fortawesome/free-solid-svg-icons";

const UserProfile: React.FC = () => {
const [isOpen, setIsOpen] = useState(false);
const { user, signOut } = useAuth();
const { currentStreak, longestStreak } = useContext(StreakContext); // Get streak data from context

const toggleMenu = () => {
setIsOpen(!isOpen);
Expand All @@ -20,17 +24,32 @@ const UserProfile: React.FC = () => {

const options = [
{ label: `Username: ${user?.user_metadata?.username}`, value: "username" },
{ label: `Current Streak: ${currentStreak} days`, value: "currentStreak" },
{ label: `Longest Streak: ${longestStreak} days`, value: "longestStreak" },
{ label: "Log out", value: "signOut" },
];

return (
<div className="relative inline-block text-left">
<button
type="button"
className="inline-flex justify-center items-center w-10 h-10 rounded-full border border-gray-300 shadow-sm bg-gray-700 text-sm font-medium hover:bg-gray-600 focus:outline-none"
className="inline-flex justify-center items-center w-12 h-12 rounded-full border border-gray-300 shadow-sm bg-gray-700 text-sm font-medium hover:bg-gray-600 focus:outline-none"
onClick={toggleMenu}
>
<UserAvatar />
<div className="relative">
<UserAvatar />
{currentStreak > 0 && (
<div className="absolute bottom-1 right-3 transform translate-x-1/2 translate-y-1/2 flex items-center justify-center">
<FontAwesomeIcon
icon={faFire}
className="text-yellow-600 text-2xl"
/>
<span className="absolute text-sm text-white">
{currentStreak}
</span>
</div>
)}
</div>
</button>
<Menu isOpen={isOpen} onSelect={handleSelect} options={options} />
</div>
Expand Down
6 changes: 4 additions & 2 deletions nepalingo-web/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
import { AuthProvider } from "./components/userAuth/AuthContext";

import { StreakProvider } from "./components/StreakContext";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<AuthProvider>
<App />
<StreakProvider>
<App />
</StreakProvider>
</AuthProvider>
</React.StrictMode>
);
10 changes: 9 additions & 1 deletion nepalingo-web/src/pages/FlashcardPage.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import Flashcard from "../components/Flashcard";
import Header from "../components/header/Header";
import React from "react";
import React, { useEffect } from "react";
import { useStreak } from "../components/StreakContext";
import ReactGA from "react-ga4";

const FlashcardPage: React.FC = () => {
const { updateStreak } = useStreak();

ReactGA.send({
hitType: "pageview",
page: window.location.pathname,
title: "flashcard page",
});

useEffect(() => {
updateStreak(); // Trigger streak update on flashcard page load
}, []);

return (
<>
<Header />
Expand Down
Loading
Loading