From 99d47db9bb4bd695f4d1c5c6bb0fac519da70140 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 21 Mar 2023 10:55:27 +0800 Subject: [PATCH] Handle PDFs and logged out state --- pkg/extension/src/scripts/background.js | 227 ++++++++++----------- pkg/extension/src/scripts/content/toast.js | 121 +++++++---- pkg/extension/src/views/cta-popup.html | 97 ++++++++- pkg/extension/src/views/toast.html | 18 +- 4 files changed, 293 insertions(+), 170 deletions(-) diff --git a/pkg/extension/src/scripts/background.js b/pkg/extension/src/scripts/background.js index 98503ec4f..efa8ecb2f 100644 --- a/pkg/extension/src/scripts/background.js +++ b/pkg/extension/src/scripts/background.js @@ -34,31 +34,16 @@ function getCurrentTab() { }) } -function setupConnection(xhr) { - xhr.setRequestHeader('Content-Type', 'application/json') - if (authToken) { - xhr.setRequestHeader('Authorization', authToken) - } -} - -/* other code */ function uploadFile({ id, uploadSignedUrl }, contentType, contentObjUrl) { return fetch(contentObjUrl) .then((r) => r.blob()) .then((blob) => { - return new Promise((resolve) => { - const xhr = new XMLHttpRequest() - xhr.open('PUT', uploadSignedUrl, true) - xhr.setRequestHeader('Content-Type', contentType) - - xhr.onerror = () => { - resolve(undefined) - } - xhr.onload = () => { - // Uploaded. - resolve({ id }) - } - xhr.send(blob) + return fetch(uploadSignedUrl, { + method: 'PUT', + headers: { + 'Content-Type': contentType, + }, + body: blob, }) }) .catch((err) => { @@ -67,104 +52,111 @@ function uploadFile({ id, uploadSignedUrl }, 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() - browserApi.tabs.sendMessage(tab.id, { - action: ACTIONS.ShowMessage, - payload: { - text: 'Unable to save page', - type: 'error', - errorCode: 401, - url: omnivoreURL, - }, - }) - } - } - - if ( - !data.uploadFileRequest || - !data.uploadFileRequest.id || - !data.uploadFileRequest.createdPageId || - 'errorCodes' in data.uploadFileRequest - ) { - browserApi.tabs.sendMessage(tab.id, { - action: ACTIONS.ShowMessage, - payload: { - text: 'Unable to save page', - type: 'error', - }, - }) - } else { - const result = await uploadFile( - data.uploadFileRequest, - contentType, - contentObjUrl - ) - URL.revokeObjectURL(contentObjUrl) - - if (!result) { - return undefined - } - - const createdPageId = data.uploadFileRequest.createdPageId - browserApi.tabs.sendMessage(tab.id, { - action: ACTIONS.UpdateStatus, - payload: { - status: 'success', - target: 'page', - requestId: createdPageId, - }, - }) - return resolve(data.uploadFileRequest) - } - } else if (xhr.status === 400) { - browserApi.tabs.sendMessage(tab.id, { - action: ACTIONS.ShowMessage, - payload: { - text: 'Unable to save page', - type: 'error', - }, - }) +async function uploadFileRequest(url, contentType) { + const data = JSON.stringify({ + query: `mutation UploadFileRequest($input: UploadFileRequestInput!) { + uploadFileRequest(input:$input) { + ... on UploadFileRequestError { + errorCodes + } + ... on UploadFileRequestSuccess { + id + createdPageId + uploadSignedUrl } - resolve(false) } + }`, + variables: { + input: { + url, + contentType, + createPageEntry: true, + }, + }, + }) + + const field = 'uploadFileRequest' + const result = await gqlRequest(omnivoreGraphqlURL + 'graphql', data) + + if (result[field]['errorCodes']) { + if (result[field]['errorCodes'][0] === 'UNAUTHORIZED') { + browserApi.tabs.sendMessage(currentTab.id, { + action: ACTIONS.UpdateStatus, + payload: { + target: 'logged_out', + status: 'logged_out', + message: 'You are not logged in.', + ctx: toolbarCtx, + }, + }) + clearClickCompleteState() + } else { + browserApi.tabs.sendMessage(currentTab.id, { + action: ACTIONS.UpdateStatus, + payload: { + status: 'failure', + message: 'Unable to save page.', + ctx: toolbarCtx, + }, + }) + } + return undefined + } + + return result.uploadFileRequest +} + +async function savePdfFile( + currentTab, + url, + requestId, + contentType, + contentObjUrl +) { + const toolbarCtx = { + omnivoreURL, + originalURL: url, + requestId: requestId, + } + completedRequests[toolbarCtx.requestId] = undefined + + browserApi.tabs.sendMessage(currentTab.id, { + action: ACTIONS.ShowToolbar, + payload: { + type: 'loading', + ctx: toolbarCtx, + }, + }) + const uploadRequestResult = await uploadFileRequest(url, contentType) + console.log('done uploading pdf', uploadRequestResult) + const uploadFileResult = await uploadFile( + uploadRequestResult, + contentType, + contentObjUrl + ) + console.log(' uploadFileResult: ', uploadFileResult) + URL.revokeObjectURL(contentObjUrl) + + if (uploadFileResult && uploadRequestResult.createdPageId) { + completedRequests[toolbarCtx.requestId] = { + requestId: toolbarCtx.requestId, + responseId: uploadRequestResult.createdPageId, } - const data = JSON.stringify({ - query: `mutation UploadFileRequest($input: UploadFileRequestInput!) { - uploadFileRequest(input:$input) { - ... on UploadFileRequestError { - errorCodes - } - ... on UploadFileRequestSuccess { - id - createdPageId - uploadSignedUrl - } - } - }`, - variables: { - input: { - url, - contentType, - createPageEntry: true, + browserApi.tabs.sendMessage(currentTab.id, { + action: ACTIONS.UpdateStatus, + payload: { + status: 'success', + target: 'page', + ctx: { + requestId: toolbarCtx.requestId, + responseId: uploadRequestResult.createdPageId, }, }, }) + } - xhr.open('POST', omnivoreGraphqlURL + 'graphql', true) - setupConnection(xhr) - - xhr.send(data) - }) + return uploadFileResult } function clearClickCompleteState() { @@ -211,14 +203,6 @@ async function saveApiRequest(currentTab, query, field, input) { try { const result = await gqlRequest(omnivoreGraphqlURL + 'graphql', requestBody) - console.log( - 'result: ', - field, - result, - result[field], - result[field]['errorCodes'] - ) - if (result[field]['errorCodes']) { if (result[field]['errorCodes'][0] === 'UNAUTHORIZED') { browserApi.tabs.sendMessage(currentTab.id, { @@ -342,8 +326,6 @@ async function processPendingRequests(tabId) { } }) - console.log('updated pending requests: ', pendingRequests) - // TODO: need to handle clearing completedRequests also } @@ -393,6 +375,7 @@ async function saveArticle(tab) { const uploadResult = await savePdfFile( tab, encodeURI(tab.url), + requestId, pageInfo.contentType, uploadContentObjUrl ) @@ -759,7 +742,6 @@ function init() { // forward messages from grab-iframe-content.js script to tabs browserApi.runtime.onMessage.addListener((request, sender, sendResponse) => { - console.log(' MESSAGE', request.action, request) if (request.forwardToTab) { delete request.forwardToTab browserApi.tabs.sendRequest(sender.tab.id, request) @@ -783,7 +765,6 @@ function init() { } if (request.action === ACTIONS.SetLabels) { - console.log('pushing setLabels: ', pendingRequests) pendingRequests.push({ id: uuidv4(), type: 'SET_LABELS', diff --git a/pkg/extension/src/scripts/content/toast.js b/pkg/extension/src/scripts/content/toast.js index eb8da3699..3f735c280 100644 --- a/pkg/extension/src/scripts/content/toast.js +++ b/pkg/extension/src/scripts/content/toast.js @@ -80,26 +80,54 @@ return root } - function createCtaModal(url) { - const fragment = document.createDocumentFragment() + async function createCtaModal(url) { + if (currentToastEl) { + currentToastEl.remove() + currentToastEl = undefined + } - const iframeEl = document.createElement('iframe') - const iframePath = '/views/cta-popup.html?url=' + encodeURIComponent(url) - const iframeUrl = ENV_EXTENSION_ORIGIN + iframePath - iframeEl.src = iframeUrl - iframeEl.style.cssText = `all: initial !important; - width: 310px !important; - height: 360px !important; - ` - fragment.appendChild(iframeEl) - document.body.appendChild(fragment) + const file = await fetch(browserApi.runtime.getURL('/views/cta-popup.html')) + const html = await file.text() - return fragment + const root = document.createElement('div') + root.attachShadow({ mode: 'open' }) + if (root.shadowRoot) { + root.shadowRoot.innerHTML = `` + } + + const toastEl = document.createElement('div') + toastEl.id = '#omnivore-toast' + toastEl.innerHTML = html + root.shadowRoot.appendChild(toastEl) + + document.body.appendChild(root) + connectButtons(root) + + return root } function displayLoggedOutView() { - console.log('displaying logged out view') - createCtaModal(ctx.omnivoreURL) + cancelAutoDismiss() + updatePageStatus('failure') + toggleRow('#omnivore-logged-out-row') + updateStatusBox( + '#omnivore-logged-out-status', + 'empty', + `You are not logged in.` + ) + disableAllButtons() + } + + function disableAllButtons() { + const actionButtons = [ + '#omnivore-toast-edit-title-btn', + '#omnivore-toast-edit-labels-btn', + '#omnivore-toast-read-now-btn', + ] + actionButtons.forEach((btnId) => { + const btn = currentToastEl.shadowRoot.querySelector(btnId) + btn.disabled = true + }) } function cancelAutoDismiss() { @@ -116,37 +144,13 @@ if (payload.ctx) { ctx = { ...ctx, ...payload.ctx } } - console.log('updated ctx: ', ctx) switch (payload.target) { case 'logged_out': displayLoggedOutView() break case 'page': - { - const statusBox = currentToastEl.shadowRoot.querySelector( - '.omnivore-toast-statusBox' - ) - switch (payload.status) { - case 'loading': - statusBox.innerHTML = systemIcons.animatedLoader - break - case 'success': - // Auto hide if everything went well and the user - // has not initiated any interaction. - hideToastTimeout = setTimeout(function () { - console.log('hiding: ', currentToastEl, doNotHide) - if (!doNotHide) { - currentToastEl.remove() - currentToastEl = undefined - } - }, 2500) - statusBox.innerHTML = systemIcons.success - break - case 'failure': - statusBox.innerHTML = systemIcons.failure - } - } + updatePageStatus(payload.status) break case 'title': updateStatusBox( @@ -207,6 +211,31 @@ }) } + function updatePageStatus(status) { + const statusBox = currentToastEl.shadowRoot.querySelector( + '.omnivore-toast-statusBox' + ) + switch (status) { + case 'loading': + statusBox.innerHTML = systemIcons.animatedLoader + break + case 'success': + // Auto hide if everything went well and the user + // has not initiated any interaction. + hideToastTimeout = setTimeout(function () { + console.log('hiding: ', currentToastEl, doNotHide) + if (!doNotHide) { + currentToastEl.remove() + currentToastEl = undefined + } + }, 2500) + statusBox.innerHTML = systemIcons.success + break + case 'failure': + statusBox.innerHTML = systemIcons.failure + } + } + function updateStatusBox(boxId, state, message, dismissAfter) { const statusBox = currentToastEl.shadowRoot.querySelector(boxId) const image = (() => { @@ -217,6 +246,8 @@ return systemIcons.success case 'failure': return systemIcons.failure + case 'none': + return '' default: return undefined } @@ -224,7 +255,7 @@ if (image) { statusBox.innerHTML = `${image}${message}` } else { - statusBox.innerText = message + statusBox.innerHTML = message } if (dismissAfter) { setTimeout(() => { @@ -256,7 +287,8 @@ { id: '#omnivore-toast-edit-labels-btn', func: editLabels }, { id: '#omnivore-toast-read-now-btn', func: readNow }, { id: '#omnivore-open-menu-btn', func: openMenu }, - { id: '#omnivore-toast-close-button', func: closeToast }, + { id: '#omnivore-toast-close-btn', func: closeToast }, + { id: '#omnivore-toast-login-btn', func: login }, ] for (const btnInfo of btns) { @@ -522,6 +554,11 @@ currentToastEl = undefined } + function login() { + window.open(new URL(`/login`, ctx.omnivoreURL), '_blank') + setTimeout(closeToast, 2000) + } + window.showToolbar = showToolbar window.updateStatus = updateStatus window.updateLabelsFromCache = updateLabelsFromCache diff --git a/pkg/extension/src/views/cta-popup.html b/pkg/extension/src/views/cta-popup.html index da7b74116..260725c08 100644 --- a/pkg/extension/src/views/cta-popup.html +++ b/pkg/extension/src/views/cta-popup.html @@ -7,7 +7,102 @@ - +
diff --git a/pkg/extension/src/views/toast.html b/pkg/extension/src/views/toast.html index 01177c5d7..d8664247c 100644 --- a/pkg/extension/src/views/toast.html +++ b/pkg/extension/src/views/toast.html @@ -30,6 +30,12 @@ padding-bottom: 10px; } + #omnivore-toast-container #omnivore-logged-out-row { + flex-direction: column; + align-items: center; + justify-content: center; + } + #omnivore-toast-container .omnivore-toast-func-row[data-state="open"] { display: flex; } @@ -77,13 +83,13 @@ #omnivore-toast-container .omnivore-save-button button { background-color: rgb(255, 210, 52) } - #omnivore-toast-container #omnivore-toast-close-button:hover { + #omnivore-toast-container #omnivore-toast-close-btn:hover { background-color: unset; } - #omnivore-toast-container #omnivore-toast-close-button svg { + #omnivore-toast-container #omnivore-toast-close-btn svg { fill: #3D3D3D; } - #omnivore-toast-container #omnivore-toast-close-button svg:hover { + #omnivore-toast-container #omnivore-toast-close-btn svg:hover { fill: #D9D9D9; stroke: white; } @@ -275,7 +281,7 @@ --> - +
+
+ + Login to Omnivore
\ No newline at end of file