import React from 'react' ; import { Provider } from "polar-shared/src/util/Providers" ; import { createObservableStore , SetStore } from "../../../web/js/react/store/ObservableStore" ; import { IDocMeta } from "polar-shared/src/metadata/IDocMeta" ; import { IDStr , URLStr } from "polar-shared/src/util/Strings" ; import { DocMetaFileRefs } from "../../../web/js/datastore/DocMetaRef" ; import { Backend } from 'polar-shared/src/datastore/Backend' ; import { usePersistenceLayerContext } from "../../repository/js/persistence_layer/PersistenceLayerApp" ; import { useAnnotationSidebarCallbacks } from './AnnotationSidebarStore' ; import { useDocMetaContext } from "../../../web/js/annotation_sidebar/DocMetaContextProvider" ; import { AnnotationMutationsContextProvider , IAnnotationMutationCallbacks } from "../../../web/js/annotation_sidebar/AnnotationMutationsContext" ; import { NULL_FUNCTION } from "polar-shared/src/util/Functions" ; import { Percentages } from "polar-shared/src/util/Percentages" ; import { Pagemarks } from "../../../web/js/metadata/Pagemarks" ; import { Preconditions } from "polar-shared/src/Preconditions" ; import { IPagemark } from "polar-shared/src/metadata/IPagemark" ; import { PDFScales , SCALE_VALUE_PAGE_WIDTH , ScaleLevelTuple } from "./ScaleLevels" ; import { PageNavigator } from "./PageNavigator" ; import { useLogger } from "../../../web/js/mui/MUILogger" ; import { DocViewerSnapshots } from "./DocViewerSnapshots" ; import { Arrays } from 'polar-shared/src/util/Arrays' ; import { Direction , FluidPagemarkFactory , NullFluidPagemarkFactory } from "./FluidPagemarkFactory" ; import { IPagemarkRect } from "polar-shared/src/metadata/IPagemarkRect" ; import { PagemarkRect } from "../../../web/js/metadata/PagemarkRect" ; import { IPagemarkRef } from "polar-shared/src/metadata/IPagemarkRef" ; import { PagemarkMode } from "polar-shared/src/metadata/PagemarkMode" ; import { Numbers } from 'polar-shared/src/util/Numbers' ; import { arrayStream } from "polar-shared/src/util/ArrayStreams" ; import { Hashcodes } from 'polar-shared/src/util/Hashcodes' ; import { TaggedCallbacks } from "../../repository/js/annotation_repo/TaggedCallbacks" ; import { Tag , Tags } from "polar-shared/src/tags/Tags" ; import { useDialogManager } from "../../../web/js/mui/dialogs/MUIDialogControllers" ; import { LocalRelatedTagsStore } from "../../../web/js/tags/related/LocalRelatedTagsStore" ; import { IRelatedTagsData , RelatedTagsManager } from "../../../web/js/tags/related/RelatedTagsManager" ; import TaggedCallbacksOpts = TaggedCallbacks . TaggedCallbacksOpts ; import ComputeNewTagsStrategy = Tags . ComputeNewTagsStrategy ; import ITagsHolder = TaggedCallbacks . ITagsHolder ; import { DocMetas } from "../../../web/js/metadata/DocMetas" ; import isEqual from 'react-fast-compare' ; import computeNextZoomLevel = PDFScales . computeNextZoomLevel ; import ScaleDelta = PDFScales . ScaleDelta ; import { useAnnotationMutationCallbacksFactory } from "../../../web/js/annotation_sidebar/AnnotationMutationCallbacks" ; import { UUIDs } from "../../../web/js/metadata/UUIDs" ; import { IOutline } from './outline/IOutline' ; import { OutlineNavigator } from "./outline/IOutlineItem" ; import { Analytics } from '../../../web/js/analytics/Analytics' ; import { ColorStr } from "../../../web/js/ui/colors/ColorSelectorBox" ; import { ActiveHighlightData } from './annotations/annotation_popup/AnnotationPopupHooks' ; /**
 * Lightweight metadata describing the currently loaded document.
 */ export interface IDocDescriptor { readonly nrPages : number ; readonly fingerprint : IDStr ; } export interface IDocScale { readonly scale : ScaleLevelTuple ; /**
     * The applied scale value derived from a string like 'page-width' but
     * actually computed as something like 1.2
     */ readonly scaleValue : number ; } export type ScaleValue = number ; export type Resizer = ( ) => void ; /**
 * Scale us and then return the new scale as a numbers
 */ export type ScaleLeveler = ( scale : ScaleLevelTuple ) => ScaleValue ; export interface IDocViewerStore { /**
     * The DocMeta currently being managed.
     */ readonly docMeta ? : IDocMeta ; /**
     * The current page number we're on.
     */ readonly page : number ; /**
     * True when the document we're viewing assert that it has loaded.
     */ readonly docLoaded : boolean ; readonly docDescriptor ? : IDocDescriptor ; /**
     * The storage URL for the document this docMeta references.
     */ readonly docURL ? : URLStr ; readonly pageNavigator ? : PageNavigator ; /**
     * Resizer that forces the current doc to fit inside its container properly
     */ readonly resizer ? : Resizer ; readonly docScale ? : IDocScale ; readonly scaleLeveler ? : ScaleLeveler ; readonly pendingWrites : number ; readonly hasPendingWrites ? : boolean ; readonly fluidPagemarkFactory : FluidPagemarkFactory ; readonly outline ? : IOutline ; readonly outlineNavigator ? : OutlineNavigator ; readonly textHighlightColor ? : ColorStr ; readonly activeHighlight ? : ActiveHighlightData ; readonly areaHighlightMode : boolean ; } export interface IPagemarkCoverage { readonly percentage : number ; readonly rect : PagemarkRect ; readonly range : Range | undefined ; } export interface IPagemarkCreateOrUpdate { /**
     * The DOM range for the covering pagemark (use for fluid pagemarks).
     */ readonly range : Range | undefined ; } export interface IPagemarkCreateToPoint extends IPagemarkCreateOrUpdate { readonly type : 'create-to-point' ; readonly x : number ; readonly y : number ; readonly width : number ; readonly height : number ; readonly pageNum : number ; } export interface IPagemarkCreateFromPage extends IPagemarkCreateOrUpdate { readonly type : 'create-from-page' ; readonly x : number ; readonly y : number ; readonly width : number ; readonly height : number ; readonly pageNum : number ; readonly fromPage : number ; } export interface IPagemarkCreateForEntireDocument { readonly type : 'create-for-entire-document' ; } export interface IPagemarkUpdate extends IPagemarkCreateOrUpdate { readonly type : 'update' ; readonly pageNum : number ; /**
     * The existing pagemark to update.
     */ readonly existing : IPagemark ; /**
     * The new pagemark's percentage.
     */ readonly percentage : number ; /**
     * The new pagemark's covering rect (for PDF)
     */ readonly rect : IPagemarkRect ; /**
     * Direction must be specified when updating / resizing pagemarks.
     */ readonly direction : Direction ; } export interface IPagemarkUpdateMode { readonly type : 'update-mode' ; readonly pageNum : number ; /**
     * The existing pagemark to update.
     */ readonly existing : IPagemark ; /**
     * The new pagemark's percentage.
     */ readonly mode : PagemarkMode ; } export interface IPagemarkDelete { readonly type : 'delete' ; readonly pageNum : number ; readonly pagemark : IPagemark ; } export type IPagemarkMutation = IPagemarkCreateToPoint | IPagemarkCreateFromPage | IPagemarkUpdate | IPagemarkDelete | IPagemarkUpdateMode | IPagemarkCreateForEntireDocument ; /**
 * The type of update for setDocMeta.
 *
 * update: regular / direct store update
 * snapshot: update from a firestore snapshot
 *
 */ export type SetDocMetaType = 'update' | 'snapshot-server' | 'snapshot-local' ; export interface IDocViewerCallbacks { /**
     * Update the DocMeta in the store after a local mutation including
     * increment the UUID.
     */ readonly updateDocMeta : ( docMeta : IDocMeta ) => IDocMeta ; readonly setDocMeta : ( docMeta : IDocMeta , hasPendingWrites : boolean , type : SetDocMetaType ) => void ; readonly setDocDescriptor : ( docDescriptor : IDocDescriptor ) => void ; readonly setDocScale : ( docScale : IDocScale ) => void ; readonly setDocLoaded : ( docLoaded : false ) => void ; readonly setResizer : ( resizer : Resizer ) => void ; readonly setScaleLeveler : ( scaleLeveler : ScaleLeveler ) => void ; readonly annotationMutations : IAnnotationMutationCallbacks ; readonly onPageJump : ( page : number ) => void ; readonly setScale : ( scaleLevel : ScaleLevelTuple ) => void ; readonly doZoom : ( delta : ScaleDelta ) => void ; readonly doZoomRestore : ( ) => void ; readonly setPage : ( page : number ) => void ; readonly setFluidPagemarkFactory : ( fluidPagemarkFactory : FluidPagemarkFactory ) => void ; readonly setDocFlagged : ( flagged : boolean ) => void ; readonly setDocArchived : ( archived : boolean ) => void ; readonly onDocTagged : ( ) => void ; readonly toggleDocFlagged : ( ) => void ; readonly toggleDocArchived : ( ) => void ; readonly docMetaProvider : ( ) => IDocMeta | undefined ; readonly setOutline : ( outline : IOutline | undefined ) => void ; readonly setOutlineNavigator : ( outlineNavigator : OutlineNavigator ) => void ; onPagemark ( opts ) : ReadonlyArray < IPagemarkRef > ; setPageNavigator ( pageNavigator ) : void ; onPagePrev ( ) : void ; onPageNext ( ) : void ; readonly setColumnLayout : ( columLayout : number ) => void ; readonly setTextHighlightColor : ( color ? : ColorStr ) => void ; readonly setActiveHighlight : ( data ? : ActiveHighlightData ) => void ; readonly setAreaHighlightMode : ( state : boolean ) => void ; readonly toggleAreaHighlightMode : ( ) => void ; } const initialStore = { page : 1 , docLoaded : false , pendingWrites : 0 , fluidPagemarkFactory : new NullFluidPagemarkFactory ( ) , areaHighlightMode : false , } ; interface Mutator { } function mutatorFactory ( storeProvider , setStore ) { function reduce ( ) { return undefined ; } return { } ; } function useCallbacksFactory ( storeProvider , setStore , mutator ) { const log = useLogger ( ) ; const docMetaContext = useDocMetaContext ( ) ; const persistenceLayerContext = usePersistenceLayerContext ( ) ; const annotationSidebarCallbacks = useAnnotationSidebarCallbacks ( ) ; const dialogs = useDialogManager ( ) ; const annotationMutationCallbacksFactory = useAnnotationMutationCallbacksFactory ( ) ; return React . useMemo ( ( ) => { async function writeUpdatedDocMetas ( updatedDocMetas ) { function mutatePendingWrites ( delta ) { const store = storeProvider ( ) ; const pendingWrites = store . pendingWrites + delta ; const hasPendingWrites = pendingWrites > 0 ; setStore ( { ... store , pendingWrites , hasPendingWrites } ) ; } try { mutatePendingWrites ( 1 ) ; await annotationMutations . writeUpdatedDocMetas ( updatedDocMetas ) ; } finally { mutatePendingWrites ( - 1 ) ; } } function updateDocMeta ( docMeta ) { const updated = DocMetas . updated ( docMeta ) ; setDocMeta ( updated , true , 'update' ) ; return updated ; } function setDocMeta ( docMeta , hasPendingWrites , type ) { Preconditions . assertPresent ( docMeta , 'docMeta' ) ; function doSetStoreAndUpdate ( docMeta ) { const store = storeProvider ( ) ; const computeDocURL = ( ) : URLStr | undefined => { if ( docMeta ) { const docMetaFileRef = DocMetaFileRefs . createFromDocMeta ( docMeta ) ; const persistenceLayer = persistenceLayerContext . persistenceLayerProvider ( ) ; if ( docMetaFileRef . docFile ) { const file = persistenceLayer . getFile ( Backend . STASH , docMetaFileRef . docFile ) ; return file . url ; } } else { console . warn ( "No docMeta for setDocMeta" ) ; } return undefined ; } ; const docURL = store . docURL || computeDocURL ( ) ; setStore ( { ... store , docMeta , docURL , hasPendingWrites } ) ; } const store = storeProvider ( ) ; const docViewerSnapshotUpdate = DocViewerSnapshots . computeUpdateType3 ( store . docMeta ?. docInfo ?. uuid , docMeta . docInfo . uuid ) ; console . log ( `DOC_WRITE: Update for docMeta was ${ docViewerSnapshotUpdate . type } for type=${ type }, cmp=${ docViewerSnapshotUpdate . cmp }: \n    curr=${ UUIDs . format ( store . docMeta ?. docInfo ?. uuid ) } \n    next=${ UUIDs . format ( docMeta . docInfo . uuid ) }` ) ; if ( [ 'snapshot-local' , 'snapshot-server' ] . includes ( type ) && docViewerSnapshotUpdate . type === 'self' ) { if ( type === 'snapshot-server' ) { setStore ( { ... store , hasPendingWrites : false } ) ; } console . log ( `DOC_WRITE: Skipping update (type=${ type }, update type: ${ docViewerSnapshotUpdate . type })` ) ; return ; } if ( docViewerSnapshotUpdate . type === 'stale' ) { console . log ( "DOC_WRITE: Skipping update (stale)" ) ; return ; } /**
             * Internally the docMeta may NOT actually be a new object because some functions update the DocMeta
             * directly (which they really shouldn't be doing) so we just perform a deep copy of the object so
             * that the stores actually fire that they were updated because they just do a shallow/quick object
             * comparison not a deep comparison.
             */ function cloneDocMetaAndUpdate ( ) { console . log ( `DOC_WRITE: APPLYING update (type=${ type }, update type: ${ docViewerSnapshotUpdate . type })` ) ; docMeta = DocMetas . copyOf ( docMeta ) ; doSetStoreAndUpdate ( docMeta ) ; docMetaContext . setDoc ( { docMeta , mutable : true } ) ; annotationSidebarCallbacks . setDocMeta ( docMeta ) ; } cloneDocMetaAndUpdate ( ) ; } function setDocDescriptor ( docDescriptor ) { const store = storeProvider ( ) ; if ( isEqual ( store . docDescriptor , docDescriptor ) ) { return ; } setStore ( { ... store , docDescriptor } ) ; } function setDocScale ( docScale ) { const store = storeProvider ( ) ; setStore ( { ... store , docScale } ) ; } function setTextHighlightColor ( color ? ) { const store = storeProvider ( ) ; setStore ( { ... store , textHighlightColor : color } ) ; } function setActiveHighlight ( activeHighlight ? ) { const store = storeProvider ( ) ; setStore ( { ... store , activeHighlight } ) ; } function setDocLoaded ( docLoaded ) { const store = storeProvider ( ) ; setStore ( { ... store , docLoaded } ) ; } function setResizer ( resizer ) { const store = storeProvider ( ) ; setStore ( { ... store , resizer } ) ; } function setScaleLeveler ( scaleLeveler ) { const store = storeProvider ( ) ; setStore ( { ... store , scaleLeveler } ) ; } function setScale ( scaleLevel ) { const store = storeProvider ( ) ; const { scaleLeveler } = store ; if ( scaleLeveler ) { const scaleValue = scaleLeveler ( scaleLevel ) ; const docScale = { scale : scaleLevel , scaleValue , } ; setDocScale ( docScale ) ; } } function doZoom ( delta ) { const { docScale , scaleLeveler } = storeProvider ( ) ; if ( ! scaleLeveler ) { return ; } const nextScale = computeNextZoomLevel ( delta , docScale ?. scaleValue ) ; if ( nextScale ) { setScale ( nextScale ) ; } } function doZoomRestore ( ) { const { scaleLeveler } = storeProvider ( ) ; if ( ! scaleLeveler ) { return ; } setScale ( SCALE_VALUE_PAGE_WIDTH ) ; } function setFluidPagemarkFactory ( fluidPagemarkFactory ) { const store = storeProvider ( ) ; setStore ( { ... store , fluidPagemarkFactory } ) ; } const annotationMutations = annotationMutationCallbacksFactory ( updateStore , NULL_FUNCTION ) ; function updateStore ( docMetas ) { return docMetas . map ( docMeta => updateDocMeta ( docMeta ) ) ; } function onPagemark ( mutation ) { function updatePagemarkRange ( pagemark , range , direction , existing ) { const store = storeProvider ( ) ; const fluidPagemark = store . fluidPagemarkFactory . create ( { range , direction , existing } ) ; pagemark . range = fluidPagemark ?. range ; } function createPagemarkToPoint ( opts , start ? ) { const store = storeProvider ( ) ; const { docMeta } = store ; if ( ! docMeta ) { return [ ] ; } const { pageNum } = opts ; const verticalOffsetWithinPageElement = opts . y ; const pageHeight = opts . height ; const percentage = Percentages . calculate ( verticalOffsetWithinPageElement , pageHeight , { noRound : true } ) ; console . log ( "Created pagemark with percentage: " , percentage ) ; function deletePagemarkForCurrentPage ( pageNum ) { Preconditions . assertNumber ( pageNum , "pageNum" ) ; Pagemarks . deletePagemark ( docMeta ! , pageNum ) ; } function createPagemarksForRange ( endPageNum , percentage ) { const createdPagemarks = Pagemarks . updatePagemarksForRange ( docMeta ! , endPageNum , percentage , { start } ) ; if ( createdPagemarks . length > 0 ) { const last = Arrays . last ( createdPagemarks ) ! ; const fluidPagemark = store . fluidPagemarkFactory . create ( { range : opts . range , direction : undefined , existing : undefined } ) ; last . pagemark . range = fluidPagemark ?. range ; updatePagemarkRange ( last . pagemark , opts . range , undefined , undefined ) ; } return createdPagemarks ; } deletePagemarkForCurrentPage ( pageNum ) ; const createdPagemarks = createPagemarksForRange ( pageNum , percentage ) ; writeUpdatedDocMetas ( [ docMeta ] ) . then ( ( ) => Analytics . event2 ( 'doc-pagemarkCreated' ) ) . catch ( err => log . error ( err ) ) ; return createdPagemarks ; } function createPagemarkFromPage ( opts ) { const createOpts = { ... opts , type : 'create-to-point' , range : opts . range , } ; return createPagemarkToPoint ( createOpts , opts . fromPage ) ; } function updatePagemark ( mutation ) { const store = storeProvider ( ) ; const docMeta = store . docMeta ! ; function createPagemark ( ) { const newPagemark = Object . assign ( { } , mutation . existing ) ; newPagemark . percentage = mutation . percentage ; newPagemark . rect = mutation . rect ; return newPagemark ; } const pagemark = createPagemark ( ) ; updatePagemarkRange ( pagemark , mutation . range , mutation . direction , mutation . existing ) ; Pagemarks . updatePagemark ( docMeta , mutation . pageNum , pagemark ) ; updateDocMeta ( docMeta ) ; writeUpdatedDocMetas ( [ docMeta ] ) . catch ( err => log . error ( err ) ) ; return [ { pageNum : mutation . pageNum , pagemark } ] ; } function deletePagemark ( mutation ) { const store = storeProvider ( ) ; const docMeta = store . docMeta ! ; const { pageNum , pagemark } = mutation ; Pagemarks . deletePagemark ( docMeta , pageNum , pagemark . id ) ; updateDocMeta ( docMeta ) ; writeUpdatedDocMetas ( [ docMeta ] ) . catch ( err => log . error ( err ) ) ; } function updatePagemarkMode ( mutation ) { const store = storeProvider ( ) ; const docMeta = store . docMeta ! ; const { pageNum , existing } = mutation ; existing . mode = mutation . mode ; Pagemarks . updatePagemark ( docMeta , pageNum , existing ) ; updateDocMeta ( docMeta ) ; writeUpdatedDocMetas ( [ docMeta ] ) . catch ( err => log . error ( err ) ) ; return [ ] ; } function createPagemarksForEntireDocument ( ) { const store = storeProvider ( ) ; const docMeta = store . docMeta ! ; interface PagemarksForPage { readonly pageNum : number ; readonly pagemarks : ReadonlyArray < IPagemark > ; } function toPagemarksForPage ( pageNum ) { const pageMeta = DocMetas . getPageMeta ( docMeta , pageNum ) ; const pagemarks = Object . values ( pageMeta . pagemarks || { } ) ; return { pageNum , pagemarks } ; } function computePagemarksForPageMapping ( ) { const pageNumbers = Numbers . range ( 1 , docMeta . docInfo . nrPages ) ; return pageNumbers . map ( toPagemarksForPage ) ; } const pagemarksForPageMapping = computePagemarksForPageMapping ( ) ; const pagemarkBlocks = arrayStream ( pagemarksForPageMapping ) . merge ( ( a , b ) => a . pagemarks . length === 0 && b . pagemarks . length === 0 ) . collect ( ) ; const batch = Hashcodes . createRandomID ( ) ; for ( const pagemarkBlock of pagemarkBlocks ) { if ( pagemarkBlock . length > 0 ) { const start = Arrays . first ( pagemarkBlock ) ! . pageNum ; const end = Arrays . last ( pagemarkBlock ) ! . pageNum ; Pagemarks . updatePagemarksForRange ( docMeta ! , end , 100 , { start , batch } ) ; } } updateDocMeta ( docMeta ) ; writeUpdatedDocMetas ( [ docMeta ] ) . then ( ( ) => Analytics . event2 ( 'doc-markAsRead' ) ) . catch ( err => log . error ( err ) ) ; return [ ] ; } switch ( mutation . type ) { case "create-to-point" : return createPagemarkToPoint ( mutation ) ; case "create-from-page" : return createPagemarkFromPage ( mutation ) ; case "update" : return updatePagemark ( mutation ) ; case "delete" : deletePagemark ( mutation ) ; return [ ] ; case "update-mode" : return updatePagemarkMode ( mutation ) ; case "create-for-entire-document" : return createPagemarksForEntireDocument ( ) ; } } function setPageNavigator ( pageNavigator ) { Preconditions . assertPresent ( pageNavigator , "pageNavigator" ) ; const store = storeProvider ( ) ; setStore ( { ... store , pageNavigator } ) ; } function onPageJump ( page ) { async function doAsync ( ) { await doPageJump ( page ) ; } doAsync ( ) . catch ( err => log . error ( 'Could not handle page jump: ' , err ) ) ; } async function doPageJump ( newPage ) { const store = storeProvider ( ) ; const { pageNavigator } = store ; if ( ! pageNavigator ) { return ; } if ( newPage <= 0 ) { return ; } if ( newPage > pageNavigator . count ) { return ; } if ( newPage === pageNavigator . get ( ) ) { return ; } await pageNavigator . jumpToPage ( newPage ) ; setStore ( { ... store , page : newPage } ) ; } function doPageNav ( delta ) { async function doAsync ( ) { const store = storeProvider ( ) ; const { page } = store ; const newPage = page + delta ; await doPageJump ( newPage ) ; } doAsync ( ) . catch ( err => log . error ( "Could not handle page nav: " , err ) ) ; } function onPageNext ( ) { doPageNav ( 1 ) ; } function onPagePrev ( ) { doPageNav ( - 1 ) ; } function setPage ( page ) { const store = storeProvider ( ) ; if ( store . page === page ) { return ; } setStore ( { ... store , page } ) ; } /**
         * Execute the mutator on the DocMeta and write the docMeta and update the
         * UI store.
         */ function writeDocMetaMutation ( mutator ) { const store = storeProvider ( ) ; const { docMeta } = store ; if ( ! docMeta ) { return ; } mutator ( docMeta ) ; writeUpdatedDocMetas ( [ docMeta ] ) . catch ( err => log . error ( err ) ) ; } function setDocFlagged ( flagged ) { writeDocMetaMutation ( docMeta => docMeta . docInfo . flagged = flagged ) ; Analytics . event2 ( "doc-flagged" , { count : 1 , flagged } ) ; } function setDocArchived ( archived ) { writeDocMetaMutation ( docMeta => docMeta . docInfo . archived = archived ) ; Analytics . event2 ( "doc-archived" , { count : 1 , archived } ) ; } interface ITaggedDocMetaHolder extends ITagsHolder { readonly docMeta : IDocMeta ; } function doDocTagged ( targets , tags , strategy ) { if ( targets . length === 0 ) { log . warn ( "doDocTagged: No targets" ) ; } for ( const target of targets ) { const { docMeta } = target ; const newTags = Tags . computeNewTags ( docMeta . docInfo . tags , tags , strategy ) ; writeDocMetaMutation ( docMeta => docMeta . docInfo . tags = Tags . toMap ( newTags ) ) ; } Analytics . event2 ( "doc-tagged" , { count : targets . length } ) ; } function onDocTagged ( ) { const { docMeta } = storeProvider ( ) ; if ( ! docMeta ) { return ; } function targets ( ) { if ( ! docMeta ) { return [ ] ; } return [ { docMeta , tags : docMeta . docInfo . tags } ] ; } function createRelatedTagsManager ( ) { function createNullRelatedTagsData ( ) { console . warn ( "Using null related tags data" ) ; return { docTagsIndex : { } , tagDocsIndex : { } , tagsIndex : { } } ; } const data = LocalRelatedTagsStore . read ( ) || createNullRelatedTagsData ( ) ; return new RelatedTagsManager ( data ) ; } const relatedTagsManager = createRelatedTagsManager ( ) ; const relatedOptionsCalculator = relatedTagsManager . toRelatedOptionsCalculator ( ) ; const tagsProvider = ( ) => relatedTagsManager . tags ( ) ; const opts = { targets , tagsProvider , dialogs , doTagged : doDocTagged , relatedOptionsCalculator } ; const callback = TaggedCallbacks . create ( opts ) ; callback ( ) ; } function toggleDocFlagged ( ) { const { docMeta } = storeProvider ( ) ; if ( ! docMeta ) { return ; } setDocFlagged ( ! docMeta ?. docInfo ?. flagged ) ; } function toggleDocArchived ( ) { const { docMeta } = storeProvider ( ) ; if ( ! docMeta ) { return ; } setDocArchived ( ! docMeta ?. docInfo ?. archived ) ; } function setOutline ( outline ) { const store = storeProvider ( ) ; setStore ( { ... store , outline } ) ; } function setOutlineNavigator ( outlineNavigator ) { const store = storeProvider ( ) ; setStore ( { ... store , outlineNavigator } ) ; } function docMetaProvider ( ) { const store = storeProvider ( ) ; return store . docMeta ; } function setColumnLayout ( columnLayout ) { writeDocMetaMutation ( docMeta => docMeta . docInfo . columnLayout = columnLayout ) ; Analytics . event2 ( 'doc-columnLayoutChanged' , { columns : columnLayout } ) ; } function toggleAreaHighlightMode ( ) { const store = storeProvider ( ) ; setStore ( { ... store , areaHighlightMode : ! store . areaHighlightMode } ) ; } function setAreaHighlightMode ( state ) { const store = storeProvider ( ) ; setStore ( { ... store , areaHighlightMode : state } ) ; } return { updateDocMeta , setDocMeta , setDocDescriptor , setDocScale , setDocLoaded , setPageNavigator , annotationMutations , onPagemark , onPageJump , onPagePrev , onPageNext , setResizer , setScaleLeveler , setScale , doZoom , doZoomRestore , setPage , setFluidPagemarkFactory , setDocFlagged , setDocArchived , onDocTagged , toggleDocFlagged , toggleDocArchived , setOutline , setOutlineNavigator , docMetaProvider , setColumnLayout , setTextHighlightColor , setActiveHighlight , toggleAreaHighlightMode , setAreaHighlightMode , } ; } , [ log , docMetaContext , persistenceLayerContext , annotationSidebarCallbacks , dialogs , annotationMutationCallbacksFactory , setStore , storeProvider ] ) ; } export const [ DocViewerStoreProviderDelegate , useDocViewerStore , useDocViewerCallbacks , useDocViewerMutator ] = createObservableStore < IDocViewerStore , Mutator , IDocViewerCallbacks > ( { initialValue : initialStore , mutatorFactory , callbacksFactory : useCallbacksFactory , enableShallowEquals : true } ) ; DocViewerStoreProviderDelegate . displayName = 'DocViewerStoreProviderDelegate' ; interface IProps { readonly children : React . ReactElement ; } const DocViewerStoreInner = React . memo ( function DocViewerStoreInner ( props ) { const docViewerCallbacks = useDocViewerCallbacks ( ) ; return ( < AnnotationMutationsContextProvider value = { docViewerCallbacks . annotationMutations } > { props . children } < /AnnotationMutationsContextProvider>); } ) ; export const DocViewerStore = React . memo ( function DocViewerStore ( props ) { return ( < DocViewerStoreProviderDelegate > < DocViewerStoreInner > { props . children } < /DocViewerStoreInner> < /DocViewerStoreProviderDelegate>); } ) ;