diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3d498ee..9b5ce82 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -6,6 +6,8 @@ import HomePage from "./pages/HomePage"; import StreamerRoute from "./components/Stream/StreamerRoute"; import NotFoundPage from "./pages/NotFoundPage"; import UserPage from "./pages/UserPage"; +import ForgotPasswordPage from "./pages/ForgotPasswordPage"; +import CategoryPage from "./pages/CategoryPage"; function App() { const [isLoggedIn, setIsLoggedIn] = useState(false); @@ -40,6 +42,8 @@ function App() { } /> } /> + }> + }> } /> diff --git a/frontend/src/assets/styles/sidebar.css b/frontend/src/assets/styles/sidebar.css index c2dcd77..02517f8 100644 --- a/frontend/src/assets/styles/sidebar.css +++ b/frontend/src/assets/styles/sidebar.css @@ -4,10 +4,10 @@ :root{ - --sideNav-LightBG: white; - --sideNav-LightText: black; + --sideBar-LightBG: white; + --sideBar-LightText: black; - --sideNav-DarkBG: black; - --sideNav-DarkText: white; + --sideBar-DarkBG: black; + --sideBar-DarkText: white; } \ No newline at end of file diff --git a/frontend/src/components/Layout/Navbar.tsx b/frontend/src/components/Layout/Navbar.tsx index ed85efe..71ad8a0 100644 --- a/frontend/src/components/Layout/Navbar.tsx +++ b/frontend/src/components/Layout/Navbar.tsx @@ -73,17 +73,22 @@ const Navbar: React.FC = ({ {isLoggedIn && ( <> - - {showSideBar && } +
+ +
)} diff --git a/frontend/src/components/Layout/Sidebar.tsx b/frontend/src/components/Layout/Sidebar.tsx index 70d4d1c..f7dd449 100644 --- a/frontend/src/components/Layout/Sidebar.tsx +++ b/frontend/src/components/Layout/Sidebar.tsx @@ -1,15 +1,16 @@ import React, { useEffect, useState } from "react"; +import { SunMoon as SunMoonIcon} from "lucide-react" +import Theme from "./Theme"; import "../../assets/styles/sidebar.css" interface SideBarProps { extraClasses?: string; - scrollActiveSideBar: boolean; } -const Sidebar: React.FC = ( {scrollActiveSideBar}) => { +const Sidebar: React.FC = () => { const [thisTheme, setThisTheme] = useState(false); const [isCursorOnSidebar, setIsCursorOnSidebar] = useState(false); - + const [triggerAnimation, setTriggerAnimation] = useState(false); useEffect(() => { const sideBarScroll = () => { document.body.style.overflow = isCursorOnSidebar ? "hidden" : "unset"; @@ -21,10 +22,22 @@ const Sidebar: React.FC = ( {scrollActiveSideBar}) => { }; }, [isCursorOnSidebar]); - return ); }; export default Sidebar; diff --git a/frontend/src/components/Layout/Theme.tsx b/frontend/src/components/Layout/Theme.tsx new file mode 100644 index 0000000..510c538 --- /dev/null +++ b/frontend/src/components/Layout/Theme.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { SunMoon as SunMoonIcon } from 'lucide-react'; + +interface ThemeProps { + children?: React.ReactNode; + onClick: () => void; +} + +const Theme: React.FC = ({ onClick }) => { + return ( + + ); +}; + +export default Theme; diff --git a/frontend/src/pages/CategoryPage.tsx b/frontend/src/pages/CategoryPage.tsx new file mode 100644 index 0000000..00493cf --- /dev/null +++ b/frontend/src/pages/CategoryPage.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +const CategoryPage = () => { + return ( +
CategoryPage
+ ) +} + +export default CategoryPage \ No newline at end of file diff --git a/frontend/src/pages/ForgotPasswordPage.tsx b/frontend/src/pages/ForgotPasswordPage.tsx new file mode 100644 index 0000000..42474d5 --- /dev/null +++ b/frontend/src/pages/ForgotPasswordPage.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +const ForgotPasswordPage = () => { + return ( +
ForgotPasswordPage
+ ) +} + +export default ForgotPasswordPage \ No newline at end of file diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index 7517be4..c2b6dfc 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -17,6 +17,11 @@ const HomePage: React.FC = ({ variant = "default" }) => { navigate(`/${streamerName}`); }; + const handleCategoryClick = (categoryID: number, categoryName: string) => { + console.log(`Navigating to category ${categoryID}`); + navigate(`category/${categoryName}`); + }; + return (
= ({ variant = "default" }) => { : "Categories that have been 'popping off' lately" } items={featuredCategories} - onClick={() => {}} //TODO + onClick={handleCategoryClick} />
); diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index ceee4f6..e6f6959 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -13,7 +13,8 @@ export default { moving_text_colour: "moving_text_colour 6s ease-in-out infinite alternate", moving_bg: 'moving_bg 200s linear infinite', 'border-spin': 'border-spin linear infinite', - floating: "floating 30s linear infinite" + floating: "floating 30s linear infinite", + burnIn: 'burnIn 1s ease-out', }, @@ -48,12 +49,13 @@ export default { '80%': { transform: 'translate(10px, -7px) rotateX(-2.5deg) rotateY(1.5deg)' }, /* Top-right tilt */ '100%': { transform: 'translate(0px, -5px) rotateX(0deg) rotateY(0deg)' }, }, + + burnIn: { + '0%' : { opacity: '0'}, + '50%' : { opacity: '0.8'}, + '100%' : { opacity: '1'}, + }, }, - - colors: { - "sideBar-bg": "var(--sideBar-LightBG)", - "sideBar-text": "var(--sideBar-LightText)" - } }, plugins: [ require('tailwind-scrollbar-hide') diff --git a/web_server/blueprints/email.py b/web_server/blueprints/email.py index 1632d4c..b588f7c 100644 --- a/web_server/blueprints/email.py +++ b/web_server/blueprints/email.py @@ -1,6 +1,3 @@ -from flask import Blueprint, session -from database.database import Database - import smtplib from email.mime.text import MIMEText @@ -10,9 +7,7 @@ from dotenv import load_dotenv load_dotenv() -email_bp = Blueprint("email", __name__) - -def send_email(username) -> None: +def send_email(email) -> None: """ Send a verification email to the user. """ @@ -23,8 +18,6 @@ def send_email(username) -> None: SMTP_EMAIL = getenv("EMAIL") SMTP_PASSWORD = getenv("EMAIL_PASSWORD") - user_email = get_user_email(username) - # Setup up the receiver details login_code = randrange(100000, 1000000) body = f""" @@ -39,7 +32,7 @@ def send_email(username) -> None: msg = MIMEText(body, "html") msg["Subject"] = "Reset Gander Login" msg["From"] = SMTP_EMAIL - msg["To"] = user_email + msg["To"] = email # Send the email using smtplib with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as smtp: @@ -53,20 +46,4 @@ def send_email(username) -> None: print("Server timed out") except Exception as e: - print("Error: ", e) - -def get_user_email(username): - """ - Get the users email address. - """ - - db = Database() - db.create_connection() - - user_email = db.fetchone("""SELECT email - FROM users - WHERE username = ?;""", - (username,)) - email = user_email["email"] - db.close_connection() - return email \ No newline at end of file + print("Error: ", e) \ No newline at end of file diff --git a/web_server/blueprints/user.py b/web_server/blueprints/user.py index d4b6960..456b893 100644 --- a/web_server/blueprints/user.py +++ b/web_server/blueprints/user.py @@ -1,7 +1,6 @@ from flask import Blueprint, jsonify, session from utils.user_utils import is_subscribed, is_following, subscription_expiration, verify_token, reset_password, get_user_id, unfollow from blueprints.utils import login_required -from utils.user_utils import get_email user_bp = Blueprint("user", __name__) @@ -26,17 +25,25 @@ def user_following(user_id: int, subscribed_id: int): return jsonify({"following": False}) @login_required -@user_bp.route('/unfollow/') +@user_bp.route('/follow/') +def follow(username): + """ + Follows a user + """ + user_id = session.get("user_id") + following_id = get_user_id(username) + follow(user_id, following_id) + + +@login_required +@user_bp.route('/unfollow/') def user_unfollow(followed_username): """ Unfollows a user """ user_id = session.get("user_id") followed_id = get_user_id(followed_username) - status = unfollow(user_id, followed_id) - - status = True if status else False - return jsonify({"status": status}) + unfollow(user_id, followed_id) @login_required @user_bp.route('/subscription_remaining/') @@ -58,16 +65,13 @@ def get_login_status(): username = session.get("username") return jsonify({'status': username is not None, 'username': username}) -@user_bp.route('/forgot_password/', defaults={'email': None}, methods=['POST']) @user_bp.route('/forgot_password/', methods=['POST']) def user_forgot_password(email): """ Will send link to email to reset password by looking at the user_id within session to see whos password should be reset Creates a super random number to be used a the link to reset password I guess a random number generator seeded with a secret """ - user_id = session.get("user_id") - if user_id != None: - email = get_email(user_id) + return @user_bp.route('/reset_password//') diff --git a/web_server/utils/user_utils.py b/web_server/utils/user_utils.py index e6ba9c2..c4bb5fc 100644 --- a/web_server/utils/user_utils.py +++ b/web_server/utils/user_utils.py @@ -72,7 +72,24 @@ def is_following(user_id: int, followed_id: int) -> bool: """, (user_id, followed_id)) return bool(result) -def unfollow(user_id: int, followed_id: int) -> bool: +def follow(user_id: int, following_id: int): + """ + Follows following_id user from user_id user + """ + with Database() as db: + data = db.execute(""" + SELECT * FROM follows + WHERE user_id = ? + AND followed_id = ? + """, (user_id, following_id)) + + if not data: + db.execute(""" + INSERT INTO follows (user_id, followed_id) + VALUES(?,?) + """, (user_id, following_id)) + +def unfollow(user_id: int, followed_id: int): """ Unfollows follow_id user from user_id user """ @@ -82,8 +99,7 @@ def unfollow(user_id: int, followed_id: int) -> bool: WHERE user_id = ? AND followed_id = ? """, (user_id, followed_id)) - return True - return False + def subscription_expiration(user_id: int, subscribed_id: int) -> int: """