@papyrus-sdk/ui-react-native 0.2.35-beta.10 → 0.2.35-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@papyrus-sdk/ui-react-native",
3
- "version": "0.2.35-beta.10",
3
+ "version": "0.2.35-beta.11",
4
4
  "license": "MIT",
5
5
  "keywords": [
6
6
  "papyrus",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "peerDependencies": {
49
49
  "@papyrus-sdk/core": "0.2.23-beta.3",
50
- "@papyrus-sdk/engine-native": "0.2.22-beta.8",
50
+ "@papyrus-sdk/engine-native": "0.2.22-beta.9",
51
51
  "@papyrus-sdk/types": "0.2.16-beta.3",
52
52
  "@react-native-clipboard/clipboard": "^1.16.3",
53
53
  "react": ">=18.2.0 <20",
@@ -21799,6 +21799,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21799
21799
  if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
21800
21800
  }));
21801
21801
  rendition.on('relocated', (location) => {
21802
+ if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
21802
21803
  const start = location && location.start;
21803
21804
  let index = start && Number.isInteger(start.index) ? start.index : -1;
21804
21805
  if (index < 0 && start && start.href) index = getSpineIndexByHref(start.href);
@@ -21917,10 +21918,11 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21917
21918
  sendEvent('VIEWER_SCROLL', { offsetY });
21918
21919
  };
21919
21920
  const handleTouchStart = (event) => {
21920
- touchStart = getTouchPoint(event);
21921
+ touchStart = event?.touches?.length > 1 ? null : getTouchPoint(event);
21921
21922
  epubScrollDiagnostics && epubScrollDiagnostics.touchStart(event);
21922
21923
  };
21923
21924
  const handleTouchMove = (event) => {
21925
+ if (event?.touches?.length > 1) touchStart = null;
21924
21926
  epubScrollDiagnostics && epubScrollDiagnostics.touchMove(event);
21925
21927
  };
21926
21928
  const handleTouchEnd = (event) => {
@@ -21928,7 +21930,15 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21928
21930
  const start = touchStart;
21929
21931
  touchStart = null;
21930
21932
  const selectedText = event?.target?.ownerDocument?.defaultView?.getSelection?.()?.toString().trim() || '';
21931
- if (!selectedText && start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
21933
+ if (!selectedText && start && end && epubViewMode === 'single' && Math.abs(end.x-start.x) >= 48 && Math.abs(end.x-start.x) > Math.abs(end.y-start.y)*1.5) {
21934
+ const active = rendition;
21935
+ const interactionSession = documentSessionId;
21936
+ epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
21937
+ if (active !== rendition || interactionSession !== documentSessionId) return;
21938
+ return end.x < start.x ? active.next() : active.prev();
21939
+ });
21940
+ epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR', {message:String(error)}));
21941
+ } else if (!selectedText && start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
21932
21942
  sendEvent('VIEWER_TAP', {});
21933
21943
  }
21934
21944
  epubScrollDiagnostics && epubScrollDiagnostics.touchEnd(event);
@@ -21973,6 +21983,8 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21973
21983
  return { pageCount, outline };
21974
21984
  };
21975
21985
 
21986
+ let epubNavigationQueue = Promise.resolve();
21987
+ let epubNavigationRequest = 0;
21976
21988
  const displayEpubPage = async (pageIndex) => {
21977
21989
  if (!rendition) return;
21978
21990
  const item = spineItems[pageIndex];
@@ -21982,9 +21994,18 @@ https://github.com/nodeca/pako/blob/main/LICENSE
21982
21994
  target,
21983
21995
  pageIndex,
21984
21996
  });
21985
- await rendition.display(target);
21986
- currentPage = pageIndex + 1;
21987
- sendState();
21997
+ const activeRendition = rendition;
21998
+ const session = documentSessionId;
21999
+ const request = ++epubNavigationRequest;
22000
+ const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
22001
+ if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
22002
+ await activeRendition.display(target);
22003
+ if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
22004
+ currentPage = pageIndex + 1;
22005
+ sendState();
22006
+ });
22007
+ epubNavigationQueue = navigation;
22008
+ await navigation;
21988
22009
  };
21989
22010
 
21990
22011
  const getTextContent = async (pageIndex) => {
@@ -22086,6 +22107,7 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22086
22107
  };
22087
22108
 
22088
22109
  let nativeMenuSelection = null;
22110
+ let epubViewMode = "continuous";
22089
22111
 
22090
22112
  const handleCommand = async (message) => {
22091
22113
  const { id, kind, payload } = message;
@@ -22098,6 +22120,9 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22098
22120
  epubAnnotations = [];
22099
22121
  annotationDocumentId = undefined;
22100
22122
  zoom = 1.0;
22123
+ epubViewMode = "continuous";
22124
+ epubNavigationRequest += 1;
22125
+ epubNavigationQueue = Promise.resolve();
22101
22126
 
22102
22127
  if (currentType === 'text') {
22103
22128
  const result = await loadText(
@@ -22152,17 +22177,23 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22152
22177
  epubAnnotationRenderers.forEach(renderer => renderer.schedule());
22153
22178
  } else {
22154
22179
  const annotation = epubAnnotations.find(a => a.id === payload.annotationId);
22155
- if (!annotation) throw new Error('Annotation not available');
22180
+ if (!annotation || (annotation.anchor.documentId && annotation.anchor.documentId !== annotationDocumentId)) throw new Error('Annotation not available for this document');
22156
22181
  // Navigation uses the CFI, never a synthetic percentage/page rectangle.
22157
22182
  const navigationSession = documentSessionId;
22158
22183
  const activeRendition = rendition;
22159
22184
  // Open the confirmed chapter first, resolve its quote, then navigate its actual range.
22160
- await activeRendition.display(annotation.anchor.href);
22161
- if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22162
- const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
22163
- const range = contents && window.PapyrusEpubAnnotations.resolveRange(contents,annotation.anchor);
22164
- if(!range)throw new Error('EPUB anchor unresolved');
22165
- await activeRendition.display(contents.cfiFromRange(range));
22185
+ epubNavigationRequest += 1;
22186
+ const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
22187
+ if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22188
+ await activeRendition.display(annotation.anchor.href);
22189
+ if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
22190
+ const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
22191
+ const range = contents && window.PapyrusEpubAnnotations.resolveRange(contents,annotation.anchor);
22192
+ if(!range)throw new Error('EPUB anchor unresolved');
22193
+ await activeRendition.display(contents.cfiFromRange(range));
22194
+ });
22195
+ epubNavigationQueue = navigation;
22196
+ await navigation;
22166
22197
  }
22167
22198
  sendResponse(id,true,null); return;
22168
22199
  }
@@ -22189,6 +22220,33 @@ https://github.com/nodeca/pako/blob/main/LICENSE
22189
22220
  return;
22190
22221
  }
22191
22222
 
22223
+ if (kind === 'epub-turn-page') {
22224
+ if (epubViewMode !== 'single') {sendResponse(id,true,null); return;}
22225
+ if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB navigation');
22226
+ const active = rendition;
22227
+ epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
22228
+ if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
22229
+ return payload.direction < 0 ? active.prev() : active.next();
22230
+ });
22231
+ await epubNavigationQueue;
22232
+ sendResponse(id,true,null); return;
22233
+ }
22234
+
22235
+ if (kind === 'set-epub-view-mode') {
22236
+ if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB layout session');
22237
+ epubViewMode = payload.viewMode === 'single' ? 'single' : 'continuous';
22238
+ const active = rendition;
22239
+ epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
22240
+ if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
22241
+ const flow = epubViewMode === 'single' ? 'paginated' : 'scrolled-continuous';
22242
+ if (active.settings.flow === flow) return;
22243
+ active.flow(flow);
22244
+ return active.q.enqueue(() => Promise.resolve());
22245
+ });
22246
+ await epubNavigationQueue;
22247
+ sendResponse(id,true,null); return;
22248
+ }
22249
+
22192
22250
  if (kind === 'set-zoom') {
22193
22251
  zoom = Math.max(0.5, Math.min(4.0, payload.zoom || 1.0));
22194
22252
  applyZoom();
@@ -1425,6 +1425,7 @@
1425
1425
  if (selectionSession === documentSessionId && selectionRendition === rendition) epubAnnotationRenderers.forEach(renderer => renderer.schedule());
1426
1426
  }));
1427
1427
  rendition.on('relocated', (location) => {
1428
+ if (selectionSession !== documentSessionId || selectionRendition !== rendition) return;
1428
1429
  const start = location && location.start;
1429
1430
  let index = start && Number.isInteger(start.index) ? start.index : -1;
1430
1431
  if (index < 0 && start && start.href) index = getSpineIndexByHref(start.href);
@@ -1543,10 +1544,11 @@
1543
1544
  sendEvent('VIEWER_SCROLL', { offsetY });
1544
1545
  };
1545
1546
  const handleTouchStart = (event) => {
1546
- touchStart = getTouchPoint(event);
1547
+ touchStart = event?.touches?.length > 1 ? null : getTouchPoint(event);
1547
1548
  epubScrollDiagnostics && epubScrollDiagnostics.touchStart(event);
1548
1549
  };
1549
1550
  const handleTouchMove = (event) => {
1551
+ if (event?.touches?.length > 1) touchStart = null;
1550
1552
  epubScrollDiagnostics && epubScrollDiagnostics.touchMove(event);
1551
1553
  };
1552
1554
  const handleTouchEnd = (event) => {
@@ -1554,7 +1556,15 @@
1554
1556
  const start = touchStart;
1555
1557
  touchStart = null;
1556
1558
  const selectedText = event?.target?.ownerDocument?.defaultView?.getSelection?.()?.toString().trim() || '';
1557
- if (!selectedText && start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
1559
+ if (!selectedText && start && end && epubViewMode === 'single' && Math.abs(end.x-start.x) >= 48 && Math.abs(end.x-start.x) > Math.abs(end.y-start.y)*1.5) {
1560
+ const active = rendition;
1561
+ const interactionSession = documentSessionId;
1562
+ epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
1563
+ if (active !== rendition || interactionSession !== documentSessionId) return;
1564
+ return end.x < start.x ? active.next() : active.prev();
1565
+ });
1566
+ epubNavigationQueue.catch(error => sendEvent('EPUB_NAVIGATION_ERROR', {message:String(error)}));
1567
+ } else if (!selectedText && start && end && Math.hypot(end.x - start.x, end.y - start.y) <= 12) {
1558
1568
  sendEvent('VIEWER_TAP', {});
1559
1569
  }
1560
1570
  epubScrollDiagnostics && epubScrollDiagnostics.touchEnd(event);
@@ -1599,6 +1609,8 @@
1599
1609
  return { pageCount, outline };
1600
1610
  };
1601
1611
 
1612
+ let epubNavigationQueue = Promise.resolve();
1613
+ let epubNavigationRequest = 0;
1602
1614
  const displayEpubPage = async (pageIndex) => {
1603
1615
  if (!rendition) return;
1604
1616
  const item = spineItems[pageIndex];
@@ -1608,9 +1620,18 @@
1608
1620
  target,
1609
1621
  pageIndex,
1610
1622
  });
1611
- await rendition.display(target);
1612
- currentPage = pageIndex + 1;
1613
- sendState();
1623
+ const activeRendition = rendition;
1624
+ const session = documentSessionId;
1625
+ const request = ++epubNavigationRequest;
1626
+ const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
1627
+ if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
1628
+ await activeRendition.display(target);
1629
+ if (request !== epubNavigationRequest || session !== documentSessionId || activeRendition !== rendition) return;
1630
+ currentPage = pageIndex + 1;
1631
+ sendState();
1632
+ });
1633
+ epubNavigationQueue = navigation;
1634
+ await navigation;
1614
1635
  };
1615
1636
 
1616
1637
  const getTextContent = async (pageIndex) => {
@@ -1712,6 +1733,7 @@
1712
1733
  };
1713
1734
 
1714
1735
  let nativeMenuSelection = null;
1736
+ let epubViewMode = "continuous";
1715
1737
 
1716
1738
  const handleCommand = async (message) => {
1717
1739
  const { id, kind, payload } = message;
@@ -1724,6 +1746,9 @@
1724
1746
  epubAnnotations = [];
1725
1747
  annotationDocumentId = undefined;
1726
1748
  zoom = 1.0;
1749
+ epubViewMode = "continuous";
1750
+ epubNavigationRequest += 1;
1751
+ epubNavigationQueue = Promise.resolve();
1727
1752
 
1728
1753
  if (currentType === 'text') {
1729
1754
  const result = await loadText(
@@ -1778,17 +1803,23 @@
1778
1803
  epubAnnotationRenderers.forEach(renderer => renderer.schedule());
1779
1804
  } else {
1780
1805
  const annotation = epubAnnotations.find(a => a.id === payload.annotationId);
1781
- if (!annotation) throw new Error('Annotation not available');
1806
+ if (!annotation || (annotation.anchor.documentId && annotation.anchor.documentId !== annotationDocumentId)) throw new Error('Annotation not available for this document');
1782
1807
  // Navigation uses the CFI, never a synthetic percentage/page rectangle.
1783
1808
  const navigationSession = documentSessionId;
1784
1809
  const activeRendition = rendition;
1785
1810
  // Open the confirmed chapter first, resolve its quote, then navigate its actual range.
1786
- await activeRendition.display(annotation.anchor.href);
1787
- if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
1788
- const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
1789
- const range = contents && window.PapyrusEpubAnnotations.resolveRange(contents,annotation.anchor);
1790
- if(!range)throw new Error('EPUB anchor unresolved');
1791
- await activeRendition.display(contents.cfiFromRange(range));
1811
+ epubNavigationRequest += 1;
1812
+ const navigation = epubNavigationQueue.catch(() => {}).then(async () => {
1813
+ if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
1814
+ await activeRendition.display(annotation.anchor.href);
1815
+ if(navigationSession !== documentSessionId || activeRendition !== rendition)throw new Error('Stale EPUB navigation');
1816
+ const contents = activeRendition.getContents().find(c => spineItems[c.sectionIndex]?.href === annotation.anchor.href);
1817
+ const range = contents && window.PapyrusEpubAnnotations.resolveRange(contents,annotation.anchor);
1818
+ if(!range)throw new Error('EPUB anchor unresolved');
1819
+ await activeRendition.display(contents.cfiFromRange(range));
1820
+ });
1821
+ epubNavigationQueue = navigation;
1822
+ await navigation;
1792
1823
  }
1793
1824
  sendResponse(id,true,null); return;
1794
1825
  }
@@ -1815,6 +1846,33 @@
1815
1846
  return;
1816
1847
  }
1817
1848
 
1849
+ if (kind === 'epub-turn-page') {
1850
+ if (epubViewMode !== 'single') {sendResponse(id,true,null); return;}
1851
+ if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB navigation');
1852
+ const active = rendition;
1853
+ epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
1854
+ if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
1855
+ return payload.direction < 0 ? active.prev() : active.next();
1856
+ });
1857
+ await epubNavigationQueue;
1858
+ sendResponse(id,true,null); return;
1859
+ }
1860
+
1861
+ if (kind === 'set-epub-view-mode') {
1862
+ if (currentType !== 'epub' || payload.documentSessionId !== documentSessionId) throw new Error('Stale EPUB layout session');
1863
+ epubViewMode = payload.viewMode === 'single' ? 'single' : 'continuous';
1864
+ const active = rendition;
1865
+ epubNavigationQueue = epubNavigationQueue.catch(() => {}).then(() => {
1866
+ if (active !== rendition || payload.documentSessionId !== documentSessionId) return;
1867
+ const flow = epubViewMode === 'single' ? 'paginated' : 'scrolled-continuous';
1868
+ if (active.settings.flow === flow) return;
1869
+ active.flow(flow);
1870
+ return active.q.enqueue(() => Promise.resolve());
1871
+ });
1872
+ await epubNavigationQueue;
1873
+ sendResponse(id,true,null); return;
1874
+ }
1875
+
1818
1876
  if (kind === 'set-zoom') {
1819
1877
  zoom = Math.max(0.5, Math.min(4.0, payload.zoom || 1.0));
1820
1878
  applyZoom();