2024-07-07 18:32:30 +03:00
import { POST } from '../modules/fetch.ts' ;
import { showErrorToast } from '../modules/toast.ts' ;
2022-01-29 00:00:11 +03:00
2024-11-11 14:13:57 +03:00
const preventListener = ( e : Event ) = > e . preventDefault ( ) ;
2022-01-29 00:00:11 +03:00
2021-05-23 17:14:03 +03:00
/ * *
* Attaches ` input ` handlers to markdown rendered tasklist checkboxes in comments .
*
* When a checkbox value changes , the corresponding [ ] or [ x ] in the markdown string
* is set accordingly and sent to the server . On success it updates the raw - content on
* error it resets the checkbox to its original value .
* /
2024-11-11 14:13:57 +03:00
export function initMarkupTasklist ( ) : void {
2021-05-23 17:14:03 +03:00
for ( const el of document . querySelectorAll ( ` .markup[data-can-edit=true] ` ) || [ ] ) {
const container = el . parentNode ;
2024-11-11 14:13:57 +03:00
const checkboxes = el . querySelectorAll < HTMLInputElement > ( ` .task-list-item input[type=checkbox] ` ) ;
2021-05-23 17:14:03 +03:00
for ( const checkbox of checkboxes ) {
2021-11-22 11:19:01 +03:00
if ( checkbox . hasAttribute ( 'data-editable' ) ) {
2021-06-14 23:42:58 +03:00
return ;
}
2021-11-22 11:19:01 +03:00
checkbox . setAttribute ( 'data-editable' , 'true' ) ;
2021-05-23 17:14:03 +03:00
checkbox . addEventListener ( 'input' , async ( ) = > {
const checkboxCharacter = checkbox . checked ? 'x' : ' ' ;
2021-11-22 11:19:01 +03:00
const position = parseInt ( checkbox . getAttribute ( 'data-source-position' ) ) + 1 ;
2021-05-23 17:14:03 +03:00
const rawContent = container . querySelector ( '.raw-content' ) ;
const oldContent = rawContent . textContent ;
2021-06-14 23:42:58 +03:00
const encoder = new TextEncoder ( ) ;
const buffer = encoder . encode ( oldContent ) ;
2023-06-13 09:44:47 +03:00
// Indexes may fall off the ends and return undefined.
if ( buffer [ position - 1 ] !== '[' . codePointAt ( 0 ) ||
buffer [ position ] !== ' ' . codePointAt ( 0 ) && buffer [ position ] !== 'x' . codePointAt ( 0 ) ||
buffer [ position + 1 ] !== ']' . codePointAt ( 0 ) ) {
// Position is probably wrong. Revert and don't allow change.
checkbox . checked = ! checkbox . checked ;
throw new Error ( ` Expected position to be space or x and surrounded by brackets, but it's not: position= ${ position } ` ) ;
}
2021-06-14 23:42:58 +03:00
buffer . set ( encoder . encode ( checkboxCharacter ) , position ) ;
const newContent = new TextDecoder ( ) . decode ( buffer ) ;
if ( newContent === oldContent ) {
return ;
}
2021-05-23 17:14:03 +03:00
// Prevent further inputs until the request is done. This does not use the
// `disabled` attribute because it causes the border to flash on click.
for ( const checkbox of checkboxes ) {
checkbox . addEventListener ( 'click' , preventListener ) ;
}
try {
2024-11-11 14:13:57 +03:00
const editContentZone = container . querySelector < HTMLDivElement > ( '.edit-content-zone' ) ;
2021-11-22 11:19:01 +03:00
const updateUrl = editContentZone . getAttribute ( 'data-update-url' ) ;
const context = editContentZone . getAttribute ( 'data-context' ) ;
2024-05-27 18:34:18 +03:00
const contentVersion = editContentZone . getAttribute ( 'data-content-version' ) ;
2021-05-23 17:14:03 +03:00
2024-02-15 16:27:07 +03:00
const requestBody = new FormData ( ) ;
requestBody . append ( 'ignore_attachments' , 'true' ) ;
requestBody . append ( 'content' , newContent ) ;
requestBody . append ( 'context' , context ) ;
2024-05-27 18:34:18 +03:00
requestBody . append ( 'content_version' , contentVersion ) ;
const response = await POST ( updateUrl , { data : requestBody } ) ;
const data = await response . json ( ) ;
if ( response . status === 400 ) {
showErrorToast ( data . errorMessage ) ;
return ;
}
editContentZone . setAttribute ( 'data-content-version' , data . contentVersion ) ;
2021-05-23 17:14:03 +03:00
rawContent . textContent = newContent ;
} catch ( err ) {
checkbox . checked = ! checkbox . checked ;
console . error ( err ) ;
}
// Enable input on checkboxes again
for ( const checkbox of checkboxes ) {
checkbox . removeEventListener ( 'click' , preventListener ) ;
}
} ) ;
}
// Enable the checkboxes as they are initially disabled by the markdown renderer
for ( const checkbox of checkboxes ) {
checkbox . disabled = false ;
}
}
}