import Joyride , { ACTIONS , CallBackProps , EVENTS , STATUS } from 'react-joyride' ; import * as React from 'react' ; import { LifecycleToggle } from '../../ui/util/LifecycleToggle' ; import { LifecycleEvents } from '../../ui/util/LifecycleEvents' ; import { Logger } from 'polar-shared/src/logger/Logger' ; import { EnhancedStep , JoyrideTours } from '../../ui/tours/JoyrideTours' ; import { Devices } from "polar-shared/src/util/Devices" ; import { AppRuntime } from "polar-shared/src/util/AppRuntime" ; import { deepMemo } from "../../react/ReactUtils" ; const log = Logger . create ( ) ; const Z_INDEX = 100000 ; export class Styles { public static IMG : React . CSSProperties = { maxWidth : '450px' , maxHeight : '325px' , marginBottom : '10px' , display : 'block' , marginLeft : 'auto' , marginRight : 'auto' , } ; public static SPLIT_BAR_IMG : React . CSSProperties = { maxWidth : '225px' , maxHeight : '225px' , marginBottom : '10px' , display : 'block' , marginLeft : '5px' , marginRight : '5px' , } ; } function useSteps ( ) { const Term = ( props ) => { return < b > < i > { props . children } < /i></b > ; } ; const Title = ( props ) => { return < div style = { { fontSize : '22px' , marginLeft : '10px' } } > { props . children } < /div>; } ; interface IconProps { className : string ; } const Icon = ( props ) => { return < div className = "text-primary" > < i className = { props . className } style = { { fontSize : '175px' , marginLeft : '5px' , } } > < /i> < /div>; } ; const steps = [ JoyrideTours . createImageStep ( { target : 'header' , content : < div > < h2 className = "text-center" > Welcome to Polar ! < / h2 > < p > Polar allows you to : < /p> < ul > < li > < b > Keep all your reading < /b> in one place.</li > < li > < b > Track all your reading < /b> statistics.</li > < li > < b > Build a personal knowledge repository < /b> with <span className="text-dark" style={{ backgroundColor: 'rgba(255,255,0.3)' }}><b>highlights</b > < /span>, tags, and annotations.</li > < li > < b > Permanently remember < /b> facts using spaced repetition and incremental reading</li > < /ul> < p > Additionally , Polar supports < b > not just PDF < /b> documents but capturing < b > web content < /b> and storing it offline in your archive - forever ! < /p> < /div>, image : "/icon.png" , placement : 'center' } ) , JoyrideTours . createImageStep ( { target : 'header' , title : < Title > Web , Desktop and Cloud . < /Title>, content : < div > < p > You 're using the <b>web</b> version of Polar. < / p > < p > Polar works on both the desktop ( MacOS , Windows , and Linux ) as well as the web ( Chrome , Firefox , and major browsers ) and is < b > fully cloud aware < /b>. < /p> < /div>, image : "/web/assets/images/web.svg" , placement : 'center' , disabled : AppRuntime . isElectron ( ) } ) , { target : '#add-tags-dropdown' , title : < Title > Create Folders and Tags < / Title > , disableBeacon : true , content : < div > < p > You can create folders or tags by selecting this button or right clicking on the sidebar . < /p> < /div>, } , JoyrideTours . createImageStep ( { target : '#enable-cloud-sync, #cloud-sync-dropdown' , title : < Title > Cloud Sync < / Title > , content : < div > < p > Polar supports < Term > cloud sync < /Term> which keeps all your documents securely backed up in the cloud . Enabling < Term > cloud sync < /Term> also allow you to keep all your computers that run Polar fully synchronized . < /p> < p > This works transparently and realtime across MacOS , Windows , and Linux . < /p> < /div>, image : < Icon className = "fas fa-cloud-upload-alt" / > , disabled : ! AppRuntime . isElectron ( ) } ) , { target : '.doc-table-col-progress' , title : < Title > Reading Progress < / Title > , disableBeacon : true , content : < div > < b > Track your reading progress < /b> in each document with pagemarks ( manually now , soon to be automatic ) . < /div>, } , { target : '.doc-dropdown' , disableBeacon : true , content : < div > Documents can be < b > tagged , flagged , archived or deleted < /b>. < /div>, styles : { tooltip : { width : '650px' } } , } , { target : '#add-content-dropdown' , title : < Title > Add Documents < /Title>, content : < div > < p > Get started now by < b > clicking here to upload your first document < /b>. < /p> < /div>, } , ] ; return steps . filter ( current => ! current . disabled ) ; } export interface IState { readonly run : boolean ; readonly stepIndex : number ; } export const RepositoryTour2 = deepMemo ( ( ) => { const [ state , setState ] = React . useState < IState > ( { run : ! LifecycleToggle . isMarked ( LifecycleEvents . TOUR_TERMINATED ) , stepIndex : 0 } ) ; const steps = useSteps ( ) ; const doStep = React . useCallback ( ( callBackProps ) => { const stepIndex = callBackProps . index + ( callBackProps . action === ACTIONS . PREV ? - 1 : 1 ) ; setState ( { ... state , stepIndex } ) ; } , [ state ] ) ; const handleCallback = React . useCallback ( ( callbackProps ) => { const step = callbackProps . step ; if ( callbackProps . action === 'update' && step . autoNext ) { const nextStep = steps [ callbackProps . index + 1 ] ; const nextHandler = ( ) : boolean => { if ( nextStep . target instanceof HTMLElement ) { return true ; } const selector = nextStep . target ; return document . querySelector ( selector ) != null ; } ; const mutationHandler = ( ) => { if ( nextHandler ( ) ) { mutationObserver . disconnect ( ) ; const stepIndex = state . stepIndex + 1 ; setState ( { ... state , stepIndex , run : false } ) ; } } ; const mutationObserver = new MutationObserver ( mutationHandler ) ; mutationObserver . observe ( document . body , { childList : true , attributes : true , subtree : true } ) ; mutationHandler ( ) ; } if ( callbackProps . status === STATUS . SKIPPED || callbackProps . status === STATUS . FINISHED ) { try { switch ( callbackProps . status ) { case STATUS . SKIPPED : LifecycleToggle . mark ( LifecycleEvents . TOUR_SKIPPED ) ; break ; case STATUS . FINISHED : LifecycleToggle . mark ( LifecycleEvents . TOUR_FINISHED ) ; break ; } } finally { LifecycleToggle . mark ( LifecycleEvents . TOUR_TERMINATED ) ; } } else if ( callbackProps . type === EVENTS . STEP_AFTER ) { if ( ! state . run ) { setTimeout ( ( ) => { setState ( { ... state , run : true } ) ; } , 250 ) ; return ; } doStep ( callbackProps ) ; } else if ( callbackProps . type === EVENTS . TARGET_NOT_FOUND ) { log . warn ( "Not found: " , callbackProps ) ; doStep ( callbackProps ) ; } } , [ doStep , state , steps ] ) ; if ( ! Devices . isDesktop ( ) ) { return null ; } return ( < Joyride steps = { steps } continuous = { true } callback = { data => handleCallback ( data ) } run = { state . run } showProgress = { true } showSkipButton = { true } stepIndex = { state . stepIndex } styles = { { options : { primaryColor : '#007bff' , zIndex : Z_INDEX , } , tooltipContainer : { textAlign : 'left' , } } } />); } ) ;