mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge branch 'main' of github.com:omnivore-app/omnivore into 895
This commit is contained in:
commit
1852ec2a6b
21 changed files with 269 additions and 228 deletions
|
|
@ -86,7 +86,7 @@ Open <http://localhost:3000> and confirm Omnivore is running
|
|||
Omnivore uses social login, but for testing there is an email + password
|
||||
option.
|
||||
|
||||
Go to <http://localhost:3000/email-registration> in your browser.
|
||||
Go to <http://localhost:3000/email-signup> in your browser.
|
||||
|
||||
|
||||
### Frontend Development
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"manifest_version": 2,
|
||||
"name": "Omnivore",
|
||||
"short_name": "Omnivore",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.24",
|
||||
"description": "Save articles to your Omnivore library",
|
||||
"author": "Omnivore Media, Inc",
|
||||
"default_locale": "en",
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
"url": "https://omnivore.app/"
|
||||
},
|
||||
"homepage_url": "https://omnivore.app/",
|
||||
"content_security_policy": "default-src 'none'; child-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; prefetch-src 'none'; worker-src 'none'; connect-src https://storage.googleapis.com/ https://omnivore.app/api/ blob:; frame-src 'none'; font-src 'none'; img-src data:; script-src 'self'; script-src-elem 'self'; script-src-attr 'none'; style-src 'self'; style-src-elem 'self'; style-src-attr 'none'; base-uri 'none'; form-action 'none'; block-all-mixed-content; upgrade-insecure-requests; report-uri https://api.jeurissen.co/reports/csp/webext/omnivore/",
|
||||
"content_security_policy": "default-src 'none'; child-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; prefetch-src 'none'; worker-src 'none'; connect-src https://storage.googleapis.com/ https://api-prod.omnivore.app/api/ blob:; frame-src 'none'; font-src 'none'; img-src data:; script-src 'self'; script-src-elem 'self'; script-src-attr 'none'; style-src 'self'; style-src-elem 'self'; style-src-attr 'none'; base-uri 'none'; form-action 'none'; block-all-mixed-content; upgrade-insecure-requests; report-uri https://api.jeurissen.co/reports/csp/webext/omnivore/",
|
||||
"icons": {
|
||||
"16": "/images/extension/icon-16.png",
|
||||
"24": "/images/extension/icon-24.png",
|
||||
|
|
@ -80,6 +80,14 @@
|
|||
},
|
||||
"default_title": "Omnivore Save Article"
|
||||
},
|
||||
"commands": {
|
||||
"_execute_browser_action": {
|
||||
"suggested_key": {
|
||||
"default": "Alt + O"
|
||||
},
|
||||
"description": "Save the current tab to Omnivore"
|
||||
}
|
||||
},
|
||||
"web_accessible_resources": [
|
||||
"views/cta-popup.html"
|
||||
]
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
"use strict";window.browserApi="object"==typeof chrome&&chrome&&chrome.runtime&&chrome||"object"==typeof browser&&browser||{},window.browserActionApi=browserApi.action||browserApi.browserAction||browserApi.pageAction,window.browserScriptingApi=browserApi.scripting||browserApi.tabs,window.ENV_EXTENSION_ORIGIN=browserApi.runtime.getURL("PATH/").replace("/PATH/",""),window.ENV_IS_FIREFOX=ENV_EXTENSION_ORIGIN.startsWith("moz-extension://"),window.ENV_IS_EDGE=navigator.userAgent.toLowerCase().indexOf("edg")>-1,window.ENV_DOES_NOT_SUPPORT_BLOB_URL_ACCESS=/^((?!chrome|android).)*safari/i.test(navigator.userAgent),window.SELECTORS={CANONICAL_URL:["head > link[rel='canonical']"],TITLE:["head > meta[property='og:title']"]},window.ACTIONS={ShowMessage:"SHOW_MESSAGE",GetContent:"GET_CONTENT",GetPageInfo:"GET_PAGE_INFO",Ping:"PING",AddIframeContent:"ADD_IFRAME_CONTENT",RefreshDarkMode:"REFRESH_DARK_MODE",GetAuthToken:"GET_AUTH_TOKEN"},window.DONT_REMOVE_ELEMENTS=["meta","script","title"],window.CREATE_ARTICLE_QUERY="mutation CreateArticle ($input: CreateArticleInput!){\n createArticle(input:$input){\n ... on CreateArticleSuccess{\n createdArticle{\n id\n title\n slug\n hasContent\n }\n user {\n id\n profile {\n id\n username\n }\n }\n}\n ... on CreateArticleError{\n errorCodes\n }\n}\n}",window.CREATE_ARTICLE_SAVING_REQUEST_QUERY="mutation CreateArticleSavingRequest ($input: CreateArticleSavingRequestInput!){\n createArticleSavingRequest(input:$input){\n ... on CreateArticleSavingRequestSuccess{\n articleSavingRequest{\n id\n }\n }\n ... on CreateArticleSavingRequestError{\n errorCodes\n }\n }\n}";
|
||||
"use strict";window.browserApi="object"==typeof chrome&&chrome&&chrome.runtime&&chrome||"object"==typeof browser&&browser||{},window.browserActionApi=browserApi.action||browserApi.browserAction||browserApi.pageAction,window.browserScriptingApi=browserApi.scripting||browserApi.tabs,window.ENV_EXTENSION_ORIGIN=browserApi.runtime.getURL("PATH/").replace("/PATH/",""),window.ENV_IS_FIREFOX=ENV_EXTENSION_ORIGIN.startsWith("moz-extension://"),window.ENV_IS_EDGE=navigator.userAgent.toLowerCase().indexOf("edg")>-1,window.ENV_DOES_NOT_SUPPORT_BLOB_URL_ACCESS=/^((?!chrome|android).)*safari/i.test(navigator.userAgent),window.SELECTORS={CANONICAL_URL:["head > link[rel='canonical']"],TITLE:["head > meta[property='og:title']"]},window.ACTIONS={ShowMessage:"SHOW_MESSAGE",GetContent:"GET_CONTENT",GetPageInfo:"GET_PAGE_INFO",Ping:"PING",AddIframeContent:"ADD_IFRAME_CONTENT",RefreshDarkMode:"REFRESH_DARK_MODE",GetAuthToken:"GET_AUTH_TOKEN"},window.DONT_REMOVE_ELEMENTS=["meta","script","title"],window.SAVE_URL_QUERY="mutation SaveUrl ($input: SaveUrlInput!) {\n saveUrl(input:$input){\n ... on SaveSuccess {\n url\n }\n ... on SaveError {\n errorCodes\n }\n }\n}",window.SAVE_FILE_QUERY="mutation SaveFile ($input: SaveFileInput!) {\n saveFile(input:$input){\n ... on SaveSuccess {\n url\n }\n ... on SaveError {\n errorCodes\n }\n }\n}",window.SAVE_PAGE_QUERY="mutation SavePage ($input: SavePageInput!) {\n savePage(input:$input){\n ... on SaveSuccess {\n url\n }\n ... on SaveError {\n errorCodes\n }\n }\n}",window.CREATE_ARTICLE_QUERY="mutation CreateArticle ($input: CreateArticleInput!){\n createArticle(input:$input){\n ... on CreateArticleSuccess{\n createdArticle{\n id\n title\n slug\n hasContent\n }\n user {\n id\n profile {\n id\n username\n }\n }\n}\n ... on CreateArticleError{\n errorCodes\n }\n}\n}",window.CREATE_ARTICLE_SAVING_REQUEST_QUERY="mutation CreateArticleSavingRequest ($input: CreateArticleSavingRequestInput!){\n createArticleSavingRequest(input:$input){\n ... on CreateArticleSavingRequestSuccess{\n articleSavingRequest{\n id\n }\n }\n ... on CreateArticleSavingRequestError{\n errorCodes\n }\n }\n}";
|
||||
|
|
@ -452,14 +452,14 @@ export function authRouter() {
|
|||
if (data.signup.__typename === 'SignupError') {
|
||||
const errorCodes = data.signup.errorCodes.join(',')
|
||||
return res.redirect(
|
||||
`${env.client.url}/email-registration?errorCodes=${errorCodes}`
|
||||
`${env.client.url}/email-signup?errorCodes=${errorCodes}`
|
||||
)
|
||||
}
|
||||
|
||||
res.redirect(`${env.client.url}/email-login?message=SIGNUP_SUCCESS`)
|
||||
} catch (e) {
|
||||
logger.info('email-signup exception:', e)
|
||||
res.redirect(`${env.client.url}/email-registration?errorCodes=UNKNOWN`)
|
||||
res.redirect(`${env.client.url}/email-signup?errorCodes=UNKNOWN`)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,13 +23,11 @@ export const saveFile = async (
|
|||
): Promise<SaveResult> => {
|
||||
console.log('saving file with input', input)
|
||||
|
||||
// /* We do not trust the values from client, lookup upload file by querying
|
||||
// * with filtering on user ID and URL to verify client's uploadFileId is valid.
|
||||
// */
|
||||
const uploadFile = await ctx.models.uploadFile.getWhere({
|
||||
id: input.uploadFileId,
|
||||
userId: saver.id,
|
||||
})
|
||||
|
||||
if (!uploadFile) {
|
||||
return {
|
||||
errorCodes: [SaveErrorCode.Unauthorized],
|
||||
|
|
@ -45,64 +43,6 @@ export const saveFile = async (
|
|||
return ctx.models.uploadFile.setFileUploadComplete(input.uploadFileId, tx)
|
||||
})
|
||||
|
||||
// if (!uploadFileData || !uploadFileData.id || !uploadFileData.fileName) {
|
||||
// console.log('error completing upload file request', input)
|
||||
// return {
|
||||
// errorCodes: [SaveErrorCode.Unknown],
|
||||
// }
|
||||
// }
|
||||
|
||||
// // const uploadFileUrlOverride = await makeStorageFilePublic(
|
||||
// // uploadFileData.id,
|
||||
// // uploadFileData.fileName
|
||||
// // )
|
||||
|
||||
// const matchedUserArticleRecord = await getPageByParam({
|
||||
// userId: saver.id,
|
||||
// url: uploadFileData.url,
|
||||
// state: ArticleSavingRequestStatus.Succeeded,
|
||||
// })
|
||||
|
||||
// if (matchedUserArticleRecord) {
|
||||
// await updatePage(
|
||||
// matchedUserArticleRecord.id,
|
||||
// {
|
||||
// savedAt: new Date(),
|
||||
// archivedAt: null,
|
||||
// },
|
||||
// ctx
|
||||
// )
|
||||
// input.clientRequestId = matchedUserArticleRecord.id
|
||||
// } else {
|
||||
// const pageId = await createPage(
|
||||
// {
|
||||
// url: uploadFile.url,
|
||||
// title: uploadFile.fileName,
|
||||
// hash: uploadFileDetails.md5Hash,
|
||||
// content: '',
|
||||
// pageType: PageType.File,
|
||||
// uploadFileId: input.uploadFileId,
|
||||
// slug: generateSlug(uploadFile.fileName),
|
||||
// userId: saver.id,
|
||||
// id: input.clientRequestId,
|
||||
// createdAt: new Date(),
|
||||
// savedAt: new Date(),
|
||||
// readingProgressPercent: 0,
|
||||
// readingProgressAnchorIndex: 0,
|
||||
// state: ArticleSavingRequestStatus.Succeeded,
|
||||
// },
|
||||
// ctx
|
||||
// )
|
||||
|
||||
// if (!pageId) {
|
||||
// console.log('error creating page in elastic', input)
|
||||
// return {
|
||||
// errorCodes: [SaveErrorCode.Unknown],
|
||||
// }
|
||||
// }
|
||||
// input.clientRequestId = pageId
|
||||
// }
|
||||
|
||||
return {
|
||||
clientRequestId: input.clientRequestId,
|
||||
url: `${homePageURL()}/${saver.profile.username}/links/${
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const fullName = 'Test User'
|
|||
|
||||
describe('Register with email', () => {
|
||||
it('creates a new user', function () {
|
||||
cy.visit('/email-registration')
|
||||
cy.visit('/email-signup')
|
||||
|
||||
cy.get('input[name=email]').type(email)
|
||||
cy.get('input[name=username]').type(username)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Cypress.Commands.add('login', (email, password) => {
|
|||
})
|
||||
|
||||
Cypress.Commands.add('registerEmail', (email, username, password, fullName) => {
|
||||
cy.visit('/email-registration')
|
||||
cy.visit('/email-signup')
|
||||
|
||||
cy.get('input[name=email]').type(email)
|
||||
cy.get('input[name=username]').type(username)
|
||||
|
|
|
|||
|
|
@ -82,13 +82,22 @@ type OmnivoreIllustrationProps = {
|
|||
|
||||
function OmnivoreIllustration({ isLargeLayout }: OmnivoreIllustrationProps) {
|
||||
return (
|
||||
<img
|
||||
style={{ marginRight: '57px', objectFit: 'fill' }}
|
||||
width='100%'
|
||||
srcSet="/static/images/landing-illustration.png 1x,
|
||||
/static/images/landing-illustration@2x.png 2x"
|
||||
alt="Screenshot of Omnivore"
|
||||
/>
|
||||
<Box css={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
marginRight: '57px',
|
||||
backgroundSize: 'cover',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPositionY: '70px',
|
||||
backgroundImage: `-webkit-image-set(
|
||||
url('/static/images/landing-illustration.png') 1x,
|
||||
url('/static/images/landing-illustration@2x.png') 2x
|
||||
)`,
|
||||
'background-image': `image-set(
|
||||
url('/static/images/landing-illustration.png') 1x,
|
||||
url('/static/images/landing-illustration@2x.png') 2x
|
||||
)`
|
||||
}} />
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 13 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 301 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 828 KiB After Width: | Height: | Size: 945 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
|
|
@ -1,3 +1,3 @@
|
|||
OMNIVORE_URL="https://demo.omnivore.app"
|
||||
OMNIVORE_GRAPHQL_URL="https://demo.omnivore.app/api/"
|
||||
OMNIVORE_GRAPHQL_URL="https://api-demo.omnivore.app/api/"
|
||||
EXTENSION_NAME="Omnivore *DEMO*"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
OMNIVORE_URL="https://omnivore.app"
|
||||
OMNIVORE_GRAPHQL_URL="https://omnivore.app/api/"
|
||||
OMNIVORE_GRAPHQL_URL="https://api-prod.omnivore.app/api/"
|
||||
EXTENSION_NAME="Omnivore"
|
||||
|
|
|
|||
|
|
@ -22,5 +22,8 @@
|
|||
"webpack": "^5.11.1",
|
||||
"webpack-cli": "^4.3.1",
|
||||
"webpack-merge": "^5.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
"256": "/images/extension/icon-256.png"
|
||||
},
|
||||
|
||||
"permissions": ["activeTab", "storage", "https://*/**", "http://*/**"],
|
||||
"permissions": ["activeTab", "storage", "contextMenus", "https://*/**", "http://*/**"],
|
||||
|
||||
"background": {
|
||||
"page": "/views/background.html",
|
||||
|
|
@ -72,5 +72,14 @@
|
|||
"default_title": "Omnivore Save Article"
|
||||
},
|
||||
|
||||
"commands": {
|
||||
"_execute_browser_action": {
|
||||
"suggested_key": {
|
||||
"default": "Alt + O"
|
||||
},
|
||||
"description": "Save the current tab to Omnivore"
|
||||
}
|
||||
},
|
||||
|
||||
"web_accessible_resources": ["views/cta-popup.html"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
let authToken = undefined;
|
||||
const omnivoreURL = process.env.OMNIVORE_URL;
|
||||
const omnivoreGraphqlURL = process.env.OMNIVORE_GRAPHQL_URL;
|
||||
|
|
@ -47,6 +49,17 @@ function removeStorage (itemsToRemove) {
|
|||
});
|
||||
}
|
||||
|
||||
function getCurrentTab () {
|
||||
return new Promise((resolve) => {
|
||||
browserApi.tabs.query({
|
||||
active: true,
|
||||
currentWindow: true
|
||||
}, function (tabs) {
|
||||
resolve(tabs[0] || null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setupConnection(xhr) {
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
if (authToken) {
|
||||
|
|
@ -65,7 +78,7 @@ function uploadFile ({ id, uploadSignedUrl }, contentType, contentObjUrl) {
|
|||
xhr.setRequestHeader('Content-Type', contentType);
|
||||
|
||||
xhr.onerror = () => {
|
||||
resolve(null);
|
||||
resolve(undefined);
|
||||
};
|
||||
xhr.onload = () => {
|
||||
// Uploaded.
|
||||
|
|
@ -80,14 +93,13 @@ function uploadFile ({ id, uploadSignedUrl }, contentType, contentObjUrl) {
|
|||
});
|
||||
}
|
||||
|
||||
function savePdfFile (tab, url, contentType, contentObjUrl) {
|
||||
function savePdfFile(tab, url, contentType, contentObjUrl) {
|
||||
return new Promise(resolve => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = async function () {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
const { data } = JSON.parse(xhr.response);
|
||||
|
||||
if ('errorCodes' in data.uploadFileRequest) {
|
||||
if (data.uploadFileRequest.errorCodes[0] === 'UNAUTHORIZED') {
|
||||
clearClickCompleteState();
|
||||
|
|
@ -103,7 +115,7 @@ function savePdfFile (tab, url, contentType, contentObjUrl) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!data.uploadFileRequest || !data.uploadFileRequest.id || 'errorCodes' in data.uploadFileRequest) {
|
||||
if (!data.uploadFileRequest || !data.uploadFileRequest.id || !data.uploadFileRequest.createdPageId || 'errorCodes' in data.uploadFileRequest) {
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: {
|
||||
|
|
@ -112,9 +124,26 @@ function savePdfFile (tab, url, contentType, contentObjUrl) {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
const uploadFileId = await uploadFile(data.uploadFileRequest, contentType, contentObjUrl);
|
||||
const result = await uploadFile(data.uploadFileRequest, contentType, contentObjUrl);
|
||||
URL.revokeObjectURL(contentObjUrl);
|
||||
return resolve(uploadFileId);
|
||||
|
||||
if (!result) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const createdPageId = data.uploadFileRequest.createdPageId
|
||||
const url = omnivoreURL + '/article/sr/' + createdPageId
|
||||
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: {
|
||||
text: 'Saved to Omnivore',
|
||||
link: url,
|
||||
linkText: 'Read Now',
|
||||
type: 'success'
|
||||
}
|
||||
})
|
||||
return resolve(data.uploadFileRequest);
|
||||
}
|
||||
} else if (xhr.status === 400) {
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
|
|
@ -137,6 +166,7 @@ function savePdfFile (tab, url, contentType, contentObjUrl) {
|
|||
}
|
||||
... on UploadFileRequestSuccess {
|
||||
id
|
||||
createdPageId
|
||||
uploadSignedUrl
|
||||
}
|
||||
}
|
||||
|
|
@ -144,7 +174,8 @@ function savePdfFile (tab, url, contentType, contentObjUrl) {
|
|||
variables: {
|
||||
input: {
|
||||
url,
|
||||
contentType
|
||||
contentType,
|
||||
createPageEntry: true,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -164,8 +195,71 @@ function clearClickCompleteState () {
|
|||
});
|
||||
}
|
||||
|
||||
function saveArticle (tab) {
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
function handleSaveResponse(tab, field, xhr) {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
const { data } = JSON.parse(xhr.response);
|
||||
const item = data[field]
|
||||
if (!item) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if ('errorCodes' in item) {
|
||||
const messagePayload = {
|
||||
text: descriptions[data.createArticle.errorCodes[0]] || 'Unable to save page',
|
||||
type: 'error'
|
||||
}
|
||||
|
||||
if (item.errorCodes[0] === 'UNAUTHORIZED') {
|
||||
messagePayload.errorCode = 401
|
||||
messagePayload.url = omnivoreURL
|
||||
clearClickCompleteState()
|
||||
}
|
||||
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: messagePayload
|
||||
})
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
const url = item['url']
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: {
|
||||
text: 'Saved to Omnivore',
|
||||
link: url ?? omnivoreURL + '/home',
|
||||
linkText: 'Read Now',
|
||||
type: 'success'
|
||||
}
|
||||
})
|
||||
|
||||
return item
|
||||
} else if (xhr.status === 400) {
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: {
|
||||
text: 'Unable to save page',
|
||||
type: 'error'
|
||||
}
|
||||
})
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function saveUrl(currentTab, url) {
|
||||
const requestId = uuidv4()
|
||||
await saveApiRequest(currentTab, SAVE_URL_QUERY, 'saveUrl', {
|
||||
source: 'extension',
|
||||
clientRequestId: requestId,
|
||||
url: encodeURI(url),
|
||||
})
|
||||
}
|
||||
|
||||
async function saveApiRequest(currentTab, query, field, input) {
|
||||
browserApi.tabs.sendMessage(currentTab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: {
|
||||
type: 'loading',
|
||||
|
|
@ -173,85 +267,48 @@ function saveArticle (tab) {
|
|||
}
|
||||
});
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
const descriptions = {
|
||||
BAD_DATA: 'Unable to save page',
|
||||
NOT_ALLOWED_TO_PARSE: 'Not allowed to parse this article',
|
||||
UNAUTHORIZED: 'Please login to Omnivore to authorize this action',
|
||||
UNABLE_TO_FETCH: 'Unable to fetch page',
|
||||
PAYLOAD_TOO_LARGE: 'This article is too large'
|
||||
};
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
const { data } = JSON.parse(xhr.response);
|
||||
if ('createArticle' in data) {
|
||||
if ('errorCodes' in data.createArticle) {
|
||||
const messagePayload = {
|
||||
text: descriptions[data.createArticle.errorCodes[0]] || 'Unable to save page',
|
||||
type: 'error'
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.open('POST', omnivoreGraphqlURL + 'graphql', true)
|
||||
setupConnection(xhr)
|
||||
|
||||
if (data.createArticle.errorCodes[0] === 'UNAUTHORIZED') {
|
||||
messagePayload.errorCode = 401;
|
||||
messagePayload.url = omnivoreURL;
|
||||
xhr.onerror = (err) => { reject(err) }
|
||||
|
||||
clearClickCompleteState();
|
||||
}
|
||||
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: messagePayload
|
||||
});
|
||||
} else {
|
||||
const article = data.createArticle.createdArticle;
|
||||
const user = data.createArticle.user;
|
||||
const link = omnivoreURL + (article.hasContent ? (`/${user.profile.username}/` + article.slug) : '/home');
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: {
|
||||
text: 'Saved to Omnivore',
|
||||
link: link,
|
||||
linkText: 'View',
|
||||
type: 'success'
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if ('errorCodes' in data.createArticleSavingRequest) {
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: {
|
||||
text: descriptions[data.createArticleSavingRequest.errorCodes[0]] || 'Unable to save page',
|
||||
type: 'error'
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const articleSavingRequest = data.createArticleSavingRequest.articleSavingRequest;
|
||||
const link = omnivoreURL + '/article/sr/' + articleSavingRequest.id;
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: {
|
||||
text: 'Saved to Omnivore',
|
||||
link: link,
|
||||
linkText: 'View',
|
||||
type: 'success'
|
||||
}
|
||||
});
|
||||
}
|
||||
xhr.onload = () => {
|
||||
try {
|
||||
const res = handleSaveResponse(currentTab, field, xhr)
|
||||
if (!res) {
|
||||
return reject()
|
||||
}
|
||||
} else if (xhr.status === 400) {
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: {
|
||||
text: 'Unable to save page',
|
||||
type: 'error'
|
||||
}
|
||||
});
|
||||
resolve(res);
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const data = JSON.stringify({
|
||||
query,
|
||||
variables: {
|
||||
input
|
||||
}
|
||||
})
|
||||
|
||||
xhr.send(data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('error saving page', err)
|
||||
browserApi.tabs.sendMessage(currentTab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: {
|
||||
text: 'Unable to save page',
|
||||
type: 'error',
|
||||
}
|
||||
});
|
||||
return undefined
|
||||
});
|
||||
}
|
||||
|
||||
function saveArticle (tab) {
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.GetContent
|
||||
}, async (response) => {
|
||||
|
|
@ -260,56 +317,42 @@ function saveArticle (tab) {
|
|||
return;
|
||||
}
|
||||
|
||||
const requestId = uuidv4()
|
||||
const { type, pageInfo, doc, uploadContentObjUrl } = response;
|
||||
|
||||
let uploadResult = null;
|
||||
|
||||
switch(type) {
|
||||
case 'pdf': {
|
||||
// For PDFs, we first upload the PDF file before passing the upload file ID in createArticle
|
||||
uploadResult = await savePdfFile(tab, encodeURI(tab.url), pageInfo.contentType, uploadContentObjUrl);
|
||||
if (!uploadResult || !uploadResult.id) {
|
||||
browserApi.tabs.sendMessage(tab.id, {
|
||||
action: ACTIONS.ShowMessage,
|
||||
payload: {
|
||||
text: 'Unable to save page',
|
||||
type: 'error'
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
case 'html': {
|
||||
await saveApiRequest(tab, SAVE_PAGE_QUERY, 'savePage', {
|
||||
source: 'extension',
|
||||
clientRequestId: requestId,
|
||||
originalContent: doc,
|
||||
title: pageInfo.title,
|
||||
url: encodeURI(tab.url),
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'url': {
|
||||
// We don't have to special case URL, it will fall through
|
||||
// and be handled when isContentAvailable returns false
|
||||
await saveApiRequest(tab, SAVE_URL_QUERY, 'saveUrl', {
|
||||
source: 'extension',
|
||||
clientRequestId: requestId,
|
||||
url: encodeURI(tab.url),
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'pdf': {
|
||||
const uploadResult = await savePdfFile(tab, encodeURI(tab.url), pageInfo.contentType, uploadContentObjUrl);
|
||||
if (!uploadResult || !uploadResult.id) {
|
||||
// If the upload failed for any reason, try to save the PDF URL instead
|
||||
await saveApiRequest(tab, SAVE_URL_QUERY, 'saveUrl', {
|
||||
source: 'extension',
|
||||
clientRequestId: requestId,
|
||||
url: encodeURI(tab.url),
|
||||
})
|
||||
return;
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const isContentAvailable = (doc && doc.length) || uploadResult;
|
||||
const query = isContentAvailable ? CREATE_ARTICLE_QUERY : CREATE_ARTICLE_SAVING_REQUEST_QUERY;
|
||||
const input = {
|
||||
url: encodeURI(tab.url)
|
||||
};
|
||||
|
||||
if (isContentAvailable) {
|
||||
input.preparedDocument = {
|
||||
document: doc,
|
||||
pageInfo
|
||||
};
|
||||
input.uploadFileId = (uploadResult && uploadResult.id) || null;
|
||||
}
|
||||
|
||||
const data = JSON.stringify({
|
||||
query,
|
||||
variables: {
|
||||
input
|
||||
}
|
||||
});
|
||||
|
||||
xhr.open('POST', omnivoreGraphqlURL + 'graphql', true);
|
||||
setupConnection(xhr);
|
||||
|
||||
xhr.send(data);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -484,18 +527,13 @@ function checkAuthOnFirstClickPostInstall (tabId) {
|
|||
});
|
||||
}
|
||||
|
||||
function getCurrentTab () {
|
||||
return new Promise((resolve) => {
|
||||
browserApi.tabs.query({
|
||||
active: true,
|
||||
currentWindow: true
|
||||
}, function (tabs) {
|
||||
resolve(tabs[0] || null);
|
||||
});
|
||||
});
|
||||
function handleActionClick () {
|
||||
executeAction(function (currentTab) {
|
||||
onExtensionClick(currentTab.id);
|
||||
})
|
||||
}
|
||||
|
||||
function handleActionClick () {
|
||||
function executeAction(action) {
|
||||
getCurrentTab().then((currentTab) => {
|
||||
browserApi.tabs.sendMessage(currentTab.id, {
|
||||
action: ACTIONS.Ping
|
||||
|
|
@ -504,7 +542,7 @@ function handleActionClick () {
|
|||
// Content script ready
|
||||
const isSignedUp = await checkAuthOnFirstClickPostInstall(currentTab.id);
|
||||
if (isSignedUp) {
|
||||
onExtensionClick(currentTab.id);
|
||||
action(currentTab);
|
||||
}
|
||||
} else {
|
||||
const extensionManifest = browserApi.runtime.getManifest();
|
||||
|
|
@ -518,7 +556,7 @@ function handleActionClick () {
|
|||
executeScripts(currentTab.id, scriptFiles, async function () {
|
||||
const isSignedUp = await checkAuthOnFirstClickPostInstall(currentTab.id);
|
||||
if (isSignedUp) {
|
||||
onExtensionClick(currentTab.id);
|
||||
action(currentTab);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -644,6 +682,17 @@ function init () {
|
|||
browserActionApi.setIcon({
|
||||
path: getIconPath(true)
|
||||
});
|
||||
|
||||
browserApi.contextMenus.create({
|
||||
id: "save-selection",
|
||||
title: "Save to Omnivore",
|
||||
contexts: ["link"],
|
||||
onclick: async function(obj) {
|
||||
executeAction(async function (currentTab) {
|
||||
await saveUrl(currentTab, obj.linkUrl)
|
||||
})
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
|
|
|
|||
|
|
@ -30,6 +30,39 @@ window.DONT_REMOVE_ELEMENTS = [
|
|||
'title'
|
||||
];
|
||||
|
||||
window.SAVE_URL_QUERY = `mutation SaveUrl ($input: SaveUrlInput!) {
|
||||
saveUrl(input:$input){
|
||||
... on SaveSuccess {
|
||||
url
|
||||
}
|
||||
... on SaveError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
window.SAVE_FILE_QUERY = `mutation SaveFile ($input: SaveFileInput!) {
|
||||
saveFile(input:$input){
|
||||
... on SaveSuccess {
|
||||
url
|
||||
}
|
||||
... on SaveError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
window.SAVE_PAGE_QUERY = `mutation SavePage ($input: SavePageInput!) {
|
||||
savePage(input:$input){
|
||||
... on SaveSuccess {
|
||||
url
|
||||
}
|
||||
... on SaveError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
window.CREATE_ARTICLE_QUERY = `mutation CreateArticle ($input: CreateArticleInput!){
|
||||
createArticle(input:$input){
|
||||
... on CreateArticleSuccess{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
}
|
||||
|
||||
browserApi.runtime.onMessage.addListener(({ action, payload }, sender, sendResponse) => {
|
||||
/* async actions */
|
||||
if (action === ACTIONS.GetContent) {
|
||||
prepareContent().then((pageContent) => {
|
||||
sendResponse({
|
||||
|
|
|
|||
|
|
@ -4655,7 +4655,7 @@ uuid@^3.3.2:
|
|||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
uuid@^8.3.0:
|
||||
uuid@^8.3.0, uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
|
|
|||
Loading…
Reference in a new issue