diff --git a/frontend/src/html/popups.html b/frontend/src/html/popups.html
index 686dd1306e99..7dff29cceec5 100644
--- a/frontend/src/html/popups.html
+++ b/frontend/src/html/popups.html
@@ -1060,9 +1060,17 @@
You can only do this once!
-
+
+
+
+
+
+
+
+
+
- set
+ set
diff --git a/frontend/src/styles/popups.scss b/frontend/src/styles/popups.scss
index 644d865acad6..c29d727145e1 100644
--- a/frontend/src/styles/popups.scss
+++ b/frontend/src/styles/popups.scss
@@ -1636,6 +1636,20 @@ body.darkMode {
.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;
diff --git a/frontend/src/ts/modals/streak-hour-offset.ts b/frontend/src/ts/modals/streak-hour-offset.ts
index 5b1dbc36a654..68b0f3e3257b 100644
--- a/frontend/src/ts/modals/streak-hour-offset.ts
+++ b/frontend/src/ts/modals/streak-hour-offset.ts
@@ -8,6 +8,10 @@ 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 {
if (!ConnectionState.get()) {
Notifications.add("You are offline", 0, {
@@ -17,17 +21,22 @@ export function show(): void {
}
void modal.show({
- focusFirstInput: true,
+ focusFirstInput: "focusAndSelect",
beforeAnimation: async (modalEl) => {
if (getSnapshot()?.streakHourOffset !== undefined) {
modalEl.qs("input")?.remove();
modalEl.qs(".preview")?.remove();
- modalEl.qs("button")?.remove();
+ modalEl.qsa("button")?.remove();
modalEl
.qs(".text")
- ?.setText("You have already set your streak hour offset.");
+ ?.setText(
+ `You have already set your streak hour offset to ${
+ getSnapshot()?.streakHourOffset ?? "?"
+ }. You can only set your streak hour offset once.`,
+ );
} else {
- modalEl.qs("input")?.setValue("0");
+ state.offset = 0;
+ updateDisplay();
updatePreview();
}
},
@@ -35,18 +44,12 @@ export function show(): void {
}
function updatePreview(): void {
- const inputValue = parseInt(
- modal.getModal().qs("input")?.getValue() ?? "",
- 10,
- );
+ const inputValue = state.offset;
const preview = modal.getModal().qs(".preview");
const date = new Date();
- date.setUTCHours(0);
- date.setUTCMinutes(0);
- date.setUTCSeconds(0);
- date.setUTCMilliseconds(0);
+ date.setUTCHours(0, 0, 0, 0);
const newDate = new Date();
newDate.setUTCHours(0);
@@ -62,23 +65,30 @@ function updatePreview(): void {
`);
}
+function updateDisplay(): void {
+ modal
+ .getModal()
+ .qs("input")
+ ?.setValue(state.offset.toFixed(1));
+}
+
function hide(): void {
void modal.hide();
}
async function apply(): Promise {
- const value = parseInt(
- modal.getModal().qs("input")?.getValue() ?? "",
- 10,
- );
+ const value = state.offset;
if (isNaN(value)) {
Notifications.add("Streak hour offset must be a number", 0);
return;
}
- if (value < -11 || value > 12) {
- Notifications.add("Streak hour offset must be between -11 and 12", 0);
+ if (value < -11 || value > 12 || (value % 1 !== 0 && value % 1 !== 0.5)) {
+ Notifications.add(
+ "Streak offset must be between -11 and 12. Times ending in .5 can be used for 30-minute increments.",
+ 0,
+ );
return;
}
@@ -88,25 +98,61 @@ async function apply(): Promise {
body: { hourOffset: value },
});
Loader.hide();
+
if (response.status !== 200) {
Notifications.add("Failed to set streak hour offset", -1, { response });
} else {
Notifications.add("Streak hour offset set", 1);
const snap = getSnapshot() as Snapshot;
+
snap.streakHourOffset = value;
setSnapshot(snap);
hide();
}
}
+function setStateToInput(): void {
+ const inputValue = parseFloat(
+ modal.getModal().qs("input")?.getValue() ?? "0",
+ );
+ if (!isNaN(inputValue)) {
+ state.offset = inputValue;
+ if (state.offset < -11) state.offset = -11;
+ if (state.offset > 12) state.offset = 12;
+ } else {
+ state.offset = 0;
+ }
+}
+
const modal = new AnimatedModal({
dialogId: "streakHourOffsetModal",
setup: async (modalEl): Promise => {
- modalEl.qs("input")?.on("input", () => {
+ modalEl.qs("input")?.on("focusout", () => {
+ setStateToInput();
+ updateDisplay();
updatePreview();
});
- modalEl.qs("button")?.on("click", () => {
+ modalEl.qs("input")?.on("keyup", (e) => {
+ if (e.key === "Enter") {
+ setStateToInput();
+ updateDisplay();
+ updatePreview();
+ }
+ });
+ modalEl.qs(".submit")?.on("click", () => {
void apply();
});
+ modalEl.qs(".decreaseOffset")?.on("click", () => {
+ state.offset -= 0.5;
+ if (state.offset < -11) state.offset = -11;
+ updateDisplay();
+ updatePreview();
+ });
+ modalEl.qs(".increaseOffset")?.on("click", () => {
+ state.offset += 0.5;
+ if (state.offset > 12) state.offset = 12;
+ updateDisplay();
+ updatePreview();
+ });
},
});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a48758b1f11e..ad23c2f832de 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -6252,6 +6252,7 @@ packages:
keygrip@1.1.0:
resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==}
engines: {node: '>= 0.6'}
+ deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}