-
Notifications
You must be signed in to change notification settings - Fork 1k
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
5 changed files
with
393 additions
and
167 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
type TypeofType = | ||
| 'bigint' | ||
| 'boolean' | ||
| 'function' | ||
| 'number' | ||
| 'object' | ||
| 'string' | ||
| 'undefined' | ||
|
||
type TypeCheckFn = (thing: unknown) => boolean | ||
|
||
|
||
/** | ||
* Curried function for creating typeof checker functions. | ||
* @param {string} type The type to check against (eg 'string', 'number') | ||
* @param {function} [secondaryTest] Optional additional test function to run in cases where a type match isn't always a sure indicator. | ||
* @returns {boolean} Whether the value matches the type | ||
*/ | ||
const isTypeof = | ||
<T>(type: TypeofType, secondaryTest?: TypeCheckFn) => | ||
(thing: unknown): thing is T => { | ||
const matches = typeof thing === type | ||
if (matches && secondaryTest) return secondaryTest(thing) | ||
return matches | ||
} | ||
|
||
export const isFunction = isTypeof<(...args: unknown[]) => unknown>('function') |
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,86 @@ | ||
import { useTranslate } from '@portal/hooks'; | ||
import { isFunction } from './type-helpers' | ||
import { FC } from 'react' | ||
import { useEffect, useState } from 'react' | ||
import React = require('react'); | ||
|
||
export const MIN_LOADER_MS = 1250 | ||
export const DEFAULT_TIMEOUT = 1000 | ||
|
||
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)) | ||
|
||
/** | ||
* Evaluate a check function will eventually resolve to `true` | ||
* | ||
* If check is initially true, immediatly return `isTrue` | ||
* If check is initially false and becomes true, return true after `timeoutMs` | ||
*/ | ||
export const useThrottledCheck = ( | ||
check: () => boolean, | ||
timeoutMs = DEFAULT_TIMEOUT, | ||
) => { | ||
const [isTrue, setIsTrue] = useState(() => check()) | ||
|
||
useEffect(() => { | ||
const doCheck = async (tries = 0) => { | ||
const waitMs = 250, | ||
waitedMs = tries * waitMs | ||
|
||
if (check()) { | ||
const debouncedDelay = | ||
waitedMs < timeoutMs ? timeoutMs - (waitedMs % timeoutMs) : 0 | ||
|
||
setTimeout(() => setIsTrue(true), debouncedDelay) | ||
return | ||
} | ||
|
||
await sleep(waitMs) | ||
|
||
doCheck(tries + 1) | ||
} | ||
|
||
if (!isTrue) { | ||
doCheck() | ||
} | ||
}, [check, isTrue]) | ||
|
||
return isTrue | ||
} | ||
|
||
/** | ||
* Show a loading spinner if XRPL isn't loaded yet by | ||
* waiting at least MIN_LOADER_MS before rendering children | ||
* in order to make the visual loading transition smooth | ||
* | ||
* e.g. if xrpl loads after 500ms, wait | ||
* another MIN_LOADER_MS - 500ms before rendering children | ||
* | ||
* @param {function} testCheck for testing only, a check function to use | ||
*/ | ||
export const XRPLGuard: FC<{ testCheck?: () => boolean, children }> = ({ | ||
testCheck, | ||
children, | ||
}) => { | ||
|
||
const { translate } = useTranslate(); | ||
const isXRPLLoaded = useThrottledCheck( | ||
// @ts-expect-error - xrpl is added via a script tag (TODO: Directly import when xrpl.js 3.0 is released) | ||
testCheck ?? (() => typeof xrpl === 'object'), | ||
MIN_LOADER_MS, | ||
) | ||
|
||
return ( | ||
<> | ||
{isXRPLLoaded ? ( | ||
isFunction(children) ? ( | ||
children() | ||
) : ( | ||
children | ||
) | ||
) : ( | ||
<div id="loader"> | ||
<img alt="(loading)" className="throbber" src="/img/xrp-loader-96.png" />{translate("Loading...")} | ||
</div>)} | ||
</> | ||
) | ||
} |
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,27 @@ | ||
type TypeofType = | ||
| 'bigint' | ||
| 'boolean' | ||
| 'function' | ||
| 'number' | ||
| 'object' | ||
| 'string' | ||
| 'undefined' | ||
|
||
type TypeCheckFn = (thing: unknown) => boolean | ||
|
||
|
||
/** | ||
* Curried function for creating typeof checker functions. | ||
* @param {string} type The type to check against (eg 'string', 'number') | ||
* @param {function} [secondaryTest] Optional additional test function to run in cases where a type match isn't always a sure indicator. | ||
* @returns {boolean} Whether the value matches the type | ||
*/ | ||
const isTypeof = | ||
<T>(type: TypeofType, secondaryTest?: TypeCheckFn) => | ||
(thing: unknown): thing is T => { | ||
const matches = typeof thing === type | ||
if (matches && secondaryTest) return secondaryTest(thing) | ||
return matches | ||
} | ||
|
||
export const isFunction = isTypeof<(...args: unknown[]) => unknown>('function') |
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,86 @@ | ||
import { useTranslate } from '@portal/hooks'; | ||
import { isFunction } from './type-helpers' | ||
import { FC } from 'react' | ||
import { useEffect, useState } from 'react' | ||
import React = require('react'); | ||
|
||
export const MIN_LOADER_MS = 1250 | ||
export const DEFAULT_TIMEOUT = 1000 | ||
|
||
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)) | ||
|
||
/** | ||
* Evaluate a check function will eventually resolve to `true` | ||
* | ||
* If check is initially true, immediatly return `isTrue` | ||
* If check is initially false and becomes true, return true after `timeoutMs` | ||
*/ | ||
export const useThrottledCheck = ( | ||
check: () => boolean, | ||
timeoutMs = DEFAULT_TIMEOUT, | ||
) => { | ||
const [isTrue, setIsTrue] = useState(() => check()) | ||
|
||
useEffect(() => { | ||
const doCheck = async (tries = 0) => { | ||
const waitMs = 250, | ||
waitedMs = tries * waitMs | ||
|
||
if (check()) { | ||
const debouncedDelay = | ||
waitedMs < timeoutMs ? timeoutMs - (waitedMs % timeoutMs) : 0 | ||
|
||
setTimeout(() => setIsTrue(true), debouncedDelay) | ||
return | ||
} | ||
|
||
await sleep(waitMs) | ||
|
||
doCheck(tries + 1) | ||
} | ||
|
||
if (!isTrue) { | ||
doCheck() | ||
} | ||
}, [check, isTrue]) | ||
|
||
return isTrue | ||
} | ||
|
||
/** | ||
* Show a loading spinner if XRPL isn't loaded yet by | ||
* waiting at least MIN_LOADER_MS before rendering children | ||
* in order to make the visual loading transition smooth | ||
* | ||
* e.g. if xrpl loads after 500ms, wait | ||
* another MIN_LOADER_MS - 500ms before rendering children | ||
* | ||
* @param {function} testCheck for testing only, a check function to use | ||
*/ | ||
export const XRPLGuard: FC<{ testCheck?: () => boolean, children }> = ({ | ||
testCheck, | ||
children, | ||
}) => { | ||
|
||
const { translate } = useTranslate(); | ||
const isXRPLLoaded = useThrottledCheck( | ||
// @ts-expect-error - xrpl is added via a script tag (TODO: Directly import when xrpl.js 3.0 is released) | ||
testCheck ?? (() => typeof xrpl === 'object'), | ||
MIN_LOADER_MS, | ||
) | ||
|
||
return ( | ||
<> | ||
{isXRPLLoaded ? ( | ||
isFunction(children) ? ( | ||
children() | ||
) : ( | ||
children | ||
) | ||
) : ( | ||
<div id="loader"> | ||
<img alt="(loading)" className="throbber" src="/img/xrp-loader-96.png" />{translate("Loading...")} | ||
</div>)} | ||
</> | ||
) | ||
} |
Oops, something went wrong.