From 770dbbd68a4fe278970859864ba667f21692088d Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Thu, 7 Apr 2022 15:54:07 -0700
Subject: [PATCH 001/125] Add margins on wider screens, keyboard commands to
adjust
---
.../components/templates/PrimaryLayout.tsx | 9 ++++++-
.../templates/article/ArticleContainer.tsx | 24 +++++++++++++++----
.../keyboardShortcuts/navigationShortcuts.ts | 14 +++++++++++
.../web/pages/[username]/[slug]/index.tsx | 19 ++++++++++++++-
4 files changed, 60 insertions(+), 6 deletions(-)
diff --git a/packages/web/components/templates/PrimaryLayout.tsx b/packages/web/components/templates/PrimaryLayout.tsx
index 17757cacd..b3b1d1db6 100644
--- a/packages/web/components/templates/PrimaryLayout.tsx
+++ b/packages/web/components/templates/PrimaryLayout.tsx
@@ -62,7 +62,14 @@ export function PrimaryLayout(props: PrimaryLayoutProps): JSX.Element {
{props.pageMetaDataProps ? (
) : null}
-
+
l.id) || []
)
const updateFontSize = async (newFontSize: number) => {
- setFontSize(newFontSize)
- await userPersonalizationMutation({ fontSize: newFontSize })
+ if (fontSize !== newFontSize) {
+ setFontSize(newFontSize)
+ await userPersonalizationMutation({ fontSize: newFontSize })
+ }
+ }
+
+ const updateMargin = async (newMargin: number) => {
+ if (margin !== newMargin) {
+ setMargin(newMargin)
+ await userPersonalizationMutation({ margin: newMargin })
+ }
}
useEffect(() => {
updateFontSize(props.fontSize ?? 20)
}, [props.fontSize])
+ useEffect(() => {
+ updateMargin(props.margin ?? 140)
+ }, [props.margin])
+
// Listen for font size and color mode change events sent from host apps (ios, macos...)
useEffect(() => {
const increaseFontSize = async () => {
@@ -101,6 +115,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
css={{
padding: '16px',
maxWidth: '94%',
+ background: props.isAppleAppEmbed ? 'unset' : theme.colors.grayBg.toString(),
'--text-font-family': styles.fontFamily,
'--text-font-size': `${styles.fontSize}px`,
'--line-height': `150%`,
@@ -112,12 +127,13 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
'--font-color-transparent': styles.readerFontColorTransparent,
'--table-header-color': styles.readerTableHeaderColor,
'--headers-color': styles.readerHeadersColor,
+ margin: `30px 0px`,
'@sm': {
'--blockquote-padding': '1em 2em',
'--blockquote-icon-font-size': '1.7rem',
'--figure-margin': '2.6875rem auto',
'--hr-margin': '2em',
- margin: `30px ${styles.margin / 2}px`,
+ margin: `30px 0px`,
},
'@md': {
maxWidth: '92%',
@@ -125,7 +141,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
'@lg': {
margin: `30px 0`,
width: 'auto',
- maxWidth: 1024 - styles.margin,
+ maxWidth: 1024 - (styles.margin),
},
}}
>
diff --git a/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts b/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts
index e516aa2fe..2ce3f7ab8 100644
--- a/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts
+++ b/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts
@@ -207,6 +207,8 @@ type ArticleKeyboardAction =
| 'openOriginalArticle'
| 'incrementFontSize'
| 'decrementFontSize'
+ | 'incrementMarginWidth'
+ | 'decrementMarginWidth'
| 'editLabels'
export function articleKeyboardCommands(
@@ -238,6 +240,18 @@ export function articleKeyboardCommands(
shortcutKeyDescription: '-',
callback: () => actionHandler('decrementFontSize'),
},
+ {
+ shortcutKeys: [']'],
+ actionDescription: 'Increase margin width',
+ shortcutKeyDescription: ']',
+ callback: () => actionHandler('incrementMarginWidth'),
+ },
+ {
+ shortcutKeys: ['['],
+ actionDescription: 'Decrease margin width',
+ shortcutKeyDescription: '[',
+ callback: () => actionHandler('decrementMarginWidth'),
+ },
{
shortcutKeys: ['l'],
actionDescription: 'Edit labels',
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index a341789af..4bbf2cc2e 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -23,6 +23,7 @@ import Script from 'next/script'
import { EditLabelsModal } from '../../../components/templates/article/EditLabelsModal'
import { Label } from '../../../lib/networking/fragments/labelFragment'
import { isVipUser } from '../../../lib/featureFlag'
+import { theme } from '../../../components/tokens/stitches.config'
const PdfArticleContainerNoSSR = dynamic(
() => import('./../../../components/templates/article/PdfArticleContainer'),
@@ -45,12 +46,16 @@ export default function Home(): JSX.Element {
const { preferencesData } = useGetUserPreferences()
const article = articleData?.article.article
const [fontSize, setFontSize] = useState(preferencesData?.fontSize ?? 20)
+ const [marginWidth, setMarginWidth] = useState(preferencesData?.fontSize ?? 20)
useKeyboardShortcuts(navigationCommands(router))
const updateFontSize = async (newFontSize: number) => {
setFontSize(newFontSize)
- await userPersonalizationMutation({ fontSize: newFontSize })
+ }
+
+ const updateMarginWidth = async (newMargin: number) => {
+ setMarginWidth(newMargin)
}
useKeyboardShortcuts(
@@ -68,6 +73,12 @@ export default function Home(): JSX.Element {
case 'decrementFontSize':
await updateFontSize(Math.max(fontSize - 2, 10))
break
+ case 'incrementMarginWidth':
+ updateMarginWidth(Math.min(marginWidth + 50, 560))
+ break
+ case 'decrementMarginWidth':
+ updateMarginWidth(Math.max(marginWidth - 50, 0))
+ break
case 'editLabels':
if (viewerData?.me && isVipUser(viewerData?.me)) {
setShowLabelsModal(true)
@@ -108,6 +119,11 @@ export default function Home(): JSX.Element {
distribution="center"
ref={scrollRef}
className="disable-webkit-callout"
+ css={{
+ '@smDown': {
+ background: theme.colors.grayBg.toString(),
+ }
+ }}
>
Date: Thu, 7 Apr 2022 20:23:27 -0700
Subject: [PATCH 002/125] WIP: reader control breakpoints
This implements the left side controls on desktop and in the
header on smaller screens.
partial work, committing as-is to test on device
---
.../elements/images/OmnivoreNameLogo.tsx | 3 +-
.../web/components/patterns/PrimaryHeader.tsx | 47 +++++++++++++-
.../templates/article/ArticleContainer.tsx | 10 +--
.../web/components/tokens/stitches.config.ts | 1 +
.../web/pages/[username]/[slug]/index.tsx | 63 ++++++++++++++++++-
5 files changed, 112 insertions(+), 12 deletions(-)
diff --git a/packages/web/components/elements/images/OmnivoreNameLogo.tsx b/packages/web/components/elements/images/OmnivoreNameLogo.tsx
index 482845d0d..0f3e25328 100644
--- a/packages/web/components/elements/images/OmnivoreNameLogo.tsx
+++ b/packages/web/components/elements/images/OmnivoreNameLogo.tsx
@@ -2,6 +2,7 @@ import { config } from '../../tokens/stitches.config'
import Image from 'next/image'
import { StyledText } from '../../elements/StyledText'
import Link from 'next/link'
+import { SpanBox } from '../LayoutPrimitives'
export function OmnivoreNameLogoImage(): JSX.Element {
return (
@@ -51,7 +52,7 @@ export function OmnivoreNameLogo(props: OmnivoreNameLogoProps): JSX.Element {
- Omnivore
+ {/* Omnivore */}
)
diff --git a/packages/web/components/patterns/PrimaryHeader.tsx b/packages/web/components/patterns/PrimaryHeader.tsx
index a74c39999..32e347c37 100644
--- a/packages/web/components/patterns/PrimaryHeader.tsx
+++ b/packages/web/components/patterns/PrimaryHeader.tsx
@@ -17,7 +17,7 @@ import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery'
import { setupAnalytics } from '../../lib/analytics'
import { Button } from '../elements/Button'
import Link from 'next/link'
-import { ArrowSquareOut } from 'phosphor-react'
+import { ArchiveBox, ArrowSquareOut, DotsThree, HighlighterCircle, TagSimple, TextAa } from 'phosphor-react'
type HeaderProps = {
user?: UserBasicData
@@ -183,12 +183,53 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
+
+
+
+ {}} style='plainIcon'>
+
+
+
+ {}} >
+
+
+ {}} >
+
+
+
+ {}} >
+
+
+
+ {}} >
+
+
+
+
+
+
{props.username ? (
-
+ {/*
@@ -202,7 +243,7 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
-
+ */}
l.id) || []
)
@@ -99,7 +100,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
}, [props.article])
const styles = {
- margin: props.margin ?? 140,
+ margin: props.margin ?? 360,
fontSize,
fontFamily: props.fontFamily ?? 'inter',
readerFontColor: theme.colors.readerFont.toString(),
@@ -114,7 +115,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
id="article-container"
css={{
padding: '16px',
- maxWidth: '94%',
+ maxWidth: '100%',
background: props.isAppleAppEmbed ? 'unset' : theme.colors.grayBg.toString(),
'--text-font-family': styles.fontFamily,
'--text-font-size': `${styles.fontSize}px`,
@@ -127,7 +128,6 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
'--font-color-transparent': styles.readerFontColorTransparent,
'--table-header-color': styles.readerTableHeaderColor,
'--headers-color': styles.readerHeadersColor,
- margin: `30px 0px`,
'@sm': {
'--blockquote-padding': '1em 2em',
'--blockquote-icon-font-size': '1.7rem',
@@ -136,7 +136,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
margin: `30px 0px`,
},
'@md': {
- maxWidth: '92%',
+ maxWidth: 1024 - (styles.margin),
},
'@lg': {
margin: `30px 0`,
diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts
index 7e9500fcf..de59a709b 100644
--- a/packages/web/components/tokens/stitches.config.ts
+++ b/packages/web/components/tokens/stitches.config.ts
@@ -156,6 +156,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
xsmDown: '(max-width: 375px)',
smDown: '(max-width: 575px)',
mdDown: '(max-width: 768px)',
+ lgDown: '(max-width: 992px)',
sm: '(min-width: 576px)',
md: '(min-width: 768px)',
lg: '(min-width: 992px)',
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index 4bbf2cc2e..697c3f9c3 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -23,7 +23,18 @@ import Script from 'next/script'
import { EditLabelsModal } from '../../../components/templates/article/EditLabelsModal'
import { Label } from '../../../lib/networking/fragments/labelFragment'
import { isVipUser } from '../../../lib/featureFlag'
-import { theme } from '../../../components/tokens/stitches.config'
+import { styled, theme } from '../../../components/tokens/stitches.config'
+import { Button } from '../../../components/elements/Button'
+import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from 'phosphor-react'
+import { Separator } from '@radix-ui/react-separator'
+
+const MenuSeparator = styled(Separator, {
+ width: '100%',
+ margin: 0,
+ backgroundColor: 'red',
+ borderBottom: `1px solid ${theme.colors.grayLine.toString()}`,
+ my: '8px',
+})
const PdfArticleContainerNoSSR = dynamic(
() => import('./../../../components/templates/article/PdfArticleContainer'),
@@ -46,15 +57,17 @@ export default function Home(): JSX.Element {
const { preferencesData } = useGetUserPreferences()
const article = articleData?.article.article
const [fontSize, setFontSize] = useState(preferencesData?.fontSize ?? 20)
- const [marginWidth, setMarginWidth] = useState(preferencesData?.fontSize ?? 20)
+ const [marginWidth, setMarginWidth] = useState(preferencesData?.margin ?? 360)
useKeyboardShortcuts(navigationCommands(router))
const updateFontSize = async (newFontSize: number) => {
setFontSize(newFontSize)
+ await userPersonalizationMutation({ fontSize: newFontSize })
}
const updateMarginWidth = async (newMargin: number) => {
+ console.log('margin', newMargin)
setMarginWidth(newMargin)
}
@@ -77,7 +90,7 @@ export default function Home(): JSX.Element {
updateMarginWidth(Math.min(marginWidth + 50, 560))
break
case 'decrementMarginWidth':
- updateMarginWidth(Math.max(marginWidth - 50, 0))
+ updateMarginWidth(Math.max(marginWidth - 50, 200))
break
case 'editLabels':
if (viewerData?.me && isVipUser(viewerData?.me)) {
@@ -108,6 +121,50 @@ export default function Home(): JSX.Element {
/>
+
+
+ {}} style='plainIcon'>
+
+
+
+
+ {}} >
+
+
+ {}} >
+
+
+
+
+ {}} >
+
+
+
+
+ {}} >
+
+
+
+
+
{article.contentReader == 'PDF' ? (
Date: Thu, 7 Apr 2022 20:50:59 -0700
Subject: [PATCH 003/125] Simplify
---
packages/web/components/patterns/PrimaryHeader.tsx | 10 +++++-----
packages/web/pages/[username]/[slug]/index.tsx | 14 +++++++-------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/packages/web/components/patterns/PrimaryHeader.tsx b/packages/web/components/patterns/PrimaryHeader.tsx
index 32e347c37..2dde10616 100644
--- a/packages/web/components/patterns/PrimaryHeader.tsx
+++ b/packages/web/components/patterns/PrimaryHeader.tsx
@@ -202,22 +202,22 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
m: '0px',
}}
>
- {}} style='plainIcon'>
+
- {}} >
+
- {}} >
+
- {}} >
+
- {}} >
+
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index 697c3f9c3..0a0711f70 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -141,25 +141,25 @@ export default function Home(): JSX.Element {
m: '0px',
}}
>
- {}} style='plainIcon'>
+
- {}} >
+
- {}} >
+
-
+
- {}} >
+
-
+
- {}} >
+
From 602dbb29be362f0af2e8a55b3cac61be52220639 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Thu, 7 Apr 2022 21:11:40 -0700
Subject: [PATCH 004/125] Play with transparent header in desktop
---
packages/web/components/patterns/PrimaryHeader.tsx | 7 ++++---
packages/web/components/templates/PrimaryLayout.tsx | 12 ++++++++++--
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/packages/web/components/patterns/PrimaryHeader.tsx b/packages/web/components/patterns/PrimaryHeader.tsx
index 2dde10616..9e40bf6f7 100644
--- a/packages/web/components/patterns/PrimaryHeader.tsx
+++ b/packages/web/components/patterns/PrimaryHeader.tsx
@@ -159,16 +159,17 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
zIndex: 5,
width: '100%',
boxShadow: props.isDisplayingShadow ? '$panelShadow' : 'unset',
- bg: '$grayBase',
p: '0px $3 0px $3',
height: '68px',
position: 'fixed',
- minHeight: '68px',
+ bg: 'transparent',
'@smDown': {
height: '48px',
- minHeight: '48px',
p: '0px 18px 0px 16px',
},
+ '@lgDown': {
+ bg: '$grayBase',
+ },
}}
>
diff --git a/packages/web/components/templates/PrimaryLayout.tsx b/packages/web/components/templates/PrimaryLayout.tsx
index b3b1d1db6..3c7764c90 100644
--- a/packages/web/components/templates/PrimaryLayout.tsx
+++ b/packages/web/components/templates/PrimaryLayout.tsx
@@ -84,8 +84,8 @@ export function PrimaryLayout(props: PrimaryLayoutProps): JSX.Element {
+
{props.children}
{showLogoutConfirmation ? (
Date: Fri, 8 Apr 2022 09:56:29 -0700
Subject: [PATCH 005/125] Encapsulate the article actions menu, standardize
header height at 48px
---
.../templates/article/ArticleActionsMenu.tsx | 69 +++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 packages/web/components/templates/article/ArticleActionsMenu.tsx
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
new file mode 100644
index 000000000..0e30ec958
--- /dev/null
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -0,0 +1,69 @@
+import { Separator } from "@radix-ui/react-separator"
+import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from "phosphor-react"
+import { Button } from "../../elements/Button"
+import { Box } from "../../elements/LayoutPrimitives"
+import { styled, theme } from "../../tokens/stitches.config"
+
+export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
+
+type ArticleActionsMenuProps = {
+ // pageTestId: string
+ // hideHeader?: boolean
+ // pageMetaDataProps?: PageMetaDataProps
+ // scrollElementRef?: MutableRefObject
+ // displayFontStepper?: boolean
+ layout: ArticleActionsMenuLayout
+}
+
+
+
+export function MenuSeparator(props: ArticleActionsMenuProps) {
+ const LineSeparator = styled(Separator, {
+ width: '100%',
+ margin: 0,
+ backgroundColor: 'red',
+ borderBottom: `1px solid ${theme.colors.grayLine.toString()}`,
+ my: '8px',
+ })
+ return (props.layout == 'vertical' ? : <>>)
+}
+
+export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
From 19e3af66d5a06861ea426b7a4ae4a98462a0ae18 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Fri, 8 Apr 2022 10:01:14 -0700
Subject: [PATCH 006/125] Encapsulate the article actions menu, standardize
header height at 48px
---
packages/web/components/elements/Button.tsx | 8 ++++
.../web/components/patterns/PrimaryHeader.tsx | 46 ++-----------------
.../components/templates/PrimaryLayout.tsx | 5 +-
.../components/templates/SettingsLayout.tsx | 3 +-
.../web/pages/[username]/[slug]/index.tsx | 36 ++-------------
5 files changed, 17 insertions(+), 81 deletions(-)
diff --git a/packages/web/components/elements/Button.tsx b/packages/web/components/elements/Button.tsx
index b3ccce06d..ccd3b0131 100644
--- a/packages/web/components/elements/Button.tsx
+++ b/packages/web/components/elements/Button.tsx
@@ -153,6 +153,14 @@ export const Button = styled('button', {
opacity: 0.8,
},
},
+ articleActionIcon: {
+ bg: 'transparent',
+ border: 'none',
+ cursor: 'pointer',
+ '&:hover': {
+ opacity: 0.8,
+ },
+ },
ghost: {
color: 'transparent',
border: 'none',
diff --git a/packages/web/components/patterns/PrimaryHeader.tsx b/packages/web/components/patterns/PrimaryHeader.tsx
index 9e40bf6f7..2d62089c7 100644
--- a/packages/web/components/patterns/PrimaryHeader.tsx
+++ b/packages/web/components/patterns/PrimaryHeader.tsx
@@ -18,6 +18,7 @@ import { setupAnalytics } from '../../lib/analytics'
import { Button } from '../elements/Button'
import Link from 'next/link'
import { ArchiveBox, ArrowSquareOut, DotsThree, HighlighterCircle, TagSimple, TextAa } from 'phosphor-react'
+import { ArticleActionsMenu } from '../templates/article/ArticleActionsMenu'
type HeaderProps = {
user?: UserBasicData
@@ -160,11 +161,10 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
width: '100%',
boxShadow: props.isDisplayingShadow ? '$panelShadow' : 'unset',
p: '0px $3 0px $3',
- height: '68px',
+ height: '48px',
position: 'fixed',
bg: 'transparent',
'@smDown': {
- height: '48px',
p: '0px 18px 0px 16px',
},
'@lgDown': {
@@ -195,33 +195,8 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
mr: '16px',
}}
>
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -230,21 +205,6 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
alignment="center"
css={{ display: 'flex', alignItems: 'center' }}
>
- {/*
-
-
-
-
-
-
- Follow us on GitHub
-
-
-
-
-
-
- */}
{props.children}
diff --git a/packages/web/components/templates/SettingsLayout.tsx b/packages/web/components/templates/SettingsLayout.tsx
index 7f8f09f26..c2df14f0d 100644
--- a/packages/web/components/templates/SettingsLayout.tsx
+++ b/packages/web/components/templates/SettingsLayout.tsx
@@ -54,8 +54,7 @@ export function SettingsLayout(props: SettingsLayoutProps): JSX.Element {
/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
{article.contentReader == 'PDF' ? (
Date: Fri, 8 Apr 2022 10:36:45 -0700
Subject: [PATCH 007/125] Use a themed font colour for the article actions
buttons
---
.../templates/article/ArticleActionsMenu.tsx | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 0e30ec958..4e0eb283c 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -44,25 +44,25 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}}
>
-
+
-
+
-
+
-
+
-
+
)
From 32b08d11124e7db5f9f4ea94a758d348eba28edc Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Fri, 8 Apr 2022 10:42:20 -0700
Subject: [PATCH 008/125] Disable dots menu Since there are no extra actions
yet
---
.../templates/article/ArticleActionsMenu.tsx | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 4e0eb283c..8be346b8c 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -7,16 +7,9 @@ import { styled, theme } from "../../tokens/stitches.config"
export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
type ArticleActionsMenuProps = {
- // pageTestId: string
- // hideHeader?: boolean
- // pageMetaDataProps?: PageMetaDataProps
- // scrollElementRef?: MutableRefObject
- // displayFontStepper?: boolean
layout: ArticleActionsMenuLayout
}
-
-
export function MenuSeparator(props: ArticleActionsMenuProps) {
const LineSeparator = styled(Separator, {
width: '100%',
@@ -33,11 +26,12 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
+
@@ -59,11 +54,11 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
-
+ {/*
-
+ */}
)
}
\ No newline at end of file
From 78129a4a85ccc15cc603ab4017f4ba03819ebcfe Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Fri, 8 Apr 2022 15:26:18 -0700
Subject: [PATCH 009/125] WIP: article actoins and reader settings
---
.../components/elements/DropdownElements.tsx | 8 ++
.../components/elements/TickedRangeSlider.tsx | 39 ++++++
.../web/components/elements/images/AIcon.tsx | 14 +++
.../templates/article/ArticleActionsMenu.tsx | 52 ++++++--
.../templates/article/EditLabelsModal.tsx | 1 -
.../templates/article/ReaderSettingsModal.tsx | 90 ++++++++++++++
.../web/pages/[username]/[slug]/index.tsx | 113 +++++++++++-------
packages/web/styles/globals.css | 36 ++++++
8 files changed, 299 insertions(+), 54 deletions(-)
create mode 100644 packages/web/components/elements/TickedRangeSlider.tsx
create mode 100644 packages/web/components/elements/images/AIcon.tsx
create mode 100644 packages/web/components/templates/article/ReaderSettingsModal.tsx
diff --git a/packages/web/components/elements/DropdownElements.tsx b/packages/web/components/elements/DropdownElements.tsx
index 818898f06..d5f3b78c6 100644
--- a/packages/web/components/elements/DropdownElements.tsx
+++ b/packages/web/components/elements/DropdownElements.tsx
@@ -65,6 +65,7 @@ const StyledLabel = styled(Label, {
})
export type DropdownAlignment = 'start' | 'end' | 'center'
+export type DropdownSide = 'top' | 'right' | 'bottom' | 'left'
type DropdownProps = {
labelText?: string
@@ -73,6 +74,8 @@ type DropdownProps = {
children: React.ReactNode
styledArrow?: boolean
align?: DropdownAlignment
+ side?: DropdownSide
+ sideOffset?: number
disabled?: boolean
css?: CSS
}
@@ -108,8 +111,11 @@ export function Dropdown({
labelText,
showArrow = true,
disabled = false,
+ side = 'bottom',
+ sideOffset = 0,
css
}: DropdownProps): JSX.Element {
+ console.log('side', side)
return (
{triggerElement}
@@ -119,6 +125,8 @@ export function Dropdown({
// remove focus from dropdown
;(document.activeElement as HTMLElement).blur()
}}
+ side={side}
+ sideOffset={sideOffset}
align={align ? align : 'center'}
>
{labelText && {labelText} }
diff --git a/packages/web/components/elements/TickedRangeSlider.tsx b/packages/web/components/elements/TickedRangeSlider.tsx
new file mode 100644
index 000000000..c112d8d73
--- /dev/null
+++ b/packages/web/components/elements/TickedRangeSlider.tsx
@@ -0,0 +1,39 @@
+
+
+import { Box, HStack } from './LayoutPrimitives'
+import { styled, theme } from '../tokens/stitches.config'
+
+type TickedRangeSliderProps = {
+ ticks?: number,
+ value: number,
+ onChange: (value: number) => void,
+ min?: number,
+ max?: number,
+ step?: number,
+}
+
+const Tick = styled(Box, {
+ background: theme.colors.grayBorderHover,
+ width: 2,
+ height: 8,
+})
+
+export function TickedRangeSlider({
+ ticks = 8,
+ min = 10,
+ max = 28,
+ step = 1,
+ value,
+ onChange,
+ } : TickedRangeSliderProps
+): JSX.Element {
+
+ return (
+
+ onChange(e.target.value as any)} value={value} type="range" min={min} max={max} step={step} className='slider'/>
+
+ {[...Array(ticks)].map((val, idx) => )}
+
+
+ )
+}
\ No newline at end of file
diff --git a/packages/web/components/elements/images/AIcon.tsx b/packages/web/components/elements/images/AIcon.tsx
new file mode 100644
index 000000000..f7972a2f0
--- /dev/null
+++ b/packages/web/components/elements/images/AIcon.tsx
@@ -0,0 +1,14 @@
+type AIconProps = {
+ size: number
+ color: string
+ style?: React.CSSProperties
+}
+
+export function AIcon(props: AIconProps): JSX.Element {
+ return (
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 8be346b8c..0dab2698f 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -1,20 +1,26 @@
import { Separator } from "@radix-ui/react-separator"
import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from "phosphor-react"
import { Button } from "../../elements/Button"
-import { Box } from "../../elements/LayoutPrimitives"
+import { Dropdown } from "../../elements/DropdownElements"
+import { Box, SpanBox } from "../../elements/LayoutPrimitives"
import { styled, theme } from "../../tokens/stitches.config"
+import { ReaderSettings } from "./ReaderSettingsModal"
export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
type ArticleActionsMenuProps = {
layout: ArticleActionsMenuLayout
+ articleActionHandler: (action: string, arg?: number) => void
}
-export function MenuSeparator(props: ArticleActionsMenuProps) {
+type MenuSeparatorProps = {
+ layout: ArticleActionsMenuLayout
+}
+
+export function MenuSeparator(props: MenuSeparatorProps) {
const LineSeparator = styled(Separator, {
width: '100%',
margin: 0,
- backgroundColor: 'red',
borderBottom: `1px solid ${theme.colors.grayLine.toString()}`,
my: '8px',
})
@@ -23,12 +29,12 @@ export function MenuSeparator(props: ArticleActionsMenuProps) {
export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element {
return (
+ <>
-
-
-
+
+
+
+ }
+ css={{ m: '0px', p: '0px', outlineStyle: 'solid', outlineWidth: '1px', outlineColor: theme.colors.grayLine.toString() }}
+ >
+
+
+
-
-
-
+
+
+
+ }
+ css={{ background: 'red' }}
+ >
+ {/* */}
+
-
+ props.articleActionHandler('showHighlights')}>
-
+ props.articleActionHandler('archive')}>
{/*
@@ -60,5 +85,6 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
*/}
+ >
)
}
\ No newline at end of file
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index 6b3e043d5..d06cc6e04 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -15,7 +15,6 @@ import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticle
import { LabelChip } from '../../elements/LabelChip'
type EditLabelsModalProps = {
- labels: Label[]
article: ArticleAttributes
onOpenChange: (open: boolean) => void
setLabels: (labels: Label[]) => void
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
new file mode 100644
index 000000000..b51669c2c
--- /dev/null
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -0,0 +1,90 @@
+import {
+ ModalRoot,
+ ModalOverlay,
+ ModalContent,
+} from '../../elements/ModalPrimitives'
+import { Box, HStack, VStack, Separator, SpanBox } from '../../elements/LayoutPrimitives'
+import { Button } from '../../elements/Button'
+import { StyledText } from '../../elements/StyledText'
+import { CrossIcon } from '../../elements/images/CrossIcon'
+import { CommentIcon } from '../../elements/images/CommentIcon'
+import { TrashIcon } from '../../elements/images/TrashIcon'
+import { styled, theme } from '../../tokens/stitches.config'
+import type { Highlight } from '../../../lib/networking/fragments/highlightFragment'
+import { HighlightView } from '../../patterns/HighlightView'
+import { useCallback, useState } from 'react'
+import { StyledTextArea } from '../../elements/StyledTextArea'
+import { ConfirmationModal } from '../../patterns/ConfirmationModal'
+import { AlignCenterHorizontalSimple, ArrowsInLineHorizontal, ArrowsOutLineHorizontal, Minus, Pen, Plus, Trash } from 'phosphor-react'
+import { AIcon } from '../../elements/images/AIcon'
+import { TickedRangeSlider } from '../../elements/TickedRangeSlider'
+
+type ReaderSettingsProps = {
+
+}
+
+export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
+ const VerticalDivider = styled(SpanBox, {
+ width: '1px',
+ height: '100%',
+ background: `${theme.colors.grayLine.toString()}`,
+ })
+ return (
+
+
+ {}}>
+
+
+
+
+ {}}>
+
+
+
+
+
+ Margin:
+
+
+
+
+ {}} />
+
+
+
+
+
+
+ Line Height:
+
+
+
+
+ {}} />
+
+
+
+
+
+
+ )
+}
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index 14b61bcb2..f35f94e8a 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -29,6 +29,8 @@ import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from 'pho
import { Separator } from '@radix-ui/react-separator'
import { Article } from '../../../components/templates/article/Article'
import { ArticleActionsMenu } from '../../../components/templates/article/ArticleActionsMenu'
+import { HighlightsModal } from '../../../components/templates/article/HighlightsModal'
+import { setLinkArchivedMutation } from '../../../lib/networking/mutations/setLinkArchivedMutation'
const MenuSeparator = styled(Separator, {
width: '100%',
@@ -48,6 +50,7 @@ export default function Home(): JSX.Element {
const scrollRef = useRef(null)
const { slug } = router.query
const [showLabelsModal, setShowLabelsModal] = useState(false)
+ const [showHighlightsModal, setShowHighlightsModal] = useState(false)
// Populate data cache
const { viewerData } = useGetViewerQuery()
@@ -73,33 +76,51 @@ export default function Home(): JSX.Element {
setMarginWidth(newMargin)
}
+ const actionHandler = async (action: string, arg?: number) => {
+ switch (action) {
+ case 'archive':
+ if (article) {
+ await setLinkArchivedMutation({
+ linkId: article.id,
+ archived: true,
+ })
+ // TODO: merge from article actions PR
+ // removeItemFromCache(cache, mutate, props.article.id)
+ router.push(`/home`)
+ }
+ break
+ case 'openOriginalArticle':
+ const url = article?.url
+ if (url) {
+ window.open(url, '_blank')
+ }
+ break
+ case 'showHighlights':
+ setShowHighlightsModal(true)
+ break
+ case 'incrementFontSize':
+ await updateFontSize(Math.min(fontSize + 2, 28))
+ break
+ case 'decrementFontSize':
+ await updateFontSize(Math.max(fontSize - 2, 10))
+ break
+ case 'incrementMarginWidth':
+ updateMarginWidth(Math.min(marginWidth + 50, 560))
+ break
+ case 'decrementMarginWidth':
+ updateMarginWidth(Math.max(marginWidth - 50, 200))
+ break
+ case 'editLabels':
+ if (viewerData?.me && isVipUser(viewerData?.me)) {
+ setShowLabelsModal(true)
+ }
+ break
+ }
+ };
+
useKeyboardShortcuts(
articleKeyboardCommands(router, async (action) => {
- switch (action) {
- case 'openOriginalArticle':
- const url = article?.url
- if (url) {
- window.open(url, '_blank')
- }
- break
- case 'incrementFontSize':
- await updateFontSize(Math.min(fontSize + 2, 28))
- break
- case 'decrementFontSize':
- await updateFontSize(Math.max(fontSize - 2, 10))
- break
- case 'incrementMarginWidth':
- updateMarginWidth(Math.min(marginWidth + 50, 560))
- break
- case 'decrementMarginWidth':
- updateMarginWidth(Math.max(marginWidth - 50, 200))
- break
- case 'editLabels':
- if (viewerData?.me && isVipUser(viewerData?.me)) {
- setShowLabelsModal(true)
- }
- break
- }
+ actionHandler(action)
})
)
@@ -124,19 +145,22 @@ export default function Home(): JSX.Element {
-
-
+ position: 'fixed',
+ flexDirection: 'row-reverse',
+ top: '-120px',
+ left: 8,
+ height: '100%',
+ width: '48px',
+ '@lgDown': {
+ display: 'none',
+ },
+ }}
+ >
+
+
{article.contentReader == 'PDF' ? (
+
+ )}
+ {showHighlightsModal && (
+ setShowHighlightsModal(false)}
+ deleteHighlightAction={(highlightId: string) => {
+ // removeHighlightCallback(highlightId)
+ }}
+ />
+ )}
{/* {showLabelsModal && (
)} */}
-
- )}
)
}
diff --git a/packages/web/styles/globals.css b/packages/web/styles/globals.css
index 573f58d7f..dda271556 100644
--- a/packages/web/styles/globals.css
+++ b/packages/web/styles/globals.css
@@ -99,3 +99,39 @@ div#appleid-signin {
border-bottom-color: transparent;
border-left-color: transparent;
}
+
+.slider {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 100%;
+ height: 1px;
+ background: var(--colors-grayBorderHover);
+}
+
+.slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 16px !important;
+ height: 16px;
+ border-radius: 50%;
+ background: var(--colors-utilityTextContrast);
+ cursor: pointer;
+}
+
+.slider::-moz-range-thumb {
+ -webkit-appearance: none;
+ width: 16px !important;
+ height: 16px;
+ border-radius: 50%;
+ background: var(--colors-utilityTextContrast);
+ cursor: pointer;
+}
+
+input[type=range]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ border: none;
+ height: 16px;
+ width: 16px;
+ border-radius: 50%;
+ background: var(--colors-grayTextContrast);
+}
\ No newline at end of file
From ba301c4f0f428c6934ce618aaacf2245dc638f81 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Fri, 8 Apr 2022 15:44:52 -0700
Subject: [PATCH 010/125] Sliders
---
.../web/components/patterns/PrimaryHeader.tsx | 2 +-
.../templates/article/ArticleActionsMenu.tsx | 4 +-
.../templates/article/ArticleContainer.tsx | 1 -
.../templates/article/ReaderSettingsModal.tsx | 44 ++++++++++---------
.../web/pages/[username]/[slug]/index.tsx | 1 +
5 files changed, 29 insertions(+), 23 deletions(-)
diff --git a/packages/web/components/patterns/PrimaryHeader.tsx b/packages/web/components/patterns/PrimaryHeader.tsx
index 2d62089c7..f539fa5fd 100644
--- a/packages/web/components/patterns/PrimaryHeader.tsx
+++ b/packages/web/components/patterns/PrimaryHeader.tsx
@@ -195,7 +195,7 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
mr: '16px',
}}
>
-
+ {/* */}
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 0dab2698f..01cc1d26c 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -1,5 +1,6 @@
import { Separator } from "@radix-ui/react-separator"
import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from "phosphor-react"
+import { UserPreferences } from "../../../lib/networking/queries/useGetUserPreferences"
import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
import { Box, SpanBox } from "../../elements/LayoutPrimitives"
@@ -10,6 +11,7 @@ export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
type ArticleActionsMenuProps = {
layout: ArticleActionsMenuLayout
+ userPreferences?: UserPreferences
articleActionHandler: (action: string, arg?: number) => void
}
@@ -54,7 +56,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}
css={{ m: '0px', p: '0px', outlineStyle: 'solid', outlineWidth: '1px', outlineColor: theme.colors.grayLine.toString() }}
>
-
+
diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx
index 6dc5dd79a..de0866809 100644
--- a/packages/web/components/templates/article/ArticleContainer.tsx
+++ b/packages/web/components/templates/article/ArticleContainer.tsx
@@ -13,7 +13,6 @@ import { ArticleHeaderToolbar } from './ArticleHeaderToolbar'
import { userPersonalizationMutation } from '../../../lib/networking/mutations/userPersonalizationMutation'
import { updateThemeLocally } from '../../../lib/themeUpdater'
import { ArticleMutations } from '../../../lib/articleActions'
-import { TextAa } from 'phosphor-react'
type ArticleContainerProps = {
article: ArticleAttributes
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
index b51669c2c..f6eccae5d 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -18,9 +18,11 @@ import { ConfirmationModal } from '../../patterns/ConfirmationModal'
import { AlignCenterHorizontalSimple, ArrowsInLineHorizontal, ArrowsOutLineHorizontal, Minus, Pen, Plus, Trash } from 'phosphor-react'
import { AIcon } from '../../elements/images/AIcon'
import { TickedRangeSlider } from '../../elements/TickedRangeSlider'
+import { UserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
type ReaderSettingsProps = {
-
+ userPreferences?: UserPreferences
+ articleActionHandler: (action: string, arg?: number) => void
}
export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
@@ -34,40 +36,42 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
- {}}>
+ width: '265px',
+ height: '70px',
+ borderBottom: `1px solid ${theme.colors.grayLine.toString()}`,
+ }}
+ >
+ props.articleActionHandler('decrementFontSize')}>
- {}}>
+ props.articleActionHandler('incrementFontSize')}>
-
+
Margin:
-
+ props.articleActionHandler('decrementMarginWidth')}>
- {}} />
-
+ {}} />
+ props.articleActionHandler('incrementMarginWidth')}>
-
-
+ */}
)
}
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index f35f94e8a..c0514ca29 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -158,6 +158,7 @@ export default function Home(): JSX.Element {
>
From 278a885f83a466cc7ed03efb003288cf640262f5 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Fri, 8 Apr 2022 20:19:18 -0700
Subject: [PATCH 011/125] Edit labels menu
---
.../templates/article/ArticleActionsMenu.tsx | 8 +-
.../templates/article/EditLabelsModal.tsx | 333 +++++++++++++-----
.../templates/article/ReaderSettingsModal.tsx | 2 +-
3 files changed, 260 insertions(+), 83 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 01cc1d26c..1d989599c 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -5,6 +5,7 @@ import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
import { Box, SpanBox } from "../../elements/LayoutPrimitives"
import { styled, theme } from "../../tokens/stitches.config"
+import { EditLabelsModal } from "./EditLabelsModal"
import { ReaderSettings } from "./ReaderSettingsModal"
export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
@@ -54,7 +55,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}
- css={{ m: '0px', p: '0px', outlineStyle: 'solid', outlineWidth: '1px', outlineColor: theme.colors.grayLine.toString() }}
+ css={{ m: '0px', p: '0px' }}
>
@@ -63,13 +64,16 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}
- css={{ background: 'red' }}
+ css={{ m: '0px', p: '0px' }}
>
+
{/* */}
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index d06cc6e04..1d74f4574 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -3,108 +3,281 @@ import {
ModalOverlay,
ModalRoot,
} from '../../elements/ModalPrimitives'
-import { HStack, VStack } from '../../elements/LayoutPrimitives'
+import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
import { StyledText } from '../../elements/StyledText'
import { CrossIcon } from '../../elements/images/CrossIcon'
-import { theme } from '../../tokens/stitches.config'
+import { styled, theme } from '../../tokens/stitches.config'
import { Label, useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery'
-import { ChangeEvent, useCallback, useState } from 'react'
+import { ChangeEvent, useCallback, useRef, useState, useMemo } from 'react'
import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
import { LabelChip } from '../../elements/LabelChip'
+import { Check, Circle, Pen, PencilSimple, PencilSimpleLine, Plus, TagSimple } from 'phosphor-react'
+import Link from 'next/link'
type EditLabelsModalProps = {
- article: ArticleAttributes
- onOpenChange: (open: boolean) => void
- setLabels: (labels: Label[]) => void
+ // labels: Label[]
+ // article: ArticleAttributes
+ // onOpenChange: (open: boolean) => void
+ // setLabels: (labels: Label[]) => void
+}
+
+type HeaderProps = {
+ filterText: string
+ setFilterText: (text: string) => void
+}
+
+const FormInput = styled('input', {
+ width: '100%',
+ fontSize: '16px',
+ fontFamily: 'inter',
+ fontWeight: 'normal',
+ lineHeight: '1.8',
+ color: '$grayTextContrast',
+ '&:focus': {
+ outline: 'none',
+ },
+})
+
+const StyledLabel = styled('label', {
+ display: 'flex',
+ justifyContent: 'flex-start',
+ '&:focus-visible': {
+ backgroundColor: 'red',
+ },
+})
+
+function Header(props: HeaderProps): JSX.Element {
+ const inputRef = useRef(null)
+
+ return (
+
+ {/* */}
+ {/*
+ Apply labels to this page
+ */}
+ {/* {
+
+ }}
+ >
+
+ */}
+ {/* */}
+
+
+
+ )
+}
+
+type LabelsListProps = {
+ // pageId: string
+ // selectedLabels: Label[]
+ availableLabels: Label[]
+// setSelectedLabels: (labels: Label[]) => void
+}
+
+function LabelsList(props: LabelsListProps): JSX.Element {
+ const isSelected = useCallback((label: Label) => {
+ // return props.selectedLabels.some((other) => {
+ // return other.id === label.id
+ // })
+ return false
+ }, [])
+
+ return (
+
+ {props.availableLabels &&
+ props.availableLabels.map((label, idx) => (
+ {
+ // console.log('selected label', label)
+ // if (props.selectedLabels.includes(label)) {
+ // props.setSelectedLabels(
+ // props.selectedLabels.filter((id) => id !== label)
+ // )
+ // } else {
+ // props.setSelectedLabels([...props.selectedLabels, label])
+ // }
+ // const result = await setLabelsMutation(props.pageId, props.selectedLabels.map((l) => l.id))
+ // console.log('result', result)
+ }}
+ >
+
+ {isSelected(label) && }
+
+
+
+
+
+ {label.name}
+
+
+ {isSelected(label) && }
+
+ {/*
+
+ */}
+ {/* */}
+
+ ))}
+ )
}
export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
- const [selectedLabels, setSelectedLabels] = useState(props.labels)
+ const [filterText, setFilterText] = useState('')
+ const [selectedLabels, setSelectedLabels] = useState([])
const { labels } = useGetLabelsQuery()
const saveAndExit = useCallback(async () => {
- const result = await setLabelsMutation(props.article.id, selectedLabels.map((l) => l.id))
- console.log('result of setting labels', result)
- props.onOpenChange(false)
- props.setLabels(selectedLabels)
+ // const result = await setLabelsMutation(props.article.id, selectedLabels.map((l) => l.id))
+ // console.log('result of setting labels', result)
+ // // props.onOpenChange(false)
+ // // props.setLabels(selectedLabels)
}, [props, selectedLabels])
- const handleChange = useCallback(
- (event: ChangeEvent) => {
- // const label = event.target.value
- // if (event.target.checked) {
- // setSelectedLabels([...selectedLabels, label])
- // } else {
- // setSelectedLabels(selectedLabels.filter((l) => l !== label))
- // }
- },
- [selectedLabels]
- )
+ const filteredLabels = useMemo(() => {
+ if (!labels) {
+ return []
+ }
+ return labels.filter((label) => {
+ return label.name.toLowerCase().includes(filterText.toLowerCase())
+ })
+ }, [labels, filterText])
return (
-
-
- {
- event.preventDefault()
- }}
- css={{ overflow: 'auto', p: '0' }}
- >
-
-
-
- Edit Labels
-
-
+ //
+ // {
+ // event.preventDefault()
+ // }}
+ // css={{ p: '0', width: '100%', maxWidth: '400px' }}
+ // >
+
+
+
+ {filterText && (
+ {
- props.onOpenChange(false)
+ // props.createLabel(filterText)
+ // setFilterText('')
}}
>
-
-
-
- {labels &&
- labels.map((label) => (
- {
- // if (selectedLabels.includes(label.id)) {
- // setSelectedLabels(
- // selectedLabels.filter((id) => id !== label.id)
- // )
- // } else {
- // setSelectedLabels([...selectedLabels, label.id])
- // }
- }}
- >
-
-
-
- ))}
-
-
- Save
-
+
+
+ {`Create new label "${filterText}"`}
+
+ )}
+ {/* Footer */}
+
+
+ Edit labels
-
-
+ //
+ //
)
-}
+}
\ No newline at end of file
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
index f6eccae5d..f1043837a 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -65,7 +65,7 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
props.articleActionHandler('decrementMarginWidth')}>
- {}} />
+ {}} />
props.articleActionHandler('incrementMarginWidth')}>
From a9988d47068e74a5b19ff8f386acfbd47b5b025a Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Fri, 8 Apr 2022 20:37:09 -0700
Subject: [PATCH 012/125] Re-enable top menu on small screens
---
.../web/components/patterns/PrimaryHeader.tsx | 24 +++++++++----------
.../templates/article/ArticleActionsMenu.tsx | 9 +++----
2 files changed, 16 insertions(+), 17 deletions(-)
diff --git a/packages/web/components/patterns/PrimaryHeader.tsx b/packages/web/components/patterns/PrimaryHeader.tsx
index f539fa5fd..bcb8b22ab 100644
--- a/packages/web/components/patterns/PrimaryHeader.tsx
+++ b/packages/web/components/patterns/PrimaryHeader.tsx
@@ -186,19 +186,17 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
- {/* */}
-
-
-
+ height: '100%',
+ width: '100%',
+ display: 'none',
+ '@lgDown': {
+ display: 'flex',
+ },
+ mr: '16px',
+ }}
+ >
+ {}} />
+
{props.username ? (
void
}
@@ -31,6 +30,8 @@ export function MenuSeparator(props: MenuSeparatorProps) {
}
export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element {
+ const { preferencesData } = useGetUserPreferences()
+
return (
<>
-
+
From 0998048163b4d8cb282db978ea7150f07f8f22c0 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Fri, 8 Apr 2022 20:50:53 -0700
Subject: [PATCH 013/125] Remove unused prop
---
packages/web/pages/[username]/[slug]/index.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index c0514ca29..f35f94e8a 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -158,7 +158,6 @@ export default function Home(): JSX.Element {
>
From d8e064807050a30e9d867dbc39858ca489baf991 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Fri, 8 Apr 2022 21:06:51 -0700
Subject: [PATCH 014/125] Remove empty arrow function
---
packages/web/components/patterns/PrimaryHeader.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/packages/web/components/patterns/PrimaryHeader.tsx b/packages/web/components/patterns/PrimaryHeader.tsx
index bcb8b22ab..f96f5138b 100644
--- a/packages/web/components/patterns/PrimaryHeader.tsx
+++ b/packages/web/components/patterns/PrimaryHeader.tsx
@@ -195,7 +195,9 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
mr: '16px',
}}
>
- {}} />
+ {
+ console.log('action handler')
+ }} />
{props.username ? (
From 0b4f223b2670b7dd99a71f0426fa969dd25946ca Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Fri, 8 Apr 2022 21:31:19 -0700
Subject: [PATCH 015/125] Spacing on top dropdowns
---
.../templates/article/ArticleActionsMenu.tsx | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 72e2d5c41..cd5ea9071 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -48,9 +48,9 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}}
>
@@ -64,9 +64,10 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
From 0d16b32ae712e65730b34b002560e4b09c2ab4d0 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Fri, 8 Apr 2022 22:00:57 -0700
Subject: [PATCH 016/125] Test with bottom sheet
---
.../components/elements/DropdownElements.tsx | 1 -
.../templates/article/ArticleActionsMenu.tsx | 19 ++++++++++++++++---
.../templates/article/EditLabelsModal.tsx | 3 ++-
.../templates/article/ReaderSettingsModal.tsx | 7 +++++--
packages/web/package.json | 1 +
5 files changed, 24 insertions(+), 7 deletions(-)
diff --git a/packages/web/components/elements/DropdownElements.tsx b/packages/web/components/elements/DropdownElements.tsx
index d5f3b78c6..6b63da219 100644
--- a/packages/web/components/elements/DropdownElements.tsx
+++ b/packages/web/components/elements/DropdownElements.tsx
@@ -115,7 +115,6 @@ export function Dropdown({
sideOffset = 0,
css
}: DropdownProps): JSX.Element {
- console.log('side', side)
return (
{triggerElement}
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index cd5ea9071..c806fa655 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -1,5 +1,7 @@
import { Separator } from "@radix-ui/react-separator"
import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from "phosphor-react"
+import { useState } from "react"
+import { BottomSheet } from "react-spring-bottom-sheet"
import { useGetUserPreferences, UserPreferences } from "../../../lib/networking/queries/useGetUserPreferences"
import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
@@ -8,6 +10,10 @@ import { styled, theme } from "../../tokens/stitches.config"
import { EditLabelsModal } from "./EditLabelsModal"
import { ReaderSettings } from "./ReaderSettingsModal"
+// if setting up the CSS is tricky, you can add this to your page somewhere:
+//
+import 'react-spring-bottom-sheet/dist/style.css'
+
export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
type ArticleActionsMenuProps = {
@@ -31,6 +37,7 @@ export function MenuSeparator(props: MenuSeparatorProps) {
export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element {
const { preferencesData } = useGetUserPreferences()
+ const [open, setOpen] = useState(false)
return (
<>
@@ -56,7 +63,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}
- css={{ m: '0px', p: '0px' }}
+ css={{ m: '0px', p: '0px' }}
>
@@ -83,8 +90,8 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
-
- props.articleActionHandler('archive')}>
+{/* // props.articleActionHandler('archive') */}
+ setOpen(true)}>
{/*
@@ -92,6 +99,12 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
*/}
+ minHeight}
+ open={open} onDismiss={() => setOpen(false)}>
+
+ {/* */}
+
>
)
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index 1d74f4574..c7205b43d 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -241,7 +241,8 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
// >
@@ -65,7 +66,9 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
props.articleActionHandler('decrementMarginWidth')}>
- {}} />
+ {
+ console.log('range changed')
+ }} />
props.articleActionHandler('incrementMarginWidth')}>
diff --git a/packages/web/package.json b/packages/web/package.json
index 7858d890b..39c1eba08 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -40,6 +40,7 @@
"react-hot-toast": "^2.1.1",
"react-intl": "^5.20.12",
"react-loading-skeleton": "^3.0.2",
+ "react-spring-bottom-sheet": "^3.4.0",
"react-twitter-widgets": "^1.10.0",
"swr": "^1.0.1",
"uuid": "^8.3.2"
From f87db8bfad58599d3f28da115031295b908abded Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 09:40:07 -0700
Subject: [PATCH 017/125] Revert "Test with bottom sheet"
This reverts commit 44faad1172580bf4b7ca85d00f0495f76226dc2d.
---
.../components/elements/DropdownElements.tsx | 1 +
.../templates/article/ArticleActionsMenu.tsx | 19 +++----------------
.../templates/article/EditLabelsModal.tsx | 3 +--
.../templates/article/ReaderSettingsModal.tsx | 7 ++-----
packages/web/package.json | 1 -
5 files changed, 7 insertions(+), 24 deletions(-)
diff --git a/packages/web/components/elements/DropdownElements.tsx b/packages/web/components/elements/DropdownElements.tsx
index 6b63da219..d5f3b78c6 100644
--- a/packages/web/components/elements/DropdownElements.tsx
+++ b/packages/web/components/elements/DropdownElements.tsx
@@ -115,6 +115,7 @@ export function Dropdown({
sideOffset = 0,
css
}: DropdownProps): JSX.Element {
+ console.log('side', side)
return (
{triggerElement}
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index c806fa655..cd5ea9071 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -1,7 +1,5 @@
import { Separator } from "@radix-ui/react-separator"
import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from "phosphor-react"
-import { useState } from "react"
-import { BottomSheet } from "react-spring-bottom-sheet"
import { useGetUserPreferences, UserPreferences } from "../../../lib/networking/queries/useGetUserPreferences"
import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
@@ -10,10 +8,6 @@ import { styled, theme } from "../../tokens/stitches.config"
import { EditLabelsModal } from "./EditLabelsModal"
import { ReaderSettings } from "./ReaderSettingsModal"
-// if setting up the CSS is tricky, you can add this to your page somewhere:
-//
-import 'react-spring-bottom-sheet/dist/style.css'
-
export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
type ArticleActionsMenuProps = {
@@ -37,7 +31,6 @@ export function MenuSeparator(props: MenuSeparatorProps) {
export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element {
const { preferencesData } = useGetUserPreferences()
- const [open, setOpen] = useState(false)
return (
<>
@@ -63,7 +56,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}
- css={{ m: '0px', p: '0px' }}
+ css={{ m: '0px', p: '0px' }}
>
@@ -90,8 +83,8 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
-{/* // props.articleActionHandler('archive') */}
- setOpen(true)}>
+
+ props.articleActionHandler('archive')}>
{/*
@@ -99,12 +92,6 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
*/}
- minHeight}
- open={open} onDismiss={() => setOpen(false)}>
-
- {/* */}
-
>
)
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index c7205b43d..1d74f4574 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -241,8 +241,7 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
// >
@@ -66,9 +65,7 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
props.articleActionHandler('decrementMarginWidth')}>
- {
- console.log('range changed')
- }} />
+ {}} />
props.articleActionHandler('incrementMarginWidth')}>
diff --git a/packages/web/package.json b/packages/web/package.json
index 39c1eba08..7858d890b 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -40,7 +40,6 @@
"react-hot-toast": "^2.1.1",
"react-intl": "^5.20.12",
"react-loading-skeleton": "^3.0.2",
- "react-spring-bottom-sheet": "^3.4.0",
"react-twitter-widgets": "^1.10.0",
"swr": "^1.0.1",
"uuid": "^8.3.2"
From d8e03c090688d5ceab78b3f3a844246b186664bd Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 09:42:38 -0700
Subject: [PATCH 018/125] Make arrow styles more closely match design
---
packages/web/components/elements/DropdownElements.tsx | 6 +++---
.../web/components/templates/article/ArticleActionsMenu.tsx | 1 +
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/packages/web/components/elements/DropdownElements.tsx b/packages/web/components/elements/DropdownElements.tsx
index d5f3b78c6..853bc71bc 100644
--- a/packages/web/components/elements/DropdownElements.tsx
+++ b/packages/web/components/elements/DropdownElements.tsx
@@ -49,12 +49,12 @@ export const DropdownContent = styled(Content, {
backgroundColor: '$grayBg',
borderRadius: '0.5em',
padding: 5,
- border: '1px solid $grayBorder',
+ outline: '1px solid $grayBorder',
boxShadow: '$cardBoxShadow',
})
const StyledArrow = styled(Arrow, {
- fill: '$grayBase',
+ fill: '$grayBg',
})
const StyledLabel = styled(Label, {
@@ -131,7 +131,7 @@ export function Dropdown({
>
{labelText && {labelText} }
{children}
- {showArrow && }
+ {showArrow && }
)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index cd5ea9071..72e5417e2 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -48,6 +48,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}}
>
Date: Sun, 10 Apr 2022 09:47:24 -0700
Subject: [PATCH 019/125] Dont autofocus the labels filter on touch devices as
the keyboard uses too much screen space
---
packages/web/components/templates/article/EditLabelsModal.tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index 1d74f4574..0a3bf1499 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -15,6 +15,7 @@ import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticle
import { LabelChip } from '../../elements/LabelChip'
import { Check, Circle, Pen, PencilSimple, PencilSimpleLine, Plus, TagSimple } from 'phosphor-react'
import Link from 'next/link'
+import { isTouchScreenDevice } from '../../../lib/deviceType'
type EditLabelsModalProps = {
// labels: Label[]
@@ -96,7 +97,7 @@ function Header(props: HeaderProps): JSX.Element {
{
From 05e7ba7ced62731ec8976f8b14730baf048757e1 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 09:48:43 -0700
Subject: [PATCH 020/125] Remove empty arrow function
---
.../web/components/templates/article/ReaderSettingsModal.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
index f1043837a..e8c70709b 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -65,7 +65,9 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
props.articleActionHandler('decrementMarginWidth')}>
- {}} />
+ {
+ console.log('range changed', value)
+ }} />
props.articleActionHandler('incrementMarginWidth')}>
From eeea5b7c28a87caac804c3ce3b54c97fdd7d241f Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 10:20:34 -0700
Subject: [PATCH 021/125] Consistent dropdown layout for article actions
We also offset a little to display the popover in the middle of
the article action button on desktop
Disable autofocus on touch devices so the keyboard doesn't take
up too much space
---
.../components/elements/DropdownElements.tsx | 27 ++++++------
.../templates/article/ArticleActionsMenu.tsx | 43 ++++++++++++-------
.../templates/article/EditLabelsModal.tsx | 2 +-
.../templates/article/ReaderSettingsModal.tsx | 8 ++--
4 files changed, 48 insertions(+), 32 deletions(-)
diff --git a/packages/web/components/elements/DropdownElements.tsx b/packages/web/components/elements/DropdownElements.tsx
index 853bc71bc..61dfeaac2 100644
--- a/packages/web/components/elements/DropdownElements.tsx
+++ b/packages/web/components/elements/DropdownElements.tsx
@@ -8,6 +8,7 @@ import {
Arrow,
Label,
} from '@radix-ui/react-dropdown-menu'
+import { PopperContentProps } from '@radix-ui/react-popover';
import { CSS } from '@stitches/react';
import { styled } from './../tokens/stitches.config'
@@ -104,18 +105,19 @@ export function DropdownOption(props: DropdownOptionProps): JSX.Element {
)
}
-export function Dropdown({
- children,
- align,
- triggerElement,
- labelText,
- showArrow = true,
- disabled = false,
- side = 'bottom',
- sideOffset = 0,
- css
-}: DropdownProps): JSX.Element {
- console.log('side', side)
+export function Dropdown(props: DropdownProps & PopperContentProps): JSX.Element {
+ const {
+ children,
+ align,
+ triggerElement,
+ labelText,
+ showArrow = true,
+ disabled = false,
+ side = 'bottom',
+ sideOffset = 0,
+ alignOffset = 0,
+ css
+ } = props
return (
{triggerElement}
@@ -128,6 +130,7 @@ export function Dropdown({
side={side}
sideOffset={sideOffset}
align={align ? align : 'center'}
+ alignOffset={alignOffset}
>
{labelText && {labelText} }
{children}
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 72e5417e2..a97a4370d 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -19,7 +19,7 @@ type MenuSeparatorProps = {
layout: ArticleActionsMenuLayout
}
-export function MenuSeparator(props: MenuSeparatorProps) {
+const MenuSeparator = (props: MenuSeparatorProps): JSX.Element => {
const LineSeparator = styled(Separator, {
width: '100%',
margin: 0,
@@ -29,6 +29,26 @@ export function MenuSeparator(props: MenuSeparatorProps) {
return (props.layout == 'vertical' ? : <>>)
}
+type ActionDropdownProps = {
+ layout: ArticleActionsMenuLayout
+ triggerElement: JSX.Element
+ children: JSX.Element
+}
+
+const ActionDropdown = (props: ActionDropdownProps): JSX.Element => {
+ return
+ {props.children}
+
+}
+
export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element {
const { preferencesData } = useGetUserPreferences()
@@ -47,38 +67,29 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
m: '0px',
}}
>
-
}
- css={{ m: '0px', p: '0px' }}
>
-
+
-
}
- css={{ m: '0px', p: '0px' }}
>
- {/* */}
-
+
props.articleActionHandler('showHighlights')}>
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index 0a3bf1499..ba441f215 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -97,7 +97,7 @@ function Header(props: HeaderProps): JSX.Element {
{
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
index e8c70709b..fbab67335 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -73,7 +73,7 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
- {/*
- {}} />
+ {
+ console.log('changed line spacing')
+ }} />
- */}
+
)
}
From 1427ffc783d531f32045f33c4b38563ac98c336a Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 10:42:02 -0700
Subject: [PATCH 022/125] Test some smaller sizes for mobile screens
---
.../web/components/templates/article/EditLabelsModal.tsx | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index ba441f215..b66b951b5 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -97,6 +97,7 @@ function Header(props: HeaderProps): JSX.Element {
Date: Sun, 10 Apr 2022 11:13:30 -0700
Subject: [PATCH 023/125] Increase small screen maxheight
---
packages/web/components/templates/article/EditLabelsModal.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index b66b951b5..c04f7321f 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -246,7 +246,7 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
maxWidth: '265px',
'@mdDown': {
maxWidth: '100%',
- maxHeight: '280px',
+ maxHeight: '320px',
},
}}>
From 0a4215c57ba18e28775a3845ea16ac77c43ae78b Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 14:52:30 -0700
Subject: [PATCH 024/125] Handle toggling label state
---
.../templates/article/ArticleActionsMenu.tsx | 1 +
.../templates/article/EditLabelsModal.tsx | 358 ++++++++++--------
2 files changed, 194 insertions(+), 165 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index a97a4370d..42b8651a5 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -1,5 +1,6 @@
import { Separator } from "@radix-ui/react-separator"
import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from "phosphor-react"
+import { useRef } from "react"
import { useGetUserPreferences, UserPreferences } from "../../../lib/networking/queries/useGetUserPreferences"
import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index c04f7321f..e9b2172e0 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -9,7 +9,7 @@ import { StyledText } from '../../elements/StyledText'
import { CrossIcon } from '../../elements/images/CrossIcon'
import { styled, theme } from '../../tokens/stitches.config'
import { Label, useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery'
-import { ChangeEvent, useCallback, useRef, useState, useMemo } from 'react'
+import { ChangeEvent, useCallback, useRef, useState, useMemo, useEffect } from 'react'
import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
import { LabelChip } from '../../elements/LabelChip'
@@ -26,6 +26,8 @@ type EditLabelsModalProps = {
type HeaderProps = {
filterText: string
+ focused: boolean
+ parentRef: React.RefObject
setFilterText: (text: string) => void
}
@@ -44,70 +46,68 @@ const FormInput = styled('input', {
const StyledLabel = styled('label', {
display: 'flex',
justifyContent: 'flex-start',
- '&:focus-visible': {
- backgroundColor: 'red',
+ '&:focus': {
+ bg: '$grayBgActive',
+ },
+ 'input:&:focus-within': {
+ bg: '$grayBgActive',
},
})
+const useToggleLabels = () => {
+ const [selectedLabels, setSelectedLabels] = useState([])
+
+ const isSelected = useCallback((label: Label): boolean => {
+ return selectedLabels.some((other) => {
+ return other.id === label.id
+ })
+ }, [selectedLabels])
+
+ const toggleLabel = useCallback((label: Label) => {
+ if (isSelected(label)) {
+ setSelectedLabels(selectedLabels.filter((other) => {
+ return other.id !== label.id
+ }))
+ } else {
+ setSelectedLabels([...selectedLabels, label])
+ }
+ }, [isSelected, selectedLabels])
+
+ return [isSelected, toggleLabel]
+}
+
function Header(props: HeaderProps): JSX.Element {
const inputRef = useRef(null)
+ useEffect(() => {
+ if (props.focused && inputRef.current) {
+ inputRef.current.focus()
+ }
+ }, [props.focused])
+
return (
- {/* */}
- {/*
- Apply labels to this page
- */}
- {/* {
-
- }}
- >
-
- */}
- {/* */}
-
)
}
-type LabelsListProps = {
- // pageId: string
- // selectedLabels: Label[]
- availableLabels: Label[]
-// setSelectedLabels: (labels: Label[]) => void
+type LabelListItemProps = {
+ label: Label
+ focused: boolean
+ selected: boolean
}
-function LabelsList(props: LabelsListProps): JSX.Element {
- const isSelected = useCallback((label: Label) => {
- // return props.selectedLabels.some((other) => {
- // return other.id === label.id
- // })
- return false
- }, [])
+function LabelListItem(props: LabelListItemProps): JSX.Element {
+ const ref = useRef(null)
+ const { label, focused, selected } = props
+
+ useEffect(() => {
+ if (props.focused && ref.current) {
+ ref.current.focus()
+ }
+ }, [props.focused])
return (
-
- {props.availableLabels &&
- props.availableLabels.map((label, idx) => (
- {
- // console.log('selected label', label)
- // if (props.selectedLabels.includes(label)) {
- // props.setSelectedLabels(
- // props.selectedLabels.filter((id) => id !== label)
- // )
- // } else {
- // props.setSelectedLabels([...props.selectedLabels, label])
- // }
- // const result = await setLabelsMutation(props.pageId, props.selectedLabels.map((l) => l.id))
- // console.log('result', result)
- }}
- >
-
- {isSelected(label) && }
-
-
-
-
-
- {label.name}
-
-
- {isSelected(label) && }
-
- {/*
-
- */}
- {/* */}
-
- ))}
- )
+ {
+ console.log('toggling label')
+ }}
+ >
+
+
+ {selected && }
+
+
+
+
+
+ {label.name}
+
+
+ {selected && }
+
+
+ )
}
export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
+ const parentRef = useRef(null)
const [filterText, setFilterText] = useState('')
- const [selectedLabels, setSelectedLabels] = useState([])
const { labels } = useGetLabelsQuery()
- const saveAndExit = useCallback(async () => {
- // const result = await setLabelsMutation(props.article.id, selectedLabels.map((l) => l.id))
- // console.log('result of setting labels', result)
- // // props.onOpenChange(false)
- // // props.setLabels(selectedLabels)
- }, [props, selectedLabels])
+ const [selectedLabels, setSelectedLabels] = useState([])
+
+ const isSelected = useCallback((label: Label): boolean => {
+ return selectedLabels.some((other) => {
+ return other.id === label.id
+ })
+ }, [selectedLabels])
+
+ const toggleLabel = useCallback((label: Label) => {
+ if (isSelected(label)) {
+ setSelectedLabels(selectedLabels.filter((other) => {
+ return other.id !== label.id
+ }))
+ } else {
+ setSelectedLabels([...selectedLabels, label])
+ }
+ }, [isSelected, selectedLabels])
const filteredLabels = useMemo(() => {
if (!labels) {
@@ -232,58 +220,98 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
})
}, [labels, filterText])
+ useEffect(() => {
+ setFocusedIndex(undefined)
+ }, [filterText])
+
+ // Move focus through the labels list on tab or arrow up/down keys
+ const [focusedIndex, setFocusedIndex] = useState(undefined)
+ const handleKeyDown = useCallback((event: React.KeyboardEvent) => {
+ if (event.key === 'ArrowUp') {
+ event.preventDefault()
+ if (focusedIndex) {
+ setFocusedIndex(Math.max(0, focusedIndex - 1))
+ } else {
+ setFocusedIndex(undefined)
+ }
+ }
+ if (event.key === 'ArrowDown' || event.key === 'Tab') {
+ event.preventDefault()
+ if (focusedIndex === undefined) {
+ setFocusedIndex(0)
+ } else {
+ setFocusedIndex(Math.min(filteredLabels.length - 1, focusedIndex + 1))
+ }
+ }
+ if (event.key === 'Enter') {
+ event.preventDefault()
+ if (focusedIndex !== undefined) {
+ const label = filteredLabels[focusedIndex]
+ if (label) {
+ toggleLabel(label)
+ }
+ }
+ }
+ }, [filteredLabels, focusedIndex])
+
return (
- //
- //
- // {
- // event.preventDefault()
- // }}
- // css={{ p: '0', width: '100%', maxWidth: '400px' }}
- // >
-
+
+
+ {labels &&
+ labels.map((label, idx) => (
+
+ ))}
+
+ {filterText && (
+ {
+ // props.createLabel(filterText)
+ // setFilterText('')
+ }}
+ >
+
+
+ {`Create new label "${filterText}"`}
+
+ )}
+ {/* Footer */}
+
-
-
- {filterText && (
- {
- // props.createLabel(filterText)
- // setFilterText('')
- }}
- >
-
-
- {`Create new label "${filterText}"`}
-
- )}
- {/* Footer */}
-
-
- Edit labels
-
-
- //
- //
+ 'a:visited': {
+ color: theme.colors.grayText.toString(),
+ },
+ }}
+ >
+
+ Edit labels
+
+
)
}
\ No newline at end of file
From 6dd39d54887089b04b08e38308076935b9c32767 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 15:14:49 -0700
Subject: [PATCH 025/125] Handle toggling labels with mouse
---
.../templates/article/EditLabelsModal.tsx | 32 +++++++++++--------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index e9b2172e0..af3db7061 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -9,7 +9,7 @@ import { StyledText } from '../../elements/StyledText'
import { CrossIcon } from '../../elements/images/CrossIcon'
import { styled, theme } from '../../tokens/stitches.config'
import { Label, useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery'
-import { ChangeEvent, useCallback, useRef, useState, useMemo, useEffect } from 'react'
+import { ChangeEvent, useCallback, useRef, useState, useMemo, useEffect, MouseEventHandler } from 'react'
import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
import { LabelChip } from '../../elements/LabelChip'
@@ -49,9 +49,6 @@ const StyledLabel = styled('label', {
'&:focus': {
bg: '$grayBgActive',
},
- 'input:&:focus-within': {
- bg: '$grayBgActive',
- },
})
const useToggleLabels = () => {
@@ -142,6 +139,7 @@ type LabelListItemProps = {
label: Label
focused: boolean
selected: boolean
+ toggleLabel: (label: Label) => void
}
function LabelListItem(props: LabelListItemProps): JSX.Element {
@@ -164,8 +162,10 @@ function LabelListItem(props: LabelListItemProps): JSX.Element {
bg: props.focused ? '$grayBgActive' : 'unset',
}}
tabIndex={props.focused ? 0 : -1}
- onClick={async () => {
- console.log('toggling label')
+ onClick={(event) => {
+ event.preventDefault()
+ props.toggleLabel(label)
+ ref.current?.blur()
}}
>
@@ -260,13 +260,16 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
distribution="start"
onKeyDown={handleKeyDown}
css={{
- p: '0',
- maxHeight: '92%',
- maxWidth: '265px',
- '@mdDown': {
- maxWidth: '100%',
+ p: '0',
+ width: '265px',
maxHeight: '92%',
- },
+ '@mdDown': {
+ maxHeight: '92%',
+ },
+ '@smDown': {
+ maxHeight: '92%',
+ maxWidth: '92%',
+ }
}}>
- {labels &&
- labels.map((label, idx) => (
+ {filteredLabels &&
+ filteredLabels.map((label, idx) => (
))}
From 92ea5efb9867b7dbf1c1032cbd6d29438397de00 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 15:43:14 -0700
Subject: [PATCH 026/125] Update naming
---
.../templates/article/EditLabelsControl.tsx | 321 ++++++++++++++++++
1 file changed, 321 insertions(+)
create mode 100644 packages/web/components/templates/article/EditLabelsControl.tsx
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
new file mode 100644
index 000000000..33d064c3e
--- /dev/null
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -0,0 +1,321 @@
+import {
+ ModalContent,
+ ModalOverlay,
+ ModalRoot,
+} from '../../elements/ModalPrimitives'
+import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
+import { Button } from '../../elements/Button'
+import { StyledText } from '../../elements/StyledText'
+import { CrossIcon } from '../../elements/images/CrossIcon'
+import { styled, theme } from '../../tokens/stitches.config'
+import { Label, useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery'
+import { ChangeEvent, useCallback, useRef, useState, useMemo, useEffect, MouseEventHandler } from 'react'
+import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
+import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
+import { LabelChip } from '../../elements/LabelChip'
+import { Check, Circle, Pen, PencilSimple, PencilSimpleLine, Plus, TagSimple } from 'phosphor-react'
+import Link from 'next/link'
+import { isTouchScreenDevice } from '../../../lib/deviceType'
+
+type EditLabelsControlProps = {
+ // labels: Label[]
+ // article: ArticleAttributes
+ // onOpenChange: (open: boolean) => void
+ // setLabels: (labels: Label[]) => void
+}
+
+type HeaderProps = {
+ filterText: string
+ focused: boolean
+ parentRef: React.RefObject
+ setFilterText: (text: string) => void
+}
+
+const FormInput = styled('input', {
+ width: '100%',
+ fontSize: '16px',
+ fontFamily: 'inter',
+ fontWeight: 'normal',
+ lineHeight: '1.8',
+ color: '$grayTextContrast',
+ '&:focus': {
+ outline: 'none',
+ },
+})
+
+const StyledLabel = styled('label', {
+ display: 'flex',
+ justifyContent: 'flex-start',
+ '&:focus': {
+ bg: '$grayBgActive',
+ },
+})
+
+const useToggleLabels = () => {
+ const [selectedLabels, setSelectedLabels] = useState([])
+
+ const isSelected = useCallback((label: Label): boolean => {
+ return selectedLabels.some((other) => {
+ return other.id === label.id
+ })
+ }, [selectedLabels])
+
+ const toggleLabel = useCallback((label: Label) => {
+ if (isSelected(label)) {
+ setSelectedLabels(selectedLabels.filter((other) => {
+ return other.id !== label.id
+ }))
+ } else {
+ setSelectedLabels([...selectedLabels, label])
+ }
+ }, [isSelected, selectedLabels])
+
+ return [isSelected, toggleLabel]
+}
+
+function Header(props: HeaderProps): JSX.Element {
+ const inputRef = useRef(null)
+
+ useEffect(() => {
+ if (props.focused && inputRef.current) {
+ inputRef.current.focus()
+ }
+ }, [props.focused])
+
+ return (
+
+
+ {
+ event.target.select()
+ //setFocused(true)
+ console.log('input::focused()')
+ }}
+ onBlur={() => {
+ // setFocused(false)
+ console.log('input::blurred')
+ }}
+ onKeyDown={(event) => {
+ console.log('keydown', event.key)
+ }}
+ onChange={(event) => {
+ console.log('event', event)
+ props.setFilterText(event.target.value)
+ }}
+ css={{
+ border: '1px solid $grayBorder',
+ borderRadius: '8px',
+ width: '100%',
+ bg: 'transparent',
+ fontSize: '16px',
+ fontFamily: 'inter',
+ fontWeight: 'normal',
+ marginBottom: '2px',
+ textIndent: '8px',
+ paddingLeft: '8px',
+ color: '$grayTextContrast',
+ '&:focus': {
+ outline: 'none',
+ boxShadow: '0px 0px 2px 2px rgba(255, 234, 159, 0.56)',
+ },
+ }}
+ />
+
+ )
+}
+
+type LabelListItemProps = {
+ label: Label
+ focused: boolean
+ selected: boolean
+ toggleLabel: (label: Label) => void
+}
+
+function LabelListItem(props: LabelListItemProps): JSX.Element {
+ const ref = useRef(null)
+ const { label, focused, selected } = props
+
+ useEffect(() => {
+ if (props.focused && ref.current) {
+ ref.current.focus()
+ }
+ }, [props.focused])
+
+ return (
+ {
+ event.preventDefault()
+ props.toggleLabel(label)
+ ref.current?.blur()
+ }}
+ >
+
+
+ {selected && }
+
+
+
+
+
+ {label.name}
+
+
+ {selected && }
+
+
+ )
+}
+
+export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
+ const parentRef = useRef(null)
+ const [filterText, setFilterText] = useState('')
+ const { labels } = useGetLabelsQuery()
+
+ const [selectedLabels, setSelectedLabels] = useState([])
+
+ const isSelected = useCallback((label: Label): boolean => {
+ return selectedLabels.some((other) => {
+ return other.id === label.id
+ })
+ }, [selectedLabels])
+
+ const toggleLabel = useCallback((label: Label) => {
+ if (isSelected(label)) {
+ setSelectedLabels(selectedLabels.filter((other) => {
+ return other.id !== label.id
+ }))
+ } else {
+ setSelectedLabels([...selectedLabels, label])
+ }
+ }, [isSelected, selectedLabels])
+
+ const filteredLabels = useMemo(() => {
+ if (!labels) {
+ return []
+ }
+ return labels.filter((label) => {
+ return label.name.toLowerCase().includes(filterText.toLowerCase())
+ })
+ }, [labels, filterText])
+
+ useEffect(() => {
+ setFocusedIndex(undefined)
+ }, [filterText])
+
+ // Move focus through the labels list on tab or arrow up/down keys
+ const [focusedIndex, setFocusedIndex] = useState(undefined)
+ const handleKeyDown = useCallback((event: React.KeyboardEvent) => {
+ if (event.key === 'ArrowUp') {
+ event.preventDefault()
+ if (focusedIndex) {
+ setFocusedIndex(Math.max(0, focusedIndex - 1))
+ } else {
+ setFocusedIndex(undefined)
+ }
+ }
+ if (event.key === 'ArrowDown' || event.key === 'Tab') {
+ event.preventDefault()
+ if (focusedIndex === undefined) {
+ setFocusedIndex(0)
+ } else {
+ setFocusedIndex(Math.min(filteredLabels.length - 1, focusedIndex + 1))
+ }
+ }
+ if (event.key === 'Enter') {
+ event.preventDefault()
+ if (focusedIndex !== undefined) {
+ const label = filteredLabels[focusedIndex]
+ if (label) {
+ toggleLabel(label)
+ }
+ }
+ }
+ }, [filteredLabels, focusedIndex])
+
+ return (
+
+
+
+ {filteredLabels &&
+ filteredLabels.map((label, idx) => (
+
+ ))}
+
+ {filterText && (
+ {
+ // props.createLabel(filterText)
+ // setFilterText('')
+ }}
+ >
+
+
+ {`Create new label "${filterText}"`}
+
+ )}
+ {/* Footer */}
+
+
+ Edit labels
+
+
+ )
+}
\ No newline at end of file
From de72df98282fdb5866fa487ce5e8591215a1c5ee Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 15:54:51 -0700
Subject: [PATCH 027/125] Naming, update small screen layout
---
.../templates/article/ArticleActionsMenu.tsx | 4 +-
.../templates/article/EditLabelsControl.tsx | 28 +-
.../templates/article/EditLabelsModal.tsx | 321 ------------------
.../web/pages/[username]/[slug]/index.tsx | 2 +-
4 files changed, 7 insertions(+), 348 deletions(-)
delete mode 100644 packages/web/components/templates/article/EditLabelsModal.tsx
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 42b8651a5..0f115e180 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -6,7 +6,7 @@ import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
import { Box, SpanBox } from "../../elements/LayoutPrimitives"
import { styled, theme } from "../../tokens/stitches.config"
-import { EditLabelsModal } from "./EditLabelsModal"
+import { EditLabelsControl } from "./EditLabelsControl"
import { ReaderSettings } from "./ReaderSettingsModal"
export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
@@ -89,7 +89,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}
>
-
+
props.articleActionHandler('showHighlights')}>
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index 33d064c3e..52f3604a4 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -83,8 +83,7 @@ function Header(props: HeaderProps): JSX.Element {
}, [props.focused])
return (
-
+
{
- event.target.select()
- //setFocused(true)
- console.log('input::focused()')
- }}
- onBlur={() => {
- // setFocused(false)
- console.log('input::blurred')
- }}
- onKeyDown={(event) => {
- console.log('keydown', event.key)
- }}
onChange={(event) => {
- console.log('event', event)
props.setFilterText(event.target.value)
}}
css={{
@@ -119,11 +105,8 @@ function Header(props: HeaderProps): JSX.Element {
width: '100%',
bg: 'transparent',
fontSize: '16px',
- fontFamily: 'inter',
- fontWeight: 'normal',
- marginBottom: '2px',
textIndent: '8px',
- paddingLeft: '8px',
+ marginBottom: '2px',
color: '$grayTextContrast',
'&:focus': {
outline: 'none',
@@ -261,15 +244,12 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
onKeyDown={handleKeyDown}
css={{
p: '0',
- width: '265px',
maxHeight: '92%',
+ maxWidth: '265px',
'@mdDown': {
+ maxWidth: '100%',
maxHeight: '92%',
},
- '@smDown': {
- maxHeight: '92%',
- maxWidth: '92%',
- }
}}>
void
- // setLabels: (labels: Label[]) => void
-}
-
-type HeaderProps = {
- filterText: string
- focused: boolean
- parentRef: React.RefObject
- setFilterText: (text: string) => void
-}
-
-const FormInput = styled('input', {
- width: '100%',
- fontSize: '16px',
- fontFamily: 'inter',
- fontWeight: 'normal',
- lineHeight: '1.8',
- color: '$grayTextContrast',
- '&:focus': {
- outline: 'none',
- },
-})
-
-const StyledLabel = styled('label', {
- display: 'flex',
- justifyContent: 'flex-start',
- '&:focus': {
- bg: '$grayBgActive',
- },
-})
-
-const useToggleLabels = () => {
- const [selectedLabels, setSelectedLabels] = useState([])
-
- const isSelected = useCallback((label: Label): boolean => {
- return selectedLabels.some((other) => {
- return other.id === label.id
- })
- }, [selectedLabels])
-
- const toggleLabel = useCallback((label: Label) => {
- if (isSelected(label)) {
- setSelectedLabels(selectedLabels.filter((other) => {
- return other.id !== label.id
- }))
- } else {
- setSelectedLabels([...selectedLabels, label])
- }
- }, [isSelected, selectedLabels])
-
- return [isSelected, toggleLabel]
-}
-
-function Header(props: HeaderProps): JSX.Element {
- const inputRef = useRef(null)
-
- useEffect(() => {
- if (props.focused && inputRef.current) {
- inputRef.current.focus()
- }
- }, [props.focused])
-
- return (
-
-
- {
- event.target.select()
- //setFocused(true)
- console.log('input::focused()')
- }}
- onBlur={() => {
- // setFocused(false)
- console.log('input::blurred')
- }}
- onKeyDown={(event) => {
- console.log('keydown', event.key)
- }}
- onChange={(event) => {
- console.log('event', event)
- props.setFilterText(event.target.value)
- }}
- css={{
- border: '1px solid $grayBorder',
- borderRadius: '8px',
- width: '100%',
- bg: 'transparent',
- fontSize: '16px',
- fontFamily: 'inter',
- fontWeight: 'normal',
- marginBottom: '2px',
- textIndent: '8px',
- paddingLeft: '8px',
- color: '$grayTextContrast',
- '&:focus': {
- outline: 'none',
- boxShadow: '0px 0px 2px 2px rgba(255, 234, 159, 0.56)',
- },
- }}
- />
-
- )
-}
-
-type LabelListItemProps = {
- label: Label
- focused: boolean
- selected: boolean
- toggleLabel: (label: Label) => void
-}
-
-function LabelListItem(props: LabelListItemProps): JSX.Element {
- const ref = useRef(null)
- const { label, focused, selected } = props
-
- useEffect(() => {
- if (props.focused && ref.current) {
- ref.current.focus()
- }
- }, [props.focused])
-
- return (
- {
- event.preventDefault()
- props.toggleLabel(label)
- ref.current?.blur()
- }}
- >
-
-
- {selected && }
-
-
-
-
-
- {label.name}
-
-
- {selected && }
-
-
- )
-}
-
-export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
- const parentRef = useRef(null)
- const [filterText, setFilterText] = useState('')
- const { labels } = useGetLabelsQuery()
-
- const [selectedLabels, setSelectedLabels] = useState([])
-
- const isSelected = useCallback((label: Label): boolean => {
- return selectedLabels.some((other) => {
- return other.id === label.id
- })
- }, [selectedLabels])
-
- const toggleLabel = useCallback((label: Label) => {
- if (isSelected(label)) {
- setSelectedLabels(selectedLabels.filter((other) => {
- return other.id !== label.id
- }))
- } else {
- setSelectedLabels([...selectedLabels, label])
- }
- }, [isSelected, selectedLabels])
-
- const filteredLabels = useMemo(() => {
- if (!labels) {
- return []
- }
- return labels.filter((label) => {
- return label.name.toLowerCase().includes(filterText.toLowerCase())
- })
- }, [labels, filterText])
-
- useEffect(() => {
- setFocusedIndex(undefined)
- }, [filterText])
-
- // Move focus through the labels list on tab or arrow up/down keys
- const [focusedIndex, setFocusedIndex] = useState(undefined)
- const handleKeyDown = useCallback((event: React.KeyboardEvent) => {
- if (event.key === 'ArrowUp') {
- event.preventDefault()
- if (focusedIndex) {
- setFocusedIndex(Math.max(0, focusedIndex - 1))
- } else {
- setFocusedIndex(undefined)
- }
- }
- if (event.key === 'ArrowDown' || event.key === 'Tab') {
- event.preventDefault()
- if (focusedIndex === undefined) {
- setFocusedIndex(0)
- } else {
- setFocusedIndex(Math.min(filteredLabels.length - 1, focusedIndex + 1))
- }
- }
- if (event.key === 'Enter') {
- event.preventDefault()
- if (focusedIndex !== undefined) {
- const label = filteredLabels[focusedIndex]
- if (label) {
- toggleLabel(label)
- }
- }
- }
- }, [filteredLabels, focusedIndex])
-
- return (
-
-
-
- {filteredLabels &&
- filteredLabels.map((label, idx) => (
-
- ))}
-
- {filterText && (
- {
- // props.createLabel(filterText)
- // setFilterText('')
- }}
- >
-
-
- {`Create new label "${filterText}"`}
-
- )}
- {/* Footer */}
-
-
- Edit labels
-
-
- )
-}
\ No newline at end of file
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index f35f94e8a..0cc663a63 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -20,7 +20,7 @@ import { articleReadingProgressMutation } from '../../../lib/networking/mutation
import { updateHighlightMutation } from '../../../lib/networking/mutations/updateHighlightMutation'
import { userPersonalizationMutation } from '../../../lib/networking/mutations/userPersonalizationMutation'
import Script from 'next/script'
-import { EditLabelsModal } from '../../../components/templates/article/EditLabelsModal'
+import { EditLabelsControl } from '../../../components/templates/article/EditLabelsControl'
import { Label } from '../../../lib/networking/fragments/labelFragment'
import { isVipUser } from '../../../lib/featureFlag'
import { styled, theme } from '../../../components/tokens/stitches.config'
From e7528cfc439351cbd2ea0040f10871fb0756677b Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 16:07:25 -0700
Subject: [PATCH 028/125] Attempt to dismiss focus on mobile
---
packages/web/components/templates/article/EditLabelsControl.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index 52f3604a4..7551fc7eb 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -92,7 +92,7 @@ function Header(props: HeaderProps): JSX.Element {
Date: Sun, 10 Apr 2022 17:22:06 -0700
Subject: [PATCH 029/125] Wire up article actions to have an article instance
when hosted in the header
---
.../web/components/patterns/PrimaryHeader.tsx | 35 +++++++++----------
.../components/templates/PrimaryLayout.tsx | 3 +-
.../templates/article/ArticleActionsMenu.tsx | 14 +++++++-
.../web/pages/[username]/[slug]/index.tsx | 29 ++++++++-------
4 files changed, 45 insertions(+), 36 deletions(-)
diff --git a/packages/web/components/patterns/PrimaryHeader.tsx b/packages/web/components/patterns/PrimaryHeader.tsx
index f96f5138b..57b6cc0ed 100644
--- a/packages/web/components/patterns/PrimaryHeader.tsx
+++ b/packages/web/components/patterns/PrimaryHeader.tsx
@@ -17,8 +17,6 @@ import { UserBasicData } from '../../lib/networking/queries/useGetViewerQuery'
import { setupAnalytics } from '../../lib/analytics'
import { Button } from '../elements/Button'
import Link from 'next/link'
-import { ArchiveBox, ArrowSquareOut, DotsThree, HighlighterCircle, TagSimple, TextAa } from 'phosphor-react'
-import { ArticleActionsMenu } from '../templates/article/ArticleActionsMenu'
type HeaderProps = {
user?: UserBasicData
@@ -28,6 +26,7 @@ type HeaderProps = {
isFixedPosition: boolean
scrollElementRef?: React.RefObject
displayFontStepper?: boolean
+ toolbarControl?: JSX.Element
setShowLogoutConfirmation: (showShareModal: boolean) => void
setShowKeyboardCommandsModal: (showShareModal: boolean) => void
}
@@ -56,9 +55,6 @@ export function PrimaryHeader(props: HeaderProps): JSX.Element {
(changeset: ScrollOffsetChangeset) => {
const isScrolledBeyondMinThreshold = changeset.current.y >= 50
const isScrollingDown = changeset.current.y > changeset.previous.y
-
- // setIsScrolled(isScrolledBeyondMinThreshold)
- // setShowHeader(!(isScrollingDown && isScrolledBeyondMinThreshold))
},
0
)
@@ -129,6 +125,7 @@ export function PrimaryHeader(props: HeaderProps): JSX.Element {
isDisplayingShadow={isScrolled}
isVisible={true}
isFixedPosition={true}
+ toolbarControl={props.toolbarControl}
displayFontStepper={props.displayFontStepper}
/>
>
@@ -145,6 +142,7 @@ type NavHeaderProps = {
isVisible?: boolean
isFixedPosition: boolean
displayFontStepper?: boolean
+ toolbarControl?: JSX.Element
}
function NavHeader(props: NavHeaderProps): JSX.Element {
@@ -185,20 +183,19 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
-
- {
- console.log('action handler')
- }} />
-
+ {props.toolbarControl && (
+
+ {props.toolbarControl}
+
+ )}
{props.username ? (
displayFontStepper?: boolean
+ headerToolbarControl?: JSX.Element
}
export function PrimaryLayout(props: PrimaryLayoutProps): JSX.Element {
@@ -76,6 +76,7 @@ export function PrimaryLayout(props: PrimaryLayoutProps): JSX.Element {
userInitials={viewerData?.me?.name.charAt(0) ?? ''}
profileImageURL={viewerData?.me?.profile.pictureUrl}
isFixedPosition={true}
+ toolbarControl={props.headerToolbarControl}
scrollElementRef={props.scrollElementRef}
displayFontStepper={props.displayFontStepper}
setShowLogoutConfirmation={setShowLogoutConfirmation}
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 0f115e180..e28a41dca 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -1,6 +1,7 @@
import { Separator } from "@radix-ui/react-separator"
import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from "phosphor-react"
import { useRef } from "react"
+import { ArticleAttributes } from "../../../lib/networking/queries/useGetArticleQuery"
import { useGetUserPreferences, UserPreferences } from "../../../lib/networking/queries/useGetUserPreferences"
import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
@@ -12,6 +13,7 @@ import { ReaderSettings } from "./ReaderSettingsModal"
export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
type ArticleActionsMenuProps = {
+ article: ArticleAttributes
layout: ArticleActionsMenuLayout
articleActionHandler: (action: string, arg?: number) => void
}
@@ -39,7 +41,7 @@ type ActionDropdownProps = {
const ActionDropdown = (props: ActionDropdownProps): JSX.Element => {
return
+{/*
+ props.articleActionHandler('editLabels')} css={{
+ // '@smDown': {
+ // display: 'none',
+ // },
+ }}>
+
+
+
+ */}
props.articleActionHandler('showHighlights')}>
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index 0cc663a63..cb7e7761e 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -31,6 +31,7 @@ import { Article } from '../../../components/templates/article/Article'
import { ArticleActionsMenu } from '../../../components/templates/article/ArticleActionsMenu'
import { HighlightsModal } from '../../../components/templates/article/HighlightsModal'
import { setLinkArchivedMutation } from '../../../lib/networking/mutations/setLinkArchivedMutation'
+import { EditLabelsModal } from '../../../components/templates/article/EditLabelsModal'
const MenuSeparator = styled(Separator, {
width: '100%',
@@ -111,9 +112,8 @@ export default function Home(): JSX.Element {
updateMarginWidth(Math.max(marginWidth - 50, 200))
break
case 'editLabels':
- if (viewerData?.me && isVipUser(viewerData?.me)) {
- setShowLabelsModal(true)
- }
+ setShowLabelsModal(true)
+ console.log('showing labels modal')
break
}
};
@@ -130,6 +130,13 @@ export default function Home(): JSX.Element {
pageTestId="home-page-tag"
scrollElementRef={scrollRef}
displayFontStepper={true}
+ headerToolbarControl={
+
+ }
pageMetaDataProps={{
title: article.title,
path: router.pathname,
@@ -157,6 +164,7 @@ export default function Home(): JSX.Element {
}}
>
@@ -205,18 +213,9 @@ export default function Home(): JSX.Element {
}}
/>
)}
- {/* {showLabelsModal && (
- {
- setShowLabelsModal(false)
- }}
- setLabels={(labels: Label[]) => {
- // setLabels(labels)
- }}
- />
- )} */}
+ {showLabelsModal && (
+
+ )}
)
}
From 19f450594312da5049e094eee976cbe006d12315 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 17:31:35 -0700
Subject: [PATCH 030/125] Add modal for the edit labels dialog
---
.../templates/article/EditLabelsModal.tsx | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 packages/web/components/templates/article/EditLabelsModal.tsx
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
new file mode 100644
index 000000000..e4a4c0a22
--- /dev/null
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -0,0 +1,22 @@
+import {
+ ModalRoot,
+ ModalOverlay,
+ ModalContent,
+} from '../../elements/ModalPrimitives'
+import { EditLabelsControl } from './EditLabelsControl'
+
+export function EditLabelsModal(): JSX.Element {
+ return (
+
+
+ {
+ event.preventDefault()
+ }}
+ css={{ overflow: 'auto', p: '0' }}
+ >
+
+
+
+ )
+}
From e2aeff0c6f3f14cdc3a7b91902b11950331d042e Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 18:10:03 -0700
Subject: [PATCH 031/125] Use single label type
---
.../elements/LabelColorDropdown.tsx | 5 +----
.../templates/article/ArticleActionsMenu.tsx | 2 +-
.../templates/article/ArticleContainer.tsx | 8 ++++++-
.../templates/article/EditLabelsControl.tsx | 21 +++++++------------
.../templates/article/EditLabelsModal.tsx | 2 +-
.../lib/networking/fragments/labelFragment.ts | 15 ++++++++++---
.../networking/queries/useGetLabelsQuery.tsx | 11 +---------
.../web/pages/[username]/[slug]/index.tsx | 18 ++++++++--------
packages/web/pages/settings/labels.tsx | 4 +---
.../web/utils/settings-page/labels/types.ts | 12 +----------
10 files changed, 42 insertions(+), 56 deletions(-)
diff --git a/packages/web/components/elements/LabelColorDropdown.tsx b/packages/web/components/elements/LabelColorDropdown.tsx
index 6e105beed..5d3ff2979 100644
--- a/packages/web/components/elements/LabelColorDropdown.tsx
+++ b/packages/web/components/elements/LabelColorDropdown.tsx
@@ -2,21 +2,18 @@ import React, { useState } from 'react'
import { styled } from '../tokens/stitches.config'
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
import { HexColorPicker } from 'react-colorful'
-import { Button } from './Button'
import { HStack, SpanBox } from './LayoutPrimitives'
import { CaretDown } from 'phosphor-react'
import { StyledText } from './StyledText'
import {
- ColorDetailsProps,
- LabelColor,
LabelColorDropdownProps,
- LabelColorHex,
LabelColorObject,
LabelOptionProps,
} from '../../utils/settings-page/labels/types'
import { labelColorObjects } from '../../utils/settings-page/labels/labelColorObjects'
import { DropdownOption } from './DropdownElements'
import { isDarkTheme } from '../../lib/themeUpdater'
+import { LabelColor } from '../../lib/networking/fragments/labelFragment'
const DropdownMenuContent = styled(DropdownMenuPrimitive.Content, {
maxWidth: 190,
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index e28a41dca..13f1f221d 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -91,7 +91,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}
>
-
+
{/*
props.articleActionHandler('editLabels')} css={{
diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx
index de0866809..fe465d6a6 100644
--- a/packages/web/components/templates/article/ArticleContainer.tsx
+++ b/packages/web/components/templates/article/ArticleContainer.tsx
@@ -1,6 +1,6 @@
import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
import { Article } from './../../../components/templates/article/Article'
-import { Box, VStack } from './../../elements/LayoutPrimitives'
+import { Box, HStack, VStack } from './../../elements/LayoutPrimitives'
import { StyledText } from './../../elements/StyledText'
import { ArticleSubtitle } from './../../patterns/ArticleSubtitle'
import { theme, ThemeId } from './../../tokens/stitches.config'
@@ -13,6 +13,7 @@ import { ArticleHeaderToolbar } from './ArticleHeaderToolbar'
import { userPersonalizationMutation } from '../../../lib/networking/mutations/userPersonalizationMutation'
import { updateThemeLocally } from '../../../lib/themeUpdater'
import { ArticleMutations } from '../../../lib/articleActions'
+import { LabelChip } from '../../elements/LabelChip'
type ArticleContainerProps = {
article: ArticleAttributes
@@ -158,6 +159,11 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
author={props.article.author}
href={props.article.url}
/>
+
+ {props.article.labels?.map((label) =>
+
+ )}
+
void
// setLabels: (labels: Label[]) => void
}
@@ -176,7 +171,7 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
const [filterText, setFilterText] = useState('')
const { labels } = useGetLabelsQuery()
- const [selectedLabels, setSelectedLabels] = useState([])
+ const [selectedLabels, setSelectedLabels] = useState(props.article.labels || [])
const isSelected = useCallback((label: Label): boolean => {
return selectedLabels.some((other) => {
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index e4a4c0a22..015905b4e 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -15,7 +15,7 @@ export function EditLabelsModal(): JSX.Element {
}}
css={{ overflow: 'auto', p: '0' }}
>
-
+ {/* */}
)
diff --git a/packages/web/lib/networking/fragments/labelFragment.ts b/packages/web/lib/networking/fragments/labelFragment.ts
index 837019fa7..5cdc5506a 100644
--- a/packages/web/lib/networking/fragments/labelFragment.ts
+++ b/packages/web/lib/networking/fragments/labelFragment.ts
@@ -1,5 +1,14 @@
import { gql } from 'graphql-request'
+export type LabelColor =
+ | '#FF5D99'
+ | '#7CFF7B'
+ | '#FFD234'
+ | '#7BE4FF'
+ | '#CE88EF'
+ | '#EF8C43'
+ | 'custom color';
+
export const labelFragment = gql`
fragment LabelFields on Label {
id
@@ -13,7 +22,7 @@ export const labelFragment = gql`
export type Label = {
id: string
name: string
- color: string
+ color: LabelColor
description?: string
- createdAt: string
-}
+ createdAt: Date
+}
\ No newline at end of file
diff --git a/packages/web/lib/networking/queries/useGetLabelsQuery.tsx b/packages/web/lib/networking/queries/useGetLabelsQuery.tsx
index c625b592e..211c53178 100644
--- a/packages/web/lib/networking/queries/useGetLabelsQuery.tsx
+++ b/packages/web/lib/networking/queries/useGetLabelsQuery.tsx
@@ -1,7 +1,6 @@
import { gql } from 'graphql-request'
import useSWR from 'swr'
-import { LabelColor } from '../../../utils/settings-page/labels/types';
-import { labelFragment } from '../fragments/labelFragment'
+import { Label, labelFragment } from '../fragments/labelFragment'
import { publicGqlFetcher } from '../networkHelpers'
type LabelsQueryResponse = {
@@ -18,14 +17,6 @@ type LabelsData = {
labels?: unknown
}
-export type Label = {
- id: string
- name: string
- color: LabelColor
- description?: string
- createdAt: Date
-}
-
export function useGetLabelsQuery(): LabelsQueryResponse {
const query = gql`
query GetLabels {
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index cb7e7761e..fdf1e117d 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -176,15 +176,15 @@ export default function Home(): JSX.Element {
/>
) : (
Date: Sun, 10 Apr 2022 20:25:17 -0700
Subject: [PATCH 032/125] Refresh labels when edited
---
.../web/components/elements/LabelChip.tsx | 35 ++++++++-----
.../templates/article/ArticleActionsMenu.tsx | 10 ++--
.../templates/article/ArticleContainer.tsx | 20 +++----
.../templates/article/EditLabelsControl.tsx | 25 ++++++---
.../networking/mutations/setLabelsMutation.ts | 22 +++++---
.../networking/queries/useGetArticleQuery.tsx | 8 ++-
.../queries/useGetNewsletterEmailsQuery.tsx | 2 +-
.../web/pages/[username]/[slug]/index.tsx | 52 ++++++++-----------
8 files changed, 98 insertions(+), 76 deletions(-)
diff --git a/packages/web/components/elements/LabelChip.tsx b/packages/web/components/elements/LabelChip.tsx
index b368825e9..64a43af06 100644
--- a/packages/web/components/elements/LabelChip.tsx
+++ b/packages/web/components/elements/LabelChip.tsx
@@ -1,3 +1,5 @@
+import Link from 'next/link'
+import { SpanBox } from './LayoutPrimitives'
import { StyledText } from './StyledText'
type LabelChipProps = {
@@ -16,19 +18,24 @@ export function LabelChip(props: LabelChipProps): JSX.Element {
}
const color = hexToRgb(props.color)
return (
-
- {props.text}
-
+
+
+ {props.text}
+
+
)
}
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 13f1f221d..1a8762f84 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -1,8 +1,7 @@
import { Separator } from "@radix-ui/react-separator"
import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from "phosphor-react"
-import { useRef } from "react"
import { ArticleAttributes } from "../../../lib/networking/queries/useGetArticleQuery"
-import { useGetUserPreferences, UserPreferences } from "../../../lib/networking/queries/useGetUserPreferences"
+import { useGetUserPreferences } from "../../../lib/networking/queries/useGetUserPreferences"
import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
import { Box, SpanBox } from "../../elements/LayoutPrimitives"
@@ -15,7 +14,7 @@ export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
type ArticleActionsMenuProps = {
article: ArticleAttributes
layout: ArticleActionsMenuLayout
- articleActionHandler: (action: string, arg?: number) => void
+ articleActionHandler: (action: string, arg?: unknown) => void
}
type MenuSeparatorProps = {
@@ -91,7 +90,10 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}
>
-
+
{/*
props.articleActionHandler('editLabels')} css={{
diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx
index fe465d6a6..8153c7e9d 100644
--- a/packages/web/components/templates/article/ArticleContainer.tsx
+++ b/packages/web/components/templates/article/ArticleContainer.tsx
@@ -1,6 +1,6 @@
import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
import { Article } from './../../../components/templates/article/Article'
-import { Box, HStack, VStack } from './../../elements/LayoutPrimitives'
+import { Box, HStack, SpanBox, VStack } from './../../elements/LayoutPrimitives'
import { StyledText } from './../../elements/StyledText'
import { ArticleSubtitle } from './../../patterns/ArticleSubtitle'
import { theme, ThemeId } from './../../tokens/stitches.config'
@@ -14,9 +14,11 @@ import { userPersonalizationMutation } from '../../../lib/networking/mutations/u
import { updateThemeLocally } from '../../../lib/themeUpdater'
import { ArticleMutations } from '../../../lib/articleActions'
import { LabelChip } from '../../elements/LabelChip'
+import { Label } from '../../../lib/networking/fragments/labelFragment'
type ArticleContainerProps = {
article: ArticleAttributes
+ labels: Label[]
articleMutations: ArticleMutations
scrollElementRef: MutableRefObject
isAppleAppEmbed: boolean
@@ -29,14 +31,10 @@ type ArticleContainerProps = {
export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
const [showShareModal, setShowShareModal] = useState(false)
- const [showLabelsModal, setShowLabelsModal] = useState(false)
const [showNotesSidebar, setShowNotesSidebar] = useState(false)
const [showReportIssuesModal, setShowReportIssuesModal] = useState(false)
const [fontSize, setFontSize] = useState(props.fontSize ?? 20)
const [margin, setMargin] = useState(props.margin ?? 360)
- const [labels, setLabels] = useState(
- props.article.labels?.map((l) => l.id) || []
- )
const updateFontSize = async (newFontSize: number) => {
if (fontSize !== newFontSize) {
@@ -159,11 +157,13 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
author={props.article.author}
href={props.article.url}
/>
-
- {props.article.labels?.map((label) =>
-
- )}
-
+ {props.labels ? (
+
+ {props.labels?.map((label) =>
+
+ )}
+
+ ) : null}
void
- // setLabels: (labels: Label[]) => void
+ articleActionHandler: (action: string, arg?: unknown) => void
}
type HeaderProps = {
@@ -170,7 +169,6 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
const parentRef = useRef(null)
const [filterText, setFilterText] = useState('')
const { labels } = useGetLabelsQuery()
-
const [selectedLabels, setSelectedLabels] = useState(props.article.labels || [])
const isSelected = useCallback((label: Label): boolean => {
@@ -179,14 +177,25 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
})
}, [selectedLabels])
- const toggleLabel = useCallback((label: Label) => {
+ const toggleLabel = useCallback(async (label: Label) => {
+ let newSelectedLabels = [...selectedLabels]
if (isSelected(label)) {
- setSelectedLabels(selectedLabels.filter((other) => {
+ newSelectedLabels = selectedLabels.filter((other) => {
return other.id !== label.id
- }))
+ })
} else {
- setSelectedLabels([...selectedLabels, label])
+ newSelectedLabels = [...selectedLabels, label]
}
+ setSelectedLabels(newSelectedLabels)
+
+ const result = await setLabelsMutation(
+ props.article.linkId,
+ newSelectedLabels.map((label) => label.id)
+ )
+ props.article.labels = result
+ props.articleActionHandler('refreshLabels', result)
+
+ console.log('refreshing article with labels', props.article.labels)
}, [isSelected, selectedLabels])
const filteredLabels = useMemo(() => {
diff --git a/packages/web/lib/networking/mutations/setLabelsMutation.ts b/packages/web/lib/networking/mutations/setLabelsMutation.ts
index 46e57b8c7..a7d3aa0bb 100644
--- a/packages/web/lib/networking/mutations/setLabelsMutation.ts
+++ b/packages/web/lib/networking/mutations/setLabelsMutation.ts
@@ -1,16 +1,26 @@
import { gql } from 'graphql-request'
+import { Label, labelFragment } from '../fragments/labelFragment'
import { gqlFetcher } from '../networkHelpers'
+type SetLabelsResult = {
+ setLabels: SetLabels
+ errorCodes?: unknown[]
+}
+
+type SetLabels = {
+ labels: Label[]
+}
+
export async function setLabelsMutation(
- pageId: string,
+ linkId: string,
labelIds: string[]
-): Promise {
+): Promise {
const mutation = gql`
mutation SetLabels($input: SetLabelsInput!) {
setLabels(input: $input) {
... on SetLabelsSuccess {
labels {
- id
+ ...LabelFields
}
}
... on SetLabelsError {
@@ -18,12 +28,12 @@ export async function setLabelsMutation(
}
}
}
+ ${labelFragment}
`
try {
- const data = await gqlFetcher(mutation, { input: { pageId, labelIds } })
- console.log(data)
- return data
+ const data = await gqlFetcher(mutation, { input: { linkId, labelIds } }) as SetLabelsResult
+ return data.errorCodes ? undefined : data.setLabels.labels
} catch (error) {
console.log('SetLabelsOutput error', error)
return undefined
diff --git a/packages/web/lib/networking/queries/useGetArticleQuery.tsx b/packages/web/lib/networking/queries/useGetArticleQuery.tsx
index b7e7c66c1..fc8fdd623 100644
--- a/packages/web/lib/networking/queries/useGetArticleQuery.tsx
+++ b/packages/web/lib/networking/queries/useGetArticleQuery.tsx
@@ -1,9 +1,10 @@
import { gql } from 'graphql-request'
import useSWRImmutable from 'swr'
+import useSWR from 'swr'
import { makeGqlFetcher, RequestContext, ssrFetcher } from '../networkHelpers'
import { articleFragment, ContentReader } from '../fragments/articleFragment'
import { Highlight, highlightFragment } from '../fragments/highlightFragment'
-import { ScopedMutator } from 'swr/dist/types'
+import { KeyedMutator, ScopedMutator } from 'swr/dist/types'
import { Label, labelFragment } from '../fragments/labelFragment'
type ArticleQueryInput = {
@@ -16,6 +17,7 @@ type ArticleQueryOutput = {
articleData?: ArticleData
articleFetchError: unknown
isLoading: boolean
+ mutate: KeyedMutator
}
type ArticleData = {
@@ -99,7 +101,7 @@ export function useGetArticleQuery({
includeFriendsHighlights,
}
- const { data, error } = useSWRImmutable(
+ const { data, error, mutate } = useSWR(
slug ? [query, username, slug, includeFriendsHighlights] : null,
makeGqlFetcher(variables)
)
@@ -110,12 +112,14 @@ export function useGetArticleQuery({
// it will be nested in the data pages, if there is one error,
// we invalidate the data and return the error. We also zero out
// the response in the case of an error.
+ console.log('result data', resultData)
if (!error && resultData && resultData.article.errorCodes) {
resultError = resultData.article.errorCodes
resultData = undefined
}
return {
+ mutate,
articleData: resultData,
articleFetchError: resultError as unknown,
isLoading: !error && !data,
diff --git a/packages/web/lib/networking/queries/useGetNewsletterEmailsQuery.tsx b/packages/web/lib/networking/queries/useGetNewsletterEmailsQuery.tsx
index 3403b8eb3..ece0316be 100644
--- a/packages/web/lib/networking/queries/useGetNewsletterEmailsQuery.tsx
+++ b/packages/web/lib/networking/queries/useGetNewsletterEmailsQuery.tsx
@@ -51,7 +51,7 @@ export function useGetNewsletterEmailsQuery(): NewsletterEmailsQueryResponse {
isValidating,
emailAddresses,
revalidate: () => {
- mutate()
+ mutate(undefined, true)
}
}
}
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index fdf1e117d..b339ebc65 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -6,7 +6,7 @@ import { useRouter } from 'next/router'
import { VStack } from './../../../components/elements/LayoutPrimitives'
import { ArticleContainer } from './../../../components/templates/article/ArticleContainer'
import { PdfArticleContainerProps } from './../../../components/templates/article/PdfArticleContainer'
-import { useRef, useState } from 'react'
+import { useEffect, useMemo, useRef, useState } from 'react'
import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
import { articleKeyboardCommands, navigationCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
import dynamic from 'next/dynamic'
@@ -20,26 +20,12 @@ import { articleReadingProgressMutation } from '../../../lib/networking/mutation
import { updateHighlightMutation } from '../../../lib/networking/mutations/updateHighlightMutation'
import { userPersonalizationMutation } from '../../../lib/networking/mutations/userPersonalizationMutation'
import Script from 'next/script'
-import { EditLabelsControl } from '../../../components/templates/article/EditLabelsControl'
-import { Label } from '../../../lib/networking/fragments/labelFragment'
-import { isVipUser } from '../../../lib/featureFlag'
-import { styled, theme } from '../../../components/tokens/stitches.config'
-import { Button } from '../../../components/elements/Button'
-import { ArchiveBox, DotsThree, HighlighterCircle, TagSimple, TextAa } from 'phosphor-react'
-import { Separator } from '@radix-ui/react-separator'
-import { Article } from '../../../components/templates/article/Article'
+import { theme } from '../../../components/tokens/stitches.config'
import { ArticleActionsMenu } from '../../../components/templates/article/ArticleActionsMenu'
import { HighlightsModal } from '../../../components/templates/article/HighlightsModal'
import { setLinkArchivedMutation } from '../../../lib/networking/mutations/setLinkArchivedMutation'
-import { EditLabelsModal } from '../../../components/templates/article/EditLabelsModal'
+import { Label } from '../../../lib/networking/fragments/labelFragment'
-const MenuSeparator = styled(Separator, {
- width: '100%',
- margin: 0,
- backgroundColor: 'red',
- borderBottom: `1px solid ${theme.colors.grayLine.toString()}`,
- my: '8px',
-})
const PdfArticleContainerNoSSR = dynamic(
() => import('./../../../components/templates/article/PdfArticleContainer'),
@@ -50,20 +36,28 @@ export default function Home(): JSX.Element {
const router = useRouter()
const scrollRef = useRef(null)
const { slug } = router.query
- const [showLabelsModal, setShowLabelsModal] = useState(false)
const [showHighlightsModal, setShowHighlightsModal] = useState(false)
// Populate data cache
const { viewerData } = useGetViewerQuery()
- const { articleData } = useGetArticleQuery({
+
+ const { preferencesData } = useGetUserPreferences()
+ const [fontSize, setFontSize] = useState(preferencesData?.fontSize ?? 20)
+ const [marginWidth, setMarginWidth] = useState(preferencesData?.margin ?? 360)
+
+ const { articleData, mutate } = useGetArticleQuery({
username: router.query.username as string,
slug: router.query.slug as string,
includeFriendsHighlights: false,
})
- const { preferencesData } = useGetUserPreferences()
const article = articleData?.article.article
- const [fontSize, setFontSize] = useState(preferencesData?.fontSize ?? 20)
- const [marginWidth, setMarginWidth] = useState(preferencesData?.margin ?? 360)
+
+ const [labels, setLabels] = useState([])
+ useEffect(() => {
+ if (article?.labels) {
+ setLabels(article.labels)
+ }
+ }, [article])
useKeyboardShortcuts(navigationCommands(router))
@@ -73,11 +67,10 @@ export default function Home(): JSX.Element {
}
const updateMarginWidth = async (newMargin: number) => {
- console.log('margin', newMargin)
setMarginWidth(newMargin)
}
- const actionHandler = async (action: string, arg?: number) => {
+ const actionHandler = async (action: string, arg?: unknown) => {
switch (action) {
case 'archive':
if (article) {
@@ -96,6 +89,9 @@ export default function Home(): JSX.Element {
window.open(url, '_blank')
}
break
+ case 'refreshLabels':
+ setLabels(arg as Label[])
+ break
case 'showHighlights':
setShowHighlightsModal(true)
break
@@ -111,10 +107,6 @@ export default function Home(): JSX.Element {
case 'decrementMarginWidth':
updateMarginWidth(Math.max(marginWidth - 50, 200))
break
- case 'editLabels':
- setShowLabelsModal(true)
- console.log('showing labels modal')
- break
}
};
@@ -194,6 +186,7 @@ export default function Home(): JSX.Element {
highlightsBaseURL={`${webBaseURL}/${viewerData.me?.profile?.username}/${slug}/highlights`}
fontSize={fontSize}
margin={marginWidth}
+ labels={labels}
articleMutations={{
createHighlightMutation,
deleteHighlightMutation,
@@ -213,9 +206,6 @@ export default function Home(): JSX.Element {
}}
/>
)}
- {showLabelsModal && (
-
- )}
)
}
From 952c137507360725e28b4b035df791246df9b368 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 20:26:45 -0700
Subject: [PATCH 033/125] In /app dont display labels on the page
---
packages/web/pages/app/[username]/[slug]/index.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/web/pages/app/[username]/[slug]/index.tsx b/packages/web/pages/app/[username]/[slug]/index.tsx
index 82a9d225d..771292dd6 100644
--- a/packages/web/pages/app/[username]/[slug]/index.tsx
+++ b/packages/web/pages/app/[username]/[slug]/index.tsx
@@ -99,6 +99,7 @@ function AppArticleEmbedContent(
fontSize={props.fontSize}
margin={props.margin}
fontFamily={props.fontFamily}
+ labels={[]}
articleMutations={{
createHighlightMutation,
deleteHighlightMutation,
From d05f091876f1edd61a700640f05b7d5d93f6b8c6 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 20:53:09 -0700
Subject: [PATCH 034/125] Add debug logging for GQL errors
---
packages/api/src/apollo.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/api/src/apollo.ts b/packages/api/src/apollo.ts
index fa869a3c3..53c94ec24 100644
--- a/packages/api/src/apollo.ts
+++ b/packages/api/src/apollo.ts
@@ -115,6 +115,7 @@ export function makeApolloServer(): ApolloServer {
schema: schema,
context: contextFunc,
formatError: (err) => {
+ console.log('server error', err)
Sentry.captureException(err)
// hide error messages from frontend on prod
return new Error('Unexpected server error')
From 197ae866a893224ee0f1fbb3062f61319f2b54c6 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 20:53:40 -0700
Subject: [PATCH 035/125] Implement margin slider
---
.../templates/article/ReaderSettingsModal.tsx | 18 +++++++++++-------
packages/web/pages/[username]/[slug]/index.tsx | 11 +++++++++--
2 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
index fbab67335..58e59bcfb 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -25,12 +25,15 @@ type ReaderSettingsProps = {
articleActionHandler: (action: string, arg?: number) => void
}
+const VerticalDivider = styled(SpanBox, {
+ width: '1px',
+ height: '100%',
+ background: `${theme.colors.grayLine.toString()}`,
+})
+
export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
- const VerticalDivider = styled(SpanBox, {
- width: '1px',
- height: '100%',
- background: `${theme.colors.grayLine.toString()}`,
- })
+ const [marginWidth, setMarginWidth] = useState(props.userPreferences?.margin ?? 0)
+
return (
props.articleActionHandler('decrementMarginWidth')}>
- {
- console.log('range changed', value)
+ {
+ setMarginWidth(value)
+ props.articleActionHandler('setMarginWidth', value)
}} />
props.articleActionHandler('incrementMarginWidth')}>
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index b339ebc65..cb820d9d8 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -101,11 +101,18 @@ export default function Home(): JSX.Element {
case 'decrementFontSize':
await updateFontSize(Math.max(fontSize - 2, 10))
break
+ case 'setMarginWidth': {
+ const value = Number(arg)
+ if (value > 200 && value < 560) {
+ updateMarginWidth(value)
+ }
+ break
+ }
case 'incrementMarginWidth':
- updateMarginWidth(Math.min(marginWidth + 50, 560))
+ updateMarginWidth(Math.min(marginWidth + 45, 560))
break
case 'decrementMarginWidth':
- updateMarginWidth(Math.max(marginWidth - 50, 200))
+ updateMarginWidth(Math.max(marginWidth - 45, 200))
break
}
};
From a104486602655f1271255a390638f8a9d272af2d Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 21:17:16 -0700
Subject: [PATCH 036/125] Clip the label chips to their radius
---
packages/web/components/elements/LabelChip.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/web/components/elements/LabelChip.tsx b/packages/web/components/elements/LabelChip.tsx
index 64a43af06..86dd3c3ba 100644
--- a/packages/web/components/elements/LabelChip.tsx
+++ b/packages/web/components/elements/LabelChip.tsx
@@ -30,6 +30,7 @@ export function LabelChip(props: LabelChipProps): JSX.Element {
padding: '4px 8px 4px 8px',
whiteSpace: 'nowrap',
cursor: 'pointer',
+ backgroundClip: 'padding-box',
border: `1px solid rgba(${color[0]}, ${color[1]}, ${color[2]}, 0.7)`,
backgroundColor: `rgba(${color[0]}, ${color[1]}, ${color[2]}, 0.08)`,
}}
From 8b579064a45b877fb6d2e8dd11b13877e25e267c Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 21:43:53 -0700
Subject: [PATCH 037/125] Dont show margin sizer on small screens that dont
allow sizing margins
---
.../web/components/templates/article/ReaderSettingsModal.tsx | 3 +++
1 file changed, 3 insertions(+)
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
index 58e59bcfb..b2ca52caa 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -61,6 +61,9 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
pb: '14px',
width: '100%',
height: '100%',
+ '@smDown': {
+ display: 'none',
+ },
}}
>
Margin:
From 4ed475ecf564eb57926d141773216c94296a6094 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 22:02:25 -0700
Subject: [PATCH 038/125] Invert margin buttons
---
.../templates/article/ReaderSettingsModal.tsx | 25 +++++++++++++------
.../queries/useGetUserPreferences.tsx | 1 +
2 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
index b2ca52caa..cfd3c8a19 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -32,6 +32,7 @@ const VerticalDivider = styled(SpanBox, {
})
export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
+ const [lineHeight, setLineHeight] = useState(props.userPreferences?.lineHeight ?? 1.5)
const [marginWidth, setMarginWidth] = useState(props.userPreferences?.margin ?? 0)
return (
@@ -61,23 +62,32 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
pb: '14px',
width: '100%',
height: '100%',
- '@smDown': {
+ '@mdDown': {
display: 'none',
},
}}
>
Margin:
- props.articleActionHandler('decrementMarginWidth')}>
-
+ {
+ const newMarginWith = Math.max(marginWidth - 45, 200)
+ setMarginWidth(newMarginWith)
+ props.articleActionHandler('setMarginWidth', newMarginWith)
+ }}>
+
{
setMarginWidth(value)
props.articleActionHandler('setMarginWidth', value)
}} />
- props.articleActionHandler('incrementMarginWidth')}>
-
+ {
+ const newMarginWith = Math.min(marginWidth + 45, 560)
+ setMarginWidth(newMarginWith)
+ props.articleActionHandler('setMarginWidth', newMarginWith)
+ }}>
+
+
- {
- console.log('changed line spacing')
+ {
+ setLineHeight(value)
+ props.articleActionHandler('setLineHeight', value)
}} />
diff --git a/packages/web/lib/networking/queries/useGetUserPreferences.tsx b/packages/web/lib/networking/queries/useGetUserPreferences.tsx
index baa1df1fd..9b74a795b 100644
--- a/packages/web/lib/networking/queries/useGetUserPreferences.tsx
+++ b/packages/web/lib/networking/queries/useGetUserPreferences.tsx
@@ -25,6 +25,7 @@ export type UserPreferences = {
fontSize: number
fontFamily: string
margin: number
+ lineHeight?: number
libraryLayoutType: string
librarySortOrder?: SortOrder
}
From 23416cd1e7accdda57a156d34cfad7d488e02636 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Sun, 10 Apr 2022 22:22:25 -0700
Subject: [PATCH 039/125] Allow adjusting line height
---
.../templates/article/ArticleContainer.tsx | 20 +++++--------------
.../templates/article/ReaderSettingsModal.tsx | 4 ++--
.../web/pages/[username]/[slug]/index.tsx | 15 ++++++++++----
3 files changed, 18 insertions(+), 21 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx
index 8153c7e9d..cb4a31003 100644
--- a/packages/web/components/templates/article/ArticleContainer.tsx
+++ b/packages/web/components/templates/article/ArticleContainer.tsx
@@ -1,6 +1,6 @@
import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
import { Article } from './../../../components/templates/article/Article'
-import { Box, HStack, SpanBox, VStack } from './../../elements/LayoutPrimitives'
+import { Box, SpanBox, VStack } from './../../elements/LayoutPrimitives'
import { StyledText } from './../../elements/StyledText'
import { ArticleSubtitle } from './../../patterns/ArticleSubtitle'
import { theme, ThemeId } from './../../tokens/stitches.config'
@@ -27,6 +27,7 @@ type ArticleContainerProps = {
margin?: number
fontSize?: number
fontFamily?: string
+ lineHeight?: number
}
export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
@@ -34,7 +35,6 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
const [showNotesSidebar, setShowNotesSidebar] = useState(false)
const [showReportIssuesModal, setShowReportIssuesModal] = useState(false)
const [fontSize, setFontSize] = useState(props.fontSize ?? 20)
- const [margin, setMargin] = useState(props.margin ?? 360)
const updateFontSize = async (newFontSize: number) => {
if (fontSize !== newFontSize) {
@@ -43,21 +43,10 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
}
}
- const updateMargin = async (newMargin: number) => {
- if (margin !== newMargin) {
- setMargin(newMargin)
- await userPersonalizationMutation({ margin: newMargin })
- }
- }
-
useEffect(() => {
updateFontSize(props.fontSize ?? 20)
}, [props.fontSize])
- useEffect(() => {
- updateMargin(props.margin ?? 140)
- }, [props.margin])
-
// Listen for font size and color mode change events sent from host apps (ios, macos...)
useEffect(() => {
const increaseFontSize = async () => {
@@ -98,8 +87,9 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
}, [props.article])
const styles = {
- margin: props.margin ?? 360,
fontSize,
+ margin: props.margin ?? 360,
+ lineHeight: props.lineHeight ?? 150,
fontFamily: props.fontFamily ?? 'inter',
readerFontColor: theme.colors.readerFont.toString(),
readerFontColorTransparent: theme.colors.readerFontTransparent.toString(),
@@ -117,7 +107,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
background: props.isAppleAppEmbed ? 'unset' : theme.colors.grayBg.toString(),
'--text-font-family': styles.fontFamily,
'--text-font-size': `${styles.fontSize}px`,
- '--line-height': `150%`,
+ '--line-height': `${styles.lineHeight}%`,
'--blockquote-padding': '0.5em 1em',
'--blockquote-icon-font-size': '1.3rem',
'--figure-margin': '1.6rem auto',
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
index cfd3c8a19..e11ff893c 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -32,8 +32,8 @@ const VerticalDivider = styled(SpanBox, {
})
export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
- const [lineHeight, setLineHeight] = useState(props.userPreferences?.lineHeight ?? 1.5)
const [marginWidth, setMarginWidth] = useState(props.userPreferences?.margin ?? 0)
+ const [lineHeight, setLineHeight] = useState(props.userPreferences?.lineHeight ?? 150)
return (
@@ -102,7 +102,7 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
- {
+ {
setLineHeight(value)
props.articleActionHandler('setLineHeight', value)
}} />
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index cb820d9d8..4bd1c9559 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -38,14 +38,13 @@ export default function Home(): JSX.Element {
const { slug } = router.query
const [showHighlightsModal, setShowHighlightsModal] = useState(false)
- // Populate data cache
const { viewerData } = useGetViewerQuery()
-
const { preferencesData } = useGetUserPreferences()
const [fontSize, setFontSize] = useState(preferencesData?.fontSize ?? 20)
const [marginWidth, setMarginWidth] = useState(preferencesData?.margin ?? 360)
+ const [lineHeight, setLineHeight] = useState(preferencesData?.lineHeight ?? 150)
- const { articleData, mutate } = useGetArticleQuery({
+ const { articleData } = useGetArticleQuery({
username: router.query.username as string,
slug: router.query.slug as string,
includeFriendsHighlights: false,
@@ -103,7 +102,7 @@ export default function Home(): JSX.Element {
break
case 'setMarginWidth': {
const value = Number(arg)
- if (value > 200 && value < 560) {
+ if (value >= 200 && value <= 560) {
updateMarginWidth(value)
}
break
@@ -114,6 +113,13 @@ export default function Home(): JSX.Element {
case 'decrementMarginWidth':
updateMarginWidth(Math.max(marginWidth - 45, 200))
break
+ case 'setLineHeight': {
+ const value = Number(arg)
+ if (value >= 100 && value <= 300) {
+ setLineHeight(value)
+ }
+ break
+ }
}
};
@@ -193,6 +199,7 @@ export default function Home(): JSX.Element {
highlightsBaseURL={`${webBaseURL}/${viewerData.me?.profile?.username}/${slug}/highlights`}
fontSize={fontSize}
margin={marginWidth}
+ lineHeight={lineHeight}
labels={labels}
articleMutations={{
createHighlightMutation,
From 9a627c225e229273e42ae223f1ba89e8e3a3a74e Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 08:58:48 -0700
Subject: [PATCH 040/125] Add tooltips to article actions
---
packages/web/components/elements/Tooltip.tsx | 18 +++++++-
.../templates/article/ArticleActionsMenu.tsx | 43 +++++++++++++------
2 files changed, 45 insertions(+), 16 deletions(-)
diff --git a/packages/web/components/elements/Tooltip.tsx b/packages/web/components/elements/Tooltip.tsx
index 241c20e8e..b1139061a 100644
--- a/packages/web/components/elements/Tooltip.tsx
+++ b/packages/web/components/elements/Tooltip.tsx
@@ -62,6 +62,15 @@ type TooltipWrappedProps = {
style?: TooltipPrimitive.TooltipContentProps['style']
}
+const DefaultTooltipStyle = {
+ backgroundColor: '#F9D354',
+ color: '#0A0806',
+}
+
+const DefaultArrowStyle = {
+ fill: '#F9D354'
+}
+
export const TooltipWrapped: FC = ({
children,
active,
@@ -73,9 +82,14 @@ export const TooltipWrapped: FC = ({
return (
{children}
-
+
{tooltipContent}
-
+
)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 1a8762f84..abcec99a3 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -4,7 +4,8 @@ import { ArticleAttributes } from "../../../lib/networking/queries/useGetArticle
import { useGetUserPreferences } from "../../../lib/networking/queries/useGetUserPreferences"
import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
-import { Box, SpanBox } from "../../elements/LayoutPrimitives"
+import { Box } from "../../elements/LayoutPrimitives"
+import { TooltipWrapped } from "../../elements/Tooltip"
import { styled, theme } from "../../tokens/stitches.config"
import { EditLabelsControl } from "./EditLabelsControl"
import { ReaderSettings } from "./ReaderSettingsModal"
@@ -59,22 +60,23 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
-
-
+
+
+
}
>
@@ -85,9 +87,12 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
-
-
+
+
+
}
>
*/}
- props.articleActionHandler('showHighlights')}>
-
-
+
+
+
+
+
props.articleActionHandler('archive')}>
-
+
+
+
{/*
From ebe22f3df8959726e6249ad9f8a3916fb8c70215 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 09:10:38 -0700
Subject: [PATCH 041/125] Wrap the button so style isnt changed
---
.../templates/article/ArticleActionsMenu.tsx | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index abcec99a3..f8fbb8c22 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -111,14 +111,15 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
*/}
-
-
+ props.articleActionHandler('showHighlights')}>
+
-
-
+
+
+
props.articleActionHandler('archive')}>
From db2251b38a342cb794f12e9e2f9dd05e8dec350e Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 09:12:53 -0700
Subject: [PATCH 042/125] Remove debug
---
packages/web/lib/networking/queries/useGetArticleQuery.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/web/lib/networking/queries/useGetArticleQuery.tsx b/packages/web/lib/networking/queries/useGetArticleQuery.tsx
index fc8fdd623..a75f5f72a 100644
--- a/packages/web/lib/networking/queries/useGetArticleQuery.tsx
+++ b/packages/web/lib/networking/queries/useGetArticleQuery.tsx
@@ -112,7 +112,6 @@ export function useGetArticleQuery({
// it will be nested in the data pages, if there is one error,
// we invalidate the data and return the error. We also zero out
// the response in the case of an error.
- console.log('result data', resultData)
if (!error && resultData && resultData.article.errorCodes) {
resultError = resultData.article.errorCodes
resultData = undefined
From 2ab7e4ccdd9848c8954972b175d607c2cc9bc30d Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 10:03:24 -0700
Subject: [PATCH 043/125] Make the action dropdowns modal
This dismisses the tooltip when the modal is active
---
packages/web/components/elements/DropdownElements.tsx | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/packages/web/components/elements/DropdownElements.tsx b/packages/web/components/elements/DropdownElements.tsx
index 61dfeaac2..9ed0299c5 100644
--- a/packages/web/components/elements/DropdownElements.tsx
+++ b/packages/web/components/elements/DropdownElements.tsx
@@ -79,6 +79,7 @@ type DropdownProps = {
sideOffset?: number
disabled?: boolean
css?: CSS
+ modal?: boolean
}
export const DropdownSeparator = styled(Separator, {
@@ -116,10 +117,11 @@ export function Dropdown(props: DropdownProps & PopperContentProps): JSX.Element
side = 'bottom',
sideOffset = 0,
alignOffset = 0,
- css
+ css,
+ modal
} = props
return (
-
+
{triggerElement}
Date: Mon, 11 Apr 2022 10:32:41 -0700
Subject: [PATCH 044/125] Increment and decrement buttons on line height, add a
reset button
---
.../templates/article/ReaderSettingsModal.tsx | 25 ++++++++++++++++---
.../web/pages/[username]/[slug]/index.tsx | 6 +++++
2 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
index e11ff893c..0923b7e09 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -15,7 +15,7 @@ import { HighlightView } from '../../patterns/HighlightView'
import { useCallback, useState } from 'react'
import { StyledTextArea } from '../../elements/StyledTextArea'
import { ConfirmationModal } from '../../patterns/ConfirmationModal'
-import { AlignCenterHorizontalSimple, ArrowsInLineHorizontal, ArrowsOutLineHorizontal, Minus, Pen, Plus, Trash } from 'phosphor-react'
+import { AlignCenterHorizontalSimple, ArrowsInLineHorizontal, ArrowsOutLineHorizontal, Minus, Pen, Plus, Trash, X } from 'phosphor-react'
import { AIcon } from '../../elements/images/AIcon'
import { TickedRangeSlider } from '../../elements/TickedRangeSlider'
import { UserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
@@ -99,17 +99,36 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
}}>
Line Height:
-
+ {
+ const newLineHeight = Math.max(lineHeight - 25, 100)
+ setLineHeight(newLineHeight)
+ props.articleActionHandler('setLineHeight', newLineHeight)
+ }}>
{
setLineHeight(value)
props.articleActionHandler('setLineHeight', value)
}} />
-
+ {
+ const newLineHeight = Math.min(lineHeight + 25, 300)
+ setLineHeight(newLineHeight)
+ props.articleActionHandler('setLineHeight', newLineHeight)
+ }}>
+
+ {
+ setMarginWidth(360)
+ setLineHeight(150)
+ props.articleActionHandler('resetReaderSettings')
+ }}
+ >
+
+ Reset to default
+
)
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index 4bd1c9559..7eb2c194e 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -120,6 +120,12 @@ export default function Home(): JSX.Element {
}
break
}
+ case 'resetReaderSettings': {
+ updateFontSize(20)
+ updateMarginWidth(360)
+ setLineHeight(150)
+ break
+ }
}
};
From c280b2cf01bf83277e3a1acd4a2bbcd9761d68a1 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 11:13:58 -0700
Subject: [PATCH 045/125] Remove items from cache when archived from the
article actions menu
---
.../networking/queries/useGetArticleQuery.tsx | 64 ++++++++++++++-----
.../queries/useGetLibraryItemsQuery.tsx | 2 +-
.../web/pages/[username]/[slug]/index.tsx | 17 ++++-
3 files changed, 63 insertions(+), 20 deletions(-)
diff --git a/packages/web/lib/networking/queries/useGetArticleQuery.tsx b/packages/web/lib/networking/queries/useGetArticleQuery.tsx
index a75f5f72a..0d7422db2 100644
--- a/packages/web/lib/networking/queries/useGetArticleQuery.tsx
+++ b/packages/web/lib/networking/queries/useGetArticleQuery.tsx
@@ -1,11 +1,11 @@
import { gql } from 'graphql-request'
-import useSWRImmutable from 'swr'
-import useSWR from 'swr'
+import useSWRImmutable, { Cache } from 'swr'
import { makeGqlFetcher, RequestContext, ssrFetcher } from '../networkHelpers'
import { articleFragment, ContentReader } from '../fragments/articleFragment'
import { Highlight, highlightFragment } from '../fragments/highlightFragment'
-import { KeyedMutator, ScopedMutator } from 'swr/dist/types'
+import { ScopedMutator } from 'swr/dist/types'
import { Label, labelFragment } from '../fragments/labelFragment'
+import { LibraryItems } from './useGetLibraryItemsQuery'
type ArticleQueryInput = {
username?: string
@@ -17,7 +17,6 @@ type ArticleQueryOutput = {
articleData?: ArticleData
articleFetchError: unknown
isLoading: boolean
- mutate: KeyedMutator
}
type ArticleData = {
@@ -79,16 +78,7 @@ const query = gql`
${highlightFragment}
${labelFragment}
`
-export const cacheArticle = (
- mutate: ScopedMutator,
- username: string,
- article: ArticleAttributes,
- includeFriendsHighlights = false
-) => {
- mutate([query, username, article.slug, includeFriendsHighlights], {
- article: { article: { ...article, cached: true } },
- })
-}
+
export function useGetArticleQuery({
username,
@@ -101,7 +91,7 @@ export function useGetArticleQuery({
includeFriendsHighlights,
}
- const { data, error, mutate } = useSWR(
+ const { data, error, mutate } = useSWRImmutable(
slug ? [query, username, slug, includeFriendsHighlights] : null,
makeGqlFetcher(variables)
)
@@ -118,7 +108,6 @@ export function useGetArticleQuery({
}
return {
- mutate,
articleData: resultData,
articleFetchError: resultError as unknown,
isLoading: !error && !data,
@@ -136,3 +125,46 @@ export async function articleQuery(
return Promise.reject()
}
+
+export const cacheArticle = (
+ mutate: ScopedMutator,
+ username: string,
+ article: ArticleAttributes,
+ includeFriendsHighlights = false
+) => {
+ mutate([query, username, article.slug, includeFriendsHighlights], {
+ article: { article: { ...article, cached: true } },
+ })
+}
+
+
+export const removeItemFromCache = (
+ cache: Cache,
+ mutate: ScopedMutator,
+ itemId: string,
+) => {
+ try {
+ const mappedCache = cache as Map
+ mappedCache.forEach((value: any, key) => {
+ if (typeof value == 'object' && 'articles' in value) {
+ const articles = value.articles as LibraryItems
+ const idx = articles.edges.findIndex((edge) => edge.node.id == itemId)
+ if (idx > -1) {
+ value.articles.edges.splice(idx, 1)
+ mutate(key, value, false)
+ }
+ }
+ })
+
+ mappedCache.forEach((value: any, key) => {
+ if (Array.isArray(value)) {
+ const idx = value.findIndex((item) => 'articles' in item)
+ if (idx > -1) {
+ mutate(key, value, false)
+ }
+ }
+ })
+ } catch (error) {
+ console.log('error removing item from cache', error)
+ }
+}
diff --git a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
index 3a761076e..ad02f5e96 100644
--- a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
+++ b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
@@ -40,7 +40,7 @@ export type LibraryItemsData = {
articles: LibraryItems
}
-type LibraryItems = {
+export type LibraryItems = {
edges: LibraryItem[]
pageInfo: PageInfo
errorCodes?: string[]
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index 7eb2c194e..dbfbb69d6 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -1,7 +1,7 @@
import { PrimaryLayout } from '../../../components/templates/PrimaryLayout'
import { LoadingView } from '../../../components/patterns/LoadingView'
import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery'
-import { useGetArticleQuery } from '../../../lib/networking/queries/useGetArticleQuery'
+import { removeItemFromCache, useGetArticleQuery } from '../../../lib/networking/queries/useGetArticleQuery'
import { useRouter } from 'next/router'
import { VStack } from './../../../components/elements/LayoutPrimitives'
import { ArticleContainer } from './../../../components/templates/article/ArticleContainer'
@@ -25,6 +25,8 @@ import { ArticleActionsMenu } from '../../../components/templates/article/Articl
import { HighlightsModal } from '../../../components/templates/article/HighlightsModal'
import { setLinkArchivedMutation } from '../../../lib/networking/mutations/setLinkArchivedMutation'
import { Label } from '../../../lib/networking/fragments/labelFragment'
+import { useSWRConfig } from 'swr'
+import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
const PdfArticleContainerNoSSR = dynamic(
@@ -34,6 +36,7 @@ const PdfArticleContainerNoSSR = dynamic(
export default function Home(): JSX.Element {
const router = useRouter()
+ const { cache, mutate } = useSWRConfig()
const scrollRef = useRef(null)
const { slug } = router.query
const [showHighlightsModal, setShowHighlightsModal] = useState(false)
@@ -73,12 +76,20 @@ export default function Home(): JSX.Element {
switch (action) {
case 'archive':
if (article) {
+ removeItemFromCache(cache, mutate, article.id)
+
await setLinkArchivedMutation({
linkId: article.id,
archived: true,
+ }).then((res) => {
+ if (res) {
+ showSuccessToast('Link archived', { position: 'bottom-right' })
+ } else {
+ // todo:
+ showErrorToast('Error archiving link', { position: 'bottom-right' })
+ }
})
- // TODO: merge from article actions PR
- // removeItemFromCache(cache, mutate, props.article.id)
+
router.push(`/home`)
}
break
From 34d892a9a26748702461b44ed505ce2fa223012a Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 11:16:04 -0700
Subject: [PATCH 046/125] Update comment
---
packages/web/pages/[username]/[slug]/index.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index dbfbb69d6..b4d84cfda 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -85,7 +85,7 @@ export default function Home(): JSX.Element {
if (res) {
showSuccessToast('Link archived', { position: 'bottom-right' })
} else {
- // todo:
+ // todo: revalidate or put back in cache?
showErrorToast('Error archiving link', { position: 'bottom-right' })
}
})
From 110a503927eb80bec2f84e7c85daa6df3898cd8c Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 11:31:28 -0700
Subject: [PATCH 047/125] Implement the Create new Label button
---
.../templates/article/EditLabelsControl.tsx | 21 ++++++++++++-------
.../mutations/createLabelMutation.ts | 14 +++++++++++--
.../labels/labelColorObjects.tsx | 6 ++++++
3 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index a4910b2ca..ed70506a6 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -12,6 +12,9 @@ import { Check, Circle, PencilSimple, Plus } from 'phosphor-react'
import { isTouchScreenDevice } from '../../../lib/deviceType'
import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
+import { createLabelMutation } from '../../../lib/networking/mutations/createLabelMutation'
+import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
+import { randomLabelColorHex } from '../../../utils/settings-page/labels/labelColorObjects'
type EditLabelsControlProps = {
article: ArticleAttributes
@@ -194,8 +197,6 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
)
props.article.labels = result
props.articleActionHandler('refreshLabels', result)
-
- console.log('refreshing article with labels', props.article.labels)
}, [isSelected, selectedLabels])
const filteredLabels = useMemo(() => {
@@ -276,17 +277,23 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
{
- // props.createLabel(filterText)
- // setFilterText('')
+ onClick={async () => {
+ const label = await createLabelMutation(filterText, randomLabelColorHex(), '')
+ if (label) {
+ showSuccessToast(`Created label ${label.name}`, { position: 'bottom-right' })
+ toggleLabel(label)
+ } else {
+ showErrorToast('Failed to create label', { position: 'bottom-right' })
+ }
}}
>
{`Create new label "${filterText}"`}
- )}
- {/* Footer */}
+
+ )}
+
{
+ const colorHexes = Object.keys(labelColorObjects).slice(0, -1)
+ const randomColorHex = colorHexes[Math.floor(Math.random() * colorHexes.length)]
+ return randomColorHex
+}
\ No newline at end of file
From 96ba44df6e45a11fe90710c58b14bcd1f39cedf8 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 11:53:43 -0700
Subject: [PATCH 048/125] Remove unused ref prop
---
.../web/components/templates/article/EditLabelsControl.tsx | 4 ----
1 file changed, 4 deletions(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index ed70506a6..1771b1fb8 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -24,7 +24,6 @@ type EditLabelsControlProps = {
type HeaderProps = {
filterText: string
focused: boolean
- parentRef: React.RefObject
setFilterText: (text: string) => void
}
@@ -169,7 +168,6 @@ function LabelListItem(props: LabelListItemProps): JSX.Element {
}
export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
- const parentRef = useRef(null)
const [filterText, setFilterText] = useState('')
const { labels } = useGetLabelsQuery()
const [selectedLabels, setSelectedLabels] = useState(props.article.labels || [])
@@ -244,7 +242,6 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
return (
From 4888c75b9afca8b528afc02f81e162ca9efa7d75 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 11:59:33 -0700
Subject: [PATCH 049/125] Reset focused index when search bar gets focus
This is needed if someone combines keyboard navigation and
using the mouse to select the textbox
---
.../templates/article/EditLabelsControl.tsx | 33 +++++--------------
1 file changed, 9 insertions(+), 24 deletions(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index 1771b1fb8..5b034a14f 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -24,6 +24,7 @@ type EditLabelsControlProps = {
type HeaderProps = {
filterText: string
focused: boolean
+ resetFocusedIndex: () => void
setFilterText: (text: string) => void
}
@@ -47,28 +48,6 @@ const StyledLabel = styled('label', {
},
})
-const useToggleLabels = () => {
- const [selectedLabels, setSelectedLabels] = useState([])
-
- const isSelected = useCallback((label: Label): boolean => {
- return selectedLabels.some((other) => {
- return other.id === label.id
- })
- }, [selectedLabels])
-
- const toggleLabel = useCallback((label: Label) => {
- if (isSelected(label)) {
- setSelectedLabels(selectedLabels.filter((other) => {
- return other.id !== label.id
- }))
- } else {
- setSelectedLabels([...selectedLabels, label])
- }
- }, [isSelected, selectedLabels])
-
- return [isSelected, toggleLabel]
-}
-
function Header(props: HeaderProps): JSX.Element {
const inputRef = useRef(null)
@@ -95,6 +74,9 @@ function Header(props: HeaderProps): JSX.Element {
onChange={(event) => {
props.setFilterText(event.target.value)
}}
+ onFocus={() => {
+ props.resetFocusedIndex()
+ }}
css={{
border: '1px solid $grayBorder',
borderRadius: '8px',
@@ -213,6 +195,7 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
// Move focus through the labels list on tab or arrow up/down keys
const [focusedIndex, setFocusedIndex] = useState(undefined)
const handleKeyDown = useCallback((event: React.KeyboardEvent) => {
+ const maxIndex = filteredLabels.length + 2
if (event.key === 'ArrowUp') {
event.preventDefault()
if (focusedIndex) {
@@ -226,8 +209,9 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
if (focusedIndex === undefined) {
setFocusedIndex(0)
} else {
- setFocusedIndex(Math.min(filteredLabels.length - 1, focusedIndex + 1))
+ setFocusedIndex(Math.min(maxIndex, focusedIndex + 1))
}
+ console.log('focusedIndex', focusedIndex)
}
if (event.key === 'Enter') {
event.preventDefault()
@@ -255,6 +239,7 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
}}>
setFocusedIndex(undefined)}
setFilterText={setFilterText} filterText={filterText}
/>
@@ -290,7 +275,7 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
)}
-
Date: Mon, 11 Apr 2022 13:05:26 -0700
Subject: [PATCH 050/125] Allow selection of the bottom edit labels footer
---
.../templates/article/EditLabelsControl.tsx | 51 ++++++++++++-------
1 file changed, 34 insertions(+), 17 deletions(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index 5b034a14f..d4a09e38d 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -43,9 +43,6 @@ const FormInput = styled('input', {
const StyledLabel = styled('label', {
display: 'flex',
justifyContent: 'flex-start',
- '&:focus': {
- bg: '$grayBgActive',
- },
})
function Header(props: HeaderProps): JSX.Element {
@@ -149,6 +146,39 @@ function LabelListItem(props: LabelListItemProps): JSX.Element {
)
}
+type EditLabelsButtonFooterProps = {
+ focused: boolean
+}
+
+function EditLabelsButtonFooter(props: EditLabelsButtonFooterProps): JSX.Element {
+ const ref = useRef(null)
+
+ useEffect(() => {
+ if (props.focused && ref.current) {
+ ref.current.focus()
+ }
+ }, [props.focused])
+
+ return (
+
+
+ Edit labels
+
+ )
+}
+
export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
const [filterText, setFilterText] = useState('')
const { labels } = useGetLabelsQuery()
@@ -211,7 +241,6 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
} else {
setFocusedIndex(Math.min(maxIndex, focusedIndex + 1))
}
- console.log('focusedIndex', focusedIndex)
}
if (event.key === 'Enter') {
event.preventDefault()
@@ -275,19 +304,7 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
)}
-
-
- Edit labels
-
+
)
}
\ No newline at end of file
From 454f379e700670b64620557d3543d3a34ee110fe Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 13:27:43 -0700
Subject: [PATCH 051/125] Navigate via keyboard to edit labels page
---
.../templates/article/EditLabelsControl.tsx | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index d4a09e38d..d0bfd9f31 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -15,6 +15,7 @@ import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMu
import { createLabelMutation } from '../../../lib/networking/mutations/createLabelMutation'
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
import { randomLabelColorHex } from '../../../utils/settings-page/labels/labelColorObjects'
+import Router, { useRouter } from 'next/router'
type EditLabelsControlProps = {
article: ArticleAttributes
@@ -180,6 +181,7 @@ function EditLabelsButtonFooter(props: EditLabelsButtonFooterProps): JSX.Element
}
export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
+ const router = useRouter()
const [filterText, setFilterText] = useState('')
const { labels } = useGetLabelsQuery()
const [selectedLabels, setSelectedLabels] = useState(props.article.labels || [])
@@ -207,7 +209,7 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
)
props.article.labels = result
props.articleActionHandler('refreshLabels', result)
- }, [isSelected, selectedLabels])
+ }, [isSelected, selectedLabels, setSelectedLabels])
const filteredLabels = useMemo(() => {
if (!labels) {
@@ -244,6 +246,10 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
}
if (event.key === 'Enter') {
event.preventDefault()
+ if (focusedIndex === maxIndex) {
+ router.push('/settings/labels')
+ return
+ }
if (focusedIndex !== undefined) {
const label = filteredLabels[focusedIndex]
if (label) {
@@ -251,7 +257,7 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
}
}
}
- }, [filteredLabels, focusedIndex])
+ }, [filteredLabels, focusedIndex, isSelected, selectedLabels, setSelectedLabels])
return (
{filterText && (
{
const label = await createLabelMutation(filterText, randomLabelColorHex(), '')
From cf300a615cd7514fbead9dbdcae6c80fa9ea8023 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 13:47:06 -0700
Subject: [PATCH 052/125] Keyboard navigation for the bottom buttons
---
.../templates/article/EditLabelsControl.tsx | 26 ++++++++++++++-----
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index d0bfd9f31..edb5ea129 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -227,22 +227,36 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
// Move focus through the labels list on tab or arrow up/down keys
const [focusedIndex, setFocusedIndex] = useState(undefined)
const handleKeyDown = useCallback((event: React.KeyboardEvent) => {
- const maxIndex = filteredLabels.length + 2
+ const maxIndex = filteredLabels.length + 1
if (event.key === 'ArrowUp') {
event.preventDefault()
+ let newIndex = focusedIndex
if (focusedIndex) {
- setFocusedIndex(Math.max(0, focusedIndex - 1))
+ newIndex = Math.max(0, focusedIndex - 1)
} else {
- setFocusedIndex(undefined)
+ newIndex = undefined
}
+ // If the `Create New label` button isn't visible we skip it
+ // when navigating with the arrow keys
+ if (focusedIndex === maxIndex && !filterText) {
+ newIndex = maxIndex - 2
+ }
+ setFocusedIndex(newIndex)
}
if (event.key === 'ArrowDown' || event.key === 'Tab') {
event.preventDefault()
+ let newIndex = focusedIndex
if (focusedIndex === undefined) {
- setFocusedIndex(0)
+ newIndex = 0
} else {
- setFocusedIndex(Math.min(maxIndex, focusedIndex + 1))
+ newIndex = Math.min(maxIndex, focusedIndex + 1)
}
+ // If the `Create New label` button isn't visible we skip it
+ // when navigating with the arrow keys
+ if (focusedIndex === maxIndex - 2 && !filterText) {
+ newIndex = maxIndex
+ }
+ setFocusedIndex(newIndex)
}
if (event.key === 'Enter') {
event.preventDefault()
@@ -310,7 +324,7 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
{`Create new label "${filterText}"`}
-
+
)}
From 113101fd6380ffb064eae991b9b0ef33cfb37193 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 13:58:38 -0700
Subject: [PATCH 053/125] Handle creating labels via keyboard
---
.../templates/article/EditLabelsControl.tsx | 41 +++++++++++--------
1 file changed, 25 insertions(+), 16 deletions(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index edb5ea129..f0b403c7e 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -183,9 +183,13 @@ function EditLabelsButtonFooter(props: EditLabelsButtonFooterProps): JSX.Element
export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
const router = useRouter()
const [filterText, setFilterText] = useState('')
- const { labels } = useGetLabelsQuery()
+ const { labels, revalidate } = useGetLabelsQuery()
const [selectedLabels, setSelectedLabels] = useState(props.article.labels || [])
+ useEffect(() => {
+ setFocusedIndex(undefined)
+ }, [filterText])
+
const isSelected = useCallback((label: Label): boolean => {
return selectedLabels.some((other) => {
return other.id === label.id
@@ -207,8 +211,11 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
props.article.linkId,
newSelectedLabels.map((label) => label.id)
)
+
props.article.labels = result
props.articleActionHandler('refreshLabels', result)
+
+ revalidate()
}, [isSelected, selectedLabels, setSelectedLabels])
const filteredLabels = useMemo(() => {
@@ -220,13 +227,9 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
})
}, [labels, filterText])
- useEffect(() => {
- setFocusedIndex(undefined)
- }, [filterText])
-
// Move focus through the labels list on tab or arrow up/down keys
const [focusedIndex, setFocusedIndex] = useState(undefined)
- const handleKeyDown = useCallback((event: React.KeyboardEvent) => {
+ const handleKeyDown = useCallback(async (event: React.KeyboardEvent) => {
const maxIndex = filteredLabels.length + 1
if (event.key === 'ArrowUp') {
event.preventDefault()
@@ -264,6 +267,10 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
router.push('/settings/labels')
return
}
+ if (focusedIndex === maxIndex - 1) {
+ await createLabelFromFilterText()
+ return
+ }
if (focusedIndex !== undefined) {
const label = filteredLabels[focusedIndex]
if (label) {
@@ -271,7 +278,17 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
}
}
}
- }, [filteredLabels, focusedIndex, isSelected, selectedLabels, setSelectedLabels])
+ }, [filterText, filteredLabels, focusedIndex, isSelected, selectedLabels, setSelectedLabels])
+
+ const createLabelFromFilterText = useCallback(async () => {
+ const label = await createLabelMutation(filterText, randomLabelColorHex(), '')
+ if (label) {
+ showSuccessToast(`Created label ${label.name}`, { position: 'bottom-right' })
+ toggleLabel(label)
+ } else {
+ showErrorToast('Failed to create label', { position: 'bottom-right' })
+ }
+ }, [filterText, selectedLabels, setSelectedLabels, toggleLabel])
return (
{
- const label = await createLabelMutation(filterText, randomLabelColorHex(), '')
- if (label) {
- showSuccessToast(`Created label ${label.name}`, { position: 'bottom-right' })
- toggleLabel(label)
- } else {
- showErrorToast('Failed to create label', { position: 'bottom-right' })
- }
- }}
+ onClick={createLabelFromFilterText}
>
From aa9797f4d44b7d30d6be7e57865d713029586f41 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 14:06:11 -0700
Subject: [PATCH 054/125] Dont reserve label space if there are no labels
---
packages/web/components/templates/article/ArticleContainer.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx
index cb4a31003..74fda11f6 100644
--- a/packages/web/components/templates/article/ArticleContainer.tsx
+++ b/packages/web/components/templates/article/ArticleContainer.tsx
@@ -148,7 +148,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
href={props.article.url}
/>
{props.labels ? (
-
+
{props.labels?.map((label) =>
)}
From 8d5425bc536cac21ce77719144405ac442ada68a Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 14:45:29 -0700
Subject: [PATCH 055/125] Show a success message when reader settings are reset
---
.../web/components/templates/article/ReaderSettingsModal.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
index 0923b7e09..e8a312370 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -19,6 +19,7 @@ import { AlignCenterHorizontalSimple, ArrowsInLineHorizontal, ArrowsOutLineHoriz
import { AIcon } from '../../elements/images/AIcon'
import { TickedRangeSlider } from '../../elements/TickedRangeSlider'
import { UserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
+import { showSuccessToast } from '../../../lib/toastHelpers'
type ReaderSettingsProps = {
userPreferences?: UserPreferences
@@ -124,6 +125,7 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
setMarginWidth(360)
setLineHeight(150)
props.articleActionHandler('resetReaderSettings')
+ showSuccessToast('Display settings reset', { position: 'bottom-right' })
}}
>
From a6566e4a9b152d7ae67f5ce820ccfb00a2e33ee6 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 14:46:02 -0700
Subject: [PATCH 056/125] Rename notes sidebar to highlights modal
---
.../components/templates/article/ArticleContainer.tsx | 11 +++++++----
.../templates/article/ArticleHeaderToolbar.tsx | 4 ++--
.../components/templates/article/HighlightsLayer.tsx | 8 ++++----
packages/web/pages/[username]/[slug]/index.tsx | 11 ++---------
4 files changed, 15 insertions(+), 19 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx
index 74fda11f6..6d318bdbb 100644
--- a/packages/web/components/templates/article/ArticleContainer.tsx
+++ b/packages/web/components/templates/article/ArticleContainer.tsx
@@ -15,6 +15,7 @@ import { updateThemeLocally } from '../../../lib/themeUpdater'
import { ArticleMutations } from '../../../lib/articleActions'
import { LabelChip } from '../../elements/LabelChip'
import { Label } from '../../../lib/networking/fragments/labelFragment'
+import { HighlightsModal } from './HighlightsModal'
type ArticleContainerProps = {
article: ArticleAttributes
@@ -28,11 +29,13 @@ type ArticleContainerProps = {
fontSize?: number
fontFamily?: string
lineHeight?: number
+ showHighlightsModal?: boolean
+ setShowHighlightsModal?: (show: boolean) => void
}
export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
const [showShareModal, setShowShareModal] = useState(false)
- const [showNotesSidebar, setShowNotesSidebar] = useState(false)
+ const [showHighlightsModal, setShowHighlightsModal] = useState(false)
const [showReportIssuesModal, setShowReportIssuesModal] = useState(false)
const [fontSize, setFontSize] = useState(props.fontSize ?? 20)
@@ -157,7 +160,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
0}
/>
@@ -193,9 +196,9 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
articleId={props.article.id}
isAppleAppEmbed={props.isAppleAppEmbed}
highlightBarDisabled={props.highlightBarDisabled}
- showNotesSidebar={showNotesSidebar}
+ showHighlightsModal={showHighlightsModal}
highlightsBaseURL={props.highlightsBaseURL}
- setShowNotesSidebar={setShowNotesSidebar}
+ setShowHighlightsModal={setShowHighlightsModal}
articleMutations={props.articleMutations}
/>
{showReportIssuesModal ? (
diff --git a/packages/web/components/templates/article/ArticleHeaderToolbar.tsx b/packages/web/components/templates/article/ArticleHeaderToolbar.tsx
index c870a7682..ca2edb16d 100644
--- a/packages/web/components/templates/article/ArticleHeaderToolbar.tsx
+++ b/packages/web/components/templates/article/ArticleHeaderToolbar.tsx
@@ -14,7 +14,7 @@ type ArticleHeaderToolbarProps = {
articleTitle: string
articleShareURL: string
hasHighlights: boolean
- setShowNotesSidebar: (showNotesSidebar: boolean) => void
+ setShowHighlightsModal: (showHighlightsModal: boolean) => void
setShowShareArticleModal: (showShareModal: boolean) => void
}
@@ -46,7 +46,7 @@ export function ArticleHeaderToolbar(
return (
{props.hasHighlights && (
- props.setShowNotesSidebar(true)} title="View all your highlights and notes">
+ props.setShowHighlightsModal(true)} title="View all your highlights and notes">
>
+ setShowHighlightsModal: React.Dispatch>
articleMutations: ArticleMutations
}
@@ -466,11 +466,11 @@ export function HighlightsLayer(props: HighlightsLayerProps): JSX.Element {
)
}
- if (props.showNotesSidebar) {
+ if (props.showHighlightsModal) {
return (
props.setShowNotesSidebar(false)}
+ onOpenChange={() => props.setShowHighlightsModal(false)}
deleteHighlightAction={(highlightId: string) => {
removeHighlightCallback(highlightId)
}}
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index b4d84cfda..c0dfc3589 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -218,6 +218,8 @@ export default function Home(): JSX.Element {
margin={marginWidth}
lineHeight={lineHeight}
labels={labels}
+ showHighlightsModal={showHighlightsModal}
+ setShowHighlightsModal={setShowHighlightsModal}
articleMutations={{
createHighlightMutation,
deleteHighlightMutation,
@@ -228,15 +230,6 @@ export default function Home(): JSX.Element {
/>
)}
- {showHighlightsModal && (
- setShowHighlightsModal(false)}
- deleteHighlightAction={(highlightId: string) => {
- // removeHighlightCallback(highlightId)
- }}
- />
- )}
)
}
From 04eef2938796bed5b746bdfb106cb1b3c33d18e0 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 15:01:11 -0700
Subject: [PATCH 057/125] Wire up show highlights modal to new article actions
---
.../templates/article/ArticleContainer.tsx | 14 +++++++-------
.../templates/article/ArticleHeaderToolbar.tsx | 9 +++++++--
packages/web/pages/app/[username]/[slug]/index.tsx | 3 +++
3 files changed, 17 insertions(+), 9 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx
index 6d318bdbb..ab3b00800 100644
--- a/packages/web/components/templates/article/ArticleContainer.tsx
+++ b/packages/web/components/templates/article/ArticleContainer.tsx
@@ -29,14 +29,14 @@ type ArticleContainerProps = {
fontSize?: number
fontFamily?: string
lineHeight?: number
- showHighlightsModal?: boolean
- setShowHighlightsModal?: (show: boolean) => void
+ showHighlightsModal: boolean
+ setShowHighlightsModal: React.Dispatch>
}
export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
const [showShareModal, setShowShareModal] = useState(false)
- const [showHighlightsModal, setShowHighlightsModal] = useState(false)
const [showReportIssuesModal, setShowReportIssuesModal] = useState(false)
+ const [showHighlightsModal, setShowHighlightsModal] = useState(props.showHighlightsModal)
const [fontSize, setFontSize] = useState(props.fontSize ?? 20)
const updateFontSize = async (newFontSize: number) => {
@@ -160,8 +160,8 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
0}
/>
@@ -195,10 +195,10 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
articleAuthor={props.article.author ?? ''}
articleId={props.article.id}
isAppleAppEmbed={props.isAppleAppEmbed}
- highlightBarDisabled={props.highlightBarDisabled}
- showHighlightsModal={showHighlightsModal}
highlightsBaseURL={props.highlightsBaseURL}
- setShowHighlightsModal={setShowHighlightsModal}
+ highlightBarDisabled={props.highlightBarDisabled}
+ showHighlightsModal={props.showHighlightsModal}
+ setShowHighlightsModal={props.setShowHighlightsModal}
articleMutations={props.articleMutations}
/>
{showReportIssuesModal ? (
diff --git a/packages/web/components/templates/article/ArticleHeaderToolbar.tsx b/packages/web/components/templates/article/ArticleHeaderToolbar.tsx
index ca2edb16d..10b0e993b 100644
--- a/packages/web/components/templates/article/ArticleHeaderToolbar.tsx
+++ b/packages/web/components/templates/article/ArticleHeaderToolbar.tsx
@@ -14,7 +14,7 @@ type ArticleHeaderToolbarProps = {
articleTitle: string
articleShareURL: string
hasHighlights: boolean
- setShowHighlightsModal: (showHighlightsModal: boolean) => void
+ setShowHighlightsModal: React.Dispatch>
setShowShareArticleModal: (showShareModal: boolean) => void
}
@@ -46,7 +46,12 @@ export function ArticleHeaderToolbar(
return (
{props.hasHighlights && (
- props.setShowHighlightsModal(true)} title="View all your highlights and notes">
+ {
+ if (props.setShowHighlightsModal) {
+ props.setShowHighlightsModal(true)
+ }
+ }}
+ title="View all your highlights and notes">
(null)
+ const [showHighlightsModal, setShowHighlightsModal] = useState(false)
const { articleData } = useGetArticleQuery({
username: props.username,
@@ -100,6 +101,8 @@ function AppArticleEmbedContent(
margin={props.margin}
fontFamily={props.fontFamily}
labels={[]}
+ showHighlightsModal={showHighlightsModal}
+ setShowHighlightsModal={setShowHighlightsModal}
articleMutations={{
createHighlightMutation,
deleteHighlightMutation,
From c2cf30624bb346695f91cd549adbfea01e38e7fb Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 15:12:38 -0700
Subject: [PATCH 058/125] Remove X icon on reset to better center button
---
.../templates/article/ReaderSettingsModal.tsx | 17 ++---------------
1 file changed, 2 insertions(+), 15 deletions(-)
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsModal.tsx
index e8a312370..eedc7042a 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsModal.tsx
@@ -1,20 +1,8 @@
-import {
- ModalRoot,
- ModalOverlay,
- ModalContent,
-} from '../../elements/ModalPrimitives'
-import { Box, HStack, VStack, Separator, SpanBox } from '../../elements/LayoutPrimitives'
+import { HStack, VStack, SpanBox } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
import { StyledText } from '../../elements/StyledText'
-import { CrossIcon } from '../../elements/images/CrossIcon'
-import { CommentIcon } from '../../elements/images/CommentIcon'
-import { TrashIcon } from '../../elements/images/TrashIcon'
import { styled, theme } from '../../tokens/stitches.config'
-import type { Highlight } from '../../../lib/networking/fragments/highlightFragment'
-import { HighlightView } from '../../patterns/HighlightView'
-import { useCallback, useState } from 'react'
-import { StyledTextArea } from '../../elements/StyledTextArea'
-import { ConfirmationModal } from '../../patterns/ConfirmationModal'
+import { useState } from 'react'
import { AlignCenterHorizontalSimple, ArrowsInLineHorizontal, ArrowsOutLineHorizontal, Minus, Pen, Plus, Trash, X } from 'phosphor-react'
import { AIcon } from '../../elements/images/AIcon'
import { TickedRangeSlider } from '../../elements/TickedRangeSlider'
@@ -128,7 +116,6 @@ export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
showSuccessToast('Display settings reset', { position: 'bottom-right' })
}}
>
-
Reset to default
From f0a725b7efc8e5f886113a15cd5f6cc0e3afb08e Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 15:20:07 -0700
Subject: [PATCH 059/125] Dont let labels list expand too wide
---
.../web/components/templates/article/ArticleActionsMenu.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index f8fbb8c22..6514f5000 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -41,7 +41,7 @@ type ActionDropdownProps = {
const ActionDropdown = (props: ActionDropdownProps): JSX.Element => {
return
Date: Mon, 11 Apr 2022 15:24:19 -0700
Subject: [PATCH 060/125] Set the label modal width to be consistent
---
.../web/components/templates/article/ArticleActionsMenu.tsx | 2 +-
packages/web/components/templates/article/EditLabelsControl.tsx | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 6514f5000..7c411dbad 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -41,7 +41,7 @@ type ActionDropdownProps = {
const ActionDropdown = (props: ActionDropdownProps): JSX.Element => {
return
Date: Mon, 11 Apr 2022 15:36:19 -0700
Subject: [PATCH 061/125] Only show the old article toolbar on ios embedded web
(/app)
---
.../templates/article/ArticleContainer.tsx | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx
index ab3b00800..3c879b7cb 100644
--- a/packages/web/components/templates/article/ArticleContainer.tsx
+++ b/packages/web/components/templates/article/ArticleContainer.tsx
@@ -157,13 +157,15 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
)}
) : null}
- 0}
- />
+ {props.isAppleAppEmbed && (
+ 0}
+ />
+ )}
Date: Mon, 11 Apr 2022 15:36:35 -0700
Subject: [PATCH 062/125] Dont set a max width on smaller devices for the
labels modal
---
.../web/components/templates/article/EditLabelsControl.tsx | 4 ----
1 file changed, 4 deletions(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index b988fda21..de29a323e 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -297,10 +297,6 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
css={{
p: '0',
maxHeight: '92%',
- '@mdDown': {
- maxWidth: '100%',
- maxHeight: '92%',
- },
}}>
Date: Mon, 11 Apr 2022 18:46:51 -0700
Subject: [PATCH 063/125] Use a modal instead of dropdown on small screens
---
.../templates/article/ArticleActionsMenu.tsx | 56 +++++++++++--------
.../templates/article/ArticleContainer.tsx | 1 -
.../templates/article/EditLabelsControl.tsx | 32 ++++++-----
.../templates/article/EditLabelsModal.tsx | 42 ++++++++++++--
.../web/pages/[username]/[slug]/index.tsx | 15 +++++
5 files changed, 103 insertions(+), 43 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 7c411dbad..3b13c4d5c 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -4,7 +4,7 @@ import { ArticleAttributes } from "../../../lib/networking/queries/useGetArticle
import { useGetUserPreferences } from "../../../lib/networking/queries/useGetUserPreferences"
import { Button } from "../../elements/Button"
import { Dropdown } from "../../elements/DropdownElements"
-import { Box } from "../../elements/LayoutPrimitives"
+import { Box, SpanBox } from "../../elements/LayoutPrimitives"
import { TooltipWrapped } from "../../elements/Tooltip"
import { styled, theme } from "../../tokens/stitches.config"
import { EditLabelsControl } from "./EditLabelsControl"
@@ -84,32 +84,40 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
-
-
-
- }
+
-
-
-{/*
- props.articleActionHandler('editLabels')} css={{
- // '@smDown': {
- // display: 'none',
- // },
+
+
+
+ }
+ >
+
+
+
+
+ props.articleActionHandler('editLabels')}
+ css={{
+ 'display': 'none',
+ '@smDown': {
+ display: 'flex',
+ },
}}>
-
-
- */}
+
props.articleActionHandler('showHighlights')}>
-
- Edit labels
+
+
+ Edit labels
+
)
}
@@ -296,7 +299,8 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
onKeyDown={handleKeyDown}
css={{
p: '0',
- maxHeight: '92%',
+ width: '100%',
+ maxHeight: '80%',
}}>
void
+ articleActionHandler: (action: string, arg?: unknown) => void
+}
+
+export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
return (
-
+
{
event.preventDefault()
}}
- css={{ overflow: 'auto', p: '0' }}
+ css={{ overflow: 'auto', p: '0', width: '100%' }}
>
- {/* */}
+
+
+ Labels
+ {
+ props.onOpenChange(false)
+ }}
+ >
+
+
+
+
+
)
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index c0dfc3589..5748a0013 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -27,6 +27,8 @@ import { setLinkArchivedMutation } from '../../../lib/networking/mutations/setLi
import { Label } from '../../../lib/networking/fragments/labelFragment'
import { useSWRConfig } from 'swr'
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
+import { EditLabelsControl } from '../../../components/templates/article/EditLabelsControl'
+import { EditLabelsModal } from '../../../components/templates/article/EditLabelsModal'
const PdfArticleContainerNoSSR = dynamic(
@@ -46,6 +48,7 @@ export default function Home(): JSX.Element {
const [fontSize, setFontSize] = useState(preferencesData?.fontSize ?? 20)
const [marginWidth, setMarginWidth] = useState(preferencesData?.margin ?? 360)
const [lineHeight, setLineHeight] = useState(preferencesData?.lineHeight ?? 150)
+ const [showEditLabelsModal, setShowEditLabelsModal] = useState(false)
const { articleData } = useGetArticleQuery({
username: router.query.username as string,
@@ -131,6 +134,10 @@ export default function Home(): JSX.Element {
}
break
}
+ case 'editLabels': {
+ setShowEditLabelsModal(true)
+ break
+ }
case 'resetReaderSettings': {
updateFontSize(20)
updateMarginWidth(360)
@@ -230,6 +237,14 @@ export default function Home(): JSX.Element {
/>
)}
+
+ {showEditLabelsModal && (
+ setShowEditLabelsModal(false)}
+ />
+ )}
)
}
From 68933689f6118f9bfaaed7b20f04d5cc9eb9f117 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 19:08:19 -0700
Subject: [PATCH 064/125] Close labels modal when tap outside
---
packages/web/components/templates/article/EditLabelsModal.tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index 2e048310e..ca45e65f8 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -23,10 +23,11 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
{
event.preventDefault()
+ props.onOpenChange(false)
}}
- css={{ overflow: 'auto', p: '0', width: '100%' }}
>
Date: Mon, 11 Apr 2022 20:38:54 -0700
Subject: [PATCH 065/125] Smaller display settings dropdown on small screens.
Better shadows
---
.../templates/article/ArticleActionsMenu.tsx | 3 ++-
.../templates/article/ReaderSettingsModal.tsx | 4 ++--
packages/web/pages/[username]/[slug]/index.tsx | 16 +++++++++++++++-
3 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 3b13c4d5c..a1d3f951b 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -41,7 +41,7 @@ type ActionDropdownProps = {
const ActionDropdown = (props: ActionDropdownProps): JSX.Element => {
return
+
- Margin:
+ Margin:
{
const newMarginWith = Math.max(marginWidth - 45, 200)
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index 5748a0013..c950b11b3 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -29,6 +29,7 @@ import { useSWRConfig } from 'swr'
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
import { EditLabelsControl } from '../../../components/templates/article/EditLabelsControl'
import { EditLabelsModal } from '../../../components/templates/article/EditLabelsModal'
+import { DisplaySettingsModal } from '../../../components/templates/article/DisplaySettingsModal'
const PdfArticleContainerNoSSR = dynamic(
@@ -49,7 +50,8 @@ export default function Home(): JSX.Element {
const [marginWidth, setMarginWidth] = useState(preferencesData?.margin ?? 360)
const [lineHeight, setLineHeight] = useState(preferencesData?.lineHeight ?? 150)
const [showEditLabelsModal, setShowEditLabelsModal] = useState(false)
-
+ const [showEditDisplaySettingsModal, setShowEditDisplaySettingsModal] = useState(false)
+
const { articleData } = useGetArticleQuery({
username: router.query.username as string,
slug: router.query.slug as string,
@@ -134,6 +136,10 @@ export default function Home(): JSX.Element {
}
break
}
+ case 'editDisplaySettings': {
+ setShowEditDisplaySettingsModal(true)
+ break
+ }
case 'editLabels': {
setShowEditLabelsModal(true)
break
@@ -245,6 +251,14 @@ export default function Home(): JSX.Element {
onOpenChange={() => setShowEditLabelsModal(false)}
/>
)}
+
+ {showEditDisplaySettingsModal && (
+ setShowEditDisplaySettingsModal(false)}
+ />
+ )}
)
}
From 5450c9ce298c6f47e487a20ec2734be5ae418b25 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 21:03:35 -0700
Subject: [PATCH 066/125] Add stand alone display settings modal
---
.../article/DisplaySettingsModal.tsx | 59 +++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 packages/web/components/templates/article/DisplaySettingsModal.tsx
diff --git a/packages/web/components/templates/article/DisplaySettingsModal.tsx b/packages/web/components/templates/article/DisplaySettingsModal.tsx
new file mode 100644
index 000000000..b2d3b40ed
--- /dev/null
+++ b/packages/web/components/templates/article/DisplaySettingsModal.tsx
@@ -0,0 +1,59 @@
+import { X } from 'phosphor-react'
+import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
+import { UserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
+import { Button } from '../../elements/Button'
+import { CrossIcon } from '../../elements/images/CrossIcon'
+import { Box, HStack, VStack } from '../../elements/LayoutPrimitives'
+import {
+ ModalRoot,
+ ModalOverlay,
+ ModalContent,
+} from '../../elements/ModalPrimitives'
+import { StyledText } from '../../elements/StyledText'
+import { theme } from '../../tokens/stitches.config'
+import { EditLabelsControl } from './EditLabelsControl'
+import { ReaderSettings } from './ReaderSettingsModal'
+
+type DisplaySettingsModalProps = {
+ onOpenChange: (open: boolean) => void
+ userPreferences?: UserPreferences
+ articleActionHandler: (action: string, arg?: number) => void
+}
+
+export function DisplaySettingsModal(props: DisplaySettingsModalProps): JSX.Element {
+ return (
+
+
+ {
+ event.preventDefault()
+ props.onOpenChange(false)
+ }}
+ >
+
+
+ Labels
+ {
+ props.onOpenChange(false)
+ }}
+ >
+
+
+
+
+
+
+
+ )
+}
From 9834303b3c5e5e75e34deb66df3c3d7c5b209e28 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Mon, 11 Apr 2022 21:17:26 -0700
Subject: [PATCH 067/125] Refine box shadows in dark mode
---
packages/web/components/tokens/stitches.config.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts
index de59a709b..1a45ef788 100644
--- a/packages/web/components/tokens/stitches.config.ts
+++ b/packages/web/components/tokens/stitches.config.ts
@@ -200,7 +200,7 @@ const darkThemeSpec = {
},
shadows: {
cardBoxShadow:
- '0px 0px 9px -2px rgba(32, 31, 29, 0.09), 0px 7px 12px rgba(32, 31, 29, 0.07)',
+ '0px 0px 9px -2px rgba(255, 255, 255, 0.09), 0px 7px 12px rgba(255, 255, 255, 0.07)',
},
}
From cbff548b9854cb17ff2c75d7ae6ae2b5eb362a7c Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Tue, 12 Apr 2022 08:42:15 -0700
Subject: [PATCH 068/125] Tweak box shadows in dark mode
---
packages/web/components/tokens/stitches.config.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts
index 1a45ef788..f9937aa33 100644
--- a/packages/web/components/tokens/stitches.config.ts
+++ b/packages/web/components/tokens/stitches.config.ts
@@ -102,8 +102,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
borderStyles: {},
shadows: {
panelShadow: '0px 4px 18px rgba(120, 123, 134, 0.12)',
- cardBoxShadow:
- '0px 0px 9px -2px rgba(32, 31, 29, 0.09), 0px 7px 12px rgba(32, 31, 29, 0.07)',
+ cardBoxShadow: '0px 0px 4px 0px rgb(255 255 255 / 10%)',
},
zIndices: {},
transitions: {},
From 244cced8b400f0a81625df82ca4f2f16bce37d7a Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Tue, 12 Apr 2022 12:35:56 -0700
Subject: [PATCH 069/125] Make the labels modal non-scrollable on small
screens, add a border
---
packages/web/components/templates/article/EditLabelsModal.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/EditLabelsModal.tsx
index ca45e65f8..1ef6da940 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/EditLabelsModal.tsx
@@ -23,7 +23,7 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
{
event.preventDefault()
props.onOpenChange(false)
From 0d1b05d4fc246e00e0ec0886c27cafa5e9108e41 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Tue, 12 Apr 2022 12:58:04 -0700
Subject: [PATCH 070/125] Rename reader settings
---
.../web/components/templates/article/ArticleActionsMenu.tsx | 4 ++--
.../components/templates/article/DisplaySettingsModal.tsx | 5 ++---
.../{ReaderSettingsModal.tsx => ReaderSettingsControl.tsx} | 3 ++-
3 files changed, 6 insertions(+), 6 deletions(-)
rename packages/web/components/templates/article/{ReaderSettingsModal.tsx => ReaderSettingsControl.tsx} (98%)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index a1d3f951b..410ae3080 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -8,7 +8,7 @@ import { Box, SpanBox } from "../../elements/LayoutPrimitives"
import { TooltipWrapped } from "../../elements/Tooltip"
import { styled, theme } from "../../tokens/stitches.config"
import { EditLabelsControl } from "./EditLabelsControl"
-import { ReaderSettings } from "./ReaderSettingsModal"
+import { ReaderSettingsControl } from "./ReaderSettingsControl"
export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
@@ -80,7 +80,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}
>
-
+
diff --git a/packages/web/components/templates/article/DisplaySettingsModal.tsx b/packages/web/components/templates/article/DisplaySettingsModal.tsx
index b2d3b40ed..c7e0623cb 100644
--- a/packages/web/components/templates/article/DisplaySettingsModal.tsx
+++ b/packages/web/components/templates/article/DisplaySettingsModal.tsx
@@ -11,8 +11,7 @@ import {
} from '../../elements/ModalPrimitives'
import { StyledText } from '../../elements/StyledText'
import { theme } from '../../tokens/stitches.config'
-import { EditLabelsControl } from './EditLabelsControl'
-import { ReaderSettings } from './ReaderSettingsModal'
+import { ReaderSettingsControl } from './ReaderSettingsControl'
type DisplaySettingsModalProps = {
onOpenChange: (open: boolean) => void
@@ -51,7 +50,7 @@ export function DisplaySettingsModal(props: DisplaySettingsModalProps): JSX.Elem
/>
-
+
diff --git a/packages/web/components/templates/article/ReaderSettingsModal.tsx b/packages/web/components/templates/article/ReaderSettingsControl.tsx
similarity index 98%
rename from packages/web/components/templates/article/ReaderSettingsModal.tsx
rename to packages/web/components/templates/article/ReaderSettingsControl.tsx
index 1f435978d..fd1e74004 100644
--- a/packages/web/components/templates/article/ReaderSettingsModal.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsControl.tsx
@@ -9,6 +9,7 @@ import { TickedRangeSlider } from '../../elements/TickedRangeSlider'
import { UserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
import { showSuccessToast } from '../../../lib/toastHelpers'
+
type ReaderSettingsProps = {
userPreferences?: UserPreferences
articleActionHandler: (action: string, arg?: number) => void
@@ -20,7 +21,7 @@ const VerticalDivider = styled(SpanBox, {
background: `${theme.colors.grayLine.toString()}`,
})
-export function ReaderSettings(props: ReaderSettingsProps): JSX.Element {
+export function ReaderSettingsControl(props: ReaderSettingsProps): JSX.Element {
const [marginWidth, setMarginWidth] = useState(props.userPreferences?.margin ?? 0)
const [lineHeight, setLineHeight] = useState(props.userPreferences?.lineHeight ?? 150)
From 372cd6afe2df25968984d9f98f1caa3a18ee5f0d Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Tue, 12 Apr 2022 13:16:25 -0700
Subject: [PATCH 071/125] More consistent boxshadows with themes
---
packages/web/components/patterns/HighlightBar.tsx | 2 +-
packages/web/components/tokens/stitches.config.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/web/components/patterns/HighlightBar.tsx b/packages/web/components/patterns/HighlightBar.tsx
index 444fb0f56..ead951168 100644
--- a/packages/web/components/patterns/HighlightBar.tsx
+++ b/packages/web/components/patterns/HighlightBar.tsx
@@ -44,7 +44,7 @@ export function HighlightBar(props: HighlightBarProps): JSX.Element {
background: '$grayBg',
borderRadius: '4px',
border: '1px solid $grayBorder',
- boxShadow: '0px 0px 9px -2px rgba(32, 31, 29, 0.09), 0px 7px 12px rgba(32, 31, 29, 0.07)',
+ boxShadow: theme.shadows.cardBoxShadow.toString(),
bottom: 'calc(38px + env(safe-area-inset-bottom, 40px))',
'@smDown': {
maxWidth: '80%',
diff --git a/packages/web/components/tokens/stitches.config.ts b/packages/web/components/tokens/stitches.config.ts
index f9937aa33..80a87e59f 100644
--- a/packages/web/components/tokens/stitches.config.ts
+++ b/packages/web/components/tokens/stitches.config.ts
@@ -102,7 +102,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
borderStyles: {},
shadows: {
panelShadow: '0px 4px 18px rgba(120, 123, 134, 0.12)',
- cardBoxShadow: '0px 0px 4px 0px rgb(255 255 255 / 10%)',
+ cardBoxShadow: '0px 0px 4px 0px rgba(0, 0, 0, 0.1)',
},
zIndices: {},
transitions: {},
From 0fa9526924954d74f78d2e8c5ffbb365b8cee7eb Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Fri, 8 Apr 2022 12:56:08 -0700
Subject: [PATCH 072/125] use searchTerm and selected labels to compute
searchQuery
---
.../App/Views/Home/HomeFeedViewIOS.swift | 6 +++---
.../App/Views/Home/HomeFeedViewMac.swift | 6 +++---
.../App/Views/Home/HomeFeedViewModel.swift | 19 +++++++++++++++++--
3 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
index 7c5e60d82..e6c0e494b 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
@@ -23,17 +23,17 @@ import Views
viewModel.loadItems(dataService: dataService, isRefresh: true)
}
.searchable(
- text: $viewModel.searchQuery,
+ text: $viewModel.searchTerm,
placement: .sidebar
) {
- if viewModel.searchQuery.isEmpty {
+ if viewModel.searchTerm.isEmpty {
Text("Inbox").searchCompletion("in:inbox ")
Text("All").searchCompletion("in:all ")
Text("Archived").searchCompletion("in:archive ")
Text("Files").searchCompletion("type:file ")
}
}
- .onChange(of: viewModel.searchQuery) { _ in
+ .onChange(of: viewModel.searchTerm) { _ in
// Maybe we should debounce this, but
// it feels like it works ok without
viewModel.loadItems(dataService: dataService, isRefresh: true)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift
index 91cad42e8..640f6fdc5 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift
@@ -77,17 +77,17 @@ import Views
.listStyle(PlainListStyle())
.navigationTitle("Home")
.searchable(
- text: $viewModel.searchQuery,
+ text: $viewModel.searchTerm,
placement: .toolbar
) {
- if viewModel.searchQuery.isEmpty {
+ if viewModel.searchTerm.isEmpty {
Text("Inbox").searchCompletion("in:inbox ")
Text("All").searchCompletion("in:all ")
Text("Archived").searchCompletion("in:archive ")
Text("Files").searchCompletion("type:file ")
}
}
- .onChange(of: viewModel.searchQuery) { _ in
+ .onChange(of: viewModel.searchTerm) { _ in
// Maybe we should debounce this, but
// it feels like it works ok without
viewModel.loadItems(dataService: dataService, isRefresh: true)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift
index 7a9af7d98..61829eafd 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift
@@ -15,7 +15,8 @@ final class HomeFeedViewModel: ObservableObject {
@Published var isLoading = false
@Published var showPushNotificationPrimer = false
@Published var itemUnderLabelEdit: FeedItem?
- @Published var searchQuery = ""
+ @Published var searchTerm = ""
+ @Published var selectedLabels = [FeedItemLabel]()
@Published var snoozePresented = false
@Published var itemToSnooze: FeedItem?
@Published var selectedLinkItem: FeedItem?
@@ -68,7 +69,7 @@ final class HomeFeedViewModel: ObservableObject {
dataService.libraryItemsPublisher(
limit: 10,
sortDescending: true,
- searchQuery: searchQuery.isEmpty ? nil : searchQuery,
+ searchQuery: searchQuery,
cursor: isRefresh ? nil : cursor
)
.sink(
@@ -195,4 +196,18 @@ final class HomeFeedViewModel: ObservableObject {
items[index].labels = labels
}
}
+
+ private var searchQuery: String? {
+ if searchTerm.isEmpty, selectedLabels.isEmpty {
+ return nil
+ }
+
+ var query = searchTerm
+
+ for label in selectedLabels {
+ query.append(" label:\(label.name)")
+ }
+
+ return query
+ }
}
From df291ecbab34f83d019d76fbea0140f1d68c472b Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Sun, 10 Apr 2022 19:25:06 -0700
Subject: [PATCH 073/125] add text chip buttons to represent labels used as
filters
---
.../App/Views/Home/HomeFeedViewIOS.swift | 91 ++++++++++++-------
.../OmnivoreKit/Sources/Views/TextChip.swift | 68 ++++++++++++++
2 files changed, 127 insertions(+), 32 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
index e6c0e494b..bf5c6b885 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
@@ -24,7 +24,7 @@ import Views
}
.searchable(
text: $viewModel.searchTerm,
- placement: .sidebar
+ placement: .navigationBarDrawer
) {
if viewModel.searchTerm.isEmpty {
Text("Inbox").searchCompletion("in:inbox ")
@@ -71,6 +71,7 @@ import Views
}
}
.navigationTitle("Home")
+ .navigationBarTitleDisplayMode(.inline)
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
// Don't refresh the list if the user is currently reading an article
if viewModel.selectedLinkItem == nil {
@@ -106,48 +107,74 @@ import Views
struct HomeFeedView: View {
@EnvironmentObject var dataService: DataService
-
@Binding var prefersListLayout: Bool
-
+ @State private var showLabelsSheet = false
@ObservedObject var viewModel: HomeFeedViewModel
+ // TODO: remove stub
+ let demoFilterChips = [
+ FeedItemLabel(id: "1", name: "Inbox", color: "#039466", createdAt: nil, description: nil),
+ FeedItemLabel(id: "2", name: "NotInbox", color: "#039466", createdAt: nil, description: nil),
+ FeedItemLabel(id: "3", name: "Atari", color: "#039466", createdAt: nil, description: nil),
+ FeedItemLabel(id: "4", name: "iOS", color: "#039466", createdAt: nil, description: nil)
+ ]
+
var body: some View {
- if prefersListLayout {
- HomeFeedListView(prefersListLayout: $prefersListLayout, viewModel: viewModel)
- } else {
- HomeFeedGridView(viewModel: viewModel)
- .toolbar {
- ToolbarItem {
- if #available(iOS 15.0, *) {
- Button("", action: {})
- .disabled(true)
- .overlay {
- if viewModel.isLoading {
- ProgressView()
+ VStack {
+ ScrollView(.horizontal, showsIndicators: false) {
+ HStack {
+ TextChipButton.makeAddLabelButton {
+ showLabelsSheet = true
+ }
+ ForEach(demoFilterChips, id: \.self) { label in
+ TextChipButton.makeRemovableLabelButton(feedItemLabel: label) {
+ print("tapped label named \(label.name)")
+ }
+ }
+ Spacer()
+ }
+ .padding(.horizontal)
+ .sheet(isPresented: $showLabelsSheet) {
+ Text("select labels stub")
+ }
+ }
+ if prefersListLayout {
+ HomeFeedListView(prefersListLayout: $prefersListLayout, viewModel: viewModel)
+ } else {
+ HomeFeedGridView(viewModel: viewModel)
+ .toolbar {
+ ToolbarItem {
+ if #available(iOS 15.0, *) {
+ Button("", action: {})
+ .disabled(true)
+ .overlay {
+ if viewModel.isLoading {
+ ProgressView()
+ }
}
- }
- } else {
- if viewModel.isLoading {
- Button(action: {}, label: { ProgressView() })
} else {
+ if viewModel.isLoading {
+ Button(action: {}, label: { ProgressView() })
+ } else {
+ Button(
+ action: { viewModel.loadItems(dataService: dataService, isRefresh: true) },
+ label: { Label("Refresh Feed", systemImage: "arrow.clockwise") }
+ )
+ }
+ }
+ }
+ ToolbarItem {
+ if UIDevice.isIPad {
Button(
- action: { viewModel.loadItems(dataService: dataService, isRefresh: true) },
- label: { Label("Refresh Feed", systemImage: "arrow.clockwise") }
+ action: { prefersListLayout.toggle() },
+ label: {
+ Label("Toggle Feed Layout", systemImage: prefersListLayout ? "square.grid.2x2" : "list.bullet")
+ }
)
}
}
}
- ToolbarItem {
- if UIDevice.isIPad {
- Button(
- action: { prefersListLayout.toggle() },
- label: {
- Label("Toggle Feed Layout", systemImage: prefersListLayout ? "square.grid.2x2" : "list.bullet")
- }
- )
- }
- }
- }
+ }
}
}
}
diff --git a/apple/OmnivoreKit/Sources/Views/TextChip.swift b/apple/OmnivoreKit/Sources/Views/TextChip.swift
index cb55573e8..3fcd16e12 100644
--- a/apple/OmnivoreKit/Sources/Views/TextChip.swift
+++ b/apple/OmnivoreKit/Sources/Views/TextChip.swift
@@ -30,3 +30,71 @@ public struct TextChip: View {
.cornerRadius(cornerRadius)
}
}
+
+public struct TextChipButton: View {
+ public static func makeAddLabelButton(onTap: @escaping () -> Void) -> TextChipButton {
+ TextChipButton(title: "Label Filter", color: .appYellow48, actionType: .add, onTap: onTap)
+ }
+
+ public static func makeShowOptionsButton(title: String, onTap: @escaping () -> Void) -> TextChipButton {
+ TextChipButton(title: title, color: .appButtonBackground, actionType: .add, onTap: onTap)
+ }
+
+ public static func makeRemovableLabelButton(
+ feedItemLabel: FeedItemLabel,
+ onTap: @escaping () -> Void
+ ) -> TextChipButton {
+ TextChipButton(
+ title: feedItemLabel.name,
+ color: Color(hex: feedItemLabel.color) ?? .appButtonBackground,
+ actionType: .remove,
+ onTap: onTap
+ )
+ }
+
+ public enum ActionType {
+ case remove
+ case add
+ case show
+
+ var systemIconName: String {
+ switch self {
+ case .remove:
+ return "xmark"
+ case .add:
+ return "plus"
+ case .show:
+ return "chevron.down"
+ }
+ }
+ }
+
+ init(title: String, color: Color, actionType: ActionType, onTap: @escaping () -> Void) {
+ self.text = title
+ self.color = color
+ self.onTap = onTap
+ self.actionType = actionType
+ }
+
+ let text: String
+ let color: Color
+ let onTap: () -> Void
+ let actionType: ActionType
+ let cornerRadius = 20.0
+
+ public var body: some View {
+ Button(action: onTap) {
+ HStack {
+ Text(text)
+ Image(systemName: actionType.systemIconName)
+ }
+ .padding(.horizontal, 10)
+ .padding(.vertical, 5)
+ .font(.appFootnote)
+ .foregroundColor(color.isDark ? .white : .black)
+ .lineLimit(1)
+ .background(color)
+ .cornerRadius(cornerRadius)
+ }
+ }
+}
From bd652dc3edfd49e94696c9baa82c895bd2792434 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Sun, 10 Apr 2022 20:42:52 -0700
Subject: [PATCH 074/125] rename selectedLabelsForItemInContext to
selectedLabels
---
.../App/Views/Labels/ApplyLabelsView.swift | 6 ++--
.../App/Views/Labels/LabelsViewModel.swift | 34 +++++++++++++------
2 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
index 1c1348c94..0d46e15b7 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
@@ -15,10 +15,10 @@ struct ApplyLabelsView: View {
var innerBody: some View {
List {
Section(header: Text("Assigned Labels")) {
- if viewModel.selectedLabelsForItemInContext.isEmpty {
+ if viewModel.selectedLabels.isEmpty {
Text("No labels are currently assigned.")
}
- ForEach(viewModel.selectedLabelsForItemInContext.applySearchFilter(labelSearchFilter), id: \.self) { label in
+ ForEach(viewModel.selectedLabels.applySearchFilter(labelSearchFilter), id: \.self) { label in
HStack {
TextChip(feedItemLabel: label)
Spacer()
@@ -34,7 +34,7 @@ struct ApplyLabelsView: View {
}
}
Section(header: Text("Available Labels")) {
- ForEach(viewModel.unselectedLabelsForItemInContext.applySearchFilter(labelSearchFilter), id: \.self) { label in
+ ForEach(viewModel.unselectedLabels.applySearchFilter(labelSearchFilter), id: \.self) { label in
HStack {
TextChip(feedItemLabel: label)
Spacer()
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsViewModel.swift
index b38109c72..e8020c684 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsViewModel.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsViewModel.swift
@@ -7,14 +7,20 @@ import Views
final class LabelsViewModel: ObservableObject {
private var hasLoadedInitialLabels = false
@Published var isLoading = false
- @Published var selectedLabelsForItemInContext = [FeedItemLabel]()
- @Published var unselectedLabelsForItemInContext = [FeedItemLabel]()
+ @Published var selectedLabels = [FeedItemLabel]()
+ @Published var unselectedLabels = [FeedItemLabel]()
@Published var labels = [FeedItemLabel]()
@Published var showCreateEmailModal = false
var subscriptions = Set()
- func loadLabels(dataService: DataService, item: FeedItem?) {
+
+ /// Loads initial set of labels when a edit labels list is displayed
+ /// - Parameters:
+ /// - dataService: `DataService` reference
+ /// - item: Optional `FeedItem` for applying labels to a single item
+ /// - initiallySelectedLabels: Optional `[FeedItem]` for filtering a list of items
+ func loadLabels(dataService: DataService, item: FeedItem?, initiallySelectedLabels: [FeedItem]?) {
guard !hasLoadedInitialLabels else { return }
isLoading = true
@@ -25,11 +31,17 @@ final class LabelsViewModel: ObservableObject {
self?.labels = allLabels
self?.hasLoadedInitialLabels = true
if let item = item {
- self?.selectedLabelsForItemInContext = item.labels
- self?.unselectedLabelsForItemInContext = allLabels.filter { label in
+ self?.selectedLabels = item.labels
+ self?.unselectedLabels = allLabels.filter { label in
!item.labels.contains(where: { $0.id == label.id })
}
}
+ if let initiallySelectedLabels = initiallySelectedLabels {
+ self?.selectedLabels = initiallySelectedLabels
+ self?.unselectedLabels = allLabels.filter { label in
+ !initiallySelectedLabels.contains(where: { $0.id == label.id })
+ }
+ }
}
)
.store(in: &subscriptions)
@@ -49,7 +61,7 @@ final class LabelsViewModel: ObservableObject {
receiveValue: { [weak self] result in
self?.isLoading = false
self?.labels.insert(result, at: 0)
- self?.unselectedLabelsForItemInContext.insert(result, at: 0)
+ self?.unselectedLabels.insert(result, at: 0)
self?.showCreateEmailModal = false
}
)
@@ -73,7 +85,7 @@ final class LabelsViewModel: ObservableObject {
func saveItemLabelChanges(itemID: String, dataService: DataService, onComplete: @escaping ([FeedItemLabel]) -> Void) {
isLoading = true
- dataService.updateArticleLabelsPublisher(itemID: itemID, labelIDs: selectedLabelsForItemInContext.map(\.id)).sink(
+ dataService.updateArticleLabelsPublisher(itemID: itemID, labelIDs: selectedLabels.map(\.id)).sink(
receiveCompletion: { [weak self] _ in
self?.isLoading = false
},
@@ -83,12 +95,12 @@ final class LabelsViewModel: ObservableObject {
}
func addLabelToItem(_ label: FeedItemLabel) {
- selectedLabelsForItemInContext.insert(label, at: 0)
- unselectedLabelsForItemInContext.removeAll { $0.id == label.id }
+ selectedLabels.insert(label, at: 0)
+ unselectedLabels.removeAll { $0.id == label.id }
}
func removeLabelFromItem(_ label: FeedItemLabel) {
- unselectedLabelsForItemInContext.insert(label, at: 0)
- selectedLabelsForItemInContext.removeAll { $0.id == label.id }
+ unselectedLabels.insert(label, at: 0)
+ selectedLabels.removeAll { $0.id == label.id }
}
}
From c8ad2f47a2c2e137cc582ed8bb8e1f8f0d121a4b Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Sun, 10 Apr 2022 21:09:42 -0700
Subject: [PATCH 075/125] adapt assign labels view to work on a list
---
.../App/Views/Home/HomeFeedViewIOS.swift | 23 ++++------
.../App/Views/Labels/ApplyLabelsView.swift | 46 ++++++++++++++++---
.../App/Views/Labels/LabelsViewModel.swift | 5 +-
3 files changed, 52 insertions(+), 22 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
index bf5c6b885..611979fc9 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
@@ -38,11 +38,14 @@ import Views
// it feels like it works ok without
viewModel.loadItems(dataService: dataService, isRefresh: true)
}
+ .onChange(of: viewModel.selectedLabels) { _ in
+ viewModel.loadItems(dataService: dataService, isRefresh: true)
+ }
.onSubmit(of: .search) {
viewModel.loadItems(dataService: dataService, isRefresh: true)
}
.sheet(item: $viewModel.itemUnderLabelEdit) { item in
- ApplyLabelsView(item: item) { labels in
+ ApplyLabelsView(mode: .item(item)) { labels in
viewModel.updateLabels(itemID: item.id, labels: labels)
}
}
@@ -52,7 +55,7 @@ import Views
viewModel: viewModel
)
.sheet(item: $viewModel.itemUnderLabelEdit) { item in
- ApplyLabelsView(item: item) { labels in
+ ApplyLabelsView(mode: .item(item)) { labels in
viewModel.updateLabels(itemID: item.id, labels: labels)
}
}
@@ -111,14 +114,6 @@ import Views
@State private var showLabelsSheet = false
@ObservedObject var viewModel: HomeFeedViewModel
- // TODO: remove stub
- let demoFilterChips = [
- FeedItemLabel(id: "1", name: "Inbox", color: "#039466", createdAt: nil, description: nil),
- FeedItemLabel(id: "2", name: "NotInbox", color: "#039466", createdAt: nil, description: nil),
- FeedItemLabel(id: "3", name: "Atari", color: "#039466", createdAt: nil, description: nil),
- FeedItemLabel(id: "4", name: "iOS", color: "#039466", createdAt: nil, description: nil)
- ]
-
var body: some View {
VStack {
ScrollView(.horizontal, showsIndicators: false) {
@@ -126,16 +121,18 @@ import Views
TextChipButton.makeAddLabelButton {
showLabelsSheet = true
}
- ForEach(demoFilterChips, id: \.self) { label in
+ ForEach(viewModel.selectedLabels, id: \.self) { label in
TextChipButton.makeRemovableLabelButton(feedItemLabel: label) {
- print("tapped label named \(label.name)")
+ viewModel.selectedLabels.removeAll { $0.id == label.id }
}
}
Spacer()
}
.padding(.horizontal)
.sheet(isPresented: $showLabelsSheet) {
- Text("select labels stub")
+ ApplyLabelsView(mode: .list(viewModel.selectedLabels)) { labels in
+ viewModel.selectedLabels = labels
+ }
}
}
if prefersListLayout {
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
index 0d46e15b7..3b0c80a85 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
@@ -4,7 +4,30 @@ import SwiftUI
import Views
struct ApplyLabelsView: View {
- let item: FeedItem
+ enum Mode {
+ case item(FeedItem)
+ case list([FeedItemLabel])
+
+ var navTitle: String {
+ switch self {
+ case .item:
+ return "Assign Labels"
+ case .list:
+ return "Apply Label Filters"
+ }
+ }
+
+ var confirmButtonText: String {
+ switch self {
+ case .item:
+ return "Save"
+ case .list:
+ return "Apply"
+ }
+ }
+ }
+
+ let mode: Mode
let commitLabelChanges: ([FeedItemLabel]) -> Void
@EnvironmentObject var dataService: DataService
@@ -63,7 +86,7 @@ struct ApplyLabelsView: View {
.disabled(viewModel.isLoading)
}
}
- .navigationTitle("Assign Labels")
+ .navigationTitle(mode.navTitle)
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
@@ -76,12 +99,18 @@ struct ApplyLabelsView: View {
ToolbarItem(placement: .navigationBarTrailing) {
Button(
action: {
- viewModel.saveItemLabelChanges(itemID: item.id, dataService: dataService) { labels in
- commitLabelChanges(labels)
+ switch mode {
+ case let .item(feedItem):
+ viewModel.saveItemLabelChanges(itemID: feedItem.id, dataService: dataService) { labels in
+ commitLabelChanges(labels)
+ presentationMode.wrappedValue.dismiss()
+ }
+ case .list:
+ commitLabelChanges(viewModel.selectedLabels)
presentationMode.wrappedValue.dismiss()
}
},
- label: { Text("Save").foregroundColor(.appGrayTextContrast) }
+ label: { Text(mode.confirmButtonText).foregroundColor(.appGrayTextContrast) }
)
}
}
@@ -112,7 +141,12 @@ struct ApplyLabelsView: View {
}
}
.onAppear {
- viewModel.loadLabels(dataService: dataService, item: item)
+ switch mode {
+ case let .item(feedItem):
+ viewModel.loadLabels(dataService: dataService, item: feedItem)
+ case let .list(labels):
+ viewModel.loadLabels(dataService: dataService, initiallySelectedLabels: labels)
+ }
}
}
}
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsViewModel.swift
index e8020c684..35159e8ca 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsViewModel.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsViewModel.swift
@@ -14,13 +14,12 @@ final class LabelsViewModel: ObservableObject {
var subscriptions = Set()
-
/// Loads initial set of labels when a edit labels list is displayed
/// - Parameters:
/// - dataService: `DataService` reference
/// - item: Optional `FeedItem` for applying labels to a single item
- /// - initiallySelectedLabels: Optional `[FeedItem]` for filtering a list of items
- func loadLabels(dataService: DataService, item: FeedItem?, initiallySelectedLabels: [FeedItem]?) {
+ /// - initiallySelectedLabels: Optional `[FeedItemLabel]` for filtering a list of items
+ func loadLabels(dataService: DataService, item: FeedItem? = nil, initiallySelectedLabels: [FeedItemLabel]? = nil) {
guard !hasLoadedInitialLabels else { return }
isLoading = true
From be68971a0323952701ef8634a7c1ded5a7be9b49 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Sun, 10 Apr 2022 21:13:28 -0700
Subject: [PATCH 076/125] use xmark for remove label button
---
.../OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
index 3b0c80a85..8cf320384 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
@@ -51,7 +51,7 @@ struct ApplyLabelsView: View {
viewModel.removeLabelFromItem(label)
}
},
- label: { Image(systemName: "trash").foregroundColor(.appGrayTextContrast) }
+ label: { Image(systemName: "xmark.circle").foregroundColor(.appGrayTextContrast) }
)
}
}
From d68c8e5e337bb50f80cb20fa38fac93838079518 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Sun, 10 Apr 2022 21:38:36 -0700
Subject: [PATCH 077/125] update search query to handle multiple labels
---
.../Sources/App/Views/Home/HomeFeedViewModel.swift | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift
index 61829eafd..763615607 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift
@@ -204,8 +204,9 @@ final class HomeFeedViewModel: ObservableObject {
var query = searchTerm
- for label in selectedLabels {
- query.append(" label:\(label.name)")
+ if !selectedLabels.isEmpty {
+ query.append(" label:")
+ query.append(selectedLabels.map(\.name).joined(separator: ","))
}
return query
From 6c96b256d1603d89c6da9ef316b219a46c18e9d9 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Tue, 12 Apr 2022 12:30:33 -0700
Subject: [PATCH 078/125] move create label submit button into navbar
---
.../Sources/App/Views/Labels/LabelsView.swift | 32 ++++++++-----------
1 file changed, 14 insertions(+), 18 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
index 627305438..39c0049b6 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
@@ -92,6 +92,10 @@ struct CreateLabelView: View {
@State private var newLabelName = ""
@State private var newLabelColor = Color.clear
+ var shouldDisableCreateButton: Bool {
+ viewModel.isLoading || newLabelName.isEmpty || newLabelColor == .clear
+ }
+
var body: some View {
NavigationView {
VStack(spacing: 16) {
@@ -104,32 +108,24 @@ struct CreateLabelView: View {
newLabelColor == .clear ? "Select Color" : newLabelColor.description,
selection: $newLabelColor
)
- Button(
- action: {
- viewModel.createLabel(
- dataService: dataService,
- name: newLabelName,
- color: newLabelColor,
- description: nil
- )
- },
- label: { Text("Create") }
- )
- .buttonStyle(SolidCapsuleButtonStyle(color: .appDeepBackground, width: 300))
- .disabled(viewModel.isLoading || newLabelName.isEmpty || newLabelColor == .clear)
Spacer()
}
.padding()
.toolbar {
- ToolbarItem(placement: .automatic) {
+ ToolbarItem(placement: .navigationBarLeading) {
Button(
action: { viewModel.showCreateEmailModal = false },
- label: {
- Image(systemName: "xmark")
- .foregroundColor(.appGrayTextContrast)
- }
+ label: { Text("Cancel").foregroundColor(.appGrayTextContrast) }
)
}
+ ToolbarItem(placement: .navigationBarTrailing) {
+ Button(
+ action: {},
+ label: { Text("Create").foregroundColor(.appGrayTextContrast) }
+ )
+ .opacity(shouldDisableCreateButton ? 0.2 : 1)
+ .disabled(shouldDisableCreateButton)
+ }
}
.navigationTitle("Create New Label")
#if os(iOS)
From 060313ab31f3f2ded2de811342714aea955d17af Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Tue, 12 Apr 2022 15:05:29 -0700
Subject: [PATCH 079/125] use hgrid to display label color selections
---
.../Sources/App/Views/Labels/LabelsView.swift | 62 ++++++++++++++++---
1 file changed, 55 insertions(+), 7 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
index 39c0049b6..903726be1 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
@@ -19,7 +19,7 @@ struct LabelsView: View {
Form {
innerBody
.alert("Are you sure you want to delete this label?", isPresented: $showDeleteConfirmation) {
- Button("Remove Link", role: .destructive) {
+ Button("Delete Label", role: .destructive) {
if let labelID = labelToRemoveID {
withAnimation {
viewModel.deleteLabel(dataService: dataService, labelID: labelID)
@@ -96,18 +96,53 @@ struct CreateLabelView: View {
viewModel.isLoading || newLabelName.isEmpty || newLabelColor == .clear
}
+ let rows = [
+ GridItem(.fixed(60)),
+ GridItem(.fixed(60)),
+ GridItem(.fixed(60))
+ ]
+
+ let swatches = (0 ... 200).map { _ in Color.random }
+
var body: some View {
NavigationView {
- VStack(spacing: 16) {
+ VStack {
+ HStack {
+ if !newLabelName.isEmpty, newLabelColor != .clear {
+ TextChip(text: newLabelName, color: newLabelColor)
+ } else {
+ Text("Assign a name and color.")
+ }
+ Spacer()
+ }
+
TextField("Label Name", text: $newLabelName)
#if os(iOS)
.keyboardType(.alphabet)
#endif
.textFieldStyle(StandardTextFieldStyle())
- ColorPicker(
- newLabelColor == .clear ? "Select Color" : newLabelColor.description,
- selection: $newLabelColor
- )
+
+ ScrollView(.horizontal, showsIndicators: false) {
+ LazyHGrid(rows: rows, alignment: .top, spacing: 20) {
+ ForEach(swatches, id: \.self) { swatch in
+ ZStack {
+ Circle()
+ .fill(swatch)
+ .frame(width: 50, height: 50)
+ .onTapGesture {
+ newLabelColor = swatch
+ }
+ .padding(10)
+
+ if newLabelColor == swatch {
+ Circle()
+ .stroke(swatch, lineWidth: 5)
+ .frame(width: 60, height: 60)
+ }
+ }
+ }
+ }
+ }
Spacer()
}
.padding()
@@ -120,7 +155,14 @@ struct CreateLabelView: View {
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(
- action: {},
+ action: {
+ viewModel.createLabel(
+ dataService: dataService,
+ name: newLabelName,
+ color: newLabelColor,
+ description: nil
+ )
+ },
label: { Text("Create").foregroundColor(.appGrayTextContrast) }
)
.opacity(shouldDisableCreateButton ? 0.2 : 1)
@@ -134,3 +176,9 @@ struct CreateLabelView: View {
}
}
}
+
+extension Color {
+ static var random: Color {
+ Color(hue: .random(in: 0 ... 1), saturation: .random(in: 0.2 ... 0.8), brightness: .random(in: 0.5 ... 0.8))
+ }
+}
From 751095b9834fb489c331b70f82ceb480aa2fc91b Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Tue, 12 Apr 2022 15:28:14 -0700
Subject: [PATCH 080/125] add padding below text chip preview
---
apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift | 1 +
1 file changed, 1 insertion(+)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
index 903726be1..fd2ab65c0 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
@@ -115,6 +115,7 @@ struct CreateLabelView: View {
}
Spacer()
}
+ .padding(.bottom, 8)
TextField("Label Name", text: $newLabelName)
#if os(iOS)
From 57b8b763b071cd7104e95cbfb68be8f6499fd969 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Tue, 12 Apr 2022 16:09:53 -0700
Subject: [PATCH 081/125] run swift gql gen
---
.../Services/DataService/GQLSchema.swift | 1425 ++++++++++++++++-
.../UpdateArticleLabelsPublisher.swift | 2 +-
apple/swiftgraphql.yml | 4 +-
3 files changed, 1401 insertions(+), 30 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift b/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift
index 6f50d6b8b..162b121e0 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift
@@ -5177,7 +5177,6 @@ extension Objects {
struct Highlight {
let __typename: TypeName = .highlight
let annotation: [String: String]
- let article: [String: Objects.Article]
let createdAt: [String: DateTime]
let createdByMe: [String: Bool]
let id: [String: String]
@@ -5214,10 +5213,6 @@ extension Objects.Highlight: Decodable {
if let value = try container.decode(String?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
- case "article":
- if let value = try container.decode(Objects.Article?.self, forKey: codingKey) {
- map.set(key: field, hash: alias, value: value as Any)
- }
case "createdAt":
if let value = try container.decode(DateTime?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
@@ -5281,7 +5276,6 @@ extension Objects.Highlight: Decodable {
}
annotation = map["annotation"]
- article = map["article"]
createdAt = map["createdAt"]
createdByMe = map["createdByMe"]
id = map["id"]
@@ -5354,25 +5348,6 @@ extension Fields where TypeLock == Objects.Highlight {
}
}
- func article(selection: Selection) throws -> Type {
- let field = GraphQLField.composite(
- name: "article",
- arguments: [],
- selection: selection.selection
- )
- select(field)
-
- switch response {
- case let .decoding(data):
- if let data = data.article[field.alias!] {
- return try selection.decode(data: data)
- }
- throw HttpError.badpayload
- case .mocking:
- return selection.mock()
- }
- }
-
func quote() throws -> String {
let field = GraphQLField.leaf(
name: "quote",
@@ -11260,6 +11235,137 @@ extension Selection where TypeLock == Never, Type == Never {
typealias DeleteLabelError = Selection
}
+extension Objects {
+ struct UpdateLabelSuccess {
+ let __typename: TypeName = .updateLabelSuccess
+ let label: [String: Objects.Label]
+
+ enum TypeName: String, Codable {
+ case updateLabelSuccess = "UpdateLabelSuccess"
+ }
+ }
+}
+
+extension Objects.UpdateLabelSuccess: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "label":
+ if let value = try container.decode(Objects.Label?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ label = map["label"]
+ }
+}
+
+extension Fields where TypeLock == Objects.UpdateLabelSuccess {
+ func label(selection: Selection) throws -> Type {
+ let field = GraphQLField.composite(
+ name: "label",
+ arguments: [],
+ selection: selection.selection
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.label[field.alias!] {
+ return try selection.decode(data: data)
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return selection.mock()
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias UpdateLabelSuccess = Selection
+}
+
+extension Objects {
+ struct UpdateLabelError {
+ let __typename: TypeName = .updateLabelError
+ let errorCodes: [String: [Enums.UpdateLabelErrorCode]]
+
+ enum TypeName: String, Codable {
+ case updateLabelError = "UpdateLabelError"
+ }
+ }
+}
+
+extension Objects.UpdateLabelError: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "errorCodes":
+ if let value = try container.decode([Enums.UpdateLabelErrorCode]?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ errorCodes = map["errorCodes"]
+ }
+}
+
+extension Fields where TypeLock == Objects.UpdateLabelError {
+ func errorCodes() throws -> [Enums.UpdateLabelErrorCode] {
+ let field = GraphQLField.leaf(
+ name: "errorCodes",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.errorCodes[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return []
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias UpdateLabelError = Selection
+}
+
extension Objects {
struct SignupSuccess {
let __typename: TypeName = .signupSuccess
@@ -11522,6 +11628,910 @@ extension Selection where TypeLock == Never, Type == Never {
typealias SetLabelsError = Selection
}
+extension Objects {
+ struct GenerateApiKeySuccess {
+ let __typename: TypeName = .generateApiKeySuccess
+ let apiKey: [String: String]
+
+ enum TypeName: String, Codable {
+ case generateApiKeySuccess = "GenerateApiKeySuccess"
+ }
+ }
+}
+
+extension Objects.GenerateApiKeySuccess: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "apiKey":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ apiKey = map["apiKey"]
+ }
+}
+
+extension Fields where TypeLock == Objects.GenerateApiKeySuccess {
+ func apiKey() throws -> String {
+ let field = GraphQLField.leaf(
+ name: "apiKey",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.apiKey[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return String.mockValue
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias GenerateApiKeySuccess = Selection
+}
+
+extension Objects {
+ struct GenerateApiKeyError {
+ let __typename: TypeName = .generateApiKeyError
+ let errorCodes: [String: [Enums.GenerateApiKeyErrorCode]]
+
+ enum TypeName: String, Codable {
+ case generateApiKeyError = "GenerateApiKeyError"
+ }
+ }
+}
+
+extension Objects.GenerateApiKeyError: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "errorCodes":
+ if let value = try container.decode([Enums.GenerateApiKeyErrorCode]?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ errorCodes = map["errorCodes"]
+ }
+}
+
+extension Fields where TypeLock == Objects.GenerateApiKeyError {
+ func errorCodes() throws -> [Enums.GenerateApiKeyErrorCode] {
+ let field = GraphQLField.leaf(
+ name: "errorCodes",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.errorCodes[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return []
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias GenerateApiKeyError = Selection
+}
+
+extension Objects {
+ struct SearchItem {
+ let __typename: TypeName = .searchItem
+ let annotation: [String: String]
+ let author: [String: String]
+ let contentReader: [String: Enums.ContentReader]
+ let createdAt: [String: DateTime]
+ let description: [String: String]
+ let id: [String: String]
+ let image: [String: String]
+ let isArchived: [String: Bool]
+ let labels: [String: [Objects.Label]]
+ let originalArticleUrl: [String: String]
+ let ownedByViewer: [String: Bool]
+ let pageId: [String: String]
+ let pageType: [String: Enums.PageType]
+ let publishedAt: [String: DateTime]
+ let quote: [String: String]
+ let readingProgressAnchorIndex: [String: Int]
+ let readingProgressPercent: [String: Double]
+ let shortId: [String: String]
+ let slug: [String: String]
+ let title: [String: String]
+ let uploadFileId: [String: String]
+ let url: [String: String]
+
+ enum TypeName: String, Codable {
+ case searchItem = "SearchItem"
+ }
+ }
+}
+
+extension Objects.SearchItem: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "annotation":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "author":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "contentReader":
+ if let value = try container.decode(Enums.ContentReader?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "createdAt":
+ if let value = try container.decode(DateTime?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "description":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "id":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "image":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "isArchived":
+ if let value = try container.decode(Bool?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "labels":
+ if let value = try container.decode([Objects.Label]?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "originalArticleUrl":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "ownedByViewer":
+ if let value = try container.decode(Bool?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "pageId":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "pageType":
+ if let value = try container.decode(Enums.PageType?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "publishedAt":
+ if let value = try container.decode(DateTime?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "quote":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "readingProgressAnchorIndex":
+ if let value = try container.decode(Int?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "readingProgressPercent":
+ if let value = try container.decode(Double?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "shortId":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "slug":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "title":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "uploadFileId":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "url":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ annotation = map["annotation"]
+ author = map["author"]
+ contentReader = map["contentReader"]
+ createdAt = map["createdAt"]
+ description = map["description"]
+ id = map["id"]
+ image = map["image"]
+ isArchived = map["isArchived"]
+ labels = map["labels"]
+ originalArticleUrl = map["originalArticleUrl"]
+ ownedByViewer = map["ownedByViewer"]
+ pageId = map["pageId"]
+ pageType = map["pageType"]
+ publishedAt = map["publishedAt"]
+ quote = map["quote"]
+ readingProgressAnchorIndex = map["readingProgressAnchorIndex"]
+ readingProgressPercent = map["readingProgressPercent"]
+ shortId = map["shortId"]
+ slug = map["slug"]
+ title = map["title"]
+ uploadFileId = map["uploadFileId"]
+ url = map["url"]
+ }
+}
+
+extension Fields where TypeLock == Objects.SearchItem {
+ func id() throws -> String {
+ let field = GraphQLField.leaf(
+ name: "id",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.id[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return String.mockValue
+ }
+ }
+
+ func title() throws -> String {
+ let field = GraphQLField.leaf(
+ name: "title",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.title[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return String.mockValue
+ }
+ }
+
+ func slug() throws -> String {
+ let field = GraphQLField.leaf(
+ name: "slug",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.slug[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return String.mockValue
+ }
+ }
+
+ func url() throws -> String {
+ let field = GraphQLField.leaf(
+ name: "url",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.url[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return String.mockValue
+ }
+ }
+
+ func pageType() throws -> Enums.PageType {
+ let field = GraphQLField.leaf(
+ name: "pageType",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.pageType[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return Enums.PageType.allCases.first!
+ }
+ }
+
+ func contentReader() throws -> Enums.ContentReader {
+ let field = GraphQLField.leaf(
+ name: "contentReader",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.contentReader[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return Enums.ContentReader.allCases.first!
+ }
+ }
+
+ func createdAt() throws -> DateTime {
+ let field = GraphQLField.leaf(
+ name: "createdAt",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.createdAt[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return DateTime.mockValue
+ }
+ }
+
+ func isArchived() throws -> Bool {
+ let field = GraphQLField.leaf(
+ name: "isArchived",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.isArchived[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return Bool.mockValue
+ }
+ }
+
+ func readingProgressPercent() throws -> Double? {
+ let field = GraphQLField.leaf(
+ name: "readingProgressPercent",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.readingProgressPercent[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func readingProgressAnchorIndex() throws -> Int? {
+ let field = GraphQLField.leaf(
+ name: "readingProgressAnchorIndex",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.readingProgressAnchorIndex[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func author() throws -> String? {
+ let field = GraphQLField.leaf(
+ name: "author",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.author[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func image() throws -> String? {
+ let field = GraphQLField.leaf(
+ name: "image",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.image[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func description() throws -> String? {
+ let field = GraphQLField.leaf(
+ name: "description",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.description[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func publishedAt() throws -> DateTime? {
+ let field = GraphQLField.leaf(
+ name: "publishedAt",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.publishedAt[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func ownedByViewer() throws -> Bool? {
+ let field = GraphQLField.leaf(
+ name: "ownedByViewer",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.ownedByViewer[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func originalArticleUrl() throws -> String? {
+ let field = GraphQLField.leaf(
+ name: "originalArticleUrl",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.originalArticleUrl[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func uploadFileId() throws -> String? {
+ let field = GraphQLField.leaf(
+ name: "uploadFileId",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.uploadFileId[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func pageId() throws -> String? {
+ let field = GraphQLField.leaf(
+ name: "pageId",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.pageId[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func shortId() throws -> String? {
+ let field = GraphQLField.leaf(
+ name: "shortId",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.shortId[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func quote() throws -> String? {
+ let field = GraphQLField.leaf(
+ name: "quote",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.quote[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func annotation() throws -> String? {
+ let field = GraphQLField.leaf(
+ name: "annotation",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return data.annotation[field.alias!]
+ case .mocking:
+ return nil
+ }
+ }
+
+ func labels(selection: Selection) throws -> Type {
+ let field = GraphQLField.composite(
+ name: "labels",
+ arguments: [],
+ selection: selection.selection
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ return try selection.decode(data: data.labels[field.alias!])
+ case .mocking:
+ return selection.mock()
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias SearchItem = Selection
+}
+
+extension Objects {
+ struct SearchItemEdge {
+ let __typename: TypeName = .searchItemEdge
+ let cursor: [String: String]
+ let node: [String: Objects.SearchItem]
+
+ enum TypeName: String, Codable {
+ case searchItemEdge = "SearchItemEdge"
+ }
+ }
+}
+
+extension Objects.SearchItemEdge: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "cursor":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "node":
+ if let value = try container.decode(Objects.SearchItem?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ cursor = map["cursor"]
+ node = map["node"]
+ }
+}
+
+extension Fields where TypeLock == Objects.SearchItemEdge {
+ func cursor() throws -> String {
+ let field = GraphQLField.leaf(
+ name: "cursor",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.cursor[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return String.mockValue
+ }
+ }
+
+ func node(selection: Selection) throws -> Type {
+ let field = GraphQLField.composite(
+ name: "node",
+ arguments: [],
+ selection: selection.selection
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.node[field.alias!] {
+ return try selection.decode(data: data)
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return selection.mock()
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias SearchItemEdge = Selection
+}
+
+extension Objects {
+ struct SearchSuccess {
+ let __typename: TypeName = .searchSuccess
+ let edges: [String: [Objects.SearchItemEdge]]
+ let pageInfo: [String: Objects.PageInfo]
+
+ enum TypeName: String, Codable {
+ case searchSuccess = "SearchSuccess"
+ }
+ }
+}
+
+extension Objects.SearchSuccess: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "edges":
+ if let value = try container.decode([Objects.SearchItemEdge]?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "pageInfo":
+ if let value = try container.decode(Objects.PageInfo?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ edges = map["edges"]
+ pageInfo = map["pageInfo"]
+ }
+}
+
+extension Fields where TypeLock == Objects.SearchSuccess {
+ func edges(selection: Selection) throws -> Type {
+ let field = GraphQLField.composite(
+ name: "edges",
+ arguments: [],
+ selection: selection.selection
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.edges[field.alias!] {
+ return try selection.decode(data: data)
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return selection.mock()
+ }
+ }
+
+ func pageInfo(selection: Selection) throws -> Type {
+ let field = GraphQLField.composite(
+ name: "pageInfo",
+ arguments: [],
+ selection: selection.selection
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.pageInfo[field.alias!] {
+ return try selection.decode(data: data)
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return selection.mock()
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias SearchSuccess = Selection
+}
+
+extension Objects {
+ struct SearchError {
+ let __typename: TypeName = .searchError
+ let errorCodes: [String: [Enums.SearchErrorCode]]
+
+ enum TypeName: String, Codable {
+ case searchError = "SearchError"
+ }
+ }
+}
+
+extension Objects.SearchError: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "errorCodes":
+ if let value = try container.decode([Enums.SearchErrorCode]?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ errorCodes = map["errorCodes"]
+ }
+}
+
+extension Fields where TypeLock == Objects.SearchError {
+ func errorCodes() throws -> [Enums.SearchErrorCode] {
+ let field = GraphQLField.leaf(
+ name: "errorCodes",
+ arguments: []
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.errorCodes[field.alias!] {
+ return data
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return []
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias SearchError = Selection
+}
+
extension Objects {
struct Mutation {
let __typename: TypeName = .mutation
@@ -11539,6 +12549,7 @@ extension Objects {
let deleteNewsletterEmail: [String: Unions.DeleteNewsletterEmailResult]
let deleteReaction: [String: Unions.DeleteReactionResult]
let deleteReminder: [String: Unions.DeleteReminderResult]
+ let generateApiKey: [String: Unions.GenerateApiKeyResult]
let googleLogin: [String: Unions.LoginResult]
let googleSignup: [String: Unions.GoogleSignupResult]
let logOut: [String: Unions.LogOutResult]
@@ -11560,6 +12571,7 @@ extension Objects {
let signup: [String: Unions.SignupResult]
let updateHighlight: [String: Unions.UpdateHighlightResult]
let updateHighlightReply: [String: Unions.UpdateHighlightReplyResult]
+ let updateLabel: [String: Unions.UpdateLabelResult]
let updateLinkShareInfo: [String: Unions.UpdateLinkShareInfoResult]
let updateReminder: [String: Unions.UpdateReminderResult]
let updateSharedComment: [String: Unions.UpdateSharedCommentResult]
@@ -11641,6 +12653,10 @@ extension Objects.Mutation: Decodable {
if let value = try container.decode(Unions.DeleteReminderResult?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
+ case "generateApiKey":
+ if let value = try container.decode(Unions.GenerateApiKeyResult?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
case "googleLogin":
if let value = try container.decode(Unions.LoginResult?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
@@ -11725,6 +12741,10 @@ extension Objects.Mutation: Decodable {
if let value = try container.decode(Unions.UpdateHighlightReplyResult?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
+ case "updateLabel":
+ if let value = try container.decode(Unions.UpdateLabelResult?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
case "updateLinkShareInfo":
if let value = try container.decode(Unions.UpdateLinkShareInfoResult?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
@@ -11773,6 +12793,7 @@ extension Objects.Mutation: Decodable {
deleteNewsletterEmail = map["deleteNewsletterEmail"]
deleteReaction = map["deleteReaction"]
deleteReminder = map["deleteReminder"]
+ generateApiKey = map["generateApiKey"]
googleLogin = map["googleLogin"]
googleSignup = map["googleSignup"]
logOut = map["logOut"]
@@ -11794,6 +12815,7 @@ extension Objects.Mutation: Decodable {
signup = map["signup"]
updateHighlight = map["updateHighlight"]
updateHighlightReply = map["updateHighlightReply"]
+ updateLabel = map["updateLabel"]
updateLinkShareInfo = map["updateLinkShareInfo"]
updateReminder = map["updateReminder"]
updateSharedComment = map["updateSharedComment"]
@@ -12507,6 +13529,25 @@ extension Fields where TypeLock == Objects.Mutation {
}
}
+ func updateLabel(input: InputObjects.UpdateLabelInput, selection: Selection) throws -> Type {
+ let field = GraphQLField.composite(
+ name: "updateLabel",
+ arguments: [Argument(name: "input", type: "UpdateLabelInput!", value: input)],
+ selection: selection.selection
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.updateLabel[field.alias!] {
+ return try selection.decode(data: data)
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return selection.mock()
+ }
+ }
+
func deleteLabel(id: String, selection: Selection) throws -> Type {
let field = GraphQLField.composite(
name: "deleteLabel",
@@ -12582,6 +13623,25 @@ extension Fields where TypeLock == Objects.Mutation {
return selection.mock()
}
}
+
+ func generateApiKey(scope: OptionalArgument = .absent(), selection: Selection) throws -> Type {
+ let field = GraphQLField.composite(
+ name: "generateApiKey",
+ arguments: [Argument(name: "scope", type: "String", value: scope)],
+ selection: selection.selection
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.generateApiKey[field.alias!] {
+ return try selection.decode(data: data)
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return selection.mock()
+ }
+ }
}
extension Selection where TypeLock == Never, Type == Never {
@@ -12603,6 +13663,7 @@ extension Objects {
let me: [String: Objects.User]
let newsletterEmails: [String: Unions.NewsletterEmailsResult]
let reminder: [String: Unions.ReminderResult]
+ let search: [String: Unions.SearchResult]
let sharedArticle: [String: Unions.SharedArticleResult]
let user: [String: Unions.UserResult]
let users: [String: Unions.UsersResult]
@@ -12674,6 +13735,10 @@ extension Objects.Query: Decodable {
if let value = try container.decode(Unions.ReminderResult?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
+ case "search":
+ if let value = try container.decode(Unions.SearchResult?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
case "sharedArticle":
if let value = try container.decode(Unions.SharedArticleResult?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
@@ -12712,6 +13777,7 @@ extension Objects.Query: Decodable {
me = map["me"]
newsletterEmails = map["newsletterEmails"]
reminder = map["reminder"]
+ search = map["search"]
sharedArticle = map["sharedArticle"]
user = map["user"]
users = map["users"]
@@ -13015,6 +14081,25 @@ extension Fields where TypeLock == Objects.Query {
return selection.mock()
}
}
+
+ func search(after: OptionalArgument = .absent(), first: OptionalArgument = .absent(), query: OptionalArgument = .absent(), selection: Selection) throws -> Type {
+ let field = GraphQLField.composite(
+ name: "search",
+ arguments: [Argument(name: "after", type: "String", value: after), Argument(name: "first", type: "Int", value: first), Argument(name: "query", type: "String", value: query)],
+ selection: selection.selection
+ )
+ select(field)
+
+ switch response {
+ case let .decoding(data):
+ if let data = data.search[field.alias!] {
+ return try selection.decode(data: data)
+ }
+ throw HttpError.badpayload
+ case .mocking:
+ return selection.mock()
+ }
+ }
}
extension Selection where TypeLock == Never, Type == Never {
@@ -16658,6 +17743,80 @@ extension Selection where TypeLock == Never, Type == Never {
typealias DeleteLabelResult = Selection
}
+extension Unions {
+ struct UpdateLabelResult {
+ let __typename: TypeName
+ let errorCodes: [String: [Enums.UpdateLabelErrorCode]]
+ let label: [String: Objects.Label]
+
+ enum TypeName: String, Codable {
+ case updateLabelSuccess = "UpdateLabelSuccess"
+ case updateLabelError = "UpdateLabelError"
+ }
+ }
+}
+
+extension Unions.UpdateLabelResult: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "errorCodes":
+ if let value = try container.decode([Enums.UpdateLabelErrorCode]?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "label":
+ if let value = try container.decode(Objects.Label?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ __typename = try container.decode(TypeName.self, forKey: DynamicCodingKeys(stringValue: "__typename")!)
+
+ errorCodes = map["errorCodes"]
+ label = map["label"]
+ }
+}
+
+extension Fields where TypeLock == Unions.UpdateLabelResult {
+ func on(updateLabelSuccess: Selection, updateLabelError: Selection) throws -> Type {
+ select([GraphQLField.fragment(type: "UpdateLabelSuccess", selection: updateLabelSuccess.selection), GraphQLField.fragment(type: "UpdateLabelError", selection: updateLabelError.selection)])
+
+ switch response {
+ case let .decoding(data):
+ switch data.__typename {
+ case .updateLabelSuccess:
+ let data = Objects.UpdateLabelSuccess(label: data.label)
+ return try updateLabelSuccess.decode(data: data)
+ case .updateLabelError:
+ let data = Objects.UpdateLabelError(errorCodes: data.errorCodes)
+ return try updateLabelError.decode(data: data)
+ }
+ case .mocking:
+ return updateLabelSuccess.mock()
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias UpdateLabelResult = Selection
+}
+
extension Unions {
struct SignupResult {
let __typename: TypeName
@@ -16806,6 +17965,160 @@ extension Selection where TypeLock == Never, Type == Never {
typealias SetLabelsResult = Selection
}
+extension Unions {
+ struct GenerateApiKeyResult {
+ let __typename: TypeName
+ let apiKey: [String: String]
+ let errorCodes: [String: [Enums.GenerateApiKeyErrorCode]]
+
+ enum TypeName: String, Codable {
+ case generateApiKeySuccess = "GenerateApiKeySuccess"
+ case generateApiKeyError = "GenerateApiKeyError"
+ }
+ }
+}
+
+extension Unions.GenerateApiKeyResult: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "apiKey":
+ if let value = try container.decode(String?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "errorCodes":
+ if let value = try container.decode([Enums.GenerateApiKeyErrorCode]?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ __typename = try container.decode(TypeName.self, forKey: DynamicCodingKeys(stringValue: "__typename")!)
+
+ apiKey = map["apiKey"]
+ errorCodes = map["errorCodes"]
+ }
+}
+
+extension Fields where TypeLock == Unions.GenerateApiKeyResult {
+ func on(generateApiKeySuccess: Selection, generateApiKeyError: Selection) throws -> Type {
+ select([GraphQLField.fragment(type: "GenerateApiKeySuccess", selection: generateApiKeySuccess.selection), GraphQLField.fragment(type: "GenerateApiKeyError", selection: generateApiKeyError.selection)])
+
+ switch response {
+ case let .decoding(data):
+ switch data.__typename {
+ case .generateApiKeySuccess:
+ let data = Objects.GenerateApiKeySuccess(apiKey: data.apiKey)
+ return try generateApiKeySuccess.decode(data: data)
+ case .generateApiKeyError:
+ let data = Objects.GenerateApiKeyError(errorCodes: data.errorCodes)
+ return try generateApiKeyError.decode(data: data)
+ }
+ case .mocking:
+ return generateApiKeySuccess.mock()
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias GenerateApiKeyResult = Selection
+}
+
+extension Unions {
+ struct SearchResult {
+ let __typename: TypeName
+ let edges: [String: [Objects.SearchItemEdge]]
+ let errorCodes: [String: [Enums.SearchErrorCode]]
+ let pageInfo: [String: Objects.PageInfo]
+
+ enum TypeName: String, Codable {
+ case searchSuccess = "SearchSuccess"
+ case searchError = "SearchError"
+ }
+ }
+}
+
+extension Unions.SearchResult: Decodable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
+
+ var map = HashMap()
+ for codingKey in container.allKeys {
+ if codingKey.isTypenameKey { continue }
+
+ let alias = codingKey.stringValue
+ let field = GraphQLField.getFieldNameFromAlias(alias)
+
+ switch field {
+ case "edges":
+ if let value = try container.decode([Objects.SearchItemEdge]?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "errorCodes":
+ if let value = try container.decode([Enums.SearchErrorCode]?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ case "pageInfo":
+ if let value = try container.decode(Objects.PageInfo?.self, forKey: codingKey) {
+ map.set(key: field, hash: alias, value: value as Any)
+ }
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: decoder.codingPath,
+ debugDescription: "Unknown key \(field)."
+ )
+ )
+ }
+ }
+
+ __typename = try container.decode(TypeName.self, forKey: DynamicCodingKeys(stringValue: "__typename")!)
+
+ edges = map["edges"]
+ errorCodes = map["errorCodes"]
+ pageInfo = map["pageInfo"]
+ }
+}
+
+extension Fields where TypeLock == Unions.SearchResult {
+ func on(searchSuccess: Selection, searchError: Selection) throws -> Type {
+ select([GraphQLField.fragment(type: "SearchSuccess", selection: searchSuccess.selection), GraphQLField.fragment(type: "SearchError", selection: searchError.selection)])
+
+ switch response {
+ case let .decoding(data):
+ switch data.__typename {
+ case .searchSuccess:
+ let data = Objects.SearchSuccess(edges: data.edges, pageInfo: data.pageInfo)
+ return try searchSuccess.decode(data: data)
+ case .searchError:
+ let data = Objects.SearchError(errorCodes: data.errorCodes)
+ return try searchError.decode(data: data)
+ }
+ case .mocking:
+ return searchSuccess.mock()
+ }
+ }
+}
+
+extension Selection where TypeLock == Never, Type == Never {
+ typealias SearchResult = Selection
+}
+
// MARK: - Enums
enum Enums {}
@@ -16957,6 +18270,8 @@ extension Enums {
case website = "WEBSITE"
+ case highlights = "HIGHLIGHTS"
+
case unknown = "UNKNOWN"
}
}
@@ -17421,6 +18736,19 @@ extension Enums {
}
}
+extension Enums {
+ /// UpdateLabelErrorCode
+ enum UpdateLabelErrorCode: String, CaseIterable, Codable {
+ case unauthorized = "UNAUTHORIZED"
+
+ case badRequest = "BAD_REQUEST"
+
+ case notFound = "NOT_FOUND"
+
+ case forbidden = "FORBIDDEN"
+ }
+}
+
extension Enums {
/// SetLabelsErrorCode
enum SetLabelsErrorCode: String, CaseIterable, Codable {
@@ -17432,6 +18760,20 @@ extension Enums {
}
}
+extension Enums {
+ /// GenerateApiKeyErrorCode
+ enum GenerateApiKeyErrorCode: String, CaseIterable, Codable {
+ case badRequest = "BAD_REQUEST"
+ }
+}
+
+extension Enums {
+ /// SearchErrorCode
+ enum SearchErrorCode: String, CaseIterable, Codable {
+ case unauthorized = "UNAUTHORIZED"
+ }
+}
+
// MARK: - Input Objects
enum InputObjects {}
@@ -18293,6 +19635,33 @@ extension InputObjects {
}
}
+extension InputObjects {
+ struct UpdateLabelInput: Encodable, Hashable {
+ var labelId: String
+
+ var color: String
+
+ var description: OptionalArgument = .absent()
+
+ var name: String
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(labelId, forKey: .labelId)
+ try container.encode(color, forKey: .color)
+ if description.hasValue { try container.encode(description, forKey: .description) }
+ try container.encode(name, forKey: .name)
+ }
+
+ enum CodingKeys: String, CodingKey {
+ case labelId
+ case color
+ case description
+ case name
+ }
+ }
+}
+
extension InputObjects {
struct LoginInput: Encodable, Hashable {
var password: String
@@ -18349,18 +19718,18 @@ extension InputObjects {
extension InputObjects {
struct SetLabelsInput: Encodable, Hashable {
- var linkId: String
+ var pageId: String
var labelIds: [String]
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
- try container.encode(linkId, forKey: .linkId)
+ try container.encode(pageId, forKey: .pageId)
try container.encode(labelIds, forKey: .labelIds)
}
enum CodingKeys: String, CodingKey {
- case linkId
+ case pageId
case labelIds
}
}
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/UpdateArticleLabelsPublisher.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/UpdateArticleLabelsPublisher.swift
index fce103d6a..0809b693a 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/UpdateArticleLabelsPublisher.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/UpdateArticleLabelsPublisher.swift
@@ -20,7 +20,7 @@ public extension DataService {
let mutation = Selection.Mutation {
try $0.setLabels(
input: InputObjects.SetLabelsInput(
- linkId: itemID,
+ pageId: itemID,
labelIds: labelIDs
),
selection: selection
diff --git a/apple/swiftgraphql.yml b/apple/swiftgraphql.yml
index 9a2b8bd2b..dd96fa3fc 100644
--- a/apple/swiftgraphql.yml
+++ b/apple/swiftgraphql.yml
@@ -4,10 +4,12 @@ scalars:
SanitizedString_undefined_15: String
SanitizedString_undefined_40: String
SanitizedString_undefined_50: String
+ SanitizedString_undefined_64: String
SanitizedString_undefined_95: String
+ SanitizedString_undefined_100: String
SanitizedString_undefined_300: String
SanitizedString_undefined_400: String
SanitizedString_undefined_2000: String
SanitizedString_undefined_4000: String
SanitizedString_undefined_8000: String
- SanitizedString_undefined_undefined: String
\ No newline at end of file
+ SanitizedString_undefined_undefined: String
From d92b526583117d9ca7d115ae4dabe2d9c2c7a2c7 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Tue, 12 Apr 2022 18:09:43 -0700
Subject: [PATCH 082/125] Give the labels control a max height
---
packages/web/components/templates/article/EditLabelsControl.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index 77f56a526..ead74eb03 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -300,7 +300,7 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
css={{
p: '0',
width: '100%',
- maxHeight: '80%',
+ maxHeight: '380px',
}}>
Date: Tue, 12 Apr 2022 18:10:43 -0700
Subject: [PATCH 083/125] Fix tooltip tags -> labels
---
.../web/components/templates/article/ArticleActionsMenu.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 410ae3080..64aa0b20c 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -95,7 +95,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
layout={props.layout}
triggerElement={
From 11429dddc060d00c6539ae319ff7d0f494480075 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Tue, 12 Apr 2022 20:08:18 -0700
Subject: [PATCH 084/125] Dont autofocus the text input on mobile
On mobile we dont want to auto focus because the keyboard opens
and takes up most of the screen space.
---
.../web/components/templates/article/EditLabelsControl.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index ead74eb03..aa62b492e 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -50,14 +50,14 @@ function Header(props: HeaderProps): JSX.Element {
const inputRef = useRef(null)
useEffect(() => {
- if (props.focused && inputRef.current) {
+ if (!isTouchScreenDevice() && props.focused && inputRef.current) {
inputRef.current.focus()
}
}, [props.focused])
return (
-
Date: Tue, 12 Apr 2022 20:09:03 -0700
Subject: [PATCH 085/125] Set the link colour. Safari needs this
---
packages/web/components/templates/article/EditLabelsControl.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index aa62b492e..96e297242 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -167,6 +167,7 @@ function EditLabelsButtonFooter(props: EditLabelsButtonFooterProps): JSX.Element
css={{
width: '100%', height: '42px',
bg: props.focused ? '$grayBgActive' : 'unset',
+ color: theme.colors.grayText.toString(),
'a:link': {
textDecoration: 'none',
},
From a728bcf88699006d00a967691b20d0149d575b5c Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Tue, 12 Apr 2022 20:36:00 -0700
Subject: [PATCH 086/125] Improve sizing of scrollable content area on mobile
---
.../web/components/templates/article/EditLabelsControl.tsx | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/EditLabelsControl.tsx
index 96e297242..6eff4bacb 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/EditLabelsControl.tsx
@@ -301,14 +301,17 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
css={{
p: '0',
width: '100%',
- maxHeight: '380px',
}}>
setFocusedIndex(undefined)}
setFilterText={setFilterText} filterText={filterText}
/>
-
+
{filteredLabels &&
filteredLabels.map((label, idx) => (
Date: Tue, 12 Apr 2022 20:50:06 -0700
Subject: [PATCH 087/125] Update to use pageId instead of linkId for SetLabels
API
---
packages/web/lib/networking/mutations/setLabelsMutation.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/web/lib/networking/mutations/setLabelsMutation.ts b/packages/web/lib/networking/mutations/setLabelsMutation.ts
index a7d3aa0bb..6953bcc45 100644
--- a/packages/web/lib/networking/mutations/setLabelsMutation.ts
+++ b/packages/web/lib/networking/mutations/setLabelsMutation.ts
@@ -12,7 +12,7 @@ type SetLabels = {
}
export async function setLabelsMutation(
- linkId: string,
+ pageId: string,
labelIds: string[]
): Promise {
const mutation = gql`
@@ -32,7 +32,7 @@ export async function setLabelsMutation(
`
try {
- const data = await gqlFetcher(mutation, { input: { linkId, labelIds } }) as SetLabelsResult
+ const data = await gqlFetcher(mutation, { input: { pageId, labelIds } }) as SetLabelsResult
return data.errorCodes ? undefined : data.setLabels.labels
} catch (error) {
console.log('SetLabelsOutput error', error)
From 928ddd69a94089ec6983783799f5695185161e18 Mon Sep 17 00:00:00 2001
From: Hongbo Wu
Date: Wed, 13 Apr 2022 12:17:12 +0800
Subject: [PATCH 088/125] validate color input in label as rgb hex value
---
packages/api/src/directives.ts | 13 ++++++++++---
packages/api/src/generated/graphql.ts | 1 +
packages/api/src/generated/schema.graphql | 2 +-
packages/api/src/scalars.ts | 9 +++++++--
packages/api/src/schema.ts | 3 ++-
5 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/packages/api/src/directives.ts b/packages/api/src/directives.ts
index fd54cbb66..b4a32bf5e 100644
--- a/packages/api/src/directives.ts
+++ b/packages/api/src/directives.ts
@@ -1,4 +1,4 @@
-import { mapSchema, getDirective, MapperKind } from '@graphql-tools/utils'
+import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils'
import { GraphQLNonNull, GraphQLScalarType, GraphQLSchema } from 'graphql'
import { SanitizedString } from './scalars'
@@ -14,19 +14,26 @@ export const sanitizeDirectiveTransformer = (schema: GraphQLSchema) => {
const allowedTags = sanitizeDirective[0].allowedTags as
| string[]
| undefined
+ const pattern = sanitizeDirective[0].pattern as RegExp | undefined
if (
fieldConfig.type instanceof GraphQLNonNull &&
fieldConfig.type.ofType instanceof GraphQLScalarType
) {
fieldConfig.type = new GraphQLNonNull(
- new SanitizedString(fieldConfig.type.ofType, allowedTags, maxLength)
+ new SanitizedString(
+ fieldConfig.type.ofType,
+ allowedTags,
+ maxLength,
+ pattern
+ )
)
} else if (fieldConfig.type instanceof GraphQLScalarType) {
fieldConfig.type = new SanitizedString(
fieldConfig.type,
allowedTags,
- maxLength
+ maxLength,
+ pattern
)
} else {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
diff --git a/packages/api/src/generated/graphql.ts b/packages/api/src/generated/graphql.ts
index d14428b39..d1e733acc 100644
--- a/packages/api/src/generated/graphql.ts
+++ b/packages/api/src/generated/graphql.ts
@@ -2501,6 +2501,7 @@ export type ResolversParentTypes = {
export type SanitizeDirectiveArgs = {
allowedTags?: Maybe>>;
maxLength?: Maybe;
+ pattern?: Maybe;
};
export type SanitizeDirectiveResolver = DirectiveResolverFn;
diff --git a/packages/api/src/generated/schema.graphql b/packages/api/src/generated/schema.graphql
index e3c5630a8..2f77183e1 100644
--- a/packages/api/src/generated/schema.graphql
+++ b/packages/api/src/generated/schema.graphql
@@ -1,4 +1,4 @@
-directive @sanitize(allowedTags: [String], maxLength: Int) on INPUT_FIELD_DEFINITION
+directive @sanitize(allowedTags: [String], maxLength: Int, pattern: String) on INPUT_FIELD_DEFINITION
type ArchiveLinkError {
errorCodes: [ArchiveLinkErrorCode!]!
diff --git a/packages/api/src/scalars.ts b/packages/api/src/scalars.ts
index 2be928e6e..454b7c38e 100644
--- a/packages/api/src/scalars.ts
+++ b/packages/api/src/scalars.ts
@@ -8,10 +8,11 @@ export class SanitizedString extends GraphQLScalarType {
constructor(
type: GraphQLScalarType,
allowedTags?: string[],
- maxLength?: number
+ maxLength?: number,
+ pattern?: RegExp
) {
super({
- name: `SanitizedString_${allowedTags}_${maxLength}`,
+ name: `SanitizedString_${allowedTags}_${maxLength}_${pattern}`,
description: 'Source string that was sanitized',
serialize(value: string) {
@@ -24,6 +25,8 @@ export class SanitizedString extends GraphQLScalarType {
throw new Error(
`Specified value cannot be longer than ${maxLength} characters`
)
+ } else if (pattern && !pattern.test(value)) {
+ throw new Error(`Specified value does not match pattern`)
}
return sanitize(value, { allowedTags: allowedTags || [] })
},
@@ -35,6 +38,8 @@ export class SanitizedString extends GraphQLScalarType {
throw new Error(
`Specified value cannot be longer than ${maxLength} characters`
)
+ } else if (pattern && !pattern.test(value)) {
+ throw new Error(`Specified value does not match pattern`)
}
return sanitize(value, { allowedTags: allowedTags || [] })
},
diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts
index 43fa75719..08def9c86 100755
--- a/packages/api/src/schema.ts
+++ b/packages/api/src/schema.ts
@@ -8,6 +8,7 @@ const schema = gql`
directive @sanitize(
allowedTags: [String]
maxLength: Int
+ pattern: String
) on INPUT_FIELD_DEFINITION
enum SortOrder {
@@ -1273,7 +1274,7 @@ const schema = gql`
input CreateLabelInput {
name: String! @sanitize(maxLength: 64)
- color: String!
+ color: String! @sanitize(pattern: "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$")
description: String @sanitize(maxLength: 100)
}
From 3b831fecb80d6c9bb2fee6012ea7299a192064c2 Mon Sep 17 00:00:00 2001
From: Hongbo Wu
Date: Wed, 13 Apr 2022 12:20:41 +0800
Subject: [PATCH 089/125] test both max length and pattern if declared
---
packages/api/src/scalars.ts | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/packages/api/src/scalars.ts b/packages/api/src/scalars.ts
index 454b7c38e..2f0de333a 100644
--- a/packages/api/src/scalars.ts
+++ b/packages/api/src/scalars.ts
@@ -25,7 +25,8 @@ export class SanitizedString extends GraphQLScalarType {
throw new Error(
`Specified value cannot be longer than ${maxLength} characters`
)
- } else if (pattern && !pattern.test(value)) {
+ }
+ if (pattern && !pattern.test(value)) {
throw new Error(`Specified value does not match pattern`)
}
return sanitize(value, { allowedTags: allowedTags || [] })
@@ -38,7 +39,8 @@ export class SanitizedString extends GraphQLScalarType {
throw new Error(
`Specified value cannot be longer than ${maxLength} characters`
)
- } else if (pattern && !pattern.test(value)) {
+ }
+ if (pattern && !pattern.test(value)) {
throw new Error(`Specified value does not match pattern`)
}
return sanitize(value, { allowedTags: allowedTags || [] })
From 5a4e4baae6e6a71163cced7b0314eb08bb036f97 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Tue, 12 Apr 2022 21:49:48 -0700
Subject: [PATCH 090/125] Handle changes to createLabel response. Trim label
names
---
packages/web/pages/settings/labels.tsx | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/packages/web/pages/settings/labels.tsx b/packages/web/pages/settings/labels.tsx
index 2c144f55a..d03e5eec9 100644
--- a/packages/web/pages/settings/labels.tsx
+++ b/packages/web/pages/settings/labels.tsx
@@ -183,18 +183,14 @@ export default function LabelsPage(): JSX.Element {
async function createLabel(): Promise {
const res = await createLabelMutation(
- nameInputText,
+ nameInputText.trim(),
labelColorHex.value,
descriptionInputText
)
if (res) {
- if (res.createLabel.errorCodes && res.createLabel.errorCodes.length > 0) {
- showErrorToast(res.createLabel.errorCodes[0])
- } else {
- showSuccessToast('Label created', { position: 'bottom-right' })
- resetLabelState()
- revalidate()
- }
+ showSuccessToast('Label created', { position: 'bottom-right' })
+ resetLabelState()
+ revalidate()
} else {
showErrorToast('Failed to create label')
}
From 8ba1be456c7aa7cd130f2f3c9bd2382c435f4c85 Mon Sep 17 00:00:00 2001
From: Hongbo Wu
Date: Wed, 13 Apr 2022 13:23:21 +0800
Subject: [PATCH 091/125] fix sanitizer invalid name
---
packages/api/src/directives.ts | 16 +++++++++-------
packages/api/src/scalars.ts | 12 ++++++++----
2 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/packages/api/src/directives.ts b/packages/api/src/directives.ts
index b4a32bf5e..435166539 100644
--- a/packages/api/src/directives.ts
+++ b/packages/api/src/directives.ts
@@ -5,16 +5,18 @@ import { SanitizedString } from './scalars'
export const sanitizeDirectiveTransformer = (schema: GraphQLSchema) => {
return mapSchema(schema, {
[MapperKind.FIELD]: (fieldConfig) => {
- const sanitizeDirective = getDirective(schema, fieldConfig, 'sanitize')
- if (!sanitizeDirective || sanitizeDirective.length < 1) {
+ const sanitizeDirective = getDirective(
+ schema,
+ fieldConfig,
+ 'sanitize'
+ )?.[0]
+ if (!sanitizeDirective) {
return fieldConfig
}
- const maxLength = sanitizeDirective[0].maxLength as number | undefined
- const allowedTags = sanitizeDirective[0].allowedTags as
- | string[]
- | undefined
- const pattern = sanitizeDirective[0].pattern as RegExp | undefined
+ const maxLength = sanitizeDirective.maxLength as number | undefined
+ const allowedTags = sanitizeDirective.allowedTags as string[] | undefined
+ const pattern = sanitizeDirective.pattern as string | undefined
if (
fieldConfig.type instanceof GraphQLNonNull &&
diff --git a/packages/api/src/scalars.ts b/packages/api/src/scalars.ts
index 2f0de333a..22404534f 100644
--- a/packages/api/src/scalars.ts
+++ b/packages/api/src/scalars.ts
@@ -9,10 +9,14 @@ export class SanitizedString extends GraphQLScalarType {
type: GraphQLScalarType,
allowedTags?: string[],
maxLength?: number,
- pattern?: RegExp
+ pattern?: string
) {
super({
- name: `SanitizedString_${allowedTags}_${maxLength}_${pattern}`,
+ // Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ as per graphql-js
+ name: `SanitizedString_${allowedTags}_${maxLength}_${pattern}`.replace(
+ /\W/g,
+ ''
+ ),
description: 'Source string that was sanitized',
serialize(value: string) {
@@ -26,7 +30,7 @@ export class SanitizedString extends GraphQLScalarType {
`Specified value cannot be longer than ${maxLength} characters`
)
}
- if (pattern && !pattern.test(value)) {
+ if (pattern && !new RegExp(pattern).test(value)) {
throw new Error(`Specified value does not match pattern`)
}
return sanitize(value, { allowedTags: allowedTags || [] })
@@ -40,7 +44,7 @@ export class SanitizedString extends GraphQLScalarType {
`Specified value cannot be longer than ${maxLength} characters`
)
}
- if (pattern && !pattern.test(value)) {
+ if (pattern && !new RegExp(pattern).test(value)) {
throw new Error(`Specified value does not match pattern`)
}
return sanitize(value, { allowedTags: allowedTags || [] })
From a806f55233e220ffbd70fb3fa36f655ea0f38b8a Mon Sep 17 00:00:00 2001
From: Hongbo Wu
Date: Wed, 13 Apr 2022 21:18:58 +0800
Subject: [PATCH 092/125] upload highlight to gcs
---
packages/api/src/datalayer/pubsub.ts | 26 ++++++++++++++++++-
packages/api/src/elastic/highlights.ts | 18 ++++++++++---
.../src/routers/svc/{pages.ts => upload.ts} | 7 +++--
packages/api/src/server.ts | 4 +--
4 files changed, 45 insertions(+), 10 deletions(-)
rename packages/api/src/routers/svc/{pages.ts => upload.ts} (89%)
diff --git a/packages/api/src/datalayer/pubsub.ts b/packages/api/src/datalayer/pubsub.ts
index 690e9376b..ba98f2952 100644
--- a/packages/api/src/datalayer/pubsub.ts
+++ b/packages/api/src/datalayer/pubsub.ts
@@ -2,7 +2,7 @@ import { PubSub } from '@google-cloud/pubsub'
import { env } from '../env'
import { ReportType } from '../generated/graphql'
import express from 'express'
-import { Page } from '../elastic/types'
+import { Highlight, Page } from '../elastic/types'
export const createPubSubClient = (): PubsubClient => {
const client = new PubSub()
@@ -49,6 +49,24 @@ export const createPubSubClient = (): PubsubClient => {
pageDeleted: (id: string, userId: string): Promise => {
return publish('pageDeleted', Buffer.from(JSON.stringify({ id, userId })))
},
+ highlightCreated: (highlight: Highlight): Promise => {
+ return publish('highlightCreated', Buffer.from(JSON.stringify(highlight)))
+ },
+ highlightUpdated: (
+ highlight: Partial,
+ userId: string
+ ): Promise => {
+ return publish(
+ 'highlightUpdated',
+ Buffer.from(JSON.stringify({ ...highlight, userId }))
+ )
+ },
+ highlightDeleted: (id: string, userId: string): Promise => {
+ return publish(
+ 'highlightDeleted',
+ Buffer.from(JSON.stringify({ id, userId }))
+ )
+ },
reportSubmitted: (
submitterId: string,
itemUrl: string,
@@ -75,6 +93,12 @@ export interface PubsubClient {
pageCreated: (page: Page) => Promise
pageUpdated: (page: Partial, userId: string) => Promise
pageDeleted: (id: string, userId: string) => Promise
+ highlightCreated: (highlight: Highlight) => Promise
+ highlightUpdated: (
+ highlight: Partial,
+ userId: string
+ ) => Promise
+ highlightDeleted: (id: string, userId: string) => Promise
reportSubmitted(
submitterId: string | undefined,
itemUrl: string,
diff --git a/packages/api/src/elastic/highlights.ts b/packages/api/src/elastic/highlights.ts
index 5a499237e..7bd6a6adf 100644
--- a/packages/api/src/elastic/highlights.ts
+++ b/packages/api/src/elastic/highlights.ts
@@ -35,7 +35,11 @@ export const addHighlightToPage = async (
retry_on_conflict: 3,
})
- return body.result === 'updated'
+ if (body.result !== 'updated') return false
+
+ await ctx.pubsub.highlightCreated(highlight)
+
+ return true
} catch (e) {
if (
e instanceof ResponseError &&
@@ -125,7 +129,11 @@ export const deleteHighlight = async (
refresh: ctx.refresh,
})
- return !!body.updated
+ if (body.result !== 'updated') return false
+
+ await ctx.pubsub.highlightDeleted(highlightId, ctx.uid)
+
+ return true
} catch (e) {
console.error('failed to delete a highlight in elastic', e)
@@ -266,7 +274,11 @@ export const updateHighlight = async (
refresh: ctx.refresh,
})
- return !!body.updated
+ if (body.result !== 'updated') return false
+
+ await ctx.pubsub.highlightUpdated(highlight, ctx.uid)
+
+ return true
} catch (e) {
if (
e instanceof ResponseError &&
diff --git a/packages/api/src/routers/svc/pages.ts b/packages/api/src/routers/svc/upload.ts
similarity index 89%
rename from packages/api/src/routers/svc/pages.ts
rename to packages/api/src/routers/svc/upload.ts
index d1e139094..b26be4cad 100644
--- a/packages/api/src/routers/svc/pages.ts
+++ b/packages/api/src/routers/svc/upload.ts
@@ -6,14 +6,13 @@ import { readPushSubscription } from '../../datalayer/pubsub'
import { generateUploadSignedUrl, uploadToSignedUrl } from '../../utils/uploads'
import { v4 as uuidv4 } from 'uuid'
import { env } from '../../env'
-import { Page } from '../../elastic/types'
import { DateTime } from 'luxon'
-export function pageServiceRouter() {
+export function uploadServiceRouter() {
const router = express.Router()
router.post('/upload/:folder', async (req, res) => {
- console.log('upload page data req', req.params.folder)
+ console.log('upload data req', req.params.folder)
const { message: msgStr, expired } = readPushSubscription(req)
if (!msgStr) {
@@ -28,7 +27,7 @@ export function pageServiceRouter() {
}
try {
- const data: Partial = JSON.parse(msgStr)
+ const data: { userId: string } = JSON.parse(msgStr)
if (!data.userId) {
console.log('No userId found in message')
res.status(400).send('Bad Request')
diff --git a/packages/api/src/server.ts b/packages/api/src/server.ts
index 27967ce5a..df70f693e 100755
--- a/packages/api/src/server.ts
+++ b/packages/api/src/server.ts
@@ -40,7 +40,7 @@ import { ApolloServer } from 'apollo-server-express'
import { pdfAttachmentsRouter } from './routers/svc/pdf_attachments'
import { corsConfig } from './utils/corsConfig'
import { initElasticsearch } from './elastic'
-import { pageServiceRouter } from './routers/svc/pages'
+import { uploadServiceRouter } from './routers/svc/upload'
const PORT = process.env.PORT || 4000
@@ -98,7 +98,7 @@ export const createApp = (): {
app.use('/svc/pubsub/links', linkServiceRouter())
app.use('/svc/pubsub/newsletters', newsletterServiceRouter())
app.use('/svc/pubsub/emails', emailsServiceRouter())
- app.use('/svc/pubsub/pages', pageServiceRouter())
+ app.use('/svc/pubsub/upload', uploadServiceRouter())
app.use('/svc/reminders', remindersServiceRouter())
app.use('/svc/pdf-attachments', pdfAttachmentsRouter())
From 0b0b521fabc633c549e3aabd8e280c9c93294466 Mon Sep 17 00:00:00 2001
From: Hongbo Wu
Date: Wed, 13 Apr 2022 22:21:20 +0800
Subject: [PATCH 093/125] use generic method for uploading
---
packages/api/src/datalayer/pubsub.ts | 63 ++++++++++++-------------
packages/api/src/elastic/highlights.ts | 15 ++++--
packages/api/src/elastic/labels.ts | 26 ++++++++--
packages/api/src/elastic/pages.ts | 11 +++--
packages/api/src/routers/svc/upload.ts | 16 +++----
packages/api/test/routers/pages.test.ts | 6 +--
6 files changed, 83 insertions(+), 54 deletions(-)
diff --git a/packages/api/src/datalayer/pubsub.ts b/packages/api/src/datalayer/pubsub.ts
index ba98f2952..e4f397acd 100644
--- a/packages/api/src/datalayer/pubsub.ts
+++ b/packages/api/src/datalayer/pubsub.ts
@@ -2,7 +2,6 @@ import { PubSub } from '@google-cloud/pubsub'
import { env } from '../env'
import { ReportType } from '../generated/graphql'
import express from 'express'
-import { Highlight, Page } from '../elastic/types'
export const createPubSubClient = (): PubsubClient => {
const client = new PubSub()
@@ -37,34 +36,34 @@ export const createPubSubClient = (): PubsubClient => {
Buffer.from(JSON.stringify({ userId, email, name, username }))
)
},
- pageUpdated: (page: Partial, userId: string): Promise => {
- return publish(
- 'pageUpdated',
- Buffer.from(JSON.stringify({ ...page, userId }))
- )
- },
- pageCreated: (page: Page): Promise => {
- return publish('pageCreated', Buffer.from(JSON.stringify(page)))
- },
- pageDeleted: (id: string, userId: string): Promise => {
- return publish('pageDeleted', Buffer.from(JSON.stringify({ id, userId })))
- },
- highlightCreated: (highlight: Highlight): Promise => {
- return publish('highlightCreated', Buffer.from(JSON.stringify(highlight)))
- },
- highlightUpdated: (
- highlight: Partial,
+ entityCreated: (
+ type: EntityType,
+ data: T,
userId: string
): Promise => {
return publish(
- 'highlightUpdated',
- Buffer.from(JSON.stringify({ ...highlight, userId }))
+ 'entityCreated',
+ Buffer.from(JSON.stringify({ type, userId, ...data }))
)
},
- highlightDeleted: (id: string, userId: string): Promise => {
+ entityUpdated: (
+ type: EntityType,
+ data: T,
+ userId: string
+ ): Promise => {
return publish(
- 'highlightDeleted',
- Buffer.from(JSON.stringify({ id, userId }))
+ 'entityUpdated',
+ Buffer.from(JSON.stringify({ type, userId, ...data }))
+ )
+ },
+ entityDeleted: (
+ type: EntityType,
+ id: string,
+ userId: string
+ ): Promise => {
+ return publish(
+ 'entityDeleted',
+ Buffer.from(JSON.stringify({ type, id, userId }))
)
},
reportSubmitted: (
@@ -83,6 +82,12 @@ export const createPubSubClient = (): PubsubClient => {
}
}
+export enum EntityType {
+ PAGE = 'page',
+ HIGHLIGHT = 'highlight',
+ LABEL = 'label',
+}
+
export interface PubsubClient {
userCreated: (
userId: string,
@@ -90,15 +95,9 @@ export interface PubsubClient {
name: string,
username: string
) => Promise
- pageCreated: (page: Page) => Promise
- pageUpdated: (page: Partial, userId: string) => Promise
- pageDeleted: (id: string, userId: string) => Promise
- highlightCreated: (highlight: Highlight) => Promise
- highlightUpdated: (
- highlight: Partial,
- userId: string
- ) => Promise
- highlightDeleted: (id: string, userId: string) => Promise
+ entityCreated: (type: EntityType, data: T, userId: string) => Promise
+ entityUpdated: (type: EntityType, data: T, userId: string) => Promise
+ entityDeleted: (type: EntityType, id: string, userId: string) => Promise
reportSubmitted(
submitterId: string | undefined,
itemUrl: string,
diff --git a/packages/api/src/elastic/highlights.ts b/packages/api/src/elastic/highlights.ts
index 7bd6a6adf..934563ca3 100644
--- a/packages/api/src/elastic/highlights.ts
+++ b/packages/api/src/elastic/highlights.ts
@@ -8,6 +8,7 @@ import {
import { ResponseError } from '@elastic/elasticsearch/lib/errors'
import { client, INDEX_ALIAS } from './index'
import { SortBy, SortOrder, SortParams } from '../utils/search'
+import { EntityType } from '../datalayer/pubsub'
export const addHighlightToPage = async (
id: string,
@@ -37,7 +38,11 @@ export const addHighlightToPage = async (
if (body.result !== 'updated') return false
- await ctx.pubsub.highlightCreated(highlight)
+ await ctx.pubsub.entityCreated(
+ EntityType.HIGHLIGHT,
+ highlight,
+ ctx.uid
+ )
return true
} catch (e) {
@@ -131,7 +136,7 @@ export const deleteHighlight = async (
if (body.result !== 'updated') return false
- await ctx.pubsub.highlightDeleted(highlightId, ctx.uid)
+ await ctx.pubsub.entityDeleted(EntityType.HIGHLIGHT, highlightId, ctx.uid)
return true
} catch (e) {
@@ -276,7 +281,11 @@ export const updateHighlight = async (
if (body.result !== 'updated') return false
- await ctx.pubsub.highlightUpdated(highlight, ctx.uid)
+ await ctx.pubsub.entityUpdated(
+ EntityType.HIGHLIGHT,
+ highlight,
+ ctx.uid
+ )
return true
} catch (e) {
diff --git a/packages/api/src/elastic/labels.ts b/packages/api/src/elastic/labels.ts
index ec1ac5188..2bb00bba8 100644
--- a/packages/api/src/elastic/labels.ts
+++ b/packages/api/src/elastic/labels.ts
@@ -1,5 +1,6 @@
import { Label, PageContext } from './types'
import { client, INDEX_ALIAS } from './index'
+import { EntityType } from '../datalayer/pubsub'
export const addLabelInPage = async (
id: string,
@@ -27,9 +28,17 @@ export const addLabelInPage = async (
retry_on_conflict: 3,
})
- return body.result === 'updated'
+ if (body.result !== 'updated') return false
+
+ await ctx.pubsub.entityCreated(
+ EntityType.LABEL,
+ { pageId: id, ...label },
+ ctx.uid
+ )
+
+ return true
} catch (e) {
- console.error('failed to update a page in elastic', e)
+ console.error('failed to add a label in elastic', e)
return false
}
}
@@ -38,9 +47,9 @@ export const deleteLabelInPages = async (
userId: string,
label: string,
ctx: PageContext
-): Promise => {
+): Promise => {
try {
- await client.updateByQuery({
+ const { body } = await client.updateByQuery({
index: INDEX_ALIAS,
body: {
script: {
@@ -75,7 +84,14 @@ export const deleteLabelInPages = async (
},
refresh: ctx.refresh,
})
+
+ if (body.result !== 'updated') return false
+
+ await ctx.pubsub.entityDeleted(EntityType.LABEL, label, ctx.uid)
+
+ return true
} catch (e) {
- console.error('failed to delete a page in elastic', e)
+ console.error('failed to delete a label in elastic', e)
+ return false
}
}
diff --git a/packages/api/src/elastic/pages.ts b/packages/api/src/elastic/pages.ts
index 96f1b5d4a..658abffc7 100644
--- a/packages/api/src/elastic/pages.ts
+++ b/packages/api/src/elastic/pages.ts
@@ -18,6 +18,7 @@ import {
SortParams,
} from '../utils/search'
import { client, INDEX_ALIAS } from './index'
+import { EntityType } from '../datalayer/pubsub'
const appendQuery = (body: SearchBody, query: string): void => {
body.query.bool.should.push({
@@ -189,7 +190,7 @@ export const createPage = async (
refresh: ctx.refresh,
})
- await ctx.pubsub.pageCreated(page)
+ await ctx.pubsub.entityCreated(EntityType.PAGE, page, ctx.uid)
return body._id as string
} catch (e) {
@@ -219,7 +220,11 @@ export const updatePage = async (
if (body.result !== 'updated') return false
- await ctx.pubsub.pageUpdated({ ...page, id }, ctx.uid)
+ await ctx.pubsub.entityUpdated>(
+ EntityType.PAGE,
+ { ...page, id },
+ ctx.uid
+ )
return true
} catch (e) {
@@ -241,7 +246,7 @@ export const deletePage = async (
if (body.deleted === 0) return false
- await ctx.pubsub.pageDeleted(id, ctx.uid)
+ await ctx.pubsub.entityDeleted(EntityType.PAGE, id, ctx.uid)
return true
} catch (e) {
diff --git a/packages/api/src/routers/svc/upload.ts b/packages/api/src/routers/svc/upload.ts
index b26be4cad..ebc935f86 100644
--- a/packages/api/src/routers/svc/upload.ts
+++ b/packages/api/src/routers/svc/upload.ts
@@ -11,8 +11,8 @@ import { DateTime } from 'luxon'
export function uploadServiceRouter() {
const router = express.Router()
- router.post('/upload/:folder', async (req, res) => {
- console.log('upload data req', req.params.folder)
+ router.post('/:folder', async (req, res) => {
+ console.log('upload data to folder', req.params.folder)
const { message: msgStr, expired } = readPushSubscription(req)
if (!msgStr) {
@@ -27,9 +27,9 @@ export function uploadServiceRouter() {
}
try {
- const data: { userId: string } = JSON.parse(msgStr)
- if (!data.userId) {
- console.log('No userId found in message')
+ const data: { userId: string; type: string } = JSON.parse(msgStr)
+ if (!data.userId || !data.type) {
+ console.log('No userId or type found in message')
res.status(400).send('Bad Request')
return
}
@@ -40,9 +40,9 @@ export function uploadServiceRouter() {
console.log('generate upload url')
const uploadUrl = await generateUploadSignedUrl(
- `${req.params.folder}/${data.userId}/${DateTime.now().toFormat(
- 'yyyy-LL-dd'
- )}/${uuidv4()}.json`,
+ `${req.params.folder}/${data.type}/${
+ data.userId
+ }/${DateTime.now().toFormat('yyyy-LL-dd')}/${uuidv4()}.json`,
contentType,
bucketName
)
diff --git a/packages/api/test/routers/pages.test.ts b/packages/api/test/routers/pages.test.ts
index 6778322ea..de7be177f 100644
--- a/packages/api/test/routers/pages.test.ts
+++ b/packages/api/test/routers/pages.test.ts
@@ -1,21 +1,21 @@
import { request } from '../util'
import 'mocha'
-describe('Pages Router', () => {
+describe('Upload Router', () => {
const token = process.env.PUBSUB_VERIFICATION_TOKEN || ''
describe('upload', () => {
it('upload data to GCS', async () => {
const data = {
message: {
- data: Buffer.from(JSON.stringify({ userId: 'userId' })).toString(
+ data: Buffer.from(JSON.stringify({ userId: 'userId', type: 'page' })).toString(
'base64'
),
publishTime: new Date().toISOString(),
},
}
await request
- .post(`/svc/pubsub/pages/upload/createdPage?token=${token}`)
+ .post(`/svc/pubsub/upload/createdEntity?token=${token}`)
.send(data)
.expect(200)
})
From 567f3dedad5d2fd5040edb16fd69fc7f3a294152 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Wed, 13 Apr 2022 08:49:45 -0700
Subject: [PATCH 094/125] use fixed list of colors for label creation
---
.../Sources/App/Views/Labels/LabelsView.swift | 46 +++++++++++++++----
1 file changed, 38 insertions(+), 8 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
index fd2ab65c0..b2af72baa 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
@@ -99,10 +99,10 @@ struct CreateLabelView: View {
let rows = [
GridItem(.fixed(60)),
GridItem(.fixed(60)),
- GridItem(.fixed(60))
+ GridItem(.fixed(70))
]
- let swatches = (0 ... 200).map { _ in Color.random }
+ let swatches = swatchHexes.map { Color(hex: $0) ?? .clear }.shuffled()
var body: some View {
NavigationView {
@@ -111,7 +111,7 @@ struct CreateLabelView: View {
if !newLabelName.isEmpty, newLabelColor != .clear {
TextChip(text: newLabelName, color: newLabelColor)
} else {
- Text("Assign a name and color.")
+ Text("Assign a name and color.").font(.appBody)
}
Spacer()
}
@@ -178,8 +178,38 @@ struct CreateLabelView: View {
}
}
-extension Color {
- static var random: Color {
- Color(hue: .random(in: 0 ... 1), saturation: .random(in: 0.2 ... 0.8), brightness: .random(in: 0.5 ... 0.8))
- }
-}
+private let swatchHexes = [
+ "#fff034",
+ "#efff34",
+ "#d1ff34",
+ "#b2ff34",
+ "#94ff34",
+ "#75ff34",
+ "#57ff34",
+ "#38ff34",
+ "#34ff4e",
+ "#34ff6d",
+ "#34ff8b",
+ "#34ffa9",
+ "#34ffc8",
+ "#34ffe6",
+ "#34f9ff",
+ "#34dbff",
+ "#34bcff",
+ "#349eff",
+ "#347fff",
+ "#3461ff",
+ "#3443ff",
+ "#4434ff",
+ "#6234ff",
+ "#8134ff",
+ "#9f34ff",
+ "#be34ff",
+ "#dc34ff",
+ "#fb34ff",
+ "#ff34e5",
+ "#ff34c7",
+ "#ff34a8",
+ "#ff348a",
+ "#ff346b"
+]
From 8978ff68f7cfbcd2ad818a906a5806ecde719c22 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 09:58:50 -0700
Subject: [PATCH 095/125] Use standard box shadow on modals
---
packages/web/components/elements/ModalPrimitives.tsx | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/packages/web/components/elements/ModalPrimitives.tsx b/packages/web/components/elements/ModalPrimitives.tsx
index db9aec2e2..3827e020b 100644
--- a/packages/web/components/elements/ModalPrimitives.tsx
+++ b/packages/web/components/elements/ModalPrimitives.tsx
@@ -1,5 +1,5 @@
import { Root, Overlay, Content } from '@radix-ui/react-dialog'
-import { styled, keyframes } from '../tokens/stitches.config'
+import { styled, keyframes, theme } from '../tokens/stitches.config'
export const ModalRoot = styled(Root, {})
@@ -25,8 +25,7 @@ const contentShow = keyframes({
const Modal = styled(Content, {
backgroundColor: '$grayBg',
borderRadius: 6,
- boxShadow:
- 'hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px',
+ boxShadow: theme.shadows.cardBoxShadow.toString(),
position: 'fixed',
'&:focus': { outline: 'none' },
})
From 049a5cf22f246b243ff25cb2fd2b32042889f5c8 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 09:59:36 -0700
Subject: [PATCH 096/125] Close highlight modal on click/tap outside
---
packages/web/components/templates/article/HighlightsModal.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/web/components/templates/article/HighlightsModal.tsx b/packages/web/components/templates/article/HighlightsModal.tsx
index 750ef1636..92358b057 100644
--- a/packages/web/components/templates/article/HighlightsModal.tsx
+++ b/packages/web/components/templates/article/HighlightsModal.tsx
@@ -30,6 +30,7 @@ export function HighlightsModal(props: HighlightsModalProps): JSX.Element {
{
event.preventDefault()
+ props.onOpenChange(false)
}}
css={{ overflow: 'auto', p: '0' }}
>
From c5233d15594d3455c7dea91d62db57391e479c04 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Wed, 13 Apr 2022 10:04:29 -0700
Subject: [PATCH 097/125] adjust add label button styling
---
.../Sources/App/Views/Home/HomeFeedViewIOS.swift | 1 +
apple/OmnivoreKit/Sources/Views/TextChip.swift | 11 +++++++++--
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
index 611979fc9..f6e31a5ed 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
@@ -128,6 +128,7 @@ import Views
}
Spacer()
}
+ .padding(.bottom, 5)
.padding(.horizontal)
.sheet(isPresented: $showLabelsSheet) {
ApplyLabelsView(mode: .list(viewModel.selectedLabels)) { labels in
diff --git a/apple/OmnivoreKit/Sources/Views/TextChip.swift b/apple/OmnivoreKit/Sources/Views/TextChip.swift
index 3fcd16e12..79e64e67e 100644
--- a/apple/OmnivoreKit/Sources/Views/TextChip.swift
+++ b/apple/OmnivoreKit/Sources/Views/TextChip.swift
@@ -33,7 +33,7 @@ public struct TextChip: View {
public struct TextChipButton: View {
public static func makeAddLabelButton(onTap: @escaping () -> Void) -> TextChipButton {
- TextChipButton(title: "Label Filter", color: .appYellow48, actionType: .add, onTap: onTap)
+ TextChipButton(title: "Labels", color: Color(.systemGray6), actionType: .show, onTap: onTap)
}
public static func makeShowOptionsButton(title: String, onTap: @escaping () -> Void) -> TextChipButton {
@@ -74,6 +74,12 @@ public struct TextChipButton: View {
self.color = color
self.onTap = onTap
self.actionType = actionType
+ self.foregroundColor = {
+ if actionType == .show {
+ return .appGrayText
+ }
+ return color.isDark ? .white : .black
+ }()
}
let text: String
@@ -81,6 +87,7 @@ public struct TextChipButton: View {
let onTap: () -> Void
let actionType: ActionType
let cornerRadius = 20.0
+ let foregroundColor: Color
public var body: some View {
Button(action: onTap) {
@@ -91,7 +98,7 @@ public struct TextChipButton: View {
.padding(.horizontal, 10)
.padding(.vertical, 5)
.font(.appFootnote)
- .foregroundColor(color.isDark ? .white : .black)
+ .foregroundColor(foregroundColor)
.lineLimit(1)
.background(color)
.cornerRadius(cornerRadius)
From 34ce209738017c5bd641e89eebfc395661ba1e34 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Wed, 13 Apr 2022 10:09:41 -0700
Subject: [PATCH 098/125] adjust text chip vertical padding
---
apple/OmnivoreKit/Sources/Views/TextChip.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apple/OmnivoreKit/Sources/Views/TextChip.swift b/apple/OmnivoreKit/Sources/Views/TextChip.swift
index 79e64e67e..2a09428e2 100644
--- a/apple/OmnivoreKit/Sources/Views/TextChip.swift
+++ b/apple/OmnivoreKit/Sources/Views/TextChip.swift
@@ -96,7 +96,7 @@ public struct TextChipButton: View {
Image(systemName: actionType.systemIconName)
}
.padding(.horizontal, 10)
- .padding(.vertical, 5)
+ .padding(.vertical, 8)
.font(.appFootnote)
.foregroundColor(foregroundColor)
.lineLimit(1)
From b57046b1d9958d4a19a12c6de6de03c4eaf3faca Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Wed, 13 Apr 2022 10:19:55 -0700
Subject: [PATCH 099/125] add edit labels buttons to list view cells
---
.../Sources/App/Views/Home/HomeFeedViewIOS.swift | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
index f6e31a5ed..799146733 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
@@ -195,6 +195,10 @@ import Views
viewModel: viewModel
)
.contextMenu {
+ Button(
+ action: { viewModel.itemUnderLabelEdit = item },
+ label: { Label("Edit Labels", systemImage: "tag") }
+ )
Button(action: {
withAnimation(.linear(duration: 0.4)) {
viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: !item.isArchived)
@@ -223,7 +227,13 @@ import Views
}
if #available(iOS 15.0, *) {
link
- .swipeActions(edge: .trailing, allowsFullSwipe: true) {
+ .swipeActions(edge: .trailing, allowsFullSwipe: false) {
+ Button(
+ action: { viewModel.itemUnderLabelEdit = item },
+ label: { Label("Edit Labels", systemImage: "tag") }
+ )
+ }
+ .swipeActions(edge: .trailing, allowsFullSwipe: false) {
if !item.isArchived {
Button {
withAnimation(.linear(duration: 0.4)) {
@@ -242,7 +252,7 @@ import Views
}.tint(.indigo)
}
}
- .swipeActions(edge: .trailing, allowsFullSwipe: true) {
+ .swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button(
role: .destructive,
action: {
From 86d6d03fe08e405f99c115985556280782502173 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 10:25:01 -0700
Subject: [PATCH 100/125] Remove default button padding, on Safari this caused
extra padding on dropdown triggers
---
.../components/templates/article/ArticleActionsMenu.tsx | 7 +++----
packages/web/styles/globals.css | 5 +++++
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 64aa0b20c..39bef218c 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -63,9 +63,8 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
alignItems: 'center',
flexDirection: props.layout == 'vertical' ? 'column' : 'row',
justifyContent: props.layout == 'vertical' ? 'center' : 'flex-end',
- gap: props.layout == 'vertical' ? '8px' : '20px',
+ gap: props.layout == 'vertical' ? '8px' : '24px',
paddingTop: '6px',
- m: '0px',
}}
>
@@ -76,8 +75,8 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
tooltipContent="Adjust Display Settings"
tooltipSide={props.layout == 'vertical' ? 'right' : 'bottom'}
>
-
-
+
+
}
>
diff --git a/packages/web/styles/globals.css b/packages/web/styles/globals.css
index dda271556..151ad8a33 100644
--- a/packages/web/styles/globals.css
+++ b/packages/web/styles/globals.css
@@ -134,4 +134,9 @@ input[type=range]::-webkit-slider-thumb {
width: 16px;
border-radius: 50%;
background: var(--colors-grayTextContrast);
+}
+
+button {
+ padding: 0px;
+ margin: 0px;
}
\ No newline at end of file
From 18cbe4a856eeeed01de7d41abe528f0ac2da116e Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 10:28:33 -0700
Subject: [PATCH 101/125] Remove font stepper from dropdown
---
.../web/components/patterns/DropdownMenu.tsx | 19 -------------------
.../web/components/patterns/PrimaryHeader.tsx | 4 ----
.../components/templates/PrimaryLayout.tsx | 2 --
.../web/pages/[username]/[slug]/index.tsx | 1 -
4 files changed, 26 deletions(-)
diff --git a/packages/web/components/patterns/DropdownMenu.tsx b/packages/web/components/patterns/DropdownMenu.tsx
index c63dca2ee..adf9f25ae 100644
--- a/packages/web/components/patterns/DropdownMenu.tsx
+++ b/packages/web/components/patterns/DropdownMenu.tsx
@@ -24,7 +24,6 @@ export type HeaderDropdownAction =
type DropdownMenuProps = {
username?: string
triggerElement: ReactNode
- displayFontStepper?: boolean
actionHandler: (action: HeaderDropdownAction) => void
}
@@ -53,24 +52,6 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element {
{ isDark ? '✓' : '' }
-
- {props.displayFontStepper && (
- <>
-
- {
- props.actionHandler('decreaseFontSize')
- }}>
- A
-
-
- {
- props.actionHandler('increaseFontSize')
- }}>
- A
-
-
- >
- )}
- displayFontStepper?: boolean
toolbarControl?: JSX.Element
setShowLogoutConfirmation: (showShareModal: boolean) => void
setShowKeyboardCommandsModal: (showShareModal: boolean) => void
@@ -126,7 +125,6 @@ export function PrimaryHeader(props: HeaderProps): JSX.Element {
isVisible={true}
isFixedPosition={true}
toolbarControl={props.toolbarControl}
- displayFontStepper={props.displayFontStepper}
/>
>
)
@@ -141,7 +139,6 @@ type NavHeaderProps = {
isDisplayingShadow?: boolean
isVisible?: boolean
isFixedPosition: boolean
- displayFontStepper?: boolean
toolbarControl?: JSX.Element
}
@@ -211,7 +208,6 @@ function NavHeader(props: NavHeaderProps): JSX.Element {
/>
}
actionHandler={props.actionHandler}
- displayFontStepper={props.displayFontStepper}
/>
) : (
diff --git a/packages/web/components/templates/PrimaryLayout.tsx b/packages/web/components/templates/PrimaryLayout.tsx
index c73a02ab2..56e85a419 100644
--- a/packages/web/components/templates/PrimaryLayout.tsx
+++ b/packages/web/components/templates/PrimaryLayout.tsx
@@ -22,7 +22,6 @@ type PrimaryLayoutProps = {
hideHeader?: boolean
pageMetaDataProps?: PageMetaDataProps
scrollElementRef?: MutableRefObject
- displayFontStepper?: boolean
headerToolbarControl?: JSX.Element
}
@@ -78,7 +77,6 @@ export function PrimaryLayout(props: PrimaryLayoutProps): JSX.Element {
isFixedPosition={true}
toolbarControl={props.headerToolbarControl}
scrollElementRef={props.scrollElementRef}
- displayFontStepper={props.displayFontStepper}
setShowLogoutConfirmation={setShowLogoutConfirmation}
setShowKeyboardCommandsModal={setShowKeyboardCommandsModal}
/>
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index c950b11b3..598f7e0cf 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -164,7 +164,6 @@ export default function Home(): JSX.Element {
Date: Wed, 13 Apr 2022 10:34:46 -0700
Subject: [PATCH 102/125] Add dropdown link to labels
---
packages/web/components/patterns/DropdownMenu.tsx | 5 +++++
packages/web/components/patterns/PrimaryHeader.tsx | 3 +++
2 files changed, 8 insertions(+)
diff --git a/packages/web/components/patterns/DropdownMenu.tsx b/packages/web/components/patterns/DropdownMenu.tsx
index adf9f25ae..317af1ade 100644
--- a/packages/web/components/patterns/DropdownMenu.tsx
+++ b/packages/web/components/patterns/DropdownMenu.tsx
@@ -16,6 +16,7 @@ export type HeaderDropdownAction =
| 'apply-lighter-theme'
| 'navigate-to-install'
| 'navigate-to-emails'
+ | 'navigate-to-labels'
| 'navigate-to-profile'
| 'increaseFontSize'
| 'decreaseFontSize'
@@ -62,6 +63,10 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element {
onSelect={() => props.actionHandler('navigate-to-emails')}
title="Emails"
/>
+ props.actionHandler('navigate-to-labels')}
+ title="Labels"
+ />
window.Intercom('show')}
title="Feedback"
diff --git a/packages/web/components/patterns/PrimaryHeader.tsx b/packages/web/components/patterns/PrimaryHeader.tsx
index 50c64844c..79294d02f 100644
--- a/packages/web/components/patterns/PrimaryHeader.tsx
+++ b/packages/web/components/patterns/PrimaryHeader.tsx
@@ -102,6 +102,9 @@ export function PrimaryHeader(props: HeaderProps): JSX.Element {
case 'navigate-to-emails':
router.push('/settings/emails')
break
+ case 'navigate-to-labels':
+ router.push('/settings/labels')
+ break
case 'navigate-to-profile':
if (props.user) {
router.push(`/${props.user.profile.username}`)
From 1e2901c4e71b5ab26394c2118ae4d3df4af12278 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Wed, 13 Apr 2022 10:48:25 -0700
Subject: [PATCH 103/125] add label editing to reader view
---
.../App/Views/Home/HomeFeedViewIOS.swift | 2 +-
.../App/Views/Home/HomeFeedViewModel.swift | 18 ++++++++++++++++--
.../Views/WebReader/WebReaderContainer.swift | 4 ++++
3 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
index 799146733..7f6893916 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
@@ -103,7 +103,7 @@ import Views
}
}
.onChange(of: viewModel.selectedLinkItem) { _ in
- viewModel.commitProgressUpdates()
+ viewModel.commitItemUpdates()
}
}
}
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift
index 763615607..4ee67a777 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift
@@ -11,6 +11,9 @@ final class HomeFeedViewModel: ObservableObject {
/// Track progress updates to be committed when user navigates back to grid view
var uncommittedReadingProgressUpdates = [String: Double]()
+ /// Track label updates to be committed when user navigates back to grid view
+ var uncommittedLabelUpdates = [String: [FeedItemLabel]]()
+
@Published var items = [FeedItem]()
@Published var isLoading = false
@Published var showPushNotificationPrimer = false
@@ -173,14 +176,18 @@ final class HomeFeedViewModel: ObservableObject {
.store(in: &subscriptions)
}
- /// Update `FeedItem`s with the cached reading progress values so it can animate when the
+ /// Update `FeedItem`s with the cached reading progress and label values so it can animate when the
/// user navigates back to the grid view (and also avoid mutations of the grid items
/// that can cause the `NavigationView` to pop.
- func commitProgressUpdates() {
+ func commitItemUpdates() {
for (key, value) in uncommittedReadingProgressUpdates {
updateProgress(itemID: key, progress: value)
}
+ for (key, value) in uncommittedLabelUpdates {
+ updateLabels(itemID: key, labels: value)
+ }
uncommittedReadingProgressUpdates = [:]
+ uncommittedLabelUpdates = [:]
}
private func updateProgress(itemID: String, progress: Double) {
@@ -191,6 +198,13 @@ final class HomeFeedViewModel: ObservableObject {
}
func updateLabels(itemID: String, labels: [FeedItemLabel]) {
+ // If item is being being displayed then delay the state update of labels until
+ // user is no longer reading the item.
+ if selectedLinkItem != nil {
+ uncommittedLabelUpdates[itemID] = labels
+ return
+ }
+
guard let item = items.first(where: { $0.id == itemID }) else { return }
if let index = items.firstIndex(of: item) {
items[index].labels = labels
diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift
index 4759c527b..ecf6144a8 100644
--- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift
@@ -132,6 +132,10 @@ import WebKit
Menu(
content: {
Group {
+ Button(
+ action: { homeFeedViewModel.itemUnderLabelEdit = item },
+ label: { Label("Edit Labels", systemImage: "tag") }
+ )
Button(
action: {
homeFeedViewModel.setLinkArchived(
From 4e4dae89d7776dbaaa245a473d5b11efd9fac308 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 10:51:56 -0700
Subject: [PATCH 104/125] Consistent naming on label controls
- Edit Labels: Edit all of a user's labels
- Set Labels: Set the labels assigned to a link
---
.../templates/article/ArticleActionsMenu.tsx | 6 +++---
...EditLabelsControl.tsx => SetLabelsControl.tsx} | 11 +++++------
.../{EditLabelsModal.tsx => SetLabelsModal.tsx} | 11 +++++------
.../lib/keyboardShortcuts/navigationShortcuts.ts | 4 ++--
packages/web/pages/[username]/[slug]/index.tsx | 15 +++++++--------
5 files changed, 22 insertions(+), 25 deletions(-)
rename packages/web/components/templates/article/{EditLabelsControl.tsx => SetLabelsControl.tsx} (97%)
rename packages/web/components/templates/article/{EditLabelsModal.tsx => SetLabelsModal.tsx} (83%)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 39bef218c..75be3afcc 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -7,7 +7,7 @@ import { Dropdown } from "../../elements/DropdownElements"
import { Box, SpanBox } from "../../elements/LayoutPrimitives"
import { TooltipWrapped } from "../../elements/Tooltip"
import { styled, theme } from "../../tokens/stitches.config"
-import { EditLabelsControl } from "./EditLabelsControl"
+import { SetLabelsControl } from "./SetLabelsControl"
import { ReaderSettingsControl } from "./ReaderSettingsControl"
export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
@@ -101,7 +101,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}
>
-
@@ -109,7 +109,7 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
props.articleActionHandler('editLabels')}
+ onClick={() => props.articleActionHandler('setLabels')}
css={{
'display': 'none',
'@smDown': {
diff --git a/packages/web/components/templates/article/EditLabelsControl.tsx b/packages/web/components/templates/article/SetLabelsControl.tsx
similarity index 97%
rename from packages/web/components/templates/article/EditLabelsControl.tsx
rename to packages/web/components/templates/article/SetLabelsControl.tsx
index 6eff4bacb..1302369d8 100644
--- a/packages/web/components/templates/article/EditLabelsControl.tsx
+++ b/packages/web/components/templates/article/SetLabelsControl.tsx
@@ -9,7 +9,6 @@ import { Label } from '../../../lib/networking/fragments/labelFragment'
import { useGetLabelsQuery } from '../../../lib/networking/queries/useGetLabelsQuery'
import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
import { Check, Circle, PencilSimple, Plus } from 'phosphor-react'
-
import { isTouchScreenDevice } from '../../../lib/deviceType'
import { setLabelsMutation } from '../../../lib/networking/mutations/setLabelsMutation'
import { createLabelMutation } from '../../../lib/networking/mutations/createLabelMutation'
@@ -17,7 +16,7 @@ import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
import { randomLabelColorHex } from '../../../utils/settings-page/labels/labelColorObjects'
import { useRouter } from 'next/router'
-type EditLabelsControlProps = {
+type SetLabelsControlProps = {
article: ArticleAttributes
articleActionHandler: (action: string, arg?: unknown) => void
}
@@ -147,11 +146,11 @@ function LabelListItem(props: LabelListItemProps): JSX.Element {
)
}
-type EditLabelsButtonFooterProps = {
+type FooterProps = {
focused: boolean
}
-function EditLabelsButtonFooter(props: EditLabelsButtonFooterProps): JSX.Element {
+function Footer(props: FooterProps): JSX.Element {
const ref = useRef(null)
useEffect(() => {
@@ -184,7 +183,7 @@ function EditLabelsButtonFooter(props: EditLabelsButtonFooterProps): JSX.Element
)
}
-export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
+export function SetLabelsControl(props: SetLabelsControlProps): JSX.Element {
const router = useRouter()
const [filterText, setFilterText] = useState('')
const { labels, revalidate } = useGetLabelsQuery()
@@ -340,7 +339,7 @@ export function EditLabelsControl(props: EditLabelsControlProps): JSX.Element {
)}
-
+
)
}
\ No newline at end of file
diff --git a/packages/web/components/templates/article/EditLabelsModal.tsx b/packages/web/components/templates/article/SetLabelsModal.tsx
similarity index 83%
rename from packages/web/components/templates/article/EditLabelsModal.tsx
rename to packages/web/components/templates/article/SetLabelsModal.tsx
index 1ef6da940..81696600f 100644
--- a/packages/web/components/templates/article/EditLabelsModal.tsx
+++ b/packages/web/components/templates/article/SetLabelsModal.tsx
@@ -1,8 +1,7 @@
-import { X } from 'phosphor-react'
import { ArticleAttributes } from '../../../lib/networking/queries/useGetArticleQuery'
import { Button } from '../../elements/Button'
import { CrossIcon } from '../../elements/images/CrossIcon'
-import { Box, HStack, VStack } from '../../elements/LayoutPrimitives'
+import { HStack, VStack } from '../../elements/LayoutPrimitives'
import {
ModalRoot,
ModalOverlay,
@@ -10,15 +9,15 @@ import {
} from '../../elements/ModalPrimitives'
import { StyledText } from '../../elements/StyledText'
import { theme } from '../../tokens/stitches.config'
-import { EditLabelsControl } from './EditLabelsControl'
+import { SetLabelsControl } from './SetLabelsControl'
-type EditLabelsModalProps = {
+type SetLabelsModalProps = {
article: ArticleAttributes
onOpenChange: (open: boolean) => void
articleActionHandler: (action: string, arg?: unknown) => void
}
-export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
+export function SetLabelsModal(props: SetLabelsModalProps): JSX.Element {
return (
@@ -49,7 +48,7 @@ export function EditLabelsModal(props: EditLabelsModalProps): JSX.Element {
/>
-
+
diff --git a/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts b/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts
index 2ce3f7ab8..98da5cf33 100644
--- a/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts
+++ b/packages/web/lib/keyboardShortcuts/navigationShortcuts.ts
@@ -209,7 +209,7 @@ type ArticleKeyboardAction =
| 'decrementFontSize'
| 'incrementMarginWidth'
| 'decrementMarginWidth'
- | 'editLabels'
+ | 'setLabels'
export function articleKeyboardCommands(
router: NextRouter | undefined,
@@ -256,7 +256,7 @@ export function articleKeyboardCommands(
shortcutKeys: ['l'],
actionDescription: 'Edit labels',
shortcutKeyDescription: 'l',
- callback: () => actionHandler('editLabels'),
+ callback: () => actionHandler('setLabels'),
},
]
}
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index 598f7e0cf..8fd47910a 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -27,8 +27,7 @@ import { setLinkArchivedMutation } from '../../../lib/networking/mutations/setLi
import { Label } from '../../../lib/networking/fragments/labelFragment'
import { useSWRConfig } from 'swr'
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
-import { EditLabelsControl } from '../../../components/templates/article/EditLabelsControl'
-import { EditLabelsModal } from '../../../components/templates/article/EditLabelsModal'
+import { SetLabelsModal } from '../../../components/templates/article/SetLabelsModal'
import { DisplaySettingsModal } from '../../../components/templates/article/DisplaySettingsModal'
@@ -49,7 +48,7 @@ export default function Home(): JSX.Element {
const [fontSize, setFontSize] = useState(preferencesData?.fontSize ?? 20)
const [marginWidth, setMarginWidth] = useState(preferencesData?.margin ?? 360)
const [lineHeight, setLineHeight] = useState(preferencesData?.lineHeight ?? 150)
- const [showEditLabelsModal, setShowEditLabelsModal] = useState(false)
+ const [showSetLabelsModal, setShowSetLabelsModal] = useState(false)
const [showEditDisplaySettingsModal, setShowEditDisplaySettingsModal] = useState(false)
const { articleData } = useGetArticleQuery({
@@ -140,8 +139,8 @@ export default function Home(): JSX.Element {
setShowEditDisplaySettingsModal(true)
break
}
- case 'editLabels': {
- setShowEditLabelsModal(true)
+ case 'setLabels': {
+ setShowSetLabelsModal(true)
break
}
case 'resetReaderSettings': {
@@ -243,11 +242,11 @@ export default function Home(): JSX.Element {
)}
- {showEditLabelsModal && (
- setShowEditLabelsModal(false)}
+ onOpenChange={() => setShowSetLabelsModal(false)}
/>
)}
From 31861f81e2ff1658b7a22670543b8b9f82f59d73 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 13:29:45 -0700
Subject: [PATCH 105/125] Add very basic help page for labels
---
packages/web/pages/help/labels.tsx | 89 ++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
create mode 100644 packages/web/pages/help/labels.tsx
diff --git a/packages/web/pages/help/labels.tsx b/packages/web/pages/help/labels.tsx
new file mode 100644
index 000000000..54da7009d
--- /dev/null
+++ b/packages/web/pages/help/labels.tsx
@@ -0,0 +1,89 @@
+/* eslint-disable @next/next/no-img-element */
+import { Box, HStack } from '../../components/elements/LayoutPrimitives'
+import { PrimaryLayout } from '../../components/templates/PrimaryLayout'
+import Link from 'next/link'
+
+export default function Labels(): JSX.Element {
+ return (
+
+
+ Organize your Omnivore library with labels
+
+ Introduction
+
+ Labels allow you to group and search for content in Omnivore. A saved page
+ can have multiple labels and search results can be filtered by label.
+
+ Adding labels to a page on iOS
+
+ On iOS you add and remove labels from a page using the Assign Labels modal.
+
+
+ You can open the Assign Labels modal from the home view or the reader view.
+ In the home view long press on an item and choose Edit Labels from the dropdowm
+ menu. In the reader view use the top right menu button.
+
+ Adding labels to a page on the web
+
+ On the web you add and remove labels from a page using the Assign Labels dropdown
+ or modal depending on your screen size. For larger monitors you will see the Labels
+ button on the left hand side of the reader view. For smaller monitors you will see
+ the labels dropdown at the top of your screen.
+
+
+ You can also use keyboard commands to open the assign labels modal. On the reader
+ view tap the l key. Once open you can use the up/down arrow keys, or the
+ tab key to navigate the available labels, and the Enter key to toggle a label.
+
+ Searching by label on iOS
+
+ On iOS you can use the Labels search chip to search for labels. This will open a modal
+ allowing you to assign multiple labels to your search. This will become an OR
+ search, meaning if you add multiple labels to your search, pages that have any of the
+ labels will be returned.
+
+ Searching by label with Advanced Search
+
+ Omnivore's advanced search syntax supports searching for multiple labels using
+ AND and OR clauses. You can also negate a label search
+ to find all pages that do not have a certain label.
+
+ Some examples:
+
+ -label:Newsletter finds all pages that do not have the label Newsletter
+ label:Cooking,Fitness finds all your pages with either the Cooking or Fitness labels
+ label:Newsletter label:Surfing finds all pages with both the Newsletter and Surfing labels
+ label:Coding -label:Newsletter finds all pages with the Coding label that do not have the Newsletter label
+
+
+ Editing your list of labels
+
+ The labels page allows you to
+ edit all of your labels. From here you can create new labels, delete existing
+ labels, or modify the color and description of a label.
+
+
+
+
+ )
+}
From da561ca58b2416a3f59dc199bc93e5c9bf701839 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 13:38:36 -0700
Subject: [PATCH 106/125] Query for labels with library items. Display properly
on the home feed
---
.../LibraryCards/GridLinkedItemCard.tsx | 4 +--
.../queries/useGetLibraryItemsQuery.tsx | 29 +++++++++++++++++--
2 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx b/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx
index bb60b4c37..5d61e35b4 100644
--- a/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx
+++ b/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx
@@ -155,8 +155,8 @@ export function GridLinkedItemCard(props: LinkedItemCardProps): JSX.Element {
)}
- {props.item.labels?.map(({ description, color }, index) => (
-
+ {props.item.labels?.map(({ name, color }, index) => (
+
))}
diff --git a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
index ad02f5e96..a12a19219 100644
--- a/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
+++ b/packages/web/lib/networking/queries/useGetLibraryItemsQuery.tsx
@@ -6,7 +6,7 @@ import { articleFragment } from '../fragments/articleFragment'
import { setLinkArchivedMutation } from '../mutations/setLinkArchivedMutation'
import { deleteLinkMutation } from '../mutations/deleteLinkMutation'
import { articleReadingProgressMutation } from '../mutations/articleReadingProgressMutation'
-import { Label } from './../fragments/labelFragment'
+import { Label, labelFragment } from './../fragments/labelFragment'
import { showErrorToast, showSuccessToast } from '../../toastHelpers'
export type LibraryItemsQueryInput = {
@@ -67,6 +67,30 @@ export type PageInfo = {
totalCount: number
}
+const libraryItemFragment = gql`
+ fragment ArticleFields on Article {
+ id
+ title
+ url
+ author
+ image
+ savedAt
+ createdAt
+ publishedAt
+ contentReader
+ originalArticleUrl
+ readingProgressPercent
+ readingProgressAnchorIndex
+ slug
+ isArchived
+ description
+ linkId
+ labels {
+ ...LabelFields
+ }
+ }
+`
+
export function useGetLibraryItemsQuery({
limit,
sortDescending,
@@ -109,7 +133,8 @@ export function useGetLibraryItemsQuery({
}
}
}
- ${articleFragment}
+ ${libraryItemFragment}
+ ${labelFragment}
`
const variables = {
From a75c8e91a464ec257dd5dc0e7036c6724a9c61aa Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Wed, 13 Apr 2022 13:42:51 -0700
Subject: [PATCH 107/125] adjust tappable area of text chip buttons
---
.../App/Views/Home/HomeFeedViewIOS.swift | 3 +--
.../OmnivoreKit/Sources/Views/TextChip.swift | 26 ++++++++++++-------
2 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
index 7f6893916..a5440b461 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
@@ -115,7 +115,7 @@ import Views
@ObservedObject var viewModel: HomeFeedViewModel
var body: some View {
- VStack {
+ VStack(spacing: 0) {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
TextChipButton.makeAddLabelButton {
@@ -128,7 +128,6 @@ import Views
}
Spacer()
}
- .padding(.bottom, 5)
.padding(.horizontal)
.sheet(isPresented: $showLabelsSheet) {
ApplyLabelsView(mode: .list(viewModel.selectedLabels)) { labels in
diff --git a/apple/OmnivoreKit/Sources/Views/TextChip.swift b/apple/OmnivoreKit/Sources/Views/TextChip.swift
index 2a09428e2..83ee595a9 100644
--- a/apple/OmnivoreKit/Sources/Views/TextChip.swift
+++ b/apple/OmnivoreKit/Sources/Views/TextChip.swift
@@ -91,17 +91,23 @@ public struct TextChipButton: View {
public var body: some View {
Button(action: onTap) {
- HStack {
- Text(text)
- Image(systemName: actionType.systemIconName)
+ VStack(spacing: 0) {
+ HStack {
+ Text(text)
+ .padding(.leading, 3)
+ Image(systemName: actionType.systemIconName)
+ }
+ .padding(.horizontal, 10)
+ .padding(.vertical, 8)
+ .font(.appFootnote)
+ .foregroundColor(foregroundColor)
+ .lineLimit(1)
+ .background(color)
+ .cornerRadius(cornerRadius)
+
+ Color.clear.contentShape(Rectangle()).frame(height: 15)
}
- .padding(.horizontal, 10)
- .padding(.vertical, 8)
- .font(.appFootnote)
- .foregroundColor(foregroundColor)
- .lineLimit(1)
- .background(color)
- .cornerRadius(cornerRadius)
+ .contentShape(Rectangle())
}
}
}
From 3ec84d3f337d5bd05175c423743f42b1d4197125 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 13:44:06 -0700
Subject: [PATCH 108/125] Fix apos escaping
---
packages/web/pages/help/labels.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/web/pages/help/labels.tsx b/packages/web/pages/help/labels.tsx
index 54da7009d..b14496ec7 100644
--- a/packages/web/pages/help/labels.tsx
+++ b/packages/web/pages/help/labels.tsx
@@ -64,7 +64,7 @@ export default function Labels(): JSX.Element {
Searching by label with Advanced Search
- Omnivore's advanced search syntax supports searching for multiple labels using
+ Omnivore's advanced search syntax supports searching for multiple labels using
AND and OR clauses. You can also negate a label search
to find all pages that do not have a certain label.
From 9c42d473c189b4220d1bbf307ad4b24cea17da1b Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 13:50:55 -0700
Subject: [PATCH 109/125] Do not display left side article actions for PDFs
---
packages/web/pages/[username]/[slug]/index.tsx | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index 8fd47910a..05348cc2e 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -196,11 +196,13 @@ export default function Home(): JSX.Element {
},
}}
>
-
+ {article.contentReader !== 'PDF' ? (
+
+ ) : null}
{article.contentReader == 'PDF' ? (
Date: Wed, 13 Apr 2022 15:28:09 -0700
Subject: [PATCH 110/125] add web label color selections and choose a random
color as the initial selection
---
.../Sources/App/Views/Labels/LabelsView.swift | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
index b2af72baa..142e51043 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
@@ -102,7 +102,12 @@ struct CreateLabelView: View {
GridItem(.fixed(70))
]
- let swatches = swatchHexes.map { Color(hex: $0) ?? .clear }.shuffled()
+ let swatches: [Color] = {
+ let webSwatches = webSwatchHexes.map { Color(hex: $0) ?? .clear }
+ var additionalSwatches = swatchHexes.map { Color(hex: $0) ?? .clear }.shuffled()
+ let firstSwatch = additionalSwatches.remove(at: 0)
+ return [firstSwatch] + webSwatches + additionalSwatches
+ }()
var body: some View {
NavigationView {
@@ -174,10 +179,22 @@ struct CreateLabelView: View {
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
+ .onAppear {
+ newLabelColor = swatches.first ?? .clear
+ }
}
}
}
+private let webSwatchHexes = [
+ "#FF5D99",
+ "#7CFF7B",
+ "#FFD234",
+ "#7BE4FF",
+ "#CE88EF",
+ "#EF8C43"
+]
+
private let swatchHexes = [
"#fff034",
"#efff34",
From 810401946e124d19e09671699e887e4d92571794 Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 16:03:07 -0700
Subject: [PATCH 111/125] Use local storage for margins and line spacing since
these are per-device preferences
---
.../templates/article/ArticleActionsMenu.tsx | 10 ++++--
.../article/DisplaySettingsModal.tsx | 9 +++--
.../article/ReaderSettingsControl.tsx | 12 +++----
.../web/pages/[username]/[slug]/index.tsx | 36 +++++++++++--------
4 files changed, 43 insertions(+), 24 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index 75be3afcc..fd6a0623c 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -9,6 +9,7 @@ import { TooltipWrapped } from "../../elements/Tooltip"
import { styled, theme } from "../../tokens/stitches.config"
import { SetLabelsControl } from "./SetLabelsControl"
import { ReaderSettingsControl } from "./ReaderSettingsControl"
+import { usePersistedState } from "../../../lib/hooks/usePersistedState"
export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
@@ -53,7 +54,8 @@ const ActionDropdown = (props: ActionDropdownProps): JSX.Element => {
}
export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element {
- const { preferencesData } = useGetUserPreferences()
+ const [lineHeight, setLineHeight] = usePersistedState({ key: 'lineHeight', initialValue: 150 })
+ const [marginWidth, setMarginWidth] = usePersistedState({ key: 'marginWidth', initialValue: 200 })
return (
<>
@@ -79,7 +81,11 @@ export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element
}
>
-
+
diff --git a/packages/web/components/templates/article/DisplaySettingsModal.tsx b/packages/web/components/templates/article/DisplaySettingsModal.tsx
index c7e0623cb..06b15a47d 100644
--- a/packages/web/components/templates/article/DisplaySettingsModal.tsx
+++ b/packages/web/components/templates/article/DisplaySettingsModal.tsx
@@ -15,7 +15,8 @@ import { ReaderSettingsControl } from './ReaderSettingsControl'
type DisplaySettingsModalProps = {
onOpenChange: (open: boolean) => void
- userPreferences?: UserPreferences
+ lineHeight: number
+ marginWidth: number
articleActionHandler: (action: string, arg?: number) => void
}
@@ -50,7 +51,11 @@ export function DisplaySettingsModal(props: DisplaySettingsModalProps): JSX.Elem
/>
-
+
diff --git a/packages/web/components/templates/article/ReaderSettingsControl.tsx b/packages/web/components/templates/article/ReaderSettingsControl.tsx
index fd1e74004..e720b3fc8 100644
--- a/packages/web/components/templates/article/ReaderSettingsControl.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsControl.tsx
@@ -6,12 +6,12 @@ import { useState } from 'react'
import { AlignCenterHorizontalSimple, ArrowsInLineHorizontal, ArrowsOutLineHorizontal, Minus, Pen, Plus, Trash, X } from 'phosphor-react'
import { AIcon } from '../../elements/images/AIcon'
import { TickedRangeSlider } from '../../elements/TickedRangeSlider'
-import { UserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
import { showSuccessToast } from '../../../lib/toastHelpers'
type ReaderSettingsProps = {
- userPreferences?: UserPreferences
+ marginWidth: number
+ lineHeight: number
articleActionHandler: (action: string, arg?: number) => void
}
@@ -22,8 +22,8 @@ const VerticalDivider = styled(SpanBox, {
})
export function ReaderSettingsControl(props: ReaderSettingsProps): JSX.Element {
- const [marginWidth, setMarginWidth] = useState(props.userPreferences?.margin ?? 0)
- const [lineHeight, setLineHeight] = useState(props.userPreferences?.lineHeight ?? 150)
+ const [lineHeight, setLineHeight] = useState(props.lineHeight)
+ const [marginWidth, setMarginWidth] = useState(props.marginWidth)
return (
@@ -87,7 +87,7 @@ export function ReaderSettingsControl(props: ReaderSettingsProps): JSX.Element {
width: '100%',
height: '100%',
}}>
- Line Height:
+ Line Spacing:
{
const newLineHeight = Math.max(lineHeight - 25, 100)
@@ -111,7 +111,7 @@ export function ReaderSettingsControl(props: ReaderSettingsProps): JSX.Element {
{
- setMarginWidth(360)
+ setMarginWidth(200)
setLineHeight(150)
props.articleActionHandler('resetReaderSettings')
showSuccessToast('Display settings reset', { position: 'bottom-right' })
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index 05348cc2e..77d78c3d4 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -6,7 +6,7 @@ import { useRouter } from 'next/router'
import { VStack } from './../../../components/elements/LayoutPrimitives'
import { ArticleContainer } from './../../../components/templates/article/ArticleContainer'
import { PdfArticleContainerProps } from './../../../components/templates/article/PdfArticleContainer'
-import { useEffect, useMemo, useRef, useState } from 'react'
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useKeyboardShortcuts } from '../../../lib/keyboardShortcuts/useKeyboardShortcuts'
import { articleKeyboardCommands, navigationCommands } from '../../../lib/keyboardShortcuts/navigationShortcuts'
import dynamic from 'next/dynamic'
@@ -22,13 +22,13 @@ import { userPersonalizationMutation } from '../../../lib/networking/mutations/u
import Script from 'next/script'
import { theme } from '../../../components/tokens/stitches.config'
import { ArticleActionsMenu } from '../../../components/templates/article/ArticleActionsMenu'
-import { HighlightsModal } from '../../../components/templates/article/HighlightsModal'
import { setLinkArchivedMutation } from '../../../lib/networking/mutations/setLinkArchivedMutation'
import { Label } from '../../../lib/networking/fragments/labelFragment'
import { useSWRConfig } from 'swr'
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
import { SetLabelsModal } from '../../../components/templates/article/SetLabelsModal'
import { DisplaySettingsModal } from '../../../components/templates/article/DisplaySettingsModal'
+import { usePersistedState } from '../../../lib/hooks/usePersistedState'
const PdfArticleContainerNoSSR = dynamic(
@@ -46,11 +46,11 @@ export default function Home(): JSX.Element {
const { viewerData } = useGetViewerQuery()
const { preferencesData } = useGetUserPreferences()
const [fontSize, setFontSize] = useState(preferencesData?.fontSize ?? 20)
- const [marginWidth, setMarginWidth] = useState(preferencesData?.margin ?? 360)
- const [lineHeight, setLineHeight] = useState(preferencesData?.lineHeight ?? 150)
+ const [lineHeight, setLineHeight] = usePersistedState({ key: 'lineHeight', initialValue: 150 })
+ const [marginWidth, setMarginWidth] = usePersistedState({ key: 'marginWidth', initialValue: 200 })
const [showSetLabelsModal, setShowSetLabelsModal] = useState(false)
const [showEditDisplaySettingsModal, setShowEditDisplaySettingsModal] = useState(false)
-
+
const { articleData } = useGetArticleQuery({
username: router.query.username as string,
slug: router.query.slug as string,
@@ -67,14 +67,21 @@ export default function Home(): JSX.Element {
useKeyboardShortcuts(navigationCommands(router))
- const updateFontSize = async (newFontSize: number) => {
+ const updateFontSize = useCallback(async(newFontSize: number) => {
+ window?.localStorage.setItem("fontSize", newFontSize.toString())
setFontSize(newFontSize)
await userPersonalizationMutation({ fontSize: newFontSize })
- }
+ }, [fontSize, setFontSize])
- const updateMarginWidth = async (newMargin: number) => {
+ const updateLineHeight = useCallback(async(newLineHeight: number) => {
+ window?.localStorage.setItem("lineHeight", newLineHeight.toString())
+ setLineHeight(newLineHeight)
+ }, [lineHeight, setLineHeight])
+
+ const updateMarginWidth = useCallback(async(newMargin: number) => {
+ window?.localStorage.setItem("marginWidth", newMargin.toString())
setMarginWidth(newMargin)
- }
+ }, [marginWidth, setMarginWidth])
const actionHandler = async (action: string, arg?: unknown) => {
switch (action) {
@@ -131,7 +138,7 @@ export default function Home(): JSX.Element {
case 'setLineHeight': {
const value = Number(arg)
if (value >= 100 && value <= 300) {
- setLineHeight(value)
+ updateLineHeight(arg as number)
}
break
}
@@ -145,8 +152,8 @@ export default function Home(): JSX.Element {
}
case 'resetReaderSettings': {
updateFontSize(20)
- updateMarginWidth(360)
- setLineHeight(150)
+ updateMarginWidth(200)
+ updateLineHeight(150)
break
}
}
@@ -197,7 +204,7 @@ export default function Home(): JSX.Element {
}}
>
{article.contentReader !== 'PDF' ? (
- setShowEditDisplaySettingsModal(false)}
/>
From c553b8317ad3f58e1ca5d842eab397eace6db77d Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 16:19:39 -0700
Subject: [PATCH 112/125] Disable label chips on main grid to see if it is
causing sizing issues
---
.../components/patterns/LibraryCards/GridLinkedItemCard.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx b/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx
index 5d61e35b4..319bc6fdb 100644
--- a/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx
+++ b/packages/web/components/patterns/LibraryCards/GridLinkedItemCard.tsx
@@ -154,11 +154,11 @@ export function GridLinkedItemCard(props: LinkedItemCardProps): JSX.Element {
/>
)}
-
+ {/*
{props.item.labels?.map(({ name, color }, index) => (
))}
-
+ */}
)
}
From a7e01275ae7c394a1031cd462e20124d4f617a24 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Wed, 13 Apr 2022 17:31:01 -0700
Subject: [PATCH 113/125] fix labels view for mac compilation
---
.../Sources/App/Views/Labels/LabelsView.swift | 5 +++--
.../Sources/Views/Colors/Colors.swift | 2 ++
.../OmnivoreKit/Sources/Views/TextChip.swift | 2 +-
.../Utils/ToolbarItemPlacementExtension.swift | 19 +++++++++++++++++++
4 files changed, 25 insertions(+), 3 deletions(-)
create mode 100644 apple/OmnivoreKit/Sources/Views/Utils/ToolbarItemPlacementExtension.swift
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
index 142e51043..24633a1e5 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
@@ -2,6 +2,7 @@ import Combine
import Models
import Services
import SwiftUI
+import Utils
import Views
struct LabelsView: View {
@@ -153,13 +154,13 @@ struct CreateLabelView: View {
}
.padding()
.toolbar {
- ToolbarItem(placement: .navigationBarLeading) {
+ ToolbarItem(placement: .barLeading) {
Button(
action: { viewModel.showCreateEmailModal = false },
label: { Text("Cancel").foregroundColor(.appGrayTextContrast) }
)
}
- ToolbarItem(placement: .navigationBarTrailing) {
+ ToolbarItem(placement: .barTrailing) {
Button(
action: {
viewModel.createLabel(
diff --git a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift
index 03c20e65d..bdd97a4ad 100644
--- a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift
+++ b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift
@@ -22,6 +22,7 @@ public extension Color {
static var systemBackground: Color { Color(.systemBackground) }
static var systemPlaceholder: Color { Color(.placeholderText) }
static var secondarySystemGroupedBackground: Color { Color(.secondarySystemGroupedBackground) }
+ static var systemGray6: Color { Color(.systemGray6) }
static var systemLabel: Color {
if #available(iOS 15.0, *) {
return Color(uiColor: .label)
@@ -34,6 +35,7 @@ public extension Color {
static var systemBackground: Color { Color(.windowBackgroundColor) }
static var systemPlaceholder: Color { Color(.placeholderTextColor) }
static var systemLabel: Color { Color(.labelColor) }
+ static var systemGray6: Color { Color(NSColor.systemGray) }
// Just for compilation. secondarySystemGroupedBackground shouldn't be used on macOS
static var secondarySystemGroupedBackground: Color { Color(.windowBackgroundColor) }
diff --git a/apple/OmnivoreKit/Sources/Views/TextChip.swift b/apple/OmnivoreKit/Sources/Views/TextChip.swift
index 83ee595a9..c07ce3463 100644
--- a/apple/OmnivoreKit/Sources/Views/TextChip.swift
+++ b/apple/OmnivoreKit/Sources/Views/TextChip.swift
@@ -33,7 +33,7 @@ public struct TextChip: View {
public struct TextChipButton: View {
public static func makeAddLabelButton(onTap: @escaping () -> Void) -> TextChipButton {
- TextChipButton(title: "Labels", color: Color(.systemGray6), actionType: .show, onTap: onTap)
+ TextChipButton(title: "Labels", color: .systemGray6, actionType: .show, onTap: onTap)
}
public static func makeShowOptionsButton(title: String, onTap: @escaping () -> Void) -> TextChipButton {
diff --git a/apple/OmnivoreKit/Sources/Views/Utils/ToolbarItemPlacementExtension.swift b/apple/OmnivoreKit/Sources/Views/Utils/ToolbarItemPlacementExtension.swift
new file mode 100644
index 000000000..f4800a5d1
--- /dev/null
+++ b/apple/OmnivoreKit/Sources/Views/Utils/ToolbarItemPlacementExtension.swift
@@ -0,0 +1,19 @@
+import SwiftUI
+
+public extension ToolbarItemPlacement {
+ static var barLeading: ToolbarItemPlacement {
+ #if os(iOS)
+ .navigationBarLeading
+ #else
+ .automatic
+ #endif
+ }
+
+ static var barTrailing: ToolbarItemPlacement {
+ #if os(iOS)
+ .navigationBarTrailing
+ #else
+ .automatic
+ #endif
+ }
+}
From adaadd27d6df350be1146cff02a5de0c945a9a03 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Wed, 13 Apr 2022 20:03:41 -0700
Subject: [PATCH 114/125] remove labels from swipe actions
---
.../Sources/App/Views/Home/HomeFeedViewIOS.swift | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
index a5440b461..23a610b1e 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
@@ -226,13 +226,7 @@ import Views
}
if #available(iOS 15.0, *) {
link
- .swipeActions(edge: .trailing, allowsFullSwipe: false) {
- Button(
- action: { viewModel.itemUnderLabelEdit = item },
- label: { Label("Edit Labels", systemImage: "tag") }
- )
- }
- .swipeActions(edge: .trailing, allowsFullSwipe: false) {
+ .swipeActions(edge: .trailing, allowsFullSwipe: true) {
if !item.isArchived {
Button {
withAnimation(.linear(duration: 0.4)) {
@@ -251,7 +245,7 @@ import Views
}.tint(.indigo)
}
}
- .swipeActions(edge: .trailing, allowsFullSwipe: false) {
+ .swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button(
role: .destructive,
action: {
From a5885fe16cbb114f673ad7af8b3d9d43aa085e3d Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Wed, 13 Apr 2022 20:46:49 -0700
Subject: [PATCH 115/125] Push props down for lineheight, margin width to keep
in sync
---
.../templates/article/ArticleActionsMenu.tsx | 9 ++--
.../article/ReaderSettingsControl.tsx | 9 +++-
.../web/pages/[username]/[slug]/index.tsx | 41 ++++++++-----------
3 files changed, 29 insertions(+), 30 deletions(-)
diff --git a/packages/web/components/templates/article/ArticleActionsMenu.tsx b/packages/web/components/templates/article/ArticleActionsMenu.tsx
index fd6a0623c..880cfea9a 100644
--- a/packages/web/components/templates/article/ArticleActionsMenu.tsx
+++ b/packages/web/components/templates/article/ArticleActionsMenu.tsx
@@ -16,6 +16,8 @@ export type ArticleActionsMenuLayout = 'horizontal' | 'vertical'
type ArticleActionsMenuProps = {
article: ArticleAttributes
layout: ArticleActionsMenuLayout
+ lineHeight: number
+ marginWidth: number
articleActionHandler: (action: string, arg?: unknown) => void
}
@@ -54,9 +56,6 @@ const ActionDropdown = (props: ActionDropdownProps): JSX.Element => {
}
export function ArticleActionsMenu(props: ArticleActionsMenuProps): JSX.Element {
- const [lineHeight, setLineHeight] = usePersistedState({ key: 'lineHeight', initialValue: 150 })
- const [marginWidth, setMarginWidth] = usePersistedState({ key: 'marginWidth', initialValue: 200 })
-
return (
<>
diff --git a/packages/web/components/templates/article/ReaderSettingsControl.tsx b/packages/web/components/templates/article/ReaderSettingsControl.tsx
index e720b3fc8..4e5f731aa 100644
--- a/packages/web/components/templates/article/ReaderSettingsControl.tsx
+++ b/packages/web/components/templates/article/ReaderSettingsControl.tsx
@@ -2,7 +2,7 @@ import { HStack, VStack, SpanBox } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
import { StyledText } from '../../elements/StyledText'
import { styled, theme } from '../../tokens/stitches.config'
-import { useState } from 'react'
+import { useEffect, useState } from 'react'
import { AlignCenterHorizontalSimple, ArrowsInLineHorizontal, ArrowsOutLineHorizontal, Minus, Pen, Plus, Trash, X } from 'phosphor-react'
import { AIcon } from '../../elements/images/AIcon'
import { TickedRangeSlider } from '../../elements/TickedRangeSlider'
@@ -25,6 +25,11 @@ export function ReaderSettingsControl(props: ReaderSettingsProps): JSX.Element {
const [lineHeight, setLineHeight] = useState(props.lineHeight)
const [marginWidth, setMarginWidth] = useState(props.marginWidth)
+ useEffect(() => {
+ setLineHeight(props.lineHeight)
+ setMarginWidth(props.marginWidth)
+ }, [props.lineHeight, props.marginWidth, setLineHeight, setMarginWidth])
+
return (
{
- setMarginWidth(200)
+ setMarginWidth(290)
setLineHeight(150)
props.articleActionHandler('resetReaderSettings')
showSuccessToast('Display settings reset', { position: 'bottom-right' })
diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx
index 77d78c3d4..282bbf0d4 100644
--- a/packages/web/pages/[username]/[slug]/index.tsx
+++ b/packages/web/pages/[username]/[slug]/index.tsx
@@ -67,23 +67,12 @@ export default function Home(): JSX.Element {
useKeyboardShortcuts(navigationCommands(router))
- const updateFontSize = useCallback(async(newFontSize: number) => {
- window?.localStorage.setItem("fontSize", newFontSize.toString())
- setFontSize(newFontSize)
- await userPersonalizationMutation({ fontSize: newFontSize })
- }, [fontSize, setFontSize])
+ const actionHandler = useCallback(async(action: string, arg?: unknown) => {
+ const updateFontSize = async(newFontSize: number) => {
+ setFontSize(newFontSize)
+ await userPersonalizationMutation({ fontSize: newFontSize })
+ }
- const updateLineHeight = useCallback(async(newLineHeight: number) => {
- window?.localStorage.setItem("lineHeight", newLineHeight.toString())
- setLineHeight(newLineHeight)
- }, [lineHeight, setLineHeight])
-
- const updateMarginWidth = useCallback(async(newMargin: number) => {
- window?.localStorage.setItem("marginWidth", newMargin.toString())
- setMarginWidth(newMargin)
- }, [marginWidth, setMarginWidth])
-
- const actionHandler = async (action: string, arg?: unknown) => {
switch (action) {
case 'archive':
if (article) {
@@ -125,20 +114,20 @@ export default function Home(): JSX.Element {
case 'setMarginWidth': {
const value = Number(arg)
if (value >= 200 && value <= 560) {
- updateMarginWidth(value)
+ setMarginWidth(value)
}
break
}
case 'incrementMarginWidth':
- updateMarginWidth(Math.min(marginWidth + 45, 560))
+ setMarginWidth(Math.min(marginWidth + 45, 560))
break
case 'decrementMarginWidth':
- updateMarginWidth(Math.max(marginWidth - 45, 200))
+ setMarginWidth(Math.max(marginWidth - 45, 200))
break
case 'setLineHeight': {
const value = Number(arg)
if (value >= 100 && value <= 300) {
- updateLineHeight(arg as number)
+ setLineHeight(arg as number)
}
break
}
@@ -152,12 +141,14 @@ export default function Home(): JSX.Element {
}
case 'resetReaderSettings': {
updateFontSize(20)
- updateMarginWidth(200)
- updateLineHeight(150)
+ setMarginWidth(290)
+ setLineHeight(150)
break
}
}
- };
+ }, [article, cache, mutate, router,
+ fontSize, setFontSize, lineHeight,
+ setLineHeight, marginWidth, setMarginWidth])
useKeyboardShortcuts(
articleKeyboardCommands(router, async (action) => {
@@ -174,6 +165,8 @@ export default function Home(): JSX.Element {
}
@@ -207,6 +200,8 @@ export default function Home(): JSX.Element {
) : null}
From 88d8ca7b7ddf75f156c5617513700aed89592aa1 Mon Sep 17 00:00:00 2001
From: Hongbo Wu
Date: Thu, 14 Apr 2022 12:09:17 +0800
Subject: [PATCH 116/125] fix reminders resolver getting page from postgres
---
packages/api/src/datalayer/reminders/model.ts | 2 +
packages/api/src/entity/reminder.ts | 3 ++
packages/api/src/resolvers/reminders/index.ts | 46 +++++++++++--------
3 files changed, 31 insertions(+), 20 deletions(-)
diff --git a/packages/api/src/datalayer/reminders/model.ts b/packages/api/src/datalayer/reminders/model.ts
index d10333e47..0bfad531d 100644
--- a/packages/api/src/datalayer/reminders/model.ts
+++ b/packages/api/src/datalayer/reminders/model.ts
@@ -32,6 +32,7 @@ export interface ReminderData {
createdAt: Date
updatedAt?: Date
remindAt: Date
+ elasticPageId?: string
}
export const keys = [
@@ -46,6 +47,7 @@ export const keys = [
'status',
'createdAt',
'updatedAt',
+ 'elasticPageId',
] as const
export const defaultedKeys = ['id', 'updatedAt', 'status'] as const
diff --git a/packages/api/src/entity/reminder.ts b/packages/api/src/entity/reminder.ts
index e5dcd9ae1..a9ec91221 100644
--- a/packages/api/src/entity/reminder.ts
+++ b/packages/api/src/entity/reminder.ts
@@ -44,4 +44,7 @@ export class Reminder {
@UpdateDateColumn()
updatedAt?: Date
+
+ @Column('text')
+ elasticPageId?: string
}
diff --git a/packages/api/src/resolvers/reminders/index.ts b/packages/api/src/resolvers/reminders/index.ts
index 807316b4d..7b11d9fe3 100644
--- a/packages/api/src/resolvers/reminders/index.ts
+++ b/packages/api/src/resolvers/reminders/index.ts
@@ -23,9 +23,10 @@ import { env } from '../../env'
import { ReminderData } from '../../datalayer/reminders/model'
import { DataModels } from '../types'
import { DateTime } from 'luxon'
-import { UserArticleData } from '../../datalayer/links/model'
import { ArticleSavingRequestData } from '../../datalayer/article_saving_request/model'
import { setLinkArchived } from '../../services/archive_link'
+import { getPageById } from '../../elastic/pages'
+import { Page } from '../../elastic/types'
const validScheduleTime = (str: string): Date | undefined => {
const scheduleTime = DateTime.fromISO(str, { setZone: true }).set({
@@ -87,17 +88,25 @@ export const createReminderResolver = authorized<
try {
if (articleId) {
// saving from web
- const link = await models.userArticle.getByArticleId(uid, articleId)
- if (!link) {
- log.error('link not found', articleId)
+ const page = await getPageById(articleId)
+ if (!page) {
+ log.error('page not found', articleId)
return {
errorCodes: [CreateReminderErrorCode.NotFound],
}
}
- linkId = link.id
+
+ if (page.userId !== uid) {
+ log.error('user not authorized', uid)
+
+ return {
+ errorCodes: [CreateReminderErrorCode.Unauthorized],
+ }
+ }
+ linkId = page.id
if (archiveUntil) {
- await archiveLinkOrRequest(uid, link, undefined, models)
+ await archiveLinkOrRequest(uid, page, undefined)
}
}
@@ -116,7 +125,7 @@ export const createReminderResolver = authorized<
articleSavingRequestId = articleSavingRequest.id
if (articleSavingRequest.articleId) {
- await archiveLinkOrRequest(uid, undefined, articleSavingRequest, models)
+ await archiveLinkOrRequest(uid, undefined, articleSavingRequest)
}
}
@@ -126,7 +135,6 @@ export const createReminderResolver = authorized<
// insert reminder to db
const reminder = await models.reminder.create({
userId: uid,
- linkId: linkId ? linkId : undefined,
articleSavingRequestId: articleSavingRequestId
? articleSavingRequestId
: undefined,
@@ -135,6 +143,7 @@ export const createReminderResolver = authorized<
sendNotification: sendNotification,
createdAt: new Date(),
remindAt: scheduledTime,
+ elasticPageId: linkId,
})
console.log('created reminder', reminder)
@@ -161,16 +170,13 @@ export const createReminderResolver = authorized<
// case it will be archived when the link is created.
const archiveLinkOrRequest = async (
uid: string,
- link: UserArticleData | undefined,
- request: ArticleSavingRequestData | undefined,
- models: DataModels
+ page: Page | undefined,
+ request: ArticleSavingRequestData | undefined
) => {
- let target: UserArticleData | undefined = link || undefined
- if (!link && request && request.articleId) {
+ let target: Page | undefined = page || undefined
+ if (!page && request && request.articleId) {
// this linkId is actually an article.id
- target =
- (await models.userArticle.getByArticleId(uid, request.articleId)) ||
- undefined
+ target = (await getPageById(request.articleId)) || undefined
}
console.log('archiving target', target)
if (target) {
@@ -200,9 +206,9 @@ export const reminderResolver = authorized<
try {
let reminder: ReminderData | null
- // get link from articleId
- const link = await models.userArticle.getByArticleId(uid, articleId)
- if (!link) {
+ // get page from articleId
+ const page = await getPageById(articleId)
+ if (!page) {
// link may not be saved yet
// check savingArticleRequest table
const articleSavingRequest =
@@ -224,7 +230,7 @@ export const reminderResolver = authorized<
})
} else {
reminder = await models.reminder.getCreatedByParameters(uid, {
- linkId: link.id,
+ elasticPageId: page.id,
})
}
From c0966d908f59ef7d2d243baafbe431b13e22019f Mon Sep 17 00:00:00 2001
From: Hongbo Wu
Date: Thu, 14 Apr 2022 12:14:15 +0800
Subject: [PATCH 117/125] upload highlight and label to gcs if successfully
updated in elastic (#420)
---
packages/api/src/elastic/highlights.ts | 4 +-
packages/api/src/elastic/labels.ts | 44 ++++++++++++++++++++--
packages/api/src/resolvers/labels/index.ts | 20 +++++-----
3 files changed, 53 insertions(+), 15 deletions(-)
diff --git a/packages/api/src/elastic/highlights.ts b/packages/api/src/elastic/highlights.ts
index 934563ca3..8277f7b9b 100644
--- a/packages/api/src/elastic/highlights.ts
+++ b/packages/api/src/elastic/highlights.ts
@@ -134,7 +134,7 @@ export const deleteHighlight = async (
refresh: ctx.refresh,
})
- if (body.result !== 'updated') return false
+ if (body.updated === 0) return false
await ctx.pubsub.entityDeleted(EntityType.HIGHLIGHT, highlightId, ctx.uid)
@@ -279,7 +279,7 @@ export const updateHighlight = async (
refresh: ctx.refresh,
})
- if (body.result !== 'updated') return false
+ if (body.updated === 0) return false
await ctx.pubsub.entityUpdated(
EntityType.HIGHLIGHT,
diff --git a/packages/api/src/elastic/labels.ts b/packages/api/src/elastic/labels.ts
index 2bb00bba8..69af5365e 100644
--- a/packages/api/src/elastic/labels.ts
+++ b/packages/api/src/elastic/labels.ts
@@ -3,14 +3,14 @@ import { client, INDEX_ALIAS } from './index'
import { EntityType } from '../datalayer/pubsub'
export const addLabelInPage = async (
- id: string,
+ pageId: string,
label: Label,
ctx: PageContext
): Promise => {
try {
const { body } = await client.update({
index: INDEX_ALIAS,
- id,
+ id: pageId,
body: {
script: {
source: `if (ctx._source.labels == null) {
@@ -32,7 +32,7 @@ export const addLabelInPage = async (
await ctx.pubsub.entityCreated(
EntityType.LABEL,
- { pageId: id, ...label },
+ { pageId, ...label },
ctx.uid
)
@@ -43,6 +43,42 @@ export const addLabelInPage = async (
}
}
+export const updateLabelsInPage = async (
+ pageId: string,
+ labels: Label[],
+ ctx: PageContext
+): Promise => {
+ try {
+ const { body } = await client.update({
+ index: INDEX_ALIAS,
+ id: pageId,
+ body: {
+ doc: {
+ labels: labels,
+ updatedAt: new Date(),
+ },
+ },
+ refresh: ctx.refresh,
+ retry_on_conflict: 3,
+ })
+
+ if (body.result !== 'updated') return false
+
+ for (const label of labels) {
+ await ctx.pubsub.entityCreated(
+ EntityType.LABEL,
+ { pageId, ...label },
+ ctx.uid
+ )
+ }
+
+ return true
+ } catch (e) {
+ console.error('failed to update labels in elastic', e)
+ return false
+ }
+}
+
export const deleteLabelInPages = async (
userId: string,
label: string,
@@ -85,7 +121,7 @@ export const deleteLabelInPages = async (
refresh: ctx.refresh,
})
- if (body.result !== 'updated') return false
+ if (body.updated === 0) return false
await ctx.pubsub.entityDeleted(EntityType.LABEL, label, ctx.uid)
diff --git a/packages/api/src/resolvers/labels/index.ts b/packages/api/src/resolvers/labels/index.ts
index a8b8b83b2..1ec144d01 100644
--- a/packages/api/src/resolvers/labels/index.ts
+++ b/packages/api/src/resolvers/labels/index.ts
@@ -28,8 +28,8 @@ import { ILike, In } from 'typeorm'
import { getRepository, setClaims } from '../../entity/utils'
import { createPubSubClient } from '../../datalayer/pubsub'
import { AppDataSource } from '../../server'
-import { getPageById, updatePage } from '../../elastic/pages'
-import { deleteLabelInPages } from '../../elastic/labels'
+import { getPageById } from '../../elastic/pages'
+import { deleteLabelInPages, updateLabelsInPage } from '../../elastic/labels'
export const labelsResolver = authorized(
async (_obj, _params, { claims: { uid }, log }) => {
@@ -231,13 +231,15 @@ export const setLabelsResolver = authorized<
}
// update labels in the page
- await updatePage(
- pageId,
- {
- labels,
- },
- { pubsub, uid }
- )
+ const updated = await updateLabelsInPage(pageId, labels, {
+ pubsub,
+ uid,
+ })
+ if (!updated) {
+ return {
+ errorCodes: [SetLabelsErrorCode.NotFound],
+ }
+ }
analytics.track({
userId: uid,
From 72a231c97e84081bc49e015c098464402efe389d Mon Sep 17 00:00:00 2001
From: Hongbo Wu
Date: Thu, 14 Apr 2022 12:59:12 +0800
Subject: [PATCH 118/125] Do not proxy image data uri (#421)
* do not proxy image data uri
* rename data uri
* rename data uri in test
---
packages/readabilityjs/Readability.js | 8 +++++---
packages/readabilityjs/test/test-readability.js | 11 +++++++++++
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/packages/readabilityjs/Readability.js b/packages/readabilityjs/Readability.js
index 36d09102c..c68b5de88 100644
--- a/packages/readabilityjs/Readability.js
+++ b/packages/readabilityjs/Readability.js
@@ -504,15 +504,17 @@ Readability.prototype = {
});
},
- /** Creates imageproxy links for all article images */
+ /** Creates imageproxy links for all article images with href source */
_createImageProxyLinks: function (articleContent) {
if (this.createImageProxyUrl !== undefined) {
- // replace all image src's
+ // replace all images' href source
const images = articleContent.getElementsByTagName('img');
Array.from(images).forEach(image => {
const src = image.getAttribute("src");
+ const dataUriRegex = /^data:image\/(?:png|jpe?g|gif);base64,/;
- if (src) {
+ // do not proxy data uri
+ if (src && !dataUriRegex.test(src)) {
const absoluteSrc = this.toAbsoluteURI(src);
const attToNumber = (str) => {
if (!str) { return 0; }
diff --git a/packages/readabilityjs/test/test-readability.js b/packages/readabilityjs/test/test-readability.js
index 7c80abfe6..44c00e44b 100644
--- a/packages/readabilityjs/test/test-readability.js
+++ b/packages/readabilityjs/test/test-readability.js
@@ -270,6 +270,17 @@ describe("Readability API", function() {
}).parse().content;
expect(content).eql(expected_xhtml);
});
+
+ it("should not proxy image with data uri", function() {
+ var dom = new JSDOM("My cat: ");
+ var expected_xhtml = "My cat:
";
+ var content = new Readability(dom.window.document).parse().content;
+ expect(content).eql(expected_xhtml);
+ });
});
});
From 3befff61af0aa375193dfdcddc5e7732ed4f16ee Mon Sep 17 00:00:00 2001
From: Hongbo Wu
Date: Thu, 14 Apr 2022 13:22:21 +0800
Subject: [PATCH 119/125] fix tests
---
packages/api/test/db.ts | 30 +++++++++----------
packages/api/test/resolvers/reminders.test.ts | 18 +++++------
2 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/packages/api/test/db.ts b/packages/api/test/db.ts
index 182248196..d7b75c4c4 100644
--- a/packages/api/test/db.ts
+++ b/packages/api/test/db.ts
@@ -1,16 +1,16 @@
-import Postgrator from 'postgrator'
-import { User } from '../src/entity/user'
-import { Profile } from '../src/entity/profile'
-import { Page } from '../src/entity/page'
-import { Link } from '../src/entity/link'
-import { Reminder } from '../src/entity/reminder'
-import { NewsletterEmail } from '../src/entity/newsletter_email'
-import { UserDeviceToken } from '../src/entity/user_device_tokens'
-import { Label } from '../src/entity/label'
-import { AppDataSource } from '../src/server'
-import { getRepository } from '../src/entity/utils'
-import { createUser } from '../src/services/create_user'
-import { SnakeNamingStrategy } from 'typeorm-naming-strategies'
+import Postgrator from "postgrator";
+import { User } from "../src/entity/user";
+import { Profile } from "../src/entity/profile";
+import { Page } from "../src/entity/page";
+import { Link } from "../src/entity/link";
+import { Reminder } from "../src/entity/reminder";
+import { NewsletterEmail } from "../src/entity/newsletter_email";
+import { UserDeviceToken } from "../src/entity/user_device_tokens";
+import { Label } from "../src/entity/label";
+import { AppDataSource } from "../src/server";
+import { getRepository } from "../src/entity/utils";
+import { createUser } from "../src/services/create_user";
+import { SnakeNamingStrategy } from "typeorm-naming-strategies";
const runMigrations = async () => {
const migrationDirectory = __dirname + '/../../db/migrations'
@@ -122,11 +122,11 @@ export const createTestLink = async (user: User, page: Page): Promise => {
export const createTestReminder = async (
user: User,
- link?: string
+ pageId?: string
): Promise => {
return getRepository(Reminder).save({
user: user,
- link: link,
+ elasticPageId: pageId,
remindAt: new Date(),
})
}
diff --git a/packages/api/test/resolvers/reminders.test.ts b/packages/api/test/resolvers/reminders.test.ts
index afc681338..907c5269a 100644
--- a/packages/api/test/resolvers/reminders.test.ts
+++ b/packages/api/test/resolvers/reminders.test.ts
@@ -1,7 +1,10 @@
-import { generateFakeUuid, graphqlRequest, request } from '../util'
import {
- createTestLink,
- createTestPage,
+ createTestElasticPage,
+ generateFakeUuid,
+ graphqlRequest,
+ request,
+} from '../util'
+import {
createTestReminder,
createTestUser,
deleteTestUser,
@@ -14,17 +17,15 @@ import {
ReminderErrorCode,
UpdateReminderErrorCode,
} from '../../src/generated/graphql'
-import { Page } from '../../src/entity/page'
-import { Link } from '../../src/entity/link'
import { DateTime } from 'luxon'
import 'mocha'
+import { Page } from '../../src/elastic/types'
describe('Reminders API', () => {
const username = 'fakeUser'
let authToken: string
let page: Page
- let link: Link
let reminder: Reminder
before(async () => {
@@ -37,9 +38,8 @@ describe('Reminders API', () => {
authToken = res.body.authToken
// create page, link and reminders test data
- page = await createTestPage()
- link = await createTestLink(user, page)
- reminder = await createTestReminder(user, link.id)
+ page = await createTestElasticPage(user)
+ reminder = await createTestReminder(user, page.id)
})
after(async () => {
From 5e075fc312ef657ecda62cf8f3f2e3a3a88709da Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 14 Apr 2022 15:48:41 +0000
Subject: [PATCH 120/125] Bump @sentry/nextjs from 6.17.7 to 6.19.6
Bumps [@sentry/nextjs](https://github.com/getsentry/sentry-javascript) from 6.17.7 to 6.19.6.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.17.7...6.19.6)
---
updated-dependencies:
- dependency-name: "@sentry/nextjs"
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
yarn.lock | 231 ++++++++++++++++++++++++------------------------------
1 file changed, 104 insertions(+), 127 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index 03be08045..e68adb18b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4681,20 +4681,20 @@
"@sendgrid/client" "^7.6.0"
"@sendgrid/helpers" "^7.6.0"
-"@sentry/browser@6.17.7":
- version "6.17.7"
- resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.17.7.tgz#04b72ae29a56c2001be525fa39c17aa7d9992e7f"
- integrity sha512-0Ad6TmB5KH5o152Hgk5tlxNiooV0Rfoj7HEzxdOnHFkl57aR7VsiPkzIBl9vxn4iyy7IheUONhHSOU1osJkv2w==
+"@sentry/browser@6.19.6":
+ version "6.19.6"
+ resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.19.6.tgz#75be467667fffa1f4745382fc7a695568609c634"
+ integrity sha512-V5QyY1cO1iuFCI78dOFbHV7vckbeQEPPq3a5dGSXlBQNYnd9Ec5xoxp5nRNpWQPOZ8/Ixt9IgRxdqVTkWib51g==
dependencies:
- "@sentry/core" "6.17.7"
- "@sentry/types" "6.17.7"
- "@sentry/utils" "6.17.7"
+ "@sentry/core" "6.19.6"
+ "@sentry/types" "6.19.6"
+ "@sentry/utils" "6.19.6"
tslib "^1.9.3"
-"@sentry/cli@^1.72.0":
- version "1.72.2"
- resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.72.2.tgz#ba57d8248fe4d2836883bbe3047fdd75cd6739b3"
- integrity sha512-H/yrxyYscOzn0YBOzPsBnIW7QxsSQrG1kQfD1FisuMhPl67D948DPkagAms4sy1v+MntBBNnD4Z+WsdRzFWXJQ==
+"@sentry/cli@^1.73.0":
+ version "1.74.3"
+ resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.3.tgz#8405a19f6bb21b2ff3d051fb8a18056cc796c5ae"
+ integrity sha512-74NiqWTgTFDPe2S99h1ge5UMe6aAC44ebareadd1P6MdaNfYz6JUEa2QrDfMq7TKccEiRFXhXBHbUI8mxzrzuQ==
dependencies:
https-proxy-agent "^5.0.0"
mkdirp "^0.5.5"
@@ -4702,6 +4702,7 @@
npmlog "^4.1.2"
progress "^2.0.3"
proxy-from-env "^1.1.0"
+ which "^2.0.2"
"@sentry/core@5.30.0":
version "5.30.0"
@@ -4714,17 +4715,6 @@
"@sentry/utils" "5.30.0"
tslib "^1.9.3"
-"@sentry/core@6.17.7":
- version "6.17.7"
- resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.17.7.tgz#f591235c06b1a4e75d748b15c539e071bd3f5cf5"
- integrity sha512-SRhLkD05lQb4eCt1ed9Dz72DKbRDlM8PJix8eC2oJLtwyFTS0IlJNkIYRrbsSKkJUm0VsKcDkzIHvUAgBBQICw==
- dependencies:
- "@sentry/hub" "6.17.7"
- "@sentry/minimal" "6.17.7"
- "@sentry/types" "6.17.7"
- "@sentry/utils" "6.17.7"
- tslib "^1.9.3"
-
"@sentry/core@6.19.3":
version "6.19.3"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.19.3.tgz#88268afc8c42716c455ad77bb4bed2bbf96abd83"
@@ -4736,6 +4726,17 @@
"@sentry/utils" "6.19.3"
tslib "^1.9.3"
+"@sentry/core@6.19.6":
+ version "6.19.6"
+ resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.19.6.tgz#7d4649d0148b5d0be1358ab02e2f869bf7363e9a"
+ integrity sha512-biEotGRr44/vBCOegkTfC9rwqaqRKIpFljKGyYU6/NtzMRooktqOhjmjmItNCMRknArdeaQwA8lk2jcZDXX3Og==
+ dependencies:
+ "@sentry/hub" "6.19.6"
+ "@sentry/minimal" "6.19.6"
+ "@sentry/types" "6.19.6"
+ "@sentry/utils" "6.19.6"
+ tslib "^1.9.3"
+
"@sentry/hub@5.30.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100"
@@ -4745,15 +4746,6 @@
"@sentry/utils" "5.30.0"
tslib "^1.9.3"
-"@sentry/hub@6.17.7":
- version "6.17.7"
- resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.17.7.tgz#5c90d661e263dad7da0e0106f1cb90cf797d93a7"
- integrity sha512-siGzcg+quGOdjRaBGAz6T3ycwHUsGgvalptSJdf5Q783FVFhU+haPul++zGOYURXOgx0RjYGWqagwO8+jljl3Q==
- dependencies:
- "@sentry/types" "6.17.7"
- "@sentry/utils" "6.17.7"
- tslib "^1.9.3"
-
"@sentry/hub@6.19.3":
version "6.19.3"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.19.3.tgz#d555c83404f19ac9b68f336b051b8e7a9d75feb0"
@@ -4763,23 +4755,22 @@
"@sentry/utils" "6.19.3"
tslib "^1.9.3"
-"@sentry/integrations@6.17.7":
- version "6.17.7"
- resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-6.17.7.tgz#c14f409fec91743db4a21232413de235946cb6d3"
- integrity sha512-/BYVoLHY5gUZNan6bSlsRYX44Qi2UlusLuwgEc1HZm9m4C2h4mVwhHy0XlhBAYWCr0q28ggtiFNzIxqHSKZu6g==
+"@sentry/hub@6.19.6":
+ version "6.19.6"
+ resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.19.6.tgz#ada83ceca0827c49534edfaba018221bc1eb75e1"
+ integrity sha512-PuEOBZxvx3bjxcXmWWZfWXG+orojQiWzv9LQXjIgroVMKM/GG4QtZbnWl1hOckUj7WtKNl4hEGO2g/6PyCV/vA==
dependencies:
- "@sentry/types" "6.17.7"
- "@sentry/utils" "6.17.7"
- localforage "^1.8.1"
+ "@sentry/types" "6.19.6"
+ "@sentry/utils" "6.19.6"
tslib "^1.9.3"
-"@sentry/integrations@^6.19.1":
- version "6.19.1"
- resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-6.19.1.tgz#ccc5da6b20b9ba9ff4e8ed79c92b512f8088fa8d"
- integrity sha512-0ViIiwx9BlPi8jvT97ERpJWmRbBUThGOyjNVzWxaa07OEjOgKI6cNgsBHWbPjaCM00WSzaOFOtYiC4464+Mucg==
+"@sentry/integrations@6.19.6", "@sentry/integrations@^6.19.1":
+ version "6.19.6"
+ resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-6.19.6.tgz#157152f16a8ad8df8a97d08bfe740909446b075a"
+ integrity sha512-K2xuA/ByhTh3qfIe0/XIsQSNf1HrRuIgtkC4TbU7T0QosybtXDsh6t/EWK+qzs2RjVE+Iaqldihstpoyew1JgA==
dependencies:
- "@sentry/types" "6.19.1"
- "@sentry/utils" "6.19.1"
+ "@sentry/types" "6.19.6"
+ "@sentry/utils" "6.19.6"
localforage "^1.8.1"
tslib "^1.9.3"
@@ -4792,15 +4783,6 @@
"@sentry/types" "5.30.0"
tslib "^1.9.3"
-"@sentry/minimal@6.17.7":
- version "6.17.7"
- resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.17.7.tgz#f19182047f19b563f40a30d45d2ce9ad7df1ec4e"
- integrity sha512-+/FGem1uXsXikX9wHPw44nevO7YTVjkkiPjyLsvnWMjv64r4Au5s+NQSFHDaytRm9IlU//+OasCAS5VAwHcYRg==
- dependencies:
- "@sentry/hub" "6.17.7"
- "@sentry/types" "6.17.7"
- tslib "^1.9.3"
-
"@sentry/minimal@6.19.3":
version "6.19.3"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.19.3.tgz#b9b7f0d7f0cd2341b243318668ac01458f9d7889"
@@ -4810,34 +4792,28 @@
"@sentry/types" "6.19.3"
tslib "^1.9.3"
-"@sentry/nextjs@^6.16.1":
- version "6.17.7"
- resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-6.17.7.tgz#00e0380ba2b709674e495b3c00b24ee82405575d"
- integrity sha512-X9XfO3vDDv3YEnOc7taGK6Sb1wfBlL3cc1jcMoOma8qW9z+8tDoM3NBPp2uOmWdfo28D0hxVOIvqFa/fiKbbEg==
+"@sentry/minimal@6.19.6":
+ version "6.19.6"
+ resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.19.6.tgz#b6cced3708e25d322039e68ebdf8fadfa445bf7d"
+ integrity sha512-T1NKcv+HTlmd8EbzUgnGPl4ySQGHWMCyZ8a8kXVMZOPDzphN3fVIzkYzWmSftCWp0rpabXPt9aRF2mfBKU+mAQ==
dependencies:
- "@sentry/core" "6.17.7"
- "@sentry/hub" "6.17.7"
- "@sentry/integrations" "6.17.7"
- "@sentry/node" "6.17.7"
- "@sentry/react" "6.17.7"
- "@sentry/tracing" "6.17.7"
- "@sentry/utils" "6.17.7"
- "@sentry/webpack-plugin" "1.18.5"
+ "@sentry/hub" "6.19.6"
+ "@sentry/types" "6.19.6"
tslib "^1.9.3"
-"@sentry/node@6.17.7":
- version "6.17.7"
- resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.17.7.tgz#c142d93328b29312098276e0cb66ec3b9e805a93"
- integrity sha512-YlBhEv8YYUimHLpwzUP5lXkagbGxGAfjvsbahhvqf7rRl8Fu5XbatAcSDcx7YE4R0Iox94IfZy95kF2NL4Idow==
+"@sentry/nextjs@^6.16.1":
+ version "6.19.6"
+ resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-6.19.6.tgz#419205659c0fe2b7695bb7c892c9014b55ddd01b"
+ integrity sha512-xV6yj9H1Ieg4uSS4SsT1x5GvrWdifuBNLPWrneQ89kWBuPVFLLH1wZA0gvDuq6AJstRZ3A5pWI2IbwpmQzxMWQ==
dependencies:
- "@sentry/core" "6.17.7"
- "@sentry/hub" "6.17.7"
- "@sentry/tracing" "6.17.7"
- "@sentry/types" "6.17.7"
- "@sentry/utils" "6.17.7"
- cookie "^0.4.1"
- https-proxy-agent "^5.0.0"
- lru_map "^0.3.3"
+ "@sentry/core" "6.19.6"
+ "@sentry/hub" "6.19.6"
+ "@sentry/integrations" "6.19.6"
+ "@sentry/node" "6.19.6"
+ "@sentry/react" "6.19.6"
+ "@sentry/tracing" "6.19.6"
+ "@sentry/utils" "6.19.6"
+ "@sentry/webpack-plugin" "1.18.8"
tslib "^1.9.3"
"@sentry/node@6.19.3":
@@ -4854,6 +4830,20 @@
lru_map "^0.3.3"
tslib "^1.9.3"
+"@sentry/node@6.19.6":
+ version "6.19.6"
+ resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.19.6.tgz#d63c4ffcf0150b4175a2e4e5021b53af46e5946f"
+ integrity sha512-kHQMfsy40ZxxdS9zMPmXCOOLWOJbQj6/aVSHt/L1QthYcgkAi7NJQNXnQIPWQDe8eP3DfNIWM7dc446coqjXrQ==
+ dependencies:
+ "@sentry/core" "6.19.6"
+ "@sentry/hub" "6.19.6"
+ "@sentry/types" "6.19.6"
+ "@sentry/utils" "6.19.6"
+ cookie "^0.4.1"
+ https-proxy-agent "^5.0.0"
+ lru_map "^0.3.3"
+ tslib "^1.9.3"
+
"@sentry/node@^5.26.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48"
@@ -4869,15 +4859,15 @@
lru_map "^0.3.3"
tslib "^1.9.3"
-"@sentry/react@6.17.7":
- version "6.17.7"
- resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.17.7.tgz#550735bd9b56f20f15059f10a4573c80d5c882a5"
- integrity sha512-rt6nAa9SJ+A4va88FASBoR5/26V2uZx/yucT9NzwoBIW41jA8lAwTmFu5MdtxGDBKDUSJlL3lt/Nkiq6LkiTaw==
+"@sentry/react@6.19.6":
+ version "6.19.6"
+ resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.19.6.tgz#4c07168637bfcef4d6556a2c4548b74a61eaed87"
+ integrity sha512-RnWZ7clg1lRgf/JFNnTOs8ZPCv566E5CwFXXb6swyjPYUMcIn95XujDQU9SU4hXZ4qXd9BRvifxqyxvq0LMXNw==
dependencies:
- "@sentry/browser" "6.17.7"
- "@sentry/minimal" "6.17.7"
- "@sentry/types" "6.17.7"
- "@sentry/utils" "6.17.7"
+ "@sentry/browser" "6.19.6"
+ "@sentry/minimal" "6.19.6"
+ "@sentry/types" "6.19.6"
+ "@sentry/utils" "6.19.6"
hoist-non-react-statics "^3.3.2"
tslib "^1.9.3"
@@ -4906,17 +4896,6 @@
"@sentry/utils" "5.30.0"
tslib "^1.9.3"
-"@sentry/tracing@6.17.7":
- version "6.17.7"
- resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.17.7.tgz#f4536683b29bb3ac7ddda5ca49494731cec6b619"
- integrity sha512-QzIDHOjjdi/0LTdrK2LTC27YEOODI473KD8KmMJ+r9PmjDeIjNzz4hJlPwQSnXR3Mu/8foxGJGXsAt3LNmKzlQ==
- dependencies:
- "@sentry/hub" "6.17.7"
- "@sentry/minimal" "6.17.7"
- "@sentry/types" "6.17.7"
- "@sentry/utils" "6.17.7"
- tslib "^1.9.3"
-
"@sentry/tracing@6.19.3":
version "6.19.3"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.19.3.tgz#dfdbd5019486c899bdf352b1152d5d253544ef70"
@@ -4928,26 +4907,32 @@
"@sentry/utils" "6.19.3"
tslib "^1.9.3"
+"@sentry/tracing@6.19.6":
+ version "6.19.6"
+ resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.19.6.tgz#faa156886afe441730f03cf9ac9c4982044b7135"
+ integrity sha512-STZdlEtTBqRmPw6Vjkzi/1kGkGPgiX0zdHaSOhSeA2HXHwx7Wnfu7veMKxtKWdO+0yW9QZGYOYqp0GVf4Swujg==
+ dependencies:
+ "@sentry/hub" "6.19.6"
+ "@sentry/minimal" "6.19.6"
+ "@sentry/types" "6.19.6"
+ "@sentry/utils" "6.19.6"
+ tslib "^1.9.3"
+
"@sentry/types@5.30.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402"
integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==
-"@sentry/types@6.17.7":
- version "6.17.7"
- resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.17.7.tgz#61946a3f6532b8f21251b264f173b02f9ea2458e"
- integrity sha512-iBlJDhrSowZKeqvutY0tCkUjrWqkLFsHrbaQ553r1Nx+/4mxHjzVYtEVGMjZAxQUEbkm0TbnQIkkT7ltglNJ9A==
-
-"@sentry/types@6.19.1":
- version "6.19.1"
- resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.1.tgz#684a231f8fe10c9cf14f9552f5da0a4492e6fdbc"
- integrity sha512-ovmNYdqD2MKLmru4calxetX1xjJdYim+HEI/GzwvVUYshsaXRq4EiQ17h3DAy90MV7JH279PmMoPGDTOpufq+Q==
-
"@sentry/types@6.19.3":
version "6.19.3"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.3.tgz#94b19da68d4d23561efb1014f72968bcea85cd0c"
integrity sha512-jHhqxp8MIWSfOc3krorirTGKTEaSFO6XrAvi+2AZhr6gvOChwOgzgrN2ZqesJcZmgCsqWV21u3usSwYeRrjOJA==
+"@sentry/types@6.19.6":
+ version "6.19.6"
+ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.6.tgz#70513f9dca05d23d7ab9c2a6cb08d4db6763ca67"
+ integrity sha512-QH34LMJidEUPZK78l+Frt3AaVFJhEmIi05Zf8WHd9/iTt+OqvCHBgq49DDr1FWFqyYWm/QgW/3bIoikFpfsXyQ==
+
"@sentry/utils@5.30.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980"
@@ -4956,22 +4941,6 @@
"@sentry/types" "5.30.0"
tslib "^1.9.3"
-"@sentry/utils@6.17.7":
- version "6.17.7"
- resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.17.7.tgz#0574bf914cc129b5e47041b75bb34dfbe0decbba"
- integrity sha512-HEEEeKlZtwfQvH0waSKv5FKRFjHkVgkkEiAigXoYGQAlaUIuwRTvZGFnsmBoKMIrA4pARkA00FwwdtMU7ziC8A==
- dependencies:
- "@sentry/types" "6.17.7"
- tslib "^1.9.3"
-
-"@sentry/utils@6.19.1":
- version "6.19.1"
- resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.19.1.tgz#9d5f50a0ceb60b63631de88b4abed07253568209"
- integrity sha512-mWcQbQ1yO7PooLpJpQK84+wOfxGeb8iUKRb8inO+2Eg6VksDbXRuJ89Yd4APBTRxBj5Wihy48bPuVfKtovtm8g==
- dependencies:
- "@sentry/types" "6.19.1"
- tslib "^1.9.3"
-
"@sentry/utils@6.19.3":
version "6.19.3"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.19.3.tgz#0c3a3f0b86c12e3b079e56e37a44e62a1226043d"
@@ -4980,12 +4949,20 @@
"@sentry/types" "6.19.3"
tslib "^1.9.3"
-"@sentry/webpack-plugin@1.18.5":
- version "1.18.5"
- resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.18.5.tgz#aaff79d8e05b8d803654490324252406c976b1cd"
- integrity sha512-HycNZEcVRj/LxaG6hLsxjHo47mpxop3j7u2aUkriE2pT7XNpeypsa0WiokYzStxzCfSu8rbAbX4PchTGLMlTjw==
+"@sentry/utils@6.19.6":
+ version "6.19.6"
+ resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.19.6.tgz#2ddc9ef036c3847084c43d0e5a55e4646bdf9021"
+ integrity sha512-fAMWcsguL0632eWrROp/vhPgI7sBj/JROWVPzpabwVkm9z3m1rQm6iLFn4qfkZL8Ozy6NVZPXOQ7EXmeU24byg==
dependencies:
- "@sentry/cli" "^1.72.0"
+ "@sentry/types" "6.19.6"
+ tslib "^1.9.3"
+
+"@sentry/webpack-plugin@1.18.8":
+ version "1.18.8"
+ resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.18.8.tgz#247a73a0aa9e28099a736bbe89ca0d35cbac7636"
+ integrity sha512-PtKr0NL62b5L3kPFGjwSNbIUwwcW5E5G6bQxAYZGpkgL1MFPnS4ND0SAsySuX0byQJRFFium5A19LpzyvQZSlQ==
+ dependencies:
+ "@sentry/cli" "^1.73.0"
"@sindresorhus/is@^0.14.0":
version "0.14.0"
From c80ce4da10dccb5cb8635bc354cb568abdc6d96d Mon Sep 17 00:00:00 2001
From: Jackson Harper
Date: Thu, 14 Apr 2022 10:31:10 -0700
Subject: [PATCH 121/125] Consistent naming convention for events
---
packages/api/src/resolvers/labels/index.ts | 6 +++---
packages/api/src/routers/svc/pdf_attachments.ts | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/packages/api/src/resolvers/labels/index.ts b/packages/api/src/resolvers/labels/index.ts
index 1ec144d01..b86a1ae42 100644
--- a/packages/api/src/resolvers/labels/index.ts
+++ b/packages/api/src/resolvers/labels/index.ts
@@ -108,7 +108,7 @@ export const createLabelResolver = authorized<
analytics.track({
userId: uid,
- event: 'createLabel',
+ event: 'label_created',
properties: {
name,
color,
@@ -178,7 +178,7 @@ export const deleteLabelResolver = authorized<
analytics.track({
userId: uid,
- event: 'deleteLabel',
+ event: 'label_deleted',
properties: {
labelId,
env: env.server.apiEnv,
@@ -243,7 +243,7 @@ export const setLabelsResolver = authorized<
analytics.track({
userId: uid,
- event: 'setLabels',
+ event: 'labels_set',
properties: {
pageId,
labelIds,
diff --git a/packages/api/src/routers/svc/pdf_attachments.ts b/packages/api/src/routers/svc/pdf_attachments.ts
index f2ff1a5cb..c57253632 100644
--- a/packages/api/src/routers/svc/pdf_attachments.ts
+++ b/packages/api/src/routers/svc/pdf_attachments.ts
@@ -44,7 +44,7 @@ export function pdfAttachmentsRouter() {
analytics.track({
userId: user.id,
- event: 'pdf-attachment-upload',
+ event: 'pdf_attachment_upload',
properties: {
env: env.server.apiEnv,
},
@@ -107,7 +107,7 @@ export function pdfAttachmentsRouter() {
analytics.track({
userId: user.id,
- event: 'pdf-attachment-create-article',
+ event: 'pdf_attachment_create_article',
properties: {
env: env.server.apiEnv,
},
From 119a9b553e4650791de6d749cea7e14791babeec Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 14 Apr 2022 18:06:20 +0000
Subject: [PATCH 122/125] Bump async from 2.6.3 to 2.6.4
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)
---
updated-dependencies:
- dependency-name: async
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
---
yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index e68adb18b..6c84d2404 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7597,9 +7597,9 @@ async-retry@^1.2.1, async-retry@^1.3.3:
retry "0.13.1"
async@^2.6.2:
- version "2.6.3"
- resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
- integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
+ version "2.6.4"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
+ integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==
dependencies:
lodash "^4.17.14"
From d772a72c953686fdb5c042c7598a4493a7dccbf7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 14 Apr 2022 18:28:23 +0000
Subject: [PATCH 123/125] Bump async from 3.2.0 to 3.2.3 in /pkg/admin
Bumps [async](https://github.com/caolan/async) from 3.2.0 to 3.2.3.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/master/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v3.2.0...v3.2.3)
---
updated-dependencies:
- dependency-name: async
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
---
pkg/admin/yarn.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/pkg/admin/yarn.lock b/pkg/admin/yarn.lock
index 42d50d340..86eba0ad7 100644
--- a/pkg/admin/yarn.lock
+++ b/pkg/admin/yarn.lock
@@ -1727,9 +1727,9 @@ array-flatten@1.1.1:
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
async@^3.1.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
- integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
+ integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
at-least-node@^1.0.0:
version "1.0.0"
From a24e3617e084852739713249f5d297fd027bc65a Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Thu, 14 Apr 2022 12:26:08 -0700
Subject: [PATCH 124/125] update min supported platform numbers
---
apple/Omnivore.xcodeproj/project.pbxproj | 40 ++++++++++++------------
apple/OmnivoreKit/Package.swift | 8 ++---
2 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/apple/Omnivore.xcodeproj/project.pbxproj b/apple/Omnivore.xcodeproj/project.pbxproj
index 3141c8f72..0a4d64fc6 100644
--- a/apple/Omnivore.xcodeproj/project.pbxproj
+++ b/apple/Omnivore.xcodeproj/project.pbxproj
@@ -1156,7 +1156,7 @@
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11.3;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.UnitTests-MacApp";
@@ -1182,7 +1182,7 @@
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11.3;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.UnitTests-MacApp";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1200,7 +1200,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = QJF2XZ86HB;
INFOPLIST_FILE = InfoPlists/iOSUITests.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 14.5;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1225,7 +1225,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = QJF2XZ86HB;
INFOPLIST_FILE = InfoPlists/iOSUITests.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 14.5;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1260,7 +1260,7 @@
"@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11.0;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.4.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
@@ -1291,7 +1291,7 @@
"@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11.0;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.4.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.ShareExtension-Mac";
@@ -1316,7 +1316,7 @@
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11.3;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.UITests-Mac";
@@ -1341,7 +1341,7 @@
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11.3;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.UITests-Mac";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1373,7 +1373,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11.0;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.4.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
@@ -1407,7 +1407,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11.0;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.4.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = app.omnivore.app;
@@ -1458,7 +1458,7 @@
DEVELOPMENT_TEAM = QJF2XZ86HB;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = InfoPlists/Omnivore.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1572,7 +1572,7 @@
"@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11.0;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.4.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
@@ -1610,7 +1610,7 @@
"@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 11.0;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.4.0;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
@@ -1674,8 +1674,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 14.1;
- MACOSX_DEPLOYMENT_TARGET = 10.15;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
VALIDATE_PRODUCT = YES;
@@ -1690,7 +1690,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJF2XZ86HB;
INFOPLIST_FILE = InfoPlists/ShareExtension.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1745,7 +1745,7 @@
DEVELOPMENT_TEAM = QJF2XZ86HB;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = InfoPlists/Omnivore.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1772,7 +1772,7 @@
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJF2XZ86HB;
INFOPLIST_FILE = InfoPlists/ShareExtension.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -1843,8 +1843,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 14.1;
- MACOSX_DEPLOYMENT_TARGET = 10.15;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
diff --git a/apple/OmnivoreKit/Package.swift b/apple/OmnivoreKit/Package.swift
index 33eb916f0..35e3008dd 100644
--- a/apple/OmnivoreKit/Package.swift
+++ b/apple/OmnivoreKit/Package.swift
@@ -6,8 +6,8 @@ let package = Package(
name: "OmnivoreKit",
defaultLocalization: "en",
platforms: [
- .iOS(.v14),
- .macOS(.v11)
+ .iOS(.v15),
+ .macOS(.v12)
],
products: [
.library(name: "App", targets: ["App"]),
@@ -17,7 +17,7 @@ let package = Package(
.library(name: "Utils", targets: ["Utils"])
],
dependencies: [
- .package(name: "AppAuth", url: "https://github.com/openid/AppAuth-iOS.git", .upToNextMajor(from: "1.4.0")),
+ .package(url: "https://github.com/openid/AppAuth-iOS.git", .upToNextMajor(from: "1.4.0")),
.package(url: "https://github.com/Square/Valet", from: "4.1.2"),
.package(url: "https://github.com/maticzav/swift-graphql", from: "2.3.1"),
.package(url: "https://github.com/siteline/SwiftUI-Introspect.git", from: "0.1.4")
@@ -37,7 +37,7 @@ let package = Package(
.target(
name: "Services",
dependencies: [
- "AppAuth",
+ .product(name: "AppAuth", package: "AppAuth-iOS"),
"Valet",
.product(name: "SwiftGraphQL", package: "swift-graphql"),
"Models",
From 6af0452d149c59fa17b46703ba7eaf0faed278c3 Mon Sep 17 00:00:00 2001
From: Satindar Dhillon
Date: Thu, 14 Apr 2022 12:45:45 -0700
Subject: [PATCH 125/125] remove ios14 specific code
---
.../App/Views/Home/HomeFeedViewIOS.swift | 233 +++++++-----------
.../App/Views/Home/HomeFeedViewMac.swift | 43 ----
.../App/Views/Labels/ApplyLabelsView.swift | 14 +-
.../Sources/App/Views/Labels/LabelsView.swift | 25 +-
.../App/Views/LinkItemDetailView.swift | 81 +-----
.../App/Views/PrimaryContentView.swift | 6 +-
.../Views/WebReader/WebReaderContainer.swift | 49 +---
.../Sources/Models/PageScrapePayload.swift | 10 +-
.../Sources/Views/Colors/Colors.swift | 8 +-
.../Sources/Views/FeedItem/GridCard.swift | 18 +-
.../Sources/Views/FormSheetWrapper.swift | 13 +-
.../PushNotificationPrimerView.swift | 11 +-
12 files changed, 145 insertions(+), 366 deletions(-)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
index 23a610b1e..46720c206 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift
@@ -14,62 +14,38 @@ import Views
var body: some View {
Group {
- if #available(iOS 15.0, *) {
- HomeFeedView(
- prefersListLayout: $prefersListLayout,
- viewModel: viewModel
- )
- .refreshable {
- viewModel.loadItems(dataService: dataService, isRefresh: true)
+ HomeFeedView(
+ prefersListLayout: $prefersListLayout,
+ viewModel: viewModel
+ )
+ .refreshable {
+ viewModel.loadItems(dataService: dataService, isRefresh: true)
+ }
+ .searchable(
+ text: $viewModel.searchTerm,
+ placement: .navigationBarDrawer
+ ) {
+ if viewModel.searchTerm.isEmpty {
+ Text("Inbox").searchCompletion("in:inbox ")
+ Text("All").searchCompletion("in:all ")
+ Text("Archived").searchCompletion("in:archive ")
+ Text("Files").searchCompletion("type:file ")
}
- .searchable(
- text: $viewModel.searchTerm,
- placement: .navigationBarDrawer
- ) {
- if viewModel.searchTerm.isEmpty {
- Text("Inbox").searchCompletion("in:inbox ")
- Text("All").searchCompletion("in:all ")
- Text("Archived").searchCompletion("in:archive ")
- Text("Files").searchCompletion("type:file ")
- }
- }
- .onChange(of: viewModel.searchTerm) { _ in
- // Maybe we should debounce this, but
- // it feels like it works ok without
- viewModel.loadItems(dataService: dataService, isRefresh: true)
- }
- .onChange(of: viewModel.selectedLabels) { _ in
- viewModel.loadItems(dataService: dataService, isRefresh: true)
- }
- .onSubmit(of: .search) {
- viewModel.loadItems(dataService: dataService, isRefresh: true)
- }
- .sheet(item: $viewModel.itemUnderLabelEdit) { item in
- ApplyLabelsView(mode: .item(item)) { labels in
- viewModel.updateLabels(itemID: item.id, labels: labels)
- }
- }
- } else {
- HomeFeedView(
- prefersListLayout: $prefersListLayout,
- viewModel: viewModel
- )
- .sheet(item: $viewModel.itemUnderLabelEdit) { item in
- ApplyLabelsView(mode: .item(item)) { labels in
- viewModel.updateLabels(itemID: item.id, labels: labels)
- }
- }
- .toolbar {
- ToolbarItem {
- if viewModel.isLoading {
- Button(action: {}, label: { ProgressView() })
- } else {
- Button(
- action: { viewModel.loadItems(dataService: dataService, isRefresh: true) },
- label: { Label("Refresh Feed", systemImage: "arrow.clockwise") }
- )
- }
- }
+ }
+ .onChange(of: viewModel.searchTerm) { _ in
+ // Maybe we should debounce this, but
+ // it feels like it works ok without
+ viewModel.loadItems(dataService: dataService, isRefresh: true)
+ }
+ .onChange(of: viewModel.selectedLabels) { _ in
+ viewModel.loadItems(dataService: dataService, isRefresh: true)
+ }
+ .onSubmit(of: .search) {
+ viewModel.loadItems(dataService: dataService, isRefresh: true)
+ }
+ .sheet(item: $viewModel.itemUnderLabelEdit) { item in
+ ApplyLabelsView(mode: .item(item)) { labels in
+ viewModel.updateLabels(itemID: item.id, labels: labels)
}
}
}
@@ -141,24 +117,13 @@ import Views
HomeFeedGridView(viewModel: viewModel)
.toolbar {
ToolbarItem {
- if #available(iOS 15.0, *) {
- Button("", action: {})
- .disabled(true)
- .overlay {
- if viewModel.isLoading {
- ProgressView()
- }
+ Button("", action: {})
+ .disabled(true)
+ .overlay {
+ if viewModel.isLoading {
+ ProgressView()
}
- } else {
- if viewModel.isLoading {
- Button(action: {}, label: { ProgressView() })
- } else {
- Button(
- action: { viewModel.loadItems(dataService: dataService, isRefresh: true) },
- label: { Label("Refresh Feed", systemImage: "arrow.clockwise") }
- )
}
- }
}
ToolbarItem {
if UIDevice.isIPad {
@@ -189,7 +154,7 @@ import Views
List {
Section {
ForEach(viewModel.items) { item in
- let link = FeedCardNavigationLink(
+ FeedCardNavigationLink(
item: item,
viewModel: viewModel
)
@@ -224,61 +189,56 @@ import Views
}
}
}
- if #available(iOS 15.0, *) {
- link
- .swipeActions(edge: .trailing, allowsFullSwipe: true) {
- if !item.isArchived {
- Button {
- withAnimation(.linear(duration: 0.4)) {
- viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: true)
- }
- } label: {
- Label("Archive", systemImage: "archivebox")
- }.tint(.green)
- } else {
- Button {
- withAnimation(.linear(duration: 0.4)) {
- viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: false)
- }
- } label: {
- Label("Unarchive", systemImage: "tray.and.arrow.down.fill")
- }.tint(.indigo)
+ .swipeActions(edge: .trailing, allowsFullSwipe: true) {
+ if !item.isArchived {
+ Button {
+ withAnimation(.linear(duration: 0.4)) {
+ viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: true)
+ }
+ } label: {
+ Label("Archive", systemImage: "archivebox")
+ }.tint(.green)
+ } else {
+ Button {
+ withAnimation(.linear(duration: 0.4)) {
+ viewModel.setLinkArchived(dataService: dataService, linkId: item.id, archived: false)
+ }
+ } label: {
+ Label("Unarchive", systemImage: "tray.and.arrow.down.fill")
+ }.tint(.indigo)
+ }
+ }
+ .swipeActions(edge: .trailing, allowsFullSwipe: true) {
+ Button(
+ role: .destructive,
+ action: {
+ itemToRemove = item
+ confirmationShown = true
+ },
+ label: {
+ Image(systemName: "trash")
+ }
+ )
+ }.alert("Are you sure?", isPresented: $confirmationShown) {
+ Button("Remove Link", role: .destructive) {
+ if let itemToRemove = itemToRemove {
+ withAnimation {
+ viewModel.removeLink(dataService: dataService, linkId: itemToRemove.id)
}
}
- .swipeActions(edge: .trailing, allowsFullSwipe: true) {
- Button(
- role: .destructive,
- action: {
- itemToRemove = item
- confirmationShown = true
- },
- label: {
- Image(systemName: "trash")
- }
- )
- }.alert("Are you sure?", isPresented: $confirmationShown) {
- Button("Remove Link", role: .destructive) {
- if let itemToRemove = itemToRemove {
- withAnimation {
- viewModel.removeLink(dataService: dataService, linkId: itemToRemove.id)
- }
- }
- self.itemToRemove = nil
- }
- Button("Cancel", role: .cancel) { self.itemToRemove = nil }
- }
- .swipeActions(edge: .leading, allowsFullSwipe: true) {
- if FeatureFlag.enableSnooze {
- Button {
- viewModel.itemToSnooze = item
- viewModel.snoozePresented = true
- } label: {
- Label { Text("Snooze") } icon: { Image.moon }
- }.tint(.appYellow48)
- }
- }
- } else {
- link
+ self.itemToRemove = nil
+ }
+ Button("Cancel", role: .cancel) { self.itemToRemove = nil }
+ }
+ .swipeActions(edge: .leading, allowsFullSwipe: true) {
+ if FeatureFlag.enableSnooze {
+ Button {
+ viewModel.itemToSnooze = item
+ viewModel.snoozePresented = true
+ } label: {
+ Label { Text("Snooze") } icon: { Image.moon }
+ }.tint(.appYellow48)
+ }
}
}
}
@@ -331,27 +291,22 @@ import Views
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 325), spacing: 24)], spacing: 24) {
ForEach(viewModel.items) { item in
- let link = GridCardNavigationLink(
+ GridCardNavigationLink(
item: item,
actionHandler: { contextMenuActionHandler(item: item, action: $0) },
isContextMenuOpen: $isContextMenuOpen,
viewModel: viewModel
)
- if #available(iOS 15.0, *) {
- link
- .alert("Are you sure?", isPresented: $confirmationShown) {
- Button("Remove Link", role: .destructive) {
- if let itemToRemove = itemToRemove {
- withAnimation {
- viewModel.removeLink(dataService: dataService, linkId: itemToRemove.id)
- }
- }
- self.itemToRemove = nil
+ .alert("Are you sure?", isPresented: $confirmationShown) {
+ Button("Remove Link", role: .destructive) {
+ if let itemToRemove = itemToRemove {
+ withAnimation {
+ viewModel.removeLink(dataService: dataService, linkId: itemToRemove.id)
}
- Button("Cancel", role: .cancel) { self.itemToRemove = nil }
}
- } else {
- link
+ self.itemToRemove = nil
+ }
+ Button("Cancel", role: .cancel) { self.itemToRemove = nil }
}
}
}
diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift
index 640f6fdc5..3ca413ff5 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewMac.swift
@@ -15,15 +15,6 @@ import Views
@ObservedObject var viewModel: HomeFeedViewModel
var body: some View {
- if #available(macOS 12.0, *) {
- innerBody
- } else {
- innerBodyMac11
- }
- }
-
- @available(macOS 12.0, *)
- var innerBody: some View {
List {
Section {
ForEach(viewModel.items) { item in
@@ -118,40 +109,6 @@ import Views
}
}
}
-
- var innerBodyMac11: some View {
- List {
- Section {
- ForEach(viewModel.items) { item in
- FeedCardNavigationLink(
- item: item,
- viewModel: viewModel
- )
- }
- }
-
- if viewModel.isLoading {
- LoadingSection()
- }
- }
- .listStyle(PlainListStyle())
- .navigationTitle("Home")
- .toolbar {
- ToolbarItem {
- Button(
- action: {
- viewModel.loadItems(dataService: dataService, isRefresh: true)
- },
- label: { Label("Refresh Feed", systemImage: "arrow.clockwise") }
- )
- }
- }
- .onAppear {
- if viewModel.items.isEmpty {
- viewModel.loadItems(dataService: dataService, isRefresh: true)
- }
- }
- }
}
#endif
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
index 8cf320384..5c81b1b70 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/ApplyLabelsView.swift
@@ -126,15 +126,11 @@ struct ApplyLabelsView: View {
EmptyView()
} else {
#if os(iOS)
- if #available(iOS 15.0, *) {
- innerBody
- .searchable(
- text: $labelSearchFilter,
- placement: .navigationBarDrawer(displayMode: .always)
- )
- } else {
- innerBody
- }
+ innerBody
+ .searchable(
+ text: $labelSearchFilter,
+ placement: .navigationBarDrawer(displayMode: .always)
+ )
#else
innerBody
#endif
diff --git a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
index 24633a1e5..c86db2087 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Labels/LabelsView.swift
@@ -16,25 +16,20 @@ struct LabelsView: View {
var body: some View {
Group {
#if os(iOS)
- if #available(iOS 15.0, *) {
- Form {
- innerBody
- .alert("Are you sure you want to delete this label?", isPresented: $showDeleteConfirmation) {
- Button("Delete Label", role: .destructive) {
- if let labelID = labelToRemoveID {
- withAnimation {
- viewModel.deleteLabel(dataService: dataService, labelID: labelID)
- }
+ Form {
+ innerBody
+ .alert("Are you sure you want to delete this label?", isPresented: $showDeleteConfirmation) {
+ Button("Delete Label", role: .destructive) {
+ if let labelID = labelToRemoveID {
+ withAnimation {
+ viewModel.deleteLabel(dataService: dataService, labelID: labelID)
}
- self.labelToRemoveID = nil
}
- Button("Cancel", role: .cancel) { self.labelToRemoveID = nil }
+ self.labelToRemoveID = nil
}
- }
- } else {
- Form { innerBody }
+ Button("Cancel", role: .cancel) { self.labelToRemoveID = nil }
+ }
}
-
#elseif os(macOS)
List {
innerBody
diff --git a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift
index 2d83208f3..d267389e3 100644
--- a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift
@@ -157,39 +157,6 @@ struct LinkItemDetailView: View {
#endif
}
- var navBariOS14: some View {
- HStack(alignment: .center) {
- Button(
- action: { self.presentationMode.wrappedValue.dismiss() },
- label: {
- Image(systemName: "chevron.backward")
- .font(.appTitleTwo)
- .foregroundColor(.appGrayTextContrast)
- .padding(.horizontal)
- }
- )
- .scaleEffect(navBarVisibilityRatio)
- Spacer()
- Button(
- action: { showFontSizePopover.toggle() },
- label: {
- Image(systemName: "textformat.size")
- .font(.appTitleTwo)
- }
- )
- .padding(.horizontal)
- .scaleEffect(navBarVisibilityRatio)
- }
- .frame(height: readerViewNavBarHeight * navBarVisibilityRatio)
- .opacity(navBarVisibilityRatio)
- .background(Color.systemBackground)
- .onTapGesture {
- showFontSizePopover = false
- }
- }
-
- @available(macOS 12.0, *)
- @available(iOS 15.0, *)
var navBar: some View {
HStack(alignment: .center) {
Button(
@@ -286,47 +253,25 @@ struct LinkItemDetailView: View {
}
)
}
- if #available(iOS 15.0, *) {
- VStack(spacing: 0) {
- navBar
- Spacer()
- }
- .navigationBarHidden(true)
- } else {
- VStack(spacing: 0) {
- navBariOS14
- Spacer()
- }
- .navigationBarHidden(true)
- }
- }
-
- } else {
- if #available(iOS 15.0, *) {
VStack(spacing: 0) {
navBar
Spacer()
}
- .onAppear {
- viewModel.loadWebAppWrapper(
- dataService: dataService,
- rawAuthCookie: authenticator.omnivoreAuthCookieString
- )
- }
- .navigationBarHidden(true)
- } else {
- VStack(spacing: 0) {
- navBariOS14
- Spacer()
- }
- .onAppear {
- viewModel.loadWebAppWrapper(
- dataService: dataService,
- rawAuthCookie: authenticator.omnivoreAuthCookieString
- )
- }
.navigationBarHidden(true)
}
+
+ } else {
+ VStack(spacing: 0) {
+ navBar
+ Spacer()
+ }
+ .onAppear {
+ viewModel.loadWebAppWrapper(
+ dataService: dataService,
+ rawAuthCookie: authenticator.omnivoreAuthCookieString
+ )
+ }
+ .navigationBarHidden(true)
}
}
#endif
diff --git a/apple/OmnivoreKit/Sources/App/Views/PrimaryContentView.swift b/apple/OmnivoreKit/Sources/App/Views/PrimaryContentView.swift
index aa8829a26..5121c2186 100644
--- a/apple/OmnivoreKit/Sources/App/Views/PrimaryContentView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/PrimaryContentView.swift
@@ -51,10 +51,8 @@ public struct PrimaryContentView: View {
.introspectSplitViewController {
$0.preferredSplitBehavior = .tile
$0.preferredPrimaryColumnWidth = 200
- if #available(iOS 14.5, *) {
- $0.presentsWithGesture = false
- $0.displayModeButtonVisibility = .always
- }
+ $0.presentsWithGesture = false
+ $0.displayModeButtonVisibility = .always
}
}
#endif
diff --git a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift
index ecf6144a8..d0c8cf42d 100644
--- a/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/WebReader/WebReaderContainer.swift
@@ -74,39 +74,6 @@ import WebKit
}
}
- var navBariOS14: some View {
- HStack(alignment: .center) {
- Button(
- action: { self.presentationMode.wrappedValue.dismiss() },
- label: {
- Image(systemName: "chevron.backward")
- .font(.appTitleTwo)
- .foregroundColor(.appGrayTextContrast)
- .padding(.horizontal)
- }
- )
- .scaleEffect(navBarVisibilityRatio)
- Spacer()
- Button(
- action: { showFontSizePopover.toggle() },
- label: {
- Image(systemName: "textformat.size")
- .font(.appTitleTwo)
- }
- )
- .padding(.horizontal)
- .scaleEffect(navBarVisibilityRatio)
- }
- .frame(height: readerViewNavBarHeight * navBarVisibilityRatio)
- .opacity(navBarVisibilityRatio)
- .background(Color.systemBackground)
- .onTapGesture {
- showFontSizePopover = false
- }
- }
-
- @available(macOS 12.0, *)
- @available(iOS 15.0, *)
var navBar: some View {
HStack(alignment: .center) {
Button(
@@ -264,19 +231,11 @@ import WebKit
}
)
}
- if #available(iOS 15.0, *) {
- VStack(spacing: 0) {
- navBar
- Spacer()
- }
- .navigationBarHidden(true)
- } else {
- VStack(spacing: 0) {
- navBariOS14
- Spacer()
- }
- .navigationBarHidden(true)
+ VStack(spacing: 0) {
+ navBar
+ Spacer()
}
+ .navigationBarHidden(true)
}.onDisappear {
// Clear the shared webview content when exiting
diff --git a/apple/OmnivoreKit/Sources/Models/PageScrapePayload.swift b/apple/OmnivoreKit/Sources/Models/PageScrapePayload.swift
index d2f1955a3..bdaebab00 100644
--- a/apple/OmnivoreKit/Sources/Models/PageScrapePayload.swift
+++ b/apple/OmnivoreKit/Sources/Models/PageScrapePayload.swift
@@ -45,7 +45,7 @@ public enum PageScraper {
}
var pageScrapePayload: PageScrapePayload?
- let propertyListKey = String(kUTTypePropertyList)
+ let propertyListKey = UTType.propertyList.identifier
let group = DispatchGroup()
@@ -76,7 +76,7 @@ public enum PageScraper {
extensionContext: NSExtensionContext?,
completion: @escaping (Result) -> Void
) {
- let urlKey = String(kUTTypeURL)
+ let urlKey = UTType.url.identifier
// First look for a URL type
let urlFound = extensionContext?.inputItems.first { inputItem in
@@ -108,7 +108,7 @@ public enum PageScraper {
return
}
- let textKey = String(kUTTypeText)
+ let textKey = UTType.text.identifier
// We didn't find a URL type, so check for a string that contains a URL
let textUrlFound = extensionContext?.inputItems.first { inputItem in
let itemProvider = (inputItem as? NSExtensionItem)?.attachments?.first(where: { attachment in
@@ -150,8 +150,8 @@ public enum PageScraper {
extensionContext: NSExtensionContext?,
completion: @escaping (Result) -> Void
) {
- let urlKey = String(kUTTypeURL)
- let textKey = String(kUTTypeUTF16PlainText)
+ let urlKey = UTType.url.identifier
+ let textKey = UTType.utf16PlainText.identifier
let inputItem = extensionContext?.inputItems.first as? NSExtensionItem
let itemProvider = inputItem?.attachments?.first(where: { attachment in
attachment.hasItemConformingToTypeIdentifier(urlKey)
diff --git a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift
index bdd97a4ad..f3ea2e262 100644
--- a/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift
+++ b/apple/OmnivoreKit/Sources/Views/Colors/Colors.swift
@@ -23,13 +23,7 @@ public extension Color {
static var systemPlaceholder: Color { Color(.placeholderText) }
static var secondarySystemGroupedBackground: Color { Color(.secondarySystemGroupedBackground) }
static var systemGray6: Color { Color(.systemGray6) }
- static var systemLabel: Color {
- if #available(iOS 15.0, *) {
- return Color(uiColor: .label)
- } else {
- return Color.appGrayTextContrast
- }
- }
+ static var systemLabel: Color { Color(uiColor: .label) }
#elseif os(macOS)
static var systemBackground: Color { Color(.windowBackgroundColor) }
diff --git a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift
index 55fef0e3b..692e430ad 100644
--- a/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift
+++ b/apple/OmnivoreKit/Sources/Views/FeedItem/GridCard.swift
@@ -68,21 +68,9 @@ public struct GridCard: View {
VStack(alignment: .leading, spacing: 16) {
// Progress Bar
Group {
- // Remove os check when dropping macOS 11
- #if os(iOS)
- if #available(iOS 15.0, *) {
- ProgressView(value: min(abs(item.readingProgress) / 100, 1))
- .tint(.appYellow48)
- .frame(maxWidth: .infinity, alignment: .leading)
- } else {
- Color.clear
- .contentShape(Rectangle())
- .frame(maxWidth: .infinity, alignment: .leading)
- }
- #else
- ProgressView(value: max(abs(item.readingProgress) / 100, 1))
- .frame(maxWidth: .infinity, alignment: .leading)
- #endif
+ ProgressView(value: min(abs(item.readingProgress) / 100, 1))
+ .tint(.appYellow48)
+ .frame(maxWidth: .infinity, alignment: .leading)
}
.onTapGesture { tapHandler() }
diff --git a/apple/OmnivoreKit/Sources/Views/FormSheetWrapper.swift b/apple/OmnivoreKit/Sources/Views/FormSheetWrapper.swift
index a998cf4d3..85fc0d33c 100644
--- a/apple/OmnivoreKit/Sources/Views/FormSheetWrapper.swift
+++ b/apple/OmnivoreKit/Sources/Views/FormSheetWrapper.swift
@@ -30,14 +30,13 @@ import SwiftUI
let controller = UIHostingController(rootView: content())
if controller.traitCollection.userInterfaceIdiom == .phone {
- if #available(iOS 15, *) {
- if let sheet = controller.sheetPresentationController {
- sheet.preferredCornerRadius = 16
- sheet.prefersGrabberVisible = false
- sheet.detents = [.medium()]
- sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
- }
+ if let sheet = controller.sheetPresentationController {
+ sheet.preferredCornerRadius = 16
+ sheet.prefersGrabberVisible = false
+ sheet.detents = [.medium()]
+ sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
}
+
controller.modalPresentationStyle = .pageSheet
} else {
controller.view.sizeToFit()
diff --git a/apple/OmnivoreKit/Sources/Views/PushNotifications/PushNotificationPrimerView.swift b/apple/OmnivoreKit/Sources/Views/PushNotifications/PushNotificationPrimerView.swift
index b2a005338..6d2f80366 100644
--- a/apple/OmnivoreKit/Sources/Views/PushNotifications/PushNotificationPrimerView.swift
+++ b/apple/OmnivoreKit/Sources/Views/PushNotifications/PushNotificationPrimerView.swift
@@ -14,7 +14,7 @@ import SwiftUI
self.denyAction = denyAction
}
- var content: some View {
+ public var body: some View {
VStack(alignment: .leading, spacing: 10) {
HStack {
Image
@@ -45,14 +45,7 @@ import SwiftUI
.foregroundColor(Color.appTextDefault)
.cornerRadius(8)
.frame(maxWidth: min(UIScreen.main.bounds.width - 20, 320))
- }
-
- public var body: some View {
- if #available(iOS 15.0, *) {
- content.interactiveDismissDisabled()
- } else {
- content
- }
+ .interactiveDismissDisabled()
}
}