2023-10-27 10:01:59 +00:00
|
|
|
import { createContext, type Dispatch, useEffect, useState, type SetStateAction } from 'react';
|
2023-05-09 00:37:23 +00:00
|
|
|
import { type Page } from '../basic/NavBar';
|
|
|
|
import { type OpenDialogs } from '../basic/MainDialogs';
|
2023-02-24 19:17:13 +00:00
|
|
|
|
2023-10-27 11:00:53 +00:00
|
|
|
import { Settings, type Version, type Origin, type Favorites } from '../models';
|
2023-02-24 19:17:13 +00:00
|
|
|
|
2023-10-27 10:01:59 +00:00
|
|
|
import { getClientVersion, getHost } from '../utils';
|
2023-02-24 19:17:13 +00:00
|
|
|
|
2023-10-27 10:01:59 +00:00
|
|
|
import defaultFederation from '../../static/federation.json';
|
2023-05-09 00:37:23 +00:00
|
|
|
import { createTheme, type Theme } from '@mui/material/styles';
|
2023-04-20 14:52:03 +00:00
|
|
|
import i18n from '../i18n/Web';
|
2023-02-24 19:17:13 +00:00
|
|
|
|
2023-10-27 10:01:59 +00:00
|
|
|
const getWindowSize = function (fontSize: number): { width: number; height: number } {
|
2023-02-24 19:17:13 +00:00
|
|
|
// returns window size in EM units
|
|
|
|
return {
|
|
|
|
width: window.innerWidth / fontSize,
|
|
|
|
height: window.innerHeight / fontSize,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
export interface SlideDirection {
|
|
|
|
in: 'left' | 'right' | undefined;
|
|
|
|
out: 'left' | 'right' | undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
export type TorStatus = 'NOTINIT' | 'STARTING' | '"Done"' | 'DONE';
|
|
|
|
|
2023-10-27 11:00:53 +00:00
|
|
|
export const isNativeRoboSats = !(window.NativeRobosats === undefined);
|
|
|
|
|
|
|
|
const pageFromPath = window.location.pathname.split('/')[1];
|
|
|
|
const isPagePathEmpty = pageFromPath === '';
|
|
|
|
const entryPage: Page = !isNativeRoboSats
|
|
|
|
? ((isPagePathEmpty ? 'robot' : pageFromPath) as Page)
|
|
|
|
: 'robot';
|
2023-02-24 19:17:13 +00:00
|
|
|
|
2024-01-02 17:13:20 +00:00
|
|
|
export const closeAll: OpenDialogs = {
|
2023-02-24 19:17:13 +00:00
|
|
|
more: false,
|
|
|
|
learn: false,
|
|
|
|
community: false,
|
|
|
|
info: false,
|
2024-01-02 17:13:20 +00:00
|
|
|
coordinator: '',
|
|
|
|
warning: false,
|
2023-02-24 19:17:13 +00:00
|
|
|
exchange: false,
|
|
|
|
client: false,
|
|
|
|
update: false,
|
|
|
|
profile: false,
|
|
|
|
};
|
|
|
|
|
2023-10-27 10:01:59 +00:00
|
|
|
const makeTheme = function (settings: Settings): Theme {
|
2023-04-20 14:52:03 +00:00
|
|
|
const theme: Theme = createTheme({
|
|
|
|
palette: {
|
|
|
|
mode: settings.mode,
|
|
|
|
background: {
|
|
|
|
default: settings.mode === 'dark' ? '#070707' : '#fff',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
typography: { fontSize: settings.fontSize },
|
|
|
|
});
|
|
|
|
|
|
|
|
return theme;
|
|
|
|
};
|
|
|
|
|
2023-10-27 10:01:59 +00:00
|
|
|
const getHostUrl = (network = 'mainnet'): string => {
|
|
|
|
let host = '';
|
|
|
|
let protocol = '';
|
|
|
|
if (window.NativeRobosats === undefined) {
|
|
|
|
host = getHost();
|
|
|
|
protocol = location.protocol;
|
|
|
|
} else {
|
|
|
|
host = defaultFederation.exp[network].Onion;
|
|
|
|
protocol = 'http:';
|
|
|
|
}
|
|
|
|
const hostUrl = `${protocol}//${host}`;
|
|
|
|
|
|
|
|
return hostUrl;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getOrigin = (network = 'mainnet'): Origin => {
|
2023-10-27 11:00:53 +00:00
|
|
|
const host = getHostUrl(network);
|
2023-10-27 10:01:59 +00:00
|
|
|
let origin: Origin = 'onion';
|
|
|
|
|
|
|
|
if (window.NativeRobosats !== undefined || host.includes('.onion')) {
|
|
|
|
origin = 'onion';
|
|
|
|
} else if (host.includes('i2p')) {
|
|
|
|
origin = 'i2p';
|
|
|
|
} else {
|
|
|
|
origin = 'clearnet';
|
|
|
|
}
|
|
|
|
|
|
|
|
return origin;
|
|
|
|
};
|
|
|
|
|
|
|
|
export interface WindowSize {
|
|
|
|
width: number;
|
|
|
|
height: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface UseAppStoreType {
|
|
|
|
theme?: Theme;
|
|
|
|
torStatus: TorStatus;
|
|
|
|
settings: Settings;
|
|
|
|
setSettings: Dispatch<SetStateAction<Settings>>;
|
|
|
|
page: Page;
|
|
|
|
setPage: Dispatch<SetStateAction<Page>>;
|
|
|
|
slideDirection: SlideDirection;
|
|
|
|
setSlideDirection: Dispatch<SetStateAction<SlideDirection>>;
|
|
|
|
navbarHeight: number;
|
|
|
|
open: OpenDialogs;
|
|
|
|
setOpen: Dispatch<SetStateAction<OpenDialogs>>;
|
|
|
|
windowSize?: WindowSize;
|
2024-01-02 17:13:20 +00:00
|
|
|
acknowledgedWarning: boolean;
|
|
|
|
setAcknowledgedWarning: Dispatch<SetStateAction<boolean>>;
|
2023-10-27 10:01:59 +00:00
|
|
|
clientVersion: {
|
|
|
|
semver: Version;
|
|
|
|
short: string;
|
|
|
|
long: string;
|
|
|
|
};
|
|
|
|
origin: Origin;
|
|
|
|
hostUrl: string;
|
|
|
|
fav: Favorites;
|
|
|
|
setFav: Dispatch<SetStateAction<Favorites>>;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const initialAppContext: UseAppStoreType = {
|
|
|
|
theme: undefined,
|
|
|
|
torStatus: 'NOTINIT',
|
|
|
|
settings: new Settings(),
|
|
|
|
setSettings: () => {},
|
|
|
|
page: entryPage,
|
|
|
|
setPage: () => {},
|
|
|
|
slideDirection: {
|
|
|
|
in: undefined,
|
|
|
|
out: undefined,
|
|
|
|
},
|
|
|
|
setSlideDirection: () => {},
|
|
|
|
navbarHeight: 2.5,
|
|
|
|
open: closeAll,
|
|
|
|
setOpen: () => {},
|
|
|
|
windowSize: undefined,
|
|
|
|
origin: getOrigin(),
|
|
|
|
hostUrl: getHostUrl(),
|
|
|
|
clientVersion: getClientVersion(),
|
2024-01-02 17:13:20 +00:00
|
|
|
acknowledgedWarning: false,
|
2023-10-27 10:01:59 +00:00
|
|
|
fav: { type: null, currency: 0, mode: 'fiat' },
|
|
|
|
setFav: () => {},
|
|
|
|
};
|
|
|
|
|
|
|
|
export const AppContext = createContext<UseAppStoreType>(initialAppContext);
|
|
|
|
|
|
|
|
export const useAppStore = (): UseAppStoreType => {
|
2023-04-20 14:52:03 +00:00
|
|
|
// State provided right at the top level of the app. A chaotic bucket of everything.
|
|
|
|
// Contains app-wide state and functions. Triggers re-renders on the full tree often.
|
|
|
|
|
2023-10-27 10:01:59 +00:00
|
|
|
// All app data structured
|
|
|
|
const navbarHeight = initialAppContext.navbarHeight;
|
|
|
|
const clientVersion = initialAppContext.clientVersion;
|
|
|
|
const hostUrl = initialAppContext.hostUrl;
|
|
|
|
const origin = initialAppContext.origin;
|
|
|
|
|
|
|
|
const [settings, setSettings] = useState<Settings>(initialAppContext.settings);
|
2023-04-24 14:47:34 +00:00
|
|
|
const [theme, setTheme] = useState<Theme>(() => {
|
|
|
|
return makeTheme(settings);
|
|
|
|
});
|
2023-10-27 10:01:59 +00:00
|
|
|
const [torStatus, setTorStatus] = useState<TorStatus>(initialAppContext.torStatus);
|
|
|
|
const [page, setPage] = useState<Page>(initialAppContext.page);
|
|
|
|
const [slideDirection, setSlideDirection] = useState<SlideDirection>(
|
|
|
|
initialAppContext.slideDirection,
|
|
|
|
);
|
|
|
|
const [open, setOpen] = useState<OpenDialogs>(initialAppContext.open);
|
|
|
|
const [windowSize, setWindowSize] = useState<WindowSize>(() =>
|
|
|
|
getWindowSize(theme.typography.fontSize),
|
|
|
|
);
|
|
|
|
const [fav, setFav] = useState<Favorites>(initialAppContext.fav);
|
2024-01-02 17:13:20 +00:00
|
|
|
const [acknowledgedWarning, setAcknowledgedWarning] = useState<boolean>(
|
|
|
|
initialAppContext.acknowledgedWarning,
|
|
|
|
);
|
2023-02-24 19:17:13 +00:00
|
|
|
|
2023-04-20 14:52:03 +00:00
|
|
|
useEffect(() => {
|
|
|
|
setTheme(makeTheme(settings));
|
2023-05-05 13:39:38 +00:00
|
|
|
}, [settings.fontSize, settings.mode, settings.lightQRs]);
|
2023-04-20 14:52:03 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
2023-10-27 10:01:59 +00:00
|
|
|
void i18n.changeLanguage(settings.language);
|
2023-04-20 14:52:03 +00:00
|
|
|
}, []);
|
2023-02-24 19:17:13 +00:00
|
|
|
|
2023-03-02 11:01:06 +00:00
|
|
|
useEffect(() => {
|
|
|
|
window.addEventListener('torStatus', (event) => {
|
2023-04-20 14:52:03 +00:00
|
|
|
// Trick to improve UX on Android webview: delay the "Connected to TOR" status by 5 secs to avoid long waits on the first request.
|
2023-05-09 00:37:23 +00:00
|
|
|
setTimeout(
|
|
|
|
() => {
|
|
|
|
setTorStatus(event?.detail);
|
|
|
|
},
|
|
|
|
event?.detail === '"Done"' ? 5000 : 0,
|
|
|
|
);
|
2023-03-02 11:01:06 +00:00
|
|
|
});
|
|
|
|
}, []);
|
|
|
|
|
2023-02-24 19:17:13 +00:00
|
|
|
useEffect(() => {
|
2023-10-27 10:01:59 +00:00
|
|
|
if (window !== undefined) {
|
2023-02-24 19:17:13 +00:00
|
|
|
window.addEventListener('resize', onResize);
|
|
|
|
}
|
|
|
|
|
|
|
|
return () => {
|
2023-10-27 10:01:59 +00:00
|
|
|
if (window !== undefined) {
|
2023-02-24 19:17:13 +00:00
|
|
|
window.removeEventListener('resize', onResize);
|
|
|
|
}
|
|
|
|
};
|
2023-10-27 10:01:59 +00:00
|
|
|
}, []);
|
2023-02-24 19:17:13 +00:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
setWindowSize(getWindowSize(theme.typography.fontSize));
|
|
|
|
}, [theme.typography.fontSize]);
|
|
|
|
|
2023-10-27 10:01:59 +00:00
|
|
|
const onResize = function (): void {
|
2023-02-24 19:17:13 +00:00
|
|
|
setWindowSize(getWindowSize(theme.typography.fontSize));
|
|
|
|
};
|
|
|
|
|
2023-04-20 14:52:03 +00:00
|
|
|
return {
|
|
|
|
theme,
|
|
|
|
torStatus,
|
|
|
|
settings,
|
|
|
|
setSettings,
|
|
|
|
page,
|
|
|
|
setPage,
|
|
|
|
slideDirection,
|
|
|
|
setSlideDirection,
|
|
|
|
navbarHeight,
|
|
|
|
open,
|
|
|
|
setOpen,
|
|
|
|
windowSize,
|
2023-10-27 10:01:59 +00:00
|
|
|
clientVersion,
|
2024-01-02 17:13:20 +00:00
|
|
|
acknowledgedWarning,
|
|
|
|
setAcknowledgedWarning,
|
2023-10-27 10:01:59 +00:00
|
|
|
hostUrl,
|
|
|
|
origin,
|
|
|
|
fav,
|
|
|
|
setFav,
|
2023-04-20 14:52:03 +00:00
|
|
|
};
|
2023-02-24 19:17:13 +00:00
|
|
|
};
|