diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 2f1c328..128df5d 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: pip install -r requirements-e2e.txt python3 -m playwright install --with-deps chromium - name: Exercise packaged mobile work journeys - run: python3 -m pytest tests/e2e/test_mobile_offline_issue_release.py tests/e2e/test_mobile_search_preview_navigation.py tests/e2e/test_mobile_search_week_plan.py tests/e2e/test_mobile_find_work_release.py tests/e2e/test_mobile_home_bootstrap_release.py tests/e2e/test_mobile_sign_out_release.py tests/e2e/test_mobile_today_handoff_release.py tests/e2e/test_mobile_today_wrap_up_release.py tests/e2e/test_mobile_today_summary_release.py tests/e2e/test_mobile_tomorrow_conflict_release.py tests/e2e/test_mobile_week_ahead_release.py tests/e2e/test_mobile_today_week_reschedule_release.py tests/e2e/test_mobile_wrap_up_handoff_release.py tests/e2e/test_mobile_following_release.py -q + run: python3 -m pytest tests/e2e/test_mobile_offline_issue_release.py tests/e2e/test_mobile_search_preview_navigation.py tests/e2e/test_mobile_search_week_plan.py tests/e2e/test_mobile_find_work_release.py tests/e2e/test_mobile_home_bootstrap_release.py tests/e2e/test_mobile_sign_out_release.py tests/e2e/test_mobile_today_handoff_release.py tests/e2e/test_mobile_today_wrap_up_release.py tests/e2e/test_mobile_today_summary_release.py tests/e2e/test_mobile_tomorrow_conflict_release.py tests/e2e/test_mobile_week_ahead_release.py tests/e2e/test_mobile_today_week_reschedule_release.py tests/e2e/test_mobile_wrap_up_handoff_release.py tests/e2e/test_mobile_following_release.py tests/e2e/test_mobile_detail_watch_release.py -q release-candidate: runs-on: ubuntu-latest 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
+