-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrecipe-edit-button.user.js
More file actions
153 lines (126 loc) · 5.43 KB
/
recipe-edit-button.user.js
File metadata and controls
153 lines (126 loc) · 5.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// ==UserScript==
// @name TRMNL Recipe Edit Button
// @namespace https://github.com/ExcuseMi/trmnl-userscripts
// @version 1.0.2
// @description Add an Edit button on recipe pages you own
// @author ExcuseMi
// @match https://trmnl.com/recipes/*
// @icon https://raw.githubusercontent.com/ExcuseMi/trmnl-userscripts/refs/heads/main/images/trmnl.svg
// @downloadURL https://raw.githubusercontent.com/ExcuseMi/trmnl-userscripts/main/recipe-edit-button.user.js
// @updateURL https://raw.githubusercontent.com/ExcuseMi/trmnl-userscripts/main/recipe-edit-button.user.js
// @grant none
// ==/UserScript==
(function() {
'use strict';
const LOG_PREFIX = '[TRMNL Recipe Edit]';
const log = (...args) => console.log(LOG_PREFIX, ...args);
const warn = (...args) => console.warn(LOG_PREFIX, ...args);
const EDIT_BTN_ID = 'trmnl-edit-btn';
function getRecipeId() {
const match = location.pathname.match(/^\/recipes\/(\d+)$/);
return match ? match[1] : null;
}
function isTargetPage() {
return getRecipeId() !== null;
}
function onNavigate() {
if (!isTargetPage()) return;
log('On recipe page. URL:', location.href);
waitForContent();
}
log('Script loaded. readyState:', document.readyState);
document.addEventListener('turbo:load', () => {
log('turbo:load fired.');
onNavigate();
});
document.addEventListener('turbo:frame-load', () => {
log('turbo:frame-load fired.');
if (!isTargetPage()) return;
trySetup();
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
log('DOMContentLoaded fired.');
onNavigate();
});
} else {
onNavigate();
}
function waitForContent() {
if (document.getElementById(EDIT_BTN_ID)) {
log('Edit button already present, skipping.');
return;
}
if (trySetup()) return;
const observeTarget = document.querySelector('.flex.justify-end.items-end.shrink-0') || document.documentElement;
log('Content not ready, observing:', observeTarget.tagName, observeTarget.className.slice(0, 60));
const observer = new MutationObserver(() => {
if (trySetup()) {
observer.disconnect();
}
});
observer.observe(observeTarget, { childList: true, subtree: true });
}
function trySetup() {
if (document.getElementById(EDIT_BTN_ID)) return true;
const recipeId = getRecipeId();
if (!recipeId) return true;
const buttonContainer = document.querySelector('.flex.justify-end.items-end.shrink-0');
if (!buttonContainer) {
log('Button container not found yet (.flex.justify-end.items-end.shrink-0).');
return false;
}
const loggedInUserId = getIntercomUserId();
if (!loggedInUserId) {
log('Intercom user ID not available yet.');
return false;
}
// DOM and user ID ready — fetch recipe ownership async
checkOwnerAndAddButton(recipeId, loggedInUserId, buttonContainer);
return true; // stop observing, async part handles the rest
}
async function checkOwnerAndAddButton(recipeId, loggedInUserId, buttonContainer) {
log(`Fetching recipe ${recipeId} JSON to check ownership...`);
let recipeData;
try {
const resp = await fetch(`https://trmnl.com/recipes/${recipeId}.json`);
if (!resp.ok) {
warn(`Fetch failed: ${resp.status} ${resp.statusText}`);
return;
}
recipeData = await resp.json();
} catch (err) {
warn('Error fetching recipe JSON:', err);
return;
}
const recipeUserId = recipeData?.data?.user_id;
log(`Recipe owner user_id: ${recipeUserId}, logged-in user_id: ${loggedInUserId}`);
if (String(recipeUserId) !== String(loggedInUserId)) {
log('Not the recipe owner, no Edit button added.');
return;
}
// Guard: another invocation may have added the button during the fetch
if (document.getElementById(EDIT_BTN_ID)) {
log('Edit button already present after fetch, skipping.');
return;
}
const editLink = document.createElement('a');
editLink.id = EDIT_BTN_ID;
editLink.href = `/plugin_settings/${recipeId}/edit`;
editLink.className = 'cursor-pointer font-medium rounded-lg text-sm px-3 py-2 inline-flex items-center transition duration-150 justify-center shrink-0 gap-1.5 whitespace-nowrap text-white bg-primary-500 dark:bg-primary-600 hover:bg-primary-600 dark:hover:bg-primary-500 focus:outline-none ml-3';
editLink.textContent = 'Edit';
buttonContainer.prepend(editLink);
log(`Edit button added → /plugin_settings/${recipeId}/edit`);
}
function getIntercomUserId() {
// Prefer the already-parsed window object (fastest)
if (window.intercomSettings?.user_id) {
return String(window.intercomSettings.user_id);
}
// Fall back to parsing the raw script tag
const script = document.getElementById('IntercomSettingsScriptTag');
if (!script) return null;
const match = script.innerHTML.match(/"user_id":\s*(\d+)/);
return match ? match[1] : null;
}
})();