Skip to content

Commit

Permalink
Improve login functionality and add logout ability
Browse files Browse the repository at this point in the history
  • Loading branch information
CalPinSW committed Aug 21, 2024
1 parent ab552a9 commit 830aba6
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 52 deletions.
12 changes: 11 additions & 1 deletion backend/src/controllers/auth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from uuid import uuid4
from flask import Blueprint, make_response, request, session
from flask import Blueprint, make_response, redirect, request, session
from src.flask_config import Config
from src.spotify import SpotifyClient


Expand All @@ -15,6 +16,15 @@ def login():
query_string = spotify.get_login_query_string(state)
return "https://accounts.spotify.com/authorize?" + query_string

@auth_controller.route("logout")
def logout():
resp = make_response("Logged out")
resp.delete_cookie("spotify_access_token")
resp.delete_cookie("spotify_refresh_token")
resp.delete_cookie("user_id")
resp.delete_cookie("session")
return resp

@auth_controller.route("get-user-code")
def auth_redirect():
code = request.args.get("code")
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import React, { FC } from "react";
import Box from "./components/Box";
import { login } from "./api";
import Button from "./components/Button";

export const Login: FC = () => {
return (
<div>
<Box>
<button onClick={login}>Click to login</button>
</Box>
<div className="w-full flex">
<Button className="w-1/3 mx-auto mt-12" onClick={login}>
Click to login
</Button>
</div>
);
};
10 changes: 10 additions & 0 deletions frontend/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ export const login = async (): Promise<void> => {
});
};

export const logout = async (): Promise<void> => {
return fetch(`${backendUrl}/auth/logout`, {
credentials: "include",
}).then(async response => {
const redirectUrl = await response.text();
console.log(redirectUrl)
window.open("/", "_self");
});
}

export const getCurrentUserDetails = async (): Promise<User> => {
return jsonRequest(
`spotify/current-user`,
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import React, { FC } from "react";

const Box: FC<
React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
> = (props) => {
> = ({className, ...props}) => {
return (
<div
className={`p-2 rounded border-solid border border-primary-500 ${props.className}`}
className={`p-2 rounded border-solid border border-primary-500 ${className}`}
{...props}
>
{props.children}
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import React, { FC } from "react";

const CustomButton: FC<
const Button: FC<
React.DetailedHTMLProps<
React.ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>
> = (props) => {
> = ({className, ...props}) => {
return (
<button
{...props}
className={`bg-secondary-300 rounded p-2 cursor-pointer hover:bg-secondary-500 ${props.className}`}
className={`bg-secondary-300 rounded p-2 cursor-pointer hover:bg-secondary-500 active:bg-secondary-600 ${className}`}
></button>
);
};

export default CustomButton;
export default Button;
41 changes: 41 additions & 0 deletions frontend/src/components/DropdownMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { FC, ReactNode, useEffect, useRef, useState } from "react";

interface ModalProps {
children: ReactNode;
trigger: ReactNode;
isMenuOpen: boolean;
closeMenu: () => void;
}

const DropdownMenu: FC<ModalProps> = ({ children, trigger, isMenuOpen, closeMenu }) => {
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleClickOutside({target}: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(target as Node)) {
closeMenu();
}
}
document.addEventListener("mousedown", handleClickOutside);

return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [dropdownRef, closeMenu]);

return (
<div className="relative" ref={dropdownRef}>
{trigger}
{isMenuOpen &&
<div

className={`bg-background-popover w-full h-fit absolute opacity-100 origin-top-right left-0 rounded-xl shadow p-6 z-50`}
role={"menu"}
>
{children}
</div>
}
</div>
);
};

export default DropdownMenu;
39 changes: 0 additions & 39 deletions frontend/src/presentational/Header.tsx

This file was deleted.

32 changes: 32 additions & 0 deletions frontend/src/presentational/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from "react";
import { Link } from "react-router-dom";
import { getCurrentUserDetails, login } from "../../api";
import { useQuery } from "@tanstack/react-query";
import { User } from "../../interfaces/User";
import UserMenu from "./UserMenu";

const Header = () => {
const { data: userData } = useQuery<User>({
queryKey: ["current-user"],
queryFn: () => {
return getCurrentUserDetails();
},
});

return (
<div className="flex justify-between top-0 h-16 sm:h-20 bg-primary-300">
<Link to="/" className="flex mx-4 my-auto text-lg">
Playlist Manager
</Link>
<div className="flex mx-4">
{userData ? (
<UserMenu userData={userData} />
) : (
<button onClick={login}>Login</button>
)}
</div>
</div>
);
};

export default Header;
30 changes: 30 additions & 0 deletions frontend/src/presentational/Header/UserMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { FC, ReactNode, useState } from "react";
import DropdownMenu from "../../components/DropdownMenu";
import { User } from "../../interfaces/User";
import Button from "../../components/Button";
import { logout } from "../../api";

interface UserMenuProps {
userData: User;
}

const UserMenu: FC<UserMenuProps> = ({userData}) => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
<DropdownMenu isMenuOpen={isMenuOpen} closeMenu={() => setIsMenuOpen(false)} trigger={
<button className="flex space-x-4" onClick={() => setIsMenuOpen((open) => !open)}>
<div className="my-auto">{userData.display_name}</div>
{userData.images && (
<img
src={userData.images[userData.images.length - 1].url}
className="h-16 sm:h-20 rounded-full"
></img>
)}
</button>
}
>
<Button className={"w-full"} onClick={logout}>Logout</Button>
</DropdownMenu>)
}

export default UserMenu
2 changes: 1 addition & 1 deletion frontend/src/presentational/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import { Outlet } from "react-router-dom";
import PlaybackFooter from "./PlaybackFooter";
import Header from "./Header";
import Header from "./Header/Header";

const Layout = () => {
return (
Expand Down

0 comments on commit 830aba6

Please sign in to comment.