Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Commit

Permalink
refactor: use jotai instead of redux (#68)
Browse files Browse the repository at this point in the history
* use jotai

* refactor

* show scene tab when no selected

* support root layer export

* refactor
  • Loading branch information
rot1024 committed Aug 27, 2021
1 parent f981f91 commit ea980cb
Show file tree
Hide file tree
Showing 56 changed files with 499 additions and 722 deletions.
7 changes: 1 addition & 6 deletions package.json
Expand Up @@ -90,7 +90,6 @@
"@types/react-ga": "^2.3.0",
"@types/react-intl": "^3.0.0",
"@types/react-leaflet": "^2.5.2",
"@types/react-redux": "^7.1.16",
"@types/storybook__addon-info": "^5.2.2",
"@types/styled-components": "^5.1.7",
"@types/webpack": "^4.41.26",
Expand Down Expand Up @@ -151,7 +150,6 @@
"@emotion/styled": "^11.1.5",
"@popperjs/core": "^2.6.0",
"@reach/router": "^1.3.4",
"@reduxjs/toolkit": "^1.5.0",
"@rot1024/use-transition": "^1.0.0",
"@sentry/browser": "^6.7.1",
"@types/gapi.auth2": "^0.0.54",
Expand All @@ -164,14 +162,13 @@
"array-move": "^3.0.1",
"axios": "^0.21.1",
"cesium": "^1.84.0",
"cesium-navigation": "^1.0.0",
"core-js": "^3.8.3",
"date-fns": "^2.17.0",
"detect-browser": "^5.2.0",
"formik": "^2.2.6",
"github-markdown-css": "^4.0.0",
"graphql": "^15.5.0",
"history": "^5.0.0",
"jotai": "^1.3.1",
"leaflet": "^1.7.1",
"lodash-es": "^4.17.20",
"mini-svg-data-uri": "^1.2.3",
Expand All @@ -193,11 +190,9 @@
"react-nl2br": "^1.0.2",
"react-player": "^2.8.2",
"react-popper": "^2.2.4",
"react-redux": "^7.2.2",
"react-spinners": "^0.10.4",
"react-svg": "^14.0.7",
"react-use": "^17.1.1",
"redux-first-history": "^4.5.0",
"regenerator-runtime": "^0.13.7",
"remark-gfm": "^1.0.0",
"resium": "^1.13.1",
Expand Down
67 changes: 28 additions & 39 deletions src/app.tsx
@@ -1,15 +1,11 @@
import React, { Suspense } from "react";
import { Router, Redirect } from "@reach/router";
import { LocationProvider } from "@reach/router";
import { createBrowserHistory } from "history";
import { reachify } from "redux-first-history";

import { Provider as IntlProvider } from "@reearth/locale";
import { Provider as ThemeProvider, styled } from "./theme";
import { Provider as GqlProvider } from "./gql";
import { Provider as LocalStateProvider } from "./state";
import { Provider as DndProvider } from "./util/use-dnd";
import { Provider as Auth0Provider } from "./auth";
import { Provider as IntlProvider } from "@reearth/locale";

import NotFound from "@reearth/components/pages/NotFound";
import Loading from "@reearth/components/atoms/Loading";
Expand Down Expand Up @@ -38,43 +34,36 @@ if (enableWhyDidYouRender && process.env.NODE_ENV === "development") {
const EarthEditor = React.lazy(() => import("@reearth/components/pages/EarthEditor"));
const Dashboard = React.lazy(() => import("@reearth/components/pages/Dashboard"));

const history = createBrowserHistory();
const reachHistory = reachify(history);

const App: React.FC = () => {
return (
<Auth0Provider>
<LocalStateProvider>
<GqlProvider>
<ThemeProvider>
<LocationProvider history={reachHistory}>
<DndProvider>
<IntlProvider>
<Suspense fallback={<Loading />}>
<StyledRouter>
<TopPage path="/" />
<Dashboard path="/dashboard/:teamId" />
<EarthEditor path="/edit/:sceneId" />
<Preview path="/edit/:sceneId/preview" />
<Redirect from="/settings" to="/settings/account" />
<AccountSettings path="/settings/account" />
<WorkspaceList path="/settings/workspaces" />
<WorkspaceSettings path="/settings/workspace/:teamId" />
<SettingsProjectList path="/settings/workspace/:teamId/projects" />
<AssetSettings path="/settings/workspace/:teamId/asset" />
<ProjectSettings path="/settings/project/:projectId" />
<PublicSettings path="/settings/project/:projectId/public" />
<DatasetSettings path="/settings/project/:projectId/dataset" />
<PluginSettings path="/settings/project/:projectId/plugins" />
<NotFound default />
</StyledRouter>
</Suspense>
</IntlProvider>
</DndProvider>
</LocationProvider>
</ThemeProvider>
</GqlProvider>
</LocalStateProvider>
<GqlProvider>
<ThemeProvider>
<DndProvider>
<IntlProvider>
<Suspense fallback={<Loading />}>
<StyledRouter>
<TopPage path="/" />
<Dashboard path="/dashboard/:teamId" />
<EarthEditor path="/edit/:sceneId" />
<Preview path="/edit/:sceneId/preview" />
<Redirect from="/settings" to="/settings/account" />
<AccountSettings path="/settings/account" />
<WorkspaceList path="/settings/workspaces" />
<WorkspaceSettings path="/settings/workspace/:teamId" />
<SettingsProjectList path="/settings/workspace/:teamId/projects" />
<AssetSettings path="/settings/workspace/:teamId/asset" />
<ProjectSettings path="/settings/project/:projectId" />
<PublicSettings path="/settings/project/:projectId/public" />
<DatasetSettings path="/settings/project/:projectId/dataset" />
<PluginSettings path="/settings/project/:projectId/plugins" />
<NotFound default />
</StyledRouter>
</Suspense>
</IntlProvider>
</DndProvider>
</ThemeProvider>
</GqlProvider>
</Auth0Provider>
);
};
Expand Down
25 changes: 24 additions & 1 deletion src/auth/hooks.ts
Expand Up @@ -4,12 +4,14 @@ import { useEffect, useState } from "react";
export const errorKey = "reeartherror";

export default function useAuth() {
const { isAuthenticated, error, isLoading, loginWithRedirect, logout } = useAuth0();
const { isAuthenticated, error, isLoading, loginWithRedirect, logout, getAccessTokenSilently } =
useAuth0();

return {
isAuthenticated: !!window.REEARTH_E2E_ACCESS_TOKEN || (isAuthenticated && !error),
isLoading,
error: error?.message,
getAccessToken: () => getAccessTokenSilently(),
login: () => loginWithRedirect(),
logout: () =>
logout({
Expand Down Expand Up @@ -46,3 +48,24 @@ export function useCleanUrl() {

return error;
}

export function useAuthenticationRequired(): [boolean, string | undefined] {
const { isAuthenticated, isLoading, error: authError, login, logout } = useAuth();

useEffect(() => {
if (isLoading || isAuthenticated) {
return;
}

if (authError) {
logout();
return;
}

login();
}, [authError, isAuthenticated, isLoading, login, logout]);

const error = useCleanUrl();

return [isAuthenticated, error];
}
2 changes: 0 additions & 2 deletions src/auth/index.ts

This file was deleted.

14 changes: 14 additions & 0 deletions src/auth/index.tsx
@@ -0,0 +1,14 @@
import React, { PropsWithChildren } from "react";
import { useAuthenticationRequired } from "./hooks";

export { default as Provider } from "./provider";
export { default as useAuth, useCleanUrl, useAuthenticationRequired } from "./hooks";

export { withAuthenticationRequired } from "@auth0/auth0-react";

export function AuthenticationRequiredPage({
children,
}: PropsWithChildren<unknown>): JSX.Element | null {
const [isAuthenticated] = useAuthenticationRequired(); // TODO: show error
return isAuthenticated && children ? <>{children}</> : null;
}
9 changes: 2 additions & 7 deletions src/components/atoms/TabArea/index.tsx
@@ -1,10 +1,9 @@
import React, { useEffect, useState } from "react";
import _ from "lodash";

import { styled, useTheme } from "@reearth/theme";
import Overlay from "@reearth/components/atoms/Overlay";
import Icon from "../Icon";
import Text from "@reearth/components/atoms/Text";
import Icon from "@reearth/components/atoms/Icon";

export type MenuAlignment = "left" | "top";

Expand Down Expand Up @@ -35,11 +34,7 @@ const TabArea = <T extends string>({
onlyIcon,
labels,
}: Props<T>) => {
const tabs: T[] | undefined = Object.keys(
_.pickBy(children, value => {
return !!value;
}),
) as T[];
const tabs: T[] = (Object.keys(children || {}) as T[]).filter(k => !!children?.[k]);
const [selectedTab, select] = useState<T | undefined>(initialSelectedMode ?? tabs?.[0]);

useEffect(() => {
Expand Down
6 changes: 4 additions & 2 deletions src/components/molecules/Dashboard/index.tsx
@@ -1,10 +1,12 @@
import React from "react";
import { styled } from "@reearth/theme";

export interface Props {
export * from "./types";

export type Props = {
className?: string;
header?: React.ReactNode;
}
};

const Dashboard: React.FC<Props> = ({ className, header, children }) => {
return (
Expand Down
6 changes: 3 additions & 3 deletions src/components/molecules/EarthEditor/ExportPane/index.tsx
Expand Up @@ -15,13 +15,13 @@ type Props = {
onExport: (format: Format) => void;
};

const ExportPane: React.FC<Props> = ({ className, show = true, onExport }) => {
const ExportPane: React.FC<Props> = ({ className, onExport }) => {
const intl = useIntl();
const [format, setFormat] = useState<Format>(defaultFormat);

const handleExport = useCallback(() => onExport(format), [format, onExport]);

return show ? (
return (
<Wrapper className={className}>
<SelectWrapper>
<Label size="s">{intl.formatMessage({ defaultMessage: "Export type" })}</Label>
Expand All @@ -42,7 +42,7 @@ const ExportPane: React.FC<Props> = ({ className, show = true, onExport }) => {
onClick={handleExport}
/>
</Wrapper>
) : null;
);
};

const Wrapper = styled.div`
Expand Down
53 changes: 21 additions & 32 deletions src/components/organisms/Dashboard/hooks.ts
@@ -1,7 +1,8 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useIntl } from "react-intl";

import { useNavigate } from "@reach/router";
import { useLocalState } from "@reearth/state";
import { useError, useTeam, useProject, useUnselectProject } from "@reearth/state";
import {
useMeQuery,
useCreateTeamMutation,
Expand All @@ -14,19 +15,17 @@ import {
useAssetsQuery,
} from "@reearth/gql";

import { User } from "@reearth/components/molecules/Common/Header";
import { Project } from "@reearth/components/molecules/Dashboard/types";

import { Team } from "@reearth/components/molecules/Dashboard/types";
import type { User } from "@reearth/components/molecules/Common/Header";
import type { Project, Team } from "@reearth/components/molecules/Dashboard";

export type AssetNodes = NonNullable<AssetsQuery["assets"]["nodes"][number]>[];

export default (teamId?: string) => {
const [{ error, currentTeam, currentProject }, setLocalState] = useLocalState(s => ({
error: s.error,
currentTeam: s.currentTeam,
currentProject: s.currentProject,
}));
const [error, setError] = useError();
const [currentTeam, setCurrentTeam] = useTeam();
const [currentProject] = useProject();
const unselectProject = useUnselectProject();

const { data, refetch } = useMeQuery();
const [modalShown, setModalShown] = useState(false);
const openModal = useCallback(() => setModalShown(true), []);
Expand All @@ -49,19 +48,19 @@ export default (teamId?: string) => {

useEffect(() => {
if (team?.id && team.id !== currentTeam?.id) {
setLocalState({ currentTeam: team });
setCurrentTeam(team);
}
}, [currentTeam, team, setLocalState]);
}, [currentTeam, team, setCurrentTeam]);

const changeTeam = useCallback(
(teamId: string) => {
const team = teams?.find(team => team.id === teamId);
if (team) {
setLocalState({ currentTeam: team });
setCurrentTeam(team);
navigate(`/dashboard/${teamId}`);
}
},
[teams, setLocalState, navigate],
[teams, setCurrentTeam, navigate],
);

const [createTeamMutation] = useCreateTeamMutation();
Expand All @@ -72,12 +71,12 @@ export default (teamId?: string) => {
refetchQueries: ["teams"],
});
if (results.data?.createTeam) {
setLocalState({ currentTeam: results.data.createTeam.team });
setCurrentTeam(results.data.createTeam.team);
navigate(`/dashboard/${results.data.createTeam.team.id}`);
}
refetch();
},
[createTeamMutation, setLocalState, refetch, navigate],
[createTeamMutation, setCurrentTeam, refetch, navigate],
);

const notificationTimeout = 5000;
Expand All @@ -89,33 +88,23 @@ export default (teamId?: string) => {
useEffect(() => {
if (!error) return;
const timerID = setTimeout(() => {
setLocalState({ error: undefined });
setError(undefined);
}, notificationTimeout);
return () => clearTimeout(timerID);
}, [error, setLocalState]);
}, [error, setError]);

const onNotificationClose = useCallback(() => {
if (error) {
setLocalState({ error: undefined });
setError(undefined);
}
}, [error, setLocalState]);
}, [error, setError]);

useEffect(() => {
// unselect project
if (currentProject) {
setLocalState({
currentProject: undefined,
sceneId: undefined,
rootLayerId: undefined,
selectedLayer: undefined,
selectedWidget: undefined,
selectedBlock: undefined,
selectedType: undefined,
camera: undefined,
isCapturing: undefined,
});
unselectProject();
}
}, [currentProject, setLocalState]);
}, [currentProject, setCurrentTeam, unselectProject]);

const handleModalClose = useCallback(
(r?: boolean) => {
Expand Down

0 comments on commit ea980cb

Please sign in to comment.