-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
326 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* @license Copyright 2024 w3ux authors & contributors | ||
SPDX-License-Identifier: GPL-3.0-only */ | ||
|
||
import type { TimeleftDuration } from "@w3ux/types"; | ||
|
||
export const defaultDuration: TimeleftDuration = { | ||
days: 0, | ||
hours: 0, | ||
minutes: 0, | ||
seconds: 0, | ||
lastMinute: false, | ||
}; | ||
|
||
export const defaultRefreshInterval = 60; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
/* @license Copyright 2024 w3ux authors & contributors | ||
SPDX-License-Identifier: GPL-3.0-only */ | ||
|
||
import { setStateWithRef } from "@w3ux/utils"; | ||
import { useEffect, useRef, useState } from "react"; | ||
import type { | ||
TimeLeftAll, | ||
TimeLeftRaw, | ||
TimeleftDuration, | ||
UseTimeleftProps, | ||
} from "@w3ux/types"; | ||
import { getDuration } from "../util"; | ||
|
||
export const useTimeLeft = (props?: UseTimeleftProps) => { | ||
const depsTimeleft = props?.depsTimeleft || []; | ||
const depsFormat = props?.depsFormat || []; | ||
|
||
// check whether timeleft is within a minute of finishing. | ||
const inLastHour = () => { | ||
const { days, hours } = getDuration(toRef.current); | ||
return !days && !hours; | ||
}; | ||
|
||
// get the amount of seconds left if timeleft is in the last minute. | ||
const lastMinuteCountdown = () => { | ||
const { seconds } = getDuration(toRef.current); | ||
if (!inLastHour()) { | ||
return 60; | ||
} | ||
return seconds; | ||
}; | ||
|
||
// calculate resulting timeleft object from latest duration. | ||
const getTimeleft = (c?: TimeleftDuration): TimeLeftAll => { | ||
const { days, hours, minutes, seconds } = c || getDuration(toRef.current); | ||
const raw: TimeLeftRaw = { | ||
days, | ||
hours, | ||
minutes, | ||
}; | ||
if (!days && !hours) { | ||
raw.seconds = seconds; | ||
} | ||
return { | ||
raw, | ||
}; | ||
}; | ||
|
||
// the end time as a date. | ||
const [to, setTo] = useState<Date | null>(null); | ||
const toRef = useRef(to); | ||
|
||
// resulting timeleft object to be returned. | ||
const [timeleft, setTimeleft] = useState<TimeLeftAll>(getTimeleft()); | ||
|
||
// timeleft refresh intervals. | ||
const [minInterval, setMinInterval] = useState< | ||
ReturnType<typeof setInterval> | undefined | ||
>(undefined); | ||
const minIntervalRef = useRef(minInterval); | ||
|
||
const [secInterval, setSecInterval] = useState< | ||
ReturnType<typeof setInterval> | undefined | ||
>(undefined); | ||
const secIntervalRef = useRef(secInterval); | ||
|
||
// refresh effects. | ||
useEffect(() => { | ||
setTimeleft(getTimeleft()); | ||
if (inLastHour()) { | ||
// refresh timeleft every second. | ||
if (!secIntervalRef.current) { | ||
const interval = setInterval(() => { | ||
if (!inLastHour()) { | ||
clearInterval(secIntervalRef.current); | ||
setStateWithRef(undefined, setSecInterval, secIntervalRef); | ||
} | ||
setTimeleft(getTimeleft()); | ||
}, 1000); | ||
|
||
setStateWithRef(interval, setSecInterval, secIntervalRef); | ||
} | ||
} | ||
// refresh timeleft every minute. | ||
else if (!minIntervalRef.current) { | ||
const interval = setInterval(() => { | ||
if (inLastHour()) { | ||
clearInterval(minIntervalRef.current); | ||
setStateWithRef(undefined, setMinInterval, minIntervalRef); | ||
} | ||
setTimeleft(getTimeleft()); | ||
}, 60000); | ||
setStateWithRef(interval, setMinInterval, minIntervalRef); | ||
} | ||
}, [to, inLastHour(), lastMinuteCountdown(), ...depsTimeleft]); | ||
|
||
// re-render the timeleft upon formatting changes. | ||
useEffect(() => { | ||
setTimeleft(getTimeleft()); | ||
}, [...depsFormat]); | ||
|
||
// clear intervals on unmount | ||
useEffect( | ||
() => () => { | ||
clearInterval(minInterval); | ||
clearInterval(secInterval); | ||
}, | ||
[] | ||
); | ||
|
||
// Set the end time and calculate timeleft. | ||
const setFromNow = (dateFrom: Date, dateTo: Date) => { | ||
setTimeleft(getTimeleft(getDuration(dateFrom))); | ||
setStateWithRef(dateTo, setTo, toRef); | ||
}; | ||
|
||
return { | ||
setFromNow, | ||
timeleft, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* @license Copyright 2024 w3ux authors & contributors | ||
SPDX-License-Identifier: GPL-3.0-only */ | ||
|
||
import { TimeleftDuration } from "@w3ux/types"; | ||
import { differenceInDays, getUnixTime, intervalToDuration } from "date-fns"; | ||
import { defaultDuration } from "./useTimeLeft/defaults"; | ||
|
||
// calculates the current timeleft duration. | ||
export const getDuration = (toDate: Date | null): TimeleftDuration => { | ||
if (!toDate) { | ||
return defaultDuration; | ||
} | ||
if (getUnixTime(toDate) <= getUnixTime(new Date())) { | ||
return defaultDuration; | ||
} | ||
|
||
toDate.setSeconds(toDate.getSeconds()); | ||
const d = intervalToDuration({ | ||
start: Date.now(), | ||
end: toDate, | ||
}); | ||
|
||
const days = differenceInDays(toDate, Date.now()); | ||
const hours = d?.hours || 0; | ||
const minutes = d?.minutes || 0; | ||
const seconds = d?.seconds || 0; | ||
const lastHour = days === 0 && hours === 0; | ||
const lastMinute = lastHour && minutes === 0; | ||
|
||
return { | ||
days, | ||
hours, | ||
minutes, | ||
seconds, | ||
lastMinute, | ||
}; | ||
}; | ||
|
||
// Helper: Adds `seconds` to the current time and returns the resulting date. | ||
export const secondsFromNow = (seconds: number): Date => { | ||
const end = new Date(); | ||
end.setSeconds(end.getSeconds() + seconds); | ||
return end; | ||
}; | ||
|
||
// Helper: Calculates the duration between the current time and the provided date. | ||
export const getDurationFromNow = (toDate: Date | null): TimeleftDuration => { | ||
if (!toDate) { | ||
return defaultDuration; | ||
} | ||
if (getUnixTime(toDate) <= getUnixTime(new Date())) { | ||
return defaultDuration; | ||
} | ||
|
||
toDate.setSeconds(toDate.getSeconds()); | ||
const d = intervalToDuration({ | ||
start: Date.now(), | ||
end: toDate, | ||
}); | ||
|
||
const days = differenceInDays(toDate, Date.now()); | ||
const hours = d?.hours || 0; | ||
const minutes = d?.minutes || 0; | ||
const seconds = d?.seconds || 0; | ||
const lastHour = days === 0 && hours === 0; | ||
const lastMinute = lastHour && minutes === 0; | ||
|
||
return { | ||
days, | ||
hours, | ||
minutes, | ||
seconds, | ||
lastMinute, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { CSSProperties, ReactNode } from "react"; | ||
|
||
// A general purpose sync status, tracking whether an item is not synced, in progress, or completed | ||
// syncing. Useful for tracking async function progress, used as React refs, etc. | ||
export type Sync = "synced" | "unsynced" | "syncing"; | ||
|
||
// A general purpose medium components are being displayed on. | ||
export type DisplayFor = "default" | "modal" | "canvas" | "card"; | ||
|
||
// A generic type for basic React components. We assume the component may have children and styling | ||
// applied to it. | ||
export interface ComponentBase { | ||
// passing react children. | ||
children?: ReactNode; | ||
// passing custom styling. | ||
style?: CSSProperties; | ||
} | ||
|
||
// An extension of the ComponentBase type with an additional className property. | ||
export type ComponentBaseWithClassName = ComponentBase & { | ||
// passing a className string. | ||
className?: string; | ||
}; | ||
|
||
// A funtion with no arguments and no return value. | ||
export type VoidFn = () => void; | ||
|
||
// A JSON value: string, number, object, array, true, false, null. | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export type AnyJson = any; | ||
|
||
// A function definition. | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export type AnyFunction = any; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,5 @@ | ||
/* @license Copyright 2024 w3ux authors & contributors | ||
SPDX-License-Identifier: GPL-3.0-only */ | ||
|
||
import { CSSProperties, ReactNode } from "react"; | ||
|
||
// A general purpose sync status, tracking whether an item is not synced, in progress, or completed | ||
// syncing. Useful for tracking async function progress, used as React refs, etc. | ||
export type Sync = "synced" | "unsynced" | "syncing"; | ||
|
||
// A general purpose medium components are being displayed on. | ||
export type DisplayFor = "default" | "modal" | "canvas" | "card"; | ||
|
||
// A generic type for basic React components. We assume the component may have children and styling | ||
// applied to it. | ||
export interface ComponentBase { | ||
// passing react children. | ||
children?: ReactNode; | ||
// passing custom styling. | ||
style?: CSSProperties; | ||
} | ||
|
||
// An extension of the ComponentBase type with an additional className property. | ||
export type ComponentBaseWithClassName = ComponentBase & { | ||
// passing a className string. | ||
className?: string; | ||
}; | ||
|
||
// A funtion with no arguments and no return value. | ||
export type VoidFn = () => void; | ||
|
||
// A JSON value: string, number, object, array, true, false, null. | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export type AnyJson = any; | ||
|
||
// A function definition. | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export type AnyFunction = any; | ||
export * from "./common"; | ||
export * from "./time"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* @license Copyright 2024 w3ux authors & contributors | ||
SPDX-License-Identifier: GPL-3.0-only */ | ||
|
||
export interface UseTimeleftProps { | ||
// Dependencies to trigger re-calculation of timeleft. | ||
depsTimeleft: unknown[]; | ||
// Dependencies to trigger re-render of timeleft, e.g. if language switching occurs. | ||
depsFormat: unknown[]; | ||
} | ||
|
||
export interface TimeleftDuration { | ||
days: number; | ||
hours: number; | ||
minutes: number; | ||
seconds: number; | ||
lastMinute: boolean; | ||
} | ||
|
||
export interface TimeLeftRaw { | ||
days: number; | ||
hours: number; | ||
minutes: number; | ||
seconds?: number; | ||
} | ||
|
||
export interface TimeLeftFormatted { | ||
days: [number, string]; | ||
hours: [number, string]; | ||
minutes: [number, string]; | ||
seconds?: [number, string]; | ||
} | ||
|
||
export interface TimeLeftAll { | ||
raw: TimeLeftRaw; | ||
} | ||
|
||
export interface TimeleftHookProps { | ||
refreshInterval: number; | ||
} |
Oops, something went wrong.