Add suggestion boxes for the major pages

This commit is contained in:
Jackson Harper
2023-08-31 17:18:24 +08:00
parent 300f4cbda2
commit e8fc315301
10 changed files with 263 additions and 54 deletions

View File

@ -0,0 +1,116 @@
import Link from 'next/link'
import { HStack, SpanBox, VStack } from './LayoutPrimitives'
import { ArrowRightIcon } from './icons/ArrowRightIcon'
import { theme } from '../tokens/stitches.config'
import { ReactNode } from 'react'
import { Button } from './Button'
import { CloseIcon } from './images/CloseIcon'
type SuggestionBoxProps = {
helpMessage: string
helpCTAText: string | undefined
helpTarget: string | undefined
size?: 'large' | 'small'
background?: string
dismissible?: boolean
onDismiss?: () => void
}
type InternalOrExternalLinkProps = {
link: string
children: ReactNode
}
const InternalOrExternalLink = (props: InternalOrExternalLinkProps) => {
const isExternal = props.link.startsWith('https')
return (
<SpanBox
css={{
cursor: 'pointer',
a: {
color: '$omnivoreCtaYellow',
},
}}
>
{!isExternal ? (
<Link href={props.link}>{props.children}</Link>
) : (
<a href={props.link} target="_blank" rel="noreferrer">
{props.children}
</a>
)}
</SpanBox>
)
}
export const SuggestionBox = (props: SuggestionBoxProps) => {
return (
<HStack
css={{
gap: '10px',
display: 'flex',
flexDirection: props.size == 'large' ? 'column' : 'row',
width: 'fit-content',
borderRadius: '5px',
background: props.background ?? '$thBackground3',
fontSize: '15px',
fontFamily: '$inter',
fontWeight: '500',
color: '$thTextContrast',
py: '10px',
px: '15px',
justifyContent: 'flex-start',
'@smDown': {
flexDirection: 'column',
alignItems: 'center',
width: '100%',
},
}}
>
<VStack>
{props.dismissible && (
<SpanBox
css={{
marginLeft: 'auto',
}}
>
<Button
style="plainIcon"
css={{
fontSize: '10',
fontWeight: '600',
}}
>
Dismiss
</Button>
</SpanBox>
)}
{props.helpMessage}
{props.helpTarget && (
<InternalOrExternalLink link={props.helpTarget}>
<SpanBox
css={{
display: 'flex',
alignItems: 'center',
color: '$omnivoreCtaYellow',
gap: '2px',
'&:hover': {
textDecoration: 'underline',
},
}}
>
<>{props.helpCTAText}</>
<ArrowRightIcon
size={25}
color={theme.colors.omnivoreCtaYellow.toString()}
/>
</SpanBox>
</InternalOrExternalLink>
)}
</VStack>
</HStack>
)
}

View File

@ -49,7 +49,6 @@ export function SettingsLayout(props: SettingsLayoutProps): JSX.Element {
<Box
css={{
height: HEADER_HEIGHT,
bg: '$grayBase',
}}
></Box>
{props.children}

View File

@ -9,6 +9,7 @@ import { searchQuery } from '../../../lib/networking/queries/search'
import { LIBRARY_LEFT_MENU_WIDTH } from './LibraryFilterMenu'
import { LayoutType } from './HomeFeedContainer'
import { ArrowRightIcon } from '../../elements/icons/ArrowRightIcon'
import { SuggestionBox } from '../../elements/SuggestionBox'
type EmptyLibraryProps = {
searchTerm: string | undefined
@ -17,7 +18,13 @@ type EmptyLibraryProps = {
layoutType: LayoutType
}
type MessageType = 'feed' | 'newsletter' | 'library'
type MessageType =
| 'inbox'
| 'files'
| 'archive'
| 'feed'
| 'newsletter'
| 'library'
type HelpMessageProps = {
type: MessageType
@ -83,6 +90,12 @@ const HelpMessage = (props: HelpMessageProps) => {
export const ErrorBox = (props: HelpMessageProps) => {
const errorTitle = useMemo(() => {
switch (props.type) {
case 'inbox':
return 'Your inbox is empty.'
case 'archive':
return 'You do not have any archived items.'
case 'files':
return 'No files found.'
case 'feed':
return 'You do not have any feed items matching this query.'
case 'newsletter':
@ -116,70 +129,70 @@ export const ErrorBox = (props: HelpMessageProps) => {
)
}
export const SuggestionBox = (props: HelpMessageProps) => {
export const Suggestion = (props: HelpMessageProps) => {
const helpMessage = useMemo(() => {
switch (props.type) {
case 'feed':
return 'Want to add an RSS or Atom Subscription?'
return ['Want to add an RSS or Atom Subscription?', 'Click Here']
case 'archive':
return [
'When you are done reading something archive it and it will be saved in Omnivore forever.',
'Read the Docs',
]
case 'files':
return [
'Drag PDFs into the library to add them to your Omnivore account.',
undefined,
]
case 'newsletter':
return 'Create an Omnivore email address and subscribe to newsletters.'
return [
'Create an Omnivore email address and subscribe to newsletters.',
'Click Here',
]
}
return "Add a link or read more about Omnivore's Advanced Search."
return [
"Add a link or read more about Omnivore's Advanced Search.",
'Read the Docs',
]
}, [props.type])
const helpTarget = useMemo(() => {
switch (props.type) {
case 'feed':
return '/settings/feeds'
case 'archive':
case 'files':
return undefined
case 'archive':
case 'inbox':
return 'https://docs.omnivore.app/using/saving'
case 'newsletter':
return '/settings/emails'
}
return 'https://docs.omnivore.app/'
}, [props.type])
const helpTargetWindow = useMemo(() => {
switch (props.type) {
case 'archive':
case 'inbox':
return '_blank'
}
return undefined
}, [props.type])
return (
<HStack
css={{
gap: '10px',
width: 'fit-content',
borderRadius: '5px',
background: '$thBackground3',
fontSize: '15px',
fontFamily: '$inter',
fontWeight: '500',
color: '$thTextContrast',
padding: '10px',
justifyContent: 'flex-start',
'@smDown': {
flexDirection: 'column',
alignItems: 'center',
width: '100%',
},
}}
>
{helpMessage}
<SpanBox css={{ cursor: 'pointer' }}>
<Link href={helpTarget} passHref>
<SpanBox
css={{
display: 'flex',
alignItems: 'center',
color: '$omnivoreCtaYellow',
gap: '2px',
'&:hover': {
textDecoration: 'underline',
},
}}
>
<>Click Here</>
<ArrowRightIcon
size={25}
color={theme.colors.omnivoreCtaYellow.toString()}
/>
</SpanBox>
</Link>
</SpanBox>
</HStack>
<>
{helpMessage[0] ? (
<SuggestionBox
helpMessage={helpMessage[0]}
helpCTAText={helpMessage[1]}
helpTarget={helpTarget}
/>
) : (
<></>
)}
</>
)
}
@ -187,6 +200,12 @@ export const EmptyLibrary = (props: EmptyLibraryProps) => {
const type = useMemo<MessageType>(() => {
if (props.searchTerm) {
switch (props.searchTerm) {
case 'in:archive':
return 'archive'
case 'in:inbox':
return 'inbox'
case 'type:file':
return 'files'
case 'label:RSS':
return 'feed'
case 'label:Newsletter':
@ -205,9 +224,7 @@ export const EmptyLibrary = (props: EmptyLibraryProps) => {
pl: '0px',
width: '100%',
'@media (max-width: 1300px)': {
flexDirection: 'column',
},
flexDirection: 'column',
'@media (max-width: 768px)': {
p: '15px',
@ -232,7 +249,7 @@ export const EmptyLibrary = (props: EmptyLibraryProps) => {
}}
>
<ErrorBox type={type} />
<SuggestionBox type={type} />
<Suggestion type={type} />
</Box>
)
}

View File

@ -8,6 +8,8 @@ import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { StyledText } from '../../elements/StyledText'
import { theme } from '../../tokens/stitches.config'
import { SettingsLayout } from '../SettingsLayout'
import { SuggestionBox } from '../../elements/SuggestionBox'
import { usePersistedState } from '../../../lib/hooks/usePersistedState'
type SettingsTableProps = {
pageId: string
@ -17,6 +19,8 @@ type SettingsTableProps = {
createTitle?: string
createAction?: () => void
suggestionInfo: SuggestionInfo
children: React.ReactNode
}
@ -52,6 +56,12 @@ type MoreOptionsProps = {
onEdit?: () => void
}
type SuggestionInfo = {
text: string
docs: string
key: string
}
const MoreOptions = (props: MoreOptionsProps) => (
<Dropdown
align={'end'}
@ -273,6 +283,11 @@ const CreateButton = (props: CreateButtonProps): JSX.Element => {
}
export const SettingsTable = (props: SettingsTableProps): JSX.Element => {
const [showSuggestion, setShowSuggestion] = usePersistedState<boolean>({
key: props.suggestionInfo.key,
initialValue: !!props.suggestionInfo,
})
return (
<SettingsLayout>
<Toaster
@ -299,6 +314,21 @@ export const SettingsTable = (props: SettingsTableProps): JSX.Element => {
},
}}
>
{props.suggestionInfo && showSuggestion && (
<Box css={{ my: '40px', width: '100%' }}>
<SuggestionBox
helpMessage={props.suggestionInfo.text}
helpCTAText={'Read the Docs'}
helpTarget={props.suggestionInfo.docs}
size={'large'}
background="$thBackground5"
dismissible={true}
onDismiss={() => {
setShowSuggestion(false)
}}
/>
</Box>
)}
<Box
css={{
width: '100%',

View File

@ -121,6 +121,11 @@ export default function Api(): JSX.Element {
setExpiresAt(neverExpiresDate)
setAddModalOpen(true)
}}
suggestionInfo={{
text: 'Create API keys to connect Omnivore to other apps such as Logseq and Obsidian or to query the API. Check out the integrations page for more info on connecting to Omnivore via the API.',
docs: 'https://docs.omnivore.app/integrations/api.html',
key: '--settings-apikeys-show-help',
}}
>
{sortedApiKeys.length > 0 ? (
sortedApiKeys.map((apiKey, i) => {

View File

@ -22,6 +22,7 @@ import {
SettingsTableRow,
} from '../../../components/templates/settings/SettingsTable'
import { ConfirmationModal } from '../../../components/patterns/ConfirmationModal'
import { SuggestionBox } from '../../../components/elements/SuggestionBox'
enum TextType {
EmailAddress,
@ -121,6 +122,11 @@ export default function EmailsPage(): JSX.Element {
headerTitle="Address"
createTitle="Create a new email address"
createAction={createEmail}
suggestionInfo={{
text: 'Create an Omnivore email address and use it to subscribe to newsletters. The newsletters will be automatically added to your library.',
docs: 'https://docs.omnivore.app/using/inbox.html',
key: '--settings-emails-show-help',
}}
>
{sortedEmailAddresses.length > 0 ? (
sortedEmailAddresses.map((email, i) => {

View File

@ -168,9 +168,14 @@ export default function RecentEmails(): JSX.Element {
return (
<SettingsTable
pageId="api-keys"
pageId="recent-emails"
pageInfoLink="https://docs.omnivore.app/using/inbox.html"
headerTitle="Recently Received Emails"
suggestionInfo={{
text: 'View all the original content of emails that have been recently received in your Omnivore inbox. If an email was not correctly classified as an article you can mark it as an article.',
docs: 'https://docs.omnivore.app/using/inbox.html',
key: '--settings-recent-emails-show-help',
}}
>
{sortedRecentEmails.length > 0 ? (
sortedRecentEmails.map((recentEmail: RecentEmail, i) => {

View File

@ -96,6 +96,11 @@ export default function Rss(): JSX.Element {
createAction={() => {
router.push('/settings/feeds/add')
}}
suggestionInfo={{
text: 'Add RSS and Atom feeds to your Omnivore account. When you add a new feed the last 24hrs of items, or at least one item will be added to your account.',
docs: 'https://docs.omnivore.app/using/feeds.html',
key: '--settings-feeds-show-help',
}}
>
{subscriptions.length === 0 ? (
<EmptySettingsRow text={isValidating ? '-' : 'No feeds subscribed'} />

View File

@ -35,6 +35,8 @@ import {
import { LabelChip } from '../../components/elements/LabelChip'
import { ConfirmationModal } from '../../components/patterns/ConfirmationModal'
import { InfoLink } from '../../components/elements/InfoLink'
import { SuggestionBox } from '../../components/elements/SuggestionBox'
import { usePersistedState } from '../../lib/hooks/usePersistedState'
const HeaderWrapper = styled(Box, {
width: '100%',
@ -153,6 +155,10 @@ export default function LabelsPage(): JSX.Element {
const [confirmRemoveLabelId, setConfirmRemoveLabelId] = useState<
string | null
>(null)
const [showLabelPageHelp, setShowLabelPageHelp] = usePersistedState<boolean>({
key: `--settings-labels-show-help`,
initialValue: true,
})
const breakpoint = 768
applyStoredTheme(false)
@ -270,6 +276,21 @@ export default function LabelsPage(): JSX.Element {
onOpenChange={() => setConfirmRemoveLabelId(null)}
/>
) : null}
{showLabelPageHelp && (
<Box css={{ mt: '40px' }}>
<SuggestionBox
helpMessage="Use this page to view and edit all your labels. Labels can be attached to individual library items, or your highlights, and are used to keep your library organized."
helpCTAText={'Read the Docs'}
helpTarget="https://docs.omnivore.app/using/organizing.html"
size={'large'}
background="$thBackground5"
dismissible={true}
onDismiss={() => {
setShowLabelPageHelp(false)
}}
/>
</Box>
)}
<HeaderWrapper>
<Box
style={{

View File

@ -44,8 +44,13 @@ export default function SubscriptionsPage(): JSX.Element {
return (
<SettingsTable
pageId="settings-subscriptions-tag"
pageInfoLink="https://docs.omnivore.app/using/inbox.html"
pageInfoLink="https://docs.omnivore.app/using/feeds.html"
headerTitle="Subscriptions"
suggestionInfo={{
text: 'View and manage all your Feed and Newsletter subscriptions. You can add RSS or Atom subscriptions on the Feeds page and create emails to subscribe to newsletters',
docs: 'https://docs.omnivore.app/using/inbox.html',
key: '--settings-recent-subscriptions-show-help',
}}
>
<>
{sortedSubscriptions.length > 0 ? (