Skip to content
Snippets Groups Projects
Commit ee9e14c1 authored by Jack Reed's avatar Jack Reed
Browse files

Move LocalStorage plugin to a configurable plugin that is an adapter

parent dec7c25f
No related branches found
No related tags found
No related merge requests found
import mirador from 'mirador/dist/es/src/index'; import mirador from 'mirador/dist/es/src/index';
import { miradorAnnotationPlugin, localStorageAnnotationPlugin } from '../../src'; import { miradorAnnotationPlugin, externalStorageAnnotationPlugin } from '../../src';
import LocalStorageAdapter from '../../src/LocalStorageAdapter';
const config = { const config = {
annotation: {
adapter: (canvasId) => new LocalStorageAdapter(`localStorage://?canvasId=${canvasId}`),
},
id: 'demo', id: 'demo',
windows: [{ windows: [{
loadedManifest: 'https://purl.stanford.edu/sn904cj3429/iiif/manifest', loadedManifest: 'https://iiif.harvardartmuseums.org/manifests/object/299843',
}], }],
}; };
const miradorInstance = mirador.viewer(config, [ mirador.viewer(config, [
miradorAnnotationPlugin, miradorAnnotationPlugin,
localStorageAnnotationPlugin, externalStorageAnnotationPlugin,
]); ]);
...@@ -20,7 +20,9 @@ ...@@ -20,7 +20,9 @@
"test:coverage": "jest --coverage", "test:coverage": "jest --coverage",
"test:watch": "jest --watch" "test:watch": "jest --watch"
}, },
"dependencies": {}, "dependencies": {
"uuid": "^8.0.0"
},
"peerDependencies": { "peerDependencies": {
"@material-ui/core": "^4.9.13", "@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1", "@material-ui/icons": "^4.9.1",
......
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import { v4 as uuid } from 'uuid';
/** */ /** */
class AnnotationCreation extends Component { class AnnotationCreation extends Component {
/** */
constructor(props) {
super(props);
this.state = { annoBody: '' };
this.submitForm = this.submitForm.bind(this);
this.updateBody = this.updateBody.bind(this);
}
/** */
submitForm(e) {
e.preventDefault();
const { canvases, receiveAnnotation, config } = this.props;
const { annoBody } = this.state;
canvases.forEach((canvas) => {
const localStorageAdapter = config.annotation.adapter(canvas.id);
const anno = {
body: {
language: 'en',
type: 'TextualBody',
value: annoBody,
},
id: `https://example.org/iiif/book1/page/manifest/${uuid()}`,
motivation: 'commenting',
target: `${canvas.id}#xywh=200,500,1000,1000`,
type: 'Annotation',
};
const newAnnoPage = localStorageAdapter.create(anno);
receiveAnnotation(canvas.id, localStorageAdapter.annotationPageId, newAnnoPage);
});
}
/** */
updateBody(e) {
this.setState({ annoBody: e.target.value });
}
/** */ /** */
render() { render() {
const { annoBody } = this.state;
return ( return (
<div> <div>
Annotation creation <form onSubmit={this.submitForm}>
<TextField
multiline
rows={6}
value={annoBody}
onChange={this.updateBody}
/>
<Button>
Cancel
</Button>
<Button variant="contained" color="primary" type="submit">
Save
</Button>
</form>
</div> </div>
); );
} }
} }
AnnotationCreation.propTypes = {
canvases: PropTypes.arrayOf(
PropTypes.shape({ id: PropTypes.string, index: PropTypes.number }),
),
config: PropTypes.shape({
annotation: PropTypes.shape({
adapter: PropTypes.func,
}),
}).isRequired,
receiveAnnotation: PropTypes.func.isRequired,
};
AnnotationCreation.defaultProps = {
canvases: [],
};
export default AnnotationCreation; export default AnnotationCreation;
/** */
export default class LocalStorageAdapter {
/** */
constructor(annotationPageId) {
this.annotationPageId = annotationPageId;
}
/** */
create(annotation) {
const emptyAnnoPage = {
id: this.annotationPageId,
items: [],
type: 'AnnotationPage',
};
const annotationPage = this.all() || emptyAnnoPage;
annotationPage.items.push(annotation);
localStorage.setItem(this.annotationPageId, JSON.stringify(annotationPage));
return annotationPage;
}
/** */
// update(annoId) {
// // todo
// }
/** */
delete(annoId) {
const annotationPage = this.all();
if (annotationPage) {
annotationPage.items = annotationPage.items.filter((item) => item.id !== annoId);
}
localStorage.setItem(this.annotationPageId, JSON.stringify(annotationPage));
return annotationPage;
}
/** */
all() {
return JSON.parse(localStorage.getItem(this.annotationPageId));
}
}
export miradorAnnotationPlugin from './plugins/miradorAnnotationPlugin'; export miradorAnnotationPlugin from './plugins/miradorAnnotationPlugin';
export localStorageAnnotationPlugin from './plugins/localStorageAnnotationPlugin'; export externalStorageAnnotationPlugin from './plugins/externalStorageAnnotationPlugin';
...@@ -5,7 +5,7 @@ import { getVisibleCanvases } from 'mirador/dist/es/src/state/selectors/canvases ...@@ -5,7 +5,7 @@ import { getVisibleCanvases } from 'mirador/dist/es/src/state/selectors/canvases
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
/** */ /** */
class LocalStorageAnnotation extends Component { class ExternalStorageAnnotation extends Component {
/** */ /** */
constructor(props) { constructor(props) {
super(props); super(props);
...@@ -15,10 +15,6 @@ class LocalStorageAnnotation extends Component { ...@@ -15,10 +15,6 @@ class LocalStorageAnnotation extends Component {
/** */ /** */
componentDidMount() { componentDidMount() {
const { canvases } = this.props; const { canvases } = this.props;
const anno = [{"@context":"http://iiif.io/api/presentation/2/context.json","@type":"oa:Annotation","motivation":["oa:commenting"],"resource":[{"@type":"dctypes:Text","format":"text/html","chars":"<p>stuff is here</p>"}],"on":[{"@type":"oa:SpecificResource","full":"https://purl.stanford.edu/sn904cj3429/iiif/canvas/sn904cj3429_1","selector":{"@type":"oa:Choice","default":{"@type":"oa:FragmentSelector","value":"xywh=2321,4718,1604,1435"},"item":{"@type":"oa:SvgSelector","value":"<svg xmlns='http://www.w3.org/2000/svg'><path xmlns=\"http://www.w3.org/2000/svg\" d=\"M2321.22749,4717.73662h802.13042v0h802.13042v717.39547v717.39547h-802.13042h-802.13042v-717.39547z\" data-paper-data=\"{&quot;strokeWidth&quot;:1,&quot;rotation&quot;:0,&quot;deleteIcon&quot;:null,&quot;rotationIcon&quot;:null,&quot;group&quot;:null,&quot;editable&quot;:true,&quot;annotation&quot;:null}\" id=\"rectangle_934e692c-1773-4528-832e-f5c440c9f52c\" fill-opacity=\"0\" fill=\"#00bfff\" fill-rule=\"nonzero\" stroke=\"#00bfff\" stroke-width=\"1\" stroke-linecap=\"butt\" stroke-linejoin=\"miter\" stroke-miterlimit=\"10\" stroke-dasharray=\"\" stroke-dashoffset=\"0\" font-family=\"none\" font-weight=\"none\" font-size=\"none\" text-anchor=\"none\" style=\"mix-blend-mode: normal\"/></svg>"}},"within":{"@id":"https://purl.stanford.edu/sn904cj3429/iiif/manifest","@type":"sc:Manifest"}}],"@id":"f0229551-4d8a-4f2e-a891-dcb2149268bb"}]
localStorage.setItem('https://purl.stanford.edu/sn904cj3429/iiif/canvas/sn904cj3429_1', JSON.stringify(anno));
this.retrieveAnnotations(canvases); this.retrieveAnnotations(canvases);
} }
...@@ -34,18 +30,13 @@ class LocalStorageAnnotation extends Component { ...@@ -34,18 +30,13 @@ class LocalStorageAnnotation extends Component {
/** */ /** */
retrieveAnnotations(canvases) { retrieveAnnotations(canvases) {
const { receiveAnnotation } = this.props; const { config, receiveAnnotation } = this.props;
console.log('retrieve');
canvases.forEach((canvas) => { canvases.forEach((canvas) => {
const annos = JSON.parse(localStorage.getItem(canvas.id)); const storageAdapter = config.annotation.adapter(canvas.id);
if (annos) { const annoPage = storageAdapter.all();
const annoListId = `${canvas.id}/localStorage/annoList`; if (annoPage) {
const annoList = { receiveAnnotation(canvas.id, storageAdapter.annotationPageId, annoPage);
'@id': annoListId,
'@type': 'sc:AnnotationList',
resources: annos,
};
receiveAnnotation(canvas.id, annoListId, annoList);
} }
}); });
} }
...@@ -61,10 +52,15 @@ class LocalStorageAnnotation extends Component { ...@@ -61,10 +52,15 @@ class LocalStorageAnnotation extends Component {
} }
} }
LocalStorageAnnotation.propTypes = { ExternalStorageAnnotation.propTypes = {
canvases: PropTypes.arrayOf( canvases: PropTypes.arrayOf(
PropTypes.shape({ id: PropTypes.string, index: PropTypes.number }), PropTypes.shape({ id: PropTypes.string, index: PropTypes.number }),
), ),
config: PropTypes.shape({
annotation: PropTypes.shape({
adapter: PropTypes.func,
}),
}).isRequired,
receiveAnnotation: PropTypes.func.isRequired, receiveAnnotation: PropTypes.func.isRequired,
TargetComponent: PropTypes.oneOfType([ TargetComponent: PropTypes.oneOfType([
PropTypes.func, PropTypes.func,
...@@ -73,7 +69,7 @@ LocalStorageAnnotation.propTypes = { ...@@ -73,7 +69,7 @@ LocalStorageAnnotation.propTypes = {
targetProps: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types targetProps: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
}; };
LocalStorageAnnotation.defaultProps = { ExternalStorageAnnotation.defaultProps = {
canvases: [], canvases: [],
}; };
...@@ -91,7 +87,7 @@ function mapStateToProps(state, { targetProps }) { ...@@ -91,7 +87,7 @@ function mapStateToProps(state, { targetProps }) {
} }
export default { export default {
component: LocalStorageAnnotation, component: ExternalStorageAnnotation,
mapDispatchToProps, mapDispatchToProps,
mapStateToProps, mapStateToProps,
mode: 'wrap', mode: 'wrap',
......
...@@ -2,6 +2,7 @@ import React, { Component } from 'react'; ...@@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import * as actions from 'mirador/dist/es/src/state/actions'; import * as actions from 'mirador/dist/es/src/state/actions';
import Button from '@material-ui/core/Button'; import Button from '@material-ui/core/Button';
import { getVisibleCanvases } from 'mirador/dist/es/src/state/selectors/canvases';
import AnnotationCreation from '../AnnotationCreation'; import AnnotationCreation from '../AnnotationCreation';
/** */ /** */
...@@ -14,10 +15,16 @@ class MiradorAnnotation extends Component { ...@@ -14,10 +15,16 @@ class MiradorAnnotation extends Component {
/** */ /** */
openCreateAnnotationCompanionWindow(e) { openCreateAnnotationCompanionWindow(e) {
const { addCompanionWindow } = this.props; const {
addCompanionWindow, canvases, receiveAnnotation, config,
} = this.props;
addCompanionWindow('custom', { addCompanionWindow('custom', {
children: ( children: (
<AnnotationCreation /> <AnnotationCreation
canvases={canvases}
receiveAnnotation={receiveAnnotation}
config={config}
/>
), ),
position: 'right', position: 'right',
title: 'New annotation', title: 'New annotation',
...@@ -44,6 +51,15 @@ class MiradorAnnotation extends Component { ...@@ -44,6 +51,15 @@ class MiradorAnnotation extends Component {
MiradorAnnotation.propTypes = { MiradorAnnotation.propTypes = {
addCompanionWindow: PropTypes.func.isRequired, addCompanionWindow: PropTypes.func.isRequired,
canvases: PropTypes.arrayOf(
PropTypes.shape({ id: PropTypes.string, index: PropTypes.number }),
),
config: PropTypes.shape({
annotation: PropTypes.shape({
adapter: PropTypes.func,
}),
}).isRequired,
receiveAnnotation: PropTypes.func.isRequired,
TargetComponent: PropTypes.oneOfType([ TargetComponent: PropTypes.oneOfType([
PropTypes.func, PropTypes.func,
PropTypes.node, PropTypes.node,
...@@ -51,16 +67,33 @@ MiradorAnnotation.propTypes = { ...@@ -51,16 +67,33 @@ MiradorAnnotation.propTypes = {
targetProps: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types targetProps: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
}; };
MiradorAnnotation.defaultProps = {
canvases: [],
};
/** */ /** */
const mapDispatchToProps = (dispatch, props) => ({ const mapDispatchToProps = (dispatch, props) => ({
addCompanionWindow: (content, additionalProps) => dispatch( addCompanionWindow: (content, additionalProps) => dispatch(
actions.addCompanionWindow(props.targetProps.windowId, { content, ...additionalProps }), actions.addCompanionWindow(props.targetProps.windowId, { content, ...additionalProps }),
), ),
receiveAnnotation: (targetId, id, annotation) => dispatch(
actions.receiveAnnotation(targetId, id, annotation),
),
}); });
/** */
function mapStateToProps(state, { targetProps }) {
return {
canvases: getVisibleCanvases(state, { windowId: targetProps.windowId }),
config: state.config,
};
}
export default { export default {
component: MiradorAnnotation, component: MiradorAnnotation,
mapDispatchToProps, mapDispatchToProps,
mapStateToProps,
mode: 'wrap', mode: 'wrap',
target: 'AnnotationSettings', target: 'AnnotationSettings',
}; };
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment