Created share token hook.

This commit is contained in:
Mike Cao 2020-10-11 02:29:55 -07:00
parent 4119e80a9a
commit 5a73c224b7
22 changed files with 90 additions and 49 deletions

View File

@ -3,12 +3,15 @@ import { FormattedMessage } from 'react-intl';
import classNames from 'classnames'; import classNames from 'classnames';
import useFetch from 'hooks/useFetch'; import useFetch from 'hooks/useFetch';
import Dot from 'components/common/Dot'; import Dot from 'components/common/Dot';
import { TOKEN_HEADER } from 'lib/constants';
import useShareToken from 'hooks/useShareToken';
import styles from './ActiveUsers.module.css'; import styles from './ActiveUsers.module.css';
export default function ActiveUsers({ websiteId, token, className }) { export default function ActiveUsers({ websiteId, className }) {
const shareToken = useShareToken();
const { data } = useFetch(`/api/website/${websiteId}/active`, { const { data } = useFetch(`/api/website/${websiteId}/active`, {
params: { token },
interval: 60000, interval: 60000,
headers: { [TOKEN_HEADER]: shareToken?.token },
}); });
const count = useMemo(() => { const count = useMemo(() => {
return data?.[0]?.x || 0; return data?.[0]?.x || 0;

View File

@ -65,7 +65,7 @@ export default function BarChart({
} }
function renderYLabel(label) { function renderYLabel(label) {
return +label > 1 ? formatLongNumber(label) : label; return +label > 1000 ? formatLongNumber(label) : label;
} }
function renderTooltip(model) { function renderTooltip(model) {

View File

@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
import MetricsTable from './MetricsTable'; import MetricsTable from './MetricsTable';
import { browserFilter } from 'lib/filters'; import { browserFilter } from 'lib/filters';
export default function BrowsersTable({ websiteId, token, ...props }) { export default function BrowsersTable({ websiteId, ...props }) {
return ( return (
<MetricsTable <MetricsTable
{...props} {...props}
@ -11,7 +11,6 @@ export default function BrowsersTable({ websiteId, token, ...props }) {
type="browser" type="browser"
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />} metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
websiteId={websiteId} websiteId={websiteId}
token={token}
dataFilter={browserFilter} dataFilter={browserFilter}
/> />
); );

View File

@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl';
import useCountryNames from 'hooks/useCountryNames'; import useCountryNames from 'hooks/useCountryNames';
import useLocale from 'hooks/useLocale'; import useLocale from 'hooks/useLocale';
export default function CountriesTable({ websiteId, token, onDataLoad, ...props }) { export default function CountriesTable({ websiteId, onDataLoad, ...props }) {
const [locale] = useLocale(); const [locale] = useLocale();
const countryNames = useCountryNames(locale); const countryNames = useCountryNames(locale);
@ -20,7 +20,6 @@ export default function CountriesTable({ websiteId, token, onDataLoad, ...props
type="country" type="country"
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />} metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
websiteId={websiteId} websiteId={websiteId}
token={token}
onDataLoad={data => onDataLoad?.(percentFilter(data))} onDataLoad={data => onDataLoad?.(percentFilter(data))}
renderLabel={renderLabel} renderLabel={renderLabel}
/> />

View File

@ -4,7 +4,7 @@ import { deviceFilter } from 'lib/filters';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { getDeviceMessage } from 'components/messages'; import { getDeviceMessage } from 'components/messages';
export default function DevicesTable({ websiteId, token, ...props }) { export default function DevicesTable({ websiteId, ...props }) {
return ( return (
<MetricsTable <MetricsTable
{...props} {...props}
@ -12,7 +12,6 @@ export default function DevicesTable({ websiteId, token, ...props }) {
type="device" type="device"
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />} metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
websiteId={websiteId} websiteId={websiteId}
token={token}
dataFilter={deviceFilter} dataFilter={deviceFilter}
renderLabel={({ x }) => getDeviceMessage(x)} renderLabel={({ x }) => getDeviceMessage(x)}
/> />

View File

@ -6,13 +6,15 @@ import useFetch from 'hooks/useFetch';
import useDateRange from 'hooks/useDateRange'; import useDateRange from 'hooks/useDateRange';
import useTimezone from 'hooks/useTimezone'; import useTimezone from 'hooks/useTimezone';
import usePageQuery from 'hooks/usePageQuery'; import usePageQuery from 'hooks/usePageQuery';
import { EVENT_COLORS } from 'lib/constants'; import useShareToken from 'hooks/useShareToken';
import { EVENT_COLORS, TOKEN_HEADER } from 'lib/constants';
export default function EventsChart({ websiteId, className, token }) { export default function EventsChart({ websiteId, className, token }) {
const [dateRange] = useDateRange(websiteId); const [dateRange] = useDateRange(websiteId);
const { startDate, endDate, unit, modified } = dateRange; const { startDate, endDate, unit, modified } = dateRange;
const [timezone] = useTimezone(); const [timezone] = useTimezone();
const { query } = usePageQuery(); const { query } = usePageQuery();
const shareToken = useShareToken();
const { data } = useFetch( const { data } = useFetch(
`/api/website/${websiteId}/events`, `/api/website/${websiteId}/events`,
@ -25,6 +27,7 @@ export default function EventsChart({ websiteId, className, token }) {
url: query.url, url: query.url,
token, token,
}, },
headers: { [TOKEN_HEADER]: shareToken?.token },
}, },
[modified], [modified],
); );

View File

@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
import MetricsTable from './MetricsTable'; import MetricsTable from './MetricsTable';
import Tag from 'components/common/Tag'; import Tag from 'components/common/Tag';
export default function EventsTable({ websiteId, token, ...props }) { export default function EventsTable({ websiteId, ...props }) {
return ( return (
<MetricsTable <MetricsTable
{...props} {...props}
@ -11,7 +11,6 @@ export default function EventsTable({ websiteId, token, ...props }) {
type="event" type="event"
metric={<FormattedMessage id="metrics.actions" defaultMessage="Actions" />} metric={<FormattedMessage id="metrics.actions" defaultMessage="Actions" />}
websiteId={websiteId} websiteId={websiteId}
token={token}
renderLabel={({ x }) => <Label value={x} />} renderLabel={({ x }) => <Label value={x} />}
/> />
); );

View File

@ -5,12 +5,15 @@ import Loading from 'components/common/Loading';
import ErrorMessage from 'components/common/ErrorMessage'; import ErrorMessage from 'components/common/ErrorMessage';
import useFetch from 'hooks/useFetch'; import useFetch from 'hooks/useFetch';
import useDateRange from 'hooks/useDateRange'; import useDateRange from 'hooks/useDateRange';
import { formatShortTime, formatNumber, formatLongNumber } from 'lib/format';
import usePageQuery from 'hooks/usePageQuery'; import usePageQuery from 'hooks/usePageQuery';
import useShareToken from 'hooks/useShareToken';
import { formatShortTime, formatNumber, formatLongNumber } from 'lib/format';
import { TOKEN_HEADER } from 'lib/constants';
import MetricCard from './MetricCard'; import MetricCard from './MetricCard';
import styles from './MetricsBar.module.css'; import styles from './MetricsBar.module.css';
export default function MetricsBar({ websiteId, token, className }) { export default function MetricsBar({ websiteId, className }) {
const shareToken = useShareToken();
const [dateRange] = useDateRange(websiteId); const [dateRange] = useDateRange(websiteId);
const { startDate, endDate, modified } = dateRange; const { startDate, endDate, modified } = dateRange;
const [format, setFormat] = useState(true); const [format, setFormat] = useState(true);
@ -25,8 +28,8 @@ export default function MetricsBar({ websiteId, token, className }) {
start_at: +startDate, start_at: +startDate,
end_at: +endDate, end_at: +endDate,
url, url,
token,
}, },
headers: { [TOKEN_HEADER]: shareToken?.token },
}, },
[modified], [modified],
); );

View File

@ -8,15 +8,15 @@ import Arrow from 'assets/arrow-right.svg';
import { percentFilter } from 'lib/filters'; import { percentFilter } from 'lib/filters';
import useDateRange from 'hooks/useDateRange'; import useDateRange from 'hooks/useDateRange';
import usePageQuery from 'hooks/usePageQuery'; import usePageQuery from 'hooks/usePageQuery';
import useShareToken from 'hooks/useShareToken';
import ErrorMessage from 'components/common/ErrorMessage'; import ErrorMessage from 'components/common/ErrorMessage';
import DataTable from './DataTable'; import DataTable from './DataTable';
import { DEFAULT_ANIMATION_DURATION } from 'lib/constants'; import { DEFAULT_ANIMATION_DURATION, TOKEN_HEADER } from 'lib/constants';
import styles from './MetricsTable.module.css'; import styles from './MetricsTable.module.css';
export default function MetricsTable({ export default function MetricsTable({
websiteId, websiteId,
websiteDomain, websiteDomain,
token,
title, title,
metric, metric,
type, type,
@ -30,6 +30,7 @@ export default function MetricsTable({
onDataLoad, onDataLoad,
...props ...props
}) { }) {
const shareToken = useShareToken();
const [dateRange] = useDateRange(websiteId); const [dateRange] = useDateRange(websiteId);
const { startDate, endDate, modified } = dateRange; const { startDate, endDate, modified } = dateRange;
const { const {
@ -47,10 +48,10 @@ export default function MetricsTable({
end_at: +endDate, end_at: +endDate,
domain: websiteDomain, domain: websiteDomain,
url, url,
token,
}, },
onDataLoad, onDataLoad,
delay: DEFAULT_ANIMATION_DURATION, delay: DEFAULT_ANIMATION_DURATION,
headers: { [TOKEN_HEADER]: shareToken?.token },
}, },
[modified], [modified],
); );

View File

@ -3,15 +3,14 @@ import MetricsTable from './MetricsTable';
import { osFilter } from 'lib/filters'; import { osFilter } from 'lib/filters';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
export default function OSTable({ websiteId, token, limit }) { export default function OSTable({ websiteId, ...props }) {
return ( return (
<MetricsTable <MetricsTable
{...props}
title={<FormattedMessage id="metrics.operating-systems" defaultMessage="Operating system" />} title={<FormattedMessage id="metrics.operating-systems" defaultMessage="Operating system" />}
type="os" type="os"
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />} metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
websiteId={websiteId} websiteId={websiteId}
token={token}
limit={limit}
dataFilter={osFilter} dataFilter={osFilter}
/> />
); );

View File

@ -10,7 +10,7 @@ import usePageQuery from 'hooks/usePageQuery';
import MetricsTable from './MetricsTable'; import MetricsTable from './MetricsTable';
import styles from './PagesTable.module.css'; import styles from './PagesTable.module.css';
export default function PagesTable({ websiteId, token, websiteDomain, showFilters, ...props }) { export default function PagesTable({ websiteId, websiteDomain, showFilters, ...props }) {
const [filter, setFilter] = useState(FILTER_COMBINED); const [filter, setFilter] = useState(FILTER_COMBINED);
const { const {
resolve, resolve,
@ -48,7 +48,6 @@ export default function PagesTable({ websiteId, token, websiteDomain, showFilter
type="url" type="url"
metric={<FormattedMessage id="metrics.views" defaultMessage="Views" />} metric={<FormattedMessage id="metrics.views" defaultMessage="Views" />}
websiteId={websiteId} websiteId={websiteId}
token={token}
dataFilter={urlFilter} dataFilter={urlFilter}
filterOptions={{ domain: websiteDomain, raw: filter === FILTER_RAW }} filterOptions={{ domain: websiteDomain, raw: filter === FILTER_RAW }}
renderLabel={renderLink} renderLabel={renderLink}

View File

@ -1,12 +1,12 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import MetricsTable from './MetricsTable'; import MetricsTable from './MetricsTable';
import { refFilter } from 'lib/filters';
import ButtonGroup from 'components/common/ButtonGroup'; import ButtonGroup from 'components/common/ButtonGroup';
import ButtonLayout from 'components/layout/ButtonLayout';
import { FILTER_DOMAIN_ONLY, FILTER_COMBINED, FILTER_RAW } from 'lib/constants'; import { FILTER_DOMAIN_ONLY, FILTER_COMBINED, FILTER_RAW } from 'lib/constants';
import ButtonLayout from '../layout/ButtonLayout'; import { refFilter } from 'lib/filters';
export default function ReferrersTable({ websiteId, websiteDomain, token, showFilters, ...props }) { export default function ReferrersTable({ websiteId, websiteDomain, showFilters, ...props }) {
const [filter, setFilter] = useState(FILTER_COMBINED); const [filter, setFilter] = useState(FILTER_COMBINED);
const buttons = [ const buttons = [
@ -41,7 +41,6 @@ export default function ReferrersTable({ websiteId, websiteDomain, token, showFi
metric={<FormattedMessage id="metrics.views" defaultMessage="Views" />} metric={<FormattedMessage id="metrics.views" defaultMessage="Views" />}
websiteId={websiteId} websiteId={websiteId}
websiteDomain={websiteDomain} websiteDomain={websiteDomain}
token={token}
dataFilter={refFilter} dataFilter={refFilter}
filterOptions={{ filterOptions={{
domain: websiteDomain, domain: websiteDomain,

View File

@ -14,15 +14,17 @@ import { getDateArray, getDateLength } from 'lib/date';
import Times from 'assets/times.svg'; import Times from 'assets/times.svg';
import styles from './WebsiteChart.module.css'; import styles from './WebsiteChart.module.css';
import ErrorMessage from '../common/ErrorMessage'; import ErrorMessage from '../common/ErrorMessage';
import useShareToken from '../../hooks/useShareToken';
import { TOKEN_HEADER } from '../../lib/constants';
export default function WebsiteChart({ export default function WebsiteChart({
websiteId, websiteId,
token,
title, title,
stickyHeader = false, stickyHeader = false,
showLink = false, showLink = false,
onDataLoad = () => {}, onDataLoad = () => {},
}) { }) {
const shareToken = useShareToken();
const [dateRange, setDateRange] = useDateRange(websiteId); const [dateRange, setDateRange] = useDateRange(websiteId);
const { startDate, endDate, unit, value, modified } = dateRange; const { startDate, endDate, unit, value, modified } = dateRange;
const [timezone] = useTimezone(); const [timezone] = useTimezone();
@ -41,9 +43,9 @@ export default function WebsiteChart({
unit, unit,
tz: timezone, tz: timezone,
url, url,
token,
}, },
onDataLoad, onDataLoad,
headers: { [TOKEN_HEADER]: shareToken?.token },
}, },
[modified], [modified],
); );
@ -64,7 +66,7 @@ export default function WebsiteChart({
return ( return (
<div className={styles.container}> <div className={styles.container}>
<WebsiteHeader websiteId={websiteId} token={token} title={title} showLink={showLink} /> <WebsiteHeader websiteId={websiteId} title={title} showLink={showLink} />
<div className={classNames(styles.header, 'row')}> <div className={classNames(styles.header, 'row')}>
<StickyHeader <StickyHeader
className={classNames(styles.metrics, 'col row')} className={classNames(styles.metrics, 'col row')}
@ -73,7 +75,7 @@ export default function WebsiteChart({
> >
{url && <PageFilter url={url} onClick={handleCloseFilter} />} {url && <PageFilter url={url} onClick={handleCloseFilter} />}
<div className="col-12 col-lg-9"> <div className="col-12 col-lg-9">
<MetricsBar websiteId={websiteId} token={token} /> <MetricsBar websiteId={websiteId} />
</div> </div>
<div className={classNames(styles.filter, 'col-12 col-lg-3')}> <div className={classNames(styles.filter, 'col-12 col-lg-3')}>
<DateFilter <DateFilter

View File

@ -8,11 +8,11 @@ import ActiveUsers from './ActiveUsers';
import Arrow from 'assets/arrow-right.svg'; import Arrow from 'assets/arrow-right.svg';
import styles from './WebsiteHeader.module.css'; import styles from './WebsiteHeader.module.css';
export default function WebsiteHeader({ websiteId, token, title, showLink = false }) { export default function WebsiteHeader({ websiteId, title, showLink = false }) {
return ( return (
<PageHeader> <PageHeader>
<div className={styles.title}>{title}</div> <div className={styles.title}>{title}</div>
<ActiveUsers className={styles.active} websiteId={websiteId} token={token} /> <ActiveUsers className={styles.active} websiteId={websiteId} />
<ButtonLayout align="right"> <ButtonLayout align="right">
<RefreshButton websiteId={websiteId} /> <RefreshButton websiteId={websiteId} />
{showLink && ( {showLink && (

View File

@ -13,6 +13,7 @@ import useFetch from 'hooks/useFetch';
import useLocale from 'hooks/useLocale'; import useLocale from 'hooks/useLocale';
import useCountryNames from 'hooks/useCountryNames'; import useCountryNames from 'hooks/useCountryNames';
import { percentFilter } from 'lib/filters'; import { percentFilter } from 'lib/filters';
import { TOKEN_HEADER } from 'lib/constants';
import styles from './RealtimeDashboard.module.css'; import styles from './RealtimeDashboard.module.css';
const REALTIME_RANGE = 30; const REALTIME_RANGE = 30;
@ -39,7 +40,7 @@ export default function RealtimeDashboard() {
params: { start_at: data?.timestamp }, params: { start_at: data?.timestamp },
disabled: !init?.websites?.length || !data, disabled: !init?.websites?.length || !data,
interval: REALTIME_INTERVAL, interval: REALTIME_INTERVAL,
headers: { 'x-umami-token': init?.token }, headers: { [TOKEN_HEADER]: init?.token },
}); });
const renderCountryName = useCallback(({ x }) => countryNames[x], []); const renderCountryName = useCallback(({ x }) => countryNames[x], []);

View File

@ -20,7 +20,8 @@ import EventsTable from '../metrics/EventsTable';
import EventsChart from '../metrics/EventsChart'; import EventsChart from '../metrics/EventsChart';
import useFetch from 'hooks/useFetch'; import useFetch from 'hooks/useFetch';
import usePageQuery from 'hooks/usePageQuery'; import usePageQuery from 'hooks/usePageQuery';
import { DEFAULT_ANIMATION_DURATION } from 'lib/constants'; import useShareToken from 'hooks/useShareToken';
import { DEFAULT_ANIMATION_DURATION, TOKEN_HEADER } from 'lib/constants';
const views = { const views = {
url: PagesTable, url: PagesTable,
@ -32,8 +33,11 @@ const views = {
event: EventsTable, event: EventsTable,
}; };
export default function WebsiteDetails({ websiteId, token }) { export default function WebsiteDetails({ websiteId }) {
const { data } = useFetch(`/api/website/${websiteId}`, { params: { token } }); const shareToken = useShareToken();
const { data } = useFetch(`/api/website/${websiteId}`, {
headers: { [TOKEN_HEADER]: shareToken?.token },
});
const [chartLoaded, setChartLoaded] = useState(false); const [chartLoaded, setChartLoaded] = useState(false);
const [countryData, setCountryData] = useState(); const [countryData, setCountryData] = useState();
const [eventsData, setEventsData] = useState(); const [eventsData, setEventsData] = useState();
@ -93,7 +97,6 @@ export default function WebsiteDetails({ websiteId, token }) {
const tableProps = { const tableProps = {
websiteId, websiteId,
token,
websiteDomain: data?.domain, websiteDomain: data?.domain,
limit: 10, limit: 10,
}; };
@ -116,7 +119,6 @@ export default function WebsiteDetails({ websiteId, token }) {
<div className={classNames(styles.chart, 'col')}> <div className={classNames(styles.chart, 'col')}>
<WebsiteChart <WebsiteChart
websiteId={websiteId} websiteId={websiteId}
token={token}
title={data.name} title={data.name}
onDataLoad={handleDataLoad} onDataLoad={handleDataLoad}
showLink={false} showLink={false}
@ -159,7 +161,7 @@ export default function WebsiteDetails({ websiteId, token }) {
<EventsTable {...tableProps} onDataLoad={setEventsData} /> <EventsTable {...tableProps} onDataLoad={setEventsData} />
</GridColumn> </GridColumn>
<GridColumn xs={12} md={12} lg={8}> <GridColumn xs={12} md={12} lg={8}>
<EventsChart className={styles.eventschart} websiteId={websiteId} token={token} /> <EventsChart className={styles.eventschart} websiteId={websiteId} />
</GridColumn> </GridColumn>
</GridRow> </GridRow>
</GridLayout> </GridLayout>

25
hooks/useShareToken.js Normal file
View File

@ -0,0 +1,25 @@
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { get } from 'lib/web';
import { setShareToken } from 'redux/actions/app';
export default function useShareToken(shareId) {
const dispatch = useDispatch();
const shareToken = useSelector(state => state.app.shareToken);
async function loadToken(id) {
const { data } = await get(`/api/share/${id}`);
if (data) {
dispatch(setShareToken(data));
}
}
useEffect(() => {
if (shareId) {
loadToken(shareId);
}
}, [shareId]);
return shareToken;
}

View File

@ -1,6 +1,6 @@
import { parse } from 'cookie'; import { parse } from 'cookie';
import { parseSecureToken, parseToken } from './crypto'; import { parseSecureToken, parseToken } from './crypto';
import { AUTH_COOKIE_NAME } from './constants'; import { AUTH_COOKIE_NAME, TOKEN_HEADER } from './constants';
import { getWebsiteById } from './queries'; import { getWebsiteById } from './queries';
export async function getAuthToken(req) { export async function getAuthToken(req) {
@ -26,7 +26,8 @@ export async function isValidToken(token, validation) {
} }
export async function allowQuery(req, skipToken) { export async function allowQuery(req, skipToken) {
const { id, token } = req.query; const { id } = req.query;
const token = req.headers[TOKEN_HEADER];
const websiteId = +id; const websiteId = +id;
const website = await getWebsiteById(websiteId); const website = await getWebsiteById(websiteId);

View File

@ -4,6 +4,7 @@ export const TIMEZONE_CONFIG = 'umami.timezone';
export const DATE_RANGE_CONFIG = 'umami.date-range'; export const DATE_RANGE_CONFIG = 'umami.date-range';
export const THEME_CONFIG = 'umami.theme'; export const THEME_CONFIG = 'umami.theme';
export const VERSION_CHECK = 'umami.version-check'; export const VERSION_CHECK = 'umami.version-check';
export const TOKEN_HEADER = 'x-umami-token';
export const THEME_COLORS = { export const THEME_COLORS = {
light: { light: {

View File

@ -2,6 +2,7 @@ import { useAuth } from 'lib/middleware';
import { ok, methodNotAllowed, badRequest } from 'lib/response'; import { ok, methodNotAllowed, badRequest } from 'lib/response';
import { getRealtimeData } from 'lib/queries'; import { getRealtimeData } from 'lib/queries';
import { parseToken } from 'lib/crypto'; import { parseToken } from 'lib/crypto';
import { TOKEN_HEADER } from 'lib/constants';
export default async (req, res) => { export default async (req, res) => {
await useAuth(req, res); await useAuth(req, res);
@ -9,7 +10,7 @@ export default async (req, res) => {
if (req.method === 'GET') { if (req.method === 'GET') {
const { start_at } = req.query; const { start_at } = req.query;
const token = req.headers['x-umami-token']; const token = req.headers[TOKEN_HEADER];
if (!token) { if (!token) {
return badRequest(res); return badRequest(res);

View File

@ -2,23 +2,23 @@ import React from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import Layout from 'components/layout/Layout'; import Layout from 'components/layout/Layout';
import WebsiteDetails from 'components/pages/WebsiteDetails'; import WebsiteDetails from 'components/pages/WebsiteDetails';
import useFetch from 'hooks/useFetch'; import useShareToken from 'hooks/useShareToken';
export default function SharePage() { export default function SharePage() {
const router = useRouter(); const router = useRouter();
const { id } = router.query; const { id } = router.query;
const shareId = id?.[0]; const shareId = id?.[0];
const { data } = useFetch(`/api/share/${shareId}`, { disabled: !shareId }); const shareToken = useShareToken(shareId);
if (!data) { if (!shareToken) {
return null; return null;
} }
const { websiteId, token } = data; const { websiteId } = shareToken;
return ( return (
<Layout> <Layout>
<WebsiteDetails websiteId={websiteId} token={token} /> <WebsiteDetails websiteId={websiteId} />
</Layout> </Layout>
); );
} }

View File

@ -19,6 +19,7 @@ const app = createSlice({
latest: null, latest: null,
hasUpdate: false, hasUpdate: false,
}, },
shareToken: null,
}, },
reducers: { reducers: {
setLocale(state, action) { setLocale(state, action) {
@ -33,10 +34,14 @@ const app = createSlice({
state.versions = action.payload; state.versions = action.payload;
return state; return state;
}, },
setShareToken(state, action) {
state.shareToken = action.payload;
return state;
},
}, },
}); });
export const { setLocale, setTheme, setVersions } = app.actions; export const { setLocale, setTheme, setVersions, setShareToken } = app.actions;
export default app.reducer; export default app.reducer;