@@ -6,6 +6,7 @@ import { PiX } from "@react-icons/all-files/pi/PiX"
66import type React from "react"
77import { useEffect , useMemo , useRef , useState } from "react"
88import Select , { type StylesConfig } from "react-select"
9+ import { useTemplatePermissions } from "../../context/TemplatePermissionsContext"
910import { useTemplateStorage } from "../../context/TemplateStorageContext"
1011import { applyTemplateStorageAccessFailure } from "../../storage/templateAccessError"
1112import type { TemplateRecord } from "../../storage/types"
@@ -55,6 +56,7 @@ export function SettingsModal({
5556 onDeleted,
5657} : SettingsModalProps ) {
5758 const provider = useTemplateStorage ( )
59+ const permissions = useTemplatePermissions ( data )
5860 const templateStorageWriteBlocked = useEditorStore (
5961 ( s ) => s . templateStorageWriteBlocked ,
6062 )
@@ -73,50 +75,11 @@ export function SettingsModal({
7375 const [ localVisibility , setLocalVisibility ] = useState < Visibility > ( ( ) =>
7476 visibilityFromRecord ( data ) ,
7577 )
76- // Whether the current user is allowed to change visibility (only the creator can).
77- const [ canChangeVisibility , setCanChangeVisibility ] = useState ( true )
7878
7979 const [ isDeleting , setIsDeleting ] = useState ( false )
8080 const [ isSaving , setIsSaving ] = useState ( false )
8181 const [ showDeleteConfirm , setShowDeleteConfirm ] = useState ( false )
8282
83- // Fetch authoritative visibility + creator check from the API.
84- useEffect ( ( ) => {
85- let cancelled = false
86-
87- if ( ! provider ) {
88- setCanChangeVisibility ( true )
89- return
90- }
91-
92- provider
93- . getTemplate ( data . id )
94- . then ( ( record ) => {
95- if ( cancelled ) return
96- if ( ! record ) {
97- setCanChangeVisibility ( true )
98- return
99- }
100- setLocalVisibility ( visibilityFromRecord ( record ) )
101- const session = provider . getCurrentUserSession ( ) as {
102- id ?: string
103- } | null
104- setCanChangeVisibility ( record . createdBy . userId === session ?. id )
105- } )
106- . catch ( ( err ) => {
107- if ( cancelled ) return
108- if (
109- applyTemplateStorageAccessFailure ( err , { denyTemplateStorageAccess } )
110- ) {
111- onCloseRef . current ( )
112- }
113- } )
114-
115- return ( ) => {
116- cancelled = true
117- }
118- } , [ provider , data . id , denyTemplateStorageAccess ] )
119-
12083 // Close on Escape
12184 useEffect ( ( ) => {
12285 const handleKeyDown = ( e : KeyboardEvent ) => {
@@ -194,13 +157,15 @@ export function SettingsModal({
194157 fontSize : "12px" ,
195158 minHeight : "32px" ,
196159 borderColor : "#E2E8F0" ,
197- backgroundColor : canChangeVisibility ? base . backgroundColor : "#F7FAFC" ,
198- opacity : canChangeVisibility ? 1 : 0.6 ,
160+ backgroundColor : permissions . changeVisibility
161+ ? base . backgroundColor
162+ : "#F7FAFC" ,
163+ opacity : permissions . changeVisibility ? 1 : 0.6 ,
199164 } ) ,
200165 menu : ( base ) => ( { ...base , zIndex : 10 } ) ,
201166 option : ( base ) => ( { ...base , fontSize : "12px" } ) ,
202167 } ) ,
203- [ canChangeVisibility ] ,
168+ [ permissions . changeVisibility ] ,
204169 )
205170
206171 // Get height of the current viewport
@@ -276,6 +241,7 @@ export function SettingsModal({
276241 }
277242 placeholder = "Enter template name"
278243 fontSize = "sm"
244+ isDisabled = { ! permissions . rename }
279245 />
280246 </ Box >
281247
@@ -299,7 +265,7 @@ export function SettingsModal({
299265 : "Only to me" ,
300266 } }
301267 onChange = { ( option ) => {
302- if ( ! canChangeVisibility || ! option ) return
268+ if ( ! permissions . changeVisibility || ! option ) return
303269 setLocalVisibility ( option . value as Visibility )
304270 } }
305271 options = { [
@@ -319,7 +285,7 @@ export function SettingsModal({
319285 ) }
320286 styles = { selectStyles }
321287 isSearchable = { false }
322- isDisabled = { ! canChangeVisibility }
288+ isDisabled = { ! permissions . changeVisibility }
323289 />
324290 </ Box >
325291 </ FlexAny >
@@ -464,12 +430,19 @@ export function SettingsModal({
464430 }
465431 fontSize = "sm"
466432 fontWeight = "medium"
467- onClick = {
468- isDeleting || isSaving || showDeleteConfirm
433+ onClick = { ( ) => {
434+ if ( ! permissions . delete ) return
435+
436+ return isDeleting || isSaving || showDeleteConfirm
469437 ? undefined
470438 : ( ) => setShowDeleteConfirm ( true )
471- }
439+ } }
472440 aria-disabled = { isDeleting || isSaving || showDeleteConfirm }
441+ disabled = { ! permissions . delete }
442+ _disabled = { {
443+ color : "gray.400" ,
444+ cursor : "not-allowed" ,
445+ } }
473446 >
474447 < Icon as = { PiTrash } boxSize = { 5 } />
475448 { isDeleting ? "Deleting…" : "Delete" }
@@ -538,6 +511,11 @@ export function SettingsModal({
538511 isSaving ||
539512 templateStorageWriteBlocked
540513 }
514+ disabled = { ! permissions . save }
515+ _disabled = { {
516+ bg : "blue.200" ,
517+ cursor : "not-allowed" ,
518+ } }
541519 >
542520 { isSaving ? "Saving…" : "Save" }
543521 </ Box >
0 commit comments