Fix unclaim permission bug - only show button for task owner
Previously, users could see and click "Unclaim" button for tasks claimed by others, leading to failed deletion attempts and temporary UI inconsistencies. Changes: - Add auth service injection to ScheduledEventCard - Add canUnclaim computed property checking if current user created the current status - Only show "Unclaim" button when canUnclaim is true (user owns the current status) - Applies NIP-09 rule: users can only delete their own events Behavior: - Alice claims task → only Alice sees "Unclaim" button - Bob marks task in-progress → only Bob sees "Unclaim" button - Prevents failed deletion attempts and optimistic update bugs - Follows Nostr protocol permissions correctly This is a quick fix. Future enhancement will track per-user status for full collaborative workflow support. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
76fbf7579f
commit
d0b3396af7
1 changed files with 17 additions and 0 deletions
|
|
@ -17,6 +17,8 @@ import {
|
||||||
} from '@/components/ui/collapsible'
|
} from '@/components/ui/collapsible'
|
||||||
import { Calendar, MapPin, Clock, CheckCircle, PlayCircle, Hand } from 'lucide-vue-next'
|
import { Calendar, MapPin, Clock, CheckCircle, PlayCircle, Hand } from 'lucide-vue-next'
|
||||||
import type { ScheduledEvent, EventCompletion, TaskStatus } from '../services/ScheduledEventService'
|
import type { ScheduledEvent, EventCompletion, TaskStatus } from '../services/ScheduledEventService'
|
||||||
|
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
|
||||||
|
import type { AuthService } from '@/modules/base/auth/auth-service'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
event: ScheduledEvent
|
event: ScheduledEvent
|
||||||
|
|
@ -39,6 +41,9 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
|
|
||||||
const emit = defineEmits<Emits>()
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
// Get auth service to check current user
|
||||||
|
const authService = injectService<AuthService>(SERVICE_TOKENS.AUTH_SERVICE)
|
||||||
|
|
||||||
// Confirmation dialog state
|
// Confirmation dialog state
|
||||||
const showConfirmDialog = ref(false)
|
const showConfirmDialog = ref(false)
|
||||||
|
|
||||||
|
|
@ -66,6 +71,15 @@ const isCompletable = computed(() => props.event.eventType === 'task')
|
||||||
// Get completion data
|
// Get completion data
|
||||||
const completion = computed(() => props.getCompletion(eventAddress.value, occurrence.value))
|
const completion = computed(() => props.getCompletion(eventAddress.value, occurrence.value))
|
||||||
|
|
||||||
|
// Get current user's pubkey
|
||||||
|
const currentUserPubkey = computed(() => authService?.user.value?.pubkey)
|
||||||
|
|
||||||
|
// Check if current user can unclaim (only if they created the current status)
|
||||||
|
const canUnclaim = computed(() => {
|
||||||
|
if (!completion.value || !currentUserPubkey.value) return false
|
||||||
|
return completion.value.pubkey === currentUserPubkey.value
|
||||||
|
})
|
||||||
|
|
||||||
// Status badges configuration
|
// Status badges configuration
|
||||||
const statusConfig = computed(() => {
|
const statusConfig = computed(() => {
|
||||||
switch (taskStatus.value) {
|
switch (taskStatus.value) {
|
||||||
|
|
@ -394,6 +408,7 @@ const dialogContent = computed(() => {
|
||||||
Mark Complete
|
Mark Complete
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
v-if="canUnclaim"
|
||||||
@click.stop="handleUnclaimTask"
|
@click.stop="handleUnclaimTask"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
@ -414,6 +429,7 @@ const dialogContent = computed(() => {
|
||||||
Mark Complete
|
Mark Complete
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
v-if="canUnclaim"
|
||||||
@click.stop="handleUnclaimTask"
|
@click.stop="handleUnclaimTask"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
@ -425,6 +441,7 @@ const dialogContent = computed(() => {
|
||||||
<!-- Completed Task -->
|
<!-- Completed Task -->
|
||||||
<template v-else-if="taskStatus === 'completed'">
|
<template v-else-if="taskStatus === 'completed'">
|
||||||
<Button
|
<Button
|
||||||
|
v-if="canUnclaim"
|
||||||
@click.stop="handleUnclaimTask"
|
@click.stop="handleUnclaimTask"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue