Skip to content

feat: add release unsubscribe action#57

Merged
AmintaCCCP merged 1 commit intomainfrom
feat/release-unsubscribe
Mar 14, 2026
Merged

feat: add release unsubscribe action#57
AmintaCCCP merged 1 commit intomainfrom
feat/release-unsubscribe

Conversation

@AmintaCCCP
Copy link
Owner

@AmintaCCCP AmintaCCCP commented Mar 14, 2026

Summary

  • add unsubscribe button beside release open URL
  • confirm before unsubscribing
  • update store + sync to backend (autoSync)

Testing

  • not run (no test script)

@coderabbitai
Copy link

coderabbitai bot commented Mar 14, 2026

📝 Walkthrough

Walkthrough

A new unsubscribe feature has been added to the ReleaseTimeline component, enabling users to unsubscribe repositories from release notifications. The implementation includes optimistic UI updates, backend synchronization via forceSyncToBackend, rollback on failure, and localized error/success alerts.

Changes

Cohort / File(s) Summary
Release Timeline Unsubscribe Feature
src/components/ReleaseTimeline.tsx
Added handleUnsubscribeRelease handler with optimistic state updates, backend sync, and error rollback. Integrated BellOff icon for unsubscribe buttons in both detailed and compact views. Updated component to use toggleReleaseSubscription and updateRepository from app store. Added localized alert messages for success and failure states.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ReleaseTimeline
    participant AppStore
    participant BackendService
    
    User->>ReleaseTimeline: Click unsubscribe button
    ReleaseTimeline->>AppStore: updateRepository (optimistic)<br/>(set subscription to false)
    ReleaseTimeline->>AppStore: toggleReleaseSubscription
    ReleaseTimeline->>BackendService: forceSyncToBackend
    
    alt Backend sync succeeds
        BackendService-->>ReleaseTimeline: Success
        ReleaseTimeline->>User: Show confirmation alert
    else Backend sync fails
        BackendService-->>ReleaseTimeline: Error
        ReleaseTimeline->>AppStore: updateRepository (rollback)<br/>(revert subscription state)
        ReleaseTimeline->>User: Show error alert
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Poem

🐰 A bell rings out, but now you choose—
To mute the chime, there's nothing to lose!
With backup plans and sync so true,
The timeline bends to honor you! 🔔✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add release unsubscribe action' clearly and specifically describes the main change: adding an unsubscribe action for releases, which is directly supported by the changeset that introduces handleUnsubscribeRelease, BellOff UI controls, and backend synchronization.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/release-unsubscribe
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can generate a title for your PR based on the changes.

Add @coderabbitai placeholder anywhere in the title of your PR and CodeRabbit will replace it with a title based on the changes in the PR. You can change the placeholder by changing the reviews.auto_title_placeholder setting.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/ReleaseTimeline.tsx`:
- Around line 295-325: The optimistic rollback path in handleUnsubscribeRelease
is unreachable because forceSyncToBackend never signals failure; change
forceSyncToBackend (and/or syncToBackend) to either re-throw errors or return a
boolean success flag, then update handleUnsubscribeRelease to await that result
and only keep the optimistic update when sync succeeded—if the returned value is
false or an exception is thrown, perform the rollback (updateRepository({
...repo, subscribed_to_releases: true }) and toggleReleaseSubscription(repo.id))
and show the failure alert; reference functions: handleUnsubscribeRelease,
forceSyncToBackend, syncToBackend.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7540d3f9-73bf-4b2a-8467-fdc12b7da31b

📥 Commits

Reviewing files that changed from the base of the PR and between 1e9f603 and fb548c0.

📒 Files selected for processing (1)
  • src/components/ReleaseTimeline.tsx

Comment on lines +295 to +325
const handleUnsubscribeRelease = async (repoId: number) => {
const repo = repositories.find((item) => item.id === repoId);
if (!repo) {
alert(t('仓库信息不完整,无法取消订阅。', 'Repository information missing. Cannot unsubscribe.'));
return;
}

const confirmMessage = language === 'zh'
? `确定取消订阅 "${repo.full_name}" 的 Release 吗?`
: `Unsubscribe from releases for "${repo.full_name}"?`;

if (!confirm(confirmMessage)) {
return;
}

const updatedRepo = { ...repo, subscribed_to_releases: false };
updateRepository(updatedRepo);
toggleReleaseSubscription(repo.id);

try {
await forceSyncToBackend();
} catch (error) {
console.error('Failed to unsubscribe release:', error);
updateRepository({ ...repo, subscribed_to_releases: true });
toggleReleaseSubscription(repo.id);
alert(t('取消订阅失败,请检查后端连接。', 'Failed to unsubscribe. Please check backend connection.'));
return;
}

alert(t('已取消订阅该仓库的 Release。', 'Unsubscribed from repository releases.'));
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify that forceSyncToBackend and syncToBackend do not re-throw errors

echo "=== Checking forceSyncToBackend implementation ==="
rg -n -A 15 'export async function forceSyncToBackend' --type ts

echo ""
echo "=== Checking syncToBackend error handling ==="
rg -n -A 60 'export async function syncToBackend' --type ts | head -80

Repository: AmintaCCCP/GithubStarsManager

Length of output: 5170


Rollback logic is unreachable—forceSyncToBackend never throws.

forceSyncToBackend calls syncToBackend() which catches all errors internally (line 222–226) without re-throwing. Both functions always resolve successfully, making the catch block at lines 316–322 dead code.

Impact:

  • When backend sync fails, the optimistic UI update persists (subscription appears removed).
  • The user sees the success alert on line 324 even though sync failed.
  • No rollback occurs, leaving local and backend state inconsistent.

To fix this, forceSyncToBackend must either propagate errors or return a success/failure indicator.

🛠️ Possible fix: Return sync result from forceSyncToBackend

Option 1: Modify forceSyncToBackend to return a boolean:

// In autoSync.ts
export async function forceSyncToBackend(): Promise<boolean> {
  if (_debounceTimer) {
    clearTimeout(_debounceTimer);
    _debounceTimer = null;
  }
  _hasPendingLocalChanges = true;
  return await syncToBackendWithResult();
}

async function syncToBackendWithResult(): Promise<boolean> {
  // ... existing syncToBackend logic ...
  try {
    // ... sync ...
    return failures.length === 0;
  } catch (err) {
    console.error('Failed to sync to backend:', err);
    return false;
  }
}

Option 2: Check sync result in component:

-    try {
-      await forceSyncToBackend();
-    } catch (error) {
-      console.error('Failed to unsubscribe release:', error);
+    const syncSuccess = await forceSyncToBackend();
+    if (!syncSuccess) {
+      console.error('Failed to unsubscribe release');
       updateRepository({ ...repo, subscribed_to_releases: true });
       toggleReleaseSubscription(repo.id);
       alert(t('取消订阅失败,请检查后端连接。', 'Failed to unsubscribe. Please check backend connection.'));
       return;
-    }
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ReleaseTimeline.tsx` around lines 295 - 325, The optimistic
rollback path in handleUnsubscribeRelease is unreachable because
forceSyncToBackend never signals failure; change forceSyncToBackend (and/or
syncToBackend) to either re-throw errors or return a boolean success flag, then
update handleUnsubscribeRelease to await that result and only keep the
optimistic update when sync succeeded—if the returned value is false or an
exception is thrown, perform the rollback (updateRepository({ ...repo,
subscribed_to_releases: true }) and toggleReleaseSubscription(repo.id)) and show
the failure alert; reference functions: handleUnsubscribeRelease,
forceSyncToBackend, syncToBackend.

@AmintaCCCP AmintaCCCP merged commit 8bd68a6 into main Mar 14, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant