2019-12-13 03:16:34 +02:00
import * as React from 'react' ;
import { useState , useEffect } from 'react' ;
import JoplinServerApi from '../lib/JoplinServerApi' ;
const { _ , _n } = require ( 'lib/locale.js' ) ;
const { themeStyle , buildStyle } = require ( '../theme.js' ) ;
const DialogButtonRow = require ( './DialogButtonRow.min' ) ;
const Note = require ( 'lib/models/Note' ) ;
const Setting = require ( 'lib/models/Setting' ) ;
2019-12-17 14:45:57 +02:00
const BaseItem = require ( 'lib/models/BaseItem' ) ;
2019-12-13 03:16:34 +02:00
const { reg } = require ( 'lib/registry.js' ) ;
const { clipboard } = require ( 'electron' ) ;
interface ShareNoteDialogProps {
theme : number ,
noteIds : Array < string > ,
onClose : Function ,
}
interface SharesMap {
[ key : string ] : any ;
}
function styles_ ( props :ShareNoteDialogProps ) {
return buildStyle ( 'ShareNoteDialog' , props . theme , ( theme :any ) = > {
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 ,
} ,
} ;
} ) ;
}
export default function ShareNoteDialog ( props :ShareNoteDialogProps ) {
console . info ( 'Render ShareNoteDialog' ) ;
const [ notes , setNotes ] = useState < any [ ] > ( [ ] ) ;
const [ sharesState , setSharesState ] = useState < string > ( 'unknown' ) ;
const [ shares , setShares ] = useState < SharesMap > ( { } ) ;
const noteCount = notes . length ;
const theme = themeStyle ( props . theme ) ;
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 ) ;
}
fetchNotes ( ) ;
} , [ props . noteIds ] ) ;
const appApi = async ( ) = > {
return reg . syncTargetNextcloud ( ) . appApi ( ) ;
} ;
const buttonRow_click = ( ) = > {
props . onClose ( ) ;
} ;
const copyLinksToClipboard = ( shares :SharesMap ) = > {
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 ;
}
} ;
const removeNoteButton_click = ( event :any ) = > {
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 ) ;
} ;
const renderNote = ( note :any ) = > {
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 >
) ;
} ;
const renderNoteList = ( notes :any ) = > {
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 > ;
} ;
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 }
2019-12-13 03:16:34 +02:00
< DialogButtonRow theme = { props . theme } onClick = { buttonRow_click } okButtonShow = { false } cancelButtonLabel = { _ ( 'Close' ) } / >
< / div >
< / div >
) ;
}