Added endpoint for fetching server-side variables.

This commit is contained in:
Mike Cao 2022-08-01 23:04:47 -07:00
parent 68d35c0fc4
commit 50e491af06
8 changed files with 62 additions and 15 deletions

View File

@ -6,10 +6,15 @@ import { setItem } from 'lib/web';
import { REPO_URL, VERSION_CHECK } from 'lib/constants'; import { REPO_URL, VERSION_CHECK } from 'lib/constants';
import Button from './Button'; import Button from './Button';
import styles from './UpdateNotice.module.css'; import styles from './UpdateNotice.module.css';
import useUser from 'hooks/useUser';
import useConfig from 'hooks/useConfig';
export default function UpdateNotice() { export default function UpdateNotice() {
const { user } = useUser();
const { updatesDisabled } = useConfig();
const { latest, checked, hasUpdate, releaseUrl } = useStore(); const { latest, checked, hasUpdate, releaseUrl } = useStore();
const [dismissed, setDismissed] = useState(false); const [dismissed, setDismissed] = useState(false);
const allowCheck = user?.is_admin && !updatesDisabled;
const updateCheck = useCallback(() => { const updateCheck = useCallback(() => {
setItem(VERSION_CHECK, { version: latest, time: Date.now() }); setItem(VERSION_CHECK, { version: latest, time: Date.now() });
@ -27,12 +32,12 @@ export default function UpdateNotice() {
} }
useEffect(() => { useEffect(() => {
if (!checked) { if (!checked && allowCheck) {
checkVersion(); checkVersion();
} }
}, []); }, [checked]);
if (!hasUpdate || dismissed) { if (!hasUpdate || dismissed || !allowCheck) {
return null; return null;
} }

View File

@ -5,9 +5,11 @@ import Link from 'components/common/Link';
import styles from './Footer.module.css'; import styles from './Footer.module.css';
import useStore from 'store/version'; import useStore from 'store/version';
import { HOMEPAGE_URL, REPO_URL } from 'lib/constants'; import { HOMEPAGE_URL, REPO_URL } from 'lib/constants';
import useConfig from 'hooks/useConfig';
export default function Footer() { export default function Footer() {
const { current } = useStore(); const { current } = useStore();
const { telemetryDisabled } = useConfig();
return ( return (
<footer className={classNames(styles.footer, 'row')}> <footer className={classNames(styles.footer, 'row')}>
@ -28,9 +30,7 @@ export default function Footer() {
<div className={classNames(styles.version, 'col-12 col-md-4')}> <div className={classNames(styles.version, 'col-12 col-md-4')}>
<Link href={REPO_URL}>{`v${current}`}</Link> <Link href={REPO_URL}>{`v${current}`}</Link>
</div> </div>
{!process.env.telemetryDisabled && ( {telemetryDisabled && <img src={`https://i.umami.is/a.png?v=${current}`} alt="" />}
<img src={`https://i.umami.is/a.png?v=${current}`} alt="" />
)}
</footer> </footer>
); );
} }

View File

@ -8,10 +8,10 @@ import ThemeButton from 'components/settings/ThemeButton';
import HamburgerButton from 'components/common/HamburgerButton'; import HamburgerButton from 'components/common/HamburgerButton';
import UpdateNotice from 'components/common/UpdateNotice'; import UpdateNotice from 'components/common/UpdateNotice';
import UserButton from 'components/settings/UserButton'; import UserButton from 'components/settings/UserButton';
import Logo from 'assets/logo.svg';
import styles from './Header.module.css';
import useUser from 'hooks/useUser'; import useUser from 'hooks/useUser';
import { HOMEPAGE_URL } from 'lib/constants'; import { HOMEPAGE_URL } from 'lib/constants';
import Logo from 'assets/logo.svg';
import styles from './Header.module.css';
export default function Header() { export default function Header() {
const { user } = useUser(); const { user } = useUser();
@ -19,7 +19,7 @@ export default function Header() {
return ( return (
<> <>
{user?.is_admin && !process.env.updatesDisabled && <UpdateNotice />} <UpdateNotice />
<header className={classNames(styles.header, 'row')}> <header className={classNames(styles.header, 'row')}>
<div className={styles.title}> <div className={styles.title}>
<Icon icon={<Logo />} size="large" className={styles.logo} /> <Icon icon={<Logo />} size="large" className={styles.logo} />

View File

@ -6,7 +6,7 @@ import useStore from 'store/app';
const selector = state => state.shareToken; const selector = state => state.shareToken;
function parseHeaders(headers = {}, { authToken, shareToken }) { function parseHeaders(headers, { authToken, shareToken }) {
if (authToken) { if (authToken) {
headers.authorization = `Bearer ${authToken}`; headers.authorization = `Bearer ${authToken}`;
} }
@ -25,7 +25,7 @@ export default function useApi() {
return { return {
get: useCallback( get: useCallback(
async (url, params, headers) => { async (url, params = {}, headers = {}) => {
return get( return get(
`${basePath}/api${url}`, `${basePath}/api${url}`,
params, params,
@ -36,7 +36,7 @@ export default function useApi() {
), ),
post: useCallback( post: useCallback(
async (url, params, headers) => { async (url, params = {}, headers = {}) => {
return post( return post(
`${basePath}/api${url}`, `${basePath}/api${url}`,
params, params,
@ -47,7 +47,7 @@ export default function useApi() {
), ),
put: useCallback( put: useCallback(
async (url, params, headers) => { async (url, params = {}, headers = {}) => {
return put( return put(
`${basePath}/api${url}`, `${basePath}/api${url}`,
params, params,
@ -58,7 +58,7 @@ export default function useApi() {
), ),
del: useCallback( del: useCallback(
async (url, params, headers) => { async (url, params = {}, headers = {}) => {
return del( return del(
`${basePath}/api${url}`, `${basePath}/api${url}`,
params, params,

24
hooks/useConfig.js Normal file
View File

@ -0,0 +1,24 @@
import { useEffect } from 'react';
import useStore, { setConfig } from 'store/app';
import useApi from 'hooks/useApi';
let fetched = false;
export default function useConfig() {
const { config } = useStore();
const { get } = useApi();
async function loadConfig() {
const { data } = await get('/config');
setConfig(data);
}
useEffect(() => {
if (!config && !fetched) {
fetched = true;
loadConfig();
}
}, []);
return config || {};
}

View File

@ -1,4 +1,3 @@
import React from 'react';
import Head from 'next/head'; import Head from 'next/head';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { IntlProvider } from 'react-intl'; import { IntlProvider } from 'react-intl';

14
pages/api/config.js Normal file
View File

@ -0,0 +1,14 @@
import { ok, methodNotAllowed } from 'lib/response';
export default async (req, res) => {
if (req.method === 'GET') {
return ok(res, {
basePath: process.env.BASE_PATH || '',
trackerScriptName: process.env.TRACKER_SCRIPT_NAME,
updatesDisabled: !!process.env.DISABLE_UPDATES,
telemetryDisabled: !!process.env.DISABLE_TELEMETRY,
});
}
return methodNotAllowed(res);
};

View File

@ -20,6 +20,7 @@ const initialState = {
dashboard: getItem(DASHBOARD_CONFIG) || defaultDashboardConfig, dashboard: getItem(DASHBOARD_CONFIG) || defaultDashboardConfig,
shareToken: null, shareToken: null,
user: null, user: null,
config: null,
}; };
const store = create(() => ({ ...initialState })); const store = create(() => ({ ...initialState }));
@ -45,4 +46,8 @@ export function setDashboard(dashboard) {
setItem(DASHBOARD_CONFIG, dashboard); setItem(DASHBOARD_CONFIG, dashboard);
} }
export function setConfig(config) {
store.setState({ config });
}
export default store; export default store;