@ -11,6 +11,8 @@ import { cssLink } from 'app/client/ui2018/links';
import { menuAnnotate } from 'app/client/ui2018/menus' ;
import { userOverrideParams } from 'app/common/gristUrls' ;
import { Disposable , dom , makeTestId , Observable , observable , styled } from "grainjs" ;
import { getUserOrgPrefObs } from "app/client/models/UserPrefs" ;
import { loadGristDoc } from "app/client/lib/imports" ;
const testId = makeTestId ( 'test-tools-' ) ;
@ -67,29 +69,121 @@ export function tools(owner: Disposable, gristDoc: GristDoc, leftPanelOpen: Obse
if ( ! doc . workspace . isSupportWorkspace ) { return null ; }
const ex = examples . find ( ( e ) = > e . matcher . test ( doc . name ) ) ;
if ( ! ex || ! ex . tutorialUrl ) { return null ; }
const appModel = gristDoc . docPageModel . appModel ;
return cssPageEntry (
cssPageLink ( cssPageIcon ( 'Page' ) , cssLinkText ( 'How-to Tutorial' ) , testId ( 'tutorial' ) ,
{ href : ex.tutorialUrl , target : '_blank' } ,
cssExampleCardOpener (
icon ( 'TypeDetails' ) ,
dom . on ( 'click' , ( ev , elem ) = > {
ev . preventDefault ( ) ;
showExampleCard ( ex , appModel , elem , true ) ;
} ) ,
testId ( 'welcome-opener' ) ,
( elem ) = > {
// Once the trigger element is attached to DOM, show the card.
setTimeout ( ( ) = > showExampleCard ( ex , appModel , elem ) , 0 ) ;
} ,
automaticHelpTool (
( info ) = > showExampleCard ( ex , info ) ,
gristDoc ,
"seenExamples" ,
ex . id
) ,
) ,
) ,
) ;
} ) ,
// Shows the 'Tour of this Document' button if a GristDocTour table exists
// at the time of running. Currently doesn't observe the set of existing tables
gristDoc . docData . getTable ( 'GristDocTour' ) &&
cssPageEntry (
cssPageLink (
cssPageIcon ( 'Page' ) ,
cssLinkText ( 'Tour of this Document' ) ,
testId ( 'doctour' ) ,
automaticHelpTool (
async ( { markAsSeen } ) = > {
const gristDocModule = await loadGristDoc ( ) ;
await gristDocModule . startDocTour ( gristDoc . docData , markAsSeen ) ;
} ,
gristDoc ,
"seenDocTours" ,
gristDoc . docId ( )
) ,
) ,
) ,
createHelpTools ( gristDoc . docPageModel . appModel , false )
) ;
}
/ * *
* Helper for showing users some kind of help ( example cards or document tours )
* automatically if they haven ' t seen it before , or if they click
* on some element to explicitly show it again . Put this in said dom element ,
* and it will provide the onclick handler and a handler which automatically
* shows when the dom element is attached , both by calling showFunc .
*
* prefKey is a key for a list of identifiers saved in user preferences .
* itemId should be a single identifier that fits in that list .
* If itemId is already present then the help will not be shown automatically ,
* otherwise it will be added to the list and saved under prefKey
* when info . markAsSeen ( ) is called .
* /
function automaticHelpTool (
showFunc : ( info : AutomaticHelpToolInfo ) = > void ,
gristDoc : GristDoc ,
prefKey : 'seenExamples' | 'seenDocTours' ,
itemId : number | string
) {
function show ( elem : HTMLElement , reopen : boolean ) {
const appModel = gristDoc . docPageModel . appModel ;
const prefObs : Observable < typeof itemId [ ] | undefined > = getUserOrgPrefObs ( appModel , prefKey ) ;
const seenIds = prefObs . get ( ) || [ ] ;
// If this help was previously dismissed, don't show it again, unless the user is reopening it.
if ( ! reopen && seenIds . includes ( itemId ) ) {
return ;
}
// When the help is closed, if it's the first time it's dismissed, save this fact, to avoid
// showing it automatically again in the future.
function markAsSeen() {
try {
if ( ! seenIds . includes ( itemId ) ) {
const seen = new Set ( seenIds ) ;
seen . add ( itemId ) ;
prefObs . set ( [ . . . seen ] . sort ( ) ) ;
}
} catch ( e ) {
// If we fail to save this preference, it's probably not worth alerting the user about,
// so just log to console.
// tslint:disable-next-line:no-console
console . warn ( "Failed to save userPref " + prefKey , e ) ;
}
}
showFunc ( { elem , reopen , markAsSeen } ) ;
}
return [
dom . on ( 'click' , ( ev , elem ) = > {
ev . preventDefault ( ) ;
show ( elem as HTMLElement , true ) ;
} ) ,
( elem : HTMLElement ) = > {
// Once the trigger element is attached to DOM, show the help
setTimeout ( ( ) = > show ( elem , false ) , 0 ) ;
}
] ;
}
/** Values which may be useful when showing an automatic help tool */
export interface AutomaticHelpToolInfo {
// Element where automaticHelpTool is attached, typically a button,
// which shows the help when clicked
elem : HTMLElement ;
// true if the help was shown explicitly by clicking elem,
// false if it's being shown automatically to new users
reopen : boolean ;
// Call this when the user explicitly dismisses the help to
// remember this in user preferences and not show it automatically on next load
markAsSeen : ( ) = > void ;
}
// When viewing a page as another user, the "Access Rules" page link includes a button to revert
// the user and open the page, and a click on the page link shows a tooltip to revert.
function addRevertViewAsUI() {