Skip to content

Commit 80206f8

Browse files
authored
Improve Popup and Options (#11)
* Add Loading Spinner to Popup * Add Copy Indicator and Disable Ability * Add openOptionsPage and update popLinks * Fix Disabled Uploads and Error Messages * Improve popLinks * Update popLinks * Improve processRemote Error Handling
1 parent d4a2123 commit 80206f8

File tree

6 files changed

+103
-53
lines changed

6 files changed

+103
-53
lines changed

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"manifest_version": 3,
3-
"version": "0.2.2",
3+
"version": "0.2.3",
44
"name": "Django Files",
55
"description": "Django Files Web Extension designed to work with django-files/django-files.",
66
"homepage_url": "https://github.com/django-files/web-extension",

src/html/options.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ <h1 class="align-middle">Django Files Extension</h1>
4747
<div class="mb-2">
4848
<label for="recentFiles" class="form-label">Recent Files</label>
4949
<input type="number" class="form-control" id="recentFiles" aria-describedby="recentFilesHelp" autocomplete="off">
50-
<div id="recentFilesHelp" class="form-text">Number of Recent Files in Popup.</div>
50+
<div id="recentFilesHelp" class="form-text">Number of Recent Files in Popup (0 to disable).</div>
5151
</div>
5252
<div class="form-check form-switch mb-2">
5353
<input class="form-check-input" type="checkbox" role="switch" id="contextMenu">

src/html/popup.html

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,17 @@ <h2>Django Files Extension</h2>
2222
<a role="button" class="btn btn-outline-success" data-href="" data-location="/gallery/">
2323
<i class="fa-regular fa-images me-2"></i> Gallery</a>
2424
</div>
25-
<a class="btn btn-outline-primary btn-sm my-1" role="button" data-href="../html/options.html">
25+
<a class="btn btn-outline-primary btn-sm my-1" role="button" data-href="options">
2626
<i class="fa-solid fa-gear me-2"></i> Open Options</a>
2727
</div>
28-
<p class="mb-0">Recent Uploads:</p>
28+
<p class="mb-0" id="recent-uploads">Recent Uploads:</p>
29+
30+
<div class="d-flex justify-content-center mt-2" id="loading-spinner">
31+
<span class="visually-hidden">Loading...</span>
32+
<div class="spinner-grow mx-1" role="status"></div>
33+
<div class="spinner-grow mx-1" role="status"></div>
34+
<div class="spinner-grow mx-1" role="status"></div>
35+
</div>
2936
<table id="recent" class="table table-striped table-sm mb-0 ">
3037
<caption class="visually-hidden">Recent Uploads</caption>
3138
<thead class="visually-hidden"><tr><th>Number</th><th>File URL</th></tr></thead>

src/js/exports.js

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,21 @@
66
*/
77
export function createContextMenus() {
88
console.log('createContextMenus')
9+
const ctx = ['link', 'image', 'video', 'audio']
910
const contexts = [
10-
[['link'], 'short', 'Create Short URL'],
11-
[['image'], 'upload-image', 'Upload Image'],
12-
[['video'], 'upload-video', 'Upload Video'],
13-
[['audio'], 'upload-audio', 'Upload Audio'],
14-
[['link', 'image', 'video', 'audio'], 'separator', 'separator-1'],
15-
[['link', 'image', 'video', 'audio'], 'options', 'Open Options'],
11+
[['link'], 'short', 'normal', 'Create Short URL'],
12+
[['image'], 'upload-image', 'normal', 'Upload Image'],
13+
[['video'], 'upload-video', 'normal', 'Upload Video'],
14+
[['audio'], 'upload-audio', 'normal', 'Upload Audio'],
15+
[ctx, 'separator-1', 'separator', 'separator'],
16+
[ctx, 'options', 'normal', 'Open Options'],
1617
]
17-
for (const context of contexts) {
18-
if (context[1].startsWith('separator')) {
19-
chrome.contextMenus.create({
20-
type: context[1],
21-
contexts: context[0],
22-
id: context[2],
23-
})
24-
} else {
25-
chrome.contextMenus.create({
26-
title: context[2],
27-
contexts: context[0],
28-
id: context[1],
29-
})
30-
}
31-
}
18+
contexts.forEach((context) => {
19+
chrome.contextMenus.create({
20+
contexts: context[0],
21+
id: context[1],
22+
type: context[2],
23+
title: context[3],
24+
})
25+
})
3226
}

src/js/popup.js

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
document.addEventListener('DOMContentLoaded', initPopup)
44

5-
document.querySelectorAll('[data-href]').forEach((el) => {
6-
el.addEventListener('click', popupLink)
7-
})
5+
const popupLinks = document.querySelectorAll('[data-href]')
6+
popupLinks.forEach((el) => el.addEventListener('click', popLinks))
87

98
/**
109
* Popup Init Function
@@ -19,6 +18,16 @@ async function initPopup() {
1918
return displayError('Missing URL or Token.')
2019
}
2120
document.getElementById('django-files-links').style.display = 'flex'
21+
console.log('options.recentFiles:', options.recentFiles)
22+
if (options.recentFiles === '0') {
23+
document
24+
.getElementById('loading-spinner')
25+
.classList.add('visually-hidden')
26+
document
27+
.getElementById('recent-uploads')
28+
.classList.add('visually-hidden')
29+
return console.log('Recent Files Disabled. Enable in Options.')
30+
}
2231

2332
let opts = {
2433
method: 'GET',
@@ -38,6 +47,8 @@ async function initPopup() {
3847
}
3948
console.log(`response.status: ${response.status}`, response, data)
4049

50+
document.getElementById('loading-spinner').classList.add('visually-hidden')
51+
4152
if (!response.ok) {
4253
console.warn('error: ' + data['error'])
4354
return displayError(data['error'])
@@ -52,32 +63,42 @@ async function initPopup() {
5263
updateTable(data)
5364

5465
const clipboard = new ClipboardJS('.clip') // eslint-disable-line
66+
// Re-Initialize data-href after updateTable
5567
document.querySelectorAll('[data-href]').forEach((el) => {
56-
el.addEventListener('click', popupLink)
68+
el.addEventListener('click', popLinks)
5769
})
5870
}
5971

6072
/**
61-
* Popup Links Callback
62-
* because firefox needs us to call window.close() from the popup
63-
* @function popupLink
73+
* Popup Links Click Callback
74+
* Firefox requires a call to window.close()
75+
* @function popLinks
6476
* @param {MouseEvent} event
6577
*/
66-
async function popupLink(event) {
67-
console.log('popupLink:', event)
68-
const { auth } = await chrome.storage.sync.get(['auth'])
69-
let url
78+
async function popLinks(event) {
79+
console.log('popLinks:', event)
80+
event.preventDefault()
7081
const anchor = event.target.closest('a')
82+
let url
7183
if (anchor?.dataset?.location) {
84+
const { auth } = await chrome.storage.sync.get(['auth'])
7285
url = auth?.url + anchor.dataset.location
73-
} else if (anchor?.dataset?.href.startsWith('http')) {
86+
} else if (anchor.dataset.href.startsWith('http')) {
7487
url = anchor.dataset.href
75-
} else {
88+
} else if (anchor.dataset.href === 'homepage') {
89+
url = chrome.runtime.getManifest().homepage_url
90+
} else if (anchor.dataset.href === 'options') {
91+
chrome.runtime.openOptionsPage()
92+
return window.close()
93+
} else if (anchor?.dataset?.href) {
7694
url = chrome.runtime.getURL(anchor.dataset.href)
7795
}
78-
console.log(`url: ${url}`)
96+
console.log('url:', url)
97+
if (!url) {
98+
return console.error('No dataset.href for anchor:', anchor)
99+
}
79100
await chrome.tabs.create({ active: true, url })
80-
window.close()
101+
return window.close()
81102
}
82103

83104
/**
@@ -117,18 +138,38 @@ function updateTable(data) {
117138
copyBtn.setAttribute('role', 'button')
118139
copyBtn.classList.add('clip')
119140
copyBtn.dataset.clipboardText = value
120-
copyBtn.innerHTML = '<i class="fa-regular fa-clipboard text-white"></i>'
141+
copyBtn.innerHTML = '<i class="fa-regular fa-clipboard"></i>'
142+
copyBtn.classList.add('link-body-emphasis')
143+
copyBtn.onclick = clipClick
121144
const cell3 = row.insertCell()
122145
cell3.appendChild(copyBtn)
123146
})
124147
}
125148

149+
/**
150+
* Clipboard Click Callback
151+
* @function clipClick
152+
* @param {MouseEvent} event
153+
*/
154+
function clipClick(event) {
155+
console.log('clipClick:', event)
156+
const element = event.target.closest('a')
157+
console.log('element:', element)
158+
element.classList.add('link-success')
159+
element.classList.remove('link-body-emphasis')
160+
setTimeout(() => {
161+
element.classList.add('link-body-emphasis')
162+
element.classList.remove('link-success')
163+
}, 500)
164+
}
165+
126166
/**
127167
* Display Popup Error Message
128168
* @function displayError
129169
* @param {String} message
130170
*/
131171
function displayError(message) {
172+
document.getElementById('loading-spinner').classList.add('visually-hidden')
132173
let div = document.getElementById('error-alert')
133174
div.innerHTML = message
134175
div.style.display = 'block'

src/js/service-worker.js

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ async function onInstalled(details) {
3333
createContextMenus()
3434
}
3535
if (details.reason === 'install') {
36-
const url = chrome.runtime.getURL('/html/options.html')
37-
await chrome.tabs.create({ active: true, url })
36+
chrome.runtime.openOptionsPage()
3837
} else if (options.showUpdate && details.reason === 'update') {
3938
const manifest = chrome.runtime.getManifest()
4039
if (manifest.version !== details.previousVersion) {
@@ -68,8 +67,7 @@ async function contextMenuClick(ctx) {
6867
await processRemote('shorten', ctx.linkUrl, 'Short Created')
6968
}
7069
} else if (ctx.menuItemId === 'options') {
71-
const url = chrome.runtime.getURL('/html/options.html')
72-
await chrome.tabs.create({ active: true, url })
70+
chrome.runtime.openOptionsPage()
7371
} else {
7472
console.warn('Action not handled.')
7573
}
@@ -102,17 +100,27 @@ async function processRemote(endpoint, url, message) {
102100
try {
103101
response = await postURL(endpoint, url)
104102
} catch (error) {
105-
await sendNotification('Fetch Error', 'Error: ' + error.message)
103+
console.log('error:', error)
104+
return await sendNotification('Fetch Error', 'Error: ' + error.message)
106105
}
107-
const data = await response.json()
108-
console.log(data)
106+
console.log('response:', response)
109107
if (response.ok) {
110-
console.log(data['url'])
111-
await clipboardWrite(data['url'])
112-
await sendNotification(message, data['url'])
108+
const data = await response.json()
109+
console.log('data:', data)
110+
await clipboardWrite(data.url)
111+
await sendNotification(message, data.url)
113112
} else {
114-
console.log(data['error'])
115-
await sendNotification('Processing Error', 'Error: ' + data['error'])
113+
try {
114+
const data = await response.json()
115+
console.log('data:', data)
116+
await sendNotification('Processing Error', `Error: ${data.error}`)
117+
} catch (error) {
118+
console.log('error:', error)
119+
await sendNotification(
120+
'Processing Error',
121+
`Error: Response Status: ${response.status}`
122+
)
123+
}
116124
}
117125
}
118126

0 commit comments

Comments
 (0)