mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: updated intance and auth wrapper logic
This commit is contained in:
parent
86196c38bc
commit
dd7c500bfe
@ -212,3 +212,4 @@ export const AuthPasswordForm: React.FC<Props> = observer((props: Props) => {
|
||||
</form>
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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",
|
||||
|
@ -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>
|
||||
);
|
||||
});
|
||||
|
@ -57,5 +57,3 @@ export const UserAuthWrapper: FC<IUserAuthWrapper> = observer((props) => {
|
||||
|
||||
return <>{children}</>;
|
||||
});
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
@ -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}</>;
|
||||
};
|
||||
|
2
web/lib/wrappers/index.ts
Normal file
2
web/lib/wrappers/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./instance-wrapper";
|
||||
export * from "./authentication-wrapper";
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
@ -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 {
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user