Closed aeschylus closed 3 years ago
Event pub/subs that are needed by Harvard's annotation tool: #1865
I think the idea of event types can help us to overcome the problem of event notification that came up in the recent comunity call.
Here is the problem (please correct me if I got it wrong): Adopters to Mirador should be able to listen to actions/changes in the store by providing custom reducers through the plugin API. Doing so, they can react on certain action types. But there is no easy way to find out what exactly has changed.
For example, a adopter wants to know if a window with a certain manifest has been opened. She can intercept a OPEN_WINDOW action in her custom reducer. But the manifest URL may not be provided in this type of action. So she has to digg in the state and that implies a knowledge of the internal structure of the state.
With event types we can provide the adopters with data that we consider as usefull for the event. In case of the open window example we could pass the manifest url and canvas index:
// in an event library
export function newWindowOpened(manifestUrl, canvasIndex) {
return { type: 'EVENT_NEW_WINDOW_OPENED', manifestUrl, canvasIndex }
}
// in an action creator (a thunk here)
export function openNewWindow(manifestId) {
return function (dispatch, getState) {
const windowId = dispatch(actions.createWindow({ manifestId })).id
const { manifestUrl } = getState().manifests[manifestId]
const { canvasIndex } = getState().windows[windowId]
// fire the event
dispatch(events.newWindowOpened(manifestUrl, canvasIndex))
}
}
Event types were only for information and hold some data, but they wouldn't have any effect on the state.
This way, we could precisely determine at which point we consider an event finalized and ready to fire. We also would not have to care about whether our action type cascades match the needs of the adopters. We would have an isolated event API.
@charbugs points out a needed revision of the plugin system to also allow plugins to be passed in on instantiation of the viewer (both in react and in the API-based functional initiation) https://github.com/ProjectMirador/mirador/issues/2082#issuecomment-470619573
I've added this as a requirement to the documentation ticket at the top of this thread, and created a more general ticket for figuring that out here: https://github.com/ProjectMirador/mirador/issues/2082#issuecomment-470619573
I wanted to chime here just to add my own outside impression of how important the developer API experience is. (These notes are also intended to be a summary of the discussion @aeschylus @sdellis and I @jeffreycwitt had on Friday April 12 as we experimented with Mirador Imports and Plugin Creations.
As Mirador3 progress and we now have a bundle we can import, people like me and others outside the core development team are able to start consuming the Mirador library.
One benefit of treating Mirador3 as react component is how easy it is (or should be) to bring Mirador into other react applications.
I've followed @mejackreed instructions for doing this, and it looks like @gigamorph has does this as well here: https://github.com/ProjectMirador/mirador/wiki/M3-Embedding-in-Another-Environment
A couple notes about this:
First, the general import seems to work well, but I think it should be noted that the import pattern is a little different than most react third party libraries. The "react" intuition would, I think, be to import "Mirador" from "mirador" and then be able to drop the Mirador component into the desired place. The need to reconstruct the "provider" and "store" seems like an un-intuitive step that will be a hang-up for a lot of people downstream. In a sense, to integrate Mirador into a react app, it seems like we have to rebuild part of the Mirador itself, basically repeat some of the code steps found in https://github.com/ProjectMirador/mirador/blob/master/src/lib/MiradorViewer.js#L28-L32. Maybe this is necessary, but I also feels kind of redundant.
Second, while the above is more an aesthetic or comfort problem, the current embed instructions and Plugin system seems to make it difficult or even impossible to embed Mirador into a wrapping react app with accompanying plugins.
If one is building a react application, in which they want to embed Mirador as a third part react app, but also want to instantiate that Mirador instance with a certain set of plugins (or even dynamically construct Mirador with a certain said of plugins), there doesn't seem to be a way to do this.
I would imagine doing something like:
import "Mirador" from "mirador"
import "PluginOne" from "plugin-one"
import "PluginTwo" from "plugin-two"
render(){
return (
<div>
<Mirador plugins={[Plugin1, Plugin2]}>
)
}
However at present it seem like the only way to add plugins is through the functional API that calls MiradorViewer.
The only way then that I can see to add several plugins is to build a new Mirador Bundle with accompanying plugins and then import my custom Mirador Fork into my larger react app wrapper.
Perhaps that is possible, but it is inconvenient. Now I can no longer import mirador and the plugins and I want directly from their maintained repos. I have to build an intermediate Mirador bundle and maintain this as well as the app I'm trying to build. It just seems like this is creating some friction that will make for a slightly frustrating developer experience down the road.
(As I think about it now, maybe it would be possible to simply import the PluginProvider and then wrap just as the provider was imported. If that would work, then my second concern would mostly go away, and would be left only with the aesthetic or ease problem described in number 1 above, namely, that it takes several imports and detailed component arrangements to actually embed the mirador react component.)
@jeffreycwitt There is another way to embedd Mirador in a react application that is not covered in the wiki page. Not sure, but I think it solves the issue with plugins you described. Here's an example: https://codesandbox.io/s/4x4vn4o4m4
In general, I agree that there is work to do to make the embedding of Mirador more convenient. Please note that we want to support different usage contexts like embedding via script tag in HTML, using npm, react and non-react apps etc.
This ticket is intended to be a gathering place for many other issues, but I want to ensure we have properly captured all the desired API functionality in detail. In practice, these areas all need to be realised as integration tests, and imply we get our distribution channels handled. My hope is that this becomes the seed of our comprehensive API documentation, and the One True Thread for debate on the external API surface area. I will edit this comment as discussion continues below.
Prior Art
These are some previous attempts to organise feedback on the shape of the Mirador API surface area and capabilities. [wiki page on embedding/import contexts]() [plugin types]() Known Consuming Contexts Known Mirador Plugins
Deployment Contexts
Mirador is a javascript library to be included in several different kinds of software contexts. These slides from the Mirador Community Update in Washington D.C. last year sums up the various contexts where Mirador needs to run as a library.
These contexts and their attendant use cases will determine the shape of its API surface area, which may not be identical for each context. Our job is to make it a pleasure to use (as a developer) from all four of these different perspectives.
Packaging/Publication
In general, we want to publish the main package with NPM
NPM
Full Bundle
Developer Experience
The developer types
npm install mirador
from their project folder.In their project Code, they can now type
Where the DOM node with an id
container
could come from a jade template, for example.How should this work in
rails
? How should this work within a react application?Tree-shaken sub-bundles
CDN
It should be possible to grab the total Mirador bundle, or any of its sub-bundles, from a CDN for creating and sharing examples in sandbox websites, static pages, presentations, or local use. Example
Configuration
Configuration should be human-readable and easily writable. An average webmaster should be able to easily tweak the configuration after reading a few examples.
Theming
More comprehensive theming usecases are gathered in #1649.
Javascript Runtime API
There should be a runtime API provided by the viewer instance that provides access to the redux store and actions, as well as a mechanism for dispatching and listening for events that occur in the viewer without having to learn redux.
Redux Store and Actions Access
Event Pub/Sub Mechanism
As a developer, I want to be able to call functions that initiate certain events in the viewer. This is necessary for creating embedded experiences in IMS LTI environments like Canvas and Blackboard, HarvardX, etc., or DH projects that provide a "guided tour"-like presentation of content. The verbosity of
viewer.store.dispatch(viewer.store.actions.nextCanvas({windowId: '1234', canvasId: "my-really-long-canvas-id"))
is annoying; I've never heard of redux, I don't know what stores are, or why I'm calling a function inside a dispatch thingy. More relatable would beviewer.nextCanvas('my-probably-long-window-id')
. I need to easily:I also need my containing page to receive notice when things in the viewer change. For example, a multi-spectral imaging program or museum conservation system may wish to display a histogram of the colours sampled at a given mouse position. I will therefore need the IIIF canvas coordinate of the current mouse pointer to retrieve accurate data from my backend. I want to listen specifically to:
Saving and Loading Config
Composable Component API
If I am writing a react application, I will want to use Mirador's components in my own application on a one-off basis. I may also want to use Mirador's redux core to build my own interfaces, or only use the single window component, but still have Mirador manage all my state for me (turn off everything but one window that fills a box à la "zen mode".
Plugin Authoring
Plugin management/Deployment
As a user, I want an easy way to include various plugins in my application or webpage or sandbox example without having to write any glue code.
Suites or Ecosystems of plugins
As an institutional developer, I or my team might maintain a suite of plugins that work together in some way. I would expect to be able to use Mirador's own action and reducer system to propagate and manage state for one plugin so that other plugins in my suite could respond to and make the same changes.
Plugin Curation and Discovery
As a developer, I want to find a list of good, well-maintained plugins that work with my version of Mirador. They should all be installed the same way, and as far as possible should not interfere with one another's operation or the global environment of my application.
Core Plugins
It may be helpful to the community to have some repository of "core" plugins that are either shipped with the main distribution bundle or otherwise prominently displayed for use with working and up-to-date examples that the core Mirador contributors keep fresh. Some examples might be:
Documentation
Required Integration Tests
Embedding Contexts
Web Page By Script Tag
Install in webpack-based react application
Install in Electron App
import
in the electron context shows the right thing with the right stylesExample Sharing Platforms
These tests will require some form of publication or packaging to work
Required Examples
Documentation is deeply improved by using examples. We should have hosted, tweakable, forkable, shareable examples of all the major setup and common customisation questions. Platforms like Observable and codePen help developers get a concrete grasp on and easy starting point for their problems and questions. We should endeavour to provide a platform for example-making as a community norm by publishing clear and well-structured examples for the following use-cases: