Skip to content

Commit 0881a30

Browse files
committed
wip useAuthenticatedUser
1 parent 31ecbd7 commit 0881a30

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+659
-712
lines changed

components/dashboard/src/AppNotifications.tsx

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
*/
66

77
import dayjs from "dayjs";
8-
import deepMerge from "deepmerge";
98
import { useCallback, useEffect, useState } from "react";
109
import Alert, { AlertType } from "./components/Alert";
1110
import { useUserLoader } from "./hooks/use-user-loader";
12-
import { getGitpodService } from "./service/service";
1311
import { isGitpodIo } from "./utils";
1412
import { trackEvent } from "./Analytics";
13+
import { useUpdateCurrentUserMutation } from "./data/current-user/update-mutation";
14+
import { User as UserProtocol } from "@gitpod/gitpod-protocol";
15+
import { User } from "@gitpod/public-api/lib/gitpod/v1/user_pb";
1516

1617
const KEY_APP_DISMISSED_NOTIFICATIONS = "gitpod-app-notifications-dismissed";
1718
const PRIVACY_POLICY_LAST_UPDATED = "2023-10-17";
@@ -24,59 +25,60 @@ interface Notification {
2425
onClose?: () => void;
2526
}
2627

27-
const UPDATED_PRIVACY_POLICY: Notification = {
28-
id: "privacy-policy-update",
29-
type: "info",
30-
preventDismiss: true,
31-
onClose: async () => {
32-
let dismissSuccess = false;
33-
try {
34-
const userUpdates = { additionalData: { profile: { acceptedPrivacyPolicyDate: dayjs().toISOString() } } };
35-
const previousUser = await getGitpodService().server.getLoggedInUser();
36-
const updatedUser = await getGitpodService().server.updateLoggedInUser(
37-
deepMerge(previousUser, userUpdates),
38-
);
39-
dismissSuccess = !!updatedUser;
40-
} catch (err) {
41-
console.error("Failed to update user's privacy policy acceptance date", err);
42-
dismissSuccess = false;
43-
} finally {
44-
trackEvent("privacy_policy_update_accepted", {
45-
path: window.location.pathname,
46-
success: dismissSuccess,
47-
});
48-
}
49-
},
50-
message: (
51-
<span className="text-md">
52-
We've updated our Privacy Policy. You can review it{" "}
53-
<a className="gp-link" href="https://www.gitpod.io/privacy" target="_blank" rel="noreferrer">
54-
here
55-
</a>
56-
.
57-
</span>
58-
),
28+
const UPDATED_PRIVACY_POLICY = (updateUser: (user: Partial<UserProtocol>) => Promise<User>) => {
29+
return {
30+
id: "privacy-policy-update",
31+
type: "info",
32+
preventDismiss: true,
33+
onClose: async () => {
34+
let dismissSuccess = false;
35+
try {
36+
const updatedUser = await updateUser({
37+
additionalData: { profile: { acceptedPrivacyPolicyDate: dayjs().toISOString() } },
38+
});
39+
dismissSuccess = !!updatedUser;
40+
} catch (err) {
41+
console.error("Failed to update user's privacy policy acceptance date", err);
42+
dismissSuccess = false;
43+
} finally {
44+
trackEvent("privacy_policy_update_accepted", {
45+
path: window.location.pathname,
46+
success: dismissSuccess,
47+
});
48+
}
49+
},
50+
message: (
51+
<span className="text-md">
52+
We've updated our Privacy Policy. You can review it{" "}
53+
<a className="gp-link" href="https://www.gitpod.io/privacy" target="_blank" rel="noreferrer">
54+
here
55+
</a>
56+
.
57+
</span>
58+
),
59+
} as Notification;
5960
};
6061

6162
export function AppNotifications() {
6263
const [topNotification, setTopNotification] = useState<Notification | undefined>(undefined);
6364
const { user, loading } = useUserLoader();
65+
const updateUser = useUpdateCurrentUserMutation();
6466

6567
useEffect(() => {
6668
const notifications = [];
6769
if (!loading && isGitpodIo()) {
6870
if (
69-
!user?.additionalData?.profile?.acceptedPrivacyPolicyDate ||
70-
new Date(PRIVACY_POLICY_LAST_UPDATED) > new Date(user.additionalData.profile.acceptedPrivacyPolicyDate)
71+
!user?.profile?.acceptedPrivacyPolicyDate ||
72+
new Date(PRIVACY_POLICY_LAST_UPDATED) > new Date(user.profile.acceptedPrivacyPolicyDate)
7173
) {
72-
notifications.push(UPDATED_PRIVACY_POLICY);
74+
notifications.push(UPDATED_PRIVACY_POLICY((u: Partial<UserProtocol>) => updateUser.mutateAsync(u)));
7375
}
7476
}
7577

7678
const dismissedNotifications = getDismissedNotifications();
7779
const topNotification = notifications.find((n) => !dismissedNotifications.includes(n.id));
7880
setTopNotification(topNotification);
79-
}, [loading, setTopNotification, user]);
81+
}, [loading, updateUser, setTopNotification, user]);
8082

8183
const dismissNotification = useCallback(() => {
8284
if (!topNotification) {

components/dashboard/src/Login.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
*/
66

77
import * as GitpodCookie from "@gitpod/gitpod-protocol/lib/util/gitpod-cookie";
8-
import { useContext, useEffect, useState, useMemo, useCallback, FC } from "react";
9-
import { UserContext } from "./user-context";
8+
import { useEffect, useState, useMemo, useCallback, FC } from "react";
109
import { getGitpodService } from "./service/service";
1110
import { iconForAuthProvider, openAuthorizeWindow, simplifyProviderName } from "./provider-utils";
1211
import gitpod from "./images/gitpod.svg";
@@ -23,6 +22,7 @@ import { useNeedsSetup } from "./dedicated-setup/use-needs-setup";
2322
import { AuthProviderDescription } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";
2423
import { Button, ButtonProps } from "@podkit/buttons/Button";
2524
import { cn } from "@podkit/lib/cn";
25+
import { useAuthenticatedUser } from "./data/current-user/authenticated-user-query";
2626

2727
export function markLoggedIn() {
2828
document.cookie = GitpodCookie.generateCookie(window.location.hostname);
@@ -36,7 +36,7 @@ type LoginProps = {
3636
onLoggedIn?: () => void;
3737
};
3838
export const Login: FC<LoginProps> = ({ onLoggedIn }) => {
39-
const { setUser } = useContext(UserContext);
39+
const { refetch: reloadUser } = useAuthenticatedUser();
4040

4141
const urlHash = useMemo(() => getURLHash(), []);
4242

@@ -67,10 +67,9 @@ export const Login: FC<LoginProps> = ({ onLoggedIn }) => {
6767

6868
const updateUser = useCallback(async () => {
6969
await getGitpodService().reconnect();
70-
const user = await getGitpodService().server.getLoggedInUser();
71-
setUser(user);
70+
reloadUser();
7271
markLoggedIn();
73-
}, [setUser]);
72+
}, [reloadUser]);
7473

7574
const authorizeSuccessful = useCallback(async () => {
7675
updateUser().catch(console.error);

components/dashboard/src/app/AdminRoute.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { useContext } from "react";
87
import { Redirect, Route } from "react-router";
9-
import { UserContext } from "../user-context";
8+
import { useAuthenticatedUser } from "../data/current-user/authenticated-user-query";
9+
import { User_RoleOrPermission } from "@gitpod/public-api/lib/gitpod/v1/user_pb";
1010

1111
// A wrapper for <Route> that redirects to the workspaces screen if the user isn't a admin.
1212
// This wrapper only accepts the component property
1313
export function AdminRoute({ component }: any) {
14-
const { user } = useContext(UserContext);
14+
const { data: user } = useAuthenticatedUser();
1515
return (
1616
<Route
1717
render={({ location }: any) =>
18-
user?.rolesOrPermissions?.includes("admin") ? (
18+
user?.rolesOrPermissions?.includes(User_RoleOrPermission.ADMIN) ? (
1919
<Route component={component}></Route>
2020
) : (
2121
<Redirect

components/dashboard/src/app/AppBlockingFlows.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@
66

77
import { FC, lazy } from "react";
88
import { useShowDedicatedSetup } from "../dedicated-setup/use-show-dedicated-setup";
9-
import { useCurrentUser } from "../user-context";
10-
import { MigrationPage, useShouldSeeMigrationPage } from "../whatsnew/MigrationPage";
119
import { useShowUserOnboarding } from "../onboarding/use-show-user-onboarding";
1210
import { useHistory } from "react-router";
1311
import { useCurrentOrg } from "../data/organizations/orgs-query";
1412
import { OrgNamingStep } from "../dedicated-setup/OrgNamingStep";
13+
import { useAuthenticatedUser } from "../data/current-user/authenticated-user-query";
1514

1615
const UserOnboarding = lazy(() => import(/* webpackPrefetch: true */ "../onboarding/UserOnboarding"));
1716
const DedicatedSetup = lazy(() => import(/* webpackPrefetch: true */ "../dedicated-setup/DedicatedSetup"));
@@ -20,9 +19,8 @@ const DedicatedSetup = lazy(() => import(/* webpackPrefetch: true */ "../dedicat
2019
// Since this runs before the app is rendered, we should avoid adding any lengthy async calls that would delay the app from loading.
2120
export const AppBlockingFlows: FC = ({ children }) => {
2221
const history = useHistory();
23-
const user = useCurrentUser();
22+
const { data: user } = useAuthenticatedUser();
2423
const org = useCurrentOrg();
25-
const shouldSeeMigrationPage = useShouldSeeMigrationPage();
2624
const showDedicatedSetup = useShowDedicatedSetup();
2725
const showUserOnboarding = useShowUserOnboarding();
2826

@@ -31,11 +29,6 @@ export const AppBlockingFlows: FC = ({ children }) => {
3129
return <></>;
3230
}
3331

34-
// If orgOnlyAttribution is enabled and the user hasn't been migrated, yet, we need to show the migration page
35-
if (shouldSeeMigrationPage) {
36-
return <MigrationPage />;
37-
}
38-
3932
// Handle dedicated setup if necessary
4033
if (showDedicatedSetup.showSetup) {
4134
return (
@@ -52,7 +45,7 @@ export const AppBlockingFlows: FC = ({ children }) => {
5245
}
5346

5447
// New user onboarding flow
55-
if (showUserOnboarding) {
48+
if (showUserOnboarding && user) {
5649
return <UserOnboarding user={user} />;
5750
}
5851

components/dashboard/src/app/AppRoutes.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import React, { useState } from "react";
7+
import React from "react";
88
import { Redirect, Route, Switch, useLocation } from "react-router";
99
import OAuthClientApproval from "../OauthClientApproval";
1010
import Menu from "../menu/Menu";
@@ -25,15 +25,13 @@ import {
2525
usagePathMain,
2626
} from "../user-settings/settings.routes";
2727
import { getURLHash, isGitpodIo } from "../utils";
28-
import { WhatsNew, shouldSeeWhatsNew } from "../whatsnew/WhatsNew";
2928
import { workspacesPathMain } from "../workspaces/workspaces.routes";
3029
import { AdminRoute } from "./AdminRoute";
3130
import { Blocked } from "./Blocked";
3231

3332
// TODO: Can we bundle-split/lazy load these like other pages?
3433
import { BlockedRepositories } from "../admin/BlockedRepositories";
3534
import { Heading1, Subheading } from "../components/typography/headings";
36-
import { useCurrentUser } from "../user-context";
3735
import PersonalAccessTokenCreateView from "../user-settings/PersonalAccessTokensCreateView";
3836
import { CreateWorkspacePage } from "../workspaces/CreateWorkspacePage";
3937
import { WebsocketClients } from "./WebsocketClients";
@@ -88,8 +86,6 @@ const ConfigurationWorkspaces = React.lazy(
8886

8987
export const AppRoutes = () => {
9088
const hash = getURLHash();
91-
const user = useCurrentUser();
92-
const [isWhatsNewShown, setWhatsNewShown] = useState(user && shouldSeeWhatsNew(user));
9389
const location = useLocation();
9490
const repoConfigListAndDetail = useFeatureFlag("repoConfigListAndDetail");
9591

@@ -103,10 +99,6 @@ export const AppRoutes = () => {
10399
return <OAuthClientApproval />;
104100
}
105101

106-
if (isWhatsNewShown) {
107-
return <WhatsNew onClose={() => setWhatsNewShown(false)} />;
108-
}
109-
110102
// TODO: Try and encapsulate this in a route for "/" (check for hash in route component, render or redirect accordingly)
111103
const isCreation = location.pathname === "/" && hash !== "";
112104
if (isCreation) {

components/dashboard/src/components/AuthorizeGit.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,34 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { FC, useCallback, useContext } from "react";
7+
import { FC, useCallback } from "react";
88
import { Link } from "react-router-dom";
99
import { useAuthProviderDescriptions } from "../data/auth-providers/auth-provider-descriptions-query";
1010
import { openAuthorizeWindow } from "../provider-utils";
11-
import { getGitpodService } from "../service/service";
12-
import { UserContext, useCurrentUser } from "../user-context";
1311
import { Button } from "./Button";
1412
import { Heading2, Heading3, Subheading } from "./typography/headings";
1513
import classNames from "classnames";
1614
import { iconForAuthProvider, simplifyProviderName } from "../provider-utils";
1715
import { useIsOwner } from "../data/organizations/members-query";
1816
import { AuthProviderDescription } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";
17+
import { useAuthenticatedUser } from "../data/current-user/authenticated-user-query";
1918

2019
export function useNeedsGitAuthorization() {
21-
const authProviders = useAuthProviderDescriptions();
22-
const user = useCurrentUser();
23-
if (!user || !authProviders.data) {
20+
const { data: user } = useAuthenticatedUser();
21+
const { data: authProviders } = useAuthProviderDescriptions();
22+
if (!user || !authProviders) {
2423
return false;
2524
}
26-
return !authProviders.data.some((ap) => user.identities.some((i) => ap.id === i.authProviderId));
25+
return !authProviders.some((ap) => user.identities.some((i) => ap.id === i.authProviderId));
2726
}
2827

2928
export const AuthorizeGit: FC<{ className?: string }> = ({ className }) => {
30-
const { setUser } = useContext(UserContext);
29+
const { refetch: reloadUser } = useAuthenticatedUser();
3130
const owner = useIsOwner();
3231
const { data: authProviders } = useAuthProviderDescriptions();
33-
const updateUser = useCallback(() => {
34-
getGitpodService().server.getLoggedInUser().then(setUser);
35-
}, [setUser]);
32+
const updateUser = useCallback(async () => {
33+
await reloadUser();
34+
}, [reloadUser]);
3635

3736
const connect = useCallback(
3837
(ap: AuthProviderDescription) => {

components/dashboard/src/data/auth-providers/auth-provider-descriptions-query.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77
import { useQuery } from "@tanstack/react-query";
88
import { authProviderClient } from "../../service/public-api";
9-
import { useCurrentUser } from "../../user-context";
109
import {
1110
AuthProviderDescription,
1211
ListAuthProviderDescriptionsRequest,
1312
} from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";
13+
import { useAuthenticatedUser } from "../current-user/authenticated-user-query";
1414

1515
export const useAuthProviderDescriptions = () => {
16-
const user = useCurrentUser();
16+
const { data: user } = useAuthenticatedUser();
1717
const query = useQuery<AuthProviderDescription[]>({
1818
queryKey: getAuthProviderDescriptionsQueryKey(),
1919
queryFn: async () => {

components/dashboard/src/data/auth-providers/delete-user-auth-provider-mutation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
88
import { authProviderClient } from "../../service/public-api";
99
import { AuthProvider, DeleteAuthProviderRequest } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";
1010
import { getUserAuthProvidersQueryKey } from "./user-auth-providers-query";
11-
import { useCurrentUser } from "../../user-context";
11+
import { useAuthenticatedUser } from "../current-user/authenticated-user-query";
1212

1313
type DeleteAuthProviderArgs = {
1414
providerId: string;
1515
};
1616
export const useDeleteUserAuthProviderMutation = () => {
1717
const queryClient = useQueryClient();
18-
const user = useCurrentUser();
18+
const { data: user } = useAuthenticatedUser();
1919

2020
return useMutation({
2121
mutationFn: async ({ providerId }: DeleteAuthProviderArgs) => {

components/dashboard/src/data/auth-providers/user-auth-providers-query.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
import { useQuery } from "@tanstack/react-query";
88
import { authProviderClient } from "../../service/public-api";
99
import { AuthProvider, ListAuthProvidersRequest } from "@gitpod/public-api/lib/gitpod/v1/authprovider_pb";
10-
import { useCurrentUser } from "../../user-context";
10+
import { useAuthenticatedUser } from "../current-user/authenticated-user-query";
1111

1212
export type OrgAuthProvidersQueryResult = AuthProvider[];
1313
export const useOrgAuthProvidersQuery = () => {
14-
const user = useCurrentUser();
14+
const { data: user } = useAuthenticatedUser();
1515

1616
return useQuery<OrgAuthProvidersQueryResult>({
1717
queryKey: getUserAuthProvidersQueryKey(user?.id ?? ""),
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License.AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { useQuery } from "@tanstack/react-query";
8+
import { userClient } from "../../service/public-api";
9+
import { GetAuthenticatedUserRequest, User } from "@gitpod/public-api/lib/gitpod/v1/user_pb";
10+
11+
export const useAuthenticatedUser = () => {
12+
const query = useQuery<User>({
13+
queryKey: getAuthenticatedUserQueryKey(),
14+
queryFn: async () => {
15+
const params = new GetAuthenticatedUserRequest();
16+
const response = await userClient.getAuthenticatedUser(params);
17+
return response.user!;
18+
},
19+
});
20+
return query;
21+
};
22+
23+
export const getAuthenticatedUserQueryKey = () => ["authenticated-user", {}];

0 commit comments

Comments
 (0)