Merge pull request #3903 from omnivore-app/feat/web-landing-and-onboarding-improvements

feat/web landing and onboarding improvements
This commit is contained in:
Jackson Harper 2024-05-07 14:52:43 +08:00 committed by GitHub
commit 0bc84670d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 510 additions and 316 deletions

View file

@ -0,0 +1,198 @@
import React from 'react'
import {
Desktop,
DeviceTabletSpeaker,
DeviceMobileCamera,
} from 'phosphor-react'
import { Box, HStack } from './LayoutPrimitives'
import { StyledText, StyledAnchor } from './StyledText'
type MobileInstallHelpProps = {
onboarding?: boolean
}
export default function AndroidInstallHelp({
onboarding = false,
}: MobileInstallHelpProps): JSX.Element {
const iosContainerStyles = {
marginTop: '12px',
width: '100%',
height: '40px',
display: 'flex',
justifyContent: !onboarding ? 'flex-end' : 'initial',
}
return (
<Box
css={{
display: 'grid',
gridTemplateColumns: '1fr 2fr',
gridTemplateRows: !onboarding ? '.5fr .5fr .5fr' : '.5fr',
backgroundColor: '$grayBg',
padding: '15px',
'@lg': {
marginTop: '0',
paddingTop: '0',
gridTemplateColumns: '1fr 2fr 1fr',
gridTemplateRows: '1fr',
height: '9rem',
},
}}
>
<Box
css={{
gridColumn: 1 / 2,
gridRow: 1 / 2,
marginRight: '$3',
minWidth: '170px',
maxWidth: '200px',
alignSelf: 'center',
'@lg': {
minWidth: '200px',
gridColumn: '1',
gridRow: '1',
},
backgroundColor: '$grayBase',
display: 'flex',
position: 'relative',
height: '116px',
}}
>
<Box
css={{
position: 'absolute',
top: '-15px',
}}
>
<img
srcSet="/static/images/mobile-app-preview.png,
/static/images/mobile-app-preview@2x.png 2x"
/>
</Box>
</Box>
<Box
css={{
gridColumn: '2',
gridRow: '1',
display: 'flex',
flexDirection: 'column',
'@lg': {
marginTop: '16px',
},
}}
>
<StyledText
as={'h3'}
css={{
fontSize: '18px',
fontWeight: 700,
marginTop: 0,
marginBottom: 0,
color: !onboarding ? '$grayTextContrast' : 'rgba(10, 8, 6, 0.8)',
lineHeight: '22.5px',
'@lg': {
fontSize: '16px',
lineHeight: '20px',
},
}}
>
Install Omnivore for Android
</StyledText>
</Box>
<StyledText
css={{
size: '14px',
my: '$2',
fontWeight: 400,
color: !onboarding ? '$grayTextContrast' : 'rgba(10, 8, 6, 0.8)',
maxWidth: '20rem',
lineHeight: '21px',
gridColumn: '1 / span 3',
gridRow: '2 / 3',
alignSelf: 'center',
'@lgDown': {
display: !onboarding ? 'initial' : 'none',
},
'@lg': {
gridColumn: '2',
gridRow: '1',
alignSelf: 'center',
marginTop: !onboarding ? '$4' : 65,
},
}}
>
Use our native Android app (currently a preview release), to save any
link, access your full library, and read offline.
<br />
</StyledText>
<HStack
alignment="center"
css={{
gridRow: '3',
display: 'flex',
alignItems: 'center',
gridColumn: '1 / span 2',
flexDirection: !onboarding ? 'row-reverse' : 'inherit',
justifyContent: !onboarding ? 'space-between' : 'center',
mt: !onboarding ? 'inherit' : 10,
'@lg': {
flexDirection: !onboarding ? 'row-reverse' : 'column-reverse',
alignItems: !onboarding ? 'center' : 'flex-end',
gridColumn: '3',
gridRow: '1',
},
}}
>
<Box
css={{
m: '0px',
marginTop: '12px',
width: '100%',
height: '60px',
display: 'flex',
marginRight: '-10px',
justifyContent: 'flex-end',
}}
>
<a href="https://omnivore.app/install/android">
<img
alt="Get it on Google Play"
style={{
padding: '0px',
height: 'auto',
width: '140px',
marginLeft: 'auto',
}}
src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png"
/>
</a>
</Box>
<HStack
css={
!onboarding
? {
width: '40%',
justifyContent: 'space-between',
maxWidth: '13rem',
visibility: 'collapse',
'@lg': {
width: '100%',
visibility: 'unset',
},
}
: {
width: '40%',
justifyContent: 'space-between',
maxWidth: '13rem',
visibility: 'unset',
'@lg': {
width: '146px',
// maxWidth: '210px'
},
}
}
></HStack>
</HStack>
</Box>
)
}

View file

@ -18,6 +18,22 @@ export const Button = styled('button', {
border: '1px solid $grayBorderHover',
},
},
ctaOmnivoreYellow: {
borderRadius: '5px',
px: '20px',
py: '8px',
fontSize: '14px',
fontWeight: '500',
cursor: 'pointer',
border: '0px solid $yellow3',
bg: '$omnivoreYellow',
color: '#2A2A2A',
'&:hover': {
opacity: 1.0,
border: '0px solid #FFD800',
background: `#FFD800`,
},
},
ctaBlue: {
borderRadius: '5px',
px: '20px',
@ -25,12 +41,12 @@ export const Button = styled('button', {
fontSize: '14px',
fontWeight: '500',
cursor: 'pointer',
border: '1px solid $yellow3',
border: '0px solid $yellow3',
bg: '$ctaBlue',
color: 'white',
'&:hover': {
opacity: '0.6',
border: '0px solid $ctaBlue',
border: '0px solid #0056D2',
background: `#0056D2`,
},
},
tldr: {
@ -69,6 +85,38 @@ export const Button = styled('button', {
outline: '1px solid $omnivoreCtaYellow',
},
},
landingCta: {
borderRadius: 10,
backgroundColor: '#1A1A1A',
padding: '12px 100px',
color: '#FFFFFF',
font: '$inter',
fontSize: '20px',
fontWeight: '500',
textDecoration: 'none',
transition: 'background-color ease-out 50ms',
boxShadow: `0px 4px 4px 0px rgba(0, 0, 0, 0.15)`,
'&:hover': {
backgroundColor: '#3D3D3D',
outline: '0px solid #1A1A1A',
},
},
landingSimple: {
borderRadius: 10,
backgroundColor: '#1A1A1A',
padding: '10px 20px',
color: '#FFFFFF',
font: '$inter',
fontSize: '20px',
fontWeight: '500',
textDecoration: 'none',
transition: 'background-color ease-out 50ms',
'&:hover': {
backgroundColor: '#1A1A1A',
outline: '0px solid #1A1A1A',
boxShadow: `0px 4px 4px 0px rgba(0, 0, 0, 0.15)`,
},
},
cancelGeneric: {
fontSize: '13px',
fontWeight: 500,
@ -84,6 +132,21 @@ export const Button = styled('button', {
outline: '1px solid $omnivoreCtaYellow',
},
},
cancelAuth: {
fontSize: '13px',
fontWeight: 500,
fontFamily: 'Inter',
cursor: 'pointer',
color: '#6A6968',
borderRadius: '5px',
border: '1px solid transparent',
p: '10px 15px',
bg: 'transparent',
'&:hover': {
backgroundColor: '#2A2A2A',
outline: '0px solid #2A2A2A',
},
},
ctaOutlineYellow: {
boxSizing: 'border-box',
borderColor: 'unset',

View file

@ -4,38 +4,16 @@ import {
DeviceTabletSpeaker,
DeviceMobileCamera,
} from 'phosphor-react'
import { Box, HStack } from '../elements/LayoutPrimitives'
import { StyledText, StyledAnchor } from '../elements/StyledText'
const TooltipStyle = {
backgroundColor: '#F9D354',
color: '#0A0806',
}
import { Box, HStack } from './LayoutPrimitives'
import { StyledText, StyledAnchor } from './StyledText'
type MobileInstallHelpProps = {
onboarding?: boolean
}
export default function MobileInstallHelp({
export default function IOSInstallHelp({
onboarding = false,
}: MobileInstallHelpProps): JSX.Element {
const [selectedTooltip, setSelectedTooltip] =
React.useState<string>('Available for Mac')
const platformSizes = [
{
label: 'Available for Mac',
icon: <Desktop color="#F9D354" />,
},
{
label: 'Available for iPad',
icon: <DeviceTabletSpeaker color="#F9D354" />,
},
{
label: 'Available for iPhone',
icon: <DeviceMobileCamera color="#F9D354" />,
},
]
const iosContainerStyles = {
marginTop: '12px',
width: '100%',
@ -143,24 +121,9 @@ export default function MobileInstallHelp({
},
}}
>
With the Omnivore for iOS and macOS app installed you can save any link
using our Share extension.
<br />
{!onboarding && (
<StyledAnchor
href="https://docs.omnivore.app/using/saving.html"
target="_blank"
rel="noreferrer"
css={{
color: '$grayTextContrast',
fontSize: '14px',
fontWeight: 600,
textDecoration: 'underline',
}}
>
Learn more about the iOS and macOS app -&gt;
</StyledAnchor>
)}
With the native Omnivore for iOS and macOS app installed you can save
any link, read offline, and listen to your saved items using
text-to-speech.
</StyledText>
<HStack
alignment="center"
@ -231,49 +194,7 @@ export default function MobileInstallHelp({
},
}
}
>
{platformSizes.map((item, idx) => (
<Box
key={`platformSize-${idx}`}
title={item.label}
css={{
ml: '$1',
}}
>
<StyledAnchor
onClick={() => setSelectedTooltip(item.label)}
css={{
mx: 'auto',
borderRadius: '50%',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
height: 35,
width: 35,
cursor: 'pointer',
backgroundColor: '$labelButtonsBg',
...(selectedTooltip !== item.label && {
filter: 'grayscale(1)',
}),
'&:focus': {
filter: 'grayscale(0)',
},
'&:active': {
filter: 'grayscale(0)',
},
'@lg': {
transition: 'filter .3s linear',
'&:hover': {
filter: 'grayscale(0)',
},
},
}}
>
{item.icon}
</StyledAnchor>
</Box>
))}
</HStack>
></HStack>
</HStack>
</Box>
)

View file

@ -9,6 +9,7 @@ export const Recaptcha = (props: RecaptchaProps): JSX.Element => {
<>
<GoogleReCaptchaCheckbox
key="recaptcha"
theme="dark"
onChange={(token) => {
console.log('recaptcha: ', token)
props.setRecaptchaToken(token)

View file

@ -45,9 +45,8 @@ export const SplitButton = (props: SplitButtonProps): JSX.Element => {
<Button
css={{
display: 'flex',
// minWidth: '70px',
bg: '$ctaBlue',
color: '#EDEDED',
bg: '$omnivoreYellow',
color: '#2A2A2A',
fontSize: '14px',
fontFamily: '$inter',
border: '0px solid transparent',
@ -56,7 +55,7 @@ export const SplitButton = (props: SplitButtonProps): JSX.Element => {
borderTopRightRadius: '5px',
borderBottomRightRadius: '5px',
'&:hover': {
opacity: 0.6,
background: '#FFD800',
border: '0px solid transparent',
},
'&:focus': {

View file

@ -16,7 +16,7 @@ export function About(props: AboutProps): JSX.Element {
<LandingHeader />
<VStack
alignment="center"
css={{ background: '#FEFCF5', color: '#3D3D3D' }}
css={{ background: '#2A2A2A', color: '#898989' }}
>
<VStack
css={{
@ -36,7 +36,7 @@ export function About(props: AboutProps): JSX.Element {
as="p"
css={{
fontWeight: '700',
color: '#3D3D3D',
color: '#EDEDED',
fontSize: 45,
lineHeight: '53px',
padding: '10px 10px 0',
@ -50,9 +50,9 @@ export function About(props: AboutProps): JSX.Element {
readers.`}
</Box>
<Box
as="p"
as="p"
css={{
color: 'rgb(125, 125, 125)',
color: '#898989',
padding: '10px',
margin: 0,
textAlign: 'center',
@ -69,7 +69,7 @@ export function About(props: AboutProps): JSX.Element {
<Box
as="p"
css={{
color: 'rgb(125, 125, 125)',
color: '#898989',
padding: '10px',
margin: 0,
textAlign: 'center',

View file

@ -15,9 +15,9 @@ export function AuthLayout(props: ProfileLayoutProps): JSX.Element {
alignment="center"
distribution="center"
css={{
// bg: '$omnivoreYellow',
height: '100vh',
bg: '$omnivoreYellow',
color: '#898989',
background: '#2A2A2A',
}}
>
{props.children}
@ -46,7 +46,7 @@ export function AuthLayout(props: ProfileLayoutProps): JSX.Element {
}}
>
<OmnivoreNameLogo
color={theme.colors.omnivoreGray.toString()}
color="#898989"
href={props.logoDestination ?? '/login'}
/>
</HStack>

View file

@ -8,6 +8,7 @@ import {
appleAuthRedirectURI,
} from '../../lib/appConfig'
import { AppleIdButton } from './auth/AppleIdButton'
import { useEffect, useRef } from 'react'
export type LoginFormProps = {
errorMessage?: string
@ -19,7 +20,7 @@ export function LoginForm(props: LoginFormProps): JSX.Element {
fontWeight: '700',
fontSize: '45px',
lineHeight: '53px',
color: '$omnivoreGray',
color: '#FFFFFF',
m: '0px',
})
@ -45,13 +46,12 @@ export function LoginForm(props: LoginFormProps): JSX.Element {
fontSize: '18px',
lineHeight: '120%',
m: '0px',
color: '$omnivoreGray',
color: '#898989',
}}
>
Save articles and read them later in our distraction-free reader.
</StyledText>
<Link passHref href="/about" style={{ textDecoration: 'none' }}>
<StyledText
css={{
fontStyle: 'normal',
@ -59,12 +59,11 @@ export function LoginForm(props: LoginFormProps): JSX.Element {
fontSize: '18px',
lineHeight: '120%',
m: '0px',
color: '$omnivoreGray',
color: '#898989',
}}
>
Learn More -&gt;
</StyledText>
</Link>
<SpanBox css={{ height: '24px' }} />
@ -106,7 +105,7 @@ export function LoginForm(props: LoginFormProps): JSX.Element {
<Link href="/auth/email-login" passHref legacyBehavior>
<StyledTextSpan
style="actionLink"
css={{ color: '$omnivoreGray', pt: '12px' }}
css={{ color: '#EDEDED', pt: '12px' }}
>
Continue with Email
</StyledTextSpan>
@ -114,10 +113,24 @@ export function LoginForm(props: LoginFormProps): JSX.Element {
</VStack>
<TermAndConditionsFooter />
</VStack>
);
)
}
function GoogleAuthButton() {
useEffect(() => {
const script = document.createElement('script')
script.src = 'https://accounts.google.com/gsi/client'
script.async = true
script.defer = true
document.body.appendChild(script)
return () => {
document.body.removeChild(script)
}
}, [])
return (
<Box css={{ overflow: 'hidden' }}>
<div
@ -155,16 +168,22 @@ export function TermAndConditionsFooter(): JSX.Element {
>
By signing up, you agree to Omnivores{' '}
<Link href="/terms" passHref legacyBehavior>
<StyledTextSpan style="captionLink" css={{ color: '$omnivoreGray' }}>
<StyledTextSpan
style="captionLink"
css={{ color: '$omnivoreLightGray' }}
>
Terms of Service
</StyledTextSpan>
</Link>{' '}
and{' '}
<Link href="/privacy" passHref legacyBehavior>
<StyledTextSpan style="captionLink" css={{ color: '$omnivoreGray' }}>
<StyledTextSpan
style="captionLink"
css={{ color: '$omnivoreLightGray' }}
>
Privacy Policy
</StyledTextSpan>
</Link>
</StyledText>
);
)
}

View file

@ -8,10 +8,8 @@ import {
import { LoginForm } from './LoginForm'
import type { LoginFormProps } from './LoginForm'
import { OmnivoreNameLogo } from '../elements/images/OmnivoreNameLogo'
import { theme } from '../tokens/stitches.config'
import featureFullWidthImage from '../../public/static/images/login/login-feature-image-full.png'
import featureHalfWidthImage from '../../public/static/images/login/login-feature-image-half.png'
export function LoginLayout(props: LoginFormProps): JSX.Element {
return (
@ -37,10 +35,7 @@ export function LoginLayout(props: LoginFormProps): JSX.Element {
width: '100%',
}}
>
<OmnivoreNameLogo
color={theme.colors.omnivoreGray.toString()}
href="/login"
/>
<OmnivoreNameLogo color="#898989" href="/login" />
</Box>
</>
)
@ -53,9 +48,10 @@ function MobileLoginLayout(props: LoginFormProps) {
alignment="center"
distribution="center"
css={{
bg: '$omnivoreYellow',
width: '100%',
flexGrow: 1,
color: '#898989',
background: '#2A2A2A',
}}
>
<LoginForm {...props} />
@ -72,8 +68,9 @@ function MediumLoginLayout(props: LoginFormProps) {
css={{
width: '100vw',
height: '100vh',
bg: '$omnivoreYellow',
overflowY: 'clip',
color: '#898989',
background: '#2A2A2A',
}}
>
<Box
@ -104,11 +101,6 @@ const srcSetToImageSet = (srcFallback: string, srcSet?: string): string => {
}
function OmnivoreIllustration() {
const { props: halfWidthImgProps } = getImgProps({
src: featureHalfWidthImage,
alt: '',
})
const { props: fullWidthImgProps } = getImgProps({
src: featureFullWidthImage,
alt: '',
@ -123,11 +115,10 @@ function OmnivoreIllustration() {
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'left',
backgroundImage: srcSetToImageSet(halfWidthImgProps.src, halfWidthImgProps.srcSet),
'@media (min-aspect-ratio: 2/1)': {
backgroundImage: srcSetToImageSet(fullWidthImgProps.src, fullWidthImgProps.srcSet),
},
backgroundImage: srcSetToImageSet(
fullWidthImgProps.src,
fullWidthImgProps.srcSet
),
}}
/>
)

View file

@ -14,7 +14,6 @@ export function ProfileLayout(props: ProfileLayoutProps): JSX.Element {
alignment="center"
distribution="center"
css={{
// bg: '$omnivoreYellow',
height: '100vh',
bg: '$omnivoreYellow',
}}
@ -45,7 +44,7 @@ export function ProfileLayout(props: ProfileLayoutProps): JSX.Element {
}}
>
<OmnivoreNameLogo
color={theme.colors.omnivoreGray.toString()}
color="#898989"
href={props.logoDestination ?? '/login'}
/>
</HStack>

View file

@ -1,4 +1,4 @@
import { Box, HStack, VStack } from '../elements/LayoutPrimitives'
import { Box, HStack, SpanBox, VStack } from '../elements/LayoutPrimitives'
import { navigationCommands } from '../../lib/keyboardShortcuts/navigationShortcuts'
import { useKeyboardShortcuts } from '../../lib/keyboardShortcuts/useKeyboardShortcuts'
import { useRouter } from 'next/router'
@ -12,12 +12,46 @@ import { logout } from '../../lib/logout'
import { SettingsMenu } from './navMenu/SettingsMenu'
import { SettingsDropdown } from './navMenu/SettingsDropdown'
import { useVerifyAuth } from '../../lib/hooks/useVerifyAuth'
import Link from 'next/link'
import { CaretLeft } from 'phosphor-react'
type SettingsLayoutProps = {
title?: string
children: React.ReactNode
}
const ReturnButton = (): JSX.Element => {
return (
<SpanBox
css={{
a: {
textDecorationColor: '$thLibraryMenuUnselected',
},
'a:visited': {
textDecorationColor: '$thLibraryMenuUnselected',
},
}}
>
<Link href="/home">
<HStack
css={{
pl: '20px',
pb: '6px',
gap: '2px',
font: '$inter',
fontWeight: '500',
color: '$thLibraryMenuUnselected',
}}
alignment="center"
>
<CaretLeft />
Return to library
</HStack>
</Link>
</SpanBox>
)
}
export function SettingsLayout(props: SettingsLayoutProps): JSX.Element {
useVerifyAuth()
@ -57,7 +91,9 @@ export function SettingsLayout(props: SettingsLayoutProps): JSX.Element {
},
}}
></Box>
<Box
<HStack
alignment="center"
distribution="start"
css={{
p: '15px',
display: 'none',
@ -68,10 +104,24 @@ export function SettingsLayout(props: SettingsLayoutProps): JSX.Element {
}}
>
<SettingsDropdown />
</Box>
<ReturnButton />
</HStack>
<HStack css={{ width: '100%', height: '100%' }} distribution="start">
<SettingsMenu />
{props.children}
<VStack css={{ width: '100%', height: '100%' }}>
<SpanBox
css={{
marginTop: '-50px',
'@mdDown': {
display: 'none',
},
}}
>
<ReturnButton />
</SpanBox>
{props.children}
</VStack>
</HStack>
<Box css={{ height: '120px', width: '100%' }} />
</VStack>

View file

@ -15,8 +15,10 @@ const ForgotPasswordForm = (): JSX.Element => {
return (
<VStack css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}>
<SpanBox css={{ width: '100%' }}>
<FormLabel>Email</FormLabel>
<VStack css={{ width: '100%', gap: '5px' }}>
<FormLabel className="required" css={{ color: '#D9D9D9' }}>
Email
</FormLabel>
<BorderedFormInput
key="email"
type="email"
@ -24,13 +26,13 @@ const ForgotPasswordForm = (): JSX.Element => {
value={email}
placeholder="Email"
autoFocus={true}
css={{ bg: 'white', color: 'black' }}
css={{ backgroundColor: '#2A2A2A', color: 'white', border: 'unset' }}
onChange={(e) => {
e.preventDefault()
setEmail(e.target.value)
}}
/>
</SpanBox>
</VStack>
</VStack>
)
}
@ -58,16 +60,16 @@ export function EmailForgotPassword(): JSX.Element {
alignment="center"
css={{
padding: '16px',
background: 'white',
minWidth: '340px',
width: '70vw',
maxWidth: '576px',
borderRadius: '8px',
border: '1px solid #3D3D3D',
boxShadow: '#B1B1B1 9px 9px 9px -9px',
background: '#343434',
border: '1px solid #6A6968',
boxShadow: '0px 4px 4px 0px rgba(0, 0, 0, 0.15)',
}}
>
<StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>
<StyledText style="subHeadline" css={{ color: '#D9D9D9' }}>
Reset your password
</StyledText>
@ -93,7 +95,7 @@ export function EmailForgotPassword(): JSX.Element {
)}
{errorMessage && <StyledText style="error">{errorMessage}</StyledText>}
<Button type="submit" style="ctaDarkYellow" css={{ my: '$2' }}>
<Button type="submit" style="ctaBlue" css={{ my: '20px' }}>
Reset Password
</Button>
<Button
@ -112,7 +114,7 @@ export function EmailForgotPassword(): JSX.Element {
>
<StyledText
css={{
color: '$omnivoreRed',
color: '$omnivoreLightGray',
textDecoration: 'underline',
cursor: 'pointer',
}}

View file

@ -1,4 +1,4 @@
import { HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { HStack, VStack } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
import { StyledText, StyledTextSpan } from '../../elements/StyledText'
import { useEffect, useRef, useState } from 'react'
@ -17,8 +17,8 @@ const LoginForm = (): JSX.Element => {
return (
<VStack css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}>
<SpanBox css={{ width: '100%' }}>
<FormLabel>Email</FormLabel>
<VStack css={{ width: '100%', gap: '5px' }}>
<FormLabel css={{ color: '#D9D9D9' }}>Email</FormLabel>
<BorderedFormInput
autoFocus={true}
key="email"
@ -26,26 +26,26 @@ const LoginForm = (): JSX.Element => {
name="email"
value={email}
placeholder="Email"
css={{ backgroundColor: 'white', color: 'black' }}
css={{ backgroundColor: '#2A2A2A', color: 'white', border: 'unset' }}
onChange={(e) => {
e.preventDefault()
setEmail(e.target.value)
}}
/>
</SpanBox>
</VStack>
<SpanBox css={{ width: '100%' }}>
<FormLabel>Password</FormLabel>
<VStack css={{ width: '100%', gap: '5px' }}>
<FormLabel css={{ color: '#D9D9D9' }}>Password</FormLabel>
<BorderedFormInput
key="password"
type="password"
name="password"
value={password}
placeholder="Password"
css={{ bg: 'white', color: 'black' }}
css={{ bg: '#2A2A2A', color: 'white', border: 'unset' }}
onChange={(e) => setPassword(e.target.value)}
/>
</SpanBox>
</VStack>
</VStack>
)
}
@ -72,16 +72,16 @@ export function EmailLogin(): JSX.Element {
alignment="center"
css={{
padding: '16px',
background: 'white',
minWidth: '340px',
width: '70vw',
maxWidth: '576px',
borderRadius: '8px',
border: '1px solid #3D3D3D',
boxShadow: '#B1B1B1 9px 9px 9px -9px',
background: '#343434',
border: '1px solid #6A6968',
boxShadow: '0px 4px 4px 0px rgba(0, 0, 0, 0.15)',
}}
>
<StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>
<StyledText style="subHeadline" css={{ color: '#D9D9D9' }}>
Login
</StyledText>
@ -118,8 +118,7 @@ export function EmailLogin(): JSX.Element {
}}
>
<Button
style={'ctaOutlineYellow'}
css={{ color: '$omnivoreGray', borderColor: '$omnivoreLightGray' }}
style={'cancelAuth'}
type="button"
onClick={async (event) => {
window.localStorage.removeItem('authVerified')
@ -134,7 +133,13 @@ export function EmailLogin(): JSX.Element {
>
Cancel
</Button>
<Button type="submit" style={'ctaDarkYellow'}>
<Button
type="submit"
style="ctaBlue"
css={{
padding: '10px 50px',
}}
>
Login
</Button>
</HStack>
@ -151,7 +156,7 @@ export function EmailLogin(): JSX.Element {
>
Don&apos;t have an account?{' '}
<Link href="/auth/email-signup" passHref legacyBehavior>
<StyledTextSpan style="actionLink" css={{ color: '$omnivoreGray' }}>
<StyledTextSpan style="actionLink" css={{ color: '$ctaBlue' }}>
Sign up
</StyledTextSpan>
</Link>
@ -169,7 +174,10 @@ export function EmailLogin(): JSX.Element {
>
Forgot your password?{' '}
<Link href="/auth/forgot-password" passHref legacyBehavior>
<StyledTextSpan style="actionLink" css={{ color: '$omnivoreGray' }}>
<StyledTextSpan
style="actionLink"
css={{ color: '$omnivoreLightGray' }}
>
Click here
</StyledTextSpan>
</Link>

View file

@ -39,7 +39,9 @@ const SignUpForm = (): JSX.Element => {
return (
<VStack css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}>
<SpanBox css={{ width: '100%' }}>
<FormLabel className="required">Email</FormLabel>
<FormLabel className="required" css={{ color: '#D9D9D9' }}>
Email
</FormLabel>
<BorderedFormInput
autoFocus={true}
key="email"
@ -47,7 +49,7 @@ const SignUpForm = (): JSX.Element => {
name="email"
defaultValue={email}
placeholder="Email"
css={{ backgroundColor: 'white', color: 'black' }}
css={{ backgroundColor: '#2A2A2A', color: 'white', border: 'unset' }}
onChange={(e) => {
e.preventDefault()
setEmail(e.target.value)
@ -56,40 +58,46 @@ const SignUpForm = (): JSX.Element => {
/>
</SpanBox>
<SpanBox css={{ width: '100%' }}>
<FormLabel className="required">Password</FormLabel>
<FormLabel className="required" css={{ color: '#D9D9D9' }}>
Password
</FormLabel>
<BorderedFormInput
key="password"
type="password"
name="password"
defaultValue={password}
placeholder="Password"
css={{ bg: 'white', color: 'black' }}
css={{ backgroundColor: '#2A2A2A', color: 'white', border: 'unset' }}
onChange={(e) => setPassword(e.target.value)}
required
/>
</SpanBox>
<SpanBox css={{ width: '100%' }}>
<FormLabel className="required">Full Name</FormLabel>
<FormLabel className="required" css={{ color: '#D9D9D9' }}>
Full Name
</FormLabel>
<BorderedFormInput
key="fullname"
type="text"
name="name"
defaultValue={fullname}
placeholder="Full Name"
css={{ bg: 'white', color: 'black' }}
css={{ backgroundColor: '#2A2A2A', color: 'white', border: 'unset' }}
onChange={(e) => setFullname(e.target.value)}
required
/>
</SpanBox>
<SpanBox css={{ width: '100%' }}>
<FormLabel className="required">Username</FormLabel>
<FormLabel className="required" css={{ color: '#D9D9D9' }}>
Username
</FormLabel>
<BorderedFormInput
key="username"
type="text"
name="username"
defaultValue={username}
placeholder="Username"
css={{ bg: 'white', color: 'black' }}
css={{ backgroundColor: '#2A2A2A', color: 'white', border: 'unset' }}
onChange={handleUsernameChange}
required
/>
@ -145,16 +153,16 @@ export function EmailSignup(): JSX.Element {
alignment="center"
css={{
padding: '16px',
background: 'white',
minWidth: '340px',
width: '70vw',
maxWidth: '576px',
borderRadius: '8px',
border: '1px solid #3D3D3D',
boxShadow: '#B1B1B1 9px 9px 9px -9px',
background: '#343434',
border: '1px solid #6A6968',
boxShadow: '0px 4px 4px 0px rgba(0, 0, 0, 0.15)',
}}
>
<StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>
<StyledText style="subHeadline" css={{ color: '#D9D9D9' }}>
Sign Up
</StyledText>
@ -204,11 +212,7 @@ export function EmailSignup(): JSX.Element {
}}
>
<Button
style={'ctaOutlineYellow'}
css={{
color: '$omnivoreGray',
borderColor: 'rgba(0, 0, 0, 0.06)',
}}
style="cancelAuth"
type="button"
onClick={async () => {
window.localStorage.removeItem('authVerified')
@ -223,7 +227,13 @@ export function EmailSignup(): JSX.Element {
>
Cancel
</Button>
<Button type="submit" style={'ctaDarkYellow'}>
<Button
type="submit"
style="ctaBlue"
css={{
padding: '10px 50px',
}}
>
Sign Up
</Button>
</HStack>
@ -238,10 +248,7 @@ export function EmailSignup(): JSX.Element {
>
Already have an account?{' '}
<Link href="/auth/email-login" passHref legacyBehavior>
<StyledTextSpan
style="actionLink"
css={{ color: '$omnivoreGray' }}
>
<StyledTextSpan style="actionLink" css={{ color: '$ctaBlue' }}>
Login instead
</StyledTextSpan>
</Link>

View file

@ -9,7 +9,8 @@ export function ResetSent(): JSX.Element {
css={{
width: '100vw',
height: '100vh',
bg: '$omnivoreYellow',
color: '#898989',
background: '#2A2A2A',
overflowY: 'clip',
}}
>
@ -17,11 +18,11 @@ export function ResetSent(): JSX.Element {
css={{
width: '100%',
margin: '40px',
color: '$omnivoreGray',
color: '#898989',
'@xl': { margin: '138px' },
}}
>
<h1>Reset email sent</h1>
<h1 style={{ color: 'white' }}>Reset email sent</h1>
<Box>
If there is an account associated with the email specified we sent a
password reset link. Click the link to reset your password. You may

View file

@ -9,7 +9,8 @@ export function VerifyEmail(): JSX.Element {
css={{
width: '100vw',
height: '100vh',
bg: '$omnivoreYellow',
color: '#898989',
background: '#2A2A2A',
overflowY: 'clip',
}}
>
@ -17,12 +18,11 @@ export function VerifyEmail(): JSX.Element {
css={{
width: '100%',
margin: '40px',
color: '$omnivoreGray',
'@xl': { margin: '138px' },
}}
>
<h1>Verify your email address</h1>
<Box>
<h1 style={{ color: 'white' }}>Verify your email address</h1>
<Box css={{ color: '#898989' }}>
We sent a verification link to the email you provided. Click the
link to verify your email. You may need to check your spam folder.
</Box>

View file

@ -240,9 +240,12 @@ const AddFromURL = (props: AddFromURLProps): JSX.Element => {
</HStack>
)}
<Button
style="ctaBlue"
style="ctaOmnivoreYellow"
type="submit"
css={{ marginLeft: 'auto', marginTop: 'auto' }}
css={{
marginLeft: 'auto',
marginTop: 'auto',
}}
>
Add
</Button>

View file

@ -8,22 +8,8 @@ const LoginButton = (): JSX.Element => {
<Button
as={Link}
href="/login"
style="ctaDarkYellow"
css={{
display: 'flex',
marginLeft: 'auto',
borderRadius: 4,
background: 'unset',
color: '#3D3D3D',
height: '42px',
fontSize: 24,
lineHeight: '24px',
fontWeight: 'normal',
alignItems: 'center',
justifyContent: 'center',
textDecoration: "none",
transition: "all ease-in 50ms"
}}
style="landingSimple"
css={{ background: 'transparent' }}
>
Login
</Button>
@ -48,7 +34,7 @@ export function LandingHeader(): JSX.Element {
width: '100%',
}}
>
<OmnivoreNameLogo color={'#3D3D3D'} href="/login" />
<OmnivoreNameLogo color="#898989" href="/login" />
<LoginButton />
</Box>
)

View file

@ -14,7 +14,8 @@ export function LandingSection(props: LandingSectionProps): JSX.Element {
css={{
width: '100%',
flexWrap: 'wrap',
flexDirection: (props?.imagePosition ?? 'left') === 'left' ? 'row-reverse' : 'row',
flexDirection:
(props?.imagePosition ?? 'left') === 'left' ? 'row-reverse' : 'row',
marginBottom: 20,
'@mdDown': {
width: '100%',
@ -40,7 +41,7 @@ export function LandingSection(props: LandingSectionProps): JSX.Element {
as="h2"
css={{
fontWeight: '700',
color: '#3D3D3D',
color: '#FFFFFF',
lineHeight: 1.25,
'@mdDown': {
fontSize: 24,
@ -58,7 +59,7 @@ export function LandingSection(props: LandingSectionProps): JSX.Element {
<Box
as="p"
css={{
color: '#666',
color: '#898989',
}}
>
{props.descriptionText}

View file

@ -15,23 +15,7 @@ import Link from 'next/link'
export function GetStartedButton(props: { lang: 'en' | 'zh' }): JSX.Element {
return (
<Button
as={Link}
href="/login"
style="ctaDarkYellow"
css={{
borderRadius: 4,
background: '$omnivoreCtaYellow',
padding: '12px 25px',
color: '#3D3D3D',
fontWeight: '600',
textDecoration: 'none',
transition: 'background-color ease-out 50ms',
'&:hover': {
backgroundColor: '$omnivoreYellow',
},
}}
>
<Button as={Link} href="/login" style="landingCta">
{props.lang == 'zh' ? `免费注册` : `Sign Up for Free`}
</Button>
)
@ -43,8 +27,6 @@ const containerStyles = {
pb: 100,
width: '100%',
maxWidth: '1224px',
background:
'linear-gradient(0deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.2)), linear-gradient(0deg, rgba(253, 250, 236, 0.7), rgba(253, 250, 236, 0.7))',
'@mdDown': {
pt: 50,
},
@ -64,7 +46,7 @@ const sections = [
{
en: {
titleText: `Save it now. Read it later.`,
descriptionText: `Save articles, PDFs, and Twitter threads as you come across them
descriptionText: `Save articles and PDFs as you come across them
using Omnivore's mobile apps and browser extensions. Read them
later using our distraction free reader.`,
},
@ -77,7 +59,7 @@ const sections = [
},
{
en: {
titleText: `Get all your newsletters in one place.`,
titleText: `Get all your RSS feeds and newsletters in one place.`,
descriptionText: `Send newsletters directly to your Omnivore library rather than
scattered across multiple inboxes. Read them on your own time, away
from the constant distractions and interruptions of your email.`,
@ -93,7 +75,7 @@ const sections = [
en: {
titleText: `Keep your reading organized, whatever that means to you.`,
descriptionText: `Keep your reading organized and easily available with labels,
filters, rules, and fully indexed text searches. We're not here
filters, rules, and ful text searches. We're not here
to tell you how to stay organized our job is to give you the tools
to build a system that works for you.`,
},
@ -107,8 +89,7 @@ const sections = [
{
en: {
titleText: `Add highlights and notes.`,
descriptionText: `Become a better reader — engage your brain and improve retention by
reading actively, not passively. Highlight key sections and add
descriptionText: `Highlight key sections and add
notes as you read. You can access your highlights and notes any time
they stay with your articles forever.`,
},
@ -123,7 +104,7 @@ const sections = [
en: {
titleText: `Sync with your second brain.`,
descriptionText: `Omnivore syncs with popular Personal Knowledge Management systems
including Logseq and Obsidian, so you can pull all your saved
including Logseq, Obsidian, and Notion, so you can pull all your saved
reading, highlights, and notes into your second brain.`,
},
zh: {
@ -137,7 +118,7 @@ const sections = [
en: {
titleText: `Listen to your reading with text-to-speech.`,
descriptionText: `Work through your to-be-read list and give your eyes a break with
TTS, exclusively in the Omnivore app for iOS. Realistic,
text-to-speech, exclusively in the Omnivore app for iOS. Realistic,
natural-sounding AI voices will read any saved article aloud.`,
},
zh: {
@ -209,41 +190,6 @@ export function LandingSectionsContainer(
/>
)
})}
<VStack
alignment="center"
css={{
width: '100vw',
backgroundColor: '#fff',
paddingBottom: '40px',
marginTop: '40px',
borderTop: '1px solid var(--colors-omnivoreYellow)',
borderBottom: '1px solid var(--colors-omnivoreYellow)',
'@md': {
marginTop: 0,
},
}}
>
{props.lang == 'en' && (
<Box
as="p"
css={{
color: '#3D3D3D',
fontWeight: '700',
fontSize: '2.5rem',
lineHeight: '1.25',
textAlign: 'center',
marginBottom: '40px',
'@mdDown': {
fontSize: '2rem',
},
}}
>
Get Started With Omnivore Today
</Box>
)}
<GetStartedButton lang={props.lang} />
</VStack>
</VStack>
)
}

View file

@ -16,7 +16,6 @@ export const NavMenuFooter = (props: NavMenuFooterProps): JSX.Element => {
position: 'fixed',
bottom: '0%',
alignItems: 'center',
backgroundColor: '$thBackground2',
width: LIBRARY_LEFT_MENU_WIDTH,
overflowY: 'auto',

View file

@ -1,4 +1,4 @@
import { ReactNode, useEffect, useMemo, useRef, useState } from "react"
import { ReactNode, useEffect, useMemo, useRef, useState } from 'react'
import { StyledText } from '../../elements/StyledText'
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
@ -30,8 +30,8 @@ import { OutlinedLabelChip } from '../../elements/OutlinedLabelChip'
import { NewsletterIcon } from '../../elements/icons/NewsletterIcon'
import { Dropdown, DropdownOption } from '../../elements/DropdownElements'
import { useRouter } from 'next/router'
import { DiscoverIcon } from "../../elements/icons/DiscoverIcon"
import { escapeQuotes } from "../../../utils/helper"
import { DiscoverIcon } from '../../elements/icons/DiscoverIcon'
import { escapeQuotes } from '../../../utils/helper'
export const LIBRARY_LEFT_MENU_WIDTH = '275px'
@ -158,9 +158,6 @@ export function LibraryFilterMenu(props: LibraryFilterMenuProps): JSX.Element {
</Box>
<LibraryNav {...props} />
<Shortcuts {...props} />
{/* <SavedSearches {...props} savedSearches={savedSearches} />
<Subscriptions {...props} subscriptions={subscriptions} />
<Labels {...props} labels={labels} /> */}
<NavMenuFooter {...props} />
<Box css={{ height: '250px ' }} />
</Box>

View file

@ -145,7 +145,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
// Brand Colors
omnivoreRed: '#FA5E4A;',
omnivoreGray: '#3D3D3D',
omnivoreYellow: 'rgb(255, 234, 159)',
omnivoreYellow: '#FFEAA0',
omnivoreLightGray: 'rgb(125, 125, 125)',
omnivoreCtaYellow: 'rgb(255, 210, 52)',
searchActiveOutline: 'rgb(255, 210, 52)',

View file

@ -12,24 +12,21 @@ export type RequestContext = {
}
}
type RequestHeaders = {
authorization?: string
pendingUserAuth?: string
}
function requestHeaders(): RequestHeaders {
function requestHeaders(): Record<string, string> {
const authToken = window?.localStorage.getItem('authToken') || undefined
const pendingAuthToken =
window?.localStorage.getItem('pendingUserAuth') || undefined
if (authToken) {
return {
'X-OmnivoreClient': 'web',
authorization: authToken,
}
}
if (pendingAuthToken) {
return {
'X-OmnivoreClient': 'web',
pendingUserAuth: pendingAuthToken,
}
}

View file

@ -1,5 +1,4 @@
import { useRouter } from 'next/router'
import Script from 'next/script'
import { useState, useEffect } from 'react'
import { LoginLayout } from './../components/templates/LoginLayout'
import { parseErrorCodes } from '../lib/queryParamParser'
@ -8,8 +7,9 @@ import { formatMessage } from '../locales/en/messages'
export default function Login(): JSX.Element {
const router = useRouter()
const [errorMessage, setErrorMessage] =
useState<string | undefined>(undefined)
const [errorMessage, setErrorMessage] = useState<string | undefined>(
undefined
)
useEffect(() => {
if (!router.isReady) return
@ -30,10 +30,6 @@ export default function Login(): JSX.Element {
return (
<>
<Script
src="https://accounts.google.com/gsi/client"
strategy="afterInteractive"
/>
<PageMetaData
title="Welcome to Omnivore"
path="/login"

View file

@ -1,8 +1,24 @@
import React from 'react'
import { SettingsLayout } from '../../../components/templates/SettingsLayout'
import MobileInstallHelp from '../../../components/elements/MobileInstallHelp'
import IOSInstallHelp from '../../../components/elements/IOSInstallHelp'
import ExtensionInstallHelp from '../../../components/elements/ExtensionsInstallHelp'
import { Box } from '../../../components/elements/LayoutPrimitives'
import AndroidInstallHelp from '../../../components/elements/AndroidInstallHelp'
const Divider = (): JSX.Element => {
return (
<Box
css={{
my: '$2',
'@lg': {
my: '12px',
height: '1px',
backgroundColor: '$grayBorder',
},
}}
/>
)
}
export default function Installation(): JSX.Element {
return (
@ -22,17 +38,10 @@ export default function Installation(): JSX.Element {
},
}}
>
<MobileInstallHelp />
<Box
css={{
my: '$2',
'@lg': {
my: '12px',
height: '1px',
backgroundColor: '$grayBorder',
},
}}
/>
<IOSInstallHelp />
<Divider />
<AndroidInstallHelp />
<Divider />
<ExtensionInstallHelp />
</Box>
</SettingsLayout>

View file

@ -1,11 +1,11 @@
import React from 'react'
import { SettingsLayout } from '../../../components/templates/SettingsLayout'
import MobileInstallHelp from '../../../components/elements/MobileInstallHelp'
import IOSInstallHelp from '../../../components/elements/IOSInstallHelp'
export default function Mobile(): JSX.Element {
return (
<SettingsLayout title="Mobile Installation">
<MobileInstallHelp />
<IOSInstallHelp />
</SettingsLayout>
)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 597 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

View file

@ -1,19 +1,20 @@
import { ComponentStory, ComponentMeta } from '@storybook/react'
import MobileInstallHelp from '../components/elements/MobileInstallHelp'
import IOSInstallHelp from '../components/elements/IOSInstallHelp'
import { Box } from '../components/elements/LayoutPrimitives'
export default {
title: 'Components/MobileInstallHelp',
component: MobileInstallHelp,
component: IOSInstallHelp,
argTypes: {
onboarding: {
description: 'Changes the appearence of the component to match onboarding page designs.',
description:
'Changes the appearence of the component to match onboarding page designs.',
control: { type: 'boolean' },
},
},
} as ComponentMeta<typeof MobileInstallHelp>
} as ComponentMeta<typeof IOSInstallHelp>
const Template: ComponentStory<typeof MobileInstallHelp> = (args) => (
const Template: ComponentStory<typeof IOSInstallHelp> = (args) => (
<Box
css={{
maxWidth: '50rem',
@ -25,7 +26,7 @@ const Template: ComponentStory<typeof MobileInstallHelp> = (args) => (
boxShadow: '0px 3px 11px 0px #201F1D0A',
}}
>
<MobileInstallHelp {...args} />
<IOSInstallHelp {...args} />
</Box>
)