Enable 'strictNullChecks'

This commit is contained in:
0x1eef 2024-05-01 12:35:56 -03:00
parent 3a9d5ee022
commit eedf2a9c8a
9 changed files with 56 additions and 38 deletions

View file

@ -22,7 +22,7 @@ export function AudioControl({
onStatusChange = () => null, onStatusChange = () => null,
}: Props) { }: Props) {
const [enabled, setEnabled] = useState<boolean>(false); const [enabled, setEnabled] = useState<boolean>(false);
const [audioStatus, setAudioStatus] = useState<TAudioStatus>(null); const [audioStatus, setAudioStatus] = useState<Maybe<TAudioStatus>>(null);
const play = (audio: HTMLAudioElement) => audio.play().catch(() => null); const play = (audio: HTMLAudioElement) => audio.play().catch(() => null);
const pause = (audio: HTMLAudioElement) => audio.pause(); const pause = (audio: HTMLAudioElement) => audio.pause();
@ -69,22 +69,26 @@ export function AudioControl({
}, [enabled, ayah?.id]); }, [enabled, ayah?.id]);
useEffect(() => { useEffect(() => {
onStatusChange(audioStatus, [ if (audioStatus) {
() => setEnabled(true), onStatusChange(audioStatus, [
() => setEnabled(false), () => setEnabled(true),
]); () => setEnabled(false),
]);
}
}, [audioStatus]); }, [audioStatus]);
if (hidden) {
return null;
}
return ( return (
!hidden && ( <>
<> {enabled && (
{enabled && ( <SoundOnIcon onClick={() => [setEnabled(false), pause(audio)]} />
<SoundOnIcon onClick={() => [setEnabled(false), pause(audio)]} /> )}
)} {!enabled && (
{!enabled && ( <SoundOffIcon onClick={() => [setEnabled(true), play(audio)]} />
<SoundOffIcon onClick={() => [setEnabled(true), play(audio)]} /> )}
)} </>
</>
)
); );
} }

View file

@ -17,7 +17,7 @@ export function LanguageSelect({ locale }: Props) {
new RegExp(`^/${locale}/`), new RegExp(`^/${locale}/`),
`/${newLocale}/`, `/${newLocale}/`,
); );
content.classList.add("invisible"); content?.classList?.add("invisible");
location.replace(path); location.replace(path);
}} }}
> >

View file

@ -1,6 +1,8 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import classNames from "classnames"; import classNames from "classnames";
type Maybe<T> = T | null | undefined;
type Props = { type Props = {
value: string; value: string;
children: JSX.Element[]; children: JSX.Element[];
@ -15,7 +17,7 @@ export function Select({
className, className,
}: Props) { }: Props) {
const [open, setOpen] = useState<boolean>(false); const [open, setOpen] = useState<boolean>(false);
const [selected, setSelected] = useState<JSX.Element>( const [selected, setSelected] = useState<Maybe<JSX.Element>>(
query(option, { within }), query(option, { within }),
); );
const close = () => setOpen(false); const close = () => setOpen(false);
@ -31,7 +33,7 @@ export function Select({
className="selected" className="selected"
onClick={e => [e.stopPropagation(), setOpen(true)]} onClick={e => [e.stopPropagation(), setOpen(true)]}
> >
{selected.props.children} {selected?.props?.children}
</span> </span>
<div className="br" /> <div className="br" />
<ul hidden={!open}> <ul hidden={!open}>
@ -46,10 +48,12 @@ export function Select({
const target = e.target as HTMLLIElement; const target = e.target as HTMLLIElement;
const value = const value =
target.getAttribute("data-value") || target.getAttribute("data-value") ||
target.parentElement.getAttribute("data-value"); target.parentElement?.getAttribute("data-value");
const option: JSX.Element = query(value, { within }); const option: Maybe<JSX.Element> = query(value, { within });
onChange(option); if (option) {
setSelected(option); onChange(option);
setSelected(option);
}
setOpen(false); setOpen(false);
}} }}
> >
@ -63,9 +67,12 @@ export function Select({
} }
function query( function query(
option: string, option: Maybe<string>,
options: { within: JSX.Element[] }, options: { within: JSX.Element[] },
): JSX.Element { ): Maybe<JSX.Element> {
if (!option) {
return null;
}
const { within } = options; const { within } = options;
return within.find(({ props: { value } }) => option === value); return within.find(({ props: { value } }) => option === value);
} }

View file

@ -22,7 +22,7 @@ export function Stream({
t, t,
}: Props) { }: Props) {
const className = endOfStream || isPaused ? ["scroll-y"] : []; const className = endOfStream || isPaused ? ["scroll-y"] : [];
const ref = useRef<HTMLUListElement>(); const ref = useRef<HTMLUListElement>(null);
const ul = useMemo<JSX.Element>(() => { const ul = useMemo<JSX.Element>(() => {
const ltr = locale === "en"; const ltr = locale === "en";
const rtl = locale === "ar"; const rtl = locale === "ar";

View file

@ -17,12 +17,12 @@ type Props = {
export function SurahIndex({ appVersion, locale, surahs, t }: Props) { export function SurahIndex({ appVersion, locale, surahs, t }: Props) {
const [theme, setTheme] = useTheme(); const [theme, setTheme] = useTheme();
const [index, setIndex] = useState<Surah[]>(surahs); const [index, setIndex] = useState<Surah[]>(surahs);
const ref = useRef<HTMLDivElement>(); const ref = useRef<HTMLDivElement>(null);
const ltr = locale === "en"; const ltr = locale === "en";
useEffect(() => { useEffect(() => {
const div = ref.current; const div = ref.current;
if (ref.current) { if (div) {
div.classList.remove("invisible"); div.classList.remove("invisible");
} }
}, [ref.current, theme]); }, [ref.current, theme]);

View file

@ -25,11 +25,11 @@ type Props = {
export function SurahStream({ surah, locale, t }: Props) { export function SurahStream({ surah, locale, t }: Props) {
const [stream, setStream] = useState<TAyat>([]); const [stream, setStream] = useState<TAyat>([]);
const [isPaused, setIsPaused] = useState<boolean>(false); const [isPaused, setIsPaused] = useState<boolean>(false);
const [audioStatus, setAudioStatus] = useState<TAudioStatus>(null); const [audioStatus, setAudioStatus] = useState<Maybe<TAudioStatus>>(null);
const [endOfStream, setEndOfStream] = useState<boolean>(false); const [endOfStream, setEndOfStream] = useState<boolean>(false);
const [theme, setTheme] = useTheme(); const [theme, setTheme] = useTheme();
const [ms, setMs] = useState<number | null>(null); const [ms, setMs] = useState<number | null>(null);
const ref = useRef<HTMLDivElement>(); const ref = useRef<HTMLDivElement>(null);
const audio = useMemo(() => new Audio(), []); const audio = useMemo(() => new Audio(), []);
const readyToRender = stream.length > 0; const readyToRender = stream.length > 0;
const ayah: Maybe<Ayah> = stream[stream.length - 1]; const ayah: Maybe<Ayah> = stream[stream.length - 1];

View file

@ -32,7 +32,7 @@ export function Timer({
}, [ayah?.id]); }, [ayah?.id]);
useEffect(() => { useEffect(() => {
if (!ayah) { if (!ayah || !ms) {
return; return;
} else if (isStalled || isPaused) { } else if (isStalled || isPaused) {
/* no-op */ /* no-op */
@ -44,13 +44,16 @@ export function Timer({
} }
}, [isStalled, isPaused, ms]); }, [isStalled, isPaused, ms]);
if (isStalled) {
return null;
}
return ( return (
!isStalled && ( <div className="timer text-base w-10 flex justify-end">
<div className="timer text-base w-10 flex justify-end"> {ms &&
{ms / 1000 <= 0 (ms / 1000 <= 0
? formatNumber(locale, 0) ? formatNumber(locale, 0)
: formatNumber(locale, ms / 1000)} : formatNumber(locale, ms / 1000))}
</div> </div>
)
); );
} }

View file

@ -13,6 +13,11 @@ import { SurahIndex } from "~/components/SurahIndex";
(e: TSurah) => new Surah(e), (e: TSurah) => new Surah(e),
); );
ReactDOM.createRoot(root).render( ReactDOM.createRoot(root).render(
<SurahIndex appVersion={appVersion} locale={locale} surahs={surahs} t={t} />, <SurahIndex
appVersion={appVersion}
locale={locale}
surahs={surahs}
t={t}
/>,
); );
})(); })();

View file

@ -1,7 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"strict": true, "strict": true,
"strictNullChecks": false,
"module": "commonjs", "module": "commonjs",
"target": "ES2020", "target": "ES2020",
"noImplicitAny": true, "noImplicitAny": true,