Skip to content

Commit

Permalink
Merge BL-14107 page and book navigation (Pair reviewed with JohnT)
Browse files Browse the repository at this point in the history
  • Loading branch information
hatton committed Dec 11, 2024
2 parents fdc257b + 3df06c8 commit fe80e7d
Show file tree
Hide file tree
Showing 60 changed files with 7,624 additions and 1,239 deletions.
52 changes: 44 additions & 8 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { StorybookConfig } from "@storybook/react-vite";
// get the vite config one level up
import viteConfigFn from "../vite.config";

const proxy = viteConfigFn({
Expand All @@ -8,6 +7,9 @@ const proxy = viteConfigFn({
}).server!.proxy;
const config: StorybookConfig = {
stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
// For stories that include an iframe and thus need access to bloomplayer.htm and the bundles it loads.
// When using those stories, use `yarn watchForStorybook` to update non-storybook code.
staticDirs: ["../dist"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
Expand All @@ -18,15 +20,49 @@ const config: StorybookConfig = {
options: {},
},
viteFinal: (config, options) => {
// setup the same proxy that we have in "vite dev" to avoid CORS issues
const server = {
...config.server,
proxy: {
...proxy,
},
const idToBookName = {
"2e492eb1-bcc5-4b2b-b756-6cda33e1eee4": "multibook-index",
"2c1b71ac-f399-446d-8398-e61a8efd4e83": "multibook-target1",
};
const updatedProxy = {
// setup the same proxy that we have in "vite dev" to avoid CORS issues. (THIS IS NOT WORKING)
// Note that it requires that paths to book on BloomLibrary.org start with "s3/" in place of "https://s3.amazonaws.com/"
...proxy,

// Simulate what a bloom-player host must do to provide books via their instanceId
// Note that this code is not giving a path all the way to the html file, so that
// it works with paths that are asking for all the other resources, too. That works
// so long as the html file of the book is named "index.htm".
"/book/": {
target: `http://localhost:6006`,
rewrite: (path) => {
const bookId = path.split("/")[2];
const bookName = idToBookName[bookId];
const rewrittenPath = path.replace(
`/book/${bookId}`,
`testBooks/${bookName}`,
);

// console.log(
// `[Storybook Proxy] ${path} --> ${rewrittenPath}`,
// );
return rewrittenPath;
},
},

return { ...config, server };
"/bloom/": {
target: "file:///",
rewrite: (path) => {
console.log(`proxy got request ${path}`);
return path.replace("/bloom/", "");
},
},
};
return {
...config,
server: { ...config.server, proxy: updatedProxy },
addons: ["@storybook/addon-interactions"],
};
},
};
export default config;
6 changes: 6 additions & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ const preview: Preview = {
date: /Date$/i,
},
},

options: {
storySort: {
order: ["Automated"],
},
},
},
};

Expand Down
21 changes: 4 additions & 17 deletions README-advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,14 @@

## Getting the pieces

yarn add bloom-player
`yarn add bloom-player`

This puts the files you need at

node_modules/bloom-player/dist
`node_modules/bloom-player/dist`

Note, however, that you won't typically import or require the bloom-player javascript. That javascript is there to be used by the bloomplayer.htm, which is intended to be the source of an iframe or WebView. So typically you will need to do some step to get the files from node_modules/bloom-player/dist to your output directory.
Copy those wherever you need them for your project.

For example, if gulp is part of your build process, you might use

gulp.src(paths.nodeFilesNeededInOutput)
.pipe(gulpCopy(outputDir, { prefix: 1 }));

along with declaring one of your paths to be

nodeFilesNeededInOutput: [
"./**/bloom-player/dist/bloomPlayer.min.js",
"./**/bloom-player/dist/simpleComprehensionQuiz.js",
"./**/bloom-player/dist/bloomplayer.htm",
"./**/bloom-player/dist/*.mp3"
]

### Other options

Expand All @@ -38,7 +25,7 @@ The url parameter points to the HTML file that is the core of the bloom book. It

The display of the book will automatically grow to be as large as will fit in the given space. If the book can rotate, it will pick an orientation depending on whether the window is wider then it is tall. If the book has special behaviors, such as motion/animation when in landscape mode, they will be triggered based on the chosen orientation.

`Bloom-player` is designed to be published using `npm` so as to be readily available to a variety of clients. The published version includes both `dest/bloomPlayer.js` and `dest/bloomPlayer.min.js`, as well as an un-minified version of the code and some mp3 files used for responding to comprehension questions. There is also a file simpleComprehensionQuiz.js, but this is needed only for books containing an obsolete kind of comprehension questions.
`Bloom-player` is designed to be published using `npm` so as to be readily available to a variety of clients. The published version includes both `dist/bloomPlayer-HASH.js`, as well as some mp3 files used by activities.

We deliberately require this component to be used in an `iframe` so that the containing web page is safe from any scripts that might get embedded in Bloom books. For this reason `bloom-player` deliberately manipulates (with `React`) the body of its HTML document.

Expand Down
81 changes: 60 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

This project, _bloom-player_, lets users view and interact with [Bloom](https://bloomlibrary.org) books in any browser.

Specifically, the books must be in _BloomPub_ format (.bloompub or .bloomd, which are zipped Bloom Digital books).
The Bloom project uses _bloom-player_ in the following places:

The Bloom project uses _bloom-player_ is used in the following places:

- In [Bloom Editor](https://github.com/bloombooks/bloomdesktop) / Publish / Bloom Reader tab, for previewing what the book will look like on a device.
- In [Bloom Reader](https://github.com/bloombooks/bloomreader) itself (starting with BR version 2.0)
- On [BloomLibrary.org](https://bloomlibrary.org)
- In [BloomPUB Viewer](https://github.com/bloombooks/bloompub-viewer)
- In [Bloom Editor](https://github.com/bloombooks/bloomdesktop) / Publish / Bloom Reader tab, for previewing what the book will look like on a device.
- In the [Bloom Reader](https://github.com/bloombooks/bloomreader) Android app
- On [BloomLibrary.org](https://bloomlibrary.org)
- In [BloomPUB Viewer](https://github.com/bloombooks/bloompub-viewer)
- In Android and IOS apps created with Reading App Builder

# Using bloom-player in a website

Expand All @@ -21,7 +20,7 @@ So to embed a book in your website, you just need an iframe element that points

```html
<iframe
src="https://bloomlibrary.org/bloom-player/bloomplayer.htm?url=path-to-your-book"
src="https://bloomlibrary.org/bloom-player/bloomplayer.htm?url=path-to-your-book"
></iframe>
```

Expand All @@ -33,7 +32,7 @@ You can customize some aspects of the bloom-player to fit your context. For exam

```html
<iframe
src="https://bloomlibrary.org/bloom-player/bloomplayer.htm?url=path-to-your-book&initiallyShowAppBar=false&allowToggleAppBar=false"
src="https://bloomlibrary.org/bloom-player/bloomplayer.htm?url=path-to-your-book&initiallyShowAppBar=false&allowToggleAppBar=false"
></iframe>
```

Expand All @@ -55,12 +54,17 @@ Default: `false`

#### showBackButton

If true, displays an arrow in the upper left corner of the app bar. When clicked, bloom-player will post a message "backButtonClicked" that the surrounding context can catch and respond to.
If true, displays a button in the upper left corner of the app bar. When clicked, bloom-player will post a message "backButtonClicked" that the your host can catch and respond to.

Example: `showBackButton=true`

Default: `false`

> [!NOTE]
> Normally this button will be a left arrow. However if bloom-player is the top level window (i.e. it is not embedded in another page), it will show as an ellipsis.
>
> This is used in BloomLibrary.org when the user has used a link from somewhere else to jump directly into to reading a book. In that case, this button isn't really taking them "back", it's instead taking them to the "Detail" page for that same book on BloomLibrary.org.
#### lang

If set, determines the initial language to be displayed. The user can still change the display language using the language picker.
Expand All @@ -71,40 +75,71 @@ Default: none (The initial language will be the vernacular language when the boo

#### hideFullScreenButton

If true, the Full Screen icon button will not appear in the upper right hand corner of the window. Otherwise, a Full Screen icon button is displayed and allows the user to toggle between full screen and window mode.
If true, the Full Screen icon button will not appear in the upper right hand corner of the window. Otherwise, a Full Screen icon button is displayed and allows the user to toggle between full screen and window mode.

Example: `hideFullScreenButton=true`

Default: `false`

#### independent

If true, bloom-player reports its own analytics directly to segment. If false, bloom-player sends messages to its host, and the host is responsible for reporting analytics.
If true, bloom-player reports its own analytics directly to segment. If false, bloom-player sends messages to its host, and the host is responsible for reporting analytics.

Example: `independent=false`

Default: `true`

#### host

If set, provides a host value for analytics. If not set, bloom-player attempts to derive a value from the available information if independent is `true` (information can be limited in an iframe) and does nothing otherwise.
If set, provides a host value for analytics. If not set, bloom-player attempts to derive a value from the available information if independent is `true` (information can be limited in an iframe) and does nothing otherwise.

Examples: `host=bloomlibrary` or `host=bloomreader` or `host=bloompubviewer` or `host=readerapp`

...or, theoretically, `[email protected]` (but we haven't used that yet)

Default: `nothing/undefined`

# How to support books that link to other books

Some books are designed to be published together as a collection. These books may link to each other using underlined phrases or buttons. Users can click on these links and also backtrack. You can see examples of this in the StoryBook stories under "MultiBook".

Instead of asking your host to handle the navigation, history, etc., this navigation happens within the one instance of Bloom Player. However, Bloom Player needs your help, because it does not have a way to know what the URL to each book is in the context of your host, which could be a website, an app, a desktop program, etc. So if you want to support these collections of linked books, your host has to do some extra work.

Bloom books link to each other using each book's "Instance ID". This is a guid that is given as a property in the `meta.json` file:

```json
{
"bookInstanceId":"0b82aab3-61fb-429e-864f-ce7671a3d372",
"title":"หมู่บ้านแห่งความดี\r\nVillage of good",
"credits":"มิได้จัดจำหน่าย  แต่จัดทำเพื่อส่งเสริมการเรียนรู้",
"tags":["topic:Fiction"],
"pageCount":16,
```

When the user clicks on a link to another book, Bloom Player will first request a resource from `/book/THE-INSTANCE-ID/index.htm`. If you do a redirect to `C:/mybook/index.htm`, then this will be followed by requests for things like `C:/mybook/basePage.css` and `C:/mybook/image1.png`.

To support this, your host needs to:

1. intercept that request
2. extract the Instance ID
3. figure out the path to that book in your system
4. if the target is still zipped in a BloomPUB, you may need to unzip it or read the resource directly out of the archive
5. redirect to a new url

If these steps are slow, you might need to cache or even pre-prepare an index for use at runtime.

As an example, the file `.storybook/main.ts` sets up Storybook's vite dev server to proxy a couple instance ids to correct folders in this repository's `/public/testBooks` directory.

# Development

If you haven't already, install `volta` globally. Volta takes care of getting all the correct versions of things like node and yarn to match what this repository expects.

Run `yarn` to get the dependencies.

Either, run `yarn storybook` (which has multiple books),
Either run `yarn storybook` (which has multiple books),

or run `yarn dev` (which will use `index-for-developing.html`).

Note: you need to have `chrome` on your path.

See package.json for other scripts.

### Testing with a book hosted on the web
Expand All @@ -115,15 +150,19 @@ Both `yarn storybook` and `yarn dev` do this for you.

### Testing with a book hosted by Bloom

Note that while testing, one option is to run Bloom, select your book, go to the publish tab, and choose Bloom Reader. Bloom will make the book available through its local fileserver. Modify index.html to use a path like this
To test Bloom Player on a book in the Bloom Editor, follow these steps:

<iframe src="bloomplayer.htm?url=http://localhost:8089/bloom/C%3A/Users/YourName/AppData/Local/Temp/PlaceForStagingBook/myBookTitle"/>

For more information, see README-advanced.md
1. Go to the Publish tab in Bloom, choose "BloomPUB", and click "Preview".
2. Back in this repository, run storybook (`yarn storybook`).
3. Choose the "Live From Bloom Editor" story.

### Running unit tests

To run unit tests use `yarn test`. This will run all "*.test.ts", which should be collocated with the thing being tested.
To run unit tests use `yarn test`. This will run all `*.test.ts`, which should be collocated with the thing being tested.

### More info

For more information, see README-advanced.md

## License

Expand Down
20 changes: 11 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"// blame.ignoreRevsFile - This allows blame to see through commits that are only there to format code.": " ",
"prepare": "husky install && git config blame.ignoreRevsFile .git-blame-ignore-revs",
"storybook": "storybook dev -p 6006",
"// run this when working with stories that use an iframe and bloomplayer.htm instead of the raw react components": " ",
"watchForStorybook": "yarn vite build --watch --mode development",
"strings:download": "crowdin download --config src/l10n/crowdin.yml && yarn strings:compile",
"strings:upload": "crowdin upload --config src/l10n/crowdin.yml",
"strings:compile": "yarn tsx ./src/l10n/compileL10Strings.ts",
Expand All @@ -42,27 +44,27 @@
},
"devDependencies": {
"@crowdin/cli": "^4.2.0",
"@storybook/addon-essentials": "8.3.6",
"@storybook/addon-interactions": "8.3.6",
"@storybook/addon-links": "8.3.6",
"@storybook/blocks": "8.3.6",
"@storybook/react": "8.3.6",
"@storybook/react-vite": "8.3.6",
"@storybook/test": "8.3.6",
"@storybook/addon-essentials": "8.4.6",
"@storybook/addon-interactions": "^8.4.6",
"@storybook/addon-links": "8.4.6",
"@storybook/blocks": "8.4.6",
"@storybook/react": "8.4.6",
"@storybook/react-vite": "8.4.6",
"@storybook/test": "8.4.6",
"@types/jquery": "^3.5.5",
"@types/jquery.nicescroll": "^3.7.1",
"@types/node": "^22.7.6",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.2",
"editorconfig": "^2.0.0",
"eslint": "^9.12.0",
"eslint-plugin-storybook": "^0.9.0",
"eslint-plugin-storybook": "^0.11.1",
"husky": "8.0.3",
"jsdom": "^25.0.1",
"less": "^4.2.0",
"prettier": "^3.3.3",
"semantic-release": "24.1.2",
"storybook": "8.3.6",
"storybook": "8.4.6",
"tsx": "^4.19.1",
"typescript": "*",
"vite": "^5.4.9",
Expand Down
Loading

0 comments on commit fe80e7d

Please sign in to comment.