Edit result tags
diff --git a/frontend/src/styles/popups.scss b/frontend/src/styles/popups.scss
index b205e273e190..f0fa36a4f34c 100644
--- a/frontend/src/styles/popups.scss
+++ b/frontend/src/styles/popups.scss
@@ -302,41 +302,3 @@ body.darkMode {
}
}
}
-
-#streakHourOffsetModal {
- .modal {
- max-width: 500px;
- .red {
- color: var(--error-color);
- }
- .group {
- display: grid;
- grid-template-columns: 1fr 1fr 1fr;
- gap: 0.5rem;
- justify-items: center;
- align-items: center;
- font-size: 2em;
- button {
- width: 100%;
- }
- input {
- text-align: center;
- }
- }
- .preview {
- & > div:first-child {
- margin-bottom: 1rem;
- }
- .row {
- display: flex;
- gap: 1rem;
- }
- div:first-child {
- flex-grow: 1;
- }
- div:last-child {
- align-self: center;
- }
- }
- }
-}
diff --git a/frontend/src/ts/components/modals/Modals.tsx b/frontend/src/ts/components/modals/Modals.tsx
index 632e0cc13172..81b34905e493 100644
--- a/frontend/src/ts/components/modals/Modals.tsx
+++ b/frontend/src/ts/components/modals/Modals.tsx
@@ -16,6 +16,7 @@ import { QuoteSearchModal } from "./QuoteSearchModal";
import { RegisterCaptchaModal } from "./RegisterCaptchaModal";
import { ShareTestSettings } from "./ShareTestSettings";
import { SimpleModal } from "./SimpleModal";
+import { StreakHourOffsetModal } from "./StreakHourOffsetModal";
import { SupportModal } from "./SupportModal";
import { VersionHistoryModal } from "./VersionHistoryModal";
@@ -40,6 +41,7 @@ export function Modals(): JSXElement {
+
>
);
}
diff --git a/frontend/src/ts/components/modals/StreakHourOffsetModal.tsx b/frontend/src/ts/components/modals/StreakHourOffsetModal.tsx
new file mode 100644
index 000000000000..8bace5e0809d
--- /dev/null
+++ b/frontend/src/ts/components/modals/StreakHourOffsetModal.tsx
@@ -0,0 +1,174 @@
+import { StreakHourOffsetSchema } from "@monkeytype/schemas/users";
+import { createForm } from "@tanstack/solid-form";
+
+import Ape from "../../ape";
+import { Snapshot } from "../../constants/default-snapshot";
+import { getSnapshot, setSnapshot } from "../../db";
+import { hideLoaderBar, showLoaderBar } from "../../states/loader-bar";
+import { hideModal } from "../../states/modals";
+import {
+ showErrorNotification,
+ showNoticeNotification,
+ showSuccessNotification,
+} from "../../states/notifications";
+import { AnimatedModal } from "../common/AnimatedModal";
+import { Button } from "../common/Button";
+import { InputField } from "../ui/form/InputField";
+import { SubmitButton } from "../ui/form/SubmitButton";
+import { allFieldsMandatory, fromSchema } from "../ui/form/utils";
+
+export function StreakHourOffsetModal() {
+ const form = createForm(() => ({
+ defaultValues: {
+ offset: "0.0",
+ },
+ onSubmitInvalid: () => {
+ showNoticeNotification("Please fill in all fields");
+ },
+ validators: {
+ onChange: allFieldsMandatory(),
+ },
+ onSubmit: async ({ value }) => {
+ const { offset } = value;
+ const hourOffset = parseFloat(offset);
+
+ showLoaderBar();
+
+ const response = await Ape.users.setStreakHourOffset({
+ body: { hourOffset },
+ });
+ hideLoaderBar();
+
+ if (response.status !== 200) {
+ showErrorNotification("Failed to set streak hour offset", { response });
+ hideModal("StreakHourOffset");
+ return;
+ }
+ showSuccessNotification("Streak hour offset set");
+ const snap = getSnapshot() as Snapshot;
+
+ snap.streakHourOffset = hourOffset;
+ setSnapshot(snap);
+ hideModal("StreakHourOffset");
+ },
+ }));
+
+ return (
+
+
+ Streaks reset at midnight UTC by default. If this is not convenient for
+ you (for example if it means that streaks reset in the middle of the
+ day), you can change the hour offset here.
+
+
+ This will not take daylight savings time into consideration!
+
+
+ You can only do this once!
+
+
+
+ );
+}
+
+function clampOffset(value: string): number {
+ const num = Number.parseFloat(value);
+ if (isNaN(num)) return 0;
+ return Math.max(-11, Math.min(12, num));
+}
+
+function getNewDate(offset: string): string {
+ const inputValue = Number.parseFloat(offset);
+ if (isNaN(inputValue)) return "-";
+ const newDate = new Date();
+ newDate.setUTCHours(0);
+ newDate.setUTCMinutes(0);
+ newDate.setUTCSeconds(0);
+ newDate.setUTCMilliseconds(0);
+
+ newDate.setHours(newDate.getHours() - -1 * Math.floor(inputValue)); //idk why, but it only works when i subtract (so i have to negate inputValue)
+ newDate.setMinutes(
+ newDate.getMinutes() - -1 * ((((inputValue % 1) + 1) % 1) * 60),
+ );
+ return newDate.toLocaleTimeString();
+}
+
+function getDate(): string {
+ const date = new Date();
+ date.setUTCHours(0, 0, 0, 0);
+ return date.toLocaleTimeString();
+}
diff --git a/frontend/src/ts/components/pages/account-settings/AccountTab.tsx b/frontend/src/ts/components/pages/account-settings/AccountTab.tsx
index 40436b03386b..99e8ee1350a9 100644
--- a/frontend/src/ts/components/pages/account-settings/AccountTab.tsx
+++ b/frontend/src/ts/components/pages/account-settings/AccountTab.tsx
@@ -1,8 +1,8 @@
import { Show } from "solid-js";
import Ape from "../../../ape";
-import * as StreakHourOffsetModal from "../../../modals/streak-hour-offset";
import { showLoaderBar } from "../../../states/loader-bar";
+import { showModal } from "../../../states/modals";
import { showErrorNotification } from "../../../states/notifications";
import { getSnapshot } from "../../../states/snapshot";
import { Button } from "../../common/Button";
@@ -126,7 +126,7 @@ function UpdateStreakOffset() {
>
button={{
text: "update hour offset",
- onClick: () => StreakHourOffsetModal.show(),
+ onClick: () => showModal("StreakHourOffset"),
}}
disabled={getSnapshot()?.streakHourOffset !== undefined}
disabledDescription=<>
diff --git a/frontend/src/ts/components/ui/form/FieldIndicator.tsx b/frontend/src/ts/components/ui/form/FieldIndicator.tsx
index 0ec357983036..a0d831054b9f 100644
--- a/frontend/src/ts/components/ui/form/FieldIndicator.tsx
+++ b/frontend/src/ts/components/ui/form/FieldIndicator.tsx
@@ -8,6 +8,7 @@ import { LoadingCircle } from "../../common/LoadingCircle";
export type FieldIndicatorProps = {
field: AnyFieldApi;
+ alwaysShow?: boolean;
};
export function FieldIndicator(props: FieldIndicatorProps) {
@@ -45,9 +46,10 @@ export function FieldIndicator(props: FieldIndicatorProps) {
diff --git a/frontend/src/ts/components/ui/form/InputField.tsx b/frontend/src/ts/components/ui/form/InputField.tsx
index 78c995343edf..cb72a3f5268f 100644
--- a/frontend/src/ts/components/ui/form/InputField.tsx
+++ b/frontend/src/ts/components/ui/form/InputField.tsx
@@ -27,6 +27,7 @@ export function InputField(props: {
min?: number;
max?: number;
step?: string | number;
+ alwaysShowFieldIndicator?: boolean;
}): JSXElement {
const [shake, setShake] = createSignal(false);
@@ -105,7 +106,10 @@ export function InputField(props: {
step={props.step?.toString()}
/>
-
+
);
diff --git a/frontend/src/ts/modals/streak-hour-offset.ts b/frontend/src/ts/modals/streak-hour-offset.ts
deleted file mode 100644
index 42bde3323f5c..000000000000
--- a/frontend/src/ts/modals/streak-hour-offset.ts
+++ /dev/null
@@ -1,156 +0,0 @@
-import Ape from "../ape";
-// import * as DB from "../db";
-import {
- showNoticeNotification,
- showErrorNotification,
- showSuccessNotification,
-} from "../states/notifications";
-
-import { showLoaderBar, hideLoaderBar } from "../states/loader-bar";
-import { getSnapshot, setSnapshot } from "../db";
-import AnimatedModal from "../utils/animated-modal";
-import { Snapshot } from "../constants/default-snapshot";
-
-let state = {
- offset: 0,
-};
-
-export function show(): void {
- void modal.show({
- focusFirstInput: "focusAndSelect",
- beforeAnimation: async (modalEl) => {
- if (getSnapshot()?.streakHourOffset !== undefined) {
- modalEl.qs("input")?.remove();
- modalEl.qs(".preview")?.remove();
- modalEl.qsa("button")?.remove();
- modalEl
- .qs(".text")
- ?.setText(
- `You have already set your streak hour offset to ${
- getSnapshot()?.streakHourOffset ?? "?"
- }. You can only set your streak hour offset once.`,
- );
- } else {
- state.offset = 0;
- updateDisplay();
- updatePreview();
- }
- },
- });
-}
-
-function updatePreview(): void {
- const inputValue = state.offset;
- const preview = modal.getModal().qs(".preview");
-
- const date = new Date();
- date.setUTCHours(0, 0, 0, 0);
-
- const newDate = new Date();
- newDate.setUTCHours(0);
- newDate.setUTCMinutes(0);
- newDate.setUTCSeconds(0);
- newDate.setUTCMilliseconds(0);
-
- newDate.setHours(newDate.getHours() - -1 * Math.floor(inputValue)); //idk why, but it only works when i subtract (so i have to negate inputValue)
- newDate.setMinutes(
- newDate.getMinutes() - -1 * ((((inputValue % 1) + 1) % 1) * 60),
- );
-
- preview?.setHtml(`
-