From def95d2b5cddaf722f80bc07bc7685673809b590 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 21 Sep 2023 12:02:02 -0500 Subject: [PATCH] Add a LandingTimeline, put the site name and description on it --- src/containers/soapbox.tsx | 9 +- .../landing-timeline/components/logo-text.tsx | 16 ++ .../components/site-banner.tsx | 24 +++ src/features/landing-timeline/index.tsx | 68 +++++++++ src/features/ui/index.tsx | 11 +- src/features/ui/util/async-components.ts | 4 + src/pages/landing-page.tsx | 137 ++++++++++++++++++ 7 files changed, 260 insertions(+), 9 deletions(-) create mode 100644 src/features/landing-timeline/components/logo-text.tsx create mode 100644 src/features/landing-timeline/components/site-banner.tsx create mode 100644 src/features/landing-timeline/index.tsx create mode 100644 src/pages/landing-page.tsx diff --git a/src/containers/soapbox.tsx b/src/containers/soapbox.tsx index fb5a15971..21897ab8c 100644 --- a/src/containers/soapbox.tsx +++ b/src/containers/soapbox.tsx @@ -18,7 +18,6 @@ import Helmet from 'soapbox/components/helmet'; import LoadingScreen from 'soapbox/components/loading-screen'; import { StatProvider } from 'soapbox/contexts/stat-context'; import EmbeddedStatus from 'soapbox/features/embedded-status'; -import PublicLayout from 'soapbox/features/public-layout'; import BundleContainer from 'soapbox/features/ui/containers/bundle-container'; import { ModalContainer, @@ -95,12 +94,8 @@ const SoapboxMount = () => { /** Render the auth layout or UI. */ const renderSwitch = () => ( - {!me && (redirectRootNoLogin - ? - : )} - - {!me && ( - + {(!me && redirectRootNoLogin) && ( + )} diff --git a/src/features/landing-timeline/components/logo-text.tsx b/src/features/landing-timeline/components/logo-text.tsx new file mode 100644 index 000000000..eeaaa0070 --- /dev/null +++ b/src/features/landing-timeline/components/logo-text.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +interface ILogoText { + children: React.ReactNode +} + +/** Big text in site colors, for displaying the site name. Resizes itself according to the screen size. */ +const LogoText: React.FC = ({ children }) => { + return ( +

+ {children} +

+ ); +}; + +export { LogoText }; \ No newline at end of file diff --git a/src/features/landing-timeline/components/site-banner.tsx b/src/features/landing-timeline/components/site-banner.tsx new file mode 100644 index 000000000..91bd0bcc3 --- /dev/null +++ b/src/features/landing-timeline/components/site-banner.tsx @@ -0,0 +1,24 @@ +import React from 'react'; + +import Markup from 'soapbox/components/markup'; +import { Stack } from 'soapbox/components/ui'; +import { useInstance } from 'soapbox/hooks'; + +import { LogoText } from './logo-text'; + +const SiteBanner: React.FC = () => { + const instance = useInstance(); + + return ( + + {instance.title} + + + + ); +}; + +export { SiteBanner }; \ No newline at end of file diff --git a/src/features/landing-timeline/index.tsx b/src/features/landing-timeline/index.tsx new file mode 100644 index 000000000..4cba39460 --- /dev/null +++ b/src/features/landing-timeline/index.tsx @@ -0,0 +1,68 @@ +import React, { useEffect } from 'react'; +import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; + +import { expandCommunityTimeline } from 'soapbox/actions/timelines'; +import { useCommunityStream } from 'soapbox/api/hooks'; +import PullToRefresh from 'soapbox/components/pull-to-refresh'; +import { Column } from 'soapbox/components/ui'; +import { useAppSelector, useAppDispatch, useSettings } from 'soapbox/hooks'; + +import Sonar from '../public-layout/components/sonar'; +import Timeline from '../ui/components/timeline'; + +import { SiteBanner } from './components/site-banner'; + +const messages = defineMessages({ + title: { id: 'column.community', defaultMessage: 'Local timeline' }, +}); + +const LandingTimeline = () => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + + const settings = useSettings(); + const onlyMedia = !!settings.getIn(['community', 'other', 'onlyMedia'], false); + const next = useAppSelector(state => state.timelines.get('community')?.next); + + const timelineId = 'community'; + + const handleLoadMore = (maxId: string) => { + dispatch(expandCommunityTimeline({ url: next, maxId, onlyMedia })); + }; + + const handleRefresh = () => { + return dispatch(expandCommunityTimeline({ onlyMedia })); + }; + + useCommunityStream({ onlyMedia }); + + useEffect(() => { + dispatch(expandCommunityTimeline({ onlyMedia })); + }, [onlyMedia]); + + return ( + +
+
+ +
+
+ +
+
+ + + } + divideType='space' + /> + +
+ ); +}; + +export default LandingTimeline; diff --git a/src/features/ui/index.tsx b/src/features/ui/index.tsx index 5ff06223e..fa1e206dd 100644 --- a/src/features/ui/index.tsx +++ b/src/features/ui/index.tsx @@ -21,7 +21,7 @@ import withHoc from 'soapbox/components/hoc/with-hoc'; import SidebarNavigation from 'soapbox/components/sidebar-navigation'; import ThumbNavigation from 'soapbox/components/thumb-navigation'; import { Layout } from 'soapbox/components/ui'; -import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig, useFeatures, useDraggedFiles, useInstance } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig, useFeatures, useDraggedFiles, useInstance, useLoggedIn } from 'soapbox/hooks'; import AdminPage from 'soapbox/pages/admin-page'; import ChatsPage from 'soapbox/pages/chats-page'; import DefaultPage from 'soapbox/pages/default-page'; @@ -31,6 +31,7 @@ import GroupPage from 'soapbox/pages/group-page'; import GroupsPage from 'soapbox/pages/groups-page'; import GroupsPendingPage from 'soapbox/pages/groups-pending-page'; import HomePage from 'soapbox/pages/home-page'; +import LandingPage from 'soapbox/pages/landing-page'; import ManageGroupsPage from 'soapbox/pages/manage-groups-page'; import ProfilePage from 'soapbox/pages/profile-page'; import RemoteInstancePage from 'soapbox/pages/remote-instance-page'; @@ -139,6 +140,7 @@ import { PasswordResetConfirm, RegisterInvite, ExternalLogin, + LandingTimeline, } from './util/async-components'; import GlobalHotkeys from './util/global-hotkeys'; import { WrappedRoute } from './util/react-router-helpers'; @@ -167,6 +169,7 @@ const SwitchingColumnsArea: React.FC = ({ children }) => const instance = useInstance(); const features = useFeatures(); const { search } = useLocation(); + const { isLoggedIn } = useLoggedIn(); const { authenticatedProfile, cryptoAddresses } = useSoapboxConfig(); const hasCrypto = cryptoAddresses.size > 0; @@ -181,7 +184,11 @@ const SwitchingColumnsArea: React.FC = ({ children }) => - + {isLoggedIn ? ( + + ) : ( + + )} {/* NOTE: we cannot nest routes in a fragment diff --git a/src/features/ui/util/async-components.ts b/src/features/ui/util/async-components.ts index bef8fcf30..120617f49 100644 --- a/src/features/ui/util/async-components.ts +++ b/src/features/ui/util/async-components.ts @@ -10,6 +10,10 @@ export function Notifications() { return import('../../notifications'); } +export function LandingTimeline() { + return import('../../landing-timeline'); +} + export function HomeTimeline() { return import('../../home-timeline'); } diff --git a/src/pages/landing-page.tsx b/src/pages/landing-page.tsx new file mode 100644 index 000000000..8a892e938 --- /dev/null +++ b/src/pages/landing-page.tsx @@ -0,0 +1,137 @@ +import clsx from 'clsx'; +import React, { useRef } from 'react'; +import { useIntl } from 'react-intl'; +import { Link } from 'react-router-dom'; + +import { uploadCompose } from 'soapbox/actions/compose'; +import LinkFooter from 'soapbox/features/ui/components/link-footer'; +import { + WhoToFollowPanel, + TrendsPanel, + SignUpPanel, + PromoPanel, + FundingPanel, + CryptoDonatePanel, + BirthdayPanel, + CtaBanner, + AnnouncementsPanel, +} from 'soapbox/features/ui/util/async-components'; +import { useAppSelector, useOwnAccount, useFeatures, useSoapboxConfig, useDraggedFiles, useAppDispatch } from 'soapbox/hooks'; + +import { Avatar, Card, CardBody, HStack, Layout } from '../components/ui'; +import ComposeForm from '../features/compose/components/compose-form'; +import BundleContainer from '../features/ui/containers/bundle-container'; + +interface ILandingPage { + children: React.ReactNode +} + +const LandingPage: React.FC = ({ children }) => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + + const me = useAppSelector(state => state.me); + const { account } = useOwnAccount(); + const features = useFeatures(); + const soapboxConfig = useSoapboxConfig(); + + const composeId = 'home'; + const composeBlock = useRef(null); + + const hasPatron = soapboxConfig.extensions.getIn(['patron', 'enabled']) === true; + const hasCrypto = typeof soapboxConfig.cryptoAddresses.getIn([0, 'ticker']) === 'string'; + const cryptoLimit = soapboxConfig.cryptoDonatePanel.get('limit', 0); + + const { isDragging, isDraggedOver } = useDraggedFiles(composeBlock, (files) => { + dispatch(uploadCompose(composeId, files, intl)); + }); + + const acct = account ? account.acct : ''; + const avatar = account ? account.avatar : ''; + + return ( + <> + + {me && ( + + + + + + + +
+ +
+
+
+
+ )} + + {children} + + {!me && ( + + {Component => } + + )} +
+ + + {!me && ( + + {Component => } + + )} + {me && features.announcements && ( + + {Component => } + + )} + {features.trends && ( + + {Component => } + + )} + {hasPatron && ( + + {Component => } + + )} + {hasCrypto && cryptoLimit > 0 && ( + + {Component => } + + )} + + {Component => } + + {features.birthdays && ( + + {Component => } + + )} + {me && features.suggestions && ( + + {Component => } + + )} + + + + ); +}; + +export default LandingPage;