Handle PDFs and logged out state

This commit is contained in:
Jackson Harper
2023-03-21 10:55:27 +08:00
parent 4524b66dc7
commit 99d47db9bb
4 changed files with 293 additions and 170 deletions

View File

@ -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',

View File

@ -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 = `<style>:host {all initial;}</style>`
}
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 = `<span style='padding-right: 10px'>${image}</span><span style='line-height: 20px'>${message}</span>`
} 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

View File

@ -7,7 +7,102 @@
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="/styles/cta-popup.css">
<style>
@font-face {
font-style: normal;
font-weight: 400;
font-family: Inter;
src:
local(""),
url("/fonts/inter-v3-latin-400.woff2") format("woff2");
}
@font-face {
font-style: normal;
font-weight: 600;
font-family: Inter;
src:
local(""),
url("/fonts/inter-v3-latin-600.woff2") format("woff2");
}
@font-face {
font-style: normal;
font-weight: 700;
font-family: Inter;
src:
local(""),
url("/fonts/inter-v3-latin-700.woff2") format("woff2");
}
html,
body {
width: 100%;
height: 100%;
}
body {
margin: 0;
}
.cta-container {
position: relative;
overflow: hidden;
box-sizing: border-box;
width: 100%;
height: 100%;
padding: 24px 12px;
background: linear-gradient(150deg, #fff 55%, #ffde8c 55%);
background-color: #fff;
color: #3d3d3d;
font-family: Inter, sans-serif;
text-align: center;
}
.cta-container__wrapper {
position: relative;
z-index: 1;
}
.cta-container__title {
padding: 0 24px 24px;
font-weight: 600;
font-size: 16px;
}
.cta-container__icon {
vertical-align: unset;
margin-bottom: -4px;
fill: none;
}
.cta-container__link {
display: flex;
align-items: center;
justify-content: center;
width: 184px;
height: 34px;
margin: 36px auto 14px;
border-radius: 40px;
background-color: #fff;
color: inherit;
font-weight: 700;
font-size: 12px;
text-decoration: none;
cursor: pointer;
}
.cta-container__text {
margin: 0;
font-size: 12px;
}
.cta-container__textlink {
color: inherit;
font-weight: 700;
}
</style>
</head>
<body>
<div class="cta-container">

View File

@ -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 @@
</button>
<span class="omnivore-toast-divider"></span>
-->
<button id="omnivore-toast-close-button">
<button id="omnivore-toast-close-btn">
<svg width="19" height="19" viewBox="0 0 19 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="9.50049" cy="9.52783" r="9" />
<path d="M12.554 11.874L12.554 11.874L10.2075 9.52783L12.554 7.18165L12.554 7.18164C12.6478 7.08785 12.7005 6.96063 12.7005 6.82798C12.7005 6.69533 12.6478 6.56812 12.554 6.47432C12.4602 6.38053 12.333 6.32783 12.2003 6.32783C12.0677 6.32783 11.9405 6.38053 11.8467 6.47432L11.8467 6.47433L9.50049 8.82087L7.15431 6.47433L7.1543 6.47432C7.0605 6.38053 6.93329 6.32783 6.80064 6.32783C6.66799 6.32783 6.54078 6.38053 6.44698 6.47432C6.35318 6.56812 6.30049 6.69533 6.30049 6.82798C6.30049 6.96063 6.35318 7.08785 6.44698 7.18164L6.44699 7.18165L8.79352 9.52783L6.44699 11.874L6.44698 11.874C6.35318 11.9678 6.30049 12.095 6.30049 12.2277C6.30049 12.3603 6.35318 12.4875 6.44698 12.5813C6.54078 12.6751 6.66799 12.7278 6.80064 12.7278C6.93329 12.7278 7.0605 12.6751 7.1543 12.5813L7.15431 12.5813L9.50049 10.2348L11.8467 12.5813L11.8467 12.5813C11.8931 12.6278 11.9483 12.6646 12.0089 12.6898C12.0696 12.7149 12.1347 12.7278 12.2003 12.7278C12.266 12.7278 12.3311 12.7149 12.3917 12.6898C12.4524 12.6646 12.5076 12.6278 12.554 12.5813C12.6004 12.5349 12.6373 12.4798 12.6624 12.4191C12.6876 12.3584 12.7005 12.2934 12.7005 12.2277C12.7005 12.162 12.6876 12.097 12.6624 12.0363C12.6373 11.9756 12.6004 11.9205 12.554 11.874Z" fill="#EBEBEB" stroke="#EBEBEB" stroke-width="0.4"/>
@ -322,6 +328,10 @@
</svg>
Delete
</button>
</div>
<div id="omnivore-logged-out-row" class="omnivore-toast-func-row" data-state="closed">
<span id="omnivore-logged-out-status" class="omnivore-toast-func-status"></span>
<a href="" id="omnivore-toast-login-btn">Login to Omnivore</a>
</div>
</div>