fixes with countdown
This commit is contained in:
@@ -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`
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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()}>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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={{
|
||||
|
||||
@@ -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 (­) into long words to enable manual hyphenation
|
||||
* Works with Typewriter component since it uses actual characters
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user