Skip to content

Commit dca7bc1

Browse files
authored
Merge pull request #1 from knktc/feature-optimze-refresh-of-page
Feature optimze refresh of page
2 parents a7499a7 + 235abe6 commit dca7bc1

File tree

3 files changed

+146
-5
lines changed

3 files changed

+146
-5
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,6 @@ __debug_*
3131
# Editor/IDE
3232
# .idea/
3333
# .vscode/
34+
35+
# macos
36+
.DS_Store

main.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ func handler(w http.ResponseWriter, r *http.Request) {
9797
return
9898
}
9999

100+
// API endpoint to get requests as JSON for AJAX updates
101+
if r.URL.Path == "/api/requests" {
102+
apiRequestsHandler(w, r)
103+
return
104+
}
105+
100106
// If it's the root path, display the main panel.
101107
if r.URL.Path == "/" {
102108
mainPageHandler(w, r)
@@ -106,6 +112,26 @@ func handler(w http.ResponseWriter, r *http.Request) {
106112
captureRequestHandler(w, r)
107113
}
108114

115+
// apiRequestsHandler handles AJAX requests for getting the list of requests
116+
func apiRequestsHandler(w http.ResponseWriter, r *http.Request) {
117+
w.Header().Set("Content-Type", "application/json")
118+
119+
mutex.RLock()
120+
// Create a list of requests based on the ordered ID list.
121+
// To have the newest requests shown at the top, we traverse the ID list in reverse order.
122+
requests := make([]RequestInfo, len(requestIDs))
123+
for i := 0; i < len(requestIDs); i++ {
124+
id := requestIDs[len(requestIDs)-1-i]
125+
requests[i] = requestsStore[id]
126+
}
127+
mutex.RUnlock()
128+
129+
if err := json.NewEncoder(w).Encode(requests); err != nil {
130+
http.Error(w, "Failed to encode requests", http.StatusInternalServerError)
131+
return
132+
}
133+
}
134+
109135
// captureRequestHandler captures the details of the incoming request and stores it.
110136
func captureRequestHandler(w http.ResponseWriter, r *http.Request) {
111137
// Read the request body.

templates/main.tmpl

Lines changed: 117 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
'subtitle': 'Send requests to your-host/your-path',
1414
'auto_refresh': 'Auto Refresh',
1515
'seconds': 'seconds',
16+
'refresh': 'Refresh',
1617
'captured_requests': 'Captured Requests',
1718
'no_requests': 'No requests captured yet.',
1819
'welcome_title': 'Welcome!',
@@ -32,6 +33,7 @@
3233
'subtitle': '发送请求到 your-host/your-path',
3334
'auto_refresh': '自动刷新',
3435
'seconds': '秒',
36+
'refresh': '刷新',
3537
'captured_requests': '已捕获的请求',
3638
'no_requests': '尚未捕获任何请求。',
3739
'welcome_title': '欢迎!',
@@ -86,10 +88,8 @@
8688

8789
// 页面加载完成后初始化语言
8890
document.addEventListener('DOMContentLoaded', () => {
89-
initLanguage();
90-
91-
// 其他原有的DOMContentLoaded处理
92-
// ...existing code...
91+
// Note: initLanguage() will be called in the main DOMContentLoaded handler
92+
// to ensure proper coordination with other initialization code
9393
});
9494
</script>
9595
</head>
@@ -104,6 +104,12 @@
104104
</div>
105105
<div class="w-1/3 flex justify-end items-center pr-4">
106106
<div class="flex items-center space-x-2">
107+
<button id="manualRefreshButton" class="hidden px-3 py-1 text-sm bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors" data-i18n-title="refresh_requests">
108+
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
109+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
110+
</svg>
111+
<span data-i18n="refresh">刷新</span>
112+
</button>
107113
<input type="checkbox" id="autoRefreshCheckbox" class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500">
108114
<label for="autoRefreshCheckbox" class="text-sm text-gray-600" data-i18n="auto_refresh">自动刷新</label>
109115
<input type="number" id="refreshIntervalInput" class="w-16 text-sm border-gray-300 rounded-md shadow-sm" min="1" value="5">
@@ -223,9 +229,13 @@
223229
</div>
224230
<script>
225231
document.addEventListener('DOMContentLoaded', () => {
232+
// Initialize language first
233+
initLanguage();
234+
226235
// --- Auto-refresh logic ---
227236
const checkbox = document.getElementById('autoRefreshCheckbox');
228237
const intervalInput = document.getElementById('refreshIntervalInput');
238+
const manualRefreshButton = document.getElementById('manualRefreshButton');
229239
let refreshInterval;
230240

231241
function startRefresh() {
@@ -235,14 +245,83 @@
235245
interval = 5; // 如果输入无效,则重置为默认值
236246
intervalInput.value = '5';
237247
}
238-
refreshInterval = setInterval(() => window.location.reload(), interval * 1000);
248+
refreshInterval = setInterval(() => updateRequestList(), interval * 1000);
239249
}
240250

241251
function stopRefresh() {
242252
clearInterval(refreshInterval);
243253
refreshInterval = null;
244254
}
245255

256+
function toggleManualRefreshButton() {
257+
if (checkbox.checked) {
258+
manualRefreshButton.classList.add('hidden');
259+
} else {
260+
manualRefreshButton.classList.remove('hidden');
261+
}
262+
}
263+
264+
// Function to update only the request list via AJAX
265+
function updateRequestList() {
266+
return fetch('/api/requests')
267+
.then(response => response.json())
268+
.then(requests => {
269+
const requestList = document.querySelector('aside .overflow-y-auto');
270+
const currentViewId = new URLSearchParams(window.location.search).get('view_id');
271+
272+
if (requests && requests.length > 0) {
273+
const ul = document.createElement('ul');
274+
ul.className = 'divide-y';
275+
276+
requests.forEach(request => {
277+
const li = document.createElement('a');
278+
li.href = `/?view_id=${request.ID}`;
279+
li.className = `block p-4 hover:bg-gray-50 ${currentViewId == request.ID ? 'bg-indigo-50 border-l-4 border-indigo-500' : ''}`;
280+
281+
const methodColor = getMethodColor(request.Method);
282+
const timestamp = new Date(request.Timestamp).toLocaleString();
283+
284+
li.innerHTML = `
285+
<div class="flex justify-between items-center mb-1">
286+
<div class="font-bold truncate ${currentViewId == request.ID ? 'text-indigo-700' : 'text-gray-800'}">
287+
<span class="px-2 mr-2 text-xs leading-5 font-semibold rounded-full ${methodColor}">
288+
${request.Method}
289+
</span>
290+
<span class="font-mono">${request.Path}</span>
291+
</div>
292+
</div>
293+
<div class="text-xs text-gray-500">${timestamp}</div>
294+
`;
295+
296+
ul.appendChild(li);
297+
});
298+
299+
requestList.innerHTML = '';
300+
requestList.appendChild(ul);
301+
} else {
302+
requestList.innerHTML = `
303+
<div class="p-6 text-center text-gray-500">
304+
<p data-i18n="no_requests">${translations[currentLang]['no_requests']}</p>
305+
</div>
306+
`;
307+
}
308+
})
309+
.catch(error => {
310+
console.error('Error updating request list:', error);
311+
});
312+
}
313+
314+
// Helper function to get method color class
315+
function getMethodColor(method) {
316+
switch(method) {
317+
case 'GET': return 'bg-green-100 text-green-800';
318+
case 'POST': return 'bg-blue-100 text-blue-800';
319+
case 'PUT': return 'bg-yellow-100 text-yellow-800';
320+
case 'DELETE': return 'bg-red-100 text-red-800';
321+
default: return 'bg-gray-100 text-gray-800';
322+
}
323+
}
324+
246325
checkbox.addEventListener('change', () => {
247326
localStorage.setItem('autoRefreshInterval', intervalInput.value);
248327
if (checkbox.checked) {
@@ -252,6 +331,38 @@
252331
localStorage.setItem('autoRefreshEnabled', 'false');
253332
stopRefresh();
254333
}
334+
toggleManualRefreshButton();
335+
});
336+
337+
// Manual refresh button click handler
338+
let isRefreshing = false;
339+
const originalButtonContent = `
340+
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
341+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
342+
</svg>
343+
<span data-i18n="refresh">${translations[currentLang]['refresh']}</span>
344+
`;
345+
346+
function resetButtonState() {
347+
manualRefreshButton.innerHTML = originalButtonContent;
348+
isRefreshing = false;
349+
}
350+
351+
manualRefreshButton.addEventListener('click', () => {
352+
if (isRefreshing) return; // Prevent multiple clicks
353+
354+
isRefreshing = true;
355+
updateRequestList();
356+
357+
// Add visual feedback
358+
manualRefreshButton.innerHTML = `
359+
<svg class="w-4 h-4 inline mr-1 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
360+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
361+
</svg>
362+
<span data-i18n="refresh">${translations[currentLang]['refresh']}</span>
363+
`;
364+
365+
setTimeout(resetButtonState, 500);
255366
});
256367

257368
intervalInput.addEventListener('change', () => {
@@ -273,6 +384,7 @@
273384
if (isEnabled) {
274385
startRefresh();
275386
}
387+
toggleManualRefreshButton(); // Initialize manual refresh button visibility
276388

277389
// --- Collapsible sections logic ---
278390
const sections = ['headers', 'body'];

0 commit comments

Comments
 (0)