2019-12-13 03:16:34 +02:00
import * as React from 'react' ;
import { useState , useEffect } from 'react' ;
2020-11-07 17:59:37 +02:00
import JoplinServerApi from '@joplin/lib/JoplinServerApi' ;
2019-12-13 03:16:34 +02:00
2020-11-07 17:59:37 +02:00
import { _ , _n } from '@joplin/lib/locale' ;
const { themeStyle , buildStyle } = require ( '@joplin/lib/theme' ) ;
2019-12-13 03:16:34 +02:00
const DialogButtonRow = require ( './DialogButtonRow.min' ) ;
2021-01-22 19:41:11 +02:00
import Note from '@joplin/lib/models/Note' ;
import Setting from '@joplin/lib/models/Setting' ;
import BaseItem from '@joplin/lib/models/BaseItem' ;
2020-11-07 17:59:37 +02:00
const { reg } = require ( '@joplin/lib/registry.js' ) ;
2019-12-13 03:16:34 +02:00
const { clipboard } = require ( 'electron' ) ;
interface ShareNoteDialogProps {
2020-11-12 21:29:22 +02:00
themeId : number ;
noteIds : Array < string > ;
onClose : Function ;
2019-12-13 03:16:34 +02:00
}
interface SharesMap {
[ key : string ] : any ;
}
2020-11-12 21:13:28 +02:00
function styles_ ( props : ShareNoteDialogProps ) {
return buildStyle ( 'ShareNoteDialog' , props . themeId , ( theme : any ) = > {
2019-12-13 03:16:34 +02:00
return {
noteList : {
marginBottom : 10 ,
} ,
note : {
flex : 1 ,
flexDirection : 'row' ,
display : 'flex' ,
alignItems : 'center' ,
border : '1px solid' ,
borderColor : theme.dividerColor ,
padding : '0.5em' ,
marginBottom : 5 ,
} ,
noteTitle : {
2019-12-17 14:45:57 +02:00
. . . theme . textStyle ,
2019-12-13 03:16:34 +02:00
flex : 1 ,
display : 'flex' ,
color : theme.color ,
} ,
noteRemoveButton : {
background : 'none' ,
border : 'none' ,
} ,
noteRemoveButtonIcon : {
color : theme.color ,
fontSize : '1.4em' ,
} ,
copyShareLinkButton : {
. . . theme . buttonStyle ,
marginBottom : 10 ,
} ,
} ;
} ) ;
}
2020-11-12 21:13:28 +02:00
export default function ShareNoteDialog ( props : ShareNoteDialogProps ) {
2019-12-13 03:16:34 +02:00
console . info ( 'Render ShareNoteDialog' ) ;
const [ notes , setNotes ] = useState < any [ ] > ( [ ] ) ;
const [ sharesState , setSharesState ] = useState < string > ( 'unknown' ) ;
const [ shares , setShares ] = useState < SharesMap > ( { } ) ;
const noteCount = notes . length ;
2020-09-15 15:01:07 +02:00
const theme = themeStyle ( props . themeId ) ;
2019-12-13 03:16:34 +02:00
const styles = styles_ ( props ) ;
useEffect ( ( ) = > {
async function fetchNotes() {
const result = [ ] ;
2020-03-14 01:46:14 +02:00
for ( const noteId of props . noteIds ) {
2019-12-13 03:16:34 +02:00
result . push ( await Note . load ( noteId ) ) ;
}
setNotes ( result ) ;
}
2020-11-25 16:40:25 +02:00
void fetchNotes ( ) ;
2019-12-13 03:16:34 +02:00
} , [ props . noteIds ] ) ;
const appApi = async ( ) = > {
return reg . syncTargetNextcloud ( ) . appApi ( ) ;
} ;
const buttonRow_click = ( ) = > {
props . onClose ( ) ;
} ;
2020-11-12 21:13:28 +02:00
const copyLinksToClipboard = ( shares : SharesMap ) = > {
2019-12-13 03:16:34 +02:00
const links = [ ] ;
for ( const n in shares ) links . push ( shares [ n ] . _url ) ;
clipboard . writeText ( links . join ( '\n' ) ) ;
} ;
const shareLinkButton_click = async ( ) = > {
let hasSynced = false ;
let tryToSync = false ;
while ( true ) {
try {
if ( tryToSync ) {
2019-12-17 14:45:57 +02:00
setSharesState ( 'synchronizing' ) ;
2020-03-13 19:42:50 +02:00
await reg . waitForSyncFinishedThenSync ( ) ;
2019-12-13 03:16:34 +02:00
tryToSync = false ;
hasSynced = true ;
}
2019-12-17 14:45:57 +02:00
setSharesState ( 'creating' ) ;
2019-12-13 03:16:34 +02:00
const api = await appApi ( ) ;
const syncTargetId = api . syncTargetId ( Setting . toPlainObject ( ) ) ;
const newShares = Object . assign ( { } , shares ) ;
2019-12-17 14:45:57 +02:00
let sharedStatusChanged = false ;
2019-12-13 03:16:34 +02:00
for ( const note of notes ) {
const result = await api . exec ( 'POST' , 'shares' , {
syncTargetId : syncTargetId ,
noteId : note.id ,
} ) ;
newShares [ note . id ] = result ;
2019-12-17 14:45:57 +02:00
const changed = await BaseItem . updateShareStatus ( note , true ) ;
if ( changed ) sharedStatusChanged = true ;
2019-12-13 03:16:34 +02:00
}
setShares ( newShares ) ;
2019-12-17 14:45:57 +02:00
if ( sharedStatusChanged ) {
setSharesState ( 'synchronizing' ) ;
2020-03-13 19:42:50 +02:00
await reg . waitForSyncFinishedThenSync ( ) ;
2019-12-17 14:45:57 +02:00
setSharesState ( 'creating' ) ;
}
2019-12-13 03:16:34 +02:00
copyLinksToClipboard ( newShares ) ;
setSharesState ( 'created' ) ;
} catch ( error ) {
if ( error . code === 404 && ! hasSynced ) {
reg . logger ( ) . info ( 'ShareNoteDialog: Note does not exist on server - trying to sync it.' , error ) ;
tryToSync = true ;
continue ;
}
reg . logger ( ) . error ( 'ShareNoteDialog: Cannot share note:' , error ) ;
setSharesState ( 'idle' ) ;
alert ( JoplinServerApi . connectionErrorMessage ( error ) ) ;
}
break ;
}
} ;
2020-11-12 21:13:28 +02:00
const removeNoteButton_click = ( event : any ) = > {
2019-12-13 03:16:34 +02:00
const newNotes = [ ] ;
for ( let i = 0 ; i < notes . length ; i ++ ) {
const n = notes [ i ] ;
if ( n . id === event . noteId ) continue ;
newNotes . push ( n ) ;
}
setNotes ( newNotes ) ;
} ;
2020-11-12 21:13:28 +02:00
const renderNote = ( note : any ) = > {
2019-12-13 03:16:34 +02:00
const removeButton = notes . length <= 1 ? null : (
< button onClick = { ( ) = > removeNoteButton_click ( { noteId : note.id } ) } style = { styles . noteRemoveButton } >
< i style = { styles . noteRemoveButtonIcon } className = { 'fa fa-times' } > < / i >
< / button >
) ;
return (
< div key = { note . id } style = { styles . note } >
< span style = { styles . noteTitle } > { note . title } < / span > { removeButton }
< / div >
) ;
} ;
2020-11-12 21:13:28 +02:00
const renderNoteList = ( notes : any ) = > {
2019-12-13 03:16:34 +02:00
const noteComps = [ ] ;
2020-03-14 01:46:14 +02:00
for ( const noteId of Object . keys ( notes ) ) {
2019-12-13 03:16:34 +02:00
noteComps . push ( renderNote ( notes [ noteId ] ) ) ;
}
return < div style = { styles . noteList } > { noteComps } < / div > ;
} ;
2020-11-12 21:13:28 +02:00
const statusMessage = ( sharesState : string ) : string = > {
2019-12-17 14:45:57 +02:00
if ( sharesState === 'synchronizing' ) return _ ( 'Synchronising...' ) ;
2019-12-13 03:16:34 +02:00
if ( sharesState === 'creating' ) return _n ( 'Generating link...' , 'Generating links...' , noteCount ) ;
if ( sharesState === 'created' ) return _n ( 'Link has been copied to clipboard!' , 'Links have been copied to clipboard!' , noteCount ) ;
return '' ;
} ;
2019-12-17 14:45:57 +02:00
const encryptionWarningMessage = ! Setting . value ( 'encryption.enabled' ) ? null : < div style = { theme . textStyle } > { _ ( 'Note: When a note is shared, it will no longer be encrypted on the server.' ) } < / div > ;
2019-12-13 03:16:34 +02:00
const rootStyle = Object . assign ( { } , theme . dialogBox ) ;
rootStyle . width = '50%' ;
return (
< div style = { theme . dialogModalLayer } >
< div style = { rootStyle } >
< div style = { theme . dialogTitle } > { _ ( 'Share Notes' ) } < / div >
{ renderNoteList ( notes ) }
2019-12-17 14:45:57 +02:00
< button disabled = { [ 'creating' , 'synchronizing' ] . indexOf ( sharesState ) >= 0 } style = { styles . copyShareLinkButton } onClick = { shareLinkButton_click } > { _n ( 'Copy Shareable Link' , 'Copy Shareable Links' , noteCount ) } < / button >
2019-12-13 03:16:34 +02:00
< div style = { theme . textStyle } > { statusMessage ( sharesState ) } < / div >
2019-12-17 19:06:55 +02:00
{ encryptionWarningMessage }
2020-09-15 15:01:07 +02:00
< DialogButtonRow themeId = { props . themeId } onClick = { buttonRow_click } okButtonShow = { false } cancelButtonLabel = { _ ( 'Close' ) } / >
2019-12-13 03:16:34 +02:00
< / div >
< / div >
) ;
}