mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge branch 'feat/integrations-page' of github.com:omnivore-app/omnivore into feat/integrations-page
This commit is contained in:
commit
835c315f77
5 changed files with 226 additions and 2 deletions
63
packages/web/components/templates/integrations/Readwise.tsx
Normal file
63
packages/web/components/templates/integrations/Readwise.tsx
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import { SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { StyledText } from '../../elements/StyledText'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { FormInput } from '../../elements/FormElements'
|
||||
import { styled } from '@stitches/react'
|
||||
import { setIntegrationMutation } from '../../../lib/networking/mutations/setIntegrationMutation'
|
||||
|
||||
const BorderedFormInput = styled(FormInput, {
|
||||
height: '40px',
|
||||
paddingLeft: '6px',
|
||||
borderRadius: '6px',
|
||||
background: 'white',
|
||||
color: '$omnivoreGray',
|
||||
border: `1px solid 1px solid rgba(0, 0, 0, 0.06)`,
|
||||
})
|
||||
|
||||
const FormLabel = styled('label', {
|
||||
fontSize: '16px',
|
||||
color: '$omnivoreGray',
|
||||
})
|
||||
|
||||
export function Readwise(): JSX.Element {
|
||||
const [token, setToken] = useState<string>('')
|
||||
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
|
||||
|
||||
const setReadwiseToken = useCallback(() => {
|
||||
setIntegrationMutation({
|
||||
token,
|
||||
type: 'READWISE',
|
||||
enabled: true,
|
||||
})
|
||||
console.log("SETTING READWISE TOKEN", token)
|
||||
}, [token])
|
||||
|
||||
return (
|
||||
<VStack alignment="center" css={{ padding: '16px' }}>
|
||||
<VStack css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}>
|
||||
<SpanBox css={{ width: '100%' }}>
|
||||
<FormLabel>
|
||||
Enter your Readwise API token. You can get your token
|
||||
{' '}
|
||||
<a href="https://readwise.io/access_token">here</a>.
|
||||
</FormLabel>
|
||||
<BorderedFormInput
|
||||
type="token"
|
||||
key="token"
|
||||
value={token}
|
||||
placeholder="Readwise Token"
|
||||
onChange={(e) => { e.preventDefault(); setToken(e.target.value); }}
|
||||
/>
|
||||
</SpanBox>
|
||||
</VStack>
|
||||
|
||||
{errorMessage && (
|
||||
<StyledText style="error">{errorMessage}</StyledText>
|
||||
)}
|
||||
<Button style="ctaDarkYellow" css={{ my: '$2' }} onClick={setReadwiseToken}>
|
||||
Set Token
|
||||
</Button>
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
import { IntegrationType } from '../queries/useGetIntegrationsQuery'
|
||||
|
||||
export type SetIntegrationInput = {
|
||||
id?: string
|
||||
type: IntegrationType
|
||||
token: string,
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
type SetIntegrationResult = {
|
||||
setIntegration?: SetIntegrationSuccess
|
||||
errorCodes?: unknown[]
|
||||
}
|
||||
|
||||
type SetIntegrationSuccess = {
|
||||
integration: Integration
|
||||
}
|
||||
|
||||
type Integration = {
|
||||
id: string
|
||||
type: IntegrationType
|
||||
token: string
|
||||
enabled: boolean
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
export async function setIntegrationMutation(
|
||||
input: SetIntegrationInput
|
||||
): Promise<string | undefined> {
|
||||
const mutation = gql`
|
||||
mutation SetIntegration(
|
||||
$input: SetIntegrationInput!
|
||||
) {
|
||||
setIntegration(input: $input) {
|
||||
... on SetIntegrationSuccess {
|
||||
integration {
|
||||
id
|
||||
type
|
||||
token
|
||||
enabled
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
... on SetIntegrationError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
try {
|
||||
const data = await gqlFetcher(mutation, { input }) as SetIntegrationResult
|
||||
console.log(input, data);
|
||||
const output = data as any
|
||||
console.log(output)
|
||||
return output?.updatedLabel
|
||||
} catch (err) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import useSWR from 'swr'
|
||||
import { publicGqlFetcher } from '../networkHelpers'
|
||||
|
||||
export interface Integration {
|
||||
id: string
|
||||
type: IntegrationType
|
||||
token: string
|
||||
enabled: boolean
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
export type IntegrationType =
|
||||
| 'READWISE'
|
||||
|
||||
interface IntegrationsQueryResponse {
|
||||
isValidating: boolean
|
||||
integrations: Integration[]
|
||||
revalidate: () => void
|
||||
}
|
||||
|
||||
interface IntegrationsQueryResponseData {
|
||||
integrations: IntegrationsData
|
||||
}
|
||||
|
||||
interface IntegrationsData {
|
||||
integrations: unknown
|
||||
}
|
||||
|
||||
export function useGetIntegrationsQuery(): IntegrationsQueryResponse {
|
||||
const query = gql`
|
||||
query GetIntegrations {
|
||||
integrations {
|
||||
... on IntegrationsSuccess {
|
||||
integrations {
|
||||
id
|
||||
type
|
||||
token
|
||||
enabled
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
... on IntegrationsError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const { data, mutate, error, isValidating } = useSWR(query, publicGqlFetcher)
|
||||
console.log('integrations data', data)
|
||||
|
||||
try {
|
||||
if (data) {
|
||||
const result = data as IntegrationsQueryResponseData
|
||||
const integrations = result.integrations.integrations as Integration[]
|
||||
return {
|
||||
isValidating,
|
||||
integrations,
|
||||
revalidate: () => {
|
||||
mutate()
|
||||
},
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
return {
|
||||
isValidating: false,
|
||||
integrations: [],
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
revalidate: () => {},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { styled } from '@stitches/react'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
import { DownloadSimple, Eye, Link, Plus } from 'phosphor-react'
|
||||
|
|
@ -13,6 +13,7 @@ import {
|
|||
import { PrimaryLayout } from '../../components/templates/PrimaryLayout'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
import { Button } from '../../components/elements/Button'
|
||||
import { useGetIntegrationsQuery } from '../../lib/networking/queries/useGetIntegrationsQuery'
|
||||
|
||||
// Styles
|
||||
const Header = styled(Box, {
|
||||
|
|
@ -46,11 +47,16 @@ type integrationsCard = {
|
|||
}
|
||||
export default function Integrations(): JSX.Element {
|
||||
applyStoredTheme(false)
|
||||
const { integrations } = useGetIntegrationsQuery()
|
||||
|
||||
const [integrationsArray, setIntegrationsArray] = useState(
|
||||
Array<integrationsCard>()
|
||||
)
|
||||
|
||||
const readwiseConnected = useMemo(() => {
|
||||
return integrations.some((i) => i.type == 'READWISE')
|
||||
}, [integrations])
|
||||
|
||||
useEffect(() => {
|
||||
setIntegrationsArray([
|
||||
{
|
||||
|
|
@ -68,7 +74,7 @@ export default function Integrations(): JSX.Element {
|
|||
title: 'ReadWise',
|
||||
subText: 'Synchronize ebooks & articles from Readwise account',
|
||||
button: {
|
||||
text: 'Connect to Readwise',
|
||||
text: readwiseConnected ? 'Remove' : 'Connect to Readwise',
|
||||
icon: <Link size={16} weight={'bold'} />,
|
||||
style: 'ctaDarkYellow',
|
||||
},
|
||||
|
|
|
|||
15
packages/web/pages/settings/integrations/readwise.tsx
Normal file
15
packages/web/pages/settings/integrations/readwise.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { PageMetaData } from '../../../components/patterns/PageMetaData'
|
||||
import { Readwise } from '../../../components/templates/integrations/Readwise'
|
||||
import { SettingsLayout } from '../../../components/templates/SettingsLayout'
|
||||
|
||||
export default function ReadwisePage(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<PageMetaData title="Readwise - Omnivore" path="/integrations/readwise" />
|
||||
<SettingsLayout>
|
||||
<Readwise />
|
||||
</SettingsLayout>
|
||||
<div data-testid="integrations-readwise-page-tag" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in a new issue