-
-
Notifications
You must be signed in to change notification settings - Fork 109
/
sync.ts
107 lines (86 loc) · 2.76 KB
/
sync.ts
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
import { FragmentEvent } from './fragments'
import { storage, setHistoryState } from './utils'
export interface BespokeSyncOption {
key?: string
}
export interface BespokeSyncState {
reference: number
index: number
fragmentIndex: number
}
const bespokeSync = (opts: BespokeSyncOption = {}) => {
const key =
opts.key ||
window.history.state?.marpBespokeSyncKey ||
Math.random().toString(36).slice(2)
const storageKey = `bespoke-marp-sync-${key}`
setHistoryState({ marpBespokeSyncKey: key })
const getState = (): Partial<BespokeSyncState> => {
const stateJSON = storage.get(storageKey)
if (!stateJSON) return Object.create(null)
return JSON.parse(stateJSON)
}
const setState = (
updater: (prevState: Partial<BespokeSyncState>) => Partial<BespokeSyncState>
) => {
const currentState = getState()
const newState = { ...currentState, ...updater(currentState) }
storage.set(storageKey, JSON.stringify(newState))
return newState
}
const initialize = () => {
window.removeEventListener('pageshow', initialize)
setState((prev) => ({ reference: (prev.reference || 0) + 1 }))
}
return (deck) => {
initialize()
Object.defineProperty(deck, 'syncKey', {
value: key,
enumerable: true,
})
// Update storage value to store current page and fragment index
// (Wrap by setTimeout to skip fragment event for initialization)
let updateFragment = true
setTimeout(() => {
deck.on('fragment', (e: FragmentEvent) => {
if (updateFragment)
setState(() => ({ index: e.index, fragmentIndex: e.fragmentIndex }))
})
}, 0)
// Listen "storage" event
window.addEventListener('storage', (e) => {
if (e.key === storageKey && e.oldValue && e.newValue) {
const prev: Partial<BespokeSyncState> = JSON.parse(e.oldValue)
const current: Partial<BespokeSyncState> = JSON.parse(e.newValue)
if (
prev.index !== current.index ||
prev.fragmentIndex !== current.fragmentIndex
) {
try {
updateFragment = false
deck.slide(current.index, {
fragment: current.fragmentIndex,
forSync: true,
})
} finally {
updateFragment = true
}
}
}
})
const destructor = () => {
const { reference } = getState()
if (reference === undefined || reference <= 1) {
storage.remove(storageKey)
} else {
setState(() => ({ reference: reference - 1 }))
}
}
window.addEventListener('pagehide', (e: PageTransitionEvent) => {
if (e.persisted) window.addEventListener('pageshow', initialize)
destructor()
})
deck.on('destroy', destructor)
}
}
export default bespokeSync