fixes with countdown

This commit is contained in:
Michael Freno
2025-12-21 15:38:49 -05:00
parent 51bf745039
commit c88df09d47
11 changed files with 50 additions and 55 deletions

View File

@@ -180,7 +180,7 @@ function AppLayout(props: { children: any }) {
<div class="flex max-w-screen flex-row">
<LeftBar />
<div
class="bg-base relative h-screen overflow-y-scroll rounded-t-lg p-4 pt-16 shadow-2xl"
class="bg-base relative h-screen overflow-y-scroll rounded-t-lg pt-16 shadow-2xl"
style={{
width: `${centerWidth()}px`,
"margin-left": `${leftBarSize()}px`

View File

@@ -2,12 +2,13 @@ import { Component, createSignal, onMount, onCleanup } from "solid-js";
interface CountdownCircleTimerProps {
duration: number;
initialRemainingTime: number;
initialRemainingTime?: number;
size: number;
strokeWidth: number;
colors: string;
children: (time: number) => any;
children: (props: { remainingTime: number }) => any;
onComplete?: () => void;
isPlaying?: boolean;
}
const CountdownCircleTimer: Component<CountdownCircleTimerProps> = (props) => {
@@ -15,7 +16,7 @@ const CountdownCircleTimer: Component<CountdownCircleTimerProps> = (props) => {
const circumference = radius * 2 * Math.PI;
const [remainingTime, setRemainingTime] = createSignal(
props.initialRemainingTime
props.initialRemainingTime ?? props.duration
);
// Calculate progress (0 to 1)
@@ -87,7 +88,7 @@ const CountdownCircleTimer: Component<CountdownCircleTimerProps> = (props) => {
transform: "translate(-50%, -50%)"
}}
>
{props.children(remainingTime())}
{props.children({ remainingTime: remainingTime() })}
</div>
</div>
);

View File

@@ -36,7 +36,7 @@ export default function DeletionForm() {
if (timer) {
timerInterval = setInterval(
() => calcRemainder(timer),
1000,
1000
) as unknown as number;
onCleanup(() => {
if (timerInterval) {
@@ -65,7 +65,7 @@ export default function DeletionForm() {
const response = await fetch("/api/trpc/misc.sendDeletionRequestEmail", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
body: JSON.stringify({ email })
});
const result = await response.json();
@@ -79,7 +79,7 @@ export default function DeletionForm() {
}
timerInterval = setInterval(
() => calcRemainder(timer),
1000,
1000
) as unknown as number;
}
} else {
@@ -96,10 +96,10 @@ export default function DeletionForm() {
};
// Countdown timer render function
const renderTime = () => {
const renderTime = ({ remainingTime }: { remainingTime: number }) => {
return (
<div class="timer">
<div class="value">{countDown().toFixed(0)}</div>
<div class="value">{remainingTime.toFixed(0)}</div>
</div>
);
};
@@ -136,7 +136,7 @@ export default function DeletionForm() {
loading()
? "bg-lavender"
: "bg-maroon hover:brightness-125 active:scale-90"
} flex w-36 justify-center rounded py-3 font-light text-white shadow-lg shadow-maroon transition-all duration-300 ease-out`}
} shadow-maroon flex w-36 justify-center rounded py-3 font-light text-white shadow-lg transition-all duration-300 ease-out`}
>
<Show when={loading()} fallback="Send Deletion Request">
<LoadingSpinner height={24} width={24} />
@@ -162,8 +162,8 @@ export default function DeletionForm() {
emailSent()
? "text-green"
: error() !== ""
? "text-red"
: "select-none opacity-0"
? "text-red"
: "opacity-0 select-none"
} mt-4 flex justify-center text-center italic transition-opacity duration-300 ease-in-out`}
>
<Show when={emailSent()} fallback={error()}>

View File

@@ -21,7 +21,7 @@ export default function EditCommentModal(props: EditCommentModalProps) {
};
return (
<div class="flex justify-center">
<div class="z-100 flex justify-center">
<div class="fixed top-48 h-fit w-11/12 sm:w-4/5 md:w-2/3">
<div
id="edit_prompt"

View File

@@ -7,7 +7,6 @@ import {
onCleanup
} from "solid-js";
import { createSignal } from "solid-js";
import { hapticFeedback } from "~/lib/client-utils";
const BarsContext = createContext<{
leftBarSize: Accessor<number>;
@@ -28,7 +27,8 @@ const BarsContext = createContext<{
setRightBarSize: () => {},
centerWidth: () => 0,
setCenterWidth: () => {},
leftBarVisible: () => true,
leftBarVisible: () =>
typeof window !== "undefined" ? window.innerWidth >= 768 : true,
setLeftBarVisible: () => {},
rightBarVisible: () => true,
setRightBarVisible: () => {},
@@ -45,12 +45,13 @@ export function BarsProvider(props: { children: any }) {
const [_rightBarNaturalSize, _setRightBarNaturalSize] = createSignal(0);
const [syncedBarSize, setSyncedBarSize] = createSignal(0);
const [centerWidth, setCenterWidth] = createSignal(0);
const [leftBarVisible, _setLeftBarVisible] = createSignal(true);
const [rightBarVisible, _setRightBarVisible] = createSignal(true);
const initialWindowWidth =
typeof window !== "undefined" ? window.innerWidth : 1024;
const isMobile = initialWindowWidth < 768;
const [leftBarVisible, setLeftBarVisible] = createSignal(!isMobile);
const [rightBarVisible, setRightBarVisible] = createSignal(true);
const [barsInitialized, setBarsInitialized] = createSignal(false);
const [windowWidth, setWindowWidth] = createSignal(
typeof window !== "undefined" ? window.innerWidth : 1024
);
const [windowWidth, setWindowWidth] = createSignal(initialWindowWidth);
let leftBarSized = false;
let rightBarSized = false;
@@ -82,6 +83,16 @@ export function BarsProvider(props: { children: any }) {
}
};
// Initialize immediately on mobile if left bar starts hidden
onMount(() => {
const isMobile = typeof window !== "undefined" && window.innerWidth < 768;
if (isMobile && !leftBarVisible()) {
// Skip waiting for left bar size on mobile when it starts hidden
leftBarSized = true;
checkAndSync();
}
});
const wrappedSetRightBarSize = (size: number) => {
if (!barsInitialized()) {
// Before initialization, capture natural size
@@ -124,16 +135,6 @@ export function BarsProvider(props: { children: any }) {
return barsInitialized() ? syncedBarSize() : naturalSize;
});
// Wrap visibility setters with haptic feedback
const setLeftBarVisible = (visible: boolean) => {
hapticFeedback(50);
_setLeftBarVisible(visible);
};
const setRightBarVisible = (visible: boolean) => {
hapticFeedback(50);
_setRightBarVisible(visible);
};
return (
<BarsContext.Provider
value={{

View File

@@ -23,16 +23,6 @@ export async function safeFetch(
}
}
/**
* Triggers haptic feedback on mobile devices
* @param duration - Duration in milliseconds (default 50ms for a light tap)
*/
export function hapticFeedback(duration: number = 50) {
if (typeof window !== "undefined" && "vibrate" in navigator) {
navigator.vibrate(duration);
}
}
/**
* Inserts soft hyphens (&shy;) into long words to enable manual hyphenation
* Works with Typewriter component since it uses actual characters

View File

@@ -183,10 +183,10 @@ export default function ContactPage() {
);
};
const renderTime = (time: number) => {
const renderTime = ({ remainingTime }: { remainingTime: number }) => {
return (
<div class="timer">
<div class="value">{time.toFixed(0)}</div>
<div class="value">{remainingTime.toFixed(0)}</div>
</div>
);
};

View File

@@ -42,6 +42,9 @@ export default function Home() {
here (github).
</a>
</Typewriter>
<Typewriter speed={100} keepAlive={2000}>
If you want to get in touch, check to side bar for various links.
</Typewriter>
<div class="pt-8 text-center">
<div class="pb-4">Some of my recent projects:</div>
<div class="flex flex-col items-center gap-2 2xl:flex-row 2xl:items-start 2xl:justify-center">

View File

@@ -272,10 +272,10 @@ export default function LoginPage() {
};
// Countdown timer render function
const renderTime = () => {
const renderTime = ({ remainingTime }: { remainingTime: number }) => {
return (
<div class="timer">
<div class="value">{countDown().toFixed(0)}</div>
<div class="value">{remainingTime.toFixed(0)}</div>
</div>
);
};

View File

@@ -181,8 +181,8 @@ export default function PasswordResetPage() {
name="description"
content="Set a new password for your account to regain access to your profile and personalized features."
/>
<div class="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800">
<div class="pt-24 text-center text-xl font-semibold text-slate-800 dark:text-slate-100">
<div>
<div class="pt-24 text-center text-xl font-semibold">
Set New Password
</div>
@@ -277,8 +277,8 @@ export default function PasswordResetPage() {
class={`${
passwordChangeLoading() || !passwordsMatch()
? "cursor-not-allowed bg-zinc-400"
: "bg-blue-400 hover:bg-blue-500 active:scale-90 dark:bg-blue-600 dark:hover:bg-blue-700"
} my-6 flex justify-center rounded px-4 py-2 font-medium text-white transition-all duration-300 ease-out`}
: "bg-blue hover:brightness-125 active:scale-90"
} my-6 flex justify-center rounded px-4 py-2 text-base font-medium transition-all duration-300 ease-out`}
>
{passwordChangeLoading() ? "Setting..." : "Set New Password"}
</button>
@@ -303,7 +303,7 @@ export default function PasswordResetPage() {
{/* Error Message */}
<Show when={error() && !showRequestNewEmail()}>
<div class="mt-4 flex justify-center">
<div class="text-sm text-red-500 italic">{error()}</div>
<div class="text-red text-sm italic">{error()}</div>
</div>
</Show>
@@ -311,11 +311,11 @@ export default function PasswordResetPage() {
<div
class={`${
showRequestNewEmail() ? "" : "opacity-0 select-none"
} flex justify-center px-4 text-red-500 italic transition-opacity duration-300 ease-in-out`}
} text-red flex justify-center px-4 italic transition-opacity duration-300 ease-in-out`}
>
Token has expired, request a new one{" "}
<A
class="pl-1 text-blue-500 underline underline-offset-4 hover:text-blue-400"
class="text-blue pl-1 underline underline-offset-4 hover:brightness-125"
href="/login/request-password-reset"
>
here
@@ -327,7 +327,7 @@ export default function PasswordResetPage() {
<div class="mt-6 flex justify-center">
<A
href="/login"
class="text-blue-500 underline underline-offset-4 transition-colors hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300"
class="text-blue underline underline-offset-4 transition-colors hover:brightness-125"
>
Back to Login
</A>

View File

@@ -111,10 +111,10 @@ export default function RequestPasswordResetPage() {
}
};
const renderTime = () => {
const renderTime = ({ remainingTime }: { remainingTime: number }) => {
return (
<div class="timer">
<div class="value">{countDown().toFixed(0)}</div>
<div class="value">{remainingTime.toFixed(0)}</div>
</div>
);
};