Skip to content

Commit

Permalink
Monday review with JT
Browse files Browse the repository at this point in the history
  • Loading branch information
hatton committed Dec 10, 2024
1 parent 2226513 commit 04a01f1
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 88 deletions.
2 changes: 1 addition & 1 deletion .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const proxy = viteConfigFn({
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 buildForStorybook` to update non-storybook code.
// When using those stories, use `yarn watchForStorybook` to update non-storybook code.
staticDirs: ["../dist"],
addons: [
"@storybook/addon-links",
Expand Down
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ This project, _bloom-player_, lets users view and interact with [Bloom](https://

The Bloom project uses _bloom-player_ 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 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
- 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 @@ -20,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 @@ -32,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 Down Expand Up @@ -75,23 +75,23 @@ 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`

Expand All @@ -106,6 +106,7 @@ Some books are designed to be published together as a collection. These books ma
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",
Expand All @@ -115,14 +116,15 @@ Bloom books link to each other using each book's "Instance ID". This is a guid t
"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`. This will be followed by requests for things like `/book/THE-INSTANCE-ID/basePage.css` and `/book/THE-INSTANCE-ID/image1.png`.
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. return the requested file (if your host is running the server) or redirect to a new url (if it is not).
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.

Expand All @@ -149,6 +151,7 @@ Both `yarn storybook` and `yarn dev` do this for you.
### Testing with a book hosted by Bloom

To test Bloom Player on a book in the Bloom Editor, follow these steps:

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.
Expand All @@ -158,6 +161,7 @@ To test Bloom Player on a book in the Bloom Editor, follow these steps:
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"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": " ",
"buildForStorybook": "yarn vite build --watch --mode development",
"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 Down
12 changes: 6 additions & 6 deletions src/bloom-player-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -581,9 +581,7 @@ export const BloomPlayerControls: React.FunctionComponent<BloomPlayerProps> = (
const amountToMoveDown =
(winHeight - actualPageHeight - controlsHeight) / 2; // don't count controlsHeight in what we move down
if (amountToMoveDown > 0) {
translateString = `translate(0, ${amountToMoveDown.toFixed(
0,
)}px) `;
translateString = `translate(0, ${amountToMoveDown.toFixed(0)}px) `;
// console.log(`** translating down ${amountToMoveDown}px`);
// console.log(` winHeight ${winHeight}px`);
// console.log(` desiredPageHeight ${desiredPageHeight}px`);
Expand Down Expand Up @@ -915,8 +913,8 @@ export const BloomPlayerControls: React.FunctionComponent<BloomPlayerProps> = (
// we have a page or book jump on our bloom player history stack
return BackButtonState.showArrow;
else if (showBackButton) {
// so our internal history is empty, but the container is
// wants us to offer a "back" to it. Bloom Reader uses this.
// so our internal history is empty, but the container
// wants us to offer a way "back" to it. Bloom Reader uses this.
if (window === window.top)
return BackButtonState.showArrow;
// Used by bloomlibrary.org if you went straight to a book via some URL
Expand All @@ -932,7 +930,9 @@ export const BloomPlayerControls: React.FunctionComponent<BloomPlayerProps> = (
playLabel={playLabel}
preferredLanguages={preferredUiLanguages}
backClicked={() => {
if (!coreRef.current?.HandleBackButtonClicked()) {
if (
!coreRef.current?.HandleBackButtonClickedIfHavePlayerHistory()
) {
sendBackToHost();
}
}}
Expand Down
69 changes: 24 additions & 45 deletions src/bloom-player-core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ export class BloomPlayerCore extends React.Component<IProps, IPlayerState> {
const parsedUrl = new URL(props.url, window.location.origin);
this.state.bookUrl = parsedUrl.pathname;
if (parsedUrl.hash) {
this.state.startPageId = parsedUrl.hash.substring(1);
this.state.startPageId = parsedUrl.hash.substring(1); // strip the "#"
} else {
// Copy into state because in a multi-book scenario, the prop.startPageIndex will
// always be the initial value, but we don't want to just got to that page of
Expand Down Expand Up @@ -355,6 +355,22 @@ export class BloomPlayerCore extends React.Component<IProps, IPlayerState> {
this.handleDocumentLevelKeyDown(e),
);

// Prevent unwanted behavior on link clicks that accidentally move a bit.
document.addEventListener(
"pointerdown",
(event) => {
if (
(event.target as HTMLElement).closest("[href], [data-href]")
) {
// Stop the swiper from starting a drag
event.stopPropagation();
// Stop the browser from showing a thing like you're trying to drag the link to some other window
event.preventDefault();
}
},
{ capture: true }, // Let us see this before children see it.
);

// Clicking on any element that has a data-href attribute will be treated as a link.
// This is the simplest way to handle such links that may be scattered on different
// types of elements throughout the book. See BL-13879.
Expand Down Expand Up @@ -411,7 +427,7 @@ export class BloomPlayerCore extends React.Component<IProps, IPlayerState> {
return canGoBack();
}
// called by bloom-player-controls
public HandleBackButtonClicked(): boolean {
public HandleBackButtonClickedIfHavePlayerHistory(): boolean {
const backLocation = goBackInHistoryIfPossible(
this.bookInfo.bookInstanceId,
);
Expand Down Expand Up @@ -543,11 +559,10 @@ export class BloomPlayerCore extends React.Component<IProps, IPlayerState> {

// parse the url
const u = new URL(newSourceUrl, window.location.origin);
// TODO: the URL parse always introduces a leading slash if it's missing, but currently that breaks... something.
// I feel like I'm hacking around something I should understand better.
// The URL parsing can introduce a slash we don't want when the input URL is not relative.
this.sourceUrl = newSourceUrl.startsWith("/")
? u.pathname
: u.pathname.substring(1);
? u.pathname // if the original was relative, fine
: u.pathname.substring(1); // if the original was not relative, don't make it look relative

// We support a two ways of interpreting URLs.
// If the url ends in .htm, it is assumed to be the URL of the htm file that
Expand Down Expand Up @@ -612,7 +627,7 @@ export class BloomPlayerCore extends React.Component<IProps, IPlayerState> {
},
);
const htmlPromise = axios.get(urlOfBookHtmlFile);
console.log("urlOfBookHtmlFile", urlOfBookHtmlFile);
// console.log("urlOfBookHtmlFile", urlOfBookHtmlFile);
const metadataPromise = axios.get(this.fullUrl("meta.json"));
Promise.all([htmlPromise, metadataPromise, distributionPromise])
.then((result) => {
Expand Down Expand Up @@ -894,7 +909,7 @@ export class BloomPlayerCore extends React.Component<IProps, IPlayerState> {
);
};

// TODO: most of this should be moved into bookInfo, including the pageIdToIndex map.
// ENHANCE: most of this should be moved into bookInfo, including the pageIdToIndex map.
// clear the pageIdToIndex map
pageIdToIndex.length = 0;
pageIdToIndex["cover"] = 0;
Expand Down Expand Up @@ -2235,12 +2250,7 @@ export class BloomPlayerCore extends React.Component<IProps, IPlayerState> {
.hasAppearanceSystem
? "appearance-system"
: ""
} ${
this.state
.importedBodyAttributes[
"class"
] ?? ""
}`}
} ${this.state.importedBodyAttributes["class"] ?? ""}`}
// Note: the contents of `slide` are what was in the .htm originally.
// So you would expect that this would replace any changes made to the dom by the activity or other code.
// You would expect that we would lose the answers a user made in an activity.
Expand Down Expand Up @@ -2361,37 +2371,6 @@ export class BloomPlayerCore extends React.Component<IProps, IPlayerState> {
// Other internal links won't work and are disabled; external ones are forced to
// open a new tab as the results of trying to display a web page in the bloom-player
// iframe or webview can be confusing.
// private fixInternalHyperlinks(div: HTMLDivElement | null): void {
// if (!div) {
// return;
// }
// const anchors = Array.from(div.getElementsByTagName("a") ?? []);
// anchors.forEach((a) => {
// const href = a.getAttribute("href"); // not a.href, which has the full page address prepended.
// if (href?.startsWith("#")) {
// const pageId = href.substring(1);
// const pageNum = this.state.pageIdToIndexMap[pageId];
// if (pageNum !== undefined) {
// a.href = ""; // may not be needed, on its own was unsuccessful in stopping attempted default navigation
// a.onclick = (ev) => {
// ev.preventDefault(); // don't try to follow the link, other than by the slideTo below
// ev.stopPropagation(); // don't allow parent listeners, particularly the one that toggles the nav bar
// this.swiperInstance.slideTo(pageNum);
// };
// } else {
// // no other kind of internal link makes sense, so let them be ignored.
// a.onclick = (ev) => {
// ev.preventDefault();
// ev.stopPropagation();
// };
// }
// } else {
// // an external link. It will likely confuse things to follow it inside whatever iframe or webview
// // bloom player may be running in, so encourage opening a new tab or similar action.
// a.setAttribute("target", "blank");
// }
// });
// }

// What we need to do when the page narration is completed (if autoadvance, go to next page).
public handlePageNarrationComplete = (page: HTMLElement | undefined) => {
Expand Down
10 changes: 10 additions & 0 deletions src/navigation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ describe("Navigation functions", () => {
const thirdBack = goBackInHistoryIfPossible("book1");
expect(thirdBack).toBeUndefined();
});

test("doesn't die if an href is empty", () => {
const event = createClickEvent("");
const result = checkClickForBookOrPageJump(
event,
"doesn't matter",
() => "doesn't matter",
);
expect(result).toEqual(undefined);
});
});
});

Expand Down
Loading

0 comments on commit 04a01f1

Please sign in to comment.