@@ -81,7 +81,7 @@ function guessPreferencesFileUri (me: NamedNode): string {
8181 . replace ( '/public/' , '/' )
8282 . split ( '/' )
8383 . slice ( 0 , - 1 )
84- . join ( '/' ) + '/Settings/Preferences .ttl'
84+ . join ( '/' ) + '/settings/prefs .ttl'
8585}
8686
8787function getMissingPreferencesFileUri ( context : AuthenticationContext , err : unknown ) : string | null {
@@ -104,51 +104,84 @@ function getMissingPreferencesFileUri (context: AuthenticationContext, err: unkn
104104 return null
105105}
106106
107- async function createPreferencesFile ( context : AuthenticationContext , uri : string ) : Promise < void > {
107+ async function ensurePreferencesFileRowInProfile (
108+ me : NamedNode ,
109+ prefsNode : NamedNode ,
110+ profileDoc : NamedNode
111+ ) : Promise < void > {
112+ const existingPreferencesFile = store . any ( me , ns . space ( 'preferencesFile' ) , undefined , profileDoc )
113+
114+ if ( existingPreferencesFile && existingPreferencesFile . equals ( prefsNode ) ) {
115+ return
116+ }
117+
118+ if ( ! store . updater ) {
119+ throw new Error ( 'Cannot create preferences file: store has no updater' )
120+ }
121+
122+ const deletions = existingPreferencesFile
123+ ? [ st ( me , ns . space ( 'preferencesFile' ) , existingPreferencesFile , profileDoc ) ]
124+ : [ ]
125+ const insertions = [ st ( me , ns . space ( 'preferencesFile' ) , prefsNode , profileDoc ) ]
126+
127+ await new Promise < void > ( ( resolve , reject ) => {
128+ store . updater ! . update ( deletions as any , insertions as any , ( _uri , ok , body ) => {
129+ if ( ok ) {
130+ resolve ( )
131+ } else {
132+ reject ( new Error ( body || 'Unable to update preferencesFile in WebID profile' ) )
133+ }
134+ } )
135+ } )
136+ }
137+
138+ function buildPreferencesFileTurtle ( me : NamedNode ) : string {
139+ const mbox = store . any ( me , ns . foaf ( 'mbox' ) )
140+ const mboxLine = mbox ? `\n<${ me . uri } > foaf:mbox <${ mbox . value } > .\n` : '\n'
141+
142+ return (
143+ '@prefix dct: <http://purl.org/dc/terms/>.\n' +
144+ '@prefix pim: <http://www.w3.org/ns/pim/space#>.\n' +
145+ '@prefix foaf: <http://xmlns.com/foaf/0.1/>.\n' +
146+ '@prefix solid: <http://www.w3.org/ns/solid/terms#>.\n\n' +
147+ '<>\n' +
148+ ' a pim:ConfigurationFile;\n\n' +
149+ ' dct:title "Preferences file" .\n' +
150+ mboxLine +
151+ `<${ me . uri } >\n` +
152+ ' solid:publicTypeIndex <publicTypeIndex.ttl> ;\n' +
153+ ' solid:privateTypeIndex <privateTypeIndex.ttl> .\n'
154+ )
155+ }
156+
157+ async function writePreferencesFileDocument ( uri : string , data : string ) : Promise < void > {
108158 if ( ! store . fetcher ) {
109159 throw new Error ( 'Cannot create preferences file: store has no fetcher' )
110160 }
111- const initialData = '# Preferences file created by solid-ui\n'
161+
112162 await store . fetcher . webOperation ( 'PUT' , uri , {
113- data : initialData ,
163+ data,
114164 contentType : 'text/turtle'
115165 } )
116- if ( ! context . me ) {
117- throw new Error ( 'Cannot reload preferences file: no logged-in WebID' )
118- }
119- context . preferencesFile = await loadPreferences ( context . me as NamedNode )
120- context . preferencesFileError = undefined
121166}
122167
123- function offerCreatePreferencesFile (
124- context : AuthenticationContext ,
125- uri : string ,
126- baseMessage : string
127- ) : void {
128- if ( context . div && context . dom ) {
129- const box = context . dom . createElement ( 'div' )
130- box . appendChild ( widgets . errorMessageBlock ( context . dom , `${ baseMessage } Create it now?` ) )
131- const createButton = context . dom . createElement ( 'button' )
132- createButton . textContent = 'Create preferences file'
133- createButton . setAttribute ( 'style' , style . buttonStyle )
134- createButton . addEventListener ( 'click' , ( ) => {
135- createButton . disabled = true
136- createPreferencesFile ( context , uri )
137- . then ( ( ) => {
138- box . appendChild ( widgets . errorMessageBlock ( context . dom as HTMLDocument , `Preferences file created: ${ uri } ` , '#dfd' ) )
139- } )
140- . catch ( error => {
141- const message = formatDynamicError ( error , 'Failed to create preferences file' )
142- box . appendChild ( widgets . errorMessageBlock ( context . dom as HTMLDocument , message ) )
143- debug . error ( message )
144- createButton . disabled = false
145- } )
146- } )
147- box . appendChild ( createButton )
148- context . div . appendChild ( box )
149- return
168+ async function createPreferencesFile ( context : AuthenticationContext , uri : string ) : Promise < void > {
169+ const me = ( context . me as NamedNode ) || authn . currentUser ( )
170+ if ( ! me ) {
171+ throw new Error ( 'Cannot create preferences file: no logged-in WebID' )
150172 }
151- debug . warn ( `${ baseMessage } You can create it at: ${ uri } ` )
173+ context . me = me
174+
175+ const profileDoc = me . doc ( )
176+ const prefsNode = store . sym ( uri )
177+
178+ await ensurePreferencesFileRowInProfile ( me , prefsNode , profileDoc )
179+
180+ const prefsTurtle = buildPreferencesFileTurtle ( me )
181+ await writePreferencesFileDocument ( uri , prefsTurtle )
182+
183+ context . preferencesFile = await loadPreferences ( me )
184+ context . preferencesFileError = undefined
152185}
153186
154187/**
@@ -255,12 +288,24 @@ export async function ensureLoadedPreferences (
255288 } else if ( err instanceof FetchError ) {
256289 if ( err . status === 404 ) {
257290 m2 = 'Your preferences file was not found (404). It may not exist yet.'
258- context . preferencesFileError = m2
259291 debug . warn ( m2 )
260292 const missingPreferencesFileUri = getMissingPreferencesFileUri ( context , err )
261293 if ( missingPreferencesFileUri ) {
262- offerCreatePreferencesFile ( context , missingPreferencesFileUri , m2 )
294+ try {
295+ await createPreferencesFile ( context , missingPreferencesFileUri )
296+ debug . log ( `Preferences file created automatically: ${ missingPreferencesFileUri } ` )
297+ return context
298+ } catch ( createError ) {
299+ const message = formatDynamicError ( createError , 'Failed to create preferences file automatically' )
300+ context . preferencesFileError = message
301+ debug . error ( message )
302+ if ( context . div && context . dom ) {
303+ context . div . appendChild ( widgets . errorMessageBlock ( context . dom , message ) )
304+ }
305+ return context
306+ }
263307 }
308+ context . preferencesFileError = m2
264309 return context
265310 }
266311 m2 = formatDynamicError ( err , 'Error reading your preferences file' )
0 commit comments