diff --git a/packages/web/components/templates/settings/SettingsTable.tsx b/packages/web/components/templates/settings/SettingsTable.tsx
index 705b34252..ed63058f1 100644
--- a/packages/web/components/templates/settings/SettingsTable.tsx
+++ b/packages/web/components/templates/settings/SettingsTable.tsx
@@ -12,7 +12,6 @@ import { PrimaryLayout } from '../PrimaryLayout'
type SettingsTableProps = {
pageId: string
- pageHeadline: string
pageInfoLink: string
headerTitle: string
@@ -35,13 +34,16 @@ type SettingsTableRowProps = {
titleElement?: JSX.Element
extraElement?: JSX.Element
- deleteTitle: string
- onDelete: () => void
+ deleteTitle?: string
+ onDelete?: () => void
+
+ dropdownItems?: JSX.Element
}
type MoreOptionsProps = {
- title: string
- onDelete: () => void
+ title?: string
+ onDelete?: () => void
+ dropdownItems?: JSX.Element
}
const MoreOptions = (props: MoreOptionsProps) => (
@@ -63,29 +65,32 @@ const MoreOptions = (props: MoreOptionsProps) => (
}
>
- {
- props.onDelete()
- }}
- >
-
-
- {
+ props.onDelete && props.onDelete()
+ }}
+ >
+
+
+
- {props.title}
-
-
-
+ '&:hover': {
+ border: 'none',
+ backgroundColor: 'transparent',
+ },
+ }}
+ >
+ {props.title}
+
+
+
+ )}
+ {props.dropdownItems && props.dropdownItems}
)
@@ -183,7 +188,11 @@ export const SettingsTableRow = (props: SettingsTableRowProps): JSX.Element => {
},
}}
>
-
+
{props.extraElement}
@@ -198,7 +207,11 @@ export const SettingsTableRow = (props: SettingsTableRowProps): JSX.Element => {
},
}}
>
-
+
@@ -261,6 +274,7 @@ export const SettingsTable = (props: SettingsTableProps): JSX.Element => {
display: 'flex',
alignItems: 'center',
marginBottom: '10px',
+ height: '60px',
}}
>
{props.createAction && props.createTitle && (
diff --git a/packages/web/lib/networking/mutations/markEmailAsItemMutation.ts b/packages/web/lib/networking/mutations/markEmailAsItemMutation.ts
new file mode 100644
index 000000000..fc6e60690
--- /dev/null
+++ b/packages/web/lib/networking/mutations/markEmailAsItemMutation.ts
@@ -0,0 +1,35 @@
+import { gqlFetcher } from '../networkHelpers'
+
+type MarkEmailAsItemDataResponseData = {
+ markEmailAsItem?: MarkEmailAsItemData
+}
+
+type MarkEmailAsItemData = {
+ success: boolean
+ errorCodes?: unknown[]
+}
+
+export async function markEmailAsItemMutation(
+ recentEmailId: string
+): Promise {
+ const mutation = `
+ mutation MarkRecentEmailAsItem($recentEmailId: ID!) {
+ markEmailAsItem(recentEmailId:$recentEmailId) {
+ ... on MarkEmailAsItemError {
+ errorCodes
+ }
+ ... on MarkEmailAsItemSuccess {
+ success
+ }
+ }
+ }`
+
+ const data = await gqlFetcher(mutation, { recentEmailId })
+ console.log('recentEmailId: ', data)
+ const output = data as MarkEmailAsItemDataResponseData | undefined
+ const error = output?.markEmailAsItem?.errorCodes?.find(() => true)
+ console.log('error: ', error)
+ if (error) {
+ throw error
+ }
+}
diff --git a/packages/web/lib/networking/queries/useGetRecentEmails.tsx b/packages/web/lib/networking/queries/useGetRecentEmails.tsx
new file mode 100644
index 000000000..71d68f886
--- /dev/null
+++ b/packages/web/lib/networking/queries/useGetRecentEmails.tsx
@@ -0,0 +1,78 @@
+import { gql } from 'graphql-request'
+import useSWR from 'swr'
+import { publicGqlFetcher } from '../networkHelpers'
+
+export interface RecentEmail {
+ id: string
+ from: string
+ to: string
+ subject: string
+ type: string
+ text: string
+ createdAt: string
+}
+
+interface RecentEmailsResponse {
+ isValidating: boolean
+ recentEmails: RecentEmail[]
+ revalidate: () => void
+}
+
+interface RecentEmailsResponseData {
+ recentEmails: RecentEmailsData
+}
+
+interface RecentEmailsData {
+ recentEmails: RecentEmail[]
+}
+
+export function useGetRecentEmailsQuery(): RecentEmailsResponse {
+ const query = gql`
+ query GetRecentEmails {
+ recentEmails {
+ ... on RecentEmailsSuccess {
+ recentEmails {
+ id
+ from
+ to
+ subject
+ type
+ text
+ createdAt
+ }
+ }
+ ... on RecentEmailsError {
+ errorCodes
+ }
+ }
+ }
+ `
+
+ const { data, mutate, error, isValidating } = useSWR(query, publicGqlFetcher)
+
+ try {
+ if (error) {
+ throw error
+ }
+
+ if (data) {
+ const result = data as RecentEmailsResponseData
+ const recentEmails = result.recentEmails.recentEmails as RecentEmail[]
+ return {
+ isValidating,
+ recentEmails,
+ revalidate: () => {
+ mutate()
+ },
+ }
+ }
+ } catch (error) {
+ console.log('error', error)
+ }
+ return {
+ isValidating: true,
+ recentEmails: [],
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ revalidate: () => {},
+ }
+}
diff --git a/packages/web/pages/settings/api.tsx b/packages/web/pages/settings/api.tsx
index a258505ed..43d0d20ee 100644
--- a/packages/web/pages/settings/api.tsx
+++ b/packages/web/pages/settings/api.tsx
@@ -114,7 +114,6 @@ export default function Api(): JSX.Element {
return (
(undefined)
+ const [confirmDeleteEmailId, setConfirmDeleteEmailId] = useState<
+ undefined | string
+ >(undefined)
applyStoredTheme(false)
@@ -117,7 +117,6 @@ export default function EmailsPage(): JSX.Element {
<>
)}
+
+
+ View recently received emails
+
+
{confirmDeleteEmailId ? (
diff --git a/packages/web/pages/settings/emails/recent.tsx b/packages/web/pages/settings/emails/recent.tsx
new file mode 100644
index 000000000..c842937d0
--- /dev/null
+++ b/packages/web/pages/settings/emails/recent.tsx
@@ -0,0 +1,226 @@
+import { useMemo, useState } from 'react'
+import { applyStoredTheme } from '../../../lib/themeUpdater'
+
+import {
+ EmptySettingsRow,
+ SettingsTable,
+ SettingsTableRow,
+} from '../../../components/templates/settings/SettingsTable'
+import { StyledText } from '../../../components/elements/StyledText'
+import {
+ RecentEmail,
+ useGetRecentEmailsQuery,
+} from '../../../lib/networking/queries/useGetRecentEmails'
+import {
+ Box,
+ HStack,
+ SpanBox,
+ VStack,
+} from '../../../components/elements/LayoutPrimitives'
+import { DropdownOption } from '../../../components/elements/DropdownElements'
+import { theme } from '../../../components/tokens/stitches.config'
+import {
+ ModalContent,
+ ModalOverlay,
+ ModalRoot,
+ ModalTitleBar,
+} from '../../../components/elements/ModalPrimitives'
+import { markEmailAsItemMutation } from '../../../lib/networking/mutations/markEmailAsItemMutation'
+import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
+
+type TypeChipProps = {
+ type: string
+}
+
+const TypeChip = (props: TypeChipProps): JSX.Element => {
+ const backgroundColor = props.type == 'article' ? '$omnivoreCtaYellow' : 'red'
+ return (
+
+ {props.type}
+
+ )
+}
+
+type MoreOptionItemProps = {
+ text: string
+ action: () => void
+}
+
+const MoreOptionItem = (props: MoreOptionItemProps): JSX.Element => {
+ return (
+ {
+ props.action()
+ }}
+ >
+
+
+ {props.text}
+
+
+
+ )
+}
+
+type ViewRecentEmailModalProps = {
+ recentEmail: RecentEmail
+ onOpenChange: (open: boolean) => void
+}
+
+const ViewRecentEmailModal = (
+ props: ViewRecentEmailModalProps
+): JSX.Element => {
+ return (
+
+
+ {
+ // remove focus from modal
+ ;(document.activeElement as HTMLElement).blur()
+ }}
+ >
+
+
+
+ {props.recentEmail.text}
+
+
+
+
+ )
+}
+
+export default function RecentEmails(): JSX.Element {
+ const { recentEmails, isValidating } = useGetRecentEmailsQuery()
+ const [viewingEmail, setViewingEmail] = useState(
+ undefined
+ )
+
+ applyStoredTheme(false)
+
+ const sortedRecentEmails = useMemo(() => {
+ return recentEmails.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
+ }, [recentEmails])
+
+ return (
+
+ {sortedRecentEmails.length > 0 ? (
+ sortedRecentEmails.map((recentEmail: RecentEmail, i) => {
+ return (
+
+
+ {recentEmail.subject}
+
+
+
+
+
+ }
+ dropdownItems={
+ <>
+ {
+ console.log('viewing text: ', recentEmail)
+ setViewingEmail(recentEmail)
+ }}
+ />
+ {recentEmail.type != 'article' && (
+ {
+ console.log('marking as email', recentEmail)
+ showSuccessToast('Marking email as article')
+ try {
+ await markEmailAsItemMutation(recentEmail.id)
+ } catch (err) {
+ console.log('error marking as article: ', err)
+ showErrorToast('Error marking item as article')
+ return
+ }
+ showSuccessToast('Email added to library')
+ }}
+ />
+ )}
+ >
+ }
+ />
+ )
+ })
+ ) : (
+
+ )}
+
+ {viewingEmail && (
+ setViewingEmail(undefined)}
+ />
+ )}
+
+ )
+}
diff --git a/packages/web/pages/settings/subscriptions.tsx b/packages/web/pages/settings/subscriptions.tsx
index ee0bab5f4..7a7826053 100644
--- a/packages/web/pages/settings/subscriptions.tsx
+++ b/packages/web/pages/settings/subscriptions.tsx
@@ -15,8 +15,9 @@ import { formattedShortDate } from '../../lib/dateFormatting'
export default function SubscriptionsPage(): JSX.Element {
const { subscriptions, revalidate, isValidating } = useGetSubscriptionsQuery()
- const [confirmUnsubscribeName, setConfirmUnsubscribeName] =
- useState(null)
+ const [confirmUnsubscribeName, setConfirmUnsubscribeName] = useState<
+ string | null
+ >(null)
applyStoredTheme(false)
@@ -40,7 +41,6 @@ export default function SubscriptionsPage(): JSX.Element {
return (