Monorepo for Tangled tangled.org

appview/pages: PR mark-as-reviewed btn #1215

merged opened by oyster.cafe targeting master from lt/ap-mark-reviewed-btn
Labels

None yet.

assignee

None yet.

Participants 2
AT URI
at://did:plc:3fwecdnvtcscjnrx2p4n7alz/sh.tangled.repo.pull/3mhpzlkussu22
+34 -42
Interdiff #0 โ†’ #1
+33 -36
appview/pages/templates/repo/fragments/diff.html
··· 179 179 {{ end }} 180 180 </div> 181 181 </div> 182 - <button 183 - type="button" 182 + <label 184 183 data-review-btn="file-{{ .Id }}" 185 - onclick="event.preventDefault(); event.stopPropagation(); window.__reviewToggle && window.__reviewToggle('file-{{ .Id }}')" 184 + onclick="event.stopPropagation()" 186 185 class="review-btn hidden p-2 items-center gap-1 text-xs text-gray-400 dark:text-gray-500 hover:text-green-600 dark:hover:text-green-400 transition-colors cursor-pointer" 187 186 title="Mark as reviewed" 188 187 > 189 - <span class="review-icon-unchecked">{{ i "circle" "size-4" }}</span> 190 - <span class="review-icon-checked hidden">{{ i "circle-check" "size-4" }}</span> 188 + <input 189 + type="checkbox" 190 + class="sr-only peer review-checkbox" 191 + data-file-id="file-{{ .Id }}" 192 + /> 193 + <span class="peer-checked:hidden">{{ i "circle" "size-4" }}</span> 194 + <span class="hidden peer-checked:inline text-green-600 dark:text-green-400">{{ i "circle-check" "size-4" }}</span> 191 195 <span class="hidden md:inline">reviewed</span> 192 - </button> 196 + </label> 193 197 </div> 194 198 </summary> 195 199 ··· 358 362 } catch { localStorage.removeItem(k); } 359 363 }); 360 364 }; 361 - pruneStale(); 365 + if (Math.random() < 0.1) pruneStale(); 362 366 363 367 const allFiles = () => 364 368 document.querySelectorAll('details[id^="file-"]'); ··· 368 372 if (!detail) return; 369 373 370 374 const btn = detail.querySelector('[data-review-btn]'); 375 + const checkbox = btn?.querySelector('input[type="checkbox"]'); 371 376 const path = CSS.escape(fileId.replace('file-', '')); 372 377 const treeLink = document.querySelector(`.filetree-link[data-path="${path}"]`); 373 378 374 - if (isReviewed) { 375 - detail.classList.add('opacity-60'); 376 - if (btn) { 377 - btn.classList.replace('text-gray-400', 'text-green-600'); 378 - btn.classList.replace('dark:text-gray-500', 'dark:text-green-400'); 379 - btn.querySelector('.review-icon-unchecked')?.classList.add('hidden'); 380 - btn.querySelector('.review-icon-checked')?.classList.remove('hidden'); 381 - } 382 - if (treeLink) { 383 - let indicator = treeLink.parentElement.querySelector('.review-indicator'); 384 - if (!indicator) { 385 - indicator = document.createElement('span'); 386 - indicator.className = 'review-indicator text-green-600 dark:text-green-400 flex-shrink-0'; 387 - indicator.innerHTML = '&#10003;'; 388 - treeLink.parentElement.appendChild(indicator); 389 - } 390 - } 391 - } else { 392 - detail.classList.remove('opacity-60'); 393 - if (btn) { 394 - btn.classList.replace('text-green-600', 'text-gray-400'); 395 - btn.classList.replace('dark:text-green-400', 'dark:text-gray-500'); 396 - btn.querySelector('.review-icon-unchecked')?.classList.remove('hidden'); 397 - btn.querySelector('.review-icon-checked')?.classList.add('hidden'); 398 - } 399 - if (treeLink) { 400 - const indicator = treeLink.parentElement.querySelector('.review-indicator'); 401 - if (indicator) indicator.remove(); 379 + detail.classList.toggle('opacity-60', isReviewed); 380 + 381 + if (checkbox) checkbox.checked = isReviewed; 382 + 383 + if (treeLink) { 384 + const existing = treeLink.parentElement.querySelector('.review-indicator'); 385 + if (isReviewed && !existing) { 386 + const indicator = document.createElement('span'); 387 + indicator.className = 'review-indicator text-green-600 dark:text-green-400 flex-shrink-0'; 388 + indicator.innerHTML = '&#10003;'; 389 + treeLink.parentElement.appendChild(indicator); 390 + } else if (!isReviewed && existing) { 391 + existing.remove(); 402 392 } 403 393 } 404 394 }; ··· 422 412 423 413 const reviewed = load(); 424 414 425 - window.__reviewToggle = (fileId) => { 415 + const toggleReview = (fileId) => { 426 416 const detail = document.getElementById(fileId); 427 417 if (!detail) return; 428 418 const isNowReviewed = !reviewed.has(fileId); ··· 437 427 updateProgress(reviewed); 438 428 }; 439 429 430 + document.getElementById('diff-area').addEventListener('change', (e) => { 431 + const checkbox = e.target.closest('.review-checkbox'); 432 + if (!checkbox) return; 433 + const fileId = checkbox.dataset.fileId; 434 + if (fileId) toggleReview(fileId); 435 + }); 436 + 440 437 document.querySelectorAll('.review-btn').forEach(btn => { 441 438 btn.classList.remove('hidden'); 442 439 btn.classList.add('flex');
appview/pages/templates/repo/pulls/pull.html

This file has not been changed.

+1 -6
types/diff.go
··· 84 84 func (d NiceDiff) FileTree() *filetree.FileTreeNode { 85 85 fs := make([]string, len(d.Diff)) 86 86 for i, s := range d.Diff { 87 - n := s.Names() 88 - if n.New == "" { 89 - fs[i] = n.Old 90 - } else { 91 - fs[i] = n.New 92 - } 87 + fs[i] = s.Id() 93 88 } 94 89 return filetree.FileTree(fs) 95 90 }

History

2 rounds 3 comments
sign up or login to add to the discussion
1 commit
expand
appview/pages: PR mark-as-reviewed btn
3/3 success
expand
expand 2 comments

types/diff.go:87-87 guarantees the filetree path matches the DOM id

pull request successfully merged
1 commit
expand
appview/pages: PR mark-as-reviewed btn
3/3 success
expand
expand 1 comment

appview/pages/templates/repo/fragments/diff.html:182 this could probably be hacked into an actual literal checkbox input, but philosophically it's not part of a form it's just a button that does mark a thing as reviewed, and it just so happens to also toggle back in our case ๐Ÿคท