Created
April 17, 2024 19:06
-
-
Save matt-e-king/3ac2127025cf5dbc03001d9b21c1984a to your computer and use it in GitHub Desktop.
Hypothesis Embed and sock in Vue app
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// HypothesisEmbed.vue | |
// This is the component that mounts on the page that has the annotations | |
// It's important to destroy the client when componenent is unmounted | |
// because if the component remounts, it creates duplicate annotations in the DOM | |
<template> | |
<div | |
id="HypothesisEmbed" | |
class="HypothesisEmbed" | |
/> | |
</template> | |
<script> | |
const scriptId = 'HypothesisEmbed__script' | |
const scriptSrc = 'https://hypothes.is/embed.js' | |
export default { | |
name: 'HypothesisEmbed', | |
mounted () { | |
if (window) { | |
if (!window.document.getElementById(scriptId)) { | |
const script = document.createElement('script') | |
script.id = scriptId | |
script.async = true | |
script.src = scriptSrc | |
window.document.getElementById('HypothesisEmbed').appendChild(script) | |
} | |
} | |
}, | |
beforeDestroy () { | |
if (window && window.document) { | |
const annotatorLink = window.document.querySelector( | |
'link[type="application/annotator+html"]' | |
) | |
if (annotatorLink) { | |
// Dispatch a 'destroy' event which is handled by the code in | |
// annotator/main.js to remove the client. | |
const destroyEvent = new Event('destroy') | |
annotatorLink.dispatchEvent(destroyEvent) | |
} | |
} | |
} | |
} | |
</script> | |
// HypothesisSocket.js | |
// This component is responsible for openining the socket to detect udpates | |
// This socket currently fails on loop, and then eventually resolves, I haven't reviewed this code in a while | |
// replace process.env.HYPOTHESIS_KEY with your developer API key | |
import debounce from 'lodash.debounce' | |
export default { | |
name: 'hypothesis-socket', | |
watch: { | |
'$route.params.page'() { | |
this.sendDataDebounce.cancel() | |
this.sendDataDebounce() | |
} | |
}, | |
mounted () { | |
if ( | |
window | |
&& | |
( | |
!this.hypSocket | |
|| (this.hypSocket && (this.hypSocket.readyState === 2 || this.hypSocket.readyState === 3)) | |
) | |
) { | |
this.connectDebounce.cancel() | |
this.connectDebounce() | |
} | |
}, | |
data () { | |
return { | |
connectDebounce: debounce(this.connect, 750), | |
sendDataDebounce: debounce(this.sendData, 750), | |
retries: 0 | |
} | |
}, | |
methods: { | |
connect () { | |
this.hypSocket = new WebSocket(`wss://hypothes.is/ws?access_token=${process.env.HYPOTHESIS_KEY}`) | |
this.hypSocket.onopen = () => { | |
console.log('Grazing Hypothesis Socket open') | |
this.hypSocket.send(JSON.stringify({"type":"whoami","id":1})) | |
this.sendData() | |
} | |
this.hypSocket.onmessage = (e) => { | |
this.$emit('message', JSON.parse(e.data)) | |
} | |
this.hypSocket.onerror = (e) => { | |
console.error('Grazing Hypothesis Socket Error: ', e) | |
this.$emit('error', e) | |
this.$sentry.captureException(e) | |
// make sure the connection is not already CLOSING or CLOSED | |
if (this.hypSocket.readyState !== 2 || this.hypSocket.readyState !== 3) { | |
this.hypSocket.close() | |
} | |
} | |
this.hypSocket.onclose = (e) => { | |
const { wasClean, code } = e | |
if (this.retries <= 12) { | |
console.log('Grazing Hypothesis Socket is closed: ', e.reason) | |
// cancel any previous attempts to open connection and then reopen | |
this.connectDebounce.cancel() | |
// only reopen it if it wasn't a clean closure | |
if (!wasClean && code !== 1000) { | |
this.connectDebounce() | |
} else { | |
this.$emit('closed') | |
} | |
this.retries++ | |
} else { | |
const error = 'Grazing Hypothesis Socket reopen retry maximum' | |
this.$emit('closed') | |
this.$sentry.captureException(new Error(error)) | |
} | |
} | |
}, | |
sendData () { | |
if (this.hypSocket) { | |
this.hypSocket.send(JSON.stringify({ | |
"filter": { | |
"match_policy": "include_any", | |
"clauses": [ | |
{ | |
"field": "/uri", | |
"operator": "one_of", | |
"value": [window.location.href], | |
} | |
], | |
"actions": { | |
"create": true, | |
"update": true, | |
"delete": true | |
} | |
} | |
})) | |
} | |
} | |
}, | |
beforeDestroy () { | |
if (this.hypSocket) { | |
this.hypSocket.close(1000) | |
} | |
}, | |
render() {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment