-
Notifications
You must be signed in to change notification settings - Fork 3
feat: add repo-level permissions context #107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Extracts canModerate check to RepoPermissionsProvider at the repo layout level. Now checkCanModerate only runs once when entering a repo, shared via context. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| }) { | ||
| const session = authClient.useSession() | ||
| const [canModerate, setCanModerate] = useState(false) | ||
| const [isLoading, setIsLoading] = useState(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ModerationBanner and StaleBanner components don't wait for permission loading to complete, causing moderation UI to be hidden during initial load even when user has permissions
View Details
📝 Patch Details
diff --git a/app/[owner]/[repo]/[postNumber]/post-header.tsx b/app/[owner]/[repo]/[postNumber]/post-header.tsx
index 8c522c7..9ce76bf 100644
--- a/app/[owner]/[repo]/[postNumber]/post-header.tsx
+++ b/app/[owner]/[repo]/[postNumber]/post-header.tsx
@@ -151,7 +151,7 @@ function RefSelector() {
function ModerationBanner() {
const { postId, pinned } = usePostMetadata()
- const { canModerate } = useRepoPermissions()
+ const { canModerate, isLoading } = useRepoPermissions()
const [isPinned, setIsPinned] = useState(pinned)
const [isPending, startTransition] = useTransition()
const router = useRouter()
@@ -159,7 +159,7 @@ function ModerationBanner() {
(s) => s.setModeratorDeletePostDialog
)
- if (!canModerate) {
+ if (!canModerate || isLoading) {
return null
}
@@ -231,7 +231,7 @@ function StaleBanner() {
const session = authClient.useSession()
const userId = session.data?.user.id
const isAuthor = userId === authorId
- const { canModerate } = useRepoPermissions()
+ const { canModerate, isLoading } = useRepoPermissions()
const [isPending, startTransition] = useTransition()
const router = useRouter()
@@ -239,7 +239,7 @@ function StaleBanner() {
return null
}
- const canRerun = isAuthor || canModerate
+ const canRerun = (isAuthor || canModerate) && !isLoading
function handleRerun() {
startTransition(async () => {
Analysis
Bug Explanation:
The RepoPermissionsProvider context provides both canModerate and isLoading states. The canModerate state starts as false (line 24 in repo-permissions-context.tsx) and isLoading starts as true. After an async permission check completes, canModerate is updated and isLoading is set to false.
However, two components (ModerationBanner at line 154 and StaleBanner at line 234 in post-header.tsx) were only destructuring canModerate without checking isLoading. This causes:
-
ModerationBanner: Returns
nullif!canModerate(line 159). During the loading phase,canModerateisfalse, so the entire moderation banner is hidden. When the permission check completes and the user actually has moderation permissions, the banner only appears after the component re-renders. This causes UI flickering or the banner appearing after a delay. -
StaleBanner: Computes
canRerun = isAuthor || canModerate(line 244). During loading,canModerateisfalse, so the rerun button won't show for moderators until the permission check completes, even though they should have access.
Fix Explanation:
I modified both components to destructure isLoading from useRepoPermissions() and incorporate it into their rendering logic:
-
ModerationBanner: Changed the early return condition from
if (!canModerate)toif (!canModerate || isLoading). This ensures the moderation banner doesn't render until the permission check completes, preventing the flickering behavior caused bycanModeratebeing initially false. -
StaleBanner: Changed
canReruncomputation fromisAuthor || canModerateto(isAuthor || canModerate) && !isLoading. This ensures the rerun button only appears after the permission check completes, preventing premature hiding of the button for moderators.
The fix ensures that moderation UI correctly represents the user's permissions once the loading state completes, eliminating the false-negative display issue.
| checkCanModerate(owner, repo).then((result) => { | ||
| setCanModerate(result) | ||
| setIsLoading(false) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| checkCanModerate(owner, repo).then((result) => { | |
| setCanModerate(result) | |
| setIsLoading(false) | |
| }) | |
| checkCanModerate(owner, repo) | |
| .then((result) => { | |
| setCanModerate(result) | |
| setIsLoading(false) | |
| }) | |
| .catch((error) => { | |
| console.error("Failed to check moderation permissions:", error) | |
| setCanModerate(false) | |
| setIsLoading(false) | |
| }) |
Missing error handler on checkCanModerate promise causes loading state to persist indefinitely when the async operation fails
View Details
📝 Patch Details
diff --git a/app/[owner]/[repo]/repo-permissions-context.tsx b/app/[owner]/[repo]/repo-permissions-context.tsx
index 8145431..1c1f0d2 100644
--- a/app/[owner]/[repo]/repo-permissions-context.tsx
+++ b/app/[owner]/[repo]/repo-permissions-context.tsx
@@ -31,10 +31,16 @@ export function RepoPermissionsProvider({
return
}
setIsLoading(true)
- checkCanModerate(owner, repo).then((result) => {
- setCanModerate(result)
- setIsLoading(false)
- })
+ checkCanModerate(owner, repo)
+ .then((result) => {
+ setCanModerate(result)
+ setIsLoading(false)
+ })
+ .catch((error) => {
+ console.error("Failed to check moderation permissions:", error)
+ setCanModerate(false)
+ setIsLoading(false)
+ })
}, [owner, repo, session.data?.user])
return (
Analysis
Bug Explanation
The checkCanModerate promise chain in repo-permissions-context.tsx (lines 34-37) was missing a .catch() error handler. The checkCanModerate server action performs multiple async operations that can fail:
- Authentication call:
auth.api.getSession()can throw errors - Database queries:
canModerate()involves database operations that can fail due to network issues, database unavailability, or connectivity problems - Authorization failures: Auth-related errors can be thrown
When any of these operations fail, the promise rejects. Without a .catch() handler:
- The
.then()callback never executes setIsLoading(false)is never calledisLoadingremainstrueindefinitely- The UI gets stuck in a loading state, providing poor user experience
- There's no visibility into what went wrong (no error logging)
Fix Explanation
Added a .catch() handler that:
- Logs the error to console for debugging/monitoring
- Sets
canModeratetofalse(fail-safe: deny access on error) - Critically: Sets
isLoadingtofalseto unblock the UI
This ensures that regardless of whether the permission check succeeds or fails, the loading state is properly cleaned up, preventing the UI from hanging indefinitely. The fail-safe default of setCanModerate(false) ensures security by denying moderation permissions if we can't verify them.
Summary
RepoPermissionsProvidercontext atapp/[owner]/[repo]/layout.tsxcheckCanModeratenow runs once per repo navigation instead of in each componentuseRepoPermissions()hook to access permissionscanModerateprop fromPostMetadataProviderTest plan
🤖 Generated with Claude Code