diff --git a/frontend/src/html/popups.html b/frontend/src/html/popups.html
index 4dd3c50507ed..12bee1f6faf9 100644
--- a/frontend/src/html/popups.html
+++ b/frontend/src/html/popups.html
@@ -355,78 +355,3 @@
-
-
diff --git a/frontend/src/styles/popups.scss b/frontend/src/styles/popups.scss
index 304c7a6d87e0..e1979360b33a 100644
--- a/frontend/src/styles/popups.scss
+++ b/frontend/src/styles/popups.scss
@@ -667,63 +667,4 @@ body.darkMode {
}
}
}
-}
-
-#editProfileModal {
- .modal {
- max-width: 600px;
- max-height: 100%;
- label {
- color: var(--sub-color);
- margin-bottom: 0.25em;
- display: block;
- }
- input:not([type="checkbox"]) {
- width: 100%;
- }
- input[type="checkbox"] {
- vertical-align: text-bottom;
- }
- textarea {
- resize: vertical;
- width: 100%;
- padding: 10px;
- line-height: 1.2rem;
- min-height: 5rem;
- max-height: 10rem;
- }
-
- .socialURL {
- display: flex;
- }
-
- .socialURL > p {
- margin-block: 0.5rem;
- margin-inline-end: 0.5rem;
- }
-
- .badgeSelectionContainer {
- display: flex;
- flex-wrap: wrap;
- }
-
- .badgeSelectionItem {
- width: max-content;
- opacity: 25%;
- cursor: pointer;
- margin-right: 0.5rem;
- margin-bottom: 0.5rem;
- padding: 0;
- border-radius: calc(var(--roundness) / 2);
- }
-
- .badgeSelectionItem.selected,
- .badgeSelectionItem:hover {
- opacity: 100%;
- }
-
- span {
- color: var(--text-color);
- }
- }
-}
+}
\ No newline at end of file
diff --git a/frontend/src/ts/components/modals/CustomTestDurationModal.tsx b/frontend/src/ts/components/modals/CustomTestDurationModal.tsx
index 39156334f7ca..4cc214a2565e 100644
--- a/frontend/src/ts/components/modals/CustomTestDurationModal.tsx
+++ b/frontend/src/ts/components/modals/CustomTestDurationModal.tsx
@@ -98,7 +98,7 @@ export function CustomTestDurationModal(): JSXElement {
form={form}
variant="button"
text="apply"
- skipDirtyCheck
+ skipUnchangedCheck
/>
diff --git a/frontend/src/ts/components/modals/CustomTextModal.tsx b/frontend/src/ts/components/modals/CustomTextModal.tsx
index ae856a66868d..1e12ed3d9769 100644
--- a/frontend/src/ts/components/modals/CustomTextModal.tsx
+++ b/frontend/src/ts/components/modals/CustomTextModal.tsx
@@ -491,7 +491,7 @@ export function CustomTextModal(): JSXElement {
diff --git a/frontend/src/ts/components/modals/EditProfileModal.tsx b/frontend/src/ts/components/modals/EditProfileModal.tsx
new file mode 100644
index 000000000000..64f487ce72af
--- /dev/null
+++ b/frontend/src/ts/components/modals/EditProfileModal.tsx
@@ -0,0 +1,274 @@
+import {
+ GithubProfileSchema,
+ TwitterProfileSchema,
+ UserProfileDetailsSchema,
+ WebsiteSchema,
+} from "@monkeytype/schemas/users";
+import { For } from "solid-js";
+import Ape from "../../ape";
+import { getHTMLById } from "../../controllers/badge-controller";
+import * as DB from "../../db";
+import {
+ showSuccessNotification,
+ showErrorNotification,
+} from "../../states/notifications";
+
+import { AnimatedModal } from "../common/AnimatedModal";
+import { hideModal } from "../../states/modals";
+import { Checkbox } from "../ui/form/Checkbox";
+import { InputField } from "../ui/form/InputField";
+import { TextareaField } from "../ui/form/TextareaField";
+import { createForm } from "@tanstack/solid-form";
+import { SubmitButton } from "../ui/form/SubmitButton";
+import { fromSchema } from "../ui/form/utils";
+
+export function EditProfile() {
+ const snapshot = DB.getSnapshot();
+ if (!snapshot) return;
+
+ const badges = snapshot.inventory?.badges ?? [];
+ const form = createForm(() => ({
+ defaultValues: {
+ bio: snapshot.details?.bio ?? "",
+ keyboard: snapshot.details?.keyboard ?? "",
+ github: snapshot.details?.socialProfiles?.github ?? "",
+ twitter: snapshot.details?.socialProfiles?.twitter ?? "",
+ website: snapshot.details?.socialProfiles?.website ?? "",
+ showActivityOnPublicProfile:
+ snapshot.details?.showActivityOnPublicProfile ?? true,
+ badgeId: badges.find((b) => b.selected)?.id ?? -1,
+ },
+ onSubmit: async ({ value }) => {
+ const updates = {
+ bio: value.bio,
+ keyboard: value.keyboard,
+ socialProfiles: {
+ twitter: value.twitter || undefined,
+ github: value.github || undefined,
+ website: value.website || undefined,
+ },
+ showActivityOnPublicProfile: value.showActivityOnPublicProfile,
+ };
+
+ const response = await Ape.users.updateProfile({
+ body: {
+ ...updates,
+ selectedBadgeId: value.badgeId,
+ },
+ });
+
+ if (response.status !== 200) {
+ showErrorNotification("Failed to update profile", { response });
+ return;
+ }
+
+ snapshot.details = response.body.data ?? updates;
+ snapshot.inventory?.badges.forEach((badge) => {
+ if (badge.id === value.badgeId) {
+ badge.selected = true;
+ } else {
+ delete badge.selected;
+ }
+ });
+
+ form.reset(value);
+ hideModal("EditProfile");
+ DB.setSnapshot(snapshot);
+ showSuccessNotification("Profile updated");
+ },
+ }));
+
+ return (
+
+
+ {(field) => (
+
+ )}
+
+
+
+
+
+
+ {(field) => (
+
+
+
+ {(badge) => (
+
+ )}
+
+
+ )}
+
+
+
+
+
+
+ {(field) => (
+
+ )}
+
+
+
+ save
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/ts/components/modals/SimpleModal.tsx b/frontend/src/ts/components/modals/SimpleModal.tsx
index cd53d9a62432..3a07ac6e9c29 100644
--- a/frontend/src/ts/components/modals/SimpleModal.tsx
+++ b/frontend/src/ts/components/modals/SimpleModal.tsx
@@ -291,7 +291,7 @@ export function SimpleModal(): JSXElement {
variant="button"
class="w-full"
text={config()?.buttonText}
- skipDirtyCheck={(config()?.inputs?.length ?? 0) === 0}
+ skipUnchangedCheck ={(config()?.inputs?.length ?? 0) === 0}
/>
diff --git a/frontend/src/ts/components/modals/WordFilterModal.tsx b/frontend/src/ts/components/modals/WordFilterModal.tsx
index 010a23071722..c2b26701f610 100644
--- a/frontend/src/ts/components/modals/WordFilterModal.tsx
+++ b/frontend/src/ts/components/modals/WordFilterModal.tsx
@@ -333,7 +333,7 @@ export function WordFilterModal(props: {
variant="button"
text="set"
class="flex-1"
- skipDirtyCheck
+ skipUnchangedCheck
disabled={loading()}
onClick={() => (submitAction = "set")}
/>
@@ -342,7 +342,7 @@ export function WordFilterModal(props: {
variant="button"
text="add"
class="flex-1"
- skipDirtyCheck
+ skipUnchangedCheck
disabled={loading()}
onClick={() => (submitAction = "add")}
/>
diff --git a/frontend/src/ts/components/pages/profile/UserDetails.tsx b/frontend/src/ts/components/pages/profile/UserDetails.tsx
index 6dd15828a218..6eb9a121c8e6 100644
--- a/frontend/src/ts/components/pages/profile/UserDetails.tsx
+++ b/frontend/src/ts/components/pages/profile/UserDetails.tsx
@@ -16,10 +16,10 @@ import { createEffect, createSignal, For, JSXElement, Show } from "solid-js";
import { Snapshot } from "../../../constants/default-snapshot";
import { addFriend, isFriend } from "../../../db";
-import * as EditProfileModal from "../../../modals/edit-profile";
import * as UserReportModal from "../../../modals/user-report";
import { bp } from "../../../states/breakpoints";
import { getUserId, isAuthenticated } from "../../../states/core";
+import { showModal } from "../../../states/modals";
import {
showNoticeNotification,
showErrorNotification,
@@ -36,6 +36,7 @@ import { Button } from "../../common/Button";
import { DiscordAvatar } from "../../common/DiscordAvatar";
import { UserBadge } from "../../common/UserBadge";
import { UserFlags } from "../../common/UserFlags";
+import { EditProfile } from "../../modals/EditProfileModal";
type Variant = "basic" | "hasSocials" | "hasBioOrKeyboard" | "full";
@@ -98,6 +99,9 @@ export function UserDetails(props: {
isAccountPage={props.isAccountPage}
/>
+
+
+
);
}
@@ -177,7 +181,7 @@ function ActionButtons(props: {
showNoticeNotification("Banned users cannot edit their profile");
return;
}
- EditProfileModal.show();
+ showModal("EditProfile");
}}
/>