chore: updated intance and auth wrapper logic

This commit is contained in:
gurusainath 2024-05-01 14:54:35 +05:30
parent 86196c38bc
commit dd7c500bfe
18 changed files with 211 additions and 63 deletions

View File

@ -212,3 +212,4 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
</form>
);
});

View File

@ -91,7 +91,7 @@ export const SignUpAuthRoot: FC = observer(() => {
);
return (
<div className="relative max-w-lg mx-auto flex flex-col space-y-6">
<div className="relative max-w-lg px-5 border border-red-500 mx-auto flex flex-col space-y-6">
<AuthHeader
workspaceSlug={workspaceSlug?.toString() || undefined}
invitationId={invitation_id?.toString() || undefined}

View File

@ -1,3 +1,10 @@
export enum EPageTypes {
"PUBLIC" = "PUBLIC",
"NON_AUTHENTICATED" = "NON_AUTHENTICATED",
"ONBOARDING" = "ONBOARDING",
"AUTHENTICATED" = "AUTHENTICATED",
}
export enum EAuthModes {
SIGN_IN = "SIGN_IN",
SIGN_UP = "SIGN_UP",

View File

@ -4,7 +4,9 @@ import { observer } from "mobx-react-lite";
import { CommandPalette } from "@/components/command-palette";
import { SidebarHamburgerToggle } from "@/components/core/sidebar";
// layouts
import { UserAuthWrapper, WorkspaceAuthWrapper, ProjectAuthWrapper } from "@/layouts/auth-layout";
import { WorkspaceAuthWrapper, ProjectAuthWrapper } from "@/layouts/auth-layout";
// wrappers
import { AuthenticationWrapper } from "@/lib/wrappers";
import { AppSidebar } from "./sidebar";
export interface IAppLayout {
@ -18,31 +20,29 @@ export const AppLayout: FC<IAppLayout> = observer((props) => {
const { children, header, withProjectWrapper = false, mobileHeader } = props;
return (
<>
<UserAuthWrapper>
<CommandPalette />
<WorkspaceAuthWrapper>
<div className="relative flex h-screen w-full overflow-hidden">
<AppSidebar />
<main className="relative flex h-full w-full flex-col overflow-hidden bg-custom-background-100">
<div className="z-[15]">
<div className="z-10 flex w-full items-center border-b border-custom-border-200">
<div className="block bg-custom-sidebar-background-100 py-4 pl-5 md:hidden">
<SidebarHamburgerToggle />
</div>
<div className="w-full">{header}</div>
<AuthenticationWrapper>
<CommandPalette />
<WorkspaceAuthWrapper>
<div className="relative flex h-screen w-full overflow-hidden">
<AppSidebar />
<main className="relative flex h-full w-full flex-col overflow-hidden bg-custom-background-100">
<div className="z-[15]">
<div className="z-10 flex w-full items-center border-b border-custom-border-200">
<div className="block bg-custom-sidebar-background-100 py-4 pl-5 md:hidden">
<SidebarHamburgerToggle />
</div>
{mobileHeader && mobileHeader}
<div className="w-full">{header}</div>
</div>
<div className="h-full w-full overflow-hidden">
<div className="relative h-full w-full overflow-x-hidden overflow-y-scroll">
{withProjectWrapper ? <ProjectAuthWrapper>{children}</ProjectAuthWrapper> : <>{children}</>}
</div>
{mobileHeader && mobileHeader}
</div>
<div className="h-full w-full overflow-hidden">
<div className="relative h-full w-full overflow-x-hidden overflow-y-scroll">
{withProjectWrapper ? <ProjectAuthWrapper>{children}</ProjectAuthWrapper> : <>{children}</>}
</div>
</main>
</div>
</WorkspaceAuthWrapper>
</UserAuthWrapper>
</>
</div>
</main>
</div>
</WorkspaceAuthWrapper>
</AuthenticationWrapper>
);
});

View File

@ -57,5 +57,3 @@ export const UserAuthWrapper: FC<IUserAuthWrapper> = observer((props) => {
return <>{children}</>;
});

View File

@ -25,7 +25,7 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
const router = useRouter();
const { workspaceSlug } = router.query;
// next themes
const { resolvedTheme, setTheme } = useTheme();
const { resolvedTheme } = useTheme();
// store hooks
const { membership, signOut, data: currentUser } = useUser();
const { fetchProjects } = useProject();

View File

@ -13,8 +13,8 @@ import { SWR_CONFIG } from "@/constants/swr-config";
import { resolveGeneralTheme } from "@/helpers/theme.helper";
// hooks
import { useInstance, useWorkspace, useUser } from "@/hooks/store";
// layouts
import InstanceLayout from "@/lib/wrappers/instance-wrapper";
// wrappers
import { InstanceWrapper } from "@/lib/wrappers";
// dynamic imports
const StoreWrapper = dynamic(() => import("@/lib/wrappers/store-wrapper"), { ssr: false });
const PostHogProvider = dynamic(() => import("@/lib/posthog-provider"), { ssr: false });
@ -45,7 +45,7 @@ export const AppProvider: FC<IAppProvider> = observer((props) => {
<>
{/* TODO: Need to handle custom themes for toast */}
<Toast theme={resolveGeneralTheme(resolvedTheme)} />
<InstanceLayout>
<InstanceWrapper>
<StoreWrapper>
<CrispWrapper user={currentUser}>
<PostHogProvider
@ -60,7 +60,7 @@ export const AppProvider: FC<IAppProvider> = observer((props) => {
</PostHogProvider>
</CrispWrapper>
</StoreWrapper>
</InstanceLayout>
</InstanceWrapper>
</>
);
});

View File

@ -1,20 +1,125 @@
import { FC, ReactNode } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
import { Spinner } from "@plane/ui";
// helpers
import { EPageTypes } from "@/helpers/authentication.helper";
// hooks
import { useUser } from "@/hooks/store";
import { useUser, useWorkspace } from "@/hooks/store";
type TPageType = "public" | "onboarding" | "private";
type TPageType = EPageTypes;
type TAuthenticationWrapper = {
children: ReactNode;
pageType: TPageType;
pageType?: TPageType;
};
const isValidURL = (url: string): boolean => {
const disallowedSchemes = /^(https?|ftp):\/\//i;
return !disallowedSchemes.test(url);
};
export const AuthenticationWrapper: FC<TAuthenticationWrapper> = (props) => {
const { children, pageType } = props;
const router = useRouter();
const { next_path } = router.query;
// props
const { children, pageType = EPageTypes.AUTHENTICATED } = props;
// hooks
const { data: currentUser } = useUser();
const {
isLoading: isUserLoading,
data: currentUser,
currentUserSettings: { isLoading: currentUserSettingsLoader, data: currentUserSettings, fetchCurrentUserSettings },
profile: { isLoading: currentUserProfileLoader, data: currentUserProfile, fetchUserProfile },
fetchCurrentUser,
} = useUser();
const { loader: workspaceLoader, workspaces, fetchWorkspaces } = useWorkspace();
console.log("currentUser", currentUser);
useSWR(
"USER_PROFILE_SETTINGS_INFORMATION",
async () => {
await fetchCurrentUser();
if (currentUser) {
fetchCurrentUserSettings();
fetchUserProfile();
fetchWorkspaces();
}
},
{ revalidateOnFocus: false, shouldRetryOnError: false }
);
return <div key={pageType}>{children}</div>;
const getWorkspaceRedirectionUrl = (): string => {
let redirectionRoute = "/profile";
// validating the next_path from the router query
if (next_path && isValidURL(next_path.toString())) {
redirectionRoute = next_path.toString();
return redirectionRoute;
}
// validate the last and fallback workspace_slug
const currentWorkspaceSlug =
currentUserSettings?.workspace?.last_workspace_slug || currentUserSettings?.workspace?.fallback_workspace_slug;
// validate the current workspace_slug is available in the user's workspace list
const isCurrentWorkspaceValid = Object.values(workspaces || {}).findIndex(
(workspace) => workspace.slug === currentWorkspaceSlug
);
if (isCurrentWorkspaceValid >= 0) redirectionRoute = `/${currentWorkspaceSlug}`;
return redirectionRoute;
};
if (isUserLoading || currentUserSettingsLoader || currentUserProfileLoader || workspaceLoader)
return (
<div className="relative flex h-screen w-full items-center justify-center">
<Spinner />
</div>
);
if (pageType === EPageTypes.PUBLIC) return <>{children}</>;
if (pageType === EPageTypes.NON_AUTHENTICATED) {
if (!currentUser) return <>{children}</>;
else {
if (currentUserProfile?.is_onboarded) {
const currentRedirectRoute = getWorkspaceRedirectionUrl();
router.push(currentRedirectRoute);
return;
} else {
router.push("/onboarding");
return;
}
}
}
if (pageType === EPageTypes.ONBOARDING) {
if (!currentUser) {
router.push("/accounts/sign-in");
return;
} else {
if (currentUser && currentUserProfile?.is_onboarded) {
const currentRedirectRoute = getWorkspaceRedirectionUrl();
router.push(currentRedirectRoute);
return;
} else return <>{children}</>;
}
}
if (pageType === EPageTypes.AUTHENTICATED) {
if (currentUser) {
if (currentUserProfile?.is_onboarded) {
return <>{children}</>;
} else {
const currentRedirectRoute = getWorkspaceRedirectionUrl();
router.push(currentRedirectRoute);
return;
}
} else {
router.push("/accounts/sign-in");
return;
}
}
return <>{children}</>;
};

View File

@ -0,0 +1,2 @@
export * from "./instance-wrapper";
export * from "./authentication-wrapper";

View File

@ -12,7 +12,7 @@ type TInstanceWrapper = {
children: ReactNode;
};
const InstanceWrapper: FC<TInstanceWrapper> = observer((props) => {
export const InstanceWrapper: FC<TInstanceWrapper> = observer((props) => {
const { children } = props;
// store
const { isLoading, instance, error, fetchInstanceInfo } = useInstance();
@ -42,5 +42,3 @@ const InstanceWrapper: FC<TInstanceWrapper> = observer((props) => {
return <>{children}</>;
});
export default InstanceWrapper;

View File

@ -13,6 +13,7 @@ import { PageHead } from "@/components/core";
// constants
import { FORGOT_PASS_LINK, NAVIGATE_TO_SIGNUP } from "@/constants/event-tracker";
// helpers
import { EPageTypes } from "@/helpers/authentication.helper";
import { cn } from "@/helpers/common.helper";
import { checkEmailValidity } from "@/helpers/string.helper";
// hooks
@ -23,6 +24,8 @@ import useTimer from "@/hooks/use-timer";
import DefaultLayout from "@/layouts/default-layout";
// lib
import { NextPageWithLayout } from "@/lib/types";
// wrappers
import { AuthenticationWrapper } from "@/lib/wrappers";
// services
import { AuthService } from "@/services/auth.service";
// images
@ -47,8 +50,8 @@ const ForgotPasswordPage: NextPageWithLayout = () => {
const { email } = router.query;
// store hooks
const { captureEvent } = useEventTracker();
// hooks
const { resolvedTheme } = useTheme();
// hooks
const { resolvedTheme } = useTheme();
// timer
const { timer: resendTimerCode, setTimer: setResendCodeTimer } = useTimer(0);
const { isRedirecting, handleRedirection } = useAuthRedirection();
@ -108,7 +111,7 @@ const ForgotPasswordPage: NextPageWithLayout = () => {
<div className="relative">
<PageHead title="Forgot Password" />
<div className="absolute inset-0 z-0">
<Image
<Image
src={resolvedTheme === "dark" ? PlaneBackgroundPatternDark : PlaneBackgroundPattern}
className="w-screen min-h-screen object-cover"
alt="Plane background pattern"
@ -199,7 +202,11 @@ const ForgotPasswordPage: NextPageWithLayout = () => {
};
ForgotPasswordPage.getLayout = function getLayout(page: ReactElement) {
return <DefaultLayout>{page}</DefaultLayout>;
return (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>{page}</AuthenticationWrapper>
</DefaultLayout>
);
};
export default ForgotPasswordPage;

View File

@ -10,6 +10,7 @@ import { Button, Input, Spinner } from "@plane/ui";
import { PasswordStrengthMeter } from "@/components/account";
import { PageHead } from "@/components/core";
// helpers
import { EPageTypes } from "@/helpers/authentication.helper";
import { API_BASE_URL } from "@/helpers/common.helper";
import { getPasswordStrength } from "@/helpers/password.helper";
// hooks
@ -18,6 +19,8 @@ import useAuthRedirection from "@/hooks/use-auth-redirection";
import DefaultLayout from "@/layouts/default-layout";
// lib
import { NextPageWithLayout } from "@/lib/types";
// wrappers
import { AuthenticationWrapper } from "@/lib/wrappers";
// services
import { AuthService } from "@/services/auth.service";
// images
@ -212,7 +215,11 @@ const ResetPasswordPage: NextPageWithLayout = () => {
};
ResetPasswordPage.getLayout = function getLayout(page: ReactElement) {
return <DefaultLayout>{page}</DefaultLayout>;
return (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>{page}</AuthenticationWrapper>
</DefaultLayout>
);
};
export default ResetPasswordPage;

View File

@ -10,6 +10,8 @@ import { SignInAuthRoot } from "@/components/account";
import { PageHead } from "@/components/core";
// constants
import { NAVIGATE_TO_SIGNUP } from "@/constants/event-tracker";
// helpers
import { EPageTypes } from "@/helpers/authentication.helper";
// hooks
import { useEventTracker, useInstance, useUser } from "@/hooks/store";
import useAuthRedirection from "@/hooks/use-auth-redirection";
@ -17,6 +19,8 @@ import useAuthRedirection from "@/hooks/use-auth-redirection";
import DefaultLayout from "@/layouts/default-layout";
// types
import { NextPageWithLayout } from "@/lib/types";
// wrappers
import { AuthenticationWrapper } from "@/lib/wrappers";
// assets
import PlaneBackgroundPatternDark from "public/onboarding/background-pattern-dark.svg";
import PlaneBackgroundPattern from "public/onboarding/background-pattern.svg";
@ -83,7 +87,11 @@ const SignInPage: NextPageWithLayout = observer(() => {
});
SignInPage.getLayout = function getLayout(page: React.ReactElement) {
return <DefaultLayout>{page}</DefaultLayout>;
return (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>{page}</AuthenticationWrapper>
</DefaultLayout>
);
};
export default SignInPage;

View File

@ -1,15 +1,23 @@
import { ReactElement } from "react";
// layouts
import { SignUpView } from "@/components/page-views";
import DefaultLayout from "@/layouts/default-layout";
// components
import { SignUpView } from "@/components/page-views";
// helpers
import { EPageTypes } from "@/helpers/authentication.helper";
// layouts
import DefaultLayout from "@/layouts/default-layout";
// type
import { NextPageWithLayout } from "@/lib/types";
// wrappers
import { AuthenticationWrapper } from "@/lib/wrappers";
const HomePage: NextPageWithLayout = () => <SignUpView />;
HomePage.getLayout = function getLayout(page: ReactElement) {
return <DefaultLayout>{page}</DefaultLayout>;
return (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>{page}</AuthenticationWrapper>
</DefaultLayout>
);
};
export default HomePage;

View File

@ -3,22 +3,25 @@ import { observer } from "mobx-react";
import { useRouter } from "next/router";
import useSWR from "swr";
import { Boxes, Check, Share2, Star, User2, X } from "lucide-react";
// hooks
import { Spinner } from "@plane/ui";
// components
import { EmptySpace, EmptySpaceItem } from "@/components/ui/empty-space";
// constants
import { WORKSPACE_INVITATION } from "@/constants/fetch-keys";
// helpers
import { EPageTypes } from "@/helpers/authentication.helper";
// hooks
import { useUser } from "@/hooks/store";
// services
// layouts
import DefaultLayout from "@/layouts/default-layout";
// ui
// icons
// types
import { NextPageWithLayout } from "@/lib/types";
import { WorkspaceService } from "@/services/workspace.service";
// constants
// wrappers
import { AuthenticationWrapper } from "@/lib/wrappers";
// services
import { WorkspaceService } from "@/services/workspace.service";
// service initialization
const workspaceService = new WorkspaceService();
const WorkspaceInvitationPage: NextPageWithLayout = observer(() => {
@ -124,7 +127,11 @@ const WorkspaceInvitationPage: NextPageWithLayout = observer(() => {
});
WorkspaceInvitationPage.getLayout = function getLayout(page: ReactElement) {
return <DefaultLayout>{page}</DefaultLayout>;
return (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.PUBLIC}>{page}</AuthenticationWrapper>
</DefaultLayout>
);
};
export default WorkspaceInvitationPage;

View File

@ -1,6 +1,8 @@
import { action, makeObservable, observable, runInAction } from "mobx";
// types
import { IUser, IUserAccount } from "@plane/types";
// helpers
import { API_BASE_URL } from "@/helpers/common.helper";
// services
import { AuthService } from "@/services/auth.service";
import { UserService } from "@/services/user.service";
@ -9,7 +11,6 @@ import { RootStore } from "@/store/root.store";
import { IAccountStore, AccountStore } from "@/store/user/account.store";
import { ProfileStore, IProfileStore } from "@/store/user/profile.store";
import { IUserMembershipStore, UserMembershipStore } from "@/store/user/user-membership.store";
import { API_BASE_URL } from "@/helpers/common.helper";
import { IUserSettingsStore, UserSettingsStore } from "./user-setting.store";
export interface IUserStore {

View File

@ -88,7 +88,6 @@ export class ProfileStore implements IProfileStore {
return userProfile;
} catch (error) {
console.log("Failed to fetch profile details");
runInAction(() => {
this.isLoading = true;
this.error = {

View File

@ -2,7 +2,7 @@ import { action, makeObservable, observable, runInAction } from "mobx";
// services
import { UserService } from "services/user.service";
// types
import { IUserSettings, TUserProfile } from "@plane/types";
import { IUserSettings } from "@plane/types";
type TError = {
status: string;