From 6de91eeab1bdf4077fae6e3aefcfd0c48cac185c Mon Sep 17 00:00:00 2001 From: Tom Casavant Date: Wed, 28 Aug 2024 11:26:49 -0400 Subject: [PATCH 1/4] Removed oauth flow for mobile support, kept in web --- background.js | 24 +++++++++++++----------- manifest.json | 4 ++-- options.js | 25 +++++++++++++++++++++---- settings.html | 14 ++++++++++++-- 4 files changed, 48 insertions(+), 19 deletions(-) diff --git a/background.js b/background.js index 4d5eeaf..61ccc06 100644 --- a/background.js +++ b/background.js @@ -1,37 +1,39 @@ -// Message listener for various actions browser.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.action === 'searchMastodon') { // Load saved settings from browser.storage.local - browser.storage.local.get(['client_id', 'client_secret', 'access_token', 'domain', 'numPosts']) - .then(({ client_id, client_secret, access_token, domain, numPosts = 5 }) => { - console.log(access_token); - console.log(domain); - if (!access_token || !domain) { - sendResponse({ success: false, error: 'Missing access token or domain.' }); + browser.storage.local.get(['client_id', 'client_secret', 'access_token', 'apiKey', 'domain', 'numPosts']) + .then(({ client_id, client_secret, access_token, apiKey, domain, numPosts = 5 }) => { + if ((!access_token && !apiKey) || !domain) { + sendResponse({ success: false, error: 'Missing access token, API key, or domain.' }); return; } const searchTerm = message.searchTerm; + const authorization = access_token ? `Bearer ${access_token}` : `Bearer ${apiKey}`; + + console.log(authorization); + fetch(`https://${domain}/api/v2/search?q=${encodeURIComponent(searchTerm)}&resolve=true&limit=${numPosts}`, { headers: { - 'Authorization': `Bearer ${access_token}` + 'Authorization': authorization } }) .then(response => response.json()) .then(data => { - console.log(data.statuses); //TODO: For some reason this sendResponse does not work unless I log data.statuses. I have no idea why. Best guess is there's some sort of race condition going on? Though I'm not familiar enough with javascript to know if that's true + console.log(data.statuses); sendResponse({ success: true, results: data.statuses }); }) .catch(error => { sendResponse({ success: false, error: error.message }); }); - return true; + return true; }) .catch(error => { sendResponse({ success: false, error: 'Failed to retrieve saved settings.' }); }); - return true; + + return true; } else if (message.action === 'getSettings') { browser.storage.local.get(['domain', 'numPosts']) .then(({ domain, numPosts = 5 }) => { diff --git a/manifest.json b/manifest.json index 0d94028..95b87ee 100644 --- a/manifest.json +++ b/manifest.json @@ -1,8 +1,8 @@ { "manifest_version": 2, "name": "DuckDuckSocial", - "version": "0.0.3", - "description": "Adds a scrollable list of posts from the social web that match your DuckDuckGo search. Requires API key from mastodon compatible server", + "version": "0.0.4", + "description": "Adds a scrollable list of posts from the social web that match your DuckDuckGo search. Mobile support requires API key", "icons": { "512": "icons/border-512.png" }, diff --git a/options.js b/options.js index 747ebaa..050f2d6 100644 --- a/options.js +++ b/options.js @@ -2,7 +2,10 @@ document.addEventListener('DOMContentLoaded', function() { const form = document.getElementById('settingsForm'); const domainInput = document.getElementById('domain'); const numPostsInput = document.getElementById('numPosts'); + const apiKeyInput = document.getElementById('apiKey'); const messageElement = document.querySelector('.message'); + const toggleButton = document.getElementById('toggleAdvancedSettings'); + const advancedSettings = document.querySelector('.advanced-settings'); // Load saved settings loadSettings(); @@ -11,6 +14,12 @@ document.addEventListener('DOMContentLoaded', function() { event.preventDefault(); saveSettings(); }); + + toggleButton.addEventListener('click', function() { + const isVisible = advancedSettings.style.display === 'block'; + advancedSettings.style.display = isVisible ? 'none' : 'block'; + toggleButton.textContent = isVisible ? 'Show Advanced Settings' : 'Hide Advanced Settings'; + }); document.getElementById('connectMastodon').addEventListener('click', function(event) { const domain = domainInput.value; @@ -38,10 +47,12 @@ document.addEventListener('DOMContentLoaded', function() { function saveSettings() { const domain = domainInput.value; const numPosts = numPostsInput.value; + const apiKey = apiKeyInput.value; browser.storage.local.set({ domain: domain, - numPosts: numPosts + numPosts: numPosts, + apiKey: apiKey // Save the API key as well }).then(() => { updateMessage('Settings saved successfully!'); }).catch(error => { @@ -49,12 +60,16 @@ document.addEventListener('DOMContentLoaded', function() { updateMessage('Failed to save settings.'); }); } - + function loadSettings() { - browser.storage.local.get(['domain', 'numPosts']) - .then(({ domain, numPosts = 5 }) => { + browser.storage.local.get(['domain', 'numPosts', 'apiKey']) + .then(({ domain, numPosts = 5, apiKey }) => { if (domain) domainInput.value = domain; if (numPosts) numPostsInput.value = numPosts; + if (apiKey) { + apiKeyInput.value = apiKey; + advancedSettingsDiv.style.display = 'block'; + } }) .catch(error => { console.error('Failed to load settings:', error); @@ -66,3 +81,5 @@ document.addEventListener('DOMContentLoaded', function() { messageElement.style.color = 'red'; } }); + + diff --git a/settings.html b/settings.html index 085483f..52d3409 100644 --- a/settings.html +++ b/settings.html @@ -17,6 +17,9 @@ box-sizing: border-box; margin-bottom: 20px; } + .advanced-settings { + display: none; + } @@ -27,11 +30,18 @@

DuckDuckSocial

- + + +
+ + +
+ + -

+

From db50fc734f460d319b35d4c21232632aa645064d Mon Sep 17 00:00:00 2001 From: Tom Casavant Date: Wed, 28 Aug 2024 11:27:56 -0400 Subject: [PATCH 2/4] added commetn back --- background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/background.js b/background.js index 61ccc06..3f712d4 100644 --- a/background.js +++ b/background.js @@ -20,7 +20,7 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => { }) .then(response => response.json()) .then(data => { - console.log(data.statuses); + console.log(data.statuses); //TODO: For some reason this sendResponse does not work unless I log data.statuses. I have no idea why. Best guess is there's some sort of race condition going on? Though I'm not familiar enough with javascript to know if that's true sendResponse({ success: true, results: data.statuses }); }) .catch(error => { From 6a1eb849ce86d89068478f1bbe8f225989e3b080 Mon Sep 17 00:00:00 2001 From: Tom Casavant Date: Fri, 30 Aug 2024 16:15:00 -0400 Subject: [PATCH 3/4] v0.0.5, added media support to fix #11 --- content.js | 28 +++++++++++++++++++++++++++- manifest.json | 2 +- styles.css | 48 ++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/content.js b/content.js index 3b35232..7463e85 100644 --- a/content.js +++ b/content.js @@ -38,7 +38,7 @@ if (searchTerm) { // Datetime var datetime = document.createElement('p'); datetime.textContent = new Date(post.created_at).toLocaleString(); - datetime.className = 'duckducksocial-datetime'; + datetime.className = 'duckducksocial-datetime'; var postHref = document.createElement('a'); postHref.href = `https://${mastodonDomain}/@${post.account.acct}/${post.id}`; @@ -46,6 +46,32 @@ if (searchTerm) { postDiv.appendChild(username); postDiv.appendChild(content); + if (post.media_attachments && post.media_attachments.length > 0) { + var mediaContainer = document.createElement('div'); + mediaContainer.className = 'duckducksocial-media-container'; + + post.media_attachments.forEach(function (media) { + var mediaElement; + + if (media.type === 'image') { + mediaElement = document.createElement('img'); + mediaElement.src = media.url; + mediaElement.alt = media.description || 'Photo from mastodon search results. No alt text available'; + mediaElement.className = 'duckducksocial-media-image'; + } else if (media.type === 'video' || media.type === 'gifv') { + mediaElement = document.createElement('video'); + mediaElement.src = media.url; + mediaElement.controls = true; + mediaElement.className = 'duckducksocial-media-video'; + } + + if (mediaElement) { + mediaContainer.appendChild(mediaElement); + } + }); + + postDiv.appendChild(mediaContainer); + } postDiv.appendChild(datetime); div.appendChild(postHref); diff --git a/manifest.json b/manifest.json index 95b87ee..d4c5245 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "DuckDuckSocial", - "version": "0.0.4", + "version": "0.0.5", "description": "Adds a scrollable list of posts from the social web that match your DuckDuckGo search. Mobile support requires API key", "icons": { "512": "icons/border-512.png" diff --git a/styles.css b/styles.css index f3e677d..bc3ef8d 100644 --- a/styles.css +++ b/styles.css @@ -36,12 +36,14 @@ border: 1px solid var(--post-border-light); border-radius: 8px; box-shadow: 0 2px 8px var(--shadow-light); - height: 15em; - width: 12.5em; + height: 15em; /* Fixed height */ + width: 12.5em; /* Fixed width */ background-color: var(--post-background-light); transition: transform 0.3s ease, box-shadow 0.3s ease; display: flex; flex-direction: column; + justify-content: space-between; /* Distribute elements evenly */ + padding: 5px; /* Add padding around elements */ overflow: hidden; } @@ -52,30 +54,56 @@ .duckducksocial-username { font-weight: bold; - margin: 3px; + margin: 0; color: var(--text-color-light); font-size: 11px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .duckducksocial-content { - margin: 0 10px 30px; - font-size: 14px; + font-size: 12px; /* Smaller font size */ color: var(--content-text-color-light); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-box-orient: vertical; - -webkit-line-clamp: 7; + -webkit-line-clamp: 3; /* Limit to 3 lines */ + margin: 3px 0; /* Margin to separate from other elements */ + flex-grow: 1; /* Allow it to take available space */ } .duckducksocial-datetime { - position: absolute; - bottom: 10px; - right: 10px; - font-size: 12px; + font-size: 10px; /* Smaller font size */ color: var(--datetime-text-color-light); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin: 0; + align-self: flex-end; /* Align to the bottom */ } +.duckducksocial-media-container { + width: 100%; + max-height: 6em; /* Limit the height of media */ + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; + flex-shrink: 0; /* Prevent shrinking */ + margin: 5px 0; /* Margin to separate from other elements */ +} + +.duckducksocial-media-image, +.duckducksocial-media-video { + max-width: 100%; + max-height: 100%; + object-fit: cover; /* Maintain aspect ratio and cover the container */ + display: block; +} + + /* Dark mode styles */ @media (prefers-color-scheme: dark) { From b09f8d1d4bd357d1e0cb4ec262b46d47782ccb60 Mon Sep 17 00:00:00 2001 From: Tom Casavant Date: Fri, 30 Aug 2024 16:19:47 -0400 Subject: [PATCH 4/4] Cleaned up styles file --- styles.css | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/styles.css b/styles.css index bc3ef8d..6c82b0e 100644 --- a/styles.css +++ b/styles.css @@ -1,4 +1,3 @@ -/* Define CSS variables for light and dark modes */ :root { --border-color-light: #ccc; --border-color-dark: #444; @@ -36,14 +35,14 @@ border: 1px solid var(--post-border-light); border-radius: 8px; box-shadow: 0 2px 8px var(--shadow-light); - height: 15em; /* Fixed height */ - width: 12.5em; /* Fixed width */ + height: 15em; + width: 12.5em; background-color: var(--post-background-light); transition: transform 0.3s ease, box-shadow 0.3s ease; display: flex; flex-direction: column; - justify-content: space-between; /* Distribute elements evenly */ - padding: 5px; /* Add padding around elements */ + justify-content: space-between; + padding: 5px; overflow: hidden; } @@ -63,43 +62,43 @@ } .duckducksocial-content { - font-size: 12px; /* Smaller font size */ + font-size: 12px; color: var(--content-text-color-light); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-box-orient: vertical; - -webkit-line-clamp: 3; /* Limit to 3 lines */ - margin: 3px 0; /* Margin to separate from other elements */ - flex-grow: 1; /* Allow it to take available space */ + -webkit-line-clamp: 3; + margin: 3px 0; + flex-grow: 1; } .duckducksocial-datetime { - font-size: 10px; /* Smaller font size */ + font-size: 10px; color: var(--datetime-text-color-light); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 0; - align-self: flex-end; /* Align to the bottom */ + align-self: flex-end; } .duckducksocial-media-container { width: 100%; - max-height: 6em; /* Limit the height of media */ + max-height: 6em; overflow: hidden; display: flex; justify-content: center; align-items: center; - flex-shrink: 0; /* Prevent shrinking */ - margin: 5px 0; /* Margin to separate from other elements */ + flex-shrink: 0; + margin: 5px 0; } .duckducksocial-media-image, .duckducksocial-media-video { max-width: 100%; max-height: 100%; - object-fit: cover; /* Maintain aspect ratio and cover the container */ + object-fit: cover; display: block; }