Skip to content

Commit 05bf685

Browse files
committed
feat(notes): add note creation
1 parent b367110 commit 05bf685

File tree

23 files changed

+634
-124
lines changed

23 files changed

+634
-124
lines changed

clients/extension/action/app.tsx

+15-18
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
import { useEffect, useState } from 'react'
22
import { EXT_ACTIONS } from '../actions'
33
import { ActionContainer } from '../components/action-container'
4+
import { sendMessage } from '../utilities/send-message'
45

5-
import type { ExtItem, ExtMessage, ExtPreview } from '../types'
6-
import type { NoteEdge } from '@common/types/pocket'
6+
import type { ExtItem, ExtMessage } from '../types'
77

88
export function App() {
99
const [isOpen, setIsOpen] = useState(false)
1010
const [saveStatus, setSaveStatus] = useState('saving')
1111
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
12-
const [preview, setPreview] = useState<ExtPreview | undefined>(undefined)
13-
const [tags, setTags] = useState<string[]>([])
14-
const [notes, setNotes] = useState<NoteEdge[] | undefined>()
12+
const [item, setItem] = useState<ExtItem | undefined>(undefined)
1513

1614
/* Setup Listeners and show popup
1715
–––––––––––––––––––––––––––––––––––––––––––––––––– */
@@ -27,24 +25,19 @@ export function App() {
2725
/* Send a message on action activating
2826
–––––––––––––––––––––––––––––––––––––––––––––––––– */
2927
useEffect(() => {
30-
if (isOpen) void chrome.runtime.sendMessage({ action: EXT_ACTIONS.BROWSER_ACTION })
28+
if (isOpen) void sendMessage({ action: EXT_ACTIONS.BROWSER_ACTION })
3129
}, [isOpen])
3230

3331
/* Handle incoming messages
3432
–––––––––––––––––––––––––––––––––––––––––––––––––– */
3533
function handleMessages(message: ExtMessage) {
3634
const { action = 'Unknown Action' } = message || {}
37-
console.log('ACTION:', { message })
35+
3836
switch (action) {
37+
case EXT_ACTIONS.ADD_NOTE_SUCCESS:
3938
case EXT_ACTIONS.SAVE_TO_POCKET_SUCCESS: {
4039
const item = message?.item as ExtItem
41-
const preview = item?.preview
42-
const suggestedTags = item?.savedItem?.suggestedTags.map((tag) => tag.name) ?? []
43-
const itemNotes = item?.savedItem?.notes
44-
45-
setPreview(preview)
46-
setTags(suggestedTags)
47-
setNotes(itemNotes)
40+
setItem(item)
4841
setSaveStatus('saved')
4942
return
5043
}
@@ -56,6 +49,12 @@ export function App() {
5649
return
5750
}
5851

52+
case EXT_ACTIONS.ADD_NOTE_FAILURE: {
53+
const { error } = message ?? 'Unknown error'
54+
setErrorMessage(error)
55+
return
56+
}
57+
5958
case EXT_ACTIONS.REMOVE_ITEM_REQUEST: {
6059
setSaveStatus('removing')
6160
return
@@ -108,17 +107,15 @@ export function App() {
108107
const actionUnSave = () => {}
109108

110109
const actionLogOut = () => {
111-
void chrome.runtime.sendMessage({ action: EXT_ACTIONS.LOGGED_OUT_OF_POCKET })
110+
void sendMessage({ action: EXT_ACTIONS.LOGGED_OUT_OF_POCKET })
112111
}
113112

114113
return (
115114
<ActionContainer
116115
errorMessage={errorMessage}
117116
saveStatus={saveStatus}
118117
isOpen={isOpen}
119-
preview={preview}
120-
tags={tags}
121-
notes={notes}
118+
item={item}
122119
actionUnSave={actionUnSave}
123120
actionLogOut={actionLogOut}
124121
/>

clients/extension/actions.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,8 @@ export enum EXT_ACTIONS {
2727
UPDATE_ITEM_PREVIEW = 'UPDATE_ITEM_PREVIEW',
2828
UPDATE_STORED_TAGS = 'UPDATE_STORED_TAGS',
2929
UPDATE_TAG_ERROR = 'UPDATE_TAG_ERROR',
30-
USER_LOG_IN = 'USER_LOG_IN'
30+
USER_LOG_IN = 'USER_LOG_IN',
31+
ADD_NOTE_REQUEST = 'ADD_NOTE_REQUEST',
32+
ADD_NOTE_SUCCESS = 'ADD_NOTE_SUCCESS',
33+
ADD_NOTE_FAILURE = 'ADD_NOTE_FAILURE'
3134
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { extensionRequest } from '../../utilities/request'
2+
import { verifySession } from '../auth'
3+
import { FRAGMENT_ITEM_PREVIEW } from './fragments/preview'
4+
import { FRAGMENT_SAVED_ITEM } from './fragments/saved-item'
5+
6+
import type { ExtItem, ExtSavedItem } from '../../types'
7+
import type { CreateNoteMarkdownInput } from '@common/types/pocket'
8+
9+
interface ExtCreateNoteMarkdown {
10+
archived: boolean
11+
contentPreview: string
12+
createdAt: string
13+
deleted: boolean
14+
docMarkdown: boolean
15+
id: string
16+
source: string | undefined
17+
title: string | undefined
18+
updatedAt: string
19+
savedItem: ExtSavedItem
20+
}
21+
22+
const gql = String.raw
23+
const query = gql`
24+
mutation CreateNoteMarkdown($input: CreateNoteMarkdownInput!) {
25+
createNoteMarkdown(input: $input) {
26+
archived
27+
contentPreview
28+
createdAt
29+
deleted
30+
docMarkdown
31+
id
32+
source
33+
title
34+
updatedAt
35+
savedItem {
36+
...ItemSaved
37+
item {
38+
... on Item {
39+
preview {
40+
...ItemPreview
41+
}
42+
}
43+
}
44+
}
45+
}
46+
}
47+
48+
${FRAGMENT_SAVED_ITEM}
49+
${FRAGMENT_ITEM_PREVIEW}
50+
`
51+
52+
export async function addNote(input: CreateNoteMarkdownInput) {
53+
// We need a token to save. This will create a session if need be
54+
// be requesting a new bearer if the current one is expired
55+
const token = await verifySession()
56+
57+
// Set up the variables we need for this request
58+
const data = { variables: { input }, query }
59+
60+
// Make the request and return it
61+
const response = await extensionRequest<{ createNoteMarkdown: ExtCreateNoteMarkdown }>(
62+
data,
63+
token
64+
)
65+
66+
const responseItem = response?.createNoteMarkdown?.savedItem
67+
const { item, ...savedItem } = responseItem
68+
const preview = item?.preview
69+
70+
// We have got a pending item back from the server ... we should handle it
71+
if (!item || !preview) throw new Error(`Trouble creating note`)
72+
73+
const validItem: ExtItem = { savedItem, preview }
74+
return validItem
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const gql = String.raw
2+
3+
export const FRAGMENT_ITEM_PREVIEW = gql`
4+
fragment ItemPreview on PocketMetadata {
5+
... on ItemSummary {
6+
previewId: id
7+
id
8+
image {
9+
caption
10+
credit
11+
url
12+
cachedImages(imageOptions: [{ width: 80, id: "ext", height: 80, fileType: PNG }]) {
13+
url
14+
id
15+
}
16+
}
17+
excerpt
18+
title
19+
authors {
20+
name
21+
}
22+
domain {
23+
name
24+
}
25+
datePublished
26+
url
27+
source
28+
}
29+
}
30+
`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const gql = String.raw
2+
3+
export const FRAGMENT_SAVED_ITEM = gql`
4+
fragment ItemSaved on SavedItem {
5+
savedId: id
6+
id
7+
suggestedTags {
8+
id
9+
name
10+
}
11+
tags {
12+
id
13+
name
14+
}
15+
notes {
16+
edges {
17+
cursor
18+
node {
19+
archived
20+
createdAt
21+
deleted
22+
id
23+
updatedAt
24+
title
25+
contentPreview
26+
}
27+
}
28+
}
29+
}
30+
`

clients/extension/background/api/upsert.ts

+9-31
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { extensionRequest } from '../../utilities/request'
22
import { verifySession } from '../auth'
33

4+
import { FRAGMENT_ITEM_PREVIEW } from './fragments/preview'
5+
import { FRAGMENT_SAVED_ITEM } from './fragments/saved-item'
46
import type { ExtSave, ExtItemResponse, ExtItem } from '../../types'
57

68
interface upsertSavedItem {
@@ -9,46 +11,23 @@ interface upsertSavedItem {
911

1012
const gql = String.raw
1113
const query = gql`
12-
mutation ItemUpsert($input: SavedItemUpsertInput!, $imageOptions: [CachedImageInput!]!) {
14+
mutation ItemUpsert($input: SavedItemUpsertInput!) {
1315
upsertSavedItem(input: $input) {
1416
item {
1517
... on Item {
1618
preview {
17-
image {
18-
cachedImages(imageOptions: $imageOptions) {
19-
url
20-
id
21-
}
22-
}
23-
title
24-
excerpt
25-
domain {
26-
name
27-
}
28-
url
29-
id
30-
source
31-
authors {
32-
id
33-
name
34-
}
19+
...ItemPreview
3520
}
3621
savedItem {
37-
suggestedTags {
38-
name
39-
}
40-
notes {
41-
edges {
42-
node {
43-
contentPreview
44-
}
45-
}
46-
}
22+
...ItemSaved
4723
}
4824
}
4925
}
5026
}
5127
}
28+
29+
${FRAGMENT_SAVED_ITEM}
30+
${FRAGMENT_ITEM_PREVIEW}
5231
`
5332

5433
export async function upsertItem(saveData: ExtSave) {
@@ -59,8 +38,7 @@ export async function upsertItem(saveData: ExtSave) {
5938
const token = await verifySession()
6039

6140
// Set up the variables we need for this request
62-
const imageOptions = [{ width: 80, id: 'ext', height: 80, fileType: 'PNG' }]
63-
const data = { variables: { input: { url }, imageOptions }, query }
41+
const data = { variables: { input: { url } }, query }
6442

6543
// Make the request and return it
6644
const response = await extensionRequest<{ upsertSavedItem: upsertSavedItem }>(data, token)

clients/extension/background/index.ts

+24-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import { EXT_ACTIONS } from '../actions'
22
import { POCKET_SAVES, POCKET_HOME } from '../constants'
3+
import { getErrorMessage } from '../utilities/error'
34
import { isSystemPage } from '../utilities/is-system'
45
import { sendMessage } from '../utilities/send-message'
56
import { getSetting } from '../utilities/storage'
7+
import { addNote } from './api/add-note'
68
import { upsertItem } from './api/upsert'
79
import { setAuth, logIn } from './auth'
810
import { setDefaultIcon, setToolbarIcon } from './icon-updates'
911

1012
// Types
11-
import { getErrorMessage } from '../utilities/error'
1213
import type { ExtMessage, ExtSave } from '../types'
14+
import type { ExtNote } from '../types/note'
1315

1416
let saveQueue: ExtSave | null = null
1517

@@ -38,7 +40,6 @@ chrome.runtime.onMessage.addListener(messageHandler)
3840
async function messageHandler(message: ExtMessage, sender: chrome.runtime.MessageSender) {
3941
const { action = EXT_ACTIONS.UNKNOWN_ACTION } = message || {}
4042

41-
console.log(message)
4243
// Messages from `popup action` don't have a sender so we need to query the active tab
4344
const activeTab = await chrome.tabs.query({ active: true, lastFocusedWindow: true })
4445

@@ -76,6 +77,12 @@ async function messageHandler(message: ExtMessage, sender: chrome.runtime.Messag
7677
return true
7778
}
7879

80+
case EXT_ACTIONS.ADD_NOTE_REQUEST: {
81+
const { noteData } = message
82+
if (noteData && tab.url) await saveNote(noteData, tab.url)
83+
return true
84+
}
85+
7986
case EXT_ACTIONS.AUTH_CODE_RECEIVED: {
8087
try {
8188
const { auth } = message
@@ -140,11 +147,6 @@ async function save(saveData: ExtSave) {
140147

141148
// send a message so the popup can display the preview
142149
sendMessage({ action: EXT_ACTIONS.SAVE_TO_POCKET_SUCCESS, item })
143-
144-
// Set the toolbar icon to saved
145-
// ?? Since we generally can't track this, it feels problematic to
146-
// ?? maintain it in a reasonable fashion. However, it is the way at the moment.
147-
await setToolbarIcon(id, true)
148150
} catch (error) {
149151
// Things have gone awry. Let's send the error along
150152
const message = getErrorMessage(error)
@@ -154,6 +156,21 @@ async function save(saveData: ExtSave) {
154156
}
155157
}
156158

159+
async function saveNote(noteData: ExtNote, source?: string) {
160+
try {
161+
// Let's add our note
162+
const item = await addNote({ ...noteData, source })
163+
164+
// send a message so the popup can display the preview
165+
sendMessage({ action: EXT_ACTIONS.ADD_NOTE_SUCCESS, item })
166+
} catch (error) {
167+
// Things have gone awry. Let's send the error along
168+
const message = getErrorMessage(error)
169+
sendMessage({ action: EXT_ACTIONS.ADD_NOTE_FAILURE, error: message })
170+
return false
171+
}
172+
}
173+
157174
export function openPocket() {
158175
void chrome.tabs.create({ url: POCKET_SAVES })
159176
}

0 commit comments

Comments
 (0)