From 62a61909700f69e173a797081a202801736c2a90 Mon Sep 17 00:00:00 2001 From: George Gevoian Date: Mon, 3 Jan 2022 18:20:58 -0600 Subject: [PATCH] (core) Add button for removing doc tours Summary: Document owners can now remove doc tours by pressing the button located to the right of 'Tour of this Document' in the left panel. Test Plan: Browser test. Reviewers: jarek Reviewed By: jarek Subscribers: jarek Differential Revision: https://phab.getgrist.com/D3202 --- app/client/components/GristDoc.ts | 6 ++- app/client/models/UserPrefs.ts | 5 +- app/client/ui/LeftPanelCommon.ts | 7 +-- app/client/ui/Tools.ts | 78 ++++++++++++++++++------------- 4 files changed, 55 insertions(+), 41 deletions(-) diff --git a/app/client/components/GristDoc.ts b/app/client/components/GristDoc.ts index 1c73d25c..563c5b2b 100644 --- a/app/client/components/GristDoc.ts +++ b/app/client/components/GristDoc.ts @@ -33,7 +33,7 @@ import {DocPageModel} from 'app/client/models/DocPageModel'; import {UserError} from 'app/client/models/errors'; import {urlState} from 'app/client/models/gristUrlState'; import {getFilterFunc, QuerySetManager} from 'app/client/models/QuerySet'; -import {getUserOrgPrefObs} from "app/client/models/UserPrefs"; +import {getUserOrgPrefObs, getUserOrgPrefsObs} from 'app/client/models/UserPrefs'; import {App} from 'app/client/ui/App'; import {DocHistory} from 'app/client/ui/DocHistory'; import {startDocTour} from "app/client/ui/DocTour"; @@ -128,6 +128,8 @@ export class GristDoc extends DisposableWithEvents { // Holds current cursor position with a view id public cursorPosition: Computed; + public readonly userOrgPrefs = getUserOrgPrefsObs(this.docPageModel.appModel); + private _actionLog: ActionLog; private _undoStack: UndoStack; private _lastOwnActionGroup: ActionGroupWithCursorPos|null = null; @@ -135,7 +137,7 @@ export class GristDoc extends DisposableWithEvents { private _docHistory: DocHistory; private _rightPanelTool = createSessionObs(this, "rightPanelTool", "none", RightPanelTool.guard); private _viewLayout: ViewLayout|null = null; - private _showGristTour = getUserOrgPrefObs(this.docPageModel.appModel, 'showGristTour'); + private _showGristTour = getUserOrgPrefObs(this.userOrgPrefs, 'showGristTour'); constructor( public readonly app: App, diff --git a/app/client/models/UserPrefs.ts b/app/client/models/UserPrefs.ts index 1fd8097e..c853fdd2 100644 --- a/app/client/models/UserPrefs.ts +++ b/app/client/models/UserPrefs.ts @@ -29,13 +29,12 @@ export function getUserOrgPrefsObs(appModel: AppModel): Observable } /** - * Creates an observable that returns a particular preference value from UserOrgPrefs, and which + * Creates an observable that returns a particular preference value from `prefsObs`, and which * stores it when set. */ export function getUserOrgPrefObs( - appModel: AppModel, prefName: Name + prefsObs: Observable, prefName: Name ): Observable { - const prefsObs = getUserOrgPrefsObs(appModel); return Computed.create(null, (use) => use(prefsObs)[prefName]) .onWrite(value => prefsObs.set({...prefsObs.get(), [prefName]: value})); } diff --git a/app/client/ui/LeftPanelCommon.ts b/app/client/ui/LeftPanelCommon.ts index b4c82ca8..d9f2b49c 100644 --- a/app/client/ui/LeftPanelCommon.ts +++ b/app/client/ui/LeftPanelCommon.ts @@ -156,17 +156,18 @@ export const cssSpacer = styled('div', ` height: 18px; `); -const cssSplitPageEntry = styled('div', ` +export const cssSplitPageEntry = styled('div', ` display: flex; align-items: center; `); -const cssPageEntryMain = styled(cssPageEntry, ` +export const cssPageEntryMain = styled(cssPageEntry, ` flex: auto; margin: 0; + min-width: 0px; `); -const cssPageEntrySmall = styled(cssPageEntry, ` +export const cssPageEntrySmall = styled(cssPageEntry, ` flex: none; border-radius: 3px; --icon-color: ${colors.lightGreen}; diff --git a/app/client/ui/Tools.ts b/app/client/ui/Tools.ts index abd6cfe4..9509f587 100644 --- a/app/client/ui/Tools.ts +++ b/app/client/ui/Tools.ts @@ -1,24 +1,28 @@ -import { GristDoc } from "app/client/components/GristDoc"; -import { urlState } from "app/client/models/gristUrlState"; -import { showExampleCard } from 'app/client/ui/ExampleCard'; -import { examples } from 'app/client/ui/ExampleInfo'; -import { createHelpTools, cssSectionHeader, cssSpacer, cssTools } from 'app/client/ui/LeftPanelCommon'; -import { cssLinkText, cssPageEntry, cssPageIcon, cssPageLink } from 'app/client/ui/LeftPanelCommon'; -import { hoverTooltip, tooltipCloseButton } from 'app/client/ui/tooltips'; -import { colors } from 'app/client/ui2018/cssVars'; -import { icon } from 'app/client/ui2018/icons'; -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"; +import {GristDoc} from 'app/client/components/GristDoc'; +import {loadGristDoc} from 'app/client/lib/imports'; +import {urlState} from 'app/client/models/gristUrlState'; +import {getUserOrgPrefObs} from 'app/client/models/UserPrefs'; +import {showExampleCard} from 'app/client/ui/ExampleCard'; +import {examples} from 'app/client/ui/ExampleInfo'; +import {createHelpTools, cssLinkText, cssPageEntry, cssPageEntryMain, cssPageEntrySmall, + cssPageIcon, cssPageLink, cssSectionHeader, cssSpacer, cssSplitPageEntry, + cssTools} from 'app/client/ui/LeftPanelCommon'; +import {hoverTooltip, tooltipCloseButton} from 'app/client/ui/tooltips'; +import {colors} from 'app/client/ui2018/cssVars'; +import {icon} from 'app/client/ui2018/icons'; +import {cssLink} from 'app/client/ui2018/links'; +import {menuAnnotate} from 'app/client/ui2018/menus'; +import {confirmModal} from 'app/client/ui2018/modals'; +import {userOverrideParams} from 'app/common/gristUrls'; +import {Computed, Disposable, dom, makeTestId, Observable, observable, styled} from 'grainjs'; const testId = makeTestId('test-tools-'); export function tools(owner: Disposable, gristDoc: GristDoc, leftPanelOpen: Observable): Element { const isOwner = gristDoc.docPageModel.currentDoc.get()?.access === 'owners'; const isOverridden = Boolean(gristDoc.docPageModel.userOverride.get()); + const hasDocTour = Computed.create(owner, use => + use(gristDoc.docModel.allTableIds.getObservable()).includes('GristDocTour')); const canViewAccessRules = observable(false); function updateCanViewAccessRules() { canViewAccessRules.set((isOwner && !isOverridden) || @@ -84,23 +88,32 @@ export function tools(owner: Disposable, gristDoc: GristDoc, leftPanelOpen: Obse ), ); }), - // 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, gristDoc.docComm, markAsSeen); - }, - gristDoc, - "seenDocTours", - gristDoc.docId() + // Show the 'Tour of this Document' button if a GristDocTour table exists. + dom.maybe(hasDocTour, () => + cssSplitPageEntry( + cssPageEntryMain( + cssPageLink(cssPageIcon('Page'), + cssLinkText('Tour of this Document'), + automaticHelpTool( + async ({markAsSeen}) => { + const gristDocModule = await loadGristDoc(); + await gristDocModule.startDocTour(gristDoc.docData, gristDoc.docComm, markAsSeen); + }, + gristDoc, + "seenDocTours", + gristDoc.docId() + ), + testId('doctour'), + ), ), + !isOwner ? null : cssPageEntrySmall( + cssPageLink(cssPageIcon('Remove'), + dom.on('click', () => confirmModal('Delete document tour?', 'Delete', () => + gristDoc.docData.sendAction(['RemoveTable', 'GristDocTour'])) + ), + testId('remove-doctour') + ), + ) ), ), createHelpTools(gristDoc.docPageModel.appModel, false) @@ -127,8 +140,7 @@ function automaticHelpTool( itemId: number | string ) { function show(elem: HTMLElement, reopen: boolean) { - const appModel = gristDoc.docPageModel.appModel; - const prefObs: Observable = getUserOrgPrefObs(appModel, prefKey); + const prefObs: Observable = getUserOrgPrefObs(gristDoc.userOrgPrefs, prefKey); const seenIds = prefObs.get() || []; // If this help was previously dismissed, don't show it again, unless the user is reopening it.