You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-10 22:11:50 +02:00
This commit is contained in:
@@ -1086,6 +1086,7 @@ packages/lib/components/EncryptionConfigScreen/utils.test.js
|
||||
packages/lib/components/EncryptionConfigScreen/utils.js
|
||||
packages/lib/components/shared/NoteList/getEmptyFolderMessage.js
|
||||
packages/lib/components/shared/NoteRevisionViewer/getHelpMessage.js
|
||||
packages/lib/components/shared/NoteRevisionViewer/useDeleteHistoryClick.js
|
||||
packages/lib/components/shared/SamlShared.js
|
||||
packages/lib/components/shared/ShareNoteDialog/onUnshareNoteClick.js
|
||||
packages/lib/components/shared/ShareNoteDialog/types.js
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1061,6 +1061,7 @@ packages/lib/components/EncryptionConfigScreen/utils.test.js
|
||||
packages/lib/components/EncryptionConfigScreen/utils.js
|
||||
packages/lib/components/shared/NoteList/getEmptyFolderMessage.js
|
||||
packages/lib/components/shared/NoteRevisionViewer/getHelpMessage.js
|
||||
packages/lib/components/shared/NoteRevisionViewer/useDeleteHistoryClick.js
|
||||
packages/lib/components/shared/SamlShared.js
|
||||
packages/lib/components/shared/ShareNoteDialog/onUnshareNoteClick.js
|
||||
packages/lib/components/shared/ShareNoteDialog/types.js
|
||||
|
@@ -24,6 +24,7 @@ import useMarkupToHtml from './hooks/useMarkupToHtml';
|
||||
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
||||
import { ScrollbarSize } from '@joplin/lib/models/settings/builtInMetadata';
|
||||
import { focus } from '@joplin/lib/utils/focusHandler';
|
||||
import useDeleteHistoryClick from '@joplin/lib/components/shared/NoteRevisionViewer/useDeleteHistoryClick';
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
@@ -89,6 +90,7 @@ const NoteRevisionViewerComponent: React.FC<Props> = ({ themeId, noteId, onBack,
|
||||
const [revisions, setRevisions] = useState<RevisionEntity[]>([]);
|
||||
const [currentRevId, setCurrentRevId] = useState('');
|
||||
const [restoring, setRestoring] = useState(false);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
|
||||
const note = useNoteContent(
|
||||
viewerRef, currentRevId, revisions, themeId, customCss, scrollbarSize, fontFamily,
|
||||
@@ -111,6 +113,17 @@ const NoteRevisionViewerComponent: React.FC<Props> = ({ themeId, noteId, onBack,
|
||||
await shim.showMessageBox(RevisionService.instance().restoreSuccessMessage(note), { type: MessageBoxType.Info });
|
||||
}, [note]);
|
||||
|
||||
const resetScreenState = useCallback(() => {
|
||||
setRevisions([]);
|
||||
setCurrentRevId(null);
|
||||
}, []);
|
||||
|
||||
const deleteHistoryButton_onClick = useDeleteHistoryClick({
|
||||
noteId: note?.id,
|
||||
setDeleting,
|
||||
resetScreenState,
|
||||
});
|
||||
|
||||
const backButton_click = useCallback(() => {
|
||||
if (onBack) onBack();
|
||||
}, [onBack]);
|
||||
@@ -169,6 +182,7 @@ const NoteRevisionViewerComponent: React.FC<Props> = ({ themeId, noteId, onBack,
|
||||
}
|
||||
|
||||
const restoreButtonTitle = _('Restore');
|
||||
const deleteHistoryButtonTitle = _('Delete history');
|
||||
const helpMessage = getHelpMessage(restoreButtonTitle);
|
||||
|
||||
const titleInput = (
|
||||
@@ -183,6 +197,9 @@ const NoteRevisionViewerComponent: React.FC<Props> = ({ themeId, noteId, onBack,
|
||||
<button disabled={!revisions.length || restoring} onClick={importButton_onClick} className='restore'style={{ ...theme.buttonStyle, marginLeft: 10, height: theme.inputStyle.height }}>
|
||||
{restoreButtonTitle}
|
||||
</button>
|
||||
<button disabled={!revisions.length || deleting} onClick={deleteHistoryButton_onClick} className='deleteHistory'style={{ ...theme.buttonStyle, marginLeft: 10, height: theme.inputStyle.height }}>
|
||||
{deleteHistoryButtonTitle}
|
||||
</button>
|
||||
<HelpButton tip={helpMessage} id="noteRevisionHelpButton" onClick={helpButton_onClick} />
|
||||
</div>
|
||||
);
|
||||
|
@@ -7,7 +7,7 @@ import Revision from '@joplin/lib/models/Revision';
|
||||
import BaseModel, { ModelType } from '@joplin/lib/BaseModel';
|
||||
import { IconButton, Text } from 'react-native-paper';
|
||||
import Dropdown from '../Dropdown';
|
||||
import ScreenHeader from '../ScreenHeader';
|
||||
import ScreenHeader, { MenuOptionType } from '../ScreenHeader';
|
||||
import { formatMsToLocal } from '@joplin/utils/time';
|
||||
import { useCallback, useContext, useMemo, useState } from 'react';
|
||||
import { PrimaryButton } from '../buttons';
|
||||
@@ -21,6 +21,7 @@ import shim, { MessageBoxType } from '@joplin/lib/shim';
|
||||
import { themeStyle } from '../global-style';
|
||||
import getHelpMessage from '@joplin/lib/components/shared/NoteRevisionViewer/getHelpMessage';
|
||||
import { DialogContext } from '../DialogManager';
|
||||
import useDeleteHistoryClick from '@joplin/lib/components/shared/NoteRevisionViewer/useDeleteHistoryClick';
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
@@ -113,6 +114,7 @@ const NoteRevisionViewer: React.FC<Props> = props => {
|
||||
const [currentRevisionId, setCurrentRevisionId] = useState<string>('');
|
||||
const { note, resources } = useRevisionNote(revisions, currentRevisionId);
|
||||
const [initialScroll, setInitialScroll] = useState(0);
|
||||
const [hasRevisions, setHasRevisions] = useState(false);
|
||||
|
||||
const options = useMemo(() => {
|
||||
const result = [];
|
||||
@@ -123,6 +125,7 @@ const NoteRevisionViewer: React.FC<Props> = props => {
|
||||
value: revision.id,
|
||||
});
|
||||
}
|
||||
setHasRevisions(result.length > 0);
|
||||
return result;
|
||||
}, [revisions]);
|
||||
|
||||
@@ -142,6 +145,31 @@ const NoteRevisionViewer: React.FC<Props> = props => {
|
||||
}
|
||||
}, [note]);
|
||||
|
||||
const resetScreenState = useCallback(() => {
|
||||
setCurrentRevisionId(null);
|
||||
setHasRevisions(false);
|
||||
revisions.length = 0;
|
||||
options.length = 0;
|
||||
}, [revisions, options]);
|
||||
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const deleteHistory_onPress = useDeleteHistoryClick({
|
||||
noteId,
|
||||
setDeleting,
|
||||
resetScreenState,
|
||||
});
|
||||
|
||||
const disableDeleteHistory = deleting || !hasRevisions;
|
||||
const menuOptions = useMemo(() => {
|
||||
const output: MenuOptionType[] = [{
|
||||
title: _('Delete history'),
|
||||
onPress: deleteHistory_onPress,
|
||||
disabled: disableDeleteHistory,
|
||||
}];
|
||||
|
||||
return output;
|
||||
}, [deleteHistory_onPress, disableDeleteHistory]);
|
||||
|
||||
const restoreButtonTitle = _('Restore');
|
||||
const helpMessageText = getHelpMessage(restoreButtonTitle);
|
||||
const dialogs = useContext(DialogContext);
|
||||
@@ -160,7 +188,7 @@ const NoteRevisionViewer: React.FC<Props> = props => {
|
||||
);
|
||||
|
||||
return <View style={styles.root}>
|
||||
<ScreenHeader title={_('Note history')} />
|
||||
<ScreenHeader menuOptions={menuOptions} title={_('Note history')} />
|
||||
<View style={styles.controls}>
|
||||
<Text variant='labelLarge'>{dropdownLabelText}</Text>
|
||||
<Dropdown
|
||||
|
@@ -0,0 +1,36 @@
|
||||
import { _ } from '../../../locale';
|
||||
import Revision from '../../../models/Revision';
|
||||
import shim, { MessageBoxType } from '../../../shim';
|
||||
const { useCallback } = shim.react();
|
||||
|
||||
interface Props {
|
||||
noteId?: string;
|
||||
setDeleting(deleting: boolean): void;
|
||||
resetScreenState(): void;
|
||||
}
|
||||
|
||||
const useDeleteHistoryClick = ({
|
||||
noteId, setDeleting, resetScreenState,
|
||||
}: Props) => {
|
||||
return useCallback(async () => {
|
||||
if (!noteId) return;
|
||||
const response = await shim.showMessageBox(_('Are you sure you want to delete all history for this note? This cannot be undone.'), {
|
||||
title: _('Warning'),
|
||||
buttons: [_('Yes'), _('No')],
|
||||
type: MessageBoxType.Confirm,
|
||||
});
|
||||
|
||||
if (response === 0) {
|
||||
setDeleting(true);
|
||||
try {
|
||||
await Revision.deleteHistoryForNote(noteId);
|
||||
await shim.showMessageBox(_('Note history has been deleted.'), { type: MessageBoxType.Info });
|
||||
} finally {
|
||||
setDeleting(false);
|
||||
}
|
||||
resetScreenState();
|
||||
}
|
||||
}, [noteId, setDeleting, resetScreenState]);
|
||||
};
|
||||
|
||||
export default useDeleteHistoryClick;
|
@@ -373,6 +373,15 @@ export default class Revision extends BaseItem {
|
||||
}
|
||||
}
|
||||
|
||||
public static async deleteHistoryForNote(noteId: string) {
|
||||
const revisions: RevisionEntity[] = await this.modelSelectAll(
|
||||
'SELECT id FROM revisions WHERE item_type = ? AND item_id = ? ORDER BY item_updated_time DESC',
|
||||
[ModelType.Note, noteId],
|
||||
);
|
||||
|
||||
await this.batchDelete(revisions.map(item => item.id), { sourceDescription: 'Revision.deleteHistoryForNote' });
|
||||
}
|
||||
|
||||
public static async revisionExists(itemType: ModelType, itemId: string, updatedTime: number) {
|
||||
const existingRev = await Revision.latestRevision(itemType, itemId);
|
||||
return existingRev && existingRev.item_updated_time === updatedTime;
|
||||
|
Reference in New Issue
Block a user