[WEB-1319] chore: handled redirection when user is not logged in (#4497)

* chore: handled redirection when user is not logged in

* dev: handle url redirection in space app

* dev: remove user from redis on successful code matching
This commit is contained in:
guru_sainath 2024-05-17 14:27:49 +05:30 committed by GitHub
parent c2e293cf3b
commit 2988d5e429
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 119 additions and 175 deletions

View File

@ -125,6 +125,8 @@ class MagicCodeProvider(CredentialAdapter):
}, },
} }
) )
# Delete the token from redis if the code match is successful
ri.delete(self.key)
return return
else: else:
raise AuthenticationException( raise AuthenticationException(

View File

@ -1,5 +1,5 @@
# Python imports # Python imports
from urllib.parse import urlencode, urljoin from urllib.parse import urlencode
# Django imports # Django imports
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -36,10 +36,7 @@ class SignInAuthSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# set the referer as session to redirect after login # set the referer as session to redirect after login
@ -58,10 +55,7 @@ class SignInAuthSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# Validate email # Validate email
@ -77,10 +71,7 @@ class SignInAuthSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# Existing User # Existing User
@ -95,10 +86,7 @@ class SignInAuthSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
if not existing_user.is_active: if not existing_user.is_active:
@ -111,10 +99,7 @@ class SignInAuthSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
try: try:
@ -125,19 +110,13 @@ class SignInAuthSpaceEndpoint(View):
# Login the user and record his device info # Login the user and record his device info
user_login(request=request, user=user, is_space=True) user_login(request=request, user=user, is_space=True)
# redirect to next path # redirect to next path
url = urljoin( url = f"{base_host(request=request, is_space=True)}{str(next_path) if next_path else ''}"
base_host(request=request, is_space=True),
str(next_path) if next_path else "",
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
except AuthenticationException as e: except AuthenticationException as e:
params = e.get_error_dict() params = e.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -158,10 +137,7 @@ class SignUpAuthSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
email = request.POST.get("email", False) email = request.POST.get("email", False)
@ -179,10 +155,7 @@ class SignUpAuthSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# Validate the email # Validate the email
email = email.strip().lower() email = email.strip().lower()
@ -198,10 +171,7 @@ class SignUpAuthSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# Existing User # Existing User
@ -218,10 +188,7 @@ class SignUpAuthSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
exc = AuthenticationException( exc = AuthenticationException(
@ -232,10 +199,7 @@ class SignUpAuthSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
try: try:
@ -246,17 +210,11 @@ class SignUpAuthSpaceEndpoint(View):
# Login the user and record his device info # Login the user and record his device info
user_login(request=request, user=user, is_space=True) user_login(request=request, user=user, is_space=True)
# redirect to referer path # redirect to referer path
url = urljoin( url = f"{base_host(request=request, is_space=True)}{str(next_path) if next_path else ''}"
base_host(request=request, is_space=True),
str(next_path) if next_path else "",
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
except AuthenticationException as e: except AuthenticationException as e:
params = e.get_error_dict() params = e.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)

View File

@ -1,6 +1,6 @@
# Python imports # Python imports
import uuid import uuid
from urllib.parse import urlencode, urljoin from urllib.parse import urlencode
# Django import # Django import
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
@ -38,10 +38,7 @@ class GitHubOauthInitiateSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
try: try:
@ -54,10 +51,7 @@ class GitHubOauthInitiateSpaceEndpoint(View):
params = e.get_error_dict() params = e.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -79,10 +73,7 @@ class GitHubCallbackSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host,
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
if not code: if not code:
@ -95,10 +86,7 @@ class GitHubCallbackSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host,
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
try: try:
@ -111,14 +99,11 @@ class GitHubCallbackSpaceEndpoint(View):
user_login(request=request, user=user, is_space=True) user_login(request=request, user=user, is_space=True)
# Process workspace and project invitations # Process workspace and project invitations
# redirect to referer path # redirect to referer path
url = urljoin(base_host, str(next_path) if next_path else "") url = f"{base_host(request=request, is_space=True)}{str(next_path) if next_path else ''}"
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
except AuthenticationException as e: except AuthenticationException as e:
params = e.get_error_dict() params = e.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host,
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)

View File

@ -1,6 +1,6 @@
# Python imports # Python imports
import uuid import uuid
from urllib.parse import urlencode, urljoin from urllib.parse import urlencode
# Django import # Django import
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
@ -36,10 +36,7 @@ class GoogleOauthInitiateSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
try: try:
@ -52,10 +49,7 @@ class GoogleOauthInitiateSpaceEndpoint(View):
params = e.get_error_dict() params = e.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -76,10 +70,7 @@ class GoogleCallbackSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host,
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
if not code: if not code:
exc = AuthenticationException( exc = AuthenticationException(
@ -91,10 +82,7 @@ class GoogleCallbackSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = next_path params["next_path"] = next_path
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host,
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
try: try:
provider = GoogleOAuthProvider( provider = GoogleOAuthProvider(
@ -105,16 +93,11 @@ class GoogleCallbackSpaceEndpoint(View):
# Login the user and record his device info # Login the user and record his device info
user_login(request=request, user=user, is_space=True) user_login(request=request, user=user, is_space=True)
# redirect to referer path # redirect to referer path
url = urljoin( url = f"{base_host(request=request, is_space=True)}{str(next_path) if next_path else ''}"
base_host, str(next_path) if next_path else "/spaces"
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
except AuthenticationException as e: except AuthenticationException as e:
params = e.get_error_dict() params = e.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host,
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)

View File

@ -1,5 +1,5 @@
# Python imports # Python imports
from urllib.parse import urlencode, urljoin from urllib.parse import urlencode
# Django imports # Django imports
from django.core.validators import validate_email from django.core.validators import validate_email
@ -84,10 +84,7 @@ class MagicSignInSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
existing_user = User.objects.filter(email=email).first() existing_user = User.objects.filter(email=email).first()
@ -100,10 +97,7 @@ class MagicSignInSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# Active User # Active User
@ -117,10 +111,7 @@ class MagicSignInSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
try: try:
provider = MagicCodeProvider( provider = MagicCodeProvider(
@ -136,17 +127,14 @@ class MagicSignInSpaceEndpoint(View):
else: else:
# Get the redirection path # Get the redirection path
path = str(next_path) if next_path else "" path = str(next_path) if next_path else ""
url = urljoin(base_host(request=request, is_space=True), path) url = f"{base_host(request=request, is_space=True)}{path}"
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
except AuthenticationException as e: except AuthenticationException as e:
params = e.get_error_dict() params = e.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -169,10 +157,7 @@ class MagicSignUpSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# Existing User # Existing User
existing_user = User.objects.filter(email=email).first() existing_user = User.objects.filter(email=email).first()
@ -185,10 +170,7 @@ class MagicSignUpSpaceEndpoint(View):
params = exc.get_error_dict() params = exc.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
try: try:
@ -199,18 +181,12 @@ class MagicSignUpSpaceEndpoint(View):
# Login the user and record his device info # Login the user and record his device info
user_login(request=request, user=user, is_space=True) user_login(request=request, user=user, is_space=True)
# redirect to referer path # redirect to referer path
url = urljoin( url = f"{base_host(request=request, is_space=True)}{str(next_path) if next_path else ''}"
base_host(request=request, is_space=True),
str(next_path) if next_path else "spaces",
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
except AuthenticationException as e: except AuthenticationException as e:
params = e.get_error_dict() params = e.get_error_dict()
if next_path: if next_path:
params["next_path"] = str(next_path) params["next_path"] = str(next_path)
url = urljoin( url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}"
base_host(request=request, is_space=True),
"?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)

View File

@ -1,6 +1,6 @@
# Python imports # Python imports
import os import os
from urllib.parse import urlencode, urljoin from urllib.parse import urlencode
# Third party imports # Third party imports
from rest_framework import status from rest_framework import status
@ -145,10 +145,7 @@ class ResetPasswordSpaceEndpoint(View):
error_message="INVALID_PASSWORD_TOKEN", error_message="INVALID_PASSWORD_TOKEN",
) )
params = exc.get_error_dict() params = exc.get_error_dict()
url = urljoin( url = f"{base_host(request=request, is_space=True)}/accounts/reset-password/?{urlencode(params)}"
base_host(request=request, is_space=True),
"accounts/reset-password?" + urlencode(params),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
password = request.POST.get("password", False) password = request.POST.get("password", False)
@ -158,10 +155,7 @@ class ResetPasswordSpaceEndpoint(View):
error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"], error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"],
error_message="INVALID_PASSWORD", error_message="INVALID_PASSWORD",
) )
url = urljoin( url = f"{base_host(request=request, is_space=True)}/accounts/reset-password/?{urlencode(exc.get_error_dict())}"
base_host(request=request, is_space=True),
"?" + urlencode(exc.get_error_dict()),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# Check the password complexity # Check the password complexity
@ -171,11 +165,7 @@ class ResetPasswordSpaceEndpoint(View):
error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"], error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"],
error_message="INVALID_PASSWORD", error_message="INVALID_PASSWORD",
) )
url = urljoin( url = f"{base_host(request=request, is_space=True)}/accounts/reset-password/?{urlencode(exc.get_error_dict())}"
base_host(request=request, is_space=True),
"accounts/reset-password?"
+ urlencode(exc.get_error_dict()),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# set_password also hashes the password that the user will get # set_password also hashes the password that the user will get
@ -193,8 +183,5 @@ class ResetPasswordSpaceEndpoint(View):
], ],
error_message="EXPIRED_PASSWORD_TOKEN", error_message="EXPIRED_PASSWORD_TOKEN",
) )
url = urljoin( url = f"{base_host(request=request, is_space=True)}/accounts/reset-password/?{urlencode(exc.get_error_dict())}"
base_host(request=request, is_space=True),
"accounts/reset-password?" + urlencode(exc.get_error_dict()),
)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)

View File

@ -1,4 +1,5 @@
"use client"; "use client";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import useSWR from "swr"; import useSWR from "swr";
// components // components
@ -9,17 +10,18 @@ import { AuthView } from "@/components/views";
import { useUser } from "@/hooks/store"; import { useUser } from "@/hooks/store";
function HomePage() { function HomePage() {
const { fetchCurrentUser, isAuthenticated, isLoading } = useUser(); const { data: currentUser, fetchCurrentUser, isAuthenticated, isLoading } = useUser();
useSWR("CURRENT_USER", () => fetchCurrentUser(), { errorRetryCount: 0 }); useSWR("CURRENT_USER", () => fetchCurrentUser(), {
errorRetryCount: 0,
revalidateIfStale: false,
revalidateOnFocus: false,
refreshWhenHidden: false,
});
if (isLoading) { if (isLoading) return <LogoSpinner />;
return <LogoSpinner />;
}
if (isAuthenticated) { if (currentUser && isAuthenticated) return <UserLoggedIn />;
return <UserLoggedIn />;
}
return <AuthView />; return <AuthView />;
} }

View File

@ -35,6 +35,7 @@ export const AuthRoot: FC = observer(() => {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const emailParam = searchParams.get("email") || undefined; const emailParam = searchParams.get("email") || undefined;
const error_code = searchParams.get("error_code") || undefined; const error_code = searchParams.get("error_code") || undefined;
const nextPath = searchParams.get("next_path") || undefined;
// states // states
const [authMode, setAuthMode] = useState<EAuthModes>(EAuthModes.SIGN_UP); const [authMode, setAuthMode] = useState<EAuthModes>(EAuthModes.SIGN_UP);
const [authStep, setAuthStep] = useState<EAuthSteps>(EAuthSteps.EMAIL); const [authStep, setAuthStep] = useState<EAuthSteps>(EAuthSteps.EMAIL);
@ -141,6 +142,7 @@ export const AuthRoot: FC = observer(() => {
<AuthUniqueCodeForm <AuthUniqueCodeForm
mode={authMode} mode={authMode}
email={email} email={email}
nextPath={nextPath}
handleEmailClear={() => { handleEmailClear={() => {
setEmail(""); setEmail("");
setAuthStep(EAuthSteps.EMAIL); setAuthStep(EAuthSteps.EMAIL);
@ -154,6 +156,7 @@ export const AuthRoot: FC = observer(() => {
isPasswordAutoset={isPasswordAutoset} isPasswordAutoset={isPasswordAutoset}
isSMTPConfigured={isSMTPConfigured} isSMTPConfigured={isSMTPConfigured}
email={email} email={email}
nextPath={nextPath}
handleEmailClear={() => { handleEmailClear={() => {
setEmail(""); setEmail("");
setAuthStep(EAuthSteps.EMAIL); setAuthStep(EAuthSteps.EMAIL);

View File

@ -19,6 +19,7 @@ type Props = {
isPasswordAutoset: boolean; isPasswordAutoset: boolean;
isSMTPConfigured: boolean; isSMTPConfigured: boolean;
mode: EAuthModes; mode: EAuthModes;
nextPath: string | undefined;
handleEmailClear: () => void; handleEmailClear: () => void;
handleAuthStep: (step: EAuthSteps) => void; handleAuthStep: (step: EAuthSteps) => void;
}; };
@ -37,7 +38,7 @@ const defaultValues: TPasswordFormValues = {
const authService = new AuthService(); const authService = new AuthService();
export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => { export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
const { email, isSMTPConfigured, handleAuthStep, handleEmailClear, mode } = props; const { email, nextPath, isSMTPConfigured, handleAuthStep, handleEmailClear, mode } = props;
// states // states
const [csrfToken, setCsrfToken] = useState<string | undefined>(undefined); const [csrfToken, setCsrfToken] = useState<string | undefined>(undefined);
const [passwordFormData, setPasswordFormData] = useState<TPasswordFormValues>({ ...defaultValues, email }); const [passwordFormData, setPasswordFormData] = useState<TPasswordFormValues>({ ...defaultValues, email });
@ -65,6 +66,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
}; };
const passwordSupport = passwordFormData.password.length > 0 && const passwordSupport = passwordFormData.password.length > 0 &&
mode === EAuthModes.SIGN_UP &&
(getPasswordStrength(passwordFormData.password) < 3 || isPasswordInputFocused) && ( (getPasswordStrength(passwordFormData.password) < 3 || isPasswordInputFocused) && (
<PasswordStrengthMeter password={passwordFormData.password} /> <PasswordStrengthMeter password={passwordFormData.password} />
); );
@ -92,6 +94,7 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
> >
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} /> <input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<input type="hidden" value={passwordFormData.email} name="email" /> <input type="hidden" value={passwordFormData.email} name="email" />
<input type="hidden" value={nextPath} name="next_path" />
<div className="space-y-1"> <div className="space-y-1">
<label className="text-sm font-medium text-onboarding-text-300" htmlFor="email"> <label className="text-sm font-medium text-onboarding-text-300" htmlFor="email">
Email Email

View File

@ -18,6 +18,7 @@ const authService = new AuthService();
type TAuthUniqueCodeForm = { type TAuthUniqueCodeForm = {
mode: EAuthModes; mode: EAuthModes;
email: string; email: string;
nextPath: string | undefined;
handleEmailClear: () => void; handleEmailClear: () => void;
generateEmailUniqueCode: (email: string) => Promise<{ code: string } | undefined>; generateEmailUniqueCode: (email: string) => Promise<{ code: string } | undefined>;
}; };
@ -33,7 +34,7 @@ const defaultValues: TUniqueCodeFormValues = {
}; };
export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => { export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
const { mode, email, handleEmailClear, generateEmailUniqueCode } = props; const { mode, email, nextPath, handleEmailClear, generateEmailUniqueCode } = props;
// hooks // hooks
// const { captureEvent } = useEventTracker(); // const { captureEvent } = useEventTracker();
// derived values // derived values
@ -81,6 +82,7 @@ export const AuthUniqueCodeForm: React.FC<TAuthUniqueCodeForm> = (props) => {
> >
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} /> <input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<input type="hidden" value={uniqueCodeFormData.email} name="email" /> <input type="hidden" value={uniqueCodeFormData.email} name="email" />
<input type="hidden" value={nextPath} name="next_path" />
<div className="space-y-1"> <div className="space-y-1">
<label className="text-sm font-medium text-onboarding-text-300" htmlFor="email"> <label className="text-sm font-medium text-onboarding-text-300" htmlFor="email">
Email Email

View File

@ -18,10 +18,10 @@ export const OAuthOptions: React.FC = observer(() => {
<div className={`mt-7 grid gap-4 overflow-hidden`}> <div className={`mt-7 grid gap-4 overflow-hidden`}>
{instance?.config?.is_google_enabled && ( {instance?.config?.is_google_enabled && (
<div className="flex h-[42px] items-center !overflow-hidden"> <div className="flex h-[42px] items-center !overflow-hidden">
<GoogleOAuthButton text="SignIn with Google" /> <GoogleOAuthButton text="Sign in with Google" />
</div> </div>
)} )}
{instance?.config?.is_github_enabled && <GithubOAuthButton text="SignIn with Github" />} {instance?.config?.is_github_enabled && <GithubOAuthButton text="Sign in with Github" />}
</div> </div>
</> </>
); );

View File

@ -1,10 +1,12 @@
import React from "react"; import React from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { Tooltip } from "@plane/ui"; import { Tooltip } from "@plane/ui";
// ui // ui
import { ReactionSelector } from "@/components/ui"; import { ReactionSelector } from "@/components/ui";
// helpers // helpers
import { groupReactions, renderEmoji } from "@/helpers/emoji.helper"; import { groupReactions, renderEmoji } from "@/helpers/emoji.helper";
import { queryParamGenerator } from "@/helpers/query-param-generator";
// hooks // hooks
import { useIssueDetails, useUser } from "@/hooks/store"; import { useIssueDetails, useUser } from "@/hooks/store";
@ -15,6 +17,15 @@ type Props = {
}; };
export const CommentReactions: React.FC<Props> = observer((props) => { export const CommentReactions: React.FC<Props> = observer((props) => {
const router = useRouter();
const pathName = usePathname();
const searchParams = useSearchParams();
// query params
const board = searchParams.get("board") || undefined;
const state = searchParams.get("state") || undefined;
const priority = searchParams.get("priority") || undefined;
const labels = searchParams.get("labels") || undefined;
const { commentId, projectId, workspaceSlug } = props; const { commentId, projectId, workspaceSlug } = props;
// hooks // hooks
const { addCommentReaction, removeCommentReaction, details, peekId } = useIssueDetails(); const { addCommentReaction, removeCommentReaction, details, peekId } = useIssueDetails();
@ -42,13 +53,15 @@ export const CommentReactions: React.FC<Props> = observer((props) => {
else handleAddReaction(reactionHex); else handleAddReaction(reactionHex);
}; };
// TODO: on onclick redirect to login page if the user is not logged in // derived values
const { queryParam } = queryParamGenerator({ peekId, board, state, priority, labels });
return ( return (
<div className="mt-2 flex items-center gap-1.5"> <div className="mt-2 flex items-center gap-1.5">
<ReactionSelector <ReactionSelector
onSelect={(value) => { onSelect={(value) => {
if (user) handleReactionClick(value); if (user) handleReactionClick(value);
// userStore.requiredLogin(() => {}); else router.push(`/?next_path=${pathName}?${queryParam}`);
}} }}
position="top" position="top"
selected={userReactions?.map((r) => r.reaction)} selected={userReactions?.map((r) => r.reaction)}
@ -77,7 +90,7 @@ export const CommentReactions: React.FC<Props> = observer((props) => {
type="button" type="button"
onClick={() => { onClick={() => {
if (user) handleReactionClick(reaction); if (user) handleReactionClick(reaction);
// userStore.requiredLogin(() => {}); else router.push(`/?next_path=${pathName}?${queryParam}`);
}} }}
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${ className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
commentReactions?.some((r) => r.actor_detail.id === user?.id && r.reaction === reaction) commentReactions?.some((r) => r.actor_detail.id === user?.id && r.reaction === reaction)

View File

@ -1,10 +1,12 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
// lib // lib
import { Tooltip } from "@plane/ui"; import { Tooltip } from "@plane/ui";
import { ReactionSelector } from "@/components/ui"; import { ReactionSelector } from "@/components/ui";
// helpers // helpers
import { groupReactions, renderEmoji } from "@/helpers/emoji.helper"; import { groupReactions, renderEmoji } from "@/helpers/emoji.helper";
import { queryParamGenerator } from "@/helpers/query-param-generator";
// hooks // hooks
import { useIssueDetails, useUser } from "@/hooks/store"; import { useIssueDetails, useUser } from "@/hooks/store";
@ -14,6 +16,16 @@ type IssueEmojiReactionsProps = {
}; };
export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer((props) => { export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer((props) => {
const router = useRouter();
const pathName = usePathname();
const searchParams = useSearchParams();
// query params
const peekId = searchParams.get("peekId") || undefined;
const board = searchParams.get("board") || undefined;
const state = searchParams.get("state") || undefined;
const priority = searchParams.get("priority") || undefined;
const labels = searchParams.get("labels") || undefined;
const { workspaceSlug, projectId } = props; const { workspaceSlug, projectId } = props;
// store // store
const issueDetailsStore = useIssueDetails(); const issueDetailsStore = useIssueDetails();
@ -46,13 +58,15 @@ export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer(
fetchCurrentUser(); fetchCurrentUser();
}, [user, fetchCurrentUser]); }, [user, fetchCurrentUser]);
// TODO: on onclick of reaction, if user not logged in redirect to login page // derived values
const { queryParam } = queryParamGenerator({ peekId, board, state, priority, labels });
return ( return (
<> <>
<ReactionSelector <ReactionSelector
onSelect={(value) => { onSelect={(value) => {
if (user) handleReactionClick(value); if (user) handleReactionClick(value);
// userStore.requiredLogin(() => {}); else router.push(`/?next_path=${pathName}?${queryParam}`);
}} }}
selected={userReactions?.map((r) => r.reaction)} selected={userReactions?.map((r) => r.reaction)}
size="md" size="md"
@ -80,7 +94,7 @@ export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer(
type="button" type="button"
onClick={() => { onClick={() => {
if (user) handleReactionClick(reaction); if (user) handleReactionClick(reaction);
// userStore.requiredLogin(() => {}); else router.push(`/?next_path=${pathName}?${queryParam}`);
}} }}
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${ className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
reactions?.some((r) => r.actor_detail.id === user?.id && r.reaction === reaction) reactions?.some((r) => r.actor_detail.id === user?.id && r.reaction === reaction)

View File

@ -2,11 +2,24 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { Tooltip } from "@plane/ui"; import { Tooltip } from "@plane/ui";
// helpers
import { queryParamGenerator } from "@/helpers/query-param-generator";
// hooks // hooks
import { useIssueDetails, useUser } from "@/hooks/store"; import { useIssueDetails, useUser } from "@/hooks/store";
export const IssueVotes: React.FC = observer((props: any) => { export const IssueVotes: React.FC = observer((props: any) => {
const router = useRouter();
const pathName = usePathname();
const searchParams = useSearchParams();
// query params
const peekId = searchParams.get("peekId") || undefined;
const board = searchParams.get("board") || undefined;
const state = searchParams.get("state") || undefined;
const priority = searchParams.get("priority") || undefined;
const labels = searchParams.get("labels") || undefined;
const { workspaceSlug, projectId } = props; const { workspaceSlug, projectId } = props;
// states // states
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
@ -49,6 +62,9 @@ export const IssueVotes: React.FC = observer((props: any) => {
const VOTES_LIMIT = 1000; const VOTES_LIMIT = 1000;
// derived values
const { queryParam } = queryParamGenerator({ peekId, board, state, priority, labels });
return ( return (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{/* upvote button 👇 */} {/* upvote button 👇 */}
@ -74,7 +90,7 @@ export const IssueVotes: React.FC = observer((props: any) => {
disabled={isSubmitting} disabled={isSubmitting}
onClick={(e) => { onClick={(e) => {
if (user) handleVote(e, 1); if (user) handleVote(e, 1);
// userStore.requiredLogin(() => {}); else router.push(`/?next_path=${pathName}?${queryParam}`);
}} }}
className={`flex items-center justify-center gap-x-1 overflow-hidden rounded border px-2 focus:outline-none ${ className={`flex items-center justify-center gap-x-1 overflow-hidden rounded border px-2 focus:outline-none ${
isUpVotedByUser ? "border-custom-primary-200 text-custom-primary-200" : "border-custom-border-300" isUpVotedByUser ? "border-custom-primary-200 text-custom-primary-200" : "border-custom-border-300"
@ -108,7 +124,7 @@ export const IssueVotes: React.FC = observer((props: any) => {
disabled={isSubmitting} disabled={isSubmitting}
onClick={(e) => { onClick={(e) => {
if (user) handleVote(e, -1); if (user) handleVote(e, -1);
// userStore.requiredLogin(() => {}); else router.push(`/?next_path=${pathName}?${queryParam}`);
}} }}
className={`flex items-center justify-center gap-x-1 overflow-hidden rounded border px-2 focus:outline-none ${ className={`flex items-center justify-center gap-x-1 overflow-hidden rounded border px-2 focus:outline-none ${
isDownVotedByUser ? "border-red-600 text-red-600" : "border-custom-border-300" isDownVotedByUser ? "border-red-600 text-red-600" : "border-custom-border-300"