diff --git a/micro-service/src/App.js b/micro-service/src/App.js
index 9ae97bcf..7211997c 100644
--- a/micro-service/src/App.js
+++ b/micro-service/src/App.js
@@ -6,6 +6,7 @@ import {
AppProvider,
createBrowserRouter,
RouterProvider,
+ NavigationProvider,
} from '@apollosproject/web-shared/providers';
import AppHeader from '@apollosproject/web-shared/components/AppHeader';
@@ -36,7 +37,9 @@ function App({ searchParams, url }) {
<>
-
+
+
+
>
);
diff --git a/micro-service/src/app/[[...slug]]/page.js b/micro-service/src/app/[[...slug]]/page.js
index e64701fc..96ece709 100644
--- a/micro-service/src/app/[[...slug]]/page.js
+++ b/micro-service/src/app/[[...slug]]/page.js
@@ -7,7 +7,7 @@ import Head from 'next/head';
export const dynamic = 'force-dynamic';
-export async function generateMetadata({ params, searchParams }, parent) {
+export async function generateMetadata({ searchParams }, parent) {
const id = searchParams?.id;
if (!id) return parent;
diff --git a/packages/web-shared/components/Breadcrumbs/Breadcrumbs.js b/packages/web-shared/components/Breadcrumbs/Breadcrumbs.js
deleted file mode 100644
index ba63e578..00000000
--- a/packages/web-shared/components/Breadcrumbs/Breadcrumbs.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import React, { useEffect, useRef } from 'react';
-import { useSearchParams, useNavigate, useLocation } from 'react-router-dom';
-import { CaretRight } from '@phosphor-icons/react';
-import { Box, Button, SystemText } from '../../ui-kit';
-import {
- remove as removeBreadcrumb,
- reset as resetBreadcrumb,
- set as setBreadcrumb,
- useBreadcrumb,
-} from '../../providers/BreadcrumbProvider';
-
-function Breadcrumbs(props = {}) {
- const navigate = useNavigate();
- const location = useLocation();
- const [searchParams, setSearchParams] = useSearchParams();
- const [state, dispatch] = useBreadcrumb();
- const prevStateRef = useRef(state);
- const currentState = state[state.length - 1];
-
- function handleBreadClick({ id, url }) {
- dispatch(removeBreadcrumb(id));
- setSearchParams(`${url}`);
- }
-
- const pathname = window.location.pathname;
- const dynamicRoute = /^\/[a-z0-9-]+$/i;
-
- if (state.length === 0) {
- return null;
- }
-
- return (
-
-
- );
-}
-
-export default Breadcrumbs;
diff --git a/packages/web-shared/components/Breadcrumbs/index.js b/packages/web-shared/components/Breadcrumbs/index.js
deleted file mode 100644
index 1690c4e4..00000000
--- a/packages/web-shared/components/Breadcrumbs/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Breadcrumbs from './Breadcrumbs';
-
-export default Breadcrumbs;
diff --git a/packages/web-shared/components/ContentChannel.js b/packages/web-shared/components/ContentChannel.js
index 92337586..7e95efbf 100644
--- a/packages/web-shared/components/ContentChannel.js
+++ b/packages/web-shared/components/ContentChannel.js
@@ -3,18 +3,18 @@ import { useNavigate } from 'react-router-dom';
import { getURLFromType } from '../utils';
import { ContentCard, Box, H3, systemPropTypes, Button } from '../ui-kit';
+import { useNavigation } from '../providers/NavigationProvider';
const PAGE_SIZE = 20;
function ContentChannel(props = {}) {
- const navigate = useNavigate();
+ const { navigate } = useNavigation();
const hasMorePages = props.data?.totalCount > props.data?.edges?.length;
const handleActionPress = (item) => {
navigate({
- pathname: '/',
- search: `?id=${getURLFromType(item.relatedNode)}`,
+ id: getURLFromType(item.relatedNode),
});
};
diff --git a/packages/web-shared/components/ContentSeriesSingle.js b/packages/web-shared/components/ContentSeriesSingle.js
index a6dab5ed..5b23583a 100644
--- a/packages/web-shared/components/ContentSeriesSingle.js
+++ b/packages/web-shared/components/ContentSeriesSingle.js
@@ -1,9 +1,8 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
-import { useNavigate, useSearchParams } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { getURLFromType } from '../utils';
-import { add as addBreadcrumb, useBreadcrumbDispatch } from '../providers/BreadcrumbProvider';
+
import { set as setModal, useModal } from '../providers/ModalProvider';
import { Box, H2, Loader, Longform, H3, ContentCard, ShareButton } from '../ui-kit';
@@ -15,13 +14,12 @@ import InteractWhenLoaded from './InteractWhenLoaded';
import TrackEventWhenLoaded from './TrackEventWhenLoaded';
import styled from 'styled-components';
import { themeGet } from '@styled-system/theme-get';
+import { useNavigation } from '../providers/NavigationProvider';
function ContentSeriesSingle(props = {}) {
- const navigate = useNavigate();
- const [searchParams, setSearchParams] = useSearchParams();
- const dispatchBreadcrumb = useBreadcrumbDispatch();
const [state, dispatch] = useModal();
const parseHTMLContent = useHTMLContent();
+ const { id, navigate } = useNavigation();
const invalidPage = !props.loading && !props.data;
@@ -35,9 +33,7 @@ function ContentSeriesSingle(props = {}) {
useEffect(() => {
if (!state.modal && invalidPage) {
- navigate({
- pathname: '/',
- });
+ navigate();
}
}, [invalidPage, navigate]);
@@ -76,14 +72,8 @@ function ContentSeriesSingle(props = {}) {
`;
const handleActionPress = (item) => {
- if (searchParams.get('id') !== getURLFromType(item)) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(item)}`,
- title: item.title,
- })
- );
- setSearchParams(`?id=${getURLFromType(item)}`);
+ if (id !== getURLFromType(item)) {
+ navigate({ id: getURLFromType(item) });
}
if (state.modal) {
const url = getURLFromType(item);
diff --git a/packages/web-shared/components/ContentSingle.js b/packages/web-shared/components/ContentSingle.js
index 79eae861..b0db0ede 100644
--- a/packages/web-shared/components/ContentSingle.js
+++ b/packages/web-shared/components/ContentSingle.js
@@ -1,6 +1,5 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
-import { useNavigate, useSearchParams } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { ChatCircleText } from '@phosphor-icons/react';
@@ -9,11 +8,6 @@ import { getURLFromType } from '../utils';
import { format, parseISO } from 'date-fns';
import FeatureFeed from './FeatureFeed';
import FeatureFeedComponentMap from './FeatureFeed/FeatureFeedComponentMap';
-import {
- add as addBreadcrumb,
- remove as removeBreadcrumb,
- useBreadcrumbDispatch,
-} from '../providers/BreadcrumbProvider';
import { set as setModal, useModal } from '../providers/ModalProvider';
import {
@@ -38,6 +32,8 @@ import VideoPlayer from './VideoPlayer';
import InteractWhenLoaded from './InteractWhenLoaded';
import TrackEventWhenLoaded from './TrackEventWhenLoaded';
import styled from 'styled-components';
+import { useNavigation } from '../providers/NavigationProvider';
+import { useShouldUsePathRouter } from '../providers/AppProvider';
const infoDivider = (
@@ -92,10 +88,10 @@ function CalendarData({ start, end, location }) {
}
function ContentSingle(props = {}) {
- const navigate = useNavigate();
+ const { navigate, id: idFromParams } = useNavigation();
const [showComments, setShowComments] = useState(false);
- const [searchParams, setSearchParams] = useSearchParams();
- const dispatchBreadcrumb = useBreadcrumbDispatch();
+ const usePathRouter = useShouldUsePathRouter();
+
const [state, dispatch] = useModal();
const parseHTMLContent = useHTMLContent();
@@ -123,14 +119,16 @@ function ContentSingle(props = {}) {
useEffect(() => {
if (!state.modal && invalidPage) {
- navigate({
- pathname: '/',
- });
+ // return home if the page is invalid
+ navigate();
}
}, [invalidPage, navigate]);
// Try and convince Google that this is the canonical URL
- const canonicalUrl = `${window.location.origin}/?id=${searchParams.get('id')}`;
+ // TODO: Find a better way to determine this.
+ const canonicalUrl = usePathRouter
+ ? `${window.location.origin}/ac/${idFromParams}`
+ : `${window.location.origin}/?id=${idFromParams}`;
// Some websites have existing canonical links that need to be updated.
useEffect(() => {
@@ -197,15 +195,10 @@ function ContentSingle(props = {}) {
)}`
: null;
const handleActionPress = (item) => {
- dispatchBreadcrumb(removeBreadcrumb());
- if (searchParams.get('id') !== getURLFromType(item)) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(item)}`,
- title: item.title,
- })
- );
- setSearchParams(`?id=${getURLFromType(item)}`);
+ if (idFromParams !== getURLFromType(item)) {
+ navigate({
+ id: getURLFromType(item),
+ });
}
if (state.modal) {
const url = getURLFromType(item);
diff --git a/packages/web-shared/components/FeatureFeed/Features/ActionListFeature.js b/packages/web-shared/components/FeatureFeed/Features/ActionListFeature.js
index e6f7e62b..2adb2d0a 100644
--- a/packages/web-shared/components/FeatureFeed/Features/ActionListFeature.js
+++ b/packages/web-shared/components/FeatureFeed/Features/ActionListFeature.js
@@ -2,15 +2,14 @@ import React from 'react';
import { getURLFromType } from '../../../utils';
import { systemPropTypes, ResourceCard, Box, H5 } from '../../../ui-kit';
import Styled from './ActionListFeature.styles';
-import { useNavigate } from 'react-router-dom';
+import { useNavigation } from '../../../providers/NavigationProvider';
function ActionListFeature({ feature, emptyPlaceholderText }) {
- const navigate = useNavigate();
+ const { navigate } = useNavigation();
const handleActionPress = (item) => {
navigate({
- pathname: '/',
- search: `?id=${getURLFromType(item.relatedNode)}`,
+ id: getURLFromType(item.relatedNode),
});
};
diff --git a/packages/web-shared/components/FeatureFeed/Features/ButtonFeature.js b/packages/web-shared/components/FeatureFeed/Features/ButtonFeature.js
index 47c41f7a..18cfec9b 100644
--- a/packages/web-shared/components/FeatureFeed/Features/ButtonFeature.js
+++ b/packages/web-shared/components/FeatureFeed/Features/ButtonFeature.js
@@ -1,21 +1,20 @@
import React from 'react';
import { withTheme } from 'styled-components';
-import { useNavigate } from 'react-router-dom';
import { getURLFromType } from '../../../utils';
import { Button, systemPropTypes } from '../../../ui-kit';
+import { useNavigation } from '../../../providers/NavigationProvider';
function ButtonFeature(props = {}) {
- const navigate = useNavigate();
+ const { navigate } = useNavigation();
// Event Handlers
const handleActionPress = () => {
if (props.feature?.action?.relatedNode?.url) {
window.open(props.transformLink(props.feature?.action?.relatedNode?.url), '_blank');
} else {
navigate({
- pathname: '/',
- search: `?id=${getURLFromType(props.feature.action.relatedNode)}`,
+ id: getURLFromType(item.relatedNode),
});
}
};
diff --git a/packages/web-shared/components/FeatureFeed/Features/HeroListFeature.js b/packages/web-shared/components/FeatureFeed/Features/HeroListFeature.js
index 64691668..601e72b5 100644
--- a/packages/web-shared/components/FeatureFeed/Features/HeroListFeature.js
+++ b/packages/web-shared/components/FeatureFeed/Features/HeroListFeature.js
@@ -1,54 +1,28 @@
import React from 'react';
import { withTheme } from 'styled-components';
-import { useSearchParams } from 'react-router-dom';
import { getURLFromType } from '../../../utils';
import { open as openModal, set as setModal, useModal } from '../../../providers/ModalProvider';
-import { add as addBreadcrumb, useBreadcrumbDispatch } from '../../../providers/BreadcrumbProvider';
import { Box, Button, H2, H4, systemPropTypes, ContentCard } from '../../../ui-kit';
import Styled from './HeroListFeature.styles';
-import { useNavigate } from 'react-router-dom';
-import { useAnalytics } from '../../../providers/AnalyticsProvider';
+import { useNavigation } from '../../../providers/NavigationProvider';
+import useHandleActionPress, {
+ useHandlePrimaryActionPress,
+} from '../../../hooks/useHandleActionPress';
function HeroListFeature(props = {}) {
const [state, dispatch] = useModal();
- const [searchParams, setSearchParams] = useSearchParams();
- const dispatchBreadcrumb = useBreadcrumbDispatch();
- const navigate = useNavigate();
- const analytics = useAnalytics();
- const handleActionPress = (item) => {
- if (item.action === 'OPEN_URL') {
- analytics.track('OpenUrl', {
- url: item?.relatedNode?.url,
- });
- return window.open(getURLFromType(item.relatedNode), '_blank');
- }
+ const { id, navigate } = useNavigation();
- if (searchParams.get('id') !== getURLFromType(item.relatedNode)) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(item.relatedNode)}`,
- title: item.relatedNode?.title,
- })
- );
- setSearchParams(`?id=${getURLFromType(item.relatedNode)}`);
- }
- navigate({
- pathname: '/',
- search: `?id=${getURLFromType(item.relatedNode)}`,
- });
- };
+ const handleActionPress = useHandleActionPress();
+ const handlePrimaryActionClick = useHandlePrimaryActionPress(props.feature);
// Event Handlers
const handleHeroCardPress = () => {
- if (searchParams.get('id') !== getURLFromType(props.feature?.heroCard?.relatedNode)) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(props.feature?.heroCard?.relatedNode)}`,
- title: props.feature?.heroCard?.relatedNode?.title,
- })
- );
- setSearchParams(`?id=${getURLFromType(props.feature?.heroCard?.relatedNode)}`);
+ if (id !== getURLFromType(props.feature?.heroCard?.relatedNode)) {
+ navigate({
+ id: getURLFromType(props.feature?.heroCard?.relatedNode),
+ });
}
if (state.modal) {
@@ -58,20 +32,8 @@ function HeroListFeature(props = {}) {
}
};
- const handlePrimaryActionClick = () => {
- if (props.feature?.primaryAction?.action === 'OPEN_FEED') {
- analytics.track('OpenFeatureFeed', {
- featureFeedId: props.feature?.primaryAction?.relatedNode?.id,
- fromFeatureId: props.feature?.id,
- title: props.feature?.title,
- });
- }
- setSearchParams(`?id=${getURLFromType(props.feature.primaryAction.relatedNode)}`);
- };
-
const actions = props.feature?.actions;
-
return (
{/* Content */}
diff --git a/packages/web-shared/components/FeatureFeed/Features/HorizontalCardListFeature.js b/packages/web-shared/components/FeatureFeed/Features/HorizontalCardListFeature.js
index 968835f6..e6392344 100644
--- a/packages/web-shared/components/FeatureFeed/Features/HorizontalCardListFeature.js
+++ b/packages/web-shared/components/FeatureFeed/Features/HorizontalCardListFeature.js
@@ -1,15 +1,13 @@
import React from 'react';
import get from 'lodash/get';
-import { useSearchParams } from 'react-router-dom';
-import { getURLFromType } from '../../../utils';
import { ContentCard, Box, H2, systemPropTypes, Button, ButtonGroup } from '../../../ui-kit';
-import { add as addBreadcrumb, useBreadcrumbDispatch } from '../../../providers/BreadcrumbProvider';
-import { open as openModal, set as setModal, useModal } from '../../../providers/ModalProvider';
import { CaretRight } from '@phosphor-icons/react';
-import { useAnalytics } from '../../../providers/AnalyticsProvider';
import Carousel from 'react-multi-carousel';
+import useHandleActionPress, {
+ useHandlePrimaryActionPress,
+} from '../../../hooks/useHandleActionPress';
const responsive = {
lg: {
@@ -27,57 +25,8 @@ const responsive = {
};
function HorizontalCardListFeature(props = {}) {
- const [searchParams, setSearchParams] = useSearchParams();
- const dispatchBreadcrumb = useBreadcrumbDispatch();
- const [state, dispatch] = useModal();
- const analytics = useAnalytics();
-
- const handleActionPress = (item) => {
- if (item.action === 'OPEN_URL') {
- analytics.track('OpenUrl', {
- url: item?.relatedNode?.url,
- });
- return window.open(getURLFromType(item.relatedNode), '_blank');
- }
-
- if (searchParams.get('id') !== getURLFromType(item.relatedNode)) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(item.relatedNode)}`,
- title: item.relatedNode?.title || item?.title,
- })
- );
- setSearchParams(`?id=${getURLFromType(item.relatedNode)}`);
- }
- if (state.modal) {
- const url = getURLFromType(item.relatedNode);
- dispatch(setModal(url));
- dispatch(openModal());
- }
- };
-
- const handlePrimaryActionPress = () => {
- if (searchParams.get('id') !== getURLFromType(props?.feature?.primaryAction.relatedNode)) {
- if (props?.feature?.title) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(props?.feature?.primaryAction.relatedNode)}`,
- title: props?.feature?.title,
- })
- );
- }
- const id = getURLFromType(props?.feature?.primaryAction.relatedNode);
- if (props.feature?.primaryAction?.action === 'OPEN_FEED') {
- analytics.track('OpenFeatureFeed', {
- featureFeedId: props.feature?.primaryAction?.relatedNode?.id,
- fromFeatureId: props.feature?.id,
- title: props.feature?.title,
- });
- }
-
- state.modal ? setSearchParams({ id }) : setSearchParams({ id, action: 'viewall' });
- }
- };
+ const handleActionPress = useHandleActionPress();
+ const handlePrimaryActionPress = useHandlePrimaryActionPress(props.feature);
if (props?.feature?.cards?.length === 0 || !props?.feature?.cards) {
return null;
diff --git a/packages/web-shared/components/FeatureFeed/Features/HorizontalMediaListFeature.js b/packages/web-shared/components/FeatureFeed/Features/HorizontalMediaListFeature.js
index 55661661..e8c63f97 100644
--- a/packages/web-shared/components/FeatureFeed/Features/HorizontalMediaListFeature.js
+++ b/packages/web-shared/components/FeatureFeed/Features/HorizontalMediaListFeature.js
@@ -1,14 +1,14 @@
import React from 'react';
import get from 'lodash/get';
-import { useSearchParams } from 'react-router-dom';
-import { getURLFromType } from '../../../utils';
import { Box, H2, systemPropTypes, Button, MediaItem, ButtonGroup } from '../../../ui-kit';
-import { add as addBreadcrumb, useBreadcrumbDispatch } from '../../../providers/BreadcrumbProvider';
-import { open as openModal, set as setModal, useModal } from '../../../providers/ModalProvider';
-import { useAnalytics } from '../../../providers/AnalyticsProvider';
import Carousel from 'react-multi-carousel';
import { CaretRight } from '@phosphor-icons/react';
+
+import useHandleActionPress, {
+ useHandlePrimaryActionPress,
+} from '../../../hooks/useHandleActionPress';
+
const SHOW_VIEW_ALL_LIMIT = 5;
const responsive = {
@@ -27,56 +27,8 @@ const responsive = {
};
function HorizontalMediaListFeature(props = {}) {
- const [searchParams, setSearchParams] = useSearchParams();
- const dispatchBreadcrumb = useBreadcrumbDispatch();
- const [state, dispatch] = useModal();
- const analytics = useAnalytics();
-
- const handleActionPress = (item) => {
- if (item.action === 'OPEN_URL') {
- analytics.track('OpenUrl', {
- url: item?.relatedNode?.url,
- });
- return window.open(getURLFromType(item.relatedNode), '_blank');
- }
-
- if (searchParams.get('id') !== getURLFromType(item.relatedNode)) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(item.relatedNode)}`,
- title: item.relatedNode?.title,
- })
- );
- setSearchParams(`?id=${getURLFromType(item.relatedNode)}`);
- }
- if (state.modal) {
- const url = getURLFromType(item.relatedNode);
- dispatch(setModal(url));
- dispatch(openModal());
- }
- };
-
- const handlePrimaryActionPress = () => {
- if (searchParams.get('id') !== getURLFromType(props?.feature?.primaryAction.relatedNode)) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(props?.feature?.primaryAction.relatedNode)}`,
- title: props?.feature?.title,
- })
- );
- const id = getURLFromType(props?.feature?.primaryAction.relatedNode);
-
- if (props.feature?.primaryAction?.action === 'OPEN_FEED') {
- analytics.track('OpenFeatureFeed', {
- featureFeedId: props.feature?.primaryAction?.relatedNode?.id,
- fromFeatureId: props.feature?.id,
- title: props.feature?.title,
- });
- }
-
- state.modal ? setSearchParams({ id }) : setSearchParams({ id, action: 'viewall' });
- }
- };
+ const handleActionPress = useHandleActionPress();
+ const handlePrimaryActionPress = useHandlePrimaryActionPress(props.feature);
if (props?.feature?.items?.length === 0 || !props?.feature?.items) {
return null;
diff --git a/packages/web-shared/components/FeatureFeed/Features/VerticalCardListFeature.js b/packages/web-shared/components/FeatureFeed/Features/VerticalCardListFeature.js
index fe598d66..9b93176a 100644
--- a/packages/web-shared/components/FeatureFeed/Features/VerticalCardListFeature.js
+++ b/packages/web-shared/components/FeatureFeed/Features/VerticalCardListFeature.js
@@ -1,66 +1,15 @@
import React from 'react';
-import { useSearchParams } from 'react-router-dom';
-
-import { getURLFromType } from '../../../utils';
import { ContentCard, Box, H2, systemPropTypes, Button } from '../../../ui-kit';
-import { add as addBreadcrumb, useBreadcrumbDispatch } from '../../../providers/BreadcrumbProvider';
-import { open as openModal, set as setModal, useModal } from '../../../providers/ModalProvider';
import Styled from './VerticalCardListFeature.styles';
import { CaretRight } from '@phosphor-icons/react';
-import { useAnalytics } from '../../../providers/AnalyticsProvider';
+import useHandleActionPress, {
+ useHandlePrimaryActionPress,
+} from '../../../hooks/useHandleActionPress';
function VerticalCardListFeature(props = {}) {
- const [searchParams, setSearchParams] = useSearchParams();
- const dispatchBreadcrumb = useBreadcrumbDispatch();
- const [state, dispatch] = useModal();
- const analytics = useAnalytics();
-
- const handleActionPress = (item) => {
- if (item.action === 'OPEN_URL') {
- analytics.track('OpenUrl', {
- url: item?.relatedNode?.url,
- });
- return window.open(getURLFromType(item.relatedNode), '_blank');
- }
-
- if (searchParams.get('id') !== getURLFromType(item.relatedNode)) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(item.relatedNode)}`,
- title: item.relatedNode?.title,
- })
- );
- setSearchParams(`?id=${getURLFromType(item.relatedNode)}`);
- }
- if (state.modal) {
- const url = getURLFromType(item.relatedNode);
- dispatch(setModal(url));
- dispatch(openModal());
- }
- };
-
- const handlePrimaryActionPress = () => {
- if (searchParams.get('id') !== getURLFromType(props?.feature?.primaryAction.relatedNode)) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(props?.feature?.primaryAction.relatedNode)}`,
- title: props?.feature?.title,
- })
- );
- const id = getURLFromType(props?.feature?.primaryAction.relatedNode);
-
- if (props.feature?.primaryAction?.action === 'OPEN_FEED') {
- analytics.track('OpenFeatureFeed', {
- featureFeedId: props.feature?.primaryAction?.relatedNode?.id,
- fromFeatureId: props.feature?.id,
- title: props.feature?.title,
- });
- }
-
- state.modal ? setSearchParams({ id }) : setSearchParams({ id, action: 'viewall' });
- }
- };
+ const handleActionPress = useHandleActionPress();
+ const handlePrimaryActionPress = useHandlePrimaryActionPress(props.feature);
const cards = props.feature?.cards;
return (
diff --git a/packages/web-shared/components/FeatureFeedList/FeatureFeedListGrid.js b/packages/web-shared/components/FeatureFeedList/FeatureFeedListGrid.js
index 7935e88b..a1a70e65 100644
--- a/packages/web-shared/components/FeatureFeedList/FeatureFeedListGrid.js
+++ b/packages/web-shared/components/FeatureFeedList/FeatureFeedListGrid.js
@@ -1,32 +1,20 @@
import React from 'react';
-import { useSearchParams } from 'react-router-dom';
import { withTheme } from 'styled-components';
import { getURLFromType } from '../../utils';
import { Box, ContentCard, H3 } from '../../ui-kit';
-import {
- add as addBreadcrumb,
- reset as resetBreadcrumb,
- useBreadcrumbDispatch,
-} from '../../providers/BreadcrumbProvider';
import { open as openModal, set as setModal, useModal } from '../../providers/ModalProvider';
+import { useNavigation } from '../../providers/NavigationProvider';
function FeatureFeedListGrid({ loading, data, emptyPlaceholderText }) {
- const [searchParams, setSearchParams] = useSearchParams();
+ const { navigate, id } = useNavigation();
const [state, dispatch] = useModal();
- const dispatchBreadcrumb = useBreadcrumbDispatch();
const handleActionPress = (item) => {
- if (searchParams.get('id') !== getURLFromType(item.relatedNode)) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(item.relatedNode)}`,
- title: item.title,
- })
- );
- setSearchParams(`?id=${getURLFromType(item.relatedNode)}`);
+ if (id !== getURLFromType(item.relatedNode)) {
+ navigate({ id: getURLFromType(item.relatedNode) });
}
if (state.modal) {
const url = getURLFromType(item.relatedNode);
diff --git a/packages/web-shared/components/LivestreamSingle.js b/packages/web-shared/components/LivestreamSingle.js
index 947bae12..8d6e3314 100644
--- a/packages/web-shared/components/LivestreamSingle.js
+++ b/packages/web-shared/components/LivestreamSingle.js
@@ -21,8 +21,10 @@ import {
import { useVideoMediaProgress, useLivestreamStatus, useHTMLContent } from '../hooks';
import VideoPlayer from './VideoPlayer';
import TrackEventWhenLoaded from './TrackEventWhenLoaded';
+import { useNavigation } from '../providers/NavigationProvider';
+
function LivestreamSingle(props = {}) {
- const navigate = useNavigate();
+ const { navigate } = useNavigation();
const parseHTMLContent = useHTMLContent();
const invalidPage = !props.loading && !props.data;
@@ -87,8 +89,7 @@ function LivestreamSingle(props = {}) {
const handleActionPress = (item) => {
navigate({
- pathname: '/',
- search: `?id=${getURLFromType(item.relatedNode)}`,
+ id: getURLFromType(item.relatedNode),
});
};
diff --git a/packages/web-shared/components/Modal/Modal.js b/packages/web-shared/components/Modal/Modal.js
index e5bfa8e6..594da6cc 100644
--- a/packages/web-shared/components/Modal/Modal.js
+++ b/packages/web-shared/components/Modal/Modal.js
@@ -4,20 +4,15 @@ import { systemPropTypes } from '../../ui-kit/_lib/system';
import Styled from './Modal.styles';
import { Box } from '../../ui-kit';
import { Searchbar } from '../../components';
-import Breadcrumbs from '../Breadcrumbs';
-import { useSearchParams } from 'react-router-dom';
+import { useNavigation } from '../../providers/NavigationProvider';
+import { X } from '@phosphor-icons/react';
+import Wordmark from '../Wordmark';
import {
- open as openModal,
- close as closeModal,
set as setModal,
useModal,
+ close as closeModal,
+ open as openModal,
} from '../../providers/ModalProvider';
-import {
- reset as resetBreadcrumb,
- useBreadcrumbDispatch,
-} from '../../providers/BreadcrumbProvider';
-import { X } from '@phosphor-icons/react';
-import Wordmark from '../Wordmark';
function ChurchLogo(props) {
const { currentChurch } = useCurrentChurch();
@@ -31,25 +26,25 @@ function ChurchLogo(props) {
const Modal = (props = {}) => {
const [state, dispatch] = useModal();
- const [searchParams, setSearchParams] = useSearchParams();
- const dispatchBreadcrumb = useBreadcrumbDispatch();
+
const ref = useRef();
+ const { id, navigate } = useNavigation();
useEffect(() => {
// Watch for changes to the `id` search param
- if (searchParams.get('id')) {
+ if (id) {
dispatch(openModal());
- dispatch(setModal(searchParams.get('id')));
+ dispatch(setModal(id));
}
- if (searchParams.get('id') === null) {
+ if (id === null) {
dispatch(closeModal());
}
- }, [dispatch, searchParams]);
+ }, [dispatch, id]);
function handleCloseModal() {
dispatch(closeModal());
- setSearchParams('');
- dispatchBreadcrumb(resetBreadcrumb());
+ // Navigate to root
+ navigate();
}
useEffect(() => {
@@ -127,7 +122,6 @@ const Modal = (props = {}) => {
-
{
const searchState = useSearchState();
- const [searchParams, setSearchParams] = useSearchParams();
- const dispatchBreadcrumb = useBreadcrumbDispatch();
const [state, dispatch] = useModal();
+ const { navigate, id } = useNavigation();
const [isResultsEmpty, setIsResultsEmpty] = useState(false);
@@ -143,16 +137,7 @@ const SearchResults = ({ autocompleteState, autocomplete }) => {
}, [autocompleteState.collections]);
const handleActionPress = (item) => {
- dispatchBreadcrumb(resetBreadcrumb());
- if (searchParams.get('id') !== getURLFromType(item)) {
- dispatchBreadcrumb(
- addBreadcrumb({
- url: `?id=${getURLFromType(item)}`,
- title: item.title,
- })
- );
- setSearchParams(`?id=${getURLFromType(item)}`);
- }
+ navigate({ id: getURLFromType(item) });
if (state.modal) {
const url = getURLFromType(item);
dispatch(setModal(url));
diff --git a/packages/web-shared/components/index.js b/packages/web-shared/components/index.js
index dd7b231e..c24e40ac 100644
--- a/packages/web-shared/components/index.js
+++ b/packages/web-shared/components/index.js
@@ -1,5 +1,4 @@
import AuthManager from './Auth';
-import Breadcrumbs from './Breadcrumbs';
import ContentChannel from './ContentChannel';
import ContentSingle from './ContentSingle';
import ContentSeriesSingle from './ContentSeriesSingle';
@@ -15,7 +14,6 @@ import Wordmark from './Wordmark';
export {
AuthManager,
- Breadcrumbs,
ContentChannel,
ContentSingle,
ContentSeriesSingle,
diff --git a/packages/web-shared/embeds/FeatureFeed.js b/packages/web-shared/embeds/FeatureFeed.js
index 09128f04..f50b599f 100644
--- a/packages/web-shared/embeds/FeatureFeed.js
+++ b/packages/web-shared/embeds/FeatureFeed.js
@@ -1,26 +1,16 @@
import React, { useEffect } from 'react';
-import { ContentItemProvider, FeatureFeedProvider, ContentFeedProvider } from '../providers';
-import {
- ContentSingle,
- FeatureFeedList,
- ContentChannel,
- LivestreamSingle,
- Breadcrumbs,
- Modal,
- ContentSeriesSingle,
-} from '../components';
+import { FeatureFeedProvider } from '../providers';
+import { FeatureFeedList, Modal } from '../components';
import { useModalState } from '../providers/ModalProvider';
import { Box } from '../ui-kit';
-import { useSearchParams } from 'react-router-dom';
import { parseSlugToIdAndType } from '../utils';
import { useAnalytics } from '../providers/AnalyticsProvider';
import { getComponentFromType } from '../utils/getContentFromURL';
+import { useNavigation } from '../providers/NavigationProvider';
function RenderFeatures(props) {
- const [searchParams] = useSearchParams();
- const _id = searchParams.get('id');
-
- const { type, randomId } = parseSlugToIdAndType(_id) ?? {};
+ const { id } = useNavigation();
+ const { type, randomId } = parseSlugToIdAndType(id) ?? {};
const Component = getComponentFromType({ type, id: randomId });
@@ -71,7 +61,6 @@ const FeatureFeed = (props) => {
) : (
<>
-
>
)}
diff --git a/packages/web-shared/embeds/Main.js b/packages/web-shared/embeds/Main.js
index 965f8c8f..18271b4c 100644
--- a/packages/web-shared/embeds/Main.js
+++ b/packages/web-shared/embeds/Main.js
@@ -1,23 +1,15 @@
-import React, { useRef, useState } from 'react';
+import React from 'react';
import { createPortal } from 'react-dom';
-import { useSearchParams } from 'react-router-dom';
import { Searchbar } from '../components';
-import { ContentItemProvider, FeatureFeedProvider, ContentFeedProvider } from '../providers';
-import {
- ContentSingle,
- FeatureFeedList,
- ContentChannel,
- LivestreamSingle,
- Breadcrumbs,
- Modal,
-} from '../components';
-import { useModalState } from '../providers/ModalProvider';
+import { FeatureFeedProvider } from '../providers';
+import { FeatureFeedList, Modal } from '../components';
import { useSearchState } from '../providers/SearchProvider';
import { Box } from '../ui-kit';
import Styled from './Search.styles';
+import NavigationProvider from '../providers/NavigationProvider';
function RenderEmbed(props) {
const searchState = useSearchState();
@@ -53,30 +45,32 @@ function RenderEmbed(props) {
}
}
-const Main = () => {
+const Main = ({ type }) => {
const widgetDivs = Array.from(document.querySelectorAll('.apollos-widget')); // Convert NodeList to Array
return (
-
+
-
- {/* Portal all widgets component */}
- {widgetDivs.map((widget, index) => {
- return createPortal(
- ,
- widget
- );
- })}
+
+
+ {/* Portal all widgets component */}
+ {widgetDivs.map((widget, index) => {
+ return createPortal(
+ ,
+ widget
+ );
+ })}
+
-
+
);
};
diff --git a/packages/web-shared/embeds/Search.js b/packages/web-shared/embeds/Search.js
index 72e2203d..4ef3be04 100644
--- a/packages/web-shared/embeds/Search.js
+++ b/packages/web-shared/embeds/Search.js
@@ -5,7 +5,6 @@ import { Searchbar } from '../components';
import { ContentItemProvider, FeatureFeedProvider, ContentFeedProvider } from '../providers';
import {
- Breadcrumbs,
ContentChannel,
ContentSeriesSingle,
ContentSingle,
@@ -16,14 +15,14 @@ import {
import { useModalState } from '../providers/ModalProvider';
import { useSearchState } from '../providers/SearchProvider';
import { Box } from '../ui-kit';
+import { useNavigation } from '../providers/NavigationProvider';
import Styled from './Search.styles';
function RenderFeatures(props) {
- const [searchParams] = useSearchParams();
- const _id = searchParams.get('id');
+ const { id } = useNavigation();
- const [type, randomId] = _id?.split(/-(.*)/s) ?? [];
+ const [type, randomId] = id?.split(/-(.*)/s) ?? [];
switch (type) {
case 'MediaContentItem':
@@ -89,7 +88,6 @@ const Search = (props) => {
) : (
<>
-
>
)}
diff --git a/packages/web-shared/hooks/useHandleActionPress.js b/packages/web-shared/hooks/useHandleActionPress.js
new file mode 100644
index 00000000..9367f71f
--- /dev/null
+++ b/packages/web-shared/hooks/useHandleActionPress.js
@@ -0,0 +1,51 @@
+import { getURLFromType } from '../utils';
+
+import { open as openModal, set as setModal, useModal } from '../providers/ModalProvider';
+import { useAnalytics } from '../providers/AnalyticsProvider';
+import { useNavigation } from '../providers/NavigationProvider';
+
+export const useHandleActionPress = () => {
+ const [state, dispatch] = useModal();
+ const analytics = useAnalytics();
+ const { navigate, id } = useNavigation();
+
+ return (item) => {
+ if (item.action === 'OPEN_URL') {
+ analytics.track('OpenUrl', {
+ url: item?.relatedNode?.url,
+ });
+ return window.open(getURLFromType(item.relatedNode), '_blank');
+ }
+
+ if (id !== getURLFromType(item.relatedNode)) {
+ navigate({ id: getURLFromType(item.relatedNode) });
+ }
+ if (state.modal) {
+ const url = getURLFromType(item.relatedNode);
+ dispatch(setModal(url));
+ dispatch(openModal());
+ }
+ };
+};
+
+export const useHandlePrimaryActionPress = ({ title, primaryAction, id }) => {
+ const analytics = useAnalytics();
+ const { navigate, id: navId } = useNavigation();
+ return () => {
+ if (navId !== getURLFromType(primaryAction.relatedNode)) {
+ const primaryActionId = getURLFromType(primaryAction.relatedNode);
+
+ if (primaryAction?.action === 'OPEN_FEED') {
+ analytics.track('OpenFeatureFeed', {
+ featureFeedId: primaryAction?.relatedNode?.id,
+ fromFeatureId: id,
+ title: title,
+ });
+ }
+
+ navigate({ id: primaryActionId });
+ }
+ };
+};
+
+export default useHandleActionPress;
diff --git a/packages/web-shared/hooks/usePath.js b/packages/web-shared/hooks/usePath.js
new file mode 100644
index 00000000..e69de29b
diff --git a/packages/web-shared/providers/AppProvider.js b/packages/web-shared/providers/AppProvider.js
index 87ea7cd1..5983226f 100644
--- a/packages/web-shared/providers/AppProvider.js
+++ b/packages/web-shared/providers/AppProvider.js
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, createContext, useContext } from 'react';
import PropTypes from 'prop-types';
import { ApolloProvider } from '@apollo/client';
@@ -6,10 +6,13 @@ import initClient from '../client';
import { ThemeProvider } from '../ui-kit';
import AuthProvider from './AuthProvider';
import AnalyticsProvider from './AnalyticsProvider';
-import BreadcrumbProvider from './BreadcrumbProvider';
import ModalProvider from './ModalProvider';
import SearchProvider from './SearchProvider';
+const ShouldUsePathRouter = createContext(false);
+
+export const useShouldUsePathRouter = () => useContext(ShouldUsePathRouter);
+
function AppProvider(props = {}) {
const [client, setClient] = useState(null);
useEffect(() => {
@@ -23,24 +26,25 @@ function AppProvider(props = {}) {
if (!client) {
return null;
}
+
return (
-
-
-
-
-
+
+
+
+
+
{props.children}
-
-
-
-
-
+
+
+
+
+
);
}
diff --git a/packages/web-shared/providers/BreadcrumbProvider.js b/packages/web-shared/providers/BreadcrumbProvider.js
deleted file mode 100644
index 95133819..00000000
--- a/packages/web-shared/providers/BreadcrumbProvider.js
+++ /dev/null
@@ -1,153 +0,0 @@
-import PropTypes from 'prop-types';
-import React, { createContext, useContext, useReducer, useEffect } from 'react';
-
-const BreadcrumbStateContext = createContext();
-const BreadcrumbDispatchContext = createContext();
-
-const initialState = [];
-
-const actionTypes = {
- add: 'add',
- remove: 'remove',
- reset: 'reset',
- set: 'set',
-};
-
-const add = (payload) => ({
- type: 'add',
- payload,
-});
-
-const remove = (payload) => ({
- type: 'remove',
- payload,
-});
-
-const set = (payload) => ({
- type: 'set',
- payload,
-});
-
-const reset = () => ({
- type: 'reset',
-});
-
-function reducer(state, action) {
- switch (action.type) {
- case actionTypes.add: {
- return [
- ...state,
- {
- id: state.length
- ? Math.max(...state.map((breadcrumb) => breadcrumb.id)) + 1
- : 0,
- title: action.payload.title,
- url: action.payload.url,
- },
- ];
- }
- case actionTypes.remove: {
- // Probably should move dropRight to utils
- const dropRight = (arr, n = 1) => {
- const amountToDrop = arr.length - (n + 1);
- return arr.slice(0, -amountToDrop);
- };
-
- return dropRight(state, action.payload);
- }
- case actionTypes.set: {
- return action.payload;
- }
- case actionTypes.reset: {
- return initialState;
- }
- default: {
- throw new Error(`Unhandled action type: ${action.type}`);
- }
- }
-}
-
-function BreadcrumbProvider(props = {}) {
- const [state, dispatch] = useReducer(reducer, initialState);
-
- useEffect(() => {
- const handlePopstate = () => {
- const newBreadcrumbHistory =
- window.history.state?.breadcrumbHistory || [];
- dispatch(set(newBreadcrumbHistory));
- };
-
- window.addEventListener('popstate', handlePopstate);
-
- return () => {
- window.removeEventListener('popstate', handlePopstate);
- };
- }, []);
-
- useEffect(() => {
- const breadcrumbHistory = state.map((breadcrumb) => ({
- id: breadcrumb.id,
- title: breadcrumb.title,
- url: breadcrumb.url,
- }));
- window.history.replaceState(
- { ...window.history.state, breadcrumbHistory },
- ''
- );
- }, [state]);
-
- return (
-
-
- {props.children}
-
-
- );
-}
-
-// const state = useBreadcrumbState();
-function useBreadcrumbState() {
- const context = useContext(BreadcrumbStateContext);
- if (context === undefined) {
- throw new Error(
- 'useBreadcrumbState must be used within a BreadcrumbProvider'
- );
- }
- return context;
-}
-
-// const dispatch = useBreadcrumbDispatch();
-function useBreadcrumbDispatch() {
- const context = useContext(BreadcrumbDispatchContext);
- if (context === undefined) {
- throw new Error(
- 'useBreadcrumbDispatch must be used within a BreadcrumbProvider'
- );
- }
- return context;
-}
-
-// const [state, dispatch] = useBreadcrumb();
-function useBreadcrumb() {
- const context = [useBreadcrumbState(), useBreadcrumbDispatch()];
- if (context === undefined) {
- throw new Error('useBreadcrumb must be used within a BreadcrumbProvider');
- }
- return context;
-}
-
-BreadcrumbProvider.propTypes = {
- children: PropTypes.oneOfType([PropTypes.element, PropTypes.node]),
-};
-
-export {
- BreadcrumbProvider as default,
- useBreadcrumb,
- useBreadcrumbState,
- useBreadcrumbDispatch,
- actionTypes,
- add,
- remove,
- reset,
- set,
-};
diff --git a/packages/web-shared/providers/NavigationProvider.js b/packages/web-shared/providers/NavigationProvider.js
new file mode 100644
index 00000000..ea0bc62b
--- /dev/null
+++ b/packages/web-shared/providers/NavigationProvider.js
@@ -0,0 +1,53 @@
+import React, { createContext, useContext } from 'react';
+import { useSearchParams, useParams, useNavigate } from 'react-router-dom';
+import { useShouldUsePathRouter } from '../providers/AppProvider';
+
+const NavigationContext = createContext({ id: null, navigate: () => {} });
+
+const NavigationProvider = (props = {}) => {
+ const [searchParams] = useSearchParams();
+ const { contentId, feedId } = useParams();
+ const shouldUsePathRouter = useShouldUsePathRouter();
+ const nativeNavigate = useNavigate();
+
+ const id = contentId || feedId || searchParams.get('id');
+ let navigate = () => {};
+
+ if (shouldUsePathRouter) {
+ navigate = ({ id } = {}) => {
+ const type = id?.includes('FeatureFeed') ? 'feed' : 'content';
+ if (id) {
+ nativeNavigate({
+ pathname: `/${type === 'content' ? 'ac' : 'af'}/${id}`,
+ });
+ // Assume that no params
+ } else {
+ nativeNavigate({
+ pathname: '/',
+ });
+ }
+ };
+ } else {
+ navigate = ({ id, type } = {}) => {
+ if (id) {
+ nativeNavigate({
+ search: `?id=${id}`,
+ });
+ } else {
+ nativeNavigate({
+ pathname: '/',
+ });
+ }
+ };
+ }
+
+ return (
+
+ {props.children}
+
+ );
+};
+
+export const useNavigation = () => useContext(NavigationContext);
+
+export default NavigationProvider;
diff --git a/packages/web-shared/providers/index.js b/packages/web-shared/providers/index.js
index f842666d..cb1cdd04 100644
--- a/packages/web-shared/providers/index.js
+++ b/packages/web-shared/providers/index.js
@@ -1,17 +1,16 @@
import AppProvider from './AppProvider';
import AnalyticsProvider from './AnalyticsProvider';
-import BreadcrumbProvider from './BreadcrumbProvider';
import ContentFeedProvider from './ContentFeedProvider';
import ContentItemProvider from './ContentItemProvider';
import FeatureFeedProvider from './FeatureFeedProvider';
import ModalProvider from './ModalProvider';
import SearchProvider from './SearchProvider';
+import NavigationProvider from './NavigationProvider';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
export {
AppProvider,
AnalyticsProvider,
- BreadcrumbProvider,
ContentFeedProvider,
ContentItemProvider,
FeatureFeedProvider,
@@ -19,4 +18,5 @@ export {
SearchProvider,
createBrowserRouter,
RouterProvider,
+ NavigationProvider,
};
diff --git a/packages/web-shared/ui-kit/ListItem/ListItem.js b/packages/web-shared/ui-kit/ListItem/ListItem.js
index ce3eac13..e40c0480 100644
--- a/packages/web-shared/ui-kit/ListItem/ListItem.js
+++ b/packages/web-shared/ui-kit/ListItem/ListItem.js
@@ -6,33 +6,18 @@ import Styled from './ListItem.styles';
import { getURLFromType } from '../../utils';
import { useNavigate } from 'react-router-dom';
-function ListItem({
- title,
- subtitle,
- leadingIcon,
- tailingIcon,
- node,
- onClick,
- ...props
-}) {
+function ListItem({ title, subtitle, leadingIcon, tailingIcon, node, onClick, ...props }) {
// If item has link, redirect to URL
const navigate = useNavigate();
const handleActionPress = () => {
navigate({
- pathname: '/',
- search: `?id=${getURLFromType(node)}`,
+ id: getURLFromType(node),
});
};
// Default tailing icon
const Arrow = (
-