import * as React from 'react'; import { useState, useEffect } from 'react'; import JoplinServerApi from '@joplin/lib/JoplinServerApi'; import { _, _n } from '@joplin/lib/locale'; import Note from '@joplin/lib/models/Note'; import Setting from '@joplin/lib/models/Setting'; import DialogButtonRow from './DialogButtonRow'; import { themeStyle, buildStyle } from '@joplin/lib/theme'; import { reg } from '@joplin/lib/registry'; import Dialog from './Dialog'; import DialogTitle from './DialogTitle'; import ShareService from '@joplin/lib/services/share/ShareService'; import { StateShare } from '@joplin/lib/services/share/reducer'; import { NoteEntity } from '@joplin/lib/services/database/types'; import Button from './Button/Button'; import { connect } from 'react-redux'; import { AppState } from '../app'; const { clipboard } = require('electron'); interface Props { themeId: number; noteIds: Array; onClose: Function; shares: StateShare[]; } function styles_(props: Props) { return buildStyle('ShareNoteDialog', props.themeId, (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: { ...theme.textStyle, 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 function ShareNoteDialog(props: Props) { console.info('Render ShareNoteDialog'); const [notes, setNotes] = useState([]); const [sharesState, setSharesState] = useState('unknown'); // const [shares, setShares] = useState({}); const noteCount = notes.length; const theme = themeStyle(props.themeId); const styles = styles_(props); useEffect(() => { void ShareService.instance().refreshShares(); }, []); useEffect(() => { async function fetchNotes() { const result = []; for (const noteId of props.noteIds) { result.push(await Note.load(noteId)); } setNotes(result); } void fetchNotes(); }, [props.noteIds]); const buttonRow_click = () => { props.onClose(); }; const copyLinksToClipboard = (shares: StateShare[]) => { const links = []; for (const share of shares) links.push(ShareService.instance().shareUrl(ShareService.instance().userId, share)); clipboard.writeText(links.join('\n')); }; const shareLinkButton_click = async () => { const service = ShareService.instance(); let hasSynced = false; let tryToSync = false; while (true) { try { if (tryToSync) { setSharesState('synchronizing'); await reg.waitForSyncFinishedThenSync(); tryToSync = false; hasSynced = true; } setSharesState('creating'); const newShares: StateShare[] = []; for (const note of notes) { const share = await service.shareNote(note.id); newShares.push(share); } setSharesState('synchronizing'); await reg.waitForSyncFinishedThenSync(); setSharesState('creating'); copyLinksToClipboard(newShares); setSharesState('created'); await ShareService.instance().refreshShares(); } 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 unshareNoteButton_click = async (event: any) => { await ShareService.instance().unshareNote(event.noteId); await ShareService.instance().refreshShares(); }; const renderNote = (note: NoteEntity) => { const unshareButton = !props.shares.find(s => s.note_id === note.id) ? null : ( // ); // const removeButton = notes.length <= 1 ? null : ( // // ); return (
{note.title}{unshareButton}
); }; const renderNoteList = (notes: any) => { const noteComps = []; for (const note of notes) { noteComps.push(renderNote(note)); } return
{noteComps}
; }; const statusMessage = (sharesState: string): string => { if (sharesState === 'synchronizing') return _('Synchronising...'); 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 ''; }; function renderEncryptionWarningMessage() { if (!Setting.value('encryption.enabled')) return null; return
{_('Note: When a note is shared, it will no longer be encrypted on the server.')}
; } function renderContent() { return (
{renderNoteList(notes)}
{statusMessage(sharesState)}
{renderEncryptionWarningMessage()}
); } return ( ); } const mapStateToProps = (state: AppState) => { return { shares: state.shareService.shares.filter(s => !!s.note_id), }; }; export default connect(mapStateToProps)(ShareNoteDialog as any);