diff --git a/README.md b/README.md
index 5674751..17d0e8c 100644
--- a/README.md
+++ b/README.md
@@ -108,9 +108,11 @@ overwriting newer views. Rename and delete affect only the saved view, never Git
sync service leaves ad-hoc Search usable. Set `STACKCHAIN_SAVED_SEARCH_DB` to override the default
`.stackchain-state/saved-searches.sqlite3` path.
-Confirmed **Watch issue** and **Watch pull request** actions on open Search results feed the mobile
-**Following** queue, including work already assigned to you or a teammate. This completes the
-Search → watch → revisit flow without changing ownership, review assignment, or scheduling work.
+Confirmed **Watch issue** and **Watch pull request** actions on open Search results and assigned My Work
+issue/pull-request details feed the mobile **Following** queue, including work already assigned to you or a teammate.
+The detail control loads authoritative Gitea state, remains single-flight while changing it, and refreshes Following only
+after the account-scoped collection confirms the same mutation. This completes the read → watch → revisit flow without
+changing ownership, review assignment, or scheduling work.
Following is a read-first, account-scoped collection: it is encrypted at rest, revisioned, bounded to
50 typed items, and synchronized across signed-in devices. Opening a row reuses the correct issue or
pull-request Search Preview; confirmed **Stop watching** removes an open item. When watched work
diff --git a/frontend/dashboard.css b/frontend/dashboard.css
index 7eacfbf..b408967 100644
--- a/frontend/dashboard.css
+++ b/frontend/dashboard.css
@@ -955,6 +955,8 @@ textarea { resize: vertical; min-height: 120px; }
.filed-claim-actions button { min-width:0; min-height:44px; }
.issue-sheet-actions { position:sticky; bottom:0; z-index:3; display:grid; gap:8px; margin-top:14px; padding:10px 4px; padding-bottom:calc(10px + env(safe-area-inset-bottom)); background:rgba(11,21,38,.98); border-top:1px solid #2a496e; }
.issue-sheet-actions button, .issue-sheet-actions a { min-height:44px; display:flex; align-items:center; justify-content:center; }
+.issue-sheet-actions #watch-issue-detail, .pull-sheet-actions #watch-pull-detail { min-width:0; min-height:44px; }
+.detail-watch-status:not(:empty) { margin-top:8px; overflow-wrap:anywhere; }
.issue-sheet-actions a { border:1px solid #60a5fa; border-radius:10px; font-weight:700; }
.issue-handoff, .pull-ownership { margin-top:14px; padding:12px; border:1px solid #2a496e; border-radius:12px; }
.issue-handoff > div, .pull-ownership > div { display:grid; gap:8px; margin-top:10px; }
diff --git a/frontend/dashboard.js b/frontend/dashboard.js
index 0d3ff56..e6df655 100644
--- a/frontend/dashboard.js
+++ b/frontend/dashboard.js
@@ -122,6 +122,18 @@
},
onReviewComplete:() => mobileStartDay.completePhase('following'),
});
+ function bindDetailWatch(kind, button, status) {
+ const feature=createDetailWatch({fetchJson:fetchReviewJson,refreshFollowing:()=>followingQueue.load(),onState:(state,watching,error)=>{
+ button.disabled=['loading','watching','unwatching'].includes(state);
+ button.textContent=watching?'Stop watching':'Watch '+(kind==='pull'?'pull request':'issue');
+ status.textContent=state==='ready'?(watching?'Watching · available in Following.':''):
+ searchPreviewWatchStatus({status:state==='error'?'watch-error':state,error,detail:{kind}});
+ }});
+ button.addEventListener('click',()=>feature.toggle().catch(()=>{}));
+ return feature;
+ }
+ const issueDetailWatch = bindDetailWatch('issue',qs('#watch-issue-detail'),qs('#issue-watch-status'));
+ const pullDetailWatch = bindDetailWatch('pull',qs('#watch-pull-detail'),qs('#pull-watch-status'));
const mobileDeliveryRecovery = createMobileDeliveryRecovery({
getItems: () => draftInbox.partition(lastDrafts).deliveries,
getIndex: item => lastDrafts.indexOf(item),
@@ -4275,6 +4287,15 @@
issueConversation = null;
issueTrigger = trigger;
qs('#issue-sheet').classList.add('open');
+ if (offlineDetail) {
+ qs('#watch-issue-detail').hidden = true;
+ qs('#issue-watch-status').textContent = 'Reconnect to change watch status.';
+ } else {
+ qs('#watch-issue-detail').hidden = item.state !== 'open';
+ void issueDetailWatch.open(item).catch(error => {
+ if (selectedIssue === item) qs('#issue-watch-status').textContent = error.message + ' Retry.';
+ });
+ }
qs('#issue-sheet-key').textContent = item.key || '';
qs('#issue-sheet-title').textContent = item.title || 'Assigned issue';
qs('#issue-sheet-status').textContent = 'Loading issue…';
@@ -4597,6 +4618,15 @@
pullConversation = null;
pullReviewState = null;
qs('#pull-sheet').classList.add('open');
+ if (offlineDetail) {
+ qs('#watch-pull-detail').hidden = true;
+ qs('#pull-watch-status').textContent = 'Reconnect to change watch status.';
+ } else {
+ qs('#watch-pull-detail').hidden = item.state !== 'open';
+ void pullDetailWatch.open(item).catch(error => {
+ if (selectedPull === item) qs('#pull-watch-status').textContent = error.message + ' Retry.';
+ });
+ }
qs('#pull-sheet-key').textContent = item.key || '';
qs('#pull-sheet-title').textContent = item.title || 'Assigned pull request';
qs('#pull-sheet-status').textContent = 'Loading pull request…';
diff --git a/frontend/index.html b/frontend/index.html
index d4da8d1..88f3bd6 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -1181,11 +1181,13 @@
+
Open in Gitea
Defer
+