Fixed mastodon checkbox on new entry page

This commit is contained in:
2026-08-27 22:08:30 +02:00
parent 1c2d1b71ff
commit d433a305f5
10 changed files with 65 additions and 6 deletions
+2
View File
@@ -9,11 +9,13 @@
* Ability to minimize settings panels to only show headers in the Admin and Profile interfaces for easy navigation * Ability to minimize settings panels to only show headers in the Admin and Profile interfaces for easy navigation
* Styled settings panel headers with distinct theme accent colors for visual distinction * Styled settings panel headers with distinct theme accent colors for visual distinction
* Settings panels are collapsed by default when opening the Admin and Profile pages * Settings panels are collapsed by default when opening the Admin and Profile pages
* Show checked-by-default Mastodon publishing on New Entry only for configured users
### Fixed ### Fixed
* Fixed element ID conflict on the Admin page so Available Themes load correctly * Fixed element ID conflict on the Admin page so Available Themes load correctly
* Positioned new-entry tag checkboxes after their label text * Positioned new-entry tag checkboxes after their label text
* Kept new-entry tag checkboxes immediately left of their labels at all viewport sizes * Kept new-entry tag checkboxes immediately left of their labels at all viewport sizes
* Prevented the new-entry form grid from placing tag checkboxes above their labels * Prevented the new-entry form grid from placing tag checkboxes above their labels
* Kept unconfigured New Entry Mastodon publishing controls hidden
## Version v0.1.1 ## Version v0.1.1
### Features ### Features
+12
View File
@@ -1358,3 +1358,15 @@ The checkbox takes the full width of the grid which forces the label to be print
### Assistant outcome ### Assistant outcome
Fixed the actual layout conflict: `.form-section label` uses a grid layout whose selector overrode the tag label's flex layout. Added the more specific `.form-section label.tag-checkbox` inline-flex rule and reset its checkbox margin, keeping each checkbox directly left of its tag text. The focused API test module passed with 35 tests and `git diff --check` passed. Fixed the actual layout conflict: `.form-section label` uses a grid layout whose selector overrode the tag label's flex layout. Added the more specific `.form-section label.tag-checkbox` inline-flex rule and reset its checkbox margin, keeping each checkbox directly left of its tag text. The focused API test module passed with 35 tests and `git diff --check` passed.
### User
Only show the Mastodon publishing on new page if the user has Mastodon posting configured. Then default the checkbox to yes
### Assistant outcome
Added a safe `configured` status to the authenticated Mastodon configuration response. New Entry now hides its Mastodon publishing section unless the current user has both an instance and access token configured; when shown, its checkbox defaults to checked. Link creation accepts `post_to_mastodon`, allowing an unchecked control to skip Mastodon while preserving dispatch of other plugins. Added posting and configuration regression coverage.
### User
In the currently running contaier the user olaf has no mastodon configured but the mastodon checkbox is still visible
### Assistant outcome
Found that `.form-section { display: grid; }` appeared later in the stylesheet than `.hidden { display: none; }`, overriding the initial hidden state of the Mastodon fieldset. Added `.form-section.hidden { display: none; }`, so the publishing control stays hidden until the authenticated configuration check explicitly reveals it.
+2
View File
@@ -242,6 +242,8 @@
223. On the new-entry page the checkbox should be following the labels - now they are positioned above. 223. On the new-entry page the checkbox should be following the labels - now they are positioned above.
224. The checkboxes should be immediately to the left of the label not below left 224. The checkboxes should be immediately to the left of the label not below left
225. The checkbox takes the full width of the grid which forces the label to be printed underneath instead of directly next to it. 225. The checkbox takes the full width of the grid which forces the label to be printed underneath instead of directly next to it.
226. Only show the Mastodon publishing on new page if the user has Mastodon posting configured. Then default the checkbox to yes
227. In the currently running contaier the user olaf has no mastodon configured but the mastodon checkbox is still visible
## Future entries ## Future entries
+6 -1
View File
@@ -24,6 +24,7 @@ class LinkCreate(BaseModel):
comment: str = '' comment: str = ''
timestamp: str | None = None timestamp: str | None = None
tags: list[str] = [] tags: list[str] = []
post_to_mastodon: bool = True
class LinkUpdate(BaseModel): class LinkUpdate(BaseModel):
@@ -89,7 +90,11 @@ def create_link_endpoint(payload: LinkCreate, response: Response, authorization:
raise HTTPException(status_code=422, detail=str(error)) from error raise HTTPException(status_code=422, detail=str(error)) from error
if duplicate: if duplicate:
response.status_code = status.HTTP_200_OK response.status_code = status.HTTP_200_OK
plugin_results = plugin_manager.dispatch({'type': 'link_created', **record}) plugin_results = plugin_manager.dispatch({
'type': 'link_created',
'post_to_mastodon': payload.post_to_mastodon,
**record,
})
mastodon_result = next((result for result in plugin_results if result.get('plugin') == 'mastodon'), None) mastodon_result = next((result for result in plugin_results if result.get('plugin') == 'mastodon'), None)
if mastodon_result and mastodon_result.get('status') == 'posted': if mastodon_result and mastodon_result.get('status') == 'posted':
mark_mastodon_posted(record['id'], info['user_id'], mastodon_result.get('post_id')) mark_mastodon_posted(record['id'], info['user_id'], mastodon_result.get('post_id'))
+2
View File
@@ -390,6 +390,8 @@ def get_user_plugin_config(plugin_name: str, user: dict = Depends(get_current_us
return {} return {}
config = json.loads(row['config']) if row['config'] else {} config = json.loads(row['config']) if row['config'] else {}
if plugin_name == 'mastodon':
config['configured'] = bool(config.get('instance') and config.get('access_token'))
if config.get('access_token'): if config.get('access_token'):
config.pop('access_token') config.pop('access_token')
return config return config
+3
View File
@@ -34,6 +34,9 @@ class MastodonPlugin(BasePlugin):
return True return True
def handle_event(self, event): def handle_event(self, event):
if not event.get('post_to_mastodon', True):
return {'status': 'skipped', 'plugin': self.name, 'reason': 'not_requested'}
config = dict(self.config) config = dict(self.config)
user_id = event.get('user_id') user_id = event.get('user_id')
if user_id: if user_id:
+13
View File
@@ -889,6 +889,18 @@ def test_link_submission_posts_to_enabled_mastodon_plugin():
server.server_close() server.server_close()
def test_link_submission_can_skip_mastodon_posting():
with patch('backend.app.api.links.plugin_manager.dispatch', return_value=[]) as dispatch:
response = client.post('/api/links', headers=login_headers(), json={
'title': 'Private share',
'url': 'https://example.com/private-share',
'post_to_mastodon': False,
})
assert response.status_code == 201
assert dispatch.call_args.args[0]['post_to_mastodon'] is False
def test_mastodon_post_without_title_omits_source_line(): def test_mastodon_post_without_title_omits_source_line():
from backend.app.services.plugin_manager import MastodonPlugin from backend.app.services.plugin_manager import MastodonPlugin
@@ -922,6 +934,7 @@ def test_plugin_config_can_be_saved_for_mastodon():
payload = client.get('/api/user/plugins/mastodon', headers=headers).json() payload = client.get('/api/user/plugins/mastodon', headers=headers).json()
assert payload['instance'] == 'mastodon.social' assert payload['instance'] == 'mastodon.social'
assert payload['post_prefix'] == 'From my #LinkLog: ' assert payload['post_prefix'] == 'From my #LinkLog: '
assert payload['configured'] is True
admin_update = client.put('/api/admin/plugins/mastodon', headers=headers, json={ admin_update = client.put('/api/admin/plugins/mastodon', headers=headers, json={
'enabled': True, 'enabled': True,
+17 -1
View File
@@ -9,6 +9,7 @@
const commentInput = document.getElementById('comment-input'); const commentInput = document.getElementById('comment-input');
const newTagsInput = document.getElementById('new-tags-input'); const newTagsInput = document.getElementById('new-tags-input');
const existingTagsEl = document.getElementById('existing-tags'); const existingTagsEl = document.getElementById('existing-tags');
const mastodonPublishing = document.getElementById('mastodon-publishing');
const mastodonEnabledCheckbox = document.getElementById('mastodon-enabled'); const mastodonEnabledCheckbox = document.getElementById('mastodon-enabled');
const scrapeButton = document.getElementById('scrape-button'); const scrapeButton = document.getElementById('scrape-button');
const scrapeStatus = document.getElementById('scrape-status'); const scrapeStatus = document.getElementById('scrape-status');
@@ -46,7 +47,7 @@
.then((user) => { .then((user) => {
currentUser = user; currentUser = user;
entryForm.classList.remove('hidden'); entryForm.classList.remove('hidden');
loadTags(); Promise.all([loadTags(), loadMastodonPublishing()]);
}) })
.catch(() => { .catch(() => {
localStorage.removeItem('linklogAccessToken'); localStorage.removeItem('linklogAccessToken');
@@ -66,6 +67,20 @@
} }
} }
async function loadMastodonPublishing() {
try {
const response = await fetch('/api/user/plugins/mastodon', {
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) return;
const config = await response.json();
mastodonEnabledCheckbox.checked = Boolean(config.configured);
mastodonPublishing.classList.toggle('hidden', !config.configured);
} catch (error) {
console.error('Could not load Mastodon configuration:', error);
}
}
// Render tag checkboxes // Render tag checkboxes
function renderTags() { function renderTags() {
existingTagsEl.innerHTML = ''; existingTagsEl.innerHTML = '';
@@ -171,6 +186,7 @@
url, url,
comment, comment,
tags, tags,
post_to_mastodon: mastodonEnabledCheckbox.checked,
}), }),
}); });
+4
View File
@@ -1006,6 +1006,10 @@ button:disabled {
gap: 8px; gap: 8px;
} }
.form-section.hidden {
display: none;
}
.form-section label { .form-section label {
display: grid; display: grid;
gap: 6px; gap: 6px;
+4 -4
View File
@@ -80,11 +80,11 @@
</label> </label>
</fieldset> </fieldset>
<fieldset class="form-section"> <fieldset id="mastodon-publishing" class="form-section hidden">
<legend>Mastodon Publishing</legend> <legend>Mastodon Publishing</legend>
<label> <label>
<input id="mastodon-enabled" type="checkbox" /> <input id="mastodon-enabled" type="checkbox" checked />
Post to Mastodon (if configured) Post to Mastodon
</label> </label>
</fieldset> </fieldset>
@@ -102,6 +102,6 @@
</footer> </footer>
<script src="/static/auth-header.js"></script> <script src="/static/auth-header.js"></script>
<script src="/static/new-entry.js"></script> <script src="/static/new-entry.js?v=2"></script>
</body> </body>
</html> </html>