@@ -7,9 +7,11 @@ import { useCallback } from 'react';
77
88import { Effect , Either } from 'effect' ;
99
10+ import { normalizeSpaceId } from '~/core/access/space-access' ;
1011import { usePersonalSpaceId } from '~/core/hooks/use-personal-space-id' ;
1112import { useSmartAccount } from '~/core/hooks/use-smart-account' ;
1213import { useSmartAccountTransaction } from '~/core/hooks/use-smart-account-transaction' ;
14+ import { getIsEditorOfSpace , getIsMemberOfSpace } from '~/core/io/queries' ;
1315import { useStatusBar } from '~/core/state/status-bar-store' ;
1416import { runEffectEither } from '~/core/telemetry/effect-runtime' ;
1517import { SPACE_REGISTRY_ADDRESS } from '~/core/utils/contracts/space-registry' ;
@@ -47,6 +49,22 @@ export function useRequestToBeMember({ spaceId }: UseRequestToBeMemberArgs) {
4749 throw new Error ( 'Invalid target space ID' ) ;
4850 }
4951
52+ // Members/editors already belong to the space; a duplicate join request errors on vote.
53+ // Check at submit time rather than via reactive access-control state, which reads false
54+ // while still hydrating and would let a fast click through. Fail open if the check errors.
55+ const normalizedSpaceId = normalizeSpaceId ( spaceId ) ;
56+ const normalizedPersonalSpaceId = normalizeSpaceId ( personalSpaceId ) ;
57+ const access = await runEffectEither (
58+ Effect . all ( [
59+ getIsMemberOfSpace ( normalizedSpaceId , normalizedPersonalSpaceId ) ,
60+ getIsEditorOfSpace ( normalizedSpaceId , normalizedPersonalSpaceId ) ,
61+ ] )
62+ ) ;
63+ if ( Either . isRight ( access ) && ( access . right [ 0 ] || access . right [ 1 ] ) ) {
64+ dispatch ( { type : 'ERROR' , payload : 'You are already a member of this space' } ) ;
65+ throw new Error ( 'User is already a member or editor of the space' ) ;
66+ }
67+
5068 console . log ( 'Requesting to be member' , {
5169 authorSpaceId : personalSpaceId ,
5270 spaceId,
0 commit comments