From 61e77cde226a8f4b6118327a501d5becb5b7cfac Mon Sep 17 00:00:00 2001 From: Maxime Bret Date: Fri, 19 Jul 2024 23:32:46 +0200 Subject: [PATCH] fix: some cleanup --- .../enhancers/navigation/manualNavigator.ts | 46 ++- .../resolvers/getNavigationForLeftPage.ts | 89 +++++ .../getNavigationForLeftSinglePage.ts | 65 ++++ .../resolvers/getNavigationForRightPage.ts | 92 +++++ .../getNavigationForRightSinglePage.ts | 67 ++++ .../getSpineItemPositionForLeftPage.ts | 40 ++ .../getSpineItemPositionForRightPage.ts | 40 ++ .../core/src/navigation/InternalNavigator.ts | 5 +- .../consolidation/withOrGuessUrlInfo.ts | 22 +- .../resolvers/NavigationResolver.ts | 344 ++++-------------- .../resolvers/getAdjustedPositionForSpread.ts | 16 + .../resolvers/getNavigationForUrl.ts | 29 +- .../resolvers/wrapPositionWithSafeEdge.ts | 45 +++ ...toreNavigationForControlledPageTurnMode.ts | 20 +- .../navigation/restoration/restorePosition.ts | 4 - .../core/src/spineItem/navigationResolver.ts | 52 --- 16 files changed, 584 insertions(+), 392 deletions(-) create mode 100644 packages/core/src/enhancers/navigation/resolvers/getNavigationForLeftPage.ts create mode 100644 packages/core/src/enhancers/navigation/resolvers/getNavigationForLeftSinglePage.ts create mode 100644 packages/core/src/enhancers/navigation/resolvers/getNavigationForRightPage.ts create mode 100644 packages/core/src/enhancers/navigation/resolvers/getNavigationForRightSinglePage.ts create mode 100644 packages/core/src/enhancers/navigation/resolvers/getSpineItemPositionForLeftPage.ts create mode 100644 packages/core/src/enhancers/navigation/resolvers/getSpineItemPositionForRightPage.ts create mode 100644 packages/core/src/navigation/resolvers/getAdjustedPositionForSpread.ts create mode 100644 packages/core/src/navigation/resolvers/wrapPositionWithSafeEdge.ts diff --git a/packages/core/src/enhancers/navigation/manualNavigator.ts b/packages/core/src/enhancers/navigation/manualNavigator.ts index 13123af2..426768fd 100644 --- a/packages/core/src/enhancers/navigation/manualNavigator.ts +++ b/packages/core/src/enhancers/navigation/manualNavigator.ts @@ -2,6 +2,8 @@ import { Reader } from "../../reader" import { Report } from "../../report" import { ViewportPosition } from "../../navigation/ViewportNavigator" import { getNavigationForSpineItemPage } from "./resolvers/getNavigationForSpineItemPage" +import { getNavigationForRightPage } from "./resolvers/getNavigationForRightPage" +import { getNavigationForLeftPage } from "./resolvers/getNavigationForLeftPage" export class ManualNavigator { movingLastDelta = { x: 0, y: 0 } @@ -11,26 +13,46 @@ export class ManualNavigator { constructor(protected reader: Reader) {} turnRight() { - const navigation = - this.reader.navigation.navigationResolver.getNavigationForRightPage( - this.reader.navigation.getNavigation().position, - ) + const navigation = this.reader.navigation.getNavigation() + const spineItem = this.reader.spineItemManager.get(navigation.spineItem) + + if (!spineItem) return + + const position = getNavigationForRightPage({ + context: this.reader.context, + navigationResolver: this.reader.navigation.navigationResolver, + position: navigation.position, + computedPageTurnDirection: + this.reader.settings.settings.computedPageTurnDirection, + spineItem, + spineItemManager: this.reader.spineItemManager, + spineLocator: this.reader.spine.locator, + }) return this.reader.navigation.navigate({ - animation: "turn", - position: navigation, + position, }) } turnLeft() { - const navigation = - this.reader.navigation.navigationResolver.getNavigationForLeftPage( - this.reader.navigation.getNavigation().position, - ) + const navigation = this.reader.navigation.getNavigation() + const spineItem = this.reader.spineItemManager.get(navigation.spineItem) + + if (!spineItem) return + + const position = getNavigationForLeftPage({ + context: this.reader.context, + navigationResolver: this.reader.navigation.navigationResolver, + position: navigation.position, + computedPageTurnDirection: + this.reader.settings.settings.computedPageTurnDirection, + spineItem, + spineItemManager: this.reader.spineItemManager, + spineLocator: this.reader.spine.locator, + }) return this.reader.navigation.navigate({ - animation: "turn", - position: navigation, + position, }) } diff --git a/packages/core/src/enhancers/navigation/resolvers/getNavigationForLeftPage.ts b/packages/core/src/enhancers/navigation/resolvers/getNavigationForLeftPage.ts new file mode 100644 index 00000000..ca1e736b --- /dev/null +++ b/packages/core/src/enhancers/navigation/resolvers/getNavigationForLeftPage.ts @@ -0,0 +1,89 @@ +import { Context } from "../../../context/Context" +import { NavigationResolver } from "../../../navigation/resolvers/NavigationResolver" +import { ViewportPosition } from "../../../navigation/ViewportNavigator" +import { SpineLocator } from "../../../spine/locationResolver" +import { SpineItem } from "../../../spineItem/createSpineItem" +import { SpineItemManager } from "../../../spineItemManager" +import { getNavigationForLeftSinglePage } from "./getNavigationForLeftSinglePage" + +/** + * Very naive approach for spread. It could be optimized but by using this approach + * we do not add complexity to the code and use the current logic to handle it correctly. + * + * @important + * Special case for vertical content, read content + */ +export const getNavigationForLeftPage = ({ + position, + spineItem, + context, + navigationResolver, + spineItemManager, + spineLocator, + computedPageTurnDirection +}: { + position: ViewportPosition + spineItem: SpineItem + context: Context + spineItemManager: SpineItemManager + navigationResolver: NavigationResolver + spineLocator: SpineLocator + computedPageTurnDirection: "horizontal" | "vertical" +}): ViewportPosition => { + const navigation = getNavigationForLeftSinglePage({ + position, + context, + navigationResolver, + computedPageTurnDirection, + spineItemManager, + spineLocator, + }) + + // when we move withing vertical content, because only y moves, we don't need two navigation + if (spineItem?.isUsingVerticalWriting() && position.x === navigation.x) { + return navigationResolver.getAdjustedPositionForSpread(navigation) + } + + if (context.state.isUsingSpreadMode) { + // in case of spread the entire screen is taken as one real page for vertical content + // in order to move out from it we add an extra page width. + // using `getNavigationForLeftSinglePage` again would keep x as it is and wrongly move y + // for the next item in case it's also a vertical content + if (spineItem?.isUsingVerticalWriting() && position.x !== navigation.x) { + return navigationResolver.getAdjustedPositionForSpread( + navigationResolver.wrapPositionWithSafeEdge( + context.isRTL() + ? { ...navigation, x: navigation.x + context.getPageSize().width } + : { + ...navigation, + x: navigation.x - context.getPageSize().width, + }, + ), + ) + } + + /** + * In vase we move vertically and the y is already different, we don't need a second navigation + * since we already jumped to a new screen + */ + if ( + computedPageTurnDirection === `vertical` && + position.y !== navigation.y + ) { + return navigationResolver.getAdjustedPositionForSpread(navigation) + } + + const doubleNavigation = getNavigationForLeftSinglePage({ + position: navigation, + context, + navigationResolver, + computedPageTurnDirection, + spineItemManager, + spineLocator, + }) + + return navigationResolver.getAdjustedPositionForSpread(doubleNavigation) + } + + return navigationResolver.getAdjustedPositionForSpread(navigation) +} diff --git a/packages/core/src/enhancers/navigation/resolvers/getNavigationForLeftSinglePage.ts b/packages/core/src/enhancers/navigation/resolvers/getNavigationForLeftSinglePage.ts new file mode 100644 index 00000000..6c1dc1d1 --- /dev/null +++ b/packages/core/src/enhancers/navigation/resolvers/getNavigationForLeftSinglePage.ts @@ -0,0 +1,65 @@ +import { Context } from "../../../context/Context" +import { NavigationResolver } from "../../../navigation/resolvers/NavigationResolver" +import { ViewportPosition } from "../../../navigation/ViewportNavigator" +import { SpineLocator } from "../../../spine/locationResolver" +import { SpineItemManager } from "../../../spineItemManager" +import { getSpineItemPositionForLeftPage } from "./getSpineItemPositionForLeftPage" + +export const getNavigationForLeftSinglePage = ({ + position, + navigationResolver, + computedPageTurnDirection, + spineItemManager, + spineLocator, + context, +}: { + position: ViewportPosition + navigationResolver: NavigationResolver + computedPageTurnDirection: "horizontal" | "vertical" + spineItemManager: SpineItemManager + spineLocator: SpineLocator + context: Context +}): ViewportPosition => { + const pageTurnDirection = computedPageTurnDirection + const spineItem = + spineLocator.getSpineItemFromPosition(position) || spineItemManager.get(0) + const defaultNavigation = position + + if (!spineItem) { + return defaultNavigation + } + + const spineItemPosition = spineLocator.getSpineItemPositionFromSpinePosition( + position, + spineItem, + ) + + const spineItemNavigation = getSpineItemPositionForLeftPage({ + position: spineItemPosition, + spineItem, + pageHeight: context.getPageSize().height, + pageWidth: context.getPageSize().width, + spineItemLocator: spineLocator.spineItemLocator, + }) + + const isNewNavigationInCurrentItem = navigationResolver.arePositionsDifferent( + spineItemNavigation, + spineItemPosition, + ) + + if (!isNewNavigationInCurrentItem) { + return navigationResolver.wrapPositionWithSafeEdge( + pageTurnDirection === `horizontal` + ? { x: position.x - context.getPageSize().width, y: 0 } + : { y: position.y - context.getPageSize().height, x: 0 }, + ) + } else { + const readingOrderPosition = + spineLocator.getSpinePositionFromSpineItemPosition( + spineItemNavigation, + spineItem, + ) + + return readingOrderPosition + } +} diff --git a/packages/core/src/enhancers/navigation/resolvers/getNavigationForRightPage.ts b/packages/core/src/enhancers/navigation/resolvers/getNavigationForRightPage.ts new file mode 100644 index 00000000..5fe1ec56 --- /dev/null +++ b/packages/core/src/enhancers/navigation/resolvers/getNavigationForRightPage.ts @@ -0,0 +1,92 @@ +import { Context } from "../../../context/Context" +import { NavigationResolver } from "../../../navigation/resolvers/NavigationResolver" +import { ViewportPosition } from "../../../navigation/ViewportNavigator" +import { SpineLocator } from "../../../spine/locationResolver" +import { SpineItem } from "../../../spineItem/createSpineItem" +import { SpineItemManager } from "../../../spineItemManager" +import { getNavigationForRightSinglePage } from "./getNavigationForRightSinglePage" + +/** + * Very naive approach for spread. It could be optimized but by using this approach + * we do not add complexity to the code and use the current logic to handle it correctly. + * + * @important + * Special case for vertical content, read content + */ +export const getNavigationForRightPage = ({ + position, + spineItem, + context, + navigationResolver, + spineItemManager, + spineLocator, + computedPageTurnDirection, +}: { + position: ViewportPosition + spineItem: SpineItem + context: Context + spineItemManager: SpineItemManager + navigationResolver: NavigationResolver + spineLocator: SpineLocator + computedPageTurnDirection: "horizontal" | "vertical" +}): ViewportPosition => { + const navigation = getNavigationForRightSinglePage({ + position, + context, + navigationResolver, + computedPageTurnDirection, + spineItemManager, + spineLocator, + }) + + // when we move withing vertical content, because only y moves, we don't need two navigation + if (spineItem?.isUsingVerticalWriting() && position.x === navigation.x) { + return navigationResolver.getAdjustedPositionForSpread(navigation) + } + + if (context.state.isUsingSpreadMode) { + // in case of spread the entire screen is taken as one real page for vertical content + // in order to move out from it we add an extra page width. + // using `getNavigationForLeftSinglePage` again would keep x as it is and wrongly move y + // for the next item in case it's also a vertical content + if (spineItem?.isUsingVerticalWriting() && position.x !== navigation.x) { + return navigationResolver.getAdjustedPositionForSpread( + navigationResolver.wrapPositionWithSafeEdge( + context.isRTL() + ? { + ...navigation, + x: navigation.x - context.getPageSize().width, + } + : { + ...navigation, + x: navigation.x + context.getPageSize().width, + }, + ), + ) + } + + /** + * In vase we move vertically and the y is already different, we don't need a second navigation + * since we already jumped to a new screen + */ + if ( + computedPageTurnDirection === `vertical` && + position.y !== navigation.y + ) { + return navigationResolver.getAdjustedPositionForSpread(navigation) + } + + const doubleNavigation = getNavigationForRightSinglePage({ + position: navigation, + context, + navigationResolver, + computedPageTurnDirection, + spineItemManager, + spineLocator, + }) + + return navigationResolver.getAdjustedPositionForSpread(doubleNavigation) + } + + return navigationResolver.getAdjustedPositionForSpread(navigation) +} diff --git a/packages/core/src/enhancers/navigation/resolvers/getNavigationForRightSinglePage.ts b/packages/core/src/enhancers/navigation/resolvers/getNavigationForRightSinglePage.ts new file mode 100644 index 00000000..9f4e37d3 --- /dev/null +++ b/packages/core/src/enhancers/navigation/resolvers/getNavigationForRightSinglePage.ts @@ -0,0 +1,67 @@ +import { Context } from "../../../context/Context" +import { NavigationResolver } from "../../../navigation/resolvers/NavigationResolver" +import { ViewportPosition } from "../../../navigation/ViewportNavigator" +import { SpineLocator } from "../../../spine/locationResolver" +import { SpineItemManager } from "../../../spineItemManager" +import { getSpineItemPositionForRightPage } from "./getSpineItemPositionForRightPage" + +export const getNavigationForRightSinglePage = ({ + position, + navigationResolver, + computedPageTurnDirection, + spineItemManager, + spineLocator, + context, +}: { + position: ViewportPosition + navigationResolver: NavigationResolver + computedPageTurnDirection: "horizontal" | "vertical" + spineItemManager: SpineItemManager + spineLocator: SpineLocator + context: Context +}): ViewportPosition => { + const pageTurnDirection = computedPageTurnDirection + const spineItem = + spineLocator.getSpineItemFromPosition(position) || spineItemManager.get(0) + const defaultNavigation = position + + if (!spineItem) { + return defaultNavigation + } + + // translate viewport position into reading item local position + const spineItemPosition = spineLocator.getSpineItemPositionFromSpinePosition( + position, + spineItem, + ) + // get reading item local position for right page + const spineItemNavigationForRightPage = getSpineItemPositionForRightPage({ + position: spineItemPosition, + spineItem, + pageHeight: context.getPageSize().height, + pageWidth: context.getPageSize().width, + spineItemLocator: spineLocator.spineItemLocator, + }) + + // check both position to see if we moved out of it + const isNewNavigationInCurrentItem = navigationResolver.arePositionsDifferent( + spineItemNavigationForRightPage, + spineItemPosition, + ) + + if (!isNewNavigationInCurrentItem) { + return navigationResolver.wrapPositionWithSafeEdge( + pageTurnDirection === `horizontal` + ? { x: position.x + context.getPageSize().width, y: 0 } + : { y: position.y + context.getPageSize().height, x: 0 }, + ) + } else { + const readingOrderPosition = + spineLocator.getSpinePositionFromSpineItemPosition( + spineItemNavigationForRightPage, + spineItem, + ) + + return readingOrderPosition + } +} diff --git a/packages/core/src/enhancers/navigation/resolvers/getSpineItemPositionForLeftPage.ts b/packages/core/src/enhancers/navigation/resolvers/getSpineItemPositionForLeftPage.ts new file mode 100644 index 00000000..f7b9fc85 --- /dev/null +++ b/packages/core/src/enhancers/navigation/resolvers/getSpineItemPositionForLeftPage.ts @@ -0,0 +1,40 @@ +import { SpineItem } from "../../../spineItem/createSpineItem" +import { SpineItemLocator } from "../../../spineItem/locationResolver" +import { + SafeSpineItemPosition, + UnsafeSpineItemPosition, +} from "../../../spineItem/types" + +export const getSpineItemPositionForLeftPage = ({ + position, + spineItem, + pageHeight, + pageWidth, + spineItemLocator, +}: { + position: UnsafeSpineItemPosition + spineItem: SpineItem + pageWidth: number + pageHeight: number + spineItemLocator: SpineItemLocator +}): SafeSpineItemPosition => { + let nextPotentialPosition = { + x: position.x - pageWidth, + y: position.y, + } + + if (spineItem.isUsingVerticalWriting()) { + nextPotentialPosition = { + x: position.x, + y: position.y + pageHeight, + } + } + + const navigationPosition = + spineItemLocator.getSpineItemClosestPositionFromUnsafePosition( + nextPotentialPosition, + spineItem, + ) + + return navigationPosition +} diff --git a/packages/core/src/enhancers/navigation/resolvers/getSpineItemPositionForRightPage.ts b/packages/core/src/enhancers/navigation/resolvers/getSpineItemPositionForRightPage.ts new file mode 100644 index 00000000..50817dce --- /dev/null +++ b/packages/core/src/enhancers/navigation/resolvers/getSpineItemPositionForRightPage.ts @@ -0,0 +1,40 @@ +import { SpineItem } from "../../../spineItem/createSpineItem" +import { SpineItemLocator } from "../../../spineItem/locationResolver" +import { + SafeSpineItemPosition, + UnsafeSpineItemPosition, +} from "../../../spineItem/types" + +export const getSpineItemPositionForRightPage = ({ + position, + spineItem, + pageHeight, + pageWidth, + spineItemLocator, +}: { + position: UnsafeSpineItemPosition + spineItem: SpineItem + pageWidth: number + pageHeight: number + spineItemLocator: SpineItemLocator +}): SafeSpineItemPosition => { + let nextPotentialPosition = { + x: position.x + pageWidth, + y: position.y, + } + + if (spineItem.isUsingVerticalWriting()) { + nextPotentialPosition = { + x: position.x, + y: position.y - pageHeight, + } + } + + const navigationPosition = + spineItemLocator.getSpineItemClosestPositionFromUnsafePosition( + nextPotentialPosition, + spineItem, + ) + + return navigationPosition +} diff --git a/packages/core/src/navigation/InternalNavigator.ts b/packages/core/src/navigation/InternalNavigator.ts index a1947cf7..1c4b9c7e 100644 --- a/packages/core/src/navigation/InternalNavigator.ts +++ b/packages/core/src/navigation/InternalNavigator.ts @@ -48,8 +48,8 @@ const report = Report.namespace(NAMESPACE) /** * Priority of info taken for restoration: - * - complete cfi * - URL + * - complete cfi * - incomplete cfi * - spine item position * - spine item (fallback) @@ -147,10 +147,7 @@ export class InternalNavigator extends DestroyableClass { * as much information as needed to reduce later lookup */ withOrGuessUrlInfo({ - context, navigationResolver, - spineItemManager, - spineLocator, }), withOrGuessDirection({ context, settings }), withOrGuessSpineItemInfo({ diff --git a/packages/core/src/navigation/consolidation/withOrGuessUrlInfo.ts b/packages/core/src/navigation/consolidation/withOrGuessUrlInfo.ts index d931c6a2..0d55226f 100644 --- a/packages/core/src/navigation/consolidation/withOrGuessUrlInfo.ts +++ b/packages/core/src/navigation/consolidation/withOrGuessUrlInfo.ts @@ -1,9 +1,5 @@ import { map, Observable } from "rxjs" import { InternalNavigationInput } from "../InternalNavigator" -import { Context } from "../../context/Context" -import { getNavigationForUrl } from "../resolvers/getNavigationForUrl" -import { SpineLocator } from "../../spine/locationResolver" -import { SpineItemManager } from "../../spineItemManager" import { NavigationResolver } from "../resolvers/NavigationResolver" type Navigation = { @@ -12,29 +8,17 @@ type Navigation = { export const withOrGuessUrlInfo = ({ - context, navigationResolver, - spineItemManager, - spineLocator, }: { - context: Context - spineItemManager: SpineItemManager navigationResolver: NavigationResolver - spineLocator: SpineLocator }) => (stream: Observable): Observable => { return stream.pipe( map((params) => { if (params.navigation.url) { - const result = getNavigationForUrl({ - context, - navigationResolver, - spineItemManager, - spineLocator, - url: params.navigation.url, - }) - - console.log({ result }) + const result = navigationResolver.getNavigationForUrl( + params.navigation.url, + ) if (result) { return { diff --git a/packages/core/src/navigation/resolvers/NavigationResolver.ts b/packages/core/src/navigation/resolvers/NavigationResolver.ts index 62cee43d..9f683c80 100644 --- a/packages/core/src/navigation/resolvers/NavigationResolver.ts +++ b/packages/core/src/navigation/resolvers/NavigationResolver.ts @@ -7,9 +7,16 @@ import { createSpineLocationResolver } from "../../spine/locationResolver" import { createCfiLocator } from "../../spine/cfiLocator" import { ReaderSettingsManager } from "../../settings/ReaderSettingsManager" import { ViewportPosition } from "../ViewportNavigator" -import { SafeSpineItemPosition } from "../../spineItem/types" +import { + SafeSpineItemPosition, + UnsafeSpineItemPosition, +} from "../../spineItem/types" +import { getAdjustedPositionForSpread } from "./getAdjustedPositionForSpread" +import { wrapPositionWithSafeEdge } from "./wrapPositionWithSafeEdge" +import { getNavigationForUrl } from "./getNavigationForUrl" +import { getNavigationFromSpineItemPosition } from "./getNavigationFromSpineItemPosition" -const NAMESPACE = `spineNavigator` +export const NAMESPACE = `spineNavigator` export type NavigationResolver = ReturnType @@ -37,54 +44,6 @@ export const createNavigationResolver = ({ arePositionsDifferent(a, b) || (!!a.spineItem && !!b.spineItem && a.spineItem !== b.spineItem) - const wrapPositionWithSafeEdge = Report.measurePerformance( - `${NAMESPACE} wrapPositionWithSafeEdge`, - 1, - (position: ViewportPosition) => { - // @todo use container width instead to increase performances - const lastSpineItem = spineItemManager.get( - spineItemManager.getLength() - 1, - ) - const distanceOfLastSpineItem = spineItemManager.getAbsolutePositionOf( - lastSpineItem || 0, - ) - - const maximumYOffset = - distanceOfLastSpineItem.bottom - context.getPageSize().height - const y = Math.min(Math.max(0, position.y), maximumYOffset) - - /** - * For RTL books we move from right to left so negative x. - * [-x, 0] - */ - if (context.isRTL()) { - return { - x: Math.max(Math.min(0, position.x), distanceOfLastSpineItem.left), - y, - } - } - - const maximumXOffset = - distanceOfLastSpineItem.right - context.getPageSize().width - - return { - x: Math.min(Math.max(0, position.x), maximumXOffset), - y, - } - }, - { disable: true }, - ) - - const getAdjustedPositionForSpread = ({ - x, - y, - }: ViewportPosition): ViewportPosition => { - const isOffsetNotAtEdge = x % context.state.visibleAreaRect.width !== 0 - const correctedX = isOffsetNotAtEdge ? x - context.getPageSize().width : x - - return { x: correctedX, y } - } - const getNavigationForCfi = ( cfi: string, ): { position: ViewportPosition; spineItem?: SpineItem } => { @@ -104,7 +63,11 @@ export const createNavigationResolver = ({ return { spineItem, - position: getAdjustedPositionForSpread(readingPosition), + position: getAdjustedPositionForSpread({ + position: readingPosition, + pageSizeWidth: context.getPageSize().width, + visibleAreaRectWidth: context.state.visibleAreaRect.width, + }), } } @@ -119,7 +82,11 @@ export const createNavigationResolver = ({ spineItem, ) - return getAdjustedPositionForSpread(position) + return getAdjustedPositionForSpread({ + position, + pageSizeWidth: context.getPageSize().width, + visibleAreaRectWidth: context.state.visibleAreaRect.width, + }) } const getNavigationForSpineIndexOrId = ( @@ -130,227 +97,16 @@ export const createNavigationResolver = ({ if (spineItem) { const position = locator.getSpinePositionFromSpineItem(spineItem) - return getAdjustedPositionForSpread(position) + return getAdjustedPositionForSpread({ + position, + pageSizeWidth: context.getPageSize().width, + visibleAreaRectWidth: context.state.visibleAreaRect.width, + }) } return { x: 0, y: 0 } } - const getNavigationForRightSinglePage = ( - position: ViewportPosition, - ): ViewportPosition => { - const pageTurnDirection = settings.settings.computedPageTurnDirection - const spineItem = - locator.getSpineItemFromPosition(position) || spineItemManager.get(0) - const defaultNavigation = position - - if (!spineItem) { - return defaultNavigation - } - - // translate viewport position into reading item local position - const spineItemPosition = locator.getSpineItemPositionFromSpinePosition( - position, - spineItem, - ) - // get reading item local position for right page - const spineItemNavigationForRightPage = - spineItemNavigator.getNavigationForRightPage(spineItemPosition, spineItem) - - // check both position to see if we moved out of it - const isNewNavigationInCurrentItem = arePositionsDifferent( - spineItemNavigationForRightPage, - spineItemPosition, - ) - - if (!isNewNavigationInCurrentItem) { - return wrapPositionWithSafeEdge( - pageTurnDirection === `horizontal` - ? { x: position.x + context.getPageSize().width, y: 0 } - : { y: position.y + context.getPageSize().height, x: 0 }, - ) - } else { - const readingOrderPosition = - locator.getSpinePositionFromSpineItemPosition( - spineItemNavigationForRightPage, - spineItem, - ) - - return readingOrderPosition - } - } - - const getNavigationForLeftSinglePage = ( - position: ViewportPosition, - ): ViewportPosition => { - const pageTurnDirection = settings.settings.computedPageTurnDirection - const spineItem = - locator.getSpineItemFromPosition(position) || spineItemManager.get(0) - const defaultNavigation = position - - if (!spineItem) { - return defaultNavigation - } - - const spineItemPosition = locator.getSpineItemPositionFromSpinePosition( - position, - spineItem, - ) - const spineItemNavigation = spineItemNavigator.getNavigationForLeftPage( - spineItemPosition, - spineItem, - ) - const isNewNavigationInCurrentItem = arePositionsDifferent( - spineItemNavigation, - spineItemPosition, - ) - - if (!isNewNavigationInCurrentItem) { - return wrapPositionWithSafeEdge( - pageTurnDirection === `horizontal` - ? { x: position.x - context.getPageSize().width, y: 0 } - : { y: position.y - context.getPageSize().height, x: 0 }, - ) - } else { - const readingOrderPosition = - locator.getSpinePositionFromSpineItemPosition( - spineItemNavigation, - spineItem, - ) - - return readingOrderPosition - } - } - - /** - * Very naive approach for spread. It could be optimized but by using this approach - * we do not add complexity to the code and use the current logic to handle it correctly. - * - * @important - * Special case for vertical content, read content - */ - const getNavigationForRightPage = ( - position: ViewportPosition, - ): ViewportPosition => { - const spineItemOnPosition = - locator.getSpineItemFromPosition(position) || spineItemManager.get(0) - - const navigation = getNavigationForRightSinglePage(position) - - // when we move withing vertical content, because only y moves, we don't need two navigation - if ( - spineItemOnPosition?.isUsingVerticalWriting() && - position.x === navigation.x - ) { - return getAdjustedPositionForSpread(navigation) - } - - if (context.state.isUsingSpreadMode) { - // in case of spread the entire screen is taken as one real page for vertical content - // in order to move out from it we add an extra page width. - // using `getNavigationForLeftSinglePage` again would keep x as it is and wrongly move y - // for the next item in case it's also a vertical content - if ( - spineItemOnPosition?.isUsingVerticalWriting() && - position.x !== navigation.x - ) { - return getAdjustedPositionForSpread( - wrapPositionWithSafeEdge( - context.isRTL() - ? { - ...navigation, - x: navigation.x - context.getPageSize().width, - } - : { - ...navigation, - x: navigation.x + context.getPageSize().width, - }, - ), - ) - } - - /** - * In vase we move vertically and the y is already different, we don't need a second navigation - * since we already jumped to a new screen - */ - if ( - settings.settings.computedPageTurnDirection === `vertical` && - position.y !== navigation.y - ) { - return getAdjustedPositionForSpread(navigation) - } - - const doubleNavigation = getNavigationForRightSinglePage(navigation) - - return getAdjustedPositionForSpread(doubleNavigation) - } - - return getAdjustedPositionForSpread(navigation) - } - - /** - * Very naive approach for spread. It could be optimized but by using this approach - * we do not add complexity to the code and use the current logic to handle it correctly. - * - * @important - * Special case for vertical content, read content - */ - const getNavigationForLeftPage = ( - position: ViewportPosition, - ): ViewportPosition => { - const spineItemOnPosition = - locator.getSpineItemFromPosition(position) || spineItemManager.get(0) - - const navigation = getNavigationForLeftSinglePage(position) - - // when we move withing vertical content, because only y moves, we don't need two navigation - if ( - spineItemOnPosition?.isUsingVerticalWriting() && - position.x === navigation.x - ) { - return getAdjustedPositionForSpread(navigation) - } - - if (context.state.isUsingSpreadMode) { - // in case of spread the entire screen is taken as one real page for vertical content - // in order to move out from it we add an extra page width. - // using `getNavigationForLeftSinglePage` again would keep x as it is and wrongly move y - // for the next item in case it's also a vertical content - if ( - spineItemOnPosition?.isUsingVerticalWriting() && - position.x !== navigation.x - ) { - return getAdjustedPositionForSpread( - wrapPositionWithSafeEdge( - context.isRTL() - ? { ...navigation, x: navigation.x + context.getPageSize().width } - : { - ...navigation, - x: navigation.x - context.getPageSize().width, - }, - ), - ) - } - - /** - * In vase we move vertically and the y is already different, we don't need a second navigation - * since we already jumped to a new screen - */ - if ( - settings.settings.computedPageTurnDirection === `vertical` && - position.y !== navigation.y - ) { - return getAdjustedPositionForSpread(navigation) - } - - const doubleNavigation = getNavigationForLeftSinglePage(navigation) - - return getAdjustedPositionForSpread(doubleNavigation) - } - - return getAdjustedPositionForSpread(navigation) - } - const getNavigationForPosition = (viewportPosition: ViewportPosition) => { const spineItem = locator.getSpineItemFromPosition(viewportPosition) @@ -371,7 +127,11 @@ export const createNavigationResolver = ({ spineItem, ) - return getAdjustedPositionForSpread(viewportNavigation) + return getAdjustedPositionForSpread({ + position: viewportNavigation, + pageSizeWidth: context.getPageSize().width, + visibleAreaRectWidth: context.state.visibleAreaRect.width, + }) } return { x: 0, y: 0 } @@ -400,8 +160,14 @@ export const createNavigationResolver = ({ : viewportPosition.y + context.state.visibleAreaRect.height * triggerPercentage const midScreenPositionSafePosition = wrapPositionWithSafeEdge({ - x: triggerXPosition, - y: triggerYPosition, + position: { + x: triggerXPosition, + y: triggerYPosition, + }, + isRTL: context.isRTL(), + pageSizeHeight: context.getPageSize().height, + pageSizeWidth: context.getPageSize().width, + spineItemManager, }) return getNavigationForPosition(midScreenPositionSafePosition) @@ -421,18 +187,46 @@ export const createNavigationResolver = ({ } return { + getNavigationForUrl: (url: string | URL) => + getNavigationForUrl({ + context, + spineItemManager, + spineLocator: locator, + url, + pageSizeWidth: context.getPageSize().width, + visibleAreaRectWidth: context.state.visibleAreaRect.width, + }), + getNavigationFromSpineItemPosition: (params: { + spineItemPosition: UnsafeSpineItemPosition + spineItem: SpineItem + }) => + getNavigationFromSpineItemPosition({ + ...params, + spineItemLocator: locator.spineItemLocator, + spineLocator: locator, + }), getNavigationForCfi, getNavigationForLastPage, getNavigationForSpineIndexOrId, - getNavigationForRightPage, - getNavigationForLeftPage, getNavigationForPosition, getMostPredominantNavigationForPosition, - wrapPositionWithSafeEdge, + wrapPositionWithSafeEdge: (position: ViewportPosition) => + wrapPositionWithSafeEdge({ + position, + isRTL: context.isRTL(), + pageSizeHeight: context.getPageSize().height, + pageSizeWidth: context.getPageSize().width, + spineItemManager, + }), isNavigationGoingForwardFrom, areNavigationDifferent, arePositionsDifferent, - getAdjustedPositionForSpread, + getAdjustedPositionForSpread: (position: ViewportPosition) => + getAdjustedPositionForSpread({ + position, + pageSizeWidth: context.getPageSize().width, + visibleAreaRectWidth: context.state.visibleAreaRect.width, + }), spineItemNavigator, cfiLocator, } diff --git a/packages/core/src/navigation/resolvers/getAdjustedPositionForSpread.ts b/packages/core/src/navigation/resolvers/getAdjustedPositionForSpread.ts new file mode 100644 index 00000000..aa25eaec --- /dev/null +++ b/packages/core/src/navigation/resolvers/getAdjustedPositionForSpread.ts @@ -0,0 +1,16 @@ +import { ViewportPosition } from "../ViewportNavigator" + +export const getAdjustedPositionForSpread = ({ + position: { x, y }, + pageSizeWidth, + visibleAreaRectWidth, +}: { + position: ViewportPosition + pageSizeWidth: number + visibleAreaRectWidth: number +}): ViewportPosition => { + const isOffsetNotAtEdge = x % visibleAreaRectWidth !== 0 + const correctedX = isOffsetNotAtEdge ? x - pageSizeWidth : x + + return { x: correctedX, y } +} diff --git a/packages/core/src/navigation/resolvers/getNavigationForUrl.ts b/packages/core/src/navigation/resolvers/getNavigationForUrl.ts index f421c916..9305a241 100644 --- a/packages/core/src/navigation/resolvers/getNavigationForUrl.ts +++ b/packages/core/src/navigation/resolvers/getNavigationForUrl.ts @@ -1,10 +1,10 @@ import { Context } from "../../context/Context" -import { NavigationResolver } from "./NavigationResolver" import { ViewportPosition } from "../ViewportNavigator" import { getClosestValidOffsetFromApproximateOffsetInPages } from "../../pagination/helpers" import { SpineLocator } from "../../spine/locationResolver" import { SpineItem } from "../../spineItem/createSpineItem" import { SpineItemManager } from "../../spineItemManager" +import { getAdjustedPositionForSpread } from "./getAdjustedPositionForSpread" const getSpineItemOffsetFromAnchor = ({ anchor, @@ -58,14 +58,16 @@ const getNavigationForAnchor = ({ anchor, spineItem, spineLocator, - navigationResolver, context, + pageSizeWidth, + visibleAreaRectWidth, }: { anchor: string spineItem: SpineItem spineLocator: SpineLocator - navigationResolver: NavigationResolver context: Context + pageSizeWidth: number + visibleAreaRectWidth: number }) => { const position = getSpinePositionFromSpineItemAnchor({ anchor, @@ -74,21 +76,27 @@ const getNavigationForAnchor = ({ spineLocator, }) - return navigationResolver.getAdjustedPositionForSpread(position) + return getAdjustedPositionForSpread({ + position, + pageSizeWidth, + visibleAreaRectWidth, + }) } export const getNavigationForUrl = ({ context, - navigationResolver, spineItemManager, spineLocator, url, + pageSizeWidth, + visibleAreaRectWidth, }: { url: string | URL spineItemManager: SpineItemManager spineLocator: SpineLocator context: Context - navigationResolver: NavigationResolver + pageSizeWidth: number + visibleAreaRectWidth: number }): { position: ViewportPosition; spineItemId: string } | undefined => { try { const validUrl = url instanceof URL ? url : new URL(url) @@ -105,12 +113,17 @@ export const getNavigationForUrl = ({ anchor: validUrl.hash, spineItem, context, - navigationResolver, spineLocator, + pageSizeWidth, + visibleAreaRectWidth, }) return { - position: navigationResolver.getAdjustedPositionForSpread(position), + position: getAdjustedPositionForSpread({ + position, + pageSizeWidth, + visibleAreaRectWidth, + }), spineItemId: existingSpineItem.id, } } diff --git a/packages/core/src/navigation/resolvers/wrapPositionWithSafeEdge.ts b/packages/core/src/navigation/resolvers/wrapPositionWithSafeEdge.ts new file mode 100644 index 00000000..d9524cf8 --- /dev/null +++ b/packages/core/src/navigation/resolvers/wrapPositionWithSafeEdge.ts @@ -0,0 +1,45 @@ +import { SpineItemManager } from "../../spineItemManager" +import { ViewportPosition } from "../ViewportNavigator" + +export const NAMESPACE = `spineNavigator` + +export const wrapPositionWithSafeEdge = ({ + position, + isRTL, + pageSizeWidth, + pageSizeHeight, + spineItemManager, +}: { + position: ViewportPosition + isRTL: boolean + pageSizeWidth: number + pageSizeHeight: number + spineItemManager: SpineItemManager +}) => { + // @todo use container width instead to increase performances + const lastSpineItem = spineItemManager.get(spineItemManager.getLength() - 1) + const distanceOfLastSpineItem = spineItemManager.getAbsolutePositionOf( + lastSpineItem || 0, + ) + + const maximumYOffset = distanceOfLastSpineItem.bottom - pageSizeHeight + const y = Math.min(Math.max(0, position.y), maximumYOffset) + + /** + * For RTL books we move from right to left so negative x. + * [-x, 0] + */ + if (isRTL) { + return { + x: Math.max(Math.min(0, position.x), distanceOfLastSpineItem.left), + y, + } + } + + const maximumXOffset = distanceOfLastSpineItem.right - pageSizeWidth + + return { + x: Math.min(Math.max(0, position.x), maximumXOffset), + y, + } +} diff --git a/packages/core/src/navigation/restoration/restoreNavigationForControlledPageTurnMode.ts b/packages/core/src/navigation/restoration/restoreNavigationForControlledPageTurnMode.ts index 025018c9..f0010b70 100644 --- a/packages/core/src/navigation/restoration/restoreNavigationForControlledPageTurnMode.ts +++ b/packages/core/src/navigation/restoration/restoreNavigationForControlledPageTurnMode.ts @@ -1,10 +1,6 @@ -import { Context } from "../../context/Context" import { SpineLocator } from "../../spine/locationResolver" -import { SpineItemLocator } from "../../spineItem/locationResolver" import { SpineItemManager } from "../../spineItemManager" import { InternalNavigationEntry } from "../InternalNavigator" -import { getNavigationForUrl } from "../resolvers/getNavigationForUrl" -import { getNavigationFromSpineItemPosition } from "../resolvers/getNavigationFromSpineItemPosition" import { NavigationResolver } from "../resolvers/NavigationResolver" export const restoreNavigationForControlledPageTurnMode = ({ @@ -12,15 +8,11 @@ export const restoreNavigationForControlledPageTurnMode = ({ navigation, navigationResolver, spineItemManager, - spineItemLocator, - context, }: { - context: Context navigation: InternalNavigationEntry spineLocator: SpineLocator navigationResolver: NavigationResolver spineItemManager: SpineItemManager - spineItemLocator: SpineItemLocator }) => { const spineItem = spineItemManager.get(navigation.spineItem) @@ -45,13 +37,7 @@ export const restoreNavigationForControlledPageTurnMode = ({ * restore from it first. */ if (navigation.url !== undefined) { - const urlResult = getNavigationForUrl({ - context, - navigationResolver, - spineItemManager, - spineLocator, - url: navigation.url, - }) + const urlResult = navigationResolver.getNavigationForUrl(navigation.url) if (urlResult) { return urlResult.position @@ -65,11 +51,9 @@ export const restoreNavigationForControlledPageTurnMode = ({ y: 0, } - return getNavigationFromSpineItemPosition({ + return navigationResolver.getNavigationFromSpineItemPosition({ spineItem, - spineItemLocator, spineItemPosition, - spineLocator, }) } diff --git a/packages/core/src/navigation/restoration/restorePosition.ts b/packages/core/src/navigation/restoration/restorePosition.ts index aab28e3f..4ffda020 100644 --- a/packages/core/src/navigation/restoration/restorePosition.ts +++ b/packages/core/src/navigation/restoration/restorePosition.ts @@ -198,8 +198,6 @@ export const restorePosition = ({ settings, spineLocator, navigationResolver, - spineItemLocator, - context, }: { spineLocator: SpineLocator settings: ReaderSettingsManager @@ -224,7 +222,5 @@ export const restorePosition = ({ spineLocator, navigationResolver, spineItemManager, - spineItemLocator, - context, }) } diff --git a/packages/core/src/spineItem/navigationResolver.ts b/packages/core/src/spineItem/navigationResolver.ts index 6d734c7e..dda30e7d 100644 --- a/packages/core/src/spineItem/navigationResolver.ts +++ b/packages/core/src/spineItem/navigationResolver.ts @@ -17,56 +17,6 @@ export const createNavigationResolver = ({ }) => { const spineItemLocator = createSpineItemLocator({ context, settings }) - const getNavigationForLeftPage = ( - position: UnsafeSpineItemPosition, - spineItem: SpineItem, - ): SafeSpineItemPosition => { - let nextPotentialPosition = { - x: position.x - context.getPageSize().width, - y: position.y, - } - - if (spineItem.isUsingVerticalWriting()) { - nextPotentialPosition = { - x: position.x, - y: position.y + context.getPageSize().height, - } - } - - const navigationPosition = - spineItemLocator.getSpineItemClosestPositionFromUnsafePosition( - nextPotentialPosition, - spineItem, - ) - - return navigationPosition - } - - const getNavigationForRightPage = ( - position: UnsafeSpineItemPosition, - spineItem: SpineItem, - ): SafeSpineItemPosition => { - let nextPotentialPosition = { - x: position.x + context.getPageSize().width, - y: position.y, - } - - if (spineItem.isUsingVerticalWriting()) { - nextPotentialPosition = { - x: position.x, - y: position.y - context.getPageSize().height, - } - } - - const navigationPosition = - spineItemLocator.getSpineItemClosestPositionFromUnsafePosition( - nextPotentialPosition, - spineItem, - ) - - return navigationPosition - } - const getNavigationForLastPage = ( spineItem: SpineItem, ): SafeSpineItemPosition => { @@ -117,8 +67,6 @@ export const createNavigationResolver = ({ } return { - getNavigationForLeftPage, - getNavigationForRightPage, getNavigationForLastPage, getNavigationForPage, getNavigationForPosition,