Skip to content

Commit bc7f455

Browse files
committed
Add login to access map
1 parent cd6f783 commit bc7f455

File tree

6 files changed

+119
-25
lines changed

6 files changed

+119
-25
lines changed

frontend/src/screens/App/index.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import stylesheet from './App.less';
1414
import AllStories from './screens/AllStories';
1515
import Outtakes from './screens/Outtakes';
1616

17+
import useLoginStore from 'shared/stores/LoginStore';
1718
import { OptimizeExperimentsProvider } from 'shared/utils/OptimizeExperiments';
1819
import 'utils/optimize';
1920
import AdminRoutes from './screens/Admin/AdminRoutes';
@@ -117,6 +118,12 @@ function ContextWrappers({
117118
}: {
118119
children: React.ReactNode;
119120
}): JSX.Element {
121+
const initialize = useLoginStore((state) => state.initialize);
122+
123+
React.useEffect(() => {
124+
initialize();
125+
}, [initialize]);
126+
120127
return <OptimizeExperimentsProvider>{children}</OptimizeExperimentsProvider>;
121128
}
122129

frontend/src/screens/App/screens/MapPane/components/MainMap/MainMap.less

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,32 @@
66
// width: 100%;
77
height: 100%;
88
}
9+
10+
.loginOverlay {
11+
height: 100%;
12+
display: flex;
13+
align-items: center;
14+
justify-content: center;
15+
background-color: #f5f5f5;
16+
}
17+
18+
.loginContent {
19+
text-align: center;
20+
padding: 2rem;
21+
background: white;
22+
border-radius: 8px;
23+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
24+
max-width: 400px;
25+
width: 100%;
26+
27+
h2 {
28+
margin: 0 0 1rem 0;
29+
color: #333;
30+
}
31+
32+
p {
33+
margin: 0 0 1.5rem 0;
34+
color: #666;
35+
}
36+
}
937
}

frontend/src/screens/App/screens/MapPane/components/MainMap/index.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
import classnames from 'classnames';
1111
import mapboxgl from 'mapbox-gl';
1212

13+
import LoginForm from 'shared/components/LoginForm';
14+
import useLoginStore from 'shared/stores/LoginStore';
1315
import * as overlays from './overlays';
1416

1517
export { OverlayId } from './overlays';
@@ -153,6 +155,23 @@ function withRouterRef<
153155
const match = useRouteMatch();
154156
const location = useLocation();
155157
const history = useHistory();
158+
const { isLoggedInToNonAnonymousAccount, isLoadingMe } = useLoginStore();
159+
160+
// Only show map if user is logged in to a non-anonymous account
161+
const canShowMap = isLoggedInToNonAnonymousAccount && !isLoadingMe;
162+
163+
if (!canShowMap) {
164+
return (
165+
<div className={stylesheet.loginOverlay}>
166+
<div className={stylesheet.loginContent}>
167+
<h2>Login Required</h2>
168+
<p>Please log in to view the map.</p>
169+
<LoginForm />
170+
</div>
171+
</div>
172+
);
173+
}
174+
156175
return (
157176
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- I give up on types
158177
// @ts-ignore

frontend/src/screens/App/screens/Welcome/index.tsx

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from 'react';
22
import { useFeatureFlag } from 'screens/App/shared/stores/FeatureFlagsStore';
33
import FeatureFlag from 'screens/App/shared/types/FeatureFlag';
44
import Button from 'shared/components/Button';
5+
import LoginForm from 'shared/components/LoginForm';
6+
import useLoginStore from 'shared/stores/LoginStore';
57

68
import PhotoAsideModal from '../PhotoAsideModal';
79
import carouselImages from './carouselImages';
@@ -21,13 +23,24 @@ export default function Welcome({
2123
// Used to hide this annoying modal in development
2224
const isWelcomeDisabled = useFeatureFlag(FeatureFlag.DISABLE_WELCOME_MODAL);
2325

26+
const { isLoggedInToNonAnonymousAccount, isLoadingMe } = useLoginStore();
27+
28+
// Only allow closing if user is logged in to a non-anonymous account
29+
const canClose = isLoggedInToNonAnonymousAccount && !isLoadingMe;
30+
31+
const handleClose = (): void => {
32+
if (canClose) {
33+
onRequestClose();
34+
}
35+
};
36+
2437
return (
2538
<PhotoAsideModal
2639
isOpen={isOpen && !isWelcomeDisabled}
2740
className={stylesheet.welcomeModal}
28-
onRequestClose={onRequestClose}
41+
onRequestClose={handleClose}
2942
shouldCloseOnOverlayClick={false}
30-
shouldCloseOnEsc
43+
shouldCloseOnEsc={canClose}
3144
size="large"
3245
isCloseButtonVisible={false}
3346
carouselProps={{
@@ -48,18 +61,31 @@ export default function Welcome({
4861
<br />
4962
<strong>Zoom in! Every dot&nbsp;is&nbsp;a&nbsp;photo.</strong>
5063
</p>
51-
<div
52-
className={classNames(
53-
stylesheet.buttonContainer,
54-
stylesheet.mobileButton,
55-
stylesheet.mobileOnly
56-
)}
57-
onClick={onRequestClose}
58-
>
59-
<Button buttonStyle="primary" className={stylesheet.explore}>
60-
Start Exploring
61-
</Button>
62-
</div>
64+
65+
{!canClose ? (
66+
<div className={stylesheet.loginSection}>
67+
<p>
68+
<strong>
69+
An email address is temporarily required to access the site.
70+
</strong>
71+
</p>
72+
<LoginForm />
73+
</div>
74+
) : (
75+
<div
76+
className={classNames(
77+
stylesheet.buttonContainer,
78+
stylesheet.mobileButton,
79+
stylesheet.mobileOnly
80+
)}
81+
onClick={handleClose}
82+
>
83+
<Button buttonStyle="primary" className={stylesheet.explore}>
84+
Start Exploring
85+
</Button>
86+
</div>
87+
)}
88+
6389
<hr />
6490
<p className={stylesheet.finePrint}>
6591
The photos on this site were retrieved from the NYC Department of
@@ -108,17 +134,19 @@ export default function Welcome({
108134
</a>
109135
</p>
110136
</div>
111-
<div
112-
className={classNames(
113-
stylesheet.buttonContainer,
114-
stylesheet.desktopOnly
115-
)}
116-
onClick={onRequestClose}
117-
>
118-
<Button buttonStyle="primary" className={stylesheet.explore}>
119-
Start Exploring
120-
</Button>
121-
</div>
137+
{canClose && (
138+
<div
139+
className={classNames(
140+
stylesheet.buttonContainer,
141+
stylesheet.desktopOnly
142+
)}
143+
onClick={handleClose}
144+
>
145+
<Button buttonStyle="primary" className={stylesheet.explore}>
146+
Start Exploring
147+
</Button>
148+
</div>
149+
)}
122150
</div>
123151
</PhotoAsideModal>
124152
);

frontend/src/screens/App/screens/Welcome/welcome.less

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@
4646
font-size: 12px;
4747
}
4848

49+
.loginSection {
50+
margin: 1rem 0;
51+
text-align: center;
52+
}
53+
4954
.buttonContainer {
5055
padding: 1rem;
5156
text-align: center;

frontend/src/shared/stores/LoginStore.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
interface State {
1111
emailAddress: string;
1212
isLoginValidated: boolean;
13+
isLoggedInToNonAnonymousAccount: boolean;
1314
isFollowMagicLinkMessageVisible: boolean;
1415
isVerifyEmailMessageVisible: boolean;
1516
isEmailUpdatedMessageVisible: boolean;
@@ -34,6 +35,7 @@ const useLoginStore = create(
3435
immer<State & Actions>((set, get) => ({
3536
emailAddress: '',
3637
isLoginValidated: false,
38+
isLoggedInToNonAnonymousAccount: false,
3739
isFollowMagicLinkMessageVisible: false,
3840
isVerifyEmailMessageVisible: false,
3941
isEmailUpdatedMessageVisible: false,
@@ -43,12 +45,15 @@ const useLoginStore = create(
4345
initialize: () => {
4446
set((draft) => {
4547
draft.isLoginValidated = false;
48+
draft.isLoggedInToNonAnonymousAccount = false;
4649
draft.isLoadingMe = true;
4750
});
4851
getMe()
4952
.then((me) => {
5053
set((draft) => {
5154
draft.emailAddress = me.email || '';
55+
// If getMe returns an email, the user is logged in to a non-anonymous account
56+
draft.isLoggedInToNonAnonymousAccount = !!me.email;
5257
draft.isFollowMagicLinkMessageVisible = false;
5358
draft.isVerifyEmailMessageVisible = false;
5459
draft.isEmailUpdatedMessageVisible = false;
@@ -70,6 +75,7 @@ const useLoginStore = create(
7075
draft.isFollowMagicLinkMessageVisible = false;
7176
draft.isEmailUpdatedMessageVisible = false;
7277
draft.isLoginValidated = false;
78+
// Don't reset isLoggedInToNonAnonymousAccount here as it's based on the server state
7379
});
7480
},
7581

@@ -94,6 +100,7 @@ const useLoginStore = create(
94100
set((draft) => {
95101
// We stay logged into the current account and can proceed
96102
draft.isLoginValidated = true;
103+
draft.isLoggedInToNonAnonymousAccount = true;
97104
draft.isFollowMagicLinkMessageVisible = false;
98105
draft.isVerifyEmailMessageVisible = false;
99106
});

0 commit comments

Comments
 (0)