-
Notifications
You must be signed in to change notification settings - Fork 4.4k
/
Copy pathindex.js
169 lines (148 loc) · 4.6 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/**
* External dependencies
*/
import { once, uniqueId, omit } from 'lodash';
/**
* WordPress dependencies
*/
import { useCallback, useEffect, useRef } from '@wordpress/element';
import { ifCondition } from '@wordpress/compose';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { parse } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import AutosaveMonitor from '../autosave-monitor';
import {
localAutosaveGet,
localAutosaveClear,
} from '../../store/controls';
const requestIdleCallback = window.requestIdleCallback ? window.requestIdleCallback : window.requestAnimationFrame;
/**
* Function which returns true if the current environment supports browser
* sessionStorage, or false otherwise. The result of this function is cached and
* reused in subsequent invocations.
*/
const hasSessionStorageSupport = once( () => {
try {
// Private Browsing in Safari 10 and earlier will throw an error when
// attempting to set into sessionStorage. The test here is intentional in
// causing a thrown error as condition bailing from local autosave.
window.sessionStorage.setItem( '__wpEditorTestSessionStorage', '' );
window.sessionStorage.removeItem( '__wpEditorTestSessionStorage' );
return true;
} catch ( error ) {
return false;
}
} );
/**
* Custom hook which manages the creation of a notice prompting the user to
* restore a local autosave, if one exists.
*/
function useAutosaveNotice() {
const {
postId,
getEditedPostAttribute,
hasRemoteAutosave,
} = useSelect( ( select ) => ( {
postId: select( 'core/editor' ).getCurrentPostId(),
getEditedPostAttribute: select( 'core/editor' ).getEditedPostAttribute,
hasRemoteAutosave: !! select( 'core/editor' ).getEditorSettings().autosave,
} ) );
const { createWarningNotice, removeNotice } = useDispatch( 'core/notices' );
const { editPost, resetEditorBlocks } = useDispatch( 'core/editor' );
useEffect( () => {
let localAutosave = localAutosaveGet( postId );
if ( ! localAutosave ) {
return;
}
try {
localAutosave = JSON.parse( localAutosave );
} catch ( error ) {
// Not usable if it can't be parsed.
return;
}
const { post_title: title, content, excerpt } = localAutosave;
const edits = { title, content, excerpt };
{
// Only display a notice if there is a difference between what has been
// saved and that which is stored in sessionStorage.
const hasDifference = Object.keys( edits ).some( ( key ) => {
return edits[ key ] !== getEditedPostAttribute( key );
} );
if ( ! hasDifference ) {
// If there is no difference, it can be safely ejected from storage.
localAutosaveClear( postId );
return;
}
}
if ( hasRemoteAutosave ) {
return;
}
const noticeId = uniqueId( 'wpEditorAutosaveRestore' );
createWarningNotice( __( 'The backup of this post in your browser is different from the version below.' ), {
id: noticeId,
actions: [
{
label: __( 'Restore the backup' ),
onClick() {
editPost( omit( edits, [ 'content' ] ) );
resetEditorBlocks( parse( edits.content ) );
removeNotice( noticeId );
},
},
],
} );
}, [ postId ] );
}
/**
* Custom hook which ejects a local autosave after a successful save occurs.
*/
function useAutosavePurge() {
const {
postId,
isDirty,
isAutosaving,
didError,
} = useSelect( ( select ) => ( {
postId: select( 'core/editor' ).getCurrentPostId(),
isDirty: select( 'core/editor' ).isEditedPostDirty(),
isAutosaving: select( 'core/editor' ).isAutosavingPost(),
didError: select( 'core/editor' ).didPostSaveRequestFail(),
} ) );
const lastIsDirty = useRef( isDirty );
const lastIsAutosaving = useRef( isAutosaving );
useEffect( () => {
if (
! didError && (
( lastIsAutosaving.current && ! isAutosaving ) ||
( lastIsDirty.current && ! isDirty )
)
) {
localAutosaveClear( postId );
}
lastIsDirty.current = isDirty;
lastIsAutosaving.current = isAutosaving;
}, [ isDirty, isAutosaving, didError ] );
}
function LocalAutosaveMonitor() {
const { __experimentalLocalAutosave } = useDispatch( 'core/editor' );
const autosave = useCallback( () => {
requestIdleCallback( __experimentalLocalAutosave );
}, [] );
useAutosaveNotice();
useAutosavePurge();
const { localAutosaveInterval } = useSelect( ( select ) => ( {
localAutosaveInterval: select( 'core/editor' )
.getEditorSettings().__experimentalLocalAutosaveInterval,
} ) );
return (
<AutosaveMonitor
interval={ localAutosaveInterval }
autosave={ autosave }
shouldThrottle
/>
);
}
export default ifCondition( hasSessionStorageSupport )( LocalAutosaveMonitor );