Skip to content

Commit

Permalink
use useUpdateCurrentUserMutation and make it fetch current state first
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexTugarev committed Nov 29, 2023
1 parent 6e11f4b commit e44ab89
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 59 deletions.
71 changes: 38 additions & 33 deletions components/dashboard/src/AppNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import dayjs from "dayjs";
import { useCallback, useEffect, useState } from "react";
import Alert, { AlertType } from "./components/Alert";
import { useUserLoader } from "./hooks/use-user-loader";
import { getGitpodService } from "./service/service";
import { isGitpodIo } from "./utils";
import { trackEvent } from "./Analytics";
import { useUpdateCurrentUserMutation } from "./data/current-user/update-mutation";
import { User as UserProtocol } from "@gitpod/gitpod-protocol";
import { User } from "@gitpod/public-api/lib/gitpod/v1/user_pb";

const KEY_APP_DISMISSED_NOTIFICATIONS = "gitpod-app-notifications-dismissed";
const PRIVACY_POLICY_LAST_UPDATED = "2023-10-17";
Expand All @@ -23,41 +25,44 @@ interface Notification {
onClose?: () => void;
}

const UPDATED_PRIVACY_POLICY: Notification = {
id: "privacy-policy-update",
type: "info",
preventDismiss: true,
onClose: async () => {
let dismissSuccess = false;
try {
const updatedUser = await getGitpodService().server.updateLoggedInUser({
additionalData: { profile: { acceptedPrivacyPolicyDate: dayjs().toISOString() } },
});
dismissSuccess = !!updatedUser;
} catch (err) {
console.error("Failed to update user's privacy policy acceptance date", err);
dismissSuccess = false;
} finally {
trackEvent("privacy_policy_update_accepted", {
path: window.location.pathname,
success: dismissSuccess,
});
}
},
message: (
<span className="text-md">
We've updated our Privacy Policy. You can review it{" "}
<a className="gp-link" href="https://www.gitpod.io/privacy" target="_blank" rel="noreferrer">
here
</a>
.
</span>
),
const UPDATED_PRIVACY_POLICY = (updateUser: (user: Partial<UserProtocol>) => Promise<User>) => {
return {
id: "privacy-policy-update",
type: "info",
preventDismiss: true,
onClose: async () => {
let dismissSuccess = false;
try {
const updatedUser = await updateUser({
additionalData: { profile: { acceptedPrivacyPolicyDate: dayjs().toISOString() } },
});
dismissSuccess = !!updatedUser;
} catch (err) {
console.error("Failed to update user's privacy policy acceptance date", err);
dismissSuccess = false;
} finally {
trackEvent("privacy_policy_update_accepted", {
path: window.location.pathname,
success: dismissSuccess,
});
}
},
message: (
<span className="text-md">
We've updated our Privacy Policy. You can review it{" "}
<a className="gp-link" href="https://www.gitpod.io/privacy" target="_blank" rel="noreferrer">
here
</a>
.
</span>
),
} as Notification;
};

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

useEffect(() => {
const notifications = [];
Expand All @@ -66,14 +71,14 @@ export function AppNotifications() {
!user?.profile?.acceptedPrivacyPolicyDate ||
new Date(PRIVACY_POLICY_LAST_UPDATED) > new Date(user.profile.acceptedPrivacyPolicyDate)
) {
notifications.push(UPDATED_PRIVACY_POLICY);
notifications.push(UPDATED_PRIVACY_POLICY((u: Partial<UserProtocol>) => updateUser.mutateAsync(u)));
}
}

const dismissedNotifications = getDismissedNotifications();
const topNotification = notifications.find((n) => !dismissedNotifications.includes(n.id));
setTopNotification(topNotification);
}, [loading, setTopNotification, user]);
}, [loading, updateUser, setTopNotification, user]);

const dismissNotification = useCallback(() => {
if (!topNotification) {
Expand Down
4 changes: 2 additions & 2 deletions components/dashboard/src/components/AuthorizeGit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export const AuthorizeGit: FC<{ className?: string }> = ({ className }) => {
const { refetch: reloadUser } = useAuthenticatedUser();
const owner = useIsOwner();
const { data: authProviders } = useAuthProviderDescriptions();
const updateUser = useCallback(() => {
reloadUser();
const updateUser = useCallback(async () => {
await reloadUser();
}, [reloadUser]);

const connect = useCallback(
Expand Down
15 changes: 12 additions & 3 deletions components/dashboard/src/data/current-user/update-mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,28 @@
* See License.AGPL.txt in the project root for license information.
*/

import { User } from "@gitpod/gitpod-protocol";
import { User as UserProtocol } from "@gitpod/gitpod-protocol";
import { useMutation } from "@tanstack/react-query";
import { trackEvent } from "../../Analytics";
import { getGitpodService } from "../../service/service";
import { useAuthenticatedUser } from "./authenticated-user-query";
import { converter } from "../../service/public-api";

type UpdateCurrentUserArgs = Partial<User>;
type UpdateCurrentUserArgs = Partial<UserProtocol>;

export const useUpdateCurrentUserMutation = () => {
return useMutation({
mutationFn: async (partialUser: UpdateCurrentUserArgs) => {
const user = await getGitpodService().server.updateLoggedInUser(partialUser);
const current = await getGitpodService().server.getLoggedInUser();
const update: UpdateCurrentUserArgs = {
id: current.id,
fullName: partialUser.fullName || current.fullName,
additionalData: {
...current.additionalData,
...partialUser.additionalData,
},
};
const user = await getGitpodService().server.updateLoggedInUser(update);
return converter.toUser(user);
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const DedicatedSetupSteps: FC<DedicatedSetupStepsProps> = ({ org, ssoConfig, onC

const updateUser = useCallback(async () => {
await getGitpodService().reconnect();
reloadUser();
await reloadUser();
}, [reloadUser]);

const handleEndSetup = useCallback(async () => {
Expand Down
2 changes: 1 addition & 1 deletion components/dashboard/src/onboarding/UserOnboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const UserOnboarding: FunctionComponent<Props> = ({ user }) => {
});

dropConfetti();
reloadUser();
await reloadUser();

// Look for the `onboarding=force` query param, and remove if present
const queryParams = new URLSearchParams(location.search);
Expand Down
6 changes: 4 additions & 2 deletions components/dashboard/src/user-settings/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useAuthenticatedUser } from "../data/current-user/authenticated-user-qu
import { getPrimaryEmail, getProfile, isOrganizationOwned } from "@gitpod/gitpod-protocol/lib/public-api-utils";
import { User } from "@gitpod/public-api/lib/gitpod/v1/user_pb";
import { User as UserProtocol } from "@gitpod/gitpod-protocol";
import { useUpdateCurrentUserMutation } from "../data/current-user/update-mutation";

export default function Account() {
const { data: user, refetch: reloadUser } = useAuthenticatedUser();
Expand All @@ -30,6 +31,7 @@ export default function Account() {
const [errorMessage, setErrorMessage] = useState("");
const canUpdateEmail = user && !isOrganizationOwned(user);
const { toast } = useToast();
const updateUser = useUpdateCurrentUserMutation();

const saveProfileState = useCallback(async () => {
if (!user || !profileState) {
Expand All @@ -54,14 +56,14 @@ export default function Account() {
profileState.email = getPrimaryEmail(user) || "";
}

await getGitpodService().server.updateLoggedInUser({
await updateUser.mutateAsync({
additionalData: {
profile: profileState,
},
});
reloadUser();
toast("Your profile information has been updated.");
}, [canUpdateEmail, profileState, reloadUser, toast, user]);
}, [updateUser, canUpdateEmail, profileState, reloadUser, toast, user]);

const deleteAccount = useCallback(async () => {
await getGitpodService().server.deleteAccount();
Expand Down
13 changes: 7 additions & 6 deletions components/dashboard/src/user-settings/Notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,32 @@
*/

import { useState } from "react";
import { getGitpodService } from "../service/service";
import { CheckboxInputField } from "../components/forms/CheckboxInputField";
import { identifyUser } from "../Analytics";
import { PageWithSettingsSubMenu } from "./PageWithSettingsSubMenu";
import { Heading2 } from "../components/typography/headings";
import { useAuthenticatedUser } from "../data/current-user/authenticated-user-query";
import { useUpdateCurrentUserMutation } from "../data/current-user/update-mutation";

export default function Notifications() {
const { data: user, refetch: reloadUser } = useAuthenticatedUser();
const [isOnboardingMail, setOnboardingMail] = useState(!!user?.emailNotificationSettings?.allowsOnboardingMail);
const [isChangelogMail, setChangelogMail] = useState(!!user?.emailNotificationSettings?.allowsChangelogMail);
const [isDevXMail, setDevXMail] = useState(!!user?.emailNotificationSettings?.allowsDevxMail);
const updateUser = useUpdateCurrentUserMutation();

const toggleOnboardingMail = async () => {
if (user && user.emailNotificationSettings) {
const newIsOnboardingMail = !isOnboardingMail;
user.emailNotificationSettings.allowsOnboardingMail = newIsOnboardingMail;
await getGitpodService().server.updateLoggedInUser({
await updateUser.mutateAsync({
additionalData: {
emailNotificationSettings: {
allowsOnboardingMail: newIsOnboardingMail,
},
},
});
reloadUser();
await reloadUser();
identifyUser({ unsubscribed_onboarding: !newIsOnboardingMail });
setOnboardingMail(newIsOnboardingMail);
}
Expand All @@ -39,14 +40,14 @@ export default function Notifications() {
if (user && user.emailNotificationSettings) {
const newIsChangelogMail = !isChangelogMail;
user.emailNotificationSettings.allowsChangelogMail = newIsChangelogMail;
await getGitpodService().server.updateLoggedInUser({
await updateUser.mutateAsync({
additionalData: {
emailNotificationSettings: {
allowsChangelogMail: newIsChangelogMail,
},
},
});
reloadUser();
await reloadUser();
identifyUser({ unsubscribed_changelog: !newIsChangelogMail });
setChangelogMail(newIsChangelogMail);
}
Expand All @@ -56,7 +57,7 @@ export default function Notifications() {
if (user && user.emailNotificationSettings) {
const newIsDevXMail = !isDevXMail;
user.emailNotificationSettings.allowsDevxMail = newIsDevXMail;
await getGitpodService().server.updateLoggedInUser({
await updateUser.mutateAsync({
additionalData: {
emailNotificationSettings: {
allowsDevXMail: newIsDevXMail,
Expand Down
16 changes: 10 additions & 6 deletions components/dashboard/src/user-settings/Preferences.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import SelectIDE from "./SelectIDE";
import { InputField } from "../components/forms/InputField";
import { TextInput } from "../components/forms/TextInputField";
import { useToast } from "../components/toasts/Toasts";
import { useUpdateCurrentUserDotfileRepoMutation } from "../data/current-user/update-mutation";
import {
useUpdateCurrentUserDotfileRepoMutation,
useUpdateCurrentUserMutation,
} from "../data/current-user/update-mutation";
import { useOrgBillingMode } from "../data/billing-mode/org-billing-mode-query";
import { useAuthenticatedUser } from "../data/current-user/authenticated-user-query";
import { converter } from "../service/public-api";
Expand All @@ -25,6 +28,7 @@ export type IDEChangedTrackLocation = "workspace_list" | "workspace_start" | "pr
export default function Preferences() {
const { toast } = useToast();
const { data: user, refetch: reloadUser } = useAuthenticatedUser();
const updateUser = useUpdateCurrentUserMutation();
const billingMode = useOrgBillingMode();
const updateDotfileRepo = useUpdateCurrentUserDotfileRepoMutation();

Expand All @@ -41,7 +45,7 @@ export default function Preferences() {
e.preventDefault();

await updateDotfileRepo.mutateAsync(dotfileRepo);
reloadUser();
await reloadUser();
toast("Your dotfiles repository was updated.");
},
[updateDotfileRepo, dotfileRepo, reloadUser, toast],
Expand All @@ -57,7 +61,7 @@ export default function Preferences() {
await getGitpodService().server.updateWorkspaceTimeoutSetting({ workspaceTimeout: workspaceTimeout });

// TODO: Once current user is in react-query, we can instead invalidate the query vs. refetching here
reloadUser();
await reloadUser();

let toastMessage = <>Default workspace timeout was updated.</>;
if (billingMode.data?.mode === "usage-based") {
Expand Down Expand Up @@ -89,14 +93,14 @@ export default function Preferences() {
if (!user) {
return;
}
await getGitpodService().server.updateLoggedInUser({
await updateUser.mutateAsync({
additionalData: {
workspaceAutostartOptions: [],
},
});
reloadUser();
await reloadUser();
toast("Workspace options have been cleared.");
}, [reloadUser, toast, user]);
}, [updateUser, reloadUser, toast, user]);

return (
<div>
Expand Down
2 changes: 1 addition & 1 deletion components/dashboard/src/user-settings/SelectIDE.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function SelectIDE(props: SelectIDEProps) {
},
};
await updateUser.mutateAsync(updates);
reloadUser();
await reloadUser();
},
[reloadUser, updateUser],
);
Expand Down
10 changes: 6 additions & 4 deletions components/dashboard/src/workspaces/CreateWorkspacePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { useListWorkspacesQuery } from "../data/workspaces/list-workspaces-query
import { useWorkspaceContext } from "../data/workspaces/resolve-context-query";
import { useDirtyState } from "../hooks/use-dirty-state";
import { openAuthorizeWindow } from "../provider-utils";
import { getGitpodService, gitpodHostUrl } from "../service/service";
import { gitpodHostUrl } from "../service/service";
import { StartWorkspaceError } from "../start/StartPage";
import { VerifyModal } from "../start/VerifyModal";
import { StartWorkspaceOptions } from "../start/start-workspace-options";
Expand All @@ -45,9 +45,11 @@ import { useAuthenticatedUser } from "../data/current-user/authenticated-user-qu
import { User_WorkspaceAutostartOption } from "@gitpod/public-api/lib/gitpod/v1/user_pb";
import { EditorReference } from "@gitpod/public-api/lib/gitpod/v1/editor_pb";
import { converter } from "../service/public-api";
import { useUpdateCurrentUserMutation } from "../data/current-user/update-mutation";

export function CreateWorkspacePage() {
const { data: user, refetch: reloadUser } = useAuthenticatedUser();
const updateUser = useUpdateCurrentUserMutation();
const currentOrg = useCurrentOrg().data;
const projects = useListAllProjectsQuery();
const workspaces = useListWorkspacesQuery({ limit: 50 });
Expand Down Expand Up @@ -106,15 +108,15 @@ export function CreateWorkspacePage() {
}),
}),
);
await getGitpodService().server.updateLoggedInUser({
await updateUser.mutateAsync({
additionalData: {
workspaceAutostartOptions: workspaceAutoStartOptions.map((o) =>
converter.fromWorkspaceAutostartOption(o),
),
},
});
reloadUser();
}, [currentOrg, selectedIde, selectedWsClass, reloadUser, useLatestIde, user, workspaceContext.data]);
await reloadUser();
}, [updateUser, currentOrg, selectedIde, selectedWsClass, reloadUser, useLatestIde, user, workspaceContext.data]);

// see if we have a matching project based on context url and project's repo url
const project = useMemo(() => {
Expand Down

0 comments on commit e44ab89

Please sign in to comment.